diff --git a/extensions/bluebubbles/src/setup-core.ts b/extensions/bluebubbles/src/setup-core.ts index 6509c5f240b..408cd255cf3 100644 --- a/extensions/bluebubbles/src/setup-core.ts +++ b/extensions/bluebubbles/src/setup-core.ts @@ -1,14 +1,12 @@ import { - applyAccountNameToChannelSection, - DEFAULT_ACCOUNT_ID, - migrateBaseNameToDefaultAccount, patchScopedAccountConfig, - normalizeAccountId, - setTopLevelChannelDmPolicyWithAllowFrom, - type ChannelSetupAdapter, - type DmPolicy, - type OpenClawConfig, -} from "openclaw/plugin-sdk/setup"; + prepareScopedSetupConfig, +} from "../../../src/channels/plugins/setup-helpers.js"; +import { setTopLevelChannelDmPolicyWithAllowFrom } from "../../../src/channels/plugins/setup-wizard-helpers.js"; +import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js"; +import type { OpenClawConfig } from "../../../src/config/config.js"; +import type { DmPolicy } from "../../../src/config/types.js"; +import { normalizeAccountId } from "../../../src/routing/session-key.js"; import { applyBlueBubblesConnectionConfig } from "./config-apply.js"; const channel = "bluebubbles" as const; @@ -39,7 +37,7 @@ export function setBlueBubblesAllowFrom( export const blueBubblesSetupAdapter: ChannelSetupAdapter = { resolveAccountId: ({ accountId }) => normalizeAccountId(accountId), applyAccountName: ({ cfg, accountId, name }) => - applyAccountNameToChannelSection({ + prepareScopedSetupConfig({ cfg, channelKey: channel, accountId, @@ -58,19 +56,13 @@ export const blueBubblesSetupAdapter: ChannelSetupAdapter = { return null; }, applyAccountConfig: ({ cfg, accountId, input }) => { - const namedConfig = applyAccountNameToChannelSection({ + const next = prepareScopedSetupConfig({ cfg, channelKey: channel, accountId, name: input.name, + migrateBaseName: true, }); - const next = - accountId !== DEFAULT_ACCOUNT_ID - ? migrateBaseNameToDefaultAccount({ - cfg: namedConfig, - channelKey: channel, - }) - : namedConfig; return applyBlueBubblesConnectionConfig({ cfg: next, accountId, diff --git a/extensions/irc/src/setup-core.ts b/extensions/irc/src/setup-core.ts index c793098063b..3c28017e1e9 100644 --- a/extensions/irc/src/setup-core.ts +++ b/extensions/irc/src/setup-core.ts @@ -1,6 +1,6 @@ import { - applyAccountNameToChannelSection, patchScopedAccountConfig, + prepareScopedSetupConfig, } from "../../../src/channels/plugins/setup-helpers.js"; import { setTopLevelChannelAllowFrom, @@ -100,7 +100,7 @@ export function setIrcGroupAccess( export const ircSetupAdapter: ChannelSetupAdapter = { resolveAccountId: ({ accountId }) => normalizeAccountId(accountId), applyAccountName: ({ cfg, accountId, name }) => - applyAccountNameToChannelSection({ + prepareScopedSetupConfig({ cfg, channelKey: channel, accountId, @@ -118,7 +118,7 @@ export const ircSetupAdapter: ChannelSetupAdapter = { }, applyAccountConfig: ({ cfg, accountId, input }) => { const setupInput = input as IrcSetupInput; - const namedConfig = applyAccountNameToChannelSection({ + const namedConfig = prepareScopedSetupConfig({ cfg, channelKey: channel, accountId, diff --git a/extensions/matrix/src/setup-core.ts b/extensions/matrix/src/setup-core.ts index d78049262a1..2e6bc895e0c 100644 --- a/extensions/matrix/src/setup-core.ts +++ b/extensions/matrix/src/setup-core.ts @@ -1,11 +1,7 @@ -import { - applyAccountNameToChannelSection, - DEFAULT_ACCOUNT_ID, - migrateBaseNameToDefaultAccount, - normalizeAccountId, - normalizeSecretInputString, - type ChannelSetupAdapter, -} from "openclaw/plugin-sdk/setup"; +import { prepareScopedSetupConfig } from "../../../src/channels/plugins/setup-helpers.js"; +import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js"; +import { normalizeSecretInputString } from "../../../src/config/types.secrets.js"; +import { normalizeAccountId } from "../../../src/routing/session-key.js"; import type { CoreConfig } from "./types.js"; const channel = "matrix" as const; @@ -45,12 +41,12 @@ export function buildMatrixConfigUpdate( export const matrixSetupAdapter: ChannelSetupAdapter = { resolveAccountId: ({ accountId }) => normalizeAccountId(accountId), applyAccountName: ({ cfg, accountId, name }) => - applyAccountNameToChannelSection({ + prepareScopedSetupConfig({ cfg: cfg as CoreConfig, channelKey: channel, accountId, name, - }), + }) as CoreConfig, validateInput: ({ input }) => { if (input.useEnv) { return null; @@ -75,19 +71,13 @@ export const matrixSetupAdapter: ChannelSetupAdapter = { return null; }, applyAccountConfig: ({ cfg, accountId, input }) => { - const namedConfig = applyAccountNameToChannelSection({ + const next = prepareScopedSetupConfig({ cfg: cfg as CoreConfig, channelKey: channel, accountId, name: input.name, - }); - const next = - accountId !== DEFAULT_ACCOUNT_ID - ? migrateBaseNameToDefaultAccount({ - cfg: namedConfig, - channelKey: channel, - }) - : namedConfig; + migrateBaseName: true, + }) as CoreConfig; if (input.useEnv) { return { ...next, diff --git a/extensions/nextcloud-talk/src/setup-core.ts b/extensions/nextcloud-talk/src/setup-core.ts index 212d81380f1..a94482b8d43 100644 --- a/extensions/nextcloud-talk/src/setup-core.ts +++ b/extensions/nextcloud-talk/src/setup-core.ts @@ -1,6 +1,6 @@ import { - applyAccountNameToChannelSection, patchScopedAccountConfig, + prepareScopedSetupConfig, } from "../../../src/channels/plugins/setup-helpers.js"; import { mergeAllowFromEntries, @@ -187,7 +187,7 @@ export const nextcloudTalkDmPolicy: ChannelSetupDmPolicy = { export const nextcloudTalkSetupAdapter: ChannelSetupAdapter = { resolveAccountId: ({ accountId }) => normalizeAccountId(accountId), applyAccountName: ({ cfg, accountId, name }) => - applyAccountNameToChannelSection({ + prepareScopedSetupConfig({ cfg, channelKey: channel, accountId, @@ -208,7 +208,7 @@ export const nextcloudTalkSetupAdapter: ChannelSetupAdapter = { }, applyAccountConfig: ({ cfg, accountId, input }) => { const setupInput = input as NextcloudSetupInput; - const namedConfig = applyAccountNameToChannelSection({ + const namedConfig = prepareScopedSetupConfig({ cfg, channelKey: channel, accountId, diff --git a/extensions/tlon/src/setup-core.ts b/extensions/tlon/src/setup-core.ts index ae95819af52..08d72f2ab28 100644 --- a/extensions/tlon/src/setup-core.ts +++ b/extensions/tlon/src/setup-core.ts @@ -1,12 +1,11 @@ import { - applyAccountNameToChannelSection, - DEFAULT_ACCOUNT_ID, - normalizeAccountId, patchScopedAccountConfig, - type ChannelSetupAdapter, - type ChannelSetupInput, - type OpenClawConfig, -} from "openclaw/plugin-sdk/setup"; + prepareScopedSetupConfig, +} from "../../../src/channels/plugins/setup-helpers.js"; +import type { ChannelSetupAdapter } from "../../../src/channels/plugins/types.adapters.js"; +import type { ChannelSetupInput } from "../../../src/channels/plugins/types.core.js"; +import type { OpenClawConfig } from "../../../src/config/config.js"; +import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../src/routing/session-key.js"; import { buildTlonAccountFields } from "./account-fields.js"; import { resolveTlonAccount } from "./types.js"; @@ -30,7 +29,7 @@ export function applyTlonSetupConfig(params: { }): OpenClawConfig { const { cfg, accountId, input } = params; const useDefault = accountId === DEFAULT_ACCOUNT_ID; - const namedConfig = applyAccountNameToChannelSection({ + const namedConfig = prepareScopedSetupConfig({ cfg, channelKey: channel, accountId, @@ -70,7 +69,7 @@ export function applyTlonSetupConfig(params: { export const tlonSetupAdapter: ChannelSetupAdapter = { resolveAccountId: ({ accountId }) => normalizeAccountId(accountId), applyAccountName: ({ cfg, accountId, name }) => - applyAccountNameToChannelSection({ + prepareScopedSetupConfig({ cfg, channelKey: channel, accountId, diff --git a/src/channels/plugins/setup-helpers.test.ts b/src/channels/plugins/setup-helpers.test.ts index c45e13a9d7f..2040271f540 100644 --- a/src/channels/plugins/setup-helpers.test.ts +++ b/src/channels/plugins/setup-helpers.test.ts @@ -1,7 +1,11 @@ import { describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js"; -import { applySetupAccountConfigPatch, createPatchedAccountSetupAdapter } from "./setup-helpers.js"; +import { + applySetupAccountConfigPatch, + createPatchedAccountSetupAdapter, + prepareScopedSetupConfig, +} from "./setup-helpers.js"; function asConfig(value: unknown): OpenClawConfig { return value as OpenClawConfig; @@ -157,3 +161,43 @@ describe("createPatchedAccountSetupAdapter", () => { expect(next.channels?.whatsapp).not.toHaveProperty("authDir"); }); }); + +describe("prepareScopedSetupConfig", () => { + it("stores the name and migrates it for named accounts when requested", () => { + const next = prepareScopedSetupConfig({ + cfg: asConfig({ + channels: { + bluebubbles: { + name: "Personal", + }, + }, + }), + channelKey: "bluebubbles", + accountId: "Work Team", + name: "Work", + migrateBaseName: true, + }); + + expect(next.channels?.bluebubbles).toMatchObject({ + accounts: { + default: { name: "Personal" }, + "work-team": { name: "Work" }, + }, + }); + expect(next.channels?.bluebubbles).not.toHaveProperty("name"); + }); + + it("keeps the base shape for the default account when migration is disabled", () => { + const next = prepareScopedSetupConfig({ + cfg: asConfig({ channels: { irc: { enabled: true } } }), + channelKey: "irc", + accountId: DEFAULT_ACCOUNT_ID, + name: "Libera", + }); + + expect(next.channels?.irc).toMatchObject({ + enabled: true, + name: "Libera", + }); + }); +}); diff --git a/src/channels/plugins/setup-helpers.ts b/src/channels/plugins/setup-helpers.ts index d4f618e870f..0f7b3f8000b 100644 --- a/src/channels/plugins/setup-helpers.ts +++ b/src/channels/plugins/setup-helpers.ts @@ -122,6 +122,31 @@ export function migrateBaseNameToDefaultAccount(params: { } as OpenClawConfig; } +export function prepareScopedSetupConfig(params: { + cfg: OpenClawConfig; + channelKey: string; + accountId: string; + name?: string; + alwaysUseAccounts?: boolean; + migrateBaseName?: boolean; +}): OpenClawConfig { + const namedConfig = applyAccountNameToChannelSection({ + cfg: params.cfg, + channelKey: params.channelKey, + accountId: params.accountId, + name: params.name, + alwaysUseAccounts: params.alwaysUseAccounts, + }); + if (!params.migrateBaseName || normalizeAccountId(params.accountId) === DEFAULT_ACCOUNT_ID) { + return namedConfig; + } + return migrateBaseNameToDefaultAccount({ + cfg: namedConfig, + channelKey: params.channelKey, + alwaysUseAccounts: params.alwaysUseAccounts, + }); +} + export function applySetupAccountConfigPatch(params: { cfg: OpenClawConfig; channelKey: string;