@fitlab-ai/agent-infra 0.8.5 → 0.9.1
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 +6 -1
- package/README.zh-CN.md +5 -1
- package/bin/cli.ts +2 -2
- package/dist/bin/cli.js +2 -2
- package/dist/bin/internal-cli.js +103 -0
- package/dist/lib/decide.js +81 -90
- package/dist/lib/defaults.json +1 -0
- package/dist/lib/git/review-snapshot.js +79 -0
- package/dist/lib/git/workflow.js +101 -0
- package/dist/lib/internal/git-workflow.js +42 -0
- package/dist/lib/internal/platform-checks.js +119 -0
- package/dist/lib/internal/platform-comment.js +129 -0
- package/dist/lib/internal/platform-context.js +46 -0
- package/dist/lib/internal/platform-issue.js +142 -0
- package/dist/lib/internal/platform-pr.js +146 -0
- package/dist/lib/internal/platform-release-notes.js +53 -0
- package/dist/lib/internal/release-workflow.js +320 -0
- package/dist/lib/internal/task-artifact.js +67 -0
- package/dist/lib/internal/task-context.js +43 -0
- package/dist/lib/internal/task-event.js +70 -0
- package/dist/lib/internal/task-ledger.js +114 -0
- package/dist/lib/internal/task-lifecycle.js +58 -0
- package/dist/lib/internal/task-short-id.js +83 -0
- package/dist/lib/internal/task-snapshot.js +47 -0
- package/dist/lib/internal/task-verify.js +50 -0
- package/dist/lib/internal/task-warning.js +74 -0
- package/dist/lib/platform/adapters.js +14 -0
- package/dist/lib/platform/context.js +146 -0
- package/dist/lib/platform/github-client.js +124 -0
- package/dist/lib/platform/github-release-notes.js +96 -0
- package/dist/lib/platform/issue-comments.js +409 -0
- package/dist/lib/platform/issue-metadata.js +183 -0
- package/dist/lib/platform/issues.js +619 -0
- package/dist/lib/platform/merged-pr-equivalence.js +54 -0
- package/dist/lib/platform/metadata-labels.js +28 -0
- package/dist/lib/platform/pr-checks.js +216 -0
- package/dist/lib/platform/pr-summary.js +88 -0
- package/dist/lib/platform/pull-request-metadata.js +38 -0
- package/dist/lib/platform/pull-requests.js +340 -0
- package/dist/lib/platform/release-notes.js +143 -0
- package/dist/lib/platform/releases.js +82 -0
- package/dist/lib/platform/types.js +22 -0
- package/dist/lib/platform/verification-required.js +102 -0
- package/dist/lib/platform/verification-sync.js +1014 -0
- package/dist/lib/platform/verification.js +6 -0
- package/dist/lib/release/channels.js +31 -0
- package/dist/lib/release/workflow.js +32 -0
- package/dist/lib/render.js +3 -2
- package/dist/lib/run/index.js +24 -7
- package/dist/lib/sandbox/build-proxy.js +63 -0
- package/dist/lib/sandbox/capture.js +25 -3
- package/dist/lib/sandbox/commands/create.js +93 -31
- package/dist/lib/sandbox/commands/enter.js +34 -11
- package/dist/lib/sandbox/commands/list-running.js +10 -8
- package/dist/lib/sandbox/commands/rebuild.js +24 -7
- package/dist/lib/sandbox/commands/show.js +2 -2
- package/dist/lib/sandbox/commands/start.js +53 -15
- package/dist/lib/sandbox/image-build.js +2 -1
- package/dist/lib/sandbox/index.js +6 -6
- package/dist/lib/sandbox/mounts.js +36 -0
- package/dist/lib/sandbox/recovery.js +562 -0
- package/dist/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
- package/dist/lib/sandbox/runtimes/base.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/java17.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/java21.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/node20.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/node22.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/python3.dockerfile +8 -1
- package/dist/lib/sandbox/shell.js +2 -1
- package/dist/lib/sandbox/task-resolver.js +5 -1
- package/dist/lib/sandbox/tools.js +19 -0
- package/dist/lib/server/protocol.js +5 -5
- package/dist/lib/task/activity-log.js +61 -0
- package/dist/lib/task/artifact-lifecycle.js +427 -0
- package/dist/lib/task/command-options.js +30 -0
- package/dist/lib/task/commands/cat.js +36 -6
- package/dist/lib/task/commands/decisions.js +80 -46
- package/dist/lib/task/commands/files.js +21 -7
- package/dist/lib/task/commands/grep.js +55 -7
- package/dist/lib/task/commands/issue-body.js +33 -19
- package/dist/lib/task/commands/log.js +27 -70
- package/dist/lib/task/commands/show.js +20 -7
- package/dist/lib/task/commands/status.js +18 -8
- package/dist/lib/task/decision-intents.js +154 -0
- package/dist/lib/task/decision-items.js +53 -0
- package/dist/lib/task/events.js +318 -0
- package/dist/lib/task/frontmatter.js +159 -1
- package/dist/lib/task/implementation-inputs.js +117 -0
- package/dist/lib/task/index.js +12 -9
- package/dist/lib/task/issue-form.js +21 -6
- package/dist/lib/task/ledger-intents.js +185 -0
- package/dist/lib/task/ledger.js +65 -35
- package/dist/lib/task/lifecycle.js +484 -0
- package/dist/lib/task/resolve-ref.js +152 -32
- package/dist/lib/task/review-artifacts.js +101 -0
- package/dist/lib/task/review-fingerprint.js +55 -0
- package/dist/lib/task/sections.js +358 -4
- package/dist/lib/task/short-id.js +292 -7
- package/dist/lib/task/snapshot.js +95 -0
- package/dist/lib/task/verification-checks.js +8 -0
- package/dist/lib/task/verification-config.js +14 -0
- package/dist/lib/task/verification-engine.js +923 -0
- package/dist/lib/task/verification-review.js +3 -0
- package/dist/lib/task/verification-types.js +2 -0
- package/dist/lib/task/verification.js +113 -0
- package/dist/lib/task/workflow-warning-intents.js +131 -0
- package/dist/lib/task/workflow-warnings.js +10 -78
- package/dist/lib/task/write.js +241 -0
- package/lib/decide.ts +57 -97
- package/lib/defaults.json +1 -0
- package/lib/git/review-snapshot.ts +73 -0
- package/lib/git/workflow.ts +101 -0
- package/lib/internal/git-workflow.ts +37 -0
- package/lib/internal/platform-checks.ts +101 -0
- package/lib/internal/platform-comment.ts +99 -0
- package/lib/internal/platform-context.ts +33 -0
- package/lib/internal/platform-issue.ts +116 -0
- package/lib/internal/platform-pr.ts +119 -0
- package/lib/internal/platform-release-notes.ts +55 -0
- package/lib/internal/release-workflow.ts +288 -0
- package/lib/internal/task-artifact.ts +47 -0
- package/lib/internal/task-context.ts +35 -0
- package/lib/internal/task-event.ts +51 -0
- package/lib/internal/task-ledger.ts +86 -0
- package/lib/internal/task-lifecycle.ts +41 -0
- package/lib/internal/task-short-id.ts +55 -0
- package/lib/internal/task-snapshot.ts +32 -0
- package/lib/internal/task-verify.ts +33 -0
- package/lib/internal/task-warning.ts +50 -0
- package/lib/platform/adapters.ts +30 -0
- package/lib/platform/context.ts +164 -0
- package/lib/platform/github-client.ts +146 -0
- package/lib/platform/github-release-notes.ts +132 -0
- package/lib/platform/issue-comments.ts +479 -0
- package/lib/platform/issue-metadata.ts +250 -0
- package/lib/platform/issues.ts +693 -0
- package/lib/platform/merged-pr-equivalence.ts +70 -0
- package/lib/platform/metadata-labels.ts +33 -0
- package/lib/platform/pr-checks.ts +247 -0
- package/lib/platform/pr-summary.ts +90 -0
- package/lib/platform/pull-request-metadata.ts +69 -0
- package/lib/platform/pull-requests.ts +386 -0
- package/lib/platform/release-notes.ts +167 -0
- package/lib/platform/releases.ts +71 -0
- package/lib/platform/types.ts +74 -0
- package/lib/platform/verification-required.ts +109 -0
- package/{runtime/platform-adapters/platform-sync.github.js → lib/platform/verification-sync.ts} +198 -367
- package/lib/platform/verification.ts +11 -0
- package/lib/release/channels.ts +30 -0
- package/lib/release/workflow.ts +31 -0
- package/lib/render.ts +3 -2
- package/lib/run/index.ts +25 -7
- package/lib/sandbox/build-proxy.ts +94 -0
- package/lib/sandbox/capture.ts +29 -3
- package/lib/sandbox/commands/create.ts +120 -36
- package/lib/sandbox/commands/enter.ts +38 -11
- package/lib/sandbox/commands/list-running.ts +10 -8
- package/lib/sandbox/commands/rebuild.ts +29 -7
- package/lib/sandbox/commands/show.ts +2 -2
- package/lib/sandbox/commands/start.ts +50 -16
- package/lib/sandbox/image-build.ts +4 -1
- package/lib/sandbox/index.ts +6 -6
- package/lib/sandbox/mounts.ts +55 -0
- package/lib/sandbox/recovery.ts +828 -0
- package/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
- package/lib/sandbox/runtimes/base.dockerfile +8 -1
- package/lib/sandbox/runtimes/java17.dockerfile +8 -1
- package/lib/sandbox/runtimes/java21.dockerfile +8 -1
- package/lib/sandbox/runtimes/node20.dockerfile +8 -1
- package/lib/sandbox/runtimes/node22.dockerfile +8 -1
- package/lib/sandbox/runtimes/python3.dockerfile +8 -1
- package/lib/sandbox/shell.ts +3 -1
- package/lib/sandbox/task-resolver.ts +5 -1
- package/lib/sandbox/tools.ts +31 -0
- package/lib/server/protocol.ts +5 -5
- package/lib/task/activity-log.ts +62 -0
- package/lib/task/artifact-lifecycle.ts +484 -0
- package/lib/task/command-options.ts +32 -0
- package/lib/task/commands/cat.ts +22 -9
- package/lib/task/commands/decisions.ts +49 -47
- package/lib/task/commands/files.ts +14 -6
- package/lib/task/commands/grep.ts +34 -7
- package/lib/task/commands/issue-body.ts +26 -21
- package/lib/task/commands/log.ts +17 -70
- package/lib/task/commands/show.ts +14 -6
- package/lib/task/commands/status.ts +17 -7
- package/lib/task/decision-intents.ts +163 -0
- package/lib/task/decision-items.ts +64 -0
- package/lib/task/events.ts +320 -0
- package/lib/task/frontmatter.ts +202 -2
- package/lib/task/implementation-inputs.ts +137 -0
- package/lib/task/index.ts +12 -9
- package/lib/task/issue-form.ts +24 -6
- package/lib/task/ledger-intents.ts +201 -0
- package/lib/task/ledger.ts +77 -31
- package/lib/task/lifecycle.ts +458 -0
- package/lib/task/resolve-ref.ts +197 -36
- package/{templates/.agents/scripts/lib/review-artifacts.js → lib/task/review-artifacts.ts} +11 -12
- package/{templates/.agents/scripts/lib/post-review-commit.js → lib/task/review-fingerprint.ts} +26 -12
- package/lib/task/sections.ts +486 -4
- package/lib/task/short-id.ts +403 -9
- package/lib/task/snapshot.ts +143 -0
- package/lib/task/verification-checks.ts +9 -0
- package/lib/task/verification-config.ts +20 -0
- package/{templates/.agents/scripts/validate-artifact.js → lib/task/verification-engine.ts} +230 -421
- package/lib/task/verification-review.ts +10 -0
- package/lib/task/verification-types.ts +29 -0
- package/lib/task/verification.ts +156 -0
- package/lib/task/workflow-warning-intents.ts +142 -0
- package/lib/task/workflow-warnings.ts +10 -78
- package/lib/task/write.ts +468 -0
- package/package.json +4 -2
- package/templates/.agents/README.en.md +4 -3
- package/templates/.agents/README.zh-CN.md +4 -3
- package/templates/.agents/rules/README.en.md +6 -5
- package/templates/.agents/rules/README.zh-CN.md +6 -5
- package/templates/.agents/rules/create-issue.en.md +1 -1
- package/templates/.agents/rules/create-issue.github.en.md +5 -184
- package/templates/.agents/rules/create-issue.github.zh-CN.md +12 -178
- package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
- package/templates/.agents/rules/human-decision-context.en.md +36 -0
- package/templates/.agents/rules/human-decision-context.zh-CN.md +36 -0
- package/templates/.agents/rules/issue-fields.github.en.md +4 -148
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +9 -144
- package/templates/.agents/rules/issue-pr-commands.en.md +43 -3
- package/templates/.agents/rules/issue-pr-commands.github.en.md +15 -220
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +23 -213
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +45 -3
- package/templates/.agents/rules/issue-sync.en.md +1 -1
- package/templates/.agents/rules/issue-sync.github.en.md +14 -301
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +36 -287
- package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
- package/templates/.agents/rules/milestone-inference.en.md +1 -1
- package/templates/.agents/rules/milestone-inference.github.en.md +7 -119
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +8 -108
- package/templates/.agents/rules/milestone-inference.zh-CN.md +1 -1
- package/templates/.agents/rules/next-step-output.en.md +12 -11
- package/templates/.agents/rules/next-step-output.zh-CN.md +12 -11
- package/templates/.agents/rules/no-mid-flow-questions.en.md +3 -3
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +3 -3
- package/templates/.agents/rules/pr-checks-commands.en.md +16 -3
- package/templates/.agents/rules/pr-checks-commands.github.en.md +10 -54
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +19 -47
- package/templates/.agents/rules/pr-checks-commands.zh-CN.md +30 -3
- package/templates/.agents/rules/pr-sync.en.md +30 -3
- package/templates/.agents/rules/pr-sync.github.en.md +12 -115
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +26 -104
- package/templates/.agents/rules/pr-sync.zh-CN.md +43 -3
- package/templates/.agents/rules/release-commands.en.md +2 -2
- package/templates/.agents/rules/release-commands.github.en.md +10 -40
- package/templates/.agents/rules/release-commands.github.zh-CN.md +9 -39
- package/templates/.agents/rules/release-commands.zh-CN.md +2 -2
- package/templates/.agents/rules/review-handshake.en.md +10 -6
- package/templates/.agents/rules/review-handshake.zh-CN.md +10 -6
- package/templates/.agents/rules/task-management.en.md +11 -2
- package/templates/.agents/rules/task-management.zh-CN.md +11 -3
- package/templates/.agents/rules/task-short-id.en.md +25 -29
- package/templates/.agents/rules/task-short-id.zh-CN.md +20 -23
- package/templates/.agents/rules/testing-discipline.en.md +20 -2
- package/templates/.agents/rules/testing-discipline.zh-CN.md +20 -2
- package/templates/.agents/scripts/lib/agent-infra-package.js +1 -9
- package/templates/.agents/scripts/task-short-id.js +71 -533
- package/templates/.agents/skills/analyze-task/SKILL.en.md +26 -56
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +30 -60
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -3
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/block-task/SKILL.en.md +21 -47
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +23 -47
- package/templates/.agents/skills/block-task/config/verify.json +1 -2
- package/templates/.agents/skills/cancel-task/SKILL.en.md +16 -45
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +16 -45
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -2
- package/templates/.agents/skills/check-task/SKILL.en.md +6 -3
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +6 -3
- package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -24
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -24
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -24
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -24
- package/templates/.agents/skills/code-task/SKILL.en.md +22 -35
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +28 -44
- package/templates/.agents/skills/code-task/config/verify.en.json +3 -3
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +3 -3
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +13 -9
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +13 -9
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.en.md +11 -1
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +11 -1
- package/templates/.agents/skills/commit/SKILL.en.md +13 -11
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +13 -11
- package/templates/.agents/skills/commit/config/verify.json +1 -2
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -11
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -11
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +4 -8
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +4 -8
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +6 -18
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +5 -17
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +13 -0
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +13 -0
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +13 -36
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +13 -36
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -3
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +10 -54
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +10 -79
- package/templates/.agents/skills/complete-task/SKILL.en.md +59 -63
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +59 -63
- package/templates/.agents/skills/complete-task/config/verify.en.json +10 -7
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +10 -7
- package/templates/.agents/skills/create-pr/SKILL.en.md +19 -44
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +19 -43
- package/templates/.agents/skills/create-pr/config/verify.json +1 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +8 -12
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +7 -11
- package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -38
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +16 -37
- package/templates/.agents/skills/create-task/SKILL.en.md +16 -13
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +16 -13
- package/templates/.agents/skills/create-task/config/verify.json +1 -2
- package/templates/.agents/skills/create-task/reference/context-capture.en.md +29 -0
- package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +29 -0
- package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-codescan/config/verify.json +1 -2
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-dependabot/config/verify.json +1 -2
- package/templates/.agents/skills/import-issue/SKILL.en.md +9 -13
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -14
- package/templates/.agents/skills/import-issue/config/verify.json +1 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +17 -42
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +22 -49
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -3
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/post-release/SKILL.en.md +11 -81
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +8 -78
- 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 +16 -163
- package/templates/.agents/skills/release/SKILL.zh-CN.md +15 -162
- package/templates/.agents/skills/restore-task/SKILL.en.md +11 -29
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +11 -29
- package/templates/.agents/skills/restore-task/config/verify.json +1 -2
- package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -25
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +13 -33
- package/templates/.agents/skills/review-analysis/config/verify.en.json +3 -4
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +3 -4
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +9 -12
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +9 -12
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +9 -2
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +9 -2
- package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +7 -0
- package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +7 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +22 -36
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +23 -41
- package/templates/.agents/skills/review-code/config/verify.en.json +3 -4
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +3 -4
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +56 -18
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +56 -18
- package/templates/.agents/skills/review-code/reference/report-template.en.md +11 -4
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +11 -4
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +7 -0
- package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +7 -0
- package/templates/.agents/skills/review-plan/SKILL.en.md +13 -25
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +13 -33
- package/templates/.agents/skills/review-plan/config/verify.en.json +3 -4
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +3 -4
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +9 -12
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +9 -12
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +9 -2
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +9 -2
- package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +7 -0
- package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +7 -0
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +1 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +3 -15
- package/templates/.agents/skills/watch-pr/SKILL.en.md +24 -14
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +25 -15
- package/templates/.agents/skills/watch-pr/config/verify.json +1 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +2 -2
- package/templates/.agents/templates/task.en.md +24 -1
- package/templates/.agents/templates/task.zh-CN.md +24 -1
- package/templates/.claude/commands/analyze-task.en.md +1 -1
- package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
- package/templates/.claude/commands/block-task.en.md +1 -1
- package/templates/.claude/commands/block-task.zh-CN.md +1 -1
- package/templates/.claude/commands/cancel-task.en.md +1 -1
- package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
- package/templates/.claude/commands/check-task.en.md +1 -1
- package/templates/.claude/commands/check-task.zh-CN.md +1 -1
- package/templates/.claude/commands/code-task.en.md +1 -1
- package/templates/.claude/commands/code-task.zh-CN.md +1 -1
- package/templates/.claude/commands/commit.en.md +1 -0
- package/templates/.claude/commands/commit.zh-CN.md +1 -0
- package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-task.en.md +1 -1
- package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
- package/templates/.claude/commands/create-pr.en.md +1 -1
- package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
- package/templates/.claude/commands/plan-task.en.md +1 -1
- package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
- package/templates/.claude/commands/review-analysis.en.md +1 -1
- package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
- package/templates/.claude/commands/review-code.en.md +1 -1
- package/templates/.claude/commands/review-code.zh-CN.md +1 -1
- package/templates/.claude/commands/review-plan.en.md +1 -1
- package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
- package/templates/.claude/commands/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/.gemini/commands/_project_/analyze-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/block-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/cancel-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/check-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/code-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/commit.en.toml +4 -0
- package/templates/.gemini/commands/_project_/commit.zh-CN.toml +4 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +2 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/complete-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/create-pr.en.toml +2 -0
- package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/plan-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/review-analysis.en.toml +2 -0
- package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/review-code.en.toml +2 -0
- package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/review-plan.en.toml +2 -0
- package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/watch-pr.en.toml +2 -0
- package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +2 -0
- package/templates/.git-hooks/check-large-files.cjs +92 -0
- package/templates/.git-hooks/check-version-format.sh +5 -1
- package/templates/.git-hooks/pre-commit +1 -0
- package/templates/.opencode/commands/analyze-task.en.md +1 -1
- package/templates/.opencode/commands/analyze-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/check-task.en.md +1 -1
- package/templates/.opencode/commands/check-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/code-task.en.md +1 -1
- package/templates/.opencode/commands/code-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/commit.en.md +2 -0
- package/templates/.opencode/commands/commit.zh-CN.md +2 -0
- package/templates/.opencode/commands/complete-task.en.md +1 -1
- package/templates/.opencode/commands/complete-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/plan-task.en.md +1 -1
- package/templates/.opencode/commands/plan-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/review-analysis.en.md +1 -1
- package/templates/.opencode/commands/review-analysis.zh-CN.md +1 -1
- package/templates/.opencode/commands/review-code.en.md +1 -1
- package/templates/.opencode/commands/review-code.zh-CN.md +1 -1
- package/templates/.opencode/commands/review-plan.en.md +1 -1
- package/templates/.opencode/commands/review-plan.zh-CN.md +1 -1
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +0 -40
- package/templates/.agents/scripts/platform-adapters/platform-sync.js +0 -13
- package/templates/.agents/scripts/review-diff-fingerprint.js +0 -170
- package/templates/.agents/scripts/workflow-warnings.js +0 -290
- package/templates/.agents/skills/code-task/scripts/detect-mode.js +0 -257
- package/templates/.agents/skills/release/scripts/manage-milestones.github.sh +0 -80
- package/templates/.agents/skills/release/scripts/manage-milestones.sh +0 -6
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { inspectRequiredChecks } from './pr-checks.js';
|
|
2
|
+
function verificationCheck(type, status, message, failType) {
|
|
3
|
+
return { type, status, message, ...(failType ? { fail_type: failType } : {}) };
|
|
4
|
+
}
|
|
5
|
+
export { inspectRequiredChecks, verificationCheck };
|
|
6
|
+
//# sourceMappingURL=verification.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
async function inspectNpmChannel(name, version, fetcher = fetch) {
|
|
2
|
+
try {
|
|
3
|
+
const response = await fetcher(`https://registry.npmjs.org/${encodeURIComponent(name)}/${encodeURIComponent(version)}`);
|
|
4
|
+
if (response.status === 404)
|
|
5
|
+
return { status: 'no-op', published: false, version, error: null };
|
|
6
|
+
if (!response.ok)
|
|
7
|
+
return { status: 'blocked', published: null, version, error: { code: 'NPM_REGISTRY_UNAVAILABLE', message: `npm registry returned ${response.status}` } };
|
|
8
|
+
const body = await response.json();
|
|
9
|
+
return { status: 'no-op', published: body.version === version, version, error: null };
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
return { status: 'blocked', published: null, version, error: { code: 'NPM_REGISTRY_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) } };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
async function inspectHomebrewChannel(url, version, fetcher = fetch) {
|
|
16
|
+
try {
|
|
17
|
+
const response = await fetcher(url);
|
|
18
|
+
if (response.status === 404)
|
|
19
|
+
return { status: 'no-op', published: false, version, error: null };
|
|
20
|
+
if (!response.ok)
|
|
21
|
+
return { status: 'blocked', published: null, version, error: { code: 'HOMEBREW_UNAVAILABLE', message: `formula endpoint returned ${response.status}` } };
|
|
22
|
+
const formula = await response.text();
|
|
23
|
+
const escaped = version.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
24
|
+
return { status: 'no-op', published: new RegExp(`(?:version\\s+["']${escaped}["']|/v?${escaped}\\.)`).test(formula), version, error: null };
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
return { status: 'blocked', published: null, version, error: { code: 'HOMEBREW_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) } };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export { inspectHomebrewChannel, inspectNpmChannel };
|
|
31
|
+
//# sourceMappingURL=channels.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
function deriveReleasePhase(facts) {
|
|
2
|
+
if (!facts.localTag)
|
|
3
|
+
return 'unprepared';
|
|
4
|
+
if (!facts.remoteBranch && !facts.remoteTag)
|
|
5
|
+
return 'prepared';
|
|
6
|
+
if (!facts.remoteBranch || !facts.remoteTag)
|
|
7
|
+
return 'partially-published';
|
|
8
|
+
if (facts.githubRelease !== true || facts.npm !== true || facts.homebrew !== true)
|
|
9
|
+
return 'published';
|
|
10
|
+
if (facts.smoke !== 'success' || !facts.postCommit)
|
|
11
|
+
return 'post-pending';
|
|
12
|
+
return 'complete';
|
|
13
|
+
}
|
|
14
|
+
function releaseSnapshot(version, facts) {
|
|
15
|
+
return { version, tag: `v${version.replace(/^v/, '')}`, phase: deriveReleasePhase(facts), facts };
|
|
16
|
+
}
|
|
17
|
+
async function runReleaseAction(input) {
|
|
18
|
+
const before = releaseSnapshot(input.version, await input.inspect());
|
|
19
|
+
if (input.action === 'inspect')
|
|
20
|
+
return { status: 'no-op', changed: false, before, snapshot: before, error: null };
|
|
21
|
+
const allowed = input.action === 'prepare' ? before.phase === 'unprepared'
|
|
22
|
+
: input.action === 'publish' ? ['prepared', 'partially-published'].includes(before.phase)
|
|
23
|
+
: ['published', 'post-pending'].includes(before.phase);
|
|
24
|
+
if (!allowed)
|
|
25
|
+
return { status: before.phase === 'complete' ? 'no-op' : 'failed', changed: false, before, snapshot: before, error: before.phase === 'complete' ? null : { code: 'RELEASE_PHASE_INVALID', message: `Cannot ${input.action} from ${before.phase}` } };
|
|
26
|
+
await input[input.action]?.();
|
|
27
|
+
const snapshot = releaseSnapshot(input.version, await input.inspect());
|
|
28
|
+
const progressed = snapshot.phase !== before.phase;
|
|
29
|
+
return { status: progressed ? 'applied' : 'blocked', changed: progressed, before, snapshot, error: progressed ? null : { code: 'RELEASE_PROGRESS_PENDING', message: `${input.action} has not reached the next observable phase` } };
|
|
30
|
+
}
|
|
31
|
+
export { deriveReleasePhase, releaseSnapshot, runReleaseAction };
|
|
32
|
+
//# sourceMappingURL=workflow.js.map
|
package/dist/lib/render.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
//
|
|
4
|
-
|
|
3
|
+
// Built-in platform strategies. "none" deliberately selects generic templates
|
|
4
|
+
// and the no-op runtime adapter instead of requiring .none.* variants.
|
|
5
|
+
const KNOWN_PLATFORMS = new Set(['github', 'none']);
|
|
5
6
|
const KNOWN_LANGUAGES = new Set(['en', 'zh-CN']);
|
|
6
7
|
function renderFile(src, dst, replacements) {
|
|
7
8
|
if (!fs.existsSync(src)) {
|
package/dist/lib/run/index.js
CHANGED
|
@@ -7,14 +7,15 @@ import { loadShortIdByTaskId, normalizeShortIdInput } from '../task/short-id.js'
|
|
|
7
7
|
import { buildTuiCommand, renderPrompt, selectTui } from './tui.js';
|
|
8
8
|
import { getSkillRunSpec } from './skills.js';
|
|
9
9
|
import { runHostCommand } from './host.js';
|
|
10
|
-
const USAGE = `Usage: ai run <skill> [task-ref] [args...] [--tui <name>]
|
|
10
|
+
const USAGE = `Usage: ai run <skill> [task-ref] [args...] [--tui <name>] [--recreate]
|
|
11
11
|
|
|
12
12
|
Task skills are scheduled inside the sandbox tmux session; ai run returns once
|
|
13
|
-
the tmux window is created.
|
|
13
|
+
the tmux window is created. --recreate authorizes container-only replacement
|
|
14
|
+
when in-place sandbox recovery fails; it is not included in the TUI prompt.
|
|
14
15
|
|
|
15
16
|
Examples:
|
|
16
17
|
ai run create-task "describe the task" --tui codex
|
|
17
|
-
ai run code-task #7 --tui codex`;
|
|
18
|
+
ai run code-task #7 --tui codex --recreate`;
|
|
18
19
|
function extractTui(args) {
|
|
19
20
|
const rest = [];
|
|
20
21
|
let tui = null;
|
|
@@ -36,12 +37,16 @@ export function parseRunArgs(args) {
|
|
|
36
37
|
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
37
38
|
throw new Error(USAGE);
|
|
38
39
|
}
|
|
39
|
-
const { rest, tui } = extractTui(args);
|
|
40
|
+
const { rest: withRecoveryFlags, tui } = extractTui(args);
|
|
41
|
+
const recreate = withRecoveryFlags.includes('--recreate');
|
|
42
|
+
const rest = withRecoveryFlags.filter((arg) => arg !== '--recreate');
|
|
40
43
|
const [skill = '', maybeTaskRef, ...remaining] = rest;
|
|
41
44
|
const spec = getSkillRunSpec(skill);
|
|
42
45
|
if (!spec)
|
|
43
46
|
throw new Error(`Unknown skill '${skill}'`);
|
|
44
47
|
if (spec.kind === 'create') {
|
|
48
|
+
if (recreate)
|
|
49
|
+
throw new Error('--recreate is only valid for sandbox task runs');
|
|
45
50
|
const createArgs = rest.slice(1);
|
|
46
51
|
if (createArgs.length === 0)
|
|
47
52
|
throw new Error('create-task requires a description');
|
|
@@ -49,7 +54,13 @@ export function parseRunArgs(args) {
|
|
|
49
54
|
}
|
|
50
55
|
if (!maybeTaskRef)
|
|
51
56
|
throw new Error(`${skill} requires a task-ref`);
|
|
52
|
-
return {
|
|
57
|
+
return {
|
|
58
|
+
skill,
|
|
59
|
+
taskRef: maybeTaskRef,
|
|
60
|
+
args: remaining,
|
|
61
|
+
tui,
|
|
62
|
+
...(recreate ? { recreate: true } : {})
|
|
63
|
+
};
|
|
53
64
|
}
|
|
54
65
|
function assertAllowedByConfig(skill, commandConfig) {
|
|
55
66
|
const allowed = commandConfig.allowedSkills;
|
|
@@ -97,7 +108,7 @@ function resolveActiveTaskIdentity(taskRef, repoRoot) {
|
|
|
97
108
|
if (normalized.kind !== 'shortId')
|
|
98
109
|
return null;
|
|
99
110
|
resolvedTaskRef = normalized.value;
|
|
100
|
-
taskId = readActiveShortIdRegistry(repoRoot)[normalized.value
|
|
111
|
+
taskId = readActiveShortIdRegistry(repoRoot)[normalized.value] ?? null;
|
|
101
112
|
}
|
|
102
113
|
if (!taskId)
|
|
103
114
|
return null;
|
|
@@ -161,7 +172,13 @@ export async function runSkill(args, options = {}) {
|
|
|
161
172
|
const writeStderr = options.writeStderr ?? ((chunk) => process.stderr.write(chunk));
|
|
162
173
|
const runSandbox = options.runSandbox ??
|
|
163
174
|
((request) => runInSandbox(request));
|
|
164
|
-
const result = await runSandbox({
|
|
175
|
+
const result = await runSandbox({
|
|
176
|
+
taskRef: parsed.taskRef,
|
|
177
|
+
branch,
|
|
178
|
+
command,
|
|
179
|
+
runId,
|
|
180
|
+
recreate: parsed.recreate
|
|
181
|
+
});
|
|
165
182
|
if (result.stdout) {
|
|
166
183
|
writeStdout(result.stdout);
|
|
167
184
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import semver from 'semver';
|
|
2
|
+
import { engineDisplayName } from './engine.js';
|
|
3
|
+
import { runSafeEngine } from './shell.js';
|
|
4
|
+
export const BUILD_PROXY_ENV_KEYS = ['HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY'];
|
|
5
|
+
export const MIN_BUILD_PROXY_DOCKER_VERSION = '20.10.0';
|
|
6
|
+
export const MIN_BUILD_PROXY_BUILDKIT_VERSION = '0.9.0';
|
|
7
|
+
export function prepareBuildProxy(enabled, hostEnv, engine) {
|
|
8
|
+
const env = { ...hostEnv };
|
|
9
|
+
if (!enabled) {
|
|
10
|
+
return { args: [], env, redactionValues: [] };
|
|
11
|
+
}
|
|
12
|
+
const entries = BUILD_PROXY_ENV_KEYS.flatMap((key) => {
|
|
13
|
+
const value = hostEnv[key]?.trim();
|
|
14
|
+
return value ? [[key, value]] : [];
|
|
15
|
+
});
|
|
16
|
+
if (entries.length === 0) {
|
|
17
|
+
throw new Error(`No non-empty build proxy variables are set. Configure one of: ${BUILD_PROXY_ENV_KEYS.join(', ')}.`);
|
|
18
|
+
}
|
|
19
|
+
for (const [key, value] of entries)
|
|
20
|
+
env[key] = value;
|
|
21
|
+
if (engine === 'wsl2') {
|
|
22
|
+
const existing = (env.WSLENV ?? '').split(':').filter(Boolean);
|
|
23
|
+
env.WSLENV = [...new Set([...existing, ...entries.map(([key]) => key)])].join(':');
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
args: entries.flatMap(([key]) => ['--secret', `id=${key},env=${key}`]),
|
|
27
|
+
env,
|
|
28
|
+
redactionValues: entries.map(([, value]) => value)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function parsedVersion(raw) {
|
|
32
|
+
return semver.coerce(raw.trim())?.version ?? null;
|
|
33
|
+
}
|
|
34
|
+
export function assertBuildProxyCompatibility(engine, runSafeEngineFn = runSafeEngine) {
|
|
35
|
+
const dockerRaw = runSafeEngineFn(engine, 'docker', ['version', '--format', '{{.Server.Version}}']);
|
|
36
|
+
const dockerVersion = parsedVersion(dockerRaw);
|
|
37
|
+
if (!dockerVersion || !semver.gte(dockerVersion, MIN_BUILD_PROXY_DOCKER_VERSION)) {
|
|
38
|
+
throw new Error(`Build proxy requires Docker Engine ${MIN_BUILD_PROXY_DOCKER_VERSION} or newer; `
|
|
39
|
+
+ `observed ${dockerVersion ?? 'an unparseable version'}.`);
|
|
40
|
+
}
|
|
41
|
+
const buildxRaw = runSafeEngineFn(engine, 'docker', ['buildx', 'inspect', '--bootstrap']);
|
|
42
|
+
const versions = [...buildxRaw.matchAll(/BuildKit(?:\s+version)?:\s*v?(\d+\.\d+\.\d+(?:[-+][^\s]+)?)/gi)]
|
|
43
|
+
.map((match) => parsedVersion(match[1] ?? ''))
|
|
44
|
+
.filter((version) => version !== null);
|
|
45
|
+
if (versions.length === 0) {
|
|
46
|
+
throw new Error(`Build proxy requires BuildKit ${MIN_BUILD_PROXY_BUILDKIT_VERSION} or newer, `
|
|
47
|
+
+ 'but the active builder version could not be determined.');
|
|
48
|
+
}
|
|
49
|
+
const oldVersion = versions.find((version) => !semver.gte(version, MIN_BUILD_PROXY_BUILDKIT_VERSION));
|
|
50
|
+
if (oldVersion) {
|
|
51
|
+
throw new Error(`Build proxy observed BuildKit ${oldVersion}; version ${MIN_BUILD_PROXY_BUILDKIT_VERSION} or newer is required.`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function redactBuildProxyValues(text, values) {
|
|
55
|
+
return [...new Set(values.filter(Boolean))]
|
|
56
|
+
.sort((left, right) => right.length - left.length)
|
|
57
|
+
.reduce((result, value) => result.replaceAll(value, '[REDACTED_BUILD_PROXY]'), text);
|
|
58
|
+
}
|
|
59
|
+
export function buildProxyFailureHint(engine) {
|
|
60
|
+
return `Build-step proxy inheritance is enabled for ${engineDisplayName(engine)}. `
|
|
61
|
+
+ 'Image pulls and Dockerfile FROM resolution use the Docker daemon or builder proxy configuration.';
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=build-proxy.js.map
|
|
@@ -3,6 +3,7 @@ import { loadConfig } from './config.js';
|
|
|
3
3
|
import { containerNameCandidates, sandboxBranchLabel, sandboxLabel } from './constants.js';
|
|
4
4
|
import { detectEngine } from './engine.js';
|
|
5
5
|
import { commandForEngine } from './shell.js';
|
|
6
|
+
import { ensureSandboxReady } from './recovery.js';
|
|
6
7
|
import { hostTimezoneEnvFlags, terminalEnvFlags } from './commands/enter.js';
|
|
7
8
|
import { fetchSandboxRows, selectSandboxContainer, startSandboxContainer } from './commands/list-running.js';
|
|
8
9
|
async function spawnCapture(file, args) {
|
|
@@ -131,7 +132,28 @@ export async function runInSandbox(request, options = {}) {
|
|
|
131
132
|
if (!found) {
|
|
132
133
|
throw new Error(`Sandbox for ${request.branch} not found. Create it first with ai sandbox create ${request.taskRef}.`);
|
|
133
134
|
}
|
|
134
|
-
|
|
135
|
+
let container = found.name;
|
|
136
|
+
if (options.ensureReady) {
|
|
137
|
+
container = (await options.ensureReady({
|
|
138
|
+
branch: request.branch,
|
|
139
|
+
row: found,
|
|
140
|
+
allowRecreate: request.recreate ?? false
|
|
141
|
+
})).container;
|
|
142
|
+
}
|
|
143
|
+
else if (config) {
|
|
144
|
+
container = (await ensureSandboxReady({
|
|
145
|
+
config,
|
|
146
|
+
engine,
|
|
147
|
+
branch: request.branch,
|
|
148
|
+
row: found,
|
|
149
|
+
allowRecreate: request.recreate,
|
|
150
|
+
recreate: async (targetBranch) => {
|
|
151
|
+
const { create } = await import('./commands/create.js');
|
|
152
|
+
await create([targetBranch, '--no-refresh']);
|
|
153
|
+
}
|
|
154
|
+
})).container;
|
|
155
|
+
}
|
|
156
|
+
else if (!found.running) {
|
|
135
157
|
(options.startContainer ?? ((name) => startSandboxContainer(engine, name)))(found.name);
|
|
136
158
|
}
|
|
137
159
|
const runId = options.runId ?? createRunId();
|
|
@@ -140,7 +162,7 @@ export async function runInSandbox(request, options = {}) {
|
|
|
140
162
|
'exec',
|
|
141
163
|
...terminalEnvFlags(),
|
|
142
164
|
...hostTimezoneEnvFlags(),
|
|
143
|
-
|
|
165
|
+
container,
|
|
144
166
|
'bash',
|
|
145
167
|
'-lc',
|
|
146
168
|
buildTmuxLauncher({ request, runId })
|
|
@@ -152,7 +174,7 @@ export async function runInSandbox(request, options = {}) {
|
|
|
152
174
|
run: {
|
|
153
175
|
runId,
|
|
154
176
|
engine,
|
|
155
|
-
container
|
|
177
|
+
container,
|
|
156
178
|
runDir
|
|
157
179
|
}
|
|
158
180
|
};
|
|
@@ -10,11 +10,14 @@ import * as toml from 'smol-toml';
|
|
|
10
10
|
import { loadConfig } from '../config.js';
|
|
11
11
|
import { assertValidBranchName, containerName, containerNameCandidates, parsePositiveIntegerOption, sandboxBranchLabel, sandboxImageConfigLabel, sandboxImageRefreshLabel, sandboxLabel, shareBranchDir, shareCommonDir, shellConfigDir, worktreeDirCandidates } from '../constants.js';
|
|
12
12
|
import { prepareDockerfile } from '../dockerfile.js';
|
|
13
|
+
import { assertBuildProxyCompatibility, buildProxyFailureHint, prepareBuildProxy, redactBuildProxyValues } from '../build-proxy.js';
|
|
13
14
|
import { detectEngine, ensureDocker } from '../engine.js';
|
|
14
15
|
import { commandForEngine, execEngine, run, runEngine, runOk, runOkEngine, runSafe, runSafeEngine, runVerboseEngine } from '../shell.js';
|
|
15
16
|
import { resolveTaskBranch } from '../task-resolver.js';
|
|
16
|
-
import { resolveTools, toolConfigDirCandidates } from '../tools.js';
|
|
17
|
+
import { resolveTools, tmpfsSeedStagingPath, tmpfsSeedTargetPath, toolConfigDirCandidates } from '../tools.js';
|
|
18
|
+
import { assertFreshSandboxReady, hydrateTmpfsSeedEntries, prepareTmpfsMounts } from '../recovery.js';
|
|
17
19
|
import { hostJoin, toEnginePath, volumeArg } from '../engines/wsl2-paths.js';
|
|
20
|
+
import { sandboxCoreBindMounts } from '../mounts.js';
|
|
18
21
|
import { clipboardHostDir, CONTAINER_CLIPBOARD_MOUNT } from '../clipboard/paths.js';
|
|
19
22
|
import { validateSelinuxDisableEnv } from '../engines/selinux.js';
|
|
20
23
|
import { dotfilesCacheDir, materializeDotfiles } from '../dotfiles.js';
|
|
@@ -47,7 +50,7 @@ alias gy='gemini --yolo; tput ed'
|
|
|
47
50
|
`;
|
|
48
51
|
const CONTAINER_HOME = '/home/devuser';
|
|
49
52
|
const CONTAINER_SHELL_CONFIG_MOUNT = `${CONTAINER_HOME}/.host-shell-config`;
|
|
50
|
-
const USAGE = `Usage: ai sandbox create <branch> [base] [--cpu <n>] [--memory <n>] [--no-refresh] [--inherit-proxy|-P]
|
|
53
|
+
const USAGE = `Usage: ai sandbox create <branch> [base] [--cpu <n>] [--memory <n>] [--no-refresh] [--inherit-proxy|-P] [--inherit-build-proxy|-B]
|
|
51
54
|
|
|
52
55
|
Host aliases:
|
|
53
56
|
${'~'}/.agent-infra/aliases/sandbox.sh is auto-created on first run and exposed
|
|
@@ -57,7 +60,9 @@ Host aliases:
|
|
|
57
60
|
|
|
58
61
|
Proxy:
|
|
59
62
|
--inherit-proxy, -P Copy non-empty standard host proxy variables into the
|
|
60
|
-
container environment through the private env file
|
|
63
|
+
container environment through the private env file.
|
|
64
|
+
--inherit-build-proxy, -B Pass uppercase HTTP_PROXY, HTTPS_PROXY, and NO_PROXY
|
|
65
|
+
to managed image build steps for this invocation.`;
|
|
61
66
|
const HOST_PROXY_ENV_KEYS = [
|
|
62
67
|
'http_proxy',
|
|
63
68
|
'HTTP_PROXY',
|
|
@@ -68,6 +73,15 @@ const HOST_PROXY_ENV_KEYS = [
|
|
|
68
73
|
'no_proxy',
|
|
69
74
|
'NO_PROXY'
|
|
70
75
|
];
|
|
76
|
+
const MANAGED_GITHUB_GIT_CONFIG = [
|
|
77
|
+
'[credential]',
|
|
78
|
+
'\thelper = !gh auth git-credential',
|
|
79
|
+
'[credential "https://github.com"]',
|
|
80
|
+
'\thelper =',
|
|
81
|
+
'\thelper = !gh auth git-credential',
|
|
82
|
+
'[url "https://github.com/"]',
|
|
83
|
+
'\tinsteadOf = git@github.com:'
|
|
84
|
+
];
|
|
71
85
|
function resolveToolDirs(config, tools, branch) {
|
|
72
86
|
return tools.map((tool) => {
|
|
73
87
|
const candidates = toolConfigDirCandidates(tool, config.project, branch);
|
|
@@ -156,6 +170,14 @@ function normalizeContainerHomeSeparators(content) {
|
|
|
156
170
|
const containerHomePattern = new RegExp(`${escapeRegExp(CONTAINER_HOME)}\\S*`, 'g');
|
|
157
171
|
return content.replace(containerHomePattern, (value) => value.replaceAll('\\', '/'));
|
|
158
172
|
}
|
|
173
|
+
function isGitHubCliCredentialHelper(line) {
|
|
174
|
+
const helperMatch = line.match(/^\s*helper\s*=\s*(.+?)\s*$/i);
|
|
175
|
+
if (!helperMatch) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
const helper = (helperMatch[1] ?? '').trim().replace(/^"(.*)"$/, '$1');
|
|
179
|
+
return /^!\s*(?:"[^"]*[\\/]+gh"|(?:\S*[\\/]+)?gh)\s+auth\s+git-credential(?:\s|$)/i.test(helper);
|
|
180
|
+
}
|
|
159
181
|
export function sanitizeGitConfig(gitconfig, home, { stripGpg = false, repoRoot = '' } = {}) {
|
|
160
182
|
const posixHome = home.replaceAll('\\', '/');
|
|
161
183
|
const normalizedGitconfig = gitconfig
|
|
@@ -168,10 +190,12 @@ export function sanitizeGitConfig(gitconfig, home, { stripGpg = false, repoRoot
|
|
|
168
190
|
const sanitized = [];
|
|
169
191
|
let inGpgSection = false;
|
|
170
192
|
let currentSection = '';
|
|
193
|
+
let currentSectionName = '';
|
|
171
194
|
for (const line of lines) {
|
|
172
195
|
const sectionMatch = line.match(/^\s*\[([^\]]+)\]\s*$/);
|
|
173
196
|
if (sectionMatch) {
|
|
174
197
|
const sectionName = (sectionMatch[1] ?? '').trim();
|
|
198
|
+
currentSectionName = sectionName;
|
|
175
199
|
currentSection = ((sectionName.match(/^([^\s"]+)/)?.[1]) ?? '').toLowerCase();
|
|
176
200
|
inGpgSection = /^gpg(?:\s+"[^"]+")?$/i.test(sectionName);
|
|
177
201
|
if (stripGpg && inGpgSection) {
|
|
@@ -197,9 +221,19 @@ export function sanitizeGitConfig(gitconfig, home, { stripGpg = false, repoRoot
|
|
|
197
221
|
if (stripGpg && currentSection === 'user' && /^\s*signingKey\s*=.*$/i.test(line)) {
|
|
198
222
|
continue;
|
|
199
223
|
}
|
|
224
|
+
if (currentSection === 'credential' && isGitHubCliCredentialHelper(line)) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (/^url\s+"https:\/\/github\.com\/"$/i.test(currentSectionName)
|
|
228
|
+
&& /^\s*insteadOf\s*=\s*git@github\.com:\s*$/i.test(line)) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
200
231
|
sanitized.push(line);
|
|
201
232
|
}
|
|
202
|
-
return
|
|
233
|
+
return [
|
|
234
|
+
...appendSafeDirectories(sanitized, repoRoot),
|
|
235
|
+
...MANAGED_GITHUB_GIT_CONFIG
|
|
236
|
+
].join('\n');
|
|
203
237
|
}
|
|
204
238
|
export function hostHasGpgKeys(home, execFn = execFileSync) {
|
|
205
239
|
return currentKeyringFingerprint(home, execFn) !== null;
|
|
@@ -926,7 +960,7 @@ export function buildTmpfsRunArgs(containerMount, tmpfs) {
|
|
|
926
960
|
const size = tmpfs.size ?? '512m';
|
|
927
961
|
return ['--tmpfs', `${containerMount}:rw,size=${size}`];
|
|
928
962
|
}
|
|
929
|
-
export function buildImage(config, tools, dockerfilePath, imageSignature, { engine, runFn = runEngine, runSafeFn = runSafeEngine, runVerboseFn = runVerboseEngine, env = process.env, refresh = false, lastRefresh } = {}) {
|
|
963
|
+
export function buildImage(config, tools, dockerfilePath, imageSignature, { engine, runFn = runEngine, runSafeFn = runSafeEngine, runVerboseFn = runVerboseEngine, env = process.env, refresh = false, lastRefresh, buildProxyArgs = [], buildEnv } = {}) {
|
|
930
964
|
const selectedEngine = engine ?? detectEngine({ engine: config.engine });
|
|
931
965
|
runVerboseFn(selectedEngine, 'docker', buildSandboxImageArgs(config, tools, dockerfilePath, imageSignature, {
|
|
932
966
|
engine: selectedEngine,
|
|
@@ -934,8 +968,9 @@ export function buildImage(config, tools, dockerfilePath, imageSignature, { engi
|
|
|
934
968
|
runSafeFn,
|
|
935
969
|
env,
|
|
936
970
|
refresh,
|
|
937
|
-
lastRefresh
|
|
938
|
-
|
|
971
|
+
lastRefresh,
|
|
972
|
+
buildProxyArgs
|
|
973
|
+
}), { cwd: config.repoRoot, env: buildEnv });
|
|
939
974
|
}
|
|
940
975
|
function readImageLabels(config, engine) {
|
|
941
976
|
return parseImageLabels(runSafeEngine(engine, 'docker', [
|
|
@@ -956,6 +991,7 @@ export async function create(args) {
|
|
|
956
991
|
memory: { type: 'string' },
|
|
957
992
|
'no-refresh': { type: 'boolean' },
|
|
958
993
|
'inherit-proxy': { type: 'boolean', short: 'P' },
|
|
994
|
+
'inherit-build-proxy': { type: 'boolean', short: 'B' },
|
|
959
995
|
help: { type: 'boolean', short: 'h' }
|
|
960
996
|
}
|
|
961
997
|
});
|
|
@@ -999,6 +1035,7 @@ export async function create(args) {
|
|
|
999
1035
|
const baseBranch = base ?? runSafe('git', ['-C', effectiveConfig.repoRoot, 'branch', '--show-current']);
|
|
1000
1036
|
const expectedImageSignature = buildImageSignature(preparedDockerfile, tools);
|
|
1001
1037
|
const engine = detectEngine(effectiveConfig);
|
|
1038
|
+
let createdTmpfsSeedPlan = [];
|
|
1002
1039
|
p.intro(pc.cyan('AI Sandbox'));
|
|
1003
1040
|
p.log.info(`Project: ${pc.bold(effectiveConfig.project)} | Branch: ${pc.bold(branch)} | Base: ${pc.bold(baseBranch || 'HEAD')}`);
|
|
1004
1041
|
if (credentialOutcome.status === 'SKIPPED') {
|
|
@@ -1024,6 +1061,12 @@ export async function create(args) {
|
|
|
1024
1061
|
&& isRefreshDue(currentLastRefresh, now, effectiveConfig.refreshIntervalDays);
|
|
1025
1062
|
const needsImageBuild = signatureStale || refreshDue;
|
|
1026
1063
|
if (needsImageBuild) {
|
|
1064
|
+
if (values['inherit-build-proxy'] && effectiveConfig.dockerfile) {
|
|
1065
|
+
throw new Error('Build proxy inheritance is unavailable with a custom sandbox Dockerfile.');
|
|
1066
|
+
}
|
|
1067
|
+
const buildProxy = prepareBuildProxy(values['inherit-build-proxy'] ?? false, process.env, engine);
|
|
1068
|
+
if (values['inherit-build-proxy'])
|
|
1069
|
+
assertBuildProxyCompatibility(engine);
|
|
1027
1070
|
const buildRefresh = !imageExists || refreshDue;
|
|
1028
1071
|
const buildLastRefresh = buildRefresh ? now : currentLastRefresh || 0;
|
|
1029
1072
|
p.log.step(refreshDue
|
|
@@ -1032,7 +1075,13 @@ export async function create(args) {
|
|
|
1032
1075
|
? 'Rebuilding stale image...'
|
|
1033
1076
|
: 'Building image for first use...');
|
|
1034
1077
|
try {
|
|
1035
|
-
buildImage(effectiveConfig, tools, preparedDockerfile.path, expectedImageSignature, {
|
|
1078
|
+
buildImage(effectiveConfig, tools, preparedDockerfile.path, expectedImageSignature, {
|
|
1079
|
+
engine,
|
|
1080
|
+
refresh: buildRefresh,
|
|
1081
|
+
lastRefresh: buildLastRefresh,
|
|
1082
|
+
buildProxyArgs: buildProxy.args,
|
|
1083
|
+
buildEnv: buildProxy.env
|
|
1084
|
+
});
|
|
1036
1085
|
p.log.success(refreshDue
|
|
1037
1086
|
? 'Image refreshed'
|
|
1038
1087
|
: imageExists
|
|
@@ -1042,10 +1091,12 @@ export async function create(args) {
|
|
|
1042
1091
|
catch (error) {
|
|
1043
1092
|
if (refreshDue && !signatureStale && imageExists) {
|
|
1044
1093
|
p.log.warn('Scheduled sandbox image refresh failed; continuing with the existing image. ' +
|
|
1045
|
-
commandErrorMessage(error))
|
|
1094
|
+
redactBuildProxyValues(commandErrorMessage(error), buildProxy.redactionValues)
|
|
1095
|
+
+ (values['inherit-build-proxy'] ? ` ${buildProxyFailureHint(engine)}` : ''));
|
|
1046
1096
|
}
|
|
1047
1097
|
else {
|
|
1048
|
-
|
|
1098
|
+
const hint = values['inherit-build-proxy'] ? `\n${buildProxyFailureHint(engine)}` : '';
|
|
1099
|
+
throw new Error(`${redactBuildProxyValues(commandErrorMessage(error), buildProxy.redactionValues)}${hint}`);
|
|
1049
1100
|
}
|
|
1050
1101
|
}
|
|
1051
1102
|
}
|
|
@@ -1209,9 +1260,12 @@ export async function create(args) {
|
|
|
1209
1260
|
branch,
|
|
1210
1261
|
repoRoot: effectiveConfig.repoRoot
|
|
1211
1262
|
});
|
|
1212
|
-
const
|
|
1263
|
+
const coreVolumes = sandboxCoreBindMounts(effectiveConfig, branch, {
|
|
1264
|
+
worktree,
|
|
1265
|
+
shellConfigHostDir: hostShellConfig.hostDir
|
|
1266
|
+
}).flatMap(({ hostPaths, containerPath, readOnly }) => [
|
|
1213
1267
|
'-v',
|
|
1214
|
-
volumeArg(engine,
|
|
1268
|
+
volumeArg(engine, hostPaths[0], containerPath, readOnly ? ':ro' : '')
|
|
1215
1269
|
]);
|
|
1216
1270
|
// Mount each declared seed read-only at an isolated staging path.
|
|
1217
1271
|
// After docker run mounts the empty tmpfs, the container's default
|
|
@@ -1220,14 +1274,17 @@ export async function create(args) {
|
|
|
1220
1274
|
// stale runtime files (logs_2.sqlite, sessions, etc.) on the host from
|
|
1221
1275
|
// being exposed or written through.
|
|
1222
1276
|
tmpfsSeedPlan = effectiveResolvedTools.flatMap(({ tool, dir }) => (tool.tmpfs?.seed ?? []).flatMap((entry, index) => {
|
|
1277
|
+
const targetPath = tmpfsSeedTargetPath(tool.containerMount, entry);
|
|
1223
1278
|
const hostPath = path.join(dir, entry);
|
|
1224
1279
|
if (!fs.existsSync(hostPath)) {
|
|
1225
1280
|
return [];
|
|
1226
1281
|
}
|
|
1227
|
-
const stagingPath =
|
|
1282
|
+
const stagingPath = tmpfsSeedStagingPath(tool.id, index);
|
|
1228
1283
|
return [{
|
|
1284
|
+
toolId: tool.id,
|
|
1285
|
+
containerMount: tool.containerMount,
|
|
1229
1286
|
stagingPath,
|
|
1230
|
-
targetPath
|
|
1287
|
+
targetPath,
|
|
1231
1288
|
volumeArgs: ['-v', volumeArg(engine, hostPath, stagingPath, ':ro')]
|
|
1232
1289
|
}];
|
|
1233
1290
|
}));
|
|
@@ -1258,14 +1315,7 @@ export async function create(args) {
|
|
|
1258
1315
|
sandboxLabel(effectiveConfig),
|
|
1259
1316
|
'--label',
|
|
1260
1317
|
`${sandboxBranchLabel(effectiveConfig)}=${branch}`,
|
|
1261
|
-
|
|
1262
|
-
volumeArg(engine, worktree, '/workspace'),
|
|
1263
|
-
'-v',
|
|
1264
|
-
volumeArg(engine, workspaceDir, '/workspace/.agents/workspace'),
|
|
1265
|
-
'-v',
|
|
1266
|
-
volumeArg(engine, shareCommon, '/share/common'),
|
|
1267
|
-
'-v',
|
|
1268
|
-
volumeArg(engine, shareBranch, '/share/branch'),
|
|
1318
|
+
...coreVolumes,
|
|
1269
1319
|
...buildClipboardVolumeArgs(engine, effectiveConfig.home),
|
|
1270
1320
|
'-v',
|
|
1271
1321
|
volumeArg(engine, path.join(effectiveConfig.repoRoot, '.git'), `${toEnginePath(engine, effectiveConfig.repoRoot)}/.git`),
|
|
@@ -1274,25 +1324,30 @@ export async function create(args) {
|
|
|
1274
1324
|
...tmpfsArgs,
|
|
1275
1325
|
...tmpfsSeedPlan.flatMap(({ volumeArgs }) => volumeArgs),
|
|
1276
1326
|
...liveMountVolumes,
|
|
1277
|
-
...shellConfigVolumes,
|
|
1278
1327
|
...envFile.dockerArgs,
|
|
1279
1328
|
...tzFlags,
|
|
1280
1329
|
'-w',
|
|
1281
1330
|
'/workspace',
|
|
1282
1331
|
effectiveConfig.imageName
|
|
1283
1332
|
]);
|
|
1333
|
+
createdTmpfsSeedPlan = tmpfsSeedPlan;
|
|
1284
1334
|
}
|
|
1285
1335
|
finally {
|
|
1286
1336
|
envFile.cleanup();
|
|
1287
1337
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1338
|
+
prepareTmpfsMounts({
|
|
1339
|
+
engine,
|
|
1340
|
+
container,
|
|
1341
|
+
mountPaths: effectiveResolvedTools
|
|
1342
|
+
.filter(({ tool }) => tool.tmpfs)
|
|
1343
|
+
.map(({ tool }) => tool.containerMount)
|
|
1344
|
+
});
|
|
1345
|
+
hydrateTmpfsSeedEntries({
|
|
1346
|
+
engine,
|
|
1347
|
+
container,
|
|
1348
|
+
entries: tmpfsSeedPlan,
|
|
1349
|
+
replace: true
|
|
1350
|
+
});
|
|
1296
1351
|
// Belt-and-suspenders: re-create the four shell-config symlinks at
|
|
1297
1352
|
// runtime so users with a custom `sandbox.dockerfile` (which won't
|
|
1298
1353
|
// include the ai-tools.dockerfile symlink fragment) still get
|
|
@@ -1346,6 +1401,13 @@ export async function create(args) {
|
|
|
1346
1401
|
preparedDockerfile.cleanup();
|
|
1347
1402
|
}
|
|
1348
1403
|
p.log.step('Verifying setup...');
|
|
1404
|
+
assertFreshSandboxReady({
|
|
1405
|
+
config: effectiveConfig,
|
|
1406
|
+
engine,
|
|
1407
|
+
branch,
|
|
1408
|
+
container,
|
|
1409
|
+
copiedEntries: createdTmpfsSeedPlan
|
|
1410
|
+
});
|
|
1349
1411
|
const runningContainers = runSafeEngine(engine, 'docker', ['ps', '--format', '{{.Names}}']).split('\n');
|
|
1350
1412
|
const checks = [
|
|
1351
1413
|
{ name: 'Container running', ok: runningContainers.includes(container) },
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { loadConfig } from '../config.js';
|
|
2
2
|
import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel } from '../constants.js';
|
|
3
3
|
import { detectEngine } from '../engine.js';
|
|
4
|
+
import { ensureSandboxReady } from '../recovery.js';
|
|
4
5
|
import { formatCredentialWarnings, formatRemaining, hasClaudeProviderAuth, reconcileClaudeCredentials, redactCommandError, validateClaudeCredentialsEnvOverride } from '../credentials.js';
|
|
5
6
|
import { runInteractiveEngine } from '../shell.js';
|
|
6
7
|
import { dotfilesCacheDir, materializeDotfiles } from '../dotfiles.js';
|
|
7
8
|
import { runInteractiveWithClipboardBridge } from '../clipboard/bridge.js';
|
|
8
9
|
import { detectHostTimezone } from '../host-timezone.js';
|
|
9
|
-
import { fetchSandboxRows, resolveBranchArg, selectSandboxContainer,
|
|
10
|
-
const USAGE = `Usage: ai sandbox exec <branch | TASK-id | N
|
|
10
|
+
import { fetchSandboxRows, resolveBranchArg, selectSandboxContainer, } from './list-running.js';
|
|
11
|
+
const USAGE = `Usage: ai sandbox exec [--recreate] <branch | TASK-id | N> [cmd...]
|
|
11
12
|
|
|
12
|
-
N
|
|
13
|
+
N references an active task short id from
|
|
13
14
|
.agents/workspace/active/.short-ids.json. They resolve only via that
|
|
14
15
|
registry — they do not reference a container's row position in
|
|
15
|
-
'ai sandbox ls' output
|
|
16
|
+
'ai sandbox ls' output. --recreate is a host recovery flag only before the
|
|
17
|
+
target; the same token after the target is passed to the container command.`;
|
|
16
18
|
const TMUX_ENTRY_PATH = '/usr/local/bin/sandbox-tmux-entry';
|
|
17
19
|
// Terminal-detection variables that interactive TUIs (e.g. claude-code)
|
|
18
20
|
// inspect to enable progressive enhancements such as the kitty keyboard
|
|
@@ -76,6 +78,19 @@ export function formatCredentialSyncStatus(result, isTTY = process.stderr.isTTY,
|
|
|
76
78
|
}
|
|
77
79
|
return null;
|
|
78
80
|
}
|
|
81
|
+
export function parseEnterArgs(args) {
|
|
82
|
+
let recreate = false;
|
|
83
|
+
let index = 0;
|
|
84
|
+
while (args[index] === '--recreate') {
|
|
85
|
+
recreate = true;
|
|
86
|
+
index += 1;
|
|
87
|
+
}
|
|
88
|
+
const target = args[index] ?? '';
|
|
89
|
+
if (!target) {
|
|
90
|
+
throw new Error(USAGE);
|
|
91
|
+
}
|
|
92
|
+
return { target, command: args.slice(index + 1), recreate };
|
|
93
|
+
}
|
|
79
94
|
export async function enter(args) {
|
|
80
95
|
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
81
96
|
process.stdout.write(`${USAGE}\n`);
|
|
@@ -87,19 +102,27 @@ export async function enter(args) {
|
|
|
87
102
|
const config = loadConfig();
|
|
88
103
|
validateClaudeCredentialsEnvOverride();
|
|
89
104
|
const engine = detectEngine(config);
|
|
90
|
-
const
|
|
91
|
-
const branch = resolveBranchArg(
|
|
105
|
+
const parsed = parseEnterArgs(args);
|
|
106
|
+
const branch = resolveBranchArg(parsed.target, { repoRoot: config.repoRoot });
|
|
92
107
|
assertValidBranchName(branch);
|
|
93
108
|
const { running, nonRunning } = fetchSandboxRows(engine, sandboxLabel(config), sandboxBranchLabel(config));
|
|
94
109
|
const found = selectSandboxContainer([...running, ...nonRunning], containerNameCandidates(config, branch));
|
|
95
110
|
if (!found) {
|
|
96
111
|
throw new Error(`No sandbox found for branch '${branch}'. Run 'ai sandbox create ${branch}' to create one.`);
|
|
97
112
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
113
|
+
const ready = await ensureSandboxReady({
|
|
114
|
+
config,
|
|
115
|
+
engine,
|
|
116
|
+
branch,
|
|
117
|
+
row: found,
|
|
118
|
+
allowRecreate: parsed.recreate,
|
|
119
|
+
recreate: async (targetBranch) => {
|
|
120
|
+
const { create } = await import('./create.js');
|
|
121
|
+
await create([targetBranch, '--no-refresh']);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
const container = ready.container;
|
|
125
|
+
const cmd = parsed.command;
|
|
103
126
|
if (config.tools.includes('claude-code')) {
|
|
104
127
|
try {
|
|
105
128
|
// Scan all projects so a refresh from a neighbouring sandbox can still flow back to the host.
|