From 2d0aa186068d8defecfaeef11479c324879992db Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Tue, 21 Apr 2026 11:17:24 -0400 Subject: [PATCH] fix: tighten channel status triage --- src/cli/command-secret-targets.import.test.ts | 14 +++++++++++++- src/cli/command-secret-targets.ts | 5 +++-- src/commands/channels.status.command-flow.test.ts | 4 +++- src/commands/channels/status.ts | 7 ++++--- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/cli/command-secret-targets.import.test.ts b/src/cli/command-secret-targets.import.test.ts index f8dcbfc84a5..168100aa9f3 100644 --- a/src/cli/command-secret-targets.import.test.ts +++ b/src/cli/command-secret-targets.import.test.ts @@ -71,11 +71,22 @@ describe("command secret targets module import", () => { includeInConfigure: true, includeInAudit: true, }, + { + id: "channels.discord.token", + targetType: "channels.discord.token", + configFile: "openclaw.json", + pathPattern: "channels.discord.token", + secretShape: "secret_input", + expectedResolvedValue: "string", + includeInPlan: true, + includeInConfigure: true, + includeInAudit: true, + }, ], }, }, { - id: "external-chat-plugin", + id: "external-chat", secrets: { secretTargetRegistryEntries: [ { @@ -112,6 +123,7 @@ describe("command secret targets module import", () => { expect(targets.has("channels.external-chat.token")).toBe(true); expect(targets.has("channels.telegram.botToken")).toBe(true); + expect(targets.has("channels.discord.token")).toBe(false); expect(targets.has("channels.telegram.gatewayToken")).toBe(false); expect(targets.has("channels.telegram.gatewayTokenRef")).toBe(false); expect(targets.has("agents.defaults.memorySearch.remote.apiKey")).toBe(true); diff --git a/src/cli/command-secret-targets.ts b/src/cli/command-secret-targets.ts index 2fd3a8e66fd..758a09c367e 100644 --- a/src/cli/command-secret-targets.ts +++ b/src/cli/command-secret-targets.ts @@ -81,8 +81,9 @@ function isScopedChannelSecretTargetEntry(params: { pathPattern?: string; refPathPattern?: string; }; + pluginChannelId: string; }): boolean { - const channelId = /^channels\.([^.]+)\./.exec(params.entry.id)?.[1]; + const channelId = normalizeOptionalString(params.pluginChannelId); if (!channelId) { return false; } @@ -107,7 +108,7 @@ function getConfiguredChannelSecretTargetIds( includePersistedAuthState: false, })) { for (const entry of plugin.secrets?.secretTargetRegistryEntries ?? []) { - if (isScopedChannelSecretTargetEntry({ entry })) { + if (isScopedChannelSecretTargetEntry({ entry, pluginChannelId: plugin.id })) { targetIds.add(entry.id); } } diff --git a/src/commands/channels.status.command-flow.test.ts b/src/commands/channels.status.command-flow.test.ts index 701141ed5f5..0fa8ae85665 100644 --- a/src/commands/channels.status.command-flow.test.ts +++ b/src/commands/channels.status.command-flow.test.ts @@ -266,7 +266,7 @@ describe("channelsStatusCommand SecretRef fallback flow", () => { effectiveConfig: { secretResolved: true, channels: {} }, diagnostics: [], }); - const { runtime, logs } = createRuntimeCapture(); + const { runtime, logs, errors } = createRuntimeCapture(); await channelsStatusCommand({ json: true, probe: false }, runtime as never); @@ -278,6 +278,8 @@ describe("channelsStatusCommand SecretRef fallback flow", () => { }), ); const payload = JSON.parse(logs.at(-1) ?? "{}"); + expect(errors.join("\n")).not.toContain("user:pass"); + expect(errors.join("\n")).not.toContain("secret-token"); expect(payload.error).toContain("Gateway target:"); expect(payload.error).not.toContain("user:pass"); expect(payload.error).not.toContain("secret-token"); diff --git a/src/commands/channels/status.ts b/src/commands/channels/status.ts index 4bed0fdf99a..7f2ce3060fc 100644 --- a/src/commands/channels/status.ts +++ b/src/commands/channels/status.ts @@ -63,7 +63,7 @@ function redactGatewayUrlSecretsInText(text: string): string { }); } -function formatChannelsStatusJsonError(err: unknown): string { +function formatChannelsStatusError(err: unknown): string { return redactGatewayUrlSecretsInText(formatErrorMessage(err)); } @@ -211,7 +211,8 @@ export async function channelsStatusCommand( } runtime.log(formatGatewayChannelsStatusLines(payload).join("\n")); } catch (err) { - runtime.error(`Gateway not reachable: ${String(err)}`); + const safeError = formatChannelsStatusError(err); + runtime.error(`Gateway not reachable: ${safeError}`); const cfg = await requireValidConfigSnapshot(runtime); if (!cfg) { return; @@ -228,7 +229,7 @@ export async function channelsStatusCommand( if (opts.json) { writeRuntimeJson(runtime, { gatewayReachable: false, - error: formatChannelsStatusJsonError(err), + error: safeError, configOnly: true, config: { path: snapshot.path,