diff --git a/apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewayProtocol.kt b/apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewayProtocol.kt index 5e3e10de8206..6db0df61fcf9 100644 --- a/apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewayProtocol.kt +++ b/apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewayProtocol.kt @@ -534,6 +534,9 @@ enum class GatewayMethod( ProjectsRegister("projects.register"), ProjectsRemove("projects.remove"), WorkerDesktopLaunch("worker.desktop.launch"), + SecretsStoreList("secrets.store.list"), + SecretsStoreSet("secrets.store.set"), + SecretsStoreDelete("secrets.store.delete"), } enum class GatewayEvent( diff --git a/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift b/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift index c7bb16617d7a..0f8e13215add 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift +++ b/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift @@ -4237,6 +4237,160 @@ public struct UiCommandResult: Codable, Sendable { public struct SecretsReloadParams: Codable, Sendable {} +public struct SecretStoreSecretEntry: Codable, Sendable { + public let name: String + public let scopekind: String + public let scopeid: String + public let createdatms: Int + public let updatedatms: Int + public let updatedby: String? + public let kind: String + + public init( + name: String, + scopekind: String, + scopeid: String, + createdatms: Int, + updatedatms: Int, + updatedby: String? = nil, + kind: String) + { + self.name = name + self.scopekind = scopekind + self.scopeid = scopeid + self.createdatms = createdatms + self.updatedatms = updatedatms + self.updatedby = updatedby + self.kind = kind + } + + private enum CodingKeys: String, CodingKey { + case name + case scopekind = "scopeKind" + case scopeid = "scopeId" + case createdatms = "createdAtMs" + case updatedatms = "updatedAtMs" + case updatedby = "updatedBy" + case kind + } +} + +public struct SecretStoreEnvEntry: Codable, Sendable { + public let name: String + public let scopekind: String + public let scopeid: String + public let createdatms: Int + public let updatedatms: Int + public let updatedby: String? + public let kind: String + public let value: String + + public init( + name: String, + scopekind: String, + scopeid: String, + createdatms: Int, + updatedatms: Int, + updatedby: String? = nil, + kind: String, + value: String) + { + self.name = name + self.scopekind = scopekind + self.scopeid = scopeid + self.createdatms = createdatms + self.updatedatms = updatedatms + self.updatedby = updatedby + self.kind = kind + self.value = value + } + + private enum CodingKeys: String, CodingKey { + case name + case scopekind = "scopeKind" + case scopeid = "scopeId" + case createdatms = "createdAtMs" + case updatedatms = "updatedAtMs" + case updatedby = "updatedBy" + case kind + case value + } +} + +public struct SecretsStoreListParams: Codable, Sendable {} + +public struct SecretsStoreListResult: Codable, Sendable { + public let entries: [SecretStoreEntry] + + public init( + entries: [SecretStoreEntry]) + { + self.entries = entries + } + + private enum CodingKeys: String, CodingKey { + case entries + } +} + +public struct SecretsStoreSetParams: Codable, Sendable { + public let name: String + public let value: String + public let kind: AnyCodable + + public init( + name: String, + value: String, + kind: AnyCodable) + { + self.name = name + self.value = value + self.kind = kind + } + + private enum CodingKeys: String, CodingKey { + case name + case value + case kind + } +} + +public struct SecretsStoreDeleteParams: Codable, Sendable { + public let name: String + + public init( + name: String) + { + self.name = name + } + + private enum CodingKeys: String, CodingKey { + case name + } +} + +public struct SecretsStoreMutationResult: Codable, Sendable { + public let ok: Bool + public let reloaded: Bool + public let warningcount: Int? + + public init( + ok: Bool, + reloaded: Bool, + warningcount: Int? = nil) + { + self.ok = ok + self.reloaded = reloaded + self.warningcount = warningcount + } + + private enum CodingKeys: String, CodingKey { + case ok + case reloaded + case warningcount = "warningCount" + } +} + public struct SecretsResolveParams: Codable, Sendable { public let commandname: String public let targetids: [String] @@ -18683,6 +18837,37 @@ public enum UiCommand: Codable, Sendable { } } +public enum SecretStoreEntry: Codable, Sendable { + case secret(SecretStoreSecretEntry) + case env(SecretStoreEnvEntry) + + private enum CodingKeys: String, CodingKey { + case discriminator = "kind" + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let discriminator = try container.decode(String.self, forKey: .discriminator) + switch discriminator { + case "secret": self = try .secret(SecretStoreSecretEntry(from: decoder)) + case "env": self = try .env(SecretStoreEnvEntry(from: decoder)) + default: + throw DecodingError.dataCorruptedError( + forKey: .discriminator, + in: container, + debugDescription: "Unknown SecretStoreEntry discriminator value" + ) + } + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .secret(let value): try value.encode(to: encoder) + case .env(let value): try value.encode(to: encoder) + } + } +} + public enum SessionPlacement: Codable, Sendable { case local(LocalSessionPlacement) case requested(RequestedSessionPlacement) diff --git a/docs/.generated/plugin-sdk-api-baseline.jsonl b/docs/.generated/plugin-sdk-api-baseline.jsonl index b8467cc986a0..6d5ea3d5bd3a 100644 --- a/docs/.generated/plugin-sdk-api-baseline.jsonl +++ b/docs/.generated/plugin-sdk-api-baseline.jsonl @@ -53,17 +53,17 @@ {"closureHash":"ff3c4616cd6212a6d698831a8b287ad87e3968c8663f4090d095bb30ec40fc1c","declaration":"export function abortAndDrainAgentHarnessRun(params: { sessionId: string; sessionKey?: string; settleMs?: number; forceClear?: boolean; reason?: string; }): Promise;","entrypoint":"agent-harness","exportName":"abortAndDrainAgentHarnessRun","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"function","recordType":"export"} {"closureHash":"58989e3fa91eecf2e05202e36548e6e64a3b9219e83154bd99866c851604e34e","declaration":"export function createAgentToolResultMiddlewareRunner(ctx: AgentToolResultMiddlewareContext, handlers?: AgentToolResultMiddleware[]): { applyToolResultMiddleware(event: AgentToolResultMiddlewareEvent): Promise; };","entrypoint":"agent-harness","exportName":"createAgentToolResultMiddlewareRunner","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"function","recordType":"export"} {"closureHash":"b8f6355d7ad1700aceecddaa6e9ccf43091a792d5b481a1ee3f55294a881a67d","declaration":"export function createCodexAppServerToolResultExtensionRunner(ctx: CodexAppServerExtensionContext, factories?: CodexAppServerExtensionFactory[]): { applyToolResultExtensions(event: CodexAppServerToolResultEvent): Promise>; };","entrypoint":"agent-harness","exportName":"createCodexAppServerToolResultExtensionRunner","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"function","recordType":"export"} -{"closureHash":"2624f0c11a7852cedded8f42322cb6e6a6c719d6be2ec4571e8d1fb252c6eaaf","declaration":"export function createOpenClawCodingTools(options?: OpenClawCodingToolsOptions): AnyAgentTool[];","entrypoint":"agent-harness","exportName":"createOpenClawCodingTools","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"function","recordType":"export"} +{"closureHash":"386470d290d980e3bf8cb8863a967d02bcf7b184acd9e33ae704649f5ff29f2f","declaration":"export function createOpenClawCodingTools(options?: OpenClawCodingToolsOptions): AnyAgentTool[];","entrypoint":"agent-harness","exportName":"createOpenClawCodingTools","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"function","recordType":"export"} {"closureHash":"13649ee853485319e7449fda25c106b3f3f53d3090e53cdbb28e220a8529eb80","declaration":"export function disposeRegisteredAgentHarnesses(): Promise;","entrypoint":"agent-harness","exportName":"disposeRegisteredAgentHarnesses","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"function","recordType":"export"} {"closureHash":"f255a91162ece4c239bb8fa745efa14a1054bc024a291e9954f75f239b2b87c3","declaration":"export function resolveActiveEmbeddedRunSessionId(sessionKey: string): string | undefined;","entrypoint":"agent-harness","exportName":"resolveActiveEmbeddedRunSessionId","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"function","recordType":"export"} {"closureHash":"27d9f61df9ccf68615cb2002a6da891e36ab0c07623bc37c4d07aa980b446881","declaration":"export function resolveWebSearchToolPolicy(params: WebSearchToolPolicyParams): WebSearchToolPolicyResolution;","entrypoint":"agent-harness","exportName":"resolveWebSearchToolPolicy","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"function","recordType":"export"} -{"closureHash":"beb6715ad81487caa2cc2ed8444d6ac2dcf83c9e71fc42d5db9214f2eb12b946","declaration":"export type AgentHarness = AgentHarness;","entrypoint":"agent-harness","exportName":"AgentHarness","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"type","recordType":"export"} -{"closureHash":"ca20bb488d357785086f04e71b0fbb300a27325217bdbfdcaa1e422eca2fe255","declaration":"export type AgentHarnessV2 = AgentHarnessV2;","entrypoint":"agent-harness","exportName":"AgentHarnessV2","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"type","recordType":"export"} +{"closureHash":"ba0ca4adfbc76bcafbbe9487387636983a2c78cf10d056c44c6a9fd9d5191914","declaration":"export type AgentHarness = AgentHarness;","entrypoint":"agent-harness","exportName":"AgentHarness","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"type","recordType":"export"} +{"closureHash":"4914539ab49e7df134ad9aa32d4b13180cffe14571dd585092cfdf77d79c490a","declaration":"export type AgentHarnessV2 = AgentHarnessV2;","entrypoint":"agent-harness","exportName":"AgentHarnessV2","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"type","recordType":"export"} {"closureHash":"4ad0f830bf11a2db48faefb1439b7fbb08d9f22d22083f5e301327e1696f2310","declaration":"export type AgentToolResultMiddleware = AgentToolResultMiddleware;","entrypoint":"agent-harness","exportName":"AgentToolResultMiddleware","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"type","recordType":"export"} {"closureHash":"008b6c78d5e44a9d1df1d7e32e6015382ee9ba1cb9b10bd4c2b467619d1c89d4","declaration":"export type AgentToolResultMiddlewareEvent = AgentToolResultMiddlewareEvent;","entrypoint":"agent-harness","exportName":"AgentToolResultMiddlewareEvent","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"type","recordType":"export"} {"closureHash":"7414a2a79c2d78c1d52538250c827305bba1a2bb25010448837c8524a9f95b3c","declaration":"export type AnyAgentTool = AnyAgentTool;","entrypoint":"agent-harness","exportName":"AnyAgentTool","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"type","recordType":"export"} -{"closureHash":"b0d89fe509b6812c24b0a46cbc7d7ce20af0009a415c4e50e20c41af02975b11","declaration":"export type EmbeddedRunAttemptParams = EmbeddedRunAttemptParams;","entrypoint":"agent-harness","exportName":"EmbeddedRunAttemptParams","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"type","recordType":"export"} -{"closureHash":"67dd9a76536fb9f41ae19a380dd71bf10682ff80c63f363b5f8511948dcd1aab","declaration":"export type EmbeddedRunAttemptParamsV2 = EmbeddedRunAttemptParamsV2;","entrypoint":"agent-harness","exportName":"EmbeddedRunAttemptParamsV2","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"type","recordType":"export"} +{"closureHash":"21dd7ef82b095493961f4dd77b41c3574f64ea94fc0a7ef48aa24df1a51d015c","declaration":"export type EmbeddedRunAttemptParams = EmbeddedRunAttemptParams;","entrypoint":"agent-harness","exportName":"EmbeddedRunAttemptParams","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"type","recordType":"export"} +{"closureHash":"38b2e8da7857da2d07a2d5b34968f66fbbad5d3e0254ddce3518c6a7e414f860","declaration":"export type EmbeddedRunAttemptParamsV2 = EmbeddedRunAttemptParamsV2;","entrypoint":"agent-harness","exportName":"EmbeddedRunAttemptParamsV2","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"type","recordType":"export"} {"closureHash":"25c6985c3bef2c6efc6a96e70af6d66fe073be0b0d311681cfc0dfc186528d9a","declaration":"export type OpenClawAgentToolResult = OpenClawAgentToolResult;","entrypoint":"agent-harness","exportName":"OpenClawAgentToolResult","importSpecifier":"openclaw/plugin-sdk/agent-harness","kind":"type","recordType":"export"} {"category":"runtime","entrypoint":"agent-harness-runtime","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","recordType":"module"} {"closureHash":"fab99dfcb01d475a1bfdbe7a8fac9be10a93dac5f12ac78c2dea673c8ab86abf","declaration":"export function abortAgentHarnessRun(sessionId: string): boolean;\nexport function abortAgentHarnessRun(sessionId: undefined, opts: { mode: \"all\" | \"compacting\"; reason?: \"restart\"; }): boolean;","entrypoint":"agent-harness-runtime","exportName":"abortAgentHarnessRun","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} @@ -82,8 +82,8 @@ {"closureHash":"f8ad3d762c9753fdc36b2b9af0980167fdc54090c7bcca2f3dbe7c8951e86821","declaration":"export function buildAgentRuntimePlan(params: BuildAgentRuntimePlanParams): AgentRuntimePlan;","entrypoint":"agent-harness-runtime","exportName":"buildAgentRuntimePlan","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"528d11abf061b0b303722737de5a6406e7e55bffa38c45144c77d896ae1b3b0a","declaration":"export function buildBootstrapContextForFiles(bootstrapFiles: WorkspaceBootstrapFile[], params: { config?: OpenClawConfig; agentId?: string | null; warn?: (message: string) => void; }): EmbeddedContextFile[];","entrypoint":"agent-harness-runtime","exportName":"buildBootstrapContextForFiles","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"44d5a4667d2f3dc3a28e5c9d7b46765b1fb3c62820288e40251f5174911c8a6a","declaration":"export function buildEmbeddedAttemptToolRunContext(params: { trigger?: EmbeddedRunTrigger; jobId?: string; memoryFlushWritePath?: string; toolsAllow?: string[]; conversationToolPolicy?: GroupToolPolicyConfig; trace?: DiagnosticTraceContext; }): { trigger?: EmbeddedRunTrigger; jobId?: string; memoryFlushWritePath?: string; runtimeToolAllowlist?: string[]; conversationToolPolicy?: GroupToolPolicyConfig; trace?: DiagnosticTraceContext; };","entrypoint":"agent-harness-runtime","exportName":"buildEmbeddedAttemptToolRunContext","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} -{"closureHash":"e194f5cdcc6a602b877027a0c105c9e850c02f81439eb655d3e9d56febc28a22","declaration":"export function buildHarnessContextEngineRuntimeContext(params: Parameters[0]): ContextEngineRuntimeContext;","entrypoint":"agent-harness-runtime","exportName":"buildHarnessContextEngineRuntimeContext","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} -{"closureHash":"74b53fe40a520f5dffa1593ed0bfea15ac8b08cc74428d03375e47e9bed0adc1","declaration":"export function buildHarnessContextEngineRuntimeContextFromUsage(params: Parameters[0]): ContextEngineRuntimeContext;","entrypoint":"agent-harness-runtime","exportName":"buildHarnessContextEngineRuntimeContextFromUsage","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} +{"closureHash":"b4fb9e08ae2a178eaa565903b17309bc3280fbfbdaff154641496fe1d0ababa1","declaration":"export function buildHarnessContextEngineRuntimeContext(params: Parameters[0]): ContextEngineRuntimeContext;","entrypoint":"agent-harness-runtime","exportName":"buildHarnessContextEngineRuntimeContext","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} +{"closureHash":"aee437c5a340973c5080306d7bbb072ceb1e2f23743161c9700578c295aedea9","declaration":"export function buildHarnessContextEngineRuntimeContextFromUsage(params: Parameters[0]): ContextEngineRuntimeContext;","entrypoint":"agent-harness-runtime","exportName":"buildHarnessContextEngineRuntimeContextFromUsage","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"33a7cd8d63d4ef045ee3a42f806026a8b621e7dd32d8d6fbf4e7b44452d5e0ce","declaration":"export function buildNativeHookRelayCommand(params: { provider: NativeHookRelayProvider; relayId: string; generation?: string; event: NativeHookRelayEvent; preToolUseUnavailable?: \"noop\"; timeoutMs?: number; executable?: string; nice?: number | false; nodeExecutable?: string; }): string;","entrypoint":"agent-harness-runtime","exportName":"buildNativeHookRelayCommand","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"3846647c3c2fb0242f70591cf95c2e02f2a5a3c44447efb4cf7a31b2dc2122fd","declaration":"export function buildSkillWorkshopPromptSection(): string[];","entrypoint":"agent-harness-runtime","exportName":"buildSkillWorkshopPromptSection","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"32e977ece65144a83ae1b0a3c8d1b9b00a2a2506ad9ef1db3b2b9770ce762dbe","declaration":"export function buildWatchedSessionsHarnessContext(params: { config?: OpenClawConfig; sessionKey?: string; sandboxed?: boolean; toolNames: Iterable; capabilityToolNames?: Iterable; }): string | undefined;","entrypoint":"agent-harness-runtime","exportName":"buildWatchedSessionsHarnessContext","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} @@ -100,7 +100,7 @@ {"closureHash":"51f76e9e37b65b358f0313e10152d950b003bc3e50f4bb26fe29a988237772e2","declaration":"export function consumePreExecutionBlockedToolCall(toolCallId: string, runId?: string): boolean;","entrypoint":"agent-harness-runtime","exportName":"consumePreExecutionBlockedToolCall","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"58989e3fa91eecf2e05202e36548e6e64a3b9219e83154bd99866c851604e34e","declaration":"export function createAgentToolResultMiddlewareRunner(ctx: AgentToolResultMiddlewareContext, handlers?: AgentToolResultMiddleware[]): { applyToolResultMiddleware(event: AgentToolResultMiddlewareEvent): Promise; };","entrypoint":"agent-harness-runtime","exportName":"createAgentToolResultMiddlewareRunner","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"b8f6355d7ad1700aceecddaa6e9ccf43091a792d5b481a1ee3f55294a881a67d","declaration":"export function createCodexAppServerToolResultExtensionRunner(ctx: CodexAppServerExtensionContext, factories?: CodexAppServerExtensionFactory[]): { applyToolResultExtensions(event: CodexAppServerToolResultEvent): Promise>; };","entrypoint":"agent-harness-runtime","exportName":"createCodexAppServerToolResultExtensionRunner","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} -{"closureHash":"b63d7e807de37989954018686a2c3ddaa30f4bb71d610221b178495f978ef9bb","declaration":"export function deliverAgentHarnessUserInputPrompt(params: PromptDeliveryParams, questions: readonly AgentHarnessUserInputQuestion[], options?: AgentHarnessUserInputPromptOptions): Promise;","entrypoint":"agent-harness-runtime","exportName":"deliverAgentHarnessUserInputPrompt","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} +{"closureHash":"2cc350195e83eedd882b89c97eb5cb04b1e3b3d305e428c55e20a65276642530","declaration":"export function deliverAgentHarnessUserInputPrompt(params: PromptDeliveryParams, questions: readonly AgentHarnessUserInputQuestion[], options?: AgentHarnessUserInputPromptOptions): Promise;","entrypoint":"agent-harness-runtime","exportName":"deliverAgentHarnessUserInputPrompt","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"6f436485257f69fa07a18203b91d4341690522f12aff4f25805c7a1e21326ee3","declaration":"export function detectAndLoadAgentHarnessPromptImages(params: { prompt: string; workspaceDir: string; model: { input?: string[]; }; existingImages?: ImageContent[]; imageOrder?: PromptImageOrderEntry[]; media?: MediaFact[]; config?: OpenClawConfig; workspaceOnly?: boolean; localRoots?: readonly string[]; sandbox?: { root: string; bridge: SandboxFsBridge; }; }): Promise<{ images: ImageContent[]; detectedRefs: Array<{ raw: string; resolved: string; type: \"path\" | \"media-uri\"; }>; loadedCount: number; skippedCount: number; }>;","entrypoint":"agent-harness-runtime","exportName":"detectAndLoadAgentHarnessPromptImages","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"13649ee853485319e7449fda25c106b3f3f53d3090e53cdbb28e220a8529eb80","declaration":"export function disposeRegisteredAgentHarnesses(): Promise;","entrypoint":"agent-harness-runtime","exportName":"disposeRegisteredAgentHarnesses","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"c38cd390190bb07ddcd247420d6e0b489a0229bd402ddca0440417217a57251a","declaration":"export function emitAgentEvent(event: Omit): void;","entrypoint":"agent-harness-runtime","exportName":"emitAgentEvent","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} @@ -162,7 +162,7 @@ {"closureHash":"40154c3b7b2a7179df33f4a3fbef5e56078be2759d5aab29f39eb0f3c92fc631","declaration":"export function projectRuntimeToolInputSchema(schema: unknown, path?: string): RuntimeToolInputSchemaProjection;","entrypoint":"agent-harness-runtime","exportName":"projectRuntimeToolInputSchema","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"e42e3dc63e8545bb9a6056336fbd6c1dc8f356dce4f0f1c6d9765254c974390e","declaration":"export function projectSettledTurnFinalizationAttemptResult(result: AgentHarnessAttemptResult): AgentHarnessSettledTurnFinalizationResult;","entrypoint":"agent-harness-runtime","exportName":"projectSettledTurnFinalizationAttemptResult","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"3fbe470f065d2c2f16de538d8347934ec2daee2081d8903db1777b0afd2a8043","declaration":"export function queueAgentHarnessMessage(sessionId: string, text: string, options?: EmbeddedAgentQueueMessageOptions): boolean;","entrypoint":"agent-harness-runtime","exportName":"queueAgentHarnessMessage","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} -{"closureHash":"ed44ea59be68bee94ef22b61cd88171c361275dcc72e786f13155e997c803b28","declaration":"export function registerNativeHookRelay(params: RegisterNativeHookRelayParams): ActiveNativeHookRelayRegistrationHandle;","entrypoint":"agent-harness-runtime","exportName":"registerNativeHookRelay","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} +{"closureHash":"2cbefa09010966eae028310dcf3e8f918b35e79339c5d662b6949dc8ac78a213","declaration":"export function registerNativeHookRelay(params: RegisterNativeHookRelayParams): ActiveNativeHookRelayRegistrationHandle;","entrypoint":"agent-harness-runtime","exportName":"registerNativeHookRelay","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"b7cc6c35506dc6955e0359aa5c53c15a4d0db23639da8c1717f8fefda953c119","declaration":"export function requestDeferredPluginToolApproval(params: { deferredApproval: DeferredPluginToolApproval; signal?: AbortSignal; }): Promise;","entrypoint":"agent-harness-runtime","exportName":"requestDeferredPluginToolApproval","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"a2306cba08cb05ff8430c8362c0cc3c821706da5a4e65a06849ad0d0fb007ae3","declaration":"export function resetAgentEventsForTest(options?: { preserveListeners?: boolean; }): void;","entrypoint":"agent-harness-runtime","exportName":"resetAgentEventsForTest","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"f255a91162ece4c239bb8fa745efa14a1054bc024a291e9954f75f239b2b87c3","declaration":"export function resolveActiveEmbeddedRunSessionId(sessionKey: string): string | undefined;","entrypoint":"agent-harness-runtime","exportName":"resolveActiveEmbeddedRunSessionId","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} @@ -197,7 +197,7 @@ {"closureHash":"e4784aeda4c24b60cd49b7f5983e70a380a4759d5176c74d30f2900952ba848f","declaration":"export function runAgentHarnessBeforeAgentFinalizeHook(params: { event: PluginHookBeforeAgentFinalizeEvent; ctx: AgentHarnessHookContext; hookRunner?: AgentHarnessHookRunner; }): Promise;","entrypoint":"agent-harness-runtime","exportName":"runAgentHarnessBeforeAgentFinalizeHook","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"8fec73ee8c7c9be3335bd6dc1bac9278f0be5beef22a5975eb06e56f504f4bd6","declaration":"export function runAgentHarnessBeforeCompactionHook(params: { sessionFile: string; messages?: AgentMessage[]; ctx: AgentHarnessHookContext; }): Promise;","entrypoint":"agent-harness-runtime","exportName":"runAgentHarnessBeforeCompactionHook","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"021b81820bef2e8d54c50a5fc159d6665b23b50a1f5c40dae76a4833839d89c9","declaration":"export function runAgentHarnessBeforeMessageWriteHook(params: { message: AgentMessage; agentId?: string; sessionKey?: string; }): AgentMessage | null;","entrypoint":"agent-harness-runtime","exportName":"runAgentHarnessBeforeMessageWriteHook","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} -{"closureHash":"d40db54ef0fcc481063d718dea2ed1bdbe64b75aee82cd0efcf02ba86039765d","declaration":"export function runAgentHarnessGatewayQuestion(params: RunAgentHarnessGatewayQuestionParams): Promise;","entrypoint":"agent-harness-runtime","exportName":"runAgentHarnessGatewayQuestion","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} +{"closureHash":"0fb8eda48df904e43c6cfd11ddc37f385e68249574bda1db6f6e4fe56ae05f32","declaration":"export function runAgentHarnessGatewayQuestion(params: RunAgentHarnessGatewayQuestionParams): Promise;","entrypoint":"agent-harness-runtime","exportName":"runAgentHarnessGatewayQuestion","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"50a4d7cb20dd8f884abbcb62d77b5b2df183fbfcd058cc7aa76edacd1f7b90d8","declaration":"export function runAgentHarnessLlmInputHook(params: { event: PluginHookLlmInputEvent; ctx: AgentHarnessHookContext; hookRunner?: AgentHarnessHookRunner; }): void;","entrypoint":"agent-harness-runtime","exportName":"runAgentHarnessLlmInputHook","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"1bd55c141c249b1c04470cafe0cd4d41dce334ea2883ce83235af56d24a15cca","declaration":"export function runAgentHarnessLlmOutputHook(params: { event: PluginHookLlmOutputEvent; ctx: AgentHarnessHookContext; hookRunner?: AgentHarnessHookRunner; }): void;","entrypoint":"agent-harness-runtime","exportName":"runAgentHarnessLlmOutputHook","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} {"closureHash":"c4c5131ab7065decd8d8e935ac3779e3baa2c52fbbbd95a6ccb624377db09ca1","declaration":"export function runBeforeToolCallHook(args: { toolName: string; params: unknown; toolKind?: PluginHookToolKind; toolInputKind?: PluginHookToolInputKind; toolCallId?: string; ctx?: HookContext; signal?: AbortSignal; approvalMode?: \"request\" | \"report\" | \"deny\" | \"defer\"; }): Promise;","entrypoint":"agent-harness-runtime","exportName":"runBeforeToolCallHook","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"function","recordType":"export"} @@ -219,13 +219,13 @@ {"closureHash":"0701a22057edc3b733da7e05948db94aa98b337b224a5718efdd85212a8602bf","declaration":"export const TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT: string;","entrypoint":"agent-harness-runtime","exportName":"TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"const","recordType":"export"} {"closureHash":"b5845c99df6c2a834a16678c320e0111bad96a3a7ac245d379aa5e6d2555d29b","declaration":"export const agentHarnessAttemptTerminal: { merge: (current: AgentRunAttemptTerminal, incoming: AgentRunAttemptTerminal) => AgentRunAttemptTerminal; normalize: (input: { aborted?: boolean; externalAbort?: boolean; idleTimedOut?: boolean; promptError?: unknown; promptErrorSource?: AgentRunAttemptFailureSource | null; timedOut?: boolean; timedOutByRunBudget?: boolean; timedOutDuringCompaction?: boolean; timedOutDuringToolExecution?: boolean; }) => AgentRunAttemptTerminal; project: (terminal: AgentRunAttemptTerminal) => { aborted: boolean; cleanupYieldAborted: boolean; externalAbort: boolean; failed: boolean; idleTimedOut: boolean; interrupted: boolean; promptError: unknown; promptErrorSource: AgentRunAttemptFailureSource | null; timedOut: boolean; timedOutByRunBudget: boolean; timedOutDuringCompaction: boolean; timedOutDuringToolExecution: boolean; }; setFailure: (terminal: AgentRunAttemptTerminal, failure: { source: AgentRunAttemptFailureSource; error: unknown; } | null) => AgentRunAttemptTerminal;};","entrypoint":"agent-harness-runtime","exportName":"agentHarnessAttemptTerminal","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"const","recordType":"export"} {"closureHash":"22bcf962aa76c76074c4ed4ad7d6558edbc3588e239263e09af74c1000bbe012","declaration":"export const embeddedAgentLog: SubsystemLogger;","entrypoint":"agent-harness-runtime","exportName":"embeddedAgentLog","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"const","recordType":"export"} -{"closureHash":"3bf4f5b98fbf675ac0208efd9de33e62abb30a5a8063e99a661f097d0dee7b84","declaration":"export const nativeHookRelayTesting: { readonly clearNativeHookRelaysForTests: () => void; readonly getNativeHookRelayInvocationsForTests: () => NativeHookRelayInvocation[]; readonly getNativeHookRelayRegistrationForTests: (relayId: string) => NativeHookRelayRegistration | undefined; readonly getNativeHookRelayBridgeDirForTests: () => string; readonly getNativeHookRelayBridgeRegistryPathForTests: (relayId: string) => string; readonly getNativeHookRelayBridgeRecordForTests: (relayId: string) => Record | undefined; readonly isNativeHookRelayBridgeLookupRetryableForTests: (error: unknown, elapsedMs?: number) => boolean; readonly formatPermissionApprovalDescriptionForTests: (request: NativeHookRelayPermissionApprovalRequest) => string; readonly permissionRequestContentFingerprintForTests: (request: NativeHookRelayPermissionApprovalRequest) => string; readonly permissionRequestToolInputKeyFingerprintForTests: (toolInput: Record) => string; readonly setNativeHookRelayPermissionApprovalRequesterForTests: (requester: NativeHookRelayPermissionApprovalRequester) => void; readonly setNativeHookRelayDeferredToolApprovalRequesterForTests: (requester: NativeHookRelayDeferredToolApprovalRequester) => void;};","entrypoint":"agent-harness-runtime","exportName":"nativeHookRelayTesting","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"const","recordType":"export"} +{"closureHash":"a7f1fbe2e7e87de375ee95a655bb3de300a7357ed9cf723c93d830ab6c38a376","declaration":"export const nativeHookRelayTesting: { readonly clearNativeHookRelaysForTests: () => void; readonly getNativeHookRelayInvocationsForTests: () => NativeHookRelayInvocation[]; readonly getNativeHookRelayRegistrationForTests: (relayId: string) => NativeHookRelayRegistration | undefined; readonly getNativeHookRelayBridgeDirForTests: () => string; readonly getNativeHookRelayBridgeRegistryPathForTests: (relayId: string) => string; readonly getNativeHookRelayBridgeRecordForTests: (relayId: string) => Record | undefined; readonly isNativeHookRelayBridgeLookupRetryableForTests: (error: unknown, elapsedMs?: number) => boolean; readonly formatPermissionApprovalDescriptionForTests: (request: NativeHookRelayPermissionApprovalRequest) => string; readonly permissionRequestContentFingerprintForTests: (request: NativeHookRelayPermissionApprovalRequest) => string; readonly permissionRequestToolInputKeyFingerprintForTests: (toolInput: Record) => string; readonly setNativeHookRelayPermissionApprovalRequesterForTests: (requester: NativeHookRelayPermissionApprovalRequester) => void; readonly setNativeHookRelayDeferredToolApprovalRequesterForTests: (requester: NativeHookRelayDeferredToolApprovalRequester) => void;};","entrypoint":"agent-harness-runtime","exportName":"nativeHookRelayTesting","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"const","recordType":"export"} {"closureHash":"f237bbc11e1d389679cd4e2ad7aa9055951b3029348e3374b81f769786a51d55","declaration":"export type AbortAndDrainAgentHarnessRunResult = AbortAndDrainEmbeddedAgentRunResult;","entrypoint":"agent-harness-runtime","exportName":"AbortAndDrainAgentHarnessRunResult","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"6f802f46717d28d260bbcb2cf4790dfed4e9ab28c4fe822d259228189cca4665","declaration":"export type AgentApprovalEventData = AgentApprovalEventData;","entrypoint":"agent-harness-runtime","exportName":"AgentApprovalEventData","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"12aac3a57ce200b0f082002be237625729f84ed7c337f1bac90bcd0688a53319","declaration":"export type AgentEventPayload = AgentEventPayload;","entrypoint":"agent-harness-runtime","exportName":"AgentEventPayload","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} -{"closureHash":"beb6715ad81487caa2cc2ed8444d6ac2dcf83c9e71fc42d5db9214f2eb12b946","declaration":"export type AgentHarness = AgentHarness;","entrypoint":"agent-harness-runtime","exportName":"AgentHarness","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} -{"closureHash":"beb6715ad81487caa2cc2ed8444d6ac2dcf83c9e71fc42d5db9214f2eb12b946","declaration":"export type AgentHarnessAttemptParams = AgentHarnessAttemptParams;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessAttemptParams","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} -{"closureHash":"f6c32237f90d7fe6a024b516a1dd2e6e395c42c66db291649dab441fc1a84ebc","declaration":"export type AgentHarnessAttemptParamsV2 = AgentHarnessAttemptParamsV2;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessAttemptParamsV2","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} +{"closureHash":"ba0ca4adfbc76bcafbbe9487387636983a2c78cf10d056c44c6a9fd9d5191914","declaration":"export type AgentHarness = AgentHarness;","entrypoint":"agent-harness-runtime","exportName":"AgentHarness","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} +{"closureHash":"ba0ca4adfbc76bcafbbe9487387636983a2c78cf10d056c44c6a9fd9d5191914","declaration":"export type AgentHarnessAttemptParams = AgentHarnessAttemptParams;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessAttemptParams","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} +{"closureHash":"f8eecc849fc5f66fc354207bb843e21ba796fe2bfff4f10ebce342bf8485d7d3","declaration":"export type AgentHarnessAttemptParamsV2 = AgentHarnessAttemptParamsV2;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessAttemptParamsV2","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"2dc2a62deea63b316142cc28d40afb88fb0d1e5ce3e048f141420726cda38248","declaration":"export type AgentHarnessAttemptResult = AgentHarnessAttemptResult;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessAttemptResult","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"c7e642234081160830fd9e18a301900f8ee1ea8c88382e5a286f0102de65b714","declaration":"export type AgentHarnessAuthBindingFingerprintParams = AgentHarnessAuthBindingFingerprintParams;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessAuthBindingFingerprintParams","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"8f496ce75f294f2aa440a49c3c5b065ddff3a4714df775116af5e53f7fd14fe5","declaration":"export type AgentHarnessCompactParams = CompactEmbeddedAgentSessionParams;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessCompactParams","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} @@ -239,8 +239,8 @@ {"closureHash":"95741b872a194687696eba4a96d6e887ec6e81922695fa0282cb2f0a46ff9f97","declaration":"export type AgentHarnessSessionForkParams = AgentHarnessSessionForkParams;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessSessionForkParams","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"f3c46fef2a6c3393b0b6ad09915abcd6f57bb58ddddde1d60ebd113c7a1c5fa6","declaration":"export type AgentHarnessSessionForkResult = AgentHarnessSessionForkResult;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessSessionForkResult","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"4b5a59730309e446f59a8ec1c554c3193aca1959916594cc657347098bd67a35","declaration":"export type AgentHarnessSettledTurnFinalizationResult = AgentHarnessSettledTurnFinalizationResult;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessSettledTurnFinalizationResult","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} -{"closureHash":"cf635da6cc7587a76acf23e9b488e32f10eac93aa77df664c79f8e9d9e7117be","declaration":"export type AgentHarnessSideQuestionParams = AgentHarnessSideQuestionParams;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessSideQuestionParams","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} -{"closureHash":"9cccf03a0d94f0917acf7759c8438a447288490f2fa616890e342faa501f84ac","declaration":"export type AgentHarnessSideQuestionParamsV2 = AgentHarnessSideQuestionParamsV2;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessSideQuestionParamsV2","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} +{"closureHash":"11ce5bb99fdea336e9aba3b87bf5ef6c7d9ed4582bc69370b4c29d92be811fb2","declaration":"export type AgentHarnessSideQuestionParams = AgentHarnessSideQuestionParams;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessSideQuestionParams","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} +{"closureHash":"0096a0e88085d1a45460673a7fad0fdd3893141f7a424b7c0b28758a584903d2","declaration":"export type AgentHarnessSideQuestionParamsV2 = AgentHarnessSideQuestionParamsV2;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessSideQuestionParamsV2","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"e84173055503c40dd0ef1cb67c6d333e9257fcf995252b27c1d58f5c904e8de9","declaration":"export type AgentHarnessSideQuestionResult = AgentHarnessSideQuestionResult;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessSideQuestionResult","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"50214d08dac98e3c1031ac7bc733b85cb2247d2eef3b6b2045758ae403af7647","declaration":"export type AgentHarnessSupport = AgentHarnessSupport;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessSupport","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"50ce26ea22bc225e6064d2e5f37356b6f4ce4a57f8b9fa42baa4c421da48aad5","declaration":"export type AgentHarnessSupportContext = AgentHarnessSupportContext;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessSupportContext","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} @@ -250,7 +250,7 @@ {"closureHash":"20a8a7c9d5ebf425c30d04eea3856c6c889a210f1fa53c99299df24882afbf92","declaration":"export type AgentHarnessUserInputOption = AgentHarnessUserInputOption;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessUserInputOption","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"6224f51b4189f10a642ad7a8f50a902ec9c6a6a4f8dac0c0de5f7bc0271a27e5","declaration":"export type AgentHarnessUserInputPromptOptions = AgentHarnessUserInputPromptOptions;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessUserInputPromptOptions","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"d8f1d0d118e1536d1f442d11aeae5334a1ed7949a6c61824f547e12931278e1d","declaration":"export type AgentHarnessUserInputQuestion = AgentHarnessUserInputQuestion;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessUserInputQuestion","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} -{"closureHash":"ca20bb488d357785086f04e71b0fbb300a27325217bdbfdcaa1e422eca2fe255","declaration":"export type AgentHarnessV2 = AgentHarnessV2;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessV2","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} +{"closureHash":"4914539ab49e7df134ad9aa32d4b13180cffe14571dd585092cfdf77d79c490a","declaration":"export type AgentHarnessV2 = AgentHarnessV2;","entrypoint":"agent-harness-runtime","exportName":"AgentHarnessV2","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"6f2af683e6a31cde5a473c77e4f03b8f0ec0351a43ea6c0af979d5b394a33f95","declaration":"export type AgentMessage = AgentMessage;","entrypoint":"agent-harness-runtime","exportName":"AgentMessage","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"4ad0f830bf11a2db48faefb1439b7fbb08d9f22d22083f5e301327e1696f2310","declaration":"export type AgentToolResultMiddleware = AgentToolResultMiddleware;","entrypoint":"agent-harness-runtime","exportName":"AgentToolResultMiddleware","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"185abd2870d4df65e9f94623061a30167d17ef1279a395201fe429398bf23500","declaration":"export type AgentToolResultMiddlewareContext = AgentToolResultMiddlewareContext;","entrypoint":"agent-harness-runtime","exportName":"AgentToolResultMiddlewareContext","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} @@ -276,8 +276,8 @@ {"closureHash":"f30b66fd82f4cd61c60880c42a3b8f152ca64a2216036c99ab4129648ed329e7","declaration":"export type EmbeddedAgentCompactResult = EmbeddedAgentCompactResult;","entrypoint":"agent-harness-runtime","exportName":"EmbeddedAgentCompactResult","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"1bbefad0193436f7340f9d0dec50be5d482973325c3c65170b1053dd05566f0a","declaration":"export type EmbeddedContextFile = EmbeddedContextFile;","entrypoint":"agent-harness-runtime","exportName":"EmbeddedContextFile","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"f30b66fd82f4cd61c60880c42a3b8f152ca64a2216036c99ab4129648ed329e7","declaration":"export type EmbeddedPiCompactResult = EmbeddedAgentCompactResult;","entrypoint":"agent-harness-runtime","exportName":"EmbeddedPiCompactResult","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} -{"closureHash":"b0d89fe509b6812c24b0a46cbc7d7ce20af0009a415c4e50e20c41af02975b11","declaration":"export type EmbeddedRunAttemptParams = EmbeddedRunAttemptParams;","entrypoint":"agent-harness-runtime","exportName":"EmbeddedRunAttemptParams","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} -{"closureHash":"67dd9a76536fb9f41ae19a380dd71bf10682ff80c63f363b5f8511948dcd1aab","declaration":"export type EmbeddedRunAttemptParamsV2 = EmbeddedRunAttemptParamsV2;","entrypoint":"agent-harness-runtime","exportName":"EmbeddedRunAttemptParamsV2","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} +{"closureHash":"21dd7ef82b095493961f4dd77b41c3574f64ea94fc0a7ef48aa24df1a51d015c","declaration":"export type EmbeddedRunAttemptParams = EmbeddedRunAttemptParams;","entrypoint":"agent-harness-runtime","exportName":"EmbeddedRunAttemptParams","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} +{"closureHash":"38b2e8da7857da2d07a2d5b34968f66fbbad5d3e0254ddce3518c6a7e414f860","declaration":"export type EmbeddedRunAttemptParamsV2 = EmbeddedRunAttemptParamsV2;","entrypoint":"agent-harness-runtime","exportName":"EmbeddedRunAttemptParamsV2","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"a6cd2eebe0d06e6578ec8438a207c5acda10ddf01041102d84e4c576a0b6a42f","declaration":"export type EmbeddedRunAttemptResult = EmbeddedRunAttemptResult;","entrypoint":"agent-harness-runtime","exportName":"EmbeddedRunAttemptResult","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"f6cb2d6411db22cb04220777720d97bcca977351de1cb344ce2ff6ab126a3a08","declaration":"export type ExecApprovalDecision = ExecApprovalDecision;","entrypoint":"agent-harness-runtime","exportName":"ExecApprovalDecision","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"7acb0dffc7bcda6c727e4afc49c955fa8e14f4f8b7b45d197ed794dfa0d73b8e","declaration":"export type ExecAutoReviewDecision = ExecAutoReviewDecision;","entrypoint":"agent-harness-runtime","exportName":"ExecAutoReviewDecision","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} @@ -293,7 +293,7 @@ {"closureHash":"d8a720bf250da8daf4dc6a52087aa8728ec33b650b3ad3906fcb40e184de66a0","declaration":"export type NativeHookRelayEvent = \"pre_tool_use\" | \"post_tool_use\" | \"permission_request\" | \"before_agent_finalize\";","entrypoint":"agent-harness-runtime","exportName":"NativeHookRelayEvent","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"cb72ca18e056e52e779d0bf5b67606e1dc160f225225b55937304541105027f4","declaration":"export type NativeHookRelayProcessResponse = NativeHookRelayProcessResponse;","entrypoint":"agent-harness-runtime","exportName":"NativeHookRelayProcessResponse","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"6ee390ae4784b987e22255c16884fb4171033bbfaefaef655e3dd2a2513814a5","declaration":"export type NativeHookRelayProvider = \"codex\";","entrypoint":"agent-harness-runtime","exportName":"NativeHookRelayProvider","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} -{"closureHash":"b8476f913ffe1828ec5a260a90635d3766d93e772f6a34f804e45ba287c863c5","declaration":"export type NativeHookRelayRegistrationHandle = NativeHookRelayRegistrationHandle;","entrypoint":"agent-harness-runtime","exportName":"NativeHookRelayRegistrationHandle","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} +{"closureHash":"74122c798a2504074c02bea6ed6dbc1a4f7e937744740c7e65d0e751e3d48afe","declaration":"export type NativeHookRelayRegistrationHandle = NativeHookRelayRegistrationHandle;","entrypoint":"agent-harness-runtime","exportName":"NativeHookRelayRegistrationHandle","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"90fea3155b5d32c22da90460fdaea94e4fd6895d1ca18613d5036ba9835153f9","declaration":"export type NodeListNode = NodeListNode;","entrypoint":"agent-harness-runtime","exportName":"NodeListNode","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"cefe1325c866f0cf7edbec5dcca2fe420237eedc0da172e54094e365894d205d","declaration":"export type NormalizedUsage = NormalizedUsage;","entrypoint":"agent-harness-runtime","exportName":"NormalizedUsage","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} {"closureHash":"25c6985c3bef2c6efc6a96e70af6d66fe073be0b0d311681cfc0dfc186528d9a","declaration":"export type OpenClawAgentToolResult = OpenClawAgentToolResult;","entrypoint":"agent-harness-runtime","exportName":"OpenClawAgentToolResult","importSpecifier":"openclaw/plugin-sdk/agent-harness-runtime","kind":"type","recordType":"export"} @@ -762,7 +762,7 @@ {"closureHash":"9fc7e45343d7bfbc6ed56d05c9f824e2f9cf6dafc3ca4b25437d1f65c2200727","declaration":"export function clearAccountEntryFields(params: { accounts?: Record; accountId: string; fields: string[]; isValueSet?: (value: unknown) => boolean; markClearedOnFieldPresence?: boolean; }): { nextAccounts?: Record; changed: boolean; cleared: boolean; };","entrypoint":"channel-core","exportName":"clearAccountEntryFields","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"function","recordType":"export"} {"closureHash":"f4e084b64b5376268885a73fe507aa5d72bee17a88ec2d7ae1d19b36ea5f1ccb","declaration":"export function createChannelConfigUiHints(params: { channelLabel: string; dmPolicy?: { channelKey: string; includeLegacyNestedPolicy?: boolean; legacyNestedPolicyOrder?: \"before\" | \"after\"; }; configWrites?: boolean; mentionPatterns?: { targetDescription: string; policyTargetDescription?: string; policyNote?: string; denyNote?: string; }; nativeCommands?: boolean; implicitMentions?: boolean; progress?: { includeCommentary?: boolean; commentaryOrder?: \"before-command\" | \"after-command\"; labels?: \"openclaw\"; titleWording?: boolean; }; streaming?: Partial>; retry?: boolean; }): HintMap;","entrypoint":"channel-core","exportName":"createChannelConfigUiHints","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"function","recordType":"export"} {"closureHash":"ed8c322fac76968f7f059b65eaa881098ed918b22705b27101166b052213a9d9","declaration":"export function createChatChannelPlugin(params: { base: ChatChannelPluginBase; security?: ChannelSecurityAdapter | ChatChannelSecurityOptions; pairing?: ChannelPairingAdapter | ChatChannelPairingOptions; threading?: ChannelThreadingAdapter | ChatChannelThreadingOptions; outbound?: ChannelOutboundAdapter | ChatChannelAttachedOutboundOptions; }): ChannelPlugin;","entrypoint":"channel-core","exportName":"createChatChannelPlugin","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"function","recordType":"export"} -{"closureHash":"19ee364314ffd766b8f15835014206753bc1f96af4d7084b97a0a1cc2774d4df","declaration":"export function defineChannelPluginEntry({ id, name, description, plugin, configSchema, setRuntime, registerCliMetadata, registerFull, registerCapabilities, }: DefineChannelPluginEntryOptions): DefinedChannelPluginEntry;","entrypoint":"channel-core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"function","recordType":"export"} +{"closureHash":"8f524c7449eac2abe9ad13db2dfe98a31b24dcfe8c861d147bb48b684759e4f8","declaration":"export function defineChannelPluginEntry({ id, name, description, plugin, configSchema, setRuntime, registerCliMetadata, registerFull, registerCapabilities, }: DefineChannelPluginEntryOptions): DefinedChannelPluginEntry;","entrypoint":"channel-core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"function","recordType":"export"} {"closureHash":"0e0a9f6b22c433bdb1165e7707b1f9a1ddac1a544a8add00b503048cf157921f","declaration":"export function defineSetupPluginEntry(plugin: TPlugin): { plugin: TPlugin; };","entrypoint":"channel-core","exportName":"defineSetupPluginEntry","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"function","recordType":"export"} {"closureHash":"8895f7af11b994dcd383ebc45d8cfb3c3b1e6d88a64a001b04f604a83f77e517","declaration":"export function parseOptionalDelimitedEntries(value?: string): string[] | undefined;","entrypoint":"channel-core","exportName":"parseOptionalDelimitedEntries","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"function","recordType":"export"} {"closureHash":"3f97534b10d08eda6a556044eaba623b3744b200a2033877e48d2f2ca924d298","declaration":"export function recoverCurrentThreadSessionId(params: { route: ChannelOutboundSessionRoute; currentSessionKey?: string | null; canRecover?: (context: ThreadAwareOutboundSessionRouteRecoveryContext) => boolean; }): string | undefined;","entrypoint":"channel-core","exportName":"recoverCurrentThreadSessionId","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"function","recordType":"export"} @@ -774,24 +774,24 @@ {"closureHash":"8126b651a679d5435eaa5d346916792c3600e5f0f75d8492b077dd361854f7dd","declaration":"export type ChannelOutboundSessionRouteParams = { cfg: OpenClawConfig; agentId: string; accountId?: string | null; target: string; currentSessionKey?: string; resolvedTarget?: { to: string; kind: ChannelDirectoryEntryKind | \"channel\"; display?: string; source: \"normalized\" | \"directory\"; }; replyToId?: string | null; threadId?: string | number | null;};","entrypoint":"channel-core","exportName":"ChannelOutboundSessionRouteParams","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"type","recordType":"export"} {"closureHash":"37b373e93fe7f86fdbce119947c9e49b17e71ce50fe47e118c4d078c5cea20ec","declaration":"export type ChannelPlugin = ChannelPlugin;","entrypoint":"channel-core","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"type","recordType":"export"} {"closureHash":"8054ffbe71b91afced900ef8a06f5e368ac10c4f99ffc1c6f538a802856f61c6","declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"channel-core","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"type","recordType":"export"} -{"closureHash":"ff621688b7b58ebfa7157b153d83921a47158e43ac312ff61df85d437216ae10","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"channel-core","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"type","recordType":"export"} +{"closureHash":"b376fa0f3d5efb8c41fccbd2265e044a7ffed909a9d27d6e4dd340fbe2dc11f4","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"channel-core","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"type","recordType":"export"} {"closureHash":"86553d1dbbbe0b04d6a233ab33dee847c2cf5f9809032d41c5bff6bec06b78da","declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"channel-core","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"type","recordType":"export"} {"closureHash":"4180509522e17f8afdf5014bc51f5601952dfa403503e66f8f53e92b0c6ddd45","declaration":"export type PluginRuntime = PluginRuntime;","entrypoint":"channel-core","exportName":"PluginRuntime","importSpecifier":"openclaw/plugin-sdk/channel-core","kind":"type","recordType":"export"} {"category":"channel","entrypoint":"channel-dm-policy","importSpecifier":"openclaw/plugin-sdk/channel-dm-policy","recordType":"module"} {"closureHash":"821fe6ccd0d9323322bf70c4d1ca5697071a0e32032f44c1e14157162c2c729d","declaration":"export function createChannelDmPolicy(params: CreateChannelDmPolicyParams): ChannelSetupDmPolicy & { promptAllowFrom: NonNullable; };","entrypoint":"channel-dm-policy","exportName":"createChannelDmPolicy","importSpecifier":"openclaw/plugin-sdk/channel-dm-policy","kind":"function","recordType":"export"} {"category":null,"entrypoint":"channel-entry-contract","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","recordType":"module"} -{"closureHash":"449cfd12281e321f062ab0decf424e0dcdc9c4542aaed374c1d513c2e520a0fb","declaration":"export function defineBundledChannelEntry({ id, name, description, importMetaUrl, plugin, outbound, secrets, configSchema, runtime, accountInspect, features, registerCliMetadata, registerFull, registerCapabilities, }: DefineBundledChannelEntryOptions): BundledChannelEntryContract;","entrypoint":"channel-entry-contract","exportName":"defineBundledChannelEntry","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"function","recordType":"export"} -{"closureHash":"73376c2d9f2518d075102db21266e4ed390cff19ee17167d8d6dcaa90391aca4","declaration":"export function defineBundledChannelSetupEntry({ importMetaUrl, plugin, secrets, runtime, legacyStateMigrations, legacySessionSurface, registerSetupRuntime, features, }: DefineBundledChannelSetupEntryOptions): BundledChannelSetupEntryContract;","entrypoint":"channel-entry-contract","exportName":"defineBundledChannelSetupEntry","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"function","recordType":"export"} +{"closureHash":"ade5895d56db98a9f2455d7cef729a3afbf19a99b798cab08dae0f540a182dc1","declaration":"export function defineBundledChannelEntry({ id, name, description, importMetaUrl, plugin, outbound, secrets, configSchema, runtime, accountInspect, features, registerCliMetadata, registerFull, registerCapabilities, }: DefineBundledChannelEntryOptions): BundledChannelEntryContract;","entrypoint":"channel-entry-contract","exportName":"defineBundledChannelEntry","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"function","recordType":"export"} +{"closureHash":"b4912722dda44b4edad6b1d4a230b669006cc970de95687bab5e25754a8a5bc5","declaration":"export function defineBundledChannelSetupEntry({ importMetaUrl, plugin, secrets, runtime, legacyStateMigrations, legacySessionSurface, registerSetupRuntime, features, }: DefineBundledChannelSetupEntryOptions): BundledChannelSetupEntryContract;","entrypoint":"channel-entry-contract","exportName":"defineBundledChannelSetupEntry","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"function","recordType":"export"} {"closureHash":"cd150287d7e286e7a0d7d6caeb05d0a1efa63b5f351f75f69ba932510a108afd","declaration":"export function loadBundledEntryExportSync(importMetaUrl: string, reference: BundledEntryModuleRef, options?: BundledEntryModuleLoadOptions): T;","entrypoint":"channel-entry-contract","exportName":"loadBundledEntryExportSync","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"function","recordType":"export"} {"closureHash":"7781a3d570d08929af5b3b6cffbb9ef4b9c8e8f1bed55234e0015a681868c812","declaration":"export type AnyAgentTool = AnyAgentTool;","entrypoint":"channel-entry-contract","exportName":"AnyAgentTool","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} -{"closureHash":"685f95aaa394a91f73185ac30346f2046707aa09c77e4cfc27a100bc3ec23f15","declaration":"export type BundledChannelEntryContract = BundledChannelEntryContract;","entrypoint":"channel-entry-contract","exportName":"BundledChannelEntryContract","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} +{"closureHash":"95021e65b73f6bdc72919ece6b0ae62b64c920090f3b90ffae35cf72dc40f5b8","declaration":"export type BundledChannelEntryContract = BundledChannelEntryContract;","entrypoint":"channel-entry-contract","exportName":"BundledChannelEntryContract","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} {"closureHash":"f4b82dfd995bd6f08b232984743389d1c854866cb845a8dbb3605acb28818688","declaration":"export type BundledChannelEntryFeatures = BundledChannelEntryFeatures;","entrypoint":"channel-entry-contract","exportName":"BundledChannelEntryFeatures","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} {"closureHash":"960693591b9bdc974bf3f0097076d90490078db3dceb35349a4f851736cde4fc","declaration":"export type BundledChannelLegacySessionSurface = BundledChannelLegacySessionSurface;","entrypoint":"channel-entry-contract","exportName":"BundledChannelLegacySessionSurface","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} {"closureHash":"edf79e6a736e5c4977e3b86ef21e7fdf0c0b2bc7deacd8edbee2ca76cb8cef43","declaration":"export type BundledChannelLegacyStateMigrationDetector = BundledChannelLegacyStateMigrationDetector;","entrypoint":"channel-entry-contract","exportName":"BundledChannelLegacyStateMigrationDetector","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} -{"closureHash":"f95a404850bcbf1c6ec8227f92ecdf94e65f96de06f63f2f993a5d838b831f62","declaration":"export type BundledChannelSetupEntryContract = BundledChannelSetupEntryContract;","entrypoint":"channel-entry-contract","exportName":"BundledChannelSetupEntryContract","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} +{"closureHash":"881ff038bfc25dad609ec2af705bb9f8d358cb6c6a97fb61e6f02ecfcf4ba171","declaration":"export type BundledChannelSetupEntryContract = BundledChannelSetupEntryContract;","entrypoint":"channel-entry-contract","exportName":"BundledChannelSetupEntryContract","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} {"closureHash":"24679ecdf2ff34157b6345a37b58ec1363ec8cb1969b522599d0d92bb7a113b7","declaration":"export type BundledChannelSetupEntryFeatures = BundledChannelSetupEntryFeatures;","entrypoint":"channel-entry-contract","exportName":"BundledChannelSetupEntryFeatures","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} {"closureHash":"367219c821915e5be4d1ba3a86754475a5f8a4194512f0401de3be11cc649c60","declaration":"export type BundledEntryModuleLoadOptions = BundledEntryModuleLoadOptions;","entrypoint":"channel-entry-contract","exportName":"BundledEntryModuleLoadOptions","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} -{"closureHash":"fcee79acac847664aaaa8af8392ae3797cd55ac1e38dfbb622fafa5b7d3cb0bf","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"channel-entry-contract","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} +{"closureHash":"716afd752a579d491a3e55ac4bac0a2109dd839b73a815c2c044c6f8d5ce186e","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"channel-entry-contract","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} {"closureHash":"e54f9b270df967e0f857de804dbe989f2c55b637c449c9948c4baae8eee18c7b","declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"channel-entry-contract","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} {"closureHash":"568b5ab033b4970cedd3916d948d31bcdd3c19ea8928c7cd7dc7abf7623b8af8","declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"channel-entry-contract","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/channel-entry-contract","kind":"type","recordType":"export"} {"category":null,"entrypoint":"channel-feedback","importSpecifier":"openclaw/plugin-sdk/channel-feedback","recordType":"module"} @@ -1103,7 +1103,7 @@ {"closureHash":"9555a73ba2717bba0bf94888017be4cecef2c51cc3887ed34f8d8ef3c79d6abf","declaration":"export const DEFAULT_INGRESS_RETRY_DEAD_LETTER_MIN_AGE_MS: number;","entrypoint":"channel-message","exportName":"DEFAULT_INGRESS_RETRY_DEAD_LETTER_MIN_AGE_MS","importSpecifier":"openclaw/plugin-sdk/channel-message","kind":"const","recordType":"export"} {"closureHash":"3a2df3ab656149630c004d2d9f71b8ceda99aa81eb6b72b0cc7b3982ed0ce044","declaration":"export const DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS: 8;","entrypoint":"channel-message","exportName":"DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS","importSpecifier":"openclaw/plugin-sdk/channel-message","kind":"const","recordType":"export"} {"closureHash":"38c735a14c65067aa7825390d1c5c8876c651d0603e84980dfaf947f5235747a","declaration":"export const INGRESS_CLAIM_PROCESS_ID: string;","entrypoint":"channel-message","exportName":"INGRESS_CLAIM_PROCESS_ID","importSpecifier":"openclaw/plugin-sdk/channel-message","kind":"const","recordType":"export"} -{"closureHash":"49e98f675c3f385cf2acea1d1b7480f1a510449c1a04bf3d65475b9a7492d2dd","declaration":"export const deliverInboundReplyWithMessageSendContext: (params: DurableInboundReplyDeliveryParams) => Promise<{ status: \"not_applicable\"; reason: \"non_final\";} | { status: \"unsupported\"; reason: \"missing_channel\" | \"missing_target\" | \"missing_outbound_handler\" | \"capability_mismatch\"; capability?: DurableFinalDeliveryRequirement;} | { status: \"handled_visible\"; delivery: ChannelDeliveryResult;} | { status: \"handled_no_send\"; reason: \"no_visible_result\"; delivery: ChannelDeliveryResult;} | { status: \"failed\"; error: unknown; sentBeforeError?: true;}>;","entrypoint":"channel-message","exportName":"deliverInboundReplyWithMessageSendContext","importSpecifier":"openclaw/plugin-sdk/channel-message","kind":"const","recordType":"export"} +{"closureHash":"e00127c8d36bc7219cc2f31cc4ded3deb1ce54ca5287fe7f40611242433ab6bd","declaration":"export const deliverInboundReplyWithMessageSendContext: (params: DurableInboundReplyDeliveryParams) => Promise<{ status: \"not_applicable\"; reason: \"non_final\";} | { status: \"unsupported\"; reason: \"missing_channel\" | \"missing_target\" | \"missing_outbound_handler\" | \"capability_mismatch\"; capability?: DurableFinalDeliveryRequirement;} | { status: \"handled_visible\"; delivery: ChannelDeliveryResult;} | { status: \"handled_no_send\"; reason: \"no_visible_result\"; delivery: ChannelDeliveryResult;} | { status: \"failed\"; error: unknown; sentBeforeError?: true;}>;","entrypoint":"channel-message","exportName":"deliverInboundReplyWithMessageSendContext","importSpecifier":"openclaw/plugin-sdk/channel-message","kind":"const","recordType":"export"} {"closureHash":"2c52eff200a5b9bd2d93ee72209c2626bbe165f59b538591db661b94773023b0","declaration":"export type AgentPlanStep = AgentPlanStep;","entrypoint":"channel-message","exportName":"AgentPlanStep","importSpecifier":"openclaw/plugin-sdk/channel-message","kind":"type","recordType":"export"} {"closureHash":"1e8bcb2c8d8f5327f5b81b8c217f722f090dccba21ba3943324010f4f80a5a50","declaration":"export type AgentPlanStepStatus = AgentPlanStepStatus;","entrypoint":"channel-message","exportName":"AgentPlanStepStatus","importSpecifier":"openclaw/plugin-sdk/channel-message","kind":"type","recordType":"export"} {"closureHash":"b036fc26e5330dfd7da1af8839bb3c23e85283b9e16e252a659dd4bb0c35cd5e","declaration":"export type ChannelDeliveryStreamingConfig = ChannelDeliveryStreamingConfig;","entrypoint":"channel-message","exportName":"ChannelDeliveryStreamingConfig","importSpecifier":"openclaw/plugin-sdk/channel-message","kind":"type","recordType":"export"} @@ -1232,7 +1232,7 @@ {"closureHash":"9555a73ba2717bba0bf94888017be4cecef2c51cc3887ed34f8d8ef3c79d6abf","declaration":"export const DEFAULT_INGRESS_RETRY_DEAD_LETTER_MIN_AGE_MS: number;","entrypoint":"channel-outbound","exportName":"DEFAULT_INGRESS_RETRY_DEAD_LETTER_MIN_AGE_MS","importSpecifier":"openclaw/plugin-sdk/channel-outbound","kind":"const","recordType":"export"} {"closureHash":"3a2df3ab656149630c004d2d9f71b8ceda99aa81eb6b72b0cc7b3982ed0ce044","declaration":"export const DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS: 8;","entrypoint":"channel-outbound","exportName":"DEFAULT_INGRESS_RETRY_MAX_ATTEMPTS","importSpecifier":"openclaw/plugin-sdk/channel-outbound","kind":"const","recordType":"export"} {"closureHash":"38c735a14c65067aa7825390d1c5c8876c651d0603e84980dfaf947f5235747a","declaration":"export const INGRESS_CLAIM_PROCESS_ID: string;","entrypoint":"channel-outbound","exportName":"INGRESS_CLAIM_PROCESS_ID","importSpecifier":"openclaw/plugin-sdk/channel-outbound","kind":"const","recordType":"export"} -{"closureHash":"49e98f675c3f385cf2acea1d1b7480f1a510449c1a04bf3d65475b9a7492d2dd","declaration":"export const deliverInboundReplyWithMessageSendContext: (params: DurableInboundReplyDeliveryParams) => Promise<{ status: \"not_applicable\"; reason: \"non_final\";} | { status: \"unsupported\"; reason: \"missing_channel\" | \"missing_target\" | \"missing_outbound_handler\" | \"capability_mismatch\"; capability?: DurableFinalDeliveryRequirement;} | { status: \"handled_visible\"; delivery: ChannelDeliveryResult;} | { status: \"handled_no_send\"; reason: \"no_visible_result\"; delivery: ChannelDeliveryResult;} | { status: \"failed\"; error: unknown; sentBeforeError?: true;}>;","entrypoint":"channel-outbound","exportName":"deliverInboundReplyWithMessageSendContext","importSpecifier":"openclaw/plugin-sdk/channel-outbound","kind":"const","recordType":"export"} +{"closureHash":"e00127c8d36bc7219cc2f31cc4ded3deb1ce54ca5287fe7f40611242433ab6bd","declaration":"export const deliverInboundReplyWithMessageSendContext: (params: DurableInboundReplyDeliveryParams) => Promise<{ status: \"not_applicable\"; reason: \"non_final\";} | { status: \"unsupported\"; reason: \"missing_channel\" | \"missing_target\" | \"missing_outbound_handler\" | \"capability_mismatch\"; capability?: DurableFinalDeliveryRequirement;} | { status: \"handled_visible\"; delivery: ChannelDeliveryResult;} | { status: \"handled_no_send\"; reason: \"no_visible_result\"; delivery: ChannelDeliveryResult;} | { status: \"failed\"; error: unknown; sentBeforeError?: true;}>;","entrypoint":"channel-outbound","exportName":"deliverInboundReplyWithMessageSendContext","importSpecifier":"openclaw/plugin-sdk/channel-outbound","kind":"const","recordType":"export"} {"closureHash":"2c52eff200a5b9bd2d93ee72209c2626bbe165f59b538591db661b94773023b0","declaration":"export type AgentPlanStep = AgentPlanStep;","entrypoint":"channel-outbound","exportName":"AgentPlanStep","importSpecifier":"openclaw/plugin-sdk/channel-outbound","kind":"type","recordType":"export"} {"closureHash":"1e8bcb2c8d8f5327f5b81b8c217f722f090dccba21ba3943324010f4f80a5a50","declaration":"export type AgentPlanStepStatus = AgentPlanStepStatus;","entrypoint":"channel-outbound","exportName":"AgentPlanStepStatus","importSpecifier":"openclaw/plugin-sdk/channel-outbound","kind":"type","recordType":"export"} {"closureHash":"b036fc26e5330dfd7da1af8839bb3c23e85283b9e16e252a659dd4bb0c35cd5e","declaration":"export type ChannelDeliveryStreamingConfig = ChannelDeliveryStreamingConfig;","entrypoint":"channel-outbound","exportName":"ChannelDeliveryStreamingConfig","importSpecifier":"openclaw/plugin-sdk/channel-outbound","kind":"type","recordType":"export"} @@ -1298,7 +1298,7 @@ {"closureHash":"cd6b0d3f962c8cb40d405e92bb06bb8e9285b386200897f550d21036022262c1","declaration":"export const PAIRING_APPROVED_MESSAGE: \"✅ OpenClaw access approved. Send a message to start chatting.\";","entrypoint":"channel-plugin-common","exportName":"PAIRING_APPROVED_MESSAGE","importSpecifier":"openclaw/plugin-sdk/channel-plugin-common","kind":"const","recordType":"export"} {"closureHash":"712435252d2fe5135542ca6b5a4375a6edea24e83829338011dc6ae7e9802fbb","declaration":"export type ChannelMessageActionContext = ChannelMessageActionContext;","entrypoint":"channel-plugin-common","exportName":"ChannelMessageActionContext","importSpecifier":"openclaw/plugin-sdk/channel-plugin-common","kind":"type","recordType":"export"} {"closureHash":"37b373e93fe7f86fdbce119947c9e49b17e71ce50fe47e118c4d078c5cea20ec","declaration":"export type ChannelPlugin = ChannelPlugin;","entrypoint":"channel-plugin-common","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk/channel-plugin-common","kind":"type","recordType":"export"} -{"closureHash":"ff621688b7b58ebfa7157b153d83921a47158e43ac312ff61df85d437216ae10","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"channel-plugin-common","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/channel-plugin-common","kind":"type","recordType":"export"} +{"closureHash":"b376fa0f3d5efb8c41fccbd2265e044a7ffed909a9d27d6e4dd340fbe2dc11f4","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"channel-plugin-common","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/channel-plugin-common","kind":"type","recordType":"export"} {"closureHash":"4180509522e17f8afdf5014bc51f5601952dfa403503e66f8f53e92b0c6ddd45","declaration":"export type PluginRuntime = PluginRuntime;","entrypoint":"channel-plugin-common","exportName":"PluginRuntime","importSpecifier":"openclaw/plugin-sdk/channel-plugin-common","kind":"type","recordType":"export"} {"category":null,"entrypoint":"channel-policy","importSpecifier":"openclaw/plugin-sdk/channel-policy","recordType":"module"} {"closureHash":"017ab29d16d0f3d02d60f3513e65a373868d26496bc93d020d8fc8c17fd875f4","declaration":"export function buildAccountScopedDmSecurityPolicy(params: { cfg: OpenClawConfig; channelKey: string; accountId?: string | null; fallbackAccountId?: string | null; policy?: string | null; allowFrom?: Array | null; defaultPolicy?: string; allowFromPathSuffix?: string; policyPathSuffix?: string; approveChannelId?: string; approveHint?: string; normalizeEntry?: (raw: string) => string; inheritSharedDefaultsFromDefaultAccount?: boolean; }): ChannelSecurityDmPolicy;","entrypoint":"channel-policy","exportName":"buildAccountScopedDmSecurityPolicy","importSpecifier":"openclaw/plugin-sdk/channel-policy","kind":"function","recordType":"export"} @@ -1897,8 +1897,8 @@ {"closureHash":"ed8c322fac76968f7f059b65eaa881098ed918b22705b27101166b052213a9d9","declaration":"export function createChatChannelPlugin(params: { base: ChatChannelPluginBase; security?: ChannelSecurityAdapter | ChatChannelSecurityOptions; pairing?: ChannelPairingAdapter | ChatChannelPairingOptions; threading?: ChannelThreadingAdapter | ChatChannelThreadingOptions; outbound?: ChannelOutboundAdapter | ChatChannelAttachedOutboundOptions; }): ChannelPlugin;","entrypoint":"core","exportName":"createChatChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export"} {"closureHash":"785b0f750c5bd20f9f3ca081886a5af79ee0e4d2c38f78f769bf994b234f5076","declaration":"export function createDedupeCache(options: DedupeCacheOptions): DedupeCache;","entrypoint":"core","exportName":"createDedupeCache","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export"} {"closureHash":"e2df8d235401aebf58eb71652fbeaedb42ca00aaa5042a76909ff2c48ba6fd54","declaration":"export function createSubsystemLogger(subsystem: string): SubsystemLogger;","entrypoint":"core","exportName":"createSubsystemLogger","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export"} -{"closureHash":"19ee364314ffd766b8f15835014206753bc1f96af4d7084b97a0a1cc2774d4df","declaration":"export function defineChannelPluginEntry({ id, name, description, plugin, configSchema, setRuntime, registerCliMetadata, registerFull, registerCapabilities, }: DefineChannelPluginEntryOptions): DefinedChannelPluginEntry;","entrypoint":"core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export"} -{"closureHash":"ea866f93256b2e5f8da5bad7c96511f00f9b42fa85f6689cc70ab26bde2dd0cb","declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, reload, nodeHostCommands, securityAuditCollectors, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"core","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export"} +{"closureHash":"8f524c7449eac2abe9ad13db2dfe98a31b24dcfe8c861d147bb48b684759e4f8","declaration":"export function defineChannelPluginEntry({ id, name, description, plugin, configSchema, setRuntime, registerCliMetadata, registerFull, registerCapabilities, }: DefineChannelPluginEntryOptions): DefinedChannelPluginEntry;","entrypoint":"core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export"} +{"closureHash":"7aeb838b8cca4b269332d981138c48fac04f71db26beb4552131a8981bded02b","declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, reload, nodeHostCommands, securityAuditCollectors, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"core","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export"} {"closureHash":"0e0a9f6b22c433bdb1165e7707b1f9a1ddac1a544a8add00b503048cf157921f","declaration":"export function defineSetupPluginEntry(plugin: TPlugin): { plugin: TPlugin; };","entrypoint":"core","exportName":"defineSetupPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export"} {"closureHash":"3c5ce0c3733cbb038aeb914af1df5d701f362f0521921b8abd0c9214db9cc4b8","declaration":"export function delegateCompactionToRuntime(params: Parameters[0]): Promise;","entrypoint":"core","exportName":"delegateCompactionToRuntime","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export"} {"closureHash":"2c282e0e308f9442bca7cdeb8e89a097531b1d54a692c9d86e5f39a8db1c2537","declaration":"export function deleteAccountFromConfigSection(params: { cfg: OpenClawConfig; sectionKey: string; accountId: string; clearBaseFields?: string[]; }): OpenClawConfig;","entrypoint":"core","exportName":"deleteAccountFromConfigSection","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export"} @@ -1945,7 +1945,7 @@ {"closureHash":"2e6ce1f4c1692270a00dc0cf08e2752a26739c3143f8091cb270063c6034605d","declaration":"export function tryReadSecretFileSync(filePath: string | undefined, label: string, options: CredentialFileReadOptions): string | undefined;\nexport function tryReadSecretFileSync(filePath: string | undefined, label: string, options?: FsSafeSecretFileReadOptions): string | undefined;\nexport function tryReadSecretFileSync(filePath: string, label: string, options: FsSafeSecretFileReadOptions | undefined, diagnostic: { configPath: string; }): ConfiguredCredentialResult;\nexport function tryReadSecretFileSync(filePath: string | undefined, label: string, options: FsSafeSecretFileReadOptions | undefined, diagnostic: { configPath: string; }): CredentialResult;","entrypoint":"core","exportName":"tryReadSecretFileSync","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export"} {"closureHash":"b40933a11984425d14a7a479bda0d2cd14cbfa9371164774f39bebbf12861df7","declaration":"export const DEFAULT_ACCOUNT_ID: \"default\";","entrypoint":"core","exportName":"DEFAULT_ACCOUNT_ID","importSpecifier":"openclaw/plugin-sdk/core","kind":"const","recordType":"export"} {"closureHash":null,"declaration":"export const DEFAULT_SECRET_FILE_MAX_BYTES: number;","entrypoint":"core","exportName":"DEFAULT_SECRET_FILE_MAX_BYTES","importSpecifier":"openclaw/plugin-sdk/core","kind":"const","recordType":"export"} -{"closureHash":"beb6715ad81487caa2cc2ed8444d6ac2dcf83c9e71fc42d5db9214f2eb12b946","declaration":"export type AgentHarness = AgentHarness;","entrypoint":"core","exportName":"AgentHarness","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} +{"closureHash":"ba0ca4adfbc76bcafbbe9487387636983a2c78cf10d056c44c6a9fd9d5191914","declaration":"export type AgentHarness = AgentHarness;","entrypoint":"core","exportName":"AgentHarness","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} {"closureHash":"d2ff4de9ee3fd111c2cc64a3ee7e557d9f161af1b6b437ee7886bcb16e426317","declaration":"export type AgentPromptGuidance = AgentPromptGuidance;","entrypoint":"core","exportName":"AgentPromptGuidance","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} {"closureHash":"2841127a90e04467adfea8126cbf03655f44ddb089ad9e0d48833c2beea39cbd","declaration":"export type AgentPromptGuidanceEntry = AgentPromptGuidanceEntry;","entrypoint":"core","exportName":"AgentPromptGuidanceEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} {"closureHash":"aa0412acefe62caff12e5271ead0545504874e824380dc21cfc0566388a05f33","declaration":"export type AgentPromptSurfaceKind = \"openclaw_main\" | \"pi_main\" | \"codex_app_server\" | \"cli_backend\" | \"acp_backend\" | \"subagent\";","entrypoint":"core","exportName":"AgentPromptSurfaceKind","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} @@ -1977,10 +1977,10 @@ {"closureHash":"54ea426dde51859b572b4c1079d2b70b412096f73da2b8c265f2749d9f01cbb5","declaration":"export type NormalizedLocation = NormalizedLocation;","entrypoint":"core","exportName":"NormalizedLocation","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} {"closureHash":"8054ffbe71b91afced900ef8a06f5e368ac10c4f99ffc1c6f538a802856f61c6","declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"core","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} {"closureHash":"5189698d102b8b064eefe1442b008312973c3994fd6b203d2205725d7ee9d118","declaration":"export type OpenClawPluginActiveModelContext = OpenClawPluginActiveModelContext;","entrypoint":"core","exportName":"OpenClawPluginActiveModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} -{"closureHash":"ff621688b7b58ebfa7157b153d83921a47158e43ac312ff61df85d437216ae10","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"core","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} +{"closureHash":"b376fa0f3d5efb8c41fccbd2265e044a7ffed909a9d27d6e4dd340fbe2dc11f4","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"core","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} {"closureHash":"c968e96cdb5c97e92d26b7b15fb5255531b45a12e7c5c0a2e7e456d31cc6e358","declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"core","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} {"closureHash":"365beb5cee974639c2f35fc7a39088721ca8e9c618442830298b98033e9c768c","declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"core","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} -{"closureHash":"05cefddc1e544e0efd37301f07a5adae39f6cc580239b314c1959b8887a23ce9","declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"core","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} +{"closureHash":"6f784a7e4e308207c5cb6c31f8cef940c2d37117f1e0517151275790e3183a0a","declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"core","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} {"closureHash":"a7ed83f0924f818b23cecb5a1f3591ff28b9499ccac9667a2b8505c2bc608ea9","declaration":"export type OpenClawPluginGatewayEventScope = OpenClawPluginGatewayEventScope;","entrypoint":"core","exportName":"OpenClawPluginGatewayEventScope","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} {"closureHash":"f81f224d9b837e6353b7a546c5a76b124bcf568fd1f1fd0c6881fda30c53120a","declaration":"export type OpenClawPluginGatewayEvents = OpenClawPluginGatewayEvents;","entrypoint":"core","exportName":"OpenClawPluginGatewayEvents","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} {"closureHash":"ddc2738dd0ece94ddde9a7c655f92c15ec6416645ecf932d4aa404c7b7761159","declaration":"export type OpenClawPluginService = OpenClawPluginService;","entrypoint":"core","exportName":"OpenClawPluginService","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export"} @@ -2215,7 +2215,7 @@ {"closureHash":"0db968dad99d84ecef5202167daadd092e3b4f456270283d3fd61585ac2bbd5a","declaration":"export type DiscordComponentSendResult = DiscordComponentSendResult;","entrypoint":"discord","exportName":"DiscordComponentSendResult","importSpecifier":"openclaw/plugin-sdk/discord","kind":"type","recordType":"export"} {"closureHash":"084b46d7dd510eb182a4705559251e824220b57dbfa6dc54392507b72ba1815e","declaration":"export type DiscordOutboundTargetResolution = DiscordOutboundTargetResolution;","entrypoint":"discord","exportName":"DiscordOutboundTargetResolution","importSpecifier":"openclaw/plugin-sdk/discord","kind":"type","recordType":"export"} {"closureHash":"8054ffbe71b91afced900ef8a06f5e368ac10c4f99ffc1c6f538a802856f61c6","declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"discord","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk/discord","kind":"type","recordType":"export"} -{"closureHash":"ff621688b7b58ebfa7157b153d83921a47158e43ac312ff61df85d437216ae10","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"discord","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/discord","kind":"type","recordType":"export"} +{"closureHash":"b376fa0f3d5efb8c41fccbd2265e044a7ffed909a9d27d6e4dd340fbe2dc11f4","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"discord","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/discord","kind":"type","recordType":"export"} {"closureHash":"4180509522e17f8afdf5014bc51f5601952dfa403503e66f8f53e92b0c6ddd45","declaration":"export type PluginRuntime = PluginRuntime;","entrypoint":"discord","exportName":"PluginRuntime","importSpecifier":"openclaw/plugin-sdk/discord","kind":"type","recordType":"export"} {"closureHash":"6b9967e919c1cd420c8356b4f9d40dbc81932b4b15a733eecdcfb2ad84f8e5d8","declaration":"export type ResolvedDiscordAccount = ResolvedDiscordAccount;","entrypoint":"discord","exportName":"ResolvedDiscordAccount","importSpecifier":"openclaw/plugin-sdk/discord","kind":"type","recordType":"export"} {"closureHash":"6bed5ca896b7034117e4757e2c6b4111dd5293dbad9416e3828dd6afa4e858ce","declaration":"export type ThreadBindingRecord = ThreadBindingRecord;","entrypoint":"discord","exportName":"ThreadBindingRecord","importSpecifier":"openclaw/plugin-sdk/discord","kind":"type","recordType":"export"} @@ -2289,7 +2289,7 @@ {"closureHash":"a8874cc3b2cb522f6ca38ba5e353ec290aad0495c56087396ecaeb1cee9981b6","declaration":"export type NodeMatchCandidate = NodeMatchCandidate;","entrypoint":"gateway-runtime","exportName":"NodeMatchCandidate","importSpecifier":"openclaw/plugin-sdk/gateway-runtime","kind":"type","recordType":"export"} {"closureHash":"909e9434e956f1dfde4cc24f68879e7c3a1aac8aea4ff2d887b37824fdef1994","declaration":"export type NodeSession = NodeSession;","entrypoint":"gateway-runtime","exportName":"NodeSession","importSpecifier":"openclaw/plugin-sdk/gateway-runtime","kind":"type","recordType":"export"} {"closureHash":"6cfef265e2aebd53c1e7ce29cb204228ac175b07f9d2f22b861e98cac0eaee4a","declaration":"export type NormalizedPluginNodeCapabilityUrl = NormalizedPluginNodeCapabilityUrl;","entrypoint":"gateway-runtime","exportName":"NormalizedPluginNodeCapabilityUrl","importSpecifier":"openclaw/plugin-sdk/gateway-runtime","kind":"type","recordType":"export"} -{"closureHash":"1cc06f8fa52d38c184dbb28d20d99bf91d026de9dcf1d225ea060a12d6a3c817","declaration":"export class GatewayClient {\n #client: BaseGatewayClient;\n constructor(opts: GatewayClientOptions);\n start(): void;\n stop(): void;\n stopAndWait(opts?: {\n timeoutMs?: number;\n }): Promise;\n request>(method: string, params?: unknown, opts?: GatewayClientRequestOptions): Promise;\n getConnectionMetadata(): GatewayClientConnectionMetadata;\n updateNodeManifest(manifest: {\n caps: string[];\n commands: string[];\n }): void;\n}","entrypoint":"gateway-runtime","exportName":"GatewayClient","importSpecifier":"openclaw/plugin-sdk/gateway-runtime","kind":"class","recordType":"export"} +{"closureHash":"f7a314876945ed3fefd288085a0be9c6736acde250fc57651b87ac4488a57f08","declaration":"export class GatewayClient {\n #client: BaseGatewayClient;\n constructor(opts: GatewayClientOptions);\n start(): void;\n stop(): void;\n stopAndWait(opts?: {\n timeoutMs?: number;\n }): Promise;\n request>(method: string, params?: unknown, opts?: GatewayClientRequestOptions): Promise;\n getConnectionMetadata(): GatewayClientConnectionMetadata;\n updateNodeManifest(manifest: {\n caps: string[];\n commands: string[];\n }): void;\n}","entrypoint":"gateway-runtime","exportName":"GatewayClient","importSpecifier":"openclaw/plugin-sdk/gateway-runtime","kind":"class","recordType":"export"} {"category":null,"entrypoint":"group-access","importSpecifier":"openclaw/plugin-sdk/group-access","recordType":"module"} {"closureHash":"b6b34cd3844debbe7cda17ad7703e4fcffe4489789ae1763df8f8ab027150203","declaration":"export function evaluateGroupRouteAccessForPolicy(params: { groupPolicy: GroupPolicy; routeAllowlistConfigured: boolean; routeMatched: boolean; routeEnabled?: boolean; }): GroupRouteAccessDecision;","entrypoint":"group-access","exportName":"evaluateGroupRouteAccessForPolicy","importSpecifier":"openclaw/plugin-sdk/group-access","kind":"function","recordType":"export"} {"closureHash":"bf1a463ce98bf23d639d6e80112ffca836df6d63049bae6fef2a11a2ef1bb74c","declaration":"export function evaluateMatchedGroupAccessForPolicy(params: { groupPolicy: GroupPolicy; allowlistConfigured: boolean; allowlistMatched: boolean; requireMatchInput?: boolean; hasMatchInput?: boolean; }): MatchedGroupAccessDecision;","entrypoint":"group-access","exportName":"evaluateMatchedGroupAccessForPolicy","importSpecifier":"openclaw/plugin-sdk/group-access","kind":"function","recordType":"export"} @@ -2360,7 +2360,7 @@ {"closureHash":"98e0cc16352b7dfcceb4957f8cd2acfe3c2b3acb223c42a5cfb885e088fcb04c","declaration":"export function resolveInboundReplyDispatchCounts(result: ChannelTurnDispatchResultLike): Record;","entrypoint":"inbound-reply-dispatch","exportName":"resolveInboundReplyDispatchCounts","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch","kind":"function","recordType":"export"} {"closureHash":"4cd63266d379acc2cdf9aba02f50b8ceb55500cf820787c370fc98d173fb1950","declaration":"export function runChannelInboundEvent(params: RunChannelTurnParams): Promise>;\nexport function runChannelInboundEvent(params: ChannelInboundEventRunnerParams): Promise>;","entrypoint":"inbound-reply-dispatch","exportName":"runChannelInboundEvent","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch","kind":"function","recordType":"export"} {"closureHash":"2c68496a174872b31aaf1ac8fad2789deb79d490c5ffbcb157325f1c95d1b51f","declaration":"export function runPreparedInboundReply(params: PreparedChannelTurn): Promise>;","entrypoint":"inbound-reply-dispatch","exportName":"runPreparedInboundReply","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch","kind":"function","recordType":"export"} -{"closureHash":"49e98f675c3f385cf2acea1d1b7480f1a510449c1a04bf3d65475b9a7492d2dd","declaration":"export const deliverInboundReplyWithMessageSendContext: (params: DurableInboundReplyDeliveryParams) => Promise<{ status: \"not_applicable\"; reason: \"non_final\";} | { status: \"unsupported\"; reason: \"missing_channel\" | \"missing_target\" | \"missing_outbound_handler\" | \"capability_mismatch\"; capability?: DurableFinalDeliveryRequirement;} | { status: \"handled_visible\"; delivery: ChannelDeliveryResult;} | { status: \"handled_no_send\"; reason: \"no_visible_result\"; delivery: ChannelDeliveryResult;} | { status: \"failed\"; error: unknown; sentBeforeError?: true;}>;","entrypoint":"inbound-reply-dispatch","exportName":"deliverInboundReplyWithMessageSendContext","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch","kind":"const","recordType":"export"} +{"closureHash":"e00127c8d36bc7219cc2f31cc4ded3deb1ce54ca5287fe7f40611242433ab6bd","declaration":"export const deliverInboundReplyWithMessageSendContext: (params: DurableInboundReplyDeliveryParams) => Promise<{ status: \"not_applicable\"; reason: \"non_final\";} | { status: \"unsupported\"; reason: \"missing_channel\" | \"missing_target\" | \"missing_outbound_handler\" | \"capability_mismatch\"; capability?: DurableFinalDeliveryRequirement;} | { status: \"handled_visible\"; delivery: ChannelDeliveryResult;} | { status: \"handled_no_send\"; reason: \"no_visible_result\"; delivery: ChannelDeliveryResult;} | { status: \"failed\"; error: unknown; sentBeforeError?: true;}>;","entrypoint":"inbound-reply-dispatch","exportName":"deliverInboundReplyWithMessageSendContext","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch","kind":"const","recordType":"export"} {"closureHash":"ab30d0470348e3af7c18a273afc0d169d91f204fba557c75c4694e8a0ea6c936","declaration":"export type AssembledInboundReply = AssembledChannelTurn;","entrypoint":"inbound-reply-dispatch","exportName":"AssembledInboundReply","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch","kind":"type","recordType":"export"} {"closureHash":"3de0d6d1bb8f9a83bd1c6e57aa8618e3fea71054236cd530c555e348bd0b7702","declaration":"export type ChannelBotLoopProtectionFacts = ChannelBotLoopProtectionFacts;","entrypoint":"inbound-reply-dispatch","exportName":"ChannelBotLoopProtectionFacts","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch","kind":"type","recordType":"export"} {"closureHash":"f0da845bbdf49b30f6718a4b7ff012a0536deb98f65418583555dc90825797ca","declaration":"export type ChannelInboundDroppedHistoryOptions = ChannelTurnDroppedHistoryOptions;","entrypoint":"inbound-reply-dispatch","exportName":"ChannelInboundDroppedHistoryOptions","importSpecifier":"openclaw/plugin-sdk/inbound-reply-dispatch","kind":"type","recordType":"export"} @@ -3195,19 +3195,19 @@ {"closureHash":"3362ea0b650933db6db467ef8ae1c82e682e946761647c8dfae401eec1237bd6","declaration":"export function callMeetingBrowserProxyOnNode(params: { runtime: PluginRuntime; adapter: NodeAdapter; nodeId: string; } & MeetingBrowserRequestParams): Promise;","entrypoint":"meeting-runtime","exportName":"callMeetingBrowserProxyOnNode","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"c5fcd1d157edcb4773235adfaf8051e3ec7ca428cb7916b6d55f2511affab56c","declaration":"export function convertMeetingBridgeAudioForStt(audio: Buffer, audioFormat: MeetingRealtimeAudioFormat): Buffer;","entrypoint":"meeting-runtime","exportName":"convertMeetingBridgeAudioForStt","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"aa9063c27c5ff714d3653571c4a2a71e9d2eea5609b161f03e4738e15411358b","declaration":"export function convertMeetingTtsAudioForBridge(audio: Buffer, sampleRate: number, audioFormat: MeetingRealtimeAudioFormat, outputFormat?: string, platformName?: string): Buffer;","entrypoint":"meeting-runtime","exportName":"convertMeetingTtsAudioForBridge","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} -{"closureHash":"41a29ffc111d6672c56e82aa27884261d3d23aded7b02186e1acd9817f450fa1","declaration":"export function createLocalMeetingRealtimeAudioTransport(params: { inputCommand: string[]; outputCommand: string[]; bargeInInputCommand?: string[]; bargeInRmsThreshold: number; bargeInPeakThreshold: number; bargeInCooldownMs: number; logger: RuntimeLogger; logScope: string; audioFormat?: MeetingRealtimeAudioFormat; spawn?: MeetingRealtimeAudioSpawn; }): MeetingRealtimeAudioTransport;","entrypoint":"meeting-runtime","exportName":"createLocalMeetingRealtimeAudioTransport","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} +{"closureHash":"5148f6adceecb8eeda162561a1893bddb99bb9fff4bb65b480fa7aad4580d6e5","declaration":"export function createLocalMeetingRealtimeAudioTransport(params: { inputCommand: string[]; outputCommand: string[]; bargeInInputCommand?: string[]; bargeInRmsThreshold: number; bargeInPeakThreshold: number; bargeInCooldownMs: number; logger: RuntimeLogger; logScope: string; audioFormat?: MeetingRealtimeAudioFormat; spawn?: MeetingRealtimeAudioSpawn; }): MeetingRealtimeAudioTransport;","entrypoint":"meeting-runtime","exportName":"createLocalMeetingRealtimeAudioTransport","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"0f81bc377d2158b9c8adb6878e454f34c2e57d53bdef9aa34d567d279800c240","declaration":"export function createMeetingBrowserNodeCaller(params: { runtime: PluginRuntime; adapter: NodeAdapter; nodeId: string; }): MeetingBrowserRequestCaller;","entrypoint":"meeting-runtime","exportName":"createMeetingBrowserNodeCaller","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"f244ca8c47f556c9f388953cf9a7581c7d4d413af93e0dfa1462779e6a687a7c","declaration":"export function createMeetingBrowserNodeInvokePolicy(options: MeetingBrowserNodePolicyOptions): OpenClawPluginNodeInvokePolicy;","entrypoint":"meeting-runtime","exportName":"createMeetingBrowserNodeInvokePolicy","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"1aa4e3ff39a2cbe162421c62b1fa4a362973381d4d9ff15c671249b1ebd566bc","declaration":"export function createMeetingNodeHost(options: MeetingNodeHostOptions): { handleCommand(paramsJSON?: string | null): Promise; };","entrypoint":"meeting-runtime","exportName":"createMeetingNodeHost","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} -{"closureHash":"7bbd4d7df1e47521e1c2a00bb1f228a7aebc93ae0a9b5fa5ed66c9c24c8a4166","declaration":"export function createMeetingRealtimeEngineBindings(params: { platform: MeetingPlatformRuntimeMetadata; config: { realtime: { agentId?: string; toolPolicy: RealtimeVoiceAgentConsultToolPolicy; }; }; fullConfig: OpenClawConfig; runtime: PluginRuntime; logger: RuntimeLogger; }): { platform: MeetingRuntimePlatform; consultAgent: (consult: MeetingAgentConsultParams) => Promise<{ text: string; }>; tools: RealtimeVoiceTool[]; handleToolCall: (call: MeetingRealtimeToolCallParams) => Promise; };","entrypoint":"meeting-runtime","exportName":"createMeetingRealtimeEngineBindings","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} +{"closureHash":"6afe0a95e9ee7fb68b75e29cb4e8134b9e53adb2113356b950d3ce8b512da325","declaration":"export function createMeetingRealtimeEngineBindings(params: { platform: MeetingPlatformRuntimeMetadata; config: { realtime: { agentId?: string; toolPolicy: RealtimeVoiceAgentConsultToolPolicy; }; }; fullConfig: OpenClawConfig; runtime: PluginRuntime; logger: RuntimeLogger; }): { platform: MeetingRuntimePlatform; consultAgent: (consult: MeetingAgentConsultParams) => Promise<{ text: string; }>; tools: RealtimeVoiceTool[]; handleToolCall: (call: MeetingRealtimeToolCallParams) => Promise; };","entrypoint":"meeting-runtime","exportName":"createMeetingRealtimeEngineBindings","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"9124db05923060c467edb149bd8abdc8be1d00d00f867cf84600972923a58489","declaration":"export function createMeetingSession(params: { platform: MeetingPlatformRuntimeMetadata; config: { realtime: { provider?: string; voiceProvider?: string; transcriptionProvider?: string; model?: string; toolPolicy: TToolPolicy; }; }; resolved: MeetingResolvedJoin; createdAt: string; }): MeetingSessionRecord;","entrypoint":"meeting-runtime","exportName":"createMeetingSession","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"fb276e07e395ab19177b3d7967fd0a8fcda79130883f15092a4a306b826481b9","declaration":"export function createMeetingSetupStatus(checks: MeetingSetupCheck[]): MeetingSetupStatus;","entrypoint":"meeting-runtime","exportName":"createMeetingSetupStatus","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"0457ac9dc4b0b2d11cdbe2070d75ae2e5c2fe5cee69017e92f849849cd06b0f3","declaration":"export function createMeetingVoiceCallGateway(params: { config: MeetingVoiceCallConfig; runtime: PluginRuntime; surface: MeetingVoiceCallSurface; connectClient: (params: { config: MeetingVoiceCallConfig; surface: MeetingVoiceCallSurface; }) => Promise; }): MeetingVoiceCallGateway;","entrypoint":"meeting-runtime","exportName":"createMeetingVoiceCallGateway","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} -{"closureHash":"153b475879020471ce39741d83aacf36f31aed63a58897eae9f8ae902a67814c","declaration":"export function createNodeMeetingRealtimeAudioTransport(params: { runtime: PluginRuntime; nodeId: string; bridgeId: string; logger: RuntimeLogger; commandName: string; logScope: string; logPrefix: string; audioFormat?: MeetingRealtimeAudioFormat; }): MeetingRealtimeAudioTransport;","entrypoint":"meeting-runtime","exportName":"createNodeMeetingRealtimeAudioTransport","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} +{"closureHash":"ae27b78b0f45a723baac51795d87d5a2004b92e6f37f8ef3989feb2d8dc10834","declaration":"export function createNodeMeetingRealtimeAudioTransport(params: { runtime: PluginRuntime; nodeId: string; bridgeId: string; logger: RuntimeLogger; commandName: string; logScope: string; logPrefix: string; audioFormat?: MeetingRealtimeAudioFormat; }): MeetingRealtimeAudioTransport;","entrypoint":"meeting-runtime","exportName":"createNodeMeetingRealtimeAudioTransport","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"c0987b15a391a92d53d3e4e12b61eb122f9547fcf4f45478cdd3c5a8973a3e52","declaration":"export function endMeetingVoiceCallGatewayCall(params: { gateway: MeetingVoiceCallGateway; callId: string; }): Promise;","entrypoint":"meeting-runtime","exportName":"endMeetingVoiceCallGatewayCall","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"b3ebc10fe7ab372e1317eaa8f78621cf277bba1e33fda57acf2254f6759d1e9d","declaration":"export function getMeetingVoiceCallGatewayCall(params: { gateway: MeetingVoiceCallGateway; callId: string; }): Promise;","entrypoint":"meeting-runtime","exportName":"getMeetingVoiceCallGatewayCall","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"aac088df8e80d9ce89c45feccdb04ace3ae4417b3fcdd0f058410ddf25c96b46","declaration":"export function isMeetingVoiceCallMissingError(error: unknown): boolean;","entrypoint":"meeting-runtime","exportName":"isMeetingVoiceCallMissingError","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} -{"closureHash":"2ddcdaee317e00718d3061c832e4fb4a6d1a43b43e926c6136ecd553d2989167","declaration":"export function joinMeetingViaVoiceCallGateway(params: { config: MeetingVoiceCallConfig; gateway: MeetingVoiceCallGateway; surface: MeetingVoiceCallSurface; dialInNumber: string; dtmfSequence?: string; logger?: RuntimeLogger; message?: string; requesterSessionKey?: string; agentId?: string; sessionKey?: string; }): Promise;","entrypoint":"meeting-runtime","exportName":"joinMeetingViaVoiceCallGateway","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} +{"closureHash":"c011a22f03d8781e96a3c286096008e5192216196e5d6fd1121063a1958f009d","declaration":"export function joinMeetingViaVoiceCallGateway(params: { config: MeetingVoiceCallConfig; gateway: MeetingVoiceCallGateway; surface: MeetingVoiceCallSurface; dialInNumber: string; dtmfSequence?: string; logger?: RuntimeLogger; message?: string; requesterSessionKey?: string; agentId?: string; sessionKey?: string; }): Promise;","entrypoint":"meeting-runtime","exportName":"joinMeetingViaVoiceCallGateway","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"bac9621cd025754fbcd4c21409b06eb0503405db81f35cac993aaf0b881ab3e1","declaration":"export function leaveMeetingWithBrowser(params: { adapter: BrowserAdapter; callBrowser: MeetingBrowserRequestCaller; launch: boolean; meetingSessionId?: string; meetingUrl: string; tab: MeetingBrowserTab; timeoutMs: number; }): Promise<{ left: boolean; note: string; }>;","entrypoint":"meeting-runtime","exportName":"leaveMeetingWithBrowser","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"c7b1aeac134b2070d053adf33026d876998b852ccd65cd2c9667119b1a1abee2","declaration":"export function openMeetingWithBrowser, Mode extends string, Health extends MeetingBrowserHealth & { browserTitle?: string; browserUrl?: string; notes?: string[]; }, Transcript extends MeetingTranscriptSnapshot>(params: { adapter: BrowserAdapter; callBrowser: MeetingBrowserRequestCaller; config: MeetingBrowserControllerConfig; session: Session; }): Promise<{ launched: boolean; browser?: Health; tab?: MeetingBrowserTab; }>;","entrypoint":"meeting-runtime","exportName":"openMeetingWithBrowser","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"34858979bd248799c2215d9c7bdd5d4a6b27660c9173bfca2e50dd4373ac27db","declaration":"export function readMeetingBrowserTab(result: unknown): MeetingBrowserCandidateTab | undefined;","entrypoint":"meeting-runtime","exportName":"readMeetingBrowserTab","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} @@ -3218,8 +3218,8 @@ {"closureHash":"a1c367ec2dd927543d490a1393128d475e10c7bd131ffd789c80d88a2d2d0cb2","declaration":"export function resolveMeetingBrowserNodeInfo(params: { runtime: PluginRuntime; adapter: NodeAdapter; requestedNode?: string; }): Promise;","entrypoint":"meeting-runtime","exportName":"resolveMeetingBrowserNodeInfo","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"f21196c9ea8ab90248ffdcdef3e942f1247ee2b9064ddb9757fffe352362c779","declaration":"export function resolveMeetingRealtimeAudioFormat(audioFormat: MeetingRealtimeAudioFormat): RealtimeVoiceAudioFormat;","entrypoint":"meeting-runtime","exportName":"resolveMeetingRealtimeAudioFormat","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"521dc5c262524a709cdd27cdc88e95004073da8223537bcbc73d5d423172dc2b","declaration":"export function speakMeetingViaVoiceCallGateway(params: { gateway: MeetingVoiceCallGateway; callId: string; message: string; }): Promise;","entrypoint":"meeting-runtime","exportName":"speakMeetingViaVoiceCallGateway","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} -{"closureHash":"d30a62bb6292da6e0da6cbd96032084122428ec76c16fb0b711813a1cd7d386e","declaration":"export function startMeetingAgentRealtimeEngine(params: { config: MeetingRealtimeEngineConfig; fullConfig: OpenClawConfig; runtime: PluginRuntime; platform: MeetingRuntimePlatform; meetingSessionId: string; requesterSessionKey?: string; logPrefix?: \"node\"; transport: MeetingRealtimeAudioTransport; logger: RuntimeLogger; providers?: RealtimeTranscriptionProviderPlugin[]; consultAgent: (params: MeetingAgentConsultParams) => Promise<{ text: string; }>; }): Promise;","entrypoint":"meeting-runtime","exportName":"startMeetingAgentRealtimeEngine","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} -{"closureHash":"0e6b815a67f214774c65fff9589717c3f0a3e6327a1c0ecce7c405a9bb627b64","declaration":"export function startMeetingRealtimeEngine(params: { config: MeetingRealtimeEngineConfig; fullConfig: OpenClawConfig; runtime: PluginRuntime; platform: MeetingRuntimePlatform; meetingSessionId: string; requesterSessionKey?: string; logPrefix?: \"node\"; talkSessionId?: string; talkContext?: { nodeId: string; bridgeId: string; }; transport: MeetingRealtimeAudioTransport; logger: RuntimeLogger; providers?: RealtimeVoiceProviderPlugin[]; consultAgent: (params: MeetingAgentConsultParams) => Promise<{ text: string; }>; tools: RealtimeVoiceTool[]; handleToolCall: (params: MeetingRealtimeToolCallParams) => Promise; }): Promise;","entrypoint":"meeting-runtime","exportName":"startMeetingRealtimeEngine","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} +{"closureHash":"a2e5889b88cbcb5a063a81b123facc75b932fe1d38b62042d21a45b354b0a48a","declaration":"export function startMeetingAgentRealtimeEngine(params: { config: MeetingRealtimeEngineConfig; fullConfig: OpenClawConfig; runtime: PluginRuntime; platform: MeetingRuntimePlatform; meetingSessionId: string; requesterSessionKey?: string; logPrefix?: \"node\"; transport: MeetingRealtimeAudioTransport; logger: RuntimeLogger; providers?: RealtimeTranscriptionProviderPlugin[]; consultAgent: (params: MeetingAgentConsultParams) => Promise<{ text: string; }>; }): Promise;","entrypoint":"meeting-runtime","exportName":"startMeetingAgentRealtimeEngine","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} +{"closureHash":"bba5f9b7093ef662ec7a464280c5fa3468847b6eb39b43cdb95011e7dadef2cc","declaration":"export function startMeetingRealtimeEngine(params: { config: MeetingRealtimeEngineConfig; fullConfig: OpenClawConfig; runtime: PluginRuntime; platform: MeetingRuntimePlatform; meetingSessionId: string; requesterSessionKey?: string; logPrefix?: \"node\"; talkSessionId?: string; talkContext?: { nodeId: string; bridgeId: string; }; transport: MeetingRealtimeAudioTransport; logger: RuntimeLogger; providers?: RealtimeVoiceProviderPlugin[]; consultAgent: (params: MeetingAgentConsultParams) => Promise<{ text: string; }>; tools: RealtimeVoiceTool[]; handleToolCall: (params: MeetingRealtimeToolCallParams) => Promise; }): Promise;","entrypoint":"meeting-runtime","exportName":"startMeetingRealtimeEngine","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"function","recordType":"export"} {"closureHash":"663b9c77231b21af697efd5042e22d1c33e9ee972adeb312ad86d81a2b2e36f7","declaration":"export type MeetingAgentConsultParams = MeetingAgentConsultParams;","entrypoint":"meeting-runtime","exportName":"MeetingAgentConsultParams","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} {"closureHash":"adf0745c611bf737fc28978d766fc2a25b7104e71b6b2fed4e9b95e519c7b115","declaration":"export type MeetingBrowserCandidateTab = MeetingBrowserCandidateTab;","entrypoint":"meeting-runtime","exportName":"MeetingBrowserCandidateTab","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} {"closureHash":"59b1526172b7491b85f14f13c1ee26d8d6726f33093770748580c767b1689a17","declaration":"export type MeetingBrowserControllerConfig = MeetingBrowserControllerConfig;","entrypoint":"meeting-runtime","exportName":"MeetingBrowserControllerConfig","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} @@ -3251,7 +3251,7 @@ {"closureHash":"8244b408803f57cf2004157e96f2ce11bb036cdf864123b15ba2fac7f03b5d43","declaration":"export type MeetingSessionRuntimeHandles = MeetingSessionRuntimeHandles;","entrypoint":"meeting-runtime","exportName":"MeetingSessionRuntimeHandles","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} {"closureHash":"aa8054fc2c442891197e89bdad61e79999ea15d813c4b127fb6150607b9e67e7","declaration":"export type MeetingSessionRuntimeJoinContext, TTransport extends string, TMode extends string, THealth extends MeetingBrowserHealth, TTab extends MeetingBrowserTab> = MeetingSessionRuntimeJoinContext;","entrypoint":"meeting-runtime","exportName":"MeetingSessionRuntimeJoinContext","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} {"closureHash":"325241724edd38da439c5df03d225e2a7cb8e56d817e98ed7c37850656d3744b","declaration":"export type MeetingSessionRuntimeMessages = MeetingSessionRuntimeMessages;","entrypoint":"meeting-runtime","exportName":"MeetingSessionRuntimeMessages","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} -{"closureHash":"9140b7d74ad1d6db84343684461ee4328a4f977b1e071843a5fcc6fe4a0593c7","declaration":"export type MeetingSessionRuntimeOptions, TRequest, TTransport extends string, TMode extends string, THealth extends MeetingBrowserHealth, TTab extends MeetingBrowserTab, TManualReason extends string, TSpeechBlockedReason extends string> = MeetingSessionRuntimeOptions;","entrypoint":"meeting-runtime","exportName":"MeetingSessionRuntimeOptions","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} +{"closureHash":"6e628916c0430337ee7725fc55520a2d35d02a50bd50a848a35d7ff50bfb98ff","declaration":"export type MeetingSessionRuntimeOptions, TRequest, TTransport extends string, TMode extends string, THealth extends MeetingBrowserHealth, TTab extends MeetingBrowserTab, TManualReason extends string, TSpeechBlockedReason extends string> = MeetingSessionRuntimeOptions;","entrypoint":"meeting-runtime","exportName":"MeetingSessionRuntimeOptions","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} {"closureHash":"d3c730cba02d30512c4cdae6e2367c662982e2c25671833ecd8701283924c642","declaration":"export type MeetingSessionState = MeetingSessionState;","entrypoint":"meeting-runtime","exportName":"MeetingSessionState","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} {"closureHash":"2496931157bf64fdb16013172ab9c023e60c5f85b98f46a3ee9532ce58d636d6","declaration":"export type MeetingSetupCheck = MeetingSetupCheck;","entrypoint":"meeting-runtime","exportName":"MeetingSetupCheck","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} {"closureHash":"f0aed6f7955a43fc387d8bbc072cb6276b0f2d18e7f829441a2ad45974bb3831","declaration":"export type MeetingSetupStatus = MeetingSetupStatus;","entrypoint":"meeting-runtime","exportName":"MeetingSetupStatus","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} @@ -3265,9 +3265,9 @@ {"closureHash":"20c70a94483aeddda4d1500cfcc5e71fab844b474ba4061c55d6c8693d40821b","declaration":"export type MeetingVoiceCallJoinResult = MeetingVoiceCallJoinResult;","entrypoint":"meeting-runtime","exportName":"MeetingVoiceCallJoinResult","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} {"closureHash":"4fb2c5928aa6f809dd4c604d71c5c99cdfe3e98b6e9c1662601c9b4ad9738162","declaration":"export type MeetingVoiceCallStatusResult = MeetingVoiceCallStatusResult;","entrypoint":"meeting-runtime","exportName":"MeetingVoiceCallStatusResult","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} {"closureHash":"5ef812397b6409a4ca228943dd1080661365959477b42e705a9aaa219757d14f","declaration":"export type MeetingVoiceCallSurface = MeetingVoiceCallSurface;","entrypoint":"meeting-runtime","exportName":"MeetingVoiceCallSurface","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"type","recordType":"export"} -{"closureHash":"0f7612ffa6432932130e8e6f83d267c98466eb46a91d43fc8c037c2303e63e05","declaration":"export interface MeetingPlatformAdapter extends MeetingPlatformAdapterContract {\n}","entrypoint":"meeting-runtime","exportName":"MeetingPlatformAdapter","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"interface","recordType":"export"} +{"closureHash":"4dafd9024220722e822bf4e24b4311e0dfb0a8585a813256e51188b5328b40f3","declaration":"export interface MeetingPlatformAdapter extends MeetingPlatformAdapterContract {\n}","entrypoint":"meeting-runtime","exportName":"MeetingPlatformAdapter","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"interface","recordType":"export"} {"closureHash":"79884bd1df1ea0d07d5ceb4c62d93c1ee744b75d31c94c988845869c0f3cbd2d","declaration":"export interface MeetingRealtimeAudioTransport {\n onFatal(handler: () => void): void;\n startInput(onAudio: (audio: Buffer) => void): void;\n beginOutput?(): void;\n stop(): Promise;\n writeOutput(audio: Buffer): Promise;\n clearOutput(): Promise;\n dispose(): Promise;\n getHealth?(): MeetingRealtimeAudioTransportHealth;\n startBargeInMonitor?(onBargeIn: (audio: Buffer) => boolean): void;\n}","entrypoint":"meeting-runtime","exportName":"MeetingRealtimeAudioTransport","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"interface","recordType":"export"} -{"closureHash":"9e91eff317b13d93107e856c077bd51db614f2aa65d3c7d4c60e86ae09f489b6","declaration":"export class MeetingSessionRuntime, TRequest, TTransport extends string, TMode extends string, THealth extends MeetingBrowserHealth, TTab extends MeetingBrowserTab, TManualReason extends string, TSpeechBlockedReason extends string> {\n readonly #sessions: Map;\n readonly #sessionLeaves: Map>>;\n readonly #sessionCleanup: MeetingSessionCleanupTracker;\n readonly #meetingLock: MeetingSessionJoinLock;\n readonly #sessionStops: Map Promise>;\n readonly #sessionSpeakers: Map void>;\n readonly #sessionHealth: Map Partial>;\n readonly #durableTranscripts: MeetingSessionDurableTranscripts;\n readonly #transcriptStore: MeetingSessionTranscriptStore;\n constructor(private readonly options: MeetingSessionRuntimeOptions);\n list(): TSession[];\n getSession(sessionId: string): TSession | undefined;\n async status(sessionId?: string): Promise<{\n found: boolean;\n session?: TSession;\n sessions?: TSession[];\n }>;\n async transcript(sessionId: string, options: {\n sinceIndex?: number;\n }): Promise<{ found: boolean; sessionId?: string; startIndex?: number; nextIndex?: number; droppedLines?: number; evicted?: boolean; lines?: MeetingTranscriptLine[]; }>;\n async startTranscriptSource(request: TranscriptStartRequest): Promise;\n async stopTranscriptSource(request: TranscriptStopRequest): Promise;\n isReusableSession(session: TSession, resolved: MeetingResolvedJoin): boolean;\n async join(request: TRequest): Promise<{\n session: TSession;\n spoken?: boolean;\n }>;\n async leave(sessionId: string, options?: {\n keepBrowserTab?: boolean;\n }): Promise>;\n async speak(sessionId: string, instructions?: string): Promise<{\n found: boolean;\n spoken: boolean;\n session?: TSession;\n }>;\n async speakWhenReady(session: TSession, instructions: string): Promise;\n hasHealthHandle(sessionId: string): boolean;\n refreshHealth(sessionId?: string): void;\n async refreshBrowserHealth(session: TSession, options: {\n force?: boolean;\n readOnly?: boolean;\n }): Promise;\n async refreshCaptionHealth(session: TSession): Promise;\n refreshSpeechReadiness(session: TSession): {\n ready: boolean;\n reason?: TSpeechBlockedReason;\n message?: string;\n };\n markSessionEnded(session: TSession, reason: string): void;\n async #joinUnlocked(request: TRequest, resolved: MeetingResolvedJoin): Promise<{\n session: TSession;\n spoken?: boolean;\n }>;\n async #leaveUnlocked(sessionId: string, options?: {\n keepBrowserTab?: boolean;\n }): Promise>;\n async #leaveSession(session: TSession, options?: {\n keepBrowserTab?: boolean;\n }): Promise>;\n #meetingKey(transport: TTransport, url: string): string;\n #inheritBrowserTabOwnership(params: {\n session: TSession;\n transport: TTransport;\n nodeId?: string;\n meetingUrl: string;\n tab?: TTab;\n }): TTab | undefined;\n async #settleRetainedBrowserTabs(retained: Array<{\n session: TSession;\n tab: TTab;\n }>, adopted?: {\n transport: TTransport;\n nodeId?: string;\n tab: TTab;\n }): Promise;\n async #rollbackFailedJoinSession(session: TSession): Promise;\n async #settleRetainedBrowserTabsAfterFailure(retained: Array<{\n session: TSession;\n tab: TTab;\n }>): Promise;\n #attachRuntimeHandles(session: TSession, handles: MeetingSessionRuntimeHandles): void;\n #dropRuntimeHandles(sessionId: string): void;\n #isManagedBrowserSession(session: TSession): boolean;\n #evaluateSpeechReadiness(session: TSession): {\n ready: boolean;\n reason?: TSpeechBlockedReason;\n message?: string;\n };\n #noteSession(session: TSession, note: string): void;\n}","entrypoint":"meeting-runtime","exportName":"MeetingSessionRuntime","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"class","recordType":"export"} +{"closureHash":"2e981750bb930e7a5afb64829def74599e4ad186e75be64c7b8418bb4cf868d9","declaration":"export class MeetingSessionRuntime, TRequest, TTransport extends string, TMode extends string, THealth extends MeetingBrowserHealth, TTab extends MeetingBrowserTab, TManualReason extends string, TSpeechBlockedReason extends string> {\n readonly #sessions: Map;\n readonly #sessionLeaves: Map>>;\n readonly #sessionCleanup: MeetingSessionCleanupTracker;\n readonly #meetingLock: MeetingSessionJoinLock;\n readonly #sessionStops: Map Promise>;\n readonly #sessionSpeakers: Map void>;\n readonly #sessionHealth: Map Partial>;\n readonly #durableTranscripts: MeetingSessionDurableTranscripts;\n readonly #transcriptStore: MeetingSessionTranscriptStore;\n constructor(private readonly options: MeetingSessionRuntimeOptions);\n list(): TSession[];\n getSession(sessionId: string): TSession | undefined;\n async status(sessionId?: string): Promise<{\n found: boolean;\n session?: TSession;\n sessions?: TSession[];\n }>;\n async transcript(sessionId: string, options: {\n sinceIndex?: number;\n }): Promise<{ found: boolean; sessionId?: string; startIndex?: number; nextIndex?: number; droppedLines?: number; evicted?: boolean; lines?: MeetingTranscriptLine[]; }>;\n async startTranscriptSource(request: TranscriptStartRequest): Promise;\n async stopTranscriptSource(request: TranscriptStopRequest): Promise;\n isReusableSession(session: TSession, resolved: MeetingResolvedJoin): boolean;\n async join(request: TRequest): Promise<{\n session: TSession;\n spoken?: boolean;\n }>;\n async leave(sessionId: string, options?: {\n keepBrowserTab?: boolean;\n }): Promise>;\n async speak(sessionId: string, instructions?: string): Promise<{\n found: boolean;\n spoken: boolean;\n session?: TSession;\n }>;\n async speakWhenReady(session: TSession, instructions: string): Promise;\n hasHealthHandle(sessionId: string): boolean;\n refreshHealth(sessionId?: string): void;\n async refreshBrowserHealth(session: TSession, options: {\n force?: boolean;\n readOnly?: boolean;\n }): Promise;\n async refreshCaptionHealth(session: TSession): Promise;\n refreshSpeechReadiness(session: TSession): {\n ready: boolean;\n reason?: TSpeechBlockedReason;\n message?: string;\n };\n markSessionEnded(session: TSession, reason: string): void;\n async #joinUnlocked(request: TRequest, resolved: MeetingResolvedJoin): Promise<{\n session: TSession;\n spoken?: boolean;\n }>;\n async #leaveUnlocked(sessionId: string, options?: {\n keepBrowserTab?: boolean;\n }): Promise>;\n async #leaveSession(session: TSession, options?: {\n keepBrowserTab?: boolean;\n }): Promise>;\n #meetingKey(transport: TTransport, url: string): string;\n #inheritBrowserTabOwnership(params: {\n session: TSession;\n transport: TTransport;\n nodeId?: string;\n meetingUrl: string;\n tab?: TTab;\n }): TTab | undefined;\n async #settleRetainedBrowserTabs(retained: Array<{\n session: TSession;\n tab: TTab;\n }>, adopted?: {\n transport: TTransport;\n nodeId?: string;\n tab: TTab;\n }): Promise;\n async #rollbackFailedJoinSession(session: TSession): Promise;\n async #settleRetainedBrowserTabsAfterFailure(retained: Array<{\n session: TSession;\n tab: TTab;\n }>): Promise;\n #attachRuntimeHandles(session: TSession, handles: MeetingSessionRuntimeHandles): void;\n #dropRuntimeHandles(sessionId: string): void;\n #isManagedBrowserSession(session: TSession): boolean;\n #evaluateSpeechReadiness(session: TSession): {\n ready: boolean;\n reason?: TSpeechBlockedReason;\n message?: string;\n };\n #noteSession(session: TSession, note: string): void;\n}","entrypoint":"meeting-runtime","exportName":"MeetingSessionRuntime","importSpecifier":"openclaw/plugin-sdk/meeting-runtime","kind":"class","recordType":"export"} {"category":null,"entrypoint":"memory-core-host-engine-foundation","importSpecifier":"openclaw/plugin-sdk/memory-core-host-engine-foundation","recordType":"module"} {"closureHash":"e2df8d235401aebf58eb71652fbeaedb42ca00aaa5042a76909ff2c48ba6fd54","declaration":"export function createSubsystemLogger(subsystem: string): SubsystemLogger;","entrypoint":"memory-core-host-engine-foundation","exportName":"createSubsystemLogger","importSpecifier":"openclaw/plugin-sdk/memory-core-host-engine-foundation","kind":"function","recordType":"export"} {"closureHash":null,"declaration":"export function isPathInside(root: string, target: string): boolean;","entrypoint":"memory-core-host-engine-foundation","exportName":"isPathInside","importSpecifier":"openclaw/plugin-sdk/memory-core-host-engine-foundation","kind":"function","recordType":"export"} @@ -3403,9 +3403,9 @@ {"category":"core","entrypoint":"plugin-entry","importSpecifier":"openclaw/plugin-sdk/plugin-entry","recordType":"module"} {"closureHash":"3d27406644141af2733cfca6e8c1240fc62327910109f585ce9073cafd7fd89c","declaration":"export function buildJsonPluginConfigSchema(schema: JsonSchemaObject, options?: BuildJsonPluginConfigSchemaOptions): OpenClawPluginConfigSchema;","entrypoint":"plugin-entry","exportName":"buildJsonPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"function","recordType":"export"} {"closureHash":"aff66c792d532642afb9c6e5a03c8d220f219250c3a8b49733f32058f22f98ca","declaration":"export function buildPluginConfigSchema(schema: ZodTypeAny, options?: BuildPluginConfigSchemaOptions): OpenClawPluginConfigSchema;","entrypoint":"plugin-entry","exportName":"buildPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"function","recordType":"export"} -{"closureHash":"ea866f93256b2e5f8da5bad7c96511f00f9b42fa85f6689cc70ab26bde2dd0cb","declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, reload, nodeHostCommands, securityAuditCollectors, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"plugin-entry","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"function","recordType":"export"} +{"closureHash":"7aeb838b8cca4b269332d981138c48fac04f71db26beb4552131a8981bded02b","declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, reload, nodeHostCommands, securityAuditCollectors, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"plugin-entry","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"function","recordType":"export"} {"closureHash":"0ef1dfa3b5deeb456480e292c0e835c3cd6d13e7bb7059fc735546507da51594","declaration":"export function emptyPluginConfigSchema(): OpenClawPluginConfigSchema;","entrypoint":"plugin-entry","exportName":"emptyPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"function","recordType":"export"} -{"closureHash":"beb6715ad81487caa2cc2ed8444d6ac2dcf83c9e71fc42d5db9214f2eb12b946","declaration":"export type AgentHarness = AgentHarness;","entrypoint":"plugin-entry","exportName":"AgentHarness","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} +{"closureHash":"ba0ca4adfbc76bcafbbe9487387636983a2c78cf10d056c44c6a9fd9d5191914","declaration":"export type AgentHarness = AgentHarness;","entrypoint":"plugin-entry","exportName":"AgentHarness","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} {"closureHash":"d2ff4de9ee3fd111c2cc64a3ee7e557d9f161af1b6b437ee7886bcb16e426317","declaration":"export type AgentPromptGuidance = AgentPromptGuidance;","entrypoint":"plugin-entry","exportName":"AgentPromptGuidance","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} {"closureHash":"2841127a90e04467adfea8126cbf03655f44ddb089ad9e0d48833c2beea39cbd","declaration":"export type AgentPromptGuidanceEntry = AgentPromptGuidanceEntry;","entrypoint":"plugin-entry","exportName":"AgentPromptGuidanceEntry","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} {"closureHash":"aa0412acefe62caff12e5271ead0545504874e824380dc21cfc0566388a05f33","declaration":"export type AgentPromptSurfaceKind = \"openclaw_main\" | \"pi_main\" | \"codex_app_server\" | \"cli_backend\" | \"acp_backend\" | \"subagent\";","entrypoint":"plugin-entry","exportName":"AgentPromptSurfaceKind","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} @@ -3421,10 +3421,10 @@ {"closureHash":"8054ffbe71b91afced900ef8a06f5e368ac10c4f99ffc1c6f538a802856f61c6","declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"plugin-entry","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} {"closureHash":"2f6e368fff73e942044cdf95c7c751b46c741e22ac0d2d4d97f45a3158f758ae","declaration":"export type OpenClawGatewayDiscoveryAdvertiseContext = OpenClawGatewayDiscoveryAdvertiseContext;","entrypoint":"plugin-entry","exportName":"OpenClawGatewayDiscoveryAdvertiseContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} {"closureHash":"b198058e0065e5c60b729a5bdf568f7e2436a006d8c4a7c63de764c7283f96ba","declaration":"export type OpenClawGatewayDiscoveryService = OpenClawGatewayDiscoveryService;","entrypoint":"plugin-entry","exportName":"OpenClawGatewayDiscoveryService","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} -{"closureHash":"ff621688b7b58ebfa7157b153d83921a47158e43ac312ff61df85d437216ae10","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"plugin-entry","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} +{"closureHash":"b376fa0f3d5efb8c41fccbd2265e044a7ffed909a9d27d6e4dd340fbe2dc11f4","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"plugin-entry","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} {"closureHash":"c968e96cdb5c97e92d26b7b15fb5255531b45a12e7c5c0a2e7e456d31cc6e358","declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} {"closureHash":"365beb5cee974639c2f35fc7a39088721ca8e9c618442830298b98033e9c768c","declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"plugin-entry","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} -{"closureHash":"05cefddc1e544e0efd37301f07a5adae39f6cc580239b314c1959b8887a23ce9","declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} +{"closureHash":"6f784a7e4e308207c5cb6c31f8cef940c2d37117f1e0517151275790e3183a0a","declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} {"closureHash":"a7ed83f0924f818b23cecb5a1f3591ff28b9499ccac9667a2b8505c2bc608ea9","declaration":"export type OpenClawPluginGatewayEventScope = OpenClawPluginGatewayEventScope;","entrypoint":"plugin-entry","exportName":"OpenClawPluginGatewayEventScope","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} {"closureHash":"f81f224d9b837e6353b7a546c5a76b124bcf568fd1f1fd0c6881fda30c53120a","declaration":"export type OpenClawPluginGatewayEvents = OpenClawPluginGatewayEvents;","entrypoint":"plugin-entry","exportName":"OpenClawPluginGatewayEvents","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} {"closureHash":"7470b211ac4d3449cd0fc353ee2eb48e9fbc2dbc9259d21c680d0203577aa94f","declaration":"export type OpenClawPluginHttpRouteHandler = OpenClawPluginHttpRouteHandler;","entrypoint":"plugin-entry","exportName":"OpenClawPluginHttpRouteHandler","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export"} @@ -3574,14 +3574,14 @@ {"closureHash":"ad45bb1eb13e99e3217eb7e7e2d29fa456bc1930059dd5e94d7500e05e54c6bc","declaration":"export function executePluginCommand(params: { command: RegisteredPluginCommand; args?: string; senderId?: string; channel: string; channelId?: PluginCommandContext[\"channelId\"]; isAuthorizedSender: boolean; senderIsOwner?: boolean; gatewayClientScopes?: PluginCommandContext[\"gatewayClientScopes\"]; agentId?: string; sessionKey?: PluginCommandContext[\"sessionKey\"]; sessionId?: PluginCommandContext[\"sessionId\"]; sessionTarget?: PluginCommandContext[\"sessionTarget\"]; sessionFile?: PluginCommandContext[\"sessionFile\"]; authProfileId?: string; commandBody: string; config: OpenClawConfig; from?: PluginCommandContext[\"from\"]; to?: PluginCommandContext[\"to\"]; originatingTo?: string; accountId?: PluginCommandContext[\"accountId\"]; messageThreadId?: PluginCommandContext[\"messageThreadId\"]; threadParentId?: PluginCommandContext[\"threadParentId\"]; diagnosticsSessions?: PluginCommandContext[\"diagnosticsSessions\"]; diagnosticsUploadApproved?: PluginCommandContext[\"diagnosticsUploadApproved\"]; diagnosticsPreviewOnly?: PluginCommandContext[\"diagnosticsPreviewOnly\"]; diagnosticsPrivateRouted?: PluginCommandContext[\"diagnosticsPrivateRouted\"]; }): Promise;","entrypoint":"plugin-runtime","exportName":"executePluginCommand","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"function","recordType":"export"} {"closureHash":"7e75fc04d621dc8d12f459d5b5b0c039a39b77c30c2b51af481e123c352f57b8","declaration":"export function getGlobalHookRunner(): HookRunner | null;","entrypoint":"plugin-runtime","exportName":"getGlobalHookRunner","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"function","recordType":"export"} {"closureHash":"db6583607237e4081814e2c5d4f8fb6f216ad3bbfe623bf16fc1a9c09c15f64b","declaration":"export function getPluginCommandSpecs(provider?: string, options?: PluginCommandSpecOptions): Array<{ name: string; description: string; descriptionLocalizations?: Record; acceptsArgs: boolean; }>;","entrypoint":"plugin-runtime","exportName":"getPluginCommandSpecs","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"function","recordType":"export"} -{"closureHash":"dbfeaea0c67177141cc6e662d6df7d97cac656a9079377c514bffb89073f3c8e","declaration":"export function getPluginRuntimeGatewayRequestScope(): PluginRuntimeGatewayRequestScope | undefined;","entrypoint":"plugin-runtime","exportName":"getPluginRuntimeGatewayRequestScope","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"function","recordType":"export"} +{"closureHash":"c94431d2a354b35f8b50107e2be87305e6e9ca4a0611245e6e2f96098e9c746e","declaration":"export function getPluginRuntimeGatewayRequestScope(): PluginRuntimeGatewayRequestScope | undefined;","entrypoint":"plugin-runtime","exportName":"getPluginRuntimeGatewayRequestScope","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"function","recordType":"export"} {"closureHash":"cfe277ce6de0c6ed50f1f54b66c889f6c95e91b058e8bcdc27aa37317a93a447","declaration":"export function listRegisteredPluginAgentPromptGuidance(params?: { surface?: AgentPromptSurfaceKind; includeLegacyGlobalGuidance?: boolean; }): string[];","entrypoint":"plugin-runtime","exportName":"listRegisteredPluginAgentPromptGuidance","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"function","recordType":"export"} {"closureHash":"693462c2a5c07646277169ede37b71ed12dc8d56c648b7fed73712900be34dcf","declaration":"export function matchPluginCommand(commandBody: string, options?: { channel?: string; }): { command: RegisteredPluginCommand; args?: string; } | null;","entrypoint":"plugin-runtime","exportName":"matchPluginCommand","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"function","recordType":"export"} {"closureHash":"8d575a525d165780642e70ff1385311c3422429c2e26c479a85baae491d62efe","declaration":"export function registerPluginCommand(pluginId: string, command: OpenClawPluginCommandDefinition, opts?: { pluginName?: string; pluginRoot?: string; allowReservedCommandNames?: boolean; allowOwnerStatusExposure?: boolean; }): CommandRegistrationResult;","entrypoint":"plugin-runtime","exportName":"registerPluginCommand","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"function","recordType":"export"} {"closureHash":"16986e5d431f584c2d5053289eca3d2225bbe47a92cdfbbc2af91b264a165846","declaration":"export function registerPluginInteractiveHandler(pluginId: string, registration: PluginInteractiveHandlerRegistration, opts?: { pluginName?: string; pluginRoot?: string; }): InteractiveRegistrationResult;","entrypoint":"plugin-runtime","exportName":"registerPluginInteractiveHandler","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"function","recordType":"export"} {"closureHash":"f20c07a48e2c949fbc889e7a32760f4e52339d9f6d3eab49e00ff249266e2396","declaration":"export function startLazyPluginServiceModule(params: { skipEnvVar?: string; overrideEnvVar?: string; validateOverrideSpecifier?: (specifier: string) => string; loadDefaultModule: () => Promise; loadOverrideModule?: (specifier: string) => Promise; startExportNames: string[]; stopExportNames?: string[]; }): Promise;","entrypoint":"plugin-runtime","exportName":"startLazyPluginServiceModule","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"function","recordType":"export"} {"closureHash":"bad43ffe69d1d9d6c92c79df4c388b3dda685013a92392daef456f1622ade659","declaration":"export type LazyPluginServiceHandle = LazyPluginServiceHandle;","entrypoint":"plugin-runtime","exportName":"LazyPluginServiceHandle","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"type","recordType":"export"} -{"closureHash":"ff621688b7b58ebfa7157b153d83921a47158e43ac312ff61df85d437216ae10","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"plugin-runtime","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"type","recordType":"export"} +{"closureHash":"b376fa0f3d5efb8c41fccbd2265e044a7ffed909a9d27d6e4dd340fbe2dc11f4","declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"plugin-runtime","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"type","recordType":"export"} {"closureHash":"365beb5cee974639c2f35fc7a39088721ca8e9c618442830298b98033e9c768c","declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"plugin-runtime","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"type","recordType":"export"} {"closureHash":"f06379ada10ce0eca4e66bdf978db2c0035e054a135dc044c6d831ac7797c08f","declaration":"export type PluginConversationBinding = PluginConversationBinding;","entrypoint":"plugin-runtime","exportName":"PluginConversationBinding","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"type","recordType":"export"} {"closureHash":"a1223d674b2b66918a576ae05b04318d7b8cbe9b1d3bc6dd7a08d1a4fa79b291","declaration":"export type PluginConversationBindingRequestParams = PluginConversationBindingRequestParams;","entrypoint":"plugin-runtime","exportName":"PluginConversationBindingRequestParams","importSpecifier":"openclaw/plugin-sdk/plugin-runtime","kind":"type","recordType":"export"} @@ -3671,10 +3671,10 @@ {"closureHash":"e66adcb55dec79be73a52bc76594b7e9210fd93c69a85b56098fbb58cecbb354","declaration":"export type WriteOAuthCredentialsOptions = WriteOAuthCredentialsOptions;","entrypoint":"provider-auth","exportName":"WriteOAuthCredentialsOptions","importSpecifier":"openclaw/plugin-sdk/provider-auth","kind":"type","recordType":"export"} {"category":null,"entrypoint":"provider-catalog-runtime","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime","recordType":"module"} {"closureHash":"b5665596a3f011454a5614681513516acd50b6c749536c43842d811980cf7ad3","declaration":"export function augmentModelCatalogWithProviderPlugins(params: { config?: OpenClawConfig; workspaceDir?: string; env?: NodeJS.ProcessEnv; metadataSnapshot?: PluginMetadataSnapshot; context: ProviderAugmentModelCatalogContext; }): Promise;","entrypoint":"provider-catalog-runtime","exportName":"augmentModelCatalogWithProviderPlugins","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime","kind":"function","recordType":"export"} -{"closureHash":"a360cb1f899330116af2fd6f2370cc53c0ff5083bd27a4d5e5977c05fa4627a6","declaration":"export function isPluginProvidersLoadInFlight(params: Parameters[0]): boolean;","entrypoint":"provider-catalog-runtime","exportName":"isPluginProvidersLoadInFlight","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime","kind":"function","recordType":"export"} -{"closureHash":"a8a3abd31253da5e27e43db29968d818f98a55a6f08e0be1e7e94265c634f7fa","declaration":"export function resolveCatalogHookProviderPluginIds(params: { config?: PluginLoadOptions[\"config\"]; workspaceDir?: string; env?: PluginLoadOptions[\"env\"]; metadataSnapshot?: ProviderManifestLoadParams[\"metadataSnapshot\"]; }): string[];","entrypoint":"provider-catalog-runtime","exportName":"resolveCatalogHookProviderPluginIds","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime","kind":"function","recordType":"export"} -{"closureHash":"0d463b51098a4f8bb1ca72db875efc938f64978bbf498632bdc7fb8b61648f4a","declaration":"export function resolveOwningPluginIdsForProvider(params: { provider: string; config?: PluginLoadOptions[\"config\"]; workspaceDir?: string; env?: PluginLoadOptions[\"env\"]; manifestRegistry?: PluginManifestRegistry; metadataSnapshot?: Pick; }): string[] | undefined;","entrypoint":"provider-catalog-runtime","exportName":"resolveOwningPluginIdsForProvider","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime","kind":"function","recordType":"export"} -{"closureHash":"4e83218d414f08a78be577d566d883f6147e50776a128e6c3e9c49057ab93cdc","declaration":"export function resolvePluginProviders(params: { config?: PluginLoadOptions[\"config\"]; workspaceDir?: string; env?: PluginLoadOptions[\"env\"]; bundledProviderVitestCompat?: boolean; onlyPluginIds?: string[]; providerRefs?: readonly string[]; modelRefs?: readonly string[]; activate?: boolean; cache?: boolean; applyAutoEnable?: boolean; pluginSdkResolution?: PluginLoadOptions[\"pluginSdkResolution\"]; mode?: \"runtime\" | \"setup\"; includeUntrustedWorkspacePlugins?: boolean; pluginMetadataSnapshot?: PluginMetadataRegistryView; skipIfLoadInFlight?: boolean; }): ProviderPlugin[];","entrypoint":"provider-catalog-runtime","exportName":"resolvePluginProviders","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime","kind":"function","recordType":"export"} +{"closureHash":"05838eda6c65861b15c694a0b048958e357afc4576b11b037962d6dd10441a4f","declaration":"export function isPluginProvidersLoadInFlight(params: Parameters[0]): boolean;","entrypoint":"provider-catalog-runtime","exportName":"isPluginProvidersLoadInFlight","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime","kind":"function","recordType":"export"} +{"closureHash":"cfa08f8f8d1ac9f6c0801b850a7912bb383d0f232cc5e8c2072b6963115479d2","declaration":"export function resolveCatalogHookProviderPluginIds(params: { config?: PluginLoadOptions[\"config\"]; workspaceDir?: string; env?: PluginLoadOptions[\"env\"]; metadataSnapshot?: ProviderManifestLoadParams[\"metadataSnapshot\"]; }): string[];","entrypoint":"provider-catalog-runtime","exportName":"resolveCatalogHookProviderPluginIds","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime","kind":"function","recordType":"export"} +{"closureHash":"de4f157ebaebeb21ad89b1aee2926820e6304298490b8234780f044ef8c6866d","declaration":"export function resolveOwningPluginIdsForProvider(params: { provider: string; config?: PluginLoadOptions[\"config\"]; workspaceDir?: string; env?: PluginLoadOptions[\"env\"]; manifestRegistry?: PluginManifestRegistry; metadataSnapshot?: Pick; }): string[] | undefined;","entrypoint":"provider-catalog-runtime","exportName":"resolveOwningPluginIdsForProvider","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime","kind":"function","recordType":"export"} +{"closureHash":"77e2900d5898546097a5b12745dce804bf7d0148ac06e7f46ec411584ae65561","declaration":"export function resolvePluginProviders(params: { config?: PluginLoadOptions[\"config\"]; workspaceDir?: string; env?: PluginLoadOptions[\"env\"]; bundledProviderVitestCompat?: boolean; onlyPluginIds?: string[]; providerRefs?: readonly string[]; modelRefs?: readonly string[]; activate?: boolean; cache?: boolean; applyAutoEnable?: boolean; pluginSdkResolution?: PluginLoadOptions[\"pluginSdkResolution\"]; mode?: \"runtime\" | \"setup\"; includeUntrustedWorkspacePlugins?: boolean; pluginMetadataSnapshot?: PluginMetadataRegistryView; skipIfLoadInFlight?: boolean; }): ProviderPlugin[];","entrypoint":"provider-catalog-runtime","exportName":"resolvePluginProviders","importSpecifier":"openclaw/plugin-sdk/provider-catalog-runtime","kind":"function","recordType":"export"} {"category":null,"entrypoint":"proxy-capture","importSpecifier":"openclaw/plugin-sdk/proxy-capture","recordType":"module"} {"closureHash":"d0e4e026d736bc00b1758d1089c44c3c5f3a09c7254291584de8cb297365804c","declaration":"export function acquireDebugProxyCaptureStore(dbPath: string, blobDir: string): { store: LegacyDebugProxyCaptureStore; release: () => void; };\nexport function acquireDebugProxyCaptureStore(options?: DebugProxyCaptureStoreOptions): { store: SharedDebugProxyCaptureStore; release: () => void; };","entrypoint":"proxy-capture","exportName":"acquireDebugProxyCaptureStore","importSpecifier":"openclaw/plugin-sdk/proxy-capture","kind":"function","recordType":"export"} {"closureHash":"d41cbb9de51b7aee1c6cf8c713b0bc44f27ac926fbb6422c05726f196d11964e","declaration":"export function captureHttpExchange(params: { url: string; method: string; requestHeaders?: Headers | Record | undefined; requestBody?: BodyInit | Buffer | string | null; response: Response; transport?: \"http\" | \"sse\"; flowId?: string; meta?: Record; }, resolved?: DebugProxySettings, deps?: DebugProxyCaptureRuntimeDeps): void;","entrypoint":"proxy-capture","exportName":"captureHttpExchange","importSpecifier":"openclaw/plugin-sdk/proxy-capture","kind":"function","recordType":"export"} @@ -4639,16 +4639,16 @@ {"closureHash":"e365d7ad911453b5139ba700931b061e560cff80b50d4cbe5e9615b5d7e13a52","declaration":"export type ScopedExpiringIdCache = ScopedExpiringIdCache;","entrypoint":"text-runtime","exportName":"ScopedExpiringIdCache","importSpecifier":"openclaw/plugin-sdk/text-runtime","kind":"type","recordType":"export"} {"closureHash":"af5502c92594c2e9049a4b4994e17c00d3adee3f6d1e68edd818a539c74c8787","declaration":"export interface CodeRegion {\n start: number;\n end: number;\n}","entrypoint":"text-runtime","exportName":"CodeRegion","importSpecifier":"openclaw/plugin-sdk/text-runtime","kind":"interface","recordType":"export"} {"category":null,"entrypoint":"tool-plugin","importSpecifier":"openclaw/plugin-sdk/tool-plugin","recordType":"module"} -{"closureHash":"0a3affca237e0370b796f418bfbe87212c7f1dc7d8066586a113c8d3f1c9c183","declaration":"export function defineToolPlugin(definition: DefineToolPluginOptions): DefinedToolPluginEntry;","entrypoint":"tool-plugin","exportName":"defineToolPlugin","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"function","recordType":"export"} +{"closureHash":"b8feaaea1648b9e535d5129273052708787bd0d3c612a339ed219012ba2c4074","declaration":"export function defineToolPlugin(definition: DefineToolPluginOptions): DefinedToolPluginEntry;","entrypoint":"tool-plugin","exportName":"defineToolPlugin","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"function","recordType":"export"} {"closureHash":"36ef907ce20f885b5f3068cbf0475dc442a68da9652579bb5bf8fb83ad34c38b","declaration":"export function getToolPluginMetadata(entry: unknown): ToolPluginMetadata | undefined;","entrypoint":"tool-plugin","exportName":"getToolPluginMetadata","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"function","recordType":"export"} {"closureHash":"bdc58154e36b2625fb3daccca3eab248d981077d04d747f0ad507cb2ff565198","declaration":"export const toolPluginMetadataSymbol: typeof toolPluginMetadataSymbol;","entrypoint":"tool-plugin","exportName":"toolPluginMetadataSymbol","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"const","recordType":"export"} -{"closureHash":"11aa5fbcec024a768b7af0c54f8fc899c202ff892edf481ba1476ff895345abb","declaration":"export type DefineToolPluginOptions = DefineToolPluginOptions;","entrypoint":"tool-plugin","exportName":"DefineToolPluginOptions","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"type","recordType":"export"} -{"closureHash":"7daed12952abd818d864a6e96c60c90bb79ddb73a94f50f3e21c676dbdb3dbd7","declaration":"export type DefinedToolPluginEntry = DefinedToolPluginEntry;","entrypoint":"tool-plugin","exportName":"DefinedToolPluginEntry","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"type","recordType":"export"} -{"closureHash":"15bac4724ce2f1cfb2aebdc5f66cd10de56c84f916bf65169ce1bfed39866e2d","declaration":"export type ToolPluginExecutionContext = ToolPluginExecutionContext;","entrypoint":"tool-plugin","exportName":"ToolPluginExecutionContext","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"type","recordType":"export"} -{"closureHash":"84872c7903f1cb60ab330047241d2bca21e43ee84f2e1073fd78cf3aa6b56dcb","declaration":"export type ToolPluginFactoryContext = ToolPluginFactoryContext;","entrypoint":"tool-plugin","exportName":"ToolPluginFactoryContext","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"type","recordType":"export"} +{"closureHash":"11b740891f678a7505617dcbd18193b20923fa24e52a728a1c933fbb42724f13","declaration":"export type DefineToolPluginOptions = DefineToolPluginOptions;","entrypoint":"tool-plugin","exportName":"DefineToolPluginOptions","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"type","recordType":"export"} +{"closureHash":"3bab1b9df86726d39f4d3d3d9f94d1f8835cc93aed94bdc150d1b434a343bcea","declaration":"export type DefinedToolPluginEntry = DefinedToolPluginEntry;","entrypoint":"tool-plugin","exportName":"DefinedToolPluginEntry","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"type","recordType":"export"} +{"closureHash":"be6e633d32ef90a0896fa3ad4081836195dfacd1a39e43f5970a6a3f2cfef282","declaration":"export type ToolPluginExecutionContext = ToolPluginExecutionContext;","entrypoint":"tool-plugin","exportName":"ToolPluginExecutionContext","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"type","recordType":"export"} +{"closureHash":"fdc66c9cbe084e4f4df2971ae295603caba55d3f9d011ce631823bdb3b2c3c85","declaration":"export type ToolPluginFactoryContext = ToolPluginFactoryContext;","entrypoint":"tool-plugin","exportName":"ToolPluginFactoryContext","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"type","recordType":"export"} {"closureHash":"f774bd092f51a31fbb34847213f9583234f585f66918d0af55e81bbbae40463b","declaration":"export type ToolPluginMetadata = ToolPluginMetadata;","entrypoint":"tool-plugin","exportName":"ToolPluginMetadata","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"type","recordType":"export"} {"closureHash":"414a89d0c80eaaf682dc25d306e7223c981c6fd25366adb973a106f4268a6a54","declaration":"export type ToolPluginStaticToolMetadata = ToolPluginStaticToolMetadata;","entrypoint":"tool-plugin","exportName":"ToolPluginStaticToolMetadata","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"type","recordType":"export"} -{"closureHash":"11e44768d91eaaa804d40fd52e29e5a93d24698515dcd50bb22f48c542b92f9a","declaration":"export type ToolPluginToolDefinition = ToolPluginToolDefinition;","entrypoint":"tool-plugin","exportName":"ToolPluginToolDefinition","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"type","recordType":"export"} +{"closureHash":"44e6ab539ae410d88a966e7b32916b8da123e8135717d40b408095a9bc3c63b5","declaration":"export type ToolPluginToolDefinition = ToolPluginToolDefinition;","entrypoint":"tool-plugin","exportName":"ToolPluginToolDefinition","importSpecifier":"openclaw/plugin-sdk/tool-plugin","kind":"type","recordType":"export"} {"category":null,"entrypoint":"tool-results","importSpecifier":"openclaw/plugin-sdk/tool-results","recordType":"module"} {"closureHash":"c5b0951c3629834dff78f92317cf1421a6bbc0faf9307be1cabb915055665280","declaration":"export function jsonResult(payload: TDetails): AgentToolResult;","entrypoint":"tool-results","exportName":"jsonResult","importSpecifier":"openclaw/plugin-sdk/tool-results","kind":"function","recordType":"export"} {"closureHash":"b9aefc637a694c74e028e92006004bef18185ed569af48ac43d36b646b0af2f3","declaration":"export function textResult(text: string, details: TDetails): AgentToolResult;","entrypoint":"tool-results","exportName":"textResult","importSpecifier":"openclaw/plugin-sdk/tool-results","kind":"function","recordType":"export"} @@ -4682,9 +4682,9 @@ {"closureHash":"9f9174f1023a8a451232f5d97d1e922b7faa88b76b58cffbfbe246a1488e0ddd","declaration":"export function readJsonWebhookBodyOrReject(params: { req: IncomingMessage; res: ServerResponse; maxBytes?: number; timeoutMs?: number; profile?: WebhookBodyReadProfile; emptyObjectOnEmpty?: boolean; invalidJsonMessage?: string; }): Promise<{ ok: true; value: unknown; } | { ok: false; }>;","entrypoint":"webhook-ingress","exportName":"readJsonWebhookBodyOrReject","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export"} {"closureHash":"1bbddd5d1c8297e315275db4d5e1c8748ff09c0be7c37b0cf456db4fc69f8909","declaration":"export function readRequestBodyWithLimit(req: IncomingMessage, options: ReadRequestBodyOptions): Promise;","entrypoint":"webhook-ingress","exportName":"readRequestBodyWithLimit","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export"} {"closureHash":"e8a1174cac83ce346a231fd25e82bb5f7618ffb57cdd9af6eaa208411f70646d","declaration":"export function readWebhookBodyOrReject(params: { req: IncomingMessage; res: ServerResponse; maxBytes?: number; timeoutMs?: number; profile?: WebhookBodyReadProfile; invalidBodyMessage?: string; }): Promise<{ ok: true; value: string; } | { ok: false; }>;","entrypoint":"webhook-ingress","exportName":"readWebhookBodyOrReject","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export"} -{"closureHash":"16a1cd83bd12e1f265a4b2e2fabe6c4d108e9570ee88c7daaf020608a5007f25","declaration":"export function registerPluginHttpRoute(params: { path?: string | null; fallbackPath?: string | null; handler: PluginHttpRouteHandler; auth: PluginHttpRouteRegistration[\"auth\"]; match?: PluginHttpRouteRegistration[\"match\"]; gatewayRuntimeScopeSurface?: PluginHttpRouteRegistration[\"gatewayRuntimeScopeSurface\"]; replaceExisting?: boolean; reuseExistingSameOwner?: boolean; throwOnFailure?: boolean; pluginId?: string; source?: string; accountId?: string; log?: (message: string) => void; registry?: PluginRegistry; }): () => void;","entrypoint":"webhook-ingress","exportName":"registerPluginHttpRoute","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export"} +{"closureHash":"cafe0449c4f72743ba5f1f4e008a9d70c749ceb4f46c5e336e23b47b5cad3e6e","declaration":"export function registerPluginHttpRoute(params: { path?: string | null; fallbackPath?: string | null; handler: PluginHttpRouteHandler; auth: PluginHttpRouteRegistration[\"auth\"]; match?: PluginHttpRouteRegistration[\"match\"]; gatewayRuntimeScopeSurface?: PluginHttpRouteRegistration[\"gatewayRuntimeScopeSurface\"]; replaceExisting?: boolean; reuseExistingSameOwner?: boolean; throwOnFailure?: boolean; pluginId?: string; source?: string; accountId?: string; log?: (message: string) => void; registry?: PluginRegistry; }): () => void;","entrypoint":"webhook-ingress","exportName":"registerPluginHttpRoute","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export"} {"closureHash":"05835f2f66f906f736b82b332a84a9714bc3a6b211890a62d8fc4dbf9258a7d4","declaration":"export function registerWebhookTarget(targetsByPath: Map, target: T, opts?: RegisterWebhookTargetOptions): RegisteredWebhookTarget;","entrypoint":"webhook-ingress","exportName":"registerWebhookTarget","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export"} -{"closureHash":"3a78257a9745ef83d912a6d4659ff16a67d2eab326098f6084119ef510f915a7","declaration":"export function registerWebhookTargetWithPluginRoute(params: { targetsByPath: Map; target: T; route: RegisterWebhookPluginRouteOptions; onLastPathTargetRemoved?: RegisterWebhookTargetOptions[\"onLastPathTargetRemoved\"]; }): RegisteredWebhookTarget;","entrypoint":"webhook-ingress","exportName":"registerWebhookTargetWithPluginRoute","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export"} +{"closureHash":"7b4334de2c9e456f1e9d1f304fef4c75072e6a132553a08d60f57b226067462d","declaration":"export function registerWebhookTargetWithPluginRoute(params: { targetsByPath: Map; target: T; route: RegisterWebhookPluginRouteOptions; onLastPathTargetRemoved?: RegisterWebhookTargetOptions[\"onLastPathTargetRemoved\"]; }): RegisteredWebhookTarget;","entrypoint":"webhook-ingress","exportName":"registerWebhookTargetWithPluginRoute","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export"} {"closureHash":"ec2f47e9bd7a075a3dd4302ac9e4c0bfe20630445db6735cf803f2d354275841","declaration":"export function requestBodyErrorToText(code: RequestBodyLimitErrorCode): string;","entrypoint":"webhook-ingress","exportName":"requestBodyErrorToText","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export"} {"closureHash":"5e1f3b468d56eb0f82104184077fdf4b6bd0c1444aa10b71a65fcaa2ec0298a2","declaration":"export function resolveRequestClientIp(req?: IncomingMessage, trustedProxies?: string[], allowRealIpFallback?: boolean): string | undefined;","entrypoint":"webhook-ingress","exportName":"resolveRequestClientIp","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export"} {"closureHash":"56c3e9868dfa6ee2a7a128eb02039a16b52696759adadbd3fe0a3bd7a6a07eeb","declaration":"export function resolveSingleWebhookTarget(targets: readonly T[], isMatch: (target: T) => boolean): WebhookTargetMatchResult;","entrypoint":"webhook-ingress","exportName":"resolveSingleWebhookTarget","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"function","recordType":"export"} @@ -4702,7 +4702,7 @@ {"closureHash":"efb74e84339c15d6f607b38d1d477b0504d47d04ae78e77083a1893c756a0b54","declaration":"export const WEBHOOK_RATE_LIMIT_DEFAULTS: Readonly<{ windowMs: 60000; maxRequests: 120; maxTrackedKeys: 4096;}>;","entrypoint":"webhook-ingress","exportName":"WEBHOOK_RATE_LIMIT_DEFAULTS","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"const","recordType":"export"} {"closureHash":"13b28ec9acae43c3bdcd683b9831154f88118dee8142e42b7eebfd59f08c602c","declaration":"export type BoundedCounter = BoundedCounter;","entrypoint":"webhook-ingress","exportName":"BoundedCounter","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"type","recordType":"export"} {"closureHash":"9e737ae8c17038ef7becbd2bf1b756cfeaed571e2ded368c0fe3a446ebd1f666","declaration":"export type FixedWindowRateLimiter = FixedWindowRateLimiter;","entrypoint":"webhook-ingress","exportName":"FixedWindowRateLimiter","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"type","recordType":"export"} -{"closureHash":"75b1673b974b3866ffe5ad188f96f05a191310fcd95ff97efbcac5829e9eafdc","declaration":"export type RegisterWebhookPluginRouteOptions = RegisterWebhookPluginRouteOptions;","entrypoint":"webhook-ingress","exportName":"RegisterWebhookPluginRouteOptions","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"type","recordType":"export"} +{"closureHash":"5e2e583b7fb4d4f5c130e01d3a7d156a2509a493b7bbd16109a2d138306f9c78","declaration":"export type RegisterWebhookPluginRouteOptions = RegisterWebhookPluginRouteOptions;","entrypoint":"webhook-ingress","exportName":"RegisterWebhookPluginRouteOptions","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"type","recordType":"export"} {"closureHash":"b3e98619854462acfac6538ac9a2d0cefb88704a0eac712563f0996b07364152","declaration":"export type RegisterWebhookTargetOptions = RegisterWebhookTargetOptions;","entrypoint":"webhook-ingress","exportName":"RegisterWebhookTargetOptions","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"type","recordType":"export"} {"closureHash":"964c2bfd8f268c411ee7b982c9de02c207f4bec402dd62c9f998a59752bb9d1f","declaration":"export type RegisteredWebhookTarget = RegisteredWebhookTarget;","entrypoint":"webhook-ingress","exportName":"RegisteredWebhookTarget","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"type","recordType":"export"} {"closureHash":"aa552e5bdaefc9eb94d3b5a5cdf8c413fad555d9f452c6a2a6af5a3ce695c9d1","declaration":"export type WebhookAnomalyTracker = WebhookAnomalyTracker;","entrypoint":"webhook-ingress","exportName":"WebhookAnomalyTracker","importSpecifier":"openclaw/plugin-sdk/webhook-ingress","kind":"type","recordType":"export"} diff --git a/docs/cli/secrets.md b/docs/cli/secrets.md index 967e04c32338..81483569f31a 100644 --- a/docs/cli/secrets.md +++ b/docs/cli/secrets.md @@ -43,7 +43,7 @@ Related: [Secrets Management](/gateway/secrets) · [1Password plugin](/plugins/o ## Shared secret store -`openclaw secrets store` writes directly to the local shared state database. The store is Gateway-wide and team-scoped; this release accepts only `--scope team`. `--scope me` is rejected because identity scope arrives with the settings UI. +`openclaw secrets store` writes directly to the local shared state database. The store is Gateway-wide and team-scoped; this release accepts only `--scope team`. `--scope me` is rejected because identity scope is not supported yet. ```bash openclaw secrets store list @@ -117,7 +117,7 @@ op read 'op://Engineering/service-account/dotenv' | openclaw secrets store impor The importer supports quoted values and multiline quoted values such as PEM keys. Use `--yes` to skip confirmation and `--dry-run` to inspect the import without writing. Kind detection follows the same name-based rule as `store set`. -The store commands do not accept `--url` or `--token`; Gateway RPC methods are not part of this storage layer. +The store CLI commands do not accept `--url` or `--token` and do not route through the Gateway. The Control UI uses the admin-scoped `secrets.store.*` RPC methods instead; those methods refresh the runtime automatically when a changed name is referenced by active config. ## Reload runtime snapshot diff --git a/docs/gateway/protocol.md b/docs/gateway/protocol.md index 7c814ccd3a63..0146f27faa0c 100644 --- a/docs/gateway/protocol.md +++ b/docs/gateway/protocol.md @@ -603,6 +603,8 @@ methods. Treat this as feature discovery, not a full enumeration of - `secrets.reload` re-resolves active SecretRefs and atomically publishes owner-aware runtime state. Eligible owner failures can publish as cold or stale degradation with `warningCount`; strict or unmapped failures reject the reload and preserve the active snapshot. - `secrets.resolve` resolves command-target secret assignments for a specific command/target set. + - `secrets.store.list` (`operator.admin`) returns team-scoped metadata and values only for `kind: "env"` entries. `kind: "secret"` entries use a distinct result shape with no value field; there is no reveal method. + - `secrets.store.set` and `secrets.store.delete` (`operator.admin`) create/update or soft-delete one team-scoped entry. After a successful write, the Gateway refreshes the active secrets runtime only when the name is referenced by a `store` SecretRef in the active source config. - `config.get` returns the current on-disk config snapshot, raw root-file `hash`, resolved `configRevisionHash`, and optional `appliedConfigHash` for the resolved revision accepted by the active Gateway runtime. - `config.set` writes a validated config payload. - `config.patch` merges a partial config update. Destructive array replacement requires the affected path in `replacePaths`; nested arrays under array entries use `[]` paths such as `agents.entries.*.skills`. diff --git a/docs/gateway/secrets.md b/docs/gateway/secrets.md index 34d6e7dbcaea..ae440ca8c9c2 100644 --- a/docs/gateway/secrets.md +++ b/docs/gateway/secrets.md @@ -267,18 +267,18 @@ but are not displayed because resolver output can contain credential material. - Reads values from OpenClaw's shared state SQLite database. - The provider has no connection settings. `secrets.defaults.store` selects its default alias. -- Only team scope is resolved in this release. Identity scope is reserved for a later settings experience. +- Only team scope is resolved in this release. Identity scope is reserved for a later release. ## Shared secret store -The shared secret store is a Gateway-wide, team-scoped place for secrets and environment values that should be available to every Gateway process using the same state database. Manage it locally with `openclaw secrets store`; there are no Gateway URL or token options for these commands. +The shared secret store is a Gateway-wide, team-scoped place for secrets and environment values that should be available to every Gateway process using the same state database. Manage it from **Settings → Secrets** in the Control UI or locally with `openclaw secrets store`. The CLI commands operate on the local state database and do not accept Gateway URL or token options. Entries have a `secret` or `env` kind. The kind controls CLI disclosure, not SecretRef resolution: -- `secret` values are write-only through the CLI. List and get output never reveal them. -- `env` values can be returned by `store list` and `store get`. Team-scoped `env` entries are also added to agent exec environments, after inherited process values and before explicit per-call env. Protected host keys and sandbox-blocked credential names are ignored with a visible warning. +- `secret` values are write-only after saving. Gateway list results, the Control UI, and CLI list/get output never include them; there is no reveal RPC. +- `env` values remain visible to administrators in the Control UI and can be returned by `store list` and `store get`. Team-scoped `env` entries are also added to agent exec environments, after inherited process values and before explicit per-call env. Protected host keys and sandbox-blocked credential names are ignored with a visible warning. `secret` entries are never injected into subprocess environments. They remain available only through `store` SecretRefs because plaintext env injection would bypass the store disclosure boundary; safe secret injection requires a future egress-substitution mechanism. @@ -298,7 +298,7 @@ Reference an entry from `openclaw.json` with the `store` source: } ``` -After changing a value used by config, run `openclaw secrets reload` so the active in-memory snapshot picks it up. +Control UI set/delete operations automatically refresh the active secrets runtime when the changed name is referenced by a `store` SecretRef in the active source config. Names that are not referenced skip that work. Direct CLI writes remain an offline/local path; after changing a config-referenced value with the CLI, run `openclaw secrets reload` so the active in-memory snapshot picks it up. Store values are not encrypted at rest. They are stored unencrypted in the shared state SQLite database (`state/openclaw.sqlite`), protected by the same `0600` file and `0700` directory permissions as other credentials in that database. Operators who need stronger storage isolation should use an external exec provider such as the [1Password plugin](/plugins/onepassword) or [Vault SecretRefs](/plugins/vault). @@ -811,9 +811,11 @@ For static credentials, runtime no longer depends on plaintext legacy auth stora - Legacy static `api_key` entries are scrubbed when discovered. - OAuth-related compatibility behavior remains separate. -## Web UI note +## Control UI -Some SecretInput unions are easier to configure in raw editor mode than in form mode. +Open **Settings → Secrets** to list, add, edit, bulk-import, or soft-delete team-scoped entries. Bulk Add accepts dotenv `NAME=VALUE` assignments, including quoted multiline values. Credential-like names default to `secret`; clear **Auto-detect secrets** to import all entries as visible environment values. + +This store page manages values only. Configure the corresponding `store` SecretRef on a supported field through its settings form or the raw editor. Identity-scoped entries are reserved for a later release and are not exposed by this page. ## Related diff --git a/docs/web/control-ui.md b/docs/web/control-ui.md index 675b41277040..19efb04423cd 100644 --- a/docs/web/control-ui.md +++ b/docs/web/control-ui.md @@ -286,8 +286,9 @@ select it to open the owning Approvals page. - View/edit `~/.openclaw/openclaw.json` (`config.get`, `config.set`). - - Settings navigation starts with Ask OpenClaw, Profile, Appearance, and Notifications up top; Connections (Connection, Channels, Communications, Talk, Devices); Agents & Tools (Agents, Labs, Models, MCP, Memory, Automation); Privacy & Security (Security, Approvals); and System (Infrastructure, Advanced, Debug, Logs, About). Language leads the Appearance page, model defaults live on Models, and Gateway host details live on Connection. + - Settings navigation starts with Ask OpenClaw, Profile, Appearance, and Notifications up top; Connections (Connection, Channels, Communications, Talk, Devices); Agents & Tools (Agents, Labs, Models, MCP, Memory, Automation); Privacy & Security (Security, Secrets, Approvals); and System (Infrastructure, Advanced, Debug, Logs, About). Language leads the Appearance page, model defaults live on Models, and Gateway host details live on Connection. - Privacy & Security: curated rows for gateway auth, exec policy, browser enablement, tool profile, device auth, and mobile pairing, above the schema-backed `security`/`approvals` sections. + - Secrets (`/settings/secrets`) manages team-scoped secret and environment entries through `secrets.store.*`. Environment values remain visible, secret values are never returned after saving, Bulk Add accepts quoted multiline dotenv values, and mutation actions are hidden when the connected Gateway does not advertise them. - Approvals includes newest-first, 30-day history for resolved exec, plugin, and system-agent requests. Filter by kind or page through older rows to review the decision, reason, source session, and resolver attribution recorded by the Gateway. - Labs exposes shipped experimental switches. Code Mode and Swarm are the current entries and save `tools.codeMode.enabled` and `tools.swarm.enabled` immediately; unshipped experiments do not appear or write speculative config keys. - Notifications: browser web-push status, subscribe/unsubscribe, and a test send. diff --git a/docs/web/urls.md b/docs/web/urls.md index 6c39e28510f9..d0c40641eff5 100644 --- a/docs/web/urls.md +++ b/docs/web/urls.md @@ -146,6 +146,7 @@ no route-specific URL parameters. | Appearance | `/settings/appearance` | `/appearance` | Shared settings parameters below | | Notifications | `/settings/notifications` | - | Shared settings parameters below | | Security | `/settings/security` | - | Shared settings parameters below | +| Secrets | `/settings/secrets` | - | Shared settings parameters below | | Advanced | `/settings/advanced` | - | Shared settings parameters below | | Approvals | `/settings/approvals` | - | Shared settings parameters below | | Automation settings | `/settings/automation` | `/automation` | Shared settings parameters below | diff --git a/packages/gateway-protocol/src/index.ts b/packages/gateway-protocol/src/index.ts index 4ba11d1e075f..4b81f97ae94f 100644 --- a/packages/gateway-protocol/src/index.ts +++ b/packages/gateway-protocol/src/index.ts @@ -37,6 +37,13 @@ export * from "./schema/sessions-suggestions.js"; export * from "./migration-api.js"; export type * from "./public-session-catalog.js"; export * from "./validator-registry.js"; +export type { + SecretStoreEntry, + SecretsStoreDeleteParams, + SecretsStoreListResult, + SecretsStoreMutationResult, + SecretsStoreSetParams, +} from "./schema/secrets.js"; // Explicit schema exports keep public protocol changes reviewable. export { isCloudWorkerPlacementState, @@ -149,6 +156,14 @@ export { WakeParamsSchema, PushTestParamsSchema, PushTestResultSchema, + SecretStoreSecretEntrySchema, + SecretStoreEnvEntrySchema, + SecretStoreEntrySchema, + SecretsStoreListParamsSchema, + SecretsStoreListResultSchema, + SecretsStoreSetParamsSchema, + SecretsStoreDeleteParamsSchema, + SecretsStoreMutationResultSchema, WebPushVapidPublicKeyParamsSchema, WebPushSubscribeParamsSchema, WebPushUnsubscribeParamsSchema, diff --git a/packages/gateway-protocol/src/schema/protocol-schema-fragment-integrations.ts b/packages/gateway-protocol/src/schema/protocol-schema-fragment-integrations.ts index 98fd40778f08..9dc577d2d38f 100644 --- a/packages/gateway-protocol/src/schema/protocol-schema-fragment-integrations.ts +++ b/packages/gateway-protocol/src/schema/protocol-schema-fragment-integrations.ts @@ -15,6 +15,14 @@ export const IntegrationProtocolSchemas = { UiCommandParams: uiCommand.UiCommandParamsSchema, UiCommandResult: uiCommand.UiCommandResultSchema, SecretsReloadParams: secrets.SecretsReloadParamsSchema, + SecretStoreSecretEntry: secrets.SecretStoreSecretEntrySchema, + SecretStoreEnvEntry: secrets.SecretStoreEnvEntrySchema, + SecretStoreEntry: secrets.SecretStoreEntrySchema, + SecretsStoreListParams: secrets.SecretsStoreListParamsSchema, + SecretsStoreListResult: secrets.SecretsStoreListResultSchema, + SecretsStoreSetParams: secrets.SecretsStoreSetParamsSchema, + SecretsStoreDeleteParams: secrets.SecretsStoreDeleteParamsSchema, + SecretsStoreMutationResult: secrets.SecretsStoreMutationResultSchema, SecretsResolveParams: secrets.SecretsResolveParamsSchema, SecretsResolveAssignment: secrets.SecretsResolveAssignmentSchema, SecretsResolveResult: secrets.SecretsResolveResultSchema, diff --git a/packages/gateway-protocol/src/schema/secrets.test.ts b/packages/gateway-protocol/src/schema/secrets.test.ts new file mode 100644 index 000000000000..8c2119f1bb9d --- /dev/null +++ b/packages/gateway-protocol/src/schema/secrets.test.ts @@ -0,0 +1,63 @@ +import { Value } from "typebox/value"; +import { describe, expect, it } from "vitest"; +import { + SecretsStoreListResultSchema, + SecretsStoreMutationResultSchema, + SecretsStoreSetParamsSchema, +} from "./secrets.js"; + +const metadata = { + name: "SERVICE_API_KEY", + scopeKind: "team", + scopeId: "", + createdAtMs: 1, + updatedAtMs: 2, + updatedBy: "Operator", +}; + +describe("secret store protocol schemas", () => { + it("makes secret values structurally unrepresentable while requiring env values", () => { + expect( + Value.Check(SecretsStoreListResultSchema, { + entries: [ + { ...metadata, kind: "secret" }, + { ...metadata, name: "SERVICE_URL", kind: "env", value: "https://service.test" }, + ], + }), + ).toBe(true); + expect( + Value.Check(SecretsStoreListResultSchema, { + entries: [{ ...metadata, kind: "secret", value: "must-not-cross-boundary" }], + }), + ).toBe(false); + expect( + Value.Check(SecretsStoreListResultSchema, { + entries: [{ ...metadata, name: "SERVICE_URL", kind: "env" }], + }), + ).toBe(false); + }); + + it("validates store mutations and their reload status", () => { + expect( + Value.Check(SecretsStoreSetParamsSchema, { + name: "SERVICE_API_KEY", + value: "value", + kind: "secret", + }), + ).toBe(true); + expect( + Value.Check(SecretsStoreSetParamsSchema, { + name: "lowercase", + value: "value", + kind: "secret", + }), + ).toBe(false); + expect( + Value.Check(SecretsStoreMutationResultSchema, { + ok: true, + reloaded: true, + warningCount: 1, + }), + ).toBe(true); + }); +}); diff --git a/packages/gateway-protocol/src/schema/secrets.ts b/packages/gateway-protocol/src/schema/secrets.ts index 36e96e69f34e..bcd55b72677f 100644 --- a/packages/gateway-protocol/src/schema/secrets.ts +++ b/packages/gateway-protocol/src/schema/secrets.ts @@ -12,6 +12,73 @@ import { NonEmptyString } from "./primitives.js"; /** Empty request payload for reloading configured secret providers. */ export const SecretsReloadParamsSchema = closedObject({}); +const SecretStoreNameSchema = Type.String({ + minLength: 1, + maxLength: 128, + pattern: "^[A-Z][A-Z0-9_]{0,127}$", +}); + +const SecretStoreEntryMetadataProperties = { + name: SecretStoreNameSchema, + scopeKind: Type.Literal("team"), + scopeId: Type.Literal(""), + createdAtMs: Type.Integer({ minimum: 0 }), + updatedAtMs: Type.Integer({ minimum: 0 }), + updatedBy: Type.Optional(Type.String()), +} as const; + +/** Secret metadata never structurally carries the stored value. */ +export const SecretStoreSecretEntrySchema = closedObject({ + ...SecretStoreEntryMetadataProperties, + kind: Type.Literal("secret"), +}); + +/** Environment entries include their value because they are intentionally visible. */ +export const SecretStoreEnvEntrySchema = closedObject({ + ...SecretStoreEntryMetadataProperties, + kind: Type.Literal("env"), + value: Type.String({ maxLength: 64 * 1024 }), +}); + +/** Team secret-store list entry, discriminated by disclosure behavior. */ +export const SecretStoreEntrySchema = Type.Union([ + SecretStoreSecretEntrySchema, + SecretStoreEnvEntrySchema, +]); + +/** Empty request payload for listing the team secret store. */ +export const SecretsStoreListParamsSchema = closedObject({}); + +/** Team secret-store inventory. */ +export const SecretsStoreListResultSchema = closedObject({ + entries: Type.Array(SecretStoreEntrySchema), +}); + +/** Create or replace one team secret-store entry. */ +export const SecretsStoreSetParamsSchema = closedObject({ + name: SecretStoreNameSchema, + value: Type.String({ maxLength: 64 * 1024 }), + kind: Type.Union([Type.Literal("secret"), Type.Literal("env")]), +}); + +/** Soft-delete one team secret-store entry. */ +export const SecretsStoreDeleteParamsSchema = closedObject({ + name: SecretStoreNameSchema, +}); + +/** Mutation acknowledgement including whether the active runtime was refreshed. */ +export const SecretsStoreMutationResultSchema = closedObject({ + ok: Type.Literal(true), + reloaded: Type.Boolean(), + warningCount: Type.Optional(Type.Integer({ minimum: 0 })), +}); + +export type SecretStoreEntry = Static; +export type SecretsStoreListResult = Static; +export type SecretsStoreSetParams = Static; +export type SecretsStoreDeleteParams = Static; +export type SecretsStoreMutationResult = Static; + /** Request payload for resolving the secrets needed by one command invocation. */ export const SecretsResolveParamsSchema = closedObject({ commandName: NonEmptyString, diff --git a/packages/gateway-protocol/src/validator-registry.ts b/packages/gateway-protocol/src/validator-registry.ts index 43fd2738a1ff..06ddd4ca1fb5 100644 --- a/packages/gateway-protocol/src/validator-registry.ts +++ b/packages/gateway-protocol/src/validator-registry.ts @@ -176,6 +176,11 @@ export const validateWebPushUnsubscribeParams = compile(S.WebPushTestParamsSchema); export const validateSecretsResolveParams = compile(S.SecretsResolveParamsSchema); export const validateSecretsResolveResult = compile(S.SecretsResolveResultSchema); +export const validateSecretsStoreListParams = compile(S.SecretsStoreListParamsSchema); +export const validateSecretsStoreListResult = compile(S.SecretsStoreListResultSchema); +export const validateSecretsStoreSetParams = compile(S.SecretsStoreSetParamsSchema); +export const validateSecretsStoreDeleteParams = compile(S.SecretsStoreDeleteParamsSchema); +export const validateSecretsStoreMutationResult = compile(S.SecretsStoreMutationResultSchema); export const validateSessionsListParams = compile(S.SessionsListParamsSchema); export const validateSessionsCatalogListParams = compile(S.SessionsCatalogListParamsSchema); export const validateSessionsCatalogReadParams = compile(S.SessionsCatalogReadParamsSchema); diff --git a/src/cli/secrets-store-cli.ts b/src/cli/secrets-store-cli.ts index a293e3d8dee4..08b53674bd37 100644 --- a/src/cli/secrets-store-cli.ts +++ b/src/cli/secrets-store-cli.ts @@ -38,10 +38,7 @@ function teamScope(scope: string | undefined): { kind: "team" } { return { kind: "team" }; } if (scope === "me") { - throw new SecretStoreCliFailure( - 2, - "Identity scope arrives with the settings UI; use --scope team.", - ); + throw new SecretStoreCliFailure(2, "Identity scope is not supported yet; use --scope team."); } throw new SecretStoreCliFailure(2, `Invalid scope "${scope}"; only "team" is supported.`); } diff --git a/src/cli/secrets-store-input.ts b/src/cli/secrets-store-input.ts index 9b1be525c6f4..868c14424df4 100644 --- a/src/cli/secrets-store-input.ts +++ b/src/cli/secrets-store-input.ts @@ -1,8 +1,8 @@ import fs from "node:fs/promises"; import { password } from "@clack/prompts"; import { readByteStreamWithLimit } from "@openclaw/media-core/read-byte-stream-with-limit"; -import { parse as parseDotEnv } from "dotenv"; import { readFileDescriptorBounded } from "../infra/boundary-file-read.js"; +import { parseSecretStoreDotEnvText } from "../secrets/store/dotenv.js"; import { SECRET_STORE_VALUE_MAX_BYTES } from "../secrets/store/secret-store.js"; const SECRET_STORE_IMPORT_MAX_BYTES = 16 * 1024 * 1024; @@ -58,7 +58,7 @@ export async function readSecretStoreInput(params: { valueFile?: string }): Prom } export function parseSecretStoreDotEnv(raw: string | Buffer): Record { - return parseDotEnv(raw); + return parseSecretStoreDotEnvText(raw.toString()); } export async function readSecretStoreImport(from?: string): Promise> { diff --git a/src/gateway/method-scopes.test.ts b/src/gateway/method-scopes.test.ts index 35a4bb4135d5..c0fe6020f003 100644 --- a/src/gateway/method-scopes.test.ts +++ b/src/gateway/method-scopes.test.ts @@ -112,6 +112,9 @@ describe("method scope resolution", () => { ["talk.session.close", ["operator.talk"]], ["update.status", ["operator.admin"]], ["update.hold", ["operator.admin"]], + ["secrets.store.list", ["operator.admin"]], + ["secrets.store.set", ["operator.admin"]], + ["secrets.store.delete", ["operator.admin"]], ["config.schema", ["operator.admin"]], ["config.patch", ["operator.admin"]], ["nativeHook.invoke", ["operator.admin"]], diff --git a/src/gateway/methods/core-descriptors.since.test.ts b/src/gateway/methods/core-descriptors.since.test.ts index d3eeeff49ffc..eacee134b16c 100644 --- a/src/gateway/methods/core-descriptors.since.test.ts +++ b/src/gateway/methods/core-descriptors.since.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { listCoreGatewayMethodMetadata } from "./core-descriptors.js"; -const CURRENT_TRAIN_METHODS = [ +const TRAIN_2026_7_METHODS = [ "question.request", "question.waitAnswer", "question.resolve", @@ -80,6 +80,20 @@ const CURRENT_TRAIN_METHODS = [ "tasks.dismiss", ] as const; +const CURRENT_TRAIN_METHODS = [ + "sessions.patchMany", + "update.hold", + "sessions.catalog.startTerminal", + "worker.desktop.observe", + "projects.list", + "projects.register", + "projects.remove", + "worker.desktop.launch", + "secrets.store.list", + "secrets.store.set", + "secrets.store.delete", +] as const; + describe("core gateway method release trains", () => { it("records a valid train for every method and dates the 2026.7 families", () => { const methods = listCoreGatewayMethodMetadata(); @@ -93,6 +107,12 @@ describe("core gateway method release trains", () => { .filter((method) => method.since === "2026.7") .map((method) => method.name) .toSorted(), + ).toEqual(TRAIN_2026_7_METHODS.toSorted()); + expect( + methods + .filter((method) => method.since === "2026.8") + .map((method) => method.name) + .toSorted(), ).toEqual(CURRENT_TRAIN_METHODS.toSorted()); expect(methods.find((method) => method.name === "update.hold")?.since).toBe("2026.8"); expect(methods.find((method) => method.name === "sessions.catalog.startTerminal")?.since).toBe( diff --git a/src/gateway/methods/core-descriptors.ts b/src/gateway/methods/core-descriptors.ts index fb72b13a3e7b..6956d1838840 100644 --- a/src/gateway/methods/core-descriptors.ts +++ b/src/gateway/methods/core-descriptors.ts @@ -498,6 +498,10 @@ const CORE_GATEWAY_METHOD_SPECS = [ ["projects.register", "projects", "operator.admin", "2026.8"], ["projects.remove", "projects", "operator.admin", "2026.8"], ["worker.desktop.launch", "environments", "operator.admin", "2026.8", { startup: true }], + // Store CRUD shares the auxiliary secrets runtime owner and appends for stable indices. + ["secrets.store.list", null, "operator.admin", "2026.8"], + ["secrets.store.set", null, "operator.admin", "2026.8", { controlPlaneWrite: true }], + ["secrets.store.delete", null, "operator.admin", "2026.8", { controlPlaneWrite: true }], ] as const satisfies readonly CoreGatewayMethodSpecRow[]; export type CoreGatewayHandlerFamily = Exclude<(typeof CORE_GATEWAY_METHOD_SPECS)[number][1], null>; diff --git a/src/gateway/server-aux-handlers.test.ts b/src/gateway/server-aux-handlers.test.ts index 94544a77e99b..2ffa802e7397 100644 --- a/src/gateway/server-aux-handlers.test.ts +++ b/src/gateway/server-aux-handlers.test.ts @@ -4,6 +4,25 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +const secretStoreMocks = vi.hoisted(() => ({ + deleteEntry: vi.fn(), + listEntries: vi.fn(() => []), + purgeEntries: vi.fn(() => 0), + writeEntry: vi.fn(), +})); + +vi.mock("../secrets/store/secret-store.js", () => { + class SecretStoreValidationError extends Error {} + return { + deleteSecretStoreEntry: secretStoreMocks.deleteEntry, + listSecretStoreEntries: secretStoreMocks.listEntries, + purgeExpiredSecretStoreEntries: secretStoreMocks.purgeEntries, + SECRET_STORE_VALUE_MAX_BYTES: 64 * 1024, + SecretStoreValidationError, + writeSecretStoreEntry: secretStoreMocks.writeEntry, + }; +}); import { getRuntimeAuthProfileStoreCredentialsRevision, getRuntimeAuthProfileStoreSnapshot, @@ -149,6 +168,21 @@ async function invokeSecretsReload(params: { }); } +async function invokeSecretStoreSet(params: { + handlers: ReturnType["extraHandlers"]; + respond: ReturnType; + name: string; +}) { + await params.handlers["secrets.store.set"]({ + req: { type: "req", id: "store-1", method: "secrets.store.set" }, + params: { name: params.name, value: "next-value", kind: "secret" }, + client: null, + isWebchatConnect: () => false, + respond: params.respond as never, + context: {} as never, + }); +} + type RespondCall = [boolean, unknown, { message?: string } | undefined]; type GatewayAuxHandlerParams = Parameters[0]; type ChannelName = Parameters[0]; @@ -240,6 +274,10 @@ function createSecretsReloadHarnessWithChannelMocks( beforeEach(() => { delete process.env.OPENCLAW_SKIP_CHANNELS; delete process.env.OPENCLAW_SKIP_PROVIDERS; + secretStoreMocks.deleteEntry.mockReset(); + secretStoreMocks.listEntries.mockReset().mockReturnValue([]); + secretStoreMocks.purgeEntries.mockReset().mockReturnValue(0); + secretStoreMocks.writeEntry.mockReset(); }); afterEach(() => { @@ -514,6 +552,60 @@ describe("gateway aux handlers", () => { expect(respond).toHaveBeenNthCalledWith(2, true, { ok: true, warningCount: 0 }); }); + it("runs a trailing refresh when a referenced store mutation overlaps reload", async () => { + const sourceConfig = asConfig({ + models: { + providers: { + test: { + apiKey: { source: "store", provider: "default", id: "SERVICE_API_KEY" }, + models: [], + }, + }, + }, + }); + activateSecretsRuntimeSnapshot(createSourceSnapshot(sourceConfig)); + let releaseFirst: (() => void) | undefined; + const firstBlocked = new Promise((resolve) => { + releaseFirst = resolve; + }); + let firstStarted: (() => void) | undefined; + const firstEntered = new Promise((resolve) => { + firstStarted = resolve; + }); + const activateRuntimeSecrets = vi + .fn() + .mockImplementationOnce(async () => { + firstStarted?.(); + await firstBlocked; + return createSourceSnapshot(sourceConfig); + }) + .mockResolvedValue(createSourceSnapshot(sourceConfig)); + const { extraHandlers, reload } = createSecretsReloadHarness({ activateRuntimeSecrets }); + const reloadPromise = reload(); + await firstEntered; + + const setRespond = vi.fn(); + const setPromise = invokeSecretStoreSet({ + handlers: extraHandlers, + respond: setRespond, + name: "SERVICE_API_KEY", + }); + await vi.waitFor(() => expect(secretStoreMocks.writeEntry).toHaveBeenCalledOnce()); + expect(activateRuntimeSecrets).toHaveBeenCalledTimes(1); + releaseFirst?.(); + await Promise.all([reloadPromise, setPromise]); + + expect(activateRuntimeSecrets).toHaveBeenCalledTimes(2); + expect(activateRuntimeSecrets.mock.calls[1]?.[1]).toMatchObject({ + forceColdRefKeys: new Set(["store:default:SERVICE_API_KEY"]), + }); + expect(setRespond).toHaveBeenCalledWith(true, { + ok: true, + reloaded: true, + warningCount: 0, + }); + }); + it("retries from the canonical source when it changes during secrets.reload preparation", async () => { const initialConfig = slackConfig("initial-secret"); const canonicalConfig = slackConfig("canonical-secret"); diff --git a/src/gateway/server-aux-handlers.ts b/src/gateway/server-aux-handlers.ts index 7ac9ef707640..e452ae5cefc5 100644 --- a/src/gateway/server-aux-handlers.ts +++ b/src/gateway/server-aux-handlers.ts @@ -339,9 +339,14 @@ export function createGatewayAuxHandlers(params: { let reloadInFlight: Promise | null = null; const runExclusiveReload = ( fn: () => Promise, + options: { joinInFlight?: boolean } = {}, ): Promise => { if (reloadInFlight) { - return reloadInFlight; + if (options.joinInFlight !== false) { + return reloadInFlight; + } + const precedingReload = reloadInFlight; + return precedingReload.catch(() => undefined).then(() => runExclusiveReload(fn, options)); } const run = (async () => { try { @@ -357,7 +362,7 @@ export function createGatewayAuxHandlers(params: { () => import("./server-methods/secrets.js").then(({ createSecretsHandlers }) => createSecretsHandlers({ - reloadSecrets: () => + reloadSecrets: (reloadOptions) => runExclusiveReload(async () => { let transaction: | { @@ -397,6 +402,7 @@ export function createGatewayAuxHandlers(params: { reason: "reload", activate: false, publishFailureAsDegraded: true, + forceColdRefKeys: reloadOptions?.forceColdRefKeys, canPublishFailureAsDegraded: () => getActiveSecretsRuntimeSnapshotRevision() === previousSnapshotRevision, }, @@ -624,7 +630,7 @@ export function createGatewayAuxHandlers(params: { } throw err; } - }), + }, reloadOptions), log: params.log, resolveSecrets: async ({ allowedPaths, @@ -701,6 +707,9 @@ export function createGatewayAuxHandlers(params: { "question.list": createLazyHandler("question.list", loadQuestionHandlers), "secrets.reload": createLazyHandler("secrets.reload", loadSecretsHandlers), "secrets.resolve": createLazyHandler("secrets.resolve", loadSecretsHandlers), + "secrets.store.list": createLazyHandler("secrets.store.list", loadSecretsHandlers), + "secrets.store.set": createLazyHandler("secrets.store.set", loadSecretsHandlers), + "secrets.store.delete": createLazyHandler("secrets.store.delete", loadSecretsHandlers), }, }; } diff --git a/src/gateway/server-aux-methods.ts b/src/gateway/server-aux-methods.ts index 7cc724fd4467..df79aa65f4ba 100644 --- a/src/gateway/server-aux-methods.ts +++ b/src/gateway/server-aux-methods.ts @@ -21,4 +21,7 @@ export const GATEWAY_AUX_METHODS = [ "question.list", "secrets.reload", "secrets.resolve", + "secrets.store.list", + "secrets.store.set", + "secrets.store.delete", ] as const; diff --git a/src/gateway/server-methods-list.test.ts b/src/gateway/server-methods-list.test.ts index 4dbc7304070f..fb7ade43b489 100644 --- a/src/gateway/server-methods-list.test.ts +++ b/src/gateway/server-methods-list.test.ts @@ -66,7 +66,7 @@ describe("listGatewayMethods", () => { }); it("appends new methods after model probing without shifting older method indices", () => { - expect(listGatewayMethods().slice(-39)).toEqual([ + expect(listGatewayMethods().slice(-42)).toEqual([ "models.probe", "migrations.memory.plan", "migrations.memory.apply", @@ -106,6 +106,9 @@ describe("listGatewayMethods", () => { "projects.register", "projects.remove", "worker.desktop.launch", + "secrets.store.list", + "secrets.store.set", + "secrets.store.delete", ]); const methods = listGatewayMethods(); expect(methods.indexOf("node.pluginSurface.refresh")).toBe( @@ -197,7 +200,7 @@ describe("listGatewayMethods", () => { "exec.approval.get", ]); expect(methods).toContain("tts.speak"); - expect(coreMethods.slice(-46)).toEqual([ + expect(coreMethods.slice(-49)).toEqual([ "sessions.catalog.continue", "sessions.catalog.archive", "approval.get", @@ -244,6 +247,9 @@ describe("listGatewayMethods", () => { "projects.register", "projects.remove", "worker.desktop.launch", + "secrets.store.list", + "secrets.store.set", + "secrets.store.delete", ]); expect(methods.indexOf("approval.get")).toBeGreaterThan(methods.indexOf("tts.speak")); expect(methods.indexOf("approval.resolve")).toBe(methods.indexOf("approval.get") + 1); @@ -260,6 +266,11 @@ describe("listGatewayMethods", () => { expect(methods.indexOf("projects.register")).toBe(methods.indexOf("projects.list") + 1); expect(methods.indexOf("projects.remove")).toBe(methods.indexOf("projects.register") + 1); expect(methods.indexOf("worker.desktop.launch")).toBe(methods.indexOf("projects.remove") + 1); + expect(methods.indexOf("secrets.store.list")).toBe( + methods.indexOf("worker.desktop.launch") + 1, + ); + expect(methods.indexOf("secrets.store.set")).toBe(methods.indexOf("secrets.store.list") + 1); + expect(methods.indexOf("secrets.store.delete")).toBe(methods.indexOf("secrets.store.set") + 1); }); it("advertises the versioned Talk session RPCs", () => { diff --git a/src/gateway/server-methods/secrets.test.ts b/src/gateway/server-methods/secrets.test.ts index e42fb9c28e75..94c2ce58f0ad 100644 --- a/src/gateway/server-methods/secrets.test.ts +++ b/src/gateway/server-methods/secrets.test.ts @@ -3,7 +3,40 @@ */ import { expectDefined } from "@openclaw/normalization-core"; -import { describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const storeMocks = vi.hoisted(() => ({ + deleteEntry: vi.fn(), + listEntries: vi.fn(() => [] as Array>), + purgeEntries: vi.fn(() => 0), + writeEntry: vi.fn(), + getSnapshot: vi.fn(() => ({ sourceConfig: {} })), + collectRefKeys: vi.fn((_config: unknown, _name: string) => new Set()), +})); + +vi.mock("../../secrets/runtime-state.js", () => ({ + collectSecretStoreRefKeysInConfig: storeMocks.collectRefKeys, + getActiveSecretsRuntimeSnapshot: storeMocks.getSnapshot, +})); + +vi.mock("../../secrets/store/secret-store.js", () => { + class SecretStoreValidationError extends Error { + constructor( + readonly code: string, + message: string, + ) { + super(message); + this.name = "SecretStoreValidationError"; + } + } + return { + deleteSecretStoreEntry: storeMocks.deleteEntry, + listSecretStoreEntries: storeMocks.listEntries, + purgeExpiredSecretStoreEntries: storeMocks.purgeEntries, + SecretStoreValidationError, + writeSecretStoreEntry: storeMocks.writeEntry, + }; +}); // Handler tests only need the registry verdicts they exercise. Dedicated // target-registry tests own bundled plugin discovery and compilation. @@ -67,6 +100,39 @@ async function invokeSecretsResolve(params: { }); } +async function invokeStoreMethod(params: { + handlers: ReturnType; + method: "secrets.store.list" | "secrets.store.set" | "secrets.store.delete"; + requestParams: Record; + respond: ReturnType; +}) { + await expectDefined( + params.handlers[params.method], + `handler ${params.method}`, + )({ + req: { type: "req", id: "store-1", method: params.method }, + params: params.requestParams, + client: { + connect: { + minProtocol: 1, + maxProtocol: 1, + client: { + id: "control-ui", + version: "test", + platform: "web", + mode: "webchat", + displayName: "Control UI", + }, + role: "operator", + scopes: ["operator.admin"], + }, + } as never, + isWebchatConnect: () => false, + respond: params.respond as never, + context: {} as never, + }); +} + function expectRespondError( respond: ReturnType, expected: { code: string; message?: string }, @@ -109,8 +175,20 @@ async function expectMemoryStatusResolveUnavailable(params: { } describe("secrets handlers", () => { + beforeEach(() => { + storeMocks.deleteEntry.mockReset(); + storeMocks.listEntries.mockReset().mockReturnValue([]); + storeMocks.purgeEntries.mockReset().mockReturnValue(0); + storeMocks.writeEntry.mockReset(); + storeMocks.getSnapshot.mockReset().mockReturnValue({ sourceConfig: {} }); + storeMocks.collectRefKeys.mockReset().mockReturnValue(new Set()); + }); + function createHandlers(overrides?: { - reloadSecrets?: () => Promise<{ warningCount: number }>; + reloadSecrets?: (options?: { + forceColdRefKeys?: ReadonlySet; + joinInFlight?: boolean; + }) => Promise<{ warningCount: number }>; resolveSecrets?: (params: { commandName: string; targetIds: string[]; @@ -277,4 +355,130 @@ describe("secrets handlers", () => { warningText: "EACCES: permission denied", }); }); + + it("lists env values without structurally disclosing secret values", async () => { + storeMocks.listEntries.mockReturnValueOnce([ + { + name: "SERVICE_API_KEY", + kind: "secret", + scopeKind: "team", + scopeId: "", + createdAtMs: 1, + updatedAtMs: 2, + updatedBy: "Operator", + valuePreview: "malicious-leak", + }, + { + name: "SERVICE_URL", + kind: "env", + scopeKind: "team", + scopeId: "", + createdAtMs: 1, + updatedAtMs: 2, + updatedBy: "Operator", + valuePreview: "https://service.test", + }, + ]); + const respond = vi.fn(); + await invokeStoreMethod({ + handlers: createHandlers(), + method: "secrets.store.list", + requestParams: {}, + respond, + }); + expect(respond.mock.calls[0]?.[1]).toMatchObject({ + entries: [ + { name: "SERVICE_API_KEY", kind: "secret" }, + { name: "SERVICE_URL", kind: "env", value: "https://service.test" }, + ], + }); + expect(JSON.stringify(respond.mock.calls[0]?.[1])).not.toContain("malicious-leak"); + }); + + it("refreshes the runtime only after mutations of referenced store names", async () => { + storeMocks.collectRefKeys.mockImplementation((_config, name) => + name === "SERVICE_API_KEY" ? new Set(["store:default:SERVICE_API_KEY"]) : new Set(), + ); + const reloadSecrets = vi.fn().mockResolvedValue({ warningCount: 2 }); + storeMocks.getSnapshot.mockReturnValue({ + sourceConfig: { + models: { + providers: { + test: { + apiKey: { source: "store", provider: "default", id: "SERVICE_API_KEY" }, + }, + }, + }, + }, + }); + const handlers = createHandlers({ reloadSecrets }); + + const setRespond = vi.fn(); + await invokeStoreMethod({ + handlers, + method: "secrets.store.set", + requestParams: { name: "SERVICE_API_KEY", value: "new-value", kind: "secret" }, + respond: setRespond, + }); + expect(storeMocks.writeEntry).toHaveBeenCalledWith({ + scope: { kind: "team" }, + name: "SERVICE_API_KEY", + value: "new-value", + kind: "secret", + updatedBy: "Control UI", + }); + expect(setRespond).toHaveBeenCalledWith(true, { + ok: true, + reloaded: true, + warningCount: 2, + }); + + const deleteRespond = vi.fn(); + await invokeStoreMethod({ + handlers, + method: "secrets.store.delete", + requestParams: { name: "SERVICE_URL" }, + respond: deleteRespond, + }); + expect(deleteRespond).toHaveBeenCalledWith(true, { ok: true, reloaded: false }); + expect(reloadSecrets).toHaveBeenCalledTimes(1); + expect(reloadSecrets).toHaveBeenCalledWith({ + forceColdRefKeys: new Set(["store:default:SERVICE_API_KEY"]), + joinInFlight: false, + }); + }); + + it("rejects invalid store params before writing", async () => { + const respond = vi.fn(); + await invokeStoreMethod({ + handlers: createHandlers(), + method: "secrets.store.set", + requestParams: { name: "lowercase", value: "value", kind: "secret" }, + respond, + }); + expect(storeMocks.writeEntry).not.toHaveBeenCalled(); + expectRespondError(respond, { code: "INVALID_REQUEST" }); + }); + + it("reports a saved entry when its required runtime refresh fails", async () => { + storeMocks.collectRefKeys.mockReturnValue(new Set(["store:default:SERVICE_API_KEY"])); + const handlers = createHandlers({ + reloadSecrets: vi.fn().mockRejectedValue(new Error("provider unavailable")), + }); + const respond = vi.fn(); + + await invokeStoreMethod({ + handlers, + method: "secrets.store.set", + requestParams: { name: "SERVICE_API_KEY", value: "new-value", kind: "secret" }, + respond, + }); + + expect(storeMocks.writeEntry).toHaveBeenCalledOnce(); + expectRespondError(respond, { + code: "UNAVAILABLE", + message: + "Secret store entry was saved, but post-write runtime refresh failed. Resolve provider errors and retry secrets.reload.", + }); + }); }); diff --git a/src/gateway/server-methods/secrets.ts b/src/gateway/server-methods/secrets.ts index 2ddc614d4948..928acbdbe02a 100644 --- a/src/gateway/server-methods/secrets.ts +++ b/src/gateway/server-methods/secrets.ts @@ -6,10 +6,59 @@ import { type ValidationError, validateSecretsResolveParams, validateSecretsResolveResult, + validateSecretsStoreDeleteParams, + validateSecretsStoreListParams, + validateSecretsStoreListResult, + validateSecretsStoreMutationResult, + validateSecretsStoreSetParams, + type SecretStoreEntry, } from "../../../packages/gateway-protocol/src/index.js"; import { formatErrorMessage as errorMessage } from "../../infra/errors.js"; +import { + collectSecretStoreRefKeysInConfig, + getActiveSecretsRuntimeSnapshot, +} from "../../secrets/runtime-state.js"; +import { + deleteSecretStoreEntry, + listSecretStoreEntries, + purgeExpiredSecretStoreEntries, + SecretStoreValidationError, + writeSecretStoreEntry, +} from "../../secrets/store/secret-store.js"; import { isKnownCoreSecretTargetId, isKnownSecretTargetId } from "../../secrets/target-registry.js"; -import type { GatewayRequestHandlers } from "./types.js"; +import type { GatewayClient, GatewayRequestHandlers } from "./types.js"; +import { assertValidParams } from "./validation.js"; + +const teamScope = { kind: "team" } as const; + +function toProtocolStoreEntry( + entry: ReturnType[number], +): SecretStoreEntry { + const metadata = { + name: entry.name, + scopeKind: "team" as const, + scopeId: "" as const, + createdAtMs: entry.createdAtMs, + updatedAtMs: entry.updatedAtMs, + ...(entry.updatedBy ? { updatedBy: entry.updatedBy } : {}), + }; + if (entry.kind === "env") { + if (typeof entry.valuePreview !== "string") { + throw new Error(`Secret store env metadata is missing its value for ${entry.name}.`); + } + return { ...metadata, kind: "env", value: entry.valuePreview }; + } + return { ...metadata, kind: "secret" }; +} + +function storeUpdatedBy(client: GatewayClient | null): string { + return ( + client?.authenticatedUserProfile?.displayName?.trim() || + client?.connect?.client?.displayName?.trim() || + client?.connect?.client?.id?.trim() || + "gateway" + ); +} function invalidSecretsResolveField( errors: ValidationError[] | null | undefined, @@ -50,7 +99,10 @@ function invalidSecretsResolveField( } export function createSecretsHandlers(params: { - reloadSecrets: () => Promise<{ warningCount: number }>; + reloadSecrets: (options?: { + forceColdRefKeys?: ReadonlySet; + joinInFlight?: boolean; + }) => Promise<{ warningCount: number }>; resolveSecrets: (params: { commandName: string; targetIds: string[]; @@ -74,6 +126,29 @@ export function createSecretsHandlers(params: { warn?: (message: string) => void; }; }): GatewayRequestHandlers { + const purgeStoreRetention = () => { + try { + purgeExpiredSecretStoreEntries(); + } catch (error) { + params.log?.warn?.(`secrets.store retention purge failed: ${errorMessage(error)}`); + } + }; + const reloadStoreReference = async ( + name: string, + ): Promise<{ reloaded: boolean; warningCount?: number }> => { + const snapshot = getActiveSecretsRuntimeSnapshot(); + const refKeys = snapshot + ? collectSecretStoreRefKeysInConfig(snapshot.sourceConfig, name) + : new Set(); + if (refKeys.size === 0) { + return { reloaded: false }; + } + // An explicit store mutation must not reuse an older credential if the + // replacement is missing or invalid; affected owners become cold instead. + const reload = await params.reloadSecrets({ forceColdRefKeys: refKeys, joinInFlight: false }); + return { reloaded: true, warningCount: reload.warningCount }; + }; + return { "secrets.reload": async ({ respond }) => { try { @@ -168,5 +243,121 @@ export function createSecretsHandlers(params: { respond(false, undefined, errorShape(ErrorCodes.UNAVAILABLE, "secrets.resolve failed")); } }, + "secrets.store.list": ({ params: requestParams, respond }) => { + if ( + !assertValidParams( + requestParams, + validateSecretsStoreListParams, + "secrets.store.list", + respond, + ) + ) { + return; + } + try { + const result = { + entries: listSecretStoreEntries({ scope: teamScope }).map(toProtocolStoreEntry), + }; + if (!validateSecretsStoreListResult(result)) { + throw new Error("secrets.store.list returned invalid payload."); + } + respond(true, result); + } catch (error) { + params.log?.warn?.(`secrets.store.list failed: ${errorMessage(error)}`); + respond(false, undefined, errorShape(ErrorCodes.UNAVAILABLE, "secrets.store.list failed")); + } + }, + "secrets.store.set": async ({ params: requestParams, respond, client }) => { + if ( + !assertValidParams( + requestParams, + validateSecretsStoreSetParams, + "secrets.store.set", + respond, + ) + ) { + return; + } + let stored = false; + try { + writeSecretStoreEntry({ + scope: teamScope, + name: requestParams.name, + value: requestParams.value, + kind: requestParams.kind, + updatedBy: storeUpdatedBy(client), + }); + stored = true; + purgeStoreRetention(); + const reload = await reloadStoreReference(requestParams.name); + const result = { + ok: true as const, + ...reload, + }; + if (!validateSecretsStoreMutationResult(result)) { + throw new Error("secrets.store.set returned invalid payload."); + } + respond(true, result); + } catch (error) { + if (error instanceof SecretStoreValidationError) { + respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, error.message)); + return; + } + params.log?.warn?.(`secrets.store.set failed: ${errorMessage(error)}`); + respond( + false, + undefined, + errorShape( + ErrorCodes.UNAVAILABLE, + stored + ? "Secret store entry was saved, but post-write runtime refresh failed. Resolve provider errors and retry secrets.reload." + : "secrets.store.set failed", + ), + ); + } + }, + "secrets.store.delete": async ({ params: requestParams, respond }) => { + if ( + !assertValidParams( + requestParams, + validateSecretsStoreDeleteParams, + "secrets.store.delete", + respond, + ) + ) { + return; + } + let deleted = false; + try { + deleteSecretStoreEntry({ scope: teamScope, name: requestParams.name }); + deleted = true; + purgeStoreRetention(); + const reload = await reloadStoreReference(requestParams.name); + const result = { + ok: true as const, + ...reload, + }; + if (!validateSecretsStoreMutationResult(result)) { + throw new Error("secrets.store.delete returned invalid payload."); + } + respond(true, result); + } catch (error) { + if (error instanceof SecretStoreValidationError) { + respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, error.message)); + return; + } + params.log?.warn?.(`secrets.store.delete failed: ${errorMessage(error)}`); + respond( + false, + undefined, + errorShape( + ErrorCodes.UNAVAILABLE, + deleted + ? "Secret store entry was deleted, but the active runtime could not refresh. Update the config reference or restore the entry, then retry secrets.reload." + : "secrets.store.delete failed", + ), + ); + } + }, }; } diff --git a/src/gateway/server-startup-config.ts b/src/gateway/server-startup-config.ts index 6b66620cd6c9..a326ff3bbf2d 100644 --- a/src/gateway/server-startup-config.ts +++ b/src/gateway/server-startup-config.ts @@ -80,6 +80,8 @@ type RuntimeSecretsActivationParams = { runtimeSourceConfig?: OpenClawConfig; /** Defer degradation/recovery publication until a larger transaction can no longer roll back. */ deferStatePublication?: boolean; + /** SecretRefs that must not retain last-known-good values during this reload. */ + forceColdRefKeys?: ReadonlySet; }; type DeferredSecretsStateTransition = { @@ -423,6 +425,7 @@ export function createRuntimeSecretsActivator(params: { allowUnavailableSecretOwners, ...(activationParams.env ? { env: activationParams.env } : {}), includeAuthStoreRefs: activationParams.includeAuthStoreRefs, + forceColdRefKeys: activationParams.forceColdRefKeys, ...(startupManifestRegistry ? { manifestRegistry: startupManifestRegistry } : {}), ...(params.pluginMetadataSnapshot ? { pluginMetadataSnapshot: params.pluginMetadataSnapshot } diff --git a/src/secrets/runtime-owner-assignments.ts b/src/secrets/runtime-owner-assignments.ts index 9483456dc7e1..148d9406d8e6 100644 --- a/src/secrets/runtime-owner-assignments.ts +++ b/src/secrets/runtime-owner-assignments.ts @@ -44,7 +44,11 @@ export function classifySecretOwnerDegradationState(params: { refs: SecretRef[]; config: OpenClawConfig; contractDigest?: string; + forceColdRefKeys?: ReadonlySet; }): "cold" | "stale" { + if (params.refs.some((ref) => params.forceColdRefKeys?.has(secretRefKey(ref)))) { + return "cold"; + } const active = getActiveSecretsRuntimeSnapshot(); if ( !active || @@ -185,6 +189,7 @@ function associateAssignmentFailureOwners(params: { assignments: SecretAssignment[]; error: unknown; config: OpenClawConfig; + forceColdRefKeys?: ReadonlySet; }): void { const validationFailures = getSecretAssignmentValidationFailures(params.error); const validationFailureRefKeys = new Set(validationFailures.map((failure) => failure.refKey)); @@ -245,6 +250,7 @@ function associateAssignmentFailureOwners(params: { assignment.ownerContractDigest ? [assignment.ownerContractDigest] : [], ), ), + forceColdRefKeys: params.forceColdRefKeys, }), failureMatched, source: getSecretAssignmentSource(assignments[0]!), @@ -331,6 +337,7 @@ function associateAssignmentFailureOwners(params: { refs, config: params.config, contractDigest: owner.contractDigest, + forceColdRefKeys: params.forceColdRefKeys, }), failureMatched: true, source, @@ -357,6 +364,7 @@ export function warnDegradedSecretOwner( async function resolveStrictAssignments(params: { assignments: SecretAssignment[]; options: SecretResolutionOptions; + forceColdRefKeys?: ReadonlySet; }): Promise> { try { const resolved = await resolveSecretRefValues( @@ -371,6 +379,7 @@ async function resolveStrictAssignments(params: { assignments: params.assignments, error, config: params.options.config, + forceColdRefKeys: params.forceColdRefKeys, }); throw error; } @@ -418,6 +427,7 @@ export async function resolveAndApplySecretAssignments(params: { context: ResolverContext; options: SecretResolutionOptions; allowOwnerIsolation?: boolean; + forceColdRefKeys?: ReadonlySet; }): Promise<{ degradedOwners: DegradedSecretOwner[]; resolvedValues: Map }> { if (!params.allowOwnerIsolation) { return { @@ -449,6 +459,7 @@ export async function resolveAndApplySecretAssignments(params: { assignments: pendingOwners.flat(), error: failure.error, config: params.options.config, + forceColdRefKeys: params.forceColdRefKeys, }); const matchingOwners = pendingOwners.filter((assignments) => assignments.some((assignment) => @@ -506,6 +517,7 @@ export async function resolveAndApplySecretAssignments(params: { assignments: readyAssignments, error, config: params.options.config, + forceColdRefKeys: params.forceColdRefKeys, }); throw error; } @@ -526,6 +538,7 @@ export async function resolveAndApplySecretAssignments(params: { assignment.ownerContractDigest ? [assignment.ownerContractDigest] : [], ), ), + forceColdRefKeys: params.forceColdRefKeys, }); const activeOwner = degradationState === "stale" diff --git a/src/secrets/runtime-state.test.ts b/src/secrets/runtime-state.test.ts index 54df4aec2e6a..9fa2b996bdac 100644 --- a/src/secrets/runtime-state.test.ts +++ b/src/secrets/runtime-state.test.ts @@ -29,6 +29,7 @@ import { activateSecretsRuntimeSnapshotState, activateSecretsRuntimeSnapshotStateIfCurrent, clearSecretsRuntimeSnapshot, + collectSecretStoreRefKeysInConfig, getActiveSecretsRuntimeConfigSnapshot, getActiveSecretsRuntimeSnapshot, getActiveSecretsRuntimeSnapshotRevision, @@ -39,6 +40,33 @@ import { type PreparedSecretsRuntimeSnapshot, } from "./runtime-state.js"; +describe("secret store references", () => { + it("finds canonical and provider-defaulted store refs without matching other sources", () => { + const config = { + secrets: { defaults: { store: "default" } }, + models: { + providers: { + one: { + apiKey: { source: "store", id: "TEAM_API_KEY" }, + models: [], + }, + }, + }, + } as unknown as OpenClawConfig; + expect(collectSecretStoreRefKeysInConfig(config, "TEAM_API_KEY")).toEqual( + new Set(["store:default:TEAM_API_KEY"]), + ); + expect( + collectSecretStoreRefKeysInConfig( + { + gateway: { auth: { token: { source: "env", provider: "default", id: "TEAM_API_KEY" } } }, + }, + "TEAM_API_KEY", + ), + ).toEqual(new Set()); + }); +}); + type PreparedSnapshotOverrides = Omit< Partial, "authStoreCredentialsRevision" | "webTools" diff --git a/src/secrets/runtime-state.ts b/src/secrets/runtime-state.ts index 91d26fae5171..8cabd06d188d 100644 --- a/src/secrets/runtime-state.ts +++ b/src/secrets/runtime-state.ts @@ -30,6 +30,7 @@ import { coerceSecretRef, isSecretRef, type SecretRef } from "../config/types.se import type { PluginManifestRegistry } from "../plugins/manifest-registry.js"; import type { PluginOrigin } from "../plugins/plugin-origin.types.js"; import { isRecord } from "../utils.js"; +import { secretRefKey } from "./ref-contract.js"; import { setActiveDegradedSecretOwners, type DegradedSecretOwner, @@ -86,6 +87,18 @@ function listLocatedSecretRefs( return refs; } +/** Canonical store SecretRef keys in config that resolve one team entry name. */ +export function collectSecretStoreRefKeysInConfig( + config: OpenClawConfig, + name: string, +): Set { + return new Set( + listLocatedSecretRefs(config, config.secrets?.defaults).flatMap(({ ref }) => + ref.source === "store" && ref.id === name ? [secretRefKey(ref)] : [], + ), + ); +} + /** Whether two configs resolve the same SecretRefs through the same provider contracts. */ export function hasSameSecretReloadContract(left: OpenClawConfig, right: OpenClawConfig): boolean { return isDeepStrictEqual( diff --git a/src/secrets/runtime-store.test.ts b/src/secrets/runtime-store.test.ts index e6a2a5aa506e..8e04cfcb1943 100644 --- a/src/secrets/runtime-store.test.ts +++ b/src/secrets/runtime-store.test.ts @@ -1,12 +1,31 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { afterEach, describe, expect, it } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { asConfig, setupSecretsRuntimeSnapshotTestHooks } from "./runtime.test-support.ts"; +const storeMocks = vi.hoisted(() => ({ + readValue: vi.fn(), +})); + +vi.mock("./store/secret-store.js", async (importOriginal) => { + const actual = await importOriginal(); + return { ...actual, readSecretStoreValue: storeMocks.readValue }; +}); + const roots: string[] = []; const { prepareSecretsRuntimeSnapshot } = setupSecretsRuntimeSnapshotTestHooks(); +beforeEach(() => { + storeMocks.readValue.mockReset().mockReturnValue({ + ok: false, + error: { + code: "SECRET_STORE_NOT_FOUND", + message: "Secret store entry was not found.", + }, + }); +}); + afterEach(async () => { await Promise.all(roots.splice(0).map((root) => fs.rm(root, { recursive: true, force: true }))); }); @@ -37,4 +56,49 @@ describe("store SecretRef runtime degradation", () => { }, ]); }); + + it("makes an intentionally mutated missing store ref cold instead of retaining its value", async () => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-runtime-store-")); + roots.push(root); + const ref = { source: "store", provider: "default", id: "SERVICE_API_KEY" } as const; + const config = asConfig({ + agents: { list: [{ id: "main", default: true }] }, + skills: { entries: { service: { apiKey: ref } } }, + }); + const runtimeOptions = { + config, + env: { OPENCLAW_STATE_DIR: path.join(root, "state") }, + includeAuthStoreRefs: false, + allowUnavailableSecretOwners: true, + loadablePluginOrigins: new Map(), + } as const; + + storeMocks.readValue.mockReturnValue({ ok: true, value: "previous-secret" }); + const active = await prepareSecretsRuntimeSnapshot(runtimeOptions); + const { activateSecretsRuntimeSnapshot } = await import("./runtime.js"); + activateSecretsRuntimeSnapshot(active); + expect(active.config.skills?.entries?.service?.apiKey).toBe("previous-secret"); + + storeMocks.readValue.mockReturnValue({ + ok: false, + error: { + code: "SECRET_STORE_NOT_FOUND", + message: "Secret store entry was not found.", + }, + }); + const refreshed = await prepareSecretsRuntimeSnapshot({ + ...runtimeOptions, + forceColdRefKeys: new Set(["store:default:SERVICE_API_KEY"]), + }); + + expect(refreshed.config.skills?.entries?.service?.apiKey).toEqual(ref); + expect(refreshed.degradedOwners).toMatchObject([ + { + ownerKind: "capability", + ownerId: "skill:service", + degradationState: "cold", + }, + ]); + expect(JSON.stringify(refreshed)).not.toContain("previous-secret"); + }); }); diff --git a/src/secrets/runtime-web-tools.ts b/src/secrets/runtime-web-tools.ts index a896de72b989..e75bcbe32001 100644 --- a/src/secrets/runtime-web-tools.ts +++ b/src/secrets/runtime-web-tools.ts @@ -141,6 +141,7 @@ function collectUnavailableWebProviders(params: { sourceConfig: OpenClawConfig; metadata: RuntimeWebSearchMetadata | RuntimeWebFetchMetadata; degradedOwners: DegradedSecretOwner[]; + forceColdRefKeys?: ReadonlySet; }): void { for (const unavailable of params.result.unavailableProviders) { let degradationState = classifySecretOwnerDegradationState({ @@ -149,6 +150,7 @@ function collectUnavailableWebProviders(params: { refs: [unavailable.ref], config: params.sourceConfig, contractDigest: unavailable.contractDigest, + forceColdRefKeys: params.forceColdRefKeys, }); if (degradationState === "stale") { const active = getActiveSecretsRuntimeSnapshot(); @@ -216,6 +218,7 @@ function associateWebProviderResolutionError(params: { config: OpenClawConfig; error: unknown; unavailableProviders: RuntimeWebProviderFailure[]; + forceColdRefKeys?: ReadonlySet; }): void { const failureByRefKey = new Map( params.unavailableProviders.map((unavailable) => [unavailable.refKey, unavailable] as const), @@ -230,6 +233,7 @@ function associateWebProviderResolutionError(params: { refs: [unavailable.ref], config: params.config, contractDigest: unavailable.contractDigest, + forceColdRefKeys: params.forceColdRefKeys, }), failureMatched: true, source: "config" as const, @@ -267,6 +271,7 @@ function associateWebProviderResolutionError(params: { refs: matches.map((match) => match.ref), config: params.config, contractDigest: owner.contractDigest, + forceColdRefKeys: params.forceColdRefKeys, }), failureMatched: true, source: "config" as const, @@ -425,6 +430,7 @@ async function resolveSecretInputWithEnvFallback(params: { contractDigest: string; providerFailuresByRefKey: RuntimeWebProviderFailureByRefKey; restrictEnvRefsToEnvVars?: boolean; + forceColdRefKeys?: ReadonlySet; }): Promise> { const { ref } = resolveSecretInputRef({ value: params.value, @@ -479,6 +485,7 @@ async function resolveSecretInputWithEnvFallback(params: { kind: params.kind, config: params.sourceConfig, error, + forceColdRefKeys: params.forceColdRefKeys, unavailableProviders: [ { providerId: params.providerId, @@ -503,6 +510,7 @@ async function resolveSecretInputWithEnvFallback(params: { kind: params.kind, config: params.sourceConfig, error, + forceColdRefKeys: params.forceColdRefKeys, unavailableProviders: [ { providerId: params.providerId, @@ -745,6 +753,7 @@ export async function resolveRuntimeWebTools(params: { resolvedConfig: OpenClawConfig; context: ResolverContext; allowUnavailableSecretOwners?: boolean; + forceColdRefKeys?: ReadonlySet; }): Promise { const defaults = params.sourceConfig.secrets?.defaults; const diagnostics: RuntimeWebDiagnostic[] = []; @@ -885,6 +894,7 @@ export async function resolveRuntimeWebTools(params: { kind: "search", config: params.sourceConfig, error, + forceColdRefKeys: params.forceColdRefKeys, unavailableProviders: error.unavailableProviders, }); }, @@ -914,6 +924,7 @@ export async function resolveRuntimeWebTools(params: { envVars, contractDigest, providerFailuresByRefKey, + forceColdRefKeys: params.forceColdRefKeys, }), setResolvedCredential: ({ resolvedConfig, provider, value }) => setResolvedWebSearchApiKey({ @@ -952,6 +963,7 @@ export async function resolveRuntimeWebTools(params: { sourceConfig: params.sourceConfig, metadata: searchMetadata, degradedOwners, + forceColdRefKeys: params.forceColdRefKeys, }); for (const owner of searchSelection.secretOwners) { secretOwners.push(toWebSecretOwnerRefState("search", owner)); @@ -1023,6 +1035,7 @@ export async function resolveRuntimeWebTools(params: { kind: "fetch", config: params.sourceConfig, error, + forceColdRefKeys: params.forceColdRefKeys, unavailableProviders: error.unavailableProviders, }); }, @@ -1053,6 +1066,7 @@ export async function resolveRuntimeWebTools(params: { contractDigest, providerFailuresByRefKey, restrictEnvRefsToEnvVars: true, + forceColdRefKeys: params.forceColdRefKeys, }), setResolvedCredential: ({ resolvedConfig, provider, value }) => setResolvedWebFetchApiKey({ @@ -1091,6 +1105,7 @@ export async function resolveRuntimeWebTools(params: { sourceConfig: params.sourceConfig, metadata: fetchMetadata, degradedOwners, + forceColdRefKeys: params.forceColdRefKeys, }); for (const owner of fetchSelection.secretOwners) { secretOwners.push(toWebSecretOwnerRefState("fetch", owner)); diff --git a/src/secrets/runtime.ts b/src/secrets/runtime.ts index 270509ba72e8..87c1c02f4f28 100644 --- a/src/secrets/runtime.ts +++ b/src/secrets/runtime.ts @@ -190,6 +190,8 @@ export async function prepareSecretsRuntimeSnapshot(params: { pluginMetadataSnapshot?: Pick; /** Isolate known non-Gateway owners and retain unchanged last-known-good values when possible. */ allowUnavailableSecretOwners?: boolean; + /** Ref keys whose owners must become cold rather than retain last-known-good values. */ + forceColdRefKeys?: ReadonlySet; /** Test override for discovered loadable plugins and their origins. */ loadablePluginOrigins?: ReadonlyMap; }): Promise { @@ -312,6 +314,7 @@ export async function prepareSecretsRuntimeSnapshot(params: { cache: context.cache, manifestRegistry: context.manifestRegistry, }, + forceColdRefKeys: params.forceColdRefKeys, }) : { degradedOwners: [], resolvedValues: new Map() }; const assignmentSecretOwners = listSecretAssignmentOwners( @@ -325,6 +328,7 @@ export async function prepareSecretsRuntimeSnapshot(params: { resolvedConfig, context, allowUnavailableSecretOwners: params.allowUnavailableSecretOwners, + forceColdRefKeys: params.forceColdRefKeys, }) : { metadata: createEmptyRuntimeWebToolsMetadata(), diff --git a/src/secrets/store/dotenv.ts b/src/secrets/store/dotenv.ts new file mode 100644 index 000000000000..6c52c3a5bad4 --- /dev/null +++ b/src/secrets/store/dotenv.ts @@ -0,0 +1,26 @@ +// Browser-safe subset of dotenv v17's parser contract; the package entrypoint +// also imports Node-only config/decryption helpers that cannot ship in Control UI. +const DOTENV_ASSIGNMENT_RE = + /^\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?$/gmu; + +/** Parse dotenv assignments, including quoted multi-line values, without Node built-ins. */ +export function parseSecretStoreDotEnvText(raw: string): Record { + const entries: Record = {}; + const normalized = raw.replace(/\r\n?/gu, "\n"); + DOTENV_ASSIGNMENT_RE.lastIndex = 0; + let match: RegExpExecArray | null; + while ((match = DOTENV_ASSIGNMENT_RE.exec(normalized)) !== null) { + const key = match[1]; + if (!key) { + continue; + } + let value = (match[2] ?? "").trim(); + const quote = value[0]; + value = value.replace(/^(['"`])([\s\S]*)\1$/gmu, "$2"); + if (quote === '"') { + value = value.replace(/\\n/gu, "\n").replace(/\\r/gu, "\r"); + } + entries[key] = value; + } + return entries; +} diff --git a/ui/src/app-navigation-groups.test.ts b/ui/src/app-navigation-groups.test.ts index 7dba96639580..93d2694aec0a 100644 --- a/ui/src/app-navigation-groups.test.ts +++ b/ui/src/app-navigation-groups.test.ts @@ -47,6 +47,7 @@ describe("sidebar entries", () => { "custodian", "channels", "security", + "secrets", "notifications", "advanced", ] as const) { @@ -63,6 +64,13 @@ describe("sidebar entries", () => { expect(system?.routes.slice(-2)).toEqual(["updates", "about"]); }); + it("places team secrets between Privacy & Security and Approvals", () => { + const security = SETTINGS_NAVIGATION_GROUPS.find( + (group) => group.labelKey === "nav.settingsGroupSecurity", + ); + expect(security?.routes).toEqual(["security", "secrets", "approvals"]); + }); + it("keeps model setup as a settings subpage without a sidebar entry", () => { expect(settingsRoutes).not.toContain("model-setup"); expect(isSettingsNavigationRoute("model-setup")).toBe(true); diff --git a/ui/src/app-navigation.test.ts b/ui/src/app-navigation.test.ts index cb9b781288f4..a01cd55dcaa8 100644 --- a/ui/src/app-navigation.test.ts +++ b/ui/src/app-navigation.test.ts @@ -127,6 +127,7 @@ describe("navigationIconForRoute", () => { "memory-import": "download", notifications: "bell", security: "shieldCheck", + secrets: "key", advanced: "fileCode", debug: "bug", logs: "scrollText", @@ -248,6 +249,7 @@ describe("titleForRoute", () => { "memory-import": "Import Memory", notifications: "Notifications", security: "Privacy & Security", + secrets: "Secrets", advanced: "Advanced", debug: "Debug", logs: "Logs", @@ -297,6 +299,7 @@ describe("subtitleForRoute", () => { "memory-import": "Bring Codex and Claude Code memory into an agent workspace.", notifications: "Browser push notifications from your gateway.", security: "Gateway auth, exec policy, tool profile, and approvals.", + secrets: "Secret values are hidden after saving. Env var values stay visible here.", advanced: "Every remaining config section, plus the raw file editor.", debug: "Snapshots, events, RPC.", logs: "Live gateway logs.", @@ -316,6 +319,7 @@ describe("pathForRoute", () => { expect(pathForRoute("plugins")).toBe("/settings/plugins"); expect(pathForRoute("approvals")).toBe("/settings/approvals"); expect(pathForRoute("labs")).toBe("/settings/labs"); + expect(pathForRoute("secrets")).toBe("/settings/secrets"); }); it("prepends base path", () => { @@ -543,6 +547,7 @@ describe("SIDEBAR_NAV_ROUTES", () => { "memory", "automation", "security", + "secrets", "approvals", "infrastructure", "advanced", diff --git a/ui/src/app-navigation.ts b/ui/src/app-navigation.ts index 1d73d0d92edf..d831ab4bf6b9 100644 --- a/ui/src/app-navigation.ts +++ b/ui/src/app-navigation.ts @@ -189,7 +189,7 @@ export const SETTINGS_NAVIGATION_GROUPS = [ }, { labelKey: "nav.settingsGroupSecurity", - routes: ["security", "approvals"], + routes: ["security", "secrets", "approvals"], }, { labelKey: "nav.settingsGroupSystem", @@ -258,6 +258,7 @@ const NAVIGATION_ICONS: NavigationItem = { "memory-import": "download", notifications: "bell", security: "shieldCheck", + secrets: "key", advanced: "fileCode", debug: "bug", logs: "scrollText", @@ -377,6 +378,7 @@ const NAVIGATION_COPY: Record { expect(routeIdFromPath("/settings/updates")).toBe("updates"); }); + it("registers the Secrets settings path", () => { + expect(pathForRoute("secrets")).toBe("/settings/secrets"); + expect(routeIdFromPath("/settings/secrets")).toBe("secrets"); + }); + it.each(DYNAMIC_STARTUP_CASES)( "loads the $label once while publishing its real location", async ({ routeId, location: initialLocation }) => { diff --git a/ui/src/app-route-paths.ts b/ui/src/app-route-paths.ts index 42e5bdd7da9b..f8517d25736c 100644 --- a/ui/src/app-route-paths.ts +++ b/ui/src/app-route-paths.ts @@ -37,6 +37,7 @@ const APP_ROUTE_DEFINITIONS = { lobsterdex: { path: "/settings/lobsterdex", aliases: ["/lobsterdex"] }, notifications: { path: "/settings/notifications" }, security: { path: "/settings/security" }, + secrets: { path: "/settings/secrets" }, advanced: { path: "/settings/advanced" }, approvals: { path: "/settings/approvals" }, automation: { path: "/settings/automation", aliases: ["/automation"] }, diff --git a/ui/src/app-routes.ts b/ui/src/app-routes.ts index 62efd60b237a..f442e09d16c9 100644 --- a/ui/src/app-routes.ts +++ b/ui/src/app-routes.ts @@ -48,6 +48,7 @@ import { page as newSessionPage } from "./pages/new-session/route.ts"; import { page as pluginPage } from "./pages/plugin/route.ts"; import { page as pluginsPage } from "./pages/plugins/route.ts"; import { page as profilePage } from "./pages/profile/route.ts"; +import { page as secretsPage } from "./pages/secrets/route.ts"; import { page as sessionsPage } from "./pages/sessions/route.ts"; import { page as skillWorkshopPage } from "./pages/skill-workshop/route.ts"; import { page as skillsPage } from "./pages/skills/route.ts"; @@ -94,6 +95,7 @@ const APP_ROUTE_TREE = [ workboardPage, worktreesPage, sessionsPage, + secretsPage, usagePage, debugPage, logsPage, diff --git a/ui/src/i18n/locales/en.ts b/ui/src/i18n/locales/en.ts index 18ca270a9fde..712d2fe18ab0 100644 --- a/ui/src/i18n/locales/en.ts +++ b/ui/src/i18n/locales/en.ts @@ -2015,6 +2015,7 @@ export const en: TranslationMap = { modelSetup: "Model Setup", memoryImport: "Import Memory", security: "Privacy & Security", + secrets: "Secrets", debug: "Debug", logs: "Logs", plugin: "Plugin", @@ -5465,6 +5466,31 @@ export const en: TranslationMap = { fa: "فارسی (Persian)", ru: "Русский (Russian)", }, + secretsStore: { + name: "Name", + value: "Value", + updated: "Last updated", + by: "{time} by {name}", + actions: "Actions", + add: "Add", + edit: "Edit", + bulk: "Bulk Add", + secret: "Secret", + hint: "Secret values are hidden after saving. Env var values stay visible here.", + required: "Enter a value.", + detect: "Auto-detect secrets", + detected: "{count} secrets detected", + detectedOne: "{count} secret detected", + unavail: "Gateway/admin required.", + badName: "Use SERVICE_API_KEY.", + tooLarge: "Max 64 KiB.", + saved: "Saved {name}.", + savedMany: "Saved {count} entries.", + warnings: "{count} runtime warnings.", + partial: "{saved}/{total}: {error}", + confirmDelete: "Delete {name}?", + deleted: "Deleted {name}.", + }, cron: { adminRequired: "Browsing only. Automation changes require operator.admin access.", tabs: { diff --git a/ui/src/lib/secrets-store/index.test.ts b/ui/src/lib/secrets-store/index.test.ts new file mode 100644 index 000000000000..29707c2ab317 --- /dev/null +++ b/ui/src/lib/secrets-store/index.test.ts @@ -0,0 +1,86 @@ +import { describe, expect, it, vi } from "vitest"; +import type { GatewayBrowserClient } from "../../api/gateway.ts"; +import { + bulkSetSecretsStoreEntries, + createInitialSecretsStoreState, + parseSecretsStoreBulkInput, + setSecretsStoreEntry, +} from "./index.ts"; + +function clientWithResponses(responses: unknown[]) { + const request = vi.fn(async (_method: string, _params?: unknown) => responses.shift()); + return { client: { request } as unknown as GatewayBrowserClient, request }; +} + +describe("secrets store state", () => { + it("parses quoted multiline dotenv values and classifies sensitive names", () => { + const parsed = parseSecretsStoreBulkInput( + 'SERVICE_URL="https://service.test?a=b"\nSERVICE_API_KEY="line one\nline two"\nPLAIN=value', + true, + ); + expect(parsed).toEqual({ + entries: [ + { name: "SERVICE_URL", value: "https://service.test?a=b", kind: "env" }, + { name: "SERVICE_API_KEY", value: "line one\nline two", kind: "secret" }, + { name: "PLAIN", value: "value", kind: "env" }, + ], + invalidNames: [], + }); + expect(parseSecretsStoreBulkInput("SERVICE_API_KEY=value", false).entries[0]?.kind).toBe("env"); + }); + + it("reloads the canonical list after a set", async () => { + const snapshot = { + entries: [ + { + name: "SERVICE_URL", + kind: "env", + value: "https://service.test", + scopeKind: "team", + scopeId: "", + createdAtMs: 1, + updatedAtMs: 2, + updatedBy: "Control UI", + }, + ], + }; + const { client, request } = clientWithResponses([{ ok: true, reloaded: false }, snapshot]); + const state = createInitialSecretsStoreState({ client, connected: true }); + + await setSecretsStoreEntry(state, { + name: "SERVICE_URL", + value: "https://service.test", + kind: "env", + }); + + expect(request.mock.calls.map(([method]) => method)).toEqual([ + "secrets.store.set", + "secrets.store.list", + ]); + expect(state.entries).toEqual(snapshot.entries); + expect(state.error).toBeNull(); + }); + + it("reloads after every sequential bulk set", async () => { + const { client, request } = clientWithResponses([ + { ok: true, reloaded: false }, + { entries: [] }, + { ok: true, reloaded: false }, + { entries: [] }, + ]); + const state = createInitialSecretsStoreState({ client, connected: true }); + + expect( + await bulkSetSecretsStoreEntries(state, [ + { name: "ONE", value: "1", kind: "env" }, + { name: "TWO_TOKEN", value: "2", kind: "secret" }, + ]), + ).toEqual({ saved: 2, warningCount: 0 }); + expect(request.mock.calls.map(([method]) => method)).toEqual([ + "secrets.store.set", + "secrets.store.list", + "secrets.store.set", + "secrets.store.list", + ]); + }); +}); diff --git a/ui/src/lib/secrets-store/index.ts b/ui/src/lib/secrets-store/index.ts new file mode 100644 index 000000000000..a76e7c8fd2e0 --- /dev/null +++ b/ui/src/lib/secrets-store/index.ts @@ -0,0 +1,193 @@ +import type { + SecretStoreEntry, + SecretsStoreListResult, + SecretsStoreMutationResult, +} from "../../../../packages/gateway-protocol/src/index.js"; +import { ENV_SECRET_REF_ID_RE } from "../../../../src/config/types.secrets.js"; +import { isSensitiveEnvName } from "../../../../src/secrets/secret-env-name.js"; +import { parseSecretStoreDotEnvText } from "../../../../src/secrets/store/dotenv.js"; +import type { GatewayBrowserClient } from "../../api/gateway.ts"; +import { t } from "../../i18n/index.ts"; +import { formatUiError } from "../format-error.ts"; + +export type SecretsStoreDraft = { + name: string; + value: string; + kind: "secret" | "env"; +}; + +type SecretsStoreBulkEntry = SecretsStoreDraft; + +export type SecretsStoreState = { + client: GatewayBrowserClient | null; + connected: boolean; + entries: SecretStoreEntry[]; + loaded: boolean; + loading: boolean; + busy: boolean; + error: string | null; +}; + +export function createInitialSecretsStoreState( + snapshot: Partial> = {}, +): SecretsStoreState { + return { + client: snapshot.client ?? null, + connected: snapshot.connected ?? false, + entries: [], + loaded: false, + loading: false, + busy: false, + error: null, + }; +} + +async function requestSnapshot(client: GatewayBrowserClient): Promise { + const result = await client.request("secrets.store.list", {}); + return result.entries; +} + +export async function loadSecretsStore(state: SecretsStoreState): Promise { + const client = state.client; + if (!client || !state.connected || state.loading) { + return false; + } + state.loading = true; + state.error = null; + try { + const entries = await requestSnapshot(client); + if (state.client === client && state.connected) { + state.entries = entries; + state.loaded = true; + return true; + } + return false; + } catch (error) { + if (state.client === client) { + state.error = formatUiError(error); + } + return false; + } finally { + if (state.client === client) { + state.loading = false; + } + } +} + +async function mutateAndReload( + state: SecretsStoreState, + mutate: (client: GatewayBrowserClient) => Promise, +): Promise { + const client = state.client; + if (!client || !state.connected || state.busy) { + return null; + } + state.busy = true; + state.error = null; + let result: SecretsStoreMutationResult | null = null; + let mutationError: unknown; + try { + result = await mutate(client); + } catch (error) { + mutationError = error; + } + try { + const entries = await requestSnapshot(client); + if (state.client === client && state.connected) { + state.entries = entries; + state.loaded = true; + } + } catch (error) { + mutationError ??= error; + } finally { + if (state.client === client) { + state.busy = false; + state.error = mutationError ? formatUiError(mutationError) : null; + } + } + return mutationError ? null : result; +} + +export function setSecretsStoreEntry( + state: SecretsStoreState, + draft: SecretsStoreDraft, +): Promise { + return mutateAndReload(state, (client) => + client.request("secrets.store.set", draft), + ); +} + +export function deleteSecretsStoreEntry( + state: SecretsStoreState, + name: string, +): Promise { + return mutateAndReload(state, (client) => + client.request("secrets.store.delete", { name }), + ); +} + +export function parseSecretsStoreBulkInput( + raw: string, + autoDetectSecrets: boolean, +): { entries: SecretsStoreBulkEntry[]; invalidNames: string[] } { + const parsed = parseSecretStoreDotEnvText(raw); + const invalidNames = Object.keys(parsed).filter((name) => !ENV_SECRET_REF_ID_RE.test(name)); + const entries: SecretsStoreBulkEntry[] = Object.entries(parsed).map(([name, value]) => ({ + name, + value, + kind: autoDetectSecrets && isSensitiveEnvName(name) ? "secret" : "env", + })); + return { entries, invalidNames }; +} + +export async function bulkSetSecretsStoreEntries( + state: SecretsStoreState, + entries: readonly SecretsStoreBulkEntry[], +): Promise<{ saved: number; warningCount: number } | null> { + const client = state.client; + if (!client || !state.connected || state.busy || entries.length === 0) { + return null; + } + state.busy = true; + state.error = null; + let saved = 0; + let warningCount = 0; + let mutationError: unknown; + try { + for (const entry of entries) { + const result = await client.request("secrets.store.set", entry); + saved += 1; + warningCount = Math.max(warningCount, result.warningCount ?? 0); + const snapshot = await requestSnapshot(client); + if (state.client === client && state.connected) { + state.entries = snapshot; + state.loaded = true; + } + } + } catch (error) { + mutationError = new Error( + t("secretsStore.partial", { + saved: String(saved), + total: String(entries.length), + error: formatUiError(error), + }), + ); + } + try { + if (mutationError) { + const snapshot = await requestSnapshot(client); + if (state.client === client && state.connected) { + state.entries = snapshot; + state.loaded = true; + } + } + } catch (error) { + mutationError ??= error; + } finally { + if (state.client === client) { + state.busy = false; + state.error = mutationError ? formatUiError(mutationError) : null; + } + } + return mutationError ? null : { saved, warningCount }; +} diff --git a/ui/src/pages/config/settings-search.test.ts b/ui/src/pages/config/settings-search.test.ts index db40c394681d..711c454348d9 100644 --- a/ui/src/pages/config/settings-search.test.ts +++ b/ui/src/pages/config/settings-search.test.ts @@ -144,6 +144,7 @@ describe("findSettingsSearchBlocks", () => { }); expect(matches).toEqual([ + expect.objectContaining({ routeId: "secrets", label: "Secrets" }), expect.objectContaining({ routeId: "advanced", search: "?section=secrets&advanced=1", @@ -302,6 +303,19 @@ describe("findSettingsSearchBlocks", () => { ]); }); + it("routes team secret-store searches to the dedicated page", () => { + const matches = findSettingsSearchBlocks({ + query: "team store", + schema: null, + value: null, + uiHints: {}, + }); + + expect(matches).toEqual([ + expect.objectContaining({ routeId: "secrets", label: "Secrets", hash: "" }), + ]); + }); + it("routes profile statistics searches to Usage", () => { const matches = findSettingsSearchBlocks({ query: "usage statistics", diff --git a/ui/src/pages/config/settings-targets.test.ts b/ui/src/pages/config/settings-targets.test.ts index cd940dbb9f86..aac431ee8c65 100644 --- a/ui/src/pages/config/settings-targets.test.ts +++ b/ui/src/pages/config/settings-targets.test.ts @@ -28,6 +28,7 @@ describe("settings search target manifest", () => { ).toEqual([ ["channels", "/settings/channels", "", ""], ["security", "/settings/security", "", ""], + ["secrets", "/settings/secrets", "", ""], ["system", "/settings/connection", "", "#settings-connection-host"], ["personal", "/settings/profile", "", "#settings-profile-identity"], ["modelBehavior", "/settings/model-providers", "", "#settings-model-behavior"], diff --git a/ui/src/pages/config/settings-targets.ts b/ui/src/pages/config/settings-targets.ts index d68b9aa6c5f3..b12f15ebd5b2 100644 --- a/ui/src/pages/config/settings-targets.ts +++ b/ui/src/pages/config/settings-targets.ts @@ -59,6 +59,13 @@ export const SETTINGS_SEARCH_TARGETS = { "quickSettings.security.toolProfile", ], }, + secrets: { + routeId: "secrets", + labelKey: "tabs.secrets", + hash: "", + searchKeys: [], + aliases: "env team store", + }, system: { routeId: "connection", labelKey: "quickSettings.system.gatewayHost", diff --git a/ui/src/pages/secrets/route.ts b/ui/src/pages/secrets/route.ts new file mode 100644 index 000000000000..cccaeb36eb0f --- /dev/null +++ b/ui/src/pages/secrets/route.ts @@ -0,0 +1,12 @@ +import { definePage } from "@openclaw/uirouter"; +import { html } from "lit"; +import { routePageSpec } from "../../app-route-paths.ts"; + +export const page = definePage({ + ...routePageSpec("secrets"), + component: () => + import("./secrets-page.ts").then(() => ({ + header: true, + render: () => html``, + })), +}); diff --git a/ui/src/pages/secrets/secrets-page.ts b/ui/src/pages/secrets/secrets-page.ts new file mode 100644 index 000000000000..9c66bdfb7335 --- /dev/null +++ b/ui/src/pages/secrets/secrets-page.ts @@ -0,0 +1,336 @@ +import { consume } from "@lit/context"; +import { html } from "lit"; +import { state } from "lit/decorators.js"; +import { ENV_SECRET_REF_ID_RE } from "../../../../src/config/types.secrets.js"; +import { isSensitiveEnvName } from "../../../../src/secrets/secret-env-name.js"; +import { titleForRoute } from "../../app-navigation.ts"; +import { applicationContext, type ApplicationContext } from "../../app/context.ts"; +import { showConfirmDialog } from "../../components/confirm-dialog.ts"; +import { renderSettingsWorkspace } from "../../components/settings-workspace.ts"; +import { t } from "../../i18n/index.ts"; +import { canCallGatewayMethod, isGatewayMethodAdvertised } from "../../lib/gateway-methods.ts"; +import { + bulkSetSecretsStoreEntries, + createInitialSecretsStoreState, + deleteSecretsStoreEntry, + loadSecretsStore, + parseSecretsStoreBulkInput, + setSecretsStoreEntry, + type SecretsStoreDraft, + type SecretsStoreState, +} from "../../lib/secrets-store/index.ts"; +import { GatewayPageController } from "../../lit/gateway-page-controller.ts"; +import { OpenClawLightDomElement } from "../../lit/openclaw-element.ts"; +import { renderSecretsStore, type SecretsDialogMode } from "./view.ts"; + +const MAX_VALUE_BYTES = 64 * 1024; + +class SecretsPage extends OpenClawLightDomElement { + @consume({ context: applicationContext, subscribe: true }) + private context!: ApplicationContext; + + @state() private store = createInitialSecretsStoreState(); + @state() private dialogMode: SecretsDialogMode = null; + @state() private draft: SecretsStoreDraft = { name: "", value: "", kind: "env" }; + @state() private secretKindOverridden = false; + @state() private bulkOpen = false; + @state() private bulkRaw = ""; + @state() private bulkAutoDetect = true; + @state() private formError: string | null = null; + @state() private notice: string | null = null; + + private readonly gateway = new GatewayPageController(this, { + getGateway: () => this.context?.gateway, + invalidateRequests: (change) => this.resetGatewayState(change.snapshot), + onSnapshot: (change) => { + if (change.initial) { + this.resetGatewayState(change.snapshot); + } + }, + ensureInitialData: () => this.ensureInitialData(), + }); + + private resetGatewayState(snapshot?: ApplicationContext["gateway"]["snapshot"]) { + this.store = createInitialSecretsStoreState({ + client: snapshot?.client ?? null, + connected: snapshot?.phase === "connected", + }); + this.dialogMode = null; + this.bulkOpen = false; + this.formError = null; + this.notice = null; + } + + private get canList(): boolean { + return this.canCall("secrets.store.list"); + } + + private get canSet(): boolean { + return this.canCall("secrets.store.set"); + } + + private get canDelete(): boolean { + return this.canCall("secrets.store.delete"); + } + + private canCall(method: "secrets.store.list" | "secrets.store.set" | "secrets.store.delete") { + return ( + isGatewayMethodAdvertised(this.gateway.snapshot ?? {}, method) === true && + canCallGatewayMethod(this.gateway.snapshot, method, "operator.admin") + ); + } + + private ensureInitialData() { + if (this.canList && !this.store.loaded && !this.store.loading) { + void this.runStoreTask((store) => loadSecretsStore(store)); + } + } + + private async runStoreTask(task: (store: SecretsStoreState) => Promise): Promise { + const store = this.store; + try { + const result = task(store); + this.requestUpdate(); + return await result; + } finally { + if (this.store === store) { + this.requestUpdate(); + } + } + } + + private refresh() { + if (!this.canList) { + return; + } + void this.runStoreTask((store) => loadSecretsStore(store)); + } + + private openAdd() { + if (!this.canSet) { + return; + } + this.notice = null; + this.formError = null; + this.secretKindOverridden = false; + this.draft = { name: "", value: "", kind: "env" }; + this.dialogMode = "add"; + } + + private openEdit(entry: (typeof this.store.entries)[number]) { + if (!this.canSet) { + return; + } + this.notice = null; + this.formError = null; + this.secretKindOverridden = true; + this.draft = { + name: entry.name, + value: entry.kind === "env" ? entry.value : "", + kind: entry.kind, + }; + this.dialogMode = "edit"; + } + + private closeDialog() { + if (!this.store.busy) { + this.dialogMode = null; + this.formError = null; + } + } + + private patchDraft(patch: Partial) { + this.draft = { ...this.draft, ...patch }; + this.formError = null; + } + + private changeDraftName(name: string) { + const normalized = name.toUpperCase(); + this.patchDraft({ + name: normalized, + ...(!this.secretKindOverridden + ? { kind: isSensitiveEnvName(normalized) ? ("secret" as const) : ("env" as const) } + : {}), + }); + } + + private validateDraft(): string | null { + if (!ENV_SECRET_REF_ID_RE.test(this.draft.name)) { + return t("secretsStore.badName"); + } + if ( + this.dialogMode === "edit" && + this.draft.kind === "secret" && + this.draft.value.length === 0 + ) { + return t("secretsStore.required"); + } + if (new TextEncoder().encode(this.draft.value).byteLength > MAX_VALUE_BYTES) { + return t("secretsStore.tooLarge"); + } + return null; + } + + private submitDraft() { + if (!this.canSet || !this.dialogMode) { + return; + } + const error = this.validateDraft(); + if (error) { + this.formError = error; + return; + } + const draft = { ...this.draft }; + void this.runStoreTask(async (store) => { + const result = await setSecretsStoreEntry(store, draft); + if (this.store !== store) { + return; + } + if (!result) { + this.formError = store.error; + return; + } + this.dialogMode = null; + this.formError = null; + this.notice = result.warningCount + ? `${t("secretsStore.saved", { name: draft.name })} ${t("secretsStore.warnings", { count: String(result.warningCount) })}` + : t("secretsStore.saved", { name: draft.name }); + }); + } + + private openBulk() { + if (!this.canSet) { + return; + } + this.notice = null; + this.formError = null; + this.bulkRaw = ""; + this.bulkAutoDetect = true; + this.bulkOpen = true; + } + + private closeBulk() { + if (!this.store.busy) { + this.bulkOpen = false; + this.formError = null; + } + } + + private get bulkParsed() { + return parseSecretsStoreBulkInput(this.bulkRaw, this.bulkAutoDetect); + } + + private submitBulk() { + if (!this.canSet || !this.bulkOpen) { + return; + } + const parsed = this.bulkParsed; + if (parsed.invalidNames.length > 0) { + this.formError = `${t("secretsStore.badName")} ${parsed.invalidNames.join(", ")}`; + return; + } + if (parsed.entries.length === 0) { + this.formError = t("secretsStore.required"); + return; + } + const oversized = parsed.entries.find( + (entry) => new TextEncoder().encode(entry.value).byteLength > MAX_VALUE_BYTES, + ); + if (oversized) { + this.formError = `${oversized.name}: ${t("secretsStore.tooLarge")}`; + return; + } + void this.runStoreTask(async (store) => { + const result = await bulkSetSecretsStoreEntries(store, parsed.entries); + if (this.store !== store) { + return; + } + if (!result) { + this.formError = store.error; + return; + } + this.bulkOpen = false; + this.formError = null; + this.notice = result.warningCount + ? `${t("secretsStore.savedMany", { count: String(result.saved) })} ${t("secretsStore.warnings", { count: String(result.warningCount) })}` + : t("secretsStore.savedMany", { count: String(result.saved) }); + }); + } + + private async removeEntry(entry: (typeof this.store.entries)[number]) { + if ( + !this.canDelete || + !(await showConfirmDialog({ + title: t("common.delete"), + message: t("secretsStore.confirmDelete", { name: entry.name }), + confirmLabel: t("common.delete"), + danger: true, + })) + ) { + return; + } + this.notice = null; + await this.runStoreTask(async (store) => { + const result = await deleteSecretsStoreEntry(store, entry.name); + if (result && this.store === store) { + this.notice = t("secretsStore.deleted", { name: entry.name }); + } + }); + } + + override render() { + const parsed = this.bulkParsed; + const body = renderSecretsStore({ + entries: this.store.entries, + loading: this.store.loading, + busy: this.store.busy, + error: this.store.error, + notice: this.notice, + canList: this.canList, + canSet: this.canSet, + canDelete: this.canDelete, + dialogMode: this.dialogMode, + draft: this.draft, + formError: this.formError, + bulkOpen: this.bulkOpen, + bulkRaw: this.bulkRaw, + bulkAutoDetect: this.bulkAutoDetect, + bulkSecretCount: parsed.entries.filter((entry) => entry.kind === "secret").length, + bulkEntryCount: parsed.entries.length, + bulkInvalidNames: parsed.invalidNames, + onRefresh: () => this.refresh(), + onOpenAdd: () => this.openAdd(), + onOpenEdit: (entry) => this.openEdit(entry), + onCloseDialog: () => this.closeDialog(), + onDraftNameChange: (name) => this.changeDraftName(name), + onDraftValueChange: (value) => this.patchDraft({ value }), + onDraftSecretChange: (secret) => { + this.secretKindOverridden = true; + this.patchDraft({ kind: secret ? "secret" : "env" }); + }, + onSubmitDraft: () => this.submitDraft(), + onOpenBulk: () => this.openBulk(), + onCloseBulk: () => this.closeBulk(), + onBulkRawChange: (raw) => { + this.bulkRaw = raw; + this.formError = null; + }, + onBulkAutoDetectChange: (enabled) => { + this.bulkAutoDetect = enabled; + this.formError = null; + }, + onSubmitBulk: () => this.submitBulk(), + onDelete: (entry) => void this.removeEntry(entry), + }); + return html` +
+
${titleForRoute("secrets")}
+
+ ${renderSettingsWorkspace(body)} + `; + } +} + +if (!customElements.get("openclaw-secrets-page")) { + customElements.define("openclaw-secrets-page", SecretsPage); +} diff --git a/ui/src/pages/secrets/secrets.e2e.test.ts b/ui/src/pages/secrets/secrets.e2e.test.ts new file mode 100644 index 000000000000..e4e87799bbf6 --- /dev/null +++ b/ui/src/pages/secrets/secrets.e2e.test.ts @@ -0,0 +1,184 @@ +import { mkdir } from "node:fs/promises"; +import path from "node:path"; +import type { Page } from "playwright"; +import { expect, it } from "vitest"; +import type { SecretStoreEntry } from "../../../../packages/gateway-protocol/src/index.js"; +import { createControlUiE2eSuite } from "../../e2e/control-ui-e2e-suite.test-support.ts"; +import { installMockGateway } from "../../test-helpers/control-ui-e2e.ts"; + +const suite = createControlUiE2eSuite({ + name: "Control UI team secrets mocked Gateway E2E", + startServerBeforeBrowser: true, + unavailableMessage: (executablePath) => + `Playwright Chromium is not installed or cannot start at ${executablePath}. Run \`pnpm --dir ui exec playwright install --with-deps chromium\`.`, +}); + +const captureUiProofEnabled = process.env.OPENCLAW_CAPTURE_UI_PROOF === "1"; +const proofDir = path.join(process.cwd(), ".artifacts", "control-ui-e2e", "secrets-store"); + +const envEntry: SecretStoreEntry = { + name: "SERVICE_URL", + kind: "env", + value: "https://service.test", + scopeKind: "team", + scopeId: "", + createdAtMs: 1_786_352_400_000, + updatedAtMs: 1_786_352_400_000, + updatedBy: "E2E Operator", +}; + +const secretEntry: SecretStoreEntry = { + name: "SERVICE_API_KEY", + kind: "secret", + scopeKind: "team", + scopeId: "", + createdAtMs: 1_786_352_400_000, + updatedAtMs: 1_786_352_400_000, + updatedBy: "E2E Operator", +}; + +const bulkEnvEntry: SecretStoreEntry = { + ...envEntry, + name: "BULK_URL", + value: "https://bulk.test", +}; + +const bulkSecretEntry: SecretStoreEntry = { + ...secretEntry, + name: "BULK_PRIVATE_KEY", +}; + +async function capture(page: Page, fileName: string) { + if (!captureUiProofEnabled) { + return; + } + await mkdir(proofDir, { recursive: true }); + await page.screenshot({ + animations: "disabled", + fullPage: true, + path: path.join(proofDir, fileName), + }); +} + +async function tableBodyContrast(page: Page): Promise { + return await page + .locator(".secrets-store__value") + .first() + .evaluate((element) => { + const parse = (value: string) => + (value.match(/[\d.]+/gu) ?? []).slice(0, 3).map((channel) => Number(channel) / 255); + const luminance = (channels: number[]) => + channels.reduce((sum, channel, index) => { + const linear = channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4; + return sum + linear * ([0.2126, 0.7152, 0.0722][index] ?? 0); + }, 0); + const group = element.closest(".settings-group"); + if (!group) { + throw new Error("Missing settings group for contrast measurement"); + } + const foreground = luminance(parse(getComputedStyle(element).color)); + const background = luminance(parse(getComputedStyle(group).backgroundColor)); + return (Math.max(foreground, background) + 0.05) / (Math.min(foreground, background) + 0.05); + }); +} + +suite.define(() => { + it("adds env and secret values, bulk imports, and deletes without revealing secrets", async () => { + await suite.withPage( + { + colorScheme: "dark", + locale: "en-US", + serviceWorkers: "block", + viewport: { height: 900, width: 1440 }, + }, + async ({ page }) => { + const gateway = await installMockGateway(page, { + featureMethods: ["secrets.store.list", "secrets.store.set", "secrets.store.delete"], + methodResponses: { + "secrets.store.list": { + sequence: [ + { entries: [] }, + { entries: [envEntry] }, + { entries: [envEntry, secretEntry] }, + { entries: [envEntry, secretEntry, bulkSecretEntry] }, + { entries: [envEntry, secretEntry, bulkSecretEntry, bulkEnvEntry] }, + { entries: [envEntry, secretEntry, bulkSecretEntry] }, + ], + }, + "secrets.store.set": { + sequence: [ + { ok: true, reloaded: false }, + { ok: true, reloaded: true, warningCount: 0 }, + { ok: true, reloaded: false }, + { ok: true, reloaded: false }, + ], + }, + "secrets.store.delete": { ok: true, reloaded: false }, + }, + }); + + await page.goto(`${suite.server.baseUrl}settings/secrets`); + await page.getByRole("heading", { name: "Secrets" }).waitFor(); + + await page.getByRole("button", { name: "Add", exact: true }).click(); + const addDialog = page.locator('openclaw-modal-dialog[label="Add"]'); + await addDialog.getByLabel("Name", { exact: true }).fill("SERVICE_URL"); + await addDialog.getByLabel("Value", { exact: true }).fill("https://service.test"); + await capture(page, "02-add-dialog.png"); + await addDialog.getByRole("button", { name: "Save", exact: true }).click(); + await page.getByRole("status").getByText("Saved SERVICE_URL.").waitFor(); + + await page.getByRole("button", { name: "Add", exact: true }).click(); + const secretDialog = page.locator('openclaw-modal-dialog[label="Add"]'); + await secretDialog.getByLabel("Name", { exact: true }).fill("SERVICE_API_KEY"); + expect(await secretDialog.locator('input[type="checkbox"]').isChecked()).toBe(true); + await secretDialog.getByLabel("Value", { exact: true }).fill("super-secret-material"); + await secretDialog.getByRole("button", { name: "Save", exact: true }).click(); + await page.getByRole("status").getByText("Saved SERVICE_API_KEY.").waitFor(); + expect(await page.content()).not.toContain("super-secret-material"); + + await page.getByRole("button", { name: "Bulk Add", exact: true }).click(); + const bulkDialog = page.locator('openclaw-modal-dialog[label="Bulk Add"]'); + await bulkDialog + .getByRole("textbox", { name: "Value", exact: true }) + .fill('BULK_PRIVATE_KEY="line one\nline two"\nBULK_URL=https://bulk.test'); + await bulkDialog.getByText("1 secret detected").waitFor(); + await capture(page, "03-bulk-add-dialog.png"); + await bulkDialog.getByRole("button", { name: "Save", exact: true }).click(); + await page.getByRole("status").getByText("Saved 2 entries.").waitFor(); + + const bulkRow = page.getByRole("row", { name: /BULK_URL/u }); + await bulkRow.getByRole("button", { name: "Actions: BULK_URL" }).click(); + await bulkRow.locator('wa-dropdown-item[value="delete"]').click(); + const confirm = page.locator('openclaw-modal-dialog[label="Delete"]'); + await confirm.getByRole("button", { name: "Delete", exact: true }).click(); + await page.getByRole("status").getByText("Deleted BULK_URL.").waitFor(); + expect(await page.getByRole("row", { name: /BULK_URL/u }).count()).toBe(0); + + expect(await gateway.getRequests("secrets.store.set")).toHaveLength(4); + expect(await gateway.getRequests("secrets.store.delete")).toHaveLength(1); + expect(await page.content()).not.toContain("super-secret-material"); + expect(await tableBodyContrast(page)).toBeGreaterThanOrEqual(9.5); + await capture(page, "01-populated-dark.png"); + }, + ); + }); + + it("keeps optional store actions hidden when the Gateway omits method discovery", async () => { + await suite.withPage({}, async ({ page }) => { + const gateway = await installMockGateway(page, { + omitFeatureMethods: true, + methodResponses: { + "secrets.store.list": { entries: [] }, + }, + }); + + await page.goto(`${suite.server.baseUrl}settings/secrets`); + await page.getByRole("heading", { name: "Secrets" }).waitFor(); + await page.getByText(/Gateway\/admin required/u).waitFor(); + expect(await page.getByRole("button", { name: "Add", exact: true }).count()).toBe(0); + expect(await page.getByRole("button", { name: "Bulk Add", exact: true }).count()).toBe(0); + expect(await gateway.getRequests("secrets.store.list")).toHaveLength(0); + }); + }); +}); diff --git a/ui/src/pages/secrets/view.test.ts b/ui/src/pages/secrets/view.test.ts new file mode 100644 index 000000000000..80969b293876 --- /dev/null +++ b/ui/src/pages/secrets/view.test.ts @@ -0,0 +1,97 @@ +import { render } from "lit"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import type { SecretStoreEntry } from "../../../../packages/gateway-protocol/src/index.js"; +import { renderSecretsStore } from "./view.ts"; + +type SecretsStoreViewProps = Parameters[0]; + +const containers: HTMLElement[] = []; + +afterEach(() => { + for (const container of containers.splice(0)) { + render(null, container); + container.remove(); + } +}); + +function mount( + entries: SecretStoreEntry[], + overrides: Partial = {}, +): HTMLElement { + const container = document.createElement("div"); + containers.push(container); + document.body.append(container); + const noop = vi.fn(); + const props: SecretsStoreViewProps = { + entries, + loading: false, + busy: false, + error: null, + notice: null, + canList: true, + canSet: true, + canDelete: true, + dialogMode: null, + draft: { name: "", value: "", kind: "env" }, + formError: null, + bulkOpen: false, + bulkRaw: "", + bulkAutoDetect: true, + bulkSecretCount: 0, + bulkEntryCount: 0, + bulkInvalidNames: [], + onRefresh: noop, + onOpenAdd: noop, + onOpenEdit: noop, + onCloseDialog: noop, + onDraftNameChange: noop, + onDraftValueChange: noop, + onDraftSecretChange: noop, + onSubmitDraft: noop, + onOpenBulk: noop, + onCloseBulk: noop, + onBulkRawChange: noop, + onBulkAutoDetectChange: noop, + onSubmitBulk: noop, + onDelete: noop, + }; + render(renderSecretsStore({ ...props, ...overrides }), container); + return container; +} + +describe("secrets store view", () => { + it("never renders a secret value even when hostile input carries one", () => { + const secret = { + name: "SERVICE_API_KEY", + kind: "secret", + value: "must-never-render", + scopeKind: "team", + scopeId: "", + createdAtMs: 1, + updatedAtMs: 2, + updatedBy: "Operator", + } as unknown as SecretStoreEntry; + const env: SecretStoreEntry = { + name: "SERVICE_URL", + kind: "env", + value: "https://service.test", + scopeKind: "team", + scopeId: "", + createdAtMs: 1, + updatedAtMs: 2, + updatedBy: "Operator", + }; + const container = mount([secret, env]); + + expect(container.innerHTML).not.toContain("must-never-render"); + expect(container.textContent).toContain("••••••••"); + expect(container.textContent).toContain("https://service.test"); + }); + + it("hides mutation controls when the gateway does not advertise them", () => { + const container = mount([], { canSet: false, canDelete: false }); + + expect(container.querySelector("button")).toBeNull(); + expect(container.textContent).toContain("Secrets"); + }); +}); diff --git a/ui/src/pages/secrets/view.ts b/ui/src/pages/secrets/view.ts new file mode 100644 index 000000000000..a61c6c0cf708 --- /dev/null +++ b/ui/src/pages/secrets/view.ts @@ -0,0 +1,374 @@ +import { html, nothing, type TemplateResult } from "lit"; +import { repeat } from "lit/directives/repeat.js"; +import type { SecretStoreEntry } from "../../../../packages/gateway-protocol/src/index.js"; +import { icon } from "../../components/icons.ts"; +import "../../components/modal-dialog.ts"; +import { + renderDocsLink, + renderSettingsEmpty, + renderSettingsPage, + renderSettingsSection, +} from "../../components/settings-ui.ts"; +import "../../components/web-awesome.ts"; +import { i18n, t } from "../../i18n/index.ts"; +import { formatRelativeTimestamp } from "../../lib/format.ts"; +import type { SecretsStoreDraft } from "../../lib/secrets-store/index.ts"; +import "../../styles/secrets-store.css"; + +export type SecretsDialogMode = "add" | "edit" | null; + +type SecretsStoreViewProps = { + entries: SecretStoreEntry[]; + loading: boolean; + busy: boolean; + error: string | null; + notice: string | null; + canList: boolean; + canSet: boolean; + canDelete: boolean; + dialogMode: SecretsDialogMode; + draft: SecretsStoreDraft; + formError: string | null; + bulkOpen: boolean; + bulkRaw: string; + bulkAutoDetect: boolean; + bulkSecretCount: number; + bulkEntryCount: number; + bulkInvalidNames: readonly string[]; + onRefresh: () => void; + onOpenAdd: () => void; + onOpenEdit: (entry: SecretStoreEntry) => void; + onCloseDialog: () => void; + onDraftNameChange: (name: string) => void; + onDraftValueChange: (value: string) => void; + onDraftSecretChange: (secret: boolean) => void; + onSubmitDraft: () => void; + onOpenBulk: () => void; + onCloseBulk: () => void; + onBulkRawChange: (raw: string) => void; + onBulkAutoDetectChange: (enabled: boolean) => void; + onSubmitBulk: () => void; + onDelete: (entry: SecretStoreEntry) => void; +}; + +const DOCS_URL = "https://docs.openclaw.ai/gateway/secrets#shared-secret-store"; +const SECRET_MASK = "••••••••"; + +function updatedLabel(entry: SecretStoreEntry): string { + const relative = formatRelativeTimestamp(entry.updatedAtMs, { fallback: t("common.unknown") }); + return entry.updatedBy + ? t("secretsStore.by", { time: relative, name: entry.updatedBy }) + : relative; +} + +function renderEntryMenu(props: SecretsStoreViewProps, entry: SecretStoreEntry): TemplateResult { + if (!props.canSet && !props.canDelete) { + return html``; + } + return html` + ) => { + if (event.detail.item.value === "edit" && props.canSet) { + props.onOpenEdit(entry); + } else if (event.detail.item.value === "delete" && props.canDelete) { + props.onDelete(entry); + } + }} + > + + ${props.canSet + ? html`${t("secretsStore.edit")}` + : nothing} + ${props.canDelete + ? html`${t("common.delete")}` + : nothing} + + `; +} + +function renderTable(props: SecretsStoreViewProps): TemplateResult { + if (!props.canList) { + return renderSettingsEmpty(t("secretsStore.unavail")); + } + if (props.loading && !props.entries.length) { + return renderSettingsEmpty(t("common.loading")); + } + if (!props.entries.length) { + return html` +
+ ${renderSettingsEmpty(t("tabs.secrets"))} ${renderDocsLink(DOCS_URL, t("common.docs"))} +
+ `; + } + return html` +
+ + + + + + + + + + + ${repeat( + props.entries, + (entry) => entry.name, + (entry) => html` + + + + + + + `, + )} + +
${t("secretsStore.name")}${t("secretsStore.value")}${t("secretsStore.updated")} + ${t("secretsStore.actions")} +
${entry.name} + ${entry.kind === "env" ? entry.value : SECRET_MASK} + + + ${renderEntryMenu(props, entry)}
+
+ `; +} + +function renderEntryDialog(props: SecretsStoreViewProps): TemplateResult | typeof nothing { + if (!props.dialogMode) { + return nothing; + } + const editing = props.dialogMode === "edit"; + return html` + +
{ + event.preventDefault(); + props.onSubmitDraft(); + }} + > +
+

${editing ? t("secretsStore.edit") : t("secretsStore.add")}

+
+ + + + ${props.formError + ? html`` + : nothing} +
+ + +
+
+
+ `; +} + +function renderBulkDialog(props: SecretsStoreViewProps): TemplateResult | typeof nothing { + if (!props.bulkOpen) { + return nothing; + } + return html` + +
{ + event.preventDefault(); + props.onSubmitBulk(); + }} + > +
+

${t("secretsStore.bulk")}

+
+ +
+ ${t(props.bulkSecretCount === 1 ? "secretsStore.detectedOne" : "secretsStore.detected", { + count: String(props.bulkSecretCount), + })} +
+ + ${props.bulkInvalidNames.length + ? html`` + : nothing} + ${props.formError + ? html`` + : nothing} +
+ + +
+
+
+ `; +} + +export function renderSecretsStore(props: SecretsStoreViewProps): TemplateResult { + const actions = props.canSet + ? html` + + + ` + : undefined; + return html` + ${renderSettingsPage( + html` + ${props.error + ? html`` + : nothing} + ${props.notice + ? html`
+ ${props.notice} +
` + : nothing} + ${renderSettingsSection( + { + title: t("tabs.secrets"), + actions, + count: props.entries.length, + }, + renderTable(props), + )} + `, + { wide: true, intro: t("secretsStore.hint") }, + )} + ${renderEntryDialog(props)} ${renderBulkDialog(props)} + `; +} diff --git a/ui/src/styles/secrets-store.css b/ui/src/styles/secrets-store.css new file mode 100644 index 000000000000..a7262cf81da5 --- /dev/null +++ b/ui/src/styles/secrets-store.css @@ -0,0 +1,213 @@ +.secrets-store__message { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-3); + margin-bottom: var(--space-4); +} + +.secrets-store__table-wrap { + width: 100%; + overflow-x: auto; +} + +.secrets-store__table { + width: 100%; + border-collapse: collapse; + table-layout: fixed; +} + +.secrets-store__table th, +.secrets-store__table td { + padding: var(--space-3) var(--space-4); + border-bottom: 1px solid color-mix(in srgb, var(--border) 75%, transparent); + text-align: left; + vertical-align: middle; +} + +.secrets-store__table th { + color: var(--text); + font-size: var(--control-ui-text-xs); + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.secrets-store__table th:nth-child(1) { + width: 27%; +} + +.secrets-store__table th:nth-child(2) { + width: 38%; +} + +.secrets-store__table th:nth-child(3) { + width: 28%; +} + +.secrets-store__table tbody tr:last-child td { + border-bottom: 0; +} + +.secrets-store__table tbody tr:focus-visible { + outline: 2px solid var(--accent); + outline-offset: -2px; +} + +.secrets-store__name { + color: var(--text-strong); + font-size: var(--control-ui-text-sm); +} + +.secrets-store__value { + display: block; + overflow: hidden; + color: var(--text-strong); + font-family: var(--mono); + font-size: var(--control-ui-text-sm); + text-overflow: ellipsis; + white-space: nowrap; +} + +.secrets-store__value--secret { + color: var(--text-strong); + letter-spacing: 0.12em; +} + +.secrets-store__updated { + color: var(--text-strong); + font-size: var(--control-ui-text-sm); +} + +.secrets-store__actions-heading, +.secrets-store__actions-cell { + width: 44px; + text-align: right !important; +} + +.secrets-store__menu-trigger { + opacity: 0; + transition: opacity 120ms ease; +} + +.secrets-store__table tr:hover .secrets-store__menu-trigger, +.secrets-store__table tr:focus-within .secrets-store__menu-trigger, +.secrets-store__menu-trigger:focus-visible { + opacity: 1; +} + +.secrets-store__empty { + display: grid; + justify-items: center; + gap: var(--space-2); + padding: var(--space-8) var(--space-4); + text-align: center; +} + +.secrets-store__empty .settings-empty, +.secrets-store__empty p { + margin: 0; +} + +.secrets-store-dialog { + display: grid; + gap: var(--space-4); + max-height: min(760px, calc(100dvh - 48px)); + padding: var(--space-5); + overflow-y: auto; + border: 1px solid var(--border); + border-radius: var(--radius-lg); + background: var(--card); + color: var(--text); +} + +.secrets-store-dialog__header h2, +.secrets-store-dialog__header p { + margin: 0; +} + +.secrets-store-dialog__header h2 { + font-size: var(--control-ui-text-lg); +} + +.secrets-store-dialog__header p { + margin-top: var(--space-1); + color: var(--muted-strong); + font-size: var(--control-ui-text-sm); + line-height: 1.45; +} + +.secrets-store-field { + display: grid; + gap: var(--space-2); + color: var(--text); + font-size: var(--control-ui-text-sm); + font-weight: 600; +} + +.secrets-store-dialog__value { + min-height: 180px; + resize: vertical; + font-family: var(--mono); + line-height: 1.45; + white-space: pre; +} + +.secrets-store-dialog__bulk { + min-height: 260px; + resize: vertical; + font-family: var(--mono); + line-height: 1.45; + white-space: pre; +} + +.secrets-store-checkbox { + display: flex; + align-items: flex-start; + gap: var(--space-3); + color: var(--text); + cursor: var(--cursor-action); +} + +.secrets-store-checkbox input { + width: 18px; + height: 18px; + margin: 2px 0 0; + accent-color: var(--accent); +} + +.secrets-store-checkbox span { + display: grid; + gap: 2px; +} + +.secrets-store-checkbox small { + color: var(--muted-strong); + font-size: var(--control-ui-text-sm); + font-weight: 400; + line-height: 1.4; +} + +.secrets-store-bulk__summary { + display: flex; + justify-content: space-between; + color: var(--muted-strong); + font-size: var(--control-ui-text-sm); +} + +.secrets-store-dialog__actions { + display: flex; + justify-content: flex-end; + gap: var(--space-2); + padding-top: var(--space-2); +} + +@media (max-width: 760px) { + .secrets-store__table { + min-width: 720px; + } + + .secrets-store__menu-trigger { + opacity: 1; + } +} diff --git a/ui/src/test-helpers/app-sidebar-cases/basics.ts b/ui/src/test-helpers/app-sidebar-cases/basics.ts index e4749a8ab785..dabeedc4068e 100644 --- a/ui/src/test-helpers/app-sidebar-cases/basics.ts +++ b/ui/src/test-helpers/app-sidebar-cases/basics.ts @@ -29,6 +29,7 @@ describe("AppSidebar update card wiring", () => { const { sidebar } = await mountSidebar(gateway, createSessions("main", ["agent:main:main"])); expect(sidebar.querySelector('.nav-item[href="/custodian"]')).toBeNull(); + expect(sidebar.querySelector('.nav-item[href="/settings/secrets"]')).toBeNull(); }); it("renders the update card in the footer after the attention slot and forwards its action", async () => {