2026-09-02 18:43:50 +07:00
|
|
|
/**
|
|
|
|
|
* 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";
|
2026-09-03 02:30:12 +07:00
|
|
|
import { bashControl } from "./bgbash/control.ts";
|
2026-09-02 18:43:50 +07:00
|
|
|
|
|
|
|
|
/** 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");
|
2026-09-02 20:09:51 +07:00
|
|
|
// 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.`;
|
2026-09-02 18:43:50 +07:00
|
|
|
}
|
|
|
|
|
}
|