Files
openclaw/src/commands/channel-account-context.ts
2026-04-06 22:44:14 +01:00

158 lines
4.6 KiB
TypeScript

import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js";
import type { ChannelPlugin } from "../channels/plugins/types.js";
import { inspectReadOnlyChannelAccount } from "../channels/read-only-account-inspect.js";
import type { OpenClawConfig } from "../config/config.js";
import { formatErrorMessage } from "../infra/errors.js";
import { isRecord } from "../utils.js";
export type ChannelDefaultAccountContext = {
accountIds: string[];
defaultAccountId: string;
account: unknown;
enabled: boolean;
configured: boolean;
diagnostics: string[];
/**
* Indicates read-only resolution was used instead of strict full-account resolution.
* This is expected for read_only mode and does not necessarily mean an error occurred.
*/
degraded: boolean;
};
export type ChannelAccountContextMode = "strict" | "read_only";
function getBooleanField(value: unknown, key: string): boolean | undefined {
const record = isRecord(value) ? value : null;
if (!record) {
return undefined;
}
return typeof record[key] === "boolean" ? record[key] : undefined;
}
function formatContextDiagnostic(params: {
commandName?: string;
pluginId: string;
accountId: string;
message: string;
}): string {
const prefix = params.commandName ? `${params.commandName}: ` : "";
return `${prefix}channels.${params.pluginId}.accounts.${params.accountId}: ${params.message}`;
}
export async function resolveDefaultChannelAccountContext(
plugin: ChannelPlugin,
cfg: OpenClawConfig,
options?: { mode?: ChannelAccountContextMode; commandName?: string },
): Promise<ChannelDefaultAccountContext> {
const mode = options?.mode ?? "strict";
const accountIds = plugin.config.listAccountIds(cfg);
const defaultAccountId = resolveChannelDefaultAccountId({
plugin,
cfg,
accountIds,
});
if (mode === "strict") {
const account = plugin.config.resolveAccount(cfg, defaultAccountId);
const enabled = plugin.config.isEnabled ? plugin.config.isEnabled(account, cfg) : true;
const configured = plugin.config.isConfigured
? await plugin.config.isConfigured(account, cfg)
: true;
return {
accountIds,
defaultAccountId,
account,
enabled,
configured,
diagnostics: [],
degraded: false,
};
}
const diagnostics: string[] = [];
let degraded = false;
const inspected =
plugin.config.inspectAccount?.(cfg, defaultAccountId) ??
(await inspectReadOnlyChannelAccount({
channelId: plugin.id,
cfg,
accountId: defaultAccountId,
}));
let account = inspected;
if (!account) {
try {
account = plugin.config.resolveAccount(cfg, defaultAccountId);
} catch (error) {
degraded = true;
diagnostics.push(
formatContextDiagnostic({
commandName: options?.commandName,
pluginId: plugin.id,
accountId: defaultAccountId,
message: `failed to resolve account (${formatErrorMessage(error)}); skipping read-only checks.`,
}),
);
return {
accountIds,
defaultAccountId,
account: {},
enabled: false,
configured: false,
diagnostics,
degraded,
};
}
} else {
degraded = true;
}
const inspectEnabled = getBooleanField(account, "enabled");
let enabled = inspectEnabled ?? true;
if (inspectEnabled === undefined && plugin.config.isEnabled) {
try {
enabled = plugin.config.isEnabled(account, cfg);
} catch (error) {
degraded = true;
enabled = false;
diagnostics.push(
formatContextDiagnostic({
commandName: options?.commandName,
pluginId: plugin.id,
accountId: defaultAccountId,
message: `failed to evaluate enabled state (${formatErrorMessage(error)}); treating as disabled.`,
}),
);
}
}
const inspectConfigured = getBooleanField(account, "configured");
let configured = inspectConfigured ?? true;
if (inspectConfigured === undefined && plugin.config.isConfigured) {
try {
configured = await plugin.config.isConfigured(account, cfg);
} catch (error) {
degraded = true;
configured = false;
diagnostics.push(
formatContextDiagnostic({
commandName: options?.commandName,
pluginId: plugin.id,
accountId: defaultAccountId,
message: `failed to evaluate configured state (${formatErrorMessage(error)}); treating as unconfigured.`,
}),
);
}
}
return {
accountIds,
defaultAccountId,
account,
enabled,
configured,
diagnostics,
degraded,
};
}