refactor: share account-scoped config adapter accessors

This commit is contained in:
Peter Steinberger
2026-03-07 23:44:16 +00:00
parent b6318d4df4
commit 846ec320e2
12 changed files with 182 additions and 103 deletions

View File

@@ -1,5 +1,9 @@
import { describe, expect, it } from "vitest";
import { mapAllowFromEntries, resolveOptionalConfigString } from "./channel-config-helpers.js";
import {
createScopedAccountConfigAccessors,
mapAllowFromEntries,
resolveOptionalConfigString,
} from "./channel-config-helpers.js";
describe("mapAllowFromEntries", () => {
it("coerces allowFrom entries to strings", () => {
@@ -25,3 +29,46 @@ describe("resolveOptionalConfigString", () => {
expect(resolveOptionalConfigString(undefined)).toBeUndefined();
});
});
describe("createScopedAccountConfigAccessors", () => {
it("maps allowFrom and defaultTo from the resolved account", () => {
const accessors = createScopedAccountConfigAccessors({
resolveAccount: ({ accountId }) => ({
allowFrom: accountId ? [accountId, 42] : ["fallback"],
defaultTo: " room:123 ",
}),
resolveAllowFrom: (account) => account.allowFrom,
formatAllowFrom: (allowFrom) => allowFrom.map((entry) => String(entry).toUpperCase()),
resolveDefaultTo: (account) => account.defaultTo,
});
expect(
accessors.resolveAllowFrom?.({
cfg: {},
accountId: "owner",
}),
).toEqual(["owner", "42"]);
expect(
accessors.formatAllowFrom?.({
cfg: {},
allowFrom: ["owner"],
}),
).toEqual(["OWNER"]);
expect(
accessors.resolveDefaultTo?.({
cfg: {},
accountId: "owner",
}),
).toBe("room:123");
});
it("omits resolveDefaultTo when no selector is provided", () => {
const accessors = createScopedAccountConfigAccessors({
resolveAccount: () => ({ allowFrom: ["owner"] }),
resolveAllowFrom: (account) => account.allowFrom,
formatAllowFrom: (allowFrom) => allowFrom.map((entry) => String(entry)),
});
expect(accessors.resolveDefaultTo).toBeUndefined();
});
});

View File

@@ -1,4 +1,5 @@
import { normalizeWhatsAppAllowFromEntries } from "../channels/plugins/normalize/whatsapp.js";
import type { ChannelConfigAdapter } from "../channels/plugins/types.adapters.js";
import type { OpenClawConfig } from "../config/config.js";
import { resolveIMessageAccount } from "../imessage/accounts.js";
import { normalizeAccountId } from "../routing/session-key.js";
@@ -25,6 +26,35 @@ export function resolveOptionalConfigString(
return normalized || undefined;
}
export function createScopedAccountConfigAccessors<ResolvedAccount>(params: {
resolveAccount: (params: { cfg: OpenClawConfig; accountId?: string | null }) => ResolvedAccount;
resolveAllowFrom: (account: ResolvedAccount) => Array<string | number> | null | undefined;
formatAllowFrom: (allowFrom: Array<string | number>) => string[];
resolveDefaultTo?: (account: ResolvedAccount) => string | number | null | undefined;
}): Pick<
ChannelConfigAdapter<ResolvedAccount>,
"resolveAllowFrom" | "formatAllowFrom" | "resolveDefaultTo"
> {
const base = {
resolveAllowFrom: ({ cfg, accountId }: { cfg: OpenClawConfig; accountId?: string | null }) =>
mapAllowFromEntries(params.resolveAllowFrom(params.resolveAccount({ cfg, accountId }))),
formatAllowFrom: ({ allowFrom }: { allowFrom: Array<string | number> }) =>
params.formatAllowFrom(allowFrom),
};
if (!params.resolveDefaultTo) {
return base;
}
return {
...base,
resolveDefaultTo: ({ cfg, accountId }) =>
resolveOptionalConfigString(
params.resolveDefaultTo?.(params.resolveAccount({ cfg, accountId })),
),
};
}
export function resolveWhatsAppConfigAllowFrom(params: {
cfg: OpenClawConfig;
accountId?: string | null;

View File

@@ -380,6 +380,7 @@ export type { ChunkMode } from "../auto-reply/chunk.js";
export { SILENT_REPLY_TOKEN, isSilentReplyText } from "../auto-reply/tokens.js";
export { formatInboundFromLabel } from "../auto-reply/envelope.js";
export {
createScopedAccountConfigAccessors,
formatTrimmedAllowFromEntries,
mapAllowFromEntries,
resolveOptionalConfigString,