@nathapp/nax-agent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +49 -0
- package/dist/coding-tools/coding-tool-bash.d.ts +46 -0
- package/dist/coding-tools/coding-tool-bash.js +44 -0
- package/dist/coding-tools/coding-tool-extras.d.ts +35 -0
- package/dist/coding-tools/coding-tool-extras.js +56 -0
- package/dist/coding-tools/coding-tool-sandbox.d.ts +74 -0
- package/dist/coding-tools/coding-tool-sandbox.js +215 -0
- package/dist/coding-tools/coding-tool-support.d.ts +132 -0
- package/dist/coding-tools/coding-tool-support.js +159 -0
- package/dist/coding-tools/universal-coding-tools.d.ts +11 -0
- package/dist/coding-tools/universal-coding-tools.js +5 -0
- package/dist/command-interceptor/index.d.ts +55 -0
- package/dist/command-interceptor/index.js +60 -0
- package/dist/command-interceptor/shell.d.ts +36 -0
- package/dist/command-interceptor/shell.js +137 -0
- package/dist/command-safety/build.d.ts +19 -0
- package/dist/command-safety/build.js +47 -0
- package/dist/command-safety/guard.d.ts +32 -0
- package/dist/command-safety/guard.js +179 -0
- package/dist/command-safety/identifiers.d.ts +24 -0
- package/dist/command-safety/identifiers.js +20 -0
- package/dist/command-safety/index.d.ts +17 -0
- package/dist/command-safety/index.js +11 -0
- package/dist/command-safety/questions.d.ts +19 -0
- package/dist/command-safety/questions.js +60 -0
- package/dist/command-safety/row.d.ts +2 -0
- package/dist/command-safety/row.js +14 -0
- package/dist/command-safety/rule-scorer.d.ts +9 -0
- package/dist/command-safety/rule-scorer.js +391 -0
- package/dist/command-safety/shadow.d.ts +33 -0
- package/dist/command-safety/shadow.js +188 -0
- package/dist/command-safety/systemone-client.d.ts +16 -0
- package/dist/command-safety/systemone-client.js +92 -0
- package/dist/command-safety/tap.d.ts +37 -0
- package/dist/command-safety/tap.js +77 -0
- package/dist/command-safety/temp-only.d.ts +6 -0
- package/dist/command-safety/temp-only.js +215 -0
- package/dist/command-safety/tmp-write.d.ts +2 -0
- package/dist/command-safety/tmp-write.js +112 -0
- package/dist/command-safety/types.d.ts +185 -0
- package/dist/command-safety/types.js +21 -0
- package/dist/config/bash-approval.d.ts +33 -0
- package/dist/config/bash-approval.js +30 -0
- package/dist/config/catalog-overrides.d.ts +94 -0
- package/dist/config/catalog-overrides.js +7 -0
- package/dist/config/native-agent/index.d.ts +8 -0
- package/dist/config/native-agent/index.js +8 -0
- package/dist/config/schemas-sandbox.d.ts +31 -0
- package/dist/config/schemas-sandbox.js +45 -0
- package/dist/cost/core/index.d.ts +8 -0
- package/dist/cost/core/index.js +7 -0
- package/dist/cost/estimate.d.ts +34 -0
- package/dist/cost/estimate.js +65 -0
- package/dist/cost/model-spec.d.ts +28 -0
- package/dist/cost/model-spec.js +29 -0
- package/dist/cost/standard-types.d.ts +9 -0
- package/dist/cost/standard-types.js +1 -0
- package/dist/cost/usage-math.d.ts +18 -0
- package/dist/cost/usage-math.js +46 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +21 -0
- package/dist/infra/agent-logger.d.ts +22 -0
- package/dist/infra/agent-logger.js +19 -0
- package/dist/infra/credentials-config.d.ts +20 -0
- package/dist/infra/credentials-config.js +15 -0
- package/dist/infra/errors.d.ts +9 -0
- package/dist/infra/errors.js +11 -0
- package/dist/infra/index.d.ts +7 -0
- package/dist/infra/index.js +7 -0
- package/dist/infra/nax-error.d.ts +10 -0
- package/dist/infra/nax-error.js +16 -0
- package/dist/infra/spin-breaker/hash.d.ts +2 -0
- package/dist/infra/spin-breaker/hash.js +5 -0
- package/dist/infra/spin-breaker/index.d.ts +120 -0
- package/dist/infra/spin-breaker/index.js +340 -0
- package/dist/internal/agent-output-env.d.ts +44 -0
- package/dist/internal/agent-output-env.js +57 -0
- package/dist/internal/argv-exec.d.ts +58 -0
- package/dist/internal/argv-exec.js +224 -0
- package/dist/internal/bounded-io.d.ts +24 -0
- package/dist/internal/bounded-io.js +51 -0
- package/dist/internal/command-spec/index.d.ts +29 -0
- package/dist/internal/command-spec/index.js +36 -0
- package/dist/internal/describe-value-type.d.ts +8 -0
- package/dist/internal/describe-value-type.js +14 -0
- package/dist/internal/exec-framing.d.ts +18 -0
- package/dist/internal/exec-framing.js +22 -0
- package/dist/internal/file-lock.d.ts +91 -0
- package/dist/internal/file-lock.js +212 -0
- package/dist/internal/file-size.d.ts +2 -0
- package/dist/internal/file-size.js +12 -0
- package/dist/internal/git-add.d.ts +43 -0
- package/dist/internal/git-add.js +59 -0
- package/dist/internal/git-env.d.ts +59 -0
- package/dist/internal/git-env.js +93 -0
- package/dist/internal/git-exec.d.ts +58 -0
- package/dist/internal/git-exec.js +115 -0
- package/dist/internal/path-file-lock.d.ts +23 -0
- package/dist/internal/path-file-lock.js +25 -0
- package/dist/internal/process-alive.d.ts +42 -0
- package/dist/internal/process-alive.js +56 -0
- package/dist/internal/process-kill.d.ts +27 -0
- package/dist/internal/process-kill.js +54 -0
- package/dist/internal/realpath.d.ts +29 -0
- package/dist/internal/realpath.js +52 -0
- package/dist/internal/redact.d.ts +40 -0
- package/dist/internal/redact.js +161 -0
- package/dist/internal/shell-quote.d.ts +2 -0
- package/dist/internal/shell-quote.js +4 -0
- package/dist/internal/sort.d.ts +20 -0
- package/dist/internal/sort.js +20 -0
- package/dist/internal/strip-control-chars.d.ts +19 -0
- package/dist/internal/strip-control-chars.js +38 -0
- package/dist/internal/thenable.d.ts +10 -0
- package/dist/internal/thenable.js +15 -0
- package/dist/internal.d.ts +91 -0
- package/dist/internal.js +91 -0
- package/dist/native/adapter-deps.d.ts +46 -0
- package/dist/native/adapter-deps.js +43 -0
- package/dist/native/auth-types.d.ts +70 -0
- package/dist/native/auth-types.js +13 -0
- package/dist/native/auth.d.ts +133 -0
- package/dist/native/auth.js +362 -0
- package/dist/native/client.d.ts +50 -0
- package/dist/native/client.js +174 -0
- package/dist/native/complete.d.ts +33 -0
- package/dist/native/complete.js +88 -0
- package/dist/native/credentials/chained-store.d.ts +30 -0
- package/dist/native/credentials/chained-store.js +68 -0
- package/dist/native/credentials/change-guard.d.ts +51 -0
- package/dist/native/credentials/change-guard.js +136 -0
- package/dist/native/credentials/exec-source.d.ts +50 -0
- package/dist/native/credentials/exec-source.js +222 -0
- package/dist/native/credentials/fingerprint.d.ts +26 -0
- package/dist/native/credentials/fingerprint.js +174 -0
- package/dist/native/credentials/helper-process.d.ts +79 -0
- package/dist/native/credentials/helper-process.js +259 -0
- package/dist/native/credentials/helper-protocol.d.ts +36 -0
- package/dist/native/credentials/helper-protocol.js +79 -0
- package/dist/native/credentials/index.d.ts +60 -0
- package/dist/native/credentials/index.js +184 -0
- package/dist/native/errors.d.ts +34 -0
- package/dist/native/errors.js +109 -0
- package/dist/native/index.d.ts +25 -0
- package/dist/native/index.js +23 -0
- package/dist/native/model-resolver.d.ts +57 -0
- package/dist/native/model-resolver.js +73 -0
- package/dist/native/models.d.ts +116 -0
- package/dist/native/models.js +183 -0
- package/dist/native/session/ask-human.d.ts +15 -0
- package/dist/native/session/ask-human.js +22 -0
- package/dist/native/session/compaction.d.ts +94 -0
- package/dist/native/session/compaction.js +170 -0
- package/dist/native/session/handle-invalid-tool-call.d.ts +111 -0
- package/dist/native/session/handle-invalid-tool-call.js +159 -0
- package/dist/native/session/loop-events/cache-boundary.d.ts +11 -0
- package/dist/native/session/loop-events/cache-boundary.js +29 -0
- package/dist/native/session/loop-events/external-handler.d.ts +38 -0
- package/dist/native/session/loop-events/external-handler.js +193 -0
- package/dist/native/session/loop-events/index.d.ts +16 -0
- package/dist/native/session/loop-events/index.js +15 -0
- package/dist/native/session/loop-events/payload-guard.d.ts +9 -0
- package/dist/native/session/loop-events/payload-guard.js +31 -0
- package/dist/native/session/loop-events/registry.d.ts +84 -0
- package/dist/native/session/loop-events/registry.js +305 -0
- package/dist/native/session/loop-events/types.d.ts +251 -0
- package/dist/native/session/loop-events/types.js +14 -0
- package/dist/native/session/loop-handlers.d.ts +73 -0
- package/dist/native/session/loop-handlers.js +137 -0
- package/dist/native/session/nudge.d.ts +29 -0
- package/dist/native/session/nudge.js +35 -0
- package/dist/native/session/rate-provenance.d.ts +17 -0
- package/dist/native/session/rate-provenance.js +39 -0
- package/dist/native/session/session.d.ts +139 -0
- package/dist/native/session/session.js +208 -0
- package/dist/native/session/tool-input-exemplar.d.ts +2 -0
- package/dist/native/session/tool-input-exemplar.js +66 -0
- package/dist/native/session/tool-input-validation.d.ts +15 -0
- package/dist/native/session/tool-input-validation.js +146 -0
- package/dist/native/session/tool-mapping.d.ts +18 -0
- package/dist/native/session/tool-mapping.js +27 -0
- package/dist/native/session/tool-result.d.ts +37 -0
- package/dist/native/session/tool-result.js +25 -0
- package/dist/native/session/transcript-store.d.ts +77 -0
- package/dist/native/session/transcript-store.js +200 -0
- package/dist/native/session/truncation-handler.d.ts +47 -0
- package/dist/native/session/truncation-handler.js +83 -0
- package/dist/native/session/turn-accumulator.d.ts +45 -0
- package/dist/native/session/turn-accumulator.js +67 -0
- package/dist/native/session/turn-ask-human.d.ts +23 -0
- package/dist/native/session/turn-ask-human.js +41 -0
- package/dist/native/session/turn-compaction-step.d.ts +74 -0
- package/dist/native/session/turn-compaction-step.js +128 -0
- package/dist/native/session/turn-complete-step.d.ts +77 -0
- package/dist/native/session/turn-complete-step.js +159 -0
- package/dist/native/session/turn-end-event.d.ts +3 -0
- package/dist/native/session/turn-end-event.js +17 -0
- package/dist/native/session/turn-events.d.ts +63 -0
- package/dist/native/session/turn-events.js +48 -0
- package/dist/native/session/turn-loop-round-trip.d.ts +116 -0
- package/dist/native/session/turn-loop-round-trip.js +318 -0
- package/dist/native/session/turn-loop.d.ts +23 -0
- package/dist/native/session/turn-loop.js +250 -0
- package/dist/native/session/turn-result.d.ts +39 -0
- package/dist/native/session/turn-result.js +64 -0
- package/dist/native/session/turn-retry.d.ts +120 -0
- package/dist/native/session/turn-retry.js +182 -0
- package/dist/native/session/turn-tool-batch.d.ts +61 -0
- package/dist/native/session/turn-tool-batch.js +259 -0
- package/dist/native/session/turn-types.d.ts +164 -0
- package/dist/native/session/turn-types.js +36 -0
- package/dist/native/session-adapter.d.ts +56 -0
- package/dist/native/session-adapter.js +369 -0
- package/dist/native/session-affinity.d.ts +36 -0
- package/dist/native/session-affinity.js +43 -0
- package/dist/native/tier-providers.d.ts +18 -0
- package/dist/native/tier-providers.js +20 -0
- package/dist/permissions/approval-audit.d.ts +10 -0
- package/dist/permissions/approval-audit.js +22 -0
- package/dist/permissions/approvals-link.d.ts +20 -0
- package/dist/permissions/approvals-link.js +62 -0
- package/dist/permissions/approvals-store.d.ts +168 -0
- package/dist/permissions/approvals-store.js +259 -0
- package/dist/permissions/approvals-taint.d.ts +33 -0
- package/dist/permissions/approvals-taint.js +138 -0
- package/dist/permissions/ask-chain.d.ts +69 -0
- package/dist/permissions/ask-chain.js +32 -0
- package/dist/permissions/ask.d.ts +23 -0
- package/dist/permissions/ask.js +25 -0
- package/dist/permissions/bash-lex.d.ts +59 -0
- package/dist/permissions/bash-lex.js +279 -0
- package/dist/permissions/grammar.d.ts +12 -0
- package/dist/permissions/grammar.js +24 -0
- package/dist/permissions/index.d.ts +11 -0
- package/dist/permissions/index.js +9 -0
- package/dist/permissions/secret-spans.d.ts +19 -0
- package/dist/permissions/secret-spans.js +88 -0
- package/dist/permissions/types.d.ts +85 -0
- package/dist/permissions/types.js +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/node-glob.d.ts +3 -0
- package/dist/runtime/node-glob.js +83 -0
- package/dist/runtime/node-runtime.d.ts +3 -0
- package/dist/runtime/node-runtime.js +126 -0
- package/dist/runtime/slot.d.ts +11 -0
- package/dist/runtime/slot.js +17 -0
- package/dist/runtime/types.d.ts +51 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/which.d.ts +8 -0
- package/dist/runtime/which.js +32 -0
- package/dist/sandbox/argv-quote.d.ts +11 -0
- package/dist/sandbox/argv-quote.js +13 -0
- package/dist/sandbox/defaults.d.ts +27 -0
- package/dist/sandbox/defaults.js +47 -0
- package/dist/sandbox/git-guards.d.ts +64 -0
- package/dist/sandbox/git-guards.js +161 -0
- package/dist/sandbox/index.d.ts +11 -0
- package/dist/sandbox/index.js +10 -0
- package/dist/sandbox/launcher.d.ts +37 -0
- package/dist/sandbox/launcher.js +160 -0
- package/dist/sandbox/messages.d.ts +17 -0
- package/dist/sandbox/messages.js +46 -0
- package/dist/sandbox/policy-builder.d.ts +44 -0
- package/dist/sandbox/policy-builder.js +149 -0
- package/dist/sandbox/policy-inputs.d.ts +53 -0
- package/dist/sandbox/policy-inputs.js +76 -0
- package/dist/sandbox/probe.d.ts +8 -0
- package/dist/sandbox/probe.js +66 -0
- package/dist/sandbox/registry.d.ts +17 -0
- package/dist/sandbox/registry.js +57 -0
- package/dist/sandbox/session-tmp.d.ts +20 -0
- package/dist/sandbox/session-tmp.js +95 -0
- package/dist/sandbox/srt-backend.d.ts +21 -0
- package/dist/sandbox/srt-backend.js +99 -0
- package/dist/sandbox/types.d.ts +102 -0
- package/dist/sandbox/types.js +1 -0
- package/dist/session/adapter-failure.d.ts +60 -0
- package/dist/session/adapter-failure.js +11 -0
- package/dist/session/agent-stream-events.d.ts +103 -0
- package/dist/session/agent-stream-events.js +6 -0
- package/dist/session/interaction-handler.d.ts +53 -0
- package/dist/session/interaction-handler.js +2 -0
- package/dist/session/no-op-interaction-handler.d.ts +9 -0
- package/dist/session/no-op-interaction-handler.js +11 -0
- package/dist/session/protocol-types.d.ts +12 -0
- package/dist/session/protocol-types.js +1 -0
- package/dist/session/session-types.d.ts +456 -0
- package/dist/session/session-types.js +64 -0
- package/dist/session/tool-descriptor.d.ts +23 -0
- package/dist/session/tool-descriptor.js +5 -0
- package/dist/session/turn-deadline.d.ts +17 -0
- package/dist/session/turn-deadline.js +23 -0
- package/dist/tools/ask-request.d.ts +35 -0
- package/dist/tools/ask-request.js +57 -0
- package/dist/tools/bash-cwd.d.ts +82 -0
- package/dist/tools/bash-cwd.js +55 -0
- package/dist/tools/bash.d.ts +69 -0
- package/dist/tools/bash.js +283 -0
- package/dist/tools/delete.d.ts +57 -0
- package/dist/tools/delete.js +154 -0
- package/dist/tools/denial-redirect.d.ts +42 -0
- package/dist/tools/denial-redirect.js +266 -0
- package/dist/tools/deny-paths.d.ts +35 -0
- package/dist/tools/deny-paths.js +76 -0
- package/dist/tools/edit-region.d.ts +50 -0
- package/dist/tools/edit-region.js +90 -0
- package/dist/tools/edit.d.ts +25 -0
- package/dist/tools/edit.js +114 -0
- package/dist/tools/exec-allowlist-text.d.ts +18 -0
- package/dist/tools/exec-allowlist-text.js +24 -0
- package/dist/tools/exec-guard.d.ts +69 -0
- package/dist/tools/exec-guard.js +151 -0
- package/dist/tools/git-commit.d.ts +59 -0
- package/dist/tools/git-commit.js +220 -0
- package/dist/tools/git-flags/index.d.ts +10 -0
- package/dist/tools/git-flags/index.js +10 -0
- package/dist/tools/git.d.ts +88 -0
- package/dist/tools/git.js +397 -0
- package/dist/tools/glob.d.ts +29 -0
- package/dist/tools/glob.js +153 -0
- package/dist/tools/grep.d.ts +21 -0
- package/dist/tools/grep.js +162 -0
- package/dist/tools/index.d.ts +36 -0
- package/dist/tools/index.js +28 -0
- package/dist/tools/narrow-grants.d.ts +19 -0
- package/dist/tools/narrow-grants.js +54 -0
- package/dist/tools/nax-owned-writes.d.ts +135 -0
- package/dist/tools/nax-owned-writes.js +259 -0
- package/dist/tools/package-managers-table.d.ts +146 -0
- package/dist/tools/package-managers-table.js +325 -0
- package/dist/tools/package-managers-types.d.ts +36 -0
- package/dist/tools/package-managers-types.js +6 -0
- package/dist/tools/package-managers.d.ts +85 -0
- package/dist/tools/package-managers.js +391 -0
- package/dist/tools/path-list.d.ts +38 -0
- package/dist/tools/path-list.js +62 -0
- package/dist/tools/policy-bash-raw.d.ts +19 -0
- package/dist/tools/policy-bash-raw.js +210 -0
- package/dist/tools/policy-bash.d.ts +64 -0
- package/dist/tools/policy-bash.js +232 -0
- package/dist/tools/policy-command-branch.d.ts +32 -0
- package/dist/tools/policy-command-branch.js +63 -0
- package/dist/tools/policy-input.d.ts +2 -0
- package/dist/tools/policy-input.js +10 -0
- package/dist/tools/policy-match.d.ts +75 -0
- package/dist/tools/policy-match.js +117 -0
- package/dist/tools/policy-paths-branch.d.ts +97 -0
- package/dist/tools/policy-paths-branch.js +314 -0
- package/dist/tools/policy.d.ts +65 -0
- package/dist/tools/policy.js +232 -0
- package/dist/tools/protected-paths.d.ts +26 -0
- package/dist/tools/protected-paths.js +8 -0
- package/dist/tools/provider-adapt.d.ts +4 -0
- package/dist/tools/provider-adapt.js +32 -0
- package/dist/tools/provider-advertise.d.ts +39 -0
- package/dist/tools/provider-advertise.js +50 -0
- package/dist/tools/provider-grants.d.ts +36 -0
- package/dist/tools/provider-grants.js +72 -0
- package/dist/tools/provider-sanitize.d.ts +19 -0
- package/dist/tools/provider-sanitize.js +53 -0
- package/dist/tools/provider-types.d.ts +41 -0
- package/dist/tools/provider-types.js +26 -0
- package/dist/tools/read-continuation.d.ts +147 -0
- package/dist/tools/read-continuation.js +176 -0
- package/dist/tools/read-file.d.ts +32 -0
- package/dist/tools/read-file.js +85 -0
- package/dist/tools/read.d.ts +25 -0
- package/dist/tools/read.js +182 -0
- package/dist/tools/registry.d.ts +125 -0
- package/dist/tools/registry.js +67 -0
- package/dist/tools/request-capability.d.ts +13 -0
- package/dist/tools/request-capability.js +23 -0
- package/dist/tools/run-command-exec.d.ts +14 -0
- package/dist/tools/run-command-exec.js +161 -0
- package/dist/tools/run-command.d.ts +107 -0
- package/dist/tools/run-command.js +398 -0
- package/dist/tools/runtime-calltool.d.ts +146 -0
- package/dist/tools/runtime-calltool.js +223 -0
- package/dist/tools/runtime.d.ts +149 -0
- package/dist/tools/runtime.js +345 -0
- package/dist/tools/scratchpad.d.ts +20 -0
- package/dist/tools/scratchpad.js +197 -0
- package/dist/tools/spill.d.ts +81 -0
- package/dist/tools/spill.js +251 -0
- package/dist/tools/tool-audit.d.ts +171 -0
- package/dist/tools/tool-audit.js +183 -0
- package/dist/tools/truncate.d.ts +98 -0
- package/dist/tools/truncate.js +297 -0
- package/dist/tools/types.d.ts +145 -0
- package/dist/tools/types.js +24 -0
- package/dist/tools/verb-denial-argv-hint.d.ts +35 -0
- package/dist/tools/verb-denial-argv-hint.js +40 -0
- package/dist/tools/write.d.ts +9 -0
- package/dist/tools/write.js +45 -0
- package/package.json +54 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The I/O half of the sandbox policy: what exists on disk right now.
|
|
3
|
+
* Kept apart from policy-builder.ts so the builder stays pure.
|
|
4
|
+
*/
|
|
5
|
+
import { readdir } from "node:fs/promises";
|
|
6
|
+
import { homedir, tmpdir } from "node:os";
|
|
7
|
+
export type GitLayout = {
|
|
8
|
+
readonly kind: "none";
|
|
9
|
+
} | {
|
|
10
|
+
readonly kind: "main";
|
|
11
|
+
readonly gitDir: string;
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: "worktree";
|
|
14
|
+
readonly gitDir: string;
|
|
15
|
+
readonly commonDir: string;
|
|
16
|
+
};
|
|
17
|
+
export declare const _policyInputDeps: {
|
|
18
|
+
git: (args: string[], cwd: string) => Promise<{
|
|
19
|
+
stdout: string;
|
|
20
|
+
stderr: string;
|
|
21
|
+
exitCode: number;
|
|
22
|
+
timedOut?: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
readdir: typeof readdir;
|
|
25
|
+
homedir: typeof homedir;
|
|
26
|
+
tmpdir: typeof tmpdir;
|
|
27
|
+
platform: () => NodeJS.Platform;
|
|
28
|
+
};
|
|
29
|
+
/** Resolved once per session. A worktree's git dir differs from its common dir. */
|
|
30
|
+
export declare function resolveGitLayout(root: string): Promise<GitLayout>;
|
|
31
|
+
/**
|
|
32
|
+
* Top-level entry names under `<root>/<stateDir>` right now (nax#2260;
|
|
33
|
+
* `stateDir` is `.nax` for nax). An entry with a glob character is skipped:
|
|
34
|
+
* nax never creates one, and a glob in the policy would make every build
|
|
35
|
+
* throw (F1) -- an agent could otherwise switch the sandbox off by creating
|
|
36
|
+
* `.nax/a*b`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function listNaxEntries(root: string, stateDir: string): Promise<string[]>;
|
|
39
|
+
/** Every `credentials*` file in `dir` (the host's credential dir), expanded to literals (F1: no globs). */
|
|
40
|
+
export declare function listCredentialFiles(dir: string): Promise<string[]>;
|
|
41
|
+
/** `os.tmpdir()` plus `/tmp` (Linux tools fall back to it; srt sets no TMPDIR there -- spec F3). */
|
|
42
|
+
export declare function defaultTempRoots(): string[];
|
|
43
|
+
/**
|
|
44
|
+
* US-002: the temp roots a CONFINED session gets -- the run's own root, plus
|
|
45
|
+
* the host tmpdir only when that is not the shared one. `os.tmpdir()` is
|
|
46
|
+
* `/tmp` on most Linux hosts and keeping it would re-grant exactly the shared
|
|
47
|
+
* directory the confinement exists to remove; a tmpdir of `/private/tmp` is
|
|
48
|
+
* the same directory seen through macOS's symlink.
|
|
49
|
+
*/
|
|
50
|
+
export declare function runTempRoots(opts: {
|
|
51
|
+
runTmpRoot: string;
|
|
52
|
+
tmpdir: string;
|
|
53
|
+
}): string[];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The I/O half of the sandbox policy: what exists on disk right now.
|
|
3
|
+
* Kept apart from policy-builder.ts so the builder stays pure.
|
|
4
|
+
*/
|
|
5
|
+
import { readdir } from "node:fs/promises";
|
|
6
|
+
import { homedir, tmpdir } from "node:os";
|
|
7
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
8
|
+
import { gitWithTimeout } from "#src/internal/git-exec";
|
|
9
|
+
import { realOrRaw } from "#src/internal/realpath";
|
|
10
|
+
import { SANDBOX_GLOB_CHARS } from "../config/schemas-sandbox.js";
|
|
11
|
+
const GIT_LAYOUT_TIMEOUT_MS = 10_000;
|
|
12
|
+
export const _policyInputDeps = {
|
|
13
|
+
git: (args, cwd) => gitWithTimeout(args, cwd, GIT_LAYOUT_TIMEOUT_MS),
|
|
14
|
+
readdir,
|
|
15
|
+
homedir,
|
|
16
|
+
tmpdir,
|
|
17
|
+
platform: () => process.platform,
|
|
18
|
+
};
|
|
19
|
+
async function gitPath(flag, root) {
|
|
20
|
+
const r = await _policyInputDeps.git(["rev-parse", flag], root);
|
|
21
|
+
if (r.exitCode !== 0)
|
|
22
|
+
return undefined;
|
|
23
|
+
const out = r.stdout.trim();
|
|
24
|
+
return realOrRaw(isAbsolute(out) ? out : resolve(root, out));
|
|
25
|
+
}
|
|
26
|
+
/** Resolved once per session. A worktree's git dir differs from its common dir. */
|
|
27
|
+
export async function resolveGitLayout(root) {
|
|
28
|
+
const gitDir = await gitPath("--git-dir", root);
|
|
29
|
+
const commonDir = await gitPath("--git-common-dir", root);
|
|
30
|
+
if (gitDir === undefined || commonDir === undefined)
|
|
31
|
+
return { kind: "none" };
|
|
32
|
+
return gitDir === commonDir ? { kind: "main", gitDir } : { kind: "worktree", gitDir, commonDir };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Top-level entry names under `<root>/<stateDir>` right now (nax#2260;
|
|
36
|
+
* `stateDir` is `.nax` for nax). An entry with a glob character is skipped:
|
|
37
|
+
* nax never creates one, and a glob in the policy would make every build
|
|
38
|
+
* throw (F1) -- an agent could otherwise switch the sandbox off by creating
|
|
39
|
+
* `.nax/a*b`.
|
|
40
|
+
*/
|
|
41
|
+
export async function listNaxEntries(root, stateDir) {
|
|
42
|
+
try {
|
|
43
|
+
const names = await _policyInputDeps.readdir(join(root, stateDir));
|
|
44
|
+
return names.filter((name) => !SANDBOX_GLOB_CHARS.test(name));
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Every `credentials*` file in `dir` (the host's credential dir), expanded to literals (F1: no globs). */
|
|
51
|
+
export async function listCredentialFiles(dir) {
|
|
52
|
+
try {
|
|
53
|
+
const entries = await _policyInputDeps.readdir(dir, { withFileTypes: true });
|
|
54
|
+
return entries.filter((e) => e.name.startsWith("credentials")).map((e) => join(dir, e.name));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** `os.tmpdir()` plus `/tmp` (Linux tools fall back to it; srt sets no TMPDIR there -- spec F3). */
|
|
61
|
+
export function defaultTempRoots() {
|
|
62
|
+
return [_policyInputDeps.tmpdir(), "/tmp"];
|
|
63
|
+
}
|
|
64
|
+
/** The shared temp root under both spellings: `/tmp`, and its macOS realpath. */
|
|
65
|
+
const SHARED_TMP_ROOTS = ["/tmp", "/private/tmp"];
|
|
66
|
+
/**
|
|
67
|
+
* US-002: the temp roots a CONFINED session gets -- the run's own root, plus
|
|
68
|
+
* the host tmpdir only when that is not the shared one. `os.tmpdir()` is
|
|
69
|
+
* `/tmp` on most Linux hosts and keeping it would re-grant exactly the shared
|
|
70
|
+
* directory the confinement exists to remove; a tmpdir of `/private/tmp` is
|
|
71
|
+
* the same directory seen through macOS's symlink.
|
|
72
|
+
*/
|
|
73
|
+
export function runTempRoots(opts) {
|
|
74
|
+
const shared = SHARED_TMP_ROOTS.some((root) => opts.tmpdir === root || opts.tmpdir.startsWith(`${root}/`));
|
|
75
|
+
return shared ? [opts.runTmpRoot] : [opts.tmpdir, opts.runTmpRoot];
|
|
76
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { tmpdir } from "node:os";
|
|
2
|
+
import { runArgv } from "#src/internal/argv-exec";
|
|
3
|
+
import type { ProbeResult, SandboxBackend } from "./types.ts";
|
|
4
|
+
export declare const _probeDeps: {
|
|
5
|
+
tmpdir: typeof tmpdir;
|
|
6
|
+
runArgv: typeof runArgv;
|
|
7
|
+
};
|
|
8
|
+
export declare function probeSandbox(backend: SandboxBackend): Promise<ProbeResult>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is the sandbox actually working here? Decided by RUNNING one wrapped
|
|
3
|
+
* command, never by a dependency check: in stock Docker bwrap is installed
|
|
4
|
+
* and every command fails (spec F4), and a sandbox that runs but does not
|
|
5
|
+
* enforce must count as absent, not present.
|
|
6
|
+
*
|
|
7
|
+
* The denied marker sits under the probe's own write root and is listed in
|
|
8
|
+
* denyWrite: srt's deny-within-allow wins on both platforms (spec 5.4). A
|
|
9
|
+
* marker merely "outside the roots" would be inside a tmp write root in
|
|
10
|
+
* production and falsely read as a leak.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { runArgv } from "#src/internal/argv-exec";
|
|
16
|
+
import { realOrRaw } from "#src/internal/realpath";
|
|
17
|
+
const PROBE_TIMEOUT_MS = 30_000;
|
|
18
|
+
export const _probeDeps = { tmpdir, runArgv };
|
|
19
|
+
function firstLine(text) {
|
|
20
|
+
return text.trim().split("\n")[0] ?? "";
|
|
21
|
+
}
|
|
22
|
+
export async function probeSandbox(backend) {
|
|
23
|
+
if (!(await backend.isSupportedPlatform())) {
|
|
24
|
+
return { available: false, reason: `platform ${process.platform} is not supported by the ${backend.name} sandbox` };
|
|
25
|
+
}
|
|
26
|
+
const dir = realOrRaw(mkdtempSync(join(_probeDeps.tmpdir(), "nax-sandbox-probe-")));
|
|
27
|
+
mkdirSync(join(dir, "allowed"));
|
|
28
|
+
mkdirSync(join(dir, "denied"));
|
|
29
|
+
const allowed = join(dir, "allowed", "marker");
|
|
30
|
+
const denied = join(dir, "denied", "marker");
|
|
31
|
+
try {
|
|
32
|
+
let argv;
|
|
33
|
+
try {
|
|
34
|
+
argv = await backend.wrap({
|
|
35
|
+
command: `echo ok > ${allowed}; echo leak > ${denied}; exit 0`,
|
|
36
|
+
shell: "/bin/sh",
|
|
37
|
+
policy: { writeRoots: [dir], denyWrite: [denied], denyRead: [], network: {} },
|
|
38
|
+
cwd: dir,
|
|
39
|
+
commandId: "nax-sandbox-probe",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
return { available: false, reason: `sandbox wrap failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
44
|
+
}
|
|
45
|
+
let result;
|
|
46
|
+
try {
|
|
47
|
+
result = await _probeDeps.runArgv({ argv, cwd: dir, timeoutMs: PROBE_TIMEOUT_MS });
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
// Exactly once per successful wrap, even when the spawn itself throws.
|
|
51
|
+
backend.commandFinished();
|
|
52
|
+
}
|
|
53
|
+
if (!existsSync(allowed)) {
|
|
54
|
+
return {
|
|
55
|
+
available: false,
|
|
56
|
+
reason: `sandbox could not run a command: ${firstLine(result.stderr) || `exit ${result.exitCode}`}`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (existsSync(denied))
|
|
60
|
+
return { available: false, reason: "sandbox ran a command but did not enforce a write deny" };
|
|
61
|
+
return { available: true };
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
rmSync(dir, { recursive: true, force: true });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-level sandbox state: one backend (srt's manager is a singleton), one
|
|
3
|
+
* probe result, and the once-per-process log lines (spec 5.7). One nax process
|
|
4
|
+
* serves one project, so one network config per process holds.
|
|
5
|
+
*/
|
|
6
|
+
import type { SandboxConfig } from "../config/schemas-sandbox.ts";
|
|
7
|
+
import type { ProbeResult, SandboxBackend } from "./types.ts";
|
|
8
|
+
export declare const _sandboxRegistryDeps: {
|
|
9
|
+
createBackend: (network: SandboxConfig["network"]) => SandboxBackend;
|
|
10
|
+
probe: (backend: SandboxBackend) => Promise<ProbeResult>;
|
|
11
|
+
};
|
|
12
|
+
export declare function sandboxBackendFor(config: SandboxConfig): SandboxBackend;
|
|
13
|
+
export declare function probeSandboxOnce(target: SandboxBackend, storyId?: string): Promise<ProbeResult>;
|
|
14
|
+
export declare function warnSandboxUnavailableOnce(reason: string, storyId?: string): void;
|
|
15
|
+
/** Run cleanup. The probe result is kept: the platform does not change. */
|
|
16
|
+
export declare function resetSandboxBackend(): Promise<void>;
|
|
17
|
+
export declare function _resetSandboxRegistryForTests(): void;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-level sandbox state: one backend (srt's manager is a singleton), one
|
|
3
|
+
* probe result, and the once-per-process log lines (spec 5.7). One nax process
|
|
4
|
+
* serves one project, so one network config per process holds.
|
|
5
|
+
*/
|
|
6
|
+
import { getSafeLogger } from "#src/infra/index";
|
|
7
|
+
import { probeSandbox } from "./probe.js";
|
|
8
|
+
import { createSrtBackend } from "./srt-backend.js";
|
|
9
|
+
export const _sandboxRegistryDeps = {
|
|
10
|
+
createBackend: (network) => createSrtBackend(network),
|
|
11
|
+
probe: (backend) => probeSandbox(backend),
|
|
12
|
+
};
|
|
13
|
+
let backend;
|
|
14
|
+
let probed;
|
|
15
|
+
let warnedUnavailable = false;
|
|
16
|
+
export function sandboxBackendFor(config) {
|
|
17
|
+
backend ??= _sandboxRegistryDeps.createBackend(config.network);
|
|
18
|
+
return backend;
|
|
19
|
+
}
|
|
20
|
+
export function probeSandboxOnce(target, storyId = "_sandbox") {
|
|
21
|
+
probed ??= _sandboxRegistryDeps.probe(target).then((result) => {
|
|
22
|
+
getSafeLogger()?.info("sandbox", "Sandbox probe", {
|
|
23
|
+
storyId,
|
|
24
|
+
backend: target.name,
|
|
25
|
+
available: result.available,
|
|
26
|
+
...(result.available ? {} : { reason: result.reason }),
|
|
27
|
+
});
|
|
28
|
+
return result;
|
|
29
|
+
}, (err) => {
|
|
30
|
+
const reason = `sandbox probe failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
31
|
+
getSafeLogger()?.warn("sandbox", "Sandbox probe threw; treating as unavailable", { storyId, reason });
|
|
32
|
+
const unavailable = { available: false, reason };
|
|
33
|
+
return unavailable;
|
|
34
|
+
});
|
|
35
|
+
return probed;
|
|
36
|
+
}
|
|
37
|
+
export function warnSandboxUnavailableOnce(reason, storyId = "_sandbox") {
|
|
38
|
+
if (warnedUnavailable)
|
|
39
|
+
return;
|
|
40
|
+
warnedUnavailable = true;
|
|
41
|
+
getSafeLogger()?.warn("sandbox", "Sandbox unavailable: raw bash is refused; gated/escalate commands run unwrapped", {
|
|
42
|
+
storyId,
|
|
43
|
+
reason,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/** Run cleanup. The probe result is kept: the platform does not change. */
|
|
47
|
+
export async function resetSandboxBackend() {
|
|
48
|
+
const current = backend;
|
|
49
|
+
backend = undefined;
|
|
50
|
+
if (current !== undefined)
|
|
51
|
+
await current.reset();
|
|
52
|
+
}
|
|
53
|
+
export function _resetSandboxRegistryForTests() {
|
|
54
|
+
backend = undefined;
|
|
55
|
+
probed = undefined;
|
|
56
|
+
warnedUnavailable = false;
|
|
57
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The three synchronous host calls {@link runTmpRoot} resolves its parent
|
|
3
|
+
* through. Injectable so tests can pin a host (see docs/architecture/conventions.md §2).
|
|
4
|
+
*/
|
|
5
|
+
export declare const _sessionTmpDeps: {
|
|
6
|
+
lstat: (path: string) => import("node:fs").Stats;
|
|
7
|
+
access: (path: string, mode: number) => void;
|
|
8
|
+
uid: () => number;
|
|
9
|
+
};
|
|
10
|
+
/** `<parent>/<runId>` — the per-run parent of every session temp directory. */
|
|
11
|
+
export declare function runTmpRoot(runId: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* `<root>/<sessionName>`, the session part reduced to [A-Za-z0-9_.-] (others
|
|
14
|
+
* become "_"). Takes an ALREADY-RESOLVED run root so a caller that needs the
|
|
15
|
+
* root too (the sandbox policy) resolves the parent once and cannot observe it
|
|
16
|
+
* flip between two calls.
|
|
17
|
+
*/
|
|
18
|
+
export declare function sessionTmpDirUnder(root: string, sessionName: string): string;
|
|
19
|
+
/** `<parent>/<runId>/<sessionName>`, each part reduced to [A-Za-z0-9_.-] (others become "_"). */
|
|
20
|
+
export declare function sessionTmpDir(runId: string, sessionName: string): string;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-session temp directories under a run-owned root (US-001, US-004).
|
|
3
|
+
*
|
|
4
|
+
* A stray `/tmp/tsconfig.json` survived an NBF restore and turned a later
|
|
5
|
+
* story's full-suite gate red. Launcher-driven Bash/Exec commands now run with
|
|
6
|
+
* `TMPDIR`/`TMP`/`TEMP` pointing at `<parent>/<runId>/<sessionName>`, which the
|
|
7
|
+
* run deletes when it ends. The directory is a flat child of the run root, so
|
|
8
|
+
* the run's own `runTmpRoot(runId)` is the only prefix a cleanup needs to
|
|
9
|
+
* remove — never a `/tmp/nax*` sweep, because a concurrent run's live
|
|
10
|
+
* directories share that prefix.
|
|
11
|
+
*
|
|
12
|
+
* The parent is `/tmp/nax`, one shared directory that is easier to find and
|
|
13
|
+
* list than a flat `/tmp/nax-<runId>`. It is shared, so it cannot be created
|
|
14
|
+
* once and trusted: a `/tmp/nax` made by one OS user is not writable by
|
|
15
|
+
* another. The parent is therefore re-resolved on EVERY call from three host
|
|
16
|
+
* facts the `_sessionTmpDeps` seam exposes, with no cache, and anything nax
|
|
17
|
+
* cannot vouch for falls back to a per-user `/tmp/nax-<uid>`.
|
|
18
|
+
*
|
|
19
|
+
* The root is the literal `/tmp` (not `os.tmpdir()`): callers record and assert
|
|
20
|
+
* this machine-stable location, and the OS inherits the rest.
|
|
21
|
+
*/
|
|
22
|
+
import { accessSync, constants, lstatSync } from "node:fs";
|
|
23
|
+
/** The literal temp root both parents hang off. */
|
|
24
|
+
const TMP_ROOT = "/tmp";
|
|
25
|
+
/** The shared parent every resolvable case nests under; `mkdir -p` in the launcher creates it. */
|
|
26
|
+
const SHARED_TMP_PARENT = `${TMP_ROOT}/nax`;
|
|
27
|
+
/**
|
|
28
|
+
* The three synchronous host calls {@link runTmpRoot} resolves its parent
|
|
29
|
+
* through. Injectable so tests can pin a host (see docs/architecture/conventions.md §2).
|
|
30
|
+
*/
|
|
31
|
+
export const _sessionTmpDeps = {
|
|
32
|
+
lstat: (path) => lstatSync(path),
|
|
33
|
+
access: (path, mode) => {
|
|
34
|
+
accessSync(path, mode);
|
|
35
|
+
},
|
|
36
|
+
// `process.getuid` is POSIX-only and this module's `/tmp` paths already assume
|
|
37
|
+
// POSIX; the fallback keeps the type total without claiming a real uid.
|
|
38
|
+
uid: () => process.getuid?.() ?? 0,
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* True when `/tmp/nax` is a real directory the current user can write and
|
|
42
|
+
* search. Absence is NOT this case: an absent parent is a usable shared parent
|
|
43
|
+
* (the launcher creates it), and every other failure means the path is there in
|
|
44
|
+
* a shape nax cannot vouch for.
|
|
45
|
+
*/
|
|
46
|
+
function isUsableSharedParent() {
|
|
47
|
+
try {
|
|
48
|
+
const stats = _sessionTmpDeps.lstat(SHARED_TMP_PARENT);
|
|
49
|
+
if (!stats.isDirectory() || stats.isSymbolicLink())
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
// ENOENT is the one failure that means "create /tmp/nax".
|
|
54
|
+
return err.code === "ENOENT";
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
_sessionTmpDeps.access(SHARED_TMP_PARENT, constants.W_OK | constants.X_OK);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Unwritable or unsearchable by this user: the fallback covers it, so the
|
|
62
|
+
// errno carries no decision the caller does not already make.
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** `/tmp/nax` when the shared parent is usable, `/tmp/nax-<uid>` otherwise. */
|
|
67
|
+
function tmpParent() {
|
|
68
|
+
return isUsableSharedParent() ? SHARED_TMP_PARENT : `${TMP_ROOT}/nax-${_sessionTmpDeps.uid()}`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Any character outside `[A-Za-z0-9_.-]` collapses to `_`, so a part can never
|
|
72
|
+
* contain a separator. `.` survives because real run ids carry one (the
|
|
73
|
+
* millisecond field of `buildRunId`'s ISO stamp), but a part that is nothing
|
|
74
|
+
* BUT dots would name the parent — so it collapses to `_` as well.
|
|
75
|
+
*/
|
|
76
|
+
function sanitizePart(part) {
|
|
77
|
+
return part.replace(/[^A-Za-z0-9_.-]/g, "_").replace(/^\.+$/, "_");
|
|
78
|
+
}
|
|
79
|
+
/** `<parent>/<runId>` — the per-run parent of every session temp directory. */
|
|
80
|
+
export function runTmpRoot(runId) {
|
|
81
|
+
return `${tmpParent()}/${sanitizePart(runId)}`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* `<root>/<sessionName>`, the session part reduced to [A-Za-z0-9_.-] (others
|
|
85
|
+
* become "_"). Takes an ALREADY-RESOLVED run root so a caller that needs the
|
|
86
|
+
* root too (the sandbox policy) resolves the parent once and cannot observe it
|
|
87
|
+
* flip between two calls.
|
|
88
|
+
*/
|
|
89
|
+
export function sessionTmpDirUnder(root, sessionName) {
|
|
90
|
+
return `${root}/${sanitizePart(sessionName)}`;
|
|
91
|
+
}
|
|
92
|
+
/** `<parent>/<runId>/<sessionName>`, each part reduced to [A-Za-z0-9_.-] (others become "_"). */
|
|
93
|
+
export function sessionTmpDir(runId, sessionName) {
|
|
94
|
+
return sessionTmpDirUnder(runTmpRoot(runId), sessionName);
|
|
95
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONLY importer of @anthropic-ai/sandbox-runtime (enforced by
|
|
3
|
+
* packages/repo-tooling/scripts/check-sandbox-imports.ts). Loaded by dynamic import so a run that
|
|
4
|
+
* never sandboxes never loads it.
|
|
5
|
+
*
|
|
6
|
+
* srt's SandboxManager is a process-wide singleton; per-call customConfig
|
|
7
|
+
* carries each story's roots, so parallel worktree stories share it (spike:
|
|
8
|
+
* concurrent roots isolated). The returned `env` of wrapWithSandboxArgv is
|
|
9
|
+
* process.env itself and is DISCARDED here (spec F6).
|
|
10
|
+
*/
|
|
11
|
+
import { mkdir } from "node:fs/promises";
|
|
12
|
+
import type { SandboxConfig } from "../config/schemas-sandbox.ts";
|
|
13
|
+
import type { SandboxBackend } from "./types.ts";
|
|
14
|
+
type SrtModule = typeof import("@anthropic-ai/sandbox-runtime");
|
|
15
|
+
export declare const _srtBackendDeps: {
|
|
16
|
+
load: () => Promise<SrtModule>;
|
|
17
|
+
platform: () => NodeJS.Platform;
|
|
18
|
+
mkdir: typeof mkdir;
|
|
19
|
+
};
|
|
20
|
+
export declare function createSrtBackend(network: SandboxConfig["network"]): SandboxBackend;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONLY importer of @anthropic-ai/sandbox-runtime (enforced by
|
|
3
|
+
* packages/repo-tooling/scripts/check-sandbox-imports.ts). Loaded by dynamic import so a run that
|
|
4
|
+
* never sandboxes never loads it.
|
|
5
|
+
*
|
|
6
|
+
* srt's SandboxManager is a process-wide singleton; per-call customConfig
|
|
7
|
+
* carries each story's roots, so parallel worktree stories share it (spike:
|
|
8
|
+
* concurrent roots isolated). The returned `env` of wrapWithSandboxArgv is
|
|
9
|
+
* process.env itself and is DISCARDED here (spec F6).
|
|
10
|
+
*/
|
|
11
|
+
import { mkdir } from "node:fs/promises";
|
|
12
|
+
import { SRT_MACOS_TMPDIR } from "./defaults.js";
|
|
13
|
+
export const _srtBackendDeps = {
|
|
14
|
+
load: () => import("@anthropic-ai/sandbox-runtime"),
|
|
15
|
+
platform: () => process.platform,
|
|
16
|
+
mkdir,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Open network = `network` WITHOUT `allowedDomains`. srt's type requires the
|
|
20
|
+
* field, but its runtime decides restriction on `allowedDomains !== undefined`
|
|
21
|
+
* (sandbox-manager.js, `hasNetworkConfig`), and `initialize` dereferences
|
|
22
|
+
* `network`, so the object must exist. This is the one cast in the module; the
|
|
23
|
+
* live suite pins the behaviour (open => no proxy variables in the argv), so
|
|
24
|
+
* an srt bump that changes it fails a test rather than silently restricting.
|
|
25
|
+
*/
|
|
26
|
+
function srtNetwork(allowedDomains) {
|
|
27
|
+
if (allowedDomains !== undefined)
|
|
28
|
+
return { allowedDomains: [...allowedDomains], deniedDomains: [] };
|
|
29
|
+
const open = { deniedDomains: [] };
|
|
30
|
+
return open;
|
|
31
|
+
}
|
|
32
|
+
function customConfig(policy) {
|
|
33
|
+
return {
|
|
34
|
+
filesystem: {
|
|
35
|
+
denyRead: [...policy.denyRead],
|
|
36
|
+
allowWrite: [...policy.writeRoots],
|
|
37
|
+
denyWrite: [...policy.denyWrite],
|
|
38
|
+
},
|
|
39
|
+
...(policy.network.allowedDomains !== undefined ? { network: srtNetwork(policy.network.allowedDomains) } : {}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function createSrtBackend(network) {
|
|
43
|
+
let initialized;
|
|
44
|
+
let loaded;
|
|
45
|
+
let inFlight = 0;
|
|
46
|
+
function initialize() {
|
|
47
|
+
initialized ??= (async () => {
|
|
48
|
+
const mod = await _srtBackendDeps.load();
|
|
49
|
+
if (_srtBackendDeps.platform() === "darwin")
|
|
50
|
+
await _srtBackendDeps.mkdir(SRT_MACOS_TMPDIR, { recursive: true });
|
|
51
|
+
await mod.SandboxManager.initialize({
|
|
52
|
+
network: srtNetwork(network.allowedDomains),
|
|
53
|
+
filesystem: { denyRead: [], allowWrite: [], denyWrite: [] },
|
|
54
|
+
});
|
|
55
|
+
loaded = mod;
|
|
56
|
+
return mod;
|
|
57
|
+
})();
|
|
58
|
+
return initialized;
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
name: "srt",
|
|
62
|
+
async isSupportedPlatform() {
|
|
63
|
+
const mod = await _srtBackendDeps.load();
|
|
64
|
+
return mod.SandboxManager.isSupportedPlatform();
|
|
65
|
+
},
|
|
66
|
+
async wrap(req) {
|
|
67
|
+
const mod = await initialize();
|
|
68
|
+
inFlight += 1;
|
|
69
|
+
try {
|
|
70
|
+
const { argv } = await mod.SandboxManager.wrapWithSandboxArgv(req.command, req.shell, customConfig(req.policy), undefined, req.cwd, { commandId: req.commandId });
|
|
71
|
+
return argv;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
inFlight -= 1;
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
annotate(commandId, stderr) {
|
|
79
|
+
if (loaded === undefined)
|
|
80
|
+
return "";
|
|
81
|
+
const annotated = loaded.SandboxManager.annotateStderrWithSandboxFailures(commandId, stderr);
|
|
82
|
+
return annotated.startsWith(stderr) ? annotated.slice(stderr.length).trim() : "";
|
|
83
|
+
},
|
|
84
|
+
commandFinished() {
|
|
85
|
+
inFlight = Math.max(0, inFlight - 1);
|
|
86
|
+
// Removes bwrap mount placeholders (Linux). Never while another wrapped
|
|
87
|
+
// command runs: a running sandbox may still depend on them.
|
|
88
|
+
if (inFlight === 0)
|
|
89
|
+
loaded?.SandboxManager.cleanupAfterCommand();
|
|
90
|
+
},
|
|
91
|
+
async reset() {
|
|
92
|
+
if (loaded !== undefined)
|
|
93
|
+
await loaded.SandboxManager.reset();
|
|
94
|
+
initialized = undefined;
|
|
95
|
+
loaded = undefined;
|
|
96
|
+
inFlight = 0;
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the OS sandbox around agent-authored commands (P4).
|
|
3
|
+
*
|
|
4
|
+
* Every path in a SandboxPolicy is absolute, literal and realpath-resolved:
|
|
5
|
+
* srt on Linux silently DROPS any denyWrite entry containing a glob
|
|
6
|
+
* (spec F1), and resolves only paths that already exist (spec 12, finding 3).
|
|
7
|
+
*/
|
|
8
|
+
import type { ArgvExecResult } from "#src/internal/argv-exec";
|
|
9
|
+
export type SandboxBackendName = "srt";
|
|
10
|
+
export interface SandboxNetworkPolicy {
|
|
11
|
+
/** Absent = unrestricted. */
|
|
12
|
+
readonly allowedDomains?: readonly string[];
|
|
13
|
+
}
|
|
14
|
+
export interface SandboxPolicy {
|
|
15
|
+
readonly writeRoots: readonly string[];
|
|
16
|
+
readonly denyWrite: readonly string[];
|
|
17
|
+
readonly denyRead: readonly string[];
|
|
18
|
+
readonly network: SandboxNetworkPolicy;
|
|
19
|
+
}
|
|
20
|
+
export type ProbeResult = {
|
|
21
|
+
readonly available: true;
|
|
22
|
+
} | {
|
|
23
|
+
readonly available: false;
|
|
24
|
+
readonly reason: string;
|
|
25
|
+
};
|
|
26
|
+
export interface SandboxWrapRequest {
|
|
27
|
+
readonly command: string;
|
|
28
|
+
readonly shell: string;
|
|
29
|
+
readonly policy: SandboxPolicy;
|
|
30
|
+
readonly cwd: string;
|
|
31
|
+
readonly commandId: string;
|
|
32
|
+
}
|
|
33
|
+
export interface SandboxBackend {
|
|
34
|
+
readonly name: SandboxBackendName;
|
|
35
|
+
isSupportedPlatform(): Promise<boolean>;
|
|
36
|
+
/** Argv ONLY. The backend's env never crosses this boundary (spec F6). */
|
|
37
|
+
wrap(req: SandboxWrapRequest): Promise<readonly string[]>;
|
|
38
|
+
/** Extra violation text for this command, or "" when there is none. */
|
|
39
|
+
annotate(commandId: string, stderr: string): string;
|
|
40
|
+
/** Called once per finished wrapped command (in-flight bookkeeping). */
|
|
41
|
+
commandFinished(): void;
|
|
42
|
+
reset(): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
export type SandboxState = {
|
|
45
|
+
readonly kind: "disabled";
|
|
46
|
+
} | {
|
|
47
|
+
readonly kind: "available";
|
|
48
|
+
readonly backend: SandboxBackendName;
|
|
49
|
+
readonly network: "open" | readonly string[];
|
|
50
|
+
/**
|
|
51
|
+
* US-002: `false` when temp writes are confined to this run's own temp
|
|
52
|
+
* root. Absent means the shared temp roots are writable (today's
|
|
53
|
+
* behaviour), so every pre-US-002 construction of this variant holds.
|
|
54
|
+
*/
|
|
55
|
+
readonly sharedTmp?: boolean;
|
|
56
|
+
} | {
|
|
57
|
+
readonly kind: "unavailable";
|
|
58
|
+
readonly backend: SandboxBackendName;
|
|
59
|
+
readonly reason: string;
|
|
60
|
+
};
|
|
61
|
+
export interface SandboxRecord {
|
|
62
|
+
readonly backend: SandboxBackendName | "none";
|
|
63
|
+
readonly wrapped: boolean;
|
|
64
|
+
readonly reason?: string;
|
|
65
|
+
readonly denialHint?: true;
|
|
66
|
+
/**
|
|
67
|
+
* Wrapped calls only: the argv the sandbox backend returned and runArgv
|
|
68
|
+
* executed. Absent when nothing was wrapped, where `executed` is already
|
|
69
|
+
* what ran. Never a substitute for `executed`, which stays the logical argv.
|
|
70
|
+
*/
|
|
71
|
+
readonly argv?: readonly string[];
|
|
72
|
+
}
|
|
73
|
+
export type LaunchSpec = {
|
|
74
|
+
readonly kind: "shell";
|
|
75
|
+
readonly shell: string;
|
|
76
|
+
readonly command: string;
|
|
77
|
+
} | {
|
|
78
|
+
readonly kind: "argv";
|
|
79
|
+
readonly argv: readonly string[];
|
|
80
|
+
};
|
|
81
|
+
export interface LaunchRequest {
|
|
82
|
+
readonly spec: LaunchSpec;
|
|
83
|
+
/** The policy root (`ctx.root`); write roots derive from it. */
|
|
84
|
+
readonly root: string;
|
|
85
|
+
/** Where the command starts; the PACKAGE dir for an Exec `target: "package"` call. */
|
|
86
|
+
readonly cwd: string;
|
|
87
|
+
readonly timeoutMs: number;
|
|
88
|
+
readonly stripEnvVars: readonly string[];
|
|
89
|
+
/** The CALLER's own overlay (Exec's Yarn no-scripts env). Never the backend's. */
|
|
90
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
91
|
+
/** Aborts the run: forwarded to runArgv, which SIGKILLs the process group. US-001. */
|
|
92
|
+
readonly signal?: AbortSignal;
|
|
93
|
+
}
|
|
94
|
+
export interface LaunchResult extends ArgvExecResult {
|
|
95
|
+
/** The logical argv (what the tool asked to run), not the sandbox wrapper argv. */
|
|
96
|
+
readonly executed: readonly string[];
|
|
97
|
+
readonly sandbox: SandboxRecord;
|
|
98
|
+
}
|
|
99
|
+
export interface CommandLauncher {
|
|
100
|
+
readonly state: SandboxState;
|
|
101
|
+
run(req: LaunchRequest): Promise<LaunchResult>;
|
|
102
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|