refactor(channels): fix account resolution and setup isolation (#114395)

* refactor(channels): fix account resolution and setup isolation

* fix(channels): keep account resolvers safely destructurable
This commit is contained in:
Peter Steinberger
2026-07-27 03:49:37 -04:00
committed by GitHub
parent e9990b818a
commit 08b1ff73e2
40 changed files with 889 additions and 708 deletions
+5 -10
View File
@@ -7,7 +7,6 @@ import {
hasConfiguredAccountValue,
} from "openclaw/plugin-sdk/account-helpers";
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import { resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
import { resolveNormalizedAccountEntry } from "openclaw/plugin-sdk/account-resolution-runtime";
import { resolveIntegerOption } from "openclaw/plugin-sdk/number-runtime";
import { resolveDefaultSecretProviderAlias } from "openclaw/plugin-sdk/provider-auth";
@@ -28,8 +27,11 @@ const DEFAULT_DISCUSSIONS_SECTION = "Sessions";
const {
listAccountIds: listClickClackAccountIds,
resolveDefaultAccountId: resolveDefaultClickClackAccountId,
} = createAccountListHelpers("clickclack", {
resolveAccountConfig: resolveMergedClickClackAccountConfig,
} = createAccountListHelpers<ClickClackAccountConfig>("clickclack", {
normalizeAccountId,
omitKeys: ["defaultAccount"],
nestedObjectKeys: ["discussions"],
hasImplicitDefaultAccount: (cfg) => {
const channel = cfg.channels?.clickclack;
return Boolean(
@@ -49,14 +51,7 @@ export function resolveClickClackAccountConfig(
accountId: string,
): ClickClackAccountConfig {
const channel = cfg.channels?.clickclack;
const merged = resolveMergedAccountConfig<ClickClackAccountConfig>({
channelConfig: cfg.channels?.clickclack as ClickClackAccountConfig | undefined,
accounts: channel?.accounts,
accountId,
omitKeys: ["defaultAccount"],
nestedObjectKeys: ["discussions"],
normalizeAccountId,
});
const merged = resolveMergedClickClackAccountConfig(cfg, accountId);
const account = resolveNormalizedAccountEntry(channel?.accounts, accountId, normalizeAccountId);
const accountTokenFile = account?.tokenFile?.trim();
if (accountTokenFile) {
+6 -56
View File
@@ -9,9 +9,9 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import {
applyAccountNameToChannelSection,
applySetupAccountConfigPatch,
migrateBaseNameToDefaultAccount,
moveSingleAccountChannelSectionToDefaultAccount,
patchScopedAccountConfig,
} from "openclaw/plugin-sdk/setup";
import { createSetupInputPresenceValidator } from "openclaw/plugin-sdk/setup-runtime";
import { resolveClickClackAccountConfig } from "./accounts.js";
@@ -160,6 +160,7 @@ export function applyClickClackSetupConfigPatch(params: {
accountId: string;
name?: string;
patch: Record<string, unknown>;
clearFields?: readonly string[];
}): OpenClawConfig {
const accountId = normalizeAccountId(params.accountId);
const scopedConfig =
@@ -183,62 +184,15 @@ export function applyClickClackSetupConfigPatch(params: {
channelKey: channel,
})
: namedConfig;
return applySetupAccountConfigPatch({
return patchScopedAccountConfig({
cfg: next,
channelKey: channel,
accountId,
patch: params.patch,
...(params.clearFields ? { clearFields: params.clearFields } : {}),
});
}
function clearClickClackSetupConfigFields(params: {
cfg: OpenClawConfig;
accountId: string;
fields: string[];
}): OpenClawConfig {
const clickclack = (params.cfg.channels as Record<string, unknown> | undefined)?.clickclack as
| (Record<string, unknown> & { accounts?: Record<string, Record<string, unknown>> })
| undefined;
if (!clickclack) {
return params.cfg;
}
const accountId = normalizeAccountId(params.accountId);
if (accountId === DEFAULT_ACCOUNT_ID) {
const nextClickClack = { ...clickclack };
for (const field of params.fields) {
delete nextClickClack[field];
}
return {
...params.cfg,
channels: {
...params.cfg.channels,
clickclack: nextClickClack,
},
} as OpenClawConfig;
}
const currentAccount = clickclack.accounts?.[accountId];
if (!currentAccount) {
return params.cfg;
}
const nextAccount = { ...currentAccount };
for (const field of params.fields) {
delete nextAccount[field];
}
return {
...params.cfg,
channels: {
...params.cfg.channels,
clickclack: {
...clickclack,
accounts: {
...clickclack.accounts,
[accountId]: nextAccount,
},
},
},
} as OpenClawConfig;
}
export function applyClickClackCredentialConfig(params: {
cfg: OpenClawConfig;
accountId: string;
@@ -253,9 +207,10 @@ export function applyClickClackCredentialConfig(params: {
: params.token !== undefined
? ["tokenFile"]
: [];
const next = applyClickClackSetupConfigPatch({
return applyClickClackSetupConfigPatch({
cfg: params.cfg,
accountId: params.accountId,
clearFields: fieldsToClear,
patch: params.useEnv
? {}
: params.tokenFile
@@ -264,11 +219,6 @@ export function applyClickClackCredentialConfig(params: {
? { token: params.token }
: {},
});
return clearClickClackSetupConfigFields({
cfg: next,
accountId: params.accountId,
fields: fieldsToClear,
});
}
export const clickClackSetupAdapter: ChannelSetupAdapter = {