/** * Background bash process output and kill tools. * Mirrors `tools/bash_tools.rs`. */ import * as fs from "node:fs"; import * as path from "node:path"; import type { JsonValue } from "@zesdex/domain"; import { type Tool, type ToolCtx } from "./mod.ts"; import { argStr } from "./util.ts"; import { bashControl } from "./bgbash/control.ts"; /** Get the output of a background bash job by ID. */ export class BashOutput implements Tool { name = "bash_output"; description = "Get the output of a background bash job by ID"; parameters = { type: "object", properties: { job_id: { type: "string", description: "Background job ID" }, }, required: ["job_id"], }; run(ctx: ToolCtx, args: JsonValue): string { const jobId = argStr(args, "job_id"); // Prevent path traversal if (jobId.includes("/") || jobId.includes("\\") || jobId.includes("..")) { throw new Error(`invalid job_id '${jobId}': must not contain path separators`); } const outputDir = path.join(ctx.sessionDir, "bash-outputs"); const outputFile = path.join(outputDir, jobId); if (fs.existsSync(outputFile)) { try { const content = fs.readFileSync(outputFile, "utf8"); return `Output for job '${jobId}':\n${content}`; } catch (e) { return `Error reading output: ${(e as Error).message}`; } } return `No output found for job '${jobId}'. The job may still be running.`; } } /** Kill a background bash job by ID. */ export class BashKill implements Tool { name = "bash_kill"; description = "Kill a background bash job by ID"; parameters = { type: "object", properties: { job_id: { type: "string", description: "Background job ID to kill" }, }, required: ["job_id"], }; run(_ctx: ToolCtx, args: JsonValue): string { const jobId = argStr(args, "job_id"); // Look up the job in the global bgbash registry. const cancelled = bashControl().cancel(jobId); if (!cancelled) { throw new Error(`no active background job found with ID '${jobId}'`); } return `Background job '${jobId}' killed.`; } }