mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-20 05:31:30 +00:00
fix: honor selected account in setup status
This commit is contained in:
@@ -166,9 +166,9 @@ export const blueBubblesSetupWizard: ChannelSetupWizard = {
|
||||
configuredScore: 1,
|
||||
unconfiguredScore: 0,
|
||||
includeStatusLine: true,
|
||||
resolveConfigured: ({ cfg }) =>
|
||||
listBlueBubblesAccountIds(cfg).some((accountId) => {
|
||||
const account = resolveBlueBubblesAccount({ cfg, accountId });
|
||||
resolveConfigured: ({ cfg, accountId }) =>
|
||||
(accountId ? [accountId] : listBlueBubblesAccountIds(cfg)).some((resolvedAccountId) => {
|
||||
const account = resolveBlueBubblesAccount({ cfg, accountId: resolvedAccountId });
|
||||
return account.configured;
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -108,9 +108,9 @@ export function createDiscordSetupWizardBase(handlers: {
|
||||
unconfiguredHint: "needs token",
|
||||
configuredScore: 2,
|
||||
unconfiguredScore: 1,
|
||||
resolveConfigured: ({ cfg }) =>
|
||||
listDiscordSetupAccountIds(cfg).some((accountId) => {
|
||||
const account = inspectDiscordSetupAccount({ cfg, accountId });
|
||||
resolveConfigured: ({ cfg, accountId }) =>
|
||||
(accountId ? [accountId] : listDiscordSetupAccountIds(cfg)).some((resolvedAccountId) => {
|
||||
const account = inspectDiscordSetupAccount({ cfg, accountId: resolvedAccountId });
|
||||
return account.configured;
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -92,10 +92,14 @@ export const googlechatSetupWizard: ChannelSetupWizard = {
|
||||
configuredHint: "configured",
|
||||
unconfiguredHint: "needs auth",
|
||||
includeStatusLine: true,
|
||||
resolveConfigured: ({ cfg }) =>
|
||||
listGoogleChatAccountIds(cfg).some(
|
||||
(accountId) => resolveGoogleChatAccount({ cfg, accountId }).credentialSource !== "none",
|
||||
),
|
||||
resolveConfigured: ({ cfg, accountId }) =>
|
||||
accountId
|
||||
? resolveGoogleChatAccount({ cfg, accountId }).credentialSource !== "none"
|
||||
: listGoogleChatAccountIds(cfg).some(
|
||||
(resolvedAccountId) =>
|
||||
resolveGoogleChatAccount({ cfg, accountId: resolvedAccountId }).credentialSource !==
|
||||
"none",
|
||||
),
|
||||
}),
|
||||
introNote: {
|
||||
title: "Google Chat setup",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
createPluginSetupWizardConfigure,
|
||||
createPluginSetupWizardStatus,
|
||||
createTestWizardPrompter,
|
||||
runSetupWizardConfigure,
|
||||
type WizardPrompter,
|
||||
@@ -30,6 +31,7 @@ vi.mock("./monitor.js", async () => {
|
||||
});
|
||||
|
||||
const googlechatConfigure = createPluginSetupWizardConfigure(googlechatPlugin);
|
||||
const googlechatStatus = createPluginSetupWizardStatus(googlechatPlugin);
|
||||
|
||||
function buildAccount(): ResolvedGoogleChatAccount {
|
||||
return {
|
||||
@@ -186,6 +188,29 @@ describe("googlechat setup", () => {
|
||||
).toBe("allowlist");
|
||||
});
|
||||
|
||||
it("reports configured state for the selected account instead of any account", async () => {
|
||||
const status = await googlechatStatus({
|
||||
cfg: {
|
||||
channels: {
|
||||
googlechat: {
|
||||
accounts: {
|
||||
default: {
|
||||
serviceAccount: { client_email: "default@example.com" },
|
||||
},
|
||||
alerts: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
accountOverrides: {
|
||||
googlechat: "alerts",
|
||||
},
|
||||
options: {},
|
||||
});
|
||||
|
||||
expect(status.configured).toBe(false);
|
||||
});
|
||||
|
||||
it("reports account-scoped config keys for named accounts", () => {
|
||||
expect(googlechatPlugin.setupWizard?.dmPolicy?.resolveConfigKeys?.({}, "alerts")).toEqual({
|
||||
policyKey: "channels.googlechat.accounts.alerts.dm.policy",
|
||||
|
||||
@@ -90,8 +90,10 @@ export const lineSetupWizard: ChannelSetupWizard = {
|
||||
configuredScore: 1,
|
||||
unconfiguredScore: 0,
|
||||
includeStatusLine: true,
|
||||
resolveConfigured: ({ cfg }) =>
|
||||
listLineAccountIds(cfg).some((accountId) => isLineConfigured(cfg, accountId)),
|
||||
resolveConfigured: ({ cfg, accountId }) =>
|
||||
accountId
|
||||
? isLineConfigured(cfg, accountId)
|
||||
: listLineAccountIds(cfg).some((resolvedAccountId) => isLineConfigured(cfg, resolvedAccountId)),
|
||||
resolveExtraStatusLines: ({ cfg }) => [`Accounts: ${listLineAccountIds(cfg).length || 0}`],
|
||||
}),
|
||||
introNote: {
|
||||
|
||||
@@ -30,10 +30,12 @@ export const mattermostSetupWizard: ChannelSetupWizard = {
|
||||
unconfiguredHint: "needs setup",
|
||||
configuredScore: 2,
|
||||
unconfiguredScore: 1,
|
||||
resolveConfigured: ({ cfg }) =>
|
||||
listMattermostAccountIds(cfg).some((accountId) =>
|
||||
isMattermostConfigured(resolveMattermostAccountWithSecrets(cfg, accountId)),
|
||||
),
|
||||
resolveConfigured: ({ cfg, accountId }) =>
|
||||
accountId
|
||||
? isMattermostConfigured(resolveMattermostAccountWithSecrets(cfg, accountId))
|
||||
: listMattermostAccountIds(cfg).some((resolvedAccountId) =>
|
||||
isMattermostConfigured(resolveMattermostAccountWithSecrets(cfg, resolvedAccountId)),
|
||||
),
|
||||
}),
|
||||
introNote: {
|
||||
title: "Mattermost bot token",
|
||||
|
||||
@@ -33,11 +33,19 @@ export const nextcloudTalkSetupWizard: ChannelSetupWizard = {
|
||||
unconfiguredHint: "self-hosted chat",
|
||||
configuredScore: 1,
|
||||
unconfiguredScore: 5,
|
||||
resolveConfigured: ({ cfg }) =>
|
||||
listNextcloudTalkAccountIds(cfg as CoreConfig).some((accountId) => {
|
||||
resolveConfigured: ({ cfg, accountId }) => {
|
||||
if (accountId) {
|
||||
const account = resolveNextcloudTalkAccount({ cfg: cfg as CoreConfig, accountId });
|
||||
return Boolean(account.secret && account.baseUrl);
|
||||
}),
|
||||
}
|
||||
return listNextcloudTalkAccountIds(cfg as CoreConfig).some((resolvedAccountId) => {
|
||||
const account = resolveNextcloudTalkAccount({
|
||||
cfg: cfg as CoreConfig,
|
||||
accountId: resolvedAccountId,
|
||||
});
|
||||
return Boolean(account.secret && account.baseUrl);
|
||||
});
|
||||
},
|
||||
}),
|
||||
introNote: {
|
||||
title: "Nextcloud Talk bot setup",
|
||||
|
||||
@@ -28,10 +28,12 @@ export const signalSetupWizard: ChannelSetupWizard = {
|
||||
unconfiguredHint: "signal-cli missing",
|
||||
configuredScore: 1,
|
||||
unconfiguredScore: 0,
|
||||
resolveConfigured: ({ cfg }) =>
|
||||
listSignalAccountIds(cfg).some(
|
||||
(accountId) => resolveSignalAccount({ cfg, accountId }).configured,
|
||||
),
|
||||
resolveConfigured: ({ cfg, accountId }) =>
|
||||
accountId
|
||||
? resolveSignalAccount({ cfg, accountId }).configured
|
||||
: listSignalAccountIds(cfg).some(
|
||||
(resolvedAccountId) => resolveSignalAccount({ cfg, accountId: resolvedAccountId }).configured,
|
||||
),
|
||||
resolveBinaryPath: ({ cfg }) => cfg.channels?.signal?.cliPath ?? "signal-cli",
|
||||
detectBinary,
|
||||
}),
|
||||
|
||||
@@ -166,9 +166,9 @@ export function createSlackSetupWizardBase(handlers: {
|
||||
unconfiguredHint: "needs tokens",
|
||||
configuredScore: 2,
|
||||
unconfiguredScore: 1,
|
||||
resolveConfigured: ({ cfg }) =>
|
||||
listSlackAccountIds(cfg).some((accountId) => {
|
||||
const account = inspectSlackAccount({ cfg, accountId });
|
||||
resolveConfigured: ({ cfg, accountId }) =>
|
||||
(accountId ? [accountId] : listSlackAccountIds(cfg)).some((resolvedAccountId) => {
|
||||
const account = inspectSlackAccount({ cfg, accountId: resolvedAccountId });
|
||||
return account.configured;
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -122,11 +122,11 @@ export const telegramSetupWizard: ChannelSetupWizard = {
|
||||
unconfiguredHint: "recommended · newcomer-friendly",
|
||||
configuredScore: 1,
|
||||
unconfiguredScore: 10,
|
||||
resolveConfigured: ({ cfg }) =>
|
||||
listTelegramAccountIds(cfg).some((accountId) => {
|
||||
const account = inspectTelegramAccount({ cfg, accountId });
|
||||
return account.configured;
|
||||
}),
|
||||
resolveConfigured: ({ cfg, accountId }) =>
|
||||
(accountId ? [accountId] : listTelegramAccountIds(cfg)).some((resolvedAccountId) => {
|
||||
const account = inspectTelegramAccount({ cfg, accountId: resolvedAccountId });
|
||||
return account.configured;
|
||||
}),
|
||||
}),
|
||||
prepare: async ({ cfg, accountId, credentialValues }) => ({
|
||||
cfg: ensureTelegramDefaultGroupMentionGate(cfg, accountId),
|
||||
|
||||
@@ -19,20 +19,20 @@ export const whatsappSetupWizard: ChannelSetupWizard = {
|
||||
unconfiguredHint: "not linked",
|
||||
configuredScore: 5,
|
||||
unconfiguredScore: 4,
|
||||
resolveConfigured: async ({ cfg }) => {
|
||||
for (const accountId of listWhatsAppAccountIds(cfg)) {
|
||||
if (await detectWhatsAppLinked(cfg, accountId)) {
|
||||
resolveConfigured: async ({ cfg, accountId }) => {
|
||||
for (const resolvedAccountId of accountId ? [accountId] : listWhatsAppAccountIds(cfg)) {
|
||||
if (await detectWhatsAppLinked(cfg, resolvedAccountId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
resolveStatusLines: async ({ cfg, configured }) => {
|
||||
resolveStatusLines: async ({ cfg, accountId, configured }) => {
|
||||
const linkedAccountId = (
|
||||
await Promise.all(
|
||||
listWhatsAppAccountIds(cfg).map(async (accountId) => ({
|
||||
accountId,
|
||||
linked: await detectWhatsAppLinked(cfg, accountId),
|
||||
(accountId ? [accountId] : listWhatsAppAccountIds(cfg)).map(async (resolvedAccountId) => ({
|
||||
accountId: resolvedAccountId,
|
||||
linked: await detectWhatsAppLinked(cfg, resolvedAccountId),
|
||||
})),
|
||||
)
|
||||
).find((entry) => entry.linked)?.accountId;
|
||||
|
||||
@@ -185,11 +185,11 @@ export const zaloSetupWizard: ChannelSetupWizard = {
|
||||
configuredScore: 1,
|
||||
unconfiguredScore: 10,
|
||||
includeStatusLine: true,
|
||||
resolveConfigured: ({ cfg }) =>
|
||||
listZaloAccountIds(cfg).some((accountId) => {
|
||||
resolveConfigured: ({ cfg, accountId }) =>
|
||||
(accountId ? [accountId] : listZaloAccountIds(cfg)).some((resolvedAccountId) => {
|
||||
const account = resolveZaloAccount({
|
||||
cfg,
|
||||
accountId,
|
||||
accountId: resolvedAccountId,
|
||||
allowUnresolvedSecretRef: true,
|
||||
});
|
||||
return (
|
||||
|
||||
@@ -302,19 +302,22 @@ export const zalouserSetupWizard: ChannelSetupWizard = {
|
||||
unconfiguredHint: "recommended · QR login",
|
||||
configuredScore: 1,
|
||||
unconfiguredScore: 15,
|
||||
resolveConfigured: async ({ cfg }) => {
|
||||
const ids = listZalouserAccountIds(cfg);
|
||||
for (const accountId of ids) {
|
||||
const account = resolveZalouserAccountSync({ cfg, accountId });
|
||||
resolveConfigured: async ({ cfg, accountId }) => {
|
||||
const ids = accountId ? [accountId] : listZalouserAccountIds(cfg);
|
||||
for (const resolvedAccountId of ids) {
|
||||
const account = resolveZalouserAccountSync({ cfg, accountId: resolvedAccountId });
|
||||
if (await checkZcaAuthenticated(account.profile)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
resolveStatusLines: async ({ cfg, configured }) => {
|
||||
resolveStatusLines: async ({ cfg, accountId, configured }) => {
|
||||
void cfg;
|
||||
return [`Zalo Personal: ${configured ? "logged in" : "needs QR login"}`];
|
||||
const label = accountId && accountId !== DEFAULT_ACCOUNT_ID
|
||||
? `Zalo Personal (${accountId})`
|
||||
: "Zalo Personal";
|
||||
return [`${label}: ${configured ? "logged in" : "needs QR login"}`];
|
||||
},
|
||||
},
|
||||
prepare: async ({ cfg, accountId, prompter, options }) => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { ChannelSetupWizard } from "./setup-wizard.js";
|
||||
|
||||
describe("createDetectedBinaryStatus", () => {
|
||||
it("builds status lines, hint, and score from binary detection", async () => {
|
||||
const resolveConfigured = vi.fn(() => true);
|
||||
const status = createDetectedBinaryStatus({
|
||||
channelLabel: "Signal",
|
||||
binaryLabel: "signal-cli",
|
||||
@@ -18,12 +19,13 @@ describe("createDetectedBinaryStatus", () => {
|
||||
unconfiguredHint: "signal-cli missing",
|
||||
configuredScore: 1,
|
||||
unconfiguredScore: 0,
|
||||
resolveConfigured: () => true,
|
||||
resolveConfigured,
|
||||
resolveBinaryPath: () => "/usr/local/bin/signal-cli",
|
||||
detectBinary: vi.fn(async () => true),
|
||||
});
|
||||
|
||||
expect(await status.resolveConfigured({ cfg: {} })).toBe(true);
|
||||
expect(await status.resolveConfigured({ cfg: {}, accountId: "work" })).toBe(true);
|
||||
expect(resolveConfigured).toHaveBeenCalledWith({ cfg: {}, accountId: "work" });
|
||||
expect(await status.resolveStatusLines?.({ cfg: {}, configured: true })).toEqual([
|
||||
"Signal: configured",
|
||||
"signal-cli: found (/usr/local/bin/signal-cli)",
|
||||
|
||||
@@ -18,7 +18,10 @@ export function createDetectedBinaryStatus(params: {
|
||||
unconfiguredHint: string;
|
||||
configuredScore: number;
|
||||
unconfiguredScore: number;
|
||||
resolveConfigured: (params: { cfg: OpenClawConfig }) => boolean | Promise<boolean>;
|
||||
resolveConfigured: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string;
|
||||
}) => boolean | Promise<boolean>;
|
||||
resolveBinaryPath: (params: { cfg: OpenClawConfig }) => string;
|
||||
detectBinary?: (path: string) => Promise<boolean>;
|
||||
}): ChannelSetupWizardStatus {
|
||||
|
||||
@@ -172,6 +172,7 @@ export function createStandardChannelSetupStatus(params: {
|
||||
resolveConfigured: ChannelSetupWizardStatus["resolveConfigured"];
|
||||
resolveExtraStatusLines?: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string;
|
||||
configured: boolean;
|
||||
}) => string[] | Promise<string[]>;
|
||||
}): ChannelSetupWizardStatus {
|
||||
@@ -190,13 +191,14 @@ export function createStandardChannelSetupStatus(params: {
|
||||
};
|
||||
|
||||
if (params.includeStatusLine || params.resolveExtraStatusLines) {
|
||||
status.resolveStatusLines = async ({ cfg, configured }) => {
|
||||
status.resolveStatusLines = async ({ cfg, accountId, configured }) => {
|
||||
const lines = params.includeStatusLine
|
||||
? [
|
||||
`${params.channelLabel}: ${configured ? params.configuredLabel : params.unconfiguredLabel}`,
|
||||
]
|
||||
: [];
|
||||
const extraLines = (await params.resolveExtraStatusLines?.({ cfg, configured })) ?? [];
|
||||
const extraLines =
|
||||
(await params.resolveExtraStatusLines?.({ cfg, accountId, configured })) ?? [];
|
||||
return [...lines, ...extraLines];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ describe("createDelegatedResolveConfigured", () => {
|
||||
status: {
|
||||
configuredLabel: "configured",
|
||||
unconfiguredLabel: "needs setup",
|
||||
resolveConfigured: async ({ cfg }) => Boolean(cfg.channels?.demo),
|
||||
resolveConfigured: async ({ cfg, accountId }) =>
|
||||
Boolean(cfg.channels?.[accountId ?? "demo"]),
|
||||
},
|
||||
credentials: [],
|
||||
}),
|
||||
@@ -32,7 +33,9 @@ describe("createDelegatedResolveConfigured", () => {
|
||||
const resolveConfigured = createDelegatedResolveConfigured(loadWizard);
|
||||
|
||||
expect(await resolveConfigured({ cfg: {} })).toBe(false);
|
||||
expect(await resolveConfigured({ cfg: { channels: { demo: {} } } })).toBe(true);
|
||||
expect(await resolveConfigured({ cfg: { channels: { work: {} } }, accountId: "work" })).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ type ResolveGroupAllowlistParams = Parameters<
|
||||
>[0];
|
||||
|
||||
export function createDelegatedResolveConfigured(loadWizard: () => Promise<ChannelSetupWizard>) {
|
||||
return async ({ cfg }: ResolveConfiguredParams) =>
|
||||
await (await loadWizard()).status.resolveConfigured({ cfg });
|
||||
return async ({ cfg, accountId }: ResolveConfiguredParams) =>
|
||||
await (await loadWizard()).status.resolveConfigured({ cfg, accountId });
|
||||
}
|
||||
|
||||
export function createDelegatedPrepare(loadWizard: () => Promise<ChannelSetupWizard>) {
|
||||
|
||||
@@ -26,17 +26,23 @@ export type ChannelSetupWizardStatus = {
|
||||
unconfiguredHint?: string;
|
||||
configuredScore?: number;
|
||||
unconfiguredScore?: number;
|
||||
resolveConfigured: (params: { cfg: OpenClawConfig }) => boolean | Promise<boolean>;
|
||||
resolveConfigured: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string;
|
||||
}) => boolean | Promise<boolean>;
|
||||
resolveStatusLines?: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string;
|
||||
configured: boolean;
|
||||
}) => string[] | Promise<string[]>;
|
||||
resolveSelectionHint?: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string;
|
||||
configured: boolean;
|
||||
}) => string | undefined | Promise<string | undefined>;
|
||||
resolveQuickstartScore?: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string;
|
||||
configured: boolean;
|
||||
}) => number | undefined | Promise<number | undefined>;
|
||||
};
|
||||
@@ -283,9 +289,11 @@ async function buildStatus(
|
||||
wizard: ChannelSetupWizard,
|
||||
ctx: ChannelSetupStatusContext,
|
||||
): Promise<ChannelSetupStatus> {
|
||||
const configured = await wizard.status.resolveConfigured({ cfg: ctx.cfg });
|
||||
const accountId = ctx.accountOverrides[plugin.id];
|
||||
const configured = await wizard.status.resolveConfigured({ cfg: ctx.cfg, accountId });
|
||||
const statusLines = (await wizard.status.resolveStatusLines?.({
|
||||
cfg: ctx.cfg,
|
||||
accountId,
|
||||
configured,
|
||||
})) ?? [
|
||||
`${plugin.meta.label}: ${configured ? wizard.status.configuredLabel : wizard.status.unconfiguredLabel}`,
|
||||
@@ -293,11 +301,13 @@ async function buildStatus(
|
||||
const selectionHint =
|
||||
(await wizard.status.resolveSelectionHint?.({
|
||||
cfg: ctx.cfg,
|
||||
accountId,
|
||||
configured,
|
||||
})) ?? (configured ? wizard.status.configuredHint : wizard.status.unconfiguredHint);
|
||||
const quickstartScore =
|
||||
(await wizard.status.resolveQuickstartScore?.({
|
||||
cfg: ctx.cfg,
|
||||
accountId,
|
||||
configured,
|
||||
})) ?? (configured ? wizard.status.configuredScore : wizard.status.unconfiguredScore);
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user