mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-21 22:21:33 +00:00
refactor: simplify tlon and discord setup accounts
This commit is contained in:
@@ -1,10 +1,26 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
inspectDiscordSetupAccount,
|
||||
listDiscordSetupAccountIds,
|
||||
resolveDiscordSetupAccountConfig,
|
||||
} from "./setup-account-state.js";
|
||||
|
||||
describe("discord setup account state", () => {
|
||||
it("lists normalized setup account ids plus the implicit default account", () => {
|
||||
expect(
|
||||
listDiscordSetupAccountIds({
|
||||
channels: {
|
||||
discord: {
|
||||
accounts: {
|
||||
Work: { token: "work-token" },
|
||||
alerts: { token: "alerts-token" },
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toEqual(["alerts", "default", "work"]);
|
||||
});
|
||||
|
||||
it("resolves setup account config when account key casing differs from normalized id", () => {
|
||||
const resolved = resolveDiscordSetupAccountConfig({
|
||||
cfg: {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
||||
import { listCombinedAccountIds } from "openclaw/plugin-sdk/account-resolution";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import {
|
||||
hasConfiguredSecretInput,
|
||||
@@ -43,13 +44,13 @@ function inspectConfiguredToken(value: unknown): {
|
||||
|
||||
export function listDiscordSetupAccountIds(cfg: OpenClawConfig): string[] {
|
||||
const accounts = cfg.channels?.discord?.accounts;
|
||||
const ids =
|
||||
accounts && typeof accounts === "object" && !Array.isArray(accounts)
|
||||
? Object.keys(accounts)
|
||||
.map((accountId) => normalizeAccountId(accountId))
|
||||
.filter(Boolean)
|
||||
: [];
|
||||
return [...new Set([DEFAULT_ACCOUNT_ID, ...ids])];
|
||||
return listCombinedAccountIds({
|
||||
configuredAccountIds:
|
||||
accounts && typeof accounts === "object" && !Array.isArray(accounts)
|
||||
? Object.keys(accounts).map((accountId) => normalizeAccountId(accountId))
|
||||
: [],
|
||||
implicitAccountId: DEFAULT_ACCOUNT_ID,
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveDefaultDiscordSetupAccountId(cfg: OpenClawConfig): string {
|
||||
|
||||
@@ -43,9 +43,8 @@ const tlonSetupWizardProxy = createTlonSetupWizardBase({
|
||||
|
||||
const tlonConfigAdapter = createHybridChannelConfigAdapter({
|
||||
sectionKey: TLON_CHANNEL_ID,
|
||||
listAccountIds: (cfg: OpenClawConfig) => listTlonAccountIds(cfg),
|
||||
resolveAccount: (cfg: OpenClawConfig, accountId?: string | null) =>
|
||||
resolveTlonAccount(cfg, accountId ?? undefined),
|
||||
listAccountIds: listTlonAccountIds,
|
||||
resolveAccount: resolveTlonAccount,
|
||||
defaultAccountId: () => "default",
|
||||
clearBaseFields: ["ship", "code", "url", "name"],
|
||||
preserveSectionOnDefaultDelete: true,
|
||||
|
||||
81
extensions/tlon/src/types.test.ts
Normal file
81
extensions/tlon/src/types.test.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "../api.js";
|
||||
import { listTlonAccountIds, resolveTlonAccount } from "./types.js";
|
||||
|
||||
describe("tlon account helpers", () => {
|
||||
it("lists named accounts and the implicit default account", () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
tlon: {
|
||||
ship: "~zod",
|
||||
accounts: {
|
||||
Work: { ship: "~bus" },
|
||||
alerts: { ship: "~nec" },
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
expect(listTlonAccountIds(cfg)).toEqual(["alerts", "default", "work"]);
|
||||
});
|
||||
|
||||
it("merges named account config over channel defaults", () => {
|
||||
const resolved = resolveTlonAccount(
|
||||
{
|
||||
channels: {
|
||||
tlon: {
|
||||
name: "Base",
|
||||
ship: "~zod",
|
||||
url: "https://urbit.example.com",
|
||||
code: "base-code",
|
||||
dmAllowlist: ["~nec"],
|
||||
groupInviteAllowlist: ["~bus"],
|
||||
defaultAuthorizedShips: ["~marzod"],
|
||||
accounts: {
|
||||
Work: {
|
||||
name: "Work",
|
||||
code: "work-code",
|
||||
dmAllowlist: ["~rovnys"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
"work",
|
||||
);
|
||||
|
||||
expect(resolved.accountId).toBe("work");
|
||||
expect(resolved.name).toBe("Work");
|
||||
expect(resolved.ship).toBe("~zod");
|
||||
expect(resolved.url).toBe("https://urbit.example.com");
|
||||
expect(resolved.code).toBe("work-code");
|
||||
expect(resolved.dmAllowlist).toEqual(["~rovnys"]);
|
||||
expect(resolved.groupInviteAllowlist).toEqual(["~bus"]);
|
||||
expect(resolved.defaultAuthorizedShips).toEqual(["~marzod"]);
|
||||
expect(resolved.configured).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps the default account on channel-level config only", () => {
|
||||
const resolved = resolveTlonAccount(
|
||||
{
|
||||
channels: {
|
||||
tlon: {
|
||||
ship: "~zod",
|
||||
url: "https://urbit.example.com",
|
||||
code: "base-code",
|
||||
accounts: {
|
||||
default: {
|
||||
ship: "~ignored",
|
||||
code: "ignored-code",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
"default",
|
||||
);
|
||||
|
||||
expect(resolved.ship).toBe("~zod");
|
||||
expect(resolved.code).toBe("base-code");
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,30 @@
|
||||
import {
|
||||
DEFAULT_ACCOUNT_ID,
|
||||
listCombinedAccountIds,
|
||||
normalizeAccountId,
|
||||
resolveMergedAccountConfig,
|
||||
} from "openclaw/plugin-sdk/account-resolution";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
|
||||
type TlonAccountConfig = {
|
||||
name?: string;
|
||||
enabled?: boolean;
|
||||
ship?: string;
|
||||
url?: string;
|
||||
code?: string;
|
||||
allowPrivateNetwork?: boolean;
|
||||
groupChannels?: string[];
|
||||
dmAllowlist?: string[];
|
||||
groupInviteAllowlist?: string[];
|
||||
autoDiscoverChannels?: boolean;
|
||||
showModelSignature?: boolean;
|
||||
autoAcceptDmInvites?: boolean;
|
||||
autoAcceptGroupInvites?: boolean;
|
||||
defaultAuthorizedShips?: string[];
|
||||
ownerShip?: string;
|
||||
accounts?: Record<string, TlonAccountConfig>;
|
||||
};
|
||||
|
||||
export type TlonResolvedAccount = {
|
||||
accountId: string;
|
||||
name: string | null;
|
||||
@@ -22,33 +47,38 @@ export type TlonResolvedAccount = {
|
||||
ownerShip: string | null;
|
||||
};
|
||||
|
||||
function resolveTlonChannelConfig(cfg: OpenClawConfig): TlonAccountConfig | undefined {
|
||||
return cfg.channels?.tlon as TlonAccountConfig | undefined;
|
||||
}
|
||||
|
||||
function resolveMergedTlonAccountConfig(
|
||||
cfg: OpenClawConfig,
|
||||
accountId: string,
|
||||
): Record<string, unknown> & TlonAccountConfig {
|
||||
const channel = resolveTlonChannelConfig(cfg);
|
||||
if (accountId === DEFAULT_ACCOUNT_ID) {
|
||||
return (channel ?? {}) as Record<string, unknown> & TlonAccountConfig;
|
||||
}
|
||||
return resolveMergedAccountConfig<Record<string, unknown> & TlonAccountConfig>({
|
||||
channelConfig: (channel ?? {}) as Record<string, unknown> & TlonAccountConfig,
|
||||
accounts: channel?.accounts as
|
||||
| Record<string, Partial<Record<string, unknown> & TlonAccountConfig>>
|
||||
| undefined,
|
||||
accountId,
|
||||
normalizeAccountId,
|
||||
});
|
||||
}
|
||||
|
||||
export function resolveTlonAccount(
|
||||
cfg: OpenClawConfig,
|
||||
accountId?: string | null,
|
||||
): TlonResolvedAccount {
|
||||
const base = cfg.channels?.tlon as
|
||||
| {
|
||||
name?: string;
|
||||
enabled?: boolean;
|
||||
ship?: string;
|
||||
url?: string;
|
||||
code?: string;
|
||||
allowPrivateNetwork?: boolean;
|
||||
groupChannels?: string[];
|
||||
dmAllowlist?: string[];
|
||||
groupInviteAllowlist?: string[];
|
||||
autoDiscoverChannels?: boolean;
|
||||
showModelSignature?: boolean;
|
||||
autoAcceptDmInvites?: boolean;
|
||||
autoAcceptGroupInvites?: boolean;
|
||||
ownerShip?: string;
|
||||
accounts?: Record<string, Record<string, unknown>>;
|
||||
}
|
||||
| undefined;
|
||||
const resolvedAccountId = normalizeAccountId(accountId);
|
||||
const base = resolveTlonChannelConfig(cfg);
|
||||
|
||||
if (!base) {
|
||||
return {
|
||||
accountId: accountId || "default",
|
||||
accountId: resolvedAccountId,
|
||||
name: null,
|
||||
enabled: false,
|
||||
configured: false,
|
||||
@@ -68,42 +98,26 @@ export function resolveTlonAccount(
|
||||
};
|
||||
}
|
||||
|
||||
const useDefault = !accountId || accountId === "default";
|
||||
const account = useDefault ? base : base.accounts?.[accountId];
|
||||
|
||||
const ship = (account?.ship ?? base.ship ?? null) as string | null;
|
||||
const url = (account?.url ?? base.url ?? null) as string | null;
|
||||
const code = (account?.code ?? base.code ?? null) as string | null;
|
||||
const allowPrivateNetwork = (account?.allowPrivateNetwork ?? base.allowPrivateNetwork ?? null) as
|
||||
| boolean
|
||||
| null;
|
||||
const groupChannels = (account?.groupChannels ?? base.groupChannels ?? []) as string[];
|
||||
const dmAllowlist = (account?.dmAllowlist ?? base.dmAllowlist ?? []) as string[];
|
||||
const groupInviteAllowlist = (account?.groupInviteAllowlist ??
|
||||
base.groupInviteAllowlist ??
|
||||
[]) as string[];
|
||||
const autoDiscoverChannels = (account?.autoDiscoverChannels ??
|
||||
base.autoDiscoverChannels ??
|
||||
null) as boolean | null;
|
||||
const showModelSignature = (account?.showModelSignature ?? base.showModelSignature ?? null) as
|
||||
| boolean
|
||||
| null;
|
||||
const autoAcceptDmInvites = (account?.autoAcceptDmInvites ?? base.autoAcceptDmInvites ?? null) as
|
||||
| boolean
|
||||
| null;
|
||||
const autoAcceptGroupInvites = (account?.autoAcceptGroupInvites ??
|
||||
base.autoAcceptGroupInvites ??
|
||||
null) as boolean | null;
|
||||
const ownerShip = (account?.ownerShip ?? base.ownerShip ?? null) as string | null;
|
||||
const defaultAuthorizedShips = ((account as Record<string, unknown>)?.defaultAuthorizedShips ??
|
||||
(base as Record<string, unknown>)?.defaultAuthorizedShips ??
|
||||
[]) as string[];
|
||||
const merged = resolveMergedTlonAccountConfig(cfg, resolvedAccountId);
|
||||
const ship = (merged.ship ?? null) as string | null;
|
||||
const url = (merged.url ?? null) as string | null;
|
||||
const code = (merged.code ?? null) as string | null;
|
||||
const allowPrivateNetwork = (merged.allowPrivateNetwork ?? null) as boolean | null;
|
||||
const groupChannels = (merged.groupChannels ?? []) as string[];
|
||||
const dmAllowlist = (merged.dmAllowlist ?? []) as string[];
|
||||
const groupInviteAllowlist = (merged.groupInviteAllowlist ?? []) as string[];
|
||||
const autoDiscoverChannels = (merged.autoDiscoverChannels ?? null) as boolean | null;
|
||||
const showModelSignature = (merged.showModelSignature ?? null) as boolean | null;
|
||||
const autoAcceptDmInvites = (merged.autoAcceptDmInvites ?? null) as boolean | null;
|
||||
const autoAcceptGroupInvites = (merged.autoAcceptGroupInvites ?? null) as boolean | null;
|
||||
const ownerShip = (merged.ownerShip ?? null) as string | null;
|
||||
const defaultAuthorizedShips = (merged.defaultAuthorizedShips ?? []) as string[];
|
||||
const configured = Boolean(ship && url && code);
|
||||
|
||||
return {
|
||||
accountId: accountId || "default",
|
||||
name: (account?.name ?? base.name ?? null) as string | null,
|
||||
enabled: (account?.enabled ?? base.enabled ?? true) !== false,
|
||||
accountId: resolvedAccountId,
|
||||
name: (merged.name ?? null) as string | null,
|
||||
enabled: merged.enabled !== false,
|
||||
configured,
|
||||
ship,
|
||||
url,
|
||||
@@ -122,12 +136,12 @@ export function resolveTlonAccount(
|
||||
}
|
||||
|
||||
export function listTlonAccountIds(cfg: OpenClawConfig): string[] {
|
||||
const base = cfg.channels?.tlon as
|
||||
| { ship?: string; accounts?: Record<string, Record<string, unknown>> }
|
||||
| undefined;
|
||||
const base = resolveTlonChannelConfig(cfg);
|
||||
if (!base) {
|
||||
return [];
|
||||
}
|
||||
const accounts = base.accounts ?? {};
|
||||
return [...(base.ship ? ["default"] : []), ...Object.keys(accounts)];
|
||||
return listCombinedAccountIds({
|
||||
configuredAccountIds: Object.keys(base.accounts ?? {}).map(normalizeAccountId),
|
||||
implicitAccountId: base.ship ? DEFAULT_ACCOUNT_ID : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user