





















@@ -1,78 +1,56 @@
1-import fs from "node:fs";
2-import path from "node:path";
1+import { findOverlappingWorkspaceAgentIds } from "../agents/agent-delete-safety.js";
32import { resolveAgentDir, resolveAgentWorkspaceDir } from "../agents/agent-scope.js";
43import { replaceConfigFile } from "../config/config.js";
54import { logConfigUpdated } from "../config/logging.js";
6-import { resolveSessionTranscriptsDirForAgent } from "../config/sessions.js";
7-import type { OpenClawConfig } from "../config/types.openclaw.js";
5+import {
6+purgeAgentSessionStoreEntries,
7+resolveSessionTranscriptsDirForAgent,
8+} from "../config/sessions.js";
9+import { callGateway, isGatewayTransportError } from "../gateway/call.js";
810import { DEFAULT_AGENT_ID, normalizeAgentId } from "../routing/session-key.js";
911import { type RuntimeEnv, writeRuntimeJson } from "../runtime.js";
1012import { defaultRuntime } from "../runtime.js";
11-import { lowercasePreservingWhitespace } from "../shared/string-coerce.js";
13+import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
1214import { createClackPrompter } from "../wizard/clack-prompter.js";
13-import {
14-createQuietRuntime,
15-purgeAgentSessionStoreEntries,
16-requireValidConfigFileSnapshot,
17-} from "./agents.command-shared.js";
15+import { createQuietRuntime, requireValidConfigFileSnapshot } from "./agents.command-shared.js";
1816import { findAgentEntryIndex, listAgentEntries, pruneAgentConfig } from "./agents.config.js";
1917import { moveToTrash } from "./onboard-helpers.js";
201821-function normalizeWorkspacePathForComparison(input: string): string {
22-const resolved = path.resolve(input.replaceAll("\0", ""));
23-let normalized = resolved;
24-try {
25-normalized = fs.realpathSync.native(resolved);
26-} catch {
27-// Keep lexical path for non-existent directories.
28-}
29-if (process.platform === "win32") {
30-return lowercasePreservingWhitespace(normalized);
31-}
32-return normalized;
33-}
34-35-function isPathWithinRoot(candidatePath: string, rootPath: string): boolean {
36-const relative = path.relative(rootPath, candidatePath);
37-return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
38-}
39-40-function workspacePathsOverlap(left: string, right: string): boolean {
41-const normalizedLeft = normalizeWorkspacePathForComparison(left);
42-const normalizedRight = normalizeWorkspacePathForComparison(right);
43-return (
44-isPathWithinRoot(normalizedLeft, normalizedRight) ||
45-isPathWithinRoot(normalizedRight, normalizedLeft)
46-);
47-}
48-49-function findOverlappingWorkspaceAgentIds(
50-cfg: OpenClawConfig,
51-agentId: string,
52-workspaceDir: string,
53-): string[] {
54-const entries = listAgentEntries(cfg);
55-const normalizedAgentId = normalizeAgentId(agentId);
56-const overlappingAgentIds: string[] = [];
57-for (const entry of entries) {
58-const otherAgentId = normalizeAgentId(entry.id);
59-if (otherAgentId === normalizedAgentId) {
60-continue;
61-}
62-const otherWorkspace = resolveAgentWorkspaceDir(cfg, otherAgentId);
63-if (workspacePathsOverlap(workspaceDir, otherWorkspace)) {
64-overlappingAgentIds.push(otherAgentId);
65-}
66-}
67-return overlappingAgentIds;
68-}
69-7019type AgentsDeleteOptions = {
7120id: string;
7221force?: boolean;
7322json?: boolean;
7423};
752425+type AgentsDeleteGatewayResult = {
26+ok: true;
27+agentId: string;
28+removedBindings: number;
29+};
30+31+async function maybeDeleteAgentThroughGateway(params: {
32+agentId: string;
33+deleteFiles: boolean;
34+}): Promise<AgentsDeleteGatewayResult | null> {
35+try {
36+return await callGateway<AgentsDeleteGatewayResult>({
37+method: "agents.delete",
38+params: {
39+agentId: params.agentId,
40+deleteFiles: params.deleteFiles,
41+},
42+mode: GATEWAY_CLIENT_MODES.CLI,
43+clientName: GATEWAY_CLIENT_NAMES.CLI,
44+requiredMethods: ["agents.delete"],
45+});
46+} catch (error) {
47+if (isGatewayTransportError(error)) {
48+return null;
49+}
50+throw error;
51+}
52+}
53+7654export async function agentsDeleteCommand(
7755opts: AgentsDeleteOptions,
7856runtime: RuntimeEnv = defaultRuntime,
@@ -127,8 +105,34 @@ export async function agentsDeleteCommand(
127105const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
128106const agentDir = resolveAgentDir(cfg, agentId);
129107const sessionsDir = resolveSessionTranscriptsDirForAgent(agentId);
130-131108const result = pruneAgentConfig(cfg, agentId);
109+110+const gatewayResult = await maybeDeleteAgentThroughGateway({
111+ agentId,
112+deleteFiles: true,
113+});
114+if (gatewayResult) {
115+const workspaceSharedWith = findOverlappingWorkspaceAgentIds(cfg, agentId, workspaceDir);
116+const workspaceRetained = workspaceSharedWith.length > 0;
117+if (opts.json) {
118+writeRuntimeJson(runtime, {
119+ agentId,
120+workspace: workspaceDir,
121+workspaceRetained: workspaceRetained || undefined,
122+workspaceRetainedReason: workspaceRetained ? "shared" : undefined,
123+workspaceSharedWith: workspaceRetained ? workspaceSharedWith : undefined,
124+ agentDir,
125+ sessionsDir,
126+removedBindings: gatewayResult.removedBindings,
127+removedAllow: result.removedAllow,
128+transport: "gateway",
129+});
130+} else {
131+runtime.log(`Deleted agent: ${agentId}`);
132+}
133+return;
134+}
135+132136await replaceConfigFile({
133137nextConfig: result.config,
134138 ...(baseHash !== undefined ? { baseHash } : {}),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。