@b00dea/cockpit 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +474 -0
- package/LICENSE +21 -0
- package/README.md +25 -2
- package/bin/cockpit.mjs +33 -0
- package/cockpit.schema.json +2644 -0
- package/dist/autonomy.d.ts +45 -0
- package/dist/autonomy.js +173 -0
- package/dist/bounded-read.d.ts +14 -0
- package/dist/bounded-read.js +42 -0
- package/dist/browser/commands.d.ts +117 -0
- package/dist/browser/commands.js +453 -0
- package/dist/browser/package-paths.d.ts +1 -0
- package/dist/browser/package-paths.js +6 -0
- package/dist/browser/proxy.d.ts +22 -0
- package/dist/browser/proxy.js +37 -0
- package/dist/browser/runtime.d.ts +118 -0
- package/dist/browser/runtime.js +186 -0
- package/dist/browser/surfaces.d.ts +141 -0
- package/dist/browser/surfaces.js +561 -0
- package/dist/browser/ui-gate.d.ts +15 -0
- package/dist/browser/ui-gate.js +18 -0
- package/dist/canonical-json.d.ts +2 -0
- package/dist/canonical-json.js +5 -0
- package/dist/cli-guard.d.ts +5 -0
- package/dist/cli-guard.js +71 -0
- package/dist/cli-main.d.ts +176 -0
- package/dist/cli-main.js +1584 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +16 -0
- package/dist/command-table.d.ts +121 -0
- package/dist/command-table.js +664 -0
- package/dist/config/load.d.ts +183 -0
- package/dist/config/load.js +367 -0
- package/dist/config/sibling-toml.d.ts +48 -0
- package/dist/config/sibling-toml.js +160 -0
- package/dist/config/surface-tools-toml.d.ts +9 -0
- package/dist/config/surface-tools-toml.js +37 -0
- package/dist/config/unconfirmed-toml.d.ts +9 -0
- package/dist/config/unconfirmed-toml.js +29 -0
- package/dist/config/verified-write.d.ts +43 -0
- package/dist/config/verified-write.js +89 -0
- package/dist/diagnostics.d.ts +26 -0
- package/dist/diagnostics.js +344 -0
- package/dist/docs/check.d.ts +16 -0
- package/dist/docs/check.js +197 -0
- package/dist/docs/decision-record.d.ts +34 -0
- package/dist/docs/decision-record.js +135 -0
- package/dist/docs/decisions.d.ts +16 -0
- package/dist/docs/decisions.js +103 -0
- package/dist/docs/markdown.d.ts +36 -0
- package/dist/docs/markdown.js +88 -0
- package/dist/docs/references.d.ts +32 -0
- package/dist/docs/references.js +148 -0
- package/dist/emit.d.ts +22 -0
- package/dist/emit.js +43 -0
- package/dist/entrypoint-kinds.d.ts +3 -0
- package/dist/entrypoint-kinds.js +11 -0
- package/dist/environment/application/confirm.d.ts +35 -0
- package/dist/environment/application/confirm.js +122 -0
- package/dist/environment/application/destructive.d.ts +6 -0
- package/dist/environment/application/destructive.js +429 -0
- package/dist/environment/application/doctor.d.ts +94 -0
- package/dist/environment/application/doctor.js +247 -0
- package/dist/environment/application/guard.d.ts +49 -0
- package/dist/environment/application/guard.js +677 -0
- package/dist/environment/application/health.d.ts +143 -0
- package/dist/environment/application/health.js +385 -0
- package/dist/environment/application/lifecycle-context.d.ts +235 -0
- package/dist/environment/application/lifecycle-context.js +154 -0
- package/dist/environment/application/lifecycle-state.d.ts +25 -0
- package/dist/environment/application/lifecycle-state.js +202 -0
- package/dist/environment/application/lifecycle.d.ts +18 -0
- package/dist/environment/application/lifecycle.js +737 -0
- package/dist/environment/application/logs.d.ts +45 -0
- package/dist/environment/application/logs.js +127 -0
- package/dist/environment/application/services.d.ts +60 -0
- package/dist/environment/application/services.js +89 -0
- package/dist/environment/application/setup.d.ts +129 -0
- package/dist/environment/application/setup.js +741 -0
- package/dist/environment/application/shared.d.ts +11 -0
- package/dist/environment/application/shared.js +61 -0
- package/dist/environment/application/siblings.d.ts +43 -0
- package/dist/environment/application/siblings.js +269 -0
- package/dist/environment/claims.d.ts +389 -0
- package/dist/environment/claims.js +1701 -0
- package/dist/environment/codex-hook-trust.d.ts +11 -0
- package/dist/environment/codex-hook-trust.js +185 -0
- package/dist/environment/compose-environment.d.ts +1 -0
- package/dist/environment/compose-environment.js +6 -0
- package/dist/environment/compose-ownership.d.ts +21 -0
- package/dist/environment/compose-ownership.js +117 -0
- package/dist/environment/compose-topology.d.ts +125 -0
- package/dist/environment/compose-topology.js +490 -0
- package/dist/environment/contract.d.ts +522 -0
- package/dist/environment/contract.js +794 -0
- package/dist/environment/declaration.d.ts +14 -0
- package/dist/environment/declaration.js +39 -0
- package/dist/environment/destructive-composition.d.ts +29 -0
- package/dist/environment/destructive-composition.js +333 -0
- package/dist/environment/docker-daemon.d.ts +30 -0
- package/dist/environment/docker-daemon.js +78 -0
- package/dist/environment/docker-endpoint.d.ts +10 -0
- package/dist/environment/docker-endpoint.js +19 -0
- package/dist/environment/env-schema-observation.d.ts +46 -0
- package/dist/environment/env-schema-observation.js +241 -0
- package/dist/environment/evidence.d.ts +136 -0
- package/dist/environment/evidence.js +437 -0
- package/dist/environment/executable.d.ts +38 -0
- package/dist/environment/executable.js +190 -0
- package/dist/environment/generated-application/authorities.d.ts +5 -0
- package/dist/environment/generated-application/authorities.js +61 -0
- package/dist/environment/generated-application/bootstrap-skill.d.ts +16 -0
- package/dist/environment/generated-application/bootstrap-skill.js +328 -0
- package/dist/environment/generated-application/check.d.ts +13 -0
- package/dist/environment/generated-application/check.js +191 -0
- package/dist/environment/generated-application/custom-surface.d.ts +26 -0
- package/dist/environment/generated-application/custom-surface.js +289 -0
- package/dist/environment/generated-application/prepare.d.ts +29 -0
- package/dist/environment/generated-application/prepare.js +278 -0
- package/dist/environment/generated-application/publication.d.ts +33 -0
- package/dist/environment/generated-application/publication.js +547 -0
- package/dist/environment/generated-application/shared.d.ts +492 -0
- package/dist/environment/generated-application/shared.js +354 -0
- package/dist/environment/generated-application/surface-observation.d.ts +4 -0
- package/dist/environment/generated-application/surface-observation.js +69 -0
- package/dist/environment/generated-application/surface-outputs.d.ts +71 -0
- package/dist/environment/generated-application/surface-outputs.js +400 -0
- package/dist/environment/generated-application/surfaces.d.ts +3 -0
- package/dist/environment/generated-application/surfaces.js +704 -0
- package/dist/environment/generated-application/sync.d.ts +6 -0
- package/dist/environment/generated-application/sync.js +124 -0
- package/dist/environment/git-observation.d.ts +202 -0
- package/dist/environment/git-observation.js +553 -0
- package/dist/environment/health-composition.d.ts +12 -0
- package/dist/environment/health-composition.js +645 -0
- package/dist/environment/host-observation.d.ts +56 -0
- package/dist/environment/host-observation.js +52 -0
- package/dist/environment/identity.d.ts +48 -0
- package/dist/environment/identity.js +85 -0
- package/dist/environment/init-application.d.ts +333 -0
- package/dist/environment/init-application.js +726 -0
- package/dist/environment/install-policy-observation.d.ts +71 -0
- package/dist/environment/install-policy-observation.js +284 -0
- package/dist/environment/install-policy.d.ts +48 -0
- package/dist/environment/install-policy.js +96 -0
- package/dist/environment/instance.d.ts +33 -0
- package/dist/environment/instance.js +122 -0
- package/dist/environment/instruction-pointer.d.ts +19 -0
- package/dist/environment/instruction-pointer.js +144 -0
- package/dist/environment/lifecycle-composition.d.ts +31 -0
- package/dist/environment/lifecycle-composition.js +742 -0
- package/dist/environment/lifecycle-stage.d.ts +120 -0
- package/dist/environment/lifecycle-stage.js +408 -0
- package/dist/environment/lifecycle.d.ts +75 -0
- package/dist/environment/lifecycle.js +354 -0
- package/dist/environment/logs.d.ts +132 -0
- package/dist/environment/logs.js +519 -0
- package/dist/environment/map-limit.d.ts +2 -0
- package/dist/environment/map-limit.js +13 -0
- package/dist/environment/mcp-observation.d.ts +9 -0
- package/dist/environment/mcp-observation.js +46 -0
- package/dist/environment/observations.d.ts +46 -0
- package/dist/environment/observations.js +346 -0
- package/dist/environment/operator-application.d.ts +79 -0
- package/dist/environment/operator-application.js +197 -0
- package/dist/environment/ports.d.ts +182 -0
- package/dist/environment/ports.js +1205 -0
- package/dist/environment/posture-observation.d.ts +68 -0
- package/dist/environment/posture-observation.js +253 -0
- package/dist/environment/presence.d.ts +4 -0
- package/dist/environment/presence.js +26 -0
- package/dist/environment/read-composition.d.ts +32 -0
- package/dist/environment/read-composition.js +397 -0
- package/dist/environment/readiness-composition.d.ts +57 -0
- package/dist/environment/readiness-composition.js +151 -0
- package/dist/environment/reclaim.d.ts +370 -0
- package/dist/environment/reclaim.js +1940 -0
- package/dist/environment/repository-identity.d.ts +13 -0
- package/dist/environment/repository-identity.js +108 -0
- package/dist/environment/runtime-measurement-record.d.ts +38 -0
- package/dist/environment/runtime-measurement-record.js +111 -0
- package/dist/environment/runtime-measurement.d.ts +27 -0
- package/dist/environment/runtime-measurement.js +224 -0
- package/dist/environment/runtime-probe-residue.d.ts +43 -0
- package/dist/environment/runtime-probe-residue.js +148 -0
- package/dist/environment/runtime-probe.d.ts +72 -0
- package/dist/environment/runtime-probe.js +725 -0
- package/dist/environment/runtime.d.ts +551 -0
- package/dist/environment/runtime.js +2405 -0
- package/dist/environment/seed.d.ts +16 -0
- package/dist/environment/seed.js +43 -0
- package/dist/environment/selftest-application.d.ts +86 -0
- package/dist/environment/selftest-application.js +713 -0
- package/dist/environment/sensitive.d.ts +3 -0
- package/dist/environment/sensitive.js +48 -0
- package/dist/environment/served-checkout-grants.d.ts +53 -0
- package/dist/environment/served-checkout-grants.js +182 -0
- package/dist/environment/setup-observation.d.ts +62 -0
- package/dist/environment/setup-observation.js +1473 -0
- package/dist/environment/setup.d.ts +43 -0
- package/dist/environment/setup.js +176 -0
- package/dist/environment/sibling-checkouts.d.ts +119 -0
- package/dist/environment/sibling-checkouts.js +254 -0
- package/dist/environment/sibling-instructions.d.ts +33 -0
- package/dist/environment/sibling-instructions.js +224 -0
- package/dist/environment/sibling-observation.d.ts +27 -0
- package/dist/environment/sibling-observation.js +45 -0
- package/dist/environment/sibling-pull.d.ts +23 -0
- package/dist/environment/sibling-pull.js +95 -0
- package/dist/environment/sibling-registration.d.ts +45 -0
- package/dist/environment/sibling-registration.js +538 -0
- package/dist/environment/stack-inputs.d.ts +18 -0
- package/dist/environment/stack-inputs.js +210 -0
- package/dist/environment/stack-inventory-prune.d.ts +31 -0
- package/dist/environment/stack-inventory-prune.js +129 -0
- package/dist/environment/stack-inventory-report.d.ts +44 -0
- package/dist/environment/stack-inventory-report.js +78 -0
- package/dist/environment/stack-inventory.d.ts +94 -0
- package/dist/environment/stack-inventory.js +418 -0
- package/dist/environment/state.d.ts +410 -0
- package/dist/environment/state.js +2153 -0
- package/dist/environment/templates.d.ts +140 -0
- package/dist/environment/templates.js +685 -0
- package/dist/environment/tool-registration.d.ts +9 -0
- package/dist/environment/tool-registration.js +92 -0
- package/dist/environment/upgrade-application.d.ts +19 -0
- package/dist/environment/upgrade-application.js +175 -0
- package/dist/environment-exec/execute.d.ts +105 -0
- package/dist/environment-exec/execute.js +1179 -0
- package/dist/environment-exec/libexec-program.d.ts +6 -0
- package/dist/environment-exec/libexec-program.js +22 -0
- package/dist/environment-exec/verified-digest.d.ts +4 -0
- package/dist/environment-exec/verified-digest.js +16 -0
- package/dist/exact-keys.d.ts +7 -0
- package/dist/exact-keys.js +15 -0
- package/dist/fix-catalogue.d.ts +32 -0
- package/dist/fix-catalogue.js +60 -0
- package/dist/freshness.d.ts +29 -0
- package/dist/freshness.js +75 -0
- package/dist/generated-environment/adopt.d.ts +16 -0
- package/dist/generated-environment/adopt.js +271 -0
- package/dist/generated-environment/command.d.ts +47 -0
- package/dist/generated-environment/command.js +75 -0
- package/dist/generated-environment/transaction.d.ts +186 -0
- package/dist/generated-environment/transaction.js +1770 -0
- package/dist/grading/evaluate.d.ts +71 -0
- package/dist/grading/evaluate.js +403 -0
- package/dist/grading/grade.d.ts +33 -0
- package/dist/grading/grade.js +57 -0
- package/dist/guards/answer-confirmation.d.ts +41 -0
- package/dist/guards/answer-confirmation.js +109 -0
- package/dist/guards/evaluator.d.ts +130 -0
- package/dist/guards/evaluator.js +1258 -0
- package/dist/guards/harness-adapter.d.ts +17 -0
- package/dist/guards/harness-adapter.js +57 -0
- package/dist/guards/host.d.ts +8 -0
- package/dist/guards/host.js +10 -0
- package/dist/guards/invocation-parser.d.ts +66 -0
- package/dist/guards/invocation-parser.js +595 -0
- package/dist/guards/local-identity.d.ts +44 -0
- package/dist/guards/local-identity.js +129 -0
- package/dist/guards/policy-change.d.ts +43 -0
- package/dist/guards/policy-change.js +159 -0
- package/dist/guards/policy-pin.d.ts +13 -0
- package/dist/guards/policy-pin.js +177 -0
- package/dist/guards/protected-locations.d.ts +62 -0
- package/dist/guards/protected-locations.js +461 -0
- package/dist/guards/read-only.d.ts +37 -0
- package/dist/guards/read-only.js +315 -0
- package/dist/init/command.d.ts +41 -0
- package/dist/init/command.js +330 -0
- package/dist/init/derive.d.ts +53 -0
- package/dist/init/derive.js +819 -0
- package/dist/init/draft.d.ts +140 -0
- package/dist/init/draft.js +1228 -0
- package/dist/init/render.d.ts +47 -0
- package/dist/init/render.js +104 -0
- package/dist/init/scaffold.d.ts +53 -0
- package/dist/init/scaffold.js +121 -0
- package/dist/invocation.d.ts +13 -0
- package/dist/invocation.js +20 -0
- package/dist/package-metadata.d.ts +9 -0
- package/dist/package-metadata.js +19 -0
- package/dist/readiness.d.ts +53 -0
- package/dist/readiness.js +88 -0
- package/dist/references/named.d.ts +24 -0
- package/dist/references/named.js +27 -0
- package/dist/refusal.d.ts +15 -0
- package/dist/refusal.js +7 -0
- package/dist/registry-derivation.d.ts +31 -0
- package/dist/registry-derivation.js +32 -0
- package/dist/registry-resolve/command.d.ts +47 -0
- package/dist/registry-resolve/command.js +226 -0
- package/dist/registry-resolve/registry.d.ts +68 -0
- package/dist/registry-resolve/registry.js +292 -0
- package/dist/registry-resolve/render-shell.d.ts +18 -0
- package/dist/registry-resolve/render-shell.js +36 -0
- package/dist/repair.d.ts +29 -0
- package/dist/repair.js +8 -0
- package/dist/repository-path.d.ts +9 -0
- package/dist/repository-path.js +16 -0
- package/dist/schema-json.d.ts +1 -0
- package/dist/schema-json.js +177 -0
- package/dist/schema.d.ts +1289 -0
- package/dist/schema.js +985 -0
- package/dist/setup/command.d.ts +4 -0
- package/dist/setup/command.js +4 -0
- package/dist/sha256.d.ts +1 -0
- package/dist/sha256.js +4 -0
- package/dist/surfaces/adoption.d.ts +41 -0
- package/dist/surfaces/adoption.js +217 -0
- package/dist/surfaces/consumer-instructions.d.ts +20 -0
- package/dist/surfaces/consumer-instructions.js +43 -0
- package/dist/surfaces/contract.d.ts +42 -0
- package/dist/surfaces/contract.js +169 -0
- package/dist/surfaces/core-declarations.d.ts +24 -0
- package/dist/surfaces/core-declarations.js +79 -0
- package/dist/surfaces/development-markers.d.ts +1 -0
- package/dist/surfaces/development-markers.js +18 -0
- package/dist/surfaces/generator-adapter.d.ts +71 -0
- package/dist/surfaces/generator-adapter.js +809 -0
- package/dist/surfaces/harness-registry.d.ts +260 -0
- package/dist/surfaces/harness-registry.js +146 -0
- package/dist/surfaces/mcp.d.ts +145 -0
- package/dist/surfaces/mcp.js +349 -0
- package/dist/surfaces/merge.d.ts +30 -0
- package/dist/surfaces/merge.js +105 -0
- package/dist/surfaces/ownership.d.ts +190 -0
- package/dist/surfaces/ownership.js +795 -0
- package/dist/surfaces/projection.d.ts +136 -0
- package/dist/surfaces/projection.js +716 -0
- package/dist/surfaces/render.d.ts +79 -0
- package/dist/surfaces/render.js +636 -0
- package/dist/surfaces/restrictions.d.ts +130 -0
- package/dist/surfaces/restrictions.js +447 -0
- package/dist/surfaces/selftest.d.ts +162 -0
- package/dist/surfaces/selftest.js +393 -0
- package/dist/surfaces/skill-contract.d.ts +85 -0
- package/dist/surfaces/skill-contract.js +449 -0
- package/dist/surfaces/sync.d.ts +57 -0
- package/dist/surfaces/sync.js +164 -0
- package/dist/team-metadata.d.ts +41 -0
- package/dist/team-metadata.js +513 -0
- package/dist/testing/gates.d.ts +91 -0
- package/dist/testing/gates.js +182 -0
- package/dist/testing/hooks.d.ts +59 -0
- package/dist/testing/hooks.js +139 -0
- package/dist/testing/test-protection.d.ts +34 -0
- package/dist/testing/test-protection.js +137 -0
- package/dist/testing/verdict.d.ts +29 -0
- package/dist/testing/verdict.js +62 -0
- package/dist/testing/weakening.d.ts +15 -0
- package/dist/testing/weakening.js +30 -0
- package/dist/work-orders/application-closure.d.ts +7 -0
- package/dist/work-orders/application-closure.js +76 -0
- package/dist/work-orders/application-context.d.ts +162 -0
- package/dist/work-orders/application-context.js +137 -0
- package/dist/work-orders/application-decisions.d.ts +27 -0
- package/dist/work-orders/application-decisions.js +121 -0
- package/dist/work-orders/application-grading.d.ts +11 -0
- package/dist/work-orders/application-grading.js +212 -0
- package/dist/work-orders/application.d.ts +6 -0
- package/dist/work-orders/application.js +223 -0
- package/dist/work-orders/composition.d.ts +101 -0
- package/dist/work-orders/composition.js +1202 -0
- package/dist/work-orders/contract.d.ts +77 -0
- package/dist/work-orders/contract.js +140 -0
- package/dist/work-orders/decisions.d.ts +23 -0
- package/dist/work-orders/decisions.js +55 -0
- package/dist/work-orders/store.d.ts +207 -0
- package/dist/work-orders/store.js +291 -0
- package/docs/README.md +68 -0
- package/docs/architecture.md +136 -0
- package/docs/capability-matrix.md +58 -0
- package/docs/cli-reference.md +807 -0
- package/docs/command-list.md +62 -0
- package/docs/compose-patterns.md +99 -0
- package/docs/concepts.md +173 -0
- package/docs/configuration-essentials.md +177 -0
- package/docs/configuration-reference.md +636 -0
- package/docs/docs-check.md +76 -0
- package/docs/environment.md +948 -0
- package/docs/extending.md +43 -0
- package/docs/faq.md +113 -0
- package/docs/getting-started.md +692 -0
- package/docs/harness-surfaces.md +500 -0
- package/docs/joining-a-team.md +76 -0
- package/docs/release-policy.md +49 -0
- package/docs/safety-and-autonomy.md +463 -0
- package/docs/troubleshooting.md +382 -0
- package/docs/tutorial.md +171 -0
- package/docs/work-and-browser.md +420 -0
- package/libexec/browser-operation.cjs +270 -0
- package/libexec/fence-marker.cjs +101 -0
- package/libexec/fence-marker.d.cts +24 -0
- package/libexec/fenced-group-runner.cjs +77 -0
- package/libexec/fenced-supervisor.cjs +137 -0
- package/libexec/generated-filesystem.cjs +731 -0
- package/libexec/install-filesystem.cjs +225 -0
- package/libexec/rulesync-generator.NOTICE.md +1112 -0
- package/libexec/rulesync-generator.cjs +743 -0
- package/libexec/surface-generator-worker.cjs +266 -0
- package/libexec/surface-output-reader.cjs +99 -0
- package/libexec/surface-scratch-cleanup.cjs +14 -0
- package/libexec/validate-harness-evidence.d.mts +183 -0
- package/libexec/validate-harness-evidence.mjs +1312 -0
- package/package.json +74 -4
- package/surfaces/README.md +33 -0
- package/surfaces/agents/evaluator.agent.md +76 -0
- package/surfaces/guards/bash-guard.sh +48 -0
- package/surfaces/guards/claude.adapter.json +25 -0
- package/surfaces/guards/codex.adapter.json +24 -0
- package/surfaces/guards/opencode.adapter.json +25 -0
- package/surfaces/references/browser.md +63 -0
- package/surfaces/references/test-data-and-personas.md +84 -0
- package/surfaces/references/testing-tiers.md +11 -0
- package/surfaces/skills/cockpit-browser.skill.md +59 -0
- package/surfaces/skills/cockpit-explain.skill.md +32 -0
- package/surfaces/skills/cockpit-fix.skill.md +37 -0
- package/surfaces/skills/cockpit-setup.skill.md +39 -0
- package/surfaces/skills/cockpit-work.skill.md +34 -0
- package/surfaces/skills/collection.json +5 -0
- package/surfaces/skills/procedures/explain.md +66 -0
- package/surfaces/skills/procedures/fix.md +95 -0
- package/surfaces/skills/procedures/setup.md +452 -0
- package/surfaces/skills/procedures/work.md +134 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Renders agent surfaces for packages/cockpit/src/surfaces/generator-adapter.ts by running the pinned
|
|
4
|
+
// Rulesync bundle (libexec/rulesync-generator.cjs) over each staged job. Runs from a staged snapshot,
|
|
5
|
+
// so it must stay self-contained.
|
|
6
|
+
//
|
|
7
|
+
// argv: <request JSON> <bundle path>. The request carries the pass root and its identity, the bundle
|
|
8
|
+
// digest and bound, the output `limits`, and one entry per job (target, features, staged roots and
|
|
9
|
+
// their identities, the warning source digest and the receipt path to write).
|
|
10
|
+
// For each job the worker writes a private receipt file ({ version, target, features, reportedPaths,
|
|
11
|
+
// actualFiles, warnings }) and reports on stdout one summary line { version: 1, jobs: [...] } holding
|
|
12
|
+
// each receipt's size and digest, or the job's error cause. The adapter reads every receipt back
|
|
13
|
+
// through a separate reader before it trusts any output.
|
|
14
|
+
//
|
|
15
|
+
// The bundle is hashed and then compiled from those same bytes, never re-read by path, so a bundle
|
|
16
|
+
// replaced between the check and the load is not what runs.
|
|
17
|
+
const fs = require('node:fs')
|
|
18
|
+
const crypto = require('node:crypto')
|
|
19
|
+
const Module = require('node:module')
|
|
20
|
+
const path = require('node:path')
|
|
21
|
+
|
|
22
|
+
const digest = value => crypto.createHash('sha256').update(value).digest('hex')
|
|
23
|
+
const safe = value => typeof value === 'string' && value.length > 0 && !path.isAbsolute(value) && !value.includes('\\') && !value.includes('\0')
|
|
24
|
+
&& value.split('/').every(part => part && part !== '.' && part !== '..')
|
|
25
|
+
const sameDirectory = (value, expected) => value.isDirectory() && !value.isSymbolicLink() && value.dev === expected.dev && value.ino === expected.ino
|
|
26
|
+
|
|
27
|
+
function authenticateBundle(bundlePath, expectedSha256, maxBytes) {
|
|
28
|
+
const before = fs.lstatSync(bundlePath)
|
|
29
|
+
if (!before.isFile() || before.isSymbolicLink() || before.size > maxBytes || fs.constants.O_NOFOLLOW === undefined) throw new Error('rulesync bundle file changed')
|
|
30
|
+
const fd = fs.openSync(bundlePath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW)
|
|
31
|
+
try {
|
|
32
|
+
const opened = fs.fstatSync(fd)
|
|
33
|
+
if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size > maxBytes) throw new Error('rulesync bundle identity changed')
|
|
34
|
+
const chunks = []
|
|
35
|
+
const chunk = Buffer.allocUnsafe(65536)
|
|
36
|
+
let total = 0
|
|
37
|
+
while (true) {
|
|
38
|
+
const count = fs.readSync(fd, chunk, 0, Math.min(chunk.length, maxBytes - total + 1), null)
|
|
39
|
+
if (count === 0) break
|
|
40
|
+
total += count
|
|
41
|
+
if (total > maxBytes) throw new Error('rulesync bundle limit exceeded')
|
|
42
|
+
chunks.push(Buffer.from(chunk.subarray(0, count)))
|
|
43
|
+
}
|
|
44
|
+
const after = fs.fstatSync(fd)
|
|
45
|
+
if (total !== opened.size || after.dev !== opened.dev || after.ino !== opened.ino || after.size !== opened.size
|
|
46
|
+
|| after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs) throw new Error('rulesync bundle changed while reading')
|
|
47
|
+
const bytes = Buffer.concat(chunks, total)
|
|
48
|
+
if (digest(bytes) !== expectedSha256) throw new Error('rulesync bundle digest changed')
|
|
49
|
+
return bytes
|
|
50
|
+
} finally { fs.closeSync(fd) }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function loadAuthenticatedBundle(bundlePath, bytes) {
|
|
54
|
+
const loaded = new Module(bundlePath, module)
|
|
55
|
+
loaded.filename = bundlePath
|
|
56
|
+
loaded.paths = Module._nodeModulePaths(path.dirname(bundlePath))
|
|
57
|
+
loaded._compile(new TextDecoder('utf-8', { fatal: true }).decode(bytes), bundlePath)
|
|
58
|
+
return loaded.exports
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function inventory(root, limits) {
|
|
62
|
+
const files = []
|
|
63
|
+
let total = 0
|
|
64
|
+
const walk = (current, prefix = '') => {
|
|
65
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
66
|
+
if (!safe(entry.name)) throw new Error('unsafe generated entry')
|
|
67
|
+
const relative = prefix ? `${prefix}/${entry.name}` : entry.name
|
|
68
|
+
const target = path.join(current, entry.name)
|
|
69
|
+
const status = fs.lstatSync(target)
|
|
70
|
+
if (status.isSymbolicLink()) throw new Error('generated symbolic link')
|
|
71
|
+
if (status.isDirectory()) walk(target, relative)
|
|
72
|
+
else if (status.isFile()) {
|
|
73
|
+
if (status.size > limits.fileMaxBytes) throw new Error('generated file limit exceeded')
|
|
74
|
+
const bytes = fs.readFileSync(target)
|
|
75
|
+
total += bytes.length
|
|
76
|
+
if (files.length >= limits.outputMaxFiles || total > limits.outputMaxBytes) throw new Error('generated output limit exceeded')
|
|
77
|
+
files.push({ path: relative, bytes: bytes.length, sha256: digest(bytes) })
|
|
78
|
+
} else throw new Error('generated non-regular entry')
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
walk(root)
|
|
82
|
+
return files.sort((left, right) => left.path.localeCompare(right.path))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const describeEntries = entries => entries.map(({ label, reason }) => `'${label}' — ${reason}`).join('; ')
|
|
86
|
+
const quoteValues = values => values.map(value => JSON.stringify(value)).join(', ')
|
|
87
|
+
// Cockpit's wording names each value the person is trusting, and only settings that grant something:
|
|
88
|
+
// an empty list grants nothing, and warning about it on every sync taught people to skip the warning.
|
|
89
|
+
function warningMessage(entries) {
|
|
90
|
+
const one = entries.length === 1
|
|
91
|
+
return `Claude Code permissions: writing ${entries.length} trust-affecting ${one ? 'setting' : 'settings'} to .claude/settings.json; `
|
|
92
|
+
+ `review ${one ? 'it' : 'them'} as you would a hook because synced agent settings can widen filesystem access, approve project servers or run commands without asking. `
|
|
93
|
+
+ `${describeEntries(entries)}.`
|
|
94
|
+
}
|
|
95
|
+
// Rulesync's own spelling of its warning, which counts a present empty server list; it is accepted
|
|
96
|
+
// only to authenticate the generated settings and is replaced by cockpit's wording above.
|
|
97
|
+
function nativeWarningMessage(entries) {
|
|
98
|
+
const one = entries.length === 1
|
|
99
|
+
return `Claude Code permissions: writing ${entries.length} trust-affecting ${one ? 'setting' : 'settings'} to .claude/settings.json; `
|
|
100
|
+
+ `review ${one ? 'it' : 'them'} as you would a hook, especially if this permissions file came from '${['rulesync', 'fetch'].join(' ')}'. `
|
|
101
|
+
+ `${describeEntries(entries)}.`
|
|
102
|
+
}
|
|
103
|
+
const knownWarningEntries = [
|
|
104
|
+
{ label: 'permissions.additionalDirectories', reason: 'moves the boundary of what Claude Code may read and edit outside the project' },
|
|
105
|
+
{ label: 'enabledMcpjsonServers', reason: 'auto-approves the named servers in the project `.mcp.json`' },
|
|
106
|
+
]
|
|
107
|
+
const record = value => value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
108
|
+
const sameStrings = (left, right) => Array.isArray(left) && Array.isArray(right) && left.length === right.length
|
|
109
|
+
&& left.every((value, index) => typeof value === 'string' && value === right[index])
|
|
110
|
+
const stringList = value => Array.isArray(value) && value.every(item => typeof item === 'string')
|
|
111
|
+
|
|
112
|
+
// A captured warning is admitted only when it is exactly the warning the staged permissions source
|
|
113
|
+
// and the generated settings justify; anything else is treated as a generator diagnostic.
|
|
114
|
+
function authenticateWarnings({ job, captured, actualFiles }) {
|
|
115
|
+
if (captured.length === 0) return []
|
|
116
|
+
if (job.target !== 'claudecode' || job.warningSourceSha256 === null) return undefined
|
|
117
|
+
const sourcePath = path.join(job.inputRoot, 'permissions.jsonc')
|
|
118
|
+
if (!fs.existsSync(sourcePath)) return undefined
|
|
119
|
+
const sourceBytes = fs.readFileSync(sourcePath)
|
|
120
|
+
if (digest(sourceBytes) !== job.warningSourceSha256 || !actualFiles.some(file => file.path === '.claude/settings.json')) return undefined
|
|
121
|
+
let source
|
|
122
|
+
let settings
|
|
123
|
+
try {
|
|
124
|
+
source = JSON.parse(sourceBytes.toString('utf8'))
|
|
125
|
+
settings = JSON.parse(fs.readFileSync(path.join(job.outputRoot, '.claude/settings.json'), 'utf8'))
|
|
126
|
+
} catch {
|
|
127
|
+
// Unparseable settings cannot justify the warning; the caller reports the warning text itself.
|
|
128
|
+
return undefined
|
|
129
|
+
}
|
|
130
|
+
if (!record(source) || !record(settings)) return undefined
|
|
131
|
+
const claude = source.claudecode
|
|
132
|
+
if (!record(claude)) return undefined
|
|
133
|
+
const sourceAdditionalDirectories = record(claude.permissions) ? claude.permissions.additionalDirectories : undefined
|
|
134
|
+
const sourceEnabledMcpjsonServers = claude.enabledMcpjsonServers
|
|
135
|
+
const additionalDirectories = record(settings.permissions) ? settings.permissions.additionalDirectories : undefined
|
|
136
|
+
const enabledMcpjsonServers = settings.enabledMcpjsonServers
|
|
137
|
+
if ((additionalDirectories !== undefined || sourceAdditionalDirectories !== undefined) && !sameStrings(additionalDirectories, sourceAdditionalDirectories)
|
|
138
|
+
|| (enabledMcpjsonServers !== undefined || sourceEnabledMcpjsonServers !== undefined) && !sameStrings(enabledMcpjsonServers, sourceEnabledMcpjsonServers)) return undefined
|
|
139
|
+
const allow = record(settings.permissions) ? settings.permissions.allow : undefined
|
|
140
|
+
const entries = []
|
|
141
|
+
if (Array.isArray(additionalDirectories) && additionalDirectories.length > 0) entries.push(knownWarningEntries[0])
|
|
142
|
+
if (Array.isArray(enabledMcpjsonServers)) entries.push(knownWarningEntries[1])
|
|
143
|
+
if (entries.length === 0 || additionalDirectories !== undefined && !stringList(additionalDirectories)
|
|
144
|
+
|| enabledMcpjsonServers !== undefined && !stringList(enabledMcpjsonServers)
|
|
145
|
+
|| allow !== undefined && !stringList(allow)) return undefined
|
|
146
|
+
if (captured.length !== 1 || captured[0] !== nativeWarningMessage(entries)) return undefined
|
|
147
|
+
const granted = [
|
|
148
|
+
...additionalDirectories?.length ? [{ label: 'permissions.additionalDirectories', reason: `${knownWarningEntries[0].reason}: ${quoteValues(additionalDirectories)}` }] : [],
|
|
149
|
+
...enabledMcpjsonServers?.length ? [{ label: 'enabledMcpjsonServers', reason: `${knownWarningEntries[1].reason}: ${quoteValues(enabledMcpjsonServers)}` }] : [],
|
|
150
|
+
...allow?.length ? [{ label: 'permissions.allow', reason: `lets Claude Code run ${allow.length} command ${allow.length === 1 ? 'pattern' : 'patterns'} without asking; the file lists each one` }] : [],
|
|
151
|
+
]
|
|
152
|
+
return granted.length === 0 ? []
|
|
153
|
+
: [{ severity: 'warning', code: 'SURFACE_GENERATOR_TRUST_WARNING', target: job.target, path: '.claude/settings.json', message: warningMessage(granted) }]
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function writeReceipt(target, value, limits) {
|
|
157
|
+
const bytes = Buffer.from(`${JSON.stringify(value)}\n`)
|
|
158
|
+
if (bytes.length > limits.stdoutMaxBytes || fs.constants.O_NOFOLLOW === undefined) throw new Error('generator receipt limit exceeded')
|
|
159
|
+
const fd = fs.openSync(target, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_NOFOLLOW, 0o600)
|
|
160
|
+
try { fs.writeFileSync(fd, bytes) } finally { fs.closeSync(fd) }
|
|
161
|
+
return { bytes: bytes.length, sha256: digest(bytes) }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function generateJob(job, bundleBytes, bundlePath, limits) {
|
|
165
|
+
let failureCause = 'nonzero'
|
|
166
|
+
const captured = []
|
|
167
|
+
let capturedBytes = 0
|
|
168
|
+
const stderr = []
|
|
169
|
+
let stderrBytes = 0
|
|
170
|
+
// Rulesync reports problems on stderr and console.warn; both are captured per job, bounded, and
|
|
171
|
+
// turned into that job's failure instead of reaching cockpit's own output.
|
|
172
|
+
const originalStderrWrite = process.stderr.write
|
|
173
|
+
process.stderr.write = (chunk, encoding, callback) => {
|
|
174
|
+
const bytes = typeof chunk === 'string' ? Buffer.from(chunk, typeof encoding === 'string' ? encoding : undefined) : Buffer.from(chunk)
|
|
175
|
+
const remaining = limits.stderrMaxBytes - stderrBytes
|
|
176
|
+
if (remaining > 0) stderr.push(bytes.subarray(0, remaining))
|
|
177
|
+
stderrBytes += bytes.length
|
|
178
|
+
const done = typeof encoding === 'function' ? encoding : callback
|
|
179
|
+
if (typeof done === 'function') queueMicrotask(done)
|
|
180
|
+
return true
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
const cwdStatus = fs.lstatSync(job.cwd)
|
|
184
|
+
const outputStatus = fs.lstatSync(job.outputRoot)
|
|
185
|
+
if (!sameDirectory(cwdStatus, job.cwdIdentity) || !sameDirectory(outputStatus, job.identity)) throw new Error('job root identity changed')
|
|
186
|
+
process.chdir(job.cwd)
|
|
187
|
+
if (!sameDirectory(fs.lstatSync('.'), job.cwdIdentity)) throw new Error('job working directory changed')
|
|
188
|
+
const root = loadAuthenticatedBundle(bundlePath, bundleBytes)
|
|
189
|
+
if (typeof root.generate !== 'function') throw new Error('rulesync root generate export unavailable')
|
|
190
|
+
const originalWarn = console.warn
|
|
191
|
+
console.warn = (...args) => {
|
|
192
|
+
const warning = args.length === 1 && typeof args[0] === 'string' ? args[0] : args.map(value => String(value)).join(' ')
|
|
193
|
+
const bytes = Buffer.byteLength(warning)
|
|
194
|
+
if (captured.length >= limits.warningCount || bytes > limits.warningBytes || capturedBytes + bytes > limits.warningTotalBytes) {
|
|
195
|
+
failureCause = 'stderr'
|
|
196
|
+
captured.splice(0, captured.length, 'surface generator emitted an unbounded diagnostic')
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
captured.push(warning)
|
|
200
|
+
capturedBytes += bytes
|
|
201
|
+
}
|
|
202
|
+
let result
|
|
203
|
+
try {
|
|
204
|
+
result = await root.generate({
|
|
205
|
+
configPath: 'rulesync.jsonc', inputRoots: [job.inputRoot], outputRoots: [job.outputRoot], targets: [job.target],
|
|
206
|
+
features: job.features, global: false, delete: false, silent: false,
|
|
207
|
+
})
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if (captured.length > 0) {
|
|
210
|
+
failureCause = 'stderr'
|
|
211
|
+
throw new Error(captured.join('\n'))
|
|
212
|
+
}
|
|
213
|
+
throw error
|
|
214
|
+
} finally { console.warn = originalWarn }
|
|
215
|
+
if (stderrBytes > 0) {
|
|
216
|
+
failureCause = 'stderr'
|
|
217
|
+
throw new Error(Buffer.concat(stderr).toString('utf8'))
|
|
218
|
+
}
|
|
219
|
+
if (result.sourceLoadFailed !== false || !Array.isArray(result.sourceLoadFailedFeatures) || result.sourceLoadFailedFeatures.length !== 0) {
|
|
220
|
+
throw new Error('rulesync could not load every requested source')
|
|
221
|
+
}
|
|
222
|
+
const features = job.features.map(feature => ({ feature, count: result[`${feature}Count`], paths: result[`${feature}Paths`] }))
|
|
223
|
+
const reportedPaths = [...new Set(features.flatMap(feature => feature.paths))].sort((left, right) => left.localeCompare(right))
|
|
224
|
+
const actualFiles = inventory(job.outputRoot, limits)
|
|
225
|
+
const warnings = authenticateWarnings({ job, captured, actualFiles })
|
|
226
|
+
if (warnings === undefined) {
|
|
227
|
+
failureCause = 'stderr'
|
|
228
|
+
throw new Error(captured.join('\n'))
|
|
229
|
+
}
|
|
230
|
+
if (!sameDirectory(fs.lstatSync(job.outputRoot), job.identity)) throw new Error('output root identity changed')
|
|
231
|
+
const receipt = writeReceipt(job.receiptOutput, { version: 1, target: job.target, features, reportedPaths, actualFiles, warnings }, limits)
|
|
232
|
+
return { id: job.id, target: job.target, kind: 'complete', ...receipt }
|
|
233
|
+
} catch (error) {
|
|
234
|
+
if (stderrBytes > 0) failureCause = 'stderr'
|
|
235
|
+
const message = stderrBytes > 0 ? Buffer.concat(stderr).toString('utf8') : String(error && (error.stack || error.message) || error)
|
|
236
|
+
return { id: job.id, target: job.target, kind: 'error', cause: failureCause, message: message.slice(0, 1024) }
|
|
237
|
+
} finally {
|
|
238
|
+
process.stderr.write = originalStderrWrite
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async function main() {
|
|
243
|
+
const request = JSON.parse(process.argv[2])
|
|
244
|
+
const bundlePath = process.argv[3]
|
|
245
|
+
const bundleBytes = authenticateBundle(bundlePath, request.bundleSha256, request.bundleMaxBytes)
|
|
246
|
+
if (!sameDirectory(fs.lstatSync('.'), request.identity)) throw new Error('pass root identity changed')
|
|
247
|
+
const summaries = []
|
|
248
|
+
for (const job of request.jobs) {
|
|
249
|
+
try {
|
|
250
|
+
summaries.push(await generateJob(job, bundleBytes, bundlePath, request.limits))
|
|
251
|
+
} finally {
|
|
252
|
+
process.chdir(request.passRoot)
|
|
253
|
+
if (!sameDirectory(fs.lstatSync('.'), request.identity)) throw new Error('pass root changed')
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
process.stdout.write(`${JSON.stringify({ version: 1, jobs: summaries })}\n`)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (require.main === module) {
|
|
260
|
+
main().catch(error => {
|
|
261
|
+
process.stderr.write(`${String(error && (error.stack || error.message) || error)}\n`)
|
|
262
|
+
process.exitCode = 1
|
|
263
|
+
})
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = { authenticateBundle, loadAuthenticatedBundle }
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Reads back the surfaces the generator worker wrote, for packages/cockpit/src/surfaces/generator-adapter.ts.
|
|
4
|
+
// It is a separate process from libexec/surface-generator-worker.cjs so no code the Rulesync bundle
|
|
5
|
+
// loaded can shape what cockpit is told the output contains. Runs from a staged snapshot, so it must
|
|
6
|
+
// stay self-contained.
|
|
7
|
+
//
|
|
8
|
+
// argv: <request JSON> carrying the pass root and its identity, the output `limits`, and one entry
|
|
9
|
+
// per job (output root and its identity, the expected path set, the receipt path to write).
|
|
10
|
+
// Each job's receipt file holds { version: 1, files: [{ path, bytes, sha256, bytesBase64 }] }; stdout
|
|
11
|
+
// is one summary line { version: 1, jobs: [...] } with each receipt's size and digest or error cause.
|
|
12
|
+
// The walk moves the cwd through each directory and re-checks its identity, so a directory swapped
|
|
13
|
+
// for a link mid-walk is refused instead of followed.
|
|
14
|
+
|
|
15
|
+
const fs = require('node:fs')
|
|
16
|
+
const crypto = require('node:crypto')
|
|
17
|
+
const request = JSON.parse(process.argv[2])
|
|
18
|
+
const limits = request.limits
|
|
19
|
+
const c = fs.constants
|
|
20
|
+
const digest = value => crypto.createHash('sha256').update(value).digest('hex')
|
|
21
|
+
const safe = value => typeof value === 'string' && value.length > 0 && value !== '.' && value !== '..' && !value.includes('/') && !value.includes('\\') && !value.includes('\0')
|
|
22
|
+
const sameDirectory = (value, expected) => value.isDirectory() && !value.isSymbolicLink() && value.dev === expected.dev && value.ino === expected.ino
|
|
23
|
+
const samePaths = (left, right) => left.length === right.length && [...left].sort().every((value, index) => value === [...right].sort()[index])
|
|
24
|
+
const readFile = name => {
|
|
25
|
+
const before = fs.lstatSync(name)
|
|
26
|
+
if (!before.isFile() || before.isSymbolicLink() || before.size > limits.fileMaxBytes || c.O_NOFOLLOW === undefined) throw new Error('unsafe output file')
|
|
27
|
+
const fd = fs.openSync(name, c.O_RDONLY | c.O_NOFOLLOW)
|
|
28
|
+
try {
|
|
29
|
+
const opened = fs.fstatSync(fd)
|
|
30
|
+
if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size > limits.fileMaxBytes) throw new Error('output identity changed')
|
|
31
|
+
const chunks = []; const chunk = Buffer.allocUnsafe(65536); let total = 0
|
|
32
|
+
while (true) {
|
|
33
|
+
const count = fs.readSync(fd, chunk, 0, Math.min(chunk.length, limits.fileMaxBytes - total + 1), null)
|
|
34
|
+
if (count === 0) break
|
|
35
|
+
total += count
|
|
36
|
+
if (total > limits.fileMaxBytes) throw new Error('output file limit exceeded')
|
|
37
|
+
chunks.push(Buffer.from(chunk.subarray(0, count)))
|
|
38
|
+
}
|
|
39
|
+
const after = fs.fstatSync(fd)
|
|
40
|
+
if (total !== opened.size || after.dev !== opened.dev || after.ino !== opened.ino || after.size !== opened.size || after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs) throw new Error('output changed while reading')
|
|
41
|
+
const bytes = Buffer.concat(chunks, total)
|
|
42
|
+
return { bytes: bytes.length, sha256: digest(bytes), bytesBase64: bytes.toString('base64') }
|
|
43
|
+
} finally { fs.closeSync(fd); }
|
|
44
|
+
}
|
|
45
|
+
const inventory = job => {
|
|
46
|
+
const files = []; let total = 0
|
|
47
|
+
const walk = (prefix = '') => {
|
|
48
|
+
const parent = fs.lstatSync('.')
|
|
49
|
+
if (!parent.isDirectory() || parent.isSymbolicLink()) throw new Error('unsafe output directory')
|
|
50
|
+
for (const entry of fs.readdirSync('.', { withFileTypes: true })) {
|
|
51
|
+
if (!safe(entry.name)) throw new Error('unsafe output entry')
|
|
52
|
+
const status = fs.lstatSync(entry.name)
|
|
53
|
+
const relative = prefix ? prefix + '/' + entry.name : entry.name
|
|
54
|
+
if (status.isSymbolicLink()) throw new Error('output symbolic link')
|
|
55
|
+
if (status.isDirectory()) {
|
|
56
|
+
process.chdir(entry.name)
|
|
57
|
+
if (!sameDirectory(fs.lstatSync('.'), status)) throw new Error('output directory changed')
|
|
58
|
+
walk(relative)
|
|
59
|
+
process.chdir('..')
|
|
60
|
+
if (!sameDirectory(fs.lstatSync('.'), parent) || !sameDirectory(fs.lstatSync(entry.name), status)) throw new Error('output directory identity changed')
|
|
61
|
+
} else if (status.isFile()) {
|
|
62
|
+
const file = readFile(entry.name)
|
|
63
|
+
total += file.bytes
|
|
64
|
+
if (files.length >= limits.outputMaxFiles || total > limits.outputMaxBytes) throw new Error('output set limit exceeded')
|
|
65
|
+
files.push({ path: relative, ...file })
|
|
66
|
+
} else throw new Error('output non-regular entry')
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
process.chdir(job.outputRoot)
|
|
70
|
+
if (!sameDirectory(fs.lstatSync('.'), job.identity)) throw new Error('output root identity changed')
|
|
71
|
+
walk()
|
|
72
|
+
if (!sameDirectory(fs.lstatSync('.'), job.identity)) throw new Error('output root changed')
|
|
73
|
+
files.sort((left, right) => left.path.localeCompare(right.path))
|
|
74
|
+
if (!samePaths(files.map(file => file.path), job.expected)) throw new Error('output path set changed')
|
|
75
|
+
return files
|
|
76
|
+
}
|
|
77
|
+
const writeReceipt = (target, value) => {
|
|
78
|
+
const bytes = Buffer.from(JSON.stringify(value) + '\n')
|
|
79
|
+
if (bytes.length > limits.stdoutMaxBytes || c.O_NOFOLLOW === undefined) throw new Error('reader receipt limit exceeded')
|
|
80
|
+
const fd = fs.openSync(target, c.O_WRONLY | c.O_CREAT | c.O_EXCL | c.O_NOFOLLOW, 0o600)
|
|
81
|
+
try { fs.writeFileSync(fd, bytes); } finally { fs.closeSync(fd); }
|
|
82
|
+
return { bytes: bytes.length, sha256: digest(bytes) }
|
|
83
|
+
}
|
|
84
|
+
const passRoot = fs.lstatSync('.')
|
|
85
|
+
if (!sameDirectory(passRoot, request.identity)) throw new Error('pass root identity changed')
|
|
86
|
+
const summaries = []
|
|
87
|
+
for (const job of request.jobs) {
|
|
88
|
+
try {
|
|
89
|
+
const files = inventory(job)
|
|
90
|
+
const receipt = writeReceipt(job.receiptOutput, { version: 1, files })
|
|
91
|
+
summaries.push({ id: job.id, target: job.target, kind: 'complete', ...receipt })
|
|
92
|
+
} catch (error) {
|
|
93
|
+
summaries.push({ id: job.id, target: job.target, kind: 'error', cause: 'artifact', message: String(error && (error.stack || error.message) || error).slice(0, 4096) })
|
|
94
|
+
} finally {
|
|
95
|
+
process.chdir(request.passRoot)
|
|
96
|
+
if (!sameDirectory(fs.lstatSync('.'), request.identity)) throw new Error('pass root changed')
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
process.stdout.write(JSON.stringify({ version: 1, jobs: summaries }) + '\n')
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Removes the private staging directory that packages/cockpit/src/surfaces/generator-adapter.ts
|
|
4
|
+
// created for one surface generation. Runs from a staged snapshot, so it must stay self-contained.
|
|
5
|
+
//
|
|
6
|
+
// argv: <request JSON { target, parent }>. Refuses (non-zero exit) any target that is not a
|
|
7
|
+
// cockpit-surfaces-* directory directly inside the named parent.
|
|
8
|
+
const fs = require('node:fs')
|
|
9
|
+
const path = require('node:path')
|
|
10
|
+
|
|
11
|
+
const request = JSON.parse(process.argv[2])
|
|
12
|
+
const target = request.target
|
|
13
|
+
if (path.dirname(target) !== request.parent || !path.basename(target).startsWith('cockpit-surfaces-')) throw new Error('cleanup target refused')
|
|
14
|
+
fs.rmSync(target, { recursive: true, force: true })
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
export interface HarnessEvidenceManifest {
|
|
2
|
+
harness: 'Claude Code' | 'Codex' | 'OpenCode'
|
|
3
|
+
version: string
|
|
4
|
+
date: string
|
|
5
|
+
capability: 'sandbox' | 'approval'
|
|
6
|
+
candidatePath: string
|
|
7
|
+
brokenPath: string
|
|
8
|
+
transcriptPath: string
|
|
9
|
+
candidateSha256: string
|
|
10
|
+
brokenSha256: string
|
|
11
|
+
transcriptSha256: string
|
|
12
|
+
runId: string
|
|
13
|
+
versionCommand: readonly string[]
|
|
14
|
+
helpCommand: readonly string[]
|
|
15
|
+
invocation: readonly string[]
|
|
16
|
+
forbiddenAction: string
|
|
17
|
+
brokenRejected: boolean
|
|
18
|
+
verdict: 'enforced' | 'unavailable'
|
|
19
|
+
syntheticFixture: string
|
|
20
|
+
events: {
|
|
21
|
+
version: HarnessProbeEvent
|
|
22
|
+
help: HarnessProbeEvent
|
|
23
|
+
brokenControl: HarnessProbeEvent
|
|
24
|
+
candidateControl: HarnessProbeEvent
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface HarnessProbeEvent {
|
|
29
|
+
cwd: string
|
|
30
|
+
command: readonly string[]
|
|
31
|
+
exitCode: number
|
|
32
|
+
stream: 'stdout' | 'stderr'
|
|
33
|
+
code: string
|
|
34
|
+
result: 'completed' | 'rejected' | 'accepted' | 'refused' | 'allowed' | 'unavailable'
|
|
35
|
+
output: string
|
|
36
|
+
outputSha256: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface HarnessEvidenceValidationOptions {
|
|
40
|
+
testOnlyExecutableIdentities?: Partial<Record<HarnessEvidenceManifest['harness'], readonly string[]>>
|
|
41
|
+
operatorExecutableIdentities?: ReadonlyMap<
|
|
42
|
+
HarnessEvidenceManifest['harness'],
|
|
43
|
+
Readonly<{ canonicalPath: string; sha256: string }>
|
|
44
|
+
>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type SelftestCapability = 'instruction-loading' | 'guard-firing' | 'skill-parity'
|
|
48
|
+
export type BuiltinHarnessRef =
|
|
49
|
+
| { kind: 'builtin'; target: 'claudecode'; name: 'Claude Code' }
|
|
50
|
+
| { kind: 'builtin'; target: 'codexcli'; name: 'Codex' }
|
|
51
|
+
| { kind: 'builtin'; target: 'opencode'; name: 'OpenCode' }
|
|
52
|
+
export interface CustomHarnessRef {
|
|
53
|
+
kind: 'custom'
|
|
54
|
+
id: string
|
|
55
|
+
declarationSha256: string
|
|
56
|
+
module: string
|
|
57
|
+
sourceSha256: string
|
|
58
|
+
export: string
|
|
59
|
+
}
|
|
60
|
+
export type SelftestHarnessRef = BuiltinHarnessRef | CustomHarnessRef
|
|
61
|
+
export type SelftestCellKey =
|
|
62
|
+
| { harness: SelftestHarnessRef; capability: 'instruction-loading' | 'guard-firing' }
|
|
63
|
+
| { harness: SelftestHarnessRef; capability: 'skill-parity'; skill: string }
|
|
64
|
+
export interface SelftestExpectation {
|
|
65
|
+
schema: 'cockpit-harness-selftest-expectation/v1'
|
|
66
|
+
generatedManifestSha256: string
|
|
67
|
+
cells: readonly SelftestCellKey[]
|
|
68
|
+
shippedSkills: readonly string[]
|
|
69
|
+
reviewHarnesses: readonly [
|
|
70
|
+
{ name: 'Claude Code'; nativeDiagnostics: boolean },
|
|
71
|
+
{ name: 'Codex'; nativeDiagnostics: boolean },
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
export interface SelftestProbeEvent {
|
|
75
|
+
cwd: string
|
|
76
|
+
command: readonly string[]
|
|
77
|
+
exitCode: number
|
|
78
|
+
signal: NodeJS.Signals | null
|
|
79
|
+
result: 'completed' | 'rejected' | 'accepted' | 'refused' | 'allowed' | 'unavailable'
|
|
80
|
+
code: string
|
|
81
|
+
stdout: string
|
|
82
|
+
stderr: string
|
|
83
|
+
stdoutSha256: string
|
|
84
|
+
stderrSha256: string
|
|
85
|
+
}
|
|
86
|
+
export interface BuiltinExecutionAuthority {
|
|
87
|
+
kind: 'native-executable'
|
|
88
|
+
name: 'Claude Code' | 'Codex' | 'OpenCode'
|
|
89
|
+
executable: string
|
|
90
|
+
}
|
|
91
|
+
export interface CustomExecutionAuthority {
|
|
92
|
+
kind: 'custom-module'
|
|
93
|
+
harnessId: string
|
|
94
|
+
module: string
|
|
95
|
+
sourceSha256: string
|
|
96
|
+
export: string
|
|
97
|
+
stepId: string
|
|
98
|
+
startedAt: string
|
|
99
|
+
finishedAt: string
|
|
100
|
+
}
|
|
101
|
+
export type SelftestExecutionAuthority = BuiltinExecutionAuthority | CustomExecutionAuthority
|
|
102
|
+
export interface SelftestSample { skill: string; features: readonly string[] }
|
|
103
|
+
export interface SelftestEvidenceRecord {
|
|
104
|
+
kind: 'evidence'
|
|
105
|
+
harness: SelftestHarnessRef
|
|
106
|
+
capability: SelftestCapability
|
|
107
|
+
generatedManifestSha256: string
|
|
108
|
+
verdict: 'enforced' | 'instructed' | 'unavailable'
|
|
109
|
+
version: string
|
|
110
|
+
measuredOn: string
|
|
111
|
+
runId: string
|
|
112
|
+
startedAt: string
|
|
113
|
+
finishedAt: string
|
|
114
|
+
expiresAt: string
|
|
115
|
+
candidatePath: string
|
|
116
|
+
brokenPath: string
|
|
117
|
+
transcriptPath: string
|
|
118
|
+
candidateSha256: string
|
|
119
|
+
brokenSha256: string
|
|
120
|
+
transcriptSha256: string
|
|
121
|
+
invocation: readonly string[]
|
|
122
|
+
versionCommand: readonly string[]
|
|
123
|
+
helpCommand: readonly string[]
|
|
124
|
+
candidateCode: string
|
|
125
|
+
brokenCode: string
|
|
126
|
+
sample: SelftestSample | null
|
|
127
|
+
authority: SelftestExecutionAuthority
|
|
128
|
+
events: { version: SelftestProbeEvent; help: SelftestProbeEvent; brokenControl: SelftestProbeEvent; candidateControl: SelftestProbeEvent }
|
|
129
|
+
}
|
|
130
|
+
export type SelftestHumanNeededRecord = {
|
|
131
|
+
kind: 'human_needed'
|
|
132
|
+
harness: SelftestHarnessRef
|
|
133
|
+
generatedManifestSha256: string
|
|
134
|
+
observedAt: string
|
|
135
|
+
reason: 'executable_missing' | 'authentication_required' | 'isolation_unavailable' | 'enforcement_probe_required'
|
|
136
|
+
operatorAction: string
|
|
137
|
+
} & (
|
|
138
|
+
| { capability: 'instruction-loading' | 'guard-firing' }
|
|
139
|
+
| { capability: 'skill-parity'; skill: string }
|
|
140
|
+
)
|
|
141
|
+
export interface SelftestSkillReviewRecord {
|
|
142
|
+
skill: string
|
|
143
|
+
harness: 'Claude Code' | 'Codex'
|
|
144
|
+
status: 'evidenced' | 'human_needed'
|
|
145
|
+
reviewedOn: string | null
|
|
146
|
+
evidenceRunId: string | null
|
|
147
|
+
reviewers: readonly ('official-authoring' | 'harness-diagnostics')[]
|
|
148
|
+
checks: { trigger: 'pass'; silence: 'pass'; bodyLoad: 'pass'; tools: 'pass'; nonGoals: 'pass' } | null
|
|
149
|
+
operatorAction: string | null
|
|
150
|
+
}
|
|
151
|
+
export interface SelftestEvidenceBundle {
|
|
152
|
+
schema: 'cockpit-harness-selftest/v1'
|
|
153
|
+
generatedManifestSha256: string
|
|
154
|
+
records: readonly (SelftestEvidenceRecord | SelftestHumanNeededRecord)[]
|
|
155
|
+
skillInventory: readonly string[]
|
|
156
|
+
skillReviews: readonly SelftestSkillReviewRecord[]
|
|
157
|
+
}
|
|
158
|
+
export interface SelftestEvidenceValidationOptions extends HarnessEvidenceValidationOptions { testOnlyNow?: string }
|
|
159
|
+
export interface SelftestEvidenceValidationResult {
|
|
160
|
+
errors: readonly string[]
|
|
161
|
+
evidence: readonly SelftestEvidenceRecord[]
|
|
162
|
+
pending: readonly SelftestHumanNeededRecord[]
|
|
163
|
+
skillReviews: readonly SelftestSkillReviewRecord[]
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export const PROBE_EVIDENCE_ROOT: '.cockpit/local/probes'
|
|
167
|
+
export function validateEvidenceManifest(
|
|
168
|
+
manifest: unknown,
|
|
169
|
+
root?: string,
|
|
170
|
+
options?: HarnessEvidenceValidationOptions,
|
|
171
|
+
): string[]
|
|
172
|
+
export function validateEvidenceManifests(
|
|
173
|
+
manifests: readonly unknown[],
|
|
174
|
+
root?: string,
|
|
175
|
+
options?: HarnessEvidenceValidationOptions,
|
|
176
|
+
): { errors: string[]; rows: HarnessEvidenceManifest[] }
|
|
177
|
+
export function validateSelftestEvidenceBundle(
|
|
178
|
+
bundle: unknown,
|
|
179
|
+
expectation: unknown,
|
|
180
|
+
root?: string,
|
|
181
|
+
options?: SelftestEvidenceValidationOptions,
|
|
182
|
+
): SelftestEvidenceValidationResult
|
|
183
|
+
export function runHarnessEvidenceValidatorCli(paths: readonly string[]): number
|