summaryrefslogtreecommitdiffhomepage
path: root/packages/tool-shell/src/shell.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-25 14:06:23 +0900
committerAdam Malczewski <[email protected]>2026-06-25 14:06:23 +0900
commit1ff0eac44cd44751af979c51c746a1774c268e8a (patch)
treebf1c4563595e5b4c23f63e1d5b0782400be7e025 /packages/tool-shell/src/shell.ts
parent54db4583e66134010375a1fa94256f36034ffdff (diff)
downloaddispatch-1ff0eac44cd44751af979c51c746a1774c268e8a.tar.gz
dispatch-1ff0eac44cd44751af979c51c746a1774c268e8a.zip
feat(ssh): wave 2 — route filesystem/shell tools behind ExecBackend
Wave 2 of transparent SSH support (4 parallel owner-agents on disjoint tool packages). The tools now resolve an ExecBackend per-call from ctx.computerId and call backend.spawn / backend.readFile / etc. instead of node:fs and node:child_process directly — so they are transport-agnostic (local now; remote over SSH later, transparent to the agent). Still LOCAL-ONLY this wave (computerId always undefined -> LocalExecBackend, behavior-identical). - tool-shell: factory takes resolveBackend; execute calls backend.spawn. spawn.ts DELETED (realSpawn was a verbatim duplicate of exec-backend's LocalExecBackend.spawn — logic moved to the sanctioned shared package). manifest dependsOn:[exec-backend]; host.getService at activation. - tool-read-file: readFile/stat/readdir -> backend.* (pure logic untouched; ENOENT .code branches kept). - tool-write-file: exists/stat/writeFile -> backend.* (pure logic untouched). - tool-edit-file: readFile/writeFile -> backend.* + forward-compatible REMOTE diagnostics skip (ctx.computerId set -> skip LSP, return empty — plan §6.1; local path byte-identical to today). LSP lookup stays lazy. - orchestrator: pre-wired @dispatch/exec-backend dep into the 4 tool package.jsons + bun install (build/config, my lane) so isolated verify resolved cleanly; agents added the ../exec-backend tsconfig ref. Verified: tsc -b EXIT 0, biome clean, 1599 vitest pass (was 1592). Refs: notes/ssh-support-plan.md (decisions §0.5/§13). No merge or push.
Diffstat (limited to 'packages/tool-shell/src/shell.ts')
-rw-r--r--packages/tool-shell/src/shell.ts15
1 files changed, 5 insertions, 10 deletions
diff --git a/packages/tool-shell/src/shell.ts b/packages/tool-shell/src/shell.ts
index cc76bca..dac7fab 100644
--- a/packages/tool-shell/src/shell.ts
+++ b/packages/tool-shell/src/shell.ts
@@ -1,4 +1,5 @@
import { resolve } from "node:path";
+import type { ExecBackendResolver } from "@dispatch/exec-backend";
import type { ToolContract, ToolExecuteContext, ToolResult } from "@dispatch/kernel";
const DEFAULT_TIMEOUT = 120_000;
@@ -15,14 +16,6 @@ export interface SpawnResult {
readonly aborted: boolean;
}
-export type SpawnShell = (params: {
- readonly command: string;
- readonly cwd: string;
- readonly signal: AbortSignal;
- readonly timeout: number;
- readonly onOutput: (data: string, stream: "stdout" | "stderr") => void;
-}) => Promise<SpawnResult>;
-
export function validateArgs(args: unknown): ValidatedArgs | { readonly error: string } {
if (args === null || args === undefined || typeof args !== "object") {
return { error: "Error: Arguments must be an object." };
@@ -88,7 +81,7 @@ export function buildResult(params: {
export function createRunShellTool(deps: {
readonly workdir: string;
- readonly spawn: SpawnShell;
+ readonly resolveBackend: ExecBackendResolver;
readonly outputCap?: number;
}): ToolContract {
const workdir = resolve(deps.workdir);
@@ -139,10 +132,12 @@ export function createRunShellTool(deps: {
output += data;
};
+ const backend = deps.resolveBackend(ctx.computerId);
+
let spawnResult: SpawnResult;
try {
- spawnResult = await deps.spawn({
+ spawnResult = await backend.spawn({
command,
cwd: effectiveCwd,
signal: ctx.signal,