aontu 0.64.0 → 0.66.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 (64) hide show
  1. package/README.md +2 -2
  2. package/dist/agentsmd.js +3 -3
  3. package/dist/agentsmd.js.map +1 -1
  4. package/dist/aontu.d.ts +1 -1
  5. package/dist/aontu.js +1 -1
  6. package/dist/cli.d.ts +16 -2
  7. package/dist/cli.js +741 -137
  8. package/dist/cli.js.map +1 -1
  9. package/dist/compat.d.ts +7 -0
  10. package/dist/compat.js +113 -0
  11. package/dist/compat.js.map +1 -0
  12. package/dist/helpdoc.js +5 -5
  13. package/dist/helpdoc.js.map +1 -1
  14. package/dist/hints.js +11 -5
  15. package/dist/hints.js.map +1 -1
  16. package/dist/lang.d.ts +1 -0
  17. package/dist/lang.js +8 -0
  18. package/dist/lang.js.map +1 -1
  19. package/dist/lsp.d.ts +1 -1
  20. package/dist/mod.d.ts +18 -3
  21. package/dist/mod.js +117 -46
  22. package/dist/mod.js.map +1 -1
  23. package/dist/pkg-net.d.ts +227 -0
  24. package/dist/pkg-net.js +1598 -0
  25. package/dist/pkg-net.js.map +1 -0
  26. package/dist/pkg-zip.d.ts +8 -0
  27. package/dist/pkg-zip.js +196 -0
  28. package/dist/pkg-zip.js.map +1 -0
  29. package/dist/pkg.d.ts +159 -0
  30. package/dist/pkg.js +767 -0
  31. package/dist/pkg.js.map +1 -0
  32. package/dist/report-sarif.js +1 -1
  33. package/dist/report-sarif.js.map +1 -1
  34. package/dist/subsume.d.ts +3 -1
  35. package/dist/subsume.js +2 -0
  36. package/dist/subsume.js.map +1 -1
  37. package/dist/tsconfig.tsbuildinfo +1 -1
  38. package/grammar/aontu.gbnf +8 -18
  39. package/grammar/aontu.lark +6 -14
  40. package/grammar/aontu.tmLanguage.json +4 -6
  41. package/package.json +3 -2
  42. package/skill/SKILL.md +3 -3
  43. package/skill/error-codes.md +4 -4
  44. package/skill/examples.md +4 -4
  45. package/skill/grammar-card.md +1 -1
  46. package/skill/init/check.sh +2 -2
  47. package/skill/tasks.md +17 -12
  48. package/src/agentsmd.ts +3 -3
  49. package/src/aontu.ts +1 -1
  50. package/src/cli.ts +851 -151
  51. package/src/compat.ts +148 -0
  52. package/src/helpdoc.ts +5 -5
  53. package/src/hints.ts +11 -7
  54. package/src/lang.ts +9 -1
  55. package/src/mod.ts +142 -51
  56. package/src/pkg-net.ts +1945 -0
  57. package/src/pkg-zip.ts +228 -0
  58. package/src/pkg.ts +1018 -0
  59. package/src/report-sarif.ts +1 -1
  60. package/src/subsume.ts +2 -2
  61. package/dist/mod-tool.d.ts +0 -58
  62. package/dist/mod-tool.js +0 -418
  63. package/dist/mod-tool.js.map +0 -1
  64. package/src/mod-tool.ts +0 -578
package/dist/cli.js CHANGED
@@ -15,7 +15,12 @@ exports.runView = runView;
15
15
  exports.runJsonSchema = runJsonSchema;
16
16
  exports.runTemplate = runTemplate;
17
17
  exports.runTrace = runTrace;
18
- exports.runMod = runMod;
18
+ exports.runRender = runRender;
19
+ exports.runPkg = runPkg;
20
+ exports.runModel = runModel;
21
+ exports.runPackageVerb = runPackageVerb;
22
+ exports.pkgToolOptions = pkgToolOptions;
23
+ exports.serveUntilInterrupted = serveUntilInterrupted;
19
24
  exports.runHash = runHash;
20
25
  exports.runGet = runGet;
21
26
  exports.runHelp = runHelp;
@@ -46,7 +51,8 @@ const report_sarif_1 = require("./report-sarif");
46
51
  const lsp_server_1 = require("./lsp-server");
47
52
  const mcp_server_1 = require("./mcp-server");
48
53
  const jsonschema_1 = require("./jsonschema");
49
- const mod_tool_1 = require("./mod-tool");
54
+ const pkg_1 = require("./pkg");
55
+ const pkg_net_1 = require("./pkg-net");
50
56
  const mod_1 = require("./mod");
51
57
  const vet_1 = require("./vet");
52
58
  const reach_1 = require("./reach");
@@ -71,11 +77,22 @@ const HELP = `Usage: aontu [options] [file]
71
77
  [--profile <file>] <file>
72
78
  aontu trace [--at <path>] [--format json] [--marker <token>]
73
79
  [--profile <file>] <file>
80
+ aontu render [--check] [--at <path>] [--format json]
81
+ [--marker <token>] [--profile <file>]
82
+ <file|folder> <path>
74
83
  aontu hash [options] <file>
75
- aontu mod tidy|verify|vendor|manifest [options] [dir]
76
- aontu get <path> [options] <file>
77
- aontu why <path> [options] <file>
78
- aontu set <path>=<value>... --entry <file> --overlay <file>
84
+ aontu sync [--frozen] [options] [dir]
85
+ aontu add <pkg>[@<version>] [options] [dir]
86
+ aontu get <pkg>[@<version>] [options] [dir]
87
+ aontu remove <pkg> [options] [dir]
88
+ aontu why <pkg> [options] [dir]
89
+ aontu publish [--yes] [--to <dir>] [options] [dir]
90
+ aontu pkg tidy|verify|vendor|manifest|refreeze|tree|outdated|serve
91
+ [options] [dir]
92
+ aontu pkg keygen <file>
93
+ aontu model get <path> [options] <file>
94
+ aontu model why <path> [options] <file>
95
+ aontu model set <path>=<value>... --entry <file> --overlay <file>
79
96
  aontu allow --role <role> [--at <path>] <roles-file> <path>...
80
97
  aontu agentsmd [--write <AGENTS.md>] [--depth <n>] <file>
81
98
  aontu fmt [-w|-l|--check|-d|--lint] [--marker <token>]
@@ -144,18 +161,57 @@ Options:
144
161
  named format keeps its meaning, and .js stays
145
162
  refused. Every verb takes it
146
163
 
147
- Mod options:
164
+ Package verbs (a module is imported; a package is published):
165
+ sync Make the project correct: resolve by minimum version
166
+ selection, fetch what is missing, write aontu_meta/pkg-lock.aon,
167
+ vendor, verify. --frozen refuses to change the lockfile
168
+ add Take on a dependency the project does not have, then sync;
169
+ refuses one it has and names get
170
+ get Add a dependency or raise its minimum, then sync. Without a
171
+ version, the newest version outside the cooldown
172
+ remove Drop a dependency, then sync
173
+ why Why is this package in the closure: every path of
174
+ requirements that reaches it
175
+ publish Publish this package. A dry run without --yes; --to <dir>
176
+ writes the repository layout into a directory instead
177
+
178
+ Package options:
148
179
  --format <f> text (default) or json
149
- --against <dir> manifest: a prior version's module tree, to gate on
180
+ --frozen sync: fail if the lockfile would change (the CI mode)
181
+ --yes publish: send it (the default is a dry run)
182
+ --to <dir> publish: write the read-path layout into a directory
183
+ --key <file> publish: the Ed25519 private key (PKCS#8 PEM) that
184
+ signs the manifest, for the key provider
185
+ --token <file> publish: the forge's OIDC token, read from a file
186
+ --base <url> the repository to read from (repeatable; overrides
187
+ pkg.aon repo.base)
188
+ --write <url> publish: the write path (overrides pkg.aon repo.write)
189
+ --against <dir> manifest: a prior version's tree, to gate on
190
+ --upstream <u> serve: fetch on miss from this repository (repeatable)
191
+ --listen <a> serve: the address to listen on (default 127.0.0.1:8017)
150
192
 
151
- Mod subcommands:
152
- tidy Resolve the module closure by minimum version selection and
153
- rewrite aontu_meta/mod-lock.aon in canonical form
154
- verify Check every locked module still means what the lockfile
155
- pins, and change nothing (the CI gate; tidy rewrites)
193
+ pkg subcommands (the rest of the package operations):
194
+ tidy Resolve the closure by minimum version selection and
195
+ rewrite aontu_meta/pkg-lock.aon in canonical form
196
+ verify Check every locked package still is and still means what
197
+ the lockfile pins, bytes before meaning, and change nothing
198
+ (the CI gate; tidy rewrites)
156
199
  vendor Materialise the locked closure into aontu_meta/vendor/
157
- manifest Print the OCI artifact a publish would push, gated on the
158
- breaking check against --against
200
+ manifest Print the manifest a publish would send, gated on the
201
+ compatibility check against --against
202
+ refreeze Recompute every canon pin and nothing else, after a
203
+ canonical-form change in the engine
204
+ tree The locked closure as a graph
205
+ outdated What could move, and what would move with it
206
+ serve Serve a repository directory; with --upstream, a caching
207
+ proxy in front of another repository
208
+ keygen Write a new Ed25519 signing key (PKCS#8 PEM) to a file,
209
+ once, and print the signer id a consumer names
210
+
211
+ model subcommands (one document, interrogated or edited):
212
+ get What the document says at a path
213
+ why Every contribution to the value at a path
214
+ set Append a path-flattened conjunct to an overlay
159
215
 
160
216
  Vet options:
161
217
  --at <path> Validate against this path of the schema ($.a.b)
@@ -234,21 +290,21 @@ Hash options:
234
290
  Hash exit codes: 0 hashed, 2 usage, 4 the document does not stand up
235
291
  on its own.
236
292
 
237
- Get options:
293
+ Model get options:
238
294
  -c, --canon Canonical-form fragment (default: generated JSON)
239
295
  --keys Keys at the node, one per line
240
296
  --types Shape view: concrete leaves lifted to their kinds
241
297
  --depth <n> Structure to depth n; deeper nodes render as top
242
298
  --format <f> text (default) or json
243
299
 
244
- Get exit codes: 0 rendered, 1 the path names nothing, 2 usage, 4 the
245
- document does not stand up on its own.
300
+ Model get exit codes: 0 rendered, 1 the path names nothing, 2 usage, 4
301
+ the document does not stand up on its own.
246
302
 
247
- Why options:
303
+ Model why options:
248
304
  --format <f> text (default) or json
249
305
 
250
- Why exit codes mirror get's: 0 explained, 1 the path names nothing,
251
- 2 usage, 4 the document does not stand up on its own.
306
+ Model why exit codes mirror get's: 0 explained, 1 the path names
307
+ nothing, 2 usage, 4 the document does not stand up on its own.
252
308
 
253
309
  View kinds: doc, lattice, tree, matrix, graph, layer, sets, layers,
254
310
  ladder, poset (the poset takes several files). The figure goes to stdout, the loss
@@ -338,7 +394,32 @@ every other line is a line of output.
338
394
 
339
395
  Template exit codes: 0 written, 1 --check drift, 2 usage or I/O.
340
396
 
341
- Set options:
397
+ Render options:
398
+ --check Compare what the generator writes with what <path>
399
+ holds, write nothing, and exit 1 on drift
400
+ --at <path> Where the component tree lives in the document
401
+ (default $.out)
402
+ --format json Print the files written, or the drift, as JSON
403
+ --marker <t> The file is a generator, and this is its marker
404
+ (default //-, and #- --- /*- <!--- by extension)
405
+ --profile <f> A profile file, whose template.ext names the
406
+ extensions it marks and template.marker the marker
407
+
408
+ The render verb writes the component tree a generator answers. The
409
+ tree is handed to jostraca, the generator runtime, which writes the
410
+ files: a tree that is one file is written to <path> itself, unless
411
+ <path> is a directory, and any other tree is written below <path>.
412
+ With --check nothing is written and <path> is compared with what the
413
+ generator writes, one "kind: file" line per difference.
414
+ A folder as the generator is a set: every regular file directly in
415
+ it, dotfiles aside, in name order, and their trees are written below
416
+ <path> as one run, so a path two of them claim is refused. A file with
417
+ no marker line in it is refused by name.
418
+
419
+ Render exit codes: 0 written or clean, 1 --check drift, 2 usage or
420
+ I/O, 4 the document does not stand up, or --at names nothing.
421
+
422
+ Model set options:
342
423
  --entry <file> The document the change is checked against
343
424
  --overlay <file> The file the change is appended to (created if
344
425
  absent; not written when the change does not hold)
@@ -354,7 +435,7 @@ Set options:
354
435
  --format <f> text (default) or json
355
436
 
356
437
  Set exit codes are vet's verdict classes: 0 valid, 1 invalid (the
357
- change contradicts a pinned value -- aontu why locates it, and
438
+ change contradicts a pinned value -- aontu model why locates it, and
358
439
  --in-place rewrites it), 2 usage, 3 incomplete, 4 the entry does not
359
440
  stand up on its own.
360
441
 
@@ -556,7 +637,7 @@ function trustOpts(trust, entryRoot) {
556
637
  return { ...text, trustWarn: makeTrustWarn(), trustWarnRoot: entryRoot };
557
638
  }
558
639
  }
559
- function takeTrust(argv) {
640
+ function takeTrust(argv, io = PROCESS_IO) {
560
641
  const rest = [];
561
642
  let trust = { kind: 'system-warn', textExt: [] };
562
643
  let textExt = [];
@@ -565,7 +646,7 @@ function takeTrust(argv) {
565
646
  if ('--trust' === arg) {
566
647
  const parsed = null == argv[i + 1] ? undefined : parseTrustArg(argv[++i]);
567
648
  if (null == parsed) {
568
- process.stderr.write('aontu: --trust needs system, none, or root[:dir]\n');
649
+ io.err('aontu: --trust needs system, none, or root[:dir]\n');
569
650
  return undefined;
570
651
  }
571
652
  trust = parsed;
@@ -573,7 +654,7 @@ function takeTrust(argv) {
573
654
  else if ('--include-root' === arg) {
574
655
  const dir = argv[++i];
575
656
  if (null == dir) {
576
- process.stderr.write('aontu: --include-root needs a directory\n');
657
+ io.err('aontu: --include-root needs a directory\n');
577
658
  return undefined;
578
659
  }
579
660
  trust = { kind: 'root', dir, textExt };
@@ -581,7 +662,7 @@ function takeTrust(argv) {
581
662
  else if ('--text-ext' === arg) {
582
663
  const list = null == argv[i + 1] ? undefined : parseTextExt(argv[++i]);
583
664
  if (null == list) {
584
- process.stderr.write('aontu: --text-ext needs extensions, without dots' +
665
+ io.err('aontu: --text-ext needs extensions, without dots' +
585
666
  ' (--text-ext md,sql)\n');
586
667
  return undefined;
587
668
  }
@@ -594,7 +675,7 @@ function takeTrust(argv) {
594
675
  return { argv: rest, trust: { ...trust, textExt } };
595
676
  }
596
677
  // `md,sql` or `.md,.sql` -- the dot is accepted and dropped, because a
597
- // reader who has just written `@"notes.txt"` reaches for one. An empty
678
+ // reader who has just written `@"./notes.txt"` reaches for one. An empty
598
679
  // element, or anything that is not an extension, is a usage error
599
680
  // rather than a silently ignored word: a flag that quietly does
600
681
  // nothing is how a document ends up refused with no reason visible.
@@ -1735,81 +1816,134 @@ const VIEW_USAGE_CODES = [
1735
1816
  'view_at_required', 'view_sets_required', 'view_group_required',
1736
1817
  'view_document_shape', 'view_style_profile', 'view_style_unknown',
1737
1818
  ];
1738
- const MOD_HELP = 'aontu mod tidy|verify|vendor|manifest [dir] (try --help)';
1739
- function runMod(argv) {
1740
- const trusted = takeTrust(argv);
1741
- if (null == trusted) {
1742
- return 2;
1743
- }
1744
- argv = trusted.argv;
1745
- const trust = trusted.trust;
1746
- const rest = [];
1747
- let format = 'text';
1748
- let against;
1819
+ const PKG_HELP = 'aontu pkg tidy|verify|vendor|manifest|refreeze|tree|outdated|serve [dir] | keygen <file> (try --help)';
1820
+ const PKG_SUBS = [
1821
+ 'tidy', 'verify', 'vendor', 'manifest', 'refreeze', 'tree', 'outdated',
1822
+ 'serve', 'keygen',
1823
+ ];
1824
+ function parsePkgArgs(argv, verb, io) {
1825
+ const out = {
1826
+ rest: [], format: 'text', frozen: false, yes: false, base: [], upstream: [],
1827
+ };
1828
+ const value = (name, i) => {
1829
+ const v = argv[i];
1830
+ if (null == v) {
1831
+ io.err(`aontu: ${name} needs a value\n`);
1832
+ }
1833
+ return v;
1834
+ };
1749
1835
  for (let i = 0; i < argv.length; i++) {
1750
1836
  const arg = argv[i];
1751
1837
  if ('-h' === arg || '--help' === arg) {
1752
- process.stdout.write(HELP);
1753
- return 0;
1838
+ io.out(HELP);
1839
+ return undefined;
1754
1840
  }
1755
1841
  if ('--format' === arg) {
1756
1842
  const f = argv[++i];
1757
1843
  if ('text' !== f && 'json' !== f) {
1758
- process.stderr.write('aontu: --format needs text or json\n');
1759
- return 2;
1844
+ io.err('aontu: --format needs text or json\n');
1845
+ return undefined;
1760
1846
  }
1761
- format = f;
1847
+ out.format = f;
1762
1848
  }
1763
- else if ('--against' === arg) {
1764
- const a = argv[++i];
1765
- if (null == a) {
1766
- process.stderr.write('aontu: --against needs a module directory\n');
1767
- return 2;
1849
+ else if ('--frozen' === arg) {
1850
+ out.frozen = true;
1851
+ }
1852
+ else if ('--yes' === arg) {
1853
+ out.yes = true;
1854
+ }
1855
+ else if ('--against' === arg || '--to' === arg || '--key' === arg ||
1856
+ '--token' === arg || '--write' === arg || '--listen' === arg) {
1857
+ const v = value(arg, ++i);
1858
+ if (null == v) {
1859
+ return undefined;
1768
1860
  }
1769
- against = a;
1861
+ out['--against' === arg ? 'against' : '--to' === arg ? 'to' :
1862
+ '--key' === arg ? 'key' : '--token' === arg ? 'token' :
1863
+ '--write' === arg ? 'write' : 'listen'] = v;
1864
+ }
1865
+ else if ('--base' === arg || '--upstream' === arg) {
1866
+ const v = value(arg, ++i);
1867
+ if (null == v) {
1868
+ return undefined;
1869
+ }
1870
+ out['--base' === arg ? 'base' : 'upstream'].push(v);
1770
1871
  }
1771
1872
  else if (arg.startsWith('-')) {
1772
- process.stderr.write(`aontu: unknown mod option ${arg} (try --help)\n`);
1773
- return 2;
1873
+ io.err(`aontu: unknown ${verb} option ${arg} (try --help)\n`);
1874
+ return undefined;
1774
1875
  }
1775
1876
  else {
1776
- rest.push(arg);
1877
+ out.rest.push(arg);
1777
1878
  }
1778
1879
  }
1779
- const sub = rest[0];
1780
- const dir = rest[1] ?? '.';
1781
- if ('get' === sub || 'publish' === sub) {
1782
- process.stderr.write('aontu: mod ' + sub + ' needs a registry client, which this build ' +
1783
- 'does not ship; vendor the module by hand and run ' +
1784
- "'aontu mod tidy'\n");
1880
+ return out;
1881
+ }
1882
+ // A verb that finds the older layout names the current one, once, and
1883
+ // reads nothing from it.
1884
+ function nameOldLayout(dir, io) {
1885
+ const old = ['aon_vendor', 'mod-lock.aon', 'mod.aon',
1886
+ (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)));
1887
+ if (0 < old.length) {
1888
+ io.err('aontu: ' + old.join(', ') + ' belong to an older layout: the package ' +
1889
+ 'file is ' + mod_1.PKG_FILE + ', the lockfile ' + (0, node_path_1.join)(mod_1.META_DIR, mod_1.LOCK_FILE) +
1890
+ ' and the vendor tree ' + (0, node_path_1.join)(mod_1.META_DIR, 'vendor') + '; rename ' +
1891
+ mod_1.PKG_FILE + '\'s `mod` block to `pkg`, then run aontu sync\n');
1892
+ }
1893
+ }
1894
+ function runPkg(argv, servers) {
1895
+ const io = servers.io ?? PROCESS_IO;
1896
+ const trusted = takeTrust(argv, io);
1897
+ if (null == trusted) {
1785
1898
  return 2;
1786
1899
  }
1787
- if (!MOD_SUBS.includes(sub) || 2 < rest.length) {
1788
- process.stderr.write(`aontu: mod needs tidy, verify, vendor or manifest\n${MOD_HELP}\n`);
1900
+ const args = parsePkgArgs(trusted.argv, 'pkg', io);
1901
+ if (null == args) {
1902
+ return '-h' === trusted.argv[0] || trusted.argv.includes('--help') ? 0 : 2;
1903
+ }
1904
+ const trust = trusted.trust;
1905
+ const sub = args.rest[0];
1906
+ const dir = args.rest[1] ?? '.';
1907
+ if (!PKG_SUBS.includes(sub) || 2 < args.rest.length) {
1908
+ io.err(`aontu: pkg needs one of ${PKG_SUBS.join(', ')}\n${PKG_HELP}\n`);
1789
1909
  return 2;
1790
1910
  }
1791
- 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'))) {
1792
- process.stderr.write('aontu: aon_vendor/ and mod-lock.aon now live under aontu_meta/: ' +
1793
- 'move them, or run aontu mod tidy and aontu mod vendor\n');
1911
+ if ('keygen' === sub) {
1912
+ if (2 !== args.rest.length) {
1913
+ io.err('aontu: pkg keygen needs the file to write\naontu pkg keygen <file>\n');
1914
+ return 2;
1915
+ }
1916
+ const made = (0, pkg_net_1.keygen)(args.rest[1]);
1917
+ if (null != made.refused) {
1918
+ io.err('aontu: ' + made.refused + '\n');
1919
+ return 2;
1920
+ }
1921
+ io.out('signer: ' + made.signer + '\n');
1922
+ return 0;
1794
1923
  }
1795
- // `--against` gates a manifest and means nothing to the other two;
1924
+ nameOldLayout(dir, io);
1925
+ // `--against` gates a manifest and means nothing to the others;
1796
1926
  // accepting it there would say it had been honoured.
1797
- if (null != against && 'manifest' !== sub) {
1798
- process.stderr.write('aontu: --against is a manifest option\n');
1799
- return 2;
1800
- }
1801
- const modopts = modToolOptions(trust, (0, node_path_1.resolve)(dir));
1802
- const report = 'tidy' === sub ? (0, mod_tool_1.modTidy)(dir, modopts) :
1803
- 'verify' === sub ? (0, mod_tool_1.modVerify)(dir, modopts) :
1804
- 'vendor' === sub ? (0, mod_tool_1.modVendor)(dir, modopts) :
1805
- (0, mod_tool_1.modManifest)(dir, modopts, against);
1806
- process.stdout.write(('json' === format ?
1807
- (0, aontu_1.exactJSON)({ aontu: { version: version(), verb: 'mod ' + sub }, ...report }, 2) :
1808
- modText(sub, report)) + '\n');
1809
- return MOD_EXIT[report.verdict];
1810
- }
1811
- const MOD_SUBS = ['tidy', 'verify', 'vendor', 'manifest'];
1812
- const MOD_EXIT = {
1927
+ if (null != args.against && 'manifest' !== sub) {
1928
+ io.err('aontu: --against is a manifest option\n');
1929
+ return 2;
1930
+ }
1931
+ const opts = pkgToolOptions(trust, (0, node_path_1.resolve)(dir));
1932
+ if ('outdated' === sub || 'serve' === sub) {
1933
+ return runPkgNet(sub, dir, args, opts, servers, io);
1934
+ }
1935
+ const report = 'tidy' === sub ? (0, pkg_1.pkgTidy)(dir, opts) :
1936
+ 'verify' === sub ? (0, pkg_1.pkgVerify)(dir, opts) :
1937
+ 'vendor' === sub ? (0, pkg_1.pkgVendor)(dir, opts) :
1938
+ 'refreeze' === sub ? (0, pkg_1.pkgRefreeze)(dir, opts) :
1939
+ 'tree' === sub ? (0, pkg_1.pkgTree)(dir, opts) :
1940
+ (0, pkg_1.pkgManifest)(dir, opts, args.against);
1941
+ io.out(('json' === args.format ?
1942
+ (0, aontu_1.exactJSON)({ aontu: { version: version(), verb: 'pkg ' + sub }, ...report }, 2) :
1943
+ pkgText(sub, report)) + '\n');
1944
+ return PKG_EXIT[report.verdict];
1945
+ }
1946
+ const PKG_EXIT = {
1813
1947
  ok: 0,
1814
1948
  missing: 1,
1815
1949
  mismatch: 1,
@@ -1817,13 +1951,19 @@ const MOD_EXIT = {
1817
1951
  // nothing to check, which is a refusal and not a pass.
1818
1952
  unlocked: 1,
1819
1953
  breaking: 1,
1954
+ frozen: 1,
1955
+ refused: 1,
1956
+ outdated: 1,
1957
+ sent: 0,
1958
+ 'dry-run': 0,
1959
+ current: 0,
1820
1960
  undecided: 3,
1821
1961
  error: 4,
1822
1962
  };
1823
1963
  // The tooling's evaluator: the same standalone evaluation the module
1824
1964
  // resolver verifies with (ts/src/mod.ts), and for the same reason —
1825
1965
  // only the engine can say what a module MEANS.
1826
- function modToolOptions(trust, entryRoot) {
1966
+ function pkgToolOptions(trust, entryRoot) {
1827
1967
  const opts = verbOpts(trust, entryRoot);
1828
1968
  // The user cache lives outside any confinement root, so a confined
1829
1969
  // run reads the vendor tree only -- as the evaluator's own module
@@ -1840,24 +1980,54 @@ function modToolOptions(trust, entryRoot) {
1840
1980
  hash: (0, aontu_1.canonHash)(val),
1841
1981
  canon: val.canon,
1842
1982
  // The same question `aontu hash` asks before it will answer:
1843
- // did this document stand up ON ITS OWN? See ModToolEval.
1983
+ // did this document stand up ON ITS OWN? See PkgToolEval.
1844
1984
  ok: 0 === ctx.err.length && true !== val.isNil,
1845
1985
  };
1846
1986
  },
1847
1987
  };
1848
1988
  }
1849
- function modText(sub, report) {
1989
+ function pkgLockLines(entries) {
1990
+ return entries.map((e) => e.key + ' ' + e.v + ' ' + e.canon);
1991
+ }
1992
+ // The renderers verify, tidy and sync share, as the Go port's do.
1993
+ function pkgMismatchLines(mismatched) {
1994
+ return mismatched.map((m) => 'canon' === m.pin ?
1995
+ m.key + ': pinned ' + m.want + ' but the store means ' +
1996
+ ('' === m.got ? 'nothing (it does not evaluate)' : m.got) :
1997
+ m.key + ': pinned ' + m.pin + ' ' + m.want + ' but the store holds ' + m.got);
1998
+ }
1999
+ // NOT a fetch: the package may well be sitting in the store. What is
2000
+ // absent is the PIN, and only a sync writes one.
2001
+ function pkgUnlockedLines(unlocked) {
2002
+ return unlocked.map((key) => key + ': not in the lockfile (run: aontu sync)');
2003
+ }
2004
+ function pkgForbiddenLines(forbidden) {
2005
+ return forbidden.map((f) => f + ': not admitted in a package');
2006
+ }
2007
+ function pkgText(sub, report) {
1850
2008
  const lines = ['verdict: ' + report.verdict];
1851
2009
  if ('manifest' === sub) {
1852
- if ('' !== report.mod) {
1853
- lines.push(report.mod + ' ' + report.version);
1854
- lines.push('config: ' + report.config);
1855
- }
1856
- for (const key of Object.keys(report.annotations).sort()) {
1857
- lines.push(key + ': ' + report.annotations[key]);
1858
- }
1859
- for (const file of report.files) {
1860
- lines.push('layer: ' + file);
2010
+ const m = report.manifest;
2011
+ if (null != m) {
2012
+ lines.push(m.package + ' ' + m.version + ' ' + m.publish);
2013
+ lines.push('archive: ' + m.archive.digest + ' (' + m.archive.files.length +
2014
+ ' files, ' + m.archive.size + ' bytes)');
2015
+ for (const mod of m.modules) {
2016
+ lines.push('module: ' + mod.path + ' ' + mod.main + ' ' + mod.canon);
2017
+ }
2018
+ for (const key of Object.keys(m.deps).sort(keyorder_1.cmpCodePoint)) {
2019
+ lines.push('dep: ' + key + ' ' + m.deps[key].v +
2020
+ (null == m.deps[key].pkg ? '' : ' (' + m.deps[key].pkg + ')'));
2021
+ }
2022
+ for (const v of m.retract ?? []) {
2023
+ lines.push('retract: ' + v);
2024
+ }
2025
+ if (null != m.moved) {
2026
+ lines.push('moved: ' + m.moved);
2027
+ }
2028
+ for (const f of m.archive.files) {
2029
+ lines.push('file: ' + f.path + ' ' + f.digest + ' ' + f.size);
2030
+ }
1861
2031
  }
1862
2032
  for (const f of report.findings) {
1863
2033
  lines.push(f.path + ': ' + f.message);
@@ -1865,44 +2035,281 @@ function modText(sub, report) {
1865
2035
  for (const miss of report.missing) {
1866
2036
  lines.push(miss + ': missing');
1867
2037
  }
2038
+ lines.push(...pkgForbiddenLines(report.forbidden));
1868
2039
  return lines.join('\n');
1869
2040
  }
1870
2041
  if ('verify' === sub) {
1871
- for (const mod of report.verified) {
1872
- lines.push(mod + ': verified');
2042
+ for (const key of report.verified) {
2043
+ lines.push(key + ': verified');
1873
2044
  }
1874
- for (const m of report.mismatched) {
1875
- lines.push(m.mod + ': pinned ' + m.want + ' but the store means ' +
1876
- ('' === m.got ? 'nothing (it does not evaluate)' : m.got));
2045
+ lines.push(...pkgMismatchLines(report.mismatched), ...pkgUnlockedLines(report.unlocked));
2046
+ for (const miss of report.missing) {
2047
+ lines.push(miss + ': not fetched (run: aontu sync)');
2048
+ }
2049
+ return lines.join('\n');
2050
+ }
2051
+ if ('refreeze' === sub) {
2052
+ for (const r of report.repinned) {
2053
+ lines.push(r.key + ': ' + r.from + ' -> ' + r.to);
1877
2054
  }
1878
- // NOT a fetch: the module may well be sitting in the store. What
1879
- // is absent is the PIN, and only a tidy writes one.
1880
- for (const mod of report.unlocked) {
1881
- lines.push(mod + ': not in the lockfile (run: aontu mod tidy)');
2055
+ for (const key of report.unchanged) {
2056
+ lines.push(key + ': unchanged');
2057
+ }
2058
+ for (const bad of report.unevaluable) {
2059
+ lines.push(bad + ': does not evaluate on its own; nothing to pin');
1882
2060
  }
1883
2061
  for (const miss of report.missing) {
1884
- lines.push(miss + ': not fetched (run: aontu mod get)');
2062
+ lines.push(miss + ': not fetched (run: aontu sync)');
1885
2063
  }
1886
2064
  return lines.join('\n');
1887
2065
  }
1888
- const done = 'tidy' === sub ? report.lock : report.vendored;
1889
- for (const item of done) {
1890
- lines.push('tidy' === sub ?
1891
- item.mod + ' ' + item.v + ' ' + item.canon : '' + item);
2066
+ if ('tree' === sub) {
2067
+ const byKey = new Map(report.nodes.map((n) => [n.key, n]));
2068
+ const seen = new Set();
2069
+ const walk = (key, depth) => {
2070
+ const node = byKey.get(key);
2071
+ const again = seen.has(key);
2072
+ seen.add(key);
2073
+ lines.push(' '.repeat(depth) + key +
2074
+ (null == node || '' === node.v ? '' : ' ' + node.v) +
2075
+ (again ? ' (above)' : null == node ? ' (not locked)' : ''));
2076
+ if (again || null == node) {
2077
+ return;
2078
+ }
2079
+ for (const dep of node.deps) {
2080
+ walk(dep, depth + 1);
2081
+ }
2082
+ };
2083
+ walk(report.root, 0);
2084
+ for (const miss of report.missing) {
2085
+ lines.push(miss + ': not fetched (run: aontu sync)');
2086
+ }
2087
+ return lines.join('\n');
1892
2088
  }
1893
- // A module that is PRESENT but does not stand up. Named separately
2089
+ const done = 'tidy' === sub ? pkgLockLines(report.lock) : report.vendored;
2090
+ lines.push(...done);
2091
+ // A package that is PRESENT but does not stand up. Named separately
1894
2092
  // from a missing one because the repair is different: a fetch cannot
1895
- // help, the module itself has to be fixed (or its own dependencies
2093
+ // help, the package itself has to be fixed (or its own dependencies
1896
2094
  // vendored beside it). Before the missing tail, as the Go port's
1897
2095
  // shared renderer orders them.
1898
2096
  for (const bad of report.unevaluable ?? []) {
1899
2097
  lines.push(bad + ': does not evaluate on its own; nothing to pin');
1900
2098
  }
2099
+ lines.push(...pkgForbiddenLines(report.forbidden ?? []));
1901
2100
  for (const miss of report.missing) {
1902
- lines.push(miss + ': not fetched (run: aontu mod get)');
2101
+ lines.push(miss + ': not fetched (run: aontu sync)');
2102
+ }
2103
+ return lines.join('\n');
2104
+ }
2105
+ // The two `pkg` subcommands that reach a repository.
2106
+ async function runPkgNet(sub, dir, args, opts, servers, io) {
2107
+ if ('serve' === sub) {
2108
+ const served = await (0, pkg_net_1.startServe)({
2109
+ dir: (0, node_path_1.resolve)(dir), upstream: args.upstream,
2110
+ listen: args.listen ?? '127.0.0.1:8017', http: servers.http(),
2111
+ });
2112
+ io.out('serving ' + (0, node_path_1.resolve)(dir) + ' at ' + served.url + '\n' +
2113
+ args.upstream.map((u) => 'upstream: ' + u + '\n').join(''));
2114
+ await servers.serve(served);
2115
+ await served.close();
2116
+ return 0;
2117
+ }
2118
+ if (null == opts.cache) {
2119
+ io.err(NO_CACHE);
2120
+ return 2;
2121
+ }
2122
+ const report = await (0, pkg_net_1.pkgOutdated)(dir, opts, servers.http(), { base: netBase(args) });
2123
+ io.out(pkgReportText('pkg outdated', args.format, report) + '\n');
2124
+ return PKG_EXIT[report.verdict];
2125
+ }
2126
+ const NO_CACHE = 'aontu: this verb reads and writes the user cache, which a ' +
2127
+ 'confined run (--trust root) does not reach and this host does not name ' +
2128
+ '(set XDG_CACHE_HOME or HOME)\n';
2129
+ function netBase(args) {
2130
+ return 0 === args.base.length ? undefined : args.base;
2131
+ }
2132
+ const PACKAGE_VERBS = ['sync', 'add', 'get', 'remove', 'why', 'publish'];
2133
+ const PACKAGE_HELP = {
2134
+ sync: 'aontu sync [--frozen] [dir] (try --help)',
2135
+ add: 'aontu add <pkg> [dir] (try --help)',
2136
+ get: 'aontu get <pkg>[@<version>] [dir] (try --help)',
2137
+ remove: 'aontu remove <pkg> [dir] (try --help)',
2138
+ why: 'aontu why <pkg> [dir] (try --help)',
2139
+ publish: 'aontu publish [--yes] [--to <dir>] [--key <file>] [dir] (try --help)',
2140
+ };
2141
+ // The top-level package verbs (ADR-039 part 5). Every one but `why`
2142
+ // reaches a repository, so every one runs behind the seam.
2143
+ async function runPackageVerb(verb, argv, servers) {
2144
+ const io = servers.io ?? PROCESS_IO;
2145
+ const trusted = takeTrust(argv, io);
2146
+ if (null == trusted) {
2147
+ return 2;
2148
+ }
2149
+ const args = parsePkgArgs(trusted.argv, verb, io);
2150
+ if (null == args) {
2151
+ return '-h' === trusted.argv[0] || trusted.argv.includes('--help') ? 0 : 2;
2152
+ }
2153
+ const wantsPkg = 'sync' !== verb && 'publish' !== verb;
2154
+ const dir = args.rest[wantsPkg ? 1 : 0] ?? '.';
2155
+ if (args.rest.length > (wantsPkg ? 2 : 1) || (wantsPkg && 0 === args.rest.length)) {
2156
+ io.err('aontu: ' + verb + (wantsPkg ? ' needs a package' : ' takes a directory') +
2157
+ '\n' + PACKAGE_HELP[verb] + '\n');
2158
+ return 2;
2159
+ }
2160
+ nameOldLayout(dir, io);
2161
+ const opts = pkgToolOptions(trusted.trust, (0, node_path_1.resolve)(dir));
2162
+ const http = servers.http();
2163
+ if ('why' === verb) {
2164
+ const report = (0, pkg_net_1.pkgWhy)(dir, opts, args.rest[0]);
2165
+ io.out(pkgReportText('why', args.format, report) + '\n');
2166
+ return PKG_EXIT[report.verdict];
2167
+ }
2168
+ if ('publish' === verb) {
2169
+ for (const f of [args.key, args.token]) {
2170
+ if (null != f && !(0, node_fs_1.existsSync)(f)) {
2171
+ io.err('aontu: cannot read ' + f + '\n');
2172
+ return 2;
2173
+ }
2174
+ }
2175
+ if (true === args.yes && null == args.key) {
2176
+ io.err('aontu: publish --yes needs --key <file>, the Ed25519 key that signs\n');
2177
+ return 2;
2178
+ }
2179
+ const report = await (0, pkg_net_1.pkgPublish)(dir, opts, http, {
2180
+ yes: args.yes, to: null == args.to ? undefined : (0, node_path_1.resolve)(args.to), key: args.key,
2181
+ token: args.token, against: args.against, base: netBase(args), write: args.write,
2182
+ });
2183
+ io.out(pkgReportText('publish', args.format, report) + '\n');
2184
+ return PKG_EXIT[report.verdict];
2185
+ }
2186
+ if (null == opts.cache) {
2187
+ io.err(NO_CACHE);
2188
+ return 2;
2189
+ }
2190
+ const net = { base: netBase(args) };
2191
+ const report = 'sync' === verb ? await (0, pkg_net_1.pkgSync)(dir, opts, http, { ...net, frozen: args.frozen }) :
2192
+ 'remove' === verb ? await (0, pkg_net_1.pkgRemove)(dir, opts, http, args.rest[0], net) :
2193
+ await (0, pkg_net_1.pkgGet)(dir, opts, http, args.rest[0], { ...net, mode: verb });
2194
+ if ('string' === typeof report) {
2195
+ io.err('aontu: ' + report + '\n');
2196
+ return 2;
2197
+ }
2198
+ io.out(pkgReportText(verb, args.format, report) + '\n');
2199
+ return PKG_EXIT[report.verdict];
2200
+ }
2201
+ function pkgReportText(verb, fmt, report) {
2202
+ return 'json' === fmt ?
2203
+ (0, aontu_1.exactJSON)({ aontu: { version: version(), verb }, ...report }, 2) :
2204
+ pkgNetText(verb, report);
2205
+ }
2206
+ function pkgNetText(verb, report) {
2207
+ const lines = ['verdict: ' + report.verdict];
2208
+ const tail = () => {
2209
+ for (const e of report.events ?? []) {
2210
+ lines.push(e.code + ': ' + e.message);
2211
+ }
2212
+ if (null != report.refusal) {
2213
+ lines.push('refused: ' + report.refusal.code + ': ' + report.refusal.message);
2214
+ }
2215
+ };
2216
+ if ('why' === verb) {
2217
+ const why = report;
2218
+ for (const p of why.paths) {
2219
+ lines.push(p.join(' -> '));
2220
+ }
2221
+ if ('missing' === why.verdict) {
2222
+ lines.push(why.pkg + ': not in the closure');
2223
+ }
2224
+ return lines.join('\n');
2225
+ }
2226
+ if ('publish' === verb) {
2227
+ const pub = report;
2228
+ lines.push(...pkgText('manifest', pub).split('\n').slice(1));
2229
+ if (null != pub.digest) {
2230
+ lines.push('digest: ' + pub.digest);
2231
+ }
2232
+ if (null != pub.signer) {
2233
+ lines.push('signer: ' + pub.signer);
2234
+ }
2235
+ if (null != pub.against) {
2236
+ lines.push('against: ' + pub.against);
2237
+ }
2238
+ if (null != pub.to) {
2239
+ lines.push('to: ' + pub.to);
2240
+ }
2241
+ if (null != pub.write) {
2242
+ lines.push('write: ' + pub.write + pkg_net_1.PUBLISH_PATH);
2243
+ }
2244
+ if ('dry-run' === pub.verdict) {
2245
+ lines.push('dry run: nothing sent (add --yes)');
2246
+ }
2247
+ if ('sent' === pub.verdict) {
2248
+ lines.push('sent');
2249
+ }
2250
+ tail();
2251
+ return lines.join('\n');
2252
+ }
2253
+ if ('pkg outdated' === verb) {
2254
+ const out = report;
2255
+ for (const e of out.locked) {
2256
+ if (0 < (0, pkg_1.versionCompare)(e.newest, e.v)) {
2257
+ lines.push(e.key + ' ' + e.v + ' -> ' + e.newest);
2258
+ for (const m of e.moves) {
2259
+ lines.push(' ' + m);
2260
+ }
2261
+ }
2262
+ else {
2263
+ lines.push(e.key + ' ' + e.v + ': current');
2264
+ }
2265
+ if (null != e.retracted) {
2266
+ lines.push(e.key + ' ' + e.v + ': retracted by ' + e.retracted);
2267
+ }
2268
+ }
2269
+ tail();
2270
+ return lines.join('\n');
2271
+ }
2272
+ const sync = report;
2273
+ if (null != sync.change) {
2274
+ lines.push('change: ' + sync.change);
2275
+ }
2276
+ for (const f of sync.fetched) {
2277
+ lines.push('fetched: ' + f);
2278
+ }
2279
+ lines.push(...pkgLockLines(sync.lock));
2280
+ for (const bad of sync.unevaluable) {
2281
+ lines.push(bad + ': does not evaluate on its own; nothing to pin');
2282
+ }
2283
+ lines.push(...pkgForbiddenLines(sync.forbidden), ...pkgMismatchLines(sync.mismatched), ...pkgUnlockedLines(sync.unlocked));
2284
+ for (const miss of sync.missing) {
2285
+ lines.push(miss + ': not fetched (run: aontu sync)');
2286
+ }
2287
+ for (const c of sync.changes) {
2288
+ lines.push('lockfile would change: ' + c);
1903
2289
  }
2290
+ tail();
1904
2291
  return lines.join('\n');
1905
2292
  }
2293
+ const MODEL_HELP = 'aontu model get|why|set ... (try --help)';
2294
+ // One document, interrogated or edited (ADR-039 part 5).
2295
+ function runModel(argv) {
2296
+ const sub = argv[0];
2297
+ if ('-h' === sub || '--help' === sub) {
2298
+ process.stdout.write(HELP);
2299
+ return 0;
2300
+ }
2301
+ if ('get' === sub) {
2302
+ return runGet(argv.slice(1));
2303
+ }
2304
+ if ('why' === sub) {
2305
+ return runWhy(argv.slice(1));
2306
+ }
2307
+ if ('set' === sub) {
2308
+ return runSet(argv.slice(1));
2309
+ }
2310
+ process.stderr.write(`aontu: model needs get, why or set\n${MODEL_HELP}\n`);
2311
+ return 2;
2312
+ }
1906
2313
  function vacuous(what, why) {
1907
2314
  process.stderr.write(`aontu: ${what}: ${why}\n`);
1908
2315
  }
@@ -2068,6 +2475,192 @@ function runTrace(argv) {
2068
2475
  }
2069
2476
  return 0;
2070
2477
  }
2478
+ // ---------------------------------------------------------------------
2479
+ // The writer (ADR-040). A generator answers a component tree; this verb
2480
+ // hands it to jostraca, which writes the files below a path or holds
2481
+ // them to it.
2482
+ const RENDER_HELP = 'aontu render [--check] [--at <path>] [--format json] ' +
2483
+ '[--marker <token>] [--profile <file>] <file|folder> <path> (try --help)';
2484
+ // Loaded at the call, so every other verb starts without it.
2485
+ function generatorRuntime() {
2486
+ return require('jostraca');
2487
+ }
2488
+ function isDirectory(path) {
2489
+ return true === (0, node_fs_1.statSync)(path, { throwIfNoEntry: false })?.isDirectory();
2490
+ }
2491
+ async function runRender(argv) {
2492
+ const trusted = takeTrust(argv);
2493
+ if (null == trusted) {
2494
+ return 2;
2495
+ }
2496
+ argv = trusted.argv;
2497
+ const trust = trusted.trust;
2498
+ const rest = [];
2499
+ const profileFiles = [];
2500
+ let format = 'text';
2501
+ let at = undefined;
2502
+ let marker = undefined;
2503
+ let check = false;
2504
+ for (let i = 0; i < argv.length; i++) {
2505
+ const arg = argv[i];
2506
+ if ('-h' === arg || '--help' === arg) {
2507
+ process.stdout.write(HELP);
2508
+ return 0;
2509
+ }
2510
+ if ('--check' === arg) {
2511
+ check = true;
2512
+ }
2513
+ else if ('--format' === arg) {
2514
+ const f = argv[++i];
2515
+ if ('text' !== f && 'json' !== f) {
2516
+ process.stderr.write('aontu: --format needs text or json\n');
2517
+ return 2;
2518
+ }
2519
+ format = f;
2520
+ }
2521
+ else if ('--at' === arg) {
2522
+ at = argv[++i];
2523
+ if (null == at || '' === at) {
2524
+ process.stderr.write('aontu: --at needs a path\n');
2525
+ return 2;
2526
+ }
2527
+ }
2528
+ else if ('--marker' === arg) {
2529
+ marker = argv[++i];
2530
+ if (null == marker || '' === marker) {
2531
+ process.stderr.write('aontu: --marker needs a token\n');
2532
+ return 2;
2533
+ }
2534
+ }
2535
+ else if ('--profile' === arg) {
2536
+ const pf = argv[++i];
2537
+ if (null == pf || '' === pf) {
2538
+ process.stderr.write('aontu: --profile needs a file\n');
2539
+ return 2;
2540
+ }
2541
+ profileFiles.push(pf);
2542
+ }
2543
+ else if (arg.startsWith('-')) {
2544
+ process.stderr.write(`aontu: unknown render option ${arg} (try --help)\n`);
2545
+ return 2;
2546
+ }
2547
+ else {
2548
+ rest.push(arg);
2549
+ }
2550
+ }
2551
+ if (2 !== rest.length) {
2552
+ process.stderr.write(`aontu: render needs a file and a path\n${RENDER_HELP}\n`);
2553
+ return 2;
2554
+ }
2555
+ const [file, dest] = rest;
2556
+ const declared = loadProfiles(profileFiles, trust);
2557
+ if ('number' === typeof declared) {
2558
+ return declared;
2559
+ }
2560
+ // A FOLDER IS A SET OF GENERATORS: every regular file directly in it,
2561
+ // dotfiles aside, in code-point order, written as one tree.
2562
+ let entries;
2563
+ try {
2564
+ entries = (0, node_fs_1.readdirSync)(file, { withFileTypes: true });
2565
+ }
2566
+ catch (err) {
2567
+ entries = undefined;
2568
+ }
2569
+ const set = undefined !== entries;
2570
+ const files = undefined === entries ? [file] :
2571
+ entries.filter((e) => e.isFile() && !e.name.startsWith('.'))
2572
+ .map((e) => e.name).sort(keyorder_1.cmpCodePoint).map((n) => (0, node_path_1.join)(file, n));
2573
+ if (0 === files.length) {
2574
+ process.stderr.write(`aontu: ${file} holds no generator\n`);
2575
+ return 2;
2576
+ }
2577
+ const trees = [];
2578
+ for (const f of files) {
2579
+ let src;
2580
+ try {
2581
+ src = (0, node_fs_1.readFileSync)(f, 'utf8');
2582
+ }
2583
+ catch (err) {
2584
+ process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`);
2585
+ return 2;
2586
+ }
2587
+ if (!/[.](aon|aontu)$/.test(f)) {
2588
+ const mark = marker ?? (0, template_1.markerFromProfiles)(declared, f) ?? (0, template_1.markerFor)(f);
2589
+ if (!(0, template_1.templateOutputs)(src, mark).some((out) => !out)) {
2590
+ process.stderr.write(`aontu: ${f} carries no ${mark} marker line, ` +
2591
+ 'so there is no aontu in it to render\n');
2592
+ return 2;
2593
+ }
2594
+ src = (0, template_1.desugarTemplate)(src, mark);
2595
+ }
2596
+ const report = (0, aontu_1.get)(src, at ?? '$.out', {
2597
+ view: 'json', path: f, ...verbOpts(trust, entryRootOf(f)),
2598
+ });
2599
+ if (!report.ok) {
2600
+ process.stderr.write(report.findings.map(renderFinding).join('\n') + '\n');
2601
+ return 4;
2602
+ }
2603
+ const tree = JSON.parse(report.out);
2604
+ // A `File` without a name is refused: the runtime ports disagree
2605
+ // about it.
2606
+ if ('File' === tree?.cmp && 'string' !== typeof tree.props?.name) {
2607
+ process.stderr.write(`aontu: ${f}: the file at ${at ?? '$.out'} has no name\n`);
2608
+ return 4;
2609
+ }
2610
+ trees.push(tree);
2611
+ }
2612
+ // ONE FILE GOES TO THE PATH ITSELF, unless the path is a directory; a
2613
+ // set is written below the path whatever its trees are.
2614
+ let folder = dest;
2615
+ const tree = set ? trees.flatMap((t) => Array.isArray(t) ? t : [t]) : trees[0];
2616
+ if (!set && 'File' === tree?.cmp && !isDirectory(dest)) {
2617
+ tree.props.name = (0, node_path_1.basename)(dest);
2618
+ folder = (0, node_path_1.dirname)(dest);
2619
+ }
2620
+ const { cmpTree, Jostraca } = generatorRuntime();
2621
+ let root;
2622
+ try {
2623
+ root = cmpTree(tree, { raw: true });
2624
+ }
2625
+ catch (err) {
2626
+ process.stderr.write(`aontu: ${file}: ${err.message}\n`);
2627
+ return 4;
2628
+ }
2629
+ const runtime = Jostraca();
2630
+ try {
2631
+ if (check) {
2632
+ const res = await runtime.check({ folder }, root);
2633
+ const drift = res.drift.map((d) => ({ kind: d.kind, path: d.path }));
2634
+ if ('json' === format) {
2635
+ process.stdout.write((0, aontu_1.exactJSON)({
2636
+ aontu: { version: version(), verb: 'render' },
2637
+ verdict: 0 === drift.length ? 'ok' : 'drift',
2638
+ checked: res.checked,
2639
+ drift,
2640
+ }, 2) + '\n');
2641
+ }
2642
+ else {
2643
+ for (const d of drift) {
2644
+ process.stdout.write(`${d.kind}: ${d.path}\n`);
2645
+ }
2646
+ }
2647
+ return 0 === drift.length ? 0 : 1;
2648
+ }
2649
+ const res = await runtime.generate({ folder }, root);
2650
+ if ('json' === format) {
2651
+ process.stdout.write((0, aontu_1.exactJSON)({
2652
+ aontu: { version: version(), verb: 'render' },
2653
+ verdict: 'ok',
2654
+ files: res.files,
2655
+ }, 2) + '\n');
2656
+ }
2657
+ return 0;
2658
+ }
2659
+ catch (err) {
2660
+ process.stderr.write(`aontu: ${err.message}\n`);
2661
+ return 2;
2662
+ }
2663
+ }
2071
2664
  function runReaches(argv) {
2072
2665
  const trusted = takeTrust(argv);
2073
2666
  if (null == trusted) {
@@ -2807,7 +3400,7 @@ function runHash(argv) {
2807
3400
  process.stdout.write(text + '\n');
2808
3401
  return 0;
2809
3402
  }
2810
- const GET_HELP = 'aontu get <path> <file> (try --help)';
3403
+ const GET_HELP = 'aontu model get <path> <file> (try --help)';
2811
3404
  function runGet(argv) {
2812
3405
  const trusted = takeTrust(argv);
2813
3406
  if (null == trusted) {
@@ -2851,7 +3444,7 @@ function runGet(argv) {
2851
3444
  format = f;
2852
3445
  }
2853
3446
  else if (arg.startsWith('-')) {
2854
- process.stderr.write(`aontu: unknown get option ${arg} (try --help)\n`);
3447
+ process.stderr.write(`aontu: unknown model get option ${arg} (try --help)\n`);
2855
3448
  return 2;
2856
3449
  }
2857
3450
  else {
@@ -2859,7 +3452,7 @@ function runGet(argv) {
2859
3452
  }
2860
3453
  }
2861
3454
  if (2 !== rest.length) {
2862
- process.stderr.write(`aontu: get needs a path and one file\n${GET_HELP}\n`);
3455
+ process.stderr.write(`aontu: model get needs a path and one file\n${GET_HELP}\n`);
2863
3456
  return 2;
2864
3457
  }
2865
3458
  const [path, file] = rest;
@@ -2883,7 +3476,7 @@ function runGet(argv) {
2883
3476
  });
2884
3477
  if ('json' === format) {
2885
3478
  process.stdout.write((0, aontu_1.exactJSON)({
2886
- aontu: { version: version(), verb: 'get' },
3479
+ aontu: { version: version(), verb: 'model get' },
2887
3480
  findings: report.findings,
2888
3481
  ok: report.ok,
2889
3482
  out: report.out,
@@ -2908,7 +3501,7 @@ function runGet(argv) {
2908
3501
  // contributions that met there, each with the site it was written at.
2909
3502
  // The positive twin of the vet report: errors explain what failed to
2910
3503
  // unify, this explains what did.
2911
- const WHY_HELP = 'aontu why <path> <file> (try --help)';
3504
+ const WHY_HELP = 'aontu model why <path> <file> (try --help)';
2912
3505
  function runWhy(argv) {
2913
3506
  const trusted = takeTrust(argv);
2914
3507
  if (null == trusted) {
@@ -2933,7 +3526,7 @@ function runWhy(argv) {
2933
3526
  format = f;
2934
3527
  }
2935
3528
  else if (arg.startsWith('-')) {
2936
- process.stderr.write(`aontu: unknown why option ${arg} (try --help)\n`);
3529
+ process.stderr.write(`aontu: unknown model why option ${arg} (try --help)\n`);
2937
3530
  return 2;
2938
3531
  }
2939
3532
  else {
@@ -2941,7 +3534,7 @@ function runWhy(argv) {
2941
3534
  }
2942
3535
  }
2943
3536
  if (2 !== rest.length) {
2944
- process.stderr.write(`aontu: why needs a path and one file\n${WHY_HELP}\n`);
3537
+ process.stderr.write(`aontu: model why needs a path and one file\n${WHY_HELP}\n`);
2945
3538
  return 2;
2946
3539
  }
2947
3540
  const [path, file] = rest;
@@ -2958,7 +3551,7 @@ function runWhy(argv) {
2958
3551
  });
2959
3552
  if ('json' === format) {
2960
3553
  process.stdout.write((0, aontu_1.exactJSON)({
2961
- aontu: { version: version(), verb: 'why' },
3554
+ aontu: { version: version(), verb: 'model why' },
2962
3555
  findings: report.findings,
2963
3556
  ok: report.ok,
2964
3557
  ...(null == report.record ? {} : { record: report.record }),
@@ -2990,7 +3583,7 @@ function renderWhyText(record) {
2990
3583
  ('literal' === c.role ? '' : ` (${c.role})`);
2991
3584
  })).join('\n');
2992
3585
  }
2993
- const SET_HELP = 'aontu set <path>=<value> --entry <file> --overlay <file> (try --help)';
3586
+ const SET_HELP = 'aontu model set <path>=<value> --entry <file> --overlay <file> (try --help)';
2994
3587
  function runSet(argv) {
2995
3588
  const trusted = takeTrust(argv);
2996
3589
  if (null == trusted) {
@@ -3031,7 +3624,7 @@ function runSet(argv) {
3031
3624
  format = f;
3032
3625
  }
3033
3626
  else if (arg.startsWith('-')) {
3034
- process.stderr.write(`aontu: unknown set option ${arg} (try --help)\n`);
3627
+ process.stderr.write(`aontu: unknown model set option ${arg} (try --help)\n`);
3035
3628
  return 2;
3036
3629
  }
3037
3630
  else {
@@ -3039,7 +3632,7 @@ function runSet(argv) {
3039
3632
  }
3040
3633
  }
3041
3634
  if (0 === assignments.length || null == entry || null == overlayFile) {
3042
- process.stderr.write(`aontu: set needs assignments, --entry and --overlay\n${SET_HELP}\n`);
3635
+ process.stderr.write(`aontu: model set needs assignments, --entry and --overlay\n${SET_HELP}\n`);
3043
3636
  return 2;
3044
3637
  }
3045
3638
  let entrySrc;
@@ -3086,7 +3679,7 @@ function runSet(argv) {
3086
3679
  }
3087
3680
  if ('json' === format) {
3088
3681
  process.stdout.write((0, aontu_1.exactJSON)({
3089
- aontu: { version: version(), verb: 'set' },
3682
+ aontu: { version: version(), verb: 'model set' },
3090
3683
  appended: report.appended,
3091
3684
  findings: report.findings,
3092
3685
  overlay: report.overlay,
@@ -3523,12 +4116,22 @@ function fmtOne(name, src, flags, marker) {
3523
4116
  }
3524
4117
  return flags.check ? 1 : strict;
3525
4118
  }
4119
+ const PROCESS_IO = {
4120
+ out: (s) => void process.stdout.write(s),
4121
+ err: (s) => void process.stderr.write(s),
4122
+ };
4123
+ // `pkg serve` runs until the process is interrupted.
4124
+ function serveUntilInterrupted() {
4125
+ return new Promise((done) => process.once('SIGINT', () => done()));
4126
+ }
3526
4127
  // Excluded: the real pair takes the process stdio, so ts/test/cli.test.ts
3527
4128
  // drives each server through a child process instead.
3528
- /* node:coverage ignore next 4 */
4129
+ /* node:coverage ignore next 6 */
3529
4130
  const SERVERS = {
3530
4131
  lsp: () => void (0, lsp_server_1.main)(),
3531
4132
  mcp: (argv) => void (0, mcp_server_1.main)(undefined, undefined, undefined, undefined, argv),
4133
+ serve: serveUntilInterrupted,
4134
+ http: pkg_net_1.defaultHttp,
3532
4135
  };
3533
4136
  // undefined: the server took the process; a number: an answer the CLI
3534
4137
  // gives itself, --help or a usage error.
@@ -3801,10 +4404,10 @@ function runInit(argv) {
3801
4404
  return 0;
3802
4405
  }
3803
4406
  const KNOWN_VERBS = [
3804
- 'agentsmd', 'allow', 'breaking', 'explain', 'fmt', 'get', 'hash',
3805
- 'help', 'init', 'jsonschema', 'lsp', 'mcp', 'mod', 'reaches',
3806
- 'relations', 'set', 'subsume', 'template', 'trace', 'trim',
3807
- 'vet', 'view', 'why',
4407
+ 'add', 'agentsmd', 'allow', 'breaking', 'explain', 'fmt', 'get', 'hash',
4408
+ 'help', 'init', 'jsonschema', 'lsp', 'mcp', 'model', 'pkg', 'publish',
4409
+ 'reaches', 'relations', 'remove', 'render', 'subsume', 'sync', 'template',
4410
+ 'trace', 'trim', 'vet', 'view', 'why',
3808
4411
  ];
3809
4412
  exports.KNOWN_VERBS = KNOWN_VERBS;
3810
4413
  // looksLikeVerb reports whether an unreadable argument was meant as a
@@ -3889,17 +4492,18 @@ function main(argv, servers = SERVERS) {
3889
4492
  if ('mcp' === argv[2]) {
3890
4493
  return servers.mcp(argv.slice(3));
3891
4494
  }
3892
- if ('set' === argv[2]) {
3893
- return finish(runSet(argv.slice(3)));
3894
- }
3895
4495
  if ('allow' === argv[2]) {
3896
4496
  return finish(runAllow(argv.slice(3)));
3897
4497
  }
3898
- if ('why' === argv[2]) {
3899
- return finish(runWhy(argv.slice(3)));
4498
+ if ('model' === argv[2]) {
4499
+ return finish(runModel(argv.slice(3)));
4500
+ }
4501
+ if ('pkg' === argv[2]) {
4502
+ const r = runPkg(argv.slice(3), servers);
4503
+ return 'number' === typeof r ? finish(r) : void r.then(finish);
3900
4504
  }
3901
- if ('get' === argv[2]) {
3902
- return finish(runGet(argv.slice(3)));
4505
+ if (PACKAGE_VERBS.includes(argv[2])) {
4506
+ return void runPackageVerb(argv[2], argv.slice(3), servers).then(finish);
3903
4507
  }
3904
4508
  if ('hash' === argv[2]) {
3905
4509
  return finish(runHash(argv.slice(3)));
@@ -3915,9 +4519,6 @@ function main(argv, servers = SERVERS) {
3915
4519
  if ('init' === argv[2]) {
3916
4520
  return finish(runInit(argv.slice(3)));
3917
4521
  }
3918
- if ('mod' === argv[2]) {
3919
- return finish(runMod(argv.slice(3)));
3920
- }
3921
4522
  if ('relations' === argv[2]) {
3922
4523
  return finish(runRelations(argv.slice(3)));
3923
4524
  }
@@ -3930,6 +4531,9 @@ function main(argv, servers = SERVERS) {
3930
4531
  if ('trace' === argv[2]) {
3931
4532
  return finish(runTrace(argv.slice(3)));
3932
4533
  }
4534
+ if ('render' === argv[2]) {
4535
+ return void runRender(argv.slice(3)).then(finish);
4536
+ }
3933
4537
  if ('reaches' === argv[2]) {
3934
4538
  return finish(runReaches(argv.slice(3)));
3935
4539
  }