refactor: share scoped account config patching

This commit is contained in:
Peter Steinberger
2026-03-10 20:22:06 +00:00
parent b517dc089a
commit 00170f8e1a
14 changed files with 118 additions and 253 deletions

View File

@@ -6,10 +6,10 @@ import type {
WizardPrompter,
} from "openclaw/plugin-sdk/bluebubbles";
import {
DEFAULT_ACCOUNT_ID,
formatDocsLink,
mergeAllowFromEntries,
normalizeAccountId,
patchScopedAccountConfig,
resolveAccountIdForConfigure,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "openclaw/plugin-sdk/bluebubbles";
@@ -38,34 +38,14 @@ function setBlueBubblesAllowFrom(
accountId: string,
allowFrom: string[],
): OpenClawConfig {
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
...cfg,
channels: {
...cfg.channels,
bluebubbles: {
...cfg.channels?.bluebubbles,
allowFrom,
},
},
};
}
return {
...cfg,
channels: {
...cfg.channels,
bluebubbles: {
...cfg.channels?.bluebubbles,
accounts: {
...cfg.channels?.bluebubbles?.accounts,
[accountId]: {
...cfg.channels?.bluebubbles?.accounts?.[accountId],
allowFrom,
},
},
},
},
};
return patchScopedAccountConfig({
cfg,
channelKey: channel,
accountId,
patch: { allowFrom },
ensureChannelEnabled: false,
ensureAccountEnabled: false,
});
}
function parseBlueBubblesAllowFromInput(raw: string): string[] {

View File

@@ -1,5 +1,6 @@
import type { OpenClawConfig, DmPolicy } from "openclaw/plugin-sdk/googlechat";
import {
applySetupAccountConfigPatch,
addWildcardAllowFrom,
formatDocsLink,
mergeAllowFromEntries,
@@ -8,7 +9,6 @@ import {
type ChannelOnboardingAdapter,
type ChannelOnboardingDmPolicy,
type WizardPrompter,
DEFAULT_ACCOUNT_ID,
migrateBaseNameToDefaultAccount,
} from "openclaw/plugin-sdk/googlechat";
import {
@@ -83,45 +83,6 @@ const dmPolicy: ChannelOnboardingDmPolicy = {
promptAllowFrom,
};
function applyAccountConfig(params: {
cfg: OpenClawConfig;
accountId: string;
patch: Record<string, unknown>;
}): OpenClawConfig {
const { cfg, accountId, patch } = params;
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
...cfg,
channels: {
...cfg.channels,
googlechat: {
...cfg.channels?.["googlechat"],
enabled: true,
...patch,
},
},
};
}
return {
...cfg,
channels: {
...cfg.channels,
googlechat: {
...cfg.channels?.["googlechat"],
enabled: true,
accounts: {
...cfg.channels?.["googlechat"]?.accounts,
[accountId]: {
...cfg.channels?.["googlechat"]?.accounts?.[accountId],
enabled: true,
...patch,
},
},
},
},
};
}
async function promptCredentials(params: {
cfg: OpenClawConfig;
prompter: WizardPrompter;
@@ -137,7 +98,7 @@ async function promptCredentials(params: {
initialValue: true,
});
if (useEnv) {
return applyAccountConfig({ cfg, accountId, patch: {} });
return applySetupAccountConfigPatch({ cfg, channelKey: channel, accountId, patch: {} });
}
}
@@ -156,8 +117,9 @@ async function promptCredentials(params: {
placeholder: "/path/to/service-account.json",
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
});
return applyAccountConfig({
return applySetupAccountConfigPatch({
cfg,
channelKey: channel,
accountId,
patch: { serviceAccountFile: String(path).trim() },
});
@@ -168,8 +130,9 @@ async function promptCredentials(params: {
placeholder: '{"type":"service_account", ... }',
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
});
return applyAccountConfig({
return applySetupAccountConfigPatch({
cfg,
channelKey: channel,
accountId,
patch: { serviceAccount: String(json).trim() },
});
@@ -200,8 +163,9 @@ async function promptAudience(params: {
initialValue: currentAudience || undefined,
validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
});
return applyAccountConfig({
return applySetupAccountConfigPatch({
cfg: params.cfg,
channelKey: channel,
accountId: params.accountId,
patch: { audienceType, audience: String(audience).trim() },
});

View File

@@ -1,6 +1,7 @@
import {
DEFAULT_ACCOUNT_ID,
formatDocsLink,
patchScopedAccountConfig,
promptChannelAccessConfig,
resolveAccountIdForConfigure,
setTopLevelChannelAllowFrom,
@@ -59,35 +60,14 @@ function updateIrcAccountConfig(
accountId: string,
patch: Partial<IrcAccountConfig>,
): CoreConfig {
const current = cfg.channels?.irc ?? {};
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
...cfg,
channels: {
...cfg.channels,
irc: {
...current,
...patch,
},
},
};
}
return {
...cfg,
channels: {
...cfg.channels,
irc: {
...current,
accounts: {
...current.accounts,
[accountId]: {
...current.accounts?.[accountId],
...patch,
},
},
},
},
};
return patchScopedAccountConfig({
cfg,
channelKey: channel,
accountId,
patch,
ensureChannelEnabled: false,
ensureAccountEnabled: false,
}) as CoreConfig;
}
function setIrcDmPolicy(cfg: CoreConfig, dmPolicy: DmPolicy): CoreConfig {

View File

@@ -4,6 +4,7 @@ import {
hasConfiguredSecretInput,
mapAllowFromEntries,
mergeAllowFromEntries,
patchScopedAccountConfig,
promptSingleChannelSecretInput,
resolveAccountIdForConfigure,
DEFAULT_ACCOUNT_ID,
@@ -39,38 +40,12 @@ function setNextcloudTalkAccountConfig(
accountId: string,
updates: Record<string, unknown>,
): CoreConfig {
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
...cfg,
channels: {
...cfg.channels,
"nextcloud-talk": {
...cfg.channels?.["nextcloud-talk"],
enabled: true,
...updates,
},
},
};
}
return {
...cfg,
channels: {
...cfg.channels,
"nextcloud-talk": {
...cfg.channels?.["nextcloud-talk"],
enabled: true,
accounts: {
...cfg.channels?.["nextcloud-talk"]?.accounts,
[accountId]: {
...cfg.channels?.["nextcloud-talk"]?.accounts?.[accountId],
enabled: cfg.channels?.["nextcloud-talk"]?.accounts?.[accountId]?.enabled ?? true,
...updates,
},
},
},
},
};
return patchScopedAccountConfig({
cfg,
channelKey: channel,
accountId,
patch: updates,
}) as CoreConfig;
}
async function noteNextcloudTalkSecretHelp(prompter: WizardPrompter): Promise<void> {

View File

@@ -1,6 +1,7 @@
import type { OpenClawConfig } from "openclaw/plugin-sdk/tlon";
import {
formatDocsLink,
patchScopedAccountConfig,
resolveAccountIdForConfigure,
DEFAULT_ACCOUNT_ID,
type ChannelOnboardingAdapter,
@@ -32,46 +33,30 @@ function applyAccountConfig(params: {
};
}): OpenClawConfig {
const { cfg, accountId, input } = params;
const useDefault = accountId === DEFAULT_ACCOUNT_ID;
const base = cfg.channels?.tlon ?? {};
const nextValues = {
enabled: true,
...(input.name ? { name: input.name } : {}),
...buildTlonAccountFields(input),
};
if (useDefault) {
return {
...cfg,
channels: {
...cfg.channels,
tlon: {
...base,
...nextValues,
},
},
};
if (accountId === DEFAULT_ACCOUNT_ID) {
return patchScopedAccountConfig({
cfg,
channelKey: channel,
accountId,
patch: nextValues,
ensureChannelEnabled: false,
ensureAccountEnabled: false,
});
}
return {
...cfg,
channels: {
...cfg.channels,
tlon: {
...base,
enabled: base.enabled ?? true,
accounts: {
...(base as { accounts?: Record<string, unknown> }).accounts,
[accountId]: {
...(base as { accounts?: Record<string, Record<string, unknown>> }).accounts?.[
accountId
],
...nextValues,
},
},
},
},
};
return patchScopedAccountConfig({
cfg,
channelKey: channel,
accountId,
patch: { enabled: cfg.channels?.tlon?.enabled ?? true },
accountPatch: nextValues,
ensureChannelEnabled: false,
ensureAccountEnabled: false,
});
}
async function noteTlonHelp(prompter: WizardPrompter): Promise<void> {

View File

@@ -5,10 +5,10 @@ import type {
WizardPrompter,
} from "openclaw/plugin-sdk/zalouser";
import {
DEFAULT_ACCOUNT_ID,
formatResolvedUnresolvedNote,
mergeAllowFromEntries,
normalizeAccountId,
patchScopedAccountConfig,
promptChannelAccessConfig,
resolveAccountIdForConfigure,
setTopLevelChannelDmPolicyWithAllowFrom,
@@ -36,37 +36,13 @@ function setZalouserAccountScopedConfig(
defaultPatch: Record<string, unknown>,
accountPatch: Record<string, unknown> = defaultPatch,
): OpenClawConfig {
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
...cfg,
channels: {
...cfg.channels,
zalouser: {
...cfg.channels?.zalouser,
enabled: true,
...defaultPatch,
},
},
} as OpenClawConfig;
}
return {
...cfg,
channels: {
...cfg.channels,
zalouser: {
...cfg.channels?.zalouser,
enabled: true,
accounts: {
...cfg.channels?.zalouser?.accounts,
[accountId]: {
...cfg.channels?.zalouser?.accounts?.[accountId],
enabled: cfg.channels?.zalouser?.accounts?.[accountId]?.enabled ?? true,
...accountPatch,
},
},
},
},
} as OpenClawConfig;
return patchScopedAccountConfig({
cfg,
channelKey: channel,
accountId,
patch: defaultPatch,
accountPatch,
}) as OpenClawConfig;
}
function setZalouserDmPolicy(

View File

@@ -9,7 +9,10 @@ import { promptAccountId as promptAccountIdSdk } from "../../../plugin-sdk/onboa
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../../routing/session-key.js";
import type { WizardPrompter } from "../../../wizard/prompts.js";
import type { PromptAccountId, PromptAccountIdParams } from "../onboarding-types.js";
import { moveSingleAccountChannelSectionToDefaultAccount } from "../setup-helpers.js";
import {
moveSingleAccountChannelSectionToDefaultAccount,
patchScopedAccountConfig,
} from "../setup-helpers.js";
export const promptAccountId: PromptAccountId = async (params: PromptAccountIdParams) => {
return await promptAccountIdSdk(params);
@@ -364,50 +367,14 @@ function patchConfigForScopedAccount(params: {
cfg,
channelKey: channel,
});
const channelConfig =
(seededCfg.channels?.[channel] as Record<string, unknown> | undefined) ?? {};
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
...seededCfg,
channels: {
...seededCfg.channels,
[channel]: {
...channelConfig,
...(ensureEnabled ? { enabled: true } : {}),
...patch,
},
},
};
}
const accounts =
(channelConfig.accounts as Record<string, Record<string, unknown>> | undefined) ?? {};
const existingAccount = accounts[accountId] ?? {};
return {
...seededCfg,
channels: {
...seededCfg.channels,
[channel]: {
...channelConfig,
...(ensureEnabled ? { enabled: true } : {}),
accounts: {
...accounts,
[accountId]: {
...existingAccount,
...(ensureEnabled
? {
enabled:
typeof existingAccount.enabled === "boolean" ? existingAccount.enabled : true,
}
: {}),
...patch,
},
},
},
},
};
return patchScopedAccountConfig({
cfg: seededCfg,
channelKey: channel,
accountId,
patch,
ensureChannelEnabled: ensureEnabled,
ensureAccountEnabled: ensureEnabled,
});
}
export function patchChannelConfigForAccount(params: {

View File

@@ -125,6 +125,23 @@ export function applySetupAccountConfigPatch(params: {
channelKey: string;
accountId: string;
patch: Record<string, unknown>;
}): OpenClawConfig {
return patchScopedAccountConfig({
cfg: params.cfg,
channelKey: params.channelKey,
accountId: params.accountId,
patch: params.patch,
});
}
export function patchScopedAccountConfig(params: {
cfg: OpenClawConfig;
channelKey: string;
accountId: string;
patch: Record<string, unknown>;
accountPatch?: Record<string, unknown>;
ensureChannelEnabled?: boolean;
ensureAccountEnabled?: boolean;
}): OpenClawConfig {
const accountId = normalizeAccountId(params.accountId);
const channels = params.cfg.channels as Record<string, unknown> | undefined;
@@ -135,6 +152,10 @@ export function applySetupAccountConfigPatch(params: {
accounts?: Record<string, Record<string, unknown>>;
})
: undefined;
const ensureChannelEnabled = params.ensureChannelEnabled ?? true;
const ensureAccountEnabled = params.ensureAccountEnabled ?? ensureChannelEnabled;
const patch = params.patch;
const accountPatch = params.accountPatch ?? patch;
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
...params.cfg,
@@ -142,27 +163,33 @@ export function applySetupAccountConfigPatch(params: {
...params.cfg.channels,
[params.channelKey]: {
...base,
enabled: true,
...params.patch,
...(ensureChannelEnabled ? { enabled: true } : {}),
...patch,
},
},
} as OpenClawConfig;
}
const accounts = base?.accounts ?? {};
const existingAccount = accounts[accountId] ?? {};
return {
...params.cfg,
channels: {
...params.cfg.channels,
[params.channelKey]: {
...base,
enabled: true,
...(ensureChannelEnabled ? { enabled: true } : {}),
accounts: {
...accounts,
[accountId]: {
...accounts[accountId],
enabled: true,
...params.patch,
...existingAccount,
...(ensureAccountEnabled
? {
enabled:
typeof existingAccount.enabled === "boolean" ? existingAccount.enabled : true,
}
: {}),
...accountPatch,
},
},
},

View File

@@ -46,6 +46,7 @@ export { PAIRING_APPROVED_MESSAGE } from "../channels/plugins/pairing-message.js
export {
applyAccountNameToChannelSection,
migrateBaseNameToDefaultAccount,
patchScopedAccountConfig,
} from "../channels/plugins/setup-helpers.js";
export { createAccountListHelpers } from "../channels/plugins/account-helpers.js";
export { collectBlueBubblesStatusIssues } from "../channels/plugins/status-issues/bluebubbles.js";

View File

@@ -546,7 +546,9 @@ export {
} from "../channels/plugins/config-helpers.js";
export {
applyAccountNameToChannelSection,
applySetupAccountConfigPatch,
migrateBaseNameToDefaultAccount,
patchScopedAccountConfig,
} from "../channels/plugins/setup-helpers.js";
export {
buildOpenGroupPolicyConfigureRouteAllowlistWarning,

View File

@@ -23,6 +23,7 @@ export {
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../channels/plugins/onboarding/helpers.js";
export { PAIRING_APPROVED_MESSAGE } from "../channels/plugins/pairing-message.js";
export { patchScopedAccountConfig } from "../channels/plugins/setup-helpers.js";
export type { BaseProbeResult } from "../channels/plugins/types.js";
export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";
export { getChatChannelMeta } from "../channels/registry.js";

View File

@@ -30,7 +30,10 @@ export {
resolveAccountIdForConfigure,
setTopLevelChannelDmPolicyWithAllowFrom,
} from "../channels/plugins/onboarding/helpers.js";
export { applyAccountNameToChannelSection } from "../channels/plugins/setup-helpers.js";
export {
applyAccountNameToChannelSection,
patchScopedAccountConfig,
} from "../channels/plugins/setup-helpers.js";
export { createAccountListHelpers } from "../channels/plugins/account-helpers.js";
export type { ChannelGroupContext, ChannelSetupInput } from "../channels/plugins/types.js";
export type { ChannelPlugin } from "../channels/plugins/types.plugin.js";

View File

@@ -8,7 +8,10 @@ export {
promptAccountId,
resolveAccountIdForConfigure,
} from "../channels/plugins/onboarding/helpers.js";
export { applyAccountNameToChannelSection } from "../channels/plugins/setup-helpers.js";
export {
applyAccountNameToChannelSection,
patchScopedAccountConfig,
} from "../channels/plugins/setup-helpers.js";
export type {
ChannelAccountSnapshot,
ChannelOutboundAdapter,

View File

@@ -27,6 +27,7 @@ export {
applyAccountNameToChannelSection,
applySetupAccountConfigPatch,
migrateBaseNameToDefaultAccount,
patchScopedAccountConfig,
} from "../channels/plugins/setup-helpers.js";
export { createAccountListHelpers } from "../channels/plugins/account-helpers.js";
export type {