aontu 0.63.0 → 0.65.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.
Files changed (101) hide show
  1. package/README.md +8 -8
  2. package/dist/agentsmd.js +3 -3
  3. package/dist/agentsmd.js.map +1 -1
  4. package/dist/aontu.d.ts +3 -3
  5. package/dist/aontu.js +4 -6
  6. package/dist/aontu.js.map +1 -1
  7. package/dist/aontumodel.js +6 -12
  8. package/dist/aontumodel.js.map +1 -1
  9. package/dist/casing.d.ts +5 -0
  10. package/dist/casing.js +90 -0
  11. package/dist/casing.js.map +1 -0
  12. package/dist/cli.d.ts +16 -3
  13. package/dist/cli.js +653 -455
  14. package/dist/cli.js.map +1 -1
  15. package/dist/compat.d.ts +7 -0
  16. package/dist/compat.js +113 -0
  17. package/dist/compat.js.map +1 -0
  18. package/dist/helpdoc.js +5 -5
  19. package/dist/helpdoc.js.map +1 -1
  20. package/dist/hints.js +14 -5
  21. package/dist/hints.js.map +1 -1
  22. package/dist/lang.d.ts +1 -0
  23. package/dist/lang.js +8 -0
  24. package/dist/lang.js.map +1 -1
  25. package/dist/lsp.d.ts +1 -1
  26. package/dist/mcp.js +0 -36
  27. package/dist/mcp.js.map +1 -1
  28. package/dist/mod.d.ts +18 -3
  29. package/dist/mod.js +117 -46
  30. package/dist/mod.js.map +1 -1
  31. package/dist/pkg-net.d.ts +227 -0
  32. package/dist/pkg-net.js +1598 -0
  33. package/dist/pkg-net.js.map +1 -0
  34. package/dist/pkg-zip.d.ts +8 -0
  35. package/dist/pkg-zip.js +196 -0
  36. package/dist/pkg-zip.js.map +1 -0
  37. package/dist/pkg.d.ts +159 -0
  38. package/dist/pkg.js +767 -0
  39. package/dist/pkg.js.map +1 -0
  40. package/dist/profile.d.ts +9 -0
  41. package/dist/profile.js +28 -0
  42. package/dist/profile.js.map +1 -0
  43. package/dist/report-sarif.js +1 -1
  44. package/dist/report-sarif.js.map +1 -1
  45. package/dist/subsume.d.ts +3 -1
  46. package/dist/subsume.js +2 -0
  47. package/dist/subsume.js.map +1 -1
  48. package/dist/trace.d.ts +21 -0
  49. package/dist/trace.js +107 -0
  50. package/dist/trace.js.map +1 -0
  51. package/dist/tsconfig.tsbuildinfo +1 -1
  52. package/dist/unify.js +1 -6
  53. package/dist/unify.js.map +1 -1
  54. package/dist/val/CmpFuncVal.d.ts +2 -0
  55. package/dist/val/CmpFuncVal.js +47 -13
  56. package/dist/val/CmpFuncVal.js.map +1 -1
  57. package/dist/val/NomFuncVal.js +11 -13
  58. package/dist/val/NomFuncVal.js.map +1 -1
  59. package/dist/val/ReferFuncVal.js +3 -0
  60. package/dist/val/ReferFuncVal.js.map +1 -1
  61. package/package.json +1 -1
  62. package/skill/SKILL.md +3 -3
  63. package/skill/error-codes.md +4 -4
  64. package/skill/examples.md +4 -4
  65. package/skill/grammar-card.md +1 -1
  66. package/skill/init/check.sh +2 -2
  67. package/skill/tasks.md +14 -12
  68. package/src/agentsmd.ts +3 -3
  69. package/src/aontu.ts +3 -8
  70. package/src/aontumodel.ts +6 -12
  71. package/src/casing.ts +95 -0
  72. package/src/cli.ts +758 -495
  73. package/src/compat.ts +148 -0
  74. package/src/helpdoc.ts +5 -5
  75. package/src/hints.ts +14 -7
  76. package/src/lang.ts +9 -1
  77. package/src/mcp.ts +0 -39
  78. package/src/mod.ts +142 -51
  79. package/src/pkg-net.ts +1945 -0
  80. package/src/pkg-zip.ts +228 -0
  81. package/src/pkg.ts +1018 -0
  82. package/src/profile.ts +42 -0
  83. package/src/report-sarif.ts +1 -1
  84. package/src/subsume.ts +2 -2
  85. package/src/trace.ts +157 -0
  86. package/src/unify.ts +1 -6
  87. package/src/val/CmpFuncVal.ts +66 -20
  88. package/src/val/NomFuncVal.ts +3 -5
  89. package/src/val/ReferFuncVal.ts +4 -0
  90. package/dist/lower.d.ts +0 -23
  91. package/dist/lower.js +0 -528
  92. package/dist/lower.js.map +0 -1
  93. package/dist/mod-tool.d.ts +0 -58
  94. package/dist/mod-tool.js +0 -403
  95. package/dist/mod-tool.js.map +0 -1
  96. package/dist/render.d.ts +0 -53
  97. package/dist/render.js +0 -432
  98. package/dist/render.js.map +0 -1
  99. package/src/lower.ts +0 -586
  100. package/src/mod-tool.ts +0 -561
  101. package/src/render.ts +0 -580
package/dist/cli.js CHANGED
@@ -13,9 +13,13 @@ exports.runRelations = runRelations;
13
13
  exports.runReaches = runReaches;
14
14
  exports.runView = runView;
15
15
  exports.runJsonSchema = runJsonSchema;
16
- exports.runRender = runRender;
17
16
  exports.runTemplate = runTemplate;
18
- exports.runMod = runMod;
17
+ exports.runTrace = runTrace;
18
+ exports.runPkg = runPkg;
19
+ exports.runModel = runModel;
20
+ exports.runPackageVerb = runPackageVerb;
21
+ exports.pkgToolOptions = pkgToolOptions;
22
+ exports.serveUntilInterrupted = serveUntilInterrupted;
19
23
  exports.runHash = runHash;
20
24
  exports.runGet = runGet;
21
25
  exports.runHelp = runHelp;
@@ -40,13 +44,14 @@ const node_path_1 = require("node:path");
40
44
  const node_os_1 = require("node:os");
41
45
  const node_readline_1 = require("node:readline");
42
46
  const aontu_1 = require("./aontu");
47
+ const trace_1 = require("./trace");
43
48
  const template_1 = require("./template");
44
- const mcp_1 = require("./mcp");
45
49
  const report_sarif_1 = require("./report-sarif");
46
50
  const lsp_server_1 = require("./lsp-server");
47
51
  const mcp_server_1 = require("./mcp-server");
48
52
  const jsonschema_1 = require("./jsonschema");
49
- const mod_tool_1 = require("./mod-tool");
53
+ const pkg_1 = require("./pkg");
54
+ const pkg_net_1 = require("./pkg-net");
50
55
  const mod_1 = require("./mod");
51
56
  const vet_1 = require("./vet");
52
57
  const reach_1 = require("./reach");
@@ -67,16 +72,23 @@ const HELP = `Usage: aontu [options] [file]
67
72
  aontu view <kind> [options] <file>...
68
73
  aontu view --views <path> [--check] [options] <file>
69
74
  aontu jsonschema [--at <path>] [--strict] [options] <file>
70
- aontu render [--at <path>] [--profile <file>]... [--unit <path>]
71
- [--stdout | --out <dir> | --check <dir> | --coverage]
72
- [--coverage-at <path>] [--strict] <file>
73
75
  aontu template [--resugar] [--check] [--marker <token>]
74
76
  [--profile <file>] <file>
77
+ aontu trace [--at <path>] [--format json] [--marker <token>]
78
+ [--profile <file>] <file>
75
79
  aontu hash [options] <file>
76
- aontu mod tidy|verify|vendor|manifest [options] [dir]
77
- aontu get <path> [options] <file>
78
- aontu why <path> [options] <file>
79
- aontu set <path>=<value>... --entry <file> --overlay <file>
80
+ aontu sync [--frozen] [options] [dir]
81
+ aontu add <pkg>[@<version>] [options] [dir]
82
+ aontu get <pkg>[@<version>] [options] [dir]
83
+ aontu remove <pkg> [options] [dir]
84
+ aontu why <pkg> [options] [dir]
85
+ aontu publish [--yes] [--to <dir>] [options] [dir]
86
+ aontu pkg tidy|verify|vendor|manifest|refreeze|tree|outdated|serve
87
+ [options] [dir]
88
+ aontu pkg keygen <file>
89
+ aontu model get <path> [options] <file>
90
+ aontu model why <path> [options] <file>
91
+ aontu model set <path>=<value>... --entry <file> --overlay <file>
80
92
  aontu allow --role <role> [--at <path>] <roles-file> <path>...
81
93
  aontu agentsmd [--write <AGENTS.md>] [--depth <n>] <file>
82
94
  aontu fmt [-w|-l|--check|-d|--lint] [--marker <token>]
@@ -145,18 +157,57 @@ Options:
145
157
  named format keeps its meaning, and .js stays
146
158
  refused. Every verb takes it
147
159
 
148
- Mod options:
160
+ Package verbs (a module is imported; a package is published):
161
+ sync Make the project correct: resolve by minimum version
162
+ selection, fetch what is missing, write aontu_meta/pkg-lock.aon,
163
+ vendor, verify. --frozen refuses to change the lockfile
164
+ add Take on a dependency the project does not have, then sync;
165
+ refuses one it has and names get
166
+ get Add a dependency or raise its minimum, then sync. Without a
167
+ version, the newest version outside the cooldown
168
+ remove Drop a dependency, then sync
169
+ why Why is this package in the closure: every path of
170
+ requirements that reaches it
171
+ publish Publish this package. A dry run without --yes; --to <dir>
172
+ writes the repository layout into a directory instead
173
+
174
+ Package options:
149
175
  --format <f> text (default) or json
150
- --against <dir> manifest: a prior version's module tree, to gate on
176
+ --frozen sync: fail if the lockfile would change (the CI mode)
177
+ --yes publish: send it (the default is a dry run)
178
+ --to <dir> publish: write the read-path layout into a directory
179
+ --key <file> publish: the Ed25519 private key (PKCS#8 PEM) that
180
+ signs the manifest, for the key provider
181
+ --token <file> publish: the forge's OIDC token, read from a file
182
+ --base <url> the repository to read from (repeatable; overrides
183
+ pkg.aon repo.base)
184
+ --write <url> publish: the write path (overrides pkg.aon repo.write)
185
+ --against <dir> manifest: a prior version's tree, to gate on
186
+ --upstream <u> serve: fetch on miss from this repository (repeatable)
187
+ --listen <a> serve: the address to listen on (default 127.0.0.1:8017)
151
188
 
152
- Mod subcommands:
153
- tidy Resolve the module closure by minimum version selection and
154
- rewrite aontu_meta/mod-lock.aon in canonical form
155
- verify Check every locked module still means what the lockfile
156
- pins, and change nothing (the CI gate; tidy rewrites)
189
+ pkg subcommands (the rest of the package operations):
190
+ tidy Resolve the closure by minimum version selection and
191
+ rewrite aontu_meta/pkg-lock.aon in canonical form
192
+ verify Check every locked package still is and still means what
193
+ the lockfile pins, bytes before meaning, and change nothing
194
+ (the CI gate; tidy rewrites)
157
195
  vendor Materialise the locked closure into aontu_meta/vendor/
158
- manifest Print the OCI artifact a publish would push, gated on the
159
- breaking check against --against
196
+ manifest Print the manifest a publish would send, gated on the
197
+ compatibility check against --against
198
+ refreeze Recompute every canon pin and nothing else, after a
199
+ canonical-form change in the engine
200
+ tree The locked closure as a graph
201
+ outdated What could move, and what would move with it
202
+ serve Serve a repository directory; with --upstream, a caching
203
+ proxy in front of another repository
204
+ keygen Write a new Ed25519 signing key (PKCS#8 PEM) to a file,
205
+ once, and print the signer id a consumer names
206
+
207
+ model subcommands (one document, interrogated or edited):
208
+ get What the document says at a path
209
+ why Every contribution to the value at a path
210
+ set Append a path-flattened conjunct to an overlay
160
211
 
161
212
  Vet options:
162
213
  --at <path> Validate against this path of the schema ($.a.b)
@@ -235,21 +286,21 @@ Hash options:
235
286
  Hash exit codes: 0 hashed, 2 usage, 4 the document does not stand up
236
287
  on its own.
237
288
 
238
- Get options:
289
+ Model get options:
239
290
  -c, --canon Canonical-form fragment (default: generated JSON)
240
291
  --keys Keys at the node, one per line
241
292
  --types Shape view: concrete leaves lifted to their kinds
242
293
  --depth <n> Structure to depth n; deeper nodes render as top
243
294
  --format <f> text (default) or json
244
295
 
245
- Get exit codes: 0 rendered, 1 the path names nothing, 2 usage, 4 the
246
- document does not stand up on its own.
296
+ Model get exit codes: 0 rendered, 1 the path names nothing, 2 usage, 4
297
+ the document does not stand up on its own.
247
298
 
248
- Why options:
299
+ Model why options:
249
300
  --format <f> text (default) or json
250
301
 
251
- Why exit codes mirror get's: 0 explained, 1 the path names nothing,
252
- 2 usage, 4 the document does not stand up on its own.
302
+ Model why exit codes mirror get's: 0 explained, 1 the path names
303
+ nothing, 2 usage, 4 the document does not stand up on its own.
253
304
 
254
305
  View kinds: doc, lattice, tree, matrix, graph, layer, sets, layers,
255
306
  ladder, poset (the poset takes several files). The figure goes to stdout, the loss
@@ -317,37 +368,11 @@ View exit codes: 0 rendered, 1 --check mismatch or lossy under
317
368
  --strict, 2 usage or --max-rows exceeded, 4 the document does not stand
318
369
  up on its own, or a relation, root or path that names nothing.
319
370
 
320
- Render options:
321
- --at <path> Render the value at this path ($.a.b); the root by
322
- default
323
- --profile <file> A profile document, aontu: render: Lang: {lang,
324
- ...}, vetted against aontu:render; repeatable,
325
- one per language
326
- --unit <path> Render only the unit with this path
327
- --stdout One unit's bytes and nothing else (with --unit when
328
- the instance has several)
329
- --out <dir> Write every unit below dir, or nothing; never deletes
330
- --check <dir> Compare every unit with dir/<path>; drift is listed
331
- --coverage Report what the render read and what it did not:
332
- model paths no output consumed, and rendered
333
- declarations no rule produced. Writes nothing
334
- --coverage-at <p> Measure coverage under this path only, instead of
335
- the document root
336
- --strict Refuse the opaque escapes (a text declaration, a raw
337
- block)
338
- --format <f> text (default) or json, the whole report; json
339
- carries the dispatch trace, one entry per emitted
340
- piece
341
-
342
- Render exit codes: 0 rendered, 1 lossy under --strict or drift under
343
- --check, 2 usage or I/O (a refused unit path included), 4 the document
344
- does not stand up or the instance is not aontu:code.
345
-
346
- A render entry file whose extension is not .aon is a TEMPLATE: a
347
- generator in the target's own syntax, whose marker lines carry aontu
348
- and whose other lines are output. It is desugared before it is
349
- evaluated, and a language the table does not know names its marker with
350
- --marker, or declares it once in a profile file that --profile reads.
371
+ A template entry file whose extension is not .aon is a GENERATOR: a
372
+ document in the target's own syntax, whose marker lines carry aontu and
373
+ whose other lines are output. It is desugared before it is evaluated,
374
+ and a language the table does not know names its marker with --marker,
375
+ or declares it once in a profile file that --profile reads.
351
376
 
352
377
  Template options:
353
378
  --resugar The file is the canonical aontu; print the template
@@ -365,7 +390,7 @@ every other line is a line of output.
365
390
 
366
391
  Template exit codes: 0 written, 1 --check drift, 2 usage or I/O.
367
392
 
368
- Set options:
393
+ Model set options:
369
394
  --entry <file> The document the change is checked against
370
395
  --overlay <file> The file the change is appended to (created if
371
396
  absent; not written when the change does not hold)
@@ -381,7 +406,7 @@ Set options:
381
406
  --format <f> text (default) or json
382
407
 
383
408
  Set exit codes are vet's verdict classes: 0 valid, 1 invalid (the
384
- change contradicts a pinned value -- aontu why locates it, and
409
+ change contradicts a pinned value -- aontu model why locates it, and
385
410
  --in-place rewrites it), 2 usage, 3 incomplete, 4 the entry does not
386
411
  stand up on its own.
387
412
 
@@ -457,11 +482,11 @@ Fmt options:
457
482
  The fmt verb prints one document in the agreed form; with no file it
458
483
  reads standard input. Several files need one of the options above.
459
484
 
460
- A file whose extension is not .aon is a GENERATOR, as it is for render:
461
- the aontu its marker lines carry is formatted, the marker stands at the
462
- left margin with the aontu indented after it, and every line of output
463
- is held on a line of its own. A file with no marker line in it is
464
- another language's, and is refused.
485
+ A file whose extension is not .aon is a GENERATOR, as it is for
486
+ template: the aontu its marker lines carry is formatted, the marker
487
+ stands at the left margin with the aontu indented after it, and every
488
+ line of output is held on a line of its own. A file with no marker line
489
+ in it is another language's, and is refused.
465
490
 
466
491
  Fmt exit codes: 0 formatted or clean, 1 a --check file would change or
467
492
  a --strict finding, 2 usage, 4 a document does not parse.
@@ -583,7 +608,7 @@ function trustOpts(trust, entryRoot) {
583
608
  return { ...text, trustWarn: makeTrustWarn(), trustWarnRoot: entryRoot };
584
609
  }
585
610
  }
586
- function takeTrust(argv) {
611
+ function takeTrust(argv, io = PROCESS_IO) {
587
612
  const rest = [];
588
613
  let trust = { kind: 'system-warn', textExt: [] };
589
614
  let textExt = [];
@@ -592,7 +617,7 @@ function takeTrust(argv) {
592
617
  if ('--trust' === arg) {
593
618
  const parsed = null == argv[i + 1] ? undefined : parseTrustArg(argv[++i]);
594
619
  if (null == parsed) {
595
- process.stderr.write('aontu: --trust needs system, none, or root[:dir]\n');
620
+ io.err('aontu: --trust needs system, none, or root[:dir]\n');
596
621
  return undefined;
597
622
  }
598
623
  trust = parsed;
@@ -600,7 +625,7 @@ function takeTrust(argv) {
600
625
  else if ('--include-root' === arg) {
601
626
  const dir = argv[++i];
602
627
  if (null == dir) {
603
- process.stderr.write('aontu: --include-root needs a directory\n');
628
+ io.err('aontu: --include-root needs a directory\n');
604
629
  return undefined;
605
630
  }
606
631
  trust = { kind: 'root', dir, textExt };
@@ -608,7 +633,7 @@ function takeTrust(argv) {
608
633
  else if ('--text-ext' === arg) {
609
634
  const list = null == argv[i + 1] ? undefined : parseTextExt(argv[++i]);
610
635
  if (null == list) {
611
- process.stderr.write('aontu: --text-ext needs extensions, without dots' +
636
+ io.err('aontu: --text-ext needs extensions, without dots' +
612
637
  ' (--text-ext md,sql)\n');
613
638
  return undefined;
614
639
  }
@@ -621,7 +646,7 @@ function takeTrust(argv) {
621
646
  return { argv: rest, trust: { ...trust, textExt } };
622
647
  }
623
648
  // `md,sql` or `.md,.sql` -- the dot is accepted and dropped, because a
624
- // reader who has just written `@"notes.txt"` reaches for one. An empty
649
+ // reader who has just written `@"./notes.txt"` reaches for one. An empty
625
650
  // element, or anything that is not an extension, is a usage error
626
651
  // rather than a silently ignored word: a flag that quietly does
627
652
  // nothing is how a document ends up refused with no reason visible.
@@ -1762,74 +1787,134 @@ const VIEW_USAGE_CODES = [
1762
1787
  'view_at_required', 'view_sets_required', 'view_group_required',
1763
1788
  'view_document_shape', 'view_style_profile', 'view_style_unknown',
1764
1789
  ];
1765
- const MOD_HELP = 'aontu mod tidy|verify|vendor|manifest [dir] (try --help)';
1766
- function runMod(argv) {
1767
- const rest = [];
1768
- let format = 'text';
1769
- let against;
1790
+ const PKG_HELP = 'aontu pkg tidy|verify|vendor|manifest|refreeze|tree|outdated|serve [dir] | keygen <file> (try --help)';
1791
+ const PKG_SUBS = [
1792
+ 'tidy', 'verify', 'vendor', 'manifest', 'refreeze', 'tree', 'outdated',
1793
+ 'serve', 'keygen',
1794
+ ];
1795
+ function parsePkgArgs(argv, verb, io) {
1796
+ const out = {
1797
+ rest: [], format: 'text', frozen: false, yes: false, base: [], upstream: [],
1798
+ };
1799
+ const value = (name, i) => {
1800
+ const v = argv[i];
1801
+ if (null == v) {
1802
+ io.err(`aontu: ${name} needs a value\n`);
1803
+ }
1804
+ return v;
1805
+ };
1770
1806
  for (let i = 0; i < argv.length; i++) {
1771
1807
  const arg = argv[i];
1772
1808
  if ('-h' === arg || '--help' === arg) {
1773
- process.stdout.write(HELP);
1774
- return 0;
1809
+ io.out(HELP);
1810
+ return undefined;
1775
1811
  }
1776
1812
  if ('--format' === arg) {
1777
1813
  const f = argv[++i];
1778
1814
  if ('text' !== f && 'json' !== f) {
1779
- process.stderr.write('aontu: --format needs text or json\n');
1780
- return 2;
1815
+ io.err('aontu: --format needs text or json\n');
1816
+ return undefined;
1781
1817
  }
1782
- format = f;
1818
+ out.format = f;
1783
1819
  }
1784
- else if ('--against' === arg) {
1785
- const a = argv[++i];
1786
- if (null == a) {
1787
- process.stderr.write('aontu: --against needs a module directory\n');
1788
- return 2;
1820
+ else if ('--frozen' === arg) {
1821
+ out.frozen = true;
1822
+ }
1823
+ else if ('--yes' === arg) {
1824
+ out.yes = true;
1825
+ }
1826
+ else if ('--against' === arg || '--to' === arg || '--key' === arg ||
1827
+ '--token' === arg || '--write' === arg || '--listen' === arg) {
1828
+ const v = value(arg, ++i);
1829
+ if (null == v) {
1830
+ return undefined;
1831
+ }
1832
+ out['--against' === arg ? 'against' : '--to' === arg ? 'to' :
1833
+ '--key' === arg ? 'key' : '--token' === arg ? 'token' :
1834
+ '--write' === arg ? 'write' : 'listen'] = v;
1835
+ }
1836
+ else if ('--base' === arg || '--upstream' === arg) {
1837
+ const v = value(arg, ++i);
1838
+ if (null == v) {
1839
+ return undefined;
1789
1840
  }
1790
- against = a;
1841
+ out['--base' === arg ? 'base' : 'upstream'].push(v);
1791
1842
  }
1792
1843
  else if (arg.startsWith('-')) {
1793
- process.stderr.write(`aontu: unknown mod option ${arg} (try --help)\n`);
1794
- return 2;
1844
+ io.err(`aontu: unknown ${verb} option ${arg} (try --help)\n`);
1845
+ return undefined;
1795
1846
  }
1796
1847
  else {
1797
- rest.push(arg);
1848
+ out.rest.push(arg);
1798
1849
  }
1799
1850
  }
1800
- const sub = rest[0];
1801
- const dir = rest[1] ?? '.';
1802
- if ('get' === sub || 'publish' === sub) {
1803
- process.stderr.write('aontu: mod ' + sub + ' needs a registry client, which this build ' +
1804
- 'does not ship; vendor the module by hand and run ' +
1805
- "'aontu mod tidy'\n");
1851
+ return out;
1852
+ }
1853
+ // A verb that finds the older layout names the current one, once, and
1854
+ // reads nothing from it.
1855
+ function nameOldLayout(dir, io) {
1856
+ const old = ['aon_vendor', 'mod-lock.aon', 'mod.aon',
1857
+ (0, node_path_1.join)(mod_1.META_DIR, 'mod-lock.aon')].filter((f) => (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, f)));
1858
+ if (0 < old.length) {
1859
+ io.err('aontu: ' + old.join(', ') + ' belong to an older layout: the package ' +
1860
+ 'file is ' + mod_1.PKG_FILE + ', the lockfile ' + (0, node_path_1.join)(mod_1.META_DIR, mod_1.LOCK_FILE) +
1861
+ ' and the vendor tree ' + (0, node_path_1.join)(mod_1.META_DIR, 'vendor') + '; rename ' +
1862
+ mod_1.PKG_FILE + '\'s `mod` block to `pkg`, then run aontu sync\n');
1863
+ }
1864
+ }
1865
+ function runPkg(argv, servers) {
1866
+ const io = servers.io ?? PROCESS_IO;
1867
+ const trusted = takeTrust(argv, io);
1868
+ if (null == trusted) {
1806
1869
  return 2;
1807
1870
  }
1808
- if (!MOD_SUBS.includes(sub) || 2 < rest.length) {
1809
- process.stderr.write(`aontu: mod needs tidy, verify, vendor or manifest\n${MOD_HELP}\n`);
1871
+ const args = parsePkgArgs(trusted.argv, 'pkg', io);
1872
+ if (null == args) {
1873
+ return '-h' === trusted.argv[0] || trusted.argv.includes('--help') ? 0 : 2;
1874
+ }
1875
+ const trust = trusted.trust;
1876
+ const sub = args.rest[0];
1877
+ const dir = args.rest[1] ?? '.';
1878
+ if (!PKG_SUBS.includes(sub) || 2 < args.rest.length) {
1879
+ io.err(`aontu: pkg needs one of ${PKG_SUBS.join(', ')}\n${PKG_HELP}\n`);
1810
1880
  return 2;
1811
1881
  }
1812
- if ((0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'aon_vendor')) || (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'mod-lock.aon'))) {
1813
- process.stderr.write('aontu: aon_vendor/ and mod-lock.aon now live under aontu_meta/: ' +
1814
- 'move them, or run aontu mod tidy and aontu mod vendor\n');
1882
+ if ('keygen' === sub) {
1883
+ if (2 !== args.rest.length) {
1884
+ io.err('aontu: pkg keygen needs the file to write\naontu pkg keygen <file>\n');
1885
+ return 2;
1886
+ }
1887
+ const made = (0, pkg_net_1.keygen)(args.rest[1]);
1888
+ if (null != made.refused) {
1889
+ io.err('aontu: ' + made.refused + '\n');
1890
+ return 2;
1891
+ }
1892
+ io.out('signer: ' + made.signer + '\n');
1893
+ return 0;
1815
1894
  }
1816
- // `--against` gates a manifest and means nothing to the other two;
1895
+ nameOldLayout(dir, io);
1896
+ // `--against` gates a manifest and means nothing to the others;
1817
1897
  // accepting it there would say it had been honoured.
1818
- if (null != against && 'manifest' !== sub) {
1819
- process.stderr.write('aontu: --against is a manifest option\n');
1898
+ if (null != args.against && 'manifest' !== sub) {
1899
+ io.err('aontu: --against is a manifest option\n');
1820
1900
  return 2;
1821
1901
  }
1822
- const report = 'tidy' === sub ? (0, mod_tool_1.modTidy)(dir, modToolOptions()) :
1823
- 'verify' === sub ? (0, mod_tool_1.modVerify)(dir, modToolOptions()) :
1824
- 'vendor' === sub ? (0, mod_tool_1.modVendor)(dir, modToolOptions()) :
1825
- (0, mod_tool_1.modManifest)(dir, modToolOptions(), against);
1826
- process.stdout.write(('json' === format ?
1827
- (0, aontu_1.exactJSON)({ aontu: { version: version(), verb: 'mod ' + sub }, ...report }, 2) :
1828
- modText(sub, report)) + '\n');
1829
- return MOD_EXIT[report.verdict];
1902
+ const opts = pkgToolOptions(trust, (0, node_path_1.resolve)(dir));
1903
+ if ('outdated' === sub || 'serve' === sub) {
1904
+ return runPkgNet(sub, dir, args, opts, servers, io);
1905
+ }
1906
+ const report = 'tidy' === sub ? (0, pkg_1.pkgTidy)(dir, opts) :
1907
+ 'verify' === sub ? (0, pkg_1.pkgVerify)(dir, opts) :
1908
+ 'vendor' === sub ? (0, pkg_1.pkgVendor)(dir, opts) :
1909
+ 'refreeze' === sub ? (0, pkg_1.pkgRefreeze)(dir, opts) :
1910
+ 'tree' === sub ? (0, pkg_1.pkgTree)(dir, opts) :
1911
+ (0, pkg_1.pkgManifest)(dir, opts, args.against);
1912
+ io.out(('json' === args.format ?
1913
+ (0, aontu_1.exactJSON)({ aontu: { version: version(), verb: 'pkg ' + sub }, ...report }, 2) :
1914
+ pkgText(sub, report)) + '\n');
1915
+ return PKG_EXIT[report.verdict];
1830
1916
  }
1831
- const MOD_SUBS = ['tidy', 'verify', 'vendor', 'manifest'];
1832
- const MOD_EXIT = {
1917
+ const PKG_EXIT = {
1833
1918
  ok: 0,
1834
1919
  missing: 1,
1835
1920
  mismatch: 1,
@@ -1837,17 +1922,28 @@ const MOD_EXIT = {
1837
1922
  // nothing to check, which is a refusal and not a pass.
1838
1923
  unlocked: 1,
1839
1924
  breaking: 1,
1925
+ frozen: 1,
1926
+ refused: 1,
1927
+ outdated: 1,
1928
+ sent: 0,
1929
+ 'dry-run': 0,
1930
+ current: 0,
1840
1931
  undecided: 3,
1841
1932
  error: 4,
1842
1933
  };
1843
1934
  // The tooling's evaluator: the same standalone evaluation the module
1844
1935
  // resolver verifies with (ts/src/mod.ts), and for the same reason —
1845
1936
  // only the engine can say what a module MEANS.
1846
- function modToolOptions() {
1937
+ function pkgToolOptions(trust, entryRoot) {
1938
+ const opts = verbOpts(trust, entryRoot);
1939
+ // The user cache lives outside any confinement root, so a confined
1940
+ // run reads the vendor tree only -- as the evaluator's own module
1941
+ // leg already does when a root is set.
1942
+ const rooted = null != opts.trust?.include?.root;
1847
1943
  return {
1848
- cache: (0, mod_1.modCacheDir)(),
1944
+ ...(rooted ? {} : { cache: (0, mod_1.modCacheDir)() }),
1849
1945
  eval: (src, path) => {
1850
- const a0 = new aontu_1.Aontu();
1946
+ const a0 = new aontu_1.Aontu(opts);
1851
1947
  const ctx = a0.ctx({ collect: true });
1852
1948
  const val = a0.unify(src, { path }, ctx);
1853
1949
  return {
@@ -1855,24 +1951,54 @@ function modToolOptions() {
1855
1951
  hash: (0, aontu_1.canonHash)(val),
1856
1952
  canon: val.canon,
1857
1953
  // The same question `aontu hash` asks before it will answer:
1858
- // did this document stand up ON ITS OWN? See ModToolEval.
1954
+ // did this document stand up ON ITS OWN? See PkgToolEval.
1859
1955
  ok: 0 === ctx.err.length && true !== val.isNil,
1860
1956
  };
1861
1957
  },
1862
1958
  };
1863
1959
  }
1864
- function modText(sub, report) {
1960
+ function pkgLockLines(entries) {
1961
+ return entries.map((e) => e.key + ' ' + e.v + ' ' + e.canon);
1962
+ }
1963
+ // The renderers verify, tidy and sync share, as the Go port's do.
1964
+ function pkgMismatchLines(mismatched) {
1965
+ return mismatched.map((m) => 'canon' === m.pin ?
1966
+ m.key + ': pinned ' + m.want + ' but the store means ' +
1967
+ ('' === m.got ? 'nothing (it does not evaluate)' : m.got) :
1968
+ m.key + ': pinned ' + m.pin + ' ' + m.want + ' but the store holds ' + m.got);
1969
+ }
1970
+ // NOT a fetch: the package may well be sitting in the store. What is
1971
+ // absent is the PIN, and only a sync writes one.
1972
+ function pkgUnlockedLines(unlocked) {
1973
+ return unlocked.map((key) => key + ': not in the lockfile (run: aontu sync)');
1974
+ }
1975
+ function pkgForbiddenLines(forbidden) {
1976
+ return forbidden.map((f) => f + ': not admitted in a package');
1977
+ }
1978
+ function pkgText(sub, report) {
1865
1979
  const lines = ['verdict: ' + report.verdict];
1866
1980
  if ('manifest' === sub) {
1867
- if ('' !== report.mod) {
1868
- lines.push(report.mod + ' ' + report.version);
1869
- lines.push('config: ' + report.config);
1870
- }
1871
- for (const key of Object.keys(report.annotations).sort()) {
1872
- lines.push(key + ': ' + report.annotations[key]);
1873
- }
1874
- for (const file of report.files) {
1875
- lines.push('layer: ' + file);
1981
+ const m = report.manifest;
1982
+ if (null != m) {
1983
+ lines.push(m.package + ' ' + m.version + ' ' + m.publish);
1984
+ lines.push('archive: ' + m.archive.digest + ' (' + m.archive.files.length +
1985
+ ' files, ' + m.archive.size + ' bytes)');
1986
+ for (const mod of m.modules) {
1987
+ lines.push('module: ' + mod.path + ' ' + mod.main + ' ' + mod.canon);
1988
+ }
1989
+ for (const key of Object.keys(m.deps).sort(keyorder_1.cmpCodePoint)) {
1990
+ lines.push('dep: ' + key + ' ' + m.deps[key].v +
1991
+ (null == m.deps[key].pkg ? '' : ' (' + m.deps[key].pkg + ')'));
1992
+ }
1993
+ for (const v of m.retract ?? []) {
1994
+ lines.push('retract: ' + v);
1995
+ }
1996
+ if (null != m.moved) {
1997
+ lines.push('moved: ' + m.moved);
1998
+ }
1999
+ for (const f of m.archive.files) {
2000
+ lines.push('file: ' + f.path + ' ' + f.digest + ' ' + f.size);
2001
+ }
1876
2002
  }
1877
2003
  for (const f of report.findings) {
1878
2004
  lines.push(f.path + ': ' + f.message);
@@ -1880,44 +2006,281 @@ function modText(sub, report) {
1880
2006
  for (const miss of report.missing) {
1881
2007
  lines.push(miss + ': missing');
1882
2008
  }
2009
+ lines.push(...pkgForbiddenLines(report.forbidden));
1883
2010
  return lines.join('\n');
1884
2011
  }
1885
2012
  if ('verify' === sub) {
1886
- for (const mod of report.verified) {
1887
- lines.push(mod + ': verified');
2013
+ for (const key of report.verified) {
2014
+ lines.push(key + ': verified');
1888
2015
  }
1889
- for (const m of report.mismatched) {
1890
- lines.push(m.mod + ': pinned ' + m.want + ' but the store means ' +
1891
- ('' === m.got ? 'nothing (it does not evaluate)' : m.got));
2016
+ lines.push(...pkgMismatchLines(report.mismatched), ...pkgUnlockedLines(report.unlocked));
2017
+ for (const miss of report.missing) {
2018
+ lines.push(miss + ': not fetched (run: aontu sync)');
1892
2019
  }
1893
- // NOT a fetch: the module may well be sitting in the store. What
1894
- // is absent is the PIN, and only a tidy writes one.
1895
- for (const mod of report.unlocked) {
1896
- lines.push(mod + ': not in the lockfile (run: aontu mod tidy)');
2020
+ return lines.join('\n');
2021
+ }
2022
+ if ('refreeze' === sub) {
2023
+ for (const r of report.repinned) {
2024
+ lines.push(r.key + ': ' + r.from + ' -> ' + r.to);
2025
+ }
2026
+ for (const key of report.unchanged) {
2027
+ lines.push(key + ': unchanged');
2028
+ }
2029
+ for (const bad of report.unevaluable) {
2030
+ lines.push(bad + ': does not evaluate on its own; nothing to pin');
1897
2031
  }
1898
2032
  for (const miss of report.missing) {
1899
- lines.push(miss + ': not fetched (run: aontu mod get)');
2033
+ lines.push(miss + ': not fetched (run: aontu sync)');
1900
2034
  }
1901
2035
  return lines.join('\n');
1902
2036
  }
1903
- const done = 'tidy' === sub ? report.lock : report.vendored;
1904
- for (const item of done) {
1905
- lines.push('tidy' === sub ?
1906
- item.mod + ' ' + item.v + ' ' + item.canon : '' + item);
2037
+ if ('tree' === sub) {
2038
+ const byKey = new Map(report.nodes.map((n) => [n.key, n]));
2039
+ const seen = new Set();
2040
+ const walk = (key, depth) => {
2041
+ const node = byKey.get(key);
2042
+ const again = seen.has(key);
2043
+ seen.add(key);
2044
+ lines.push(' '.repeat(depth) + key +
2045
+ (null == node || '' === node.v ? '' : ' ' + node.v) +
2046
+ (again ? ' (above)' : null == node ? ' (not locked)' : ''));
2047
+ if (again || null == node) {
2048
+ return;
2049
+ }
2050
+ for (const dep of node.deps) {
2051
+ walk(dep, depth + 1);
2052
+ }
2053
+ };
2054
+ walk(report.root, 0);
2055
+ for (const miss of report.missing) {
2056
+ lines.push(miss + ': not fetched (run: aontu sync)');
2057
+ }
2058
+ return lines.join('\n');
1907
2059
  }
1908
- // A module that is PRESENT but does not stand up. Named separately
2060
+ const done = 'tidy' === sub ? pkgLockLines(report.lock) : report.vendored;
2061
+ lines.push(...done);
2062
+ // A package that is PRESENT but does not stand up. Named separately
1909
2063
  // from a missing one because the repair is different: a fetch cannot
1910
- // help, the module itself has to be fixed (or its own dependencies
2064
+ // help, the package itself has to be fixed (or its own dependencies
1911
2065
  // vendored beside it). Before the missing tail, as the Go port's
1912
2066
  // shared renderer orders them.
1913
2067
  for (const bad of report.unevaluable ?? []) {
1914
2068
  lines.push(bad + ': does not evaluate on its own; nothing to pin');
1915
2069
  }
2070
+ lines.push(...pkgForbiddenLines(report.forbidden ?? []));
1916
2071
  for (const miss of report.missing) {
1917
- lines.push(miss + ': not fetched (run: aontu mod get)');
2072
+ lines.push(miss + ': not fetched (run: aontu sync)');
1918
2073
  }
1919
2074
  return lines.join('\n');
1920
2075
  }
2076
+ // The two `pkg` subcommands that reach a repository.
2077
+ async function runPkgNet(sub, dir, args, opts, servers, io) {
2078
+ if ('serve' === sub) {
2079
+ const served = await (0, pkg_net_1.startServe)({
2080
+ dir: (0, node_path_1.resolve)(dir), upstream: args.upstream,
2081
+ listen: args.listen ?? '127.0.0.1:8017', http: servers.http(),
2082
+ });
2083
+ io.out('serving ' + (0, node_path_1.resolve)(dir) + ' at ' + served.url + '\n' +
2084
+ args.upstream.map((u) => 'upstream: ' + u + '\n').join(''));
2085
+ await servers.serve(served);
2086
+ await served.close();
2087
+ return 0;
2088
+ }
2089
+ if (null == opts.cache) {
2090
+ io.err(NO_CACHE);
2091
+ return 2;
2092
+ }
2093
+ const report = await (0, pkg_net_1.pkgOutdated)(dir, opts, servers.http(), { base: netBase(args) });
2094
+ io.out(pkgReportText('pkg outdated', args.format, report) + '\n');
2095
+ return PKG_EXIT[report.verdict];
2096
+ }
2097
+ const NO_CACHE = 'aontu: this verb reads and writes the user cache, which a ' +
2098
+ 'confined run (--trust root) does not reach and this host does not name ' +
2099
+ '(set XDG_CACHE_HOME or HOME)\n';
2100
+ function netBase(args) {
2101
+ return 0 === args.base.length ? undefined : args.base;
2102
+ }
2103
+ const PACKAGE_VERBS = ['sync', 'add', 'get', 'remove', 'why', 'publish'];
2104
+ const PACKAGE_HELP = {
2105
+ sync: 'aontu sync [--frozen] [dir] (try --help)',
2106
+ add: 'aontu add <pkg> [dir] (try --help)',
2107
+ get: 'aontu get <pkg>[@<version>] [dir] (try --help)',
2108
+ remove: 'aontu remove <pkg> [dir] (try --help)',
2109
+ why: 'aontu why <pkg> [dir] (try --help)',
2110
+ publish: 'aontu publish [--yes] [--to <dir>] [--key <file>] [dir] (try --help)',
2111
+ };
2112
+ // The top-level package verbs (ADR-039 part 5). Every one but `why`
2113
+ // reaches a repository, so every one runs behind the seam.
2114
+ async function runPackageVerb(verb, argv, servers) {
2115
+ const io = servers.io ?? PROCESS_IO;
2116
+ const trusted = takeTrust(argv, io);
2117
+ if (null == trusted) {
2118
+ return 2;
2119
+ }
2120
+ const args = parsePkgArgs(trusted.argv, verb, io);
2121
+ if (null == args) {
2122
+ return '-h' === trusted.argv[0] || trusted.argv.includes('--help') ? 0 : 2;
2123
+ }
2124
+ const wantsPkg = 'sync' !== verb && 'publish' !== verb;
2125
+ const dir = args.rest[wantsPkg ? 1 : 0] ?? '.';
2126
+ if (args.rest.length > (wantsPkg ? 2 : 1) || (wantsPkg && 0 === args.rest.length)) {
2127
+ io.err('aontu: ' + verb + (wantsPkg ? ' needs a package' : ' takes a directory') +
2128
+ '\n' + PACKAGE_HELP[verb] + '\n');
2129
+ return 2;
2130
+ }
2131
+ nameOldLayout(dir, io);
2132
+ const opts = pkgToolOptions(trusted.trust, (0, node_path_1.resolve)(dir));
2133
+ const http = servers.http();
2134
+ if ('why' === verb) {
2135
+ const report = (0, pkg_net_1.pkgWhy)(dir, opts, args.rest[0]);
2136
+ io.out(pkgReportText('why', args.format, report) + '\n');
2137
+ return PKG_EXIT[report.verdict];
2138
+ }
2139
+ if ('publish' === verb) {
2140
+ for (const f of [args.key, args.token]) {
2141
+ if (null != f && !(0, node_fs_1.existsSync)(f)) {
2142
+ io.err('aontu: cannot read ' + f + '\n');
2143
+ return 2;
2144
+ }
2145
+ }
2146
+ if (true === args.yes && null == args.key) {
2147
+ io.err('aontu: publish --yes needs --key <file>, the Ed25519 key that signs\n');
2148
+ return 2;
2149
+ }
2150
+ const report = await (0, pkg_net_1.pkgPublish)(dir, opts, http, {
2151
+ yes: args.yes, to: null == args.to ? undefined : (0, node_path_1.resolve)(args.to), key: args.key,
2152
+ token: args.token, against: args.against, base: netBase(args), write: args.write,
2153
+ });
2154
+ io.out(pkgReportText('publish', args.format, report) + '\n');
2155
+ return PKG_EXIT[report.verdict];
2156
+ }
2157
+ if (null == opts.cache) {
2158
+ io.err(NO_CACHE);
2159
+ return 2;
2160
+ }
2161
+ const net = { base: netBase(args) };
2162
+ const report = 'sync' === verb ? await (0, pkg_net_1.pkgSync)(dir, opts, http, { ...net, frozen: args.frozen }) :
2163
+ 'remove' === verb ? await (0, pkg_net_1.pkgRemove)(dir, opts, http, args.rest[0], net) :
2164
+ await (0, pkg_net_1.pkgGet)(dir, opts, http, args.rest[0], { ...net, mode: verb });
2165
+ if ('string' === typeof report) {
2166
+ io.err('aontu: ' + report + '\n');
2167
+ return 2;
2168
+ }
2169
+ io.out(pkgReportText(verb, args.format, report) + '\n');
2170
+ return PKG_EXIT[report.verdict];
2171
+ }
2172
+ function pkgReportText(verb, fmt, report) {
2173
+ return 'json' === fmt ?
2174
+ (0, aontu_1.exactJSON)({ aontu: { version: version(), verb }, ...report }, 2) :
2175
+ pkgNetText(verb, report);
2176
+ }
2177
+ function pkgNetText(verb, report) {
2178
+ const lines = ['verdict: ' + report.verdict];
2179
+ const tail = () => {
2180
+ for (const e of report.events ?? []) {
2181
+ lines.push(e.code + ': ' + e.message);
2182
+ }
2183
+ if (null != report.refusal) {
2184
+ lines.push('refused: ' + report.refusal.code + ': ' + report.refusal.message);
2185
+ }
2186
+ };
2187
+ if ('why' === verb) {
2188
+ const why = report;
2189
+ for (const p of why.paths) {
2190
+ lines.push(p.join(' -> '));
2191
+ }
2192
+ if ('missing' === why.verdict) {
2193
+ lines.push(why.pkg + ': not in the closure');
2194
+ }
2195
+ return lines.join('\n');
2196
+ }
2197
+ if ('publish' === verb) {
2198
+ const pub = report;
2199
+ lines.push(...pkgText('manifest', pub).split('\n').slice(1));
2200
+ if (null != pub.digest) {
2201
+ lines.push('digest: ' + pub.digest);
2202
+ }
2203
+ if (null != pub.signer) {
2204
+ lines.push('signer: ' + pub.signer);
2205
+ }
2206
+ if (null != pub.against) {
2207
+ lines.push('against: ' + pub.against);
2208
+ }
2209
+ if (null != pub.to) {
2210
+ lines.push('to: ' + pub.to);
2211
+ }
2212
+ if (null != pub.write) {
2213
+ lines.push('write: ' + pub.write + pkg_net_1.PUBLISH_PATH);
2214
+ }
2215
+ if ('dry-run' === pub.verdict) {
2216
+ lines.push('dry run: nothing sent (add --yes)');
2217
+ }
2218
+ if ('sent' === pub.verdict) {
2219
+ lines.push('sent');
2220
+ }
2221
+ tail();
2222
+ return lines.join('\n');
2223
+ }
2224
+ if ('pkg outdated' === verb) {
2225
+ const out = report;
2226
+ for (const e of out.locked) {
2227
+ if (0 < (0, pkg_1.versionCompare)(e.newest, e.v)) {
2228
+ lines.push(e.key + ' ' + e.v + ' -> ' + e.newest);
2229
+ for (const m of e.moves) {
2230
+ lines.push(' ' + m);
2231
+ }
2232
+ }
2233
+ else {
2234
+ lines.push(e.key + ' ' + e.v + ': current');
2235
+ }
2236
+ if (null != e.retracted) {
2237
+ lines.push(e.key + ' ' + e.v + ': retracted by ' + e.retracted);
2238
+ }
2239
+ }
2240
+ tail();
2241
+ return lines.join('\n');
2242
+ }
2243
+ const sync = report;
2244
+ if (null != sync.change) {
2245
+ lines.push('change: ' + sync.change);
2246
+ }
2247
+ for (const f of sync.fetched) {
2248
+ lines.push('fetched: ' + f);
2249
+ }
2250
+ lines.push(...pkgLockLines(sync.lock));
2251
+ for (const bad of sync.unevaluable) {
2252
+ lines.push(bad + ': does not evaluate on its own; nothing to pin');
2253
+ }
2254
+ lines.push(...pkgForbiddenLines(sync.forbidden), ...pkgMismatchLines(sync.mismatched), ...pkgUnlockedLines(sync.unlocked));
2255
+ for (const miss of sync.missing) {
2256
+ lines.push(miss + ': not fetched (run: aontu sync)');
2257
+ }
2258
+ for (const c of sync.changes) {
2259
+ lines.push('lockfile would change: ' + c);
2260
+ }
2261
+ tail();
2262
+ return lines.join('\n');
2263
+ }
2264
+ const MODEL_HELP = 'aontu model get|why|set ... (try --help)';
2265
+ // One document, interrogated or edited (ADR-039 part 5).
2266
+ function runModel(argv) {
2267
+ const sub = argv[0];
2268
+ if ('-h' === sub || '--help' === sub) {
2269
+ process.stdout.write(HELP);
2270
+ return 0;
2271
+ }
2272
+ if ('get' === sub) {
2273
+ return runGet(argv.slice(1));
2274
+ }
2275
+ if ('why' === sub) {
2276
+ return runWhy(argv.slice(1));
2277
+ }
2278
+ if ('set' === sub) {
2279
+ return runSet(argv.slice(1));
2280
+ }
2281
+ process.stderr.write(`aontu: model needs get, why or set\n${MODEL_HELP}\n`);
2282
+ return 2;
2283
+ }
1921
2284
  function vacuous(what, why) {
1922
2285
  process.stderr.write(`aontu: ${what}: ${why}\n`);
1923
2286
  }
@@ -1981,6 +2344,108 @@ function runRelations(argv) {
1981
2344
  }
1982
2345
  return RELATIONS_EXIT[report.verdict];
1983
2346
  }
2347
+ const TRACE_HELP = 'aontu trace [--at <path>] [--format json] [--marker <token>] ' +
2348
+ '[--profile <file>] <file>';
2349
+ // WHAT WROTE THIS LINE. Every piece a rule stamped, under the
2350
+ // component tree, with the file it reached, the rule set that wrote it
2351
+ // and the model node the dispatch matched.
2352
+ function runTrace(argv) {
2353
+ const trusted = takeTrust(argv);
2354
+ if (null == trusted) {
2355
+ return 2;
2356
+ }
2357
+ argv = trusted.argv;
2358
+ const trust = trusted.trust;
2359
+ const rest = [];
2360
+ const profileFiles = [];
2361
+ let format = 'text';
2362
+ let at = undefined;
2363
+ let marker = undefined;
2364
+ for (let i = 0; i < argv.length; i++) {
2365
+ const arg = argv[i];
2366
+ if ('-h' === arg || '--help' === arg) {
2367
+ process.stdout.write(HELP);
2368
+ return 0;
2369
+ }
2370
+ if ('--format' === arg) {
2371
+ const f = argv[++i];
2372
+ if ('text' !== f && 'json' !== f) {
2373
+ process.stderr.write('aontu: --format needs text or json\n');
2374
+ return 2;
2375
+ }
2376
+ format = f;
2377
+ }
2378
+ else if ('--at' === arg) {
2379
+ at = argv[++i];
2380
+ if (null == at) {
2381
+ process.stderr.write('aontu: --at needs a path\n');
2382
+ return 2;
2383
+ }
2384
+ }
2385
+ else if ('--marker' === arg) {
2386
+ marker = argv[++i];
2387
+ if (null == marker) {
2388
+ process.stderr.write('aontu: --marker needs a token\n');
2389
+ return 2;
2390
+ }
2391
+ }
2392
+ else if ('--profile' === arg) {
2393
+ const pf = argv[++i];
2394
+ if (null == pf) {
2395
+ process.stderr.write('aontu: --profile needs a file\n');
2396
+ return 2;
2397
+ }
2398
+ profileFiles.push(pf);
2399
+ }
2400
+ else if (arg.startsWith('-')) {
2401
+ process.stderr.write(`aontu: unknown trace option ${arg} (try --help)\n`);
2402
+ return 2;
2403
+ }
2404
+ else {
2405
+ rest.push(arg);
2406
+ }
2407
+ }
2408
+ if (1 !== rest.length) {
2409
+ process.stderr.write(`aontu: trace needs one file\n${TRACE_HELP}\n`);
2410
+ return 2;
2411
+ }
2412
+ let src;
2413
+ try {
2414
+ src = (0, node_fs_1.readFileSync)(rest[0], 'utf8');
2415
+ }
2416
+ catch (err) {
2417
+ process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`);
2418
+ return 2;
2419
+ }
2420
+ const declared = loadProfiles(profileFiles, trust);
2421
+ if ('number' === typeof declared) {
2422
+ return declared;
2423
+ }
2424
+ // A GENERATOR IS AN ENTRY, not a preprocessing step: the file whose
2425
+ // provenance is asked for is the one the author edits.
2426
+ if (!rest[0].endsWith('.aon')) {
2427
+ src = (0, template_1.desugarTemplate)(src, marker ??
2428
+ (0, template_1.markerFromProfiles)(declared, rest[0]) ?? (0, template_1.markerFor)(rest[0]));
2429
+ }
2430
+ const report = (0, trace_1.traceRun)(src, {
2431
+ path: rest[0], at,
2432
+ ...verbOpts(trust, entryRootOf(rest[0])),
2433
+ });
2434
+ if ('error' === report.verdict) {
2435
+ // An error report always carries its findings.
2436
+ const errors = report.errors;
2437
+ process.stderr.write(errors.map(renderFinding).join('\n') + '\n');
2438
+ return 4;
2439
+ }
2440
+ if ('json' === format) {
2441
+ process.stdout.write(JSON.stringify({ trace: report.trace }) + '\n');
2442
+ return 0;
2443
+ }
2444
+ for (const e of report.trace) {
2445
+ process.stdout.write([e.file, e.at, e.node, e.rule].join('\t') + '\n');
2446
+ }
2447
+ return 0;
2448
+ }
1984
2449
  function runReaches(argv) {
1985
2450
  const trusted = takeTrust(argv);
1986
2451
  if (null == trusted) {
@@ -2530,281 +2995,6 @@ function runJsonSchema(argv) {
2530
2995
  return 'error' === report.verdict ? 4 :
2531
2996
  strict && 'lossy' === report.verdict ? 1 : 0;
2532
2997
  }
2533
- const RENDER_HELP = 'aontu render [--at <path>] [--profile <file>]... [--unit <path>] ' +
2534
- '[--stdout | --out <dir> | --check <dir> | --coverage] ' +
2535
- '[--coverage-at <path>] [--strict] [--marker <token>] <file> (try --help)';
2536
- function runRender(argv) {
2537
- const trusted = takeTrust(argv);
2538
- if (null == trusted) {
2539
- return 2;
2540
- }
2541
- argv = trusted.argv;
2542
- const trust = trusted.trust;
2543
- const files = [];
2544
- const profileFiles = [];
2545
- let format = 'text';
2546
- let at = undefined;
2547
- let unit = undefined;
2548
- let out = undefined;
2549
- let check = undefined;
2550
- let toStdout = false;
2551
- let strict = false;
2552
- let coverage = false;
2553
- let coverageAt = undefined;
2554
- let marker = undefined;
2555
- for (let i = 0; i < argv.length; i++) {
2556
- const arg = argv[i];
2557
- if ('-h' === arg || '--help' === arg) {
2558
- process.stdout.write(HELP);
2559
- return 0;
2560
- }
2561
- if ('--format' === arg) {
2562
- const f = argv[++i];
2563
- if ('text' !== f && 'json' !== f) {
2564
- process.stderr.write('aontu: --format needs text or json\n');
2565
- return 2;
2566
- }
2567
- format = f;
2568
- }
2569
- else if ('--at' === arg) {
2570
- at = argv[++i];
2571
- if (null == at) {
2572
- process.stderr.write('aontu: --at needs a path\n');
2573
- return 2;
2574
- }
2575
- }
2576
- else if ('--unit' === arg) {
2577
- unit = argv[++i];
2578
- if (null == unit) {
2579
- process.stderr.write('aontu: --unit needs a unit path\n');
2580
- return 2;
2581
- }
2582
- }
2583
- else if ('--profile' === arg) {
2584
- const pf = argv[++i];
2585
- if (null == pf) {
2586
- process.stderr.write('aontu: --profile needs a file\n');
2587
- return 2;
2588
- }
2589
- profileFiles.push(pf);
2590
- }
2591
- else if ('--out' === arg) {
2592
- out = argv[++i];
2593
- if (null == out) {
2594
- process.stderr.write('aontu: --out needs a directory\n');
2595
- return 2;
2596
- }
2597
- }
2598
- else if ('--check' === arg) {
2599
- check = argv[++i];
2600
- if (null == check) {
2601
- process.stderr.write('aontu: --check needs a directory\n');
2602
- return 2;
2603
- }
2604
- }
2605
- else if ('--stdout' === arg) {
2606
- toStdout = true;
2607
- }
2608
- else if ('--coverage' === arg) {
2609
- coverage = true;
2610
- }
2611
- else if ('--marker' === arg) {
2612
- marker = argv[++i];
2613
- if (null == marker) {
2614
- process.stderr.write('aontu: --marker needs a token\n');
2615
- return 2;
2616
- }
2617
- }
2618
- else if ('--coverage-at' === arg) {
2619
- coverageAt = argv[++i];
2620
- if (null == coverageAt) {
2621
- process.stderr.write('aontu: --coverage-at needs a path\n');
2622
- return 2;
2623
- }
2624
- }
2625
- else if ('--strict' === arg) {
2626
- strict = true;
2627
- }
2628
- else if (arg.startsWith('-')) {
2629
- process.stderr.write(`aontu: unknown render option ${arg} (try --help)\n`);
2630
- return 2;
2631
- }
2632
- else {
2633
- files.push(arg);
2634
- }
2635
- }
2636
- if (1 !== files.length) {
2637
- process.stderr.write(`aontu: render needs one file\n${RENDER_HELP}\n`);
2638
- return 2;
2639
- }
2640
- const modes = [toStdout, undefined !== out, undefined !== check, coverage]
2641
- .filter((on) => on).length;
2642
- if (1 < modes) {
2643
- process.stderr.write('aontu: render takes one of --stdout, --out, --check or --coverage\n');
2644
- return 2;
2645
- }
2646
- // A NARROWER MEASURE NEEDS SOMETHING TO NARROW. `--coverage-at`
2647
- // without `--coverage` asks for a report the run does not compute,
2648
- // and answering silently would be the wrong half of the request.
2649
- if (undefined !== coverageAt && !coverage) {
2650
- process.stderr.write('aontu: --coverage-at needs --coverage\n');
2651
- return 2;
2652
- }
2653
- let src;
2654
- try {
2655
- src = (0, node_fs_1.readFileSync)(files[0], 'utf8');
2656
- }
2657
- catch (err) {
2658
- process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`);
2659
- return 2;
2660
- }
2661
- const loadedProfiles = loadProfiles(profileFiles, trust);
2662
- if ('number' === typeof loadedProfiles) {
2663
- return loadedProfiles;
2664
- }
2665
- const profiles = loadedProfiles;
2666
- if (!files[0].endsWith('.aon')) {
2667
- src = (0, template_1.desugarTemplate)(src, marker ??
2668
- (0, template_1.markerFromProfiles)(profiles, files[0]) ?? (0, template_1.markerFor)(files[0]));
2669
- }
2670
- // A RENDER WITH NO PROFILE PRODUCES NO UNITS, and said so with zero
2671
- // bytes and exit 0. The profile is what maps a model onto a
2672
- // language, so without one there is nothing for the renderer to
2673
- // write -- which is a usable answer only if the caller is told.
2674
- const noProfiles = 0 === profiles.length;
2675
- const report = (0, aontu_1.render)(src, {
2676
- at, unit, strict, profiles, path: files[0],
2677
- coverage, coverageAt,
2678
- // THE JSON REPORT CARRIES THE TRACE (D9), which is what the shape
2679
- // there has always said; a text run computes it only when the
2680
- // coverage report needs it.
2681
- trace: 'json' === format,
2682
- ...verbOpts(trust, entryRootOf(files[0])),
2683
- });
2684
- // Said once, whatever the format: stdout stays the report.
2685
- if ('error' !== report.verdict && 0 === report.units.length) {
2686
- vacuous('nothing was rendered', noProfiles
2687
- ? 'no profile was given, and the document declares none' +
2688
- ' (see aontu help tasks)'
2689
- : 'the document produced no units under this profile');
2690
- }
2691
- if ('json' === format) {
2692
- process.stdout.write((0, aontu_1.exactJSON)({
2693
- aontu: { version: version(), verb: 'render' },
2694
- verdict: report.verdict,
2695
- units: report.units,
2696
- lossy: report.lossy,
2697
- ...(null == report.errors ? {} : { errors: report.errors }),
2698
- ...(null == report.trace ? {} : { trace: report.trace }),
2699
- ...(null == report.coverage ? {} : { coverage: report.coverage }),
2700
- }, 2) + '\n');
2701
- return renderExit(report, 0);
2702
- }
2703
- if ('error' === report.verdict) {
2704
- process.stderr.write(report.errors.map(renderFinding).join('\n') + '\n');
2705
- return renderExit(report, 0);
2706
- }
2707
- let drift = 0;
2708
- if (toStdout) {
2709
- // ONE UNIT'S BYTES AND NOTHING ELSE, so the output can be piped
2710
- // into a formatter or a file.
2711
- if (1 !== report.units.length) {
2712
- process.stderr.write('aontu: --stdout needs exactly one unit, and the instance has ' +
2713
- `${report.units.length}; --unit names one\n`);
2714
- return 2;
2715
- }
2716
- process.stdout.write(report.units[0].text);
2717
- }
2718
- else if (undefined !== out) {
2719
- // EVERY UNIT BELOW <dir>, OR NOTHING: every unit rendered first
2720
- // (the report above), and no file touched unless all did. The
2721
- // directory is realpath-confined; a unit path is already a relative
2722
- // descent (render_path refuses the rest), and the check here is
2723
- // against the symlink inside it. render never deletes.
2724
- for (const u of report.units) {
2725
- if ((0, mcp_1.outsideRoot)(out, (0, node_path_1.resolve)(out, u.path))) {
2726
- process.stderr.write(`aontu: ${u.path} escapes ${out}\n`);
2727
- return 2;
2728
- }
2729
- }
2730
- for (const u of report.units) {
2731
- const full = (0, node_path_1.resolve)(out, u.path);
2732
- try {
2733
- (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(full), { recursive: true });
2734
- (0, node_fs_1.writeFileSync)(full, u.text, 'utf8');
2735
- }
2736
- catch (err) {
2737
- process.stderr.write(`aontu: cannot write ${u.path}: ${err.message}\n`);
2738
- return 2;
2739
- }
2740
- process.stderr.write(`wrote ${u.path}\n`);
2741
- }
2742
- }
2743
- else if (undefined !== check) {
2744
- // RENDER AND COMPARE: a unit whose bytes differ from the file at
2745
- // <dir>/<path>, or whose file is absent, is drift, listed by path.
2746
- // The CI form.
2747
- for (const u of report.units) {
2748
- let have = undefined;
2749
- try {
2750
- have = (0, node_fs_1.readFileSync)((0, node_path_1.resolve)(check, u.path), 'utf8');
2751
- }
2752
- catch {
2753
- // Absent is drift, reported below.
2754
- }
2755
- if (undefined === have) {
2756
- drift++;
2757
- process.stderr.write(`aontu: ${u.path} is missing from ${check}\n`);
2758
- }
2759
- else if (have !== u.text) {
2760
- drift++;
2761
- process.stderr.write(`aontu: ${u.path} differs from the rendered unit\n`);
2762
- }
2763
- }
2764
- }
2765
- else if (coverage) {
2766
- // THE COVERAGE REPORT (P7), one line per finding and a count at
2767
- // the end: dead model first, then the declarations no rule
2768
- // produced. A clean report is the count line alone.
2769
- const cov = report.coverage;
2770
- for (const d of cov.dead) {
2771
- process.stdout.write(`dead: ${d}\n`);
2772
- }
2773
- for (const u of cov.unruled) {
2774
- process.stdout.write(`unruled: ${u.unit} ${u.path}\n`);
2775
- }
2776
- process.stdout.write(`coverage: ${cov.read.length} path(s) read, ${cov.dead.length} ` +
2777
- `no output consumed, ${cov.unruled.length} declaration(s) ` +
2778
- 'no rule produced\n');
2779
- }
2780
- else {
2781
- // THE SUMMARY: one line per unit -- its path, its language and its
2782
- // size -- since several units have no one text to print.
2783
- for (const u of report.units) {
2784
- process.stdout.write(`${u.path}\t${u.lang}\t${u.text.length} bytes\n`);
2785
- }
2786
- }
2787
- for (const l of report.lossy) {
2788
- process.stderr.write(`lossy: ${l.unit} ${l.path} tier ${l.tier} ${l.construct}: ${l.reason}\n`);
2789
- }
2790
- return renderExit(report, drift);
2791
- }
2792
- // D8's exit table over a report: a refused unit path is usage (2), a
2793
- // strict refusal is lossy (1), any other error is the document's (4);
2794
- // drift under --check is 1.
2795
- function renderExit(report, drift) {
2796
- if ('error' === report.verdict) {
2797
- const errors = report.errors;
2798
- if (errors.every((f) => 'render_path' === f.code)) {
2799
- return 2;
2800
- }
2801
- if (errors.every((f) => 'render_strict' === f.code)) {
2802
- return 1;
2803
- }
2804
- return 4;
2805
- }
2806
- return 0 < drift ? 1 : 0;
2807
- }
2808
2998
  const TEMPLATE_HELP = 'aontu template [--resugar] [--check] [--marker <token>] <file> (try --help)';
2809
2999
  function runTemplate(argv) {
2810
3000
  const trusted = takeTrust(argv);
@@ -2896,9 +3086,9 @@ function runTemplate(argv) {
2896
3086
  return 0;
2897
3087
  }
2898
3088
  // The profiles named by --profile, vetted, or the exit code that says
2899
- // why not. A profile is a language declared as data: `render` matches
2900
- // one to a unit by `lang`, and `template` and `fmt` match one to a file
2901
- // by the extensions its `template.ext` names.
3089
+ // why not. A profile is a language declared as data: `template` and
3090
+ // `fmt` match one to a file by the extensions its `template.ext`
3091
+ // names.
2902
3092
  function loadProfiles(profileFiles, trust) {
2903
3093
  const profiles = [];
2904
3094
  const langs = new Map();
@@ -2911,7 +3101,7 @@ function loadProfiles(profileFiles, trust) {
2911
3101
  process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`);
2912
3102
  return 2;
2913
3103
  }
2914
- const loaded = (0, aontu_1.renderProfile)(text, { path: (0, node_path_1.resolve)(pf), ...verbOpts(trust, entryRootOf(pf)) });
3104
+ const loaded = (0, aontu_1.loadProfile)(text, { path: (0, node_path_1.resolve)(pf), ...verbOpts(trust, entryRootOf(pf)) });
2915
3105
  if (undefined !== loaded.errors) {
2916
3106
  process.stderr.write(loaded.errors.map(renderFinding).join('\n') + '\n');
2917
3107
  return 4;
@@ -2995,7 +3185,7 @@ function runHash(argv) {
2995
3185
  process.stdout.write(text + '\n');
2996
3186
  return 0;
2997
3187
  }
2998
- const GET_HELP = 'aontu get <path> <file> (try --help)';
3188
+ const GET_HELP = 'aontu model get <path> <file> (try --help)';
2999
3189
  function runGet(argv) {
3000
3190
  const trusted = takeTrust(argv);
3001
3191
  if (null == trusted) {
@@ -3039,7 +3229,7 @@ function runGet(argv) {
3039
3229
  format = f;
3040
3230
  }
3041
3231
  else if (arg.startsWith('-')) {
3042
- process.stderr.write(`aontu: unknown get option ${arg} (try --help)\n`);
3232
+ process.stderr.write(`aontu: unknown model get option ${arg} (try --help)\n`);
3043
3233
  return 2;
3044
3234
  }
3045
3235
  else {
@@ -3047,7 +3237,7 @@ function runGet(argv) {
3047
3237
  }
3048
3238
  }
3049
3239
  if (2 !== rest.length) {
3050
- process.stderr.write(`aontu: get needs a path and one file\n${GET_HELP}\n`);
3240
+ process.stderr.write(`aontu: model get needs a path and one file\n${GET_HELP}\n`);
3051
3241
  return 2;
3052
3242
  }
3053
3243
  const [path, file] = rest;
@@ -3071,7 +3261,7 @@ function runGet(argv) {
3071
3261
  });
3072
3262
  if ('json' === format) {
3073
3263
  process.stdout.write((0, aontu_1.exactJSON)({
3074
- aontu: { version: version(), verb: 'get' },
3264
+ aontu: { version: version(), verb: 'model get' },
3075
3265
  findings: report.findings,
3076
3266
  ok: report.ok,
3077
3267
  out: report.out,
@@ -3096,7 +3286,7 @@ function runGet(argv) {
3096
3286
  // contributions that met there, each with the site it was written at.
3097
3287
  // The positive twin of the vet report: errors explain what failed to
3098
3288
  // unify, this explains what did.
3099
- const WHY_HELP = 'aontu why <path> <file> (try --help)';
3289
+ const WHY_HELP = 'aontu model why <path> <file> (try --help)';
3100
3290
  function runWhy(argv) {
3101
3291
  const trusted = takeTrust(argv);
3102
3292
  if (null == trusted) {
@@ -3121,7 +3311,7 @@ function runWhy(argv) {
3121
3311
  format = f;
3122
3312
  }
3123
3313
  else if (arg.startsWith('-')) {
3124
- process.stderr.write(`aontu: unknown why option ${arg} (try --help)\n`);
3314
+ process.stderr.write(`aontu: unknown model why option ${arg} (try --help)\n`);
3125
3315
  return 2;
3126
3316
  }
3127
3317
  else {
@@ -3129,7 +3319,7 @@ function runWhy(argv) {
3129
3319
  }
3130
3320
  }
3131
3321
  if (2 !== rest.length) {
3132
- process.stderr.write(`aontu: why needs a path and one file\n${WHY_HELP}\n`);
3322
+ process.stderr.write(`aontu: model why needs a path and one file\n${WHY_HELP}\n`);
3133
3323
  return 2;
3134
3324
  }
3135
3325
  const [path, file] = rest;
@@ -3146,7 +3336,7 @@ function runWhy(argv) {
3146
3336
  });
3147
3337
  if ('json' === format) {
3148
3338
  process.stdout.write((0, aontu_1.exactJSON)({
3149
- aontu: { version: version(), verb: 'why' },
3339
+ aontu: { version: version(), verb: 'model why' },
3150
3340
  findings: report.findings,
3151
3341
  ok: report.ok,
3152
3342
  ...(null == report.record ? {} : { record: report.record }),
@@ -3178,7 +3368,7 @@ function renderWhyText(record) {
3178
3368
  ('literal' === c.role ? '' : ` (${c.role})`);
3179
3369
  })).join('\n');
3180
3370
  }
3181
- const SET_HELP = 'aontu set <path>=<value> --entry <file> --overlay <file> (try --help)';
3371
+ const SET_HELP = 'aontu model set <path>=<value> --entry <file> --overlay <file> (try --help)';
3182
3372
  function runSet(argv) {
3183
3373
  const trusted = takeTrust(argv);
3184
3374
  if (null == trusted) {
@@ -3219,7 +3409,7 @@ function runSet(argv) {
3219
3409
  format = f;
3220
3410
  }
3221
3411
  else if (arg.startsWith('-')) {
3222
- process.stderr.write(`aontu: unknown set option ${arg} (try --help)\n`);
3412
+ process.stderr.write(`aontu: unknown model set option ${arg} (try --help)\n`);
3223
3413
  return 2;
3224
3414
  }
3225
3415
  else {
@@ -3227,7 +3417,7 @@ function runSet(argv) {
3227
3417
  }
3228
3418
  }
3229
3419
  if (0 === assignments.length || null == entry || null == overlayFile) {
3230
- process.stderr.write(`aontu: set needs assignments, --entry and --overlay\n${SET_HELP}\n`);
3420
+ process.stderr.write(`aontu: model set needs assignments, --entry and --overlay\n${SET_HELP}\n`);
3231
3421
  return 2;
3232
3422
  }
3233
3423
  let entrySrc;
@@ -3274,7 +3464,7 @@ function runSet(argv) {
3274
3464
  }
3275
3465
  if ('json' === format) {
3276
3466
  process.stdout.write((0, aontu_1.exactJSON)({
3277
- aontu: { version: version(), verb: 'set' },
3467
+ aontu: { version: version(), verb: 'model set' },
3278
3468
  appended: report.appended,
3279
3469
  findings: report.findings,
3280
3470
  overlay: report.overlay,
@@ -3583,8 +3773,8 @@ function runFmt(argv) {
3583
3773
  }
3584
3774
  else if ('--marker' === arg) {
3585
3775
  // THE MARKER SAYS THE FILE IS A GENERATOR, whatever its
3586
- // extension: `render` and `template` take the same option for
3587
- // the same reason, a language the table has never seen.
3776
+ // extension: `fmt` and `template` take the same option for the
3777
+ // same reason, a language the table has never seen.
3588
3778
  marker = argv[++i];
3589
3779
  if (null == marker) {
3590
3780
  process.stderr.write('aontu: --marker needs a token\n');
@@ -3711,12 +3901,22 @@ function fmtOne(name, src, flags, marker) {
3711
3901
  }
3712
3902
  return flags.check ? 1 : strict;
3713
3903
  }
3904
+ const PROCESS_IO = {
3905
+ out: (s) => void process.stdout.write(s),
3906
+ err: (s) => void process.stderr.write(s),
3907
+ };
3908
+ // `pkg serve` runs until the process is interrupted.
3909
+ function serveUntilInterrupted() {
3910
+ return new Promise((done) => process.once('SIGINT', () => done()));
3911
+ }
3714
3912
  // Excluded: the real pair takes the process stdio, so ts/test/cli.test.ts
3715
3913
  // drives each server through a child process instead.
3716
- /* node:coverage ignore next 4 */
3914
+ /* node:coverage ignore next 6 */
3717
3915
  const SERVERS = {
3718
3916
  lsp: () => void (0, lsp_server_1.main)(),
3719
3917
  mcp: (argv) => void (0, mcp_server_1.main)(undefined, undefined, undefined, undefined, argv),
3918
+ serve: serveUntilInterrupted,
3919
+ http: pkg_net_1.defaultHttp,
3720
3920
  };
3721
3921
  // undefined: the server took the process; a number: an answer the CLI
3722
3922
  // gives itself, --help or a usage error.
@@ -3989,10 +4189,10 @@ function runInit(argv) {
3989
4189
  return 0;
3990
4190
  }
3991
4191
  const KNOWN_VERBS = [
3992
- 'agentsmd', 'allow', 'breaking', 'explain', 'fmt', 'get', 'hash',
3993
- 'help', 'init', 'jsonschema', 'lsp', 'mcp', 'mod', 'reaches',
3994
- 'relations', 'render', 'set', 'subsume', 'template', 'trim', 'vet',
3995
- 'view', 'why',
4192
+ 'add', 'agentsmd', 'allow', 'breaking', 'explain', 'fmt', 'get', 'hash',
4193
+ 'help', 'init', 'jsonschema', 'lsp', 'mcp', 'model', 'pkg', 'publish',
4194
+ 'reaches', 'relations', 'remove', 'subsume', 'sync', 'template', 'trace',
4195
+ 'trim', 'vet', 'view', 'why',
3996
4196
  ];
3997
4197
  exports.KNOWN_VERBS = KNOWN_VERBS;
3998
4198
  // looksLikeVerb reports whether an unreadable argument was meant as a
@@ -4077,17 +4277,18 @@ function main(argv, servers = SERVERS) {
4077
4277
  if ('mcp' === argv[2]) {
4078
4278
  return servers.mcp(argv.slice(3));
4079
4279
  }
4080
- if ('set' === argv[2]) {
4081
- return finish(runSet(argv.slice(3)));
4082
- }
4083
4280
  if ('allow' === argv[2]) {
4084
4281
  return finish(runAllow(argv.slice(3)));
4085
4282
  }
4086
- if ('why' === argv[2]) {
4087
- return finish(runWhy(argv.slice(3)));
4283
+ if ('model' === argv[2]) {
4284
+ return finish(runModel(argv.slice(3)));
4285
+ }
4286
+ if ('pkg' === argv[2]) {
4287
+ const r = runPkg(argv.slice(3), servers);
4288
+ return 'number' === typeof r ? finish(r) : void r.then(finish);
4088
4289
  }
4089
- if ('get' === argv[2]) {
4090
- return finish(runGet(argv.slice(3)));
4290
+ if (PACKAGE_VERBS.includes(argv[2])) {
4291
+ return void runPackageVerb(argv[2], argv.slice(3), servers).then(finish);
4091
4292
  }
4092
4293
  if ('hash' === argv[2]) {
4093
4294
  return finish(runHash(argv.slice(3)));
@@ -4103,21 +4304,18 @@ function main(argv, servers = SERVERS) {
4103
4304
  if ('init' === argv[2]) {
4104
4305
  return finish(runInit(argv.slice(3)));
4105
4306
  }
4106
- if ('mod' === argv[2]) {
4107
- return finish(runMod(argv.slice(3)));
4108
- }
4109
4307
  if ('relations' === argv[2]) {
4110
4308
  return finish(runRelations(argv.slice(3)));
4111
4309
  }
4112
4310
  if ('jsonschema' === argv[2]) {
4113
4311
  return finish(runJsonSchema(argv.slice(3)));
4114
4312
  }
4115
- if ('render' === argv[2]) {
4116
- return finish(runRender(argv.slice(3)));
4117
- }
4118
4313
  if ('template' === argv[2]) {
4119
4314
  return finish(runTemplate(argv.slice(3)));
4120
4315
  }
4316
+ if ('trace' === argv[2]) {
4317
+ return finish(runTrace(argv.slice(3)));
4318
+ }
4121
4319
  if ('reaches' === argv[2]) {
4122
4320
  return finish(runReaches(argv.slice(3)));
4123
4321
  }
@@ -4212,5 +4410,5 @@ function main(argv, servers = SERVERS) {
4212
4410
  else {
4213
4411
  runStdin(mode, format, trust).then((code) => finish(code));
4214
4412
  }
4215
- } /* node:coverage ignore next 20 */
4413
+ } /* node:coverage ignore next 21 */
4216
4414
  //# sourceMappingURL=cli.js.map