import type { IncomingMessage, ServerResponse } from "node:http"; import type { Duplex } from "node:stream"; import type { Command } from "commander"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import type { DiagnosticEventPrivateData, DiagnosticEventInput, DiagnosticEventMetadata, DiagnosticEventPayload, } from "../infra/diagnostic-events.js"; import type { SecurityAuditFinding } from "../security/audit.types.js"; import type { PluginLogger } from "./logger-types.js"; type ChannelPlugin = import("../channels/plugins/types.plugin.js").ChannelPlugin; type PluginInteractiveHandlerResult = { handled?: boolean; } | void; export type PluginInteractiveRegistration< TContext = unknown, TChannel extends string = string, TResult = PluginInteractiveHandlerResult, > = { channel: TChannel; namespace: string; handler: (ctx: TContext) => Promise | TResult; }; export type PluginInteractiveHandlerRegistration = PluginInteractiveRegistration; export type OpenClawPluginHttpRouteAuth = "gateway" | "plugin"; export type OpenClawPluginHttpRouteMatch = "exact" | "prefix"; export type OpenClawPluginGatewayRuntimeScopeSurface = "write-default" | "trusted-operator"; export type OpenClawPluginHttpRouteHandler = ( req: IncomingMessage, res: ServerResponse, ) => Promise | boolean | void; export type OpenClawPluginHttpRouteUpgradeHandler = ( req: IncomingMessage, socket: Duplex, head: Buffer, ) => Promise | boolean | void; export type OpenClawPluginHttpRouteParams = { path: string; handler: OpenClawPluginHttpRouteHandler; handleUpgrade?: OpenClawPluginHttpRouteUpgradeHandler; auth: OpenClawPluginHttpRouteAuth; match?: OpenClawPluginHttpRouteMatch; gatewayRuntimeScopeSurface?: OpenClawPluginGatewayRuntimeScopeSurface; nodeCapability?: { surface: string; ttlMs?: number; }; replaceExisting?: boolean; }; export type OpenClawPluginHostedMediaResolver = ( mediaUrl: string, ) => string | null | undefined | Promise; export type OpenClawPluginCliContext = { /** * Command object where this plugin should register its commands. * * For root CLI registrations this is the root `openclaw` program. For nested * registrations it is the resolved parent command from `parentPath`. */ program: Command; parentPath: readonly string[]; config: OpenClawConfig; workspaceDir?: string; logger: PluginLogger; }; export type OpenClawPluginCliRegistrar = (ctx: OpenClawPluginCliContext) => void | Promise; /** * Top-level CLI metadata for plugin-owned commands. * * Descriptors are the parse-time contract for lazy plugin CLI registration. * If you want OpenClaw to keep a plugin command lazy-loaded while still * advertising it at the root CLI level, provide descriptors that cover every * top-level command root registered by that plugin CLI surface. */ export type OpenClawPluginCliCommandDescriptor = { name: string; description: string; hasSubcommands: boolean; }; export type OpenClawPluginNodeCliFeatureOptions = { /** Explicit node feature command names owned under `openclaw nodes`. */ commands?: string[]; /** * Parse-time command descriptors for lazy node feature CLI registration. * * Descriptors are registered under `openclaw nodes`, so a descriptor named * `"camera"` exposes `openclaw nodes camera`. */ descriptors?: OpenClawPluginCliCommandDescriptor[]; }; export type OpenClawPluginReloadRegistration = { restartPrefixes?: string[]; hotPrefixes?: string[]; noopPrefixes?: string[]; }; export type { OpenClawPluginNodeHostCommand, OpenClawPluginNodeHostCommandAvailabilityContext, OpenClawPluginNodeHostCommandIo, } from "./types.node-host.js"; export type OpenClawPluginNodeInvokeTransportResult = | { ok: true; payload?: unknown; payloadJSON?: string | null; } | { ok: false; code?: string; message: string; details?: Record; }; type OpenClawPluginNodeInvokeApprovalDecision = "allow-once" | "allow-always" | "deny"; type OpenClawPluginNodeInvokePolicyApprovalRuntime = { request: (input: { title: string; description: string; severity?: "info" | "warning" | "critical"; toolName?: string; toolCallId?: string; agentId?: string; sessionKey?: string; timeoutMs?: number; }) => Promise<{ id?: string; decision?: OpenClawPluginNodeInvokeApprovalDecision | null; }>; }; export type OpenClawPluginNodeInvokePolicyContext = { nodeId: string; command: string; params: unknown; timeoutMs?: number; idempotencyKey?: string; config: OpenClawConfig; pluginConfig?: Record; node?: { nodeId: string; displayName?: string; platform?: string; deviceFamily?: string; commands?: string[]; }; client?: { connId?: string; scopes?: string[]; } | null; approvals?: OpenClawPluginNodeInvokePolicyApprovalRuntime; invokeNode: (input?: { params?: unknown; timeoutMs?: number; idempotencyKey?: string; }) => Promise; }; export type OpenClawPluginNodeInvokePolicyResult = | { ok: true; payload?: unknown; payloadJSON?: string | null; } | { ok: false; message: string; code?: string; details?: Record; unavailable?: boolean; }; export type OpenClawPluginNodeInvokePolicy = { commands: string[]; /** * Platforms where these node-handled commands should be allowlisted by default. * Omit for commands that require explicit `gateway.nodes.allowCommands`. */ defaultPlatforms?: Array<"ios" | "android" | "macos" | "windows" | "linux" | "unknown">; /** * Dangerous policy commands are filtered out of default allowlists unless * explicitly allowed by config. */ dangerous?: boolean; /** * iOS foreground-restricted commands should be queued for foreground delivery * when an iOS node reports BACKGROUND_UNAVAILABLE. */ foregroundRestrictedOnIos?: boolean; handle: ( ctx: OpenClawPluginNodeInvokePolicyContext, ) => Promise | OpenClawPluginNodeInvokePolicyResult; }; export type OpenClawPluginSecurityAuditContext = { config: OpenClawConfig; sourceConfig: OpenClawConfig; env: NodeJS.ProcessEnv; stateDir: string; configPath: string; }; export type OpenClawPluginSecurityAuditCollector = ( ctx: OpenClawPluginSecurityAuditContext, ) => SecurityAuditFinding[] | Promise; export type OpenClawGatewayDiscoveryAdvertiseContext = { machineDisplayName: string; gatewayPort: number; gatewayTlsEnabled: boolean; gatewayTlsFingerprintSha256?: string; gatewayDirectReachable: boolean; canvasPort?: number; tailnetDns?: string; sshPort?: number; cliPath?: string; minimal: boolean; }; export type OpenClawGatewayDiscoveryService = { id: string; advertise: ( ctx: OpenClawGatewayDiscoveryAdvertiseContext, ) => void | Promise void | Promise }>; }; /** Context passed to long-lived plugin services. */ export type OpenClawPluginServiceContext = { config: OpenClawConfig; workspaceDir?: string; stateDir: string; logger: PluginLogger; gatewayEvents?: import("./gateway-events.js").OpenClawPluginGatewayEvents; startupTrace?: { detail?: (name: string, metrics: ReadonlyArray) => void; measure: (name: string, run: () => T | Promise) => Promise; }; internalDiagnostics?: { emit: (event: DiagnosticEventInput, privateData?: DiagnosticEventPrivateData) => void; onEvent: ( listener: ( event: DiagnosticEventPayload, metadata: DiagnosticEventMetadata, privateData: DiagnosticEventPrivateData, ) => void, ) => () => void; }; }; /** Background service registered by a plugin during `register(api)`. */ export type OpenClawPluginService = { id: string; start: (ctx: OpenClawPluginServiceContext) => void | Promise; stop?: (ctx: OpenClawPluginServiceContext) => void | Promise; }; export type OpenClawPluginChannelRegistration = { plugin: ChannelPlugin; }; /** * Public label exposed to plugin `register(api)` calls. * * Keep this as a compatibility signal for plugin authors. Loader internals * should derive explicit capability booleans from the mode instead of branching * on raw strings throughout the code path. * * - `full`: live runtime activation; long-lived side effects may start. * - `discovery`: read-only capability discovery; skip sockets/workers/clients. * - `tool-discovery`: capability discovery for executable tools; skip channel runtime hydration. * - `setup-only`: lightweight channel setup entry only. * - `setup-runtime`: setup flow that also needs the runtime channel entry. * - `cli-metadata`: CLI command metadata collection. */ export type PluginRegistrationMode = | "full" | "discovery" | "tool-discovery" | "setup-only" | "setup-runtime" | "cli-metadata";