From 73e641c1b483dea6ee971839c52cef2d268425d9 Mon Sep 17 00:00:00 2001 From: xaeon2026 Date: Mon, 9 Mar 2026 09:50:45 -0400 Subject: [PATCH 1/4] Fix LINE doctor warnings for file-backed credentials --- extensions/line/src/channel.startup.test.ts | 51 +++++++++++++++++++++ extensions/line/src/channel.ts | 6 ++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/extensions/line/src/channel.startup.test.ts b/extensions/line/src/channel.startup.test.ts index e4de0f38e3b..f5a6dd7310e 100644 --- a/extensions/line/src/channel.startup.test.ts +++ b/extensions/line/src/channel.startup.test.ts @@ -129,3 +129,54 @@ describe("linePlugin gateway.startAccount", () => { await task; }); }); + +describe("linePlugin status", () => { + it("does not report missing token or secret when snapshot came from file-backed config", async () => { + const snapshot = await linePlugin.status?.buildAccountSnapshot?.({ + account: { + accountId: "default", + name: "Default", + enabled: true, + channelAccessToken: "token-from-file", + channelSecret: "secret-from-file", + tokenSource: "file", + config: {} as ResolvedLineAccount["config"], + } as never, + cfg: {} as OpenClawConfig, + runtime: undefined, + probe: undefined, + audit: undefined, + }); + + expect(snapshot?.configured).toBe(true); + expect(linePlugin.status?.collectStatusIssues?.([snapshot as never])).toEqual([]); + }); + + it("keeps per-field warnings when only one credential is missing", async () => { + const snapshot = await linePlugin.status?.buildAccountSnapshot?.({ + account: { + accountId: "default", + name: "Default", + enabled: true, + channelAccessToken: " ", + channelSecret: "secret-from-file", + tokenSource: "file", + config: {} as ResolvedLineAccount["config"], + } as never, + cfg: {} as OpenClawConfig, + runtime: undefined, + probe: undefined, + audit: undefined, + }); + + expect(snapshot?.configured).toBe(false); + expect(linePlugin.status?.collectStatusIssues?.([snapshot as never])).toEqual([ + { + channel: "line", + accountId: "default", + kind: "config", + message: "LINE channel access token not configured", + }, + ]); + }); +}); diff --git a/extensions/line/src/channel.ts b/extensions/line/src/channel.ts index 9388579ab38..37fbda8c54a 100644 --- a/extensions/line/src/channel.ts +++ b/extensions/line/src/channel.ts @@ -569,7 +569,7 @@ export const linePlugin: ChannelPlugin = { const issues: ChannelStatusIssue[] = []; for (const account of accounts) { const accountId = account.accountId ?? DEFAULT_ACCOUNT_ID; - if (!account.channelAccessToken?.trim()) { + if (account.channelAccessTokenConfigured === false) { issues.push({ channel: "line", accountId, @@ -577,7 +577,7 @@ export const linePlugin: ChannelPlugin = { message: "LINE channel access token not configured", }); } - if (!account.channelSecret?.trim()) { + if (account.channelSecretConfigured === false) { issues.push({ channel: "line", accountId, @@ -605,6 +605,8 @@ export const linePlugin: ChannelPlugin = { }); return { ...base, + channelAccessTokenConfigured: Boolean(account.channelAccessToken?.trim()), + channelSecretConfigured: Boolean(account.channelSecret?.trim()), tokenSource: account.tokenSource, mode: "webhook", }; From 53cd6fac13f5d0cbe4bdf5a43bd29af73fa014e1 Mon Sep 17 00:00:00 2001 From: xaeon2026 Date: Mon, 9 Mar 2026 14:20:46 -0400 Subject: [PATCH 2/4] Add channelAccessTokenConfigured/channelSecretConfigured to ChannelAccountSnapshot type The LINE channel sets these boolean fields in its status snapshot and uses them in collectStatusIssues(), but they were missing from the shared ChannelAccountSnapshot type definition. This caused a type mismatch that only surfaced at compile time with strict type checking. --- src/channels/plugins/types.core.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/channels/plugins/types.core.ts b/src/channels/plugins/types.core.ts index 22f8e458e79..c1ee5c815ef 100644 --- a/src/channels/plugins/types.core.ts +++ b/src/channels/plugins/types.core.ts @@ -156,6 +156,8 @@ export type ChannelAccountSnapshot = { profile?: unknown; channelAccessToken?: string; channelSecret?: string; + channelAccessTokenConfigured?: boolean; + channelSecretConfigured?: boolean; }; export type ChannelLogSink = { From 24e03d5d28e4b0bde89e318d69880a2804178598 Mon Sep 17 00:00:00 2001 From: jackal092927 Date: Mon, 9 Mar 2026 17:09:15 -0400 Subject: [PATCH 3/4] chore: fix CONTRIBUTING.md trailing whitespace (oxfmt) --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d02d1f2059..1127d7dc791 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,7 +58,6 @@ Welcome to the lobster tank! 🦞 - **Jonathan Taylor** - ACP subsystem, Gateway features/bugs, Gog/Mog/Sog CLI's, SEDMAT - GitHub [@visionik](https://github.com/visionik) · X: [@visionik](https://x.com/visionik) - - **Josh Lehman** - Compaction, Tlon/Urbit subsystem - GitHub [@jalehman](https://github.com/jalehman) · X: [@jlehman\_](https://x.com/jlehman_) @@ -73,7 +72,7 @@ Welcome to the lobster tank! 🦞 - **Robin Waslander** - Security, PR triage, bug fixes - GitHub: [@hydro13](https://github.com/hydro13) · X: [@Robin_waslander](https://x.com/Robin_waslander) - + ## How to Contribute 1. **Bugs & small fixes** → Open a PR! From 4c1fc56bd30b032669503cef0d69fe6ca417eb6c Mon Sep 17 00:00:00 2001 From: jackal092927 Date: Wed, 11 Mar 2026 01:50:35 -0400 Subject: [PATCH 4/4] test(line): cover secret-missing file-backed snapshots --- extensions/line/src/channel.startup.test.ts | 62 +++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/extensions/line/src/channel.startup.test.ts b/extensions/line/src/channel.startup.test.ts index f5a6dd7310e..81a85ff89e7 100644 --- a/extensions/line/src/channel.startup.test.ts +++ b/extensions/line/src/channel.startup.test.ts @@ -179,4 +179,66 @@ describe("linePlugin status", () => { }, ]); }); + + it("keeps per-field warnings when only the channel secret is missing", async () => { + const snapshot = await linePlugin.status?.buildAccountSnapshot?.({ + account: { + accountId: "default", + name: "Default", + enabled: true, + channelAccessToken: "token-from-file", + channelSecret: " ", + tokenSource: "file", + config: {} as ResolvedLineAccount["config"], + } as never, + cfg: {} as OpenClawConfig, + runtime: undefined, + probe: undefined, + audit: undefined, + }); + + expect(snapshot?.configured).toBe(false); + expect(linePlugin.status?.collectStatusIssues?.([snapshot as never])).toEqual([ + { + channel: "line", + accountId: "default", + kind: "config", + message: "LINE channel secret not configured", + }, + ]); + }); + + it("reports both warnings when both file-backed credentials are missing", async () => { + const snapshot = await linePlugin.status?.buildAccountSnapshot?.({ + account: { + accountId: "default", + name: "Default", + enabled: true, + channelAccessToken: " ", + channelSecret: " ", + tokenSource: "file", + config: {} as ResolvedLineAccount["config"], + } as never, + cfg: {} as OpenClawConfig, + runtime: undefined, + probe: undefined, + audit: undefined, + }); + + expect(snapshot?.configured).toBe(false); + expect(linePlugin.status?.collectStatusIssues?.([snapshot as never])).toEqual([ + { + channel: "line", + accountId: "default", + kind: "config", + message: "LINE channel access token not configured", + }, + { + channel: "line", + accountId: "default", + kind: "config", + message: "LINE channel secret not configured", + }, + ]); + }); });