aontu 0.63.0 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/agentsmd.js +3 -3
- package/dist/agentsmd.js.map +1 -1
- package/dist/aontu.d.ts +3 -3
- package/dist/aontu.js +4 -6
- package/dist/aontu.js.map +1 -1
- package/dist/aontumodel.js +6 -12
- package/dist/aontumodel.js.map +1 -1
- package/dist/casing.d.ts +5 -0
- package/dist/casing.js +90 -0
- package/dist/casing.js.map +1 -0
- package/dist/cli.d.ts +16 -3
- package/dist/cli.js +653 -455
- 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 +14 -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/mcp.js +0 -36
- package/dist/mcp.js.map +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/profile.d.ts +9 -0
- package/dist/profile.js +28 -0
- package/dist/profile.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/trace.d.ts +21 -0
- package/dist/trace.js +107 -0
- package/dist/trace.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/unify.js +1 -6
- package/dist/unify.js.map +1 -1
- package/dist/val/CmpFuncVal.d.ts +2 -0
- package/dist/val/CmpFuncVal.js +47 -13
- package/dist/val/CmpFuncVal.js.map +1 -1
- package/dist/val/NomFuncVal.js +11 -13
- package/dist/val/NomFuncVal.js.map +1 -1
- package/dist/val/ReferFuncVal.js +3 -0
- package/dist/val/ReferFuncVal.js.map +1 -1
- package/package.json +1 -1
- 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 +14 -12
- package/src/agentsmd.ts +3 -3
- package/src/aontu.ts +3 -8
- package/src/aontumodel.ts +6 -12
- package/src/casing.ts +95 -0
- package/src/cli.ts +758 -495
- package/src/compat.ts +148 -0
- package/src/helpdoc.ts +5 -5
- package/src/hints.ts +14 -7
- package/src/lang.ts +9 -1
- package/src/mcp.ts +0 -39
- 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/profile.ts +42 -0
- package/src/report-sarif.ts +1 -1
- package/src/subsume.ts +2 -2
- package/src/trace.ts +157 -0
- package/src/unify.ts +1 -6
- package/src/val/CmpFuncVal.ts +66 -20
- package/src/val/NomFuncVal.ts +3 -5
- package/src/val/ReferFuncVal.ts +4 -0
- package/dist/lower.d.ts +0 -23
- package/dist/lower.js +0 -528
- package/dist/lower.js.map +0 -1
- package/dist/mod-tool.d.ts +0 -58
- package/dist/mod-tool.js +0 -403
- package/dist/mod-tool.js.map +0 -1
- package/dist/render.d.ts +0 -53
- package/dist/render.js +0 -432
- package/dist/render.js.map +0 -1
- package/src/lower.ts +0 -586
- package/src/mod-tool.ts +0 -561
- package/src/render.ts +0 -580
package/src/cli.ts
CHANGED
|
@@ -17,25 +17,35 @@ import {
|
|
|
17
17
|
hcanon, canonHash,
|
|
18
18
|
get, why, patch, agentsMd,
|
|
19
19
|
allow,
|
|
20
|
-
|
|
21
|
-
renderProfile,
|
|
20
|
+
loadProfile,
|
|
22
21
|
} from './aontu'
|
|
23
22
|
import type { AllowDecision, AllowReport, AllowVerdict } from './allow'
|
|
24
|
-
import
|
|
23
|
+
import { traceRun } from './trace'
|
|
25
24
|
import {
|
|
26
25
|
desugarTemplate, resugarTemplate, templateOutputs, markerFor,
|
|
27
26
|
markerFromProfiles,
|
|
28
27
|
} from './template'
|
|
29
|
-
import { outsideRoot } from './mcp'
|
|
30
28
|
import { sarifReport } from './report-sarif'
|
|
31
29
|
import { main as lspMain } from './lsp-server'
|
|
32
30
|
import { main as mcpMain } from './mcp-server'
|
|
33
31
|
import { jsonSchema } from './jsonschema'
|
|
34
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
pkgTidy, pkgVerify, pkgVendor, pkgManifest, pkgRefreeze, pkgTree,
|
|
34
|
+
versionCompare,
|
|
35
|
+
} from './pkg'
|
|
36
|
+
import {
|
|
37
|
+
pkgSync, pkgGet, pkgRemove, pkgWhy, pkgPublish, pkgOutdated, startServe,
|
|
38
|
+
defaultHttp, keygen, PUBLISH_PATH,
|
|
39
|
+
} from './pkg-net'
|
|
35
40
|
import type {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
PkgSyncReport, PkgChangeReport, PkgWhyReport, PkgPublishReport,
|
|
42
|
+
PkgOutdatedReport, Served, PkgHttp,
|
|
43
|
+
} from './pkg-net'
|
|
44
|
+
import type {
|
|
45
|
+
PkgTidyReport, PkgVerifyReport, PkgVendorReport, PkgManifestReport,
|
|
46
|
+
PkgRefreezeReport, PkgTreeReport, PkgToolOptions,
|
|
47
|
+
} from './pkg'
|
|
48
|
+
import { modCacheDir, PKG_FILE, LOCK_FILE, META_DIR } from './mod'
|
|
39
49
|
import { VET_MAX_ERRORS } from './vet'
|
|
40
50
|
import type {
|
|
41
51
|
VetReport, VetFinding, VetVerdict, VetCoverage,
|
|
@@ -83,16 +93,23 @@ const HELP = `Usage: aontu [options] [file]
|
|
|
83
93
|
aontu view <kind> [options] <file>...
|
|
84
94
|
aontu view --views <path> [--check] [options] <file>
|
|
85
95
|
aontu jsonschema [--at <path>] [--strict] [options] <file>
|
|
86
|
-
aontu render [--at <path>] [--profile <file>]... [--unit <path>]
|
|
87
|
-
[--stdout | --out <dir> | --check <dir> | --coverage]
|
|
88
|
-
[--coverage-at <path>] [--strict] <file>
|
|
89
96
|
aontu template [--resugar] [--check] [--marker <token>]
|
|
90
97
|
[--profile <file>] <file>
|
|
98
|
+
aontu trace [--at <path>] [--format json] [--marker <token>]
|
|
99
|
+
[--profile <file>] <file>
|
|
91
100
|
aontu hash [options] <file>
|
|
92
|
-
aontu
|
|
93
|
-
aontu
|
|
94
|
-
aontu
|
|
95
|
-
aontu
|
|
101
|
+
aontu sync [--frozen] [options] [dir]
|
|
102
|
+
aontu add <pkg>[@<version>] [options] [dir]
|
|
103
|
+
aontu get <pkg>[@<version>] [options] [dir]
|
|
104
|
+
aontu remove <pkg> [options] [dir]
|
|
105
|
+
aontu why <pkg> [options] [dir]
|
|
106
|
+
aontu publish [--yes] [--to <dir>] [options] [dir]
|
|
107
|
+
aontu pkg tidy|verify|vendor|manifest|refreeze|tree|outdated|serve
|
|
108
|
+
[options] [dir]
|
|
109
|
+
aontu pkg keygen <file>
|
|
110
|
+
aontu model get <path> [options] <file>
|
|
111
|
+
aontu model why <path> [options] <file>
|
|
112
|
+
aontu model set <path>=<value>... --entry <file> --overlay <file>
|
|
96
113
|
aontu allow --role <role> [--at <path>] <roles-file> <path>...
|
|
97
114
|
aontu agentsmd [--write <AGENTS.md>] [--depth <n>] <file>
|
|
98
115
|
aontu fmt [-w|-l|--check|-d|--lint] [--marker <token>]
|
|
@@ -161,18 +178,57 @@ Options:
|
|
|
161
178
|
named format keeps its meaning, and .js stays
|
|
162
179
|
refused. Every verb takes it
|
|
163
180
|
|
|
164
|
-
|
|
181
|
+
Package verbs (a module is imported; a package is published):
|
|
182
|
+
sync Make the project correct: resolve by minimum version
|
|
183
|
+
selection, fetch what is missing, write aontu_meta/pkg-lock.aon,
|
|
184
|
+
vendor, verify. --frozen refuses to change the lockfile
|
|
185
|
+
add Take on a dependency the project does not have, then sync;
|
|
186
|
+
refuses one it has and names get
|
|
187
|
+
get Add a dependency or raise its minimum, then sync. Without a
|
|
188
|
+
version, the newest version outside the cooldown
|
|
189
|
+
remove Drop a dependency, then sync
|
|
190
|
+
why Why is this package in the closure: every path of
|
|
191
|
+
requirements that reaches it
|
|
192
|
+
publish Publish this package. A dry run without --yes; --to <dir>
|
|
193
|
+
writes the repository layout into a directory instead
|
|
194
|
+
|
|
195
|
+
Package options:
|
|
165
196
|
--format <f> text (default) or json
|
|
166
|
-
--
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
197
|
+
--frozen sync: fail if the lockfile would change (the CI mode)
|
|
198
|
+
--yes publish: send it (the default is a dry run)
|
|
199
|
+
--to <dir> publish: write the read-path layout into a directory
|
|
200
|
+
--key <file> publish: the Ed25519 private key (PKCS#8 PEM) that
|
|
201
|
+
signs the manifest, for the key provider
|
|
202
|
+
--token <file> publish: the forge's OIDC token, read from a file
|
|
203
|
+
--base <url> the repository to read from (repeatable; overrides
|
|
204
|
+
pkg.aon repo.base)
|
|
205
|
+
--write <url> publish: the write path (overrides pkg.aon repo.write)
|
|
206
|
+
--against <dir> manifest: a prior version's tree, to gate on
|
|
207
|
+
--upstream <u> serve: fetch on miss from this repository (repeatable)
|
|
208
|
+
--listen <a> serve: the address to listen on (default 127.0.0.1:8017)
|
|
209
|
+
|
|
210
|
+
pkg subcommands (the rest of the package operations):
|
|
211
|
+
tidy Resolve the closure by minimum version selection and
|
|
212
|
+
rewrite aontu_meta/pkg-lock.aon in canonical form
|
|
213
|
+
verify Check every locked package still is and still means what
|
|
214
|
+
the lockfile pins, bytes before meaning, and change nothing
|
|
215
|
+
(the CI gate; tidy rewrites)
|
|
173
216
|
vendor Materialise the locked closure into aontu_meta/vendor/
|
|
174
|
-
manifest Print the
|
|
175
|
-
|
|
217
|
+
manifest Print the manifest a publish would send, gated on the
|
|
218
|
+
compatibility check against --against
|
|
219
|
+
refreeze Recompute every canon pin and nothing else, after a
|
|
220
|
+
canonical-form change in the engine
|
|
221
|
+
tree The locked closure as a graph
|
|
222
|
+
outdated What could move, and what would move with it
|
|
223
|
+
serve Serve a repository directory; with --upstream, a caching
|
|
224
|
+
proxy in front of another repository
|
|
225
|
+
keygen Write a new Ed25519 signing key (PKCS#8 PEM) to a file,
|
|
226
|
+
once, and print the signer id a consumer names
|
|
227
|
+
|
|
228
|
+
model subcommands (one document, interrogated or edited):
|
|
229
|
+
get What the document says at a path
|
|
230
|
+
why Every contribution to the value at a path
|
|
231
|
+
set Append a path-flattened conjunct to an overlay
|
|
176
232
|
|
|
177
233
|
Vet options:
|
|
178
234
|
--at <path> Validate against this path of the schema ($.a.b)
|
|
@@ -251,21 +307,21 @@ Hash options:
|
|
|
251
307
|
Hash exit codes: 0 hashed, 2 usage, 4 the document does not stand up
|
|
252
308
|
on its own.
|
|
253
309
|
|
|
254
|
-
|
|
310
|
+
Model get options:
|
|
255
311
|
-c, --canon Canonical-form fragment (default: generated JSON)
|
|
256
312
|
--keys Keys at the node, one per line
|
|
257
313
|
--types Shape view: concrete leaves lifted to their kinds
|
|
258
314
|
--depth <n> Structure to depth n; deeper nodes render as top
|
|
259
315
|
--format <f> text (default) or json
|
|
260
316
|
|
|
261
|
-
|
|
262
|
-
document does not stand up on its own.
|
|
317
|
+
Model get exit codes: 0 rendered, 1 the path names nothing, 2 usage, 4
|
|
318
|
+
the document does not stand up on its own.
|
|
263
319
|
|
|
264
|
-
|
|
320
|
+
Model why options:
|
|
265
321
|
--format <f> text (default) or json
|
|
266
322
|
|
|
267
|
-
|
|
268
|
-
2 usage, 4 the document does not stand up on its own.
|
|
323
|
+
Model why exit codes mirror get's: 0 explained, 1 the path names
|
|
324
|
+
nothing, 2 usage, 4 the document does not stand up on its own.
|
|
269
325
|
|
|
270
326
|
View kinds: doc, lattice, tree, matrix, graph, layer, sets, layers,
|
|
271
327
|
ladder, poset (the poset takes several files). The figure goes to stdout, the loss
|
|
@@ -333,37 +389,11 @@ View exit codes: 0 rendered, 1 --check mismatch or lossy under
|
|
|
333
389
|
--strict, 2 usage or --max-rows exceeded, 4 the document does not stand
|
|
334
390
|
up on its own, or a relation, root or path that names nothing.
|
|
335
391
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
one per language
|
|
342
|
-
--unit <path> Render only the unit with this path
|
|
343
|
-
--stdout One unit's bytes and nothing else (with --unit when
|
|
344
|
-
the instance has several)
|
|
345
|
-
--out <dir> Write every unit below dir, or nothing; never deletes
|
|
346
|
-
--check <dir> Compare every unit with dir/<path>; drift is listed
|
|
347
|
-
--coverage Report what the render read and what it did not:
|
|
348
|
-
model paths no output consumed, and rendered
|
|
349
|
-
declarations no rule produced. Writes nothing
|
|
350
|
-
--coverage-at <p> Measure coverage under this path only, instead of
|
|
351
|
-
the document root
|
|
352
|
-
--strict Refuse the opaque escapes (a text declaration, a raw
|
|
353
|
-
block)
|
|
354
|
-
--format <f> text (default) or json, the whole report; json
|
|
355
|
-
carries the dispatch trace, one entry per emitted
|
|
356
|
-
piece
|
|
357
|
-
|
|
358
|
-
Render exit codes: 0 rendered, 1 lossy under --strict or drift under
|
|
359
|
-
--check, 2 usage or I/O (a refused unit path included), 4 the document
|
|
360
|
-
does not stand up or the instance is not aontu:code.
|
|
361
|
-
|
|
362
|
-
A render entry file whose extension is not .aon is a TEMPLATE: a
|
|
363
|
-
generator in the target's own syntax, whose marker lines carry aontu
|
|
364
|
-
and whose other lines are output. It is desugared before it is
|
|
365
|
-
evaluated, and a language the table does not know names its marker with
|
|
366
|
-
--marker, or declares it once in a profile file that --profile reads.
|
|
392
|
+
A template entry file whose extension is not .aon is a GENERATOR: a
|
|
393
|
+
document in the target's own syntax, whose marker lines carry aontu and
|
|
394
|
+
whose other lines are output. It is desugared before it is evaluated,
|
|
395
|
+
and a language the table does not know names its marker with --marker,
|
|
396
|
+
or declares it once in a profile file that --profile reads.
|
|
367
397
|
|
|
368
398
|
Template options:
|
|
369
399
|
--resugar The file is the canonical aontu; print the template
|
|
@@ -381,7 +411,7 @@ every other line is a line of output.
|
|
|
381
411
|
|
|
382
412
|
Template exit codes: 0 written, 1 --check drift, 2 usage or I/O.
|
|
383
413
|
|
|
384
|
-
|
|
414
|
+
Model set options:
|
|
385
415
|
--entry <file> The document the change is checked against
|
|
386
416
|
--overlay <file> The file the change is appended to (created if
|
|
387
417
|
absent; not written when the change does not hold)
|
|
@@ -397,7 +427,7 @@ Set options:
|
|
|
397
427
|
--format <f> text (default) or json
|
|
398
428
|
|
|
399
429
|
Set exit codes are vet's verdict classes: 0 valid, 1 invalid (the
|
|
400
|
-
change contradicts a pinned value -- aontu why locates it, and
|
|
430
|
+
change contradicts a pinned value -- aontu model why locates it, and
|
|
401
431
|
--in-place rewrites it), 2 usage, 3 incomplete, 4 the entry does not
|
|
402
432
|
stand up on its own.
|
|
403
433
|
|
|
@@ -473,11 +503,11 @@ Fmt options:
|
|
|
473
503
|
The fmt verb prints one document in the agreed form; with no file it
|
|
474
504
|
reads standard input. Several files need one of the options above.
|
|
475
505
|
|
|
476
|
-
A file whose extension is not .aon is a GENERATOR, as it is for
|
|
477
|
-
the aontu its marker lines carry is formatted, the marker
|
|
478
|
-
left margin with the aontu indented after it, and every
|
|
479
|
-
is held on a line of its own. A file with no marker line
|
|
480
|
-
another language's, and is refused.
|
|
506
|
+
A file whose extension is not .aon is a GENERATOR, as it is for
|
|
507
|
+
template: the aontu its marker lines carry is formatted, the marker
|
|
508
|
+
stands at the left margin with the aontu indented after it, and every
|
|
509
|
+
line of output is held on a line of its own. A file with no marker line
|
|
510
|
+
in it is another language's, and is refused.
|
|
481
511
|
|
|
482
512
|
Fmt exit codes: 0 formatted or clean, 1 a --check file would change or
|
|
483
513
|
a --strict finding, 2 usage, 4 a document does not parse.
|
|
@@ -630,7 +660,7 @@ function trustOpts(trust: TrustArg, entryRoot: string): any {
|
|
|
630
660
|
}
|
|
631
661
|
|
|
632
662
|
|
|
633
|
-
function takeTrust(argv: string[]):
|
|
663
|
+
function takeTrust(argv: string[], io: Io = PROCESS_IO):
|
|
634
664
|
{ argv: string[], trust: TrustArg } | undefined {
|
|
635
665
|
const rest: string[] = []
|
|
636
666
|
let trust: TrustArg = { kind: 'system-warn', textExt: [] }
|
|
@@ -640,7 +670,7 @@ function takeTrust(argv: string[]):
|
|
|
640
670
|
if ('--trust' === arg) {
|
|
641
671
|
const parsed = null == argv[i + 1] ? undefined : parseTrustArg(argv[++i])
|
|
642
672
|
if (null == parsed) {
|
|
643
|
-
|
|
673
|
+
io.err(
|
|
644
674
|
'aontu: --trust needs system, none, or root[:dir]\n')
|
|
645
675
|
return undefined
|
|
646
676
|
}
|
|
@@ -649,7 +679,7 @@ function takeTrust(argv: string[]):
|
|
|
649
679
|
else if ('--include-root' === arg) {
|
|
650
680
|
const dir = argv[++i]
|
|
651
681
|
if (null == dir) {
|
|
652
|
-
|
|
682
|
+
io.err('aontu: --include-root needs a directory\n')
|
|
653
683
|
return undefined
|
|
654
684
|
}
|
|
655
685
|
trust = { kind: 'root', dir, textExt }
|
|
@@ -657,7 +687,7 @@ function takeTrust(argv: string[]):
|
|
|
657
687
|
else if ('--text-ext' === arg) {
|
|
658
688
|
const list = null == argv[i + 1] ? undefined : parseTextExt(argv[++i])
|
|
659
689
|
if (null == list) {
|
|
660
|
-
|
|
690
|
+
io.err(
|
|
661
691
|
'aontu: --text-ext needs extensions, without dots' +
|
|
662
692
|
' (--text-ext md,sql)\n')
|
|
663
693
|
return undefined
|
|
@@ -673,7 +703,7 @@ function takeTrust(argv: string[]):
|
|
|
673
703
|
|
|
674
704
|
|
|
675
705
|
// `md,sql` or `.md,.sql` -- the dot is accepted and dropped, because a
|
|
676
|
-
// reader who has just written `@"notes.txt"` reaches for one. An empty
|
|
706
|
+
// reader who has just written `@"./notes.txt"` reaches for one. An empty
|
|
677
707
|
// element, or anything that is not an extension, is a usage error
|
|
678
708
|
// rather than a silently ignored word: a flag that quietly does
|
|
679
709
|
// nothing is how a document ends up refused with no reason visible.
|
|
@@ -2077,102 +2107,186 @@ const VIEW_USAGE_CODES = [
|
|
|
2077
2107
|
'view_document_shape', 'view_style_profile', 'view_style_unknown',
|
|
2078
2108
|
]
|
|
2079
2109
|
|
|
2080
|
-
const
|
|
2110
|
+
const PKG_HELP =
|
|
2111
|
+
'aontu pkg tidy|verify|vendor|manifest|refreeze|tree|outdated|serve [dir] | keygen <file> (try --help)'
|
|
2081
2112
|
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2113
|
+
const PKG_SUBS = [
|
|
2114
|
+
'tidy', 'verify', 'vendor', 'manifest', 'refreeze', 'tree', 'outdated',
|
|
2115
|
+
'serve', 'keygen',
|
|
2116
|
+
]
|
|
2086
2117
|
|
|
2118
|
+
// The options every package verb parses, so `sync`, `get`, `publish`
|
|
2119
|
+
// and the `pkg` subcommands read one table.
|
|
2120
|
+
type PkgArgs = {
|
|
2121
|
+
rest: string[]
|
|
2122
|
+
format: SubsumeFormat
|
|
2123
|
+
against?: string
|
|
2124
|
+
frozen: boolean
|
|
2125
|
+
yes: boolean
|
|
2126
|
+
to?: string
|
|
2127
|
+
key?: string
|
|
2128
|
+
token?: string
|
|
2129
|
+
base: string[]
|
|
2130
|
+
write?: string
|
|
2131
|
+
upstream: string[]
|
|
2132
|
+
listen?: string
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
function parsePkgArgs(argv: string[], verb: string, io: Io): PkgArgs | undefined {
|
|
2136
|
+
const out: PkgArgs = {
|
|
2137
|
+
rest: [], format: 'text', frozen: false, yes: false, base: [], upstream: [],
|
|
2138
|
+
}
|
|
2139
|
+
const value = (name: string, i: number): string | undefined => {
|
|
2140
|
+
const v = argv[i]
|
|
2141
|
+
if (null == v) {
|
|
2142
|
+
io.err(`aontu: ${name} needs a value\n`)
|
|
2143
|
+
}
|
|
2144
|
+
return v
|
|
2145
|
+
}
|
|
2087
2146
|
for (let i = 0; i < argv.length; i++) {
|
|
2088
2147
|
const arg = argv[i]
|
|
2089
2148
|
if ('-h' === arg || '--help' === arg) {
|
|
2090
|
-
|
|
2091
|
-
return
|
|
2149
|
+
io.out(HELP)
|
|
2150
|
+
return undefined
|
|
2092
2151
|
}
|
|
2093
2152
|
if ('--format' === arg) {
|
|
2094
2153
|
const f = argv[++i]
|
|
2095
2154
|
if ('text' !== f && 'json' !== f) {
|
|
2096
|
-
|
|
2097
|
-
return
|
|
2155
|
+
io.err('aontu: --format needs text or json\n')
|
|
2156
|
+
return undefined
|
|
2098
2157
|
}
|
|
2099
|
-
format = f
|
|
2158
|
+
out.format = f
|
|
2100
2159
|
}
|
|
2101
|
-
else if ('--
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2160
|
+
else if ('--frozen' === arg) {
|
|
2161
|
+
out.frozen = true
|
|
2162
|
+
}
|
|
2163
|
+
else if ('--yes' === arg) {
|
|
2164
|
+
out.yes = true
|
|
2165
|
+
}
|
|
2166
|
+
else if ('--against' === arg || '--to' === arg || '--key' === arg ||
|
|
2167
|
+
'--token' === arg || '--write' === arg || '--listen' === arg) {
|
|
2168
|
+
const v = value(arg, ++i)
|
|
2169
|
+
if (null == v) {
|
|
2170
|
+
return undefined
|
|
2171
|
+
}
|
|
2172
|
+
out['--against' === arg ? 'against' : '--to' === arg ? 'to' :
|
|
2173
|
+
'--key' === arg ? 'key' : '--token' === arg ? 'token' :
|
|
2174
|
+
'--write' === arg ? 'write' : 'listen'] = v
|
|
2175
|
+
}
|
|
2176
|
+
else if ('--base' === arg || '--upstream' === arg) {
|
|
2177
|
+
const v = value(arg, ++i)
|
|
2178
|
+
if (null == v) {
|
|
2179
|
+
return undefined
|
|
2106
2180
|
}
|
|
2107
|
-
|
|
2181
|
+
out['--base' === arg ? 'base' : 'upstream'].push(v)
|
|
2108
2182
|
}
|
|
2109
2183
|
else if (arg.startsWith('-')) {
|
|
2110
|
-
|
|
2111
|
-
return
|
|
2184
|
+
io.err(`aontu: unknown ${verb} option ${arg} (try --help)\n`)
|
|
2185
|
+
return undefined
|
|
2112
2186
|
}
|
|
2113
2187
|
else {
|
|
2114
|
-
rest.push(arg)
|
|
2188
|
+
out.rest.push(arg)
|
|
2115
2189
|
}
|
|
2116
2190
|
}
|
|
2191
|
+
return out
|
|
2192
|
+
}
|
|
2117
2193
|
|
|
2118
|
-
const sub = rest[0]
|
|
2119
|
-
const dir = rest[1] ?? '.'
|
|
2120
2194
|
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2195
|
+
// A verb that finds the older layout names the current one, once, and
|
|
2196
|
+
// reads nothing from it.
|
|
2197
|
+
function nameOldLayout(dir: string, io: Io): void {
|
|
2198
|
+
const old = ['aon_vendor', 'mod-lock.aon', 'mod.aon',
|
|
2199
|
+
join(META_DIR, 'mod-lock.aon')].filter((f) => existsSync(join(dir, f)))
|
|
2200
|
+
if (0 < old.length) {
|
|
2201
|
+
io.err(
|
|
2202
|
+
'aontu: ' + old.join(', ') + ' belong to an older layout: the package ' +
|
|
2203
|
+
'file is ' + PKG_FILE + ', the lockfile ' + join(META_DIR, LOCK_FILE) +
|
|
2204
|
+
' and the vendor tree ' + join(META_DIR, 'vendor') + '; rename ' +
|
|
2205
|
+
PKG_FILE + '\'s `mod` block to `pkg`, then run aontu sync\n')
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
|
|
2210
|
+
function runPkg(argv: string[], servers: Servers): number | Promise<number> {
|
|
2211
|
+
const io = servers.io ?? PROCESS_IO
|
|
2212
|
+
const trusted = takeTrust(argv, io)
|
|
2213
|
+
if (null == trusted) {
|
|
2126
2214
|
return 2
|
|
2127
2215
|
}
|
|
2216
|
+
const args = parsePkgArgs(trusted.argv, 'pkg', io)
|
|
2217
|
+
if (null == args) {
|
|
2218
|
+
return '-h' === trusted.argv[0] || trusted.argv.includes('--help') ? 0 : 2
|
|
2219
|
+
}
|
|
2220
|
+
const trust = trusted.trust
|
|
2221
|
+
const sub = args.rest[0]
|
|
2222
|
+
const dir = args.rest[1] ?? '.'
|
|
2128
2223
|
|
|
2129
|
-
if (!
|
|
2130
|
-
|
|
2131
|
-
`aontu:
|
|
2224
|
+
if (!PKG_SUBS.includes(sub) || 2 < args.rest.length) {
|
|
2225
|
+
io.err(
|
|
2226
|
+
`aontu: pkg needs one of ${PKG_SUBS.join(', ')}\n${PKG_HELP}\n`)
|
|
2132
2227
|
return 2
|
|
2133
2228
|
}
|
|
2134
2229
|
|
|
2135
|
-
if (
|
|
2136
|
-
|
|
2137
|
-
'aontu:
|
|
2138
|
-
|
|
2230
|
+
if ('keygen' === sub) {
|
|
2231
|
+
if (2 !== args.rest.length) {
|
|
2232
|
+
io.err('aontu: pkg keygen needs the file to write\naontu pkg keygen <file>\n')
|
|
2233
|
+
return 2
|
|
2234
|
+
}
|
|
2235
|
+
const made = keygen(args.rest[1])
|
|
2236
|
+
if (null != made.refused) {
|
|
2237
|
+
io.err('aontu: ' + made.refused + '\n')
|
|
2238
|
+
return 2
|
|
2239
|
+
}
|
|
2240
|
+
io.out('signer: ' + made.signer + '\n')
|
|
2241
|
+
return 0
|
|
2139
2242
|
}
|
|
2140
2243
|
|
|
2141
|
-
|
|
2244
|
+
nameOldLayout(dir, io)
|
|
2245
|
+
|
|
2246
|
+
// `--against` gates a manifest and means nothing to the others;
|
|
2142
2247
|
// accepting it there would say it had been honoured.
|
|
2143
|
-
if (null != against && 'manifest' !== sub) {
|
|
2144
|
-
|
|
2248
|
+
if (null != args.against && 'manifest' !== sub) {
|
|
2249
|
+
io.err('aontu: --against is a manifest option\n')
|
|
2145
2250
|
return 2
|
|
2146
2251
|
}
|
|
2147
2252
|
|
|
2148
|
-
const
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2253
|
+
const opts = pkgToolOptions(trust, resolve(dir))
|
|
2254
|
+
|
|
2255
|
+
if ('outdated' === sub || 'serve' === sub) {
|
|
2256
|
+
return runPkgNet(sub, dir, args, opts, servers, io)
|
|
2257
|
+
}
|
|
2153
2258
|
|
|
2154
|
-
|
|
2155
|
-
|
|
2259
|
+
const report =
|
|
2260
|
+
'tidy' === sub ? pkgTidy(dir, opts) :
|
|
2261
|
+
'verify' === sub ? pkgVerify(dir, opts) :
|
|
2262
|
+
'vendor' === sub ? pkgVendor(dir, opts) :
|
|
2263
|
+
'refreeze' === sub ? pkgRefreeze(dir, opts) :
|
|
2264
|
+
'tree' === sub ? pkgTree(dir, opts) :
|
|
2265
|
+
pkgManifest(dir, opts, args.against)
|
|
2266
|
+
|
|
2267
|
+
io.out(('json' === args.format ?
|
|
2268
|
+
exactJSON({ aontu: { version: version(), verb: 'pkg ' + sub }, ...report },
|
|
2156
2269
|
2) :
|
|
2157
|
-
|
|
2270
|
+
pkgText(sub, report)) + '\n')
|
|
2158
2271
|
|
|
2159
|
-
return
|
|
2272
|
+
return PKG_EXIT[report.verdict]
|
|
2160
2273
|
}
|
|
2161
2274
|
|
|
2162
2275
|
|
|
2163
|
-
const MOD_SUBS = ['tidy', 'verify', 'vendor', 'manifest']
|
|
2164
|
-
|
|
2165
2276
|
// The verdict classes: `ok` 0, a refused gate 1, an open question 3, a
|
|
2166
2277
|
// document that does not stand up 4 -- `subsume`'s classes, because a
|
|
2167
2278
|
// manifest gate IS a subsumption check and a caller reading exit codes
|
|
2168
2279
|
// should not have to learn a second table.
|
|
2169
|
-
type
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2280
|
+
type PkgVerdict =
|
|
2281
|
+
PkgTidyReport['verdict'] |
|
|
2282
|
+
PkgVerifyReport['verdict'] |
|
|
2283
|
+
PkgVendorReport['verdict'] |
|
|
2284
|
+
PkgManifestReport['verdict'] |
|
|
2285
|
+
PkgRefreezeReport['verdict'] |
|
|
2286
|
+
PkgTreeReport['verdict'] |
|
|
2287
|
+
'frozen' | 'refused' | 'sent' | 'dry-run' | 'outdated' | 'current'
|
|
2288
|
+
|
|
2289
|
+
const PKG_EXIT: Record<PkgVerdict, number> = {
|
|
2176
2290
|
ok: 0,
|
|
2177
2291
|
missing: 1,
|
|
2178
2292
|
mismatch: 1,
|
|
@@ -2180,6 +2294,12 @@ const MOD_EXIT: Record<ModVerdict, number> = {
|
|
|
2180
2294
|
// nothing to check, which is a refusal and not a pass.
|
|
2181
2295
|
unlocked: 1,
|
|
2182
2296
|
breaking: 1,
|
|
2297
|
+
frozen: 1,
|
|
2298
|
+
refused: 1,
|
|
2299
|
+
outdated: 1,
|
|
2300
|
+
sent: 0,
|
|
2301
|
+
'dry-run': 0,
|
|
2302
|
+
current: 0,
|
|
2183
2303
|
undecided: 3,
|
|
2184
2304
|
error: 4,
|
|
2185
2305
|
}
|
|
@@ -2188,11 +2308,16 @@ const MOD_EXIT: Record<ModVerdict, number> = {
|
|
|
2188
2308
|
// The tooling's evaluator: the same standalone evaluation the module
|
|
2189
2309
|
// resolver verifies with (ts/src/mod.ts), and for the same reason —
|
|
2190
2310
|
// only the engine can say what a module MEANS.
|
|
2191
|
-
function
|
|
2311
|
+
function pkgToolOptions(trust: TrustArg, entryRoot: string): PkgToolOptions {
|
|
2312
|
+
const opts = verbOpts(trust, entryRoot)
|
|
2313
|
+
// The user cache lives outside any confinement root, so a confined
|
|
2314
|
+
// run reads the vendor tree only -- as the evaluator's own module
|
|
2315
|
+
// leg already does when a root is set.
|
|
2316
|
+
const rooted = null != (opts.trust as any)?.include?.root
|
|
2192
2317
|
return {
|
|
2193
|
-
cache: modCacheDir(),
|
|
2318
|
+
...(rooted ? {} : { cache: modCacheDir() }),
|
|
2194
2319
|
eval: (src: string, path: string) => {
|
|
2195
|
-
const a0 = new Aontu()
|
|
2320
|
+
const a0 = new Aontu(opts)
|
|
2196
2321
|
const ctx = a0.ctx({ collect: true })
|
|
2197
2322
|
const val: any = a0.unify(src, { path }, ctx)
|
|
2198
2323
|
return {
|
|
@@ -2200,7 +2325,7 @@ function modToolOptions() {
|
|
|
2200
2325
|
hash: canonHash(val),
|
|
2201
2326
|
canon: val.canon,
|
|
2202
2327
|
// The same question `aontu hash` asks before it will answer:
|
|
2203
|
-
// did this document stand up ON ITS OWN? See
|
|
2328
|
+
// did this document stand up ON ITS OWN? See PkgToolEval.
|
|
2204
2329
|
ok: 0 === ctx.err.length && true !== val.isNil,
|
|
2205
2330
|
}
|
|
2206
2331
|
},
|
|
@@ -2208,19 +2333,54 @@ function modToolOptions() {
|
|
|
2208
2333
|
}
|
|
2209
2334
|
|
|
2210
2335
|
|
|
2211
|
-
function
|
|
2336
|
+
function pkgLockLines(entries: any[]): string[] {
|
|
2337
|
+
return entries.map((e) => e.key + ' ' + e.v + ' ' + e.canon)
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
// The renderers verify, tidy and sync share, as the Go port's do.
|
|
2341
|
+
function pkgMismatchLines(mismatched: any[]): string[] {
|
|
2342
|
+
return mismatched.map((m) => 'canon' === m.pin ?
|
|
2343
|
+
m.key + ': pinned ' + m.want + ' but the store means ' +
|
|
2344
|
+
('' === m.got ? 'nothing (it does not evaluate)' : m.got) :
|
|
2345
|
+
m.key + ': pinned ' + m.pin + ' ' + m.want + ' but the store holds ' + m.got)
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
// NOT a fetch: the package may well be sitting in the store. What is
|
|
2349
|
+
// absent is the PIN, and only a sync writes one.
|
|
2350
|
+
function pkgUnlockedLines(unlocked: string[]): string[] {
|
|
2351
|
+
return unlocked.map((key) => key + ': not in the lockfile (run: aontu sync)')
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
function pkgForbiddenLines(forbidden: string[]): string[] {
|
|
2355
|
+
return forbidden.map((f) => f + ': not admitted in a package')
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
|
|
2359
|
+
function pkgText(sub: string, report: any): string {
|
|
2212
2360
|
const lines = ['verdict: ' + report.verdict]
|
|
2213
2361
|
|
|
2214
2362
|
if ('manifest' === sub) {
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
lines.push('
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2363
|
+
const m = report.manifest
|
|
2364
|
+
if (null != m) {
|
|
2365
|
+
lines.push(m.package + ' ' + m.version + ' ' + m.publish)
|
|
2366
|
+
lines.push('archive: ' + m.archive.digest + ' (' + m.archive.files.length +
|
|
2367
|
+
' files, ' + m.archive.size + ' bytes)')
|
|
2368
|
+
for (const mod of m.modules) {
|
|
2369
|
+
lines.push('module: ' + mod.path + ' ' + mod.main + ' ' + mod.canon)
|
|
2370
|
+
}
|
|
2371
|
+
for (const key of Object.keys(m.deps).sort(cmpCodePoint)) {
|
|
2372
|
+
lines.push('dep: ' + key + ' ' + m.deps[key].v +
|
|
2373
|
+
(null == m.deps[key].pkg ? '' : ' (' + m.deps[key].pkg + ')'))
|
|
2374
|
+
}
|
|
2375
|
+
for (const v of m.retract ?? []) {
|
|
2376
|
+
lines.push('retract: ' + v)
|
|
2377
|
+
}
|
|
2378
|
+
if (null != m.moved) {
|
|
2379
|
+
lines.push('moved: ' + m.moved)
|
|
2380
|
+
}
|
|
2381
|
+
for (const f of m.archive.files) {
|
|
2382
|
+
lines.push('file: ' + f.path + ' ' + f.digest + ' ' + f.size)
|
|
2383
|
+
}
|
|
2224
2384
|
}
|
|
2225
2385
|
for (const f of report.findings) {
|
|
2226
2386
|
lines.push(f.path + ': ' + f.message)
|
|
@@ -2228,48 +2388,313 @@ function modText(sub: string, report: any): string {
|
|
|
2228
2388
|
for (const miss of report.missing) {
|
|
2229
2389
|
lines.push(miss + ': missing')
|
|
2230
2390
|
}
|
|
2391
|
+
lines.push(...pkgForbiddenLines(report.forbidden))
|
|
2231
2392
|
return lines.join('\n')
|
|
2232
2393
|
}
|
|
2233
2394
|
|
|
2234
2395
|
if ('verify' === sub) {
|
|
2235
|
-
for (const
|
|
2236
|
-
lines.push(
|
|
2396
|
+
for (const key of report.verified) {
|
|
2397
|
+
lines.push(key + ': verified')
|
|
2398
|
+
}
|
|
2399
|
+
lines.push(...pkgMismatchLines(report.mismatched), ...pkgUnlockedLines(report.unlocked))
|
|
2400
|
+
for (const miss of report.missing) {
|
|
2401
|
+
lines.push(miss + ': not fetched (run: aontu sync)')
|
|
2237
2402
|
}
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2403
|
+
return lines.join('\n')
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
if ('refreeze' === sub) {
|
|
2407
|
+
for (const r of report.repinned) {
|
|
2408
|
+
lines.push(r.key + ': ' + r.from + ' -> ' + r.to)
|
|
2241
2409
|
}
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2410
|
+
for (const key of report.unchanged) {
|
|
2411
|
+
lines.push(key + ': unchanged')
|
|
2412
|
+
}
|
|
2413
|
+
for (const bad of report.unevaluable) {
|
|
2414
|
+
lines.push(bad + ': does not evaluate on its own; nothing to pin')
|
|
2246
2415
|
}
|
|
2247
2416
|
for (const miss of report.missing) {
|
|
2248
|
-
lines.push(miss + ': not fetched (run: aontu
|
|
2417
|
+
lines.push(miss + ': not fetched (run: aontu sync)')
|
|
2249
2418
|
}
|
|
2250
2419
|
return lines.join('\n')
|
|
2251
2420
|
}
|
|
2252
2421
|
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2422
|
+
if ('tree' === sub) {
|
|
2423
|
+
const byKey = new Map(report.nodes.map((n: any) => [n.key, n]))
|
|
2424
|
+
const seen = new Set<string>()
|
|
2425
|
+
const walk = (key: string, depth: number): void => {
|
|
2426
|
+
const node: any = byKey.get(key)
|
|
2427
|
+
const again = seen.has(key)
|
|
2428
|
+
seen.add(key)
|
|
2429
|
+
lines.push(' '.repeat(depth) + key +
|
|
2430
|
+
(null == node || '' === node.v ? '' : ' ' + node.v) +
|
|
2431
|
+
(again ? ' (above)' : null == node ? ' (not locked)' : ''))
|
|
2432
|
+
if (again || null == node) {
|
|
2433
|
+
return
|
|
2434
|
+
}
|
|
2435
|
+
for (const dep of node.deps) {
|
|
2436
|
+
walk(dep, depth + 1)
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
walk(report.root, 0)
|
|
2440
|
+
for (const miss of report.missing) {
|
|
2441
|
+
lines.push(miss + ': not fetched (run: aontu sync)')
|
|
2442
|
+
}
|
|
2443
|
+
return lines.join('\n')
|
|
2257
2444
|
}
|
|
2258
|
-
|
|
2445
|
+
|
|
2446
|
+
const done: any[] = 'tidy' === sub ? pkgLockLines(report.lock) : report.vendored
|
|
2447
|
+
lines.push(...done)
|
|
2448
|
+
// A package that is PRESENT but does not stand up. Named separately
|
|
2259
2449
|
// from a missing one because the repair is different: a fetch cannot
|
|
2260
|
-
// help, the
|
|
2450
|
+
// help, the package itself has to be fixed (or its own dependencies
|
|
2261
2451
|
// vendored beside it). Before the missing tail, as the Go port's
|
|
2262
2452
|
// shared renderer orders them.
|
|
2263
2453
|
for (const bad of report.unevaluable ?? []) {
|
|
2264
2454
|
lines.push(bad + ': does not evaluate on its own; nothing to pin')
|
|
2265
2455
|
}
|
|
2456
|
+
lines.push(...pkgForbiddenLines(report.forbidden ?? []))
|
|
2266
2457
|
for (const miss of report.missing) {
|
|
2267
|
-
lines.push(miss + ': not fetched (run: aontu
|
|
2458
|
+
lines.push(miss + ': not fetched (run: aontu sync)')
|
|
2268
2459
|
}
|
|
2269
2460
|
return lines.join('\n')
|
|
2270
2461
|
}
|
|
2271
2462
|
|
|
2272
2463
|
|
|
2464
|
+
// The two `pkg` subcommands that reach a repository.
|
|
2465
|
+
async function runPkgNet(sub: string, dir: string, args: PkgArgs,
|
|
2466
|
+
opts: PkgToolOptions, servers: Servers, io: Io): Promise<number> {
|
|
2467
|
+
if ('serve' === sub) {
|
|
2468
|
+
const served = await startServe({
|
|
2469
|
+
dir: resolve(dir), upstream: args.upstream,
|
|
2470
|
+
listen: args.listen ?? '127.0.0.1:8017', http: servers.http(),
|
|
2471
|
+
})
|
|
2472
|
+
io.out('serving ' + resolve(dir) + ' at ' + served.url + '\n' +
|
|
2473
|
+
args.upstream.map((u) => 'upstream: ' + u + '\n').join(''))
|
|
2474
|
+
await servers.serve(served)
|
|
2475
|
+
await served.close()
|
|
2476
|
+
return 0
|
|
2477
|
+
}
|
|
2478
|
+
if (null == opts.cache) {
|
|
2479
|
+
io.err(NO_CACHE)
|
|
2480
|
+
return 2
|
|
2481
|
+
}
|
|
2482
|
+
const report = await pkgOutdated(dir, opts, servers.http(), { base: netBase(args) })
|
|
2483
|
+
io.out(pkgReportText('pkg outdated', args.format, report) + '\n')
|
|
2484
|
+
return PKG_EXIT[report.verdict]
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
const NO_CACHE = 'aontu: this verb reads and writes the user cache, which a ' +
|
|
2488
|
+
'confined run (--trust root) does not reach and this host does not name ' +
|
|
2489
|
+
'(set XDG_CACHE_HOME or HOME)\n'
|
|
2490
|
+
|
|
2491
|
+
function netBase(args: PkgArgs): string[] | undefined {
|
|
2492
|
+
return 0 === args.base.length ? undefined : args.base
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
const PACKAGE_VERBS = ['sync', 'add', 'get', 'remove', 'why', 'publish']
|
|
2496
|
+
|
|
2497
|
+
const PACKAGE_HELP: Record<string, string> = {
|
|
2498
|
+
sync: 'aontu sync [--frozen] [dir] (try --help)',
|
|
2499
|
+
add: 'aontu add <pkg> [dir] (try --help)',
|
|
2500
|
+
get: 'aontu get <pkg>[@<version>] [dir] (try --help)',
|
|
2501
|
+
remove: 'aontu remove <pkg> [dir] (try --help)',
|
|
2502
|
+
why: 'aontu why <pkg> [dir] (try --help)',
|
|
2503
|
+
publish: 'aontu publish [--yes] [--to <dir>] [--key <file>] [dir] (try --help)',
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
// The top-level package verbs (ADR-039 part 5). Every one but `why`
|
|
2507
|
+
// reaches a repository, so every one runs behind the seam.
|
|
2508
|
+
async function runPackageVerb(verb: string, argv: string[], servers: Servers):
|
|
2509
|
+
Promise<number> {
|
|
2510
|
+
const io = servers.io ?? PROCESS_IO
|
|
2511
|
+
const trusted = takeTrust(argv, io)
|
|
2512
|
+
if (null == trusted) {
|
|
2513
|
+
return 2
|
|
2514
|
+
}
|
|
2515
|
+
const args = parsePkgArgs(trusted.argv, verb, io)
|
|
2516
|
+
if (null == args) {
|
|
2517
|
+
return '-h' === trusted.argv[0] || trusted.argv.includes('--help') ? 0 : 2
|
|
2518
|
+
}
|
|
2519
|
+
const wantsPkg = 'sync' !== verb && 'publish' !== verb
|
|
2520
|
+
const dir = args.rest[wantsPkg ? 1 : 0] ?? '.'
|
|
2521
|
+
if (args.rest.length > (wantsPkg ? 2 : 1) || (wantsPkg && 0 === args.rest.length)) {
|
|
2522
|
+
io.err('aontu: ' + verb + (wantsPkg ? ' needs a package' : ' takes a directory') +
|
|
2523
|
+
'\n' + PACKAGE_HELP[verb] + '\n')
|
|
2524
|
+
return 2
|
|
2525
|
+
}
|
|
2526
|
+
nameOldLayout(dir, io)
|
|
2527
|
+
const opts = pkgToolOptions(trusted.trust, resolve(dir))
|
|
2528
|
+
const http = servers.http()
|
|
2529
|
+
|
|
2530
|
+
if ('why' === verb) {
|
|
2531
|
+
const report = pkgWhy(dir, opts, args.rest[0])
|
|
2532
|
+
io.out(pkgReportText('why', args.format, report) + '\n')
|
|
2533
|
+
return PKG_EXIT[report.verdict]
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
if ('publish' === verb) {
|
|
2537
|
+
for (const f of [args.key, args.token]) {
|
|
2538
|
+
if (null != f && !existsSync(f)) {
|
|
2539
|
+
io.err('aontu: cannot read ' + f + '\n')
|
|
2540
|
+
return 2
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
if (true === args.yes && null == args.key) {
|
|
2544
|
+
io.err('aontu: publish --yes needs --key <file>, the Ed25519 key that signs\n')
|
|
2545
|
+
return 2
|
|
2546
|
+
}
|
|
2547
|
+
const report = await pkgPublish(dir, opts, http, {
|
|
2548
|
+
yes: args.yes, to: null == args.to ? undefined : resolve(args.to), key: args.key,
|
|
2549
|
+
token: args.token, against: args.against, base: netBase(args), write: args.write,
|
|
2550
|
+
})
|
|
2551
|
+
io.out(pkgReportText('publish', args.format, report) + '\n')
|
|
2552
|
+
return PKG_EXIT[report.verdict]
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2555
|
+
if (null == opts.cache) {
|
|
2556
|
+
io.err(NO_CACHE)
|
|
2557
|
+
return 2
|
|
2558
|
+
}
|
|
2559
|
+
const net = { base: netBase(args) }
|
|
2560
|
+
const report: PkgSyncReport | PkgChangeReport | string =
|
|
2561
|
+
'sync' === verb ? await pkgSync(dir, opts, http, { ...net, frozen: args.frozen }) :
|
|
2562
|
+
'remove' === verb ? await pkgRemove(dir, opts, http, args.rest[0], net) :
|
|
2563
|
+
await pkgGet(dir, opts, http, args.rest[0], { ...net, mode: verb as 'add' | 'get' })
|
|
2564
|
+
if ('string' === typeof report) {
|
|
2565
|
+
io.err('aontu: ' + report + '\n')
|
|
2566
|
+
return 2
|
|
2567
|
+
}
|
|
2568
|
+
io.out(pkgReportText(verb, args.format, report) + '\n')
|
|
2569
|
+
return PKG_EXIT[report.verdict]
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2572
|
+
|
|
2573
|
+
function pkgReportText(verb: string, fmt: SubsumeFormat, report: any): string {
|
|
2574
|
+
return 'json' === fmt ?
|
|
2575
|
+
exactJSON({ aontu: { version: version(), verb }, ...report }, 2) :
|
|
2576
|
+
pkgNetText(verb, report)
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
|
|
2580
|
+
function pkgNetText(verb: string, report: any): string {
|
|
2581
|
+
const lines = ['verdict: ' + report.verdict]
|
|
2582
|
+
const tail = (): void => {
|
|
2583
|
+
for (const e of report.events ?? []) {
|
|
2584
|
+
lines.push(e.code + ': ' + e.message)
|
|
2585
|
+
}
|
|
2586
|
+
if (null != report.refusal) {
|
|
2587
|
+
lines.push('refused: ' + report.refusal.code + ': ' + report.refusal.message)
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
if ('why' === verb) {
|
|
2592
|
+
const why = report as PkgWhyReport
|
|
2593
|
+
for (const p of why.paths) {
|
|
2594
|
+
lines.push(p.join(' -> '))
|
|
2595
|
+
}
|
|
2596
|
+
if ('missing' === why.verdict) {
|
|
2597
|
+
lines.push(why.pkg + ': not in the closure')
|
|
2598
|
+
}
|
|
2599
|
+
return lines.join('\n')
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2602
|
+
if ('publish' === verb) {
|
|
2603
|
+
const pub = report as PkgPublishReport
|
|
2604
|
+
lines.push(...pkgText('manifest', pub).split('\n').slice(1))
|
|
2605
|
+
if (null != pub.digest) {
|
|
2606
|
+
lines.push('digest: ' + pub.digest)
|
|
2607
|
+
}
|
|
2608
|
+
if (null != pub.signer) {
|
|
2609
|
+
lines.push('signer: ' + pub.signer)
|
|
2610
|
+
}
|
|
2611
|
+
if (null != pub.against) {
|
|
2612
|
+
lines.push('against: ' + pub.against)
|
|
2613
|
+
}
|
|
2614
|
+
if (null != pub.to) {
|
|
2615
|
+
lines.push('to: ' + pub.to)
|
|
2616
|
+
}
|
|
2617
|
+
if (null != pub.write) {
|
|
2618
|
+
lines.push('write: ' + pub.write + PUBLISH_PATH)
|
|
2619
|
+
}
|
|
2620
|
+
if ('dry-run' === pub.verdict) {
|
|
2621
|
+
lines.push('dry run: nothing sent (add --yes)')
|
|
2622
|
+
}
|
|
2623
|
+
if ('sent' === pub.verdict) {
|
|
2624
|
+
lines.push('sent')
|
|
2625
|
+
}
|
|
2626
|
+
tail()
|
|
2627
|
+
return lines.join('\n')
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2630
|
+
if ('pkg outdated' === verb) {
|
|
2631
|
+
const out = report as PkgOutdatedReport
|
|
2632
|
+
for (const e of out.locked) {
|
|
2633
|
+
if (0 < versionCompare(e.newest, e.v)) {
|
|
2634
|
+
lines.push(e.key + ' ' + e.v + ' -> ' + e.newest)
|
|
2635
|
+
for (const m of e.moves) {
|
|
2636
|
+
lines.push(' ' + m)
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
else {
|
|
2640
|
+
lines.push(e.key + ' ' + e.v + ': current')
|
|
2641
|
+
}
|
|
2642
|
+
if (null != e.retracted) {
|
|
2643
|
+
lines.push(e.key + ' ' + e.v + ': retracted by ' + e.retracted)
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
tail()
|
|
2647
|
+
return lines.join('\n')
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
const sync = report as PkgChangeReport
|
|
2651
|
+
if (null != sync.change) {
|
|
2652
|
+
lines.push('change: ' + sync.change)
|
|
2653
|
+
}
|
|
2654
|
+
for (const f of sync.fetched) {
|
|
2655
|
+
lines.push('fetched: ' + f)
|
|
2656
|
+
}
|
|
2657
|
+
lines.push(...pkgLockLines(sync.lock))
|
|
2658
|
+
for (const bad of sync.unevaluable) {
|
|
2659
|
+
lines.push(bad + ': does not evaluate on its own; nothing to pin')
|
|
2660
|
+
}
|
|
2661
|
+
lines.push(...pkgForbiddenLines(sync.forbidden), ...pkgMismatchLines(sync.mismatched),
|
|
2662
|
+
...pkgUnlockedLines(sync.unlocked))
|
|
2663
|
+
for (const miss of sync.missing) {
|
|
2664
|
+
lines.push(miss + ': not fetched (run: aontu sync)')
|
|
2665
|
+
}
|
|
2666
|
+
for (const c of sync.changes) {
|
|
2667
|
+
lines.push('lockfile would change: ' + c)
|
|
2668
|
+
}
|
|
2669
|
+
tail()
|
|
2670
|
+
return lines.join('\n')
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
|
|
2674
|
+
const MODEL_HELP = 'aontu model get|why|set ... (try --help)'
|
|
2675
|
+
|
|
2676
|
+
// One document, interrogated or edited (ADR-039 part 5).
|
|
2677
|
+
function runModel(argv: string[]): number {
|
|
2678
|
+
const sub = argv[0]
|
|
2679
|
+
if ('-h' === sub || '--help' === sub) {
|
|
2680
|
+
process.stdout.write(HELP)
|
|
2681
|
+
return 0
|
|
2682
|
+
}
|
|
2683
|
+
if ('get' === sub) {
|
|
2684
|
+
return runGet(argv.slice(1))
|
|
2685
|
+
}
|
|
2686
|
+
if ('why' === sub) {
|
|
2687
|
+
return runWhy(argv.slice(1))
|
|
2688
|
+
}
|
|
2689
|
+
if ('set' === sub) {
|
|
2690
|
+
return runSet(argv.slice(1))
|
|
2691
|
+
}
|
|
2692
|
+
process.stderr.write(`aontu: model needs get, why or set\n${MODEL_HELP}\n`)
|
|
2693
|
+
return 2
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
|
|
2697
|
+
|
|
2273
2698
|
function vacuous(what: string, why: string): void {
|
|
2274
2699
|
process.stderr.write(`aontu: ${what}: ${why}\n`)
|
|
2275
2700
|
}
|
|
@@ -2341,6 +2766,120 @@ function runRelations(argv: string[]): number {
|
|
|
2341
2766
|
return RELATIONS_EXIT[report.verdict]
|
|
2342
2767
|
}
|
|
2343
2768
|
|
|
2769
|
+
const TRACE_HELP =
|
|
2770
|
+
'aontu trace [--at <path>] [--format json] [--marker <token>] ' +
|
|
2771
|
+
'[--profile <file>] <file>'
|
|
2772
|
+
|
|
2773
|
+
|
|
2774
|
+
// WHAT WROTE THIS LINE. Every piece a rule stamped, under the
|
|
2775
|
+
// component tree, with the file it reached, the rule set that wrote it
|
|
2776
|
+
// and the model node the dispatch matched.
|
|
2777
|
+
function runTrace(argv: string[]): number {
|
|
2778
|
+
const trusted = takeTrust(argv)
|
|
2779
|
+
if (null == trusted) {
|
|
2780
|
+
return 2
|
|
2781
|
+
}
|
|
2782
|
+
argv = trusted.argv
|
|
2783
|
+
const trust = trusted.trust
|
|
2784
|
+
const rest: string[] = []
|
|
2785
|
+
const profileFiles: string[] = []
|
|
2786
|
+
let format: 'text' | 'json' = 'text'
|
|
2787
|
+
let at: string | undefined = undefined
|
|
2788
|
+
let marker: string | undefined = undefined
|
|
2789
|
+
|
|
2790
|
+
for (let i = 0; i < argv.length; i++) {
|
|
2791
|
+
const arg = argv[i]
|
|
2792
|
+
if ('-h' === arg || '--help' === arg) {
|
|
2793
|
+
process.stdout.write(HELP)
|
|
2794
|
+
return 0
|
|
2795
|
+
}
|
|
2796
|
+
if ('--format' === arg) {
|
|
2797
|
+
const f = argv[++i]
|
|
2798
|
+
if ('text' !== f && 'json' !== f) {
|
|
2799
|
+
process.stderr.write('aontu: --format needs text or json\n')
|
|
2800
|
+
return 2
|
|
2801
|
+
}
|
|
2802
|
+
format = f
|
|
2803
|
+
}
|
|
2804
|
+
else if ('--at' === arg) {
|
|
2805
|
+
at = argv[++i]
|
|
2806
|
+
if (null == at) {
|
|
2807
|
+
process.stderr.write('aontu: --at needs a path\n')
|
|
2808
|
+
return 2
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
else if ('--marker' === arg) {
|
|
2812
|
+
marker = argv[++i]
|
|
2813
|
+
if (null == marker) {
|
|
2814
|
+
process.stderr.write('aontu: --marker needs a token\n')
|
|
2815
|
+
return 2
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
else if ('--profile' === arg) {
|
|
2819
|
+
const pf = argv[++i]
|
|
2820
|
+
if (null == pf) {
|
|
2821
|
+
process.stderr.write('aontu: --profile needs a file\n')
|
|
2822
|
+
return 2
|
|
2823
|
+
}
|
|
2824
|
+
profileFiles.push(pf)
|
|
2825
|
+
}
|
|
2826
|
+
else if (arg.startsWith('-')) {
|
|
2827
|
+
process.stderr.write(`aontu: unknown trace option ${arg} (try --help)\n`)
|
|
2828
|
+
return 2
|
|
2829
|
+
}
|
|
2830
|
+
else {
|
|
2831
|
+
rest.push(arg)
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
if (1 !== rest.length) {
|
|
2836
|
+
process.stderr.write(`aontu: trace needs one file\n${TRACE_HELP}\n`)
|
|
2837
|
+
return 2
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
let src: string
|
|
2841
|
+
try {
|
|
2842
|
+
src = readFileSync(rest[0], 'utf8')
|
|
2843
|
+
}
|
|
2844
|
+
catch (err: any) {
|
|
2845
|
+
process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`)
|
|
2846
|
+
return 2
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2849
|
+
const declared = loadProfiles(profileFiles, trust)
|
|
2850
|
+
if ('number' === typeof declared) {
|
|
2851
|
+
return declared
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
// A GENERATOR IS AN ENTRY, not a preprocessing step: the file whose
|
|
2855
|
+
// provenance is asked for is the one the author edits.
|
|
2856
|
+
if (!rest[0].endsWith('.aon')) {
|
|
2857
|
+
src = desugarTemplate(src, marker ??
|
|
2858
|
+
markerFromProfiles(declared, rest[0]) ?? markerFor(rest[0]))
|
|
2859
|
+
}
|
|
2860
|
+
|
|
2861
|
+
const report = traceRun(src, {
|
|
2862
|
+
path: rest[0], at,
|
|
2863
|
+
...verbOpts(trust, entryRootOf(rest[0])),
|
|
2864
|
+
})
|
|
2865
|
+
if ('error' === report.verdict) {
|
|
2866
|
+
// An error report always carries its findings.
|
|
2867
|
+
const errors = report.errors as VetFinding[]
|
|
2868
|
+
process.stderr.write(errors.map(renderFinding).join('\n') + '\n')
|
|
2869
|
+
return 4
|
|
2870
|
+
}
|
|
2871
|
+
if ('json' === format) {
|
|
2872
|
+
process.stdout.write(JSON.stringify({ trace: report.trace }) + '\n')
|
|
2873
|
+
return 0
|
|
2874
|
+
}
|
|
2875
|
+
for (const e of report.trace) {
|
|
2876
|
+
process.stdout.write(
|
|
2877
|
+
[e.file, e.at, e.node, e.rule].join('\t') + '\n')
|
|
2878
|
+
}
|
|
2879
|
+
return 0
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
|
|
2344
2883
|
function runReaches(argv: string[]): number {
|
|
2345
2884
|
const trusted = takeTrust(argv)
|
|
2346
2885
|
if (null == trusted) {
|
|
@@ -2955,302 +3494,6 @@ function runJsonSchema(argv: string[]): number {
|
|
|
2955
3494
|
}
|
|
2956
3495
|
|
|
2957
3496
|
|
|
2958
|
-
const RENDER_HELP =
|
|
2959
|
-
'aontu render [--at <path>] [--profile <file>]... [--unit <path>] ' +
|
|
2960
|
-
'[--stdout | --out <dir> | --check <dir> | --coverage] ' +
|
|
2961
|
-
'[--coverage-at <path>] [--strict] [--marker <token>] <file> (try --help)'
|
|
2962
|
-
|
|
2963
|
-
function runRender(argv: string[]): number {
|
|
2964
|
-
const trusted = takeTrust(argv)
|
|
2965
|
-
if (null == trusted) {
|
|
2966
|
-
return 2
|
|
2967
|
-
}
|
|
2968
|
-
argv = trusted.argv
|
|
2969
|
-
const trust = trusted.trust
|
|
2970
|
-
const files: string[] = []
|
|
2971
|
-
const profileFiles: string[] = []
|
|
2972
|
-
let format: SubsumeFormat = 'text'
|
|
2973
|
-
let at: string | undefined = undefined
|
|
2974
|
-
let unit: string | undefined = undefined
|
|
2975
|
-
let out: string | undefined = undefined
|
|
2976
|
-
let check: string | undefined = undefined
|
|
2977
|
-
let toStdout = false
|
|
2978
|
-
let strict = false
|
|
2979
|
-
let coverage = false
|
|
2980
|
-
let coverageAt: string | undefined = undefined
|
|
2981
|
-
let marker: string | undefined = undefined
|
|
2982
|
-
|
|
2983
|
-
for (let i = 0; i < argv.length; i++) {
|
|
2984
|
-
const arg = argv[i]
|
|
2985
|
-
if ('-h' === arg || '--help' === arg) {
|
|
2986
|
-
process.stdout.write(HELP)
|
|
2987
|
-
return 0
|
|
2988
|
-
}
|
|
2989
|
-
if ('--format' === arg) {
|
|
2990
|
-
const f = argv[++i]
|
|
2991
|
-
if ('text' !== f && 'json' !== f) {
|
|
2992
|
-
process.stderr.write('aontu: --format needs text or json\n')
|
|
2993
|
-
return 2
|
|
2994
|
-
}
|
|
2995
|
-
format = f
|
|
2996
|
-
}
|
|
2997
|
-
else if ('--at' === arg) {
|
|
2998
|
-
at = argv[++i]
|
|
2999
|
-
if (null == at) {
|
|
3000
|
-
process.stderr.write('aontu: --at needs a path\n')
|
|
3001
|
-
return 2
|
|
3002
|
-
}
|
|
3003
|
-
}
|
|
3004
|
-
else if ('--unit' === arg) {
|
|
3005
|
-
unit = argv[++i]
|
|
3006
|
-
if (null == unit) {
|
|
3007
|
-
process.stderr.write('aontu: --unit needs a unit path\n')
|
|
3008
|
-
return 2
|
|
3009
|
-
}
|
|
3010
|
-
}
|
|
3011
|
-
else if ('--profile' === arg) {
|
|
3012
|
-
const pf = argv[++i]
|
|
3013
|
-
if (null == pf) {
|
|
3014
|
-
process.stderr.write('aontu: --profile needs a file\n')
|
|
3015
|
-
return 2
|
|
3016
|
-
}
|
|
3017
|
-
profileFiles.push(pf)
|
|
3018
|
-
}
|
|
3019
|
-
else if ('--out' === arg) {
|
|
3020
|
-
out = argv[++i]
|
|
3021
|
-
if (null == out) {
|
|
3022
|
-
process.stderr.write('aontu: --out needs a directory\n')
|
|
3023
|
-
return 2
|
|
3024
|
-
}
|
|
3025
|
-
}
|
|
3026
|
-
else if ('--check' === arg) {
|
|
3027
|
-
check = argv[++i]
|
|
3028
|
-
if (null == check) {
|
|
3029
|
-
process.stderr.write('aontu: --check needs a directory\n')
|
|
3030
|
-
return 2
|
|
3031
|
-
}
|
|
3032
|
-
}
|
|
3033
|
-
else if ('--stdout' === arg) {
|
|
3034
|
-
toStdout = true
|
|
3035
|
-
}
|
|
3036
|
-
else if ('--coverage' === arg) {
|
|
3037
|
-
coverage = true
|
|
3038
|
-
}
|
|
3039
|
-
else if ('--marker' === arg) {
|
|
3040
|
-
marker = argv[++i]
|
|
3041
|
-
if (null == marker) {
|
|
3042
|
-
process.stderr.write('aontu: --marker needs a token\n')
|
|
3043
|
-
return 2
|
|
3044
|
-
}
|
|
3045
|
-
}
|
|
3046
|
-
else if ('--coverage-at' === arg) {
|
|
3047
|
-
coverageAt = argv[++i]
|
|
3048
|
-
if (null == coverageAt) {
|
|
3049
|
-
process.stderr.write('aontu: --coverage-at needs a path\n')
|
|
3050
|
-
return 2
|
|
3051
|
-
}
|
|
3052
|
-
}
|
|
3053
|
-
else if ('--strict' === arg) {
|
|
3054
|
-
strict = true
|
|
3055
|
-
}
|
|
3056
|
-
else if (arg.startsWith('-')) {
|
|
3057
|
-
process.stderr.write(
|
|
3058
|
-
`aontu: unknown render option ${arg} (try --help)\n`)
|
|
3059
|
-
return 2
|
|
3060
|
-
}
|
|
3061
|
-
else {
|
|
3062
|
-
files.push(arg)
|
|
3063
|
-
}
|
|
3064
|
-
}
|
|
3065
|
-
|
|
3066
|
-
if (1 !== files.length) {
|
|
3067
|
-
process.stderr.write(`aontu: render needs one file\n${RENDER_HELP}\n`)
|
|
3068
|
-
return 2
|
|
3069
|
-
}
|
|
3070
|
-
const modes = [toStdout, undefined !== out, undefined !== check, coverage]
|
|
3071
|
-
.filter((on) => on).length
|
|
3072
|
-
if (1 < modes) {
|
|
3073
|
-
process.stderr.write(
|
|
3074
|
-
'aontu: render takes one of --stdout, --out, --check or --coverage\n')
|
|
3075
|
-
return 2
|
|
3076
|
-
}
|
|
3077
|
-
// A NARROWER MEASURE NEEDS SOMETHING TO NARROW. `--coverage-at`
|
|
3078
|
-
// without `--coverage` asks for a report the run does not compute,
|
|
3079
|
-
// and answering silently would be the wrong half of the request.
|
|
3080
|
-
if (undefined !== coverageAt && !coverage) {
|
|
3081
|
-
process.stderr.write('aontu: --coverage-at needs --coverage\n')
|
|
3082
|
-
return 2
|
|
3083
|
-
}
|
|
3084
|
-
|
|
3085
|
-
let src: string
|
|
3086
|
-
try {
|
|
3087
|
-
src = readFileSync(files[0], 'utf8')
|
|
3088
|
-
}
|
|
3089
|
-
catch (err: any) {
|
|
3090
|
-
process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`)
|
|
3091
|
-
return 2
|
|
3092
|
-
}
|
|
3093
|
-
|
|
3094
|
-
const loadedProfiles = loadProfiles(profileFiles, trust)
|
|
3095
|
-
if ('number' === typeof loadedProfiles) {
|
|
3096
|
-
return loadedProfiles
|
|
3097
|
-
}
|
|
3098
|
-
const profiles = loadedProfiles
|
|
3099
|
-
|
|
3100
|
-
if (!files[0].endsWith('.aon')) {
|
|
3101
|
-
src = desugarTemplate(src, marker ??
|
|
3102
|
-
markerFromProfiles(profiles, files[0]) ?? markerFor(files[0]))
|
|
3103
|
-
}
|
|
3104
|
-
|
|
3105
|
-
// A RENDER WITH NO PROFILE PRODUCES NO UNITS, and said so with zero
|
|
3106
|
-
// bytes and exit 0. The profile is what maps a model onto a
|
|
3107
|
-
// language, so without one there is nothing for the renderer to
|
|
3108
|
-
// write -- which is a usable answer only if the caller is told.
|
|
3109
|
-
const noProfiles = 0 === profiles.length
|
|
3110
|
-
const report = render(src, {
|
|
3111
|
-
at, unit, strict, profiles, path: files[0],
|
|
3112
|
-
coverage, coverageAt,
|
|
3113
|
-
// THE JSON REPORT CARRIES THE TRACE (D9), which is what the shape
|
|
3114
|
-
// there has always said; a text run computes it only when the
|
|
3115
|
-
// coverage report needs it.
|
|
3116
|
-
trace: 'json' === format,
|
|
3117
|
-
...verbOpts(trust, entryRootOf(files[0])),
|
|
3118
|
-
})
|
|
3119
|
-
|
|
3120
|
-
// Said once, whatever the format: stdout stays the report.
|
|
3121
|
-
if ('error' !== report.verdict && 0 === report.units.length) {
|
|
3122
|
-
vacuous('nothing was rendered',
|
|
3123
|
-
noProfiles
|
|
3124
|
-
? 'no profile was given, and the document declares none' +
|
|
3125
|
-
' (see aontu help tasks)'
|
|
3126
|
-
: 'the document produced no units under this profile')
|
|
3127
|
-
}
|
|
3128
|
-
|
|
3129
|
-
if ('json' === format) {
|
|
3130
|
-
process.stdout.write(exactJSON({
|
|
3131
|
-
aontu: { version: version(), verb: 'render' },
|
|
3132
|
-
verdict: report.verdict,
|
|
3133
|
-
units: report.units,
|
|
3134
|
-
lossy: report.lossy,
|
|
3135
|
-
...(null == report.errors ? {} : { errors: report.errors }),
|
|
3136
|
-
...(null == report.trace ? {} : { trace: report.trace }),
|
|
3137
|
-
...(null == report.coverage ? {} : { coverage: report.coverage }),
|
|
3138
|
-
}, 2) + '\n')
|
|
3139
|
-
return renderExit(report, 0)
|
|
3140
|
-
}
|
|
3141
|
-
if ('error' === report.verdict) {
|
|
3142
|
-
process.stderr.write(
|
|
3143
|
-
(report.errors as VetFinding[]).map(renderFinding).join('\n') + '\n')
|
|
3144
|
-
return renderExit(report, 0)
|
|
3145
|
-
}
|
|
3146
|
-
|
|
3147
|
-
let drift = 0
|
|
3148
|
-
if (toStdout) {
|
|
3149
|
-
// ONE UNIT'S BYTES AND NOTHING ELSE, so the output can be piped
|
|
3150
|
-
// into a formatter or a file.
|
|
3151
|
-
if (1 !== report.units.length) {
|
|
3152
|
-
process.stderr.write(
|
|
3153
|
-
'aontu: --stdout needs exactly one unit, and the instance has ' +
|
|
3154
|
-
`${report.units.length}; --unit names one\n`)
|
|
3155
|
-
return 2
|
|
3156
|
-
}
|
|
3157
|
-
process.stdout.write(report.units[0].text)
|
|
3158
|
-
}
|
|
3159
|
-
else if (undefined !== out) {
|
|
3160
|
-
// EVERY UNIT BELOW <dir>, OR NOTHING: every unit rendered first
|
|
3161
|
-
// (the report above), and no file touched unless all did. The
|
|
3162
|
-
// directory is realpath-confined; a unit path is already a relative
|
|
3163
|
-
// descent (render_path refuses the rest), and the check here is
|
|
3164
|
-
// against the symlink inside it. render never deletes.
|
|
3165
|
-
for (const u of report.units) {
|
|
3166
|
-
if (outsideRoot(out, resolve(out, u.path))) {
|
|
3167
|
-
process.stderr.write(`aontu: ${u.path} escapes ${out}\n`)
|
|
3168
|
-
return 2
|
|
3169
|
-
}
|
|
3170
|
-
}
|
|
3171
|
-
for (const u of report.units) {
|
|
3172
|
-
const full = resolve(out, u.path)
|
|
3173
|
-
try {
|
|
3174
|
-
mkdirSync(dirname(full), { recursive: true })
|
|
3175
|
-
writeFileSync(full, u.text, 'utf8')
|
|
3176
|
-
}
|
|
3177
|
-
catch (err: any) {
|
|
3178
|
-
process.stderr.write(`aontu: cannot write ${u.path}: ${err.message}\n`)
|
|
3179
|
-
return 2
|
|
3180
|
-
}
|
|
3181
|
-
process.stderr.write(`wrote ${u.path}\n`)
|
|
3182
|
-
}
|
|
3183
|
-
}
|
|
3184
|
-
else if (undefined !== check) {
|
|
3185
|
-
// RENDER AND COMPARE: a unit whose bytes differ from the file at
|
|
3186
|
-
// <dir>/<path>, or whose file is absent, is drift, listed by path.
|
|
3187
|
-
// The CI form.
|
|
3188
|
-
for (const u of report.units) {
|
|
3189
|
-
let have: string | undefined = undefined
|
|
3190
|
-
try {
|
|
3191
|
-
have = readFileSync(resolve(check, u.path), 'utf8')
|
|
3192
|
-
}
|
|
3193
|
-
catch {
|
|
3194
|
-
// Absent is drift, reported below.
|
|
3195
|
-
}
|
|
3196
|
-
if (undefined === have) {
|
|
3197
|
-
drift++
|
|
3198
|
-
process.stderr.write(`aontu: ${u.path} is missing from ${check}\n`)
|
|
3199
|
-
}
|
|
3200
|
-
else if (have !== u.text) {
|
|
3201
|
-
drift++
|
|
3202
|
-
process.stderr.write(`aontu: ${u.path} differs from the rendered unit\n`)
|
|
3203
|
-
}
|
|
3204
|
-
}
|
|
3205
|
-
}
|
|
3206
|
-
else if (coverage) {
|
|
3207
|
-
// THE COVERAGE REPORT (P7), one line per finding and a count at
|
|
3208
|
-
// the end: dead model first, then the declarations no rule
|
|
3209
|
-
// produced. A clean report is the count line alone.
|
|
3210
|
-
const cov = report.coverage as RenderCoverage
|
|
3211
|
-
for (const d of cov.dead) {
|
|
3212
|
-
process.stdout.write(`dead: ${d}\n`)
|
|
3213
|
-
}
|
|
3214
|
-
for (const u of cov.unruled) {
|
|
3215
|
-
process.stdout.write(`unruled: ${u.unit} ${u.path}\n`)
|
|
3216
|
-
}
|
|
3217
|
-
process.stdout.write(
|
|
3218
|
-
`coverage: ${cov.read.length} path(s) read, ${cov.dead.length} ` +
|
|
3219
|
-
`no output consumed, ${cov.unruled.length} declaration(s) ` +
|
|
3220
|
-
'no rule produced\n')
|
|
3221
|
-
}
|
|
3222
|
-
else {
|
|
3223
|
-
// THE SUMMARY: one line per unit -- its path, its language and its
|
|
3224
|
-
// size -- since several units have no one text to print.
|
|
3225
|
-
for (const u of report.units) {
|
|
3226
|
-
process.stdout.write(`${u.path}\t${u.lang}\t${u.text.length} bytes\n`)
|
|
3227
|
-
}
|
|
3228
|
-
}
|
|
3229
|
-
for (const l of report.lossy) {
|
|
3230
|
-
process.stderr.write(
|
|
3231
|
-
`lossy: ${l.unit} ${l.path} tier ${l.tier} ${l.construct}: ${l.reason}\n`)
|
|
3232
|
-
}
|
|
3233
|
-
return renderExit(report, drift)
|
|
3234
|
-
}
|
|
3235
|
-
|
|
3236
|
-
// D8's exit table over a report: a refused unit path is usage (2), a
|
|
3237
|
-
// strict refusal is lossy (1), any other error is the document's (4);
|
|
3238
|
-
// drift under --check is 1.
|
|
3239
|
-
function renderExit(report: RenderReport, drift: number): number {
|
|
3240
|
-
if ('error' === report.verdict) {
|
|
3241
|
-
const errors = report.errors as VetFinding[]
|
|
3242
|
-
if (errors.every((f) => 'render_path' === f.code)) {
|
|
3243
|
-
return 2
|
|
3244
|
-
}
|
|
3245
|
-
if (errors.every((f) => 'render_strict' === f.code)) {
|
|
3246
|
-
return 1
|
|
3247
|
-
}
|
|
3248
|
-
return 4
|
|
3249
|
-
}
|
|
3250
|
-
return 0 < drift ? 1 : 0
|
|
3251
|
-
}
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
3497
|
const TEMPLATE_HELP =
|
|
3255
3498
|
'aontu template [--resugar] [--check] [--marker <token>] <file> (try --help)'
|
|
3256
3499
|
|
|
@@ -3356,9 +3599,9 @@ function runTemplate(argv: string[]): number {
|
|
|
3356
3599
|
|
|
3357
3600
|
|
|
3358
3601
|
// The profiles named by --profile, vetted, or the exit code that says
|
|
3359
|
-
// why not. A profile is a language declared as data: `
|
|
3360
|
-
// one to a
|
|
3361
|
-
//
|
|
3602
|
+
// why not. A profile is a language declared as data: `template` and
|
|
3603
|
+
// `fmt` match one to a file by the extensions its `template.ext`
|
|
3604
|
+
// names.
|
|
3362
3605
|
function loadProfiles(
|
|
3363
3606
|
profileFiles: string[], trust: TrustArg
|
|
3364
3607
|
): any[] | number {
|
|
@@ -3373,7 +3616,7 @@ function loadProfiles(
|
|
|
3373
3616
|
process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`)
|
|
3374
3617
|
return 2
|
|
3375
3618
|
}
|
|
3376
|
-
const loaded =
|
|
3619
|
+
const loaded = loadProfile(text,
|
|
3377
3620
|
{ path: resolve(pf), ...verbOpts(trust, entryRootOf(pf)) })
|
|
3378
3621
|
if (undefined !== loaded.errors) {
|
|
3379
3622
|
process.stderr.write(loaded.errors.map(renderFinding).join('\n') + '\n')
|
|
@@ -3470,7 +3713,7 @@ function runHash(argv: string[]): number {
|
|
|
3470
3713
|
}
|
|
3471
3714
|
|
|
3472
3715
|
|
|
3473
|
-
const GET_HELP = 'aontu get <path> <file> (try --help)'
|
|
3716
|
+
const GET_HELP = 'aontu model get <path> <file> (try --help)'
|
|
3474
3717
|
|
|
3475
3718
|
function runGet(argv: string[]): number {
|
|
3476
3719
|
const trusted = takeTrust(argv)
|
|
@@ -3516,7 +3759,7 @@ function runGet(argv: string[]): number {
|
|
|
3516
3759
|
format = f
|
|
3517
3760
|
}
|
|
3518
3761
|
else if (arg.startsWith('-')) {
|
|
3519
|
-
process.stderr.write(`aontu: unknown get option ${arg} (try --help)\n`)
|
|
3762
|
+
process.stderr.write(`aontu: unknown model get option ${arg} (try --help)\n`)
|
|
3520
3763
|
return 2
|
|
3521
3764
|
}
|
|
3522
3765
|
else {
|
|
@@ -3525,7 +3768,7 @@ function runGet(argv: string[]): number {
|
|
|
3525
3768
|
}
|
|
3526
3769
|
|
|
3527
3770
|
if (2 !== rest.length) {
|
|
3528
|
-
process.stderr.write(`aontu: get needs a path and one file\n${GET_HELP}\n`)
|
|
3771
|
+
process.stderr.write(`aontu: model get needs a path and one file\n${GET_HELP}\n`)
|
|
3529
3772
|
return 2
|
|
3530
3773
|
}
|
|
3531
3774
|
const [path, file] = rest
|
|
@@ -3553,7 +3796,7 @@ function runGet(argv: string[]): number {
|
|
|
3553
3796
|
})
|
|
3554
3797
|
if ('json' === format) {
|
|
3555
3798
|
process.stdout.write(exactJSON({
|
|
3556
|
-
aontu: { version: version(), verb: 'get' },
|
|
3799
|
+
aontu: { version: version(), verb: 'model get' },
|
|
3557
3800
|
findings: report.findings,
|
|
3558
3801
|
ok: report.ok,
|
|
3559
3802
|
out: report.out,
|
|
@@ -3582,7 +3825,7 @@ function runGet(argv: string[]): number {
|
|
|
3582
3825
|
// The positive twin of the vet report: errors explain what failed to
|
|
3583
3826
|
// unify, this explains what did.
|
|
3584
3827
|
|
|
3585
|
-
const WHY_HELP = 'aontu why <path> <file> (try --help)'
|
|
3828
|
+
const WHY_HELP = 'aontu model why <path> <file> (try --help)'
|
|
3586
3829
|
|
|
3587
3830
|
function runWhy(argv: string[]): number {
|
|
3588
3831
|
const trusted = takeTrust(argv)
|
|
@@ -3609,7 +3852,7 @@ function runWhy(argv: string[]): number {
|
|
|
3609
3852
|
format = f
|
|
3610
3853
|
}
|
|
3611
3854
|
else if (arg.startsWith('-')) {
|
|
3612
|
-
process.stderr.write(`aontu: unknown why option ${arg} (try --help)\n`)
|
|
3855
|
+
process.stderr.write(`aontu: unknown model why option ${arg} (try --help)\n`)
|
|
3613
3856
|
return 2
|
|
3614
3857
|
}
|
|
3615
3858
|
else {
|
|
@@ -3618,7 +3861,7 @@ function runWhy(argv: string[]): number {
|
|
|
3618
3861
|
}
|
|
3619
3862
|
|
|
3620
3863
|
if (2 !== rest.length) {
|
|
3621
|
-
process.stderr.write(`aontu: why needs a path and one file\n${WHY_HELP}\n`)
|
|
3864
|
+
process.stderr.write(`aontu: model why needs a path and one file\n${WHY_HELP}\n`)
|
|
3622
3865
|
return 2
|
|
3623
3866
|
}
|
|
3624
3867
|
const [path, file] = rest
|
|
@@ -3637,7 +3880,7 @@ function runWhy(argv: string[]): number {
|
|
|
3637
3880
|
})
|
|
3638
3881
|
if ('json' === format) {
|
|
3639
3882
|
process.stdout.write(exactJSON({
|
|
3640
|
-
aontu: { version: version(), verb: 'why' },
|
|
3883
|
+
aontu: { version: version(), verb: 'model why' },
|
|
3641
3884
|
findings: report.findings,
|
|
3642
3885
|
ok: report.ok,
|
|
3643
3886
|
...(null == report.record ? {} : { record: report.record }),
|
|
@@ -3675,7 +3918,7 @@ function renderWhyText(record: WhyRecord): string {
|
|
|
3675
3918
|
|
|
3676
3919
|
|
|
3677
3920
|
const SET_HELP =
|
|
3678
|
-
'aontu set <path>=<value> --entry <file> --overlay <file> (try --help)'
|
|
3921
|
+
'aontu model set <path>=<value> --entry <file> --overlay <file> (try --help)'
|
|
3679
3922
|
|
|
3680
3923
|
function runSet(argv: string[]): number {
|
|
3681
3924
|
const trusted = takeTrust(argv)
|
|
@@ -3718,7 +3961,7 @@ function runSet(argv: string[]): number {
|
|
|
3718
3961
|
format = f
|
|
3719
3962
|
}
|
|
3720
3963
|
else if (arg.startsWith('-')) {
|
|
3721
|
-
process.stderr.write(`aontu: unknown set option ${arg} (try --help)\n`)
|
|
3964
|
+
process.stderr.write(`aontu: unknown model set option ${arg} (try --help)\n`)
|
|
3722
3965
|
return 2
|
|
3723
3966
|
}
|
|
3724
3967
|
else {
|
|
@@ -3728,7 +3971,7 @@ function runSet(argv: string[]): number {
|
|
|
3728
3971
|
|
|
3729
3972
|
if (0 === assignments.length || null == entry || null == overlayFile) {
|
|
3730
3973
|
process.stderr.write(
|
|
3731
|
-
`aontu: set needs assignments, --entry and --overlay\n${SET_HELP}\n`)
|
|
3974
|
+
`aontu: model set needs assignments, --entry and --overlay\n${SET_HELP}\n`)
|
|
3732
3975
|
return 2
|
|
3733
3976
|
}
|
|
3734
3977
|
|
|
@@ -3780,7 +4023,7 @@ function runSet(argv: string[]): number {
|
|
|
3780
4023
|
|
|
3781
4024
|
if ('json' === format) {
|
|
3782
4025
|
process.stdout.write(exactJSON({
|
|
3783
|
-
aontu: { version: version(), verb: 'set' },
|
|
4026
|
+
aontu: { version: version(), verb: 'model set' },
|
|
3784
4027
|
appended: report.appended,
|
|
3785
4028
|
findings: report.findings,
|
|
3786
4029
|
overlay: report.overlay,
|
|
@@ -4138,8 +4381,8 @@ function runFmt(argv: string[]): number | Promise<number> {
|
|
|
4138
4381
|
}
|
|
4139
4382
|
else if ('--marker' === arg) {
|
|
4140
4383
|
// THE MARKER SAYS THE FILE IS A GENERATOR, whatever its
|
|
4141
|
-
// extension: `
|
|
4142
|
-
//
|
|
4384
|
+
// extension: `fmt` and `template` take the same option for the
|
|
4385
|
+
// same reason, a language the table has never seen.
|
|
4143
4386
|
marker = argv[++i]
|
|
4144
4387
|
if (null == marker) {
|
|
4145
4388
|
process.stderr.write('aontu: --marker needs a token\n')
|
|
@@ -4280,17 +4523,38 @@ function fmtOne(
|
|
|
4280
4523
|
}
|
|
4281
4524
|
|
|
4282
4525
|
|
|
4526
|
+
// Where a verb writes. The package verbs are asynchronous, so a test
|
|
4527
|
+
// cannot capture the process streams around them; it hands these in.
|
|
4528
|
+
type Io = { out: (s: string) => void, err: (s: string) => void }
|
|
4529
|
+
|
|
4530
|
+
const PROCESS_IO: Io = {
|
|
4531
|
+
out: (s) => void process.stdout.write(s),
|
|
4532
|
+
err: (s) => void process.stderr.write(s),
|
|
4533
|
+
}
|
|
4534
|
+
|
|
4283
4535
|
type Servers = {
|
|
4284
4536
|
lsp: () => void
|
|
4285
4537
|
mcp: (argv: string[]) => void
|
|
4538
|
+
// `pkg serve` runs until this settles: the process's SIGINT, or a
|
|
4539
|
+
// test's say-so.
|
|
4540
|
+
serve: (served: Served) => Promise<void>
|
|
4541
|
+
http: () => PkgHttp
|
|
4542
|
+
io?: Io
|
|
4543
|
+
}
|
|
4544
|
+
|
|
4545
|
+
// `pkg serve` runs until the process is interrupted.
|
|
4546
|
+
function serveUntilInterrupted(): Promise<void> {
|
|
4547
|
+
return new Promise((done) => process.once('SIGINT', () => done()))
|
|
4286
4548
|
}
|
|
4287
4549
|
|
|
4288
4550
|
// Excluded: the real pair takes the process stdio, so ts/test/cli.test.ts
|
|
4289
4551
|
// drives each server through a child process instead.
|
|
4290
|
-
/* node:coverage ignore next
|
|
4552
|
+
/* node:coverage ignore next 6 */
|
|
4291
4553
|
const SERVERS: Servers = {
|
|
4292
4554
|
lsp: () => void lspMain(),
|
|
4293
4555
|
mcp: (argv) => void mcpMain(undefined, undefined, undefined, undefined, argv),
|
|
4556
|
+
serve: serveUntilInterrupted,
|
|
4557
|
+
http: defaultHttp,
|
|
4294
4558
|
}
|
|
4295
4559
|
|
|
4296
4560
|
// undefined: the server took the process; a number: an answer the CLI
|
|
@@ -4614,10 +4878,10 @@ function runInit(argv: string[]): number {
|
|
|
4614
4878
|
|
|
4615
4879
|
|
|
4616
4880
|
const KNOWN_VERBS = [
|
|
4617
|
-
'agentsmd', 'allow', 'breaking', 'explain', 'fmt', 'get', 'hash',
|
|
4618
|
-
'help', 'init', 'jsonschema', 'lsp', 'mcp', '
|
|
4619
|
-
'
|
|
4620
|
-
'view', 'why',
|
|
4881
|
+
'add', 'agentsmd', 'allow', 'breaking', 'explain', 'fmt', 'get', 'hash',
|
|
4882
|
+
'help', 'init', 'jsonschema', 'lsp', 'mcp', 'model', 'pkg', 'publish',
|
|
4883
|
+
'reaches', 'relations', 'remove', 'subsume', 'sync', 'template', 'trace',
|
|
4884
|
+
'trim', 'vet', 'view', 'why',
|
|
4621
4885
|
]
|
|
4622
4886
|
|
|
4623
4887
|
|
|
@@ -4712,20 +4976,22 @@ function main(argv: string[], servers: Servers = SERVERS): void {
|
|
|
4712
4976
|
return servers.mcp(argv.slice(3))
|
|
4713
4977
|
}
|
|
4714
4978
|
|
|
4715
|
-
if ('set' === argv[2]) {
|
|
4716
|
-
return finish(runSet(argv.slice(3)))
|
|
4717
|
-
}
|
|
4718
4979
|
|
|
4719
4980
|
if ('allow' === argv[2]) {
|
|
4720
4981
|
return finish(runAllow(argv.slice(3)))
|
|
4721
4982
|
}
|
|
4722
4983
|
|
|
4723
|
-
if ('
|
|
4724
|
-
return finish(
|
|
4984
|
+
if ('model' === argv[2]) {
|
|
4985
|
+
return finish(runModel(argv.slice(3)))
|
|
4725
4986
|
}
|
|
4726
4987
|
|
|
4727
|
-
if ('
|
|
4728
|
-
|
|
4988
|
+
if ('pkg' === argv[2]) {
|
|
4989
|
+
const r = runPkg(argv.slice(3), servers)
|
|
4990
|
+
return 'number' === typeof r ? finish(r) : void r.then(finish)
|
|
4991
|
+
}
|
|
4992
|
+
|
|
4993
|
+
if (PACKAGE_VERBS.includes(argv[2])) {
|
|
4994
|
+
return void runPackageVerb(argv[2], argv.slice(3), servers).then(finish)
|
|
4729
4995
|
}
|
|
4730
4996
|
|
|
4731
4997
|
if ('hash' === argv[2]) {
|
|
@@ -4746,10 +5012,6 @@ function main(argv: string[], servers: Servers = SERVERS): void {
|
|
|
4746
5012
|
return finish(runInit(argv.slice(3)))
|
|
4747
5013
|
}
|
|
4748
5014
|
|
|
4749
|
-
if ('mod' === argv[2]) {
|
|
4750
|
-
return finish(runMod(argv.slice(3)))
|
|
4751
|
-
}
|
|
4752
|
-
|
|
4753
5015
|
if ('relations' === argv[2]) {
|
|
4754
5016
|
return finish(runRelations(argv.slice(3)))
|
|
4755
5017
|
}
|
|
@@ -4758,14 +5020,15 @@ function main(argv: string[], servers: Servers = SERVERS): void {
|
|
|
4758
5020
|
return finish(runJsonSchema(argv.slice(3)))
|
|
4759
5021
|
}
|
|
4760
5022
|
|
|
4761
|
-
if ('render' === argv[2]) {
|
|
4762
|
-
return finish(runRender(argv.slice(3)))
|
|
4763
|
-
}
|
|
4764
5023
|
|
|
4765
5024
|
if ('template' === argv[2]) {
|
|
4766
5025
|
return finish(runTemplate(argv.slice(3)))
|
|
4767
5026
|
}
|
|
4768
5027
|
|
|
5028
|
+
if ('trace' === argv[2]) {
|
|
5029
|
+
return finish(runTrace(argv.slice(3)))
|
|
5030
|
+
}
|
|
5031
|
+
|
|
4769
5032
|
if ('reaches' === argv[2]) {
|
|
4770
5033
|
return finish(runReaches(argv.slice(3)))
|
|
4771
5034
|
}
|
|
@@ -4869,7 +5132,7 @@ function main(argv: string[], servers: Servers = SERVERS): void {
|
|
|
4869
5132
|
else {
|
|
4870
5133
|
runStdin(mode, format, trust).then((code) => finish(code))
|
|
4871
5134
|
}
|
|
4872
|
-
} /* node:coverage ignore next
|
|
5135
|
+
} /* node:coverage ignore next 21 */
|
|
4873
5136
|
|
|
4874
5137
|
|
|
4875
5138
|
// No require.main guard here: bin/aontu.js is the executable entry and
|
|
@@ -4881,9 +5144,9 @@ export {
|
|
|
4881
5144
|
runReaches,
|
|
4882
5145
|
runView,
|
|
4883
5146
|
runJsonSchema,
|
|
4884
|
-
runRender,
|
|
4885
5147
|
runTemplate,
|
|
4886
|
-
|
|
5148
|
+
runTrace,
|
|
5149
|
+
runPkg, runModel, runPackageVerb, pkgToolOptions, serveUntilInterrupted,
|
|
4887
5150
|
runHash, runGet, runHelp, runExplain, runInit, nearestVerb,
|
|
4888
5151
|
looksLikeVerb,
|
|
4889
5152
|
KNOWN_VERBS,
|