@bjoernrochel/sevro 1.0.0 → 1.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/CONTRIBUTING.md +34 -4
- package/README.md +7 -6
- package/dist/examples/basic/graded.json +13 -0
- package/dist/examples/basic/host.js +9 -0
- package/dist/examples/basic/host.js.map +1 -0
- package/dist/examples/basic/prompt-only.json +7 -0
- package/dist/examples/recipes/candidate.js +23 -0
- package/dist/examples/recipes/candidate.js.map +1 -0
- package/dist/examples/recipes/cases/coding-change.json +21 -0
- package/dist/examples/recipes/cases/host-tools.json +21 -0
- package/dist/examples/recipes/cases/mise.json +21 -0
- package/dist/examples/recipes/cases/node.json +21 -0
- package/dist/examples/recipes/cases/python-uv.json +21 -0
- package/dist/examples/recipes/cases/semantic.json +17 -0
- package/dist/examples/recipes/coding-change/check.js +16 -0
- package/dist/examples/recipes/coding-change/check.js.map +1 -0
- package/dist/examples/recipes/executable.js +20 -0
- package/dist/examples/recipes/executable.js.map +1 -0
- package/dist/examples/recipes/extension-artifacts.js +56 -0
- package/dist/examples/recipes/extension-artifacts.js.map +1 -0
- package/dist/examples/recipes/extension-methods.js +167 -0
- package/dist/examples/recipes/extension-methods.js.map +1 -0
- package/dist/examples/recipes/extension.js +28 -0
- package/dist/examples/recipes/extension.js.map +1 -0
- package/dist/examples/recipes/modules.js +9 -0
- package/dist/examples/recipes/modules.js.map +1 -0
- package/dist/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
- package/dist/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
- package/dist/examples/recipes/repository-skill/README.md +3 -0
- package/dist/examples/recipes/run-inputs.js +144 -0
- package/dist/examples/recipes/run-inputs.js.map +1 -0
- package/dist/examples/recipes/run-options.js +103 -0
- package/dist/examples/recipes/run-options.js.map +1 -0
- package/dist/examples/recipes/run.js +62 -0
- package/dist/examples/recipes/run.js.map +1 -0
- package/dist/examples/recipes/runtime/empty.json +1 -0
- package/dist/examples/recipes/runtime/goals.json +1 -0
- package/dist/examples/recipes/runtime/mise.json +6 -0
- package/dist/examples/recipes/runtime/path.json +1 -0
- package/dist/examples/recipes/runtime/plugins.json +4 -0
- package/dist/examples/recipes/runtime/python-uv.json +10 -0
- package/dist/examples/recipes/semantic-judge.js +27 -0
- package/dist/examples/recipes/semantic-judge.js.map +1 -0
- package/dist/examples/templates/coding/adapter.js +14 -0
- package/dist/examples/templates/coding/adapter.js.map +1 -0
- package/dist/examples/templates/coding/case.json +15 -0
- package/dist/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
- package/dist/examples/templates/repository-skill/extension-data.js +61 -0
- package/dist/examples/templates/repository-skill/extension-data.js.map +1 -0
- package/dist/examples/templates/repository-skill/extension.js +29 -0
- package/dist/examples/templates/repository-skill/extension.js.map +1 -0
- package/dist/examples/templates/response/adapter.js +13 -0
- package/dist/examples/templates/response/adapter.js.map +1 -0
- package/dist/examples/templates/response/case.json +13 -0
- package/dist/src/advisory.js +86 -0
- package/dist/src/advisory.js.map +1 -0
- package/dist/src/candidate-artifacts.js +139 -0
- package/dist/src/candidate-artifacts.js.map +1 -0
- package/dist/src/candidate-codex-state.js +110 -0
- package/dist/src/candidate-codex-state.js.map +1 -0
- package/dist/src/case-validation.js +92 -0
- package/dist/src/case-validation.js.map +1 -0
- package/dist/src/cli/cancellation.js +21 -0
- package/dist/src/cli/cancellation.js.map +1 -0
- package/dist/src/cli/command.js +60 -0
- package/dist/src/cli/command.js.map +1 -0
- package/dist/src/cli/completion.js +14 -0
- package/dist/src/cli/completion.js.map +1 -0
- package/dist/src/cli/demo.js +45 -0
- package/dist/src/cli/demo.js.map +1 -0
- package/dist/src/cli/evaluation.js +40 -0
- package/dist/src/cli/evaluation.js.map +1 -0
- package/dist/src/cli/extension.js +54 -0
- package/dist/src/cli/extension.js.map +1 -0
- package/dist/src/cli/help.js +190 -0
- package/dist/src/cli/help.js.map +1 -0
- package/dist/src/cli/hosts.js +33 -0
- package/dist/src/cli/hosts.js.map +1 -0
- package/dist/src/cli/init-settings.js +29 -0
- package/dist/src/cli/init-settings.js.map +1 -0
- package/dist/src/cli/init.js +82 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/invocation.js +431 -0
- package/dist/src/cli/invocation.js.map +1 -0
- package/dist/src/cli/loading.js +110 -0
- package/dist/src/cli/loading.js.map +1 -0
- package/dist/src/cli/preparation.js +88 -0
- package/dist/src/cli/preparation.js.map +1 -0
- package/dist/src/cli/presentation.js +61 -0
- package/dist/src/cli/presentation.js.map +1 -0
- package/dist/src/cli/progress.js +5 -0
- package/dist/src/cli/progress.js.map +1 -0
- package/dist/src/cli/run-manifest.js +108 -0
- package/dist/src/cli/run-manifest.js.map +1 -0
- package/dist/src/cli/run-options.js +367 -0
- package/dist/src/cli/run-options.js.map +1 -0
- package/dist/src/cli/run-overrides.js +51 -0
- package/dist/src/cli/run-overrides.js.map +1 -0
- package/dist/src/cli/settings.js +127 -0
- package/dist/src/cli/settings.js.map +1 -0
- package/dist/src/cli/summary.js +28 -0
- package/dist/src/cli/summary.js.map +1 -0
- package/dist/src/cli.js +20 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/contract-types.js +2 -0
- package/dist/src/contract-types.js.map +1 -0
- package/dist/src/engine.js +4 -0
- package/dist/src/engine.js.map +1 -0
- package/dist/src/evaluation/advisory.js +85 -0
- package/dist/src/evaluation/advisory.js.map +1 -0
- package/dist/src/evaluation/check-plan.js +292 -0
- package/dist/src/evaluation/check-plan.js.map +1 -0
- package/dist/src/evaluation/collection.js +61 -0
- package/dist/src/evaluation/collection.js.map +1 -0
- package/dist/src/evaluation/context.js +2 -0
- package/dist/src/evaluation/context.js.map +1 -0
- package/dist/src/evaluation/grading-host.js +28 -0
- package/dist/src/evaluation/grading-host.js.map +1 -0
- package/dist/src/evaluation/grading.js +154 -0
- package/dist/src/evaluation/grading.js.map +1 -0
- package/dist/src/evaluation/invocation-plan.js +312 -0
- package/dist/src/evaluation/invocation-plan.js.map +1 -0
- package/dist/src/evaluation/plan.js +376 -0
- package/dist/src/evaluation/plan.js.map +1 -0
- package/dist/src/evaluation/progress.js +14 -0
- package/dist/src/evaluation/progress.js.map +1 -0
- package/dist/src/evaluation/resources.js +159 -0
- package/dist/src/evaluation/resources.js.map +1 -0
- package/dist/src/evaluation/run.js +148 -0
- package/dist/src/evaluation/run.js.map +1 -0
- package/dist/src/evaluation/scheduler.js +27 -0
- package/dist/src/evaluation/scheduler.js.map +1 -0
- package/dist/src/evaluation/semantic.js +183 -0
- package/dist/src/evaluation/semantic.js.map +1 -0
- package/dist/src/evaluation/trial.js +593 -0
- package/dist/src/evaluation/trial.js.map +1 -0
- package/dist/src/evaluation/types.js +20 -0
- package/dist/src/evaluation/types.js.map +1 -0
- package/dist/src/evidence/host-results.js +67 -0
- package/dist/src/evidence/host-results.js.map +1 -0
- package/dist/src/evidence/retained-artifacts.js +17 -0
- package/dist/src/evidence/retained-artifacts.js.map +1 -0
- package/dist/src/evidence/trial-writer.js +131 -0
- package/dist/src/evidence/trial-writer.js.map +1 -0
- package/dist/src/evidence/usage.js +14 -0
- package/dist/src/evidence/usage.js.map +1 -0
- package/dist/src/executable.js +20 -0
- package/dist/src/executable.js.map +1 -0
- package/dist/src/extension-client.js +212 -0
- package/dist/src/extension-client.js.map +1 -0
- package/dist/src/extension-session.js +236 -0
- package/dist/src/extension-session.js.map +1 -0
- package/dist/src/fixtures/advisory.js +206 -0
- package/dist/src/fixtures/advisory.js.map +1 -0
- package/dist/src/fixtures/bin.js +41 -0
- package/dist/src/fixtures/bin.js.map +1 -0
- package/dist/src/fixtures/cleanup.js +43 -0
- package/dist/src/fixtures/cleanup.js.map +1 -0
- package/dist/src/fixtures/create.js +94 -0
- package/dist/src/fixtures/create.js.map +1 -0
- package/dist/src/fixtures/generated.js +181 -0
- package/dist/src/fixtures/generated.js.map +1 -0
- package/dist/src/fixtures/git-hooks.js +16 -0
- package/dist/src/fixtures/git-hooks.js.map +1 -0
- package/dist/src/fixtures/preparation.js +201 -0
- package/dist/src/fixtures/preparation.js.map +1 -0
- package/dist/src/fixtures/repository.js +255 -0
- package/dist/src/fixtures/repository.js.map +1 -0
- package/dist/src/fixtures/setup.js +175 -0
- package/dist/src/fixtures/setup.js.map +1 -0
- package/dist/src/fixtures/tools.js +57 -0
- package/dist/src/fixtures/tools.js.map +1 -0
- package/dist/src/fixtures/workspace-state.js +155 -0
- package/dist/src/fixtures/workspace-state.js.map +1 -0
- package/dist/src/generated/cli-result.cjs +2 -0
- package/dist/src/generated/extension.cjs +2 -0
- package/dist/src/generated/report.cjs +2 -0
- package/dist/src/generated/run-evidence.cjs +2 -0
- package/dist/src/generated/runtime.cjs +2 -0
- package/dist/src/git.js +72 -0
- package/dist/src/git.js.map +1 -0
- package/dist/src/graders/artifact.js +68 -0
- package/dist/src/graders/artifact.js.map +1 -0
- package/dist/src/graders/git-head.js +112 -0
- package/dist/src/graders/git-head.js.map +1 -0
- package/dist/src/graders/output.js +345 -0
- package/dist/src/graders/output.js.map +1 -0
- package/dist/src/graders/semantic.js +132 -0
- package/dist/src/graders/semantic.js.map +1 -0
- package/dist/src/graders/shell.js +321 -0
- package/dist/src/graders/shell.js.map +1 -0
- package/dist/src/host-diagnostic-redaction.js +52 -0
- package/dist/src/host-diagnostic-redaction.js.map +1 -0
- package/dist/src/host-diagnostics.js +248 -0
- package/dist/src/host-diagnostics.js.map +1 -0
- package/dist/src/hosts/claude-continuation.js +89 -0
- package/dist/src/hosts/claude-continuation.js.map +1 -0
- package/dist/src/hosts/claude-credential.js +121 -0
- package/dist/src/hosts/claude-credential.js.map +1 -0
- package/dist/src/hosts/claude-events.js +136 -0
- package/dist/src/hosts/claude-events.js.map +1 -0
- package/dist/src/hosts/claude-keychain.js +100 -0
- package/dist/src/hosts/claude-keychain.js.map +1 -0
- package/dist/src/hosts/claude-login-recovery.js +67 -0
- package/dist/src/hosts/claude-login-recovery.js.map +1 -0
- package/dist/src/hosts/claude-login-refresh.js +146 -0
- package/dist/src/hosts/claude-login-refresh.js.map +1 -0
- package/dist/src/hosts/claude-login-store.js +124 -0
- package/dist/src/hosts/claude-login-store.js.map +1 -0
- package/dist/src/hosts/claude-login.js +74 -0
- package/dist/src/hosts/claude-login.js.map +1 -0
- package/dist/src/hosts/claude-native-goal.js +138 -0
- package/dist/src/hosts/claude-native-goal.js.map +1 -0
- package/dist/src/hosts/claude-process.js +10 -0
- package/dist/src/hosts/claude-process.js.map +1 -0
- package/dist/src/hosts/claude-refresh-lock.js +76 -0
- package/dist/src/hosts/claude-refresh-lock.js.map +1 -0
- package/dist/src/hosts/claude-repository-invocation.js +229 -0
- package/dist/src/hosts/claude-repository-invocation.js.map +1 -0
- package/dist/src/hosts/claude-settings.js +156 -0
- package/dist/src/hosts/claude-settings.js.map +1 -0
- package/dist/src/hosts/claude-skill-activations.js +131 -0
- package/dist/src/hosts/claude-skill-activations.js.map +1 -0
- package/dist/src/hosts/claude-state.js +237 -0
- package/dist/src/hosts/claude-state.js.map +1 -0
- package/dist/src/hosts/claude-tool-calls.js +150 -0
- package/dist/src/hosts/claude-tool-calls.js.map +1 -0
- package/dist/src/hosts/claude.js +373 -0
- package/dist/src/hosts/claude.js.map +1 -0
- package/dist/src/hosts/codex-app-server.js +764 -0
- package/dist/src/hosts/codex-app-server.js.map +1 -0
- package/dist/src/hosts/codex-configuration.js +91 -0
- package/dist/src/hosts/codex-configuration.js.map +1 -0
- package/dist/src/hosts/codex-events.js +104 -0
- package/dist/src/hosts/codex-events.js.map +1 -0
- package/dist/src/hosts/codex-exec-controls.js +434 -0
- package/dist/src/hosts/codex-exec-controls.js.map +1 -0
- package/dist/src/hosts/codex-invocation.js +54 -0
- package/dist/src/hosts/codex-invocation.js.map +1 -0
- package/dist/src/hosts/codex-marketplace.js +185 -0
- package/dist/src/hosts/codex-marketplace.js.map +1 -0
- package/dist/src/hosts/codex-native-calls.js +516 -0
- package/dist/src/hosts/codex-native-calls.js.map +1 -0
- package/dist/src/hosts/codex-process.js +10 -0
- package/dist/src/hosts/codex-process.js.map +1 -0
- package/dist/src/hosts/codex-profile.js +206 -0
- package/dist/src/hosts/codex-profile.js.map +1 -0
- package/dist/src/hosts/codex-session-files.js +89 -0
- package/dist/src/hosts/codex-session-files.js.map +1 -0
- package/dist/src/hosts/codex-state.js +310 -0
- package/dist/src/hosts/codex-state.js.map +1 -0
- package/dist/src/hosts/codex.js +527 -0
- package/dist/src/hosts/codex.js.map +1 -0
- package/dist/src/hosts/isolation-common.js +43 -0
- package/dist/src/hosts/isolation-common.js.map +1 -0
- package/dist/src/hosts/isolation-roots.js +64 -0
- package/dist/src/hosts/isolation-roots.js.map +1 -0
- package/dist/src/hosts/isolation.js +12 -0
- package/dist/src/hosts/isolation.js.map +1 -0
- package/dist/src/hosts/linux-sandbox.js +127 -0
- package/dist/src/hosts/linux-sandbox.js.map +1 -0
- package/dist/src/hosts/mac-sandbox.js +137 -0
- package/dist/src/hosts/mac-sandbox.js.map +1 -0
- package/dist/src/hosts/native-controls.js +151 -0
- package/dist/src/hosts/native-controls.js.map +1 -0
- package/dist/src/hosts/native-request.js +33 -0
- package/dist/src/hosts/native-request.js.map +1 -0
- package/dist/src/hosts/process-lifecycle.js +167 -0
- package/dist/src/hosts/process-lifecycle.js.map +1 -0
- package/dist/src/hosts/protected-worktrees.js +81 -0
- package/dist/src/hosts/protected-worktrees.js.map +1 -0
- package/dist/src/hosts/skill-hook-catalog.js +100 -0
- package/dist/src/hosts/skill-hook-catalog.js.map +1 -0
- package/dist/src/hosts/skill-hook-handler.js +228 -0
- package/dist/src/hosts/skill-hook-handler.js.map +1 -0
- package/dist/src/hosts/skill-hooks.js +423 -0
- package/dist/src/hosts/skill-hooks.js.map +1 -0
- package/dist/src/hosts/workspace-fingerprint.js +72 -0
- package/dist/src/hosts/workspace-fingerprint.js.map +1 -0
- package/dist/src/identity.js +167 -0
- package/dist/src/identity.js.map +1 -0
- package/dist/src/instrumentation.js +83 -0
- package/dist/src/instrumentation.js.map +1 -0
- package/dist/src/native-transcript-state.js +85 -0
- package/dist/src/native-transcript-state.js.map +1 -0
- package/dist/src/native-transcript-view.js +65 -0
- package/dist/src/native-transcript-view.js.map +1 -0
- package/dist/src/native-transcripts.js +208 -0
- package/dist/src/native-transcripts.js.map +1 -0
- package/dist/src/paths.js +13 -0
- package/dist/src/paths.js.map +1 -0
- package/dist/src/process-lifecycle.js +101 -0
- package/dist/src/process-lifecycle.js.map +1 -0
- package/dist/src/provenance.js +263 -0
- package/dist/src/provenance.js.map +1 -0
- package/dist/src/prune.js +228 -0
- package/dist/src/prune.js.map +1 -0
- package/dist/src/report.js +276 -0
- package/dist/src/report.js.map +1 -0
- package/dist/src/resolved-case.js +29 -0
- package/dist/src/resolved-case.js.map +1 -0
- package/dist/src/results-lock.js +40 -0
- package/dist/src/results-lock.js.map +1 -0
- package/dist/src/results.js +127 -0
- package/dist/src/results.js.map +1 -0
- package/dist/src/run-owner.js +283 -0
- package/dist/src/run-owner.js.map +1 -0
- package/dist/src/runner-paths.js +11 -0
- package/dist/src/runner-paths.js.map +1 -0
- package/dist/src/runtime/config.js +323 -0
- package/dist/src/runtime/config.js.map +1 -0
- package/dist/src/runtime/discovery-apple.js +65 -0
- package/dist/src/runtime/discovery-apple.js.map +1 -0
- package/dist/src/runtime/discovery-providers.js +37 -0
- package/dist/src/runtime/discovery-providers.js.map +1 -0
- package/dist/src/runtime/discovery.js +40 -0
- package/dist/src/runtime/discovery.js.map +1 -0
- package/dist/src/runtime/hook-processes.js +81 -0
- package/dist/src/runtime/hook-processes.js.map +1 -0
- package/dist/src/runtime/hooks.js +304 -0
- package/dist/src/runtime/hooks.js.map +1 -0
- package/dist/src/runtime/paths.js +244 -0
- package/dist/src/runtime/paths.js.map +1 -0
- package/dist/src/runtime/query.js +76 -0
- package/dist/src/runtime/query.js.map +1 -0
- package/dist/src/runtime/seeds.js +98 -0
- package/dist/src/runtime/seeds.js.map +1 -0
- package/dist/src/runtime/shared-cache.js +81 -0
- package/dist/src/runtime/shared-cache.js.map +1 -0
- package/dist/src/runtime/state.js +106 -0
- package/dist/src/runtime/state.js.map +1 -0
- package/dist/src/runtime-process.js +148 -0
- package/dist/src/runtime-process.js.map +1 -0
- package/dist/src/schema-types.js +2 -0
- package/dist/src/schema-types.js.map +1 -0
- package/dist/src/schema.js +16 -0
- package/dist/src/schema.js.map +1 -0
- package/dist/src/shell.js +5 -0
- package/dist/src/shell.js.map +1 -0
- package/dist/src/sqlite-lock.js +18 -0
- package/dist/src/sqlite-lock.js.map +1 -0
- package/dist/src/storage.js +21 -0
- package/dist/src/storage.js.map +1 -0
- package/dist/src/transcript-retention.js +109 -0
- package/dist/src/transcript-retention.js.map +1 -0
- package/dist/src/value-guards.js +26 -0
- package/dist/src/value-guards.js.map +1 -0
- package/docs/architecture.md +5 -2
- package/docs/creating-cases.md +2 -3
- package/docs/development-cli.md +12 -7
- package/docs/documentation-quality.md +4 -3
- package/docs/evaluation-workflow.md +8 -8
- package/docs/getting-started.md +7 -6
- package/docs/guide-evaluation.md +1 -1
- package/docs/identity-v1.md +4 -1
- package/docs/installing.md +31 -13
- package/docs/licensing.md +2 -2
- package/docs/native-hosts.md +1 -1
- package/docs/recipes.md +41 -40
- package/docs/releases.md +20 -4
- package/docs/run-settings-v1.md +3 -3
- package/docs/specs/repository-guide.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/typescript-quality.md +18 -1
- package/examples/recipes/executable.ts +19 -0
- package/examples/recipes/extension-artifacts.ts +1 -1
- package/examples/recipes/extension.ts +2 -1
- package/examples/recipes/modules.ts +9 -0
- package/examples/recipes/run-inputs.ts +14 -19
- package/examples/recipes/run-options.ts +9 -8
- package/examples/recipes/run.ts +21 -7
- package/examples/templates/repository-skill/extension-data.ts +1 -1
- package/examples/templates/repository-skill/extension.ts +2 -1
- package/package.json +14 -6
- package/src/cli/demo.ts +3 -2
- package/src/cli/help.ts +4 -3
- package/src/cli/init-settings.ts +5 -4
- package/src/cli/init.ts +9 -8
- package/src/cli/invocation.ts +2 -1
- package/src/cli/preparation.ts +2 -2
- package/src/cli.ts +1 -1
- package/src/executable.ts +19 -0
- package/src/extension-client.ts +6 -12
- package/src/fixtures/setup.ts +3 -2
- package/src/git.ts +2 -1
- package/src/graders/git-head.ts +3 -2
- package/src/graders/shell.ts +4 -3
- package/src/hosts/claude-keychain.ts +3 -2
- package/src/hosts/claude-login-refresh.ts +3 -2
- package/src/hosts/claude-refresh-lock.ts +2 -1
- package/src/hosts/isolation-roots.ts +3 -2
- package/src/hosts/process-lifecycle.ts +4 -6
- package/src/hosts/protected-worktrees.ts +2 -1
- package/src/hosts/skill-hook-handler.ts +1 -1
- package/src/hosts/skill-hooks.ts +10 -2
- package/src/provenance.ts +7 -6
- package/src/results-lock.ts +4 -4
- package/src/run-owner.ts +15 -9
- package/src/runner-paths.ts +21 -0
- package/src/runtime/hook-processes.ts +2 -1
- package/src/runtime/hooks.ts +9 -7
- package/src/runtime-process.ts +194 -0
- package/src/sqlite-lock.ts +25 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["paths.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n access,\n lstat,\n readdir,\n readlink,\n realpath,\n stat,\n} from \"node:fs/promises\";\nimport { constants, lstatSync, readlinkSync, realpathSync } from \"node:fs\";\nimport { homedir, userInfo } from \"node:os\";\nimport { nativeStateProtectedRoots } from \"../native-transcript-state\";\nimport { workspaceStatePath } from \"../fixtures/workspace-state\";\nimport { insideRoot as insideRuntimeRoot } from \"../paths\";\nimport { containsControl } from \"../value-guards\";\nexport { insideRoot as insideRuntimeRoot } from \"../paths\";\nimport {\n basename,\n delimiter,\n dirname,\n isAbsolute,\n join,\n resolve,\n} from \"node:path\";\n\nexport function isRuntimeHomeRoot(root: string): boolean {\n return [homedir(), userInfo().homedir]\n .map((home) => resolve(home))\n .includes(root);\n}\n\nexport function requireRuntimeReadRoots(\n readRoots: string[],\n protectedRoots: string[],\n): void {\n const hardRoots = [\n ...protectedRoots.filter((root) => !isRuntimeHomeRoot(root)),\n ...runtimeCredentialRoots(),\n ...nativeStateProtectedRoots(),\n workspaceStatePath(),\n ];\n for (const root of readRoots) {\n if (root === \"/\" || isRuntimeHomeRoot(root))\n throw new Error(\"runtime read root is too broad\");\n if (overlapsRuntimeRoots([root], hardRoots))\n throw new Error(\"runtime read root overlaps protected data\");\n }\n}\n\nfunction runtimeCredentialRoots(): string[] {\n return credentialRoots().flatMap((root) => [\n root,\n canonicalCredentialRoot(root),\n ]);\n}\n\nfunction overlapsRuntimeRoots(roots: string[], deniedRoots: string[]): boolean {\n return roots.some((root) =>\n deniedRoots.some(\n (denied) =>\n insideRuntimeRoot(denied, root) || insideRuntimeRoot(root, denied),\n ),\n );\n}\n\nfunction canonicalCredentialRoot(path: string): string {\n try {\n return realpathSync(path);\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code !== \"ENOENT\") throw cause;\n const target = missingCredentialLink(path);\n if (target) return canonicalCredentialRoot(target);\n return join(canonicalCredentialRoot(dirname(path)), basename(path));\n }\n}\n\nfunction missingCredentialLink(path: string): string | null {\n try {\n return lstatSync(path).isSymbolicLink()\n ? resolve(dirname(path), readlinkSync(path))\n : null;\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw cause;\n }\n}\n\nexport function runtimeExecutableProtection(\n readRoots: string[],\n protectedRoots: string[],\n allowHomeRuntime: boolean,\n): string[] {\n if (!allowHomeRuntime) return protectedRoots;\n requireRuntimeReadRoots(readRoots, []);\n return protectedRoots.filter((root) => !isRuntimeHomeRoot(root));\n}\n\nfunction credentialRoots(): string[] {\n return [homedir(), userInfo().homedir].flatMap((home) =>\n [\n \".ssh\",\n \".aws\",\n \".codex\",\n \".claude\",\n \".config/gcloud\",\n \".local/share/keyrings\",\n ].map((path) => join(home, path)),\n );\n}\n\nexport async function canonicalRuntimeRoot(path: string): Promise<string> {\n try {\n return await realpath(path);\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code !== \"ENOENT\") throw cause;\n const target = await missingRuntimeLink(path);\n if (target) return canonicalRuntimeRoot(target);\n return join(await canonicalRuntimeRoot(dirname(path)), basename(path));\n }\n}\n\nasync function missingRuntimeLink(path: string): Promise<string | null> {\n try {\n return (await lstat(path)).isSymbolicLink()\n ? resolve(dirname(path), await readlink(path))\n : null;\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw cause;\n }\n}\n\nfunction installationRoot(directory: string): string | null {\n if (basename(directory) !== \"bin\") return null;\n const root = dirname(directory);\n const broad = [\n \"/\",\n \"/usr\",\n \"/usr/local\",\n \"/opt/homebrew\",\n ...[homedir(), userInfo().homedir].flatMap((home) => [\n home,\n join(home, \".local\"),\n ]),\n ];\n return broad.includes(root) ? null : root;\n}\n\nasync function linkedToolRoots(directory: string): Promise<string[]> {\n const entries = await readdir(directory, { withFileTypes: true });\n if (entries.length > 4096)\n throw new Error(\"runtime PATH directory exceeds entry limit\");\n const roots: string[] = [];\n for (const entry of entries.filter((entry) => entry.isSymbolicLink())) {\n const target = await linkedTarget(join(directory, entry.name));\n if (!target) continue;\n const bin = dirname(target);\n roots.push(bin);\n const installation = installationRoot(bin);\n if (installation) roots.push(installation);\n }\n return roots;\n}\n\nasync function linkedTarget(path: string): Promise<string | null> {\n try {\n const target = await realpath(path);\n return (await stat(target)).isFile() ? target : null;\n } catch (cause) {\n if (\n [\"EACCES\", \"EPERM\", \"ENOENT\"].includes(\n (cause as NodeJS.ErrnoException).code ?? \"\",\n )\n )\n return null;\n throw cause;\n }\n}\n\n/** Missing PATH entries are inert; relative entries cannot grant host access. */\nexport async function runtimePathDirectories(\n path: string | undefined,\n): Promise<string[]> {\n if (path === undefined) return [];\n const roots: string[] = [];\n for (const entry of path.split(delimiter)) {\n roots.push(...(await pathEntryRoots(entry)));\n }\n return [...new Set(roots)].sort();\n}\n\nfunction requirePathEntry(entry: string): void {\n if (!isAbsolute(entry) || containsControl(entry))\n throw new Error(\"runtime PATH entries must be absolute and nonempty\");\n}\n\n/** Ambient tool discovery must not grant access to the operator's credentials. */\nexport async function runtimeInheritedPath(path: string): Promise<string> {\n const deniedRoots = runtimeCredentialRoots();\n const selected: string[] = [];\n for (const entry of path.split(delimiter)) {\n requirePathEntry(entry);\n const paths = [resolve(entry), await canonicalRuntimeRoot(entry)];\n if (overlapsRuntimeRoots(paths, deniedRoots)) continue;\n if (overlapsRuntimeRoots(await pathEntryRoots(entry), deniedRoots))\n continue;\n selected.push(entry);\n }\n if (selected.length === 0)\n throw new Error(\"runtime inherited PATH has no unprotected entries\");\n return selected.join(delimiter);\n}\n\nasync function pathEntryRoots(entry: string): Promise<string[]> {\n requirePathEntry(entry);\n const directory = await existingDirectory(entry);\n if (!directory) return [];\n const installation = installationRoot(directory);\n return [\n resolve(entry),\n directory,\n ...(installation ? [installation] : []),\n ...(await linkedToolRoots(directory)),\n ];\n}\n\nasync function existingDirectory(path: string): Promise<string | null> {\n try {\n const canonical = await realpath(path);\n if (!(await stat(canonical)).isDirectory())\n throw new Error(\"runtime PATH entry is not a directory\");\n await access(canonical, constants.R_OK | constants.X_OK);\n return canonical;\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw cause;\n }\n}\n\nexport function runtimeDeclaredPath(\n path: string,\n environment: Record<string, string>,\n base: string,\n): string {\n const expanded = path.replace(\n /\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g,\n (_match: string, name: string) => {\n const value = environment[name];\n if (value === undefined)\n throw new Error(`runtime path variable is undeclared: ${name}`);\n return value;\n },\n );\n if (containsControl(expanded) || expanded.includes(\"${\"))\n throw new Error(\"runtime path contains invalid characters\");\n return expanded.startsWith(\"~/\")\n ? join(homedir(), expanded.slice(2))\n : resolve(base, expanded);\n}\n\nexport async function declaredRuntimeRoots(\n paths: string[],\n environment: Record<string, string>,\n base: string,\n): Promise<string[]> {\n const roots = await Promise.all(\n paths.map(async (path) => {\n const directory = await existingDirectory(\n runtimeDeclaredPath(path, environment, base),\n );\n if (!directory) throw new Error(\"declared runtime read root is missing\");\n return [directory, runtimeDeclaredPath(path, environment, base)];\n }),\n );\n return [...new Set(roots.flat())];\n}\n\nexport async function optionalRuntimeRoots(\n paths: string[],\n environment: Record<string, string>,\n base: string,\n protectedRoots: string[] = [],\n): Promise<{ roots: string[]; skipped: string[] }> {\n const roots: string[] = [],\n skipped: string[] = [];\n for (const path of paths) {\n const declared = runtimeDeclaredPath(path, environment, base);\n requireRuntimeReadRoots(\n [declared, await canonicalRuntimeRoot(declared)],\n protectedRoots,\n );\n const directory = await existingDirectory(declared);\n if (directory) roots.push(directory, declared);\n else skipped.push(declared);\n }\n return {\n roots: [...new Set(roots)].sort(),\n skipped: [...new Set(skipped)].sort(),\n };\n}\n"]}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { terminateOwnedProcessGroup } from "../process-lifecycle.js";
|
|
4
|
+
export class RuntimeMetadataQueryError extends Error {
|
|
5
|
+
noActiveDeveloper;
|
|
6
|
+
constructor(message, noActiveDeveloper = false) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.noActiveDeveloper = noActiveDeveloper;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function exitError(code, stderr) {
|
|
12
|
+
const noSelection = code === 2 &&
|
|
13
|
+
Buffer.concat(stderr)
|
|
14
|
+
.toString("utf8")
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.includes("unable to get active developer directory");
|
|
17
|
+
return new RuntimeMetadataQueryError(`exit ${String(code)}`, noSelection);
|
|
18
|
+
}
|
|
19
|
+
function stopQuery(child) {
|
|
20
|
+
if (child.pid === undefined)
|
|
21
|
+
return;
|
|
22
|
+
terminateOwnedProcessGroup({ pid: child.pid, kill: (signal) => child.kill(signal) }, { alwaysGroup: true, fallbackError: "propagate" });
|
|
23
|
+
}
|
|
24
|
+
/** Bound both output streams and stop every descendant of this metadata query. */
|
|
25
|
+
export function runtimeMetadataQuery(executable, argument, environment) {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const child = spawn(executable, [argument], {
|
|
28
|
+
detached: true,
|
|
29
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
30
|
+
env: {
|
|
31
|
+
...environment,
|
|
32
|
+
HOME: homedir(),
|
|
33
|
+
HOMEBREW_NO_AUTO_UPDATE: "1",
|
|
34
|
+
LC_ALL: "C",
|
|
35
|
+
LANG: "C",
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const chunks = [];
|
|
39
|
+
const stderr = [];
|
|
40
|
+
let bytes = 0;
|
|
41
|
+
const fail = (reason, cause) => {
|
|
42
|
+
stopQuery(child);
|
|
43
|
+
reject(new Error(reason, { cause }));
|
|
44
|
+
};
|
|
45
|
+
const timer = setTimeout(() => {
|
|
46
|
+
fail("timeout exceeded");
|
|
47
|
+
}, 3000);
|
|
48
|
+
const consume = (chunk) => {
|
|
49
|
+
bytes += chunk.length;
|
|
50
|
+
if (bytes > 8192)
|
|
51
|
+
fail("output limit exceeded");
|
|
52
|
+
};
|
|
53
|
+
child.stdout.on("data", (chunk) => {
|
|
54
|
+
consume(chunk);
|
|
55
|
+
if (bytes <= 8192)
|
|
56
|
+
chunks.push(chunk);
|
|
57
|
+
});
|
|
58
|
+
child.stderr.on("data", (chunk) => {
|
|
59
|
+
consume(chunk);
|
|
60
|
+
if (bytes <= 8192)
|
|
61
|
+
stderr.push(chunk);
|
|
62
|
+
});
|
|
63
|
+
child.on("error", (cause) => {
|
|
64
|
+
fail("provider unavailable", cause);
|
|
65
|
+
});
|
|
66
|
+
child.on("close", (code) => {
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
stopQuery(child);
|
|
69
|
+
if (code !== 0)
|
|
70
|
+
reject(exitError(code, stderr));
|
|
71
|
+
else
|
|
72
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,2BAA2B;AAC9D,OAAO,EAAE,OAAO,EAAE,gBAAgB;AAClC,OAAO,EAAE,0BAA0B,EAAE,gCAA6B;AAElE,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAGvC;IAFX,YACE,OAAe,EACN,oBAAoB,KAAK;QAElC,KAAK,CAAC,OAAO,CAAC,CAAC;QAFN,sBAAiB,GAAjB,iBAAiB,CAAQ;IAGpC,CAAC;CACF;AAED,SAAS,SAAS,CAChB,IAAmB,EACnB,MAAgB;IAEhB,MAAM,WAAW,GACf,IAAI,KAAK,CAAC;QACV,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;aAClB,QAAQ,CAAC,MAAM,CAAC;aAChB,WAAW,EAAE;aACb,QAAQ,CAAC,0CAA0C,CAAC,CAAC;IAC1D,OAAO,IAAI,yBAAyB,CAAC,QAAQ,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,SAAS,CAAC,KAAmB;IACpC,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS;QAAE,OAAO;IACpC,0BAA0B,CACxB,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,EACxD,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,CAClD,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,oBAAoB,CAClC,UAAkB,EAClB,QAAgB,EAChB,WAAmC;IAEnC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC,QAAQ,CAAC,EAAE;YAC1C,QAAQ,EAAE,IAAI;YACd,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;YACjC,GAAG,EAAE;gBACH,GAAG,WAAW;gBACd,IAAI,EAAE,OAAO,EAAE;gBACf,uBAAuB,EAAE,GAAG;gBAC5B,MAAM,EAAE,GAAG;gBACX,IAAI,EAAE,GAAG;aACV;SACF,CAAC,CAAC;QACH,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,MAAM,IAAI,GAAG,CAAC,MAAc,EAAE,KAAe,EAAE,EAAE;YAC/C,SAAS,CAAC,KAAK,CAAC,CAAC;YACjB,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAC3B,CAAC,EAAE,IAAI,CAAC,CAAC;QACT,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,EAAE;YAChC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;YACtB,IAAI,KAAK,GAAG,IAAI;gBAAE,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAClD,CAAC,CAAC;QACF,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxC,OAAO,CAAC,KAAK,CAAC,CAAC;YACf,IAAI,KAAK,IAAI,IAAI;gBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxC,OAAO,CAAC,KAAK,CAAC,CAAC;YACf,IAAI,KAAK,IAAI,IAAI;gBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE;YACjC,IAAI,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,SAAS,CAAC,KAAK,CAAC,CAAC;YACjB,IAAI,IAAI,KAAK,CAAC;gBAAE,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;;gBAC3C,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { spawn, type ChildProcess } from \"node:child_process\";\nimport { homedir } from \"node:os\";\nimport { terminateOwnedProcessGroup } from \"../process-lifecycle\";\n\nexport class RuntimeMetadataQueryError extends Error {\n constructor(\n message: string,\n readonly noActiveDeveloper = false,\n ) {\n super(message);\n }\n}\n\nfunction exitError(\n code: number | null,\n stderr: Buffer[],\n): RuntimeMetadataQueryError {\n const noSelection =\n code === 2 &&\n Buffer.concat(stderr)\n .toString(\"utf8\")\n .toLowerCase()\n .includes(\"unable to get active developer directory\");\n return new RuntimeMetadataQueryError(`exit ${String(code)}`, noSelection);\n}\n\nfunction stopQuery(child: ChildProcess): void {\n if (child.pid === undefined) return;\n terminateOwnedProcessGroup(\n { pid: child.pid, kill: (signal) => child.kill(signal) },\n { alwaysGroup: true, fallbackError: \"propagate\" },\n );\n}\n\n/** Bound both output streams and stop every descendant of this metadata query. */\nexport function runtimeMetadataQuery(\n executable: string,\n argument: string,\n environment: Record<string, string>,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const child = spawn(executable, [argument], {\n detached: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n env: {\n ...environment,\n HOME: homedir(),\n HOMEBREW_NO_AUTO_UPDATE: \"1\",\n LC_ALL: \"C\",\n LANG: \"C\",\n },\n });\n const chunks: Buffer[] = [];\n const stderr: Buffer[] = [];\n let bytes = 0;\n const fail = (reason: string, cause?: unknown) => {\n stopQuery(child);\n reject(new Error(reason, { cause }));\n };\n const timer = setTimeout(() => {\n fail(\"timeout exceeded\");\n }, 3000);\n const consume = (chunk: Buffer) => {\n bytes += chunk.length;\n if (bytes > 8192) fail(\"output limit exceeded\");\n };\n child.stdout.on(\"data\", (chunk: Buffer) => {\n consume(chunk);\n if (bytes <= 8192) chunks.push(chunk);\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n consume(chunk);\n if (bytes <= 8192) stderr.push(chunk);\n });\n child.on(\"error\", (cause: Error) => {\n fail(\"provider unavailable\", cause);\n });\n child.on(\"close\", (code) => {\n clearTimeout(timer);\n stopQuery(child);\n if (code !== 0) reject(exitError(code, stderr));\n else resolve(Buffer.concat(chunks).toString(\"utf8\"));\n });\n });\n}\n"]}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, readdir, realpath, stat, writeFile, } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { hashJson, sha256 } from "../identity.js";
|
|
4
|
+
import { insideRuntimeRoot } from "./paths.js";
|
|
5
|
+
class SeedSnapshot {
|
|
6
|
+
root;
|
|
7
|
+
retainContents;
|
|
8
|
+
entries = 0;
|
|
9
|
+
bytes = 0;
|
|
10
|
+
files = [];
|
|
11
|
+
constructor(root, retainContents = false) {
|
|
12
|
+
this.root = root;
|
|
13
|
+
this.retainContents = retainContents;
|
|
14
|
+
}
|
|
15
|
+
async digest() {
|
|
16
|
+
await this.visit("", new Set());
|
|
17
|
+
return hashJson(this.files.map(({ path, sha256, mode }) => ({ path, sha256, mode })));
|
|
18
|
+
}
|
|
19
|
+
async visit(path, ancestors) {
|
|
20
|
+
const logical = join(this.root, path);
|
|
21
|
+
const canonical = await realpath(logical);
|
|
22
|
+
if (!insideRuntimeRoot(this.root, canonical))
|
|
23
|
+
throw new Error("runtime seed link escapes its source");
|
|
24
|
+
if (++this.entries > 16384)
|
|
25
|
+
throw new Error("runtime seed exceeds entry limit");
|
|
26
|
+
const info = await stat(canonical);
|
|
27
|
+
if (info.isDirectory()) {
|
|
28
|
+
this.files.push({
|
|
29
|
+
path,
|
|
30
|
+
sha256: "directory",
|
|
31
|
+
mode: info.mode & 0o777,
|
|
32
|
+
bytes: null,
|
|
33
|
+
});
|
|
34
|
+
await this.visitDirectory(path, canonical, ancestors);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (!info.isFile())
|
|
38
|
+
throw new Error("runtime seed contains an unsupported file");
|
|
39
|
+
await this.visitFile(path, canonical, info);
|
|
40
|
+
}
|
|
41
|
+
async visitDirectory(path, canonical, ancestors) {
|
|
42
|
+
if (ancestors.has(canonical))
|
|
43
|
+
throw new Error("runtime seed contains a directory cycle");
|
|
44
|
+
const parents = new Set([...ancestors, canonical]);
|
|
45
|
+
for (const name of (await readdir(canonical)).sort())
|
|
46
|
+
await this.visit(join(path, name), parents);
|
|
47
|
+
}
|
|
48
|
+
async visitFile(path, canonical, info) {
|
|
49
|
+
this.bytes += info.size;
|
|
50
|
+
if (this.bytes > 512 * 1024 * 1024)
|
|
51
|
+
throw new Error("runtime seed exceeds 512 MiB");
|
|
52
|
+
const bytes = await readFile(canonical);
|
|
53
|
+
this.files.push({
|
|
54
|
+
path,
|
|
55
|
+
sha256: sha256(bytes),
|
|
56
|
+
mode: info.mode & 0o777,
|
|
57
|
+
bytes: this.retainContents ? bytes : null,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async materialize(target) {
|
|
61
|
+
for (const file of this.files) {
|
|
62
|
+
const path = join(target, file.path);
|
|
63
|
+
if (file.bytes === null)
|
|
64
|
+
await mkdir(path, { mode: 0o700 });
|
|
65
|
+
else
|
|
66
|
+
await writeFile(path, file.bytes, {
|
|
67
|
+
flag: "wx",
|
|
68
|
+
mode: file.mode,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
for (const file of [...this.files].reverse())
|
|
72
|
+
await chmod(join(target, file.path), file.mode);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export async function runtimeSeedDigest(source) {
|
|
76
|
+
return new SeedSnapshot(await realpath(source)).digest();
|
|
77
|
+
}
|
|
78
|
+
const verifiedSeeds = new WeakMap();
|
|
79
|
+
/** Contents stay in runner memory; no writable role or filesystem alias owns them. */
|
|
80
|
+
export async function materializeRuntimeSeed(seed, target) {
|
|
81
|
+
let verified = verifiedSeeds.get(seed);
|
|
82
|
+
if (verified?.source !== seed.source || verified.digest !== seed.sha256) {
|
|
83
|
+
verified = {
|
|
84
|
+
source: seed.source,
|
|
85
|
+
digest: seed.sha256,
|
|
86
|
+
snapshot: verifiedSnapshot(seed),
|
|
87
|
+
};
|
|
88
|
+
verifiedSeeds.set(seed, verified);
|
|
89
|
+
}
|
|
90
|
+
await (await verified.snapshot).materialize(target);
|
|
91
|
+
}
|
|
92
|
+
async function verifiedSnapshot(seed) {
|
|
93
|
+
const snapshot = new SeedSnapshot(await realpath(seed.source), true);
|
|
94
|
+
if ((await snapshot.digest()) !== seed.sha256)
|
|
95
|
+
throw new Error("runtime seed changed after configuration snapshot");
|
|
96
|
+
return snapshot;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=seeds.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seeds.js","sourceRoot":"","sources":["seeds.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n chmod,\n mkdir,\n readFile,\n readdir,\n realpath,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { hashJson, sha256 } from \"../identity\";\nimport type { RuntimePolicy } from \"./config\";\nimport { insideRuntimeRoot } from \"./paths\";\n\nclass SeedSnapshot {\n private entries = 0;\n private bytes = 0;\n private readonly files: {\n path: string;\n sha256: string;\n mode: number;\n bytes: Buffer | null;\n }[] = [];\n constructor(\n private readonly root: string,\n private readonly retainContents = false,\n ) {}\n\n async digest(): Promise<string> {\n await this.visit(\"\", new Set());\n return hashJson(\n this.files.map(({ path, sha256, mode }) => ({ path, sha256, mode })),\n );\n }\n\n private async visit(path: string, ancestors: Set<string>): Promise<void> {\n const logical = join(this.root, path);\n const canonical = await realpath(logical);\n if (!insideRuntimeRoot(this.root, canonical))\n throw new Error(\"runtime seed link escapes its source\");\n if (++this.entries > 16384)\n throw new Error(\"runtime seed exceeds entry limit\");\n const info = await stat(canonical);\n if (info.isDirectory()) {\n this.files.push({\n path,\n sha256: \"directory\",\n mode: info.mode & 0o777,\n bytes: null,\n });\n await this.visitDirectory(path, canonical, ancestors);\n return;\n }\n if (!info.isFile())\n throw new Error(\"runtime seed contains an unsupported file\");\n await this.visitFile(path, canonical, info);\n }\n\n private async visitDirectory(\n path: string,\n canonical: string,\n ancestors: Set<string>,\n ): Promise<void> {\n if (ancestors.has(canonical))\n throw new Error(\"runtime seed contains a directory cycle\");\n const parents = new Set([...ancestors, canonical]);\n for (const name of (await readdir(canonical)).sort())\n await this.visit(join(path, name), parents);\n }\n\n private async visitFile(\n path: string,\n canonical: string,\n info: { size: number; mode: number },\n ): Promise<void> {\n this.bytes += info.size;\n if (this.bytes > 512 * 1024 * 1024)\n throw new Error(\"runtime seed exceeds 512 MiB\");\n const bytes = await readFile(canonical);\n this.files.push({\n path,\n sha256: sha256(bytes),\n mode: info.mode & 0o777,\n bytes: this.retainContents ? bytes : null,\n });\n }\n async materialize(target: string): Promise<void> {\n for (const file of this.files) {\n const path = join(target, file.path);\n if (file.bytes === null) await mkdir(path, { mode: 0o700 });\n else\n await writeFile(path, file.bytes, {\n flag: \"wx\",\n mode: file.mode,\n });\n }\n for (const file of [...this.files].reverse())\n await chmod(join(target, file.path), file.mode);\n }\n}\n\nexport async function runtimeSeedDigest(source: string): Promise<string> {\n return new SeedSnapshot(await realpath(source)).digest();\n}\n\ntype RuntimeSeed = NonNullable<RuntimePolicy[\"seeds\"]>[number];\nconst verifiedSeeds = new WeakMap<\n RuntimeSeed,\n { source: string; digest: string; snapshot: Promise<SeedSnapshot> }\n>();\n\n/** Contents stay in runner memory; no writable role or filesystem alias owns them. */\nexport async function materializeRuntimeSeed(\n seed: RuntimeSeed,\n target: string,\n): Promise<void> {\n let verified = verifiedSeeds.get(seed);\n if (verified?.source !== seed.source || verified.digest !== seed.sha256) {\n verified = {\n source: seed.source,\n digest: seed.sha256,\n snapshot: verifiedSnapshot(seed),\n };\n verifiedSeeds.set(seed, verified);\n }\n await (await verified.snapshot).materialize(target);\n}\n\nasync function verifiedSnapshot(seed: RuntimeSeed): Promise<SeedSnapshot> {\n const snapshot = new SeedSnapshot(await realpath(seed.source), true);\n if ((await snapshot.digest()) !== seed.sha256)\n throw new Error(\"runtime seed changed after configuration snapshot\");\n return snapshot;\n}\n"]}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { access, lstat, realpath, symlink } from "node:fs/promises";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { userInfo } from "node:os";
|
|
4
|
+
import { isAbsolute, join } from "node:path";
|
|
5
|
+
import { declaredRuntimeRoots, requireRuntimeReadRoots } from "./paths.js";
|
|
6
|
+
export async function resolveSharedCaches(caches, environment, base, protectedRoots) {
|
|
7
|
+
return Promise.all(caches.map(async (cache) => {
|
|
8
|
+
const roots = await declaredRuntimeRoots([cache.source], environment, base);
|
|
9
|
+
requireRuntimeReadRoots(roots, protectedRoots);
|
|
10
|
+
const [declared] = roots;
|
|
11
|
+
if (!declared)
|
|
12
|
+
throw new Error("runtime shared cache source is missing");
|
|
13
|
+
const source = await realpath(declared);
|
|
14
|
+
await requireSharedCache(source);
|
|
15
|
+
return { ...cache, source };
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
export function selectedSharedCaches(policy, role = "candidate") {
|
|
19
|
+
return (policy?.sharedCacheDirectories ?? []).filter((cache) => cache.roles.includes(role));
|
|
20
|
+
}
|
|
21
|
+
export async function sharedCacheRoots(policy, role = "candidate", protectedRoots) {
|
|
22
|
+
const roots = selectedSharedCaches(policy, role).map((cache) => cache.source);
|
|
23
|
+
return requireWritableCacheRoots(roots, protectedRoots);
|
|
24
|
+
}
|
|
25
|
+
export function requireSharedCachePolicy(policy) {
|
|
26
|
+
if (!policy)
|
|
27
|
+
return;
|
|
28
|
+
const caches = policy.sharedCacheDirectories ?? [];
|
|
29
|
+
const protectedRoots = [
|
|
30
|
+
...policy.readOnlyRoots,
|
|
31
|
+
...(policy.seeds ?? []).map((seed) => seed.source),
|
|
32
|
+
];
|
|
33
|
+
for (const cache of caches) {
|
|
34
|
+
requireRuntimeReadRoots([cache.source], [
|
|
35
|
+
...protectedRoots,
|
|
36
|
+
...caches
|
|
37
|
+
.filter((other) => other !== cache)
|
|
38
|
+
.map((other) => other.source),
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function sharedCacheDenyRoots(policy, role = "candidate") {
|
|
43
|
+
return (policy?.sharedCacheDirectories ?? [])
|
|
44
|
+
.filter((cache) => !cache.roles.includes(role))
|
|
45
|
+
.map((cache) => cache.source);
|
|
46
|
+
}
|
|
47
|
+
export async function requireWritableCacheRoots(roots, protectedRoots) {
|
|
48
|
+
requireRuntimeReadRoots(roots, protectedRoots);
|
|
49
|
+
for (const root of roots)
|
|
50
|
+
await requireSharedCache(root);
|
|
51
|
+
return roots;
|
|
52
|
+
}
|
|
53
|
+
async function requireSharedCache(source) {
|
|
54
|
+
if (!isAbsolute(source) || (await realpath(source)) !== source)
|
|
55
|
+
throw new Error("runtime shared cache must be a canonical directory");
|
|
56
|
+
const info = await lstat(source);
|
|
57
|
+
if (!info.isDirectory() || info.uid !== userInfo().uid)
|
|
58
|
+
throw new Error("runtime shared cache must be an owned directory");
|
|
59
|
+
await access(source, constants.R_OK | constants.W_OK | constants.X_OK);
|
|
60
|
+
}
|
|
61
|
+
/** A slot aliases the operator-selected cache; its contents are never copied. */
|
|
62
|
+
export async function linkSharedCaches(policy, role, root) {
|
|
63
|
+
for (const cache of selectedSharedCaches(policy, role)) {
|
|
64
|
+
const target = join(root, cache.target);
|
|
65
|
+
try {
|
|
66
|
+
await symlink(cache.source, target, "dir");
|
|
67
|
+
}
|
|
68
|
+
catch (cause) {
|
|
69
|
+
if (cause.code !== "EEXIST")
|
|
70
|
+
throw cause;
|
|
71
|
+
await requireCacheSlot(target, cache.source, cause);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function requireCacheSlot(target, source, cause) {
|
|
76
|
+
if (!(await lstat(target)).isSymbolicLink())
|
|
77
|
+
throw new Error("runtime shared cache slot was replaced", { cause });
|
|
78
|
+
if ((await realpath(target)) !== source)
|
|
79
|
+
throw new Error("runtime shared cache slot changed its source", { cause });
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=shared-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared-cache.js","sourceRoot":"","sources":["shared-cache.ts"],"names":[],"mappings":"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","sourcesContent":["import { access, lstat, realpath, symlink } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { userInfo } from \"node:os\";\nimport { isAbsolute, join } from \"node:path\";\nimport type { RuntimePolicy, RuntimeRole } from \"./config\";\nimport { declaredRuntimeRoots, requireRuntimeReadRoots } from \"./paths\";\n\nexport interface SharedCacheDirectory {\n source: string;\n target: string;\n roles: RuntimeRole[];\n}\n\nexport async function resolveSharedCaches(\n caches: SharedCacheDirectory[],\n environment: Record<string, string>,\n base: string,\n protectedRoots: string[],\n): Promise<SharedCacheDirectory[]> {\n return Promise.all(\n caches.map(async (cache) => {\n const roots = await declaredRuntimeRoots(\n [cache.source],\n environment,\n base,\n );\n requireRuntimeReadRoots(roots, protectedRoots);\n const [declared] = roots;\n if (!declared) throw new Error(\"runtime shared cache source is missing\");\n const source = await realpath(declared);\n await requireSharedCache(source);\n return { ...cache, source };\n }),\n );\n}\n\nexport function selectedSharedCaches(\n policy: RuntimePolicy | undefined,\n role: RuntimeRole = \"candidate\",\n): SharedCacheDirectory[] {\n return (policy?.sharedCacheDirectories ?? []).filter((cache) =>\n cache.roles.includes(role),\n );\n}\n\nexport async function sharedCacheRoots(\n policy: RuntimePolicy | undefined,\n role: RuntimeRole = \"candidate\",\n protectedRoots: string[],\n): Promise<string[]> {\n const roots = selectedSharedCaches(policy, role).map((cache) => cache.source);\n return requireWritableCacheRoots(roots, protectedRoots);\n}\n\nexport function requireSharedCachePolicy(\n policy: RuntimePolicy | undefined,\n): void {\n if (!policy) return;\n const caches = policy.sharedCacheDirectories ?? [];\n const protectedRoots = [\n ...policy.readOnlyRoots,\n ...(policy.seeds ?? []).map((seed) => seed.source),\n ];\n for (const cache of caches) {\n requireRuntimeReadRoots(\n [cache.source],\n [\n ...protectedRoots,\n ...caches\n .filter((other) => other !== cache)\n .map((other) => other.source),\n ],\n );\n }\n}\n\nexport function sharedCacheDenyRoots(\n policy: RuntimePolicy | undefined,\n role: RuntimeRole = \"candidate\",\n): string[] {\n return (policy?.sharedCacheDirectories ?? [])\n .filter((cache) => !cache.roles.includes(role))\n .map((cache) => cache.source);\n}\n\nexport async function requireWritableCacheRoots(\n roots: string[],\n protectedRoots: string[],\n): Promise<string[]> {\n requireRuntimeReadRoots(roots, protectedRoots);\n for (const root of roots) await requireSharedCache(root);\n return roots;\n}\n\nasync function requireSharedCache(source: string): Promise<void> {\n if (!isAbsolute(source) || (await realpath(source)) !== source)\n throw new Error(\"runtime shared cache must be a canonical directory\");\n const info = await lstat(source);\n if (!info.isDirectory() || info.uid !== userInfo().uid)\n throw new Error(\"runtime shared cache must be an owned directory\");\n await access(source, constants.R_OK | constants.W_OK | constants.X_OK);\n}\n\n/** A slot aliases the operator-selected cache; its contents are never copied. */\nexport async function linkSharedCaches(\n policy: RuntimePolicy,\n role: RuntimeRole,\n root: string,\n): Promise<void> {\n for (const cache of selectedSharedCaches(policy, role)) {\n const target = join(root, cache.target);\n try {\n await symlink(cache.source, target, \"dir\");\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code !== \"EEXIST\") throw cause;\n await requireCacheSlot(target, cache.source, cause);\n }\n }\n}\n\nasync function requireCacheSlot(\n target: string,\n source: string,\n cause: unknown,\n): Promise<void> {\n if (!(await lstat(target)).isSymbolicLink())\n throw new Error(\"runtime shared cache slot was replaced\", { cause });\n if ((await realpath(target)) !== source)\n throw new Error(\"runtime shared cache slot changed its source\", { cause });\n}\n"]}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { mkdir, realpath, stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { requireRuntimePolicy, runtimeRoleSeeds, } from "./config.js";
|
|
4
|
+
import { materializeRuntimeSeed } from "./seeds.js";
|
|
5
|
+
import { requireRuntimeReadRoots } from "./paths.js";
|
|
6
|
+
import { hashJson } from "../identity.js";
|
|
7
|
+
import { linkSharedCaches, sharedCacheRoots, selectedSharedCaches, } from "./shared-cache.js";
|
|
8
|
+
export function runtimeObservations(policy, role = "candidate") {
|
|
9
|
+
if (!policy)
|
|
10
|
+
return [];
|
|
11
|
+
return [
|
|
12
|
+
{
|
|
13
|
+
id: "sevro.host.runtime",
|
|
14
|
+
completeness: "complete",
|
|
15
|
+
data: {
|
|
16
|
+
method: "runner_configuration",
|
|
17
|
+
role,
|
|
18
|
+
policyDigest: runtimeDigest(policy),
|
|
19
|
+
environmentNames: Object.keys(policy.environment).sort(),
|
|
20
|
+
readOnlyRoots: policy.readOnlyRoots,
|
|
21
|
+
...discoveryObservations(policy),
|
|
22
|
+
...(policy.sharedCacheDirectories?.length
|
|
23
|
+
? { sharedCacheDirectories: selectedSharedCaches(policy, role) }
|
|
24
|
+
: {}),
|
|
25
|
+
seeds: runtimeRoleSeeds(policy, role).map((seed) => ({
|
|
26
|
+
target: seed.target,
|
|
27
|
+
sha256: seed.sha256,
|
|
28
|
+
})),
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
function discoveryObservations(policy) {
|
|
34
|
+
return {
|
|
35
|
+
discovery: policy.discovery ?? [],
|
|
36
|
+
skippedOptionalReadOnlyRoots: policy.skippedOptionalReadOnlyRoots ?? [],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function runtimeDigest(policy) {
|
|
40
|
+
const hooks = policy.hooks ?? {};
|
|
41
|
+
return hashJson({
|
|
42
|
+
...policy,
|
|
43
|
+
seeds: policy.seeds ?? [],
|
|
44
|
+
hooks: {
|
|
45
|
+
nativeGoal: hooks.nativeGoal ?? false,
|
|
46
|
+
plugins: hooks.plugins ?? [],
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/** Private role state contains copied seeds and aliases to explicitly shared caches. */
|
|
51
|
+
export async function prepareRuntimeState(workspace, policy, role, privateRoot) {
|
|
52
|
+
requireRuntimePolicy(policy);
|
|
53
|
+
const root = await runtimeRoot(workspace, role, privateRoot);
|
|
54
|
+
const writableCacheRoots = await sharedCacheRoots(policy, role, [
|
|
55
|
+
workspace,
|
|
56
|
+
root,
|
|
57
|
+
]);
|
|
58
|
+
const home = join(root, "home"), temp = join(root, "tmp");
|
|
59
|
+
await Promise.all([home, temp].map((path) => mkdir(path, { recursive: true, mode: 0o700 })));
|
|
60
|
+
for (const seed of runtimeRoleSeeds(policy, role))
|
|
61
|
+
await copyRuntimeSeed(seed, join(root, seed.target));
|
|
62
|
+
await linkSharedCaches(policy, role, root);
|
|
63
|
+
const environment = Object.fromEntries(Object.entries(policy.environment).map(([name, value]) => [
|
|
64
|
+
name,
|
|
65
|
+
value.replaceAll("{{sevro.runtime}}", root),
|
|
66
|
+
]));
|
|
67
|
+
const isolatedEnvironment = {
|
|
68
|
+
...environment,
|
|
69
|
+
HOME: home,
|
|
70
|
+
TMPDIR: temp,
|
|
71
|
+
UV_OFFLINE: "1",
|
|
72
|
+
PYTHONDONTWRITEBYTECODE: "1",
|
|
73
|
+
};
|
|
74
|
+
return {
|
|
75
|
+
root,
|
|
76
|
+
home,
|
|
77
|
+
temp,
|
|
78
|
+
environment: isolatedEnvironment,
|
|
79
|
+
writableCacheRoots,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async function runtimeRoot(workspace, role, privateRoot) {
|
|
83
|
+
const root = privateRoot ?? join(workspace, ".git", "sevro-runtime", role);
|
|
84
|
+
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
85
|
+
return realpath(root);
|
|
86
|
+
}
|
|
87
|
+
async function copyRuntimeSeed(seed, target) {
|
|
88
|
+
const canonical = await realpath(seed.source);
|
|
89
|
+
requireRuntimeReadRoots([seed.source, canonical], []);
|
|
90
|
+
if (await seedTargetExists(target))
|
|
91
|
+
return;
|
|
92
|
+
await materializeRuntimeSeed(seed, target);
|
|
93
|
+
}
|
|
94
|
+
async function seedTargetExists(target) {
|
|
95
|
+
try {
|
|
96
|
+
if ((await stat(target)).isDirectory())
|
|
97
|
+
return true;
|
|
98
|
+
throw new Error("runtime seed target is not a directory");
|
|
99
|
+
}
|
|
100
|
+
catch (cause) {
|
|
101
|
+
if (cause.code !== "ENOENT")
|
|
102
|
+
throw cause;
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["state.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, realpath, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport {\n requireRuntimePolicy,\n runtimeRoleSeeds,\n type RuntimeRole,\n type RuntimePolicy,\n} from \"./config\";\nimport { materializeRuntimeSeed } from \"./seeds\";\nimport { requireRuntimeReadRoots } from \"./paths\";\nimport { hashJson } from \"../identity\";\nimport {\n linkSharedCaches,\n sharedCacheRoots,\n selectedSharedCaches,\n} from \"./shared-cache\";\n\nexport type { RuntimeRole } from \"./config\";\n\nexport function runtimeObservations(\n policy: RuntimePolicy | undefined,\n role: RuntimeRole = \"candidate\",\n) {\n if (!policy) return [];\n return [\n {\n id: \"sevro.host.runtime\",\n completeness: \"complete\" as const,\n data: {\n method: \"runner_configuration\",\n role,\n policyDigest: runtimeDigest(policy),\n environmentNames: Object.keys(policy.environment).sort(),\n readOnlyRoots: policy.readOnlyRoots,\n ...discoveryObservations(policy),\n ...(policy.sharedCacheDirectories?.length\n ? { sharedCacheDirectories: selectedSharedCaches(policy, role) }\n : {}),\n seeds: runtimeRoleSeeds(policy, role).map((seed) => ({\n target: seed.target,\n sha256: seed.sha256,\n })),\n },\n },\n ];\n}\n\nfunction discoveryObservations(policy: RuntimePolicy) {\n return {\n discovery: policy.discovery ?? [],\n skippedOptionalReadOnlyRoots: policy.skippedOptionalReadOnlyRoots ?? [],\n };\n}\n\nfunction runtimeDigest(policy: RuntimePolicy): string {\n const hooks = policy.hooks ?? {};\n return hashJson({\n ...policy,\n seeds: policy.seeds ?? [],\n hooks: {\n nativeGoal: hooks.nativeGoal ?? false,\n plugins: hooks.plugins ?? [],\n },\n });\n}\n\n/** Private role state contains copied seeds and aliases to explicitly shared caches. */\nexport async function prepareRuntimeState(\n workspace: string,\n policy: RuntimePolicy,\n role: RuntimeRole,\n privateRoot?: string,\n) {\n requireRuntimePolicy(policy);\n const root = await runtimeRoot(workspace, role, privateRoot);\n const writableCacheRoots = await sharedCacheRoots(policy, role, [\n workspace,\n root,\n ]);\n const home = join(root, \"home\"),\n temp = join(root, \"tmp\");\n await Promise.all(\n [home, temp].map((path) => mkdir(path, { recursive: true, mode: 0o700 })),\n );\n for (const seed of runtimeRoleSeeds(policy, role))\n await copyRuntimeSeed(seed, join(root, seed.target));\n await linkSharedCaches(policy, role, root);\n const environment = Object.fromEntries(\n Object.entries(policy.environment).map(([name, value]) => [\n name,\n value.replaceAll(\"{{sevro.runtime}}\", root),\n ]),\n );\n const isolatedEnvironment: Record<string, string> = {\n ...environment,\n HOME: home,\n TMPDIR: temp,\n UV_OFFLINE: \"1\",\n PYTHONDONTWRITEBYTECODE: \"1\",\n };\n return {\n root,\n home,\n temp,\n environment: isolatedEnvironment,\n writableCacheRoots,\n };\n}\n\nasync function runtimeRoot(\n workspace: string,\n role: RuntimeRole,\n privateRoot: string | undefined,\n): Promise<string> {\n const root = privateRoot ?? join(workspace, \".git\", \"sevro-runtime\", role);\n await mkdir(root, { recursive: true, mode: 0o700 });\n return realpath(root);\n}\n\nasync function copyRuntimeSeed(\n seed: NonNullable<RuntimePolicy[\"seeds\"]>[number],\n target: string,\n): Promise<void> {\n const canonical = await realpath(seed.source);\n requireRuntimeReadRoots([seed.source, canonical], []);\n if (await seedTargetExists(target)) return;\n await materializeRuntimeSeed(seed, target);\n}\n\nasync function seedTargetExists(target: string): Promise<boolean> {\n try {\n if ((await stat(target)).isDirectory()) return true;\n throw new Error(\"runtime seed target is not a directory\");\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code !== \"ENOENT\") throw cause;\n return false;\n }\n}\n"]}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { spawn as nodeSpawn, spawnSync as nodeSpawnSync, } from "node:child_process";
|
|
2
|
+
import { Readable, Writable } from "node:stream";
|
|
3
|
+
import { constants } from "node:os";
|
|
4
|
+
function emptyStream() {
|
|
5
|
+
return new ReadableStream({
|
|
6
|
+
start(controller) {
|
|
7
|
+
controller.close();
|
|
8
|
+
},
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
function readable(stream) {
|
|
12
|
+
// Node and Bun declare different overloads for the same Web Streams API.
|
|
13
|
+
return stream
|
|
14
|
+
? Readable.toWeb(stream)
|
|
15
|
+
: emptyStream();
|
|
16
|
+
}
|
|
17
|
+
function output(stream) {
|
|
18
|
+
return stream ?? emptyStream();
|
|
19
|
+
}
|
|
20
|
+
function writer(stream) {
|
|
21
|
+
if (!stream)
|
|
22
|
+
return null;
|
|
23
|
+
const destination = Writable.toWeb(stream).getWriter();
|
|
24
|
+
return {
|
|
25
|
+
write: (data) => destination.write(typeof data === "string" ? Buffer.from(data) : data),
|
|
26
|
+
end: () => destination.close(),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function commandName(argv) {
|
|
30
|
+
const name = argv[0];
|
|
31
|
+
if (!name)
|
|
32
|
+
throw new Error("process command must be nonempty");
|
|
33
|
+
return name;
|
|
34
|
+
}
|
|
35
|
+
function stdinMode(input) {
|
|
36
|
+
return input === "pipe" || input instanceof Uint8Array ? "pipe" : "ignore";
|
|
37
|
+
}
|
|
38
|
+
function processId(child) {
|
|
39
|
+
if (child.pid !== undefined)
|
|
40
|
+
return child.pid;
|
|
41
|
+
child.once("error", () => { });
|
|
42
|
+
throw new Error("could not start process");
|
|
43
|
+
}
|
|
44
|
+
function exitCode(code, signal) {
|
|
45
|
+
return code ?? 128 + (signal ? constants.signals[signal] : 0);
|
|
46
|
+
}
|
|
47
|
+
function completion(child) {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
child.once("error", reject);
|
|
50
|
+
child.once("exit", (code, signal) => {
|
|
51
|
+
resolve(exitCode(code, signal));
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async function initialInput(stdin, input) {
|
|
56
|
+
if (!(input instanceof Uint8Array))
|
|
57
|
+
return;
|
|
58
|
+
if (!stdin)
|
|
59
|
+
throw new Error("process input pipe unavailable");
|
|
60
|
+
await stdin.write(input);
|
|
61
|
+
await stdin.end();
|
|
62
|
+
}
|
|
63
|
+
function nodeProcess(argv, options) {
|
|
64
|
+
const child = nodeSpawn(commandName(argv), argv.slice(1), {
|
|
65
|
+
cwd: options.cwd,
|
|
66
|
+
env: options.env,
|
|
67
|
+
detached: options.detached,
|
|
68
|
+
stdio: [
|
|
69
|
+
stdinMode(options.stdin),
|
|
70
|
+
options.stdout ?? "pipe",
|
|
71
|
+
options.stderr ?? "pipe",
|
|
72
|
+
],
|
|
73
|
+
});
|
|
74
|
+
const pid = processId(child);
|
|
75
|
+
const exited = completion(child);
|
|
76
|
+
const stdin = writer(child.stdin);
|
|
77
|
+
return {
|
|
78
|
+
pid,
|
|
79
|
+
stdin,
|
|
80
|
+
stdout: readable(child.stdout),
|
|
81
|
+
stderr: readable(child.stderr),
|
|
82
|
+
exited: Promise.all([exited, initialInput(stdin, options.stdin)]).then(([code]) => code),
|
|
83
|
+
get signalCode() {
|
|
84
|
+
return child.signalCode;
|
|
85
|
+
},
|
|
86
|
+
kill(signal) {
|
|
87
|
+
child.kill(signal);
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function bunProcess(argv, options) {
|
|
92
|
+
const child = Bun.spawn(argv, {
|
|
93
|
+
...options,
|
|
94
|
+
stdin: options.stdin ?? "ignore",
|
|
95
|
+
stderr: options.stderr ?? "pipe",
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
pid: child.pid,
|
|
99
|
+
stdin: typeof child.stdin === "object" ? child.stdin : null,
|
|
100
|
+
stdout: output(child.stdout),
|
|
101
|
+
stderr: output(child.stderr),
|
|
102
|
+
exited: child.exited,
|
|
103
|
+
get signalCode() {
|
|
104
|
+
return child.signalCode ?? null;
|
|
105
|
+
},
|
|
106
|
+
kill(signal) {
|
|
107
|
+
child.kill(signal);
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Keep the runner's process/stream contract independent of its JS runtime. */
|
|
112
|
+
export function spawn(argv, options = {}) {
|
|
113
|
+
return process.versions.bun
|
|
114
|
+
? bunProcess(argv, options)
|
|
115
|
+
: nodeProcess(argv, options);
|
|
116
|
+
}
|
|
117
|
+
function buffer(bytes) {
|
|
118
|
+
return bytes ?? Buffer.alloc(0);
|
|
119
|
+
}
|
|
120
|
+
function bunSync(argv, options) {
|
|
121
|
+
const result = Bun.spawnSync(argv, options);
|
|
122
|
+
return {
|
|
123
|
+
exitCode: result.exitCode,
|
|
124
|
+
stdout: buffer(result.stdout),
|
|
125
|
+
stderr: buffer(result.stderr),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function nodeSync(argv, options) {
|
|
129
|
+
const result = nodeSpawnSync(commandName(argv), argv.slice(1), {
|
|
130
|
+
cwd: options.cwd,
|
|
131
|
+
env: options.env,
|
|
132
|
+
stdio: ["ignore", options.stdout ?? "pipe", options.stderr ?? "pipe"],
|
|
133
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
134
|
+
});
|
|
135
|
+
if (result.error)
|
|
136
|
+
throw result.error;
|
|
137
|
+
return {
|
|
138
|
+
exitCode: result.status ?? 1,
|
|
139
|
+
stdout: buffer(result.stdout),
|
|
140
|
+
stderr: buffer(result.stderr),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
export function spawnSync(argv, options = {}) {
|
|
144
|
+
return process.versions.bun
|
|
145
|
+
? bunSync(argv, options)
|
|
146
|
+
: nodeSync(argv, options);
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=runtime-process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-process.js","sourceRoot":"","sources":["runtime-process.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n spawn as nodeSpawn,\n spawnSync as nodeSpawnSync,\n type ChildProcess,\n} from \"node:child_process\";\nimport { Readable, Writable } from \"node:stream\";\nimport { constants } from \"node:os\";\n\ninterface ProcessWriter {\n write(data: string | Uint8Array): unknown;\n end(): unknown;\n}\n\nexport interface Subprocess {\n pid: number;\n stdin: ProcessWriter | null;\n stdout: ReadableStream<Uint8Array>;\n stderr: ReadableStream<Uint8Array>;\n exited: Promise<number>;\n readonly signalCode: string | null;\n kill(signal: NodeJS.Signals): void;\n}\n\ninterface SpawnOptions {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n detached?: boolean;\n stdin?: \"pipe\" | \"ignore\" | Uint8Array;\n stdout?: \"pipe\" | \"ignore\";\n stderr?: \"pipe\" | \"ignore\";\n}\n\nfunction emptyStream(): ReadableStream<Uint8Array> {\n return new ReadableStream({\n start(controller) {\n controller.close();\n },\n });\n}\n\nfunction readable(stream: Readable | null): ReadableStream<Uint8Array> {\n // Node and Bun declare different overloads for the same Web Streams API.\n return stream\n ? (Readable.toWeb(stream) as unknown as ReadableStream<Uint8Array>)\n : emptyStream();\n}\n\nfunction output(\n stream: ReadableStream<Uint8Array> | null | undefined,\n): ReadableStream<Uint8Array> {\n return stream ?? emptyStream();\n}\n\nfunction writer(stream: Writable | null): ProcessWriter | null {\n if (!stream) return null;\n const destination = Writable.toWeb(stream).getWriter();\n return {\n write: (data) =>\n destination.write(typeof data === \"string\" ? Buffer.from(data) : data),\n end: () => destination.close(),\n };\n}\n\nfunction commandName(argv: string[]): string {\n const name = argv[0];\n if (!name) throw new Error(\"process command must be nonempty\");\n return name;\n}\n\nfunction stdinMode(input: SpawnOptions[\"stdin\"]) {\n return input === \"pipe\" || input instanceof Uint8Array ? \"pipe\" : \"ignore\";\n}\n\nfunction processId(child: ChildProcess): number {\n if (child.pid !== undefined) return child.pid;\n child.once(\"error\", () => {});\n throw new Error(\"could not start process\");\n}\n\nfunction exitCode(code: number | null, signal: NodeJS.Signals | null): number {\n return code ?? 128 + (signal ? constants.signals[signal] : 0);\n}\n\nfunction completion(child: ChildProcess): Promise<number> {\n return new Promise((resolve, reject) => {\n child.once(\"error\", reject);\n child.once(\"exit\", (code, signal) => {\n resolve(exitCode(code, signal));\n });\n });\n}\n\nasync function initialInput(\n stdin: ProcessWriter | null,\n input: SpawnOptions[\"stdin\"],\n) {\n if (!(input instanceof Uint8Array)) return;\n if (!stdin) throw new Error(\"process input pipe unavailable\");\n await stdin.write(input);\n await stdin.end();\n}\n\nfunction nodeProcess(argv: string[], options: SpawnOptions): Subprocess {\n const child = nodeSpawn(commandName(argv), argv.slice(1), {\n cwd: options.cwd,\n env: options.env,\n detached: options.detached,\n stdio: [\n stdinMode(options.stdin),\n options.stdout ?? \"pipe\",\n options.stderr ?? \"pipe\",\n ],\n });\n const pid = processId(child);\n const exited = completion(child);\n const stdin = writer(child.stdin);\n return {\n pid,\n stdin,\n stdout: readable(child.stdout),\n stderr: readable(child.stderr),\n exited: Promise.all([exited, initialInput(stdin, options.stdin)]).then(\n ([code]) => code,\n ),\n get signalCode() {\n return child.signalCode;\n },\n kill(signal) {\n child.kill(signal);\n },\n };\n}\n\nfunction bunProcess(argv: string[], options: SpawnOptions): Subprocess {\n const child = Bun.spawn(argv, {\n ...options,\n stdin: options.stdin ?? \"ignore\",\n stderr: options.stderr ?? \"pipe\",\n });\n return {\n pid: child.pid,\n stdin: typeof child.stdin === \"object\" ? child.stdin : null,\n stdout: output(child.stdout),\n stderr: output(child.stderr),\n exited: child.exited,\n get signalCode() {\n return child.signalCode ?? null;\n },\n kill(signal) {\n child.kill(signal);\n },\n };\n}\n\n/** Keep the runner's process/stream contract independent of its JS runtime. */\nexport function spawn(argv: string[], options: SpawnOptions = {}): Subprocess {\n return process.versions.bun\n ? bunProcess(argv, options)\n : nodeProcess(argv, options);\n}\n\nfunction buffer(bytes: Buffer | null | undefined): Buffer {\n return bytes ?? Buffer.alloc(0);\n}\n\nfunction bunSync(argv: string[], options: SpawnOptions) {\n const result = Bun.spawnSync(argv, options);\n return {\n exitCode: result.exitCode,\n stdout: buffer(result.stdout),\n stderr: buffer(result.stderr),\n };\n}\n\nfunction nodeSync(argv: string[], options: SpawnOptions) {\n const result = nodeSpawnSync(commandName(argv), argv.slice(1), {\n cwd: options.cwd,\n env: options.env,\n stdio: [\"ignore\", options.stdout ?? \"pipe\", options.stderr ?? \"pipe\"],\n maxBuffer: 64 * 1024 * 1024,\n });\n if (result.error) throw result.error;\n return {\n exitCode: result.status ?? 1,\n stdout: buffer(result.stdout),\n stderr: buffer(result.stderr),\n };\n}\n\nexport function spawnSync(argv: string[], options: SpawnOptions = {}) {\n return process.versions.bun\n ? bunSync(argv, options)\n : nodeSync(argv, options);\n}\n"]}
|