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.
- package/README.md +2 -2
- package/dist/agentsmd.js +3 -3
- package/dist/agentsmd.js.map +1 -1
- package/dist/aontu.d.ts +1 -1
- package/dist/aontu.js +1 -1
- package/dist/cli.d.ts +16 -2
- package/dist/cli.js +741 -137
- package/dist/cli.js.map +1 -1
- package/dist/compat.d.ts +7 -0
- package/dist/compat.js +113 -0
- package/dist/compat.js.map +1 -0
- package/dist/helpdoc.js +5 -5
- package/dist/helpdoc.js.map +1 -1
- package/dist/hints.js +11 -5
- package/dist/hints.js.map +1 -1
- package/dist/lang.d.ts +1 -0
- package/dist/lang.js +8 -0
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +1 -1
- package/dist/mod.d.ts +18 -3
- package/dist/mod.js +117 -46
- package/dist/mod.js.map +1 -1
- package/dist/pkg-net.d.ts +227 -0
- package/dist/pkg-net.js +1598 -0
- package/dist/pkg-net.js.map +1 -0
- package/dist/pkg-zip.d.ts +8 -0
- package/dist/pkg-zip.js +196 -0
- package/dist/pkg-zip.js.map +1 -0
- package/dist/pkg.d.ts +159 -0
- package/dist/pkg.js +767 -0
- package/dist/pkg.js.map +1 -0
- package/dist/report-sarif.js +1 -1
- package/dist/report-sarif.js.map +1 -1
- package/dist/subsume.d.ts +3 -1
- package/dist/subsume.js +2 -0
- package/dist/subsume.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/grammar/aontu.gbnf +8 -18
- package/grammar/aontu.lark +6 -14
- package/grammar/aontu.tmLanguage.json +4 -6
- package/package.json +3 -2
- package/skill/SKILL.md +3 -3
- package/skill/error-codes.md +4 -4
- package/skill/examples.md +4 -4
- package/skill/grammar-card.md +1 -1
- package/skill/init/check.sh +2 -2
- package/skill/tasks.md +17 -12
- package/src/agentsmd.ts +3 -3
- package/src/aontu.ts +1 -1
- package/src/cli.ts +851 -151
- package/src/compat.ts +148 -0
- package/src/helpdoc.ts +5 -5
- package/src/hints.ts +11 -7
- package/src/lang.ts +9 -1
- package/src/mod.ts +142 -51
- package/src/pkg-net.ts +1945 -0
- package/src/pkg-zip.ts +228 -0
- package/src/pkg.ts +1018 -0
- package/src/report-sarif.ts +1 -1
- package/src/subsume.ts +2 -2
- package/dist/mod-tool.d.ts +0 -58
- package/dist/mod-tool.js +0 -418
- package/dist/mod-tool.js.map +0 -1
- 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.
|
|
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
|
|
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
|
|
76
|
-
aontu
|
|
77
|
-
aontu
|
|
78
|
-
aontu
|
|
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
|
-
|
|
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
|
-
--
|
|
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
|
-
|
|
152
|
-
tidy Resolve the
|
|
153
|
-
rewrite aontu_meta/
|
|
154
|
-
verify Check every locked
|
|
155
|
-
pins, and change nothing
|
|
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
|
|
158
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
303
|
+
Model why options:
|
|
248
304
|
--format <f> text (default) or json
|
|
249
305
|
|
|
250
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
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
|
-
|
|
1753
|
-
return
|
|
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
|
-
|
|
1759
|
-
return
|
|
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 ('--
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
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
|
|
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
|
-
|
|
1773
|
-
return
|
|
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
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
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
|
-
|
|
1788
|
-
|
|
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 (
|
|
1792
|
-
|
|
1793
|
-
'
|
|
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
|
-
|
|
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
|
-
|
|
1799
|
-
return 2;
|
|
1800
|
-
}
|
|
1801
|
-
const
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1853
|
-
|
|
1854
|
-
lines.push('
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
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
|
|
1872
|
-
lines.push(
|
|
2042
|
+
for (const key of report.verified) {
|
|
2043
|
+
lines.push(key + ': verified');
|
|
1873
2044
|
}
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
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
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
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
|
|
2062
|
+
lines.push(miss + ': not fetched (run: aontu sync)');
|
|
1885
2063
|
}
|
|
1886
2064
|
return lines.join('\n');
|
|
1887
2065
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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', '
|
|
3806
|
-
'relations', '
|
|
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 ('
|
|
3899
|
-
return finish(
|
|
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 (
|
|
3902
|
-
return
|
|
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
|
}
|