@fitlab-ai/agent-infra 0.9.1 → 0.9.2
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/README.md +16 -4
- package/README.zh-CN.md +16 -4
- package/bin/cli.ts +12 -0
- package/dist/bin/cli.js +14 -0
- package/dist/bin/internal-cli.js +15 -0
- package/dist/lib/agent-client.js +163 -0
- package/dist/lib/agent-clients/adapter.js +294 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +62 -0
- package/dist/lib/agent-clients/adapters/codex-sandbox.js +210 -0
- package/dist/lib/agent-clients/adapters/codex.js +54 -0
- package/dist/lib/agent-clients/adapters/gemini-cli.js +64 -0
- package/dist/lib/agent-clients/adapters/index.js +20 -0
- package/dist/lib/agent-clients/adapters/opencode.js +64 -0
- package/dist/lib/agent-clients/config.js +177 -0
- package/dist/lib/agent-clients/custom-tuis.js +99 -0
- package/dist/lib/agent-clients/invocation.js +9 -0
- package/dist/lib/agent-clients/next-steps.js +38 -0
- package/dist/lib/agent-clients/project-assets.js +45 -0
- package/dist/lib/agent-clients/reconcile.js +235 -0
- package/dist/lib/agent-clients/registry.js +72 -0
- package/dist/lib/agent-clients/schema.js +57 -0
- package/dist/lib/agent-clients/types.js +29 -0
- package/dist/lib/builtin-tuis.js +9 -14
- package/dist/lib/defaults.json +85 -73
- package/dist/lib/git/review-snapshot.js +25 -10
- package/dist/lib/git/reviewed-commit-equivalence.js +110 -0
- package/dist/lib/init.js +47 -91
- package/dist/lib/internal/agent-client.js +117 -0
- package/dist/lib/internal/git-workflow.js +1 -1
- package/dist/lib/internal/release-workflow.js +71 -16
- package/dist/lib/internal/task-orchestration.js +151 -0
- package/dist/lib/internal/task-review.js +65 -0
- package/dist/lib/merge.js +2 -1
- package/dist/lib/platform/adapters.js +38 -1
- package/dist/lib/platform/change-request-git-evidence.js +113 -0
- package/dist/lib/platform/merged-pr-equivalence.js +41 -11
- package/dist/lib/platform/pr-checks.js +38 -10
- package/dist/lib/platform/pull-requests.js +110 -6
- package/dist/lib/platform/releases.js +1 -1
- package/dist/lib/platform/verification-required.js +8 -10
- package/dist/lib/prompt.js +15 -3
- package/dist/lib/release/channels.js +4 -1
- package/dist/lib/release/workflow.js +4 -2
- package/dist/lib/remove-dir.js +25 -0
- package/dist/lib/run/tui.js +8 -2
- package/dist/lib/sandbox/agent-client-reconciler.js +234 -0
- package/dist/lib/sandbox/clipboard/linux.js +2 -1
- package/dist/lib/sandbox/commands/create.js +76 -180
- package/dist/lib/sandbox/commands/enter.js +17 -1
- package/dist/lib/sandbox/commands/prune.js +2 -2
- package/dist/lib/sandbox/commands/rm.js +3 -2
- package/dist/lib/sandbox/config.js +21 -4
- package/dist/lib/sandbox/constants.js +3 -0
- package/dist/lib/sandbox/dockerfile.js +2 -1
- package/dist/lib/sandbox/managed-fs.js +10 -5
- package/dist/lib/sandbox/recovery.js +52 -36
- package/dist/lib/sandbox/shell.js +1 -1
- package/dist/lib/sandbox/tool-types.js +8 -0
- package/dist/lib/sandbox/tools.js +2 -126
- package/dist/lib/task/artifact-lifecycle.js +1 -49
- package/dist/lib/task/commands/decisions.js +50 -20
- package/dist/lib/task/delegation-receipts.js +107 -0
- package/dist/lib/task/events.js +100 -1
- package/dist/lib/task/orchestration.js +420 -0
- package/dist/lib/task/review-artifacts.js +143 -59
- package/dist/lib/task/review-finalization.js +166 -0
- package/dist/lib/task/review-fingerprint.js +4 -0
- package/dist/lib/task/verification-checks.js +2 -1
- package/dist/lib/task/verification-engine.js +156 -28
- package/dist/lib/task/verification-review.js +1 -1
- package/dist/lib/task/verification.js +3 -1
- package/dist/lib/task/workspace-snapshot.js +34 -0
- package/dist/lib/update.js +42 -161
- package/lib/agent-client.ts +214 -0
- package/lib/agent-clients/adapter.ts +461 -0
- package/lib/agent-clients/adapters/claude-code.ts +70 -0
- package/lib/agent-clients/adapters/codex-sandbox.ts +245 -0
- package/lib/agent-clients/adapters/codex.ts +58 -0
- package/lib/agent-clients/adapters/gemini-cli.ts +65 -0
- package/lib/agent-clients/adapters/index.ts +27 -0
- package/lib/agent-clients/adapters/opencode.ts +66 -0
- package/lib/agent-clients/config.ts +278 -0
- package/lib/agent-clients/custom-tuis.ts +149 -0
- package/lib/agent-clients/invocation.ts +19 -0
- package/lib/agent-clients/next-steps.ts +72 -0
- package/lib/agent-clients/project-assets.ts +96 -0
- package/lib/agent-clients/reconcile.ts +324 -0
- package/lib/agent-clients/registry.ts +144 -0
- package/lib/agent-clients/schema.ts +62 -0
- package/lib/agent-clients/types.ts +78 -0
- package/lib/benchmark/schemas/v1.0.0/case-manifest.schema.json +120 -0
- package/lib/benchmark/schemas/v1.0.0/common.schema.json +109 -0
- package/lib/benchmark/schemas/v1.0.0/grader-result.schema.json +160 -0
- package/lib/benchmark/schemas/v1.0.0/run-manifest.schema.json +122 -0
- package/lib/benchmark/schemas/v1.0.0/subject.schema.json +227 -0
- package/lib/builtin-tuis.ts +16 -15
- package/lib/defaults.json +24 -12
- package/lib/git/review-snapshot.ts +39 -9
- package/lib/git/reviewed-commit-equivalence.ts +135 -0
- package/lib/init.ts +53 -121
- package/lib/internal/agent-client.ts +131 -0
- package/lib/internal/git-workflow.ts +1 -1
- package/lib/internal/release-workflow.ts +68 -16
- package/lib/internal/task-orchestration.ts +132 -0
- package/lib/internal/task-review.ts +65 -0
- package/lib/merge.ts +2 -1
- package/lib/platform/adapters.ts +142 -3
- package/lib/platform/change-request-git-evidence.ts +159 -0
- package/lib/platform/merged-pr-equivalence.ts +51 -14
- package/lib/platform/pr-checks.ts +50 -22
- package/lib/platform/pull-requests.ts +134 -25
- package/lib/platform/releases.ts +1 -1
- package/lib/platform/verification-required.ts +12 -10
- package/lib/prompt.ts +16 -3
- package/lib/release/channels.ts +4 -1
- package/lib/release/workflow.ts +4 -3
- package/lib/remove-dir.ts +28 -0
- package/lib/run/tui.ts +9 -1
- package/lib/sandbox/agent-client-reconciler.ts +378 -0
- package/lib/sandbox/clipboard/linux.ts +2 -1
- package/lib/sandbox/commands/create.ts +111 -192
- package/lib/sandbox/commands/enter.ts +23 -1
- package/lib/sandbox/commands/prune.ts +2 -2
- package/lib/sandbox/commands/rm.ts +3 -2
- package/lib/sandbox/config.ts +31 -4
- package/lib/sandbox/constants.ts +6 -0
- package/lib/sandbox/dockerfile.ts +2 -1
- package/lib/sandbox/managed-fs.ts +18 -5
- package/lib/sandbox/recovery.ts +98 -67
- package/lib/sandbox/shell.ts +1 -1
- package/lib/sandbox/tool-types.ts +143 -0
- package/lib/sandbox/tools.ts +5 -157
- package/lib/task/artifact-lifecycle.ts +1 -38
- package/lib/task/commands/decisions.ts +53 -20
- package/lib/task/delegation-receipts.ts +170 -0
- package/lib/task/events.ts +110 -2
- package/lib/task/orchestration.ts +543 -0
- package/lib/task/review-artifacts.ts +201 -69
- package/lib/task/review-finalization.ts +275 -0
- package/lib/task/review-fingerprint.ts +5 -0
- package/lib/task/verification-checks.ts +2 -1
- package/lib/task/verification-engine.ts +177 -28
- package/lib/task/verification-review.ts +1 -0
- package/lib/task/verification.ts +5 -2
- package/lib/task/workspace-snapshot.ts +36 -0
- package/lib/update.ts +48 -184
- package/package.json +2 -2
- package/templates/.agents/README.en.md +21 -20
- package/templates/.agents/README.zh-CN.md +21 -20
- package/templates/.agents/hooks/lifecycle-delegation.js +64 -0
- package/templates/.agents/rules/README.en.md +2 -0
- package/templates/.agents/rules/README.zh-CN.md +2 -0
- package/templates/.agents/rules/commit-and-pr.en.md +1 -0
- package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -0
- package/templates/.agents/rules/human-decision-context.en.md +14 -12
- package/templates/.agents/rules/human-decision-context.zh-CN.md +14 -12
- package/templates/.agents/rules/lifecycle-orchestration.en.md +21 -0
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +21 -0
- package/templates/.agents/rules/next-step-output.en.md +16 -0
- package/templates/.agents/rules/next-step-output.zh-CN.md +16 -0
- package/templates/.agents/rules/no-mid-flow-questions.en.md +2 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +2 -0
- package/templates/.agents/rules/release-commands.github.en.md +11 -5
- package/templates/.agents/rules/release-commands.github.zh-CN.md +11 -5
- package/templates/.agents/rules/review-handshake.en.md +4 -4
- package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
- package/templates/.agents/rules/review-method.en.md +113 -0
- package/templates/.agents/rules/review-method.zh-CN.md +98 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -16
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -16
- package/templates/.agents/skills/close-codescan/SKILL.en.md +4 -4
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +4 -4
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +14 -16
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +14 -16
- package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
- package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
- package/templates/.agents/skills/commit/SKILL.en.md +11 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +11 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +24 -28
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +24 -28
- package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.en.md +10 -10
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +10 -10
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
- package/templates/.agents/skills/create-task/SKILL.en.md +11 -11
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +11 -11
- package/templates/.agents/skills/import-codescan/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/init-labels/SKILL.en.md +4 -4
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/init-milestones/SKILL.en.md +4 -4
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
- package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -3
- package/templates/.agents/skills/review-analysis/config/verify.en.json +6 -0
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +6 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +13 -15
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +13 -15
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +51 -5
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +52 -6
- package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +25 -22
- package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +25 -22
- package/templates/.agents/skills/review-code/SKILL.en.md +28 -8
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +28 -8
- package/templates/.agents/skills/review-code/config/verify.en.json +7 -0
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +7 -0
- package/templates/.agents/skills/review-code/reference/concurrency-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/concurrency-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-code/reference/cross-platform-risks.en.md +27 -0
- package/templates/.agents/skills/review-code/reference/cross-platform-risks.zh-CN.md +27 -0
- package/templates/.agents/skills/review-code/reference/documentation-antipatterns.en.md +13 -0
- package/templates/.agents/skills/review-code/reference/documentation-antipatterns.zh-CN.md +13 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +22 -24
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +22 -24
- package/templates/.agents/skills/review-code/reference/report-template.en.md +54 -7
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +54 -7
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +29 -24
- package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +29 -24
- package/templates/.agents/skills/review-code/reference/security-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/security-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-plan/SKILL.en.md +13 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -3
- package/templates/.agents/skills/review-plan/config/verify.en.json +6 -0
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +6 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +13 -15
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +13 -15
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +61 -5
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +61 -5
- package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +21 -24
- package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +21 -24
- package/templates/.agents/skills/run-task/SKILL.en.md +22 -0
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +22 -0
- package/templates/.agents/skills/run-task/config/verify.en.json +9 -0
- package/templates/.agents/skills/run-task/config/verify.zh-CN.json +9 -0
- package/templates/.agents/skills/test/SKILL.en.md +4 -4
- package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/test-integration/SKILL.en.md +4 -4
- package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -5
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +647 -203
- package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +4 -4
- package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/watch-pr/SKILL.en.md +8 -8
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +8 -8
- package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +6 -0
- package/templates/.claude/commands/run-task.en.md +8 -0
- package/templates/.claude/commands/run-task.zh-CN.md +8 -0
- package/templates/.claude/settings.json +24 -0
- package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +3 -0
- package/templates/.gemini/commands/_project_/run-task.en.toml +8 -0
- package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +8 -0
- package/templates/.opencode/commands/run-task.en.md +11 -0
- package/templates/.opencode/commands/run-task.zh-CN.md +11 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
1
|
import path from 'node:path';
|
|
3
|
-
import { run } from './shell.ts';
|
|
2
|
+
import { run, runSafe } from './shell.ts';
|
|
3
|
+
import { removeDirRecursive } from '../remove-dir.ts';
|
|
4
4
|
|
|
5
5
|
export function assertManagedPath(root: string, target: string): void {
|
|
6
6
|
const resolvedRoot = path.resolve(root);
|
|
@@ -15,13 +15,26 @@ export function assertManagedPath(root: string, target: string): void {
|
|
|
15
15
|
|
|
16
16
|
export function removeManagedDir(root: string, dir: string): void {
|
|
17
17
|
assertManagedPath(root, dir);
|
|
18
|
-
|
|
18
|
+
removeDirRecursive(dir);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export function removeWorktreeDir(
|
|
21
|
+
export function removeWorktreeDir(
|
|
22
|
+
repoRoot: string,
|
|
23
|
+
worktreeBase: string,
|
|
24
|
+
dir: string,
|
|
25
|
+
{ runFn = run, runSafeFn = runSafe }: {
|
|
26
|
+
runFn?: typeof run;
|
|
27
|
+
runSafeFn?: typeof runSafe;
|
|
28
|
+
} = {}
|
|
29
|
+
): void {
|
|
22
30
|
try {
|
|
23
|
-
|
|
31
|
+
runFn('git', ['-C', repoRoot, 'worktree', 'remove', dir, '--force']);
|
|
24
32
|
} catch {
|
|
33
|
+
// On WSL2 the worktree is registered under its `/mnt/<drive>/...` path, so
|
|
34
|
+
// `git worktree remove <windows-path>` cannot match it. Delete the managed
|
|
35
|
+
// directory directly, then prune the now-dangling worktree metadata so the
|
|
36
|
+
// branch is no longer reported as checked out.
|
|
25
37
|
removeManagedDir(worktreeBase, dir);
|
|
38
|
+
runSafeFn('git', ['-C', repoRoot, 'worktree', 'prune']);
|
|
26
39
|
}
|
|
27
40
|
}
|
package/lib/sandbox/recovery.ts
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
containerNameCandidates,
|
|
5
|
+
sandboxBranchLabel,
|
|
6
|
+
sandboxLabel,
|
|
7
|
+
sandboxRuntimeCapabilityLabel
|
|
8
|
+
} from './constants.ts';
|
|
9
|
+
import { isAgentClientId } from '../agent-clients/types.ts';
|
|
10
|
+
import {
|
|
11
|
+
createSandboxCapabilityPlan
|
|
12
|
+
} from './agent-client-reconciler.ts';
|
|
13
|
+
import type {
|
|
14
|
+
SandboxRecoveryFindingDescriptor,
|
|
15
|
+
SandboxRecoveryRepair
|
|
16
|
+
} from './tool-types.ts';
|
|
4
17
|
import type { SandboxConfig } from './config.ts';
|
|
5
18
|
import { toEnginePath } from './engines/wsl2-paths.ts';
|
|
6
19
|
import { sandboxCoreBindMounts } from './mounts.ts';
|
|
@@ -9,6 +22,7 @@ import {
|
|
|
9
22
|
declaredTmpfsSeedEntries,
|
|
10
23
|
resolveTools,
|
|
11
24
|
toolConfigDirCandidates,
|
|
25
|
+
type SandboxTool,
|
|
12
26
|
type TmpfsSeedEntry
|
|
13
27
|
} from './tools.ts';
|
|
14
28
|
import {
|
|
@@ -23,10 +37,22 @@ export type SandboxRecoveryFinding = {
|
|
|
23
37
|
message: string;
|
|
24
38
|
path?: string;
|
|
25
39
|
seed?: TmpfsSeedEntry;
|
|
40
|
+
repair?: SandboxRecoveryRepair;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type SandboxAgentClientCheckSnapshot = {
|
|
44
|
+
adapterId: string;
|
|
45
|
+
checkId: string;
|
|
46
|
+
applicable: boolean;
|
|
47
|
+
healthy: boolean;
|
|
48
|
+
finding: SandboxRecoveryFindingDescriptor;
|
|
49
|
+
repair?: SandboxRecoveryRepair;
|
|
26
50
|
};
|
|
27
51
|
|
|
28
52
|
export type SandboxRecoverySnapshot = {
|
|
29
53
|
identityOk: boolean;
|
|
54
|
+
runtimeCapabilityOk?: boolean;
|
|
55
|
+
unexpectedCapabilityMounts?: string[];
|
|
30
56
|
mounts: Array<{
|
|
31
57
|
path: string;
|
|
32
58
|
expectedType: string;
|
|
@@ -48,12 +74,7 @@ export type SandboxRecoverySnapshot = {
|
|
|
48
74
|
targetState: 'ok' | 'missing' | 'wrong-type' | 'inaccessible';
|
|
49
75
|
}>;
|
|
50
76
|
aliasesReadable: boolean;
|
|
51
|
-
|
|
52
|
-
commandAvailable: boolean;
|
|
53
|
-
stateWritable: boolean;
|
|
54
|
-
promptsSourceExists: boolean;
|
|
55
|
-
promptsValid: boolean;
|
|
56
|
-
};
|
|
77
|
+
agentClientChecks: SandboxAgentClientCheckSnapshot[];
|
|
57
78
|
};
|
|
58
79
|
|
|
59
80
|
export type SandboxReadyResult = {
|
|
@@ -114,6 +135,19 @@ export function classifySandboxRecovery(snapshot: SandboxRecoverySnapshot): Sand
|
|
|
114
135
|
message: 'Container identity does not match the requested sandbox branch.'
|
|
115
136
|
});
|
|
116
137
|
}
|
|
138
|
+
if (snapshot.runtimeCapabilityOk === false) {
|
|
139
|
+
findings.push({
|
|
140
|
+
repairKind: 'hard-failure',
|
|
141
|
+
message: 'Container runtime capability signature does not match the current sandbox plan.'
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
for (const mountPath of snapshot.unexpectedCapabilityMounts ?? []) {
|
|
145
|
+
findings.push({
|
|
146
|
+
repairKind: 'hard-failure',
|
|
147
|
+
message: `Disabled Agent Client capability remains mounted at ${mountPath}.`,
|
|
148
|
+
path: mountPath
|
|
149
|
+
});
|
|
150
|
+
}
|
|
117
151
|
|
|
118
152
|
for (const mount of snapshot.mounts) {
|
|
119
153
|
if (
|
|
@@ -177,25 +211,11 @@ export function classifySandboxRecovery(snapshot: SandboxRecoverySnapshot): Sand
|
|
|
177
211
|
});
|
|
178
212
|
}
|
|
179
213
|
|
|
180
|
-
|
|
181
|
-
if (!
|
|
214
|
+
for (const check of snapshot.agentClientChecks) {
|
|
215
|
+
if (check.applicable && !check.healthy) {
|
|
182
216
|
findings.push({
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
if (!snapshot.codex.stateWritable) {
|
|
188
|
-
findings.push({
|
|
189
|
-
repairKind: 'permissions',
|
|
190
|
-
message: 'Codex state directory is not writable by devuser.',
|
|
191
|
-
path: '/home/devuser/.codex'
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
if (snapshot.codex.promptsSourceExists && !snapshot.codex.promptsValid) {
|
|
195
|
-
findings.push({
|
|
196
|
-
repairKind: 'builtin-link',
|
|
197
|
-
message: 'Codex prompts link does not point to the workspace commands directory.',
|
|
198
|
-
path: '/home/devuser/.codex/prompts'
|
|
217
|
+
...check.finding,
|
|
218
|
+
...(check.repair === undefined ? {} : { repair: check.repair })
|
|
199
219
|
});
|
|
200
220
|
}
|
|
201
221
|
}
|
|
@@ -277,11 +297,10 @@ function hostSourceAccessible(hostPath: string, writable: boolean): boolean {
|
|
|
277
297
|
function expectedMounts(params: {
|
|
278
298
|
config: SandboxConfig;
|
|
279
299
|
branch: string;
|
|
280
|
-
|
|
300
|
+
tools: readonly SandboxTool[];
|
|
281
301
|
actualMounts: Map<string, DockerMount>;
|
|
282
302
|
}): ExpectedMount[] {
|
|
283
|
-
const { config, branch, actualMounts } = params;
|
|
284
|
-
const tools = resolveTools(config);
|
|
303
|
+
const { config, branch, tools, actualMounts } = params;
|
|
285
304
|
const core = sandboxCoreBindMounts(config, branch).map((mount) => ({
|
|
286
305
|
path: mount.containerPath,
|
|
287
306
|
expectedType: 'bind' as const,
|
|
@@ -301,7 +320,13 @@ function expectedMounts(params: {
|
|
|
301
320
|
})
|
|
302
321
|
);
|
|
303
322
|
const persistentTools = tools
|
|
304
|
-
.filter((tool) =>
|
|
323
|
+
.filter((tool) =>
|
|
324
|
+
!tool.tmpfs
|
|
325
|
+
&& (
|
|
326
|
+
config.agentClientSource === 'canonical'
|
|
327
|
+
|| actualMounts.has(tool.containerMount)
|
|
328
|
+
)
|
|
329
|
+
)
|
|
305
330
|
.map((tool) => ({
|
|
306
331
|
path: tool.containerMount,
|
|
307
332
|
expectedType: 'bind' as const,
|
|
@@ -401,7 +426,8 @@ export function collectSandboxRecoverySnapshot(params: {
|
|
|
401
426
|
});
|
|
402
427
|
}
|
|
403
428
|
}
|
|
404
|
-
const
|
|
429
|
+
const capabilityPlan = createSandboxCapabilityPlan(params.config);
|
|
430
|
+
const tools = [...capabilityPlan.tools];
|
|
405
431
|
const seeds = declaredTmpfsSeedEntries(tools).map((seed) => {
|
|
406
432
|
const mounted = mountsByDestination.get(seed.stagingPath)?.Type === 'bind';
|
|
407
433
|
return {
|
|
@@ -434,11 +460,18 @@ export function collectSandboxRecoverySnapshot(params: {
|
|
|
434
460
|
}));
|
|
435
461
|
const labels = inspection.Config?.Labels ?? {};
|
|
436
462
|
const branchLabel = labels[sandboxBranchLabel(params.config)];
|
|
437
|
-
const
|
|
463
|
+
const selectedToolIds = new Set(tools.map((tool) => tool.id));
|
|
464
|
+
const enforceCanonicalPlan = params.config.agentClientSource === 'canonical';
|
|
465
|
+
const unexpectedCapabilityMounts = enforceCanonicalPlan
|
|
466
|
+
? capabilityPlan.cleanupInventory
|
|
467
|
+
.filter((tool) => isAgentClientId(tool.id) && !selectedToolIds.has(tool.id))
|
|
468
|
+
.map((tool) => tool.containerMount)
|
|
469
|
+
.filter((mountPath) => mountsByDestination.has(mountPath))
|
|
470
|
+
: [];
|
|
438
471
|
const mountSnapshots = expectedMounts({
|
|
439
472
|
config: params.config,
|
|
440
473
|
branch: params.branch,
|
|
441
|
-
|
|
474
|
+
tools,
|
|
442
475
|
actualMounts: mountsByDestination
|
|
443
476
|
}).map((expected) => {
|
|
444
477
|
const actual = mountsByDestination.get(expected.path);
|
|
@@ -478,6 +511,10 @@ export function collectSandboxRecoverySnapshot(params: {
|
|
|
478
511
|
identityOk: typeof inspection.Id === 'string'
|
|
479
512
|
&& inspection.Id.length > 0
|
|
480
513
|
&& branchLabel === params.branch,
|
|
514
|
+
runtimeCapabilityOk: enforceCanonicalPlan
|
|
515
|
+
? labels[sandboxRuntimeCapabilityLabel(params.config)] === capabilityPlan.runtimeSignature
|
|
516
|
+
: undefined,
|
|
517
|
+
unexpectedCapabilityMounts,
|
|
481
518
|
mounts: mountSnapshots,
|
|
482
519
|
tmpfs,
|
|
483
520
|
seeds,
|
|
@@ -488,38 +525,31 @@ export function collectSandboxRecoverySnapshot(params: {
|
|
|
488
525
|
['/home/devuser/.bash_aliases'],
|
|
489
526
|
runOkFn
|
|
490
527
|
),
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
params.container,
|
|
517
|
-
'test "$(readlink -- "$1")" = "$2"',
|
|
518
|
-
['/home/devuser/.codex/prompts', '/workspace/.codex/commands'],
|
|
519
|
-
runOkFn
|
|
520
|
-
)
|
|
521
|
-
}
|
|
522
|
-
: undefined
|
|
528
|
+
agentClientChecks: capabilityPlan.recoveryChecks.map(({ adapterId, check }) => {
|
|
529
|
+
const applicable = check.when === undefined || probe(
|
|
530
|
+
params.engine,
|
|
531
|
+
params.container,
|
|
532
|
+
check.when.script,
|
|
533
|
+
[...check.when.args],
|
|
534
|
+
runOkFn,
|
|
535
|
+
check.when.user
|
|
536
|
+
);
|
|
537
|
+
return {
|
|
538
|
+
adapterId,
|
|
539
|
+
checkId: check.id,
|
|
540
|
+
applicable,
|
|
541
|
+
healthy: !applicable || probe(
|
|
542
|
+
params.engine,
|
|
543
|
+
params.container,
|
|
544
|
+
check.probe.script,
|
|
545
|
+
[...check.probe.args],
|
|
546
|
+
runOkFn,
|
|
547
|
+
check.probe.user
|
|
548
|
+
),
|
|
549
|
+
finding: check.finding,
|
|
550
|
+
...(check.repair === undefined ? {} : { repair: check.repair })
|
|
551
|
+
};
|
|
552
|
+
})
|
|
523
553
|
};
|
|
524
554
|
}
|
|
525
555
|
|
|
@@ -626,10 +656,11 @@ function repairFindings(params: {
|
|
|
626
656
|
deps: params.deps
|
|
627
657
|
});
|
|
628
658
|
const runVerboseFn = params.deps?.runVerbose ?? runVerboseEngine;
|
|
629
|
-
|
|
659
|
+
for (const repair of params.findings.flatMap((finding) =>
|
|
660
|
+
finding.repair === undefined ? [] : [finding.repair]
|
|
661
|
+
)) {
|
|
630
662
|
runVerboseFn(params.engine, 'docker', [
|
|
631
|
-
'exec', '--user',
|
|
632
|
-
'/workspace/.codex/commands', '/home/devuser/.codex/prompts'
|
|
663
|
+
'exec', '--user', repair.user, params.container, repair.command, ...repair.args
|
|
633
664
|
]);
|
|
634
665
|
}
|
|
635
666
|
}
|
package/lib/sandbox/shell.ts
CHANGED
|
@@ -143,7 +143,7 @@ export function runSafe(cmd: string, args: string[], opts: CommandOptions = {}):
|
|
|
143
143
|
export function commandForEngine(engine: string, cmd: string, args: string[] = []): { cmd: string; args: string[] } {
|
|
144
144
|
if (engine === 'wsl2') {
|
|
145
145
|
const resolvedWrapper = resolveCommand('wsl.exe');
|
|
146
|
-
return { cmd: resolvedWrapper, args: ['--', cmd, ...args] };
|
|
146
|
+
return { cmd: resolvedWrapper, args: ['--exec', cmd, ...args] };
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
if (cmd === 'docker') {
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
type SandboxToolInstall =
|
|
2
|
+
| Readonly<{ type: 'npm'; cmd: string }>
|
|
3
|
+
| Readonly<{ type: 'shell'; cmd: string }>;
|
|
4
|
+
|
|
5
|
+
type SandboxTool = {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
install: SandboxToolInstall;
|
|
9
|
+
sandboxBase: string;
|
|
10
|
+
containerMount: string;
|
|
11
|
+
versionCmd: string;
|
|
12
|
+
setupHint: string;
|
|
13
|
+
envVars?: Record<string, string>;
|
|
14
|
+
hostPreSeedFiles?: Array<{ hostPath: string; sandboxName: string }>;
|
|
15
|
+
hostPreSeedDirs?: Array<{ hostDir: string; sandboxSubdir: string }>;
|
|
16
|
+
pathRewriteFiles?: string[];
|
|
17
|
+
hostLiveMounts?: Array<{ hostPath: string; containerSubpath: string }>;
|
|
18
|
+
postSetupCmds?: string[];
|
|
19
|
+
tmpfs?: { size?: string; seed?: string[] };
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type SandboxAlias = Readonly<{
|
|
23
|
+
name: string;
|
|
24
|
+
command: string;
|
|
25
|
+
}>;
|
|
26
|
+
|
|
27
|
+
const SANDBOX_HOOK_PHASES = [
|
|
28
|
+
'prepare',
|
|
29
|
+
'before-container-create',
|
|
30
|
+
'after-container-start',
|
|
31
|
+
'before-enter'
|
|
32
|
+
] as const;
|
|
33
|
+
|
|
34
|
+
type SandboxHookPhase = (typeof SANDBOX_HOOK_PHASES)[number];
|
|
35
|
+
|
|
36
|
+
type SandboxHookStatus =
|
|
37
|
+
| 'ready'
|
|
38
|
+
| 'skip-client-runtime'
|
|
39
|
+
| 'warning'
|
|
40
|
+
| 'fatal'
|
|
41
|
+
| 'healthy'
|
|
42
|
+
| 'hard-failure';
|
|
43
|
+
|
|
44
|
+
type SandboxHookCommand = Readonly<{
|
|
45
|
+
command: string;
|
|
46
|
+
args?: readonly string[];
|
|
47
|
+
cwd?: string;
|
|
48
|
+
env?: NodeJS.ProcessEnv;
|
|
49
|
+
}>;
|
|
50
|
+
|
|
51
|
+
type SandboxHookCommandResult = Readonly<{
|
|
52
|
+
stdout: string;
|
|
53
|
+
}>;
|
|
54
|
+
|
|
55
|
+
type AgentClientSandboxHookResult = Readonly<{
|
|
56
|
+
status: SandboxHookStatus;
|
|
57
|
+
message?: string;
|
|
58
|
+
}>;
|
|
59
|
+
|
|
60
|
+
type SandboxResolvedToolState = Readonly<{
|
|
61
|
+
tool: SandboxTool;
|
|
62
|
+
dir: string;
|
|
63
|
+
}>;
|
|
64
|
+
|
|
65
|
+
type SandboxHookCreateContext = Readonly<{
|
|
66
|
+
hostHome: string;
|
|
67
|
+
resolvedTools: readonly SandboxResolvedToolState[];
|
|
68
|
+
}>;
|
|
69
|
+
|
|
70
|
+
type AgentClientSandboxHookContext = Readonly<{
|
|
71
|
+
signal: AbortSignal;
|
|
72
|
+
runCommand(command: SandboxHookCommand): Promise<SandboxHookCommandResult>;
|
|
73
|
+
config?: Readonly<Record<string, unknown>>;
|
|
74
|
+
plan?: Readonly<Record<string, unknown>>;
|
|
75
|
+
create?: SandboxHookCreateContext;
|
|
76
|
+
}>;
|
|
77
|
+
|
|
78
|
+
type AgentClientSandboxHook = Readonly<{
|
|
79
|
+
id: string;
|
|
80
|
+
phase: SandboxHookPhase;
|
|
81
|
+
timeoutMs?: number;
|
|
82
|
+
run(context: AgentClientSandboxHookContext): Promise<AgentClientSandboxHookResult>;
|
|
83
|
+
}>;
|
|
84
|
+
|
|
85
|
+
type SandboxToolContext = Readonly<{
|
|
86
|
+
home: string;
|
|
87
|
+
project: string;
|
|
88
|
+
}>;
|
|
89
|
+
|
|
90
|
+
type SandboxRecoveryProbe = Readonly<{
|
|
91
|
+
script: string;
|
|
92
|
+
args: readonly string[];
|
|
93
|
+
user?: string;
|
|
94
|
+
}>;
|
|
95
|
+
|
|
96
|
+
type SandboxRecoveryFindingDescriptor = Readonly<{
|
|
97
|
+
repairKind: 'permissions' | 'builtin-link' | 'hard-failure';
|
|
98
|
+
message: string;
|
|
99
|
+
path?: string;
|
|
100
|
+
}>;
|
|
101
|
+
|
|
102
|
+
type SandboxRecoveryRepair = Readonly<{
|
|
103
|
+
user: string;
|
|
104
|
+
command: string;
|
|
105
|
+
args: readonly string[];
|
|
106
|
+
}>;
|
|
107
|
+
|
|
108
|
+
type AgentClientSandboxRecoveryCheck = Readonly<{
|
|
109
|
+
id: string;
|
|
110
|
+
when?: SandboxRecoveryProbe;
|
|
111
|
+
probe: SandboxRecoveryProbe;
|
|
112
|
+
finding: SandboxRecoveryFindingDescriptor;
|
|
113
|
+
repair?: SandboxRecoveryRepair;
|
|
114
|
+
}>;
|
|
115
|
+
|
|
116
|
+
type AgentClientSandboxDescriptor = Readonly<{
|
|
117
|
+
createTool(context: SandboxToolContext): SandboxTool;
|
|
118
|
+
aliases: readonly SandboxAlias[];
|
|
119
|
+
hooks: readonly AgentClientSandboxHook[];
|
|
120
|
+
recoveryChecks?: readonly AgentClientSandboxRecoveryCheck[];
|
|
121
|
+
}>;
|
|
122
|
+
|
|
123
|
+
export { SANDBOX_HOOK_PHASES };
|
|
124
|
+
export type {
|
|
125
|
+
AgentClientSandboxDescriptor,
|
|
126
|
+
AgentClientSandboxHook,
|
|
127
|
+
AgentClientSandboxHookContext,
|
|
128
|
+
AgentClientSandboxHookResult,
|
|
129
|
+
AgentClientSandboxRecoveryCheck,
|
|
130
|
+
SandboxAlias,
|
|
131
|
+
SandboxHookCommand,
|
|
132
|
+
SandboxHookCommandResult,
|
|
133
|
+
SandboxHookPhase,
|
|
134
|
+
SandboxHookStatus,
|
|
135
|
+
SandboxHookCreateContext,
|
|
136
|
+
SandboxRecoveryFindingDescriptor,
|
|
137
|
+
SandboxRecoveryProbe,
|
|
138
|
+
SandboxRecoveryRepair,
|
|
139
|
+
SandboxResolvedToolState,
|
|
140
|
+
SandboxTool,
|
|
141
|
+
SandboxToolContext,
|
|
142
|
+
SandboxToolInstall
|
|
143
|
+
};
|
package/lib/sandbox/tools.ts
CHANGED
|
@@ -1,33 +1,10 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { createSandboxCapabilityPlan } from './agent-client-reconciler.ts';
|
|
2
3
|
import { safeNameCandidates, sanitizeBranchName } from './constants.ts';
|
|
3
4
|
import { hostJoin } from './engines/wsl2-paths.ts';
|
|
5
|
+
import type { SandboxTool, SandboxToolInstall } from './tool-types.ts';
|
|
4
6
|
|
|
5
|
-
export type SandboxToolInstall
|
|
6
|
-
| { type: 'npm'; cmd: string }
|
|
7
|
-
| { type: 'shell'; cmd: string };
|
|
8
|
-
|
|
9
|
-
export type SandboxTool = {
|
|
10
|
-
id: string;
|
|
11
|
-
name: string;
|
|
12
|
-
install: SandboxToolInstall;
|
|
13
|
-
sandboxBase: string;
|
|
14
|
-
containerMount: string;
|
|
15
|
-
versionCmd: string;
|
|
16
|
-
setupHint: string;
|
|
17
|
-
envVars?: Record<string, string>;
|
|
18
|
-
hostPreSeedFiles?: Array<{ hostPath: string; sandboxName: string }>;
|
|
19
|
-
hostPreSeedDirs?: Array<{ hostDir: string; sandboxSubdir: string }>;
|
|
20
|
-
pathRewriteFiles?: string[];
|
|
21
|
-
hostLiveMounts?: Array<{ hostPath: string; containerSubpath: string }>;
|
|
22
|
-
postSetupCmds?: string[];
|
|
23
|
-
// When set, containerMount is mounted as an in-container tmpfs (RAM) instead
|
|
24
|
-
// of bind-mounting the host config dir, keeping high-churn tool logs off the
|
|
25
|
-
// host disk. `seed` lists the host-dir entries (relative to the tool's config
|
|
26
|
-
// dir) to copy into the tmpfs when the container starts. It is an explicit
|
|
27
|
-
// allowlist so runtime files (e.g. logs_2.sqlite, sessions) left in the host
|
|
28
|
-
// dir stay out of the container and seeded config cannot write back.
|
|
29
|
-
tmpfs?: { size?: string; seed?: string[] };
|
|
30
|
-
};
|
|
7
|
+
export type { SandboxTool, SandboxToolInstall } from './tool-types.ts';
|
|
31
8
|
|
|
32
9
|
export type TmpfsSeedEntry = {
|
|
33
10
|
toolId: string;
|
|
@@ -64,114 +41,11 @@ type ToolsConfig = {
|
|
|
64
41
|
project: string;
|
|
65
42
|
tools: string[];
|
|
66
43
|
customTools?: SandboxTool[];
|
|
44
|
+
agentClientState?: import('../agent-clients/types.ts').AgentClientState;
|
|
67
45
|
};
|
|
68
46
|
|
|
69
47
|
const TOOL_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
70
48
|
|
|
71
|
-
function createBuiltinTools(home: string, project: string): Record<string, SandboxTool> {
|
|
72
|
-
return {
|
|
73
|
-
'claude-code': {
|
|
74
|
-
id: 'claude-code',
|
|
75
|
-
name: 'Claude Code',
|
|
76
|
-
install: { type: 'npm', cmd: '@anthropic-ai/claude-code@latest' },
|
|
77
|
-
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'claude-code'),
|
|
78
|
-
containerMount: '/home/devuser/.claude',
|
|
79
|
-
versionCmd: 'claude --version',
|
|
80
|
-
setupHint: 'Authenticates via host credentials live-mounted at ~/.claude/.credentials.json',
|
|
81
|
-
// Claude Code stores user data (.claude.json — onboarding state, theme,
|
|
82
|
-
// workspace trust) at $HOME/.claude.json by default, which sits OUTSIDE
|
|
83
|
-
// the bind-mounted /home/devuser/.claude tree, so our preseeded
|
|
84
|
-
// .claude.json never gets read and the theme picker re-runs on every
|
|
85
|
-
// container start. Pinning CLAUDE_CONFIG_DIR to the tool mount relocates
|
|
86
|
-
// .claude.json into the same directory as .credentials.json/settings.json,
|
|
87
|
-
// letting ensureClaudeOnboarding actually take effect.
|
|
88
|
-
envVars: { CLAUDE_CONFIG_DIR: '/home/devuser/.claude' },
|
|
89
|
-
hostPreSeedDirs: [
|
|
90
|
-
{ hostDir: hostJoin(home, '.claude', 'plugins'), sandboxSubdir: 'plugins' }
|
|
91
|
-
],
|
|
92
|
-
pathRewriteFiles: [
|
|
93
|
-
'plugins/installed_plugins.json',
|
|
94
|
-
'plugins/known_marketplaces.json'
|
|
95
|
-
],
|
|
96
|
-
hostLiveMounts: [
|
|
97
|
-
{
|
|
98
|
-
hostPath: hostJoin(home, '.agent-infra', 'credentials', project, 'claude-code', '.credentials.json'),
|
|
99
|
-
containerSubpath: '.credentials.json'
|
|
100
|
-
}
|
|
101
|
-
]
|
|
102
|
-
},
|
|
103
|
-
codex: {
|
|
104
|
-
id: 'codex',
|
|
105
|
-
name: 'Codex',
|
|
106
|
-
install: { type: 'npm', cmd: '@openai/codex' },
|
|
107
|
-
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'codex'),
|
|
108
|
-
containerMount: '/home/devuser/.codex',
|
|
109
|
-
versionCmd: 'codex --version',
|
|
110
|
-
setupHint: 'Run codex once inside the container and choose Device Code login if needed.',
|
|
111
|
-
// codex churns ~/.codex/logs_2.sqlite heavily (upstream openai/codex#24275);
|
|
112
|
-
// a bind-mount would write-amplify onto the host SSD via virtiofs. Mount the
|
|
113
|
-
// codex home as tmpfs so those logs stay in RAM and die with the container.
|
|
114
|
-
// Seeded config (config.toml, model-catalogs) is copied into the tmpfs at
|
|
115
|
-
// startup; runtime files like logs_2.sqlite stay in RAM.
|
|
116
|
-
tmpfs: { size: '512m', seed: ['config.toml', 'model-catalogs'] },
|
|
117
|
-
hostLiveMounts: [
|
|
118
|
-
{ hostPath: hostJoin(home, '.codex', 'auth.json'), containerSubpath: 'auth.json' }
|
|
119
|
-
],
|
|
120
|
-
postSetupCmds: [
|
|
121
|
-
'test -d /workspace/.codex/commands && ln -sfn /workspace/.codex/commands /home/devuser/.codex/prompts || true'
|
|
122
|
-
]
|
|
123
|
-
},
|
|
124
|
-
opencode: {
|
|
125
|
-
id: 'opencode',
|
|
126
|
-
name: 'OpenCode',
|
|
127
|
-
install: { type: 'npm', cmd: 'opencode-ai' },
|
|
128
|
-
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'opencode'),
|
|
129
|
-
containerMount: '/home/devuser/.local/share/opencode',
|
|
130
|
-
versionCmd: 'opencode version',
|
|
131
|
-
setupHint: 'Configure OpenCode credentials inside the container before first use.',
|
|
132
|
-
// OpenCode reads opencode.json from $XDG_CONFIG_HOME/opencode by default,
|
|
133
|
-
// outside this tool mount. Pin the config file path so the inherited
|
|
134
|
-
// sandbox opencode.json is the one the TUI actually reads.
|
|
135
|
-
envVars: { OPENCODE_CONFIG: '/home/devuser/.local/share/opencode/opencode.json' },
|
|
136
|
-
hostLiveMounts: [
|
|
137
|
-
{
|
|
138
|
-
hostPath: hostJoin(home, '.local', 'share', 'opencode', 'auth.json'),
|
|
139
|
-
containerSubpath: 'auth.json'
|
|
140
|
-
}
|
|
141
|
-
]
|
|
142
|
-
},
|
|
143
|
-
'gemini-cli': {
|
|
144
|
-
id: 'gemini-cli',
|
|
145
|
-
name: 'Gemini CLI',
|
|
146
|
-
install: { type: 'npm', cmd: '@google/gemini-cli' },
|
|
147
|
-
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'gemini-cli'),
|
|
148
|
-
containerMount: '/home/devuser/.gemini',
|
|
149
|
-
versionCmd: 'gemini --version',
|
|
150
|
-
setupHint: 'Run gemini inside the container to finish authentication.',
|
|
151
|
-
hostLiveMounts: [
|
|
152
|
-
{ hostPath: hostJoin(home, '.gemini', 'oauth_creds.json'), containerSubpath: 'oauth_creds.json' }
|
|
153
|
-
],
|
|
154
|
-
hostPreSeedFiles: [
|
|
155
|
-
{ hostPath: hostJoin(home, '.gemini', 'settings.json'), sandboxName: 'settings.json' },
|
|
156
|
-
{ hostPath: hostJoin(home, '.gemini', 'google_accounts.json'), sandboxName: 'google_accounts.json' }
|
|
157
|
-
]
|
|
158
|
-
},
|
|
159
|
-
'agent-infra': {
|
|
160
|
-
id: 'agent-infra',
|
|
161
|
-
name: 'agent-infra CLI',
|
|
162
|
-
install: { type: 'npm', cmd: '@fitlab-ai/agent-infra@latest' },
|
|
163
|
-
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'agent-infra'),
|
|
164
|
-
containerMount: '/home/devuser/.agent-infra-cli',
|
|
165
|
-
versionCmd: 'ai version --raw',
|
|
166
|
-
setupHint: 'Provides the ai and agent-infra CLI commands inside the sandbox.'
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export function builtinToolIds(): string[] {
|
|
172
|
-
return Object.keys(createBuiltinTools('', ''));
|
|
173
|
-
}
|
|
174
|
-
|
|
175
49
|
function validateTool(tool: SandboxTool): void {
|
|
176
50
|
if (!tool.id || !TOOL_ID_PATTERN.test(tool.id)) {
|
|
177
51
|
throw new Error(`Invalid sandbox tool id: ${String(tool.id)}`);
|
|
@@ -379,33 +253,7 @@ export function parseCustomTools(value: unknown, options: { home: string }): San
|
|
|
379
253
|
}
|
|
380
254
|
|
|
381
255
|
export function resolveTools(config: ToolsConfig): SandboxTool[] {
|
|
382
|
-
|
|
383
|
-
const customs = config.customTools ?? [];
|
|
384
|
-
|
|
385
|
-
const seen = new Set<string>();
|
|
386
|
-
for (const tool of customs) {
|
|
387
|
-
if (builtins[tool.id]) {
|
|
388
|
-
throw new Error(`Custom sandbox tool id "${tool.id}" collides with a built-in tool`);
|
|
389
|
-
}
|
|
390
|
-
if (seen.has(tool.id)) {
|
|
391
|
-
throw new Error(`Duplicate sandbox tool id "${tool.id}" in customTools`);
|
|
392
|
-
}
|
|
393
|
-
seen.add(tool.id);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
const merged: Record<string, SandboxTool> = { ...builtins };
|
|
397
|
-
for (const tool of customs) {
|
|
398
|
-
merged[tool.id] = tool;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
return config.tools.map((id) => {
|
|
402
|
-
const tool = merged[id];
|
|
403
|
-
if (!tool) {
|
|
404
|
-
throw new Error(`Unknown sandbox tool: ${id}`);
|
|
405
|
-
}
|
|
406
|
-
validateTool(tool);
|
|
407
|
-
return tool;
|
|
408
|
-
});
|
|
256
|
+
return [...createSandboxCapabilityPlan(config).tools];
|
|
409
257
|
}
|
|
410
258
|
|
|
411
259
|
export function toolConfigDir(tool: SandboxTool, project: string, branch: string): string {
|