fix: honor selected account in setup status

This commit is contained in:
Tak Hoffman
2026-04-03 11:48:58 -05:00
parent 5edefc4d5b
commit 51f6bc4940
19 changed files with 124 additions and 58 deletions

View File

@@ -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;
}),
}),

View File

@@ -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;
}),
}),

View File

@@ -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",

View File

@@ -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",

View File

@@ -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: {

View File

@@ -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",

View File

@@ -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",

View File

@@ -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,
}),

View File

@@ -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;
}),
}),

View File

@@ -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),

View File

@@ -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;

View File

@@ -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 (

View File

@@ -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 }) => {