diff --git a/extensions/line/src/channel.startup.test.ts b/extensions/line/src/channel.startup.test.ts index 000b94ee471..505945eec73 100644 --- a/extensions/line/src/channel.startup.test.ts +++ b/extensions/line/src/channel.startup.test.ts @@ -129,3 +129,116 @@ 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", + }, + ]); + }); + + 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", + }, + ]); + }); +}); diff --git a/extensions/line/src/channel.ts b/extensions/line/src/channel.ts index fd81a4c8f8a..7ec447eb7c9 100644 --- a/extensions/line/src/channel.ts +++ b/extensions/line/src/channel.ts @@ -335,7 +335,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, @@ -343,7 +343,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, @@ -371,6 +371,8 @@ export const linePlugin: ChannelPlugin = { }); return { ...base, + channelAccessTokenConfigured: Boolean(account.channelAccessToken?.trim()), + channelSecretConfigured: Boolean(account.channelSecret?.trim()), tokenSource: account.tokenSource, mode: "webhook", }; diff --git a/src/channels/plugins/types.core.ts b/src/channels/plugins/types.core.ts index f7275d81ed2..4f65c449ccc 100644 --- a/src/channels/plugins/types.core.ts +++ b/src/channels/plugins/types.core.ts @@ -198,6 +198,8 @@ export type ChannelAccountSnapshot = { profile?: unknown; channelAccessToken?: string; channelSecret?: string; + channelAccessTokenConfigured?: boolean; + channelSecretConfigured?: boolean; }; export type ChannelLogSink = {