mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 03:12:42 +00:00
refactor!: rename chat providers to channels
This commit is contained in:
196
src/commands/channels/list.ts
Normal file
196
src/commands/channels/list.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
import {
|
||||
CLAUDE_CLI_PROFILE_ID,
|
||||
CODEX_CLI_PROFILE_ID,
|
||||
loadAuthProfileStore,
|
||||
} from "../../agents/auth-profiles.js";
|
||||
import { listChannelPlugins } from "../../channels/plugins/index.js";
|
||||
import { buildChannelAccountSnapshot } from "../../channels/plugins/status.js";
|
||||
import type {
|
||||
ChannelAccountSnapshot,
|
||||
ChannelPlugin,
|
||||
} from "../../channels/plugins/types.js";
|
||||
import { withProgress } from "../../cli/progress.js";
|
||||
import {
|
||||
formatUsageReportLines,
|
||||
loadProviderUsageSummary,
|
||||
} from "../../infra/provider-usage.js";
|
||||
import { defaultRuntime, type RuntimeEnv } from "../../runtime.js";
|
||||
import { formatDocsLink } from "../../terminal/links.js";
|
||||
import { theme } from "../../terminal/theme.js";
|
||||
import { formatChannelAccountLabel, requireValidConfig } from "./shared.js";
|
||||
|
||||
export type ChannelsListOptions = {
|
||||
json?: boolean;
|
||||
usage?: boolean;
|
||||
};
|
||||
|
||||
const colorValue = (value: string) => {
|
||||
if (value === "none") return theme.error(value);
|
||||
if (value === "env") return theme.accent(value);
|
||||
return theme.success(value);
|
||||
};
|
||||
|
||||
function formatEnabled(value: boolean | undefined): string {
|
||||
return value === false ? theme.error("disabled") : theme.success("enabled");
|
||||
}
|
||||
|
||||
function formatConfigured(value: boolean): string {
|
||||
return value ? theme.success("configured") : theme.warn("not configured");
|
||||
}
|
||||
|
||||
function formatTokenSource(source?: string): string {
|
||||
const value = source || "none";
|
||||
return `token=${colorValue(value)}`;
|
||||
}
|
||||
|
||||
function formatSource(label: string, source?: string): string {
|
||||
const value = source || "none";
|
||||
return `${label}=${colorValue(value)}`;
|
||||
}
|
||||
|
||||
function formatLinked(value: boolean): string {
|
||||
return value ? theme.success("linked") : theme.warn("not linked");
|
||||
}
|
||||
|
||||
function shouldShowConfigured(channel: ChannelPlugin): boolean {
|
||||
return channel.meta.showConfigured !== false;
|
||||
}
|
||||
|
||||
function formatAccountLine(params: {
|
||||
channel: ChannelPlugin;
|
||||
snapshot: ChannelAccountSnapshot;
|
||||
}): string {
|
||||
const { channel, snapshot } = params;
|
||||
const label = formatChannelAccountLabel({
|
||||
channel: channel.id,
|
||||
accountId: snapshot.accountId,
|
||||
name: snapshot.name,
|
||||
channelStyle: theme.accent,
|
||||
accountStyle: theme.heading,
|
||||
});
|
||||
const bits: string[] = [];
|
||||
if (snapshot.linked !== undefined) {
|
||||
bits.push(formatLinked(snapshot.linked));
|
||||
}
|
||||
if (
|
||||
shouldShowConfigured(channel) &&
|
||||
typeof snapshot.configured === "boolean"
|
||||
) {
|
||||
bits.push(formatConfigured(snapshot.configured));
|
||||
}
|
||||
if (snapshot.tokenSource) {
|
||||
bits.push(formatTokenSource(snapshot.tokenSource));
|
||||
}
|
||||
if (snapshot.botTokenSource) {
|
||||
bits.push(formatSource("bot", snapshot.botTokenSource));
|
||||
}
|
||||
if (snapshot.appTokenSource) {
|
||||
bits.push(formatSource("app", snapshot.appTokenSource));
|
||||
}
|
||||
if (snapshot.baseUrl) {
|
||||
bits.push(`base=${theme.muted(snapshot.baseUrl)}`);
|
||||
}
|
||||
if (typeof snapshot.enabled === "boolean") {
|
||||
bits.push(formatEnabled(snapshot.enabled));
|
||||
}
|
||||
return `- ${label}: ${bits.join(", ")}`;
|
||||
}
|
||||
async function loadUsageWithProgress(
|
||||
runtime: RuntimeEnv,
|
||||
): Promise<Awaited<ReturnType<typeof loadProviderUsageSummary>> | null> {
|
||||
try {
|
||||
return await withProgress(
|
||||
{ label: "Fetching usage snapshot…", indeterminate: true, enabled: true },
|
||||
async () => await loadProviderUsageSummary(),
|
||||
);
|
||||
} catch (err) {
|
||||
runtime.error(String(err));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function channelsListCommand(
|
||||
opts: ChannelsListOptions,
|
||||
runtime: RuntimeEnv = defaultRuntime,
|
||||
) {
|
||||
const cfg = await requireValidConfig(runtime);
|
||||
if (!cfg) return;
|
||||
const includeUsage = opts.usage !== false;
|
||||
|
||||
const plugins = listChannelPlugins();
|
||||
|
||||
const authStore = loadAuthProfileStore();
|
||||
const authProfiles = Object.entries(authStore.profiles).map(
|
||||
([profileId, profile]) => ({
|
||||
id: profileId,
|
||||
provider: profile.provider,
|
||||
type: profile.type,
|
||||
isExternal:
|
||||
profileId === CLAUDE_CLI_PROFILE_ID ||
|
||||
profileId === CODEX_CLI_PROFILE_ID,
|
||||
}),
|
||||
);
|
||||
if (opts.json) {
|
||||
const usage = includeUsage ? await loadProviderUsageSummary() : undefined;
|
||||
const chat: Record<string, string[]> = {};
|
||||
for (const plugin of plugins) {
|
||||
chat[plugin.id] = plugin.config.listAccountIds(cfg);
|
||||
}
|
||||
const payload = { chat, auth: authProfiles, ...(usage ? { usage } : {}) };
|
||||
runtime.log(JSON.stringify(payload, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
lines.push(theme.heading("Chat channels:"));
|
||||
|
||||
for (const plugin of plugins) {
|
||||
const accounts = plugin.config.listAccountIds(cfg);
|
||||
if (!accounts || accounts.length === 0) continue;
|
||||
for (const accountId of accounts) {
|
||||
const snapshot = await buildChannelAccountSnapshot({
|
||||
plugin,
|
||||
cfg,
|
||||
accountId,
|
||||
});
|
||||
lines.push(
|
||||
formatAccountLine({
|
||||
channel: plugin,
|
||||
snapshot,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
lines.push(theme.heading("Auth providers (OAuth + API keys):"));
|
||||
if (authProfiles.length === 0) {
|
||||
lines.push(theme.muted("- none"));
|
||||
} else {
|
||||
for (const profile of authProfiles) {
|
||||
const external = profile.isExternal ? theme.muted(" (synced)") : "";
|
||||
lines.push(
|
||||
`- ${theme.accent(profile.id)} (${theme.success(profile.type)}${external})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
runtime.log(lines.join("\n"));
|
||||
|
||||
if (includeUsage) {
|
||||
runtime.log("");
|
||||
const usage = await loadUsageWithProgress(runtime);
|
||||
if (usage) {
|
||||
const usageLines = formatUsageReportLines(usage);
|
||||
if (usageLines.length > 0) {
|
||||
usageLines[0] = theme.accent(usageLines[0]);
|
||||
runtime.log(usageLines.join("\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runtime.log("");
|
||||
runtime.log(
|
||||
`Docs: ${formatDocsLink("/gateway/configuration", "gateway/configuration")}`,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user