feat(cli): clarify agents list output
This commit is contained in:
@@ -1,8 +1,12 @@
|
|||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
resolveAgentDir,
|
resolveAgentDir,
|
||||||
resolveAgentWorkspaceDir,
|
resolveAgentWorkspaceDir,
|
||||||
} from "../agents/agent-scope.js";
|
} from "../agents/agent-scope.js";
|
||||||
import { ensureAuthProfileStore } from "../agents/auth-profiles.js";
|
import { ensureAuthProfileStore } from "../agents/auth-profiles.js";
|
||||||
|
import { DEFAULT_IDENTITY_FILENAME } from "../agents/workspace.js";
|
||||||
import type { ClawdbotConfig } from "../config/config.js";
|
import type { ClawdbotConfig } from "../config/config.js";
|
||||||
import {
|
import {
|
||||||
CONFIG_PATH_CLAWDBOT,
|
CONFIG_PATH_CLAWDBOT,
|
||||||
@@ -10,6 +14,21 @@ import {
|
|||||||
writeConfigFile,
|
writeConfigFile,
|
||||||
} from "../config/config.js";
|
} from "../config/config.js";
|
||||||
import { resolveSessionTranscriptsDirForAgent } from "../config/sessions.js";
|
import { resolveSessionTranscriptsDirForAgent } from "../config/sessions.js";
|
||||||
|
import {
|
||||||
|
listDiscordAccountIds,
|
||||||
|
resolveDefaultDiscordAccountId,
|
||||||
|
resolveDiscordAccount,
|
||||||
|
} from "../discord/accounts.js";
|
||||||
|
import {
|
||||||
|
listIMessageAccountIds,
|
||||||
|
resolveDefaultIMessageAccountId,
|
||||||
|
resolveIMessageAccount,
|
||||||
|
} from "../imessage/accounts.js";
|
||||||
|
import {
|
||||||
|
type ChatProviderId,
|
||||||
|
getChatProviderMeta,
|
||||||
|
normalizeChatProviderId,
|
||||||
|
} from "../providers/registry.js";
|
||||||
import {
|
import {
|
||||||
DEFAULT_ACCOUNT_ID,
|
DEFAULT_ACCOUNT_ID,
|
||||||
DEFAULT_AGENT_ID,
|
DEFAULT_AGENT_ID,
|
||||||
@@ -17,9 +36,28 @@ import {
|
|||||||
} from "../routing/session-key.js";
|
} from "../routing/session-key.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
import { defaultRuntime } from "../runtime.js";
|
import { defaultRuntime } from "../runtime.js";
|
||||||
|
import {
|
||||||
|
listSignalAccountIds,
|
||||||
|
resolveDefaultSignalAccountId,
|
||||||
|
resolveSignalAccount,
|
||||||
|
} from "../signal/accounts.js";
|
||||||
|
import {
|
||||||
|
listSlackAccountIds,
|
||||||
|
resolveDefaultSlackAccountId,
|
||||||
|
resolveSlackAccount,
|
||||||
|
} from "../slack/accounts.js";
|
||||||
|
import {
|
||||||
|
listTelegramAccountIds,
|
||||||
|
resolveDefaultTelegramAccountId,
|
||||||
|
resolveTelegramAccount,
|
||||||
|
} from "../telegram/accounts.js";
|
||||||
import { resolveUserPath } from "../utils.js";
|
import { resolveUserPath } from "../utils.js";
|
||||||
import { normalizeChatProviderId } from "../providers/registry.js";
|
import {
|
||||||
import { resolveDefaultWhatsAppAccountId } from "../web/accounts.js";
|
listWhatsAppAccountIds,
|
||||||
|
resolveDefaultWhatsAppAccountId,
|
||||||
|
resolveWhatsAppAuthDir,
|
||||||
|
} from "../web/accounts.js";
|
||||||
|
import { webAuthExists } from "../web/session.js";
|
||||||
import { createClackPrompter } from "../wizard/clack-prompter.js";
|
import { createClackPrompter } from "../wizard/clack-prompter.js";
|
||||||
import { WizardCancelledError } from "../wizard/prompts.js";
|
import { WizardCancelledError } from "../wizard/prompts.js";
|
||||||
import { applyAuthChoice, warnIfModelConfigLooksOff } from "./auth-choice.js";
|
import { applyAuthChoice, warnIfModelConfigLooksOff } from "./auth-choice.js";
|
||||||
@@ -52,11 +90,16 @@ type AgentsDeleteOptions = {
|
|||||||
export type AgentSummary = {
|
export type AgentSummary = {
|
||||||
id: string;
|
id: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
identityName?: string;
|
||||||
|
identityEmoji?: string;
|
||||||
|
identitySource?: "identity" | "config";
|
||||||
workspace: string;
|
workspace: string;
|
||||||
agentDir: string;
|
agentDir: string;
|
||||||
model?: string;
|
model?: string;
|
||||||
bindings: number;
|
bindings: number;
|
||||||
bindingDetails?: string[];
|
bindingDetails?: string[];
|
||||||
|
routes?: string[];
|
||||||
|
providers?: string[];
|
||||||
isDefault: boolean;
|
isDefault: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,6 +114,28 @@ type AgentBinding = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type AgentIdentity = {
|
||||||
|
name?: string;
|
||||||
|
emoji?: string;
|
||||||
|
creature?: string;
|
||||||
|
vibe?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ProviderAccountStatus = {
|
||||||
|
provider: ChatProviderId;
|
||||||
|
accountId: string;
|
||||||
|
name?: string;
|
||||||
|
state:
|
||||||
|
| "linked"
|
||||||
|
| "not linked"
|
||||||
|
| "configured"
|
||||||
|
| "not configured"
|
||||||
|
| "enabled"
|
||||||
|
| "disabled";
|
||||||
|
enabled?: boolean;
|
||||||
|
configured?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
function createQuietRuntime(runtime: RuntimeEnv): RuntimeEnv {
|
function createQuietRuntime(runtime: RuntimeEnv): RuntimeEnv {
|
||||||
return { ...runtime, log: () => {} };
|
return { ...runtime, log: () => {} };
|
||||||
}
|
}
|
||||||
@@ -88,6 +153,35 @@ function resolveAgentModel(cfg: ClawdbotConfig, agentId: string) {
|
|||||||
return raw?.primary?.trim() || undefined;
|
return raw?.primary?.trim() || undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseIdentityMarkdown(content: string): AgentIdentity {
|
||||||
|
const identity: AgentIdentity = {};
|
||||||
|
const lines = content.split(/\r?\n/);
|
||||||
|
for (const line of lines) {
|
||||||
|
const match = line.match(/^\s*(?:-\s*)?([A-Za-z ]+):\s*(.+?)\s*$/);
|
||||||
|
if (!match) continue;
|
||||||
|
const label = match[1]?.trim().toLowerCase();
|
||||||
|
const value = match[2]?.trim();
|
||||||
|
if (!value) continue;
|
||||||
|
if (label === "name") identity.name = value;
|
||||||
|
if (label === "emoji") identity.emoji = value;
|
||||||
|
if (label === "creature") identity.creature = value;
|
||||||
|
if (label === "vibe") identity.vibe = value;
|
||||||
|
}
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadAgentIdentity(workspace: string): AgentIdentity | null {
|
||||||
|
const identityPath = path.join(workspace, DEFAULT_IDENTITY_FILENAME);
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync(identityPath, "utf-8");
|
||||||
|
const parsed = parseIdentityMarkdown(content);
|
||||||
|
if (!parsed.name && !parsed.emoji) return null;
|
||||||
|
return parsed;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function buildAgentSummaries(cfg: ClawdbotConfig): AgentSummary[] {
|
export function buildAgentSummaries(cfg: ClawdbotConfig): AgentSummary[] {
|
||||||
const defaultAgentId = normalizeAgentId(
|
const defaultAgentId = normalizeAgentId(
|
||||||
cfg.routing?.defaultAgentId ?? DEFAULT_AGENT_ID,
|
cfg.routing?.defaultAgentId ?? DEFAULT_AGENT_ID,
|
||||||
@@ -111,15 +205,30 @@ export function buildAgentSummaries(cfg: ClawdbotConfig): AgentSummary[] {
|
|||||||
.sort((a, b) => a.localeCompare(b)),
|
.sort((a, b) => a.localeCompare(b)),
|
||||||
];
|
];
|
||||||
|
|
||||||
return ordered.map((id) => ({
|
return ordered.map((id) => {
|
||||||
id,
|
const workspace = resolveAgentWorkspaceDir(cfg, id);
|
||||||
name: resolveAgentName(cfg, id),
|
const identity = loadAgentIdentity(workspace);
|
||||||
workspace: resolveAgentWorkspaceDir(cfg, id),
|
const fallbackIdentity = id === defaultAgentId ? cfg.identity : undefined;
|
||||||
agentDir: resolveAgentDir(cfg, id),
|
const identityName = identity?.name ?? fallbackIdentity?.name?.trim();
|
||||||
model: resolveAgentModel(cfg, id),
|
const identityEmoji = identity?.emoji ?? fallbackIdentity?.emoji?.trim();
|
||||||
bindings: bindingCounts.get(id) ?? 0,
|
const identitySource = identity
|
||||||
isDefault: id === defaultAgentId,
|
? "identity"
|
||||||
}));
|
: fallbackIdentity && (identityName || identityEmoji)
|
||||||
|
? "config"
|
||||||
|
: undefined;
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: resolveAgentName(cfg, id),
|
||||||
|
identityName,
|
||||||
|
identityEmoji,
|
||||||
|
identitySource,
|
||||||
|
workspace,
|
||||||
|
agentDir: resolveAgentDir(cfg, id),
|
||||||
|
model: resolveAgentModel(cfg, id),
|
||||||
|
bindings: bindingCounts.get(id) ?? 0,
|
||||||
|
isDefault: id === defaultAgentId,
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyAgentConfig(
|
export function applyAgentConfig(
|
||||||
@@ -271,25 +380,230 @@ export function pruneAgentConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function formatSummary(summary: AgentSummary) {
|
function formatSummary(summary: AgentSummary) {
|
||||||
const name =
|
|
||||||
summary.name && summary.name !== summary.id ? ` "${summary.name}"` : "";
|
|
||||||
const defaultTag = summary.isDefault ? " (default)" : "";
|
const defaultTag = summary.isDefault ? " (default)" : "";
|
||||||
const parts = [
|
const header =
|
||||||
`${summary.id}${name}${defaultTag}`,
|
summary.name && summary.name !== summary.id
|
||||||
`workspace: ${summary.workspace}`,
|
? `${summary.id}${defaultTag} (${summary.name})`
|
||||||
`agentDir: ${summary.agentDir}`,
|
: `${summary.id}${defaultTag}`;
|
||||||
summary.model ? `model: ${summary.model}` : null,
|
|
||||||
`bindings: ${summary.bindings}`,
|
const identityParts = [];
|
||||||
].filter(Boolean);
|
if (summary.identityEmoji) identityParts.push(summary.identityEmoji);
|
||||||
const lines = [`- ${parts.join(" | ")}`];
|
if (summary.identityName) identityParts.push(summary.identityName);
|
||||||
|
const identityLine =
|
||||||
|
identityParts.length > 0 ? identityParts.join(" ") : null;
|
||||||
|
const identitySource =
|
||||||
|
summary.identitySource === "identity"
|
||||||
|
? "IDENTITY.md"
|
||||||
|
: summary.identitySource === "config"
|
||||||
|
? "config"
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const lines = [`- ${header}`];
|
||||||
|
if (identityLine) {
|
||||||
|
lines.push(
|
||||||
|
` Identity: ${identityLine}${identitySource ? ` (${identitySource})` : ""}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
lines.push(` Workspace: ${summary.workspace}`);
|
||||||
|
lines.push(` Agent dir: ${summary.agentDir}`);
|
||||||
|
if (summary.model) lines.push(` Model: ${summary.model}`);
|
||||||
|
lines.push(` Routing rules: ${summary.bindings}`);
|
||||||
|
|
||||||
|
if (summary.routes?.length) {
|
||||||
|
lines.push(` Routing: ${summary.routes.join(", ")}`);
|
||||||
|
}
|
||||||
|
if (summary.providers?.length) {
|
||||||
|
lines.push(" Providers:");
|
||||||
|
for (const provider of summary.providers) {
|
||||||
|
lines.push(` - ${provider}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (summary.bindingDetails?.length) {
|
if (summary.bindingDetails?.length) {
|
||||||
|
lines.push(" Routing rules:");
|
||||||
for (const binding of summary.bindingDetails) {
|
for (const binding of summary.bindingDetails) {
|
||||||
lines.push(` - ${binding}`);
|
lines.push(` - ${binding}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lines.join("\n");
|
return lines.join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function providerAccountKey(provider: ChatProviderId, accountId?: string) {
|
||||||
|
return `${provider}:${accountId ?? DEFAULT_ACCOUNT_ID}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatProviderAccountLabel(params: {
|
||||||
|
provider: ChatProviderId;
|
||||||
|
accountId: string;
|
||||||
|
name?: string;
|
||||||
|
}): string {
|
||||||
|
const label = getChatProviderMeta(params.provider).label;
|
||||||
|
const account = params.name?.trim()
|
||||||
|
? `${params.accountId} (${params.name.trim()})`
|
||||||
|
: params.accountId;
|
||||||
|
return `${label} ${account}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatProviderState(entry: ProviderAccountStatus): string {
|
||||||
|
const parts = [entry.state];
|
||||||
|
if (entry.enabled === false && entry.state !== "disabled") {
|
||||||
|
parts.push("disabled");
|
||||||
|
}
|
||||||
|
return parts.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildProviderStatusIndex(
|
||||||
|
cfg: ClawdbotConfig,
|
||||||
|
): Promise<Map<string, ProviderAccountStatus>> {
|
||||||
|
const map = new Map<string, ProviderAccountStatus>();
|
||||||
|
|
||||||
|
for (const accountId of listWhatsAppAccountIds(cfg)) {
|
||||||
|
const { authDir } = resolveWhatsAppAuthDir({ cfg, accountId });
|
||||||
|
const linked = await webAuthExists(authDir);
|
||||||
|
const enabled =
|
||||||
|
cfg.whatsapp?.accounts?.[accountId]?.enabled ?? cfg.web?.enabled ?? true;
|
||||||
|
const hasConfig = Boolean(cfg.whatsapp);
|
||||||
|
map.set(providerAccountKey("whatsapp", accountId), {
|
||||||
|
provider: "whatsapp",
|
||||||
|
accountId,
|
||||||
|
name: cfg.whatsapp?.accounts?.[accountId]?.name,
|
||||||
|
state: linked ? "linked" : "not linked",
|
||||||
|
enabled,
|
||||||
|
configured: linked || hasConfig,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const accountId of listTelegramAccountIds(cfg)) {
|
||||||
|
const account = resolveTelegramAccount({ cfg, accountId });
|
||||||
|
const configured = Boolean(account.token);
|
||||||
|
map.set(providerAccountKey("telegram", accountId), {
|
||||||
|
provider: "telegram",
|
||||||
|
accountId,
|
||||||
|
name: account.name,
|
||||||
|
state: configured ? "configured" : "not configured",
|
||||||
|
enabled: account.enabled,
|
||||||
|
configured,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const accountId of listDiscordAccountIds(cfg)) {
|
||||||
|
const account = resolveDiscordAccount({ cfg, accountId });
|
||||||
|
const configured = Boolean(account.token);
|
||||||
|
map.set(providerAccountKey("discord", accountId), {
|
||||||
|
provider: "discord",
|
||||||
|
accountId,
|
||||||
|
name: account.name,
|
||||||
|
state: configured ? "configured" : "not configured",
|
||||||
|
enabled: account.enabled,
|
||||||
|
configured,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const accountId of listSlackAccountIds(cfg)) {
|
||||||
|
const account = resolveSlackAccount({ cfg, accountId });
|
||||||
|
const configured = Boolean(account.botToken && account.appToken);
|
||||||
|
map.set(providerAccountKey("slack", accountId), {
|
||||||
|
provider: "slack",
|
||||||
|
accountId,
|
||||||
|
name: account.name,
|
||||||
|
state: configured ? "configured" : "not configured",
|
||||||
|
enabled: account.enabled,
|
||||||
|
configured,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const accountId of listSignalAccountIds(cfg)) {
|
||||||
|
const account = resolveSignalAccount({ cfg, accountId });
|
||||||
|
map.set(providerAccountKey("signal", accountId), {
|
||||||
|
provider: "signal",
|
||||||
|
accountId,
|
||||||
|
name: account.name,
|
||||||
|
state: account.configured ? "configured" : "not configured",
|
||||||
|
enabled: account.enabled,
|
||||||
|
configured: account.configured,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const accountId of listIMessageAccountIds(cfg)) {
|
||||||
|
const account = resolveIMessageAccount({ cfg, accountId });
|
||||||
|
map.set(providerAccountKey("imessage", accountId), {
|
||||||
|
provider: "imessage",
|
||||||
|
accountId,
|
||||||
|
name: account.name,
|
||||||
|
state: account.enabled ? "enabled" : "disabled",
|
||||||
|
enabled: account.enabled,
|
||||||
|
configured: Boolean(cfg.imessage),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveDefaultAccountId(
|
||||||
|
cfg: ClawdbotConfig,
|
||||||
|
provider: ChatProviderId,
|
||||||
|
): string {
|
||||||
|
switch (provider) {
|
||||||
|
case "whatsapp":
|
||||||
|
return resolveDefaultWhatsAppAccountId(cfg) || DEFAULT_ACCOUNT_ID;
|
||||||
|
case "telegram":
|
||||||
|
return resolveDefaultTelegramAccountId(cfg) || DEFAULT_ACCOUNT_ID;
|
||||||
|
case "discord":
|
||||||
|
return resolveDefaultDiscordAccountId(cfg) || DEFAULT_ACCOUNT_ID;
|
||||||
|
case "slack":
|
||||||
|
return resolveDefaultSlackAccountId(cfg) || DEFAULT_ACCOUNT_ID;
|
||||||
|
case "signal":
|
||||||
|
return resolveDefaultSignalAccountId(cfg) || DEFAULT_ACCOUNT_ID;
|
||||||
|
case "imessage":
|
||||||
|
return resolveDefaultIMessageAccountId(cfg) || DEFAULT_ACCOUNT_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldShowProviderEntry(
|
||||||
|
entry: ProviderAccountStatus,
|
||||||
|
cfg: ClawdbotConfig,
|
||||||
|
): boolean {
|
||||||
|
if (entry.provider === "whatsapp") {
|
||||||
|
return entry.state === "linked" || Boolean(cfg.whatsapp);
|
||||||
|
}
|
||||||
|
if (entry.provider === "imessage") {
|
||||||
|
return Boolean(cfg.imessage);
|
||||||
|
}
|
||||||
|
return Boolean(entry.configured);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatProviderEntry(entry: ProviderAccountStatus): string {
|
||||||
|
const label = formatProviderAccountLabel({
|
||||||
|
provider: entry.provider,
|
||||||
|
accountId: entry.accountId,
|
||||||
|
name: entry.name,
|
||||||
|
});
|
||||||
|
return `${label}: ${formatProviderState(entry)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeBindings(
|
||||||
|
cfg: ClawdbotConfig,
|
||||||
|
bindings: AgentBinding[],
|
||||||
|
): string[] {
|
||||||
|
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 accountId =
|
||||||
|
binding.match.accountId ?? resolveDefaultAccountId(cfg, provider);
|
||||||
|
const key = providerAccountKey(provider, accountId);
|
||||||
|
if (!seen.has(key)) {
|
||||||
|
const label = formatProviderAccountLabel({
|
||||||
|
provider,
|
||||||
|
accountId,
|
||||||
|
});
|
||||||
|
seen.set(key, label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...seen.values()];
|
||||||
|
}
|
||||||
|
|
||||||
async function requireValidConfig(
|
async function requireValidConfig(
|
||||||
runtime: RuntimeEnv,
|
runtime: RuntimeEnv,
|
||||||
): Promise<ClawdbotConfig | null> {
|
): Promise<ClawdbotConfig | null> {
|
||||||
@@ -317,28 +631,82 @@ export async function agentsListCommand(
|
|||||||
if (!cfg) return;
|
if (!cfg) return;
|
||||||
|
|
||||||
const summaries = buildAgentSummaries(cfg);
|
const summaries = buildAgentSummaries(cfg);
|
||||||
|
const bindingMap = new Map<string, AgentBinding[]>();
|
||||||
|
for (const binding of cfg.routing?.bindings ?? []) {
|
||||||
|
const agentId = normalizeAgentId(binding.agentId);
|
||||||
|
const list = bindingMap.get(agentId) ?? [];
|
||||||
|
list.push(binding as AgentBinding);
|
||||||
|
bindingMap.set(agentId, list);
|
||||||
|
}
|
||||||
|
|
||||||
if (opts.bindings) {
|
if (opts.bindings) {
|
||||||
const bindingMap = new Map<string, string[]>();
|
|
||||||
for (const binding of cfg.routing?.bindings ?? []) {
|
|
||||||
const agentId = normalizeAgentId(binding.agentId);
|
|
||||||
const list = bindingMap.get(agentId) ?? [];
|
|
||||||
list.push(describeBinding(binding as AgentBinding));
|
|
||||||
bindingMap.set(agentId, list);
|
|
||||||
}
|
|
||||||
for (const summary of summaries) {
|
for (const summary of summaries) {
|
||||||
const details = bindingMap.get(summary.id);
|
const bindings = bindingMap.get(summary.id) ?? [];
|
||||||
if (details && details.length > 0) {
|
if (bindings.length > 0) {
|
||||||
summary.bindingDetails = details;
|
summary.bindingDetails = bindings.map((binding) =>
|
||||||
|
describeBinding(binding as AgentBinding),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const providerStatus = await buildProviderStatusIndex(cfg);
|
||||||
|
const allProviderEntries = [...providerStatus.values()];
|
||||||
|
|
||||||
|
for (const summary of summaries) {
|
||||||
|
const bindings = bindingMap.get(summary.id) ?? [];
|
||||||
|
const routes = summarizeBindings(cfg, bindings);
|
||||||
|
if (routes.length > 0) {
|
||||||
|
summary.routes = routes;
|
||||||
|
} else if (summary.isDefault) {
|
||||||
|
summary.routes = ["default (no explicit rules)"];
|
||||||
|
}
|
||||||
|
|
||||||
|
const providerLines: string[] = [];
|
||||||
|
if (bindings.length > 0) {
|
||||||
|
const seen = new Set<string>();
|
||||||
|
for (const binding of bindings) {
|
||||||
|
const provider = normalizeChatProviderId(binding.match.provider);
|
||||||
|
if (!provider) continue;
|
||||||
|
const accountId =
|
||||||
|
binding.match.accountId ?? resolveDefaultAccountId(cfg, provider);
|
||||||
|
const key = providerAccountKey(provider, accountId);
|
||||||
|
if (seen.has(key)) continue;
|
||||||
|
seen.add(key);
|
||||||
|
const status = providerStatus.get(key);
|
||||||
|
if (status) {
|
||||||
|
providerLines.push(formatProviderEntry(status));
|
||||||
|
} else {
|
||||||
|
providerLines.push(
|
||||||
|
`${formatProviderAccountLabel({ provider, accountId })}: unknown`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (summary.isDefault) {
|
||||||
|
for (const entry of allProviderEntries) {
|
||||||
|
if (shouldShowProviderEntry(entry, cfg)) {
|
||||||
|
providerLines.push(formatProviderEntry(entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (providerLines.length > 0) {
|
||||||
|
summary.providers = providerLines;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (opts.json) {
|
if (opts.json) {
|
||||||
runtime.log(JSON.stringify(summaries, null, 2));
|
runtime.log(JSON.stringify(summaries, null, 2));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.log(["Agents:", ...summaries.map(formatSummary)].join("\n"));
|
const lines = ["Agents:", ...summaries.map(formatSummary)];
|
||||||
|
lines.push(
|
||||||
|
"Routing rules map provider/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.",
|
||||||
|
);
|
||||||
|
runtime.log(lines.join("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
function describeBinding(binding: AgentBinding) {
|
function describeBinding(binding: AgentBinding) {
|
||||||
@@ -434,6 +802,13 @@ export async function agentsAddCommand(
|
|||||||
runtime.exit(1);
|
runtime.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!workspaceFlag) {
|
||||||
|
runtime.error(
|
||||||
|
"Non-interactive mode requires --workspace. Re-run without flags to use the wizard.",
|
||||||
|
);
|
||||||
|
runtime.exit(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const agentId = normalizeAgentId(nameInput);
|
const agentId = normalizeAgentId(nameInput);
|
||||||
if (agentId === DEFAULT_AGENT_ID) {
|
if (agentId === DEFAULT_AGENT_ID) {
|
||||||
runtime.error(`"${DEFAULT_AGENT_ID}" is reserved. Choose another name.`);
|
runtime.error(`"${DEFAULT_AGENT_ID}" is reserved. Choose another name.`);
|
||||||
|
|||||||
Reference in New Issue
Block a user