@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,321 @@
|
|
|
1
|
+
import { spawn } from "../runtime-process.js";
|
|
2
|
+
import { runtimeLocalHttpEnabled, runtimeSeedSources } from "../runtime/config.js";
|
|
3
|
+
import { candidateArtifactEnvironment, } from "../candidate-artifacts.js";
|
|
4
|
+
import { mkdir, stat } from "node:fs/promises";
|
|
5
|
+
import { delimiter, join } from "node:path";
|
|
6
|
+
import { prepareIsolatedCommand } from "../hosts/isolation.js";
|
|
7
|
+
import { prepareRuntimeState } from "../runtime/state.js";
|
|
8
|
+
import { sharedCacheDenyRoots } from "../runtime/shared-cache.js";
|
|
9
|
+
import { hashJson } from "../identity.js";
|
|
10
|
+
import { captureBoundedText, ProcessDeadline, terminateOwnedProcessGroup, } from "../process-lifecycle.js";
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
12
|
+
const MAX_TIMEOUT_MS = 120_000;
|
|
13
|
+
const MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
14
|
+
function requireShellDeclaration(id, value) {
|
|
15
|
+
if (!id || !value || typeof value !== "object")
|
|
16
|
+
throw new Error("invalid shell check declaration");
|
|
17
|
+
}
|
|
18
|
+
function requireShellKeys(config) {
|
|
19
|
+
const allowed = [
|
|
20
|
+
"run",
|
|
21
|
+
"expectedExitCode",
|
|
22
|
+
"timeoutMs",
|
|
23
|
+
"expectExact",
|
|
24
|
+
"expectRegex",
|
|
25
|
+
"notRegex",
|
|
26
|
+
"flags",
|
|
27
|
+
];
|
|
28
|
+
if (Object.keys(config).some((key) => !allowed.includes(key)))
|
|
29
|
+
throw new Error("unsupported shell check configuration");
|
|
30
|
+
}
|
|
31
|
+
function requireShellCommand(run) {
|
|
32
|
+
if (typeof run !== "string" || !run.trim() || run.length > 4096)
|
|
33
|
+
throw new Error("shell check requires a bounded command");
|
|
34
|
+
}
|
|
35
|
+
function requireExpectedExit(code) {
|
|
36
|
+
if (typeof code !== "number" ||
|
|
37
|
+
!Number.isSafeInteger(code) ||
|
|
38
|
+
code < 0 ||
|
|
39
|
+
code > 255)
|
|
40
|
+
throw new Error("invalid expected shell exit code");
|
|
41
|
+
}
|
|
42
|
+
function requireShellTimeout(timeout) {
|
|
43
|
+
if (typeof timeout !== "number" ||
|
|
44
|
+
!Number.isSafeInteger(timeout) ||
|
|
45
|
+
timeout < 1 ||
|
|
46
|
+
timeout > MAX_TIMEOUT_MS)
|
|
47
|
+
throw new Error("invalid shell check timeout");
|
|
48
|
+
}
|
|
49
|
+
function requireExactShellOutput(output) {
|
|
50
|
+
if (output !== undefined &&
|
|
51
|
+
(typeof output !== "string" ||
|
|
52
|
+
Buffer.byteLength(output, "utf8") > MAX_OUTPUT_BYTES))
|
|
53
|
+
throw new Error("invalid exact shell output expectation");
|
|
54
|
+
}
|
|
55
|
+
function requireShellFlags(flags) {
|
|
56
|
+
if (flags !== "" &&
|
|
57
|
+
(typeof flags !== "string" ||
|
|
58
|
+
!/^[isu]*$/.test(flags) ||
|
|
59
|
+
new Set(flags).size !== flags.length))
|
|
60
|
+
throw new Error("invalid shell regex flags");
|
|
61
|
+
}
|
|
62
|
+
function requireFlagPattern(flags, expected, forbidden) {
|
|
63
|
+
if (flags && expected === undefined && forbidden === undefined)
|
|
64
|
+
throw new Error("shell regex flags require a pattern");
|
|
65
|
+
}
|
|
66
|
+
function shellOptions(config) {
|
|
67
|
+
const { run, expectedExitCode = 0, timeoutMs = DEFAULT_TIMEOUT_MS, expectExact, expectRegex, notRegex, flags = "", } = config;
|
|
68
|
+
requireShellCommand(run);
|
|
69
|
+
requireExpectedExit(expectedExitCode);
|
|
70
|
+
requireShellTimeout(timeoutMs);
|
|
71
|
+
requireExactShellOutput(expectExact);
|
|
72
|
+
requireShellFlags(flags);
|
|
73
|
+
requireFlagPattern(flags, expectRegex, notRegex);
|
|
74
|
+
return {
|
|
75
|
+
run,
|
|
76
|
+
expectedExitCode,
|
|
77
|
+
timeoutMs,
|
|
78
|
+
expectExact,
|
|
79
|
+
expectRegex,
|
|
80
|
+
notRegex,
|
|
81
|
+
flags,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function compileShellPattern(value, flags) {
|
|
85
|
+
if (value === undefined)
|
|
86
|
+
return undefined;
|
|
87
|
+
if (typeof value !== "string" || value.length > 4096)
|
|
88
|
+
throw new Error("invalid shell regex pattern");
|
|
89
|
+
try {
|
|
90
|
+
return new RegExp(value, `m${flags}`);
|
|
91
|
+
}
|
|
92
|
+
catch (cause) {
|
|
93
|
+
throw new Error("invalid shell regex pattern or flags", { cause });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function prepareShellCheck({ id, configuration, }) {
|
|
97
|
+
requireShellDeclaration(id, configuration);
|
|
98
|
+
requireShellKeys(configuration);
|
|
99
|
+
const config = shellOptions(configuration);
|
|
100
|
+
const expectRegex = compileShellPattern(config.expectRegex, config.flags), notRegex = compileShellPattern(config.notRegex, config.flags);
|
|
101
|
+
return {
|
|
102
|
+
id,
|
|
103
|
+
run: config.run,
|
|
104
|
+
expectedExitCode: config.expectedExitCode,
|
|
105
|
+
timeoutMs: config.timeoutMs,
|
|
106
|
+
expectExact: config.expectExact,
|
|
107
|
+
expectRegex,
|
|
108
|
+
notRegex,
|
|
109
|
+
captureStdout: config.expectExact !== undefined ||
|
|
110
|
+
expectRegex !== undefined ||
|
|
111
|
+
notRegex !== undefined,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export function prepareShellChecks(declarations) {
|
|
115
|
+
return declarations.map(prepareShellCheck);
|
|
116
|
+
}
|
|
117
|
+
/** Grade bounded shell output without exposing it in retained evidence. */
|
|
118
|
+
export function assessShellCheck(check, result) {
|
|
119
|
+
if (result.exitCode !== check.expectedExitCode)
|
|
120
|
+
return {
|
|
121
|
+
passed: false,
|
|
122
|
+
detail: `exit code ${result.exitCode} (expected ${check.expectedExitCode})`,
|
|
123
|
+
};
|
|
124
|
+
if (check.captureStdout && result.stdout === null)
|
|
125
|
+
throw new Error("shell stdout observation is missing");
|
|
126
|
+
const detail = shellOutputFailure(check, result.stdout);
|
|
127
|
+
return assessedShellOutput(detail, result.exitCode);
|
|
128
|
+
}
|
|
129
|
+
function shellOutputFailure(check, out) {
|
|
130
|
+
return (exactOutputFailure(check, out) ??
|
|
131
|
+
expectedPatternFailure(check, out) ??
|
|
132
|
+
forbiddenPatternFailure(check, out));
|
|
133
|
+
}
|
|
134
|
+
function assessedShellOutput(detail, exitCode) {
|
|
135
|
+
return detail === undefined
|
|
136
|
+
? { passed: true, detail: `exit code ${exitCode}` }
|
|
137
|
+
: { passed: false, detail };
|
|
138
|
+
}
|
|
139
|
+
function exactOutputFailure(check, out) {
|
|
140
|
+
if (check.expectExact === undefined)
|
|
141
|
+
return undefined;
|
|
142
|
+
if (out === null)
|
|
143
|
+
throw new Error("shell stdout observation is missing");
|
|
144
|
+
const actual = out.endsWith("\n") ? out.slice(0, -1) : out;
|
|
145
|
+
return actual === check.expectExact
|
|
146
|
+
? undefined
|
|
147
|
+
: "exact shell output did not match";
|
|
148
|
+
}
|
|
149
|
+
function expectedPatternFailure(check, out) {
|
|
150
|
+
return check.expectRegex && !check.expectRegex.test(String(out))
|
|
151
|
+
? "expected shell output pattern did not match"
|
|
152
|
+
: undefined;
|
|
153
|
+
}
|
|
154
|
+
function forbiddenPatternFailure(check, out) {
|
|
155
|
+
return check.notRegex && check.notRegex.test(String(out))
|
|
156
|
+
? "forbidden shell output pattern matched"
|
|
157
|
+
: undefined;
|
|
158
|
+
}
|
|
159
|
+
async function boundedOutput(stream) {
|
|
160
|
+
return captureBoundedText(stream, {
|
|
161
|
+
limit: MAX_OUTPUT_BYTES,
|
|
162
|
+
overflow: "reject",
|
|
163
|
+
exceeded: () => new Error("shell stdout exceeds 1 MiB"),
|
|
164
|
+
cancelOnError: true,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
async function shellRuntime(options) {
|
|
168
|
+
if (options.runtimePolicy) {
|
|
169
|
+
const runtime = await prepareRuntimeState(options.workspace, options.runtimePolicy, "checks", join(options.privateStateRoot, "runtime", hashJson(options.workspace)));
|
|
170
|
+
return {
|
|
171
|
+
...runtime,
|
|
172
|
+
uvCache: join(runtime.root, "uv-cache"),
|
|
173
|
+
privateRuntimeRoot: runtime.root,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const root = join(options.privateStateRoot, "runtime", hashJson(options.workspace)), home = join(root, "check-home"), temp = join(root, "check-tmp");
|
|
177
|
+
await Promise.all([
|
|
178
|
+
mkdir(home, { recursive: true, mode: 0o700 }),
|
|
179
|
+
mkdir(temp, { recursive: true, mode: 0o700 }),
|
|
180
|
+
]);
|
|
181
|
+
return {
|
|
182
|
+
root,
|
|
183
|
+
home,
|
|
184
|
+
temp,
|
|
185
|
+
uvCache: join(options.workspace, ".git", "sevro-runtime", "uv-cache"),
|
|
186
|
+
environment: {},
|
|
187
|
+
privateRuntimeRoot: root,
|
|
188
|
+
writableCacheRoots: [],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function shellRuntimeEnvironment(runtime, enabled) {
|
|
192
|
+
return enabled
|
|
193
|
+
? {
|
|
194
|
+
UV_CACHE_DIR: runtime.uvCache,
|
|
195
|
+
UV_PROJECT_ENVIRONMENT: join(runtime.root, "project-environment"),
|
|
196
|
+
UV_OFFLINE: "1",
|
|
197
|
+
PYTHONDONTWRITEBYTECODE: "1",
|
|
198
|
+
}
|
|
199
|
+
: {};
|
|
200
|
+
}
|
|
201
|
+
function shellEnvironment(options, runtime) {
|
|
202
|
+
return {
|
|
203
|
+
...runtime.environment,
|
|
204
|
+
...candidateArtifactEnvironment(options.candidateArtifactRoots),
|
|
205
|
+
...(options.candidateTranscriptRoot
|
|
206
|
+
? { SEVRO_CANDIDATE_TRANSCRIPTS: options.candidateTranscriptRoot }
|
|
207
|
+
: {}),
|
|
208
|
+
PATH: [
|
|
209
|
+
options.fixtureBinDir,
|
|
210
|
+
options.toolchainBinDir,
|
|
211
|
+
runtime.environment.PATH ?? "/usr/bin:/bin:/usr/sbin:/sbin",
|
|
212
|
+
]
|
|
213
|
+
.filter(Boolean)
|
|
214
|
+
.join(delimiter),
|
|
215
|
+
HOME: runtime.home,
|
|
216
|
+
TMPDIR: runtime.temp,
|
|
217
|
+
LANG: process.env.LANG ?? "C",
|
|
218
|
+
GIT_CONFIG_GLOBAL: "/dev/null",
|
|
219
|
+
GIT_CONFIG_NOSYSTEM: "1",
|
|
220
|
+
...shellRuntimeEnvironment(runtime, options.uvRuntimeCache),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
async function requireShellRuntimeCache(options, runtime) {
|
|
224
|
+
if (options.uvRuntimeCache && !(await stat(runtime.uvCache)).isDirectory())
|
|
225
|
+
throw new Error("isolated UV cache is missing");
|
|
226
|
+
}
|
|
227
|
+
function shellStdout(check, output) {
|
|
228
|
+
if (!check.captureStdout)
|
|
229
|
+
return null;
|
|
230
|
+
if (!(output instanceof ReadableStream))
|
|
231
|
+
throw new Error("shell stdout pipe is unavailable");
|
|
232
|
+
return output;
|
|
233
|
+
}
|
|
234
|
+
class ShellExecution {
|
|
235
|
+
check;
|
|
236
|
+
options;
|
|
237
|
+
isolation;
|
|
238
|
+
runtime;
|
|
239
|
+
proc;
|
|
240
|
+
deadline;
|
|
241
|
+
constructor(check, options, isolation, runtime) {
|
|
242
|
+
this.check = check;
|
|
243
|
+
this.options = options;
|
|
244
|
+
this.isolation = isolation;
|
|
245
|
+
this.runtime = runtime;
|
|
246
|
+
this.deadline = new ProcessDeadline({
|
|
247
|
+
timeoutMs: check.timeoutMs,
|
|
248
|
+
signal: options.signal,
|
|
249
|
+
interruption: (reason) => new Error(reason === "timeout"
|
|
250
|
+
? "shell check timed out"
|
|
251
|
+
: "shell check cancelled"),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
async run() {
|
|
255
|
+
try {
|
|
256
|
+
const running = this.spawn();
|
|
257
|
+
const stream = shellStdout(this.check, running.stdout);
|
|
258
|
+
return await this.deadline.wait(() => Promise.all([
|
|
259
|
+
running.exited,
|
|
260
|
+
stream ? boundedOutput(stream) : Promise.resolve(null),
|
|
261
|
+
]).then(([exitCode, stdout]) => ({ exitCode, stdout })));
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
await this.stop();
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
this.deadline.dispose();
|
|
269
|
+
await this.isolation.release();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
spawn() {
|
|
273
|
+
this.proc = spawn(this.isolation.argv, {
|
|
274
|
+
cwd: this.options.workspace,
|
|
275
|
+
env: shellEnvironment(this.options, this.runtime),
|
|
276
|
+
detached: true,
|
|
277
|
+
stdin: "ignore",
|
|
278
|
+
stdout: this.check.captureStdout ? "pipe" : "ignore",
|
|
279
|
+
stderr: "ignore",
|
|
280
|
+
});
|
|
281
|
+
return this.proc;
|
|
282
|
+
}
|
|
283
|
+
async stop() {
|
|
284
|
+
const proc = this.proc;
|
|
285
|
+
if (!proc)
|
|
286
|
+
return;
|
|
287
|
+
terminateOwnedProcessGroup(proc);
|
|
288
|
+
await proc.exited;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/** Run a check without inherited credentials or retaining raw process output. */
|
|
292
|
+
export async function runShellCheck(check, options) {
|
|
293
|
+
if (options.signal?.aborted)
|
|
294
|
+
throw new Error("shell check cancelled");
|
|
295
|
+
const runtime = await shellRuntime(options);
|
|
296
|
+
const isolated = await prepareIsolatedCommand({
|
|
297
|
+
argv: ["/bin/sh", "-e", "-c", check.run],
|
|
298
|
+
workspace: options.workspace,
|
|
299
|
+
protectedRoots: [...options.protectedRoots, ...shellSeedSources(options)],
|
|
300
|
+
protectedRootsCanonical: options.protectedRootsCanonical,
|
|
301
|
+
privateStateRoot: options.privateStateRoot,
|
|
302
|
+
denyNetwork: true,
|
|
303
|
+
localHttp: runtimeLocalHttpEnabled(options.runtimePolicy, "checks"),
|
|
304
|
+
readOnlyRoots: options.runtimePolicy?.readOnlyRoots,
|
|
305
|
+
candidateArtifactRoots: options.candidateArtifactRoots,
|
|
306
|
+
transcriptReadRoots: options.candidateTranscriptRoot
|
|
307
|
+
? [options.candidateTranscriptRoot]
|
|
308
|
+
: [],
|
|
309
|
+
writableRuntimeRoot: runtime.privateRuntimeRoot,
|
|
310
|
+
writableCacheRoots: runtime.writableCacheRoots,
|
|
311
|
+
});
|
|
312
|
+
await requireShellRuntimeCache(options, runtime);
|
|
313
|
+
return new ShellExecution(check, options, isolated, runtime).run();
|
|
314
|
+
}
|
|
315
|
+
function shellSeedSources(options) {
|
|
316
|
+
return [
|
|
317
|
+
...runtimeSeedSources(options.runtimePolicy),
|
|
318
|
+
...sharedCacheDenyRoots(options.runtimePolicy, "checks"),
|
|
319
|
+
];
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["shell.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn, type Subprocess } from \"../runtime-process\";\nimport { runtimeLocalHttpEnabled, runtimeSeedSources } from \"../runtime/config\";\nimport {\n candidateArtifactEnvironment,\n type CandidateArtifactRoots,\n} from \"../candidate-artifacts\";\nimport { mkdir, stat } from \"node:fs/promises\";\nimport { delimiter, join } from \"node:path\";\nimport { prepareIsolatedCommand } from \"../hosts/isolation\";\nimport type { RuntimePolicy } from \"../runtime/config\";\nimport { prepareRuntimeState } from \"../runtime/state\";\nimport { sharedCacheDenyRoots } from \"../runtime/shared-cache\";\nimport { hashJson } from \"../identity\";\nimport {\n captureBoundedText,\n ProcessDeadline,\n terminateOwnedProcessGroup,\n} from \"../process-lifecycle\";\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst MAX_TIMEOUT_MS = 120_000;\nconst MAX_OUTPUT_BYTES = 1024 * 1024;\n\nexport interface ShellCheckDeclaration {\n id: string;\n grader: \"sevro.shell\";\n configuration: Record<string, unknown>;\n}\n\nexport interface PreparedShellCheck {\n id: string;\n run: string;\n expectedExitCode: number;\n timeoutMs: number;\n expectExact?: string;\n expectRegex?: RegExp;\n notRegex?: RegExp;\n captureStdout: boolean;\n}\n\nfunction requireShellDeclaration(\n id: string,\n value: unknown,\n): asserts value is Record<string, unknown> {\n if (!id || !value || typeof value !== \"object\")\n throw new Error(\"invalid shell check declaration\");\n}\nfunction requireShellKeys(config: Record<string, unknown>): void {\n const allowed = [\n \"run\",\n \"expectedExitCode\",\n \"timeoutMs\",\n \"expectExact\",\n \"expectRegex\",\n \"notRegex\",\n \"flags\",\n ];\n if (Object.keys(config).some((key) => !allowed.includes(key)))\n throw new Error(\"unsupported shell check configuration\");\n}\nfunction requireShellCommand(run: unknown): asserts run is string {\n if (typeof run !== \"string\" || !run.trim() || run.length > 4096)\n throw new Error(\"shell check requires a bounded command\");\n}\nfunction requireExpectedExit(code: unknown): asserts code is number {\n if (\n typeof code !== \"number\" ||\n !Number.isSafeInteger(code) ||\n code < 0 ||\n code > 255\n )\n throw new Error(\"invalid expected shell exit code\");\n}\nfunction requireShellTimeout(timeout: unknown): asserts timeout is number {\n if (\n typeof timeout !== \"number\" ||\n !Number.isSafeInteger(timeout) ||\n timeout < 1 ||\n timeout > MAX_TIMEOUT_MS\n )\n throw new Error(\"invalid shell check timeout\");\n}\nfunction requireExactShellOutput(\n output: unknown,\n): asserts output is string | undefined {\n if (\n output !== undefined &&\n (typeof output !== \"string\" ||\n Buffer.byteLength(output, \"utf8\") > MAX_OUTPUT_BYTES)\n )\n throw new Error(\"invalid exact shell output expectation\");\n}\nfunction requireShellFlags(flags: unknown): asserts flags is string {\n if (\n flags !== \"\" &&\n (typeof flags !== \"string\" ||\n !/^[isu]*$/.test(flags) ||\n new Set(flags).size !== flags.length)\n )\n throw new Error(\"invalid shell regex flags\");\n}\nfunction requireFlagPattern(\n flags: string,\n expected: unknown,\n forbidden: unknown,\n): void {\n if (flags && expected === undefined && forbidden === undefined)\n throw new Error(\"shell regex flags require a pattern\");\n}\nfunction shellOptions(config: Record<string, unknown>) {\n const {\n run,\n expectedExitCode = 0,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n expectExact,\n expectRegex,\n notRegex,\n flags = \"\",\n } = config;\n requireShellCommand(run);\n requireExpectedExit(expectedExitCode);\n requireShellTimeout(timeoutMs);\n requireExactShellOutput(expectExact);\n requireShellFlags(flags);\n requireFlagPattern(flags, expectRegex, notRegex);\n return {\n run,\n expectedExitCode,\n timeoutMs,\n expectExact,\n expectRegex,\n notRegex,\n flags,\n };\n}\nfunction compileShellPattern(\n value: unknown,\n flags: string,\n): RegExp | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"string\" || value.length > 4096)\n throw new Error(\"invalid shell regex pattern\");\n try {\n return new RegExp(value, `m${flags}`);\n } catch (cause) {\n throw new Error(\"invalid shell regex pattern or flags\", { cause });\n }\n}\nfunction prepareShellCheck({\n id,\n configuration,\n}: ShellCheckDeclaration): PreparedShellCheck {\n requireShellDeclaration(id, configuration);\n requireShellKeys(configuration);\n const config = shellOptions(configuration);\n const expectRegex = compileShellPattern(config.expectRegex, config.flags),\n notRegex = compileShellPattern(config.notRegex, config.flags);\n return {\n id,\n run: config.run,\n expectedExitCode: config.expectedExitCode,\n timeoutMs: config.timeoutMs,\n expectExact: config.expectExact,\n expectRegex,\n notRegex,\n captureStdout:\n config.expectExact !== undefined ||\n expectRegex !== undefined ||\n notRegex !== undefined,\n };\n}\nexport function prepareShellChecks(\n declarations: ShellCheckDeclaration[],\n): PreparedShellCheck[] {\n return declarations.map(prepareShellCheck);\n}\n\nexport interface ShellCheckResult {\n exitCode: number;\n stdout: string | null;\n}\n\n/** Grade bounded shell output without exposing it in retained evidence. */\nexport function assessShellCheck(\n check: PreparedShellCheck,\n result: ShellCheckResult,\n): { passed: boolean; detail: string } {\n if (result.exitCode !== check.expectedExitCode)\n return {\n passed: false,\n detail: `exit code ${result.exitCode} (expected ${check.expectedExitCode})`,\n };\n if (check.captureStdout && result.stdout === null)\n throw new Error(\"shell stdout observation is missing\");\n const detail = shellOutputFailure(check, result.stdout);\n return assessedShellOutput(detail, result.exitCode);\n}\n\nfunction shellOutputFailure(\n check: PreparedShellCheck,\n out: string | null,\n): string | undefined {\n return (\n exactOutputFailure(check, out) ??\n expectedPatternFailure(check, out) ??\n forbiddenPatternFailure(check, out)\n );\n}\n\nfunction assessedShellOutput(\n detail: string | undefined,\n exitCode: number,\n): { passed: boolean; detail: string } {\n return detail === undefined\n ? { passed: true, detail: `exit code ${exitCode}` }\n : { passed: false, detail };\n}\nfunction exactOutputFailure(\n check: PreparedShellCheck,\n out: string | null,\n): string | undefined {\n if (check.expectExact === undefined) return undefined;\n if (out === null) throw new Error(\"shell stdout observation is missing\");\n const actual = out.endsWith(\"\\n\") ? out.slice(0, -1) : out;\n return actual === check.expectExact\n ? undefined\n : \"exact shell output did not match\";\n}\nfunction expectedPatternFailure(\n check: PreparedShellCheck,\n out: string | null,\n): string | undefined {\n return check.expectRegex && !check.expectRegex.test(String(out))\n ? \"expected shell output pattern did not match\"\n : undefined;\n}\nfunction forbiddenPatternFailure(\n check: PreparedShellCheck,\n out: string | null,\n): string | undefined {\n return check.notRegex && check.notRegex.test(String(out))\n ? \"forbidden shell output pattern matched\"\n : undefined;\n}\n\nasync function boundedOutput(\n stream: ReadableStream<Uint8Array>,\n): Promise<string> {\n return captureBoundedText(stream, {\n limit: MAX_OUTPUT_BYTES,\n overflow: \"reject\",\n exceeded: () => new Error(\"shell stdout exceeds 1 MiB\"),\n cancelOnError: true,\n });\n}\n\ninterface ShellRunOptions {\n candidateTranscriptRoot?: string;\n candidateArtifactRoots?: CandidateArtifactRoots;\n runtimePolicy?: RuntimePolicy;\n workspace: string;\n fixtureBinDir?: string;\n toolchainBinDir?: string;\n uvRuntimeCache?: boolean;\n protectedRoots: string[];\n protectedRootsCanonical?: boolean;\n privateStateRoot: string;\n signal?: AbortSignal;\n}\nasync function shellRuntime(options: ShellRunOptions) {\n if (options.runtimePolicy) {\n const runtime = await prepareRuntimeState(\n options.workspace,\n options.runtimePolicy,\n \"checks\",\n join(options.privateStateRoot, \"runtime\", hashJson(options.workspace)),\n );\n return {\n ...runtime,\n uvCache: join(runtime.root, \"uv-cache\"),\n privateRuntimeRoot: runtime.root,\n };\n }\n const root = join(\n options.privateStateRoot,\n \"runtime\",\n hashJson(options.workspace),\n ),\n home = join(root, \"check-home\"),\n temp = join(root, \"check-tmp\");\n await Promise.all([\n mkdir(home, { recursive: true, mode: 0o700 }),\n mkdir(temp, { recursive: true, mode: 0o700 }),\n ]);\n return {\n root,\n home,\n temp,\n uvCache: join(options.workspace, \".git\", \"sevro-runtime\", \"uv-cache\"),\n environment: {} as Record<string, string>,\n privateRuntimeRoot: root,\n writableCacheRoots: [],\n };\n}\ntype ShellRuntime = Awaited<ReturnType<typeof shellRuntime>>;\ntype Isolation = Awaited<ReturnType<typeof prepareIsolatedCommand>>;\nfunction shellRuntimeEnvironment(\n runtime: ShellRuntime,\n enabled: boolean | undefined,\n): Record<string, string> {\n return enabled\n ? {\n UV_CACHE_DIR: runtime.uvCache,\n UV_PROJECT_ENVIRONMENT: join(runtime.root, \"project-environment\"),\n UV_OFFLINE: \"1\",\n PYTHONDONTWRITEBYTECODE: \"1\",\n }\n : {};\n}\nfunction shellEnvironment(\n options: ShellRunOptions,\n runtime: ShellRuntime,\n): Record<string, string> {\n return {\n ...runtime.environment,\n ...candidateArtifactEnvironment(options.candidateArtifactRoots),\n ...(options.candidateTranscriptRoot\n ? { SEVRO_CANDIDATE_TRANSCRIPTS: options.candidateTranscriptRoot }\n : {}),\n PATH: [\n options.fixtureBinDir,\n options.toolchainBinDir,\n runtime.environment.PATH ?? \"/usr/bin:/bin:/usr/sbin:/sbin\",\n ]\n .filter(Boolean)\n .join(delimiter),\n HOME: runtime.home,\n TMPDIR: runtime.temp,\n LANG: process.env.LANG ?? \"C\",\n GIT_CONFIG_GLOBAL: \"/dev/null\",\n GIT_CONFIG_NOSYSTEM: \"1\",\n ...shellRuntimeEnvironment(runtime, options.uvRuntimeCache),\n };\n}\nasync function requireShellRuntimeCache(\n options: ShellRunOptions,\n runtime: ShellRuntime,\n): Promise<void> {\n if (options.uvRuntimeCache && !(await stat(runtime.uvCache)).isDirectory())\n throw new Error(\"isolated UV cache is missing\");\n}\nfunction shellStdout(\n check: PreparedShellCheck,\n output: Subprocess[\"stdout\"],\n): ReadableStream<Uint8Array> | null {\n if (!check.captureStdout) return null;\n if (!(output instanceof ReadableStream))\n throw new Error(\"shell stdout pipe is unavailable\");\n return output;\n}\nclass ShellExecution {\n private proc?: Subprocess;\n private readonly deadline: ProcessDeadline;\n constructor(\n private check: PreparedShellCheck,\n private options: ShellRunOptions,\n private isolation: Isolation,\n private runtime: ShellRuntime,\n ) {\n this.deadline = new ProcessDeadline({\n timeoutMs: check.timeoutMs,\n signal: options.signal,\n interruption: (reason) =>\n new Error(\n reason === \"timeout\"\n ? \"shell check timed out\"\n : \"shell check cancelled\",\n ),\n });\n }\n async run(): Promise<ShellCheckResult> {\n try {\n const running = this.spawn();\n const stream = shellStdout(this.check, running.stdout);\n return await this.deadline.wait(() =>\n Promise.all([\n running.exited,\n stream ? boundedOutput(stream) : Promise.resolve(null),\n ]).then(([exitCode, stdout]) => ({ exitCode, stdout })),\n );\n } catch (error) {\n await this.stop();\n throw error;\n } finally {\n this.deadline.dispose();\n await this.isolation.release();\n }\n }\n private spawn() {\n this.proc = spawn(this.isolation.argv, {\n cwd: this.options.workspace,\n env: shellEnvironment(this.options, this.runtime),\n detached: true,\n stdin: \"ignore\",\n stdout: this.check.captureStdout ? \"pipe\" : \"ignore\",\n stderr: \"ignore\",\n });\n return this.proc;\n }\n private async stop(): Promise<void> {\n const proc = this.proc;\n if (!proc) return;\n terminateOwnedProcessGroup(proc);\n await proc.exited;\n }\n}\n\n/** Run a check without inherited credentials or retaining raw process output. */\nexport async function runShellCheck(\n check: PreparedShellCheck,\n options: ShellRunOptions,\n): Promise<ShellCheckResult> {\n if (options.signal?.aborted) throw new Error(\"shell check cancelled\");\n const runtime = await shellRuntime(options);\n const isolated = await prepareIsolatedCommand({\n argv: [\"/bin/sh\", \"-e\", \"-c\", check.run],\n workspace: options.workspace,\n protectedRoots: [...options.protectedRoots, ...shellSeedSources(options)],\n protectedRootsCanonical: options.protectedRootsCanonical,\n privateStateRoot: options.privateStateRoot,\n denyNetwork: true,\n localHttp: runtimeLocalHttpEnabled(options.runtimePolicy, \"checks\"),\n readOnlyRoots: options.runtimePolicy?.readOnlyRoots,\n candidateArtifactRoots: options.candidateArtifactRoots,\n transcriptReadRoots: options.candidateTranscriptRoot\n ? [options.candidateTranscriptRoot]\n : [],\n writableRuntimeRoot: runtime.privateRuntimeRoot,\n writableCacheRoots: runtime.writableCacheRoots,\n });\n await requireShellRuntimeCache(options, runtime);\n return new ShellExecution(check, options, isolated, runtime).run();\n}\n\nfunction shellSeedSources(options: ShellRunOptions): string[] {\n return [\n ...runtimeSeedSources(options.runtimePolicy),\n ...sharedCacheDenyRoots(options.runtimePolicy, \"checks\"),\n ];\n}\n"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { isRecord } from "./value-guards.js";
|
|
3
|
+
function credentialStrings(value) {
|
|
4
|
+
const pending = [value];
|
|
5
|
+
const secrets = [];
|
|
6
|
+
for (let visited = 0; visited < 256; visited++) {
|
|
7
|
+
const item = pending.pop();
|
|
8
|
+
if (typeof item === "string") {
|
|
9
|
+
if (item.length >= 8)
|
|
10
|
+
secrets.push(item);
|
|
11
|
+
}
|
|
12
|
+
else if (isRecord(item))
|
|
13
|
+
pending.push(...Object.values(item));
|
|
14
|
+
}
|
|
15
|
+
return secrets;
|
|
16
|
+
}
|
|
17
|
+
/** Read credentials only for scrubbing; failures belong to the actual host preparation. */
|
|
18
|
+
export async function diagnosticSecrets(path) {
|
|
19
|
+
try {
|
|
20
|
+
if ((await stat(path)).size > 1024 * 1024)
|
|
21
|
+
return [];
|
|
22
|
+
const text = await readFile(path, "utf8");
|
|
23
|
+
try {
|
|
24
|
+
const value = JSON.parse(text);
|
|
25
|
+
return credentialStrings(value);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return text.trim() ? [text.trim()] : [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function redactDiagnosticText(text, secrets) {
|
|
36
|
+
for (const secret of secrets) {
|
|
37
|
+
text = text.replaceAll(secret, "[redacted]");
|
|
38
|
+
text = redactCredentialSuffix(text, secret);
|
|
39
|
+
}
|
|
40
|
+
return text
|
|
41
|
+
.replace(/(authorization\s*:\s*)[^\r\n]+/gi, "$1[redacted]")
|
|
42
|
+
.replace(/\bBearer\s+\S+/gi, "Bearer [redacted]")
|
|
43
|
+
.replace(/\bsk-[A-Za-z0-9_-]+/g, "[redacted]")
|
|
44
|
+
.replace(/((?:api[_-]?key|access[_-]?token|refresh[_-]?token)\s*[=:]\s*)\S+/gi, "$1[redacted]");
|
|
45
|
+
}
|
|
46
|
+
function redactCredentialSuffix(text, secret) {
|
|
47
|
+
const start = text.lastIndexOf(secret.slice(0, Math.min(8, secret.length)));
|
|
48
|
+
if (start >= 0 && secret.startsWith(text.slice(start)))
|
|
49
|
+
return text.slice(0, start) + "[redacted]";
|
|
50
|
+
return text;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=host-diagnostic-redaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-diagnostic-redaction.js","sourceRoot":"","sources":["host-diagnostic-redaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,yBAAyB;AAClD,OAAO,EAAE,QAAQ,EAAE,0BAAuB;AAE1C,SAAS,iBAAiB,CAAC,KAAc;IACvC,MAAM,OAAO,GAAc,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAC3B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC;gBAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC;aAAM,IAAI,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY;IAClD,IAAI,CAAC;QACH,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI;YAAE,OAAO,EAAE,CAAC;QACrD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1C,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,OAAiB;IAClE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAC7C,IAAI,GAAG,sBAAsB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,IAAI;SACR,OAAO,CAAC,kCAAkC,EAAE,cAAc,CAAC;SAC3D,OAAO,CAAC,kBAAkB,EAAE,mBAAmB,CAAC;SAChD,OAAO,CAAC,sBAAsB,EAAE,YAAY,CAAC;SAC7C,OAAO,CACN,qEAAqE,EACrE,cAAc,CACf,CAAC;AACN,CAAC;AAED,SAAS,sBAAsB,CAAC,IAAY,EAAE,MAAc;IAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC5E,IAAI,KAAK,IAAI,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,YAAY,CAAC;IAC7C,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import { readFile, stat } from \"node:fs/promises\";\nimport { isRecord } from \"./value-guards\";\n\nfunction credentialStrings(value: unknown): string[] {\n const pending: unknown[] = [value];\n const secrets: string[] = [];\n for (let visited = 0; visited < 256; visited++) {\n const item = pending.pop();\n if (typeof item === \"string\") {\n if (item.length >= 8) secrets.push(item);\n } else if (isRecord(item)) pending.push(...Object.values(item));\n }\n return secrets;\n}\n\n/** Read credentials only for scrubbing; failures belong to the actual host preparation. */\nexport async function diagnosticSecrets(path: string): Promise<string[]> {\n try {\n if ((await stat(path)).size > 1024 * 1024) return [];\n const text = await readFile(path, \"utf8\");\n try {\n const value: unknown = JSON.parse(text);\n return credentialStrings(value);\n } catch {\n return text.trim() ? [text.trim()] : [];\n }\n } catch {\n return [];\n }\n}\n\nexport function redactDiagnosticText(text: string, secrets: string[]): string {\n for (const secret of secrets) {\n text = text.replaceAll(secret, \"[redacted]\");\n text = redactCredentialSuffix(text, secret);\n }\n return text\n .replace(/(authorization\\s*:\\s*)[^\\r\\n]+/gi, \"$1[redacted]\")\n .replace(/\\bBearer\\s+\\S+/gi, \"Bearer [redacted]\")\n .replace(/\\bsk-[A-Za-z0-9_-]+/g, \"[redacted]\")\n .replace(\n /((?:api[_-]?key|access[_-]?token|refresh[_-]?token)\\s*[=:]\\s*)\\S+/gi,\n \"$1[redacted]\",\n );\n}\n\nfunction redactCredentialSuffix(text: string, secret: string): string {\n const start = text.lastIndexOf(secret.slice(0, Math.min(8, secret.length)));\n if (start >= 0 && secret.startsWith(text.slice(start)))\n return text.slice(0, start) + \"[redacted]\";\n return text;\n}\n"]}
|