mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 10:21:24 +00:00
refactor(commands): split CLI commands
This commit is contained in:
246
src/commands/agents.config.ts
Normal file
246
src/commands/agents.config.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import {
|
||||
resolveAgentDir,
|
||||
resolveAgentWorkspaceDir,
|
||||
resolveDefaultAgentId,
|
||||
} from "../agents/agent-scope.js";
|
||||
import { DEFAULT_IDENTITY_FILENAME } from "../agents/workspace.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import { normalizeAgentId } from "../routing/session-key.js";
|
||||
|
||||
export type AgentSummary = {
|
||||
id: string;
|
||||
name?: string;
|
||||
identityName?: string;
|
||||
identityEmoji?: string;
|
||||
identitySource?: "identity" | "config";
|
||||
workspace: string;
|
||||
agentDir: string;
|
||||
model?: string;
|
||||
bindings: number;
|
||||
bindingDetails?: string[];
|
||||
routes?: string[];
|
||||
providers?: string[];
|
||||
isDefault: boolean;
|
||||
};
|
||||
|
||||
type AgentEntry = NonNullable<
|
||||
NonNullable<ClawdbotConfig["agents"]>["list"]
|
||||
>[number];
|
||||
|
||||
type AgentIdentity = {
|
||||
name?: string;
|
||||
emoji?: string;
|
||||
creature?: string;
|
||||
vibe?: string;
|
||||
};
|
||||
|
||||
export function listAgentEntries(cfg: ClawdbotConfig): AgentEntry[] {
|
||||
const list = cfg.agents?.list;
|
||||
if (!Array.isArray(list)) return [];
|
||||
return list.filter((entry): entry is AgentEntry =>
|
||||
Boolean(entry && typeof entry === "object"),
|
||||
);
|
||||
}
|
||||
|
||||
export function findAgentEntryIndex(
|
||||
list: AgentEntry[],
|
||||
agentId: string,
|
||||
): number {
|
||||
const id = normalizeAgentId(agentId);
|
||||
return list.findIndex((entry) => normalizeAgentId(entry.id) === id);
|
||||
}
|
||||
|
||||
function resolveAgentName(cfg: ClawdbotConfig, agentId: string) {
|
||||
const entry = listAgentEntries(cfg).find(
|
||||
(agent) => normalizeAgentId(agent.id) === normalizeAgentId(agentId),
|
||||
);
|
||||
return entry?.name?.trim() || undefined;
|
||||
}
|
||||
|
||||
function resolveAgentModel(cfg: ClawdbotConfig, agentId: string) {
|
||||
const entry = listAgentEntries(cfg).find(
|
||||
(agent) => normalizeAgentId(agent.id) === normalizeAgentId(agentId),
|
||||
);
|
||||
if (entry?.model) {
|
||||
if (typeof entry.model === "string" && entry.model.trim()) {
|
||||
return entry.model.trim();
|
||||
}
|
||||
if (typeof entry.model === "object") {
|
||||
const primary = entry.model.primary?.trim();
|
||||
if (primary) return primary;
|
||||
}
|
||||
}
|
||||
const raw = cfg.agents?.defaults?.model;
|
||||
if (typeof raw === "string") return raw;
|
||||
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[] {
|
||||
const defaultAgentId = normalizeAgentId(resolveDefaultAgentId(cfg));
|
||||
const configuredAgents = listAgentEntries(cfg);
|
||||
const orderedIds =
|
||||
configuredAgents.length > 0
|
||||
? configuredAgents.map((agent) => normalizeAgentId(agent.id))
|
||||
: [defaultAgentId];
|
||||
const bindingCounts = new Map<string, number>();
|
||||
for (const binding of cfg.bindings ?? []) {
|
||||
const agentId = normalizeAgentId(binding.agentId);
|
||||
bindingCounts.set(agentId, (bindingCounts.get(agentId) ?? 0) + 1);
|
||||
}
|
||||
|
||||
const ordered = orderedIds.filter(
|
||||
(id, index) => orderedIds.indexOf(id) === index,
|
||||
);
|
||||
|
||||
return ordered.map((id) => {
|
||||
const workspace = resolveAgentWorkspaceDir(cfg, id);
|
||||
const identity = loadAgentIdentity(workspace);
|
||||
const configIdentity = configuredAgents.find(
|
||||
(agent) => normalizeAgentId(agent.id) === id,
|
||||
)?.identity;
|
||||
const identityName = identity?.name ?? configIdentity?.name?.trim();
|
||||
const identityEmoji = identity?.emoji ?? configIdentity?.emoji?.trim();
|
||||
const identitySource = identity
|
||||
? "identity"
|
||||
: configIdentity && (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(
|
||||
cfg: ClawdbotConfig,
|
||||
params: {
|
||||
agentId: string;
|
||||
name?: string;
|
||||
workspace?: string;
|
||||
agentDir?: string;
|
||||
model?: string;
|
||||
},
|
||||
): ClawdbotConfig {
|
||||
const agentId = normalizeAgentId(params.agentId);
|
||||
const name = params.name?.trim();
|
||||
const list = listAgentEntries(cfg);
|
||||
const index = findAgentEntryIndex(list, agentId);
|
||||
const base = index >= 0 ? list[index] : { id: agentId };
|
||||
const nextEntry: AgentEntry = {
|
||||
...base,
|
||||
...(name ? { name } : {}),
|
||||
...(params.workspace ? { workspace: params.workspace } : {}),
|
||||
...(params.agentDir ? { agentDir: params.agentDir } : {}),
|
||||
...(params.model ? { model: params.model } : {}),
|
||||
};
|
||||
const nextList = [...list];
|
||||
if (index >= 0) {
|
||||
nextList[index] = nextEntry;
|
||||
} else {
|
||||
if (
|
||||
nextList.length === 0 &&
|
||||
agentId !== normalizeAgentId(resolveDefaultAgentId(cfg))
|
||||
) {
|
||||
nextList.push({ id: resolveDefaultAgentId(cfg) });
|
||||
}
|
||||
nextList.push(nextEntry);
|
||||
}
|
||||
return {
|
||||
...cfg,
|
||||
agents: {
|
||||
...cfg.agents,
|
||||
list: nextList,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function pruneAgentConfig(
|
||||
cfg: ClawdbotConfig,
|
||||
agentId: string,
|
||||
): {
|
||||
config: ClawdbotConfig;
|
||||
removedBindings: number;
|
||||
removedAllow: number;
|
||||
} {
|
||||
const id = normalizeAgentId(agentId);
|
||||
const agents = listAgentEntries(cfg);
|
||||
const nextAgentsList = agents.filter(
|
||||
(entry) => normalizeAgentId(entry.id) !== id,
|
||||
);
|
||||
const nextAgents = nextAgentsList.length > 0 ? nextAgentsList : undefined;
|
||||
|
||||
const bindings = cfg.bindings ?? [];
|
||||
const filteredBindings = bindings.filter(
|
||||
(binding) => normalizeAgentId(binding.agentId) !== id,
|
||||
);
|
||||
|
||||
const allow = cfg.tools?.agentToAgent?.allow ?? [];
|
||||
const filteredAllow = allow.filter((entry) => entry !== id);
|
||||
|
||||
const nextAgentsConfig = cfg.agents
|
||||
? { ...cfg.agents, list: nextAgents }
|
||||
: nextAgents
|
||||
? { list: nextAgents }
|
||||
: undefined;
|
||||
const nextTools = cfg.tools?.agentToAgent
|
||||
? {
|
||||
...cfg.tools,
|
||||
agentToAgent: {
|
||||
...cfg.tools.agentToAgent,
|
||||
allow: filteredAllow.length > 0 ? filteredAllow : undefined,
|
||||
},
|
||||
}
|
||||
: cfg.tools;
|
||||
|
||||
return {
|
||||
config: {
|
||||
...cfg,
|
||||
agents: nextAgentsConfig,
|
||||
bindings: filteredBindings.length > 0 ? filteredBindings : undefined,
|
||||
tools: nextTools,
|
||||
},
|
||||
removedBindings: bindings.length - filteredBindings.length,
|
||||
removedAllow: allow.length - filteredAllow.length,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user