aontu 0.64.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 +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 +15 -2
- package/dist/cli.js +523 -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/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 +6 -6
- package/src/agentsmd.ts +3 -3
- package/src/aontu.ts +1 -1
- package/src/cli.ts +610 -150
- 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/src/cli.ts
CHANGED
|
@@ -29,11 +29,23 @@ import { sarifReport } from './report-sarif'
|
|
|
29
29
|
import { main as lspMain } from './lsp-server'
|
|
30
30
|
import { main as mcpMain } from './mcp-server'
|
|
31
31
|
import { jsonSchema } from './jsonschema'
|
|
32
|
-
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'
|
|
33
40
|
import type {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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'
|
|
37
49
|
import { VET_MAX_ERRORS } from './vet'
|
|
38
50
|
import type {
|
|
39
51
|
VetReport, VetFinding, VetVerdict, VetCoverage,
|
|
@@ -86,10 +98,18 @@ const HELP = `Usage: aontu [options] [file]
|
|
|
86
98
|
aontu trace [--at <path>] [--format json] [--marker <token>]
|
|
87
99
|
[--profile <file>] <file>
|
|
88
100
|
aontu hash [options] <file>
|
|
89
|
-
aontu
|
|
90
|
-
aontu
|
|
91
|
-
aontu
|
|
92
|
-
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>
|
|
93
113
|
aontu allow --role <role> [--at <path>] <roles-file> <path>...
|
|
94
114
|
aontu agentsmd [--write <AGENTS.md>] [--depth <n>] <file>
|
|
95
115
|
aontu fmt [-w|-l|--check|-d|--lint] [--marker <token>]
|
|
@@ -158,18 +178,57 @@ Options:
|
|
|
158
178
|
named format keeps its meaning, and .js stays
|
|
159
179
|
refused. Every verb takes it
|
|
160
180
|
|
|
161
|
-
|
|
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:
|
|
162
196
|
--format <f> text (default) or json
|
|
163
|
-
--
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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)
|
|
170
216
|
vendor Materialise the locked closure into aontu_meta/vendor/
|
|
171
|
-
manifest Print the
|
|
172
|
-
|
|
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
|
|
173
232
|
|
|
174
233
|
Vet options:
|
|
175
234
|
--at <path> Validate against this path of the schema ($.a.b)
|
|
@@ -248,21 +307,21 @@ Hash options:
|
|
|
248
307
|
Hash exit codes: 0 hashed, 2 usage, 4 the document does not stand up
|
|
249
308
|
on its own.
|
|
250
309
|
|
|
251
|
-
|
|
310
|
+
Model get options:
|
|
252
311
|
-c, --canon Canonical-form fragment (default: generated JSON)
|
|
253
312
|
--keys Keys at the node, one per line
|
|
254
313
|
--types Shape view: concrete leaves lifted to their kinds
|
|
255
314
|
--depth <n> Structure to depth n; deeper nodes render as top
|
|
256
315
|
--format <f> text (default) or json
|
|
257
316
|
|
|
258
|
-
|
|
259
|
-
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.
|
|
260
319
|
|
|
261
|
-
|
|
320
|
+
Model why options:
|
|
262
321
|
--format <f> text (default) or json
|
|
263
322
|
|
|
264
|
-
|
|
265
|
-
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.
|
|
266
325
|
|
|
267
326
|
View kinds: doc, lattice, tree, matrix, graph, layer, sets, layers,
|
|
268
327
|
ladder, poset (the poset takes several files). The figure goes to stdout, the loss
|
|
@@ -352,7 +411,7 @@ every other line is a line of output.
|
|
|
352
411
|
|
|
353
412
|
Template exit codes: 0 written, 1 --check drift, 2 usage or I/O.
|
|
354
413
|
|
|
355
|
-
|
|
414
|
+
Model set options:
|
|
356
415
|
--entry <file> The document the change is checked against
|
|
357
416
|
--overlay <file> The file the change is appended to (created if
|
|
358
417
|
absent; not written when the change does not hold)
|
|
@@ -368,7 +427,7 @@ Set options:
|
|
|
368
427
|
--format <f> text (default) or json
|
|
369
428
|
|
|
370
429
|
Set exit codes are vet's verdict classes: 0 valid, 1 invalid (the
|
|
371
|
-
change contradicts a pinned value -- aontu why locates it, and
|
|
430
|
+
change contradicts a pinned value -- aontu model why locates it, and
|
|
372
431
|
--in-place rewrites it), 2 usage, 3 incomplete, 4 the entry does not
|
|
373
432
|
stand up on its own.
|
|
374
433
|
|
|
@@ -601,7 +660,7 @@ function trustOpts(trust: TrustArg, entryRoot: string): any {
|
|
|
601
660
|
}
|
|
602
661
|
|
|
603
662
|
|
|
604
|
-
function takeTrust(argv: string[]):
|
|
663
|
+
function takeTrust(argv: string[], io: Io = PROCESS_IO):
|
|
605
664
|
{ argv: string[], trust: TrustArg } | undefined {
|
|
606
665
|
const rest: string[] = []
|
|
607
666
|
let trust: TrustArg = { kind: 'system-warn', textExt: [] }
|
|
@@ -611,7 +670,7 @@ function takeTrust(argv: string[]):
|
|
|
611
670
|
if ('--trust' === arg) {
|
|
612
671
|
const parsed = null == argv[i + 1] ? undefined : parseTrustArg(argv[++i])
|
|
613
672
|
if (null == parsed) {
|
|
614
|
-
|
|
673
|
+
io.err(
|
|
615
674
|
'aontu: --trust needs system, none, or root[:dir]\n')
|
|
616
675
|
return undefined
|
|
617
676
|
}
|
|
@@ -620,7 +679,7 @@ function takeTrust(argv: string[]):
|
|
|
620
679
|
else if ('--include-root' === arg) {
|
|
621
680
|
const dir = argv[++i]
|
|
622
681
|
if (null == dir) {
|
|
623
|
-
|
|
682
|
+
io.err('aontu: --include-root needs a directory\n')
|
|
624
683
|
return undefined
|
|
625
684
|
}
|
|
626
685
|
trust = { kind: 'root', dir, textExt }
|
|
@@ -628,7 +687,7 @@ function takeTrust(argv: string[]):
|
|
|
628
687
|
else if ('--text-ext' === arg) {
|
|
629
688
|
const list = null == argv[i + 1] ? undefined : parseTextExt(argv[++i])
|
|
630
689
|
if (null == list) {
|
|
631
|
-
|
|
690
|
+
io.err(
|
|
632
691
|
'aontu: --text-ext needs extensions, without dots' +
|
|
633
692
|
' (--text-ext md,sql)\n')
|
|
634
693
|
return undefined
|
|
@@ -644,7 +703,7 @@ function takeTrust(argv: string[]):
|
|
|
644
703
|
|
|
645
704
|
|
|
646
705
|
// `md,sql` or `.md,.sql` -- the dot is accepted and dropped, because a
|
|
647
|
-
// 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
|
|
648
707
|
// element, or anything that is not an extension, is a usage error
|
|
649
708
|
// rather than a silently ignored word: a flag that quietly does
|
|
650
709
|
// nothing is how a document ends up refused with no reason visible.
|
|
@@ -2048,110 +2107,186 @@ const VIEW_USAGE_CODES = [
|
|
|
2048
2107
|
'view_document_shape', 'view_style_profile', 'view_style_unknown',
|
|
2049
2108
|
]
|
|
2050
2109
|
|
|
2051
|
-
const
|
|
2110
|
+
const PKG_HELP =
|
|
2111
|
+
'aontu pkg tidy|verify|vendor|manifest|refreeze|tree|outdated|serve [dir] | keygen <file> (try --help)'
|
|
2052
2112
|
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2113
|
+
const PKG_SUBS = [
|
|
2114
|
+
'tidy', 'verify', 'vendor', 'manifest', 'refreeze', 'tree', 'outdated',
|
|
2115
|
+
'serve', 'keygen',
|
|
2116
|
+
]
|
|
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
|
+
}
|
|
2063
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
|
+
}
|
|
2064
2146
|
for (let i = 0; i < argv.length; i++) {
|
|
2065
2147
|
const arg = argv[i]
|
|
2066
2148
|
if ('-h' === arg || '--help' === arg) {
|
|
2067
|
-
|
|
2068
|
-
return
|
|
2149
|
+
io.out(HELP)
|
|
2150
|
+
return undefined
|
|
2069
2151
|
}
|
|
2070
2152
|
if ('--format' === arg) {
|
|
2071
2153
|
const f = argv[++i]
|
|
2072
2154
|
if ('text' !== f && 'json' !== f) {
|
|
2073
|
-
|
|
2074
|
-
return
|
|
2155
|
+
io.err('aontu: --format needs text or json\n')
|
|
2156
|
+
return undefined
|
|
2075
2157
|
}
|
|
2076
|
-
format = f
|
|
2158
|
+
out.format = f
|
|
2077
2159
|
}
|
|
2078
|
-
else if ('--
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
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
|
|
2083
2180
|
}
|
|
2084
|
-
|
|
2181
|
+
out['--base' === arg ? 'base' : 'upstream'].push(v)
|
|
2085
2182
|
}
|
|
2086
2183
|
else if (arg.startsWith('-')) {
|
|
2087
|
-
|
|
2088
|
-
return
|
|
2184
|
+
io.err(`aontu: unknown ${verb} option ${arg} (try --help)\n`)
|
|
2185
|
+
return undefined
|
|
2089
2186
|
}
|
|
2090
2187
|
else {
|
|
2091
|
-
rest.push(arg)
|
|
2188
|
+
out.rest.push(arg)
|
|
2092
2189
|
}
|
|
2093
2190
|
}
|
|
2191
|
+
return out
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
|
|
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
|
+
}
|
|
2094
2208
|
|
|
2095
|
-
const sub = rest[0]
|
|
2096
|
-
const dir = rest[1] ?? '.'
|
|
2097
2209
|
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
"'aontu mod tidy'\n")
|
|
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) {
|
|
2103
2214
|
return 2
|
|
2104
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] ?? '.'
|
|
2105
2223
|
|
|
2106
|
-
if (!
|
|
2107
|
-
|
|
2108
|
-
`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`)
|
|
2109
2227
|
return 2
|
|
2110
2228
|
}
|
|
2111
2229
|
|
|
2112
|
-
if (
|
|
2113
|
-
|
|
2114
|
-
'aontu:
|
|
2115
|
-
|
|
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
|
|
2116
2242
|
}
|
|
2117
2243
|
|
|
2118
|
-
|
|
2244
|
+
nameOldLayout(dir, io)
|
|
2245
|
+
|
|
2246
|
+
// `--against` gates a manifest and means nothing to the others;
|
|
2119
2247
|
// accepting it there would say it had been honoured.
|
|
2120
|
-
if (null != against && 'manifest' !== sub) {
|
|
2121
|
-
|
|
2248
|
+
if (null != args.against && 'manifest' !== sub) {
|
|
2249
|
+
io.err('aontu: --against is a manifest option\n')
|
|
2122
2250
|
return 2
|
|
2123
2251
|
}
|
|
2124
2252
|
|
|
2125
|
-
const
|
|
2253
|
+
const opts = pkgToolOptions(trust, resolve(dir))
|
|
2126
2254
|
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
'vendor' === sub ? modVendor(dir, modopts) :
|
|
2131
|
-
modManifest(dir, modopts, against)
|
|
2255
|
+
if ('outdated' === sub || 'serve' === sub) {
|
|
2256
|
+
return runPkgNet(sub, dir, args, opts, servers, io)
|
|
2257
|
+
}
|
|
2132
2258
|
|
|
2133
|
-
|
|
2134
|
-
|
|
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 },
|
|
2135
2269
|
2) :
|
|
2136
|
-
|
|
2270
|
+
pkgText(sub, report)) + '\n')
|
|
2137
2271
|
|
|
2138
|
-
return
|
|
2272
|
+
return PKG_EXIT[report.verdict]
|
|
2139
2273
|
}
|
|
2140
2274
|
|
|
2141
2275
|
|
|
2142
|
-
const MOD_SUBS = ['tidy', 'verify', 'vendor', 'manifest']
|
|
2143
|
-
|
|
2144
2276
|
// The verdict classes: `ok` 0, a refused gate 1, an open question 3, a
|
|
2145
2277
|
// document that does not stand up 4 -- `subsume`'s classes, because a
|
|
2146
2278
|
// manifest gate IS a subsumption check and a caller reading exit codes
|
|
2147
2279
|
// should not have to learn a second table.
|
|
2148
|
-
type
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
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> = {
|
|
2155
2290
|
ok: 0,
|
|
2156
2291
|
missing: 1,
|
|
2157
2292
|
mismatch: 1,
|
|
@@ -2159,6 +2294,12 @@ const MOD_EXIT: Record<ModVerdict, number> = {
|
|
|
2159
2294
|
// nothing to check, which is a refusal and not a pass.
|
|
2160
2295
|
unlocked: 1,
|
|
2161
2296
|
breaking: 1,
|
|
2297
|
+
frozen: 1,
|
|
2298
|
+
refused: 1,
|
|
2299
|
+
outdated: 1,
|
|
2300
|
+
sent: 0,
|
|
2301
|
+
'dry-run': 0,
|
|
2302
|
+
current: 0,
|
|
2162
2303
|
undecided: 3,
|
|
2163
2304
|
error: 4,
|
|
2164
2305
|
}
|
|
@@ -2167,7 +2308,7 @@ const MOD_EXIT: Record<ModVerdict, number> = {
|
|
|
2167
2308
|
// The tooling's evaluator: the same standalone evaluation the module
|
|
2168
2309
|
// resolver verifies with (ts/src/mod.ts), and for the same reason —
|
|
2169
2310
|
// only the engine can say what a module MEANS.
|
|
2170
|
-
function
|
|
2311
|
+
function pkgToolOptions(trust: TrustArg, entryRoot: string): PkgToolOptions {
|
|
2171
2312
|
const opts = verbOpts(trust, entryRoot)
|
|
2172
2313
|
// The user cache lives outside any confinement root, so a confined
|
|
2173
2314
|
// run reads the vendor tree only -- as the evaluator's own module
|
|
@@ -2184,7 +2325,7 @@ function modToolOptions(trust: TrustArg, entryRoot: string) {
|
|
|
2184
2325
|
hash: canonHash(val),
|
|
2185
2326
|
canon: val.canon,
|
|
2186
2327
|
// The same question `aontu hash` asks before it will answer:
|
|
2187
|
-
// did this document stand up ON ITS OWN? See
|
|
2328
|
+
// did this document stand up ON ITS OWN? See PkgToolEval.
|
|
2188
2329
|
ok: 0 === ctx.err.length && true !== val.isNil,
|
|
2189
2330
|
}
|
|
2190
2331
|
},
|
|
@@ -2192,19 +2333,54 @@ function modToolOptions(trust: TrustArg, entryRoot: string) {
|
|
|
2192
2333
|
}
|
|
2193
2334
|
|
|
2194
2335
|
|
|
2195
|
-
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 {
|
|
2196
2360
|
const lines = ['verdict: ' + report.verdict]
|
|
2197
2361
|
|
|
2198
2362
|
if ('manifest' === sub) {
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
lines.push('
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
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
|
+
}
|
|
2208
2384
|
}
|
|
2209
2385
|
for (const f of report.findings) {
|
|
2210
2386
|
lines.push(f.path + ': ' + f.message)
|
|
@@ -2212,48 +2388,313 @@ function modText(sub: string, report: any): string {
|
|
|
2212
2388
|
for (const miss of report.missing) {
|
|
2213
2389
|
lines.push(miss + ': missing')
|
|
2214
2390
|
}
|
|
2391
|
+
lines.push(...pkgForbiddenLines(report.forbidden))
|
|
2215
2392
|
return lines.join('\n')
|
|
2216
2393
|
}
|
|
2217
2394
|
|
|
2218
2395
|
if ('verify' === sub) {
|
|
2219
|
-
for (const
|
|
2220
|
-
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)')
|
|
2402
|
+
}
|
|
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)
|
|
2221
2409
|
}
|
|
2222
|
-
for (const
|
|
2223
|
-
lines.push(
|
|
2224
|
-
('' === m.got ? 'nothing (it does not evaluate)' : m.got))
|
|
2410
|
+
for (const key of report.unchanged) {
|
|
2411
|
+
lines.push(key + ': unchanged')
|
|
2225
2412
|
}
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
for (const mod of report.unlocked) {
|
|
2229
|
-
lines.push(mod + ': not in the lockfile (run: aontu mod tidy)')
|
|
2413
|
+
for (const bad of report.unevaluable) {
|
|
2414
|
+
lines.push(bad + ': does not evaluate on its own; nothing to pin')
|
|
2230
2415
|
}
|
|
2231
2416
|
for (const miss of report.missing) {
|
|
2232
|
-
lines.push(miss + ': not fetched (run: aontu
|
|
2417
|
+
lines.push(miss + ': not fetched (run: aontu sync)')
|
|
2233
2418
|
}
|
|
2234
2419
|
return lines.join('\n')
|
|
2235
2420
|
}
|
|
2236
2421
|
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
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')
|
|
2241
2444
|
}
|
|
2242
|
-
|
|
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
|
|
2243
2449
|
// from a missing one because the repair is different: a fetch cannot
|
|
2244
|
-
// help, the
|
|
2450
|
+
// help, the package itself has to be fixed (or its own dependencies
|
|
2245
2451
|
// vendored beside it). Before the missing tail, as the Go port's
|
|
2246
2452
|
// shared renderer orders them.
|
|
2247
2453
|
for (const bad of report.unevaluable ?? []) {
|
|
2248
2454
|
lines.push(bad + ': does not evaluate on its own; nothing to pin')
|
|
2249
2455
|
}
|
|
2456
|
+
lines.push(...pkgForbiddenLines(report.forbidden ?? []))
|
|
2250
2457
|
for (const miss of report.missing) {
|
|
2251
|
-
lines.push(miss + ': not fetched (run: aontu
|
|
2458
|
+
lines.push(miss + ': not fetched (run: aontu sync)')
|
|
2252
2459
|
}
|
|
2253
2460
|
return lines.join('\n')
|
|
2254
2461
|
}
|
|
2255
2462
|
|
|
2256
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
|
+
|
|
2257
2698
|
function vacuous(what: string, why: string): void {
|
|
2258
2699
|
process.stderr.write(`aontu: ${what}: ${why}\n`)
|
|
2259
2700
|
}
|
|
@@ -3272,7 +3713,7 @@ function runHash(argv: string[]): number {
|
|
|
3272
3713
|
}
|
|
3273
3714
|
|
|
3274
3715
|
|
|
3275
|
-
const GET_HELP = 'aontu get <path> <file> (try --help)'
|
|
3716
|
+
const GET_HELP = 'aontu model get <path> <file> (try --help)'
|
|
3276
3717
|
|
|
3277
3718
|
function runGet(argv: string[]): number {
|
|
3278
3719
|
const trusted = takeTrust(argv)
|
|
@@ -3318,7 +3759,7 @@ function runGet(argv: string[]): number {
|
|
|
3318
3759
|
format = f
|
|
3319
3760
|
}
|
|
3320
3761
|
else if (arg.startsWith('-')) {
|
|
3321
|
-
process.stderr.write(`aontu: unknown get option ${arg} (try --help)\n`)
|
|
3762
|
+
process.stderr.write(`aontu: unknown model get option ${arg} (try --help)\n`)
|
|
3322
3763
|
return 2
|
|
3323
3764
|
}
|
|
3324
3765
|
else {
|
|
@@ -3327,7 +3768,7 @@ function runGet(argv: string[]): number {
|
|
|
3327
3768
|
}
|
|
3328
3769
|
|
|
3329
3770
|
if (2 !== rest.length) {
|
|
3330
|
-
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`)
|
|
3331
3772
|
return 2
|
|
3332
3773
|
}
|
|
3333
3774
|
const [path, file] = rest
|
|
@@ -3355,7 +3796,7 @@ function runGet(argv: string[]): number {
|
|
|
3355
3796
|
})
|
|
3356
3797
|
if ('json' === format) {
|
|
3357
3798
|
process.stdout.write(exactJSON({
|
|
3358
|
-
aontu: { version: version(), verb: 'get' },
|
|
3799
|
+
aontu: { version: version(), verb: 'model get' },
|
|
3359
3800
|
findings: report.findings,
|
|
3360
3801
|
ok: report.ok,
|
|
3361
3802
|
out: report.out,
|
|
@@ -3384,7 +3825,7 @@ function runGet(argv: string[]): number {
|
|
|
3384
3825
|
// The positive twin of the vet report: errors explain what failed to
|
|
3385
3826
|
// unify, this explains what did.
|
|
3386
3827
|
|
|
3387
|
-
const WHY_HELP = 'aontu why <path> <file> (try --help)'
|
|
3828
|
+
const WHY_HELP = 'aontu model why <path> <file> (try --help)'
|
|
3388
3829
|
|
|
3389
3830
|
function runWhy(argv: string[]): number {
|
|
3390
3831
|
const trusted = takeTrust(argv)
|
|
@@ -3411,7 +3852,7 @@ function runWhy(argv: string[]): number {
|
|
|
3411
3852
|
format = f
|
|
3412
3853
|
}
|
|
3413
3854
|
else if (arg.startsWith('-')) {
|
|
3414
|
-
process.stderr.write(`aontu: unknown why option ${arg} (try --help)\n`)
|
|
3855
|
+
process.stderr.write(`aontu: unknown model why option ${arg} (try --help)\n`)
|
|
3415
3856
|
return 2
|
|
3416
3857
|
}
|
|
3417
3858
|
else {
|
|
@@ -3420,7 +3861,7 @@ function runWhy(argv: string[]): number {
|
|
|
3420
3861
|
}
|
|
3421
3862
|
|
|
3422
3863
|
if (2 !== rest.length) {
|
|
3423
|
-
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`)
|
|
3424
3865
|
return 2
|
|
3425
3866
|
}
|
|
3426
3867
|
const [path, file] = rest
|
|
@@ -3439,7 +3880,7 @@ function runWhy(argv: string[]): number {
|
|
|
3439
3880
|
})
|
|
3440
3881
|
if ('json' === format) {
|
|
3441
3882
|
process.stdout.write(exactJSON({
|
|
3442
|
-
aontu: { version: version(), verb: 'why' },
|
|
3883
|
+
aontu: { version: version(), verb: 'model why' },
|
|
3443
3884
|
findings: report.findings,
|
|
3444
3885
|
ok: report.ok,
|
|
3445
3886
|
...(null == report.record ? {} : { record: report.record }),
|
|
@@ -3477,7 +3918,7 @@ function renderWhyText(record: WhyRecord): string {
|
|
|
3477
3918
|
|
|
3478
3919
|
|
|
3479
3920
|
const SET_HELP =
|
|
3480
|
-
'aontu set <path>=<value> --entry <file> --overlay <file> (try --help)'
|
|
3921
|
+
'aontu model set <path>=<value> --entry <file> --overlay <file> (try --help)'
|
|
3481
3922
|
|
|
3482
3923
|
function runSet(argv: string[]): number {
|
|
3483
3924
|
const trusted = takeTrust(argv)
|
|
@@ -3520,7 +3961,7 @@ function runSet(argv: string[]): number {
|
|
|
3520
3961
|
format = f
|
|
3521
3962
|
}
|
|
3522
3963
|
else if (arg.startsWith('-')) {
|
|
3523
|
-
process.stderr.write(`aontu: unknown set option ${arg} (try --help)\n`)
|
|
3964
|
+
process.stderr.write(`aontu: unknown model set option ${arg} (try --help)\n`)
|
|
3524
3965
|
return 2
|
|
3525
3966
|
}
|
|
3526
3967
|
else {
|
|
@@ -3530,7 +3971,7 @@ function runSet(argv: string[]): number {
|
|
|
3530
3971
|
|
|
3531
3972
|
if (0 === assignments.length || null == entry || null == overlayFile) {
|
|
3532
3973
|
process.stderr.write(
|
|
3533
|
-
`aontu: set needs assignments, --entry and --overlay\n${SET_HELP}\n`)
|
|
3974
|
+
`aontu: model set needs assignments, --entry and --overlay\n${SET_HELP}\n`)
|
|
3534
3975
|
return 2
|
|
3535
3976
|
}
|
|
3536
3977
|
|
|
@@ -3582,7 +4023,7 @@ function runSet(argv: string[]): number {
|
|
|
3582
4023
|
|
|
3583
4024
|
if ('json' === format) {
|
|
3584
4025
|
process.stdout.write(exactJSON({
|
|
3585
|
-
aontu: { version: version(), verb: 'set' },
|
|
4026
|
+
aontu: { version: version(), verb: 'model set' },
|
|
3586
4027
|
appended: report.appended,
|
|
3587
4028
|
findings: report.findings,
|
|
3588
4029
|
overlay: report.overlay,
|
|
@@ -4082,17 +4523,38 @@ function fmtOne(
|
|
|
4082
4523
|
}
|
|
4083
4524
|
|
|
4084
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
|
+
|
|
4085
4535
|
type Servers = {
|
|
4086
4536
|
lsp: () => void
|
|
4087
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()))
|
|
4088
4548
|
}
|
|
4089
4549
|
|
|
4090
4550
|
// Excluded: the real pair takes the process stdio, so ts/test/cli.test.ts
|
|
4091
4551
|
// drives each server through a child process instead.
|
|
4092
|
-
/* node:coverage ignore next
|
|
4552
|
+
/* node:coverage ignore next 6 */
|
|
4093
4553
|
const SERVERS: Servers = {
|
|
4094
4554
|
lsp: () => void lspMain(),
|
|
4095
4555
|
mcp: (argv) => void mcpMain(undefined, undefined, undefined, undefined, argv),
|
|
4556
|
+
serve: serveUntilInterrupted,
|
|
4557
|
+
http: defaultHttp,
|
|
4096
4558
|
}
|
|
4097
4559
|
|
|
4098
4560
|
// undefined: the server took the process; a number: an answer the CLI
|
|
@@ -4416,10 +4878,10 @@ function runInit(argv: string[]): number {
|
|
|
4416
4878
|
|
|
4417
4879
|
|
|
4418
4880
|
const KNOWN_VERBS = [
|
|
4419
|
-
'agentsmd', 'allow', 'breaking', 'explain', 'fmt', 'get', 'hash',
|
|
4420
|
-
'help', 'init', 'jsonschema', 'lsp', 'mcp', '
|
|
4421
|
-
'relations', '
|
|
4422
|
-
'vet', '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',
|
|
4423
4885
|
]
|
|
4424
4886
|
|
|
4425
4887
|
|
|
@@ -4514,20 +4976,22 @@ function main(argv: string[], servers: Servers = SERVERS): void {
|
|
|
4514
4976
|
return servers.mcp(argv.slice(3))
|
|
4515
4977
|
}
|
|
4516
4978
|
|
|
4517
|
-
if ('set' === argv[2]) {
|
|
4518
|
-
return finish(runSet(argv.slice(3)))
|
|
4519
|
-
}
|
|
4520
4979
|
|
|
4521
4980
|
if ('allow' === argv[2]) {
|
|
4522
4981
|
return finish(runAllow(argv.slice(3)))
|
|
4523
4982
|
}
|
|
4524
4983
|
|
|
4525
|
-
if ('
|
|
4526
|
-
return finish(
|
|
4984
|
+
if ('model' === argv[2]) {
|
|
4985
|
+
return finish(runModel(argv.slice(3)))
|
|
4986
|
+
}
|
|
4987
|
+
|
|
4988
|
+
if ('pkg' === argv[2]) {
|
|
4989
|
+
const r = runPkg(argv.slice(3), servers)
|
|
4990
|
+
return 'number' === typeof r ? finish(r) : void r.then(finish)
|
|
4527
4991
|
}
|
|
4528
4992
|
|
|
4529
|
-
if (
|
|
4530
|
-
return
|
|
4993
|
+
if (PACKAGE_VERBS.includes(argv[2])) {
|
|
4994
|
+
return void runPackageVerb(argv[2], argv.slice(3), servers).then(finish)
|
|
4531
4995
|
}
|
|
4532
4996
|
|
|
4533
4997
|
if ('hash' === argv[2]) {
|
|
@@ -4548,10 +5012,6 @@ function main(argv: string[], servers: Servers = SERVERS): void {
|
|
|
4548
5012
|
return finish(runInit(argv.slice(3)))
|
|
4549
5013
|
}
|
|
4550
5014
|
|
|
4551
|
-
if ('mod' === argv[2]) {
|
|
4552
|
-
return finish(runMod(argv.slice(3)))
|
|
4553
|
-
}
|
|
4554
|
-
|
|
4555
5015
|
if ('relations' === argv[2]) {
|
|
4556
5016
|
return finish(runRelations(argv.slice(3)))
|
|
4557
5017
|
}
|
|
@@ -4686,7 +5146,7 @@ export {
|
|
|
4686
5146
|
runJsonSchema,
|
|
4687
5147
|
runTemplate,
|
|
4688
5148
|
runTrace,
|
|
4689
|
-
|
|
5149
|
+
runPkg, runModel, runPackageVerb, pkgToolOptions, serveUntilInterrupted,
|
|
4690
5150
|
runHash, runGet, runHelp, runExplain, runInit, nearestVerb,
|
|
4691
5151
|
looksLikeVerb,
|
|
4692
5152
|
KNOWN_VERBS,
|