@intentius/chant 0.124.23 → 0.124.25

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.
Files changed (210) hide show
  1. package/dist/ci-jobs.d.ts +41 -1
  2. package/dist/ci-jobs.d.ts.map +1 -1
  3. package/dist/ci-jobs.js +48 -1
  4. package/dist/cli/commands/build.d.ts.map +1 -1
  5. package/dist/cli/commands/build.js +14 -0
  6. package/dist/cli/handlers/approve.d.ts.map +1 -1
  7. package/dist/cli/handlers/approve.js +9 -0
  8. package/dist/cli/handlers/emulator.d.ts +13 -2
  9. package/dist/cli/handlers/emulator.d.ts.map +1 -1
  10. package/dist/cli/handlers/emulator.js +63 -3
  11. package/dist/cli/handlers/promote.d.ts.map +1 -1
  12. package/dist/cli/handlers/promote.js +7 -1
  13. package/dist/cli/handlers/run-wave.d.ts.map +1 -1
  14. package/dist/cli/handlers/run-wave.js +5 -4
  15. package/dist/cli/handlers/run.d.ts +4 -3
  16. package/dist/cli/handlers/run.d.ts.map +1 -1
  17. package/dist/cli/handlers/run.js +73 -9
  18. package/dist/cli/main.d.ts.map +1 -1
  19. package/dist/cli/main.js +38 -11
  20. package/dist/cli/mcp/workspace-tools.d.ts +3 -3
  21. package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
  22. package/dist/cli/mcp/workspace-tools.js +27 -4
  23. package/dist/cli/parse-args.d.ts.map +1 -1
  24. package/dist/cli/parse-args.js +31 -0
  25. package/dist/cli/registry.d.ts +12 -0
  26. package/dist/cli/registry.d.ts.map +1 -1
  27. package/dist/components/cli-support.d.ts +45 -6
  28. package/dist/components/cli-support.d.ts.map +1 -1
  29. package/dist/components/cli-support.js +97 -22
  30. package/dist/components/driver.d.ts.map +1 -1
  31. package/dist/components/driver.js +25 -10
  32. package/dist/effect-receipt.d.ts +13 -0
  33. package/dist/effect-receipt.d.ts.map +1 -1
  34. package/dist/effect-receipt.js +19 -0
  35. package/dist/graph-detail.d.ts.map +1 -1
  36. package/dist/graph-detail.js +24 -2
  37. package/dist/lexicon-module.d.ts +7 -1
  38. package/dist/lexicon-module.d.ts.map +1 -1
  39. package/dist/lexicon-module.js +7 -2
  40. package/dist/lifecycle/git.d.ts +11 -0
  41. package/dist/lifecycle/git.d.ts.map +1 -1
  42. package/dist/lifecycle/git.js +62 -6
  43. package/dist/lifecycle/no-record.d.ts +59 -0
  44. package/dist/lifecycle/no-record.d.ts.map +1 -0
  45. package/dist/lifecycle/no-record.js +88 -0
  46. package/dist/lifecycle/receipt-plan.d.ts.map +1 -1
  47. package/dist/lifecycle/receipt-plan.js +4 -2
  48. package/dist/op/activities/shell.d.ts.map +1 -1
  49. package/dist/op/activities/shell.js +30 -9
  50. package/dist/op/emulator-lifecycle.d.ts +23 -0
  51. package/dist/op/emulator-lifecycle.d.ts.map +1 -1
  52. package/dist/op/emulator-lifecycle.js +39 -2
  53. package/dist/op/gate.d.ts.map +1 -1
  54. package/dist/op/gate.js +3 -0
  55. package/dist/op/index.d.ts +1 -1
  56. package/dist/op/index.d.ts.map +1 -1
  57. package/dist/op/index.js +1 -1
  58. package/dist/op/local-executor.d.ts +10 -0
  59. package/dist/op/local-executor.d.ts.map +1 -1
  60. package/dist/op/local-executor.js +71 -7
  61. package/dist/op/op-waves-notify.d.ts +2 -0
  62. package/dist/op/op-waves-notify.d.ts.map +1 -1
  63. package/dist/op/op-waves-notify.js +9 -3
  64. package/dist/op/op-waves-run.d.ts +8 -2
  65. package/dist/op/op-waves-run.d.ts.map +1 -1
  66. package/dist/op/op-waves-run.js +36 -11
  67. package/dist/op/op-waves.d.ts +85 -1
  68. package/dist/op/op-waves.d.ts.map +1 -1
  69. package/dist/op/op-waves.js +153 -1
  70. package/dist/op/receipt-store.d.ts +5 -0
  71. package/dist/op/receipt-store.d.ts.map +1 -1
  72. package/dist/op/receipt-store.js +4 -3
  73. package/dist/op/runtimes/local.d.ts.map +1 -1
  74. package/dist/op/runtimes/local.js +6 -0
  75. package/dist/op/step-console.d.ts +65 -0
  76. package/dist/op/step-console.d.ts.map +1 -0
  77. package/dist/op/step-console.js +140 -0
  78. package/dist/op/step-output-ref.d.ts +26 -18
  79. package/dist/op/step-output-ref.d.ts.map +1 -1
  80. package/dist/op/step-output-ref.js +86 -63
  81. package/dist/point-decision.d.ts.map +1 -1
  82. package/dist/point-decision.js +3 -0
  83. package/dist/wave-runtime.mjs +239 -239
  84. package/dist/workspace/agent-cli.d.ts.map +1 -1
  85. package/dist/workspace/agent-cli.js +3 -0
  86. package/dist/workspace/compose-graph.d.ts +22 -3
  87. package/dist/workspace/compose-graph.d.ts.map +1 -1
  88. package/dist/workspace/compose-graph.js +18 -5
  89. package/dist/workspace/conformance/__writer_fixture__/answers/answer.schema.json +16 -1
  90. package/dist/workspace/conformance/writer.js +1 -1
  91. package/dist/workspace/decide.d.ts +51 -9
  92. package/dist/workspace/decide.d.ts.map +1 -1
  93. package/dist/workspace/decide.js +125 -14
  94. package/dist/workspace/graph.schema.json +68 -1
  95. package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
  96. package/dist/workspace/lineage-upgrade.js +20 -11
  97. package/dist/workspace/nested-graph.d.ts +1 -1
  98. package/dist/workspace/nested-graph.d.ts.map +1 -1
  99. package/dist/workspace/nested-graph.js +5 -1
  100. package/dist/workspace/point-answer.schema.json +16 -1
  101. package/dist/workspace/points-cli.d.ts +5 -3
  102. package/dist/workspace/points-cli.d.ts.map +1 -1
  103. package/dist/workspace/points-cli.js +16 -7
  104. package/dist/workspace/points-write.schema.json +39 -4
  105. package/dist/workspace/points.schema.json +42 -3
  106. package/dist/workspace/reason-codes.d.ts +2 -1
  107. package/dist/workspace/reason-codes.d.ts.map +1 -1
  108. package/dist/workspace/reason-codes.js +2 -1
  109. package/dist/workspace/records-cli.d.ts +12 -2
  110. package/dist/workspace/records-cli.d.ts.map +1 -1
  111. package/dist/workspace/records-cli.js +27 -7
  112. package/dist/workspace/records-write.d.ts.map +1 -1
  113. package/dist/workspace/records-write.js +2 -3
  114. package/dist/workspace/records.d.ts +10 -4
  115. package/dist/workspace/records.d.ts.map +1 -1
  116. package/dist/workspace/records.js +16 -3
  117. package/dist/workspace/records.schema.json +2 -1
  118. package/dist/workspace/runs.d.ts.map +1 -1
  119. package/dist/workspace/runs.js +4 -2
  120. package/dist/workspace/trust/verify-cli.d.ts.map +1 -1
  121. package/dist/workspace/trust/verify-cli.js +2 -1
  122. package/dist/workspace/trust/verify.d.ts +22 -0
  123. package/dist/workspace/trust/verify.d.ts.map +1 -1
  124. package/dist/workspace/trust/verify.js +94 -14
  125. package/dist/workspace/write-scope.d.ts +3 -1
  126. package/dist/workspace/write-scope.d.ts.map +1 -1
  127. package/dist/workspace/write-scope.js +11 -4
  128. package/package.json +1 -1
  129. package/src/ci-jobs.ts +58 -1
  130. package/src/cli/commands/build.ts +18 -0
  131. package/src/cli/compiled-cli.e2e.test.ts +45 -1
  132. package/src/cli/handlers/approve.ts +9 -0
  133. package/src/cli/handlers/emulator.test.ts +61 -1
  134. package/src/cli/handlers/emulator.ts +65 -4
  135. package/src/cli/handlers/promote.test.ts +7 -0
  136. package/src/cli/handlers/promote.ts +7 -1
  137. package/src/cli/handlers/run-no-record.e2e.test.ts +87 -0
  138. package/src/cli/handlers/run-wave.ts +5 -4
  139. package/src/cli/handlers/run.test.ts +126 -0
  140. package/src/cli/handlers/run.ts +69 -9
  141. package/src/cli/main.test.ts +34 -0
  142. package/src/cli/main.ts +38 -11
  143. package/src/cli/mcp/workspace-tools.e2e.test.ts +1 -1
  144. package/src/cli/mcp/workspace-tools.ts +27 -4
  145. package/src/cli/parse-args.ts +21 -0
  146. package/src/cli/registry.ts +12 -0
  147. package/src/components/cli-support.test.ts +75 -0
  148. package/src/components/cli-support.ts +130 -25
  149. package/src/components/driver.test.ts +28 -0
  150. package/src/components/driver.ts +23 -10
  151. package/src/components/generate-disabled.test.ts +56 -0
  152. package/src/effect-receipt.ts +22 -0
  153. package/src/graph-ci-edges.test.ts +50 -0
  154. package/src/graph-detail.ts +21 -2
  155. package/src/lexicon-module.ts +9 -2
  156. package/src/lifecycle/git.ts +61 -6
  157. package/src/lifecycle/no-record.test.ts +175 -0
  158. package/src/lifecycle/no-record.ts +108 -0
  159. package/src/lifecycle/receipt-plan.test.ts +9 -0
  160. package/src/lifecycle/receipt-plan.ts +4 -1
  161. package/src/op/activities/shell.ts +28 -6
  162. package/src/op/effect-step.test.ts +95 -1
  163. package/src/op/emulator-lifecycle.test.ts +24 -1
  164. package/src/op/emulator-lifecycle.ts +58 -2
  165. package/src/op/gate.ts +3 -0
  166. package/src/op/index.ts +1 -1
  167. package/src/op/local-executor.test.ts +65 -0
  168. package/src/op/local-executor.ts +88 -8
  169. package/src/op/op-waves-jobs.test.ts +213 -0
  170. package/src/op/op-waves-notify.ts +11 -5
  171. package/src/op/op-waves-run.ts +42 -11
  172. package/src/op/op-waves.ts +202 -2
  173. package/src/op/receipt-store.ts +13 -3
  174. package/src/op/runtimes/local.ts +6 -0
  175. package/src/op/step-console.test.ts +150 -0
  176. package/src/op/step-console.ts +155 -0
  177. package/src/op/step-output-ref.test.ts +68 -24
  178. package/src/op/step-output-ref.ts +85 -65
  179. package/src/point-decision-bundle.test.ts +1 -0
  180. package/src/point-decision.ts +3 -0
  181. package/src/workspace/agent-cli.ts +2 -0
  182. package/src/workspace/compose-graph.test.ts +55 -0
  183. package/src/workspace/compose-graph.ts +28 -6
  184. package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +16 -1
  185. package/src/workspace/conformance/writer.ts +1 -1
  186. package/src/workspace/decide.test.ts +87 -3
  187. package/src/workspace/decide.ts +142 -17
  188. package/src/workspace/graph-contract.test.ts +26 -1
  189. package/src/workspace/graph.schema.json +68 -1
  190. package/src/workspace/lineage-upgrade.test.ts +21 -0
  191. package/src/workspace/lineage-upgrade.ts +19 -11
  192. package/src/workspace/nested-graph.test.ts +14 -0
  193. package/src/workspace/nested-graph.ts +7 -2
  194. package/src/workspace/point-answer.schema.json +16 -1
  195. package/src/workspace/points-cli.ts +16 -8
  196. package/src/workspace/points-write.schema.json +39 -4
  197. package/src/workspace/points.schema.json +42 -3
  198. package/src/workspace/reason-codes.ts +2 -1
  199. package/src/workspace/record-assets.test.ts +14 -0
  200. package/src/workspace/records-cli.ts +28 -7
  201. package/src/workspace/records-write-commands.e2e.test.ts +32 -0
  202. package/src/workspace/records-write.ts +2 -3
  203. package/src/workspace/records.schema.json +2 -1
  204. package/src/workspace/records.ts +17 -4
  205. package/src/workspace/runs.ts +4 -2
  206. package/src/workspace/trust/trust.test.ts +105 -0
  207. package/src/workspace/trust/verify-cli.ts +2 -1
  208. package/src/workspace/trust/verify.ts +106 -13
  209. package/src/workspace/write-scope.test.ts +18 -0
  210. package/src/workspace/write-scope.ts +16 -5
@@ -8,6 +8,12 @@
8
8
  * that touches those files must be attested by a signer the base policy
9
9
  * trusts, and by an admin when the base policy grants the admin role.
10
10
  *
11
+ * The write scope is policy too (#3079): when the workspace declaration at
12
+ * base has a `writeScope` or `agents` block, a commit that changes either
13
+ * block, or removes the declaration, is a protected write on the same terms.
14
+ * Other edits to the declaration are not. The declaration is found from
15
+ * `cwd`, as `chant workspace check --changes` finds it.
16
+ *
11
17
  * Attestation is opt-in (#2525 rule 6). Without a signers file at base the
12
18
  * check passes and says so, unless `--require attested` asks for more.
13
19
  *
@@ -21,6 +27,11 @@
21
27
  * `--require`, `runs` reports and nothing fails (studio-035 d).
22
28
  */
23
29
  import { execFileSync } from "node:child_process";
30
+ import { posix } from "node:path";
31
+ import { isDeepStrictEqual } from "node:util";
32
+ import { DECLARATION_FILES, WorkspaceReadError } from "../declaration.js";
33
+ import { parseJsonText } from "../jsonc.js";
34
+ import { locateWorkspace } from "../which-chant.js";
24
35
  import { policyAtBase, commitProvenance, resolveBase } from "./provenance.js";
25
36
  import { policyWriters, protectedPaths } from "./policy.js";
26
37
  import { fileAt, nextVersion, rotationPath, signerHistory } from "./rotation.js";
@@ -45,6 +56,47 @@ export function commitsTouching(repo, range, paths) {
45
56
  }
46
57
  return out;
47
58
  }
59
+ const SCOPE_BLOCKS = ["writeScope", "agents"];
60
+ function scopeState(repo, rev, dir) {
61
+ const present = DECLARATION_FILES.map((name) => ({ name, text: fileAt(repo, rev, posix.join(dir, name)) })).filter((f) => f.text !== undefined);
62
+ if (present.length === 0)
63
+ return { blocks: {} };
64
+ if (present.length > 1)
65
+ return { unreadable: present.map((f) => f.text).join("\0") };
66
+ const parsed = parseJsonText(present[0].text, { jsonc: present[0].name.endsWith(".jsonc") });
67
+ if (!parsed.ok || typeof parsed.value !== "object" || parsed.value === null || Array.isArray(parsed.value))
68
+ return { unreadable: present[0].text };
69
+ const obj = parsed.value;
70
+ return { blocks: Object.fromEntries(SCOPE_BLOCKS.filter((b) => obj[b] !== undefined).map((b) => [b, obj[b]])) };
71
+ }
72
+ /** The scope blocks that differ between two states; all of them when either can't be read and the texts differ. */
73
+ function changedBlocks(a, b) {
74
+ if ("unreadable" in a || "unreadable" in b) {
75
+ return "unreadable" in a && "unreadable" in b && a.unreadable === b.unreadable ? [] : [...SCOPE_BLOCKS];
76
+ }
77
+ return SCOPE_BLOCKS.filter((k) => !isDeepStrictEqual(a.blocks[k], b.blocks[k]));
78
+ }
79
+ /**
80
+ * The directory, from the repository root, of the declaration found from
81
+ * `cwd` at `base`, with the scope blocks it has there. Null when there is no
82
+ * declaration, or one with neither block: then nothing in it is protected. A
83
+ * declaration that can't be read at base counts as having both.
84
+ */
85
+ function scopedDeclarationAt(repo, cwd, base) {
86
+ let dir;
87
+ try {
88
+ const root = locateWorkspace(cwd, base).root;
89
+ dir = root === "." ? "" : root;
90
+ }
91
+ catch (err) {
92
+ if (err instanceof WorkspaceReadError)
93
+ return null;
94
+ throw err;
95
+ }
96
+ const state = scopeState(repo, base, dir);
97
+ const blocks = "unreadable" in state ? [...SCOPE_BLOCKS] : SCOPE_BLOCKS.filter((b) => b in state.blocks);
98
+ return blocks.length > 0 ? { dir, blocks } : null;
99
+ }
48
100
  /** Whether a merge commit changes nothing beyond what its parents bring. */
49
101
  function mergeWithoutOwnChanges(repo, commit, parents) {
50
102
  if (parents < 2)
@@ -73,6 +125,7 @@ export function verifyChange(opts) {
73
125
  protectedPaths: protectedPaths(policy),
74
126
  protectedChanged: [],
75
127
  protectedWrites: [],
128
+ declaration: null,
76
129
  rotation: null,
77
130
  notes: [],
78
131
  failures: [],
@@ -99,7 +152,8 @@ export function verifyChange(opts) {
99
152
  }
100
153
  // Every commit in the change, oldest first, with its parents.
101
154
  const listing = git(repo, ["rev-list", "--reverse", "--parents", `${base.commit}..${head}`]).split("\n").filter(Boolean);
102
- const parentsOf = new Map(listing.map((l) => { const [c, ...p] = l.split(" "); return [c, p.length]; }));
155
+ const parentList = new Map(listing.map((l) => { const [c, ...p] = l.split(" "); return [c, p]; }));
156
+ const parentsOf = new Map([...parentList].map(([c, p]) => [c, p.length]));
103
157
  report.runs = changeRuns(repo, [...parentsOf.keys()], policy.runners);
104
158
  if (opts.require === "attested-runs") {
105
159
  if (policy.runners.length === 0 && report.runs.commits.length > 0) {
@@ -145,24 +199,50 @@ export function verifyChange(opts) {
145
199
  report.commits.push(v);
146
200
  }
147
201
  // Protected writes: every commit that touches the policy needs a writer's signature.
202
+ const writers = policyWriters(policy);
203
+ const judge = (commit, paths, blocks) => {
204
+ const v = verdicts.get(commit) ?? { ...commitProvenance(repo, policy, commit, opts.attestors), commit, subject: "" };
205
+ const allowed = v.level === "attested" && v.principal !== undefined && writers.has(v.principal);
206
+ const who = policy.roles.admin?.length ? "an admin the policy at base names" : "a signer the policy at base lists";
207
+ const what = blocks ? `${paths.join(", ")} (${blocks.join(", ")})` : paths.join(", ");
208
+ const reason = allowed
209
+ ? `signed by ${v.principal}, ${who.replace(/^an? /, "")}`
210
+ : v.level === "attested"
211
+ ? `signed by ${v.principal}, who is not ${who}`
212
+ : `${what} is protected and needs a signature by ${who}: ${v.reason}`;
213
+ report.protectedWrites.push({ commit, paths, ...(blocks ? { blocks } : {}), level: v.level, ...(v.principal ? { principal: v.principal } : {}), allowed, reason });
214
+ if (!allowed)
215
+ report.failures.push(`${commit.slice(0, 8)} changes ${what}: ${reason}`);
216
+ };
148
217
  if (changedPaths.length > 0) {
149
- const writers = policyWriters(policy);
150
218
  const touching = commitsTouching(repo, `${base.commit}..${head}`, report.protectedPaths);
151
219
  if (touching.size === 0) {
152
220
  report.failures.push(`the policy files ${changedPaths.join(", ")} differ from base, and no commit in the change accounts for it`);
153
221
  }
154
- for (const [commit, paths] of touching) {
155
- const v = verdicts.get(commit) ?? { ...commitProvenance(repo, policy, commit, opts.attestors), commit, subject: "" };
156
- const allowed = v.level === "attested" && v.principal !== undefined && writers.has(v.principal);
157
- const who = policy.roles.admin?.length ? "an admin the policy at base names" : "a signer the policy at base lists";
158
- const reason = allowed
159
- ? `signed by ${v.principal}, ${who.replace(/^an? /, "")}`
160
- : v.level === "attested"
161
- ? `signed by ${v.principal}, who is not ${who}`
162
- : `${paths.join(", ")} is protected and needs a signature by ${who}: ${v.reason}`;
163
- report.protectedWrites.push({ commit, paths, level: v.level, ...(v.principal ? { principal: v.principal } : {}), allowed, reason });
164
- if (!allowed)
165
- report.failures.push(`${commit.slice(0, 8)} changes ${paths.join(", ")}: ${reason}`);
222
+ for (const [commit, paths] of touching)
223
+ judge(commit, paths);
224
+ }
225
+ // The write scope in the declaration at base (#3079): a commit that changes writeScope or agents.
226
+ const scoped = scopedDeclarationAt(repo, opts.cwd ?? repo, base.commit);
227
+ if (scoped) {
228
+ const files = DECLARATION_FILES.map((name) => posix.join(scoped.dir, name));
229
+ const changed = changedBlocks(scopeState(repo, mergeBase, scoped.dir), scopeState(repo, head, scoped.dir));
230
+ report.declaration = { path: files.find((f) => fileAt(repo, base.commit, f) !== undefined) ?? files[0], blocks: scoped.blocks, changed };
231
+ if (changed.length > 0) {
232
+ let accounted = false;
233
+ for (const [commit, paths] of commitsTouching(repo, `${base.commit}..${head}`, files)) {
234
+ const after = scopeState(repo, commit, scoped.dir);
235
+ // A merge changes a block only when it differs from every parent.
236
+ const per = (parentList.get(commit) ?? []).map((p) => new Set(changedBlocks(scopeState(repo, p, scoped.dir), after)));
237
+ const blocks = SCOPE_BLOCKS.filter((b) => per.length > 0 && per.every((s) => s.has(b)));
238
+ if (blocks.length === 0)
239
+ continue;
240
+ accounted = true;
241
+ judge(commit, paths, blocks);
242
+ }
243
+ if (!accounted) {
244
+ report.failures.push(`${changed.join(" and ")} in ${report.declaration.path} differ from base, and no commit in the change accounts for it`);
245
+ }
166
246
  }
167
247
  }
168
248
  // A new signer set must be signed by a threshold of the set at base (#2553).
@@ -37,7 +37,7 @@
37
37
  * `write-scope-class-unknown` until the plugin is installed at the pinned
38
38
  * version or the entry is removed.
39
39
  */
40
- import { type AgentDeclaration, type ClassScope, type Declaration, type PrincipalClass, type ProtectedPath, type WriteVerb } from "./declaration.js";
40
+ import { WorkspaceReadError, type AgentDeclaration, type ClassScope, type Declaration, type PrincipalClass, type ProtectedPath, type WriteVerb } from "./declaration.js";
41
41
  import { classesOf, roleGrants, type ClassRegistry } from "./principal-classes.js";
42
42
  import { type TrustPolicy } from "./trust/policy.js";
43
43
  import { AGENT_TRAILER } from "./trailers.js";
@@ -164,6 +164,8 @@ export interface ScopeSource {
164
164
  policy: TrustPolicy;
165
165
  /** The principal classes: the core four and those the declaration's pins supply, path pins read where the declaration was (#3080). */
166
166
  classes: ClassRegistry;
167
+ /** Why the working tree's declaration couldn't be read when `declaration` is null: declaration-missing, or declaration-invalid with the problem (#3433). */
168
+ failure?: WorkspaceReadError;
167
169
  }
168
170
  /**
169
171
  * The scope for a write from `cwd`: the declaration and the trust policy at
@@ -1 +1 @@
1
- {"version":3,"file":"write-scope.d.ts","sourceRoot":"","sources":["../../src/workspace/write-scope.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"write-scope.d.ts","sourceRoot":"","sources":["../../src/workspace/write-scope.ts"],"names":[],"mappings":"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"}
@@ -275,17 +275,22 @@ export function judgePath(declaration, writer, path, change) {
275
275
  message: `${who(writer)} may not write ${path}${allowance}: writeScope.${writer.class}.protected lists ${guarded.path}`,
276
276
  };
277
277
  }
278
- function readAt(cwd, at) {
278
+ /** The declaration at `at` (the working tree without it), or the read error that says why there is none. */
279
+ function readOrFailure(cwd, at) {
279
280
  try {
280
281
  const located = locateWorkspace(cwd, at);
281
282
  return { declaration: readDeclaration(located.tree), rootOnDisk: located.rootOnDisk, root: located.root, tree: located.tree };
282
283
  }
283
284
  catch (err) {
284
285
  if (err instanceof WorkspaceReadError)
285
- return null;
286
+ return err;
286
287
  throw err;
287
288
  }
288
289
  }
290
+ function readAt(cwd, at) {
291
+ const read = readOrFailure(cwd, at);
292
+ return read instanceof WorkspaceReadError ? null : read;
293
+ }
289
294
  /**
290
295
  * The scope for a write from `cwd`: the declaration and the trust policy at
291
296
  * the base revision (`origin/HEAD`, `main` or `master`), so a write can't
@@ -306,8 +311,10 @@ export function scopeSource(cwd, at) {
306
311
  if (atBase)
307
312
  return { ...withClasses(atBase), from: "base", policy };
308
313
  }
309
- const here = readAt(cwd);
310
- return here ? { ...withClasses(here), from: "working-tree", policy } : { declaration: null, from: null, rootOnDisk: null, root: null, policy, classes: coreClassRegistry() };
314
+ const here = readOrFailure(cwd);
315
+ if (here instanceof WorkspaceReadError)
316
+ return { declaration: null, from: null, rootOnDisk: null, root: null, policy, classes: coreClassRegistry(), failure: here };
317
+ return { ...withClasses(here), from: "working-tree", policy };
311
318
  }
312
319
  const toPosix = (p) => (sep === "/" ? p : p.split(sep).join("/"));
313
320
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.124.23",
3
+ "version": "0.124.25",
4
4
  "description": "Declarative infrastructure-as-code toolkit — TypeScript on Node.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
package/src/ci-jobs.ts CHANGED
@@ -15,7 +15,9 @@
15
15
  * - the `setup` steps and `environment` a job may carry, checked once per
16
16
  * forge ({@link assertSetupSteps}, {@link assertEnvironment},
17
17
  * {@link gitlabSetupScript}, {@link assertGitlabEnvironment});
18
- * - GitLab's `rules:` expressions and `id_tokens:` declaration.
18
+ * - GitLab's `rules:` expressions and `id_tokens:` declaration;
19
+ * - where a job runs and the id tokens it asks for (#3914), {@link githubRunsOn},
20
+ * {@link gitlabTags}, {@link gitlabIdTokensDoc} and {@link idTokenAudienceEnv}.
19
21
  *
20
22
  * The Forgejo dialect (action refs, dropped `permissions:` and
21
23
  * `environment:`, the header note naming what was dropped) is the forgejo
@@ -96,11 +98,66 @@ export function dirnameOf(path: string): string {
96
98
  return i <= 0 ? "." : path.slice(0, i);
97
99
  }
98
100
 
101
+ /**
102
+ * Where a generated job runs (#3914): a runner label, a list of labels the
103
+ * runner must all carry, or on GitHub a runner group with optional labels.
104
+ * `runs-on` on GitHub and Forgejo, `tags` on GitLab.
105
+ */
106
+ export type CiRunner = string | readonly string[] | { group: string; labels?: readonly string[] };
107
+
108
+ /** True when `runner` is a GitHub runner group, which only GitHub has. */
109
+ export function isRunnerGroup(runner: CiRunner | undefined): runner is { group: string; labels?: readonly string[] } {
110
+ return runner !== undefined && typeof runner === "object" && !Array.isArray(runner);
111
+ }
112
+
113
+ /**
114
+ * Refuse a runner group on a forge that has none (#3914). `subject` and
115
+ * `field` name whose runner it is, such as `Op waves "x"` and `wave "prod"
116
+ * runner`.
117
+ */
118
+ export function assertRunnerLabels(subject: string, field: string, runner: CiRunner | undefined, forge: "GitLab" | "Forgejo"): void {
119
+ if (!isRunnerGroup(runner)) return;
120
+ throw new Error(
121
+ `${subject}: ${field} is the runner group "${runner.group}", which only GitHub has. ` +
122
+ `${forge === "GitLab" ? "GitLab picks a runner by its tags" : "Forgejo picks a runner by its labels"}; give a label or a list of labels.`,
123
+ );
124
+ }
125
+
126
+ /**
127
+ * Each id token's audience as a variable, `<NAME>_AUDIENCE` (#3914). On
128
+ * GitHub and Forgejo the audience is chosen when the job requests the token,
129
+ * so the job reads it from there; GitLab declares it in `id_tokens:`
130
+ * ({@link gitlabIdTokensDoc}).
131
+ */
132
+ export function idTokenAudienceEnv(idTokens: Readonly<Record<string, string>> | undefined): Record<string, string> {
133
+ return Object.fromEntries(Object.entries(idTokens ?? {}).map(([name, aud]) => [`${name}_AUDIENCE`, aud]));
134
+ }
135
+
136
+ /** A job's GitLab `id_tokens:` for `{ <variable>: <audience> }` (#3914), or undefined when there are none. */
137
+ export function gitlabIdTokensDoc(idTokens: Readonly<Record<string, string>> | undefined): Record<string, { aud: string }> | undefined {
138
+ const entries = Object.entries(idTokens ?? {});
139
+ return entries.length > 0 ? Object.fromEntries(entries.map(([name, aud]) => [name, { aud }])) : undefined;
140
+ }
141
+
142
+ /** A job's GitLab `tags:` for `runner` (#3914), or undefined when it names none. */
143
+ export function gitlabTags(runner: CiRunner | undefined): string[] | undefined {
144
+ if (runner === undefined || isRunnerGroup(runner)) return undefined;
145
+ return typeof runner === "string" ? [runner] : [...runner];
146
+ }
147
+
99
148
  // ── GitHub Actions and Forgejo Actions ──────────────────────────────────────
100
149
 
101
150
  /** The runner label a generated job asks for; the Forgejo dialect rewrites it to the project's label. */
102
151
  export const GITHUB_RUNNER = "ubuntu-latest";
103
152
 
153
+ /** A job's `runs-on` for `runner` (#3914): {@link GITHUB_RUNNER} when it names none. */
154
+ export function githubRunsOn(runner: CiRunner | undefined): string | string[] | { group: string; labels?: string[] } {
155
+ if (runner === undefined) return GITHUB_RUNNER;
156
+ if (typeof runner === "string") return runner;
157
+ if (isRunnerGroup(runner)) return { group: runner.group, ...(runner.labels && runner.labels.length > 0 ? { labels: [...runner.labels] } : {}) };
158
+ return [...runner];
159
+ }
160
+
104
161
  /** The job token, which GitHub and Forgejo both mint for every run. */
105
162
  export const GITHUB_JOB_TOKEN = "${{ github.token }}";
106
163
 
@@ -32,6 +32,7 @@ import { loadPolicyChecks } from "../../lint/policy";
32
32
  import { armSandboxPolicyExecution, runProjectPolicies } from "../../lint/policy-sandbox";
33
33
  import { sortedJsonReplacer } from "../../utils";
34
34
  import { rankFoldBlockers, toCollapsedFormat, type FoldRankResult } from "../../discovery/fold-rank";
35
+ import { foldExecutionCounts } from "../../discovery/fold-import";
35
36
  import { formatError, formatWarning, formatSuccess, formatBold, formatInfo } from "../format";
36
37
  import { writeFileSync, mkdirSync } from "fs";
37
38
  import { resolve, dirname, join, relative } from "path";
@@ -396,6 +397,23 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
396
397
  : (decision.reason ?? "fell back to run");
397
398
  console.error(formatInfo(`[fold:${decision.mode}] ${rel} — ${detail}`));
398
399
  }
400
+ // typescript-as-data#177 — the exact instrument beside the sampled one.
401
+ // `test/leftness` reports "0 MB of definition-library code on the path"
402
+ // from a `node --cpu-prof` recording, and a profile has a sampling
403
+ // floor: 0 MB is everything the profiler could see rather than nothing
404
+ // having run. L10.1's counters have no floor.
405
+ //
406
+ // Inside --verbose with the per-file lines, not beside them. The default
407
+ // path is deliberately quiet for the reason above, and the leftness
408
+ // capture already runs --verbose and greps this log for `[fold:fold]`.
409
+ const counts = foldExecutionCounts();
410
+ console.error(
411
+ formatInfo(
412
+ `[fold:counts] factoryInvocations=${counts.factoryInvocations} ` +
413
+ `projectFactoryInvocations=${counts.projectFactoryInvocations} ` +
414
+ `factoryInterpretations=${counts.factoryInterpretations}`,
415
+ ),
416
+ );
399
417
  } else {
400
418
  console.error(formatInfo(summarizeFoldDecisions(result.foldDecisions)));
401
419
  }
@@ -7,7 +7,9 @@
7
7
  * Each run is traced through a load hook that NODE_OPTIONS hands to the CLI's
8
8
  * node, so the test can say what loaded: none of chant's TypeScript source,
9
9
  * ever, and tsx only for a command that imports TypeScript (here the
10
- * lexicon and the project source).
10
+ * lexicon and the project source). tsx's ESM hooks run on a thread of their
11
+ * own, about 200 MB of the process (#3276), so a workspace read must not
12
+ * register it either.
11
13
  *
12
14
  * End to end because it packs, installs from the registry and spawns the CLI.
13
15
  */
@@ -20,12 +22,14 @@ import { afterAll, beforeAll, describe, expect, test } from "vitest";
20
22
 
21
23
  const coreRoot = resolve(import.meta.dirname, "..", "..");
22
24
  const renderRoot = resolve(coreRoot, "..", "..", "lexicons", "render");
25
+ const referenceDecisions = resolve(coreRoot, "..", "..", "reference-workspace", "decisions");
23
26
  const version = (JSON.parse(readFileSync(join(coreRoot, "package.json"), "utf-8")) as { version: string }).version;
24
27
 
25
28
  let scratch: string;
26
29
  let project: string;
27
30
  let cjsProject: string;
28
31
  let waves: string;
32
+ let workspace: string;
29
33
  let chant: string;
30
34
  let tracer: string;
31
35
 
@@ -126,6 +130,31 @@ beforeAll(() => {
126
130
  writeFileSync(join(waves, ".gitignore"), ".chant/\n");
127
131
  git("add", "-A");
128
132
  git("commit", "-qm", "ignore");
133
+
134
+ // A workspace whose declaration names a .mjs record kind, with a member
135
+ // that has a chant.config.ts, for the workspace reads below (#3276).
136
+ workspace = join(scratch, "workspace");
137
+ mkdirSync(join(workspace, "decisions"), { recursive: true });
138
+ mkdirSync(join(workspace, "app"));
139
+ for (const file of ["decision.kind.mjs", "decision.schema.json", "ref-001-how-the-app-is-deployed.md"]) {
140
+ writeFileSync(join(workspace, "decisions", file), readFileSync(join(referenceDecisions, file)));
141
+ }
142
+ writeFileSync(
143
+ join(workspace, "chant.workspace.json"),
144
+ JSON.stringify({
145
+ name: "e2e",
146
+ schema: 1,
147
+ members: [{ name: "app", dir: "app", kind: "chant" }],
148
+ records: [{ kind: "decisions/decision.kind.mjs" }],
149
+ }),
150
+ );
151
+ writeFileSync(join(workspace, "app", "chant.config.ts"), `export default { lexicons: [] as string[] };\n`);
152
+ symlinkSync(join(project, "node_modules"), join(workspace, "node_modules"), "dir");
153
+ const wsGit = (...args: string[]) => execFileSync("git", ["-c", "user.email=e2e@chant", "-c", "user.name=e2e", ...args], { cwd: workspace, stdio: "ignore" });
154
+ wsGit("init", "-q", "-b", "main");
155
+ writeFileSync(join(workspace, ".gitignore"), "node_modules\n");
156
+ wsGit("add", "-A");
157
+ wsGit("commit", "-qm", "workspace");
129
158
  }, 600_000);
130
159
 
131
160
  afterAll(() => {
@@ -148,6 +177,21 @@ describe("the installed chant CLI (#3878)", () => {
148
177
  expect(r.loaded).toMatchObject({ chantSrc: 0, tsx: 0 });
149
178
  });
150
179
 
180
+ test("workspace ls reads a .mjs record kind from a member with a chant.config.ts without loading tsx", () => {
181
+ const r = run(join(workspace, "app"), ["workspace", "ls", "--json"]);
182
+ expect(r.status, r.stderr).toBe(0);
183
+ const ls = JSON.parse(r.stdout) as { workspace: { records: Array<{ name: string; kind: string | null }> } };
184
+ expect(ls.workspace.records.map((k) => k.kind)).toEqual(["decision"]);
185
+ expect(r.loaded).toMatchObject({ chantSrc: 0, tsx: 0 });
186
+ });
187
+
188
+ test("workspace records reads the declared .mjs kind's records without loading tsx", () => {
189
+ const r = run(join(workspace, "app"), ["workspace", "records", "--json"]);
190
+ expect(r.status, r.stderr).toBe(0);
191
+ expect(r.stdout).toContain("ref-001");
192
+ expect(r.loaded).toMatchObject({ chantSrc: 0, tsx: 0 });
193
+ });
194
+
151
195
  test("build loads the TypeScript lexicon and project through tsx, against the one compiled core", () => {
152
196
  const r = run(project, ["build", "src", "-o", "out/render.json"]);
153
197
  expect(r.status, r.stderr).toBe(0);
@@ -24,6 +24,7 @@ import { FAN_OUT_GATE_OP, OVERRIDE_GATE_OP, WORKSPACE_UPGRADE_GATE_OP } from "..
24
24
  import { PR_GATE_OP } from "../../pr-loop";
25
25
  import { pushLifecycleReport, requireLifecycleLedger } from "../../lifecycle/git";
26
26
  import { LIFECYCLE_LOCAL_NOTE, isLifecycleLocalNote } from "../../lifecycle/local-note";
27
+ import { ledgerRecordingOff, NO_RECORD_ENV } from "../../lifecycle/no-record";
27
28
  import { formatError, formatWarning, formatSuccess, formatInfo } from "../format";
28
29
  import type { CommandContext } from "../registry";
29
30
 
@@ -267,6 +268,14 @@ export async function recordGateApproval(
267
268
  gate: string,
268
269
  opts: GateApprovalOptions,
269
270
  ): Promise<GateApprovalOutcome> {
271
+ // #3955: with recording off the approval would be dropped with the process.
272
+ if (ledgerRecordingOff()) {
273
+ console.error(formatError({
274
+ message: `Cannot record the approval of "${gate}": ${NO_RECORD_ENV} is set, so nothing is written to chant/lifecycle`,
275
+ hint: `Unset ${NO_RECORD_ENV} to approve the gate.`,
276
+ }));
277
+ return { ok: false };
278
+ }
270
279
  const { ops } = await discoverOps();
271
280
  // `fan-out` is the op name every `chant components fan-out` gate is recorded
272
281
  // under (../handlers/fan-out.ts). It is a command rather than a declaration,
@@ -42,7 +42,7 @@ vi.mock("node:child_process", async () => {
42
42
  return { ...actual, exec: execWithCustom, execFile: execFileWithCustom };
43
43
  });
44
44
 
45
- const { runEmulator, formatEmulatorHelp, parsePortMappings } = await import("./emulator");
45
+ const { runEmulator, formatEmulatorHelp, parsePortMappings, parseUpOptions } = await import("./emulator");
46
46
 
47
47
  function cap(): EmulatorCapability {
48
48
  return {
@@ -286,3 +286,63 @@ describe("chant emulator --port and --help (#3673)", () => {
286
286
  expect(formatEmulatorHelp([])).toContain("No lexicon configured for this project has a local emulator.");
287
287
  });
288
288
  });
289
+
290
+ describe("chant emulator up --image, --network, --setting, --loopback (#3952)", () => {
291
+ const floci = (): EmulatorCapability => ({ ...cap(), spec: { ...cap().spec, settingsPrefix: "FLOCI_" } });
292
+ const other = (): EmulatorCapability => ({
293
+ spec: { name: "chant-floci-az", image: "floci/floci-az:1.0.0", containerPort: 4577, healthPath: "/health" },
294
+ env: () => ({}),
295
+ });
296
+
297
+ beforeEach(() => {
298
+ upMock.mockReset();
299
+ vi.restoreAllMocks();
300
+ });
301
+
302
+ test("up passes each option to the lifecycle", async () => {
303
+ stderr();
304
+ upMock.mockResolvedValue({ endpoint: "http://localhost:4566" });
305
+ const exit = await runEmulator({
306
+ args: makeArgs({
307
+ path: "up", image: "floci/floci:1.6.0", network: "loom", loopback: true,
308
+ setting: ["FLOCI_STORAGE_MODE=memory", "FLOCI_X=a=b"], port: ["4567"],
309
+ }),
310
+ plugins: [plugin("aws", floci())],
311
+ serializers: [],
312
+ });
313
+ expect(exit).toBe(0);
314
+ expect(upMock).toHaveBeenCalledWith({
315
+ image: "floci/floci:1.6.0", network: "loom", loopback: true,
316
+ settings: { FLOCI_STORAGE_MODE: "memory", FLOCI_X: "a=b" }, port: 4567,
317
+ });
318
+ });
319
+
320
+ test("a setting no selected emulator takes is refused before anything starts", async () => {
321
+ const err = stderr();
322
+ const exit = await runEmulator({
323
+ args: makeArgs({ path: "up", setting: ["HOME=/tmp"] }),
324
+ plugins: [plugin("aws", floci())],
325
+ serializers: [],
326
+ });
327
+ expect(exit).toBe(1);
328
+ expect(upMock).not.toHaveBeenCalled();
329
+ expect(err.join("\n")).toContain("--setting HOME: no selected emulator takes it; the selected ones take FLOCI_* (chant-floci)");
330
+ });
331
+
332
+ test("settings go only to the emulator whose prefix matches; --image needs one emulator", () => {
333
+ const a = floci();
334
+ const b = other();
335
+ const opts = parseUpOptions({ setting: ["FLOCI_A=1"], network: "n" }, [a, b]);
336
+ expect(opts.get(a)).toEqual({ network: "n", settings: { FLOCI_A: "1" } });
337
+ expect(opts.get(b)).toEqual({ network: "n" });
338
+ expect(() => parseUpOptions({ image: "x:1" }, [a, b])).toThrow(/--image x:1 is ambiguous with 2 emulators/);
339
+ expect(() => parseUpOptions({ setting: ["FLOCI_A"] }, [a])).toThrow(/expected <KEY>=<VALUE>/);
340
+ });
341
+
342
+ test("the options apply to up only", async () => {
343
+ const err = stderr();
344
+ const exit = await runEmulator({ args: makeArgs({ path: "status", loopback: true }), plugins: [plugin("aws", floci())], serializers: [] });
345
+ expect(exit).toBe(1);
346
+ expect(err.join("\n")).toContain("--loopback applies to `chant emulator up` only");
347
+ });
348
+ });
@@ -1,9 +1,9 @@
1
1
  import { exec, execFile } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
- import { emulatorLifecycle, emulatorsOf, type EmulatorCapability } from "../../op";
3
+ import { checkSettings, emulatorLifecycle, emulatorsOf, type EmulatorCapability, type EmulatorUpArgs } from "../../op";
4
4
  import type { LexiconPlugin } from "../../lexicon";
5
5
  import { formatError, formatWarning, formatSuccess } from "../format";
6
- import type { CommandContext } from "../registry";
6
+ import type { CommandContext, ParsedArgs } from "../registry";
7
7
 
8
8
  const execAsync = promisify(exec);
9
9
  const execFileAsync = promisify(execFile);
@@ -116,13 +116,60 @@ export function parsePortMappings(values: readonly string[], targets: readonly E
116
116
  return ports;
117
117
  }
118
118
 
119
+ /** The `up` options other than the port (#3952), per selected emulator. */
120
+ type UpOptions = Pick<EmulatorUpArgs, "image" | "network" | "settings" | "loopback">;
121
+
122
+ /**
123
+ * `--image`, `--network`, `--setting` and `--loopback` as each selected
124
+ * emulator's `up` options (#3952). `--image` needs exactly one emulator
125
+ * selected, the way a bare `--port` does. A `--setting` goes to each emulator
126
+ * whose settings prefix its key starts with, and a key that no selected
127
+ * emulator takes is refused rather than passed to a container as plain env.
128
+ */
129
+ export function parseUpOptions(
130
+ args: Pick<ParsedArgs, "image" | "network" | "setting" | "loopback">,
131
+ targets: readonly EmulatorCapability[],
132
+ ): Map<EmulatorCapability, UpOptions> {
133
+ if (args.image !== undefined && targets.length !== 1) {
134
+ throw new Error(`--image ${args.image} is ambiguous with ${targets.length} emulators selected; pick one with --lexicon <name>`);
135
+ }
136
+ const settings = new Map<EmulatorCapability, Record<string, string>>();
137
+ for (const value of args.setting ?? []) {
138
+ const match = /^([^=]+)=(.*)$/s.exec(value);
139
+ if (!match) throw new Error(`--setting ${value}: expected <KEY>=<VALUE>, e.g. --setting FLOCI_<NAME>=<value>`);
140
+ const [, key, val] = match as unknown as [string, string, string];
141
+ const takers = targets.filter((t) => t.spec.settingsPrefix && key.startsWith(t.spec.settingsPrefix));
142
+ if (takers.length === 0) {
143
+ const prefixes = targets.filter((t) => t.spec.settingsPrefix).map((t) => `${t.spec.settingsPrefix}* (${t.spec.name})`);
144
+ throw new Error(`--setting ${key}: no selected emulator takes it; ${prefixes.length ? `the selected ones take ${prefixes.join(", ")}` : "the selected ones take no settings"}`);
145
+ }
146
+ for (const t of takers) {
147
+ const own = { ...settings.get(t), [key]: val };
148
+ checkSettings(t.spec, own);
149
+ settings.set(t, own);
150
+ }
151
+ }
152
+ const out = new Map<EmulatorCapability, UpOptions>();
153
+ for (const t of targets) {
154
+ const opts: UpOptions = {};
155
+ if (args.image !== undefined) opts.image = args.image;
156
+ if (args.network !== undefined) opts.network = args.network;
157
+ if (args.loopback) opts.loopback = true;
158
+ const own = settings.get(t);
159
+ if (own) opts.settings = own;
160
+ out.set(t, opts);
161
+ }
162
+ return out;
163
+ }
164
+
119
165
  /**
120
166
  * `chant emulator --help` (#3673): the subcommands and flags, and for each
121
167
  * configured lexicon's emulator the port, endpoint and sign-in a client uses.
122
168
  */
123
169
  export function formatEmulatorHelp(plugins: readonly LexiconPlugin[]): string {
124
170
  const lines = [
125
- "Usage: chant emulator <up|down|status> [--lexicon <name>] [--port <container-port>=<host-port>] [--json]",
171
+ "Usage: chant emulator <up|down|status> [--lexicon <name>] [--port <container-port>=<host-port>] [--image <ref>]",
172
+ " [--network <name>] [--setting <KEY>=<VALUE>] [--loopback] [--json]",
126
173
  "",
127
174
  "Boot, stop or inspect the local emulators of the project's configured lexicons.",
128
175
  "",
@@ -137,6 +184,11 @@ export function formatEmulatorHelp(plugins: readonly LexiconPlugin[]): string {
137
184
  " --lexicon <name> Act on one lexicon's emulators only.",
138
185
  " --port <c>=<h> up: publish container port <c> on host port <h>; repeatable.",
139
186
  " A bare <h> works when one emulator is selected.",
187
+ " --image <ref> up: start this image instead of the pinned default; one emulator only.",
188
+ " --network <name> up: join each started container to this Docker network.",
189
+ " --setting <K>=<V> up: pass an emulator setting as -e K=V; repeatable. The key must",
190
+ " start with the emulator's prefix (FLOCI_ for aws).",
191
+ " --loopback up: publish ports on 127.0.0.1 only, not on every interface.",
140
192
  " --json Print the machine-readable report on stdout.",
141
193
  "",
142
194
  ];
@@ -195,9 +247,18 @@ export async function runEmulator(ctx: CommandContext): Promise<number> {
195
247
  }
196
248
 
197
249
  let ports: Map<number, number>;
250
+ let upOptions: Map<EmulatorCapability, UpOptions>;
198
251
  try {
199
252
  if (args.port?.length && action !== "up") throw new Error("--port applies to `chant emulator up` only");
253
+ const upOnly = [
254
+ args.image !== undefined && "--image",
255
+ args.network !== undefined && "--network",
256
+ args.setting?.length && "--setting",
257
+ args.loopback && "--loopback",
258
+ ].filter(Boolean);
259
+ if (upOnly.length && action !== "up") throw new Error(`${upOnly.join(", ")} applies to \`chant emulator up\` only`);
200
260
  ports = parsePortMappings(args.port ?? [], targets.map((t) => t.cap));
261
+ upOptions = parseUpOptions(args, targets.map((t) => t.cap));
201
262
  } catch (err) {
202
263
  console.error(formatError({ message: err instanceof Error ? err.message : String(err) }));
203
264
  return 1;
@@ -209,7 +270,7 @@ export async function runEmulator(ctx: CommandContext): Promise<number> {
209
270
  if (action === "up") {
210
271
  const port = ports.get(cap.spec.containerPort);
211
272
  try {
212
- const { endpoint } = await lc.up(port === undefined ? {} : { port });
273
+ const { endpoint } = await lc.up({ ...upOptions.get(cap), ...(port === undefined ? {} : { port }) });
213
274
  reports.push({ lexicon, name: cap.spec.name, endpoint, env: cap.env(endpoint) });
214
275
  } catch (err) {
215
276
  console.error(formatError({ message: err instanceof Error ? err.message : String(err) }));