mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-15 23:24:03 -06:00
a1064adb68
* fix(diagnostics-otel): harness.run root span adopts diagnostic trace context Fall back to internalOrTrustedParentContext when there is no active tracked parent, so the openclaw.harness.run root span (and its whole OTel subtree) adopts the diagnostic traceId instead of minting a new OTel root. This lets spans propagated via the diagnostic traceparent (e.g. a2a-gateway a2a.call / downstream a2a.task) share one trace with the run internals. * fix(diagnostics-otel): propagate exported span context Co-authored-by: HughTang <44694613+HughTang@users.noreply.github.com> * fix(diagnostics): break propagation import cycle * fix(diagnostics): preserve trace propagation contracts --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
329 lines
10 KiB
TypeScript
329 lines
10 KiB
TypeScript
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 { DiagnosticTracePropagationBridge as DiagnosticTracePropagationBridgeContract } from "../infra/diagnostic-trace-propagation.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 DiagnosticTracePropagationBridge = DiagnosticTracePropagationBridgeContract<
|
|
DiagnosticEventPayload,
|
|
DiagnosticEventMetadata
|
|
>;
|
|
|
|
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> | 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> | boolean | void;
|
|
|
|
export type OpenClawPluginHttpRouteUpgradeHandler = (
|
|
req: IncomingMessage,
|
|
socket: Duplex,
|
|
head: Buffer,
|
|
) => Promise<boolean | void> | 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<string | null | undefined>;
|
|
|
|
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<void>;
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
type OpenClawPluginCliCommandDescriptor = {
|
|
name: string;
|
|
description: string;
|
|
hasSubcommands: boolean;
|
|
};
|
|
|
|
/** Root-command metadata that is available before a plugin registrar is activated. */
|
|
export type OpenClawPluginCliRootCommandDescriptor = OpenClawPluginCliCommandDescriptor & {
|
|
machineOutput?: (params: { argv: readonly string[]; stdoutIsTTY: boolean }) => boolean;
|
|
};
|
|
|
|
type OpenClawPluginRootCliRegistrationOptions = {
|
|
/** Omit or pass an empty path for root commands. */
|
|
parentPath?: readonly [];
|
|
commands?: readonly string[];
|
|
descriptors?: readonly OpenClawPluginCliRootCommandDescriptor[];
|
|
};
|
|
|
|
/** Backward-compatible registration shape for dynamic root or nested paths. */
|
|
type OpenClawPluginLegacyCliRegistrationOptions = {
|
|
parentPath?: readonly string[];
|
|
commands?: readonly string[];
|
|
descriptors?: readonly OpenClawPluginCliCommandDescriptor[];
|
|
};
|
|
|
|
export type OpenClawPluginCliRegistrationOptions =
|
|
| OpenClawPluginRootCliRegistrationOptions
|
|
| OpenClawPluginLegacyCliRegistrationOptions;
|
|
|
|
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<string, unknown>;
|
|
};
|
|
|
|
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<string, unknown>;
|
|
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<OpenClawPluginNodeInvokeTransportResult>;
|
|
};
|
|
|
|
export type OpenClawPluginNodeInvokePolicyResult =
|
|
| {
|
|
ok: true;
|
|
payload?: unknown;
|
|
payloadJSON?: string | null;
|
|
}
|
|
| {
|
|
ok: false;
|
|
message: string;
|
|
code?: string;
|
|
details?: Record<string, unknown>;
|
|
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.commands.allow`.
|
|
*/
|
|
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> | OpenClawPluginNodeInvokePolicyResult;
|
|
};
|
|
|
|
export type OpenClawPluginSecurityAuditContext = {
|
|
config: OpenClawConfig;
|
|
sourceConfig: OpenClawConfig;
|
|
env: NodeJS.ProcessEnv;
|
|
stateDir: string;
|
|
configPath: string;
|
|
};
|
|
|
|
export type OpenClawPluginSecurityAuditCollector = (
|
|
ctx: OpenClawPluginSecurityAuditContext,
|
|
) => SecurityAuditFinding[] | Promise<SecurityAuditFinding[]>;
|
|
|
|
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 | { stop?: () => void | Promise<void> }>;
|
|
};
|
|
|
|
/** 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<readonly [string, number | string]>) => void;
|
|
measure: <T>(name: string, run: () => T | Promise<T>) => Promise<T>;
|
|
};
|
|
internalDiagnostics?: {
|
|
emit: (event: DiagnosticEventInput, privateData?: DiagnosticEventPrivateData) => void;
|
|
onEvent: (
|
|
listener: (
|
|
event: DiagnosticEventPayload,
|
|
metadata: DiagnosticEventMetadata,
|
|
privateData: DiagnosticEventPrivateData,
|
|
) => void,
|
|
) => () => void;
|
|
registerTracePropagationBridge?: (bridge: DiagnosticTracePropagationBridge) => () => void;
|
|
};
|
|
};
|
|
|
|
/** Background service registered by a plugin during `register(api)`. */
|
|
export type OpenClawPluginService = {
|
|
id: string;
|
|
start: (ctx: OpenClawPluginServiceContext) => void | Promise<void>;
|
|
stop?: (ctx: OpenClawPluginServiceContext) => void | Promise<void>;
|
|
};
|
|
|
|
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";
|