refactor!: rename chat providers to channels

This commit is contained in:
Peter Steinberger
2026-01-13 06:16:43 +00:00
parent 0cd632ba84
commit 90342a4f3a
393 changed files with 8004 additions and 6737 deletions

View File

@@ -7,6 +7,16 @@ import {
} from "../agents/agent-scope.js";
import { ensureAuthProfileStore } from "../agents/auth-profiles.js";
import { DEFAULT_IDENTITY_FILENAME } from "../agents/workspace.js";
import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js";
import {
getChannelPlugin,
listChannelPlugins,
} from "../channels/plugins/index.js";
import {
type ChatChannelId,
getChatChannelMeta,
normalizeChatChannelId,
} from "../channels/registry.js";
import type { ClawdbotConfig } from "../config/config.js";
import {
CONFIG_PATH_CLAWDBOT,
@@ -14,16 +24,7 @@ import {
writeConfigFile,
} from "../config/config.js";
import { resolveSessionTranscriptsDirForAgent } from "../config/sessions.js";
import { resolveProviderDefaultAccountId } from "../providers/plugins/helpers.js";
import {
getProviderPlugin,
listProviderPlugins,
} from "../providers/plugins/index.js";
import {
type ChatProviderId,
getChatProviderMeta,
normalizeChatProviderId,
} from "../providers/registry.js";
import type { AgentBinding } from "../config/types.js";
import {
DEFAULT_ACCOUNT_ID,
DEFAULT_AGENT_ID,
@@ -36,9 +37,9 @@ import { createClackPrompter } from "../wizard/clack-prompter.js";
import { WizardCancelledError } from "../wizard/prompts.js";
import { applyAuthChoice, warnIfModelConfigLooksOff } from "./auth-choice.js";
import { promptAuthChoiceGrouped } from "./auth-choice-prompt.js";
import { setupChannels } from "./onboard-channels.js";
import { ensureWorkspaceAndSessions, moveToTrash } from "./onboard-helpers.js";
import { setupProviders } from "./onboard-providers.js";
import type { ProviderChoice } from "./onboard-types.js";
import type { ChannelChoice } from "./onboard-types.js";
type AgentsListOptions = {
json?: boolean;
@@ -77,17 +78,6 @@ export type AgentSummary = {
isDefault: boolean;
};
type AgentBinding = {
agentId: string;
match: {
provider: string;
accountId?: string;
peer?: { kind: "dm" | "group" | "channel"; id: string };
guildId?: string;
teamId?: string;
};
};
type AgentEntry = NonNullable<
NonNullable<ClawdbotConfig["agents"]>["list"]
>[number];
@@ -100,7 +90,7 @@ type AgentIdentity = {
};
type ProviderAccountStatus = {
provider: ChatProviderId;
provider: ChatChannelId;
accountId: string;
name?: string;
state:
@@ -276,7 +266,7 @@ export function applyAgentConfig(
function bindingMatchKey(match: AgentBinding["match"]) {
const accountId = match.accountId?.trim() || DEFAULT_ACCOUNT_ID;
return [
match.provider,
match.channel,
accountId,
match.peer?.kind ?? "",
match.peer?.id ?? "",
@@ -438,16 +428,16 @@ function formatSummary(summary: AgentSummary) {
return lines.join("\n");
}
function providerAccountKey(provider: ChatProviderId, accountId?: string) {
function providerAccountKey(provider: ChatChannelId, accountId?: string) {
return `${provider}:${accountId ?? DEFAULT_ACCOUNT_ID}`;
}
function formatProviderAccountLabel(params: {
provider: ChatProviderId;
function formatChannelAccountLabel(params: {
provider: ChatChannelId;
accountId: string;
name?: string;
}): string {
const label = getChatProviderMeta(params.provider).label;
const label = getChatChannelMeta(params.provider).label;
const account = params.name?.trim()
? `${params.accountId} (${params.name.trim()})`
: params.accountId;
@@ -467,7 +457,7 @@ async function buildProviderStatusIndex(
): Promise<Map<string, ProviderAccountStatus>> {
const map = new Map<string, ProviderAccountStatus>();
for (const plugin of listProviderPlugins()) {
for (const plugin of listChannelPlugins()) {
const accountIds = plugin.config.listAccountIds(cfg);
for (const accountId of accountIds) {
const account = plugin.config.resolveAccount(cfg, accountId);
@@ -514,18 +504,18 @@ async function buildProviderStatusIndex(
function resolveDefaultAccountId(
cfg: ClawdbotConfig,
provider: ChatProviderId,
provider: ChatChannelId,
): string {
const plugin = getProviderPlugin(provider);
const plugin = getChannelPlugin(provider);
if (!plugin) return DEFAULT_ACCOUNT_ID;
return resolveProviderDefaultAccountId({ plugin, cfg });
return resolveChannelDefaultAccountId({ plugin, cfg });
}
function shouldShowProviderEntry(
entry: ProviderAccountStatus,
cfg: ClawdbotConfig,
): boolean {
const plugin = getProviderPlugin(entry.provider);
const plugin = getChannelPlugin(entry.provider);
if (!plugin) return Boolean(entry.configured);
if (plugin.meta.showConfigured === false) {
const providerConfig = (cfg as Record<string, unknown>)[plugin.id];
@@ -535,7 +525,7 @@ function shouldShowProviderEntry(
}
function formatProviderEntry(entry: ProviderAccountStatus): string {
const label = formatProviderAccountLabel({
const label = formatChannelAccountLabel({
provider: entry.provider,
accountId: entry.accountId,
name: entry.name,
@@ -550,14 +540,14 @@ function summarizeBindings(
if (bindings.length === 0) return [];
const seen = new Map<string, string>();
for (const binding of bindings) {
const provider = normalizeChatProviderId(binding.match.provider);
if (!provider) continue;
const channel = normalizeChatChannelId(binding.match.channel);
if (!channel) continue;
const accountId =
binding.match.accountId ?? resolveDefaultAccountId(cfg, provider);
const key = providerAccountKey(provider, accountId);
binding.match.accountId ?? resolveDefaultAccountId(cfg, channel);
const key = providerAccountKey(channel, accountId);
if (!seen.has(key)) {
const label = formatProviderAccountLabel({
provider,
const label = formatChannelAccountLabel({
provider: channel,
accountId,
});
seen.set(key, label);
@@ -628,11 +618,11 @@ export async function agentsListCommand(
if (bindings.length > 0) {
const seen = new Set<string>();
for (const binding of bindings) {
const provider = normalizeChatProviderId(binding.match.provider);
if (!provider) continue;
const channel = normalizeChatChannelId(binding.match.channel);
if (!channel) continue;
const accountId =
binding.match.accountId ?? resolveDefaultAccountId(cfg, provider);
const key = providerAccountKey(provider, accountId);
binding.match.accountId ?? resolveDefaultAccountId(cfg, channel);
const key = providerAccountKey(channel, accountId);
if (seen.has(key)) continue;
seen.add(key);
const status = providerStatus.get(key);
@@ -640,7 +630,7 @@ export async function agentsListCommand(
providerLines.push(formatProviderEntry(status));
} else {
providerLines.push(
`${formatProviderAccountLabel({ provider, accountId })}: unknown`,
`${formatChannelAccountLabel({ provider: channel, accountId })}: unknown`,
);
}
}
@@ -663,17 +653,17 @@ export async function agentsListCommand(
const lines = ["Agents:", ...summaries.map(formatSummary)];
lines.push(
"Routing rules map provider/account/peer to an agent. Use --bindings for full rules.",
"Routing rules map channel/account/peer to an agent. Use --bindings for full rules.",
);
lines.push(
"Provider status reflects local config/creds. For live health: clawdbot providers status --probe.",
"Channel status reflects local config/creds. For live health: clawdbot channels status --probe.",
);
runtime.log(lines.join("\n"));
}
function describeBinding(binding: AgentBinding) {
const match = binding.match;
const parts = [match.provider];
const parts = [match.channel];
if (match.accountId) parts.push(`accountId=${match.accountId}`);
if (match.peer) parts.push(`peer=${match.peer.kind}:${match.peer.id}`);
if (match.guildId) parts.push(`guild=${match.guildId}`);
@@ -681,23 +671,23 @@ function describeBinding(binding: AgentBinding) {
return parts.join(" ");
}
function buildProviderBindings(params: {
function buildChannelBindings(params: {
agentId: string;
selection: ProviderChoice[];
selection: ChannelChoice[];
config: ClawdbotConfig;
accountIds?: Partial<Record<ProviderChoice, string>>;
accountIds?: Partial<Record<ChannelChoice, string>>;
}): AgentBinding[] {
const bindings: AgentBinding[] = [];
const agentId = normalizeAgentId(params.agentId);
for (const provider of params.selection) {
const match: AgentBinding["match"] = { provider };
const accountId = params.accountIds?.[provider]?.trim();
for (const channel of params.selection) {
const match: AgentBinding["match"] = { channel };
const accountId = params.accountIds?.[channel]?.trim();
if (accountId) {
match.accountId = accountId;
} else {
const plugin = getProviderPlugin(provider);
const plugin = getChannelPlugin(channel);
if (plugin?.meta.forceAccountBinding) {
match.accountId = resolveDefaultAccountId(params.config, provider);
match.accountId = resolveDefaultAccountId(params.config, channel);
}
}
bindings.push({ agentId, match });
@@ -717,10 +707,10 @@ function parseBindingSpecs(params: {
for (const raw of specs) {
const trimmed = raw?.trim();
if (!trimmed) continue;
const [providerRaw, accountRaw] = trimmed.split(":", 2);
const provider = normalizeChatProviderId(providerRaw);
if (!provider) {
errors.push(`Unknown provider "${providerRaw}".`);
const [channelRaw, accountRaw] = trimmed.split(":", 2);
const channel = normalizeChatChannelId(channelRaw);
if (!channel) {
errors.push(`Unknown channel "${channelRaw}".`);
continue;
}
let accountId = accountRaw?.trim();
@@ -729,12 +719,12 @@ function parseBindingSpecs(params: {
continue;
}
if (!accountId) {
const plugin = getProviderPlugin(provider);
const plugin = getChannelPlugin(channel);
if (plugin?.meta.forceAccountBinding) {
accountId = resolveDefaultAccountId(params.config, provider);
accountId = resolveDefaultAccountId(params.config, channel);
}
}
const match: AgentBinding["match"] = { provider };
const match: AgentBinding["match"] = { channel };
if (accountId) match.accountId = accountId;
bindings.push({ agentId, match });
}
@@ -958,30 +948,30 @@ export async function agentsAddCommand(
agentDir,
});
let selection: ProviderChoice[] = [];
const providerAccountIds: Partial<Record<ProviderChoice, string>> = {};
nextConfig = await setupProviders(nextConfig, runtime, prompter, {
let selection: ChannelChoice[] = [];
const channelAccountIds: Partial<Record<ChannelChoice, string>> = {};
nextConfig = await setupChannels(nextConfig, runtime, prompter, {
allowSignalInstall: true,
onSelection: (value) => {
selection = value;
},
promptAccountIds: true,
onAccountId: (provider, accountId) => {
providerAccountIds[provider] = accountId;
onAccountId: (channel, accountId) => {
channelAccountIds[channel] = accountId;
},
});
if (selection.length > 0) {
const wantsBindings = await prompter.confirm({
message: "Route selected providers to this agent now? (bindings)",
message: "Route selected channels to this agent now? (bindings)",
initialValue: false,
});
if (wantsBindings) {
const desiredBindings = buildProviderBindings({
const desiredBindings = buildChannelBindings({
agentId,
selection,
config: nextConfig,
accountIds: providerAccountIds,
accountIds: channelAccountIds,
});
const result = applyAgentBindings(nextConfig, desiredBindings);
nextConfig = result.config;