@caupulican/pi-adaptative 0.81.32 → 0.81.37
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 +46 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/README.md +69 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +53 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +1282 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +1453 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/templates/builder-validator.json +14 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/templates/claude-trio.json +32 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/templates/full-provider-review.json +15 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/templates/provider-prompt-smoke.json +14 -0
- package/dist/bundled-resources/prompts/extensionify.md +5 -4
- package/dist/bundled-resources/prompts/skillify.md +5 -5
- package/dist/bundled-resources/skills/pi-harness-learning/SKILL.md +6 -3
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +10 -8
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts +5 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -2
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +12 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +42 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autonomy/approval-gate.d.ts.map +1 -1
- package/dist/core/autonomy/approval-gate.js +2 -0
- package/dist/core/autonomy/approval-gate.js.map +1 -1
- package/dist/core/autonomy/foreground-envelope.d.ts.map +1 -1
- package/dist/core/autonomy/foreground-envelope.js +2 -0
- package/dist/core/autonomy/foreground-envelope.js.map +1 -1
- package/dist/core/autonomy/gates.d.ts.map +1 -1
- package/dist/core/autonomy/gates.js +9 -2
- package/dist/core/autonomy/gates.js.map +1 -1
- package/dist/core/autonomy/risk-assessment.d.ts.map +1 -1
- package/dist/core/autonomy/risk-assessment.js +2 -0
- package/dist/core/autonomy/risk-assessment.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +12 -3
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +67 -26
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/bash-execution-controller.d.ts +9 -4
- package/dist/core/bash-execution-controller.d.ts.map +1 -1
- package/dist/core/bash-execution-controller.js +7 -6
- package/dist/core/bash-execution-controller.js.map +1 -1
- package/dist/core/bash-executor.d.ts +2 -0
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +10 -5
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/context/context-store-retention.d.ts +4 -1
- package/dist/core/context/context-store-retention.d.ts.map +1 -1
- package/dist/core/context/context-store-retention.js +42 -151
- package/dist/core/context/context-store-retention.js.map +1 -1
- package/dist/core/context-pipeline.d.ts.map +1 -1
- package/dist/core/context-pipeline.js +6 -6
- package/dist/core/context-pipeline.js.map +1 -1
- package/dist/core/default-tool-surface.d.ts +7 -0
- package/dist/core/default-tool-surface.d.ts.map +1 -1
- package/dist/core/default-tool-surface.js +29 -10
- package/dist/core/default-tool-surface.js.map +1 -1
- package/dist/core/doctor.d.ts +10 -10
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +30 -20
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/exec.d.ts +2 -0
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +26 -39
- package/dist/core/exec.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +9 -2
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +105 -21
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +13 -3
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +3 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +2 -0
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/goals/goal-state.d.ts +10 -0
- package/dist/core/goals/goal-state.d.ts.map +1 -1
- package/dist/core/goals/goal-state.js +37 -0
- package/dist/core/goals/goal-state.js.map +1 -1
- package/dist/core/goals/goal-tool-core.d.ts +11 -0
- package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
- package/dist/core/goals/goal-tool-core.js +51 -1
- package/dist/core/goals/goal-tool-core.js.map +1 -1
- package/dist/core/improvement-loop.d.ts.map +1 -1
- package/dist/core/improvement-loop.js +26 -5
- package/dist/core/improvement-loop.js.map +1 -1
- package/dist/core/memory/memory-manager.d.ts.map +1 -1
- package/dist/core/memory/memory-manager.js +1 -0
- package/dist/core/memory/memory-manager.js.map +1 -1
- package/dist/core/model-capability.d.ts.map +1 -1
- package/dist/core/model-capability.js +2 -0
- package/dist/core/model-capability.js.map +1 -1
- package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
- package/dist/core/model-router/tool-escalation.js +17 -3
- package/dist/core/model-router/tool-escalation.js.map +1 -1
- package/dist/core/models/fitness-store.d.ts.map +1 -1
- package/dist/core/models/fitness-store.js +1 -1
- package/dist/core/models/fitness-store.js.map +1 -1
- package/dist/core/models/local-runtime.d.ts +6 -4
- package/dist/core/models/local-runtime.d.ts.map +1 -1
- package/dist/core/models/local-runtime.js +108 -36
- package/dist/core/models/local-runtime.js.map +1 -1
- package/dist/core/output-guard.d.ts.map +1 -1
- package/dist/core/output-guard.js +23 -8
- package/dist/core/output-guard.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +44 -59
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/profile-resource-selection.d.ts +16 -0
- package/dist/core/profile-resource-selection.d.ts.map +1 -1
- package/dist/core/profile-resource-selection.js +41 -0
- package/dist/core/profile-resource-selection.js.map +1 -1
- package/dist/core/python-runtime.d.ts +46 -0
- package/dist/core/python-runtime.d.ts.map +1 -0
- package/dist/core/python-runtime.js +157 -0
- package/dist/core/python-runtime.js.map +1 -0
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +18 -1
- package/dist/core/reflection-controller.js.map +1 -1
- package/dist/core/reload-blockers.d.ts +1 -0
- package/dist/core/reload-blockers.d.ts.map +1 -1
- package/dist/core/reload-blockers.js +6 -2
- package/dist/core/reload-blockers.js.map +1 -1
- package/dist/core/resource-loader.d.ts +2 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +50 -28
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/runtime-builder.d.ts +4 -0
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +31 -9
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/security/untrusted-boundary.d.ts +2 -2
- package/dist/core/security/untrusted-boundary.d.ts.map +1 -1
- package/dist/core/security/untrusted-boundary.js +4 -3
- package/dist/core/security/untrusted-boundary.js.map +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +3 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +9 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +13 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tasks/session-task-state.d.ts +10 -0
- package/dist/core/tasks/session-task-state.d.ts.map +1 -0
- package/dist/core/tasks/session-task-state.js +29 -0
- package/dist/core/tasks/session-task-state.js.map +1 -0
- package/dist/core/tasks/task-command.d.ts +30 -0
- package/dist/core/tasks/task-command.d.ts.map +1 -0
- package/dist/core/tasks/task-command.js +79 -0
- package/dist/core/tasks/task-command.js.map +1 -0
- package/dist/core/tasks/task-state.d.ts +71 -0
- package/dist/core/tasks/task-state.d.ts.map +1 -0
- package/dist/core/tasks/task-state.js +372 -0
- package/dist/core/tasks/task-state.js.map +1 -0
- package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -1
- package/dist/core/tool-selection/tool-selection-controller.js +1 -1
- package/dist/core/tool-selection/tool-selection-controller.js.map +1 -1
- package/dist/core/toolkit/script-runner.d.ts.map +1 -1
- package/dist/core/toolkit/script-runner.js +58 -24
- package/dist/core/toolkit/script-runner.js.map +1 -1
- package/dist/core/tools/bash.d.ts +20 -2
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +132 -220
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/extensionify.d.ts +2 -1
- package/dist/core/tools/extensionify.d.ts.map +1 -1
- package/dist/core/tools/extensionify.js +11 -8
- package/dist/core/tools/extensionify.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +57 -48
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/git-filter.d.ts.map +1 -1
- package/dist/core/tools/git-filter.js +20 -32
- package/dist/core/tools/git-filter.js.map +1 -1
- package/dist/core/tools/goal.d.ts +1 -1
- package/dist/core/tools/goal.d.ts.map +1 -1
- package/dist/core/tools/goal.js +10 -3
- package/dist/core/tools/goal.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +92 -80
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +9 -6
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +24 -9
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +2 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +7 -4
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/python.d.ts +64 -0
- package/dist/core/tools/python.d.ts.map +1 -0
- package/dist/core/tools/python.js +241 -0
- package/dist/core/tools/python.js.map +1 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +4 -3
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/shell-contract-router.d.ts +13 -0
- package/dist/core/tools/shell-contract-router.d.ts.map +1 -0
- package/dist/core/tools/shell-contract-router.js +295 -0
- package/dist/core/tools/shell-contract-router.js.map +1 -0
- package/dist/core/tools/task-steps.d.ts +46 -0
- package/dist/core/tools/task-steps.d.ts.map +1 -0
- package/dist/core/tools/task-steps.js +160 -0
- package/dist/core/tools/task-steps.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +23 -4
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +18 -4
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/auth-dialogs-controller.d.ts +10 -0
- package/dist/modes/interactive/auth-dialogs-controller.d.ts.map +1 -1
- package/dist/modes/interactive/auth-dialogs-controller.js +94 -27
- package/dist/modes/interactive/auth-dialogs-controller.js.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.d.ts.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.js +2 -1
- package/dist/modes/interactive/auto-learn-controller.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts +1 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +8 -0
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +6 -15
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +3 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +20 -8
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.d.ts +4 -1
- package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.js +22 -39
- package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +1 -1
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/config-backup.d.ts +1 -0
- package/dist/modes/interactive/config-backup.d.ts.map +1 -1
- package/dist/modes/interactive/config-backup.js +14 -3
- package/dist/modes/interactive/config-backup.js.map +1 -1
- package/dist/modes/interactive/editor-overlay-host.d.ts +12 -0
- package/dist/modes/interactive/editor-overlay-host.d.ts.map +1 -1
- package/dist/modes/interactive/editor-overlay-host.js +55 -0
- package/dist/modes/interactive/editor-overlay-host.js.map +1 -1
- package/dist/modes/interactive/extension-ui-host.d.ts +2 -1
- package/dist/modes/interactive/extension-ui-host.d.ts.map +1 -1
- package/dist/modes/interactive/extension-ui-host.js +161 -96
- package/dist/modes/interactive/extension-ui-host.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +23 -28
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +26 -8
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/profile-menu-controller.d.ts +5 -1
- package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
- package/dist/modes/interactive/profile-menu-controller.js +118 -24
- package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
- package/dist/modes/interactive/resource-shell-commands.d.ts +1 -0
- package/dist/modes/interactive/resource-shell-commands.d.ts.map +1 -1
- package/dist/modes/interactive/resource-shell-commands.js +5 -1
- package/dist/modes/interactive/resource-shell-commands.js.map +1 -1
- package/dist/modes/interactive/session-flow-commands.d.ts +10 -0
- package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-flow-commands.js +143 -7
- package/dist/modes/interactive/session-flow-commands.js.map +1 -1
- package/dist/modes/interactive/session-io-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-io-commands.js +80 -50
- package/dist/modes/interactive/session-io-commands.js.map +1 -1
- package/dist/modes/interactive/startup-checks.d.ts.map +1 -1
- package/dist/modes/interactive/startup-checks.js +14 -17
- package/dist/modes/interactive/startup-checks.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +20 -19
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +10 -19
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/child-process.d.ts +13 -7
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js +56 -3
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +3 -2
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +17 -10
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/external-editor-command.d.ts +8 -0
- package/dist/utils/external-editor-command.d.ts.map +1 -0
- package/dist/utils/external-editor-command.js +60 -0
- package/dist/utils/external-editor-command.js.map +1 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +10 -2
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/shell.d.ts +6 -8
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +61 -70
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +23 -3
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +117 -21
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/work-directory.d.ts +36 -0
- package/dist/utils/work-directory.d.ts.map +1 -0
- package/dist/utils/work-directory.js +462 -0
- package/dist/utils/work-directory.js.map +1 -0
- package/docs/index.md +4 -0
- package/docs/python.md +43 -0
- package/docs/resources.md +2 -2
- package/docs/rpc.md +1 -1
- package/docs/settings.md +7 -3
- package/docs/task-steps.md +90 -0
- package/docs/termux.md +7 -0
- package/docs/tmux-agent-manager.md +48 -0
- package/docs/usage.md +5 -6
- package/docs/windows.md +104 -8
- package/docs/work-directory.md +41 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +32 -37
- package/package.json +9 -7
- package/scripts/postinstall-python-runtime.mjs +20 -0
|
@@ -1,18 +1,23 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import chalk from "chalk";
|
|
2
3
|
import { spawnSync } from "child_process";
|
|
3
|
-
import { chmodSync, createWriteStream, existsSync, mkdirSync, readdirSync, renameSync, rmSync, writeFileSync, } from "fs";
|
|
4
|
+
import { chmodSync, createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync, renameSync, rmSync, writeFileSync, } from "fs";
|
|
4
5
|
import { createRequire } from "module";
|
|
5
6
|
import { arch, platform } from "os";
|
|
6
7
|
import { dirname, join } from "path";
|
|
7
8
|
import { Readable } from "stream";
|
|
8
9
|
import { pipeline } from "stream/promises";
|
|
9
10
|
import { pathToFileURL } from "url";
|
|
10
|
-
import { APP_NAME, getBinDir } from "../config.js";
|
|
11
|
-
import { spawnProcess,
|
|
11
|
+
import { APP_NAME, getAgentDir, getBinDir } from "../config.js";
|
|
12
|
+
import { spawnProcess, waitForChildProcessWithTermination } from "./child-process.js";
|
|
13
|
+
import { getProcessWorkRun } from "./work-directory.js";
|
|
12
14
|
const TOOLS_DIR = getBinDir();
|
|
13
15
|
const NETWORK_TIMEOUT_MS = 10_000;
|
|
14
16
|
const DOWNLOAD_TIMEOUT_MS = 120_000;
|
|
17
|
+
const COMMAND_PROBE_TIMEOUT_MS = 5_000;
|
|
18
|
+
const ARCHIVE_EXTRACTION_TIMEOUT_MS = 5 * 60_000;
|
|
15
19
|
const FFF_NODE_VERSION = "0.9.6";
|
|
20
|
+
export const UV_VERSION = "0.11.28";
|
|
16
21
|
const FFF_MANAGED_DIR = join(TOOLS_DIR, "fff-node");
|
|
17
22
|
const FFF_MANAGED_PACKAGE_JSON = join(FFF_MANAGED_DIR, "package.json");
|
|
18
23
|
const moduleRequire = createRequire(import.meta.url);
|
|
@@ -69,11 +74,49 @@ const TOOLS = {
|
|
|
69
74
|
return null;
|
|
70
75
|
},
|
|
71
76
|
},
|
|
77
|
+
uv: {
|
|
78
|
+
name: "uv",
|
|
79
|
+
repo: "astral-sh/uv",
|
|
80
|
+
binaryName: "uv",
|
|
81
|
+
systemBinaryNames: ["uv"],
|
|
82
|
+
tagPrefix: "",
|
|
83
|
+
pinnedVersion: UV_VERSION,
|
|
84
|
+
getAssetName: (_version, plat, architecture) => {
|
|
85
|
+
if (architecture !== "arm64" && architecture !== "x64")
|
|
86
|
+
return null;
|
|
87
|
+
const archStr = architecture === "arm64" ? "aarch64" : "x86_64";
|
|
88
|
+
if (plat === "darwin")
|
|
89
|
+
return `uv-${archStr}-apple-darwin.tar.gz`;
|
|
90
|
+
if (plat === "linux")
|
|
91
|
+
return `uv-${archStr}-unknown-linux-musl.tar.gz`;
|
|
92
|
+
if (plat === "win32")
|
|
93
|
+
return `uv-${archStr}-pc-windows-msvc.zip`;
|
|
94
|
+
return null;
|
|
95
|
+
},
|
|
96
|
+
sha256ByAsset: {
|
|
97
|
+
"uv-aarch64-apple-darwin.tar.gz": "33540eb7c883ab857eff79bd5ac2aa31fe27b595abecb4a9c003a2c998447232",
|
|
98
|
+
"uv-aarch64-pc-windows-msvc.zip": "3248109afad3ec59baad299d324ff53de17e2d9a3b3e21580ffd26744b11e036",
|
|
99
|
+
"uv-aarch64-unknown-linux-musl.tar.gz": "da10cdfa7d92212b7acb62021a0fd61bcf8580c58c3632ec915d10c3a1a7906b",
|
|
100
|
+
"uv-x86_64-apple-darwin.tar.gz": "2ad79983127ffca7d77b77ce6a24278d7e4f7b817a1acf72fea5f8124b4aac5e",
|
|
101
|
+
"uv-x86_64-pc-windows-msvc.zip": "0a23463216d09c6a72ff80ef5dc5a795f07dc1575cb84d24596c2f124a441b7b",
|
|
102
|
+
"uv-x86_64-unknown-linux-musl.tar.gz": "f02146b371c35c287d860f003ece7345c86e358a3fd70a9b63700cd141ee7fb4",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
72
105
|
};
|
|
106
|
+
export function getPinnedToolAsset(tool, targetPlatform = platform(), targetArchitecture = arch()) {
|
|
107
|
+
const config = TOOLS[tool];
|
|
108
|
+
if (!config.pinnedVersion || !config.sha256ByAsset)
|
|
109
|
+
return null;
|
|
110
|
+
const assetName = config.getAssetName(config.pinnedVersion, targetPlatform, targetArchitecture);
|
|
111
|
+
if (!assetName)
|
|
112
|
+
return null;
|
|
113
|
+
const expectedSha256 = config.sha256ByAsset[assetName];
|
|
114
|
+
return expectedSha256 ? { version: config.pinnedVersion, assetName, expectedSha256 } : null;
|
|
115
|
+
}
|
|
73
116
|
// Check if a command exists in PATH by trying to run it
|
|
74
117
|
function commandExists(cmd) {
|
|
75
118
|
try {
|
|
76
|
-
const result = spawnSync(cmd, ["--version"], { stdio: "pipe" });
|
|
119
|
+
const result = spawnSync(cmd, ["--version"], { stdio: "pipe", timeout: COMMAND_PROBE_TIMEOUT_MS });
|
|
77
120
|
// Check for ENOENT error (command not found)
|
|
78
121
|
return result.error === undefined || result.error === null;
|
|
79
122
|
}
|
|
@@ -172,6 +215,16 @@ async function downloadFile(url, dest) {
|
|
|
172
215
|
const fileStream = createWriteStream(dest);
|
|
173
216
|
await pipeline(Readable.fromWeb(response.body), fileStream);
|
|
174
217
|
}
|
|
218
|
+
export async function verifyFileSha256(filePath, expectedSha256) {
|
|
219
|
+
const hash = createHash("sha256");
|
|
220
|
+
await new Promise((resolve, reject) => {
|
|
221
|
+
const stream = createReadStream(filePath);
|
|
222
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
223
|
+
stream.once("error", reject);
|
|
224
|
+
stream.once("end", resolve);
|
|
225
|
+
});
|
|
226
|
+
return hash.digest("hex") === expectedSha256.toLowerCase();
|
|
227
|
+
}
|
|
175
228
|
function findBinaryRecursively(rootDir, binaryFileName) {
|
|
176
229
|
const stack = [rootDir];
|
|
177
230
|
while (stack.length > 0) {
|
|
@@ -206,7 +259,7 @@ function formatSpawnFailure(result) {
|
|
|
206
259
|
return `exit status ${result.status ?? "unknown"}`;
|
|
207
260
|
}
|
|
208
261
|
function runExtractionCommand(command, args) {
|
|
209
|
-
const result = spawnSync(command, args, { stdio: "pipe" });
|
|
262
|
+
const result = spawnSync(command, args, { stdio: "pipe", timeout: ARCHIVE_EXTRACTION_TIMEOUT_MS });
|
|
210
263
|
if (!result.error && result.status === 0) {
|
|
211
264
|
return null;
|
|
212
265
|
}
|
|
@@ -285,8 +338,8 @@ async function downloadTool(tool) {
|
|
|
285
338
|
throw new Error(`Unknown tool: ${tool}`);
|
|
286
339
|
const plat = platform();
|
|
287
340
|
const architecture = arch();
|
|
288
|
-
//
|
|
289
|
-
let version = await getLatestVersion(config.repo);
|
|
341
|
+
// Pinned tools are reproducible; legacy search tools retain their current latest-release behavior.
|
|
342
|
+
let version = config.pinnedVersion ?? (await getLatestVersion(config.repo));
|
|
290
343
|
if (tool === "fd" && plat === "darwin" && architecture === "x64") {
|
|
291
344
|
version = "10.3.0";
|
|
292
345
|
}
|
|
@@ -298,14 +351,22 @@ async function downloadTool(tool) {
|
|
|
298
351
|
// Create tools directory
|
|
299
352
|
mkdirSync(TOOLS_DIR, { recursive: true });
|
|
300
353
|
const downloadUrl = `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${version}/${assetName}`;
|
|
301
|
-
const
|
|
354
|
+
const downloadWorkDir = getProcessWorkRun(getAgentDir(), "downloads", "tools").path;
|
|
355
|
+
const archivePath = join(downloadWorkDir, assetName);
|
|
302
356
|
const binaryExt = plat === "win32" ? ".exe" : "";
|
|
303
357
|
const binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);
|
|
304
|
-
// Download
|
|
358
|
+
// Download and verify pinned artifacts before extraction.
|
|
305
359
|
await downloadFile(downloadUrl, archivePath);
|
|
360
|
+
const expectedSha256 = config.sha256ByAsset?.[assetName];
|
|
361
|
+
if (config.sha256ByAsset && !expectedSha256) {
|
|
362
|
+
throw new Error(`No pinned SHA-256 is registered for ${assetName}`);
|
|
363
|
+
}
|
|
364
|
+
if (expectedSha256 && !(await verifyFileSha256(archivePath, expectedSha256))) {
|
|
365
|
+
throw new Error(`SHA-256 verification failed for ${assetName}`);
|
|
366
|
+
}
|
|
306
367
|
// Extract into a unique temp directory. fd and rg downloads can run concurrently
|
|
307
368
|
// during startup, so sharing a fixed directory causes races.
|
|
308
|
-
const extractDir = join(
|
|
369
|
+
const extractDir = join(downloadWorkDir, `extract-${config.binaryName}-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`);
|
|
309
370
|
mkdirSync(extractDir, { recursive: true });
|
|
310
371
|
try {
|
|
311
372
|
if (assetName.endsWith(".tar.gz")) {
|
|
@@ -348,6 +409,7 @@ async function downloadTool(tool) {
|
|
|
348
409
|
const TERMUX_PACKAGES = {
|
|
349
410
|
fd: "fd",
|
|
350
411
|
rg: "ripgrep",
|
|
412
|
+
uv: "uv",
|
|
351
413
|
};
|
|
352
414
|
const FFF_PLATFORM_PACKAGES = {
|
|
353
415
|
"darwin/arm64": "@ff-labs/fff-bin-darwin-arm64",
|
|
@@ -437,13 +499,22 @@ export function loadAvailableFffNodePackage(requires) {
|
|
|
437
499
|
}
|
|
438
500
|
async function runNpmInstall(args) {
|
|
439
501
|
try {
|
|
440
|
-
const child = spawnProcess("npm", args, {
|
|
502
|
+
const child = spawnProcess("npm", args, {
|
|
503
|
+
detached: process.platform !== "win32",
|
|
504
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
505
|
+
});
|
|
441
506
|
let stderr = "";
|
|
442
507
|
child.stderr?.on("data", (chunk) => {
|
|
443
|
-
stderr
|
|
508
|
+
stderr = `${stderr}${chunk.toString()}`.slice(-64 * 1024);
|
|
444
509
|
});
|
|
445
|
-
const
|
|
446
|
-
|
|
510
|
+
const terminal = await waitForChildProcessWithTermination(child, {
|
|
511
|
+
timeoutMs: ARCHIVE_EXTRACTION_TIMEOUT_MS,
|
|
512
|
+
killGraceMs: 2_000,
|
|
513
|
+
});
|
|
514
|
+
if (terminal.reason === "timeout") {
|
|
515
|
+
stderr = `${stderr}\nnpm install timed out after ${ARCHIVE_EXTRACTION_TIMEOUT_MS}ms`.trim();
|
|
516
|
+
}
|
|
517
|
+
return { code: terminal.code, stderr };
|
|
447
518
|
}
|
|
448
519
|
catch (error) {
|
|
449
520
|
return { code: 1, stderr: error instanceof Error ? error.message : String(error) };
|
|
@@ -587,8 +658,22 @@ requires) {
|
|
|
587
658
|
});
|
|
588
659
|
return fffNodeInstallPromise;
|
|
589
660
|
}
|
|
661
|
+
async function installTermuxManagedTool(tool, silent) {
|
|
662
|
+
const packageName = TERMUX_PACKAGES[tool];
|
|
663
|
+
if (!silent)
|
|
664
|
+
console.log(chalk.dim(`${TOOLS[tool].name} not found. Installing with Termux pkg...`));
|
|
665
|
+
const child = spawnProcess("pkg", ["install", "-y", packageName], {
|
|
666
|
+
env: process.env,
|
|
667
|
+
stdio: silent ? "ignore" : "inherit",
|
|
668
|
+
});
|
|
669
|
+
const terminal = await waitForChildProcessWithTermination(child, { timeoutMs: 300_000, killGraceMs: 5_000 });
|
|
670
|
+
if (terminal.reason !== "exited" || terminal.code !== 0) {
|
|
671
|
+
throw new Error(`pkg install ${packageName} ${terminal.reason === "timeout" ? "timed out" : `exited with code ${terminal.code ?? 1}`}`);
|
|
672
|
+
}
|
|
673
|
+
return getToolPath(tool) ?? undefined;
|
|
674
|
+
}
|
|
590
675
|
// Ensure a tool is available, downloading if necessary
|
|
591
|
-
// Returns the path to the tool, or
|
|
676
|
+
// Returns the path to the tool, or undefined if unavailable
|
|
592
677
|
export async function ensureTool(tool, silent = false) {
|
|
593
678
|
const existingPath = getToolPath(tool);
|
|
594
679
|
if (existingPath) {
|
|
@@ -603,14 +688,25 @@ export async function ensureTool(tool, silent = false) {
|
|
|
603
688
|
}
|
|
604
689
|
return undefined;
|
|
605
690
|
}
|
|
606
|
-
// On Android/Termux, Linux
|
|
607
|
-
//
|
|
691
|
+
// On Android/Termux, upstream Linux archives target glibc/musl rather than Bionic.
|
|
692
|
+
// uv is a required managed runtime and the user explicitly authorized provisioning it;
|
|
693
|
+
// preserve guide-only behavior for optional search tools.
|
|
608
694
|
if (platform() === "android") {
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
695
|
+
if (tool !== "uv") {
|
|
696
|
+
const packageName = TERMUX_PACKAGES[tool];
|
|
697
|
+
if (!silent)
|
|
698
|
+
console.log(chalk.yellow(`${config.name} not found. Install with: pkg install ${packageName}`));
|
|
699
|
+
return undefined;
|
|
700
|
+
}
|
|
701
|
+
try {
|
|
702
|
+
return await runExclusiveToolDownload(tool, () => installTermuxManagedTool(tool, silent));
|
|
703
|
+
}
|
|
704
|
+
catch (error) {
|
|
705
|
+
if (!silent) {
|
|
706
|
+
console.log(chalk.yellow(`Failed to install ${config.name}: ${error instanceof Error ? error.message : error}`));
|
|
707
|
+
}
|
|
708
|
+
return undefined;
|
|
612
709
|
}
|
|
613
|
-
return undefined;
|
|
614
710
|
}
|
|
615
711
|
// Tool not found - download it
|
|
616
712
|
if (!silent) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools-manager.js","sourceRoot":"","sources":["../../src/utils/tools-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import chalk from \"chalk\";\nimport { type SpawnSyncReturns, spawnSync } from \"child_process\";\nimport {\n\tchmodSync,\n\tcreateWriteStream,\n\texistsSync,\n\tmkdirSync,\n\treaddirSync,\n\trenameSync,\n\trmSync,\n\twriteFileSync,\n} from \"fs\";\nimport { createRequire } from \"module\";\nimport { arch, platform } from \"os\";\nimport { dirname, join } from \"path\";\nimport { Readable } from \"stream\";\nimport { pipeline } from \"stream/promises\";\nimport { pathToFileURL } from \"url\";\nimport { APP_NAME, getBinDir } from \"../config.ts\";\nimport { spawnProcess, waitForChildProcess } from \"./child-process.ts\";\n\nconst TOOLS_DIR = getBinDir();\nconst NETWORK_TIMEOUT_MS = 10_000;\nconst DOWNLOAD_TIMEOUT_MS = 120_000;\nconst FFF_NODE_VERSION = \"0.9.6\";\nconst FFF_MANAGED_DIR = join(TOOLS_DIR, \"fff-node\");\nconst FFF_MANAGED_PACKAGE_JSON = join(FFF_MANAGED_DIR, \"package.json\");\n\ntype ModuleRequire = ((id: string) => unknown) & { resolve?: (id: string) => string };\n\nconst moduleRequire = createRequire(import.meta.url);\nconst executableDirRequire = createRequire(pathToFileURL(join(dirname(process.execPath), \"package.json\")).href);\n\nfunction isOfflineModeEnabled(): boolean {\n\tconst value = process.env.PI_OFFLINE;\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\" || value.toLowerCase() === \"yes\";\n}\n\ninterface ToolConfig {\n\tname: string;\n\trepo: string; // GitHub repo (e.g., \"sharkdp/fd\")\n\tbinaryName: string; // Name of the binary inside the archive\n\tsystemBinaryNames?: string[]; // Alternative system command names to try before downloading\n\ttagPrefix: string; // Prefix for tags (e.g., \"v\" for v1.0.0, \"\" for 1.0.0)\n\tgetAssetName: (version: string, plat: string, architecture: string) => string | null;\n}\n\nconst TOOLS: Record<string, ToolConfig> = {\n\tfd: {\n\t\tname: \"fd\",\n\t\trepo: \"sharkdp/fd\",\n\t\tbinaryName: \"fd\",\n\t\tsystemBinaryNames: [\"fd\", \"fdfind\"],\n\t\ttagPrefix: \"v\",\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t} else if (plat === \"linux\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-unknown-linux-gnu.tar.gz`;\n\t\t\t} else if (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t},\n\trg: {\n\t\tname: \"ripgrep\",\n\t\trepo: \"BurntSushi/ripgrep\",\n\t\tbinaryName: \"rg\",\n\t\ttagPrefix: \"\",\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t} else if (plat === \"linux\") {\n\t\t\t\tif (architecture === \"arm64\") {\n\t\t\t\t\treturn `ripgrep-${version}-aarch64-unknown-linux-gnu.tar.gz`;\n\t\t\t\t}\n\t\t\t\treturn `ripgrep-${version}-x86_64-unknown-linux-musl.tar.gz`;\n\t\t\t} else if (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t},\n};\n\n// Check if a command exists in PATH by trying to run it\nfunction commandExists(cmd: string): boolean {\n\ttry {\n\t\tconst result = spawnSync(cmd, [\"--version\"], { stdio: \"pipe\" });\n\t\t// Check for ENOENT error (command not found)\n\t\treturn result.error === undefined || result.error === null;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// Get the path to a tool (system-wide or in our tools dir)\nexport function getToolPath(tool: \"fd\" | \"rg\"): string | null {\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\n\t// Check our tools directory first\n\tconst localPath = join(TOOLS_DIR, config.binaryName + (platform() === \"win32\" ? \".exe\" : \"\"));\n\tif (existsSync(localPath)) {\n\t\treturn localPath;\n\t}\n\n\t// Check system PATH - if found, just return the command name (it's in PATH)\n\tconst systemBinaryNames = config.systemBinaryNames ?? [config.binaryName];\n\tfor (const systemBinaryName of systemBinaryNames) {\n\t\tif (commandExists(systemBinaryName)) {\n\t\t\treturn systemBinaryName;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/** Presence check for a SYSTEM tool the doctor only ever reports on -- never installs. */\nexport interface SystemToolStatus {\n\tpresent: boolean;\n\tcommand?: string;\n\tversion?: string;\n}\n\nconst PYTHON_COMMANDS = [\"python3\", \"python\"];\n\n/**\n * Detect a usable Python interpreter. SYSTEM tool (see src/core/doctor.ts):\n * the doctor reports presence/version, it never installs this itself.\n *\n * @param commands Override the candidate command names, for tests.\n */\nexport function detectPython(commands: readonly string[] = PYTHON_COMMANDS): SystemToolStatus {\n\tfor (const command of commands) {\n\t\ttry {\n\t\t\tconst result = spawnSync(command, [\"--version\"], { encoding: \"utf-8\", stdio: \"pipe\", timeout: 5_000 });\n\t\t\tif (result.error) continue;\n\t\t\t// Python 2 prints its version to stderr; Python 3 prints to stdout. Some\n\t\t\t// platforms' `python` alias is one or the other, so check both.\n\t\t\tconst version = `${result.stdout ?? \"\"}${result.stderr ?? \"\"}`.trim() || undefined;\n\t\t\tif (result.status !== 0 && !version) continue;\n\t\t\treturn { present: true, command, version };\n\t\t} catch {\n\t\t\t// Try the next candidate.\n\t\t}\n\t}\n\treturn { present: false };\n}\n\n/**\n * Runs `<command> --version` (or `versionArgs`) and returns its trimmed\n * combined stdout+stderr, or undefined if the command can't be run. Used by\n * the doctor (src/core/doctor.ts) to show a version alongside a tool it has\n * already located by some other means (e.g. getToolPath(\"rg\"), or the binary\n * path OllamaRuntime.detect() reports) -- callers that only need a yes/no\n * presence check should use commandExists/getToolPath/detectPython instead.\n */\nexport function probeVersion(command: string, versionArgs: readonly string[] = [\"--version\"]): string | undefined {\n\ttry {\n\t\tconst result = spawnSync(command, versionArgs, { encoding: \"utf-8\", stdio: \"pipe\", timeout: 5_000 });\n\t\tif (result.error) return undefined;\n\t\tconst output = `${result.stdout ?? \"\"}${result.stderr ?? \"\"}`.trim();\n\t\treturn output || undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n// Fetch latest release version from GitHub\nasync function getLatestVersion(repo: string): Promise<string> {\n\tconst response = await fetch(`https://api.github.com/repos/${repo}/releases/latest`, {\n\t\theaders: { \"User-Agent\": `${APP_NAME}-coding-agent` },\n\t\tsignal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(`GitHub API error: ${response.status}`);\n\t}\n\n\tconst data = (await response.json()) as { tag_name: string };\n\treturn data.tag_name.replace(/^v/, \"\");\n}\n\n// Download a file from URL\nasync function downloadFile(url: string, dest: string): Promise<void> {\n\tconst response = await fetch(url, {\n\t\tsignal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS),\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to download: ${response.status}`);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"No response body\");\n\t}\n\n\tconst fileStream = createWriteStream(dest);\n\tawait pipeline(Readable.fromWeb(response.body as any), fileStream);\n}\n\nfunction findBinaryRecursively(rootDir: string, binaryFileName: string): string | null {\n\tconst stack: string[] = [rootDir];\n\n\twhile (stack.length > 0) {\n\t\tconst currentDir = stack.pop();\n\t\tif (!currentDir) continue;\n\n\t\tconst entries = readdirSync(currentDir, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(currentDir, entry.name);\n\t\t\tif (entry.isFile() && entry.name === binaryFileName) {\n\t\t\t\treturn fullPath;\n\t\t\t}\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tstack.push(fullPath);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction formatSpawnFailure(result: SpawnSyncReturns<Buffer>): string {\n\tif (result.error?.message) {\n\t\treturn result.error.message;\n\t}\n\tconst stderr = result.stderr?.toString().trim();\n\tif (stderr) {\n\t\treturn stderr;\n\t}\n\tconst stdout = result.stdout?.toString().trim();\n\tif (stdout) {\n\t\treturn stdout;\n\t}\n\treturn `exit status ${result.status ?? \"unknown\"}`;\n}\n\nfunction runExtractionCommand(command: string, args: string[]): string | null {\n\tconst result = spawnSync(command, args, { stdio: \"pipe\" });\n\tif (!result.error && result.status === 0) {\n\t\treturn null;\n\t}\n\treturn `${command}: ${formatSpawnFailure(result)}`;\n}\n\nfunction extractTarGzArchive(archivePath: string, extractDir: string, assetName: string): void {\n\tconst failure = runExtractionCommand(\"tar\", [\"xzf\", archivePath, \"-C\", extractDir]);\n\tif (failure) {\n\t\tthrow new Error(`Failed to extract ${assetName}: ${failure}`);\n\t}\n}\n\nfunction getWindowsTarCommand(): string {\n\tconst systemRoot = process.env.SystemRoot ?? process.env.WINDIR;\n\tif (systemRoot) {\n\t\tconst systemTar = join(systemRoot, \"System32\", \"tar.exe\");\n\t\tif (existsSync(systemTar)) {\n\t\t\treturn systemTar;\n\t\t}\n\t}\n\treturn \"tar.exe\";\n}\n\nfunction extractZipArchive(archivePath: string, extractDir: string, assetName: string): void {\n\tconst failures: string[] = [];\n\n\tif (platform() === \"win32\") {\n\t\t// Windows ships bsdtar as tar.exe, which supports zip files. Prefer the\n\t\t// System32 binary over Git Bash's GNU tar, which does not handle zip archives.\n\t\tconst tarFailure = runExtractionCommand(getWindowsTarCommand(), [\"xf\", archivePath, \"-C\", extractDir]);\n\t\tif (!tarFailure) return;\n\t\tfailures.push(tarFailure);\n\n\t\tconst script =\n\t\t\t\"& { param($archive, $destination) $ErrorActionPreference = 'Stop'; Expand-Archive -LiteralPath $archive -DestinationPath $destination -Force }\";\n\t\tconst powershellFailure = runExtractionCommand(\"powershell.exe\", [\n\t\t\t\"-NoLogo\",\n\t\t\t\"-NoProfile\",\n\t\t\t\"-NonInteractive\",\n\t\t\t\"-ExecutionPolicy\",\n\t\t\t\"Bypass\",\n\t\t\t\"-Command\",\n\t\t\tscript,\n\t\t\tarchivePath,\n\t\t\textractDir,\n\t\t]);\n\t\tif (!powershellFailure) return;\n\t\tfailures.push(powershellFailure);\n\t} else {\n\t\tconst unzipFailure = runExtractionCommand(\"unzip\", [\"-q\", archivePath, \"-d\", extractDir]);\n\t\tif (!unzipFailure) return;\n\t\tfailures.push(unzipFailure);\n\n\t\tconst tarFailure = runExtractionCommand(\"tar\", [\"xf\", archivePath, \"-C\", extractDir]);\n\t\tif (!tarFailure) return;\n\t\tfailures.push(tarFailure);\n\t}\n\n\tthrow new Error(`Failed to extract ${assetName}: ${failures.join(\"; \")}`);\n}\n\n// Download and install a tool\nconst toolDownloadPromises = new Map<\"fd\" | \"rg\", Promise<string | undefined>>();\n\nexport function runExclusiveToolDownload(\n\ttool: \"fd\" | \"rg\",\n\tinstaller: () => Promise<string | undefined>,\n): Promise<string | undefined> {\n\tconst existing = toolDownloadPromises.get(tool);\n\tif (existing) return existing;\n\tconst promise = installer().finally(() => {\n\t\tif (toolDownloadPromises.get(tool) === promise) {\n\t\t\ttoolDownloadPromises.delete(tool);\n\t\t}\n\t});\n\ttoolDownloadPromises.set(tool, promise);\n\treturn promise;\n}\n\nasync function downloadTool(tool: \"fd\" | \"rg\"): Promise<string> {\n\tconst config = TOOLS[tool];\n\tif (!config) throw new Error(`Unknown tool: ${tool}`);\n\n\tconst plat = platform();\n\tconst architecture = arch();\n\n\t// Get latest version\n\tlet version = await getLatestVersion(config.repo);\n\tif (tool === \"fd\" && plat === \"darwin\" && architecture === \"x64\") {\n\t\tversion = \"10.3.0\";\n\t}\n\n\t// Get asset name for this platform\n\tconst assetName = config.getAssetName(version, plat, architecture);\n\tif (!assetName) {\n\t\tthrow new Error(`Unsupported platform: ${plat}/${architecture}`);\n\t}\n\n\t// Create tools directory\n\tmkdirSync(TOOLS_DIR, { recursive: true });\n\n\tconst downloadUrl = `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${version}/${assetName}`;\n\tconst archivePath = join(TOOLS_DIR, assetName);\n\tconst binaryExt = plat === \"win32\" ? \".exe\" : \"\";\n\tconst binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);\n\n\t// Download\n\tawait downloadFile(downloadUrl, archivePath);\n\n\t// Extract into a unique temp directory. fd and rg downloads can run concurrently\n\t// during startup, so sharing a fixed directory causes races.\n\tconst extractDir = join(\n\t\tTOOLS_DIR,\n\t\t`extract_tmp_${config.binaryName}_${process.pid}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,\n\t);\n\tmkdirSync(extractDir, { recursive: true });\n\n\ttry {\n\t\tif (assetName.endsWith(\".tar.gz\")) {\n\t\t\textractTarGzArchive(archivePath, extractDir, assetName);\n\t\t} else if (assetName.endsWith(\".zip\")) {\n\t\t\textractZipArchive(archivePath, extractDir, assetName);\n\t\t} else {\n\t\t\tthrow new Error(`Unsupported archive format: ${assetName}`);\n\t\t}\n\n\t\t// Find the binary in extracted files. Some archives contain files directly\n\t\t// at root, others nest under a versioned subdirectory.\n\t\tconst binaryFileName = config.binaryName + binaryExt;\n\t\tconst extractedDir = join(extractDir, assetName.replace(/\\.(tar\\.gz|zip)$/, \"\"));\n\t\tconst extractedBinaryCandidates = [join(extractedDir, binaryFileName), join(extractDir, binaryFileName)];\n\t\tlet extractedBinary = extractedBinaryCandidates.find((candidate) => existsSync(candidate));\n\n\t\tif (!extractedBinary) {\n\t\t\textractedBinary = findBinaryRecursively(extractDir, binaryFileName) ?? undefined;\n\t\t}\n\n\t\tif (extractedBinary) {\n\t\t\trenameSync(extractedBinary, binaryPath);\n\t\t} else {\n\t\t\tthrow new Error(`Binary not found in archive: expected ${binaryFileName} under ${extractDir}`);\n\t\t}\n\n\t\t// Make executable (Unix only)\n\t\tif (plat !== \"win32\") {\n\t\t\tchmodSync(binaryPath, 0o755);\n\t\t}\n\t} finally {\n\t\t// Cleanup\n\t\trmSync(archivePath, { force: true });\n\t\trmSync(extractDir, { recursive: true, force: true });\n\t}\n\n\treturn binaryPath;\n}\n\n// Termux package names for tools\nconst TERMUX_PACKAGES: Record<string, string> = {\n\tfd: \"fd\",\n\trg: \"ripgrep\",\n};\n\nconst FFF_PLATFORM_PACKAGES: Record<string, string> = {\n\t\"darwin/arm64\": \"@ff-labs/fff-bin-darwin-arm64\",\n\t\"darwin/x64\": \"@ff-labs/fff-bin-darwin-x64\",\n\t\"linux/arm64/glibc\": \"@ff-labs/fff-bin-linux-arm64-gnu\",\n\t\"linux/arm64/musl\": \"@ff-labs/fff-bin-linux-arm64-musl\",\n\t\"linux/x64/glibc\": \"@ff-labs/fff-bin-linux-x64-gnu\",\n\t\"linux/x64/musl\": \"@ff-labs/fff-bin-linux-x64-musl\",\n\t\"win32/arm64\": \"@ff-labs/fff-bin-win32-arm64\",\n\t\"win32/x64\": \"@ff-labs/fff-bin-win32-x64\",\n};\n\nlet fffNodeInstallPromise: Promise<unknown | undefined> | undefined;\n\nfunction detectLinuxLibc(): \"glibc\" | \"musl\" {\n\tlet output = \"\";\n\ttry {\n\t\tconst result = spawnSync(\"ldd\", [\"--version\"], { encoding: \"utf-8\", stdio: \"pipe\", timeout: 5000 });\n\t\toutput = `${result.stdout ?? \"\"}${result.stderr ?? \"\"}`;\n\t} catch (e: unknown) {\n\t\tconst err = e as { stdout?: string | Buffer; stderr?: string | Buffer };\n\t\toutput = `${String(err.stdout ?? \"\")}${String(err.stderr ?? \"\")}`;\n\t}\n\treturn output.toLowerCase().includes(\"musl\") ? \"musl\" : \"glibc\";\n}\n\nfunction getFffPlatformPackageName(): string | undefined {\n\tconst plat = platform();\n\tconst architecture = arch();\n\tif (plat === \"linux\") {\n\t\treturn FFF_PLATFORM_PACKAGES[`${plat}/${architecture}/${detectLinuxLibc()}`];\n\t}\n\treturn FFF_PLATFORM_PACKAGES[`${plat}/${architecture}`];\n}\n\nfunction createManagedFffRequire(): ModuleRequire | undefined {\n\tif (!existsSync(FFF_MANAGED_PACKAGE_JSON)) return undefined;\n\treturn createRequire(pathToFileURL(FFF_MANAGED_PACKAGE_JSON).href);\n}\n\nfunction findFffNodeDistEntry(startPath: string): string | undefined {\n\tlet currentDir = dirname(startPath);\n\twhile (currentDir !== dirname(currentDir)) {\n\t\tconst candidate = join(currentDir, \"node_modules\", \"@ff-labs\", \"fff-node\", \"dist\", \"src\", \"index.js\");\n\t\tif (existsSync(candidate)) return candidate;\n\t\tcurrentDir = dirname(currentDir);\n\t}\n\treturn undefined;\n}\n\nfunction loadFffNodeDistEntry(requireFff: ModuleRequire): unknown | undefined {\n\tif (!requireFff.resolve) return undefined;\n\ttry {\n\t\tconst ffiPath = requireFff.resolve(\"ffi-rs\");\n\t\tconst fffEntry = findFffNodeDistEntry(ffiPath);\n\t\treturn fffEntry ? requireFff(fffEntry) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction loadFffNodeWith(requireFff: ModuleRequire): unknown | undefined {\n\ttry {\n\t\treturn requireFff(\"@ff-labs/fff-node\");\n\t} catch {\n\t\treturn loadFffNodeDistEntry(requireFff);\n\t}\n}\n\n/**\n * @param requires Override the resolution candidates, for tests (mirrors\n * fff-search-backend.ts's loadFffModule(requires?)). Whether this resolves\n * depends on the ambient environment -- @ff-labs/fff-node is a real npm\n * dependency (package.json), so moduleRequire succeeds wherever a normal\n * `npm install`/`npm ci` provisioned it (e.g. CI) even though it fails on a\n * dev checkout that never ran that install here (this repo resolves fff-node\n * via the separate managed-dir path instead). A test asserting \"nothing is\n * available\" must pass `[]` here rather than relying on that being true.\n */\nexport function loadAvailableFffNodePackage(requires?: readonly ModuleRequire[]): unknown | undefined {\n\tconst candidates =\n\t\trequires ??\n\t\t[moduleRequire, executableDirRequire, createManagedFffRequire()].filter((candidate): candidate is ModuleRequire =>\n\t\t\tBoolean(candidate),\n\t\t);\n\tfor (const requireFff of candidates) {\n\t\tconst loaded = loadFffNodeWith(requireFff);\n\t\tif (loaded) return loaded;\n\t}\n\treturn undefined;\n}\n\nasync function runNpmInstall(args: string[]): Promise<{ code: number | null; stderr: string }> {\n\ttry {\n\t\tconst child = spawnProcess(\"npm\", args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n\t\tlet stderr = \"\";\n\t\tchild.stderr?.on(\"data\", (chunk) => {\n\t\t\tstderr += chunk.toString();\n\t\t});\n\t\tconst code = await waitForChildProcess(child);\n\t\treturn { code, stderr };\n\t} catch (error) {\n\t\treturn { code: 1, stderr: error instanceof Error ? error.message : String(error) };\n\t}\n}\n\n/**\n * Outcome of the most recent {@link ensureFffNodePackage} call, kept for\n * observability (e.g. a future `doctor` check) since the function itself\n * only ever returns the loaded module or `undefined` either way.\n *\n * `install-failed` is distinguished from `offline`/`unsupported-platform`\n * because it is the only one worth *retrying*: offline mode and an\n * unsupported platform are stable for the life of the process, but a real\n * install attempt can fail on a transient issue (registry hiccup, timeout)\n * that may no longer apply on the next search. See\n * DefaultFffSearchBackend.getFinder in fff-search-backend.ts, which uses\n * this distinction to decide whether a failed finder is retryable.\n */\nexport type FffInstallOutcome =\n\t| { status: \"already-available\" }\n\t| { status: \"offline\" }\n\t| { status: \"unsupported-platform\" }\n\t| { status: \"installed\" }\n\t| { status: \"install-failed\"; reason: string };\n\n/**\n * How long a genuine install failure gates out a NEW npm spawn. An agent turn\n * can fire several find/grep calls in quick succession, and each one now\n * primes the finder in the background (see tryFffFind/tryFffGrep) -- without\n * this, a persistently-failing install (registry down, disk full, ...) would\n * re-spawn npm on every single one of those calls instead of once.\n *\n * This gates the SPAWN inside ensureFffNodePackage, not whether a failed\n * finder is retryable (see isFffInstallRetryable): DefaultFffSearchBackend\n * always evicts a failed finder so the next search re-enters this function,\n * and it is THIS cooldown check -- evaluated fresh, at call time -- that\n * decides whether that re-entry is a real attempt or a fast, spawn-free bail.\n * (An earlier version conflated the two: it gated eviction itself on the\n * cooldown, which is checked once, immediately after the failure it's timing\n * -- i.e. always still within the window -- so the failed finder was never\n * evicted and the retry never got a chance to happen at all.)\n */\nexport const FFF_INSTALL_RETRY_COOLDOWN_MS = 30_000;\n\nlet lastFffInstallOutcome: FffInstallOutcome | undefined;\nlet lastFffInstallFailureAt: number | undefined;\n\n/** The outcome of the last {@link ensureFffNodePackage} call, if any. */\nexport function getLastFffInstallOutcome(): FffInstallOutcome | undefined {\n\treturn lastFffInstallOutcome;\n}\n\n/** Whether the last install outcome was a genuine failure worth retrying (as opposed to a stable \"not applicable\" result). Cooldown-independent by design -- see FFF_INSTALL_RETRY_COOLDOWN_MS. */\nexport function isFffInstallRetryable(): boolean {\n\treturn lastFffInstallOutcome?.status === \"install-failed\";\n}\n\n/**\n * Pure decision logic behind {@link isFffInstallCoolingDown}, exposed directly\n * so tests can assert the cooldown boundary without faking the system clock.\n */\nexport function computeIsFffInstallCoolingDown(\n\toutcome: FffInstallOutcome | undefined,\n\tfailedAt: number | undefined,\n\tnow: number,\n): boolean {\n\tif (outcome?.status !== \"install-failed\") return false;\n\tif (failedAt === undefined) return false;\n\treturn now - failedAt < FFF_INSTALL_RETRY_COOLDOWN_MS;\n}\n\n/** Whether a real install attempt happened too recently to try again right now. */\nfunction isFffInstallCoolingDown(): boolean {\n\treturn computeIsFffInstallCoolingDown(lastFffInstallOutcome, lastFffInstallFailureAt, Date.now());\n}\n\n/** Records a genuine install failure and stamps when it happened, so the cooldown above has a start time to measure from. */\nfunction recordFffInstallFailure(reason: string): void {\n\tlastFffInstallOutcome = { status: \"install-failed\", reason };\n\tlastFffInstallFailureAt = Date.now();\n}\n\nasync function installManagedFffNodePackage(platformPackage: string, silent: boolean): Promise<unknown | undefined> {\n\ttry {\n\t\tmkdirSync(FFF_MANAGED_DIR, { recursive: true });\n\t\tif (!existsSync(FFF_MANAGED_PACKAGE_JSON)) {\n\t\t\twriteFileSync(FFF_MANAGED_PACKAGE_JSON, '{\"name\":\"pi-managed-fff-node\",\"private\":true,\"version\":\"0.0.0\"}\\n');\n\t\t}\n\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.dim(\"FFF native search not found. Installing managed FFF package...\"));\n\t\t}\n\n\t\tconst args = [\n\t\t\t\"install\",\n\t\t\t\"--ignore-scripts\",\n\t\t\t\"--omit=dev\",\n\t\t\t\"--include=optional\",\n\t\t\t\"--no-audit\",\n\t\t\t\"--no-fund\",\n\t\t\t\"--package-lock=false\",\n\t\t\t\"--prefix\",\n\t\t\tFFF_MANAGED_DIR,\n\t\t\t`@ff-labs/fff-node@${FFF_NODE_VERSION}`,\n\t\t\t`${platformPackage}@${FFF_NODE_VERSION}`,\n\t\t];\n\t\tconst result = await runNpmInstall(args);\n\t\tif (result.code !== 0) {\n\t\t\tconst reason = result.stderr.trim() || `npm exited with code ${result.code}`;\n\t\t\tif (!silent) {\n\t\t\t\tconsole.log(chalk.yellow(`Failed to install FFF native search: ${reason}`));\n\t\t\t}\n\t\t\trecordFffInstallFailure(reason);\n\t\t\treturn undefined;\n\t\t}\n\t\tconst loaded = loadFffNodeWith(createRequire(pathToFileURL(FFF_MANAGED_PACKAGE_JSON).href));\n\t\tif (!loaded) {\n\t\t\tconst reason = \"Managed FFF install completed but @ff-labs/fff-node could not be loaded.\";\n\t\t\tif (!silent) {\n\t\t\t\tconsole.log(chalk.yellow(reason));\n\t\t\t}\n\t\t\trecordFffInstallFailure(reason);\n\t\t\treturn undefined;\n\t\t}\n\t\tlastFffInstallOutcome = { status: \"installed\" };\n\t\treturn loaded;\n\t} catch (error) {\n\t\t// Never let a filesystem/spawn surprise (e.g. a read-only home directory)\n\t\t// crash the caller: fall back like any other install failure, but keep\n\t\t// the reason observable.\n\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.yellow(`Failed to install FFF native search: ${reason}`));\n\t\t}\n\t\trecordFffInstallFailure(reason);\n\t\treturn undefined;\n\t}\n}\n\nexport async function ensureFffNodePackage(\n\tsilent: boolean = false,\n\tforceManagedInstall: boolean = false,\n\t/** Override the \"is it already available\" resolution candidates, for tests. See loadAvailableFffNodePackage's doc. */\n\trequires?: readonly ModuleRequire[],\n): Promise<unknown | undefined> {\n\tconst existing = forceManagedInstall ? undefined : loadAvailableFffNodePackage(requires);\n\tif (existing) {\n\t\tlastFffInstallOutcome = { status: \"already-available\" };\n\t\treturn existing;\n\t}\n\n\tif (isOfflineModeEnabled()) {\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.yellow(\"FFF native search not found. Offline mode enabled, skipping install.\"));\n\t\t}\n\t\tlastFffInstallOutcome = { status: \"offline\" };\n\t\treturn undefined;\n\t}\n\n\t// A prior attempt failed too recently to try again: bail out fast (no npm\n\t// spawn, no platform/libc probing) rather than repeating a doomed attempt.\n\t// Leaves lastFffInstallOutcome/lastFffInstallFailureAt untouched -- this\n\t// isn't a new attempt, so there's nothing new to record.\n\tif (isFffInstallCoolingDown()) {\n\t\treturn undefined;\n\t}\n\n\tconst platformPackage = getFffPlatformPackageName();\n\tif (!platformPackage) {\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.yellow(`FFF native search is not available for ${platform()}/${arch()}.`));\n\t\t}\n\t\tlastFffInstallOutcome = { status: \"unsupported-platform\" };\n\t\treturn undefined;\n\t}\n\n\tfffNodeInstallPromise ??= installManagedFffNodePackage(platformPackage, silent).finally(() => {\n\t\tfffNodeInstallPromise = undefined;\n\t});\n\treturn fffNodeInstallPromise;\n}\n\n// Ensure a tool is available, downloading if necessary\n// Returns the path to the tool, or null if unavailable\nexport async function ensureTool(tool: \"fd\" | \"rg\", silent: boolean = false): Promise<string | undefined> {\n\tconst existingPath = getToolPath(tool);\n\tif (existingPath) {\n\t\treturn existingPath;\n\t}\n\n\tconst config = TOOLS[tool];\n\tif (!config) return undefined;\n\n\tif (isOfflineModeEnabled()) {\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.yellow(`${config.name} not found. Offline mode enabled, skipping download.`));\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t// On Android/Termux, Linux binaries don't work due to Bionic libc incompatibility.\n\t// Users must install via pkg.\n\tif (platform() === \"android\") {\n\t\tconst pkgName = TERMUX_PACKAGES[tool] ?? tool;\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.yellow(`${config.name} not found. Install with: pkg install ${pkgName}`));\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t// Tool not found - download it\n\tif (!silent) {\n\t\tconsole.log(chalk.dim(`${config.name} not found. Downloading...`));\n\t}\n\n\ttry {\n\t\tconst path = await runExclusiveToolDownload(tool, () => downloadTool(tool));\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.dim(`${config.name} installed to ${path}`));\n\t\t}\n\t\treturn path;\n\t} catch (e) {\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.yellow(`Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`));\n\t\t}\n\t\treturn undefined;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tools-manager.js","sourceRoot":"","sources":["../../src/utils/tools-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport chalk from \"chalk\";\nimport { type SpawnSyncReturns, spawnSync } from \"child_process\";\nimport {\n\tchmodSync,\n\tcreateReadStream,\n\tcreateWriteStream,\n\texistsSync,\n\tmkdirSync,\n\treaddirSync,\n\trenameSync,\n\trmSync,\n\twriteFileSync,\n} from \"fs\";\nimport { createRequire } from \"module\";\nimport { arch, platform } from \"os\";\nimport { dirname, join } from \"path\";\nimport { Readable } from \"stream\";\nimport { pipeline } from \"stream/promises\";\nimport { pathToFileURL } from \"url\";\nimport { APP_NAME, getAgentDir, getBinDir } from \"../config.ts\";\nimport { spawnProcess, waitForChildProcessWithTermination } from \"./child-process.ts\";\nimport { getProcessWorkRun } from \"./work-directory.ts\";\n\nconst TOOLS_DIR = getBinDir();\nconst NETWORK_TIMEOUT_MS = 10_000;\nconst DOWNLOAD_TIMEOUT_MS = 120_000;\nconst COMMAND_PROBE_TIMEOUT_MS = 5_000;\nconst ARCHIVE_EXTRACTION_TIMEOUT_MS = 5 * 60_000;\nconst FFF_NODE_VERSION = \"0.9.6\";\nexport const UV_VERSION = \"0.11.28\";\nconst FFF_MANAGED_DIR = join(TOOLS_DIR, \"fff-node\");\nconst FFF_MANAGED_PACKAGE_JSON = join(FFF_MANAGED_DIR, \"package.json\");\n\ntype ModuleRequire = ((id: string) => unknown) & { resolve?: (id: string) => string };\n\nconst moduleRequire = createRequire(import.meta.url);\nconst executableDirRequire = createRequire(pathToFileURL(join(dirname(process.execPath), \"package.json\")).href);\n\nfunction isOfflineModeEnabled(): boolean {\n\tconst value = process.env.PI_OFFLINE;\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\" || value.toLowerCase() === \"yes\";\n}\n\ninterface ToolConfig {\n\tname: string;\n\trepo: string; // GitHub repo (e.g., \"sharkdp/fd\")\n\tbinaryName: string; // Name of the binary inside the archive\n\tsystemBinaryNames?: string[]; // Alternative system command names to try before downloading\n\ttagPrefix: string; // Prefix for tags (e.g., \"v\" for v1.0.0, \"\" for 1.0.0)\n\tgetAssetName: (version: string, plat: string, architecture: string) => string | null;\n\tpinnedVersion?: string;\n\tsha256ByAsset?: Readonly<Record<string, string>>;\n}\n\nconst TOOLS: Record<\"fd\" | \"rg\" | \"uv\", ToolConfig> = {\n\tfd: {\n\t\tname: \"fd\",\n\t\trepo: \"sharkdp/fd\",\n\t\tbinaryName: \"fd\",\n\t\tsystemBinaryNames: [\"fd\", \"fdfind\"],\n\t\ttagPrefix: \"v\",\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t} else if (plat === \"linux\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-unknown-linux-gnu.tar.gz`;\n\t\t\t} else if (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t},\n\trg: {\n\t\tname: \"ripgrep\",\n\t\trepo: \"BurntSushi/ripgrep\",\n\t\tbinaryName: \"rg\",\n\t\ttagPrefix: \"\",\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t} else if (plat === \"linux\") {\n\t\t\t\tif (architecture === \"arm64\") {\n\t\t\t\t\treturn `ripgrep-${version}-aarch64-unknown-linux-gnu.tar.gz`;\n\t\t\t\t}\n\t\t\t\treturn `ripgrep-${version}-x86_64-unknown-linux-musl.tar.gz`;\n\t\t\t} else if (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t},\n\tuv: {\n\t\tname: \"uv\",\n\t\trepo: \"astral-sh/uv\",\n\t\tbinaryName: \"uv\",\n\t\tsystemBinaryNames: [\"uv\"],\n\t\ttagPrefix: \"\",\n\t\tpinnedVersion: UV_VERSION,\n\t\tgetAssetName: (_version, plat, architecture) => {\n\t\t\tif (architecture !== \"arm64\" && architecture !== \"x64\") return null;\n\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\tif (plat === \"darwin\") return `uv-${archStr}-apple-darwin.tar.gz`;\n\t\t\tif (plat === \"linux\") return `uv-${archStr}-unknown-linux-musl.tar.gz`;\n\t\t\tif (plat === \"win32\") return `uv-${archStr}-pc-windows-msvc.zip`;\n\t\t\treturn null;\n\t\t},\n\t\tsha256ByAsset: {\n\t\t\t\"uv-aarch64-apple-darwin.tar.gz\": \"33540eb7c883ab857eff79bd5ac2aa31fe27b595abecb4a9c003a2c998447232\",\n\t\t\t\"uv-aarch64-pc-windows-msvc.zip\": \"3248109afad3ec59baad299d324ff53de17e2d9a3b3e21580ffd26744b11e036\",\n\t\t\t\"uv-aarch64-unknown-linux-musl.tar.gz\": \"da10cdfa7d92212b7acb62021a0fd61bcf8580c58c3632ec915d10c3a1a7906b\",\n\t\t\t\"uv-x86_64-apple-darwin.tar.gz\": \"2ad79983127ffca7d77b77ce6a24278d7e4f7b817a1acf72fea5f8124b4aac5e\",\n\t\t\t\"uv-x86_64-pc-windows-msvc.zip\": \"0a23463216d09c6a72ff80ef5dc5a795f07dc1575cb84d24596c2f124a441b7b\",\n\t\t\t\"uv-x86_64-unknown-linux-musl.tar.gz\": \"f02146b371c35c287d860f003ece7345c86e358a3fd70a9b63700cd141ee7fb4\",\n\t\t},\n\t},\n};\n\nexport type ManagedToolName = keyof typeof TOOLS;\n\nexport interface PinnedToolAsset {\n\tversion: string;\n\tassetName: string;\n\texpectedSha256: string;\n}\n\nexport function getPinnedToolAsset(\n\ttool: ManagedToolName,\n\ttargetPlatform: string = platform(),\n\ttargetArchitecture: string = arch(),\n): PinnedToolAsset | null {\n\tconst config: ToolConfig = TOOLS[tool];\n\tif (!config.pinnedVersion || !config.sha256ByAsset) return null;\n\tconst assetName = config.getAssetName(config.pinnedVersion, targetPlatform, targetArchitecture);\n\tif (!assetName) return null;\n\tconst expectedSha256 = config.sha256ByAsset[assetName];\n\treturn expectedSha256 ? { version: config.pinnedVersion, assetName, expectedSha256 } : null;\n}\n\n// Check if a command exists in PATH by trying to run it\nfunction commandExists(cmd: string): boolean {\n\ttry {\n\t\tconst result = spawnSync(cmd, [\"--version\"], { stdio: \"pipe\", timeout: COMMAND_PROBE_TIMEOUT_MS });\n\t\t// Check for ENOENT error (command not found)\n\t\treturn result.error === undefined || result.error === null;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// Get the path to a tool (system-wide or in our tools dir)\nexport function getToolPath(tool: ManagedToolName): string | null {\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\n\t// Check our tools directory first\n\tconst localPath = join(TOOLS_DIR, config.binaryName + (platform() === \"win32\" ? \".exe\" : \"\"));\n\tif (existsSync(localPath)) {\n\t\treturn localPath;\n\t}\n\n\t// Check system PATH - if found, just return the command name (it's in PATH)\n\tconst systemBinaryNames = config.systemBinaryNames ?? [config.binaryName];\n\tfor (const systemBinaryName of systemBinaryNames) {\n\t\tif (commandExists(systemBinaryName)) {\n\t\t\treturn systemBinaryName;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/** Presence check for a SYSTEM tool the doctor only ever reports on -- never installs. */\nexport interface SystemToolStatus {\n\tpresent: boolean;\n\tcommand?: string;\n\tversion?: string;\n}\n\nconst PYTHON_COMMANDS = [\"python3\", \"python\"];\n\n/**\n * Detect a usable Python interpreter. SYSTEM tool (see src/core/doctor.ts):\n * the doctor reports presence/version, it never installs this itself.\n *\n * @param commands Override the candidate command names, for tests.\n */\nexport function detectPython(commands: readonly string[] = PYTHON_COMMANDS): SystemToolStatus {\n\tfor (const command of commands) {\n\t\ttry {\n\t\t\tconst result = spawnSync(command, [\"--version\"], { encoding: \"utf-8\", stdio: \"pipe\", timeout: 5_000 });\n\t\t\tif (result.error) continue;\n\t\t\t// Python 2 prints its version to stderr; Python 3 prints to stdout. Some\n\t\t\t// platforms' `python` alias is one or the other, so check both.\n\t\t\tconst version = `${result.stdout ?? \"\"}${result.stderr ?? \"\"}`.trim() || undefined;\n\t\t\tif (result.status !== 0 && !version) continue;\n\t\t\treturn { present: true, command, version };\n\t\t} catch {\n\t\t\t// Try the next candidate.\n\t\t}\n\t}\n\treturn { present: false };\n}\n\n/**\n * Runs `<command> --version` (or `versionArgs`) and returns its trimmed\n * combined stdout+stderr, or undefined if the command can't be run. Used by\n * the doctor (src/core/doctor.ts) to show a version alongside a tool it has\n * already located by some other means (e.g. getToolPath(\"rg\"), or the binary\n * path OllamaRuntime.detect() reports) -- callers that only need a yes/no\n * presence check should use commandExists/getToolPath/detectPython instead.\n */\nexport function probeVersion(command: string, versionArgs: readonly string[] = [\"--version\"]): string | undefined {\n\ttry {\n\t\tconst result = spawnSync(command, versionArgs, { encoding: \"utf-8\", stdio: \"pipe\", timeout: 5_000 });\n\t\tif (result.error) return undefined;\n\t\tconst output = `${result.stdout ?? \"\"}${result.stderr ?? \"\"}`.trim();\n\t\treturn output || undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n// Fetch latest release version from GitHub\nasync function getLatestVersion(repo: string): Promise<string> {\n\tconst response = await fetch(`https://api.github.com/repos/${repo}/releases/latest`, {\n\t\theaders: { \"User-Agent\": `${APP_NAME}-coding-agent` },\n\t\tsignal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(`GitHub API error: ${response.status}`);\n\t}\n\n\tconst data = (await response.json()) as { tag_name: string };\n\treturn data.tag_name.replace(/^v/, \"\");\n}\n\n// Download a file from URL\nasync function downloadFile(url: string, dest: string): Promise<void> {\n\tconst response = await fetch(url, {\n\t\tsignal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS),\n\t});\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to download: ${response.status}`);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"No response body\");\n\t}\n\n\tconst fileStream = createWriteStream(dest);\n\tawait pipeline(Readable.fromWeb(response.body as any), fileStream);\n}\n\nexport async function verifyFileSha256(filePath: string, expectedSha256: string): Promise<boolean> {\n\tconst hash = createHash(\"sha256\");\n\tawait new Promise<void>((resolve, reject) => {\n\t\tconst stream = createReadStream(filePath);\n\t\tstream.on(\"data\", (chunk) => hash.update(chunk));\n\t\tstream.once(\"error\", reject);\n\t\tstream.once(\"end\", resolve);\n\t});\n\treturn hash.digest(\"hex\") === expectedSha256.toLowerCase();\n}\n\nfunction findBinaryRecursively(rootDir: string, binaryFileName: string): string | null {\n\tconst stack: string[] = [rootDir];\n\n\twhile (stack.length > 0) {\n\t\tconst currentDir = stack.pop();\n\t\tif (!currentDir) continue;\n\n\t\tconst entries = readdirSync(currentDir, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(currentDir, entry.name);\n\t\t\tif (entry.isFile() && entry.name === binaryFileName) {\n\t\t\t\treturn fullPath;\n\t\t\t}\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tstack.push(fullPath);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction formatSpawnFailure(result: SpawnSyncReturns<Buffer>): string {\n\tif (result.error?.message) {\n\t\treturn result.error.message;\n\t}\n\tconst stderr = result.stderr?.toString().trim();\n\tif (stderr) {\n\t\treturn stderr;\n\t}\n\tconst stdout = result.stdout?.toString().trim();\n\tif (stdout) {\n\t\treturn stdout;\n\t}\n\treturn `exit status ${result.status ?? \"unknown\"}`;\n}\n\nfunction runExtractionCommand(command: string, args: string[]): string | null {\n\tconst result = spawnSync(command, args, { stdio: \"pipe\", timeout: ARCHIVE_EXTRACTION_TIMEOUT_MS });\n\tif (!result.error && result.status === 0) {\n\t\treturn null;\n\t}\n\treturn `${command}: ${formatSpawnFailure(result)}`;\n}\n\nfunction extractTarGzArchive(archivePath: string, extractDir: string, assetName: string): void {\n\tconst failure = runExtractionCommand(\"tar\", [\"xzf\", archivePath, \"-C\", extractDir]);\n\tif (failure) {\n\t\tthrow new Error(`Failed to extract ${assetName}: ${failure}`);\n\t}\n}\n\nfunction getWindowsTarCommand(): string {\n\tconst systemRoot = process.env.SystemRoot ?? process.env.WINDIR;\n\tif (systemRoot) {\n\t\tconst systemTar = join(systemRoot, \"System32\", \"tar.exe\");\n\t\tif (existsSync(systemTar)) {\n\t\t\treturn systemTar;\n\t\t}\n\t}\n\treturn \"tar.exe\";\n}\n\nfunction extractZipArchive(archivePath: string, extractDir: string, assetName: string): void {\n\tconst failures: string[] = [];\n\n\tif (platform() === \"win32\") {\n\t\t// Windows ships bsdtar as tar.exe, which supports zip files. Prefer the\n\t\t// System32 binary over Git Bash's GNU tar, which does not handle zip archives.\n\t\tconst tarFailure = runExtractionCommand(getWindowsTarCommand(), [\"xf\", archivePath, \"-C\", extractDir]);\n\t\tif (!tarFailure) return;\n\t\tfailures.push(tarFailure);\n\n\t\tconst script =\n\t\t\t\"& { param($archive, $destination) $ErrorActionPreference = 'Stop'; Expand-Archive -LiteralPath $archive -DestinationPath $destination -Force }\";\n\t\tconst powershellFailure = runExtractionCommand(\"powershell.exe\", [\n\t\t\t\"-NoLogo\",\n\t\t\t\"-NoProfile\",\n\t\t\t\"-NonInteractive\",\n\t\t\t\"-ExecutionPolicy\",\n\t\t\t\"Bypass\",\n\t\t\t\"-Command\",\n\t\t\tscript,\n\t\t\tarchivePath,\n\t\t\textractDir,\n\t\t]);\n\t\tif (!powershellFailure) return;\n\t\tfailures.push(powershellFailure);\n\t} else {\n\t\tconst unzipFailure = runExtractionCommand(\"unzip\", [\"-q\", archivePath, \"-d\", extractDir]);\n\t\tif (!unzipFailure) return;\n\t\tfailures.push(unzipFailure);\n\n\t\tconst tarFailure = runExtractionCommand(\"tar\", [\"xf\", archivePath, \"-C\", extractDir]);\n\t\tif (!tarFailure) return;\n\t\tfailures.push(tarFailure);\n\t}\n\n\tthrow new Error(`Failed to extract ${assetName}: ${failures.join(\"; \")}`);\n}\n\n// Download and install a tool\nconst toolDownloadPromises = new Map<ManagedToolName, Promise<string | undefined>>();\n\nexport function runExclusiveToolDownload(\n\ttool: ManagedToolName,\n\tinstaller: () => Promise<string | undefined>,\n): Promise<string | undefined> {\n\tconst existing = toolDownloadPromises.get(tool);\n\tif (existing) return existing;\n\tconst promise = installer().finally(() => {\n\t\tif (toolDownloadPromises.get(tool) === promise) {\n\t\t\ttoolDownloadPromises.delete(tool);\n\t\t}\n\t});\n\ttoolDownloadPromises.set(tool, promise);\n\treturn promise;\n}\n\nasync function downloadTool(tool: ManagedToolName): Promise<string> {\n\tconst config: ToolConfig = TOOLS[tool];\n\tif (!config) throw new Error(`Unknown tool: ${tool}`);\n\n\tconst plat = platform();\n\tconst architecture = arch();\n\n\t// Pinned tools are reproducible; legacy search tools retain their current latest-release behavior.\n\tlet version = config.pinnedVersion ?? (await getLatestVersion(config.repo));\n\tif (tool === \"fd\" && plat === \"darwin\" && architecture === \"x64\") {\n\t\tversion = \"10.3.0\";\n\t}\n\n\t// Get asset name for this platform\n\tconst assetName = config.getAssetName(version, plat, architecture);\n\tif (!assetName) {\n\t\tthrow new Error(`Unsupported platform: ${plat}/${architecture}`);\n\t}\n\n\t// Create tools directory\n\tmkdirSync(TOOLS_DIR, { recursive: true });\n\n\tconst downloadUrl = `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${version}/${assetName}`;\n\tconst downloadWorkDir = getProcessWorkRun(getAgentDir(), \"downloads\", \"tools\").path;\n\tconst archivePath = join(downloadWorkDir, assetName);\n\tconst binaryExt = plat === \"win32\" ? \".exe\" : \"\";\n\tconst binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);\n\n\t// Download and verify pinned artifacts before extraction.\n\tawait downloadFile(downloadUrl, archivePath);\n\tconst expectedSha256 = config.sha256ByAsset?.[assetName];\n\tif (config.sha256ByAsset && !expectedSha256) {\n\t\tthrow new Error(`No pinned SHA-256 is registered for ${assetName}`);\n\t}\n\tif (expectedSha256 && !(await verifyFileSha256(archivePath, expectedSha256))) {\n\t\tthrow new Error(`SHA-256 verification failed for ${assetName}`);\n\t}\n\n\t// Extract into a unique temp directory. fd and rg downloads can run concurrently\n\t// during startup, so sharing a fixed directory causes races.\n\tconst extractDir = join(\n\t\tdownloadWorkDir,\n\t\t`extract-${config.binaryName}-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`,\n\t);\n\tmkdirSync(extractDir, { recursive: true });\n\n\ttry {\n\t\tif (assetName.endsWith(\".tar.gz\")) {\n\t\t\textractTarGzArchive(archivePath, extractDir, assetName);\n\t\t} else if (assetName.endsWith(\".zip\")) {\n\t\t\textractZipArchive(archivePath, extractDir, assetName);\n\t\t} else {\n\t\t\tthrow new Error(`Unsupported archive format: ${assetName}`);\n\t\t}\n\n\t\t// Find the binary in extracted files. Some archives contain files directly\n\t\t// at root, others nest under a versioned subdirectory.\n\t\tconst binaryFileName = config.binaryName + binaryExt;\n\t\tconst extractedDir = join(extractDir, assetName.replace(/\\.(tar\\.gz|zip)$/, \"\"));\n\t\tconst extractedBinaryCandidates = [join(extractedDir, binaryFileName), join(extractDir, binaryFileName)];\n\t\tlet extractedBinary = extractedBinaryCandidates.find((candidate) => existsSync(candidate));\n\n\t\tif (!extractedBinary) {\n\t\t\textractedBinary = findBinaryRecursively(extractDir, binaryFileName) ?? undefined;\n\t\t}\n\n\t\tif (extractedBinary) {\n\t\t\trenameSync(extractedBinary, binaryPath);\n\t\t} else {\n\t\t\tthrow new Error(`Binary not found in archive: expected ${binaryFileName} under ${extractDir}`);\n\t\t}\n\n\t\t// Make executable (Unix only)\n\t\tif (plat !== \"win32\") {\n\t\t\tchmodSync(binaryPath, 0o755);\n\t\t}\n\t} finally {\n\t\t// Cleanup\n\t\trmSync(archivePath, { force: true });\n\t\trmSync(extractDir, { recursive: true, force: true });\n\t}\n\n\treturn binaryPath;\n}\n\n// Termux package names for tools\nconst TERMUX_PACKAGES: Record<ManagedToolName, string> = {\n\tfd: \"fd\",\n\trg: \"ripgrep\",\n\tuv: \"uv\",\n};\n\nconst FFF_PLATFORM_PACKAGES: Record<string, string> = {\n\t\"darwin/arm64\": \"@ff-labs/fff-bin-darwin-arm64\",\n\t\"darwin/x64\": \"@ff-labs/fff-bin-darwin-x64\",\n\t\"linux/arm64/glibc\": \"@ff-labs/fff-bin-linux-arm64-gnu\",\n\t\"linux/arm64/musl\": \"@ff-labs/fff-bin-linux-arm64-musl\",\n\t\"linux/x64/glibc\": \"@ff-labs/fff-bin-linux-x64-gnu\",\n\t\"linux/x64/musl\": \"@ff-labs/fff-bin-linux-x64-musl\",\n\t\"win32/arm64\": \"@ff-labs/fff-bin-win32-arm64\",\n\t\"win32/x64\": \"@ff-labs/fff-bin-win32-x64\",\n};\n\nlet fffNodeInstallPromise: Promise<unknown | undefined> | undefined;\n\nfunction detectLinuxLibc(): \"glibc\" | \"musl\" {\n\tlet output = \"\";\n\ttry {\n\t\tconst result = spawnSync(\"ldd\", [\"--version\"], { encoding: \"utf-8\", stdio: \"pipe\", timeout: 5000 });\n\t\toutput = `${result.stdout ?? \"\"}${result.stderr ?? \"\"}`;\n\t} catch (e: unknown) {\n\t\tconst err = e as { stdout?: string | Buffer; stderr?: string | Buffer };\n\t\toutput = `${String(err.stdout ?? \"\")}${String(err.stderr ?? \"\")}`;\n\t}\n\treturn output.toLowerCase().includes(\"musl\") ? \"musl\" : \"glibc\";\n}\n\nfunction getFffPlatformPackageName(): string | undefined {\n\tconst plat = platform();\n\tconst architecture = arch();\n\tif (plat === \"linux\") {\n\t\treturn FFF_PLATFORM_PACKAGES[`${plat}/${architecture}/${detectLinuxLibc()}`];\n\t}\n\treturn FFF_PLATFORM_PACKAGES[`${plat}/${architecture}`];\n}\n\nfunction createManagedFffRequire(): ModuleRequire | undefined {\n\tif (!existsSync(FFF_MANAGED_PACKAGE_JSON)) return undefined;\n\treturn createRequire(pathToFileURL(FFF_MANAGED_PACKAGE_JSON).href);\n}\n\nfunction findFffNodeDistEntry(startPath: string): string | undefined {\n\tlet currentDir = dirname(startPath);\n\twhile (currentDir !== dirname(currentDir)) {\n\t\tconst candidate = join(currentDir, \"node_modules\", \"@ff-labs\", \"fff-node\", \"dist\", \"src\", \"index.js\");\n\t\tif (existsSync(candidate)) return candidate;\n\t\tcurrentDir = dirname(currentDir);\n\t}\n\treturn undefined;\n}\n\nfunction loadFffNodeDistEntry(requireFff: ModuleRequire): unknown | undefined {\n\tif (!requireFff.resolve) return undefined;\n\ttry {\n\t\tconst ffiPath = requireFff.resolve(\"ffi-rs\");\n\t\tconst fffEntry = findFffNodeDistEntry(ffiPath);\n\t\treturn fffEntry ? requireFff(fffEntry) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction loadFffNodeWith(requireFff: ModuleRequire): unknown | undefined {\n\ttry {\n\t\treturn requireFff(\"@ff-labs/fff-node\");\n\t} catch {\n\t\treturn loadFffNodeDistEntry(requireFff);\n\t}\n}\n\n/**\n * @param requires Override the resolution candidates, for tests (mirrors\n * fff-search-backend.ts's loadFffModule(requires?)). Whether this resolves\n * depends on the ambient environment -- @ff-labs/fff-node is a real npm\n * dependency (package.json), so moduleRequire succeeds wherever a normal\n * `npm install`/`npm ci` provisioned it (e.g. CI) even though it fails on a\n * dev checkout that never ran that install here (this repo resolves fff-node\n * via the separate managed-dir path instead). A test asserting \"nothing is\n * available\" must pass `[]` here rather than relying on that being true.\n */\nexport function loadAvailableFffNodePackage(requires?: readonly ModuleRequire[]): unknown | undefined {\n\tconst candidates =\n\t\trequires ??\n\t\t[moduleRequire, executableDirRequire, createManagedFffRequire()].filter((candidate): candidate is ModuleRequire =>\n\t\t\tBoolean(candidate),\n\t\t);\n\tfor (const requireFff of candidates) {\n\t\tconst loaded = loadFffNodeWith(requireFff);\n\t\tif (loaded) return loaded;\n\t}\n\treturn undefined;\n}\n\nasync function runNpmInstall(args: string[]): Promise<{ code: number | null; stderr: string }> {\n\ttry {\n\t\tconst child = spawnProcess(\"npm\", args, {\n\t\t\tdetached: process.platform !== \"win32\",\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t});\n\t\tlet stderr = \"\";\n\t\tchild.stderr?.on(\"data\", (chunk) => {\n\t\t\tstderr = `${stderr}${chunk.toString()}`.slice(-64 * 1024);\n\t\t});\n\t\tconst terminal = await waitForChildProcessWithTermination(child, {\n\t\t\ttimeoutMs: ARCHIVE_EXTRACTION_TIMEOUT_MS,\n\t\t\tkillGraceMs: 2_000,\n\t\t});\n\t\tif (terminal.reason === \"timeout\") {\n\t\t\tstderr = `${stderr}\\nnpm install timed out after ${ARCHIVE_EXTRACTION_TIMEOUT_MS}ms`.trim();\n\t\t}\n\t\treturn { code: terminal.code, stderr };\n\t} catch (error) {\n\t\treturn { code: 1, stderr: error instanceof Error ? error.message : String(error) };\n\t}\n}\n\n/**\n * Outcome of the most recent {@link ensureFffNodePackage} call, kept for\n * observability (e.g. a future `doctor` check) since the function itself\n * only ever returns the loaded module or `undefined` either way.\n *\n * `install-failed` is distinguished from `offline`/`unsupported-platform`\n * because it is the only one worth *retrying*: offline mode and an\n * unsupported platform are stable for the life of the process, but a real\n * install attempt can fail on a transient issue (registry hiccup, timeout)\n * that may no longer apply on the next search. See\n * DefaultFffSearchBackend.getFinder in fff-search-backend.ts, which uses\n * this distinction to decide whether a failed finder is retryable.\n */\nexport type FffInstallOutcome =\n\t| { status: \"already-available\" }\n\t| { status: \"offline\" }\n\t| { status: \"unsupported-platform\" }\n\t| { status: \"installed\" }\n\t| { status: \"install-failed\"; reason: string };\n\n/**\n * How long a genuine install failure gates out a NEW npm spawn. An agent turn\n * can fire several find/grep calls in quick succession, and each one now\n * primes the finder in the background (see tryFffFind/tryFffGrep) -- without\n * this, a persistently-failing install (registry down, disk full, ...) would\n * re-spawn npm on every single one of those calls instead of once.\n *\n * This gates the SPAWN inside ensureFffNodePackage, not whether a failed\n * finder is retryable (see isFffInstallRetryable): DefaultFffSearchBackend\n * always evicts a failed finder so the next search re-enters this function,\n * and it is THIS cooldown check -- evaluated fresh, at call time -- that\n * decides whether that re-entry is a real attempt or a fast, spawn-free bail.\n * (An earlier version conflated the two: it gated eviction itself on the\n * cooldown, which is checked once, immediately after the failure it's timing\n * -- i.e. always still within the window -- so the failed finder was never\n * evicted and the retry never got a chance to happen at all.)\n */\nexport const FFF_INSTALL_RETRY_COOLDOWN_MS = 30_000;\n\nlet lastFffInstallOutcome: FffInstallOutcome | undefined;\nlet lastFffInstallFailureAt: number | undefined;\n\n/** The outcome of the last {@link ensureFffNodePackage} call, if any. */\nexport function getLastFffInstallOutcome(): FffInstallOutcome | undefined {\n\treturn lastFffInstallOutcome;\n}\n\n/** Whether the last install outcome was a genuine failure worth retrying (as opposed to a stable \"not applicable\" result). Cooldown-independent by design -- see FFF_INSTALL_RETRY_COOLDOWN_MS. */\nexport function isFffInstallRetryable(): boolean {\n\treturn lastFffInstallOutcome?.status === \"install-failed\";\n}\n\n/**\n * Pure decision logic behind {@link isFffInstallCoolingDown}, exposed directly\n * so tests can assert the cooldown boundary without faking the system clock.\n */\nexport function computeIsFffInstallCoolingDown(\n\toutcome: FffInstallOutcome | undefined,\n\tfailedAt: number | undefined,\n\tnow: number,\n): boolean {\n\tif (outcome?.status !== \"install-failed\") return false;\n\tif (failedAt === undefined) return false;\n\treturn now - failedAt < FFF_INSTALL_RETRY_COOLDOWN_MS;\n}\n\n/** Whether a real install attempt happened too recently to try again right now. */\nfunction isFffInstallCoolingDown(): boolean {\n\treturn computeIsFffInstallCoolingDown(lastFffInstallOutcome, lastFffInstallFailureAt, Date.now());\n}\n\n/** Records a genuine install failure and stamps when it happened, so the cooldown above has a start time to measure from. */\nfunction recordFffInstallFailure(reason: string): void {\n\tlastFffInstallOutcome = { status: \"install-failed\", reason };\n\tlastFffInstallFailureAt = Date.now();\n}\n\nasync function installManagedFffNodePackage(platformPackage: string, silent: boolean): Promise<unknown | undefined> {\n\ttry {\n\t\tmkdirSync(FFF_MANAGED_DIR, { recursive: true });\n\t\tif (!existsSync(FFF_MANAGED_PACKAGE_JSON)) {\n\t\t\twriteFileSync(FFF_MANAGED_PACKAGE_JSON, '{\"name\":\"pi-managed-fff-node\",\"private\":true,\"version\":\"0.0.0\"}\\n');\n\t\t}\n\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.dim(\"FFF native search not found. Installing managed FFF package...\"));\n\t\t}\n\n\t\tconst args = [\n\t\t\t\"install\",\n\t\t\t\"--ignore-scripts\",\n\t\t\t\"--omit=dev\",\n\t\t\t\"--include=optional\",\n\t\t\t\"--no-audit\",\n\t\t\t\"--no-fund\",\n\t\t\t\"--package-lock=false\",\n\t\t\t\"--prefix\",\n\t\t\tFFF_MANAGED_DIR,\n\t\t\t`@ff-labs/fff-node@${FFF_NODE_VERSION}`,\n\t\t\t`${platformPackage}@${FFF_NODE_VERSION}`,\n\t\t];\n\t\tconst result = await runNpmInstall(args);\n\t\tif (result.code !== 0) {\n\t\t\tconst reason = result.stderr.trim() || `npm exited with code ${result.code}`;\n\t\t\tif (!silent) {\n\t\t\t\tconsole.log(chalk.yellow(`Failed to install FFF native search: ${reason}`));\n\t\t\t}\n\t\t\trecordFffInstallFailure(reason);\n\t\t\treturn undefined;\n\t\t}\n\t\tconst loaded = loadFffNodeWith(createRequire(pathToFileURL(FFF_MANAGED_PACKAGE_JSON).href));\n\t\tif (!loaded) {\n\t\t\tconst reason = \"Managed FFF install completed but @ff-labs/fff-node could not be loaded.\";\n\t\t\tif (!silent) {\n\t\t\t\tconsole.log(chalk.yellow(reason));\n\t\t\t}\n\t\t\trecordFffInstallFailure(reason);\n\t\t\treturn undefined;\n\t\t}\n\t\tlastFffInstallOutcome = { status: \"installed\" };\n\t\treturn loaded;\n\t} catch (error) {\n\t\t// Never let a filesystem/spawn surprise (e.g. a read-only home directory)\n\t\t// crash the caller: fall back like any other install failure, but keep\n\t\t// the reason observable.\n\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.yellow(`Failed to install FFF native search: ${reason}`));\n\t\t}\n\t\trecordFffInstallFailure(reason);\n\t\treturn undefined;\n\t}\n}\n\nexport async function ensureFffNodePackage(\n\tsilent: boolean = false,\n\tforceManagedInstall: boolean = false,\n\t/** Override the \"is it already available\" resolution candidates, for tests. See loadAvailableFffNodePackage's doc. */\n\trequires?: readonly ModuleRequire[],\n): Promise<unknown | undefined> {\n\tconst existing = forceManagedInstall ? undefined : loadAvailableFffNodePackage(requires);\n\tif (existing) {\n\t\tlastFffInstallOutcome = { status: \"already-available\" };\n\t\treturn existing;\n\t}\n\n\tif (isOfflineModeEnabled()) {\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.yellow(\"FFF native search not found. Offline mode enabled, skipping install.\"));\n\t\t}\n\t\tlastFffInstallOutcome = { status: \"offline\" };\n\t\treturn undefined;\n\t}\n\n\t// A prior attempt failed too recently to try again: bail out fast (no npm\n\t// spawn, no platform/libc probing) rather than repeating a doomed attempt.\n\t// Leaves lastFffInstallOutcome/lastFffInstallFailureAt untouched -- this\n\t// isn't a new attempt, so there's nothing new to record.\n\tif (isFffInstallCoolingDown()) {\n\t\treturn undefined;\n\t}\n\n\tconst platformPackage = getFffPlatformPackageName();\n\tif (!platformPackage) {\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.yellow(`FFF native search is not available for ${platform()}/${arch()}.`));\n\t\t}\n\t\tlastFffInstallOutcome = { status: \"unsupported-platform\" };\n\t\treturn undefined;\n\t}\n\n\tfffNodeInstallPromise ??= installManagedFffNodePackage(platformPackage, silent).finally(() => {\n\t\tfffNodeInstallPromise = undefined;\n\t});\n\treturn fffNodeInstallPromise;\n}\n\nasync function installTermuxManagedTool(tool: ManagedToolName, silent: boolean): Promise<string | undefined> {\n\tconst packageName = TERMUX_PACKAGES[tool];\n\tif (!silent) console.log(chalk.dim(`${TOOLS[tool].name} not found. Installing with Termux pkg...`));\n\tconst child = spawnProcess(\"pkg\", [\"install\", \"-y\", packageName], {\n\t\tenv: process.env,\n\t\tstdio: silent ? \"ignore\" : \"inherit\",\n\t});\n\tconst terminal = await waitForChildProcessWithTermination(child, { timeoutMs: 300_000, killGraceMs: 5_000 });\n\tif (terminal.reason !== \"exited\" || terminal.code !== 0) {\n\t\tthrow new Error(\n\t\t\t`pkg install ${packageName} ${terminal.reason === \"timeout\" ? \"timed out\" : `exited with code ${terminal.code ?? 1}`}`,\n\t\t);\n\t}\n\treturn getToolPath(tool) ?? undefined;\n}\n\n// Ensure a tool is available, downloading if necessary\n// Returns the path to the tool, or undefined if unavailable\nexport async function ensureTool(tool: ManagedToolName, silent: boolean = false): Promise<string | undefined> {\n\tconst existingPath = getToolPath(tool);\n\tif (existingPath) {\n\t\treturn existingPath;\n\t}\n\n\tconst config = TOOLS[tool];\n\tif (!config) return undefined;\n\n\tif (isOfflineModeEnabled()) {\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.yellow(`${config.name} not found. Offline mode enabled, skipping download.`));\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t// On Android/Termux, upstream Linux archives target glibc/musl rather than Bionic.\n\t// uv is a required managed runtime and the user explicitly authorized provisioning it;\n\t// preserve guide-only behavior for optional search tools.\n\tif (platform() === \"android\") {\n\t\tif (tool !== \"uv\") {\n\t\t\tconst packageName = TERMUX_PACKAGES[tool];\n\t\t\tif (!silent) console.log(chalk.yellow(`${config.name} not found. Install with: pkg install ${packageName}`));\n\t\t\treturn undefined;\n\t\t}\n\t\ttry {\n\t\t\treturn await runExclusiveToolDownload(tool, () => installTermuxManagedTool(tool, silent));\n\t\t} catch (error) {\n\t\t\tif (!silent) {\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.yellow(`Failed to install ${config.name}: ${error instanceof Error ? error.message : error}`),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t// Tool not found - download it\n\tif (!silent) {\n\t\tconsole.log(chalk.dim(`${config.name} not found. Downloading...`));\n\t}\n\n\ttry {\n\t\tconst path = await runExclusiveToolDownload(tool, () => downloadTool(tool));\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.dim(`${config.name} installed to ${path}`));\n\t\t}\n\t\treturn path;\n\t} catch (e) {\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.yellow(`Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`));\n\t\t}\n\t\treturn undefined;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare const PI_WORK_ROOT_ENV = "PI_WORK_ROOT";
|
|
2
|
+
export declare const WORK_RUN_MANIFEST_FILE = ".pi-work-run.json";
|
|
3
|
+
export interface WorkRetentionOptions {
|
|
4
|
+
maxAgeMs?: number;
|
|
5
|
+
maxRuns?: number;
|
|
6
|
+
maxTotalBytes?: number;
|
|
7
|
+
/** Fixed tenant-wide cap for recursive file entries inspected during one prune. */
|
|
8
|
+
maxScannedEntries?: number;
|
|
9
|
+
/** Fixed cap for run directories inspected during one prune. */
|
|
10
|
+
maxScannedRuns?: number;
|
|
11
|
+
now?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface AcquireWorkRunOptions {
|
|
14
|
+
agentDir: string;
|
|
15
|
+
category: string;
|
|
16
|
+
tenant: string;
|
|
17
|
+
runId?: string;
|
|
18
|
+
retention?: WorkRetentionOptions | false;
|
|
19
|
+
now?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface WorkRunLease {
|
|
22
|
+
readonly path: string;
|
|
23
|
+
readonly category: string;
|
|
24
|
+
readonly tenant: string;
|
|
25
|
+
readonly runId: string;
|
|
26
|
+
touch(): void;
|
|
27
|
+
release(): void;
|
|
28
|
+
}
|
|
29
|
+
export declare function getWorkRoot(agentDir: string): string;
|
|
30
|
+
export declare function getWorkTenantDir(agentDir: string, category: string, tenant: string): string;
|
|
31
|
+
export declare function getWorkRunDir(agentDir: string, category: string, tenant: string, runId: string): string;
|
|
32
|
+
export declare function createWorkRunId(now?: number): string;
|
|
33
|
+
export declare function pruneWorkTenant(agentDir: string, category: string, tenant: string, options?: WorkRetentionOptions): string[];
|
|
34
|
+
export declare function getProcessWorkRun(agentDir: string, category: string, tenant: string, runId?: string): WorkRunLease;
|
|
35
|
+
export declare function acquireWorkRun(options: AcquireWorkRunOptions): WorkRunLease;
|
|
36
|
+
//# sourceMappingURL=work-directory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-directory.d.ts","sourceRoot":"","sources":["../../src/utils/work-directory.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAC/C,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAgD1D,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mFAAmF;IACnF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gEAAgE;IAChE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,oBAAoB,GAAG,KAAK,CAAC;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,IAAI,IAAI,CAAC;CAChB;AAyOD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAI3F;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKvG;AAED,wBAAgB,eAAe,CAAC,GAAG,SAAa,GAAG,MAAM,CAExD;AAED,wBAAgB,eAAe,CAC9B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,oBAAyB,GAChC,MAAM,EAAE,CA0DV;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,CAOlH;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,YAAY,CA2F3E","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n\ttype Dir,\n\texistsSync,\n\tlstatSync,\n\tmkdirSync,\n\topendirSync,\n\treadFileSync,\n\trmdirSync,\n\trmSync,\n\tunlinkSync,\n\tutimesSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { hostname } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const PI_WORK_ROOT_ENV = \"PI_WORK_ROOT\";\nexport const WORK_RUN_MANIFEST_FILE = \".pi-work-run.json\";\n\nconst LEASES_DIRECTORY = \".leases\";\nconst CLEANUP_MARKER_FILE = \".cleanup.json\";\nconst MAX_METADATA_BYTES = 4 * 1024;\nconst MAX_LEASE_MARKERS = 256;\nconst MAX_DIRECTORY_DEPTH = 64;\nconst DEFAULT_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;\nconst DEFAULT_MAX_RUNS = 64;\nconst DEFAULT_MAX_TOTAL_BYTES = 512 * 1024 * 1024;\nconst DEFAULT_MAX_SCANNED_ENTRIES = 100_000;\nconst DEFAULT_MAX_SCANNED_RUNS = 10_000;\nconst SEGMENT_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/;\nconst WINDOWS_RESERVED_NAMES = new Set([\n\t\"aux\",\n\t\"clock$\",\n\t\"con\",\n\t\"nul\",\n\t\"prn\",\n\t...Array.from({ length: 9 }, (_, index) => `com${index + 1}`),\n\t...Array.from({ length: 9 }, (_, index) => `lpt${index + 1}`),\n]);\n\ninterface WorkRunManifest {\n\tversion: 1;\n\tcategory: string;\n\ttenant: string;\n\trunId: string;\n\tcreatedAt: number;\n}\n\ninterface WorkMarker {\n\tversion: 1;\n\thostname: string;\n\tpid: number;\n\tcreatedAt: number;\n}\n\ninterface InspectedRun {\n\tpath: string;\n\tmtimeMs: number;\n\tbytes: number;\n}\n\ninterface ScanBudget {\n\tremaining: number;\n}\n\nexport interface WorkRetentionOptions {\n\tmaxAgeMs?: number;\n\tmaxRuns?: number;\n\tmaxTotalBytes?: number;\n\t/** Fixed tenant-wide cap for recursive file entries inspected during one prune. */\n\tmaxScannedEntries?: number;\n\t/** Fixed cap for run directories inspected during one prune. */\n\tmaxScannedRuns?: number;\n\tnow?: number;\n}\n\nexport interface AcquireWorkRunOptions {\n\tagentDir: string;\n\tcategory: string;\n\ttenant: string;\n\trunId?: string;\n\tretention?: WorkRetentionOptions | false;\n\tnow?: number;\n}\n\nexport interface WorkRunLease {\n\treadonly path: string;\n\treadonly category: string;\n\treadonly tenant: string;\n\treadonly runId: string;\n\ttouch(): void;\n\trelease(): void;\n}\n\nconst processWorkRuns = new Map<string, WorkRunLease>();\n\nfunction nonNegativeLimit(value: number | undefined, fallback: number): number {\n\treturn value !== undefined && Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;\n}\n\nfunction assertPortableSegment(label: string, value: string): void {\n\tif (!SEGMENT_PATTERN.test(value) || value.endsWith(\".\")) {\n\t\tthrow new Error(`${label} must be a lowercase portable path segment (1-64 characters)`);\n\t}\n\tconst stem = value.split(\".\", 1)[0];\n\tif (stem && WINDOWS_RESERVED_NAMES.has(stem)) {\n\t\tthrow new Error(`${label} uses a Windows-reserved path name: ${value}`);\n\t}\n}\n\nfunction ensureDirectory(path: string): void {\n\ttry {\n\t\tmkdirSync(path);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n\t}\n\tconst stats = lstatSync(path);\n\tif (!stats.isDirectory() || stats.isSymbolicLink()) {\n\t\tthrow new Error(`Work path is not a real directory: ${path}`);\n\t}\n}\n\nfunction ensureTenantDirectory(agentDir: string, category: string, tenant: string): string {\n\tmkdirSync(agentDir, { recursive: true });\n\tconst workRoot = getWorkRoot(agentDir);\n\tensureDirectory(workRoot);\n\tconst categoryDir = join(workRoot, category);\n\tensureDirectory(categoryDir);\n\tconst tenantDir = join(categoryDir, tenant);\n\tensureDirectory(tenantDir);\n\treturn tenantDir;\n}\n\nfunction readSmallJson(path: string): unknown {\n\tconst stats = lstatSync(path);\n\tif (!stats.isFile() || stats.isSymbolicLink() || stats.size > MAX_METADATA_BYTES) {\n\t\tthrow new Error(`Invalid work metadata file: ${path}`);\n\t}\n\treturn JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n}\n\nfunction isManifest(value: unknown, category: string, tenant: string, runId: string): value is WorkRunManifest {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst manifest = value as Partial<WorkRunManifest>;\n\treturn (\n\t\tmanifest.version === 1 &&\n\t\tmanifest.category === category &&\n\t\tmanifest.tenant === tenant &&\n\t\tmanifest.runId === runId &&\n\t\ttypeof manifest.createdAt === \"number\" &&\n\t\tNumber.isFinite(manifest.createdAt)\n\t);\n}\n\nfunction isMarker(value: unknown): value is WorkMarker {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst marker = value as Partial<WorkMarker>;\n\treturn (\n\t\tmarker.version === 1 &&\n\t\ttypeof marker.hostname === \"string\" &&\n\t\ttypeof marker.pid === \"number\" &&\n\t\tNumber.isSafeInteger(marker.pid) &&\n\t\tmarker.pid > 0 &&\n\t\ttypeof marker.createdAt === \"number\" &&\n\t\tNumber.isFinite(marker.createdAt)\n\t);\n}\n\nfunction markerState(path: string): \"active\" | \"stale\" | \"unknown\" {\n\ttry {\n\t\tconst value = readSmallJson(path);\n\t\tif (!isMarker(value)) return \"unknown\";\n\t\tif (value.hostname !== hostname()) return \"active\";\n\t\ttry {\n\t\t\tprocess.kill(value.pid, 0);\n\t\t\treturn \"active\";\n\t\t} catch (error) {\n\t\t\treturn (error as NodeJS.ErrnoException).code === \"EPERM\" ? \"active\" : \"stale\";\n\t\t}\n\t} catch (error) {\n\t\treturn (error as NodeJS.ErrnoException).code === \"ENOENT\" ? \"stale\" : \"unknown\";\n\t}\n}\n\nfunction removeFile(path: string): void {\n\ttry {\n\t\tunlinkSync(path);\n\t} catch {}\n}\n\nfunction cleanupMarkerBlocksAcquisition(runDir: string): boolean {\n\tconst markerPath = join(runDir, CLEANUP_MARKER_FILE);\n\tif (!existsSync(markerPath)) return false;\n\tconst state = markerState(markerPath);\n\tif (state !== \"stale\") return true;\n\tremoveFile(markerPath);\n\treturn existsSync(markerPath);\n}\n\nfunction hasActiveLease(runDir: string): boolean {\n\tconst leasesDir = join(runDir, LEASES_DIRECTORY);\n\tif (!existsSync(leasesDir)) return false;\n\tlet handle: Dir | undefined;\n\ttry {\n\t\thandle = opendirSync(leasesDir);\n\t\tlet scanned = 0;\n\t\twhile (true) {\n\t\t\tconst entry = handle.readSync();\n\t\t\tif (!entry) break;\n\t\t\tscanned++;\n\t\t\tif (scanned > MAX_LEASE_MARKERS) return true;\n\t\t\tif (!entry.isFile()) return true;\n\t\t\tconst markerPath = join(leasesDir, entry.name);\n\t\t\tconst state = markerState(markerPath);\n\t\t\tif (state === \"active\" || state === \"unknown\") return true;\n\t\t\tremoveFile(markerPath);\n\t\t}\n\t} catch {\n\t\treturn true;\n\t} finally {\n\t\ttry {\n\t\t\thandle?.closeSync();\n\t\t} catch {}\n\t}\n\ttry {\n\t\trmdirSync(leasesDir);\n\t} catch {}\n\treturn false;\n}\n\nfunction measureDirectory(path: string, budget: ScanBudget, depth = 0): { bytes: number; complete: boolean } {\n\tif (depth > MAX_DIRECTORY_DEPTH || budget.remaining <= 0) return { bytes: 0, complete: false };\n\tlet bytes = 0;\n\tlet complete = true;\n\tlet handle: Dir | undefined;\n\ttry {\n\t\thandle = opendirSync(path);\n\t\twhile (true) {\n\t\t\tconst entry = handle.readSync();\n\t\t\tif (!entry) break;\n\t\t\tif (budget.remaining <= 0) return { bytes, complete: false };\n\t\t\tbudget.remaining--;\n\t\t\tconst childPath = join(path, entry.name);\n\t\t\ttry {\n\t\t\t\tconst stats = lstatSync(childPath);\n\t\t\t\tif (stats.isDirectory() && !stats.isSymbolicLink()) {\n\t\t\t\t\tconst child = measureDirectory(childPath, budget, depth + 1);\n\t\t\t\t\tbytes += child.bytes;\n\t\t\t\t\tcomplete = complete && child.complete;\n\t\t\t\t} else {\n\t\t\t\t\tbytes += stats.size;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tcomplete = false;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\treturn { bytes, complete: false };\n\t} finally {\n\t\ttry {\n\t\t\thandle?.closeSync();\n\t\t} catch {}\n\t}\n\treturn { bytes, complete };\n}\n\nfunction inspectRun(\n\trunPath: string,\n\tcategory: string,\n\ttenant: string,\n\trunId: string,\n\tbudget: ScanBudget,\n\tunknownBytes: number,\n): InspectedRun | undefined {\n\ttry {\n\t\tconst stats = lstatSync(runPath);\n\t\tif (!stats.isDirectory() || stats.isSymbolicLink()) return undefined;\n\t\tconst manifest = readSmallJson(join(runPath, WORK_RUN_MANIFEST_FILE));\n\t\tif (!isManifest(manifest, category, tenant, runId)) return undefined;\n\t\tconst measured = measureDirectory(runPath, budget);\n\t\treturn {\n\t\t\tpath: runPath,\n\t\t\tmtimeMs: stats.mtimeMs,\n\t\t\tbytes: measured.complete ? measured.bytes : unknownBytes,\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction createCleanupMarker(runDir: string, now: number): string | undefined {\n\tconst path = join(runDir, CLEANUP_MARKER_FILE);\n\tconst marker: WorkMarker = { version: 1, hostname: hostname(), pid: process.pid, createdAt: now };\n\ttry {\n\t\twriteFileSync(path, JSON.stringify(marker), { encoding: \"utf8\", flag: \"wx\" });\n\t\treturn path;\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code !== \"EEXIST\") return undefined;\n\t\tif (markerState(path) !== \"stale\") return undefined;\n\t\tremoveFile(path);\n\t\ttry {\n\t\t\twriteFileSync(path, JSON.stringify(marker), { encoding: \"utf8\", flag: \"wx\" });\n\t\t\treturn path;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n\nfunction removeRunIfInactive(runPath: string, now: number): boolean {\n\tconst cleanupMarker = createCleanupMarker(runPath, now);\n\tif (!cleanupMarker) return false;\n\tif (hasActiveLease(runPath)) {\n\t\tremoveFile(cleanupMarker);\n\t\treturn false;\n\t}\n\ttry {\n\t\trmSync(runPath, { recursive: true, force: true });\n\t\treturn !existsSync(runPath);\n\t} catch {\n\t\tremoveFile(cleanupMarker);\n\t\treturn false;\n\t}\n}\n\nexport function getWorkRoot(agentDir: string): string {\n\treturn join(agentDir, \"work\");\n}\n\nexport function getWorkTenantDir(agentDir: string, category: string, tenant: string): string {\n\tassertPortableSegment(\"Work category\", category);\n\tassertPortableSegment(\"Work tenant\", tenant);\n\treturn join(getWorkRoot(agentDir), category, tenant);\n}\n\nexport function getWorkRunDir(agentDir: string, category: string, tenant: string, runId: string): string {\n\tassertPortableSegment(\"Work category\", category);\n\tassertPortableSegment(\"Work tenant\", tenant);\n\tassertPortableSegment(\"Work run id\", runId);\n\treturn join(getWorkRoot(agentDir), category, tenant, runId);\n}\n\nexport function createWorkRunId(now = Date.now()): string {\n\treturn `run-${now.toString(36)}-${process.pid.toString(36)}-${randomUUID().slice(0, 12)}`;\n}\n\nexport function pruneWorkTenant(\n\tagentDir: string,\n\tcategory: string,\n\ttenant: string,\n\toptions: WorkRetentionOptions = {},\n): string[] {\n\tconst tenantDir = getWorkTenantDir(agentDir, category, tenant);\n\tif (!existsSync(tenantDir)) return [];\n\ttry {\n\t\tconst stats = lstatSync(tenantDir);\n\t\tif (!stats.isDirectory() || stats.isSymbolicLink()) return [];\n\t} catch {\n\t\treturn [];\n\t}\n\n\tconst now = options.now ?? Date.now();\n\tconst maxAgeMs = nonNegativeLimit(options.maxAgeMs, DEFAULT_MAX_AGE_MS);\n\tconst maxRuns = nonNegativeLimit(options.maxRuns, DEFAULT_MAX_RUNS);\n\tconst maxTotalBytes = nonNegativeLimit(options.maxTotalBytes, DEFAULT_MAX_TOTAL_BYTES);\n\tconst maxScannedRuns = nonNegativeLimit(options.maxScannedRuns, DEFAULT_MAX_SCANNED_RUNS);\n\tconst budget: ScanBudget = {\n\t\tremaining: nonNegativeLimit(options.maxScannedEntries, DEFAULT_MAX_SCANNED_ENTRIES),\n\t};\n\tconst unknownBytes = Math.max(1, maxTotalBytes + 1);\n\tconst runs: InspectedRun[] = [];\n\tlet truncated = false;\n\tlet handle: Dir | undefined;\n\ttry {\n\t\thandle = opendirSync(tenantDir);\n\t\twhile (true) {\n\t\t\tconst entry = handle.readSync();\n\t\t\tif (!entry) break;\n\t\t\tif (entry.name.startsWith(\".\")) continue;\n\t\t\tif (runs.length >= maxScannedRuns) {\n\t\t\t\ttruncated = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!entry.isDirectory() || entry.isSymbolicLink()) continue;\n\t\t\tconst inspected = inspectRun(join(tenantDir, entry.name), category, tenant, entry.name, budget, unknownBytes);\n\t\t\tif (inspected) runs.push(inspected);\n\t\t}\n\t} catch {\n\t\treturn [];\n\t} finally {\n\t\ttry {\n\t\t\thandle?.closeSync();\n\t\t} catch {}\n\t}\n\n\tconst removed: string[] = [];\n\tlet retainedCount = runs.length + (truncated ? maxRuns + 1 : 0);\n\tlet retainedBytes = runs.reduce((sum, run) => sum + run.bytes, 0);\n\tfor (const run of runs.sort((left, right) => left.mtimeMs - right.mtimeMs)) {\n\t\tconst expired = now - run.mtimeMs > maxAgeMs;\n\t\tconst overCount = retainedCount > maxRuns;\n\t\tconst overBytes = retainedBytes > maxTotalBytes;\n\t\tif (!expired && !overCount && !overBytes) continue;\n\t\tif (!removeRunIfInactive(run.path, now)) continue;\n\t\tremoved.push(run.path);\n\t\tretainedCount--;\n\t\tretainedBytes -= run.bytes;\n\t}\n\treturn removed;\n}\n\nexport function getProcessWorkRun(agentDir: string, category: string, tenant: string, runId?: string): WorkRunLease {\n\tconst key = `${agentDir}\\0${category}\\0${tenant}\\0${runId ?? \"\"}`;\n\tconst existing = processWorkRuns.get(key);\n\tif (existing && existsSync(existing.path)) return existing;\n\tconst acquired = acquireWorkRun({ agentDir, category, tenant, runId });\n\tprocessWorkRuns.set(key, acquired);\n\treturn acquired;\n}\n\nexport function acquireWorkRun(options: AcquireWorkRunOptions): WorkRunLease {\n\tconst now = options.now ?? Date.now();\n\tconst runId = options.runId ?? createWorkRunId(now);\n\tassertPortableSegment(\"Work category\", options.category);\n\tassertPortableSegment(\"Work tenant\", options.tenant);\n\tassertPortableSegment(\"Work run id\", runId);\n\tconst tenantDir = ensureTenantDirectory(options.agentDir, options.category, options.tenant);\n\tconst runDir = join(tenantDir, runId);\n\tlet created = false;\n\ttry {\n\t\tmkdirSync(runDir);\n\t\tcreated = true;\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n\t\tconst stats = lstatSync(runDir);\n\t\tif (!stats.isDirectory() || stats.isSymbolicLink()) {\n\t\t\tthrow new Error(`Work run path is not a real directory: ${runDir}`);\n\t\t}\n\t}\n\n\tconst manifestPath = join(runDir, WORK_RUN_MANIFEST_FILE);\n\tif (created) {\n\t\tconst manifest: WorkRunManifest = {\n\t\t\tversion: 1,\n\t\t\tcategory: options.category,\n\t\t\ttenant: options.tenant,\n\t\t\trunId,\n\t\t\tcreatedAt: now,\n\t\t};\n\t\twriteFileSync(manifestPath, JSON.stringify(manifest), { encoding: \"utf8\", flag: \"wx\" });\n\t} else {\n\t\tlet manifest: unknown;\n\t\ttry {\n\t\t\tmanifest = readSmallJson(manifestPath);\n\t\t} catch {\n\t\t\tthrow new Error(`Refusing to adopt unowned work directory: ${runDir}`);\n\t\t}\n\t\tif (!isManifest(manifest, options.category, options.tenant, runId)) {\n\t\t\tthrow new Error(`Work directory manifest does not match its path: ${runDir}`);\n\t\t}\n\t}\n\n\tif (cleanupMarkerBlocksAcquisition(runDir)) {\n\t\tthrow new Error(`Work run is being cleaned: ${runDir}`);\n\t}\n\tconst leasesDir = join(runDir, LEASES_DIRECTORY);\n\tensureDirectory(leasesDir);\n\tconst markerPath = join(leasesDir, `active-${process.pid}-${randomUUID()}.json`);\n\tconst marker: WorkMarker = { version: 1, hostname: hostname(), pid: process.pid, createdAt: now };\n\twriteFileSync(markerPath, JSON.stringify(marker), { encoding: \"utf8\", flag: \"wx\" });\n\tif (cleanupMarkerBlocksAcquisition(runDir)) {\n\t\tremoveFile(markerPath);\n\t\tthrow new Error(`Work run entered cleanup while it was being acquired: ${runDir}`);\n\t}\n\ttry {\n\t\tutimesSync(runDir, new Date(now), new Date(now));\n\t} catch {}\n\n\tif (options.retention !== false) {\n\t\ttry {\n\t\t\tpruneWorkTenant(options.agentDir, options.category, options.tenant, options.retention);\n\t\t} catch {}\n\t}\n\n\tlet released = false;\n\treturn {\n\t\tpath: runDir,\n\t\tcategory: options.category,\n\t\ttenant: options.tenant,\n\t\trunId,\n\t\ttouch(): void {\n\t\t\tif (released) return;\n\t\t\tconst touchedAt = new Date();\n\t\t\ttry {\n\t\t\t\tutimesSync(markerPath, touchedAt, touchedAt);\n\t\t\t\tutimesSync(runDir, touchedAt, touchedAt);\n\t\t\t} catch {}\n\t\t},\n\t\trelease(): void {\n\t\t\tif (released) return;\n\t\t\treleased = true;\n\t\t\tremoveFile(markerPath);\n\t\t\ttry {\n\t\t\t\trmdirSync(leasesDir);\n\t\t\t} catch {}\n\t\t\tconst touchedAt = new Date();\n\t\t\ttry {\n\t\t\t\tutimesSync(runDir, touchedAt, touchedAt);\n\t\t\t} catch {}\n\t\t},\n\t};\n}\n"]}
|