@intentius/chant 0.79.0 → 0.81.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/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +30 -0
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts +5 -0
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +13 -0
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/terraform-state.d.ts +13 -0
- package/dist/audit/terraform-state.d.ts.map +1 -1
- package/dist/cli/command-group.d.ts +6 -0
- package/dist/cli/command-group.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +44 -3
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.d.ts +8 -0
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/import-agents.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/update.d.ts +6 -0
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/vendor.d.ts +5 -0
- package/dist/cli/commands/vendor.d.ts.map +1 -1
- package/dist/cli/conflict-check.d.ts +6 -0
- package/dist/cli/conflict-check.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/init.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +7 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/promote.d.ts +20 -0
- package/dist/cli/handlers/promote.d.ts.map +1 -0
- package/dist/cli/handlers/run-generate.d.ts +39 -0
- package/dist/cli/handlers/run-generate.d.ts.map +1 -0
- package/dist/cli/handlers/run.d.ts +4 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/vendor.d.ts +8 -3
- package/dist/cli/handlers/vendor.d.ts.map +1 -1
- package/dist/cli/main.d.ts +12 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/plugins.d.ts +14 -0
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +46 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/fetch.d.ts +10 -0
- package/dist/codegen/fetch.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +3 -1
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/discover.d.ts.map +1 -1
- package/dist/components/driver.d.ts +16 -2
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/fan-out-support.d.ts.map +1 -1
- package/dist/components/gate-plan.d.ts +40 -0
- package/dist/components/gate-plan.d.ts.map +1 -0
- package/dist/components/pilots/alb-ecs.pilot.d.ts +7 -1
- package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -1
- package/dist/components/presets/ecs-fargate.d.ts +10 -4
- package/dist/components/presets/ecs-fargate.d.ts.map +1 -1
- package/dist/components/promote.d.ts +264 -0
- package/dist/components/promote.d.ts.map +1 -0
- package/dist/components/sandbox/driver.d.ts.map +1 -1
- package/dist/components/sandbox/run.d.ts.map +1 -1
- package/dist/components/verbs/build-archive.d.ts +22 -11
- package/dist/components/verbs/build-archive.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/config-static.d.ts +59 -0
- package/dist/config-static.d.ts.map +1 -0
- package/dist/config.d.ts +104 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/content-digest.d.ts +17 -0
- package/dist/content-digest.d.ts.map +1 -0
- package/dist/discovery/files.d.ts +48 -4
- package/dist/discovery/files.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +32 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/sandbox/driver.d.ts +20 -0
- package/dist/discovery/sandbox/driver.d.ts.map +1 -1
- package/dist/discovery/sandbox/fork.d.ts +20 -0
- package/dist/discovery/sandbox/fork.d.ts.map +1 -1
- package/dist/discovery/walk.d.ts +119 -0
- package/dist/discovery/walk.d.ts.map +1 -0
- package/dist/graph-ir.d.ts +17 -1
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/join-key.d.ts +31 -0
- package/dist/join-key.d.ts.map +1 -0
- package/dist/lexicon-module.d.ts +110 -0
- package/dist/lexicon-module.d.ts.map +1 -0
- package/dist/lexicon.d.ts +82 -1
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/build-ledger-store.d.ts.map +1 -1
- package/dist/lifecycle/digest.d.ts +5 -1
- package/dist/lifecycle/digest.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +20 -5
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts +64 -0
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/lease.d.ts +8 -1
- package/dist/lifecycle/lease.d.ts.map +1 -1
- package/dist/lifecycle/legacy-digest.d.ts +59 -0
- package/dist/lifecycle/legacy-digest.d.ts.map +1 -0
- package/dist/lifecycle/member-ledger.d.ts +67 -0
- package/dist/lifecycle/member-ledger.d.ts.map +1 -0
- package/dist/lifecycle/plan-digest.d.ts +4 -3
- package/dist/lifecycle/plan-digest.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts +42 -1
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/status.d.ts +90 -5
- package/dist/lifecycle/status.d.ts.map +1 -1
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +2 -3
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.d.ts +32 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +2 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/propose-upgrade.d.ts +75 -0
- package/dist/op/activities/propose-upgrade.d.ts.map +1 -0
- package/dist/op/activity-contract-registry.d.ts.map +1 -1
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-name.d.ts +7 -0
- package/dist/op/gate-name.d.ts.map +1 -1
- package/dist/op/gate-summary.d.ts +16 -0
- package/dist/op/gate-summary.d.ts.map +1 -1
- package/dist/op/gate.d.ts +16 -2
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/generate-pipeline.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/project-root.d.ts +26 -0
- package/dist/project-root.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.d.ts +37 -0
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts +11 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts.map +1 -0
- package/dist/workspace/checks/generated.d.ts +156 -0
- package/dist/workspace/checks/generated.d.ts.map +1 -0
- package/dist/workspace/checks/ledgers.d.ts +78 -0
- package/dist/workspace/checks/ledgers.d.ts.map +1 -0
- package/dist/workspace/checks/links.d.ts +25 -0
- package/dist/workspace/checks/links.d.ts.map +1 -0
- package/dist/workspace/checks/pipelines.d.ts +76 -0
- package/dist/workspace/checks/pipelines.d.ts.map +1 -0
- package/dist/workspace/checks.d.ts +161 -0
- package/dist/workspace/checks.d.ts.map +1 -0
- package/dist/workspace/compose-graph.d.ts +115 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -0
- package/dist/workspace/compose-reports.d.ts +68 -0
- package/dist/workspace/compose-reports.d.ts.map +1 -0
- package/dist/workspace/declaration.d.ts +220 -0
- package/dist/workspace/declaration.d.ts.map +1 -0
- package/dist/workspace/declaration.schema.json +434 -0
- package/dist/workspace/generated-files.d.ts +65 -0
- package/dist/workspace/generated-files.d.ts.map +1 -0
- package/dist/workspace/glob.d.ts +13 -0
- package/dist/workspace/glob.d.ts.map +1 -0
- package/dist/workspace/graph-cli.d.ts +75 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -0
- package/dist/workspace/init.d.ts +96 -0
- package/dist/workspace/init.d.ts.map +1 -0
- package/dist/workspace/jsonc.d.ts +38 -0
- package/dist/workspace/jsonc.d.ts.map +1 -0
- package/dist/workspace/kinds.d.ts +192 -0
- package/dist/workspace/kinds.d.ts.map +1 -0
- package/dist/workspace/lineage-check.d.ts +84 -0
- package/dist/workspace/lineage-check.d.ts.map +1 -0
- package/dist/workspace/lineage-cli.d.ts +31 -0
- package/dist/workspace/lineage-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-init.d.ts +101 -0
- package/dist/workspace/lineage-init.d.ts.map +1 -0
- package/dist/workspace/lineage-lock.d.ts +258 -0
- package/dist/workspace/lineage-lock.d.ts.map +1 -0
- package/dist/workspace/lineage-merge.d.ts +14 -0
- package/dist/workspace/lineage-merge.d.ts.map +1 -0
- package/dist/workspace/lineage-migrations.d.ts +178 -0
- package/dist/workspace/lineage-migrations.d.ts.map +1 -0
- package/dist/workspace/lineage-update.d.ts +59 -0
- package/dist/workspace/lineage-update.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts +40 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade.d.ts +146 -0
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -0
- package/dist/workspace/lineage-vendor.d.ts +63 -0
- package/dist/workspace/lineage-vendor.d.ts.map +1 -0
- package/dist/workspace/lineage-version.d.ts +32 -0
- package/dist/workspace/lineage-version.d.ts.map +1 -0
- package/dist/workspace/links.d.ts +150 -0
- package/dist/workspace/links.d.ts.map +1 -0
- package/dist/workspace/ls.d.ts +99 -0
- package/dist/workspace/ls.d.ts.map +1 -0
- package/dist/workspace/member-commands.d.ts +139 -0
- package/dist/workspace/member-commands.d.ts.map +1 -0
- package/dist/workspace/member-handles.d.ts +54 -0
- package/dist/workspace/member-handles.d.ts.map +1 -0
- package/dist/workspace/member-pipeline.d.ts +143 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -0
- package/dist/workspace/member-run.d.ts +67 -0
- package/dist/workspace/member-run.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +65 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -0
- package/dist/workspace/record-source.d.ts +22 -0
- package/dist/workspace/record-source.d.ts.map +1 -0
- package/dist/workspace/records-cli.d.ts +81 -0
- package/dist/workspace/records-cli.d.ts.map +1 -0
- package/dist/workspace/records.d.ts +118 -0
- package/dist/workspace/records.d.ts.map +1 -0
- package/dist/workspace/root-refusal.d.ts +53 -0
- package/dist/workspace/root-refusal.d.ts.map +1 -0
- package/dist/workspace/status.d.ts +150 -0
- package/dist/workspace/status.d.ts.map +1 -0
- package/dist/workspace/template-manifest.d.ts +90 -0
- package/dist/workspace/template-manifest.d.ts.map +1 -0
- package/dist/workspace/tree.d.ts +36 -0
- package/dist/workspace/tree.d.ts.map +1 -0
- package/dist/workspace/trust/attestor.d.ts +67 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -0
- package/dist/workspace/trust/policy.d.ts +111 -0
- package/dist/workspace/trust/policy.d.ts.map +1 -0
- package/dist/workspace/trust/provenance.d.ts +67 -0
- package/dist/workspace/trust/provenance.d.ts.map +1 -0
- package/dist/workspace/trust/ssh-commit.d.ts +43 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -0
- package/dist/workspace/trust/test-repo.d.ts +42 -0
- package/dist/workspace/trust/test-repo.d.ts.map +1 -0
- package/dist/workspace/trust/verify-cli.d.ts +9 -0
- package/dist/workspace/trust/verify-cli.d.ts.map +1 -0
- package/dist/workspace/trust/verify.d.ts +66 -0
- package/dist/workspace/trust/verify.d.ts.map +1 -0
- package/dist/workspace/which-chant.d.ts +58 -0
- package/dist/workspace/which-chant.d.ts.map +1 -0
- package/dist/workspace/workspace-kinds.schema.json +133 -0
- package/package.json +4 -3
- package/src/audit/core.ts +13 -2
- package/src/audit/discover.ts +102 -36
- package/src/audit/report-html.ts +3 -1
- package/src/audit/report-model.ts +14 -1
- package/src/audit/terraform-state.test.ts +81 -1
- package/src/audit/terraform-state.ts +25 -0
- package/src/cli/command-group.ts +7 -0
- package/src/cli/commands/audit-truncation.test.ts +167 -0
- package/src/cli/commands/audit.ts +132 -29
- package/src/cli/commands/check-lexicon-examples.ts +2 -1
- package/src/cli/commands/check-lexicon.ts +24 -0
- package/src/cli/commands/doctor.test.ts +30 -0
- package/src/cli/commands/doctor.ts +21 -0
- package/src/cli/commands/import-agents.ts +15 -4
- package/src/cli/commands/init.ts +76 -25
- package/src/cli/commands/lint.test.ts +52 -1
- package/src/cli/commands/lint.ts +41 -71
- package/src/cli/commands/onboard.ts +15 -0
- package/src/cli/commands/update.ts +18 -5
- package/src/cli/commands/vendor.ts +11 -6
- package/src/cli/conflict-check.test.ts +18 -1
- package/src/cli/conflict-check.ts +13 -0
- package/src/cli/discovery-skip.test.ts +168 -0
- package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +38 -0
- package/src/cli/handlers/build-member.test.ts +127 -0
- package/src/cli/handlers/build.test.ts +11 -0
- package/src/cli/handlers/build.ts +35 -1
- package/src/cli/handlers/components.test.ts +127 -0
- package/src/cli/handlers/components.ts +70 -8
- package/src/cli/handlers/dev.ts +6 -1
- package/src/cli/handlers/fan-out.ts +9 -2
- package/src/cli/handlers/graph.test.ts +20 -0
- package/src/cli/handlers/graph.ts +4 -2
- package/src/cli/handlers/init.ts +54 -1
- package/src/cli/handlers/lifecycle.ts +2 -1
- package/src/cli/handlers/misc.ts +16 -0
- package/src/cli/handlers/operator.test.ts +68 -0
- package/src/cli/handlers/operator.ts +39 -10
- package/src/cli/handlers/promote.test.ts +377 -0
- package/src/cli/handlers/promote.ts +391 -0
- package/src/cli/handlers/run-generate-member.test.ts +83 -0
- package/src/cli/handlers/run-generate.test.ts +158 -0
- package/src/cli/handlers/run-generate.ts +168 -0
- package/src/cli/handlers/run.test.ts +47 -0
- package/src/cli/handlers/run.ts +41 -6
- package/src/cli/handlers/vendor.ts +147 -43
- package/src/cli/main.test.ts +137 -1
- package/src/cli/main.ts +290 -24
- package/src/cli/mcp/op-tools.ts +2 -1
- package/src/cli/path-lexicon-messages.test.ts +152 -0
- package/src/cli/plugins.ts +43 -5
- package/src/cli/registry.ts +46 -1
- package/src/cli/static-config-read.test.ts +208 -0
- package/src/codegen/fetch.test.ts +63 -1
- package/src/codegen/fetch.ts +30 -1
- package/src/components/__fixtures__/alb-ecs-service.json +0 -8
- package/src/components/capability-plugin-loader.ts +3 -1
- package/src/components/cli-support.ts +9 -3
- package/src/components/discover.test.ts +27 -57
- package/src/components/discover.ts +20 -51
- package/src/components/driver.test.ts +90 -12
- package/src/components/driver.ts +43 -5
- package/src/components/fan-out-support.ts +2 -1
- package/src/components/gate-plan.ts +53 -0
- package/src/components/pilots/README.md +1 -1
- package/src/components/pilots/alb-ecs.pilot.ts +7 -7
- package/src/components/presets/ecs-fargate.ts +10 -5
- package/src/components/promote.test.ts +645 -0
- package/src/components/promote.ts +641 -0
- package/src/components/sandbox/driver.ts +8 -8
- package/src/components/sandbox/run.ts +4 -1
- package/src/components/verbs/build-archive.ts +27 -21
- package/src/components/verbs/run-agent.ts +3 -5
- package/src/config-static.test.ts +136 -0
- package/src/config-static.ts +232 -0
- package/src/config.test.ts +40 -0
- package/src/config.ts +161 -11
- package/src/content-digest.test.ts +120 -0
- package/src/content-digest.ts +21 -0
- package/src/discovery/files.test.ts +127 -1
- package/src/discovery/files.ts +91 -59
- package/src/discovery/fold-import.ts +127 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/sandbox/driver.ts +73 -35
- package/src/discovery/sandbox/fork-diagnostic.test.ts +136 -13
- package/src/discovery/sandbox/fork.ts +83 -43
- package/src/discovery/sandbox/path-lexicon-multifile.test.ts +285 -0
- package/src/discovery/sandbox/path-lexicon-parity.test.ts +162 -0
- package/src/discovery/walk.test.ts +263 -0
- package/src/discovery/walk.ts +334 -0
- package/src/effect-receipt.ts +2 -2
- package/src/graph-ir.ts +18 -1
- package/src/join-key.test.ts +25 -0
- package/src/join-key.ts +39 -0
- package/src/lexicon-module.test.ts +203 -0
- package/src/lexicon-module.ts +215 -0
- package/src/lexicon.ts +105 -1
- package/src/lifecycle/build-ledger-store.ts +13 -2
- package/src/lifecycle/digest.ts +9 -5
- package/src/lifecycle/gate-ledger.test.ts +20 -0
- package/src/lifecycle/gate-ledger.ts +28 -6
- package/src/lifecycle/git.test.ts +13 -1
- package/src/lifecycle/git.ts +221 -176
- package/src/lifecycle/lease.ts +25 -10
- package/src/lifecycle/legacy-digest.test.ts +206 -0
- package/src/lifecycle/legacy-digest.ts +103 -0
- package/src/lifecycle/member-ledger.test.ts +319 -0
- package/src/lifecycle/member-ledger.ts +145 -0
- package/src/lifecycle/plan-digest.ts +7 -7
- package/src/lifecycle/release-ledger.test.ts +20 -0
- package/src/lifecycle/release-ledger.ts +53 -3
- package/src/lifecycle/status.test.ts +151 -1
- package/src/lifecycle/status.ts +228 -7
- package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +2 -3
- package/src/op/activities/activity-contracts.ts +37 -0
- package/src/op/activities/index.ts +10 -0
- package/src/op/activities/propose-upgrade.ts +206 -0
- package/src/op/activity-contract-registry.ts +18 -4
- package/src/op/activity-registry.ts +26 -0
- package/src/op/discover.test.ts +14 -4
- package/src/op/discover.ts +19 -23
- package/src/op/gate-approval.ts +3 -1
- package/src/op/gate-name.ts +8 -0
- package/src/op/gate-summary.test.ts +14 -0
- package/src/op/gate-summary.ts +25 -2
- package/src/op/gate.ts +36 -7
- package/src/op/generate-pipeline.test.ts +14 -5
- package/src/op/generate-pipeline.ts +2 -1
- package/src/op/runtimes/local.ts +2 -1
- package/src/project-root.test.ts +66 -1
- package/src/project-root.ts +48 -0
- package/src/workspace/__fixtures__/contract-repo.ts +100 -0
- package/src/workspace/__fixtures__/two-member-workspace.ts +35 -0
- package/src/workspace/behold-kinds.test.ts +162 -0
- package/src/workspace/check-contract.test.ts +127 -0
- package/src/workspace/check.schema.json +347 -0
- package/src/workspace/checks/generated.test.ts +287 -0
- package/src/workspace/checks/generated.ts +475 -0
- package/src/workspace/checks/ledgers.test.ts +139 -0
- package/src/workspace/checks/ledgers.ts +246 -0
- package/src/workspace/checks/links.ts +183 -0
- package/src/workspace/checks/pipelines.test.ts +133 -0
- package/src/workspace/checks/pipelines.ts +225 -0
- package/src/workspace/checks-repo.test.ts +71 -0
- package/src/workspace/checks.test.ts +450 -0
- package/src/workspace/checks.ts +461 -0
- package/src/workspace/compose-graph.test.ts +146 -0
- package/src/workspace/compose-graph.ts +228 -0
- package/src/workspace/compose-reports.test.ts +109 -0
- package/src/workspace/compose-reports.ts +186 -0
- package/src/workspace/declaration.schema.json +434 -0
- package/src/workspace/declaration.test.ts +347 -0
- package/src/workspace/declaration.ts +623 -0
- package/src/workspace/generated-files.test.ts +114 -0
- package/src/workspace/generated-files.ts +94 -0
- package/src/workspace/glob.ts +49 -0
- package/src/workspace/graph-cli.ts +220 -0
- package/src/workspace/graph-contract.test.ts +204 -0
- package/src/workspace/graph.schema.json +662 -0
- package/src/workspace/init.test.ts +223 -0
- package/src/workspace/init.ts +585 -0
- package/src/workspace/jsonc.test.ts +65 -0
- package/src/workspace/jsonc.ts +231 -0
- package/src/workspace/kinds.test.ts +288 -0
- package/src/workspace/kinds.ts +506 -0
- package/src/workspace/lineage-check.ts +264 -0
- package/src/workspace/lineage-cli.ts +114 -0
- package/src/workspace/lineage-init.test.ts +231 -0
- package/src/workspace/lineage-init.ts +365 -0
- package/src/workspace/lineage-lock.test.ts +224 -0
- package/src/workspace/lineage-lock.ts +357 -0
- package/src/workspace/lineage-merge.ts +44 -0
- package/src/workspace/lineage-migrations.test.ts +178 -0
- package/src/workspace/lineage-migrations.ts +343 -0
- package/src/workspace/lineage-update.ts +160 -0
- package/src/workspace/lineage-upgrade-cli.ts +151 -0
- package/src/workspace/lineage-upgrade.test.ts +474 -0
- package/src/workspace/lineage-upgrade.ts +624 -0
- package/src/workspace/lineage-vendor.test.ts +126 -0
- package/src/workspace/lineage-vendor.ts +215 -0
- package/src/workspace/lineage-version.ts +127 -0
- package/src/workspace/links.test.ts +344 -0
- package/src/workspace/links.ts +331 -0
- package/src/workspace/ls-contract.test.ts +200 -0
- package/src/workspace/ls.schema.json +190 -0
- package/src/workspace/ls.ts +254 -0
- package/src/workspace/member-commands.e2e.test.ts +244 -0
- package/src/workspace/member-commands.test.ts +217 -0
- package/src/workspace/member-commands.ts +540 -0
- package/src/workspace/member-handles.test.ts +76 -0
- package/src/workspace/member-handles.ts +228 -0
- package/src/workspace/member-pipeline.test.ts +135 -0
- package/src/workspace/member-pipeline.ts +317 -0
- package/src/workspace/member-run.ts +146 -0
- package/src/workspace/read-contract.test.ts +129 -0
- package/src/workspace/reason-codes.test.ts +113 -0
- package/src/workspace/reason-codes.ts +81 -0
- package/src/workspace/record-source.ts +117 -0
- package/src/workspace/records-cli.ts +198 -0
- package/src/workspace/records-contract.test.ts +93 -0
- package/src/workspace/records.schema.json +150 -0
- package/src/workspace/records.test.ts +243 -0
- package/src/workspace/records.ts +384 -0
- package/src/workspace/root-refusal.ts +97 -0
- package/src/workspace/status-contract.test.ts +308 -0
- package/src/workspace/status.schema.json +210 -0
- package/src/workspace/status.ts +412 -0
- package/src/workspace/template-manifest.test.ts +79 -0
- package/src/workspace/template-manifest.ts +221 -0
- package/src/workspace/tree.ts +132 -0
- package/src/workspace/trust/attestor.ts +93 -0
- package/src/workspace/trust/policy.ts +306 -0
- package/src/workspace/trust/provenance.ts +181 -0
- package/src/workspace/trust/ssh-commit.ts +138 -0
- package/src/workspace/trust/test-repo.ts +138 -0
- package/src/workspace/trust/trust.test.ts +397 -0
- package/src/workspace/trust/verify-cli.ts +56 -0
- package/src/workspace/trust/verify.ts +194 -0
- package/src/workspace/which-chant.test.ts +117 -0
- package/src/workspace/which-chant.ts +151 -0
- package/src/workspace/workspace-kinds.schema.json +133 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template parameters (#2627, #2524 D9): `chant.template.json` at a
|
|
3
|
+
* template's root declares the parameters `chant init --from` takes and the
|
|
4
|
+
* files they are substituted into.
|
|
5
|
+
*
|
|
6
|
+
* ```json
|
|
7
|
+
* {
|
|
8
|
+
* "parameters": {
|
|
9
|
+
* "name": { "type": "string", "default": "Reference app", "pattern": "^[A-Za-z0-9][A-Za-z0-9 ._-]*$" }
|
|
10
|
+
* },
|
|
11
|
+
* "files": ["app/src/server.mjs"]
|
|
12
|
+
* }
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* A listed file carries `{{chant:<name>}}` where the value goes. The `chant:`
|
|
16
|
+
* prefix keeps the placeholder apart from TypeScript template literals
|
|
17
|
+
* (`${x}`), GitHub and Forgejo expressions (`${{ x }}`) and Handlebars or
|
|
18
|
+
* Mustache (`{{x}}`), so a template can hold any of those in a listed file.
|
|
19
|
+
* Only listed files are substituted, the value is inserted as written, and a
|
|
20
|
+
* placeholder naming an undeclared parameter is an error in the template.
|
|
21
|
+
*
|
|
22
|
+
* The manifest is JSON and read as data: no template code runs at init or
|
|
23
|
+
* upgrade. It is template metadata, like `.chant/migrations/`, so it is not
|
|
24
|
+
* copied into the project. The values used are recorded in the lineage's
|
|
25
|
+
* `parameters`, and `chant workspace upgrade` substitutes them again into both
|
|
26
|
+
* the merge base and the target version, so an unedited file carrying a value
|
|
27
|
+
* still has its recorded hash.
|
|
28
|
+
*
|
|
29
|
+
* `hostBound` is declared and validated but not acted on yet: recording a
|
|
30
|
+
* value per host belongs to export and import (#2552). A host-bound value is
|
|
31
|
+
* recorded in `parameters` like any other until then.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import { posix } from "node:path";
|
|
35
|
+
import { z } from "zod";
|
|
36
|
+
import { LockError } from "./lineage-lock";
|
|
37
|
+
import { MIGRATIONS_DIR } from "./lineage-migrations";
|
|
38
|
+
|
|
39
|
+
/** The manifest's path, relative to the template's root (the `#<member>` directory, when one is given). */
|
|
40
|
+
export const TEMPLATE_MANIFEST = "chant.template.json";
|
|
41
|
+
|
|
42
|
+
const PARAM_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
43
|
+
|
|
44
|
+
/** `{{chant:<name>}}`. */
|
|
45
|
+
const PLACEHOLDER = /\{\{chant:([A-Za-z_][A-Za-z0-9_]*)\}\}/g;
|
|
46
|
+
|
|
47
|
+
export function placeholder(name: string): string {
|
|
48
|
+
return `{{chant:${name}}}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const ParameterSchema = z
|
|
52
|
+
.object({
|
|
53
|
+
type: z.literal("string"),
|
|
54
|
+
default: z.string().optional(),
|
|
55
|
+
/** A regular expression every value must match, checked before substitution. */
|
|
56
|
+
pattern: z.string().optional(),
|
|
57
|
+
/** The value differs per host (#2524 D9). Recorded per host once #2552 lands. */
|
|
58
|
+
hostBound: z.boolean().optional(),
|
|
59
|
+
description: z.string().optional(),
|
|
60
|
+
})
|
|
61
|
+
.strict();
|
|
62
|
+
export type TemplateParameter = z.infer<typeof ParameterSchema>;
|
|
63
|
+
|
|
64
|
+
const ManifestSchema = z
|
|
65
|
+
.object({
|
|
66
|
+
$comment: z.string().optional(),
|
|
67
|
+
parameters: z.record(z.string(), ParameterSchema),
|
|
68
|
+
files: z.array(z.string().min(1)),
|
|
69
|
+
})
|
|
70
|
+
.strict();
|
|
71
|
+
export type TemplateManifest = z.infer<typeof ManifestSchema>;
|
|
72
|
+
|
|
73
|
+
function has(obj: object, key: string): boolean {
|
|
74
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export class TemplateParameterError extends LockError {
|
|
78
|
+
override name = "TemplateParameterError";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Read the manifest from a template's files, or null when it has none.
|
|
83
|
+
* Throws on a manifest that is not valid JSON or does not match the schema.
|
|
84
|
+
*/
|
|
85
|
+
export function readManifest(files: Map<string, Buffer>): TemplateManifest | null {
|
|
86
|
+
const data = files.get(TEMPLATE_MANIFEST);
|
|
87
|
+
if (!data) return null;
|
|
88
|
+
let raw: unknown;
|
|
89
|
+
try {
|
|
90
|
+
raw = JSON.parse(data.toString("utf-8"));
|
|
91
|
+
} catch (err) {
|
|
92
|
+
throw new TemplateParameterError(`${TEMPLATE_MANIFEST} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
93
|
+
}
|
|
94
|
+
const parsed = ManifestSchema.safeParse(raw);
|
|
95
|
+
if (!parsed.success) {
|
|
96
|
+
throw new TemplateParameterError(
|
|
97
|
+
`invalid ${TEMPLATE_MANIFEST}: ${parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
const manifest = parsed.data;
|
|
101
|
+
for (const [name, p] of Object.entries(manifest.parameters)) {
|
|
102
|
+
if (!PARAM_NAME.test(name)) {
|
|
103
|
+
throw new TemplateParameterError(`invalid ${TEMPLATE_MANIFEST}: parameter name "${name}" is not a letter or _ followed by letters, digits or _`);
|
|
104
|
+
}
|
|
105
|
+
if (p.pattern !== undefined) {
|
|
106
|
+
try {
|
|
107
|
+
new RegExp(p.pattern, "u");
|
|
108
|
+
} catch {
|
|
109
|
+
throw new TemplateParameterError(`invalid ${TEMPLATE_MANIFEST}: parameters.${name}.pattern is not a regular expression`);
|
|
110
|
+
}
|
|
111
|
+
if (p.default !== undefined) checkValue(name, p, p.default, "its default");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for (const f of manifest.files) {
|
|
115
|
+
const norm = posix.normalize(f);
|
|
116
|
+
if (norm !== f || f.startsWith("/") || f.startsWith("../") || f === TEMPLATE_MANIFEST || f.startsWith(`${MIGRATIONS_DIR}/`)) {
|
|
117
|
+
throw new TemplateParameterError(`invalid ${TEMPLATE_MANIFEST}: files lists "${f}", which is not a normalised path to a template file`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return manifest;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function checkValue(name: string, p: TemplateParameter, value: string, what: string): void {
|
|
124
|
+
if (p.pattern !== undefined && !new RegExp(p.pattern, "u").test(value)) {
|
|
125
|
+
throw new TemplateParameterError(`parameter ${name}: ${what} ${JSON.stringify(value)} does not match ${p.pattern}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Parse `--param name=value` arguments (split at the first `=`). A name given
|
|
131
|
+
* twice is refused rather than silently taking the last value.
|
|
132
|
+
*/
|
|
133
|
+
export function parseParamArgs(args: readonly string[]): Record<string, string> {
|
|
134
|
+
const out: Record<string, string> = {};
|
|
135
|
+
for (const arg of args) {
|
|
136
|
+
const eq = arg.indexOf("=");
|
|
137
|
+
if (eq <= 0) throw new TemplateParameterError(`--param ${arg}: expected <name>=<value>`);
|
|
138
|
+
const name = arg.slice(0, eq);
|
|
139
|
+
if (has(out, name)) throw new TemplateParameterError(`--param ${name} is given twice`);
|
|
140
|
+
out[name] = arg.slice(eq + 1);
|
|
141
|
+
}
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function declaredList(manifest: TemplateManifest | null): string {
|
|
146
|
+
const names = manifest ? Object.keys(manifest.parameters).sort() : [];
|
|
147
|
+
return names.length > 0 ? `declared: ${names.join(", ")}` : "the template declares none";
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The value of every declared parameter: the given value, or the default.
|
|
152
|
+
* Refuses an undeclared name (listing the declared ones), a parameter with no
|
|
153
|
+
* value and no default, and a value that does not match its pattern.
|
|
154
|
+
*/
|
|
155
|
+
export function resolveParameters(manifest: TemplateManifest | null, given: Record<string, string>): Record<string, string> {
|
|
156
|
+
const declared = manifest?.parameters ?? {};
|
|
157
|
+
const unknown = Object.keys(given).filter((n) => !has(declared, n)).sort();
|
|
158
|
+
if (unknown.length > 0) {
|
|
159
|
+
throw new TemplateParameterError(`unknown parameter${unknown.length > 1 ? "s" : ""} ${unknown.join(", ")} (${declaredList(manifest)})`);
|
|
160
|
+
}
|
|
161
|
+
const values: Record<string, string> = {};
|
|
162
|
+
const missing: string[] = [];
|
|
163
|
+
for (const name of Object.keys(declared).sort()) {
|
|
164
|
+
const p = declared[name];
|
|
165
|
+
const value = has(given, name) ? given[name] : p.default;
|
|
166
|
+
if (value === undefined) {
|
|
167
|
+
missing.push(name);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
checkValue(name, p, value, has(given, name) ? "the value" : "its default");
|
|
171
|
+
values[name] = value;
|
|
172
|
+
}
|
|
173
|
+
if (missing.length > 0) {
|
|
174
|
+
throw new TemplateParameterError(
|
|
175
|
+
`parameter${missing.length > 1 ? "s" : ""} ${missing.join(", ")} ${missing.length > 1 ? "have" : "has"} no default; pass ${missing.map((n) => `--param ${n}=<value>`).join(" ")}`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
return values;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The template's files as the project gets them: the manifest removed, and
|
|
183
|
+
* each listed file with its placeholders replaced. A listed file that is
|
|
184
|
+
* missing, or a placeholder naming no declared parameter, is refused.
|
|
185
|
+
*/
|
|
186
|
+
export function substituteParameters(
|
|
187
|
+
files: Map<string, Buffer>,
|
|
188
|
+
manifest: TemplateManifest | null,
|
|
189
|
+
values: Record<string, string>,
|
|
190
|
+
): Map<string, Buffer> {
|
|
191
|
+
const out = new Map(files);
|
|
192
|
+
out.delete(TEMPLATE_MANIFEST);
|
|
193
|
+
if (!manifest) return out;
|
|
194
|
+
for (const path of manifest.files) {
|
|
195
|
+
const data = out.get(path);
|
|
196
|
+
if (!data) throw new TemplateParameterError(`${TEMPLATE_MANIFEST} lists ${path}, which is not in the template`);
|
|
197
|
+
const text = data.toString("utf-8");
|
|
198
|
+
const replaced = text.replace(PLACEHOLDER, (_m, name: string) => {
|
|
199
|
+
if (!has(values, name)) {
|
|
200
|
+
throw new TemplateParameterError(`${path} has ${placeholder(name)}, but ${TEMPLATE_MANIFEST} declares no parameter ${name}`);
|
|
201
|
+
}
|
|
202
|
+
return values[name];
|
|
203
|
+
});
|
|
204
|
+
if (replaced !== text) out.set(path, Buffer.from(replaced, "utf-8"));
|
|
205
|
+
}
|
|
206
|
+
return out;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* The values an upgrade substitutes into a version of the template: the
|
|
211
|
+
* recorded value of every parameter that version declares, and the default of
|
|
212
|
+
* one it adds. A recorded parameter the version no longer declares is dropped.
|
|
213
|
+
*/
|
|
214
|
+
export function carryParameters(manifest: TemplateManifest | null, recorded: Record<string, unknown>): Record<string, string> {
|
|
215
|
+
const declared = manifest?.parameters ?? {};
|
|
216
|
+
const given: Record<string, string> = {};
|
|
217
|
+
for (const [name, value] of Object.entries(recorded)) {
|
|
218
|
+
if (has(declared, name) && typeof value === "string") given[name] = value;
|
|
219
|
+
}
|
|
220
|
+
return resolveParameters(manifest, given);
|
|
221
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The files a workspace read looks at: the working tree, or one commit's git
|
|
3
|
+
* objects (`--at <rev>`, #2524 D15). Reading a revision needs no checkout and
|
|
4
|
+
* no network; the whole tree is listed once from the local object store.
|
|
5
|
+
*
|
|
6
|
+
* Paths are relative to the tree's root, `/`-separated, and `""` is the root.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { execFileSync } from "node:child_process";
|
|
10
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
|
|
13
|
+
export interface WorkspaceTree {
|
|
14
|
+
/** Appended to messages, such as " at 91c7547e". Empty for the working tree. */
|
|
15
|
+
label: string;
|
|
16
|
+
/** Whether `path` is a file, a directory, or missing. */
|
|
17
|
+
stat(path: string): "file" | "dir" | undefined;
|
|
18
|
+
/** The names of the entries directly inside directory `path`, or undefined when it is missing. */
|
|
19
|
+
list(path: string): { name: string; type: "file" | "dir" }[] | undefined;
|
|
20
|
+
/** The text of the file at `path`. Throws when it can't be read. */
|
|
21
|
+
read(path: string): string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Join tree-relative path parts, leaving out `""` and `"."`. */
|
|
25
|
+
export function joinPath(...parts: string[]): string {
|
|
26
|
+
return parts.filter((p) => p !== "" && p !== ".").join("/");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The working tree under `root`. */
|
|
30
|
+
export function workingTree(root: string): WorkspaceTree {
|
|
31
|
+
return {
|
|
32
|
+
label: "",
|
|
33
|
+
stat(path) {
|
|
34
|
+
try {
|
|
35
|
+
const s = statSync(join(root, path));
|
|
36
|
+
return s.isDirectory() ? "dir" : s.isFile() ? "file" : undefined;
|
|
37
|
+
} catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
list(path) {
|
|
42
|
+
let dirents;
|
|
43
|
+
try {
|
|
44
|
+
dirents = readdirSync(join(root, path), { withFileTypes: true });
|
|
45
|
+
} catch {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const out: { name: string; type: "file" | "dir" }[] = [];
|
|
49
|
+
for (const d of dirents) {
|
|
50
|
+
let type: "file" | "dir" | undefined = d.isDirectory() ? "dir" : d.isFile() ? "file" : undefined;
|
|
51
|
+
if (!type && d.isSymbolicLink()) {
|
|
52
|
+
try {
|
|
53
|
+
const s = statSync(join(root, path, d.name));
|
|
54
|
+
type = s.isDirectory() ? "dir" : s.isFile() ? "file" : undefined;
|
|
55
|
+
} catch {
|
|
56
|
+
// A dangling link is no entry.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (type) out.push({ name: d.name, type });
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
},
|
|
63
|
+
read(path) {
|
|
64
|
+
return readFileSync(join(root, path), "utf-8");
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function git(cwd: string, args: string[]): string {
|
|
70
|
+
return execFileSync("git", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], maxBuffer: 512 * 1024 * 1024 });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The top of the git repository holding `cwd`, or undefined outside one. */
|
|
74
|
+
export function gitTop(cwd: string): string | undefined {
|
|
75
|
+
try {
|
|
76
|
+
return git(cwd, ["rev-parse", "--show-toplevel"]).trim();
|
|
77
|
+
} catch {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The full commit id `rev` names in the repository at `top`, or undefined. */
|
|
83
|
+
export function resolveCommit(top: string, rev: string): string | undefined {
|
|
84
|
+
if (rev.startsWith("-")) return undefined;
|
|
85
|
+
try {
|
|
86
|
+
return git(top, ["rev-parse", "--verify", "--quiet", `${rev}^{commit}`]).trim();
|
|
87
|
+
} catch {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The tree of `commit` in the repository at `top`, rooted at `prefix` (a
|
|
94
|
+
* directory relative to `top`, `""` for the top itself).
|
|
95
|
+
*/
|
|
96
|
+
export function gitTree(top: string, commit: string, prefix = ""): WorkspaceTree {
|
|
97
|
+
const entries = new Map<string, "file" | "dir">([["", "dir"]]);
|
|
98
|
+
const children = new Map<string, { name: string; type: "file" | "dir" }[]>([["", []]]);
|
|
99
|
+
const out = git(top, ["ls-tree", "-r", "-t", "-z", "--full-tree", commit]);
|
|
100
|
+
for (const line of out.split("\0")) {
|
|
101
|
+
if (!line) continue;
|
|
102
|
+
const tab = line.indexOf("\t");
|
|
103
|
+
const type = line.slice(0, tab).split(" ")[1];
|
|
104
|
+
const full = line.slice(tab + 1);
|
|
105
|
+
let path: string;
|
|
106
|
+
if (prefix === "") path = full;
|
|
107
|
+
else if (full.startsWith(`${prefix}/`)) path = full.slice(prefix.length + 1);
|
|
108
|
+
else continue;
|
|
109
|
+
const kind = type === "tree" ? "dir" : type === "blob" ? "file" : undefined;
|
|
110
|
+
if (!kind) continue;
|
|
111
|
+
entries.set(path, kind);
|
|
112
|
+
const slash = path.lastIndexOf("/");
|
|
113
|
+
const parent = slash < 0 ? "" : path.slice(0, slash);
|
|
114
|
+
if (!children.has(parent)) children.set(parent, []);
|
|
115
|
+
children.get(parent)!.push({ name: path.slice(slash + 1), type: kind });
|
|
116
|
+
if (kind === "dir" && !children.has(path)) children.set(path, []);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
label: ` at ${commit.slice(0, 8)}`,
|
|
120
|
+
stat: (path) => entries.get(path),
|
|
121
|
+
list: (path) => (entries.get(path) === "dir" ? [...(children.get(path) ?? [])] : undefined),
|
|
122
|
+
read(path) {
|
|
123
|
+
if (entries.get(path) !== "file") throw new Error(`${path} is not a file${` at ${commit.slice(0, 8)}`}`);
|
|
124
|
+
return git(top, ["cat-file", "blob", `${commit}:${joinPath(prefix, path)}`]);
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Directory names a workspace walk never enters. */
|
|
130
|
+
export function skippedDir(name: string): boolean {
|
|
131
|
+
return name === "node_modules" || name.startsWith(".");
|
|
132
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attestors and provenance levels (#2524 D5).
|
|
3
|
+
*
|
|
4
|
+
* An attestor answers one question about a commit: does it carry an
|
|
5
|
+
* attestation that the policy read at base accepts? Concrete attestors are
|
|
6
|
+
* plugins. Core ships the contract and the first one, ssh-signed commits
|
|
7
|
+
* (./ssh-commit.ts). Plugin attestors will arrive the way kinds do, through
|
|
8
|
+
* `workspaceKinds` (#2525 rule 5), once the declaration can name them (#2534,
|
|
9
|
+
* #2535); until then {@link registerAttestor} is the entry point.
|
|
10
|
+
*
|
|
11
|
+
* What counts as proof: a signature the attestor checked against a key the
|
|
12
|
+
* base policy lists. Never a trailer, a typed-in name, an author field or an
|
|
13
|
+
* environment variable.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { UNATTESTED_APPROVER } from "../../lifecycle/gate-origin";
|
|
17
|
+
import type { TrustPolicy } from "./policy";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* How far a record's authorship is established. The list is closed and
|
|
21
|
+
* ordered from strongest to weakest; a gate may require `attested`.
|
|
22
|
+
*
|
|
23
|
+
* - `attested`: an attestor verified a signature by a signer the base policy lists.
|
|
24
|
+
* - `attested-unverifiable-here`: there is a signature, but nothing here can
|
|
25
|
+
* check it (a signature format no attestor handles, or a missing tool).
|
|
26
|
+
* - `adopted`: not attested, but inside a commit range the base policy admits.
|
|
27
|
+
* - `unattested`: none of the above. The same word, with the same meaning, as
|
|
28
|
+
* the approver a gate resolution records when its channel cannot attest to a
|
|
29
|
+
* name (#2384, {@link UNATTESTED_APPROVER}); D5 keeps that meaning.
|
|
30
|
+
*/
|
|
31
|
+
export const PROVENANCE_LEVELS = ["attested", "attested-unverifiable-here", "adopted", UNATTESTED_APPROVER] as const;
|
|
32
|
+
export type ProvenanceLevel = (typeof PROVENANCE_LEVELS)[number];
|
|
33
|
+
|
|
34
|
+
/** What one attestor says about one commit. */
|
|
35
|
+
export interface CommitAttestation {
|
|
36
|
+
level: "attested" | "attested-unverifiable-here" | "unattested";
|
|
37
|
+
/** The attestor that gave this answer. */
|
|
38
|
+
attestor: string;
|
|
39
|
+
/** The principal whose key verified, when `attested`. */
|
|
40
|
+
principal?: string;
|
|
41
|
+
/** The key's fingerprint, when known. */
|
|
42
|
+
key?: string;
|
|
43
|
+
/** Why the level is what it is, in a sentence. */
|
|
44
|
+
reason: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Read access to the repository an attestor checks. */
|
|
48
|
+
export interface AttestorContext {
|
|
49
|
+
/** The repository's top directory. */
|
|
50
|
+
repo: string;
|
|
51
|
+
/** The policy read at base. */
|
|
52
|
+
policy: TrustPolicy;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** A commit attestor. Plugins implement this. */
|
|
56
|
+
export interface CommitAttestor {
|
|
57
|
+
/** A stable name, such as `ssh-commit`. */
|
|
58
|
+
readonly name: string;
|
|
59
|
+
attestCommit(ctx: AttestorContext, commit: string): CommitAttestation;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const registered: CommitAttestor[] = [];
|
|
63
|
+
|
|
64
|
+
/** Add an attestor. Later registrations are asked after earlier ones. */
|
|
65
|
+
export function registerAttestor(attestor: CommitAttestor): void {
|
|
66
|
+
if (!registered.some((a) => a.name === attestor.name)) registered.push(attestor);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Remove every attestor registered by {@link registerAttestor}. For tests. */
|
|
70
|
+
export function resetAttestors(): void {
|
|
71
|
+
registered.length = 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Ask each attestor in turn. The first `attested` answer wins; otherwise
|
|
76
|
+
* `attested-unverifiable-here` beats `unattested`, so a signature nobody here
|
|
77
|
+
* can check is still reported as there.
|
|
78
|
+
*/
|
|
79
|
+
export function attestCommit(ctx: AttestorContext, commit: string, attestors: readonly CommitAttestor[]): CommitAttestation {
|
|
80
|
+
let best: CommitAttestation | undefined;
|
|
81
|
+
for (const a of attestors) {
|
|
82
|
+
const r = a.attestCommit(ctx, commit);
|
|
83
|
+
if (r.level === "attested") return r;
|
|
84
|
+
if (!best || (best.level === "unattested" && r.level === "attested-unverifiable-here")) best = r;
|
|
85
|
+
}
|
|
86
|
+
return best ?? { level: "unattested", attestor: "none", reason: "no attestor is registered" };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The attestors in use: the built-in ones first, then any registered. */
|
|
90
|
+
export async function activeAttestors(): Promise<CommitAttestor[]> {
|
|
91
|
+
const { sshCommitAttestor } = await import("./ssh-commit");
|
|
92
|
+
return [sshCommitAttestor, ...registered.filter((a) => a.name !== sshCommitAttestor.name)];
|
|
93
|
+
}
|