Files
paseo/packages/cli/src/commands/agent/delete.ts
T
Mohamed Boudra ca443b3ecf fix: handle Windows drive-letter paths across the codebase (#148)
* Add metrics collection and terminal performance tests

* fix: handle Windows drive-letter paths across the codebase

Windows paths like C:\Users\foo\project were broken in multiple places:
- agent-storage slugified D:\MyProject as D:-MyProject (illegal colon)
- terminal-manager rejected all non-/ paths as relative
- bootstrap parser misparsed drive colons as TCP host:port
- daemon/client connection helpers misclassified Windows paths
- CLI cwd filtering used hardcoded / separators
- checkout-git worktree detection used hardcoded / in path checks
- worktree archive used split("/").pop() instead of path.basename()

All path helpers now normalize separators and handle Windows
drive letters with case-insensitive comparison where needed.
2026-03-27 18:17:48 +07:00

126 lines
3.7 KiB
TypeScript

import type { Command } from "commander";
import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
import { isSameOrDescendantPath } from "../../utils/paths.js";
export function addDeleteOptions(cmd: Command): Command {
return cmd
.description("Delete an agent (interrupt if running, then hard-delete)")
.argument("[id]", "Agent ID (or prefix) - optional if --all or --cwd specified")
.option("--all", "Delete all agents")
.option("--cwd <path>", "Delete all agents in directory");
}
import type {
CommandOptions,
SingleResult,
OutputSchema,
CommandError,
} from "../../output/index.js";
export interface DeleteResult {
deletedCount: number;
agentIds: string[];
}
export const deleteSchema: OutputSchema<DeleteResult> = {
idField: (item) => item.agentIds.join("\n"),
columns: [{ header: "DELETED", field: "deletedCount" }],
};
export interface AgentDeleteOptions extends CommandOptions {
all?: boolean;
cwd?: string;
}
export type AgentDeleteResult = SingleResult<DeleteResult>;
export async function runDeleteCommand(
id: string | undefined,
options: AgentDeleteOptions,
_command: Command,
): Promise<AgentDeleteResult> {
const host = getDaemonHost({ host: options.host as string | undefined });
if (!id && !options.all && !options.cwd) {
const error: CommandError = {
code: "MISSING_ARGUMENT",
message: "Agent ID required unless --all or --cwd is specified",
details: "Usage: paseo agent delete <id> | --all | --cwd <path>",
};
throw error;
}
let client;
try {
client = await connectToDaemon({ host: options.host as string | undefined });
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const error: CommandError = {
code: "DAEMON_NOT_RUNNING",
message: `Cannot connect to daemon at ${host}: ${message}`,
details: "Start the daemon with: paseo daemon start",
};
throw error;
}
try {
const fetchPayload = await client.fetchAgents({ filter: { includeArchived: true } });
let agents = fetchPayload.entries.map((entry) => entry.agent);
const deletedIds: string[] = [];
if (options.all) {
agents = agents.filter((a) => !a.archivedAt);
} else if (options.cwd) {
agents = agents.filter((a) => {
if (a.archivedAt) return false;
return isSameOrDescendantPath(options.cwd!, a.cwd);
});
} else if (id) {
const fetchResult = await client.fetchAgent(id);
if (!fetchResult) {
const error: CommandError = {
code: "AGENT_NOT_FOUND",
message: `No agent found matching: ${id}`,
details: "Use `paseo ls` to list available agents",
};
throw error;
}
agents = [fetchResult.agent];
}
for (const agent of agents) {
try {
if (agent.status === "running") {
await client.cancelAgent(agent.id);
}
await client.deleteAgent(agent.id);
deletedIds.push(agent.id);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
console.error(`Warning: Failed to delete agent ${agent.id.slice(0, 7)}: ${message}`);
}
}
await client.close();
return {
type: "single",
data: {
deletedCount: deletedIds.length,
agentIds: deletedIds,
},
schema: deleteSchema,
};
} catch (err) {
await client.close().catch(() => {});
if (err && typeof err === "object" && "code" in err) {
throw err;
}
const message = err instanceof Error ? err.message : String(err);
const error: CommandError = {
code: "DELETE_AGENT_FAILED",
message: `Failed to delete agent(s): ${message}`,
};
throw error;
}
}