fix: honor twitch default runtime account

This commit is contained in:
Tak Hoffman
2026-04-03 14:46:18 -05:00
parent 1ff586cda1
commit f56a9f3b3b
4 changed files with 101 additions and 6 deletions

View File

@@ -1,5 +1,10 @@
import { describe, expect, it } from "vitest";
import { getAccountConfig, listAccountIds } from "./config.js";
import {
getAccountConfig,
listAccountIds,
resolveDefaultTwitchAccountId,
resolveTwitchAccountContext,
} from "./config.js";
describe("getAccountConfig", () => {
const mockMultiAccountConfig = {
@@ -116,3 +121,46 @@ describe("listAccountIds", () => {
).toEqual(["default", "secondary"]);
});
});
describe("resolveDefaultTwitchAccountId", () => {
it("prefers channels.twitch.defaultAccount when configured", () => {
expect(
resolveDefaultTwitchAccountId({
channels: {
twitch: {
defaultAccount: "secondary",
accounts: {
default: { username: "default" },
secondary: { username: "secondary" },
},
},
},
} as Parameters<typeof resolveDefaultTwitchAccountId>[0]),
).toBe("secondary");
});
});
describe("resolveTwitchAccountContext", () => {
it("uses configured defaultAccount when accountId is omitted", () => {
const context = resolveTwitchAccountContext({
channels: {
twitch: {
defaultAccount: "secondary",
accounts: {
default: {
username: "default-bot",
accessToken: "oauth:default-token",
},
secondary: {
username: "second-bot",
accessToken: "oauth:second-token",
},
},
},
},
} as Parameters<typeof resolveTwitchAccountContext>[0]);
expect(context.accountId).toBe("secondary");
expect(context.account?.username).toBe("second-bot");
});
});

View File

@@ -118,11 +118,26 @@ export function listAccountIds(cfg: OpenClawConfig): string[] {
});
}
export function resolveDefaultTwitchAccountId(cfg: OpenClawConfig): string {
const preferred =
typeof cfg.channels?.twitch?.defaultAccount === "string"
? cfg.channels.twitch.defaultAccount.trim()
: "";
const ids = listAccountIds(cfg);
if (preferred && ids.includes(preferred)) {
return preferred;
}
if (ids.includes(DEFAULT_ACCOUNT_ID)) {
return DEFAULT_ACCOUNT_ID;
}
return ids[0] ?? DEFAULT_ACCOUNT_ID;
}
export function resolveTwitchAccountContext(
cfg: OpenClawConfig,
accountId?: string | null,
): ResolvedTwitchAccountContext {
const resolvedAccountId = accountId?.trim() || DEFAULT_ACCOUNT_ID;
const resolvedAccountId = accountId?.trim() || resolveDefaultTwitchAccountId(cfg);
const account = getAccountConfig(cfg, resolvedAccountId);
const tokenResolution = resolveTwitchToken(cfg, { accountId: resolvedAccountId });
return {

View File

@@ -44,3 +44,34 @@ describe("twitchPlugin.status.buildAccountSnapshot", () => {
expect(snapshot?.accountId).toBe("secondary");
});
});
describe("twitchPlugin.config", () => {
it("uses configured defaultAccount for omitted-account plugin resolution", () => {
const cfg = {
channels: {
twitch: {
defaultAccount: "secondary",
accounts: {
default: {
channel: "default-channel",
username: "default",
accessToken: "oauth:default-token",
clientId: "default-client",
enabled: true,
},
secondary: {
channel: "secondary-channel",
username: "secondary",
accessToken: "oauth:secondary-token",
clientId: "secondary-client",
enabled: true,
},
},
},
},
} as OpenClawConfig;
expect(twitchPlugin.config.defaultAccountId?.(cfg)).toBe("secondary");
expect(twitchPlugin.config.resolveAccount(cfg).accountId).toBe("secondary");
});
});

View File

@@ -25,6 +25,7 @@ import {
DEFAULT_ACCOUNT_ID,
getAccountConfig,
listAccountIds,
resolveDefaultTwitchAccountId,
resolveTwitchAccountContext,
resolveTwitchSnapshotAccountId,
} from "./config.js";
@@ -81,7 +82,7 @@ export const twitchPlugin: ChannelPlugin<ResolvedTwitchAccount> =
config: {
listAccountIds: (cfg: OpenClawConfig): string[] => listAccountIds(cfg),
resolveAccount: (cfg: OpenClawConfig, accountId?: string | null): ResolvedTwitchAccount => {
const resolvedAccountId = accountId ?? DEFAULT_ACCOUNT_ID;
const resolvedAccountId = accountId ?? resolveDefaultTwitchAccountId(cfg);
const account = getAccountConfig(cfg, resolvedAccountId);
if (!account) {
return {
@@ -98,9 +99,9 @@ export const twitchPlugin: ChannelPlugin<ResolvedTwitchAccount> =
...account,
};
},
defaultAccountId: (): string => DEFAULT_ACCOUNT_ID,
defaultAccountId: (cfg: OpenClawConfig): string => resolveDefaultTwitchAccountId(cfg),
isConfigured: (_account: unknown, cfg: OpenClawConfig): boolean =>
resolveTwitchAccountContext(cfg, DEFAULT_ACCOUNT_ID).configured,
resolveTwitchAccountContext(cfg).configured,
isEnabled: (account: ResolvedTwitchAccount | undefined): boolean =>
account?.enabled !== false,
describeAccount: (account: TwitchAccountConfig | undefined) =>
@@ -130,7 +131,7 @@ export const twitchPlugin: ChannelPlugin<ResolvedTwitchAccount> =
kind: ChannelResolveKind;
runtime: import("openclaw/plugin-sdk/runtime-env").RuntimeEnv;
}): Promise<ChannelResolveResult[]> => {
const account = getAccountConfig(cfg, accountId ?? DEFAULT_ACCOUNT_ID);
const account = getAccountConfig(cfg, accountId ?? resolveDefaultTwitchAccountId(cfg));
if (!account) {
return inputs.map((input) => ({
input,