iosm-cli 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +5 -6
- package/dist/core/background-processes.d.ts.map +1 -1
- package/dist/core/background-processes.js +3 -2
- package/dist/core/background-processes.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +3 -2
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +5 -2
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/modes/telegram/telegram-bridge-mode.d.ts.map +1 -1
- package/dist/modes/telegram/telegram-bridge-mode.js +153 -32
- package/dist/modes/telegram/telegram-bridge-mode.js.map +1 -1
- package/dist/utils/shell.d.ts +8 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +45 -0
- package/dist/utils/shell.js.map +1 -1
- package/docs/configuration.md +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.5] - 2026-04-06
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **Telegram API 429 resilience** — added automatic `retry after N` backoff/retry handling for all Telegram API operations (including `sendMessage`, `editMessageText`, `sendDocument`, and control actions)
|
|
15
|
+
- **Telegram status message recovery** — when live status edit target becomes invalid (`MESSAGE_ID_INVALID`, deleted or non-editable message), bridge now recreates status message and continues updates on a fresh `message_id`
|
|
16
|
+
- **Bridge runtime stability under delivery failures** — wrapped RPC event callbacks and final output delivery with fail-safe guards to prevent bridge process crashes from transient Telegram delivery errors
|
|
17
|
+
|
|
18
|
+
## [0.3.4] - 2026-04-06
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **Windows shell command compatibility** — added runtime auto-adaptation for Windows command syntax, routing cmd-style commands (`%VAR%`, `dir`, `C:\...`) to `cmd.exe` and PowerShell-style commands (`$env:...`, `Get-*`) to `powershell.exe`
|
|
23
|
+
- **Telegram bridge command execution on Windows** — fixed failures when users sent native Windows shell commands through Telegram-driven runs while IOSM executed under bash
|
|
24
|
+
- **Execution path consistency** — applied shell adaptation uniformly across foreground bash execution, bash tool execution, and detached background processes
|
|
25
|
+
|
|
26
|
+
### Tests
|
|
27
|
+
|
|
28
|
+
- Added regression suite for Windows command adaptation and explicit-shell passthrough behavior
|
|
29
|
+
|
|
10
30
|
## [0.3.3] - 2026-04-05
|
|
11
31
|
|
|
12
32
|
### Fixed
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<h1>IOSM CLI 0.3.
|
|
3
|
+
<h1>IOSM CLI 0.3.5</h1>
|
|
4
4
|
|
|
5
5
|
<p><strong>Terminal-native AI runtime for controlled, measurable engineering work on real codebases.</strong></p>
|
|
6
6
|
|
|
@@ -32,12 +32,11 @@ It is not a chat interface. It is a runtime.
|
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
35
|
-
## ✦ What's New in 0.3.
|
|
35
|
+
## ✦ What's New in 0.3.5
|
|
36
36
|
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
- Added regression tests for META prompt routing (actionable vs question-like prompts) and unknown external tool rendering
|
|
37
|
+
- Added Telegram API rate-limit resilience: bridge now respects `retry after N` and auto-retries on HTTP `429` across all Telegram methods
|
|
38
|
+
- Fixed live status update reliability: invalid or deleted status messages are now recreated automatically and status streaming continues
|
|
39
|
+
- Hardened Telegram bridge runtime: event callbacks and final output delivery are now fail-safe so transient Telegram send/edit errors do not crash the process
|
|
41
40
|
|
|
42
41
|
## ✦ Major Additions in 0.2.16
|
|
43
42
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"background-processes.d.ts","sourceRoot":"","sources":["../../src/core/background-processes.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,YAAY,GAAG,SAAS,CAAC;AAiB9F,MAAM,WAAW,uBAAuB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,uBAAuB,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,2BAA2B;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,8BAA8B;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACvB;AA2KD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,2BAA2B,GAAG,uBAAuB,
|
|
1
|
+
{"version":3,"file":"background-processes.d.ts","sourceRoot":"","sources":["../../src/core/background-processes.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,YAAY,GAAG,SAAS,CAAC;AAiB9F,MAAM,WAAW,uBAAuB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,uBAAuB,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,2BAA2B;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,8BAA8B;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACvB;AA2KD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,2BAA2B,GAAG,uBAAuB,CA4DlG;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,uBAAuB,EAAE,CAiB9F;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,CAOrG;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,CA8BtG;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,MAAM,GAAG,SAAS,CASxG;AAED,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IACT,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf,GACC,8BAA8B,CAuDhC"}
|
|
@@ -2,7 +2,7 @@ import { randomBytes } from "node:crypto";
|
|
|
2
2
|
import { closeSync, existsSync, fstatSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { spawn, spawnSync } from "child_process";
|
|
5
|
-
import { getShellConfig, getShellEnv, killProcessTree } from "../utils/shell.js";
|
|
5
|
+
import { adaptCommandForShell, getShellConfig, getShellEnv, killProcessTree } from "../utils/shell.js";
|
|
6
6
|
const BACKGROUND_DIR_SEGMENTS = [".iosm", "background", "processes"];
|
|
7
7
|
const MAX_LOG_TAIL_BYTES = 256 * 1024;
|
|
8
8
|
const BACKGROUND_STOP_FORCE_KILL_DELAY_MS = 1500;
|
|
@@ -187,9 +187,10 @@ export function startBackgroundProcess(input) {
|
|
|
187
187
|
const metaPath = join(processesDir, `${id}.json`);
|
|
188
188
|
const now = new Date().toISOString();
|
|
189
189
|
const { shell, args } = getShellConfig();
|
|
190
|
+
const adaptedCommand = adaptCommandForShell(input.command);
|
|
190
191
|
const wrappedCommand = [
|
|
191
192
|
"(",
|
|
192
|
-
|
|
193
|
+
adaptedCommand,
|
|
193
194
|
")",
|
|
194
195
|
"__iosm_bg_exit_code=$?",
|
|
195
196
|
`__iosm_bg_finished_at=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || node -e 'console.log(new Date().toISOString())')`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"background-processes.js","sourceRoot":"","sources":["../../src/core/background-processes.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport {\n\tcloseSync,\n\texistsSync,\n\tfstatSync,\n\tmkdirSync,\n\topenSync,\n\treadFileSync,\n\treadSync,\n\treaddirSync,\n\trmSync,\n\tstatSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { spawn, spawnSync } from \"child_process\";\nimport { getShellConfig, getShellEnv, killProcessTree } from \"../utils/shell.js\";\n\nexport type BackgroundProcessStatus = \"running\" | \"done\" | \"error\" | \"terminated\" | \"unknown\";\n\ninterface BackgroundProcessMeta {\n\tid: string;\n\tcommand: string;\n\tcwd: string;\n\trootCwd: string;\n\tpid: number;\n\tcreatedAt: string;\n\tstartedAt: string;\n\tsource?: \"interactive\" | \"tool\";\n\trequestedStopAt?: string;\n\tlogPath: string;\n\tfinishedAtPath: string;\n\texitCodePath: string;\n}\n\nexport interface BackgroundProcessRecord {\n\tid: string;\n\tcommand: string;\n\tcwd: string;\n\trootCwd: string;\n\tpid: number;\n\tcreatedAt: string;\n\tstartedAt: string;\n\tsource?: \"interactive\" | \"tool\";\n\trequestedStopAt?: string;\n\tlogPath: string;\n\tfinishedAtPath: string;\n\texitCodePath: string;\n\tmetaPath: string;\n\tstatus: BackgroundProcessStatus;\n\tfinishedAt?: string;\n\texitCode?: number;\n}\n\nexport interface StartBackgroundProcessInput {\n\trootCwd: string;\n\tcommand: string;\n\tcwd?: string;\n\tsource?: \"interactive\" | \"tool\";\n}\n\nexport interface PruneBackgroundProcessesResult {\n\tremoved: number;\n\tremovedIds: string[];\n\tskippedRunning: number;\n\tskippedRecent: number;\n\tthresholdHours: number;\n}\n\nconst BACKGROUND_DIR_SEGMENTS = [\".iosm\", \"background\", \"processes\"] as const;\nconst MAX_LOG_TAIL_BYTES = 256 * 1024;\nconst BACKGROUND_STOP_FORCE_KILL_DELAY_MS = 1500;\nconst BACKGROUND_STOP_FALLBACK_EXIT_CODE = 143;\n\nfunction getProcessesDir(rootCwd: string): string {\n\treturn join(rootCwd, ...BACKGROUND_DIR_SEGMENTS);\n}\n\nfunction shellQuote(value: string): string {\n\treturn `'${value.replace(/'/g, `'\\\"'\\\"'`)}'`;\n}\n\nfunction isProcessAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException | undefined)?.code === \"EPERM\") return true;\n\t\treturn false;\n\t}\n}\n\nfunction parseIntegerFile(path: string): number | undefined {\n\tif (!existsSync(path)) return undefined;\n\ttry {\n\t\tconst raw = readFileSync(path, \"utf8\").trim();\n\t\tif (!raw) return undefined;\n\t\tconst value = Number.parseInt(raw, 10);\n\t\treturn Number.isFinite(value) ? value : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction parseTextFile(path: string): string | undefined {\n\tif (!existsSync(path)) return undefined;\n\ttry {\n\t\tconst raw = readFileSync(path, \"utf8\").trim();\n\t\treturn raw || undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction writeCompletionArtifacts(meta: BackgroundProcessMeta, finishedAt: string, exitCode: number): void {\n\twriteFileSync(meta.finishedAtPath, `${finishedAt}\\n`, \"utf8\");\n\twriteFileSync(meta.exitCodePath, `${exitCode}\\n`, \"utf8\");\n}\n\nfunction signalProcessTree(pid: number, signal: \"SIGTERM\" | \"SIGKILL\"): void {\n\tif (process.platform === \"win32\") {\n\t\t// /F is forced kill; omit it for soft terminate attempt.\n\t\tconst args = signal === \"SIGKILL\" ? [\"/F\", \"/T\", \"/PID\", String(pid)] : [\"/T\", \"/PID\", String(pid)];\n\t\ttry {\n\t\t\tspawnSync(\"taskkill\", args, { stdio: \"ignore\", timeout: 5000 });\n\t\t} catch {\n\t\t\t// Ignore termination errors (process may already be gone).\n\t\t}\n\t\treturn;\n\t}\n\n\ttry {\n\t\tprocess.kill(-pid, signal);\n\t} catch {\n\t\ttry {\n\t\t\tprocess.kill(pid, signal);\n\t\t} catch {\n\t\t\t// Process already dead.\n\t\t}\n\t}\n}\n\nfunction scheduleForceKill(pid: number): void {\n\tconst timer = setTimeout(() => {\n\t\tif (!isProcessAlive(pid)) return;\n\t\tkillProcessTree(pid);\n\t}, BACKGROUND_STOP_FORCE_KILL_DELAY_MS);\n\ttimer.unref();\n}\n\nfunction toRecord(meta: BackgroundProcessMeta, metaPath: string): BackgroundProcessRecord {\n\tconst finishedAt = parseTextFile(meta.finishedAtPath);\n\tconst exitCode = parseIntegerFile(meta.exitCodePath);\n\tconst alive = isProcessAlive(meta.pid);\n\tlet status: BackgroundProcessStatus = \"unknown\";\n\tif (!alive && meta.requestedStopAt) {\n\t\tstatus = \"terminated\";\n\t} else if (alive) {\n\t\tstatus = \"running\";\n\t} else if (finishedAt && typeof exitCode === \"number\") {\n\t\tstatus = exitCode === 0 ? \"done\" : \"error\";\n\t}\n\n\treturn {\n\t\tid: meta.id,\n\t\tcommand: meta.command,\n\t\tcwd: meta.cwd,\n\t\trootCwd: meta.rootCwd,\n\t\tpid: meta.pid,\n\t\tcreatedAt: meta.createdAt,\n\t\tstartedAt: meta.startedAt,\n\t\tsource: meta.source,\n\t\trequestedStopAt: meta.requestedStopAt,\n\t\tlogPath: meta.logPath,\n\t\tfinishedAtPath: meta.finishedAtPath,\n\t\texitCodePath: meta.exitCodePath,\n\t\tmetaPath,\n\t\tstatus,\n\t\tfinishedAt,\n\t\texitCode,\n\t};\n}\n\nfunction loadMeta(metaPath: string): BackgroundProcessMeta | undefined {\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(metaPath, \"utf8\")) as Partial<BackgroundProcessMeta>;\n\t\tif (\n\t\t\ttypeof parsed.id !== \"string\" ||\n\t\t\ttypeof parsed.command !== \"string\" ||\n\t\t\ttypeof parsed.cwd !== \"string\" ||\n\t\t\ttypeof parsed.rootCwd !== \"string\" ||\n\t\t\ttypeof parsed.pid !== \"number\" ||\n\t\t\ttypeof parsed.createdAt !== \"string\" ||\n\t\t\ttypeof parsed.startedAt !== \"string\" ||\n\t\t\ttypeof parsed.logPath !== \"string\" ||\n\t\t\ttypeof parsed.finishedAtPath !== \"string\" ||\n\t\t\ttypeof parsed.exitCodePath !== \"string\"\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tid: parsed.id,\n\t\t\tcommand: parsed.command,\n\t\t\tcwd: parsed.cwd,\n\t\t\trootCwd: parsed.rootCwd,\n\t\t\tpid: parsed.pid,\n\t\t\tcreatedAt: parsed.createdAt,\n\t\t\tstartedAt: parsed.startedAt,\n\t\t\tsource: parsed.source,\n\t\t\trequestedStopAt: parsed.requestedStopAt,\n\t\t\tlogPath: parsed.logPath,\n\t\t\tfinishedAtPath: parsed.finishedAtPath,\n\t\t\texitCodePath: parsed.exitCodePath,\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction saveMeta(metaPath: string, meta: BackgroundProcessMeta): void {\n\twriteFileSync(metaPath, `${JSON.stringify(meta, null, 2)}\\n`, \"utf8\");\n}\n\nfunction readFileTailUtf8(path: string, maxBytes: number): string {\n\tif (!existsSync(path)) return \"\";\n\tconst fd = openSync(path, \"r\");\n\ttry {\n\t\tconst size = fstatSync(fd).size;\n\t\tif (size <= 0) return \"\";\n\t\tconst readSize = Math.min(size, maxBytes);\n\t\tconst buffer = Buffer.alloc(readSize);\n\t\treadSync(fd, buffer, 0, readSize, size - readSize);\n\t\treturn buffer.toString(\"utf8\");\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n}\n\nexport function startBackgroundProcess(input: StartBackgroundProcessInput): BackgroundProcessRecord {\n\tconst rootCwd = resolve(input.rootCwd);\n\tconst processCwd = resolve(input.cwd ?? rootCwd);\n\tif (!existsSync(processCwd) || !statSync(processCwd).isDirectory()) {\n\t\tthrow new Error(`Background process cwd does not exist: ${processCwd}`);\n\t}\n\n\tconst processesDir = getProcessesDir(rootCwd);\n\tmkdirSync(processesDir, { recursive: true });\n\n\tconst id = `bg_${Date.now()}_${randomBytes(4).toString(\"hex\")}`;\n\tconst logPath = join(processesDir, `${id}.log`);\n\tconst finishedAtPath = join(processesDir, `${id}.finished`);\n\tconst exitCodePath = join(processesDir, `${id}.exitcode`);\n\tconst metaPath = join(processesDir, `${id}.json`);\n\tconst now = new Date().toISOString();\n\n\tconst { shell, args } = getShellConfig();\n\tconst wrappedCommand = [\n\t\t\"(\",\n\t\tinput.command,\n\t\t\")\",\n\t\t\"__iosm_bg_exit_code=$?\",\n\t\t`__iosm_bg_finished_at=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || node -e 'console.log(new Date().toISOString())')`,\n\t\t`printf \"%s\\\\n\" \"$__iosm_bg_finished_at\" > ${shellQuote(finishedAtPath)}`,\n\t\t`printf \"%s\\\\n\" \"$__iosm_bg_exit_code\" > ${shellQuote(exitCodePath)}`,\n\t\t'exit \"$__iosm_bg_exit_code\"',\n\t].join(\"\\n\");\n\n\tconst logFd = openSync(logPath, \"a\");\n\tconst child = spawn(shell, [...args, wrappedCommand], {\n\t\tcwd: processCwd,\n\t\tdetached: true,\n\t\tenv: getShellEnv(),\n\t\tstdio: [\"ignore\", logFd, logFd],\n\t});\n\tcloseSync(logFd);\n\n\tif (!child.pid) {\n\t\tthrow new Error(\"Failed to start background process: missing pid.\");\n\t}\n\n\tchild.unref();\n\n\tconst meta: BackgroundProcessMeta = {\n\t\tid,\n\t\tcommand: input.command,\n\t\tcwd: processCwd,\n\t\trootCwd,\n\t\tpid: child.pid,\n\t\tcreatedAt: now,\n\t\tstartedAt: now,\n\t\tsource: input.source,\n\t\tlogPath,\n\t\tfinishedAtPath,\n\t\texitCodePath,\n\t};\n\tsaveMeta(metaPath, meta);\n\treturn toRecord(meta, metaPath);\n}\n\nexport function listBackgroundProcesses(rootCwd: string, limit = 20): BackgroundProcessRecord[] {\n\tconst dir = getProcessesDir(resolve(rootCwd));\n\tif (!existsSync(dir)) return [];\n\tconst maxItems = Math.max(1, Math.min(200, Math.floor(limit)));\n\tconst files = readdirSync(dir)\n\t\t.filter((name) => name.endsWith(\".json\"))\n\t\t.map((name) => join(dir, name))\n\t\t.sort((a, b) => b.localeCompare(a))\n\t\t.slice(0, maxItems);\n\n\tconst records: BackgroundProcessRecord[] = [];\n\tfor (const metaPath of files) {\n\t\tconst meta = loadMeta(metaPath);\n\t\tif (!meta) continue;\n\t\trecords.push(toRecord(meta, metaPath));\n\t}\n\treturn records;\n}\n\nexport function getBackgroundProcess(rootCwd: string, id: string): BackgroundProcessRecord | undefined {\n\tconst dir = getProcessesDir(resolve(rootCwd));\n\tconst metaPath = join(dir, `${id}.json`);\n\tif (!existsSync(metaPath)) return undefined;\n\tconst meta = loadMeta(metaPath);\n\tif (!meta) return undefined;\n\treturn toRecord(meta, metaPath);\n}\n\nexport function stopBackgroundProcess(rootCwd: string, id: string): BackgroundProcessRecord | undefined {\n\tconst dir = getProcessesDir(resolve(rootCwd));\n\tconst metaPath = join(dir, `${id}.json`);\n\tconst meta = loadMeta(metaPath);\n\tif (!meta) return undefined;\n\n\tconst wasAlive = isProcessAlive(meta.pid);\n\tif (!wasAlive) {\n\t\treturn toRecord(meta, metaPath);\n\t}\n\n\tmeta.requestedStopAt = new Date().toISOString();\n\tsaveMeta(metaPath, meta);\n\n\t// Try graceful shutdown first and then enforce a hard kill in the background if needed.\n\tsignalProcessTree(meta.pid, \"SIGTERM\");\n\tscheduleForceKill(meta.pid);\n\n\tif (isProcessAlive(meta.pid)) {\n\t\treturn toRecord(meta, metaPath);\n\t}\n\n\t// Process already stopped synchronously; write fallback completion markers if shell did not flush them.\n\tconst hasFinishedAt = existsSync(meta.finishedAtPath);\n\tconst hasExitCode = existsSync(meta.exitCodePath);\n\tif (!hasFinishedAt || !hasExitCode) {\n\t\twriteCompletionArtifacts(meta, new Date().toISOString(), BACKGROUND_STOP_FALLBACK_EXIT_CODE);\n\t}\n\n\treturn toRecord(meta, metaPath);\n}\n\nexport function readBackgroundProcessLogTail(rootCwd: string, id: string, lines = 80): string | undefined {\n\tconst record = getBackgroundProcess(rootCwd, id);\n\tif (!record) return undefined;\n\tconst maxLines = Math.max(1, Math.min(1000, Math.floor(lines)));\n\tconst tail = readFileTailUtf8(record.logPath, MAX_LOG_TAIL_BYTES);\n\tif (!tail) return \"\";\n\tconst chunks = tail.split(/\\r?\\n/);\n\tconst sliced = chunks.slice(-maxLines);\n\treturn sliced.join(\"\\n\").trimEnd();\n}\n\nexport function pruneBackgroundProcesses(\n\trootCwd: string,\n\toptions?: {\n\t\tmaxAgeHours?: number;\n\t\tlimit?: number;\n\t},\n): PruneBackgroundProcessesResult {\n\tconst thresholdHoursRaw = options?.maxAgeHours;\n\tconst thresholdHours =\n\t\ttypeof thresholdHoursRaw === \"number\" && Number.isFinite(thresholdHoursRaw)\n\t\t\t? Math.max(1, Math.floor(thresholdHoursRaw))\n\t\t\t: 168;\n\tconst limitRaw = options?.limit;\n\tconst limit = typeof limitRaw === \"number\" && Number.isFinite(limitRaw) ? Math.max(1, Math.floor(limitRaw)) : 1000;\n\tconst thresholdMs = thresholdHours * 60 * 60 * 1000;\n\tconst now = Date.now();\n\tconst result: PruneBackgroundProcessesResult = {\n\t\tremoved: 0,\n\t\tremovedIds: [],\n\t\tskippedRunning: 0,\n\t\tskippedRecent: 0,\n\t\tthresholdHours,\n\t};\n\n\tconst dir = getProcessesDir(resolve(rootCwd));\n\tif (!existsSync(dir)) return result;\n\n\tconst metaPaths = readdirSync(dir)\n\t\t.filter((name) => name.endsWith(\".json\"))\n\t\t.map((name) => join(dir, name))\n\t\t.sort((a, b) => b.localeCompare(a))\n\t\t.slice(0, limit);\n\n\tfor (const metaPath of metaPaths) {\n\t\tconst meta = loadMeta(metaPath);\n\t\tif (!meta) continue;\n\t\tconst record = toRecord(meta, metaPath);\n\t\tif (record.status === \"running\") {\n\t\t\tresult.skippedRunning += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst createdAtMs = Date.parse(record.createdAt);\n\t\tif (Number.isFinite(createdAtMs) && now - createdAtMs < thresholdMs) {\n\t\t\tresult.skippedRecent += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst filesToDelete = [record.metaPath, record.logPath, record.finishedAtPath, record.exitCodePath];\n\t\tfor (const filePath of filesToDelete) {\n\t\t\ttry {\n\t\t\t\trmSync(filePath, { force: true });\n\t\t\t} catch {\n\t\t\t\t// Ignore cleanup errors for best-effort pruning.\n\t\t\t}\n\t\t}\n\t\tresult.removed += 1;\n\t\tresult.removedIds.push(record.id);\n\t}\n\n\treturn result;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"background-processes.js","sourceRoot":"","sources":["../../src/core/background-processes.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport {\n\tcloseSync,\n\texistsSync,\n\tfstatSync,\n\tmkdirSync,\n\topenSync,\n\treadFileSync,\n\treadSync,\n\treaddirSync,\n\trmSync,\n\tstatSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { spawn, spawnSync } from \"child_process\";\nimport { adaptCommandForShell, getShellConfig, getShellEnv, killProcessTree } from \"../utils/shell.js\";\n\nexport type BackgroundProcessStatus = \"running\" | \"done\" | \"error\" | \"terminated\" | \"unknown\";\n\ninterface BackgroundProcessMeta {\n\tid: string;\n\tcommand: string;\n\tcwd: string;\n\trootCwd: string;\n\tpid: number;\n\tcreatedAt: string;\n\tstartedAt: string;\n\tsource?: \"interactive\" | \"tool\";\n\trequestedStopAt?: string;\n\tlogPath: string;\n\tfinishedAtPath: string;\n\texitCodePath: string;\n}\n\nexport interface BackgroundProcessRecord {\n\tid: string;\n\tcommand: string;\n\tcwd: string;\n\trootCwd: string;\n\tpid: number;\n\tcreatedAt: string;\n\tstartedAt: string;\n\tsource?: \"interactive\" | \"tool\";\n\trequestedStopAt?: string;\n\tlogPath: string;\n\tfinishedAtPath: string;\n\texitCodePath: string;\n\tmetaPath: string;\n\tstatus: BackgroundProcessStatus;\n\tfinishedAt?: string;\n\texitCode?: number;\n}\n\nexport interface StartBackgroundProcessInput {\n\trootCwd: string;\n\tcommand: string;\n\tcwd?: string;\n\tsource?: \"interactive\" | \"tool\";\n}\n\nexport interface PruneBackgroundProcessesResult {\n\tremoved: number;\n\tremovedIds: string[];\n\tskippedRunning: number;\n\tskippedRecent: number;\n\tthresholdHours: number;\n}\n\nconst BACKGROUND_DIR_SEGMENTS = [\".iosm\", \"background\", \"processes\"] as const;\nconst MAX_LOG_TAIL_BYTES = 256 * 1024;\nconst BACKGROUND_STOP_FORCE_KILL_DELAY_MS = 1500;\nconst BACKGROUND_STOP_FALLBACK_EXIT_CODE = 143;\n\nfunction getProcessesDir(rootCwd: string): string {\n\treturn join(rootCwd, ...BACKGROUND_DIR_SEGMENTS);\n}\n\nfunction shellQuote(value: string): string {\n\treturn `'${value.replace(/'/g, `'\\\"'\\\"'`)}'`;\n}\n\nfunction isProcessAlive(pid: number): boolean {\n\ttry {\n\t\tprocess.kill(pid, 0);\n\t\treturn true;\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException | undefined)?.code === \"EPERM\") return true;\n\t\treturn false;\n\t}\n}\n\nfunction parseIntegerFile(path: string): number | undefined {\n\tif (!existsSync(path)) return undefined;\n\ttry {\n\t\tconst raw = readFileSync(path, \"utf8\").trim();\n\t\tif (!raw) return undefined;\n\t\tconst value = Number.parseInt(raw, 10);\n\t\treturn Number.isFinite(value) ? value : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction parseTextFile(path: string): string | undefined {\n\tif (!existsSync(path)) return undefined;\n\ttry {\n\t\tconst raw = readFileSync(path, \"utf8\").trim();\n\t\treturn raw || undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction writeCompletionArtifacts(meta: BackgroundProcessMeta, finishedAt: string, exitCode: number): void {\n\twriteFileSync(meta.finishedAtPath, `${finishedAt}\\n`, \"utf8\");\n\twriteFileSync(meta.exitCodePath, `${exitCode}\\n`, \"utf8\");\n}\n\nfunction signalProcessTree(pid: number, signal: \"SIGTERM\" | \"SIGKILL\"): void {\n\tif (process.platform === \"win32\") {\n\t\t// /F is forced kill; omit it for soft terminate attempt.\n\t\tconst args = signal === \"SIGKILL\" ? [\"/F\", \"/T\", \"/PID\", String(pid)] : [\"/T\", \"/PID\", String(pid)];\n\t\ttry {\n\t\t\tspawnSync(\"taskkill\", args, { stdio: \"ignore\", timeout: 5000 });\n\t\t} catch {\n\t\t\t// Ignore termination errors (process may already be gone).\n\t\t}\n\t\treturn;\n\t}\n\n\ttry {\n\t\tprocess.kill(-pid, signal);\n\t} catch {\n\t\ttry {\n\t\t\tprocess.kill(pid, signal);\n\t\t} catch {\n\t\t\t// Process already dead.\n\t\t}\n\t}\n}\n\nfunction scheduleForceKill(pid: number): void {\n\tconst timer = setTimeout(() => {\n\t\tif (!isProcessAlive(pid)) return;\n\t\tkillProcessTree(pid);\n\t}, BACKGROUND_STOP_FORCE_KILL_DELAY_MS);\n\ttimer.unref();\n}\n\nfunction toRecord(meta: BackgroundProcessMeta, metaPath: string): BackgroundProcessRecord {\n\tconst finishedAt = parseTextFile(meta.finishedAtPath);\n\tconst exitCode = parseIntegerFile(meta.exitCodePath);\n\tconst alive = isProcessAlive(meta.pid);\n\tlet status: BackgroundProcessStatus = \"unknown\";\n\tif (!alive && meta.requestedStopAt) {\n\t\tstatus = \"terminated\";\n\t} else if (alive) {\n\t\tstatus = \"running\";\n\t} else if (finishedAt && typeof exitCode === \"number\") {\n\t\tstatus = exitCode === 0 ? \"done\" : \"error\";\n\t}\n\n\treturn {\n\t\tid: meta.id,\n\t\tcommand: meta.command,\n\t\tcwd: meta.cwd,\n\t\trootCwd: meta.rootCwd,\n\t\tpid: meta.pid,\n\t\tcreatedAt: meta.createdAt,\n\t\tstartedAt: meta.startedAt,\n\t\tsource: meta.source,\n\t\trequestedStopAt: meta.requestedStopAt,\n\t\tlogPath: meta.logPath,\n\t\tfinishedAtPath: meta.finishedAtPath,\n\t\texitCodePath: meta.exitCodePath,\n\t\tmetaPath,\n\t\tstatus,\n\t\tfinishedAt,\n\t\texitCode,\n\t};\n}\n\nfunction loadMeta(metaPath: string): BackgroundProcessMeta | undefined {\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(metaPath, \"utf8\")) as Partial<BackgroundProcessMeta>;\n\t\tif (\n\t\t\ttypeof parsed.id !== \"string\" ||\n\t\t\ttypeof parsed.command !== \"string\" ||\n\t\t\ttypeof parsed.cwd !== \"string\" ||\n\t\t\ttypeof parsed.rootCwd !== \"string\" ||\n\t\t\ttypeof parsed.pid !== \"number\" ||\n\t\t\ttypeof parsed.createdAt !== \"string\" ||\n\t\t\ttypeof parsed.startedAt !== \"string\" ||\n\t\t\ttypeof parsed.logPath !== \"string\" ||\n\t\t\ttypeof parsed.finishedAtPath !== \"string\" ||\n\t\t\ttypeof parsed.exitCodePath !== \"string\"\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn {\n\t\t\tid: parsed.id,\n\t\t\tcommand: parsed.command,\n\t\t\tcwd: parsed.cwd,\n\t\t\trootCwd: parsed.rootCwd,\n\t\t\tpid: parsed.pid,\n\t\t\tcreatedAt: parsed.createdAt,\n\t\t\tstartedAt: parsed.startedAt,\n\t\t\tsource: parsed.source,\n\t\t\trequestedStopAt: parsed.requestedStopAt,\n\t\t\tlogPath: parsed.logPath,\n\t\t\tfinishedAtPath: parsed.finishedAtPath,\n\t\t\texitCodePath: parsed.exitCodePath,\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction saveMeta(metaPath: string, meta: BackgroundProcessMeta): void {\n\twriteFileSync(metaPath, `${JSON.stringify(meta, null, 2)}\\n`, \"utf8\");\n}\n\nfunction readFileTailUtf8(path: string, maxBytes: number): string {\n\tif (!existsSync(path)) return \"\";\n\tconst fd = openSync(path, \"r\");\n\ttry {\n\t\tconst size = fstatSync(fd).size;\n\t\tif (size <= 0) return \"\";\n\t\tconst readSize = Math.min(size, maxBytes);\n\t\tconst buffer = Buffer.alloc(readSize);\n\t\treadSync(fd, buffer, 0, readSize, size - readSize);\n\t\treturn buffer.toString(\"utf8\");\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n}\n\nexport function startBackgroundProcess(input: StartBackgroundProcessInput): BackgroundProcessRecord {\n\tconst rootCwd = resolve(input.rootCwd);\n\tconst processCwd = resolve(input.cwd ?? rootCwd);\n\tif (!existsSync(processCwd) || !statSync(processCwd).isDirectory()) {\n\t\tthrow new Error(`Background process cwd does not exist: ${processCwd}`);\n\t}\n\n\tconst processesDir = getProcessesDir(rootCwd);\n\tmkdirSync(processesDir, { recursive: true });\n\n\tconst id = `bg_${Date.now()}_${randomBytes(4).toString(\"hex\")}`;\n\tconst logPath = join(processesDir, `${id}.log`);\n\tconst finishedAtPath = join(processesDir, `${id}.finished`);\n\tconst exitCodePath = join(processesDir, `${id}.exitcode`);\n\tconst metaPath = join(processesDir, `${id}.json`);\n\tconst now = new Date().toISOString();\n\n\tconst { shell, args } = getShellConfig();\n\tconst adaptedCommand = adaptCommandForShell(input.command);\n\tconst wrappedCommand = [\n\t\t\"(\",\n\t\tadaptedCommand,\n\t\t\")\",\n\t\t\"__iosm_bg_exit_code=$?\",\n\t\t`__iosm_bg_finished_at=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || node -e 'console.log(new Date().toISOString())')`,\n\t\t`printf \"%s\\\\n\" \"$__iosm_bg_finished_at\" > ${shellQuote(finishedAtPath)}`,\n\t\t`printf \"%s\\\\n\" \"$__iosm_bg_exit_code\" > ${shellQuote(exitCodePath)}`,\n\t\t'exit \"$__iosm_bg_exit_code\"',\n\t].join(\"\\n\");\n\n\tconst logFd = openSync(logPath, \"a\");\n\tconst child = spawn(shell, [...args, wrappedCommand], {\n\t\tcwd: processCwd,\n\t\tdetached: true,\n\t\tenv: getShellEnv(),\n\t\tstdio: [\"ignore\", logFd, logFd],\n\t});\n\tcloseSync(logFd);\n\n\tif (!child.pid) {\n\t\tthrow new Error(\"Failed to start background process: missing pid.\");\n\t}\n\n\tchild.unref();\n\n\tconst meta: BackgroundProcessMeta = {\n\t\tid,\n\t\tcommand: input.command,\n\t\tcwd: processCwd,\n\t\trootCwd,\n\t\tpid: child.pid,\n\t\tcreatedAt: now,\n\t\tstartedAt: now,\n\t\tsource: input.source,\n\t\tlogPath,\n\t\tfinishedAtPath,\n\t\texitCodePath,\n\t};\n\tsaveMeta(metaPath, meta);\n\treturn toRecord(meta, metaPath);\n}\n\nexport function listBackgroundProcesses(rootCwd: string, limit = 20): BackgroundProcessRecord[] {\n\tconst dir = getProcessesDir(resolve(rootCwd));\n\tif (!existsSync(dir)) return [];\n\tconst maxItems = Math.max(1, Math.min(200, Math.floor(limit)));\n\tconst files = readdirSync(dir)\n\t\t.filter((name) => name.endsWith(\".json\"))\n\t\t.map((name) => join(dir, name))\n\t\t.sort((a, b) => b.localeCompare(a))\n\t\t.slice(0, maxItems);\n\n\tconst records: BackgroundProcessRecord[] = [];\n\tfor (const metaPath of files) {\n\t\tconst meta = loadMeta(metaPath);\n\t\tif (!meta) continue;\n\t\trecords.push(toRecord(meta, metaPath));\n\t}\n\treturn records;\n}\n\nexport function getBackgroundProcess(rootCwd: string, id: string): BackgroundProcessRecord | undefined {\n\tconst dir = getProcessesDir(resolve(rootCwd));\n\tconst metaPath = join(dir, `${id}.json`);\n\tif (!existsSync(metaPath)) return undefined;\n\tconst meta = loadMeta(metaPath);\n\tif (!meta) return undefined;\n\treturn toRecord(meta, metaPath);\n}\n\nexport function stopBackgroundProcess(rootCwd: string, id: string): BackgroundProcessRecord | undefined {\n\tconst dir = getProcessesDir(resolve(rootCwd));\n\tconst metaPath = join(dir, `${id}.json`);\n\tconst meta = loadMeta(metaPath);\n\tif (!meta) return undefined;\n\n\tconst wasAlive = isProcessAlive(meta.pid);\n\tif (!wasAlive) {\n\t\treturn toRecord(meta, metaPath);\n\t}\n\n\tmeta.requestedStopAt = new Date().toISOString();\n\tsaveMeta(metaPath, meta);\n\n\t// Try graceful shutdown first and then enforce a hard kill in the background if needed.\n\tsignalProcessTree(meta.pid, \"SIGTERM\");\n\tscheduleForceKill(meta.pid);\n\n\tif (isProcessAlive(meta.pid)) {\n\t\treturn toRecord(meta, metaPath);\n\t}\n\n\t// Process already stopped synchronously; write fallback completion markers if shell did not flush them.\n\tconst hasFinishedAt = existsSync(meta.finishedAtPath);\n\tconst hasExitCode = existsSync(meta.exitCodePath);\n\tif (!hasFinishedAt || !hasExitCode) {\n\t\twriteCompletionArtifacts(meta, new Date().toISOString(), BACKGROUND_STOP_FALLBACK_EXIT_CODE);\n\t}\n\n\treturn toRecord(meta, metaPath);\n}\n\nexport function readBackgroundProcessLogTail(rootCwd: string, id: string, lines = 80): string | undefined {\n\tconst record = getBackgroundProcess(rootCwd, id);\n\tif (!record) return undefined;\n\tconst maxLines = Math.max(1, Math.min(1000, Math.floor(lines)));\n\tconst tail = readFileTailUtf8(record.logPath, MAX_LOG_TAIL_BYTES);\n\tif (!tail) return \"\";\n\tconst chunks = tail.split(/\\r?\\n/);\n\tconst sliced = chunks.slice(-maxLines);\n\treturn sliced.join(\"\\n\").trimEnd();\n}\n\nexport function pruneBackgroundProcesses(\n\trootCwd: string,\n\toptions?: {\n\t\tmaxAgeHours?: number;\n\t\tlimit?: number;\n\t},\n): PruneBackgroundProcessesResult {\n\tconst thresholdHoursRaw = options?.maxAgeHours;\n\tconst thresholdHours =\n\t\ttypeof thresholdHoursRaw === \"number\" && Number.isFinite(thresholdHoursRaw)\n\t\t\t? Math.max(1, Math.floor(thresholdHoursRaw))\n\t\t\t: 168;\n\tconst limitRaw = options?.limit;\n\tconst limit = typeof limitRaw === \"number\" && Number.isFinite(limitRaw) ? Math.max(1, Math.floor(limitRaw)) : 1000;\n\tconst thresholdMs = thresholdHours * 60 * 60 * 1000;\n\tconst now = Date.now();\n\tconst result: PruneBackgroundProcessesResult = {\n\t\tremoved: 0,\n\t\tremovedIds: [],\n\t\tskippedRunning: 0,\n\t\tskippedRecent: 0,\n\t\tthresholdHours,\n\t};\n\n\tconst dir = getProcessesDir(resolve(rootCwd));\n\tif (!existsSync(dir)) return result;\n\n\tconst metaPaths = readdirSync(dir)\n\t\t.filter((name) => name.endsWith(\".json\"))\n\t\t.map((name) => join(dir, name))\n\t\t.sort((a, b) => b.localeCompare(a))\n\t\t.slice(0, limit);\n\n\tfor (const metaPath of metaPaths) {\n\t\tconst meta = loadMeta(metaPath);\n\t\tif (!meta) continue;\n\t\tconst record = toRecord(meta, metaPath);\n\t\tif (record.status === \"running\") {\n\t\t\tresult.skippedRunning += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst createdAtMs = Date.parse(record.createdAt);\n\t\tif (Number.isFinite(createdAtMs) && now - createdAtMs < thresholdMs) {\n\t\t\tresult.skippedRecent += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst filesToDelete = [record.metaPath, record.logPath, record.finishedAtPath, record.exitCodePath];\n\t\tfor (const filePath of filesToDelete) {\n\t\t\ttry {\n\t\t\t\trmSync(filePath, { force: true });\n\t\t\t} catch {\n\t\t\t\t// Ignore cleanup errors for best-effort pruning.\n\t\t\t}\n\t\t}\n\t\tresult.removed += 1;\n\t\tresult.removedIds.push(record.id);\n\t}\n\n\treturn result;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash-executor.d.ts","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAOtD,MAAM,WAAW,mBAAmB;IACnC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,mCAAmC;IACnC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IAC1B,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,mDAAmD;IACnD,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iDAAiD;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"bash-executor.d.ts","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAOtD,MAAM,WAAW,mBAAmB;IACnC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,mCAAmC;IACnC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IAC1B,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,mDAAmD;IACnD,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iDAAiD;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CA0H/F;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC9C,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,cAAc,EAC1B,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC,CAsFrB"}
|
|
@@ -11,7 +11,7 @@ import { tmpdir } from "node:os";
|
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
import { spawn } from "child_process";
|
|
13
13
|
import stripAnsi from "strip-ansi";
|
|
14
|
-
import { getShellConfig, getShellEnv, killProcessTree, sanitizeBinaryOutput } from "../utils/shell.js";
|
|
14
|
+
import { adaptCommandForShell, getShellConfig, getShellEnv, killProcessTree, sanitizeBinaryOutput } from "../utils/shell.js";
|
|
15
15
|
import { DEFAULT_MAX_BYTES, truncateTail } from "./tools/truncate.js";
|
|
16
16
|
// ============================================================================
|
|
17
17
|
// Implementation
|
|
@@ -33,7 +33,8 @@ import { DEFAULT_MAX_BYTES, truncateTail } from "./tools/truncate.js";
|
|
|
33
33
|
export function executeBash(command, options) {
|
|
34
34
|
return new Promise((resolve, reject) => {
|
|
35
35
|
const { shell, args } = getShellConfig();
|
|
36
|
-
const
|
|
36
|
+
const adaptedCommand = adaptCommandForShell(command);
|
|
37
|
+
const child = spawn(shell, [...args, adaptedCommand], {
|
|
37
38
|
cwd: options?.cwd,
|
|
38
39
|
detached: true,
|
|
39
40
|
env: getShellEnv(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash-executor.js","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash command execution with streaming support and cancellation.\n *\n * This module provides a unified bash execution implementation used by:\n * - AgentSession.executeBash() for interactive and RPC modes\n * - Direct calls from modes that need bash execution\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { type ChildProcess, spawn } from \"child_process\";\nimport stripAnsi from \"strip-ansi\";\nimport { getShellConfig, getShellEnv, killProcessTree, sanitizeBinaryOutput } from \"../utils/shell.js\";\nimport type { BashOperations } from \"./tools/bash.js\";\nimport { DEFAULT_MAX_BYTES, truncateTail } from \"./tools/truncate.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BashExecutorOptions {\n\t/** Callback for streaming output chunks (already sanitized) */\n\tonChunk?: (chunk: string) => void;\n\t/** AbortSignal for cancellation */\n\tsignal?: AbortSignal;\n\t/** Working directory for command execution. Defaults to process.cwd() */\n\tcwd?: string;\n}\n\nexport interface BashResult {\n\t/** Combined stdout + stderr output (sanitized, possibly truncated) */\n\toutput: string;\n\t/** Process exit code (undefined if killed/cancelled) */\n\texitCode: number | undefined;\n\t/** Whether the command was cancelled via signal */\n\tcancelled: boolean;\n\t/** Whether the output was truncated */\n\ttruncated: boolean;\n\t/** Path to temp file containing full output (if output exceeded truncation threshold) */\n\tfullOutputPath?: string;\n\t/** Background process id (present only for detached background execution). */\n\tbackgroundTaskId?: string;\n\t/** Path to JSON status metadata for a background process. */\n\tbackgroundStatusPath?: string;\n\t/** Path to log file for a background process. */\n\tbackgroundLogPath?: string;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Execute a bash command with optional streaming and cancellation support.\n *\n * Features:\n * - Streams sanitized output via onChunk callback\n * - Writes large output to temp file for later retrieval\n * - Supports cancellation via AbortSignal\n * - Sanitizes output (strips ANSI, removes binary garbage, normalizes newlines)\n * - Truncates output if it exceeds the default max bytes\n *\n * @param command - The bash command to execute\n * @param options - Optional streaming callback and abort signal\n * @returns Promise resolving to execution result\n */\nexport function executeBash(command: string, options?: BashExecutorOptions): Promise<BashResult> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst { shell, args } = getShellConfig();\n\t\tconst child: ChildProcess = spawn(shell, [...args, command], {\n\t\t\tcwd: options?.cwd,\n\t\t\tdetached: true,\n\t\t\tenv: getShellEnv(),\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t});\n\n\t\t// Track sanitized output for truncation\n\t\tconst outputChunks: string[] = [];\n\t\tlet outputBytes = 0;\n\t\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\t\t// Temp file for large output\n\t\tlet tempFilePath: string | undefined;\n\t\tlet tempFileStream: WriteStream | undefined;\n\t\tlet totalBytes = 0;\n\n\t\t// Handle abort signal\n\t\tconst abortHandler = () => {\n\t\t\tif (child.pid) {\n\t\t\t\tkillProcessTree(child.pid);\n\t\t\t}\n\t\t};\n\n\t\tif (options?.signal) {\n\t\t\tif (options.signal.aborted) {\n\t\t\t\t// Already aborted, don't even start\n\t\t\t\tchild.kill();\n\t\t\t\tresolve({\n\t\t\t\t\toutput: \"\",\n\t\t\t\t\texitCode: undefined,\n\t\t\t\t\tcancelled: true,\n\t\t\t\t\ttruncated: false,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler, { once: true });\n\t\t}\n\n\t\tconst decoder = new TextDecoder();\n\n\t\tconst handleData = (data: Buffer) => {\n\t\t\ttotalBytes += data.length;\n\n\t\t\t// Sanitize once at the source: strip ANSI, replace binary garbage, normalize newlines\n\t\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t\t// Start writing to temp file if exceeds threshold\n\t\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\t\t\ttempFilePath = join(tmpdir(), `iosm-bash-${id}.log`);\n\t\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\t\t// Write already-buffered chunks to temp file\n\t\t\t\tfor (const chunk of outputChunks) {\n\t\t\t\t\ttempFileStream.write(chunk);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.write(text);\n\t\t\t}\n\n\t\t\t// Keep rolling buffer of sanitized text\n\t\t\toutputChunks.push(text);\n\t\t\toutputBytes += text.length;\n\t\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\t\tconst removed = outputChunks.shift()!;\n\t\t\t\toutputBytes -= removed.length;\n\t\t\t}\n\n\t\t\t// Stream to callback if provided\n\t\t\tif (options?.onChunk) {\n\t\t\t\toptions.onChunk(text);\n\t\t\t}\n\t\t};\n\n\t\tchild.stdout?.on(\"data\", handleData);\n\t\tchild.stderr?.on(\"data\", handleData);\n\n\t\tchild.on(\"close\", (code) => {\n\t\t\t// Clean up abort listener\n\t\t\tif (options?.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.end();\n\t\t\t}\n\n\t\t\t// Combine buffered chunks for truncation (already sanitized)\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\n\t\t\t// code === null means killed (cancelled)\n\t\t\tconst cancelled = code === null;\n\n\t\t\tresolve({\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: cancelled ? undefined : code,\n\t\t\t\tcancelled,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t});\n\t\t});\n\n\t\tchild.on(\"error\", (err) => {\n\t\t\t// Clean up abort listener\n\t\t\tif (options?.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.end();\n\t\t\t}\n\n\t\t\treject(err);\n\t\t});\n\t});\n}\n\n/**\n * Execute a bash command using custom BashOperations.\n * Used for remote execution (SSH, containers, etc.).\n */\nexport async function executeBashWithOperations(\n\tcommand: string,\n\tcwd: string,\n\toperations: BashOperations,\n\toptions?: BashExecutorOptions,\n): Promise<BashResult> {\n\tconst outputChunks: string[] = [];\n\tlet outputBytes = 0;\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\tlet tempFilePath: string | undefined;\n\tlet tempFileStream: WriteStream | undefined;\n\tlet totalBytes = 0;\n\n\tconst decoder = new TextDecoder();\n\n\tconst onData = (data: Buffer) => {\n\t\ttotalBytes += data.length;\n\n\t\t// Sanitize: strip ANSI, replace binary garbage, normalize newlines\n\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t// Start writing to temp file if exceeds threshold\n\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\t\ttempFilePath = join(tmpdir(), `iosm-bash-${id}.log`);\n\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\tfor (const chunk of outputChunks) {\n\t\t\t\ttempFileStream.write(chunk);\n\t\t\t}\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.write(text);\n\t\t}\n\n\t\t// Keep rolling buffer\n\t\toutputChunks.push(text);\n\t\toutputBytes += text.length;\n\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\tconst removed = outputChunks.shift()!;\n\t\t\toutputBytes -= removed.length;\n\t\t}\n\n\t\t// Stream to callback\n\t\tif (options?.onChunk) {\n\t\t\toptions.onChunk(text);\n\t\t}\n\t};\n\n\ttry {\n\t\tconst result = await operations.exec(command, cwd, {\n\t\t\tonData,\n\t\t\tsignal: options?.signal,\n\t\t});\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\n\t\tconst fullOutput = outputChunks.join(\"\");\n\t\tconst truncationResult = truncateTail(fullOutput);\n\t\tconst cancelled = options?.signal?.aborted ?? false;\n\n\t\treturn {\n\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\texitCode: cancelled ? undefined : (result.exitCode ?? undefined),\n\t\t\tcancelled,\n\t\t\ttruncated: truncationResult.truncated,\n\t\t\tfullOutputPath: tempFilePath,\n\t\t};\n\t} catch (err) {\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\n\t\t// Check if it was an abort\n\t\tif (options?.signal?.aborted) {\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\t\t\treturn {\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: undefined,\n\t\t\t\tcancelled: true,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t};\n\t\t}\n\n\t\tthrow err;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bash-executor.js","sourceRoot":"","sources":["../../src/core/bash-executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash command execution with streaming support and cancellation.\n *\n * This module provides a unified bash execution implementation used by:\n * - AgentSession.executeBash() for interactive and RPC modes\n * - Direct calls from modes that need bash execution\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { type ChildProcess, spawn } from \"child_process\";\nimport stripAnsi from \"strip-ansi\";\nimport { adaptCommandForShell, getShellConfig, getShellEnv, killProcessTree, sanitizeBinaryOutput } from \"../utils/shell.js\";\nimport type { BashOperations } from \"./tools/bash.js\";\nimport { DEFAULT_MAX_BYTES, truncateTail } from \"./tools/truncate.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BashExecutorOptions {\n\t/** Callback for streaming output chunks (already sanitized) */\n\tonChunk?: (chunk: string) => void;\n\t/** AbortSignal for cancellation */\n\tsignal?: AbortSignal;\n\t/** Working directory for command execution. Defaults to process.cwd() */\n\tcwd?: string;\n}\n\nexport interface BashResult {\n\t/** Combined stdout + stderr output (sanitized, possibly truncated) */\n\toutput: string;\n\t/** Process exit code (undefined if killed/cancelled) */\n\texitCode: number | undefined;\n\t/** Whether the command was cancelled via signal */\n\tcancelled: boolean;\n\t/** Whether the output was truncated */\n\ttruncated: boolean;\n\t/** Path to temp file containing full output (if output exceeded truncation threshold) */\n\tfullOutputPath?: string;\n\t/** Background process id (present only for detached background execution). */\n\tbackgroundTaskId?: string;\n\t/** Path to JSON status metadata for a background process. */\n\tbackgroundStatusPath?: string;\n\t/** Path to log file for a background process. */\n\tbackgroundLogPath?: string;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Execute a bash command with optional streaming and cancellation support.\n *\n * Features:\n * - Streams sanitized output via onChunk callback\n * - Writes large output to temp file for later retrieval\n * - Supports cancellation via AbortSignal\n * - Sanitizes output (strips ANSI, removes binary garbage, normalizes newlines)\n * - Truncates output if it exceeds the default max bytes\n *\n * @param command - The bash command to execute\n * @param options - Optional streaming callback and abort signal\n * @returns Promise resolving to execution result\n */\nexport function executeBash(command: string, options?: BashExecutorOptions): Promise<BashResult> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst { shell, args } = getShellConfig();\n\t\tconst adaptedCommand = adaptCommandForShell(command);\n\t\tconst child: ChildProcess = spawn(shell, [...args, adaptedCommand], {\n\t\t\tcwd: options?.cwd,\n\t\t\tdetached: true,\n\t\t\tenv: getShellEnv(),\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t});\n\n\t\t// Track sanitized output for truncation\n\t\tconst outputChunks: string[] = [];\n\t\tlet outputBytes = 0;\n\t\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\t\t// Temp file for large output\n\t\tlet tempFilePath: string | undefined;\n\t\tlet tempFileStream: WriteStream | undefined;\n\t\tlet totalBytes = 0;\n\n\t\t// Handle abort signal\n\t\tconst abortHandler = () => {\n\t\t\tif (child.pid) {\n\t\t\t\tkillProcessTree(child.pid);\n\t\t\t}\n\t\t};\n\n\t\tif (options?.signal) {\n\t\t\tif (options.signal.aborted) {\n\t\t\t\t// Already aborted, don't even start\n\t\t\t\tchild.kill();\n\t\t\t\tresolve({\n\t\t\t\t\toutput: \"\",\n\t\t\t\t\texitCode: undefined,\n\t\t\t\t\tcancelled: true,\n\t\t\t\t\ttruncated: false,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler, { once: true });\n\t\t}\n\n\t\tconst decoder = new TextDecoder();\n\n\t\tconst handleData = (data: Buffer) => {\n\t\t\ttotalBytes += data.length;\n\n\t\t\t// Sanitize once at the source: strip ANSI, replace binary garbage, normalize newlines\n\t\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t\t// Start writing to temp file if exceeds threshold\n\t\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\t\t\ttempFilePath = join(tmpdir(), `iosm-bash-${id}.log`);\n\t\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\t\t// Write already-buffered chunks to temp file\n\t\t\t\tfor (const chunk of outputChunks) {\n\t\t\t\t\ttempFileStream.write(chunk);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.write(text);\n\t\t\t}\n\n\t\t\t// Keep rolling buffer of sanitized text\n\t\t\toutputChunks.push(text);\n\t\t\toutputBytes += text.length;\n\t\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\t\tconst removed = outputChunks.shift()!;\n\t\t\t\toutputBytes -= removed.length;\n\t\t\t}\n\n\t\t\t// Stream to callback if provided\n\t\t\tif (options?.onChunk) {\n\t\t\t\toptions.onChunk(text);\n\t\t\t}\n\t\t};\n\n\t\tchild.stdout?.on(\"data\", handleData);\n\t\tchild.stderr?.on(\"data\", handleData);\n\n\t\tchild.on(\"close\", (code) => {\n\t\t\t// Clean up abort listener\n\t\t\tif (options?.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.end();\n\t\t\t}\n\n\t\t\t// Combine buffered chunks for truncation (already sanitized)\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\n\t\t\t// code === null means killed (cancelled)\n\t\t\tconst cancelled = code === null;\n\n\t\t\tresolve({\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: cancelled ? undefined : code,\n\t\t\t\tcancelled,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t});\n\t\t});\n\n\t\tchild.on(\"error\", (err) => {\n\t\t\t// Clean up abort listener\n\t\t\tif (options?.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\n\t\t\tif (tempFileStream) {\n\t\t\t\ttempFileStream.end();\n\t\t\t}\n\n\t\t\treject(err);\n\t\t});\n\t});\n}\n\n/**\n * Execute a bash command using custom BashOperations.\n * Used for remote execution (SSH, containers, etc.).\n */\nexport async function executeBashWithOperations(\n\tcommand: string,\n\tcwd: string,\n\toperations: BashOperations,\n\toptions?: BashExecutorOptions,\n): Promise<BashResult> {\n\tconst outputChunks: string[] = [];\n\tlet outputBytes = 0;\n\tconst maxOutputBytes = DEFAULT_MAX_BYTES * 2;\n\n\tlet tempFilePath: string | undefined;\n\tlet tempFileStream: WriteStream | undefined;\n\tlet totalBytes = 0;\n\n\tconst decoder = new TextDecoder();\n\n\tconst onData = (data: Buffer) => {\n\t\ttotalBytes += data.length;\n\n\t\t// Sanitize: strip ANSI, replace binary garbage, normalize newlines\n\t\tconst text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\\r/g, \"\");\n\n\t\t// Start writing to temp file if exceeds threshold\n\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\tconst id = randomBytes(8).toString(\"hex\");\n\t\t\ttempFilePath = join(tmpdir(), `iosm-bash-${id}.log`);\n\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\tfor (const chunk of outputChunks) {\n\t\t\t\ttempFileStream.write(chunk);\n\t\t\t}\n\t\t}\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.write(text);\n\t\t}\n\n\t\t// Keep rolling buffer\n\t\toutputChunks.push(text);\n\t\toutputBytes += text.length;\n\t\twhile (outputBytes > maxOutputBytes && outputChunks.length > 1) {\n\t\t\tconst removed = outputChunks.shift()!;\n\t\t\toutputBytes -= removed.length;\n\t\t}\n\n\t\t// Stream to callback\n\t\tif (options?.onChunk) {\n\t\t\toptions.onChunk(text);\n\t\t}\n\t};\n\n\ttry {\n\t\tconst result = await operations.exec(command, cwd, {\n\t\t\tonData,\n\t\t\tsignal: options?.signal,\n\t\t});\n\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\n\t\tconst fullOutput = outputChunks.join(\"\");\n\t\tconst truncationResult = truncateTail(fullOutput);\n\t\tconst cancelled = options?.signal?.aborted ?? false;\n\n\t\treturn {\n\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\texitCode: cancelled ? undefined : (result.exitCode ?? undefined),\n\t\t\tcancelled,\n\t\t\ttruncated: truncationResult.truncated,\n\t\t\tfullOutputPath: tempFilePath,\n\t\t};\n\t} catch (err) {\n\t\tif (tempFileStream) {\n\t\t\ttempFileStream.end();\n\t\t}\n\n\t\t// Check if it was an abort\n\t\tif (options?.signal?.aborted) {\n\t\t\tconst fullOutput = outputChunks.join(\"\");\n\t\t\tconst truncationResult = truncateTail(fullOutput);\n\t\t\treturn {\n\t\t\t\toutput: truncationResult.truncated ? truncationResult.content : fullOutput,\n\t\t\t\texitCode: undefined,\n\t\t\t\tcancelled: true,\n\t\t\t\ttruncated: truncationResult.truncated,\n\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t};\n\t\t}\n\n\t\tthrow err;\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AAMtD,OAAO,EAAoD,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AACtH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAU5D,QAAA,MAAM,UAAU;;;;EAMd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;OAMG;IACH,IAAI,EAAE,CACL,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QACR,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;KACxB,KACG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CAC1C;AAmGD,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AAMtD,OAAO,EAAoD,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AACtH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAU5D,QAAA,MAAM,UAAU;;;;EAMd,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;OAMG;IACH,IAAI,EAAE,CACL,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QACR,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;KACxB,KACG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CAC1C;AAmGD,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;AAgC5E,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,oGAAoG;IACpG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,+DAA+D;IAC/D,eAAe,CAAC,EAAE,mBAAmB,CAAC;CACtC;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CA0MnG;AAED,0EAA0E;AAC1E,eAAO,MAAM,QAAQ;;;;QAAgC,CAAC"}
|
package/dist/core/tools/bash.js
CHANGED
|
@@ -5,7 +5,7 @@ import { join } from "node:path";
|
|
|
5
5
|
import { Type } from "@sinclair/typebox";
|
|
6
6
|
import { spawn } from "child_process";
|
|
7
7
|
import stripAnsi from "strip-ansi";
|
|
8
|
-
import { getShellConfig, getShellEnv, killProcessTree, sanitizeBinaryOutput } from "../../utils/shell.js";
|
|
8
|
+
import { adaptCommandForShell, getShellConfig, getShellEnv, killProcessTree, sanitizeBinaryOutput } from "../../utils/shell.js";
|
|
9
9
|
import { startBackgroundProcess } from "../background-processes.js";
|
|
10
10
|
import { isSandboxEnabledFromEnv, wrapCommandWithSandbox } from "../sandbox/executor.js";
|
|
11
11
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, truncateTail } from "./truncate.js";
|
|
@@ -128,7 +128,10 @@ function resolveSpawnContext(command, cwd, spawnHook) {
|
|
|
128
128
|
if (modified.cwd !== baseContext.cwd) {
|
|
129
129
|
process.stderr.write(`[iosm] Extension modified bash cwd: ${baseContext.cwd} → ${modified.cwd}\n`);
|
|
130
130
|
}
|
|
131
|
-
return
|
|
131
|
+
return {
|
|
132
|
+
...modified,
|
|
133
|
+
command: adaptCommandForShell(modified.command),
|
|
134
|
+
};
|
|
132
135
|
}
|
|
133
136
|
export function createBashTool(cwd, options) {
|
|
134
137
|
const ops = options?.operations ?? defaultBashOperations;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { createWriteStream, existsSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AgentTool } from \"@mariozechner/pi-agent-core\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { spawn } from \"child_process\";\nimport stripAnsi from \"strip-ansi\";\nimport { getShellConfig, getShellEnv, killProcessTree, sanitizeBinaryOutput } from \"../../utils/shell.js\";\nimport { startBackgroundProcess } from \"../background-processes.js\";\nimport { isSandboxEnabledFromEnv, wrapCommandWithSandbox } from \"../sandbox/executor.js\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateTail } from \"./truncate.js\";\nimport type { ToolPermissionGuard } from \"./permissions.js\";\n\n/**\n * Generate a unique temp file path for bash output\n */\nfunction getTempFilePath(): string {\n\tconst id = randomBytes(8).toString(\"hex\");\n\treturn join(tmpdir(), `iosm-bash-${id}.log`);\n}\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n\trun_in_background: Type.Optional(\n\t\tType.Boolean({ description: \"Run command in detached background mode and return task id immediately\" }),\n\t),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n\tbackgroundTaskId?: string;\n\tbackgroundStatusPath?: string;\n\tbackgroundLogPath?: string;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (e.g., SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command - The command to execute\n\t * @param cwd - Working directory\n\t * @param options - Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<{ exitCode: number | null }>;\n}\n\n/**\n * Default bash operations using local shell\n */\nconst defaultBashOperations: BashOperations = {\n\texec: (command, cwd, { onData, signal, timeout, env }) => {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst { shell, args } = getShellConfig();\n\n\t\t\tif (!existsSync(cwd)) {\n\t\t\t\treject(new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet wrapped;\n\t\t\ttry {\n\t\t\t\twrapped = wrapCommandWithSandbox({\n\t\t\t\t\tcommand: shell,\n\t\t\t\t\targs: [...args, command],\n\t\t\t\t\tcwd,\n\t\t\t\t\tenabled: isSandboxEnabledFromEnv(),\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\treject(error instanceof Error ? error : new Error(String(error)));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst child = spawn(wrapped.command, wrapped.args, {\n\t\t\t\tcwd,\n\t\t\t\tdetached: true,\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t});\n\n\t\t\tlet timedOut = false;\n\n\t\t\t// Set timeout if provided\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tif (timeout !== undefined && timeout > 0) {\n\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\ttimedOut = true;\n\t\t\t\t\tif (child.pid) {\n\t\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t\t}\n\t\t\t\t}, timeout * 1000);\n\t\t\t}\n\n\t\t\t// Stream stdout and stderr\n\t\t\tif (child.stdout) {\n\t\t\t\tchild.stdout.on(\"data\", onData);\n\t\t\t}\n\t\t\tif (child.stderr) {\n\t\t\t\tchild.stderr.on(\"data\", onData);\n\t\t\t}\n\n\t\t\t// Handle shell spawn errors\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t\treject(err);\n\t\t\t});\n\n\t\t\t// Handle abort signal - kill entire process tree\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) {\n\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tif (signal) {\n\t\t\t\tif (signal.aborted) {\n\t\t\t\t\tonAbort();\n\t\t\t\t} else {\n\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle process exit\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (timedOut) {\n\t\t\t\t\treject(new Error(`timeout:${timeout}`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tresolve({ exitCode: code });\n\t\t\t});\n\t\t});\n\t},\n};\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nfunction sanitizeBashChunk(text: string): string {\n\treturn sanitizeBinaryOutput(stripAnsi(text)).replace(/\\r/g, \"\");\n}\n\nfunction resolveSpawnContext(command: string, cwd: string, spawnHook?: BashSpawnHook): BashSpawnContext {\n\tconst baseContext: BashSpawnContext = {\n\t\tcommand,\n\t\tcwd,\n\t\tenv: { ...getShellEnv() },\n\t};\n\n\tif (!spawnHook) return baseContext;\n\n\tconst modified = spawnHook(baseContext);\n\n\tif (modified.command !== baseContext.command) {\n\t\tprocess.stderr.write(\n\t\t\t`[iosm] Extension modified bash command:\\n before: ${baseContext.command}\\n after: ${modified.command}\\n`,\n\t\t);\n\t}\n\tif (modified.cwd !== baseContext.cwd) {\n\t\tprocess.stderr.write(`[iosm] Extension modified bash cwd: ${baseContext.cwd} → ${modified.cwd}\\n`);\n\t}\n\n\treturn modified;\n}\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (e.g., \"shopt -s expand_aliases\" for alias support) */\n\tcommandPrefix?: string;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n\t/** Optional permission guard executed before command starts */\n\tpermissionGuard?: ToolPermissionGuard;\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\tconst ops = options?.operations ?? defaultBashOperations;\n\tconst commandPrefix = options?.commandPrefix;\n\tconst spawnHook = options?.spawnHook;\n\tconst permissionGuard = options?.permissionGuard;\n\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tparameters: bashSchema,\n\t\texecute: async (\n\t\t\t_toolCallId: string,\n\t\t\t{ command, timeout, run_in_background }: { command: string; timeout?: number; run_in_background?: boolean },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t) => {\n\t\t\tif (permissionGuard) {\n\t\t\t\tconst allowed = await permissionGuard({\n\t\t\t\t\ttoolName: \"bash\",\n\t\t\t\t\tcwd,\n\t\t\t\t\tinput: { command, timeout, run_in_background },\n\t\t\t\t\tsummary: command.trim().slice(0, 200),\n\t\t\t\t});\n\t\t\t\tif (!allowed) {\n\t\t\t\t\tthrow new Error(\"Permission denied for bash command.\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Apply command prefix if configured (e.g., \"shopt -s expand_aliases\" for alias support)\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);\n\t\t\tif (run_in_background) {\n\t\t\t\tif (ops !== defaultBashOperations) {\n\t\t\t\t\tthrow new Error(\"Background bash is not supported with custom execution operations.\");\n\t\t\t\t}\n\t\t\t\tconst background = startBackgroundProcess({\n\t\t\t\t\trootCwd: cwd,\n\t\t\t\t\tcwd: spawnContext.cwd,\n\t\t\t\t\tcommand: spawnContext.command,\n\t\t\t\t\tsource: \"tool\",\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `Started background process ${background.id} (pid ${background.pid}). Use /bg status ${background.id}, /bg logs ${background.id}, or /bg stop ${background.id}.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tbackgroundTaskId: background.id,\n\t\t\t\t\t\tbackgroundStatusPath: background.metaPath,\n\t\t\t\t\t\tbackgroundLogPath: background.logPath,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t// We'll stream to a temp file if output gets large\n\t\t\t\tlet tempFilePath: string | undefined;\n\t\t\t\tlet tempFileStream: ReturnType<typeof createWriteStream> | undefined;\n\t\t\t\tlet totalBytes = 0;\n\n\t\t\t\tconst decoder = new TextDecoder();\n\t\t\t\t// Keep a rolling buffer of recent sanitized output\n\t\t\t\tconst chunks: string[] = [];\n\t\t\t\tlet rollingOutput = \"\";\n\t\t\t\tlet chunksBytes = 0;\n\t\t\t\t// Keep more than we need so we have enough context for tail truncation\n\t\t\t\tconst maxChunksBytes = DEFAULT_MAX_BYTES * 2;\n\n\t\t\t\tconst pushText = (text: string) => {\n\t\t\t\t\tif (!text) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Start writing to temp file once we exceed the threshold\n\t\t\t\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\t\t\t\ttempFilePath = getTempFilePath();\n\t\t\t\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\t\t\t\t// Write all buffered chunks to the file\n\t\t\t\t\t\tfor (const chunk of chunks) {\n\t\t\t\t\t\t\ttempFileStream.write(chunk);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Write to temp file if we have one\n\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\ttempFileStream.write(text);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Keep rolling buffer of recent data\n\t\t\t\t\tchunks.push(text);\n\t\t\t\t\trollingOutput += text;\n\t\t\t\t\tchunksBytes += Buffer.byteLength(text, \"utf-8\");\n\n\t\t\t\t\t// Trim old chunks if buffer is too large\n\t\t\t\t\twhile (chunksBytes > maxChunksBytes && chunks.length > 1) {\n\t\t\t\t\t\tconst removed = chunks.shift()!;\n\t\t\t\t\t\tchunksBytes -= Buffer.byteLength(removed, \"utf-8\");\n\t\t\t\t\t\trollingOutput = rollingOutput.slice(removed.length);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Stream partial output to callback (truncated rolling buffer)\n\t\t\t\t\tif (onUpdate) {\n\t\t\t\t\t\tconst truncation = truncateTail(rollingOutput);\n\t\t\t\t\t\tonUpdate({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: truncation.content || \"\" }],\n\t\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\t\ttruncation: truncation.truncated ? truncation : undefined,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\t\ttotalBytes += data.length;\n\t\t\t\t\tpushText(sanitizeBashChunk(decoder.decode(data, { stream: true })));\n\t\t\t\t};\n\n\t\t\t\tconst flushDecoder = () => {\n\t\t\t\t\tpushText(sanitizeBashChunk(decoder.decode()));\n\t\t\t\t};\n\n\t\t\t\tops.exec(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\tonData: handleData,\n\t\t\t\t\tsignal,\n\t\t\t\t\ttimeout,\n\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t})\n\t\t\t\t\t.then(({ exitCode }) => {\n\t\t\t\t\t\tflushDecoder();\n\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Apply tail truncation\n\t\t\t\t\t\tconst truncation = truncateTail(rollingOutput);\n\t\t\t\t\t\tlet outputText = truncation.content || \"(no output)\";\n\n\t\t\t\t\t\t// Build details with truncation info\n\t\t\t\t\t\tlet details: BashToolDetails | undefined;\n\n\t\t\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\t\t\tdetails = {\n\t\t\t\t\t\t\t\ttruncation,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t// Build actionable notice\n\t\t\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\t\t\tconst endLine = truncation.totalLines;\n\n\t\t\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\t\t\t// Edge case: last line alone > 30KB\n\t\t\t\t\t\t\t\tconst lastLineSize = formatSize(\n\t\t\t\t\t\t\t\t\tBuffer.byteLength(rollingOutput.split(\"\\n\").pop() || \"\", \"utf-8\"),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\t\t\toutputText += `\\n\\nCommand exited with code ${exitCode}`;\n\t\t\t\t\t\t\treject(new Error(outputText));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: outputText }], details });\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err: Error) => {\n\t\t\t\t\t\tflushDecoder();\n\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet output = rollingOutput;\n\n\t\t\t\t\t\tif (err.message === \"aborted\") {\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += \"Command aborted\";\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else if (err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += `Command timed out after ${timeoutSecs} seconds`;\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t});\n\t\t},\n\t};\n}\n\n/** Default bash tool using process.cwd() - for backwards compatibility */\nexport const bashTool = createBashTool(process.cwd());\n"]}
|
|
1
|
+
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { createWriteStream, existsSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AgentTool } from \"@mariozechner/pi-agent-core\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { spawn } from \"child_process\";\nimport stripAnsi from \"strip-ansi\";\nimport { adaptCommandForShell, getShellConfig, getShellEnv, killProcessTree, sanitizeBinaryOutput } from \"../../utils/shell.js\";\nimport { startBackgroundProcess } from \"../background-processes.js\";\nimport { isSandboxEnabledFromEnv, wrapCommandWithSandbox } from \"../sandbox/executor.js\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateTail } from \"./truncate.js\";\nimport type { ToolPermissionGuard } from \"./permissions.js\";\n\n/**\n * Generate a unique temp file path for bash output\n */\nfunction getTempFilePath(): string {\n\tconst id = randomBytes(8).toString(\"hex\");\n\treturn join(tmpdir(), `iosm-bash-${id}.log`);\n}\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n\trun_in_background: Type.Optional(\n\t\tType.Boolean({ description: \"Run command in detached background mode and return task id immediately\" }),\n\t),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n\tbackgroundTaskId?: string;\n\tbackgroundStatusPath?: string;\n\tbackgroundLogPath?: string;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (e.g., SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command - The command to execute\n\t * @param cwd - Working directory\n\t * @param options - Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<{ exitCode: number | null }>;\n}\n\n/**\n * Default bash operations using local shell\n */\nconst defaultBashOperations: BashOperations = {\n\texec: (command, cwd, { onData, signal, timeout, env }) => {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst { shell, args } = getShellConfig();\n\n\t\t\tif (!existsSync(cwd)) {\n\t\t\t\treject(new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet wrapped;\n\t\t\ttry {\n\t\t\t\twrapped = wrapCommandWithSandbox({\n\t\t\t\t\tcommand: shell,\n\t\t\t\t\targs: [...args, command],\n\t\t\t\t\tcwd,\n\t\t\t\t\tenabled: isSandboxEnabledFromEnv(),\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\treject(error instanceof Error ? error : new Error(String(error)));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst child = spawn(wrapped.command, wrapped.args, {\n\t\t\t\tcwd,\n\t\t\t\tdetached: true,\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t});\n\n\t\t\tlet timedOut = false;\n\n\t\t\t// Set timeout if provided\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tif (timeout !== undefined && timeout > 0) {\n\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\ttimedOut = true;\n\t\t\t\t\tif (child.pid) {\n\t\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t\t}\n\t\t\t\t}, timeout * 1000);\n\t\t\t}\n\n\t\t\t// Stream stdout and stderr\n\t\t\tif (child.stdout) {\n\t\t\t\tchild.stdout.on(\"data\", onData);\n\t\t\t}\n\t\t\tif (child.stderr) {\n\t\t\t\tchild.stderr.on(\"data\", onData);\n\t\t\t}\n\n\t\t\t// Handle shell spawn errors\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t\treject(err);\n\t\t\t});\n\n\t\t\t// Handle abort signal - kill entire process tree\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) {\n\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tif (signal) {\n\t\t\t\tif (signal.aborted) {\n\t\t\t\t\tonAbort();\n\t\t\t\t} else {\n\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle process exit\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (timedOut) {\n\t\t\t\t\treject(new Error(`timeout:${timeout}`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tresolve({ exitCode: code });\n\t\t\t});\n\t\t});\n\t},\n};\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nfunction sanitizeBashChunk(text: string): string {\n\treturn sanitizeBinaryOutput(stripAnsi(text)).replace(/\\r/g, \"\");\n}\n\nfunction resolveSpawnContext(command: string, cwd: string, spawnHook?: BashSpawnHook): BashSpawnContext {\n\tconst baseContext: BashSpawnContext = {\n\t\tcommand,\n\t\tcwd,\n\t\tenv: { ...getShellEnv() },\n\t};\n\n\tif (!spawnHook) return baseContext;\n\n\tconst modified = spawnHook(baseContext);\n\n\tif (modified.command !== baseContext.command) {\n\t\tprocess.stderr.write(\n\t\t\t`[iosm] Extension modified bash command:\\n before: ${baseContext.command}\\n after: ${modified.command}\\n`,\n\t\t);\n\t}\n\tif (modified.cwd !== baseContext.cwd) {\n\t\tprocess.stderr.write(`[iosm] Extension modified bash cwd: ${baseContext.cwd} → ${modified.cwd}\\n`);\n\t}\n\n\treturn {\n\t\t...modified,\n\t\tcommand: adaptCommandForShell(modified.command),\n\t};\n}\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (e.g., \"shopt -s expand_aliases\" for alias support) */\n\tcommandPrefix?: string;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n\t/** Optional permission guard executed before command starts */\n\tpermissionGuard?: ToolPermissionGuard;\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\tconst ops = options?.operations ?? defaultBashOperations;\n\tconst commandPrefix = options?.commandPrefix;\n\tconst spawnHook = options?.spawnHook;\n\tconst permissionGuard = options?.permissionGuard;\n\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tparameters: bashSchema,\n\t\texecute: async (\n\t\t\t_toolCallId: string,\n\t\t\t{ command, timeout, run_in_background }: { command: string; timeout?: number; run_in_background?: boolean },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t) => {\n\t\t\tif (permissionGuard) {\n\t\t\t\tconst allowed = await permissionGuard({\n\t\t\t\t\ttoolName: \"bash\",\n\t\t\t\t\tcwd,\n\t\t\t\t\tinput: { command, timeout, run_in_background },\n\t\t\t\t\tsummary: command.trim().slice(0, 200),\n\t\t\t\t});\n\t\t\t\tif (!allowed) {\n\t\t\t\t\tthrow new Error(\"Permission denied for bash command.\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Apply command prefix if configured (e.g., \"shopt -s expand_aliases\" for alias support)\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);\n\t\t\tif (run_in_background) {\n\t\t\t\tif (ops !== defaultBashOperations) {\n\t\t\t\t\tthrow new Error(\"Background bash is not supported with custom execution operations.\");\n\t\t\t\t}\n\t\t\t\tconst background = startBackgroundProcess({\n\t\t\t\t\trootCwd: cwd,\n\t\t\t\t\tcwd: spawnContext.cwd,\n\t\t\t\t\tcommand: spawnContext.command,\n\t\t\t\t\tsource: \"tool\",\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `Started background process ${background.id} (pid ${background.pid}). Use /bg status ${background.id}, /bg logs ${background.id}, or /bg stop ${background.id}.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tbackgroundTaskId: background.id,\n\t\t\t\t\t\tbackgroundStatusPath: background.metaPath,\n\t\t\t\t\t\tbackgroundLogPath: background.logPath,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t// We'll stream to a temp file if output gets large\n\t\t\t\tlet tempFilePath: string | undefined;\n\t\t\t\tlet tempFileStream: ReturnType<typeof createWriteStream> | undefined;\n\t\t\t\tlet totalBytes = 0;\n\n\t\t\t\tconst decoder = new TextDecoder();\n\t\t\t\t// Keep a rolling buffer of recent sanitized output\n\t\t\t\tconst chunks: string[] = [];\n\t\t\t\tlet rollingOutput = \"\";\n\t\t\t\tlet chunksBytes = 0;\n\t\t\t\t// Keep more than we need so we have enough context for tail truncation\n\t\t\t\tconst maxChunksBytes = DEFAULT_MAX_BYTES * 2;\n\n\t\t\t\tconst pushText = (text: string) => {\n\t\t\t\t\tif (!text) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Start writing to temp file once we exceed the threshold\n\t\t\t\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\t\t\t\ttempFilePath = getTempFilePath();\n\t\t\t\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\t\t\t\t// Write all buffered chunks to the file\n\t\t\t\t\t\tfor (const chunk of chunks) {\n\t\t\t\t\t\t\ttempFileStream.write(chunk);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Write to temp file if we have one\n\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\ttempFileStream.write(text);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Keep rolling buffer of recent data\n\t\t\t\t\tchunks.push(text);\n\t\t\t\t\trollingOutput += text;\n\t\t\t\t\tchunksBytes += Buffer.byteLength(text, \"utf-8\");\n\n\t\t\t\t\t// Trim old chunks if buffer is too large\n\t\t\t\t\twhile (chunksBytes > maxChunksBytes && chunks.length > 1) {\n\t\t\t\t\t\tconst removed = chunks.shift()!;\n\t\t\t\t\t\tchunksBytes -= Buffer.byteLength(removed, \"utf-8\");\n\t\t\t\t\t\trollingOutput = rollingOutput.slice(removed.length);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Stream partial output to callback (truncated rolling buffer)\n\t\t\t\t\tif (onUpdate) {\n\t\t\t\t\t\tconst truncation = truncateTail(rollingOutput);\n\t\t\t\t\t\tonUpdate({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: truncation.content || \"\" }],\n\t\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\t\ttruncation: truncation.truncated ? truncation : undefined,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\t\ttotalBytes += data.length;\n\t\t\t\t\tpushText(sanitizeBashChunk(decoder.decode(data, { stream: true })));\n\t\t\t\t};\n\n\t\t\t\tconst flushDecoder = () => {\n\t\t\t\t\tpushText(sanitizeBashChunk(decoder.decode()));\n\t\t\t\t};\n\n\t\t\t\tops.exec(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\tonData: handleData,\n\t\t\t\t\tsignal,\n\t\t\t\t\ttimeout,\n\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t})\n\t\t\t\t\t.then(({ exitCode }) => {\n\t\t\t\t\t\tflushDecoder();\n\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Apply tail truncation\n\t\t\t\t\t\tconst truncation = truncateTail(rollingOutput);\n\t\t\t\t\t\tlet outputText = truncation.content || \"(no output)\";\n\n\t\t\t\t\t\t// Build details with truncation info\n\t\t\t\t\t\tlet details: BashToolDetails | undefined;\n\n\t\t\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\t\t\tdetails = {\n\t\t\t\t\t\t\t\ttruncation,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t// Build actionable notice\n\t\t\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\t\t\tconst endLine = truncation.totalLines;\n\n\t\t\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\t\t\t// Edge case: last line alone > 30KB\n\t\t\t\t\t\t\t\tconst lastLineSize = formatSize(\n\t\t\t\t\t\t\t\t\tBuffer.byteLength(rollingOutput.split(\"\\n\").pop() || \"\", \"utf-8\"),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\t\t\toutputText += `\\n\\nCommand exited with code ${exitCode}`;\n\t\t\t\t\t\t\treject(new Error(outputText));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: outputText }], details });\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err: Error) => {\n\t\t\t\t\t\tflushDecoder();\n\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet output = rollingOutput;\n\n\t\t\t\t\t\tif (err.message === \"aborted\") {\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += \"Command aborted\";\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else if (err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += `Command timed out after ${timeoutSecs} seconds`;\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t});\n\t\t},\n\t};\n}\n\n/** Default bash tool using process.cwd() - for backwards compatibility */\nexport const bashTool = createBashTool(process.cwd());\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telegram-bridge-mode.d.ts","sourceRoot":"","sources":["../../../src/modes/telegram/telegram-bridge-mode.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAYtE,MAAM,WAAW,yBAAyB;IACzC;;OAEG;IACH,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,yEAAyE;IACzE,eAAe,EAAE,eAAe,CAAC;IACjC,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;
|
|
1
|
+
{"version":3,"file":"telegram-bridge-mode.d.ts","sourceRoot":"","sources":["../../../src/modes/telegram/telegram-bridge-mode.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAYtE,MAAM,WAAW,yBAAyB;IACzC;;OAEG;IACH,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,yEAAyE;IACzE,eAAe,EAAE,eAAe,CAAC;IACjC,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAoUD,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,KAAK,CAAC,CAK9F"}
|