@intentius/chant 0.46.0 → 0.50.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/catalog.d.ts +13 -3
- package/dist/audit/catalog.d.ts.map +1 -1
- package/dist/audit/core.d.ts +30 -3
- package/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +9 -2
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/fetch.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +6 -0
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/report.d.ts.map +1 -1
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/audit/secrets.d.ts +95 -0
- package/dist/audit/secrets.d.ts.map +1 -0
- package/dist/audit/wrangler.d.ts +33 -0
- package/dist/audit/wrangler.d.ts.map +1 -0
- package/dist/build.d.ts +3 -3
- package/dist/build.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +7 -0
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts +23 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts +31 -0
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts +12 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +32 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -0
- package/dist/cli/handlers/scenario.d.ts +39 -0
- package/dist/cli/handlers/scenario.d.ts.map +1 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/server.d.ts +35 -2
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/tools/explain.d.ts +6 -0
- package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
- package/dist/cli/mcp/types.d.ts +29 -1
- package/dist/cli/mcp/types.d.ts.map +1 -1
- package/dist/cli/plugins.d.ts +1 -1
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +14 -2
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/reporters/stylish.d.ts +15 -1
- package/dist/cli/reporters/stylish.d.ts.map +1 -1
- package/dist/codegen/docs-rule-scanning.d.ts.map +1 -1
- package/dist/components/auto-release.d.ts +4 -0
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/capability.d.ts +17 -2
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +7 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/component.d.ts +15 -0
- package/dist/components/component.d.ts.map +1 -1
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/starter-plugin.d.ts +2 -0
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/components/verbs/ensure-secret.d.ts +50 -0
- package/dist/components/verbs/ensure-secret.d.ts.map +1 -0
- package/dist/components/verbs/index.d.ts +13 -0
- package/dist/components/verbs/index.d.ts.map +1 -1
- package/dist/components/verbs/r2-sync.d.ts +76 -0
- package/dist/components/verbs/r2-sync.d.ts.map +1 -0
- package/dist/components/verbs/run-agent.d.ts +499 -0
- package/dist/components/verbs/run-agent.d.ts.map +1 -0
- package/dist/components/verbs/sign.d.ts +30 -0
- package/dist/components/verbs/sign.d.ts.map +1 -1
- package/dist/components/verbs/wrangler.d.ts +108 -0
- package/dist/components/verbs/wrangler.d.ts.map +1 -0
- package/dist/composite.d.ts +6 -1
- package/dist/composite.d.ts.map +1 -1
- package/dist/config.d.ts +26 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/deep-observation.d.ts +14 -0
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/discovery/collect.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +15 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/fold-rank.d.ts +66 -0
- package/dist/discovery/fold-rank.d.ts.map +1 -0
- package/dist/discovery/index.d.ts +15 -0
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/param-deps.d.ts +17 -0
- package/dist/discovery/param-deps.d.ts.map +1 -0
- package/dist/effect-receipt.d.ts +177 -0
- package/dist/effect-receipt.d.ts.map +1 -0
- package/dist/fold/fold.d.ts +55 -2
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/fold/subset.d.ts +20 -0
- package/dist/fold/subset.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lexicon-schema.d.ts +2 -0
- package/dist/lexicon-schema.d.ts.map +1 -1
- package/dist/lexicon.d.ts +137 -3
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/change-set.d.ts +33 -5
- package/dist/lifecycle/change-set.d.ts.map +1 -1
- package/dist/lifecycle/converge-ledger.d.ts +90 -0
- package/dist/lifecycle/converge-ledger.d.ts.map +1 -0
- package/dist/lifecycle/deep-diff.d.ts +18 -0
- package/dist/lifecycle/deep-diff.d.ts.map +1 -1
- package/dist/lifecycle/deep-observe.d.ts +9 -1
- package/dist/lifecycle/deep-observe.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +33 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -0
- package/dist/lifecycle/git.d.ts +145 -21
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +6 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/lease.d.ts +113 -0
- package/dist/lifecycle/lease.d.ts.map +1 -0
- package/dist/lifecycle/observation-baseline.d.ts +21 -3
- package/dist/lifecycle/observation-baseline.d.ts.map +1 -1
- package/dist/lifecycle/receipt-plan.d.ts +62 -0
- package/dist/lifecycle/receipt-plan.d.ts.map +1 -0
- package/dist/lifecycle/release-ledger.d.ts +20 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/scenario-eval.d.ts +42 -0
- package/dist/lifecycle/scenario-eval.d.ts.map +1 -0
- package/dist/lifecycle/scenario.d.ts +163 -0
- package/dist/lifecycle/scenario.d.ts.map +1 -0
- package/dist/lifecycle/symptoms.d.ts +63 -0
- package/dist/lifecycle/symptoms.d.ts.map +1 -0
- package/dist/lifecycle/teardown.d.ts +6 -4
- package/dist/lifecycle/teardown.d.ts.map +1 -1
- package/dist/lifecycle/unobserved-gate.d.ts +67 -0
- package/dist/lifecycle/unobserved-gate.d.ts.map +1 -0
- package/dist/lint/knowledge-checks.d.ts +48 -0
- package/dist/lint/knowledge-checks.d.ts.map +1 -0
- package/dist/lint/output-checks.d.ts +5 -0
- package/dist/lint/output-checks.d.ts.map +1 -0
- package/dist/lint/output-docs.d.ts +94 -0
- package/dist/lint/output-docs.d.ts.map +1 -0
- package/dist/lint/pipeline-change-gate.d.ts +101 -0
- package/dist/lint/pipeline-change-gate.d.ts.map +1 -0
- package/dist/lint/post-synth.d.ts +41 -0
- package/dist/lint/post-synth.d.ts.map +1 -1
- package/dist/lint/receipt-checks.d.ts +9 -0
- package/dist/lint/receipt-checks.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.d.ts +11 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.d.ts.map +1 -0
- package/dist/lint/rules/cor022-receipt-leaf.d.ts +13 -0
- package/dist/lint/rules/cor022-receipt-leaf.d.ts.map +1 -0
- package/dist/lint/rules/cor024-receipt-secret-pointer.d.ts +3 -0
- package/dist/lint/rules/cor024-receipt-secret-pointer.d.ts.map +1 -0
- package/dist/lint/rules/evl001-non-literal-expression.d.ts.map +1 -1
- package/dist/lint/rules/index.d.ts +3 -1
- package/dist/lint/rules/index.d.ts.map +1 -1
- package/dist/lsp/lexicon-providers.d.ts +7 -0
- package/dist/lsp/lexicon-providers.d.ts.map +1 -1
- package/dist/okf-read.d.ts +78 -0
- package/dist/okf-read.d.ts.map +1 -0
- package/dist/op/activity-contract.d.ts +139 -0
- package/dist/op/activity-contract.d.ts.map +1 -0
- package/dist/op/builders.d.ts +140 -3
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/converge-rule.d.ts +161 -0
- package/dist/op/converge-rule.d.ts.map +1 -0
- package/dist/op/generate-pipeline.d.ts +39 -0
- package/dist/op/generate-pipeline.d.ts.map +1 -0
- package/dist/op/index.d.ts +18 -2
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +2 -1
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/op-verb-class.d.ts +42 -0
- package/dist/op/op-verb-class.d.ts.map +1 -0
- package/dist/op/operator.d.ts +128 -0
- package/dist/op/operator.d.ts.map +1 -0
- package/dist/op/receipt-store.d.ts +138 -0
- package/dist/op/receipt-store.d.ts.map +1 -0
- package/dist/op/step-output-ref.d.ts +187 -0
- package/dist/op/step-output-ref.d.ts.map +1 -0
- package/dist/op/types.d.ts +49 -2
- package/dist/op/types.d.ts.map +1 -1
- package/dist/provenance.d.ts +73 -3
- package/dist/provenance.d.ts.map +1 -1
- package/dist/runtime-adapter.d.ts +7 -1
- package/dist/runtime-adapter.d.ts.map +1 -1
- package/dist/secret-materialization.d.ts +138 -0
- package/dist/secret-materialization.d.ts.map +1 -0
- package/dist/secret-provenance.d.ts +218 -0
- package/dist/secret-provenance.d.ts.map +1 -0
- package/dist/serializer.d.ts +29 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/toml.d.ts +40 -5
- package/dist/toml.d.ts.map +1 -1
- package/dist/yaml.d.ts.map +1 -1
- package/package.json +4 -1
- package/src/audit/catalog.test.ts +1 -1
- package/src/audit/catalog.ts +75 -3
- package/src/audit/core.test.ts +57 -0
- package/src/audit/core.ts +0 -0
- package/src/audit/detect-bundle.test.ts +1 -1
- package/src/audit/discover.test.ts +24 -0
- package/src/audit/discover.ts +40 -4
- package/src/audit/fetch.test.ts +216 -3
- package/src/audit/fetch.ts +270 -59
- package/src/audit/report-html.ts +5 -2
- package/src/audit/report-model.ts +9 -0
- package/src/audit/report.test.ts +22 -0
- package/src/audit/report.ts +3 -2
- package/src/audit/rules-doc.ts +13 -1
- package/src/audit/secrets.test.ts +303 -0
- package/src/audit/secrets.ts +406 -0
- package/src/audit/wrangler.test.ts +230 -0
- package/src/audit/wrangler.ts +290 -0
- package/src/build.test.ts +41 -0
- package/src/build.ts +39 -6
- package/src/cli/command-group.ts +1 -1
- package/src/cli/commands/__fixtures__/audit-fountain/agents/fleet.yaml +27 -0
- package/src/cli/commands/__fixtures__/audit-fountain/k8s/deploy.yaml +16 -0
- package/src/cli/commands/__fixtures__/audit-fountain-clean/fleet.yaml +20 -0
- package/src/cli/commands/__fixtures__/schemas/sarif-2.1.0.schema.json +2882 -0
- package/src/cli/commands/audit.test.ts +268 -1
- package/src/cli/commands/audit.ts +87 -18
- package/src/cli/commands/build.test.ts +245 -0
- package/src/cli/commands/build.ts +210 -21
- package/src/cli/commands/lint.ts +15 -3
- package/src/cli/handlers/build.ts +2 -0
- package/src/cli/handlers/components.test.ts +199 -1
- package/src/cli/handlers/components.ts +160 -3
- package/src/cli/handlers/explain.test.ts +70 -1
- package/src/cli/handlers/graph.test.ts +20 -0
- package/src/cli/handlers/graph.ts +12 -3
- package/src/cli/handlers/lifecycle.test.ts +115 -1
- package/src/cli/handlers/lifecycle.ts +96 -15
- package/src/cli/handlers/operator.test.ts +255 -0
- package/src/cli/handlers/operator.ts +240 -0
- package/src/cli/handlers/scenario.test.ts +456 -0
- package/src/cli/handlers/scenario.ts +330 -0
- package/src/cli/main.test.ts +23 -0
- package/src/cli/main.ts +72 -1
- package/src/cli/mcp/resource-handlers.ts +38 -1
- package/src/cli/mcp/server.test.ts +323 -3
- package/src/cli/mcp/server.ts +84 -7
- package/src/cli/mcp/tools/explain.ts +51 -2
- package/src/cli/mcp/types.ts +27 -1
- package/src/cli/plugins.ts +4 -2
- package/src/cli/registry.ts +14 -2
- package/src/cli/reporters/stylish.test.ts +154 -0
- package/src/cli/reporters/stylish.ts +154 -33
- package/src/codegen/docs-rule-scanning.test.ts +42 -0
- package/src/codegen/docs-rule-scanning.ts +25 -2
- package/src/components/README.md +7 -0
- package/src/components/auto-release.ts +6 -0
- package/src/components/capability.ts +17 -2
- package/src/components/cli-support.test.ts +17 -0
- package/src/components/cli-support.ts +13 -1
- package/src/components/component-schema.test.ts +32 -0
- package/src/components/component.schema.json +6 -0
- package/src/components/component.test.ts +21 -0
- package/src/components/component.ts +15 -0
- package/src/components/driver.ts +12 -4
- package/src/components/registry.test.ts +7 -2
- package/src/components/starter-plugin.ts +17 -0
- package/src/components/verbs/ensure-secret.test.ts +130 -0
- package/src/components/verbs/ensure-secret.ts +79 -0
- package/src/components/verbs/index.ts +13 -0
- package/src/components/verbs/r2-sync.test.ts +107 -0
- package/src/components/verbs/r2-sync.ts +124 -0
- package/src/components/verbs/run-agent.test.ts +683 -0
- package/src/components/verbs/run-agent.ts +786 -0
- package/src/components/verbs/sign.test.ts +19 -0
- package/src/components/verbs/sign.ts +34 -2
- package/src/components/verbs/wrangler.test.ts +170 -0
- package/src/components/verbs/wrangler.ts +241 -0
- package/src/composite.ts +31 -2
- package/src/config.test.ts +15 -0
- package/src/config.ts +30 -0
- package/src/deep-observation.test.ts +19 -0
- package/src/deep-observation.ts +17 -0
- package/src/discovery/collect.ts +11 -2
- package/src/discovery/fold-import.test.ts +54 -0
- package/src/discovery/fold-import.ts +178 -38
- package/src/discovery/fold-rank.test.ts +197 -0
- package/src/discovery/fold-rank.ts +346 -0
- package/src/discovery/index.ts +16 -1
- package/src/discovery/param-deps.test.ts +118 -0
- package/src/discovery/param-deps.ts +170 -0
- package/src/effect-receipt-exclusion.test.ts +190 -0
- package/src/effect-receipt.test.ts +419 -0
- package/src/effect-receipt.ts +412 -0
- package/src/fold/fold.test.ts +6 -2
- package/src/fold/fold.ts +184 -3
- package/src/fold/subset.test.ts +95 -6
- package/src/fold/subset.ts +66 -2
- package/src/index.ts +4 -0
- package/src/lexicon-schema.ts +3 -0
- package/src/lexicon.ts +151 -5
- package/src/lifecycle/change-set.ts +46 -7
- package/src/lifecycle/converge-ledger.test.ts +199 -0
- package/src/lifecycle/converge-ledger.ts +179 -0
- package/src/lifecycle/deep-diff.test.ts +79 -1
- package/src/lifecycle/deep-diff.ts +23 -0
- package/src/lifecycle/deep-observe.ts +13 -2
- package/src/lifecycle/gate-ledger.test.ts +103 -0
- package/src/lifecycle/gate-ledger.ts +140 -0
- package/src/lifecycle/git.test.ts +430 -0
- package/src/lifecycle/git.ts +446 -84
- package/src/lifecycle/index.ts +6 -0
- package/src/lifecycle/lease.test.ts +343 -0
- package/src/lifecycle/lease.ts +270 -0
- package/src/lifecycle/observation-baseline.test.ts +46 -0
- package/src/lifecycle/observation-baseline.ts +33 -1
- package/src/lifecycle/receipt-plan.test.ts +250 -0
- package/src/lifecycle/receipt-plan.ts +249 -0
- package/src/lifecycle/release-ledger.ts +20 -0
- package/src/lifecycle/scenario-eval.test.ts +199 -0
- package/src/lifecycle/scenario-eval.ts +158 -0
- package/src/lifecycle/scenario.test.ts +195 -0
- package/src/lifecycle/scenario.ts +321 -0
- package/src/lifecycle/symptoms.test.ts +116 -0
- package/src/lifecycle/symptoms.ts +126 -0
- package/src/lifecycle/teardown.test.ts +31 -0
- package/src/lifecycle/teardown.ts +6 -4
- package/src/lifecycle/unobserved-gate.test.ts +109 -0
- package/src/lifecycle/unobserved-gate.ts +102 -0
- package/src/lint/knowledge-checks.test.ts +80 -0
- package/src/lint/knowledge-checks.ts +74 -0
- package/src/lint/output-checks.test.ts +85 -0
- package/src/lint/output-checks.ts +99 -0
- package/src/lint/output-docs.test.ts +220 -0
- package/src/lint/output-docs.ts +204 -0
- package/src/lint/pipeline-change-gate.test.ts +144 -0
- package/src/lint/pipeline-change-gate.ts +153 -0
- package/src/lint/post-synth.test.ts +97 -0
- package/src/lint/post-synth.ts +60 -0
- package/src/lint/receipt-checks.test.ts +101 -0
- package/src/lint/receipt-checks.ts +93 -0
- package/src/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.ts +26 -0
- package/src/lint/rules/comp/comp.test.ts +49 -1
- package/src/lint/rules/cor022-receipt-leaf.test.ts +116 -0
- package/src/lint/rules/cor022-receipt-leaf.ts +130 -0
- package/src/lint/rules/cor024-receipt-secret-pointer.test.ts +121 -0
- package/src/lint/rules/cor024-receipt-secret-pointer.ts +218 -0
- package/src/lint/rules/evl001-non-literal-expression.test.ts +35 -3
- package/src/lint/rules/evl001-non-literal-expression.ts +7 -0
- package/src/lint/rules/index.ts +7 -1
- package/src/lsp/lexicon-providers.test.ts +44 -0
- package/src/lsp/lexicon-providers.ts +11 -1
- package/src/okf-read.test.ts +149 -0
- package/src/okf-read.ts +197 -0
- package/src/op/activity-contract.test.ts +180 -0
- package/src/op/activity-contract.ts +278 -0
- package/src/op/builders-exports.test.ts +17 -1
- package/src/op/builders.ts +198 -6
- package/src/op/converge-rule.test.ts +179 -0
- package/src/op/converge-rule.ts +311 -0
- package/src/op/effect-step.test.ts +311 -0
- package/src/op/generate-pipeline.test.ts +53 -0
- package/src/op/generate-pipeline.ts +99 -0
- package/src/op/index.ts +40 -3
- package/src/op/local-executor.test.ts +92 -0
- package/src/op/local-executor.ts +212 -29
- package/src/op/op-verb-class.test.ts +126 -0
- package/src/op/op-verb-class.ts +115 -0
- package/src/op/op.test.ts +25 -2
- package/src/op/operator.test.ts +346 -0
- package/src/op/operator.ts +213 -0
- package/src/op/receipt-store.ts +211 -0
- package/src/op/step-output-ref.test.ts +334 -0
- package/src/op/step-output-ref.ts +453 -0
- package/src/op/types.ts +51 -2
- package/src/provenance.test.ts +151 -4
- package/src/provenance.ts +118 -4
- package/src/runtime-adapter.ts +31 -10
- package/src/secret-materialization.test.ts +199 -0
- package/src/secret-materialization.ts +235 -0
- package/src/secret-provenance.test.ts +388 -0
- package/src/secret-provenance.ts +475 -0
- package/src/serializer.ts +30 -0
- package/src/toml.test.ts +157 -384
- package/src/toml.ts +371 -5
- package/src/yaml.test.ts +88 -0
- package/src/yaml.ts +76 -6
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrangler config audit (#446) — a config format the audit engine could not
|
|
3
|
+
* parse at all before this: `wrangler.toml`, Cloudflare Workers' native
|
|
4
|
+
* declarative deploy file (routes, bindings, vars, observability). Fitting,
|
|
5
|
+
* since chant's own hosted audit path targets the same Workers runtime.
|
|
6
|
+
*
|
|
7
|
+
* Follows `secrets.ts`'s precedent for a format the issue says should ship
|
|
8
|
+
* "only a detector and a set of checks — no authoring surface, no output
|
|
9
|
+
* serializer": no lexicon plugin, no `AuditInput`/`classifyFiles` involvement,
|
|
10
|
+
* no npm package to install. `auditWranglerConfigs` scans the raw candidate
|
|
11
|
+
* files directly (the detector is `isWranglerConfigPath`) and is called
|
|
12
|
+
* alongside `scanForSecrets` from the CLI. `wrangler.toml` is also NOT the
|
|
13
|
+
* `fly` lexicon's `.fly` (this repo already has a `fly` lexicon covering the
|
|
14
|
+
* Machines/flaps REST API with its own `FLY0xx` ids) — this module owns a
|
|
15
|
+
* disjoint namespace (`WRG0xx`) and `AuditFinding.lexicon = "wrangler"`.
|
|
16
|
+
*
|
|
17
|
+
* Pure: no fs, no network, no Node-only global — Workers-safe (epic #350).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { parseTOML, TomlParseError } from "../toml";
|
|
21
|
+
import type { AuditFinding } from "./core";
|
|
22
|
+
|
|
23
|
+
/** The minimal file shape the scanner needs (matches `discover.ts`'s `RepoFile`). */
|
|
24
|
+
export interface ScannableFile {
|
|
25
|
+
path: string;
|
|
26
|
+
content: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Detector: does this path look like a Wrangler config? (native TOML only — see module doc.) */
|
|
30
|
+
export function isWranglerConfigPath(path: string): boolean {
|
|
31
|
+
const base = path.split("/").pop() ?? path;
|
|
32
|
+
return base === "wrangler.toml";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type JsonRecord = Record<string, unknown>;
|
|
36
|
+
|
|
37
|
+
function isRecord(v: unknown): v is JsonRecord {
|
|
38
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function asArray(v: unknown): unknown[] {
|
|
42
|
+
return Array.isArray(v) ? v : [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** One config scope: the top-level document, or one `[env.<name>]` block. */
|
|
46
|
+
interface Scope {
|
|
47
|
+
/** `"top-level"`, or the env name (`"production"`, `"staging"`, …). */
|
|
48
|
+
name: string;
|
|
49
|
+
/** Whether this scope is the account's default deploy target (`wrangler deploy` with no `--env`). */
|
|
50
|
+
isTopLevel: boolean;
|
|
51
|
+
config: JsonRecord;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function scopesOf(doc: JsonRecord): Scope[] {
|
|
55
|
+
const scopes: Scope[] = [{ name: "top-level", isTopLevel: true, config: doc }];
|
|
56
|
+
const env = doc.env;
|
|
57
|
+
if (isRecord(env)) {
|
|
58
|
+
for (const [name, cfg] of Object.entries(env)) {
|
|
59
|
+
if (isRecord(cfg)) scopes.push({ name, isTopLevel: false, config: cfg });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return scopes;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const PROD_NAME_RE = /^(prod|production|live)$/i;
|
|
66
|
+
const NON_PROD_NAME_RE = /^(dev|development|staging|stage|preview|test|testing|sandbox|local)$/i;
|
|
67
|
+
|
|
68
|
+
/** Best-effort 1-based line number of the first line containing `needle`, or undefined. */
|
|
69
|
+
function lineOf(content: string, needle: string): number | undefined {
|
|
70
|
+
if (!needle) return undefined;
|
|
71
|
+
const idx = content.indexOf(needle);
|
|
72
|
+
if (idx === -1) return undefined;
|
|
73
|
+
return content.slice(0, idx).split("\n").length;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ── WRG001: workers_dev in a production-named environment ──────────────────
|
|
77
|
+
|
|
78
|
+
function checkWorkersDevInProd(file: ScannableFile, doc: JsonRecord): AuditFinding[] {
|
|
79
|
+
const out: AuditFinding[] = [];
|
|
80
|
+
for (const scope of scopesOf(doc)) {
|
|
81
|
+
if (!PROD_NAME_RE.test(scope.name)) continue;
|
|
82
|
+
if (scope.config.workers_dev !== true) continue;
|
|
83
|
+
out.push({
|
|
84
|
+
checkId: "WRG001",
|
|
85
|
+
severity: "warning",
|
|
86
|
+
message: `Environment "${scope.name}" sets workers_dev = true — the Worker is reachable on the public *.workers.dev subdomain, bypassing whatever access control a custom domain/zone would provide.`,
|
|
87
|
+
file: file.path,
|
|
88
|
+
lexicon: "wrangler",
|
|
89
|
+
entity: scope.name,
|
|
90
|
+
line: lineOf(file.content, "workers_dev"),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ── WRG002: sensitive-named key stored in [vars] instead of a secret ───────
|
|
97
|
+
|
|
98
|
+
const SENSITIVE_VAR_RE = /(SECRET|TOKEN|API[_-]?KEY|PASSWORD|PASSWD|PRIVATE[_-]?KEY|ACCESS[_-]?KEY|CLIENT[_-]?SECRET|\bAUTH\b)/i;
|
|
99
|
+
|
|
100
|
+
function checkSecretShapedVars(file: ScannableFile, doc: JsonRecord): AuditFinding[] {
|
|
101
|
+
const out: AuditFinding[] = [];
|
|
102
|
+
for (const scope of scopesOf(doc)) {
|
|
103
|
+
const vars = scope.config.vars;
|
|
104
|
+
if (!isRecord(vars)) continue;
|
|
105
|
+
for (const key of Object.keys(vars)) {
|
|
106
|
+
if (!SENSITIVE_VAR_RE.test(key)) continue;
|
|
107
|
+
out.push({
|
|
108
|
+
checkId: "WRG002",
|
|
109
|
+
severity: "error",
|
|
110
|
+
message: `"${key}" in [vars] (${scope.name}) looks like a credential name. Plaintext vars are committed to source and visible via 'wrangler dev'/the dashboard — use 'wrangler secret put ${key}' instead.`,
|
|
111
|
+
file: file.path,
|
|
112
|
+
lexicon: "wrangler",
|
|
113
|
+
entity: key,
|
|
114
|
+
line: lineOf(file.content, key),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── WRG003: observability explicitly disabled ───────────────────────────────
|
|
122
|
+
|
|
123
|
+
function checkObservabilityDisabled(file: ScannableFile, doc: JsonRecord): AuditFinding[] {
|
|
124
|
+
const out: AuditFinding[] = [];
|
|
125
|
+
for (const scope of scopesOf(doc)) {
|
|
126
|
+
const obs = scope.config.observability;
|
|
127
|
+
if (!isRecord(obs)) continue;
|
|
128
|
+
if (obs.enabled !== false) continue;
|
|
129
|
+
out.push({
|
|
130
|
+
checkId: "WRG003",
|
|
131
|
+
severity: "info",
|
|
132
|
+
message: `Environment "${scope.name}" explicitly disables observability (observability.enabled = false) — no Workers Logs are recorded for this deployment, which will slow down or block incident investigation.`,
|
|
133
|
+
file: file.path,
|
|
134
|
+
lexicon: "wrangler",
|
|
135
|
+
entity: scope.name,
|
|
136
|
+
line: lineOf(file.content, "observability"),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ── WRG004: an account-wide wildcard route (unrestricted ingress) ──────────
|
|
143
|
+
|
|
144
|
+
function routePatterns(config: JsonRecord): string[] {
|
|
145
|
+
const patterns: string[] = [];
|
|
146
|
+
for (const r of asArray(config.routes)) {
|
|
147
|
+
if (typeof r === "string") patterns.push(r);
|
|
148
|
+
else if (isRecord(r) && typeof r.pattern === "string") patterns.push(r.pattern);
|
|
149
|
+
}
|
|
150
|
+
if (typeof config.route === "string") patterns.push(config.route);
|
|
151
|
+
return patterns;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** A route pattern with no host scoping at all — matches every zone in the account, not just one. */
|
|
155
|
+
function isAccountWildcard(pattern: string): boolean {
|
|
156
|
+
const p = pattern.trim();
|
|
157
|
+
return p === "*" || p === "*/*";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function checkWildcardRoute(file: ScannableFile, doc: JsonRecord): AuditFinding[] {
|
|
161
|
+
const out: AuditFinding[] = [];
|
|
162
|
+
for (const scope of scopesOf(doc)) {
|
|
163
|
+
for (const pattern of routePatterns(scope.config)) {
|
|
164
|
+
if (!isAccountWildcard(pattern)) continue;
|
|
165
|
+
out.push({
|
|
166
|
+
checkId: "WRG004",
|
|
167
|
+
severity: "error",
|
|
168
|
+
message: `Environment "${scope.name}" declares route "${pattern}" — an unscoped wildcard that matches every zone/path on the account, not one domain. Scope it to a specific zone (e.g. "example.com/*").`,
|
|
169
|
+
file: file.path,
|
|
170
|
+
lexicon: "wrangler",
|
|
171
|
+
entity: scope.name,
|
|
172
|
+
line: lineOf(file.content, pattern),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ── WRG005: a non-prod environment shares a data-store id with production ──
|
|
180
|
+
|
|
181
|
+
interface BindingSpec {
|
|
182
|
+
key: string;
|
|
183
|
+
idField: string;
|
|
184
|
+
}
|
|
185
|
+
const BINDING_KINDS: BindingSpec[] = [
|
|
186
|
+
{ key: "kv_namespaces", idField: "id" },
|
|
187
|
+
{ key: "r2_buckets", idField: "bucket_name" },
|
|
188
|
+
{ key: "d1_databases", idField: "database_id" },
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
function checkCrossEnvSharedResource(file: ScannableFile, doc: JsonRecord): AuditFinding[] {
|
|
192
|
+
const out: AuditFinding[] = [];
|
|
193
|
+
const scopes = scopesOf(doc);
|
|
194
|
+
// The top-level scope is the default deploy target (`wrangler deploy` with
|
|
195
|
+
// no `--env`) — treated as "production" only when the file also declares at
|
|
196
|
+
// least one dev/staging-like env, so a single-environment file (top-level
|
|
197
|
+
// only) never trips this on itself.
|
|
198
|
+
const hasNonProdEnv = scopes.some((s) => !s.isTopLevel && NON_PROD_NAME_RE.test(s.name));
|
|
199
|
+
const prodScopeNames = new Set(
|
|
200
|
+
scopes.filter((s) => (s.isTopLevel && hasNonProdEnv) || PROD_NAME_RE.test(s.name)).map((s) => s.name),
|
|
201
|
+
);
|
|
202
|
+
const nonProdScopes = scopes.filter((s) => NON_PROD_NAME_RE.test(s.name));
|
|
203
|
+
if (prodScopeNames.size === 0 || nonProdScopes.length === 0) return out;
|
|
204
|
+
|
|
205
|
+
for (const { key, idField } of BINDING_KINDS) {
|
|
206
|
+
// id -> set of scope names that declare a binding with that id
|
|
207
|
+
const byId = new Map<string, Set<string>>();
|
|
208
|
+
for (const scope of scopes) {
|
|
209
|
+
for (const binding of asArray(scope.config[key])) {
|
|
210
|
+
if (!isRecord(binding)) continue;
|
|
211
|
+
const id = binding[idField];
|
|
212
|
+
if (typeof id !== "string" || id === "") continue;
|
|
213
|
+
const owners = byId.get(id) ?? new Set<string>();
|
|
214
|
+
owners.add(scope.name);
|
|
215
|
+
byId.set(id, owners);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
for (const [id, owners] of byId) {
|
|
219
|
+
const prodOwner = [...owners].find((n) => prodScopeNames.has(n));
|
|
220
|
+
const nonProdOwner = [...owners].find((n) => NON_PROD_NAME_RE.test(n));
|
|
221
|
+
if (!prodOwner || !nonProdOwner) continue;
|
|
222
|
+
out.push({
|
|
223
|
+
checkId: "WRG005",
|
|
224
|
+
severity: "error",
|
|
225
|
+
message: `"${nonProdOwner}" and "${prodOwner}" both bind ${key} ${idField}="${id}" — the non-production environment has full read/write access to the production data store instead of its own copy.`,
|
|
226
|
+
file: file.path,
|
|
227
|
+
lexicon: "wrangler",
|
|
228
|
+
entity: `${key}:${id}`,
|
|
229
|
+
line: lineOf(file.content, id),
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return out;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ── WRG006: static assets/site served from the project root ────────────────
|
|
237
|
+
|
|
238
|
+
const ROOT_DIR_RE = /^\.?\/?$/;
|
|
239
|
+
|
|
240
|
+
function checkAssetsServeRoot(file: ScannableFile, doc: JsonRecord): AuditFinding[] {
|
|
241
|
+
const out: AuditFinding[] = [];
|
|
242
|
+
for (const scope of scopesOf(doc)) {
|
|
243
|
+
const candidates: Array<{ table: string; field: string; value: unknown }> = [];
|
|
244
|
+
if (isRecord(scope.config.site)) candidates.push({ table: "site", field: "bucket", value: scope.config.site.bucket });
|
|
245
|
+
if (isRecord(scope.config.assets)) candidates.push({ table: "assets", field: "directory", value: scope.config.assets.directory });
|
|
246
|
+
for (const c of candidates) {
|
|
247
|
+
if (typeof c.value !== "string" || !ROOT_DIR_RE.test(c.value)) continue;
|
|
248
|
+
out.push({
|
|
249
|
+
checkId: "WRG006",
|
|
250
|
+
severity: "warning",
|
|
251
|
+
message: `Environment "${scope.name}" sets [${c.table}].${c.field} = "${c.value}" — the project root is served as public static assets, including any files not meant to be public (config, source maps, .git).`,
|
|
252
|
+
file: file.path,
|
|
253
|
+
lexicon: "wrangler",
|
|
254
|
+
entity: scope.name,
|
|
255
|
+
line: lineOf(file.content, c.field),
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return out;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const CHECKS: Array<(file: ScannableFile, doc: JsonRecord) => AuditFinding[]> = [
|
|
263
|
+
checkWorkersDevInProd,
|
|
264
|
+
checkSecretShapedVars,
|
|
265
|
+
checkObservabilityDisabled,
|
|
266
|
+
checkWildcardRoute,
|
|
267
|
+
checkCrossEnvSharedResource,
|
|
268
|
+
checkAssetsServeRoot,
|
|
269
|
+
];
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Audit every `wrangler.toml` in `files`. Pure with respect to the filesystem
|
|
273
|
+
* and network. A file that fails to parse contributes no findings — the audit
|
|
274
|
+
* contract is "runs against any repo," never a crash on odd/partial config.
|
|
275
|
+
*/
|
|
276
|
+
export function auditWranglerConfigs(files: ScannableFile[]): AuditFinding[] {
|
|
277
|
+
const findings: AuditFinding[] = [];
|
|
278
|
+
for (const file of files) {
|
|
279
|
+
if (!isWranglerConfigPath(file.path)) continue;
|
|
280
|
+
let doc: JsonRecord;
|
|
281
|
+
try {
|
|
282
|
+
doc = parseTOML(file.content);
|
|
283
|
+
} catch (err) {
|
|
284
|
+
if (err instanceof TomlParseError) continue;
|
|
285
|
+
throw err;
|
|
286
|
+
}
|
|
287
|
+
for (const check of CHECKS) findings.push(...check(file, doc));
|
|
288
|
+
}
|
|
289
|
+
return findings;
|
|
290
|
+
}
|
package/src/build.test.ts
CHANGED
|
@@ -338,6 +338,47 @@ export const discovered = {
|
|
|
338
338
|
expect(result.entities.get("discovered")?.entityType).toBe("Discovered");
|
|
339
339
|
});
|
|
340
340
|
|
|
341
|
+
test("a contributor sees the discovered entities, read-only", async () => {
|
|
342
|
+
await writeFile(
|
|
343
|
+
join(testDir, "app.infra.ts"),
|
|
344
|
+
`
|
|
345
|
+
export const discovered = {
|
|
346
|
+
lexicon: "test",
|
|
347
|
+
entityType: "Discovered",
|
|
348
|
+
props: {},
|
|
349
|
+
[Symbol.for("chant.declarable")]: true,
|
|
350
|
+
};
|
|
351
|
+
`,
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
let seenNames: string[] = [];
|
|
355
|
+
const result = await build(testDir, [recordingSerializer([])], undefined, {
|
|
356
|
+
buildRoots: [
|
|
357
|
+
async (ctx) => {
|
|
358
|
+
seenNames = [...(ctx.entities?.keys() ?? [])];
|
|
359
|
+
return { entities: new Map() };
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
expect(result.errors).toEqual([]);
|
|
365
|
+
expect(seenNames).toContain("discovered");
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
test("a later contributor sees what an earlier one contributed", async () => {
|
|
369
|
+
const seen: string[][] = [];
|
|
370
|
+
await build(testDir, [recordingSerializer([])], undefined, {
|
|
371
|
+
buildRoots: [
|
|
372
|
+
async () => ({ entities: new Map([["first", entity("First")]]) }),
|
|
373
|
+
async (ctx) => {
|
|
374
|
+
seen.push([...(ctx.entities?.keys() ?? [])]);
|
|
375
|
+
return { entities: new Map() };
|
|
376
|
+
},
|
|
377
|
+
],
|
|
378
|
+
});
|
|
379
|
+
expect(seen[0]).toContain("first");
|
|
380
|
+
});
|
|
381
|
+
|
|
341
382
|
test("contributors run exactly once per build", async () => {
|
|
342
383
|
let calls = 0;
|
|
343
384
|
await build(testDir, [], undefined, {
|
package/src/build.ts
CHANGED
|
@@ -2,10 +2,13 @@ import type { Declarable } from "./declarable";
|
|
|
2
2
|
import type { Serializer, SerializerResult } from "./serializer";
|
|
3
3
|
import type { OwnershipMarker } from "./ownership";
|
|
4
4
|
import type { BuildError, DiscoveryErrorType } from "./errors";
|
|
5
|
-
import type { IntrinsicDef, BuildRootContribution } from "./lexicon";
|
|
5
|
+
import type { IntrinsicDef, BuildRootContribution, BuildRootContributor } from "./lexicon";
|
|
6
6
|
import type { BuildParamProvenance } from "./provenance";
|
|
7
7
|
import { DiscoveryError, BuildError as BuildErrorClass } from "./errors";
|
|
8
8
|
import { LexiconOutput, isLexiconOutput } from "./lexicon-output";
|
|
9
|
+
import { isSecretDeclaration } from "./secret-provenance";
|
|
10
|
+
import { splitReceiptEntities } from "./effect-receipt";
|
|
11
|
+
import { isScenario } from "./lifecycle/scenario";
|
|
9
12
|
import { AttrRef } from "./attrref";
|
|
10
13
|
import { isAttrRefLike } from "./utils";
|
|
11
14
|
import { isChildProject, type ChildProjectInstance } from "./child-project";
|
|
@@ -112,7 +115,10 @@ export function computeStackGraph(
|
|
|
112
115
|
// Dependency map: node → producers it depends on.
|
|
113
116
|
const nodes = lexiconNames.length
|
|
114
117
|
? [...lexiconNames]
|
|
115
|
-
:
|
|
118
|
+
: // Secret provenance declarations (#1828) and plan scenarios (#1292) never
|
|
119
|
+
// form a stack — their pseudo-lexicon has no serializer and must not
|
|
120
|
+
// appear in the manifest.
|
|
121
|
+
[...new Set([...entities.values()].filter((e) => !isSecretDeclaration(e) && !isScenario(e)).map((e) => e.lexicon))];
|
|
116
122
|
const deps = new Map<string, Set<string>>();
|
|
117
123
|
for (const n of nodes) deps.set(n, new Set());
|
|
118
124
|
for (const { from, to } of edges) {
|
|
@@ -229,7 +235,7 @@ export interface BuildOptions {
|
|
|
229
235
|
* binaries); a contributed name colliding with a discovered entity is a
|
|
230
236
|
* build error, never a silent overwrite.
|
|
231
237
|
*/
|
|
232
|
-
buildRoots?:
|
|
238
|
+
buildRoots?: BuildRootContributor[];
|
|
233
239
|
}
|
|
234
240
|
|
|
235
241
|
export interface BuildResult {
|
|
@@ -294,6 +300,14 @@ export function partitionByLexicon(
|
|
|
294
300
|
for (const [name, entity] of entities) {
|
|
295
301
|
// LexiconOutput instances are collected separately; skip them here
|
|
296
302
|
if (isLexiconOutput(entity)) continue;
|
|
303
|
+
// Secret provenance declarations (#1828) are serializer-neutral: data
|
|
304
|
+
// that lint and lexicons read from the entity map, never output. Keeping
|
|
305
|
+
// them out of every partition means no serializer sees them and no
|
|
306
|
+
// "No serializer found" warning fires for their pseudo-lexicon.
|
|
307
|
+
if (isSecretDeclaration(entity)) continue;
|
|
308
|
+
// Plan scenarios (#1292) are serializer-neutral the same way: a checkable
|
|
309
|
+
// expectation the CLI reads off the entity map, never output.
|
|
310
|
+
if (isScenario(entity)) continue;
|
|
297
311
|
const lexicon = entity.lexicon;
|
|
298
312
|
if (!partitions.has(lexicon)) {
|
|
299
313
|
partitions.set(lexicon, new Map());
|
|
@@ -590,13 +604,18 @@ export interface BuildRootMergeResult {
|
|
|
590
604
|
*/
|
|
591
605
|
export async function mergeBuildRootEntities(
|
|
592
606
|
entities: Map<string, Declarable>,
|
|
593
|
-
contributors: ReadonlyArray<
|
|
607
|
+
contributors: ReadonlyArray<BuildRootContributor>,
|
|
594
608
|
): Promise<BuildRootMergeResult> {
|
|
595
609
|
const warnings: string[] = [];
|
|
596
610
|
const errors: string[] = [];
|
|
597
611
|
for (const contribute of contributors) {
|
|
598
612
|
try {
|
|
599
|
-
|
|
613
|
+
// The discovered set, read-only, so a contributor can react to what the
|
|
614
|
+
// project DECLARED (a committed-encrypted `declareSecret()` naming a
|
|
615
|
+
// ciphertext file to resolve). Contributors run in order, so this also
|
|
616
|
+
// carries what earlier contributors added; the merge below is still the
|
|
617
|
+
// only writer, which is what keeps the collision refusal meaningful.
|
|
618
|
+
const contribution = await contribute({ entities });
|
|
600
619
|
warnings.push(...(contribution.warnings ?? []));
|
|
601
620
|
for (const [name, entity] of contribution.entities) {
|
|
602
621
|
if (entities.has(name)) {
|
|
@@ -785,20 +804,34 @@ async function buildFromDiscoveryResult(
|
|
|
785
804
|
const outputs = new Map<string, string | SerializerResult>();
|
|
786
805
|
for (const [lexiconName, lexiconEntities] of partitions) {
|
|
787
806
|
const serializer = serializersByName.get(lexiconName);
|
|
807
|
+
// #1832: effect receipts are withheld from the apply-bound entity set at
|
|
808
|
+
// this seam — the narrowest choke point every applier's input flows
|
|
809
|
+
// through, since appliers consume the serialized outputs assembled here.
|
|
810
|
+
// Receipts ride `SerializeContext.receipts` instead, so a lexicon can
|
|
811
|
+
// render them for visibility (#1835) without them ever entering the
|
|
812
|
+
// document an applier writes (or prunes) from. The `effect()` step is the
|
|
813
|
+
// sole receipt writer (epic #1703, decision 3).
|
|
814
|
+
const { applyBound, receipts } = splitReceiptEntities(lexiconEntities);
|
|
788
815
|
if (serializer) {
|
|
789
816
|
const lexiconLexiconOutputs = [
|
|
790
817
|
...(outputsByLexicon.get(lexiconName) ?? []),
|
|
791
818
|
...unassignedOutputs,
|
|
792
819
|
];
|
|
793
|
-
const serialized = serializer.serialize(
|
|
820
|
+
const serialized = serializer.serialize(applyBound, lexiconLexiconOutputs, {
|
|
794
821
|
ownership: options?.ownership,
|
|
795
822
|
config: options?.config,
|
|
823
|
+
...(receipts.size > 0 ? { receipts } : {}),
|
|
796
824
|
});
|
|
797
825
|
// Collect any non-fatal serializer diagnostics into the build warnings.
|
|
798
826
|
if (typeof serialized !== "string" && serialized.warnings) {
|
|
799
827
|
for (const w of serialized.warnings) warnings.push(w);
|
|
800
828
|
}
|
|
801
829
|
outputs.set(lexiconName, serialized);
|
|
830
|
+
} else if (applyBound.size === 0 && receipts.size > 0) {
|
|
831
|
+
// A partition holding only receipts (the core factory's "chant"
|
|
832
|
+
// pseudo-lexicon) has nothing apply-bound to serialize; that is the
|
|
833
|
+
// designed shape until a lexicon materializes the receipt (#1835), not
|
|
834
|
+
// a missing-serializer problem.
|
|
802
835
|
} else {
|
|
803
836
|
warnings.push(`No serializer found for lexicon "${lexiconName}"`);
|
|
804
837
|
}
|
package/src/cli/command-group.ts
CHANGED
|
@@ -71,7 +71,7 @@ export interface CommandGroup {
|
|
|
71
71
|
export const RESERVED_COMMAND_NAMES: ReadonlySet<string> = new Set([
|
|
72
72
|
"build", "lint", "list", "describe", "import", "audit", "migrate", "carve",
|
|
73
73
|
"init", "update", "doctor", "dev", "run", "graph", "vendor", "lifecycle",
|
|
74
|
-
"lc", "components", "emulator", "serve",
|
|
74
|
+
"lc", "components", "emulator", "serve", "operator", "approve",
|
|
75
75
|
]);
|
|
76
76
|
|
|
77
77
|
/**
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
apiVersion: fountain.dev/v1
|
|
2
|
+
kind: Environment
|
|
3
|
+
metadata:
|
|
4
|
+
name: dev
|
|
5
|
+
spec:
|
|
6
|
+
networking_type: unrestricted
|
|
7
|
+
env_vars:
|
|
8
|
+
API_URL: https://api.example.com
|
|
9
|
+
AWS_SECRET_ACCESS_KEY: not-a-real-secret
|
|
10
|
+
---
|
|
11
|
+
apiVersion: fountain.dev/v1
|
|
12
|
+
kind: Vault
|
|
13
|
+
metadata:
|
|
14
|
+
name: staging
|
|
15
|
+
spec:
|
|
16
|
+
secrets:
|
|
17
|
+
- key: API_URL
|
|
18
|
+
value: https://staging.example.com
|
|
19
|
+
---
|
|
20
|
+
apiVersion: fountain.dev/v1
|
|
21
|
+
kind: Agent
|
|
22
|
+
metadata:
|
|
23
|
+
name: researcher
|
|
24
|
+
spec:
|
|
25
|
+
model: anthropic/claude-sonnet-4-6
|
|
26
|
+
runtime: claude
|
|
27
|
+
environment: dev
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
apiVersion: fountain.dev/v1
|
|
2
|
+
kind: Environment
|
|
3
|
+
metadata:
|
|
4
|
+
name: dev
|
|
5
|
+
spec:
|
|
6
|
+
networking_type: limited
|
|
7
|
+
networking_config:
|
|
8
|
+
allowed_hosts:
|
|
9
|
+
- github.com
|
|
10
|
+
env_vars:
|
|
11
|
+
API_URL: https://api.example.com
|
|
12
|
+
---
|
|
13
|
+
apiVersion: fountain.dev/v1
|
|
14
|
+
kind: Agent
|
|
15
|
+
metadata:
|
|
16
|
+
name: researcher
|
|
17
|
+
spec:
|
|
18
|
+
model: anthropic/claude-sonnet-4-6
|
|
19
|
+
runtime: claude
|
|
20
|
+
environment: dev
|