@volter/twin-standard 1.0.113 → 1.0.114
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 +1 -0
- package/dist/src/cli.js +1 -1
- package/dist/src/conformance.d.ts +5 -2
- package/dist/src/conformance.js +8 -6
- package/dist/src/create-pack.d.ts +3 -1
- package/dist/src/create-pack.js +13 -5
- package/package.json +2 -2
- package/src/cli.ts +1 -1
- package/src/conformance.ts +5 -4
- package/src/create-pack.ts +12 -5
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ place a pack is judged judges it by the same standard.
|
|
|
8
8
|
```bash
|
|
9
9
|
twin-standard create <dir> --init <vendor> --package <@scope/name> # fixed files, in your repository
|
|
10
10
|
twin-standard create <unit> --index # fixed handler map and fetch after derivation
|
|
11
|
+
twin-standard create <unit> --index --building # construction only: keep unfinished operations as gaps
|
|
11
12
|
twin-standard gate <pack repository> [<pack or lane dir>…] # every unit passes the form section, or exit 1
|
|
12
13
|
twin-standard gate-published <name>@<version>… [--registry <url>] # a published version, as the catalog grades a submission
|
|
13
14
|
twin-standard grade <pack or lane dir>… [--static] [--form] # each criterion's result
|
package/dist/src/cli.js
CHANGED
|
@@ -31,7 +31,7 @@ import { standardPackageFile } from "./package-file.js";
|
|
|
31
31
|
import { gatePublished } from "./published.js";
|
|
32
32
|
const [command, ...args] = process.argv.slice(2);
|
|
33
33
|
const usage = () => {
|
|
34
|
-
console.error('usage: twin-standard gate <repo> [units…] | gate-published <name>@<version>… [--registry <url>] | grade <unit>… [--static] [--form] | derive <unit> [--out <dir>] | read-page <url> [--raw] | check-sources <unit> [--recheck] | conform <pack dir> | <name>@<version> [--registry <url>] | create <dir> --init <vendor> --package <@scope/name> | create <unit> --index | facts <pack dir> [--out <file>] | prepare-consumer <pack dir> --app <new dir> --cli <volter executable> --integrity <sha512> --out <json> [--candidate <tgz>] | walk-consumer <pack dir> --prepared-consumer <json> [--out <file>] | assess <pack dir> --prepared-consumer <json> [--browser] [--out <file>] | --version');
|
|
34
|
+
console.error('usage: twin-standard gate <repo> [units…] | gate-published <name>@<version>… [--registry <url>] | grade <unit>… [--static] [--form] | derive <unit> [--out <dir>] | read-page <url> [--raw] | check-sources <unit> [--recheck] | conform <pack dir> | <name>@<version> [--registry <url>] | create <dir> --init <vendor> --package <@scope/name> | create <unit> --index [--building] | facts <pack dir> [--out <file>] | prepare-consumer <pack dir> --app <new dir> --cli <volter executable> --integrity <sha512> --out <json> [--candidate <tgz>] | walk-consumer <pack dir> --prepared-consumer <json> [--out <file>] | assess <pack dir> --prepared-consumer <json> [--browser] [--out <file>] | --version');
|
|
35
35
|
process.exit(2);
|
|
36
36
|
};
|
|
37
37
|
if (command === '--version') {
|
|
@@ -26,8 +26,11 @@ type Decision = {
|
|
|
26
26
|
* the vendor's own API's operation of that id, or, when its own API has none, the one lane's that has it. So an id two
|
|
27
27
|
* units share is two decisions, and a lane's operation its root serves stays the lane's gap (Clerk's Backend and
|
|
28
28
|
* Frontend APIs both name `ListOrganizationMemberships`). A demanded operation (journeys/demand.json, the vendor's
|
|
29
|
-
* backend calls) is the unit's a row naming no lane would decide.
|
|
30
|
-
|
|
29
|
+
* backend calls) is the unit's a row naming no lane would decide. Construction may leave unfinished operations as
|
|
30
|
+
* gaps; the default and conformance callers require completion. Both modes hold implemented operations to the table. */
|
|
31
|
+
export declare function decisionTableProblems(unit: string, byUnit: Record<string, Owners>, decisions: Decision[], demanded?: string[], doors?: string[], options?: {
|
|
32
|
+
building?: boolean;
|
|
33
|
+
}): string[];
|
|
31
34
|
/** Every check over the pack at `dir`, in order. */
|
|
32
35
|
export declare function conformance(dir: string): Promise<ConformanceResult[]>;
|
|
33
36
|
export {};
|
package/dist/src/conformance.js
CHANGED
|
@@ -73,8 +73,9 @@ const manifestOf = async (unit) => (await import(__rewriteRelativeImportExtensio
|
|
|
73
73
|
* the vendor's own API's operation of that id, or, when its own API has none, the one lane's that has it. So an id two
|
|
74
74
|
* units share is two decisions, and a lane's operation its root serves stays the lane's gap (Clerk's Backend and
|
|
75
75
|
* Frontend APIs both name `ListOrganizationMemberships`). A demanded operation (journeys/demand.json, the vendor's
|
|
76
|
-
* backend calls) is the unit's a row naming no lane would decide.
|
|
77
|
-
|
|
76
|
+
* backend calls) is the unit's a row naming no lane would decide. Construction may leave unfinished operations as
|
|
77
|
+
* gaps; the default and conformance callers require completion. Both modes hold implemented operations to the table. */
|
|
78
|
+
export function decisionTableProblems(unit, byUnit, decisions, demanded = [], doors = [], options = {}) {
|
|
78
79
|
const owners = { ...(byUnit[unit] ?? {}), ...Object.fromEntries(doors.map((id) => [`door:${id}`, 'door'])) };
|
|
79
80
|
const holders = (id) => Object.keys(byUnit).filter((u) => id in byUnit[u]);
|
|
80
81
|
const unnamedUnit = (id) => (id in (byUnit[''] ?? {}) ? '' : holders(id).length === 1 ? holders(id)[0] : undefined);
|
|
@@ -94,12 +95,13 @@ export function decisionTableProblems(unit, byUnit, decisions, demanded = [], do
|
|
|
94
95
|
for (const d of mine) {
|
|
95
96
|
if (!(d.operation in owners))
|
|
96
97
|
out.push(`${d.operation} is decided for ${unit || 'the root'}, which has no such operation`);
|
|
97
|
-
else if (owners[d.operation] === 'gap' && d.mechanism !== 'unmodeled')
|
|
98
|
+
else if (!options.building && owners[d.operation] === 'gap' && d.mechanism !== 'unmodeled')
|
|
98
99
|
out.push(`${d.operation} is decided ${d.mechanism} and served by nothing`);
|
|
99
100
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
if (!options.building)
|
|
102
|
+
for (const op of demanded)
|
|
103
|
+
if (op && unnamedUnit(op) === unit && owners[op] === 'gap')
|
|
104
|
+
out.push(`${op} is demanded (journeys/demand.json) and served by nothing`);
|
|
103
105
|
return out;
|
|
104
106
|
}
|
|
105
107
|
/** Every operation a unit serves is decided, every operation demanded or refreshed is served (create-pack --index's
|
|
@@ -8,7 +8,9 @@ export declare function handlerIndex(dir: string): string;
|
|
|
8
8
|
* serves, the demand it answers and the mechanism that serves it (`handler` or `core`); an operation it does not serve
|
|
9
9
|
* is `unmodeled` in the manifest. The unit's owners are the vendor's dispatch's for that unit. A problem stops the index:
|
|
10
10
|
* six packs once shipped answering 404 to everything, and scope once grew past demand, both unseen by the tools. */
|
|
11
|
-
export declare function decisionProblems(dir: string
|
|
11
|
+
export declare function decisionProblems(dir: string, options?: {
|
|
12
|
+
building?: boolean;
|
|
13
|
+
}): Promise<string[]>;
|
|
12
14
|
/** A unit's fetch.ts, a fixed file: `createPackFetch` over its parts as they stand (the manifest, the surface, the handler
|
|
13
15
|
* map, and its doors, clock and screens where it has them). `--init` writes it, and `--index` again when a door file,
|
|
14
16
|
* the clock or a screen is added. `name` is the fetch's export (`createClerkFetch`, a lane's `createFapiLaneFetch`). */
|
package/dist/src/create-pack.js
CHANGED
|
@@ -22,6 +22,8 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
22
22
|
// vendor facts, the descriptor's hosts and the families
|
|
23
23
|
// bun scripts/create-pack.ts <unit> --index write src/semantics/index.ts, the handler map, and src/fetch.ts from the
|
|
24
24
|
// families' files (every exported function, by its name)
|
|
25
|
+
// bun scripts/create-pack.ts <unit> --index --building the same during construction; unfinished operations stay gaps,
|
|
26
|
+
// and implemented operations must match their decisions
|
|
25
27
|
// bun scripts/create-pack.ts <unit> --brief [--out f] the author's brief: every operation the derived core does not
|
|
26
28
|
// serve and no handler answers yet, by family, with what the
|
|
27
29
|
// vendored spec says of it (summary, description, parameters,
|
|
@@ -101,7 +103,7 @@ export function handlerIndex(dir) {
|
|
|
101
103
|
* serves, the demand it answers and the mechanism that serves it (`handler` or `core`); an operation it does not serve
|
|
102
104
|
* is `unmodeled` in the manifest. The unit's owners are the vendor's dispatch's for that unit. A problem stops the index:
|
|
103
105
|
* six packs once shipped answering 404 to everything, and scope once grew past demand, both unseen by the tools. */
|
|
104
|
-
export async function decisionProblems(dir) {
|
|
106
|
+
export async function decisionProblems(dir, options = {}) {
|
|
105
107
|
const out = [];
|
|
106
108
|
const journeys = existsSync(join(dir, 'journeys')) ? join(dir, 'journeys') : join(dir, '..', 'journeys');
|
|
107
109
|
const tablePath = join(journeys, 'decisions.json');
|
|
@@ -129,10 +131,10 @@ export async function decisionProblems(dir) {
|
|
|
129
131
|
// a refresh reads the vendor's list or retrieve, and a World rooted at a second World's twin of the pack reads it from
|
|
130
132
|
// that twin (pack-done's (g)): an operation a refresh scope names is one the pack serves
|
|
131
133
|
const { manifest } = await import(__rewriteRelativeImportExtension(resolve(dir, 'src', 'manifest.ts')));
|
|
132
|
-
out.push(...decisionTableProblems(unit, byUnit, table.decisions ?? [], demanded, manifest?.doors?.map((d) => d.id) ?? []));
|
|
134
|
+
out.push(...decisionTableProblems(unit, byUnit, table.decisions ?? [], demanded, manifest?.doors?.map((d) => d.id) ?? [], options));
|
|
133
135
|
for (const [name, decl] of Object.entries(manifest?.resources ?? {})) {
|
|
134
136
|
for (const id of [decl.refresh?.list ?? decl.refresh?.get, decl.refresh?.detail])
|
|
135
|
-
if (id && id in owners && owners[id] === 'gap')
|
|
137
|
+
if (!options.building && id && id in owners && owners[id] === 'gap')
|
|
136
138
|
out.push(`${id}: ${name}'s refresh reads it, and nothing serves it (serve it, or declare refresh none with why)`);
|
|
137
139
|
}
|
|
138
140
|
return out;
|
|
@@ -454,7 +456,11 @@ export function laneInitFiles(vendor, lanes) {
|
|
|
454
456
|
export async function createPack(args) {
|
|
455
457
|
const [unit, ...rest] = args;
|
|
456
458
|
if (!unit || (!rest.includes('--index') && !rest.includes('--brief') && !rest.includes('--init'))) {
|
|
457
|
-
console.error('usage: bun scripts/create-pack.ts <dir> --init <vendor> | <unit> --index | --brief [--out <file>]');
|
|
459
|
+
console.error('usage: bun scripts/create-pack.ts <dir> --init <vendor> | <unit> --index [--building] | --brief [--out <file>]');
|
|
460
|
+
process.exit(2);
|
|
461
|
+
}
|
|
462
|
+
if (rest.includes('--building') && (!rest.includes('--index') || rest.includes('--init') || rest.includes('--brief'))) {
|
|
463
|
+
console.error('create-pack: --building requires --index alone; it generates an unfinished pack, not completion evidence');
|
|
458
464
|
process.exit(2);
|
|
459
465
|
}
|
|
460
466
|
const dir = resolve(unit);
|
|
@@ -576,12 +582,14 @@ export async function createPack(args) {
|
|
|
576
582
|
if (odd)
|
|
577
583
|
process.exit(1);
|
|
578
584
|
if (!lanesOnly) {
|
|
579
|
-
const problems = await decisionProblems(dir);
|
|
585
|
+
const problems = await decisionProblems(dir, { building: rest.includes('--building') });
|
|
580
586
|
if (problems.length) {
|
|
581
587
|
console.error(`create-pack: ${basename(dir)}: ${problems.length} problem(s) with what the unit serves:\n ${problems.join('\n ')}`);
|
|
582
588
|
process.exit(1);
|
|
583
589
|
}
|
|
584
590
|
}
|
|
591
|
+
if (rest.includes('--building'))
|
|
592
|
+
console.log('create-pack: construction index only; unfinished operations remain gaps; run ordinary --index before completion');
|
|
585
593
|
}
|
|
586
594
|
if (rest.includes('--brief')) {
|
|
587
595
|
const text = await brief(unit);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/twin-standard",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.114",
|
|
4
4
|
"description": "The Protocol 3 standard for Volter twin packs: the criteria a pack is graded by, the grader, the gate a pack repository and the twin catalog run on every change and submission, and the spec derivation the grade checks against. Pin a version: every place a pack is judged judges by that standard.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twin",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"yaml": "2.9.0"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@volter/world-core": "3.0.
|
|
78
|
+
"@volter/world-core": "3.0.122",
|
|
79
79
|
"@volter/world-runtime": "*",
|
|
80
80
|
"playwright": "^1.63.0"
|
|
81
81
|
},
|
package/src/cli.ts
CHANGED
|
@@ -24,7 +24,7 @@ import { gatePublished } from './published.ts';
|
|
|
24
24
|
|
|
25
25
|
const [command, ...args] = process.argv.slice(2);
|
|
26
26
|
const usage = (): never => {
|
|
27
|
-
console.error('usage: twin-standard gate <repo> [units…] | gate-published <name>@<version>… [--registry <url>] | grade <unit>… [--static] [--form] | derive <unit> [--out <dir>] | read-page <url> [--raw] | check-sources <unit> [--recheck] | conform <pack dir> | <name>@<version> [--registry <url>] | create <dir> --init <vendor> --package <@scope/name> | create <unit> --index | facts <pack dir> [--out <file>] | prepare-consumer <pack dir> --app <new dir> --cli <volter executable> --integrity <sha512> --out <json> [--candidate <tgz>] | walk-consumer <pack dir> --prepared-consumer <json> [--out <file>] | assess <pack dir> --prepared-consumer <json> [--browser] [--out <file>] | --version');
|
|
27
|
+
console.error('usage: twin-standard gate <repo> [units…] | gate-published <name>@<version>… [--registry <url>] | grade <unit>… [--static] [--form] | derive <unit> [--out <dir>] | read-page <url> [--raw] | check-sources <unit> [--recheck] | conform <pack dir> | <name>@<version> [--registry <url>] | create <dir> --init <vendor> --package <@scope/name> | create <unit> --index [--building] | facts <pack dir> [--out <file>] | prepare-consumer <pack dir> --app <new dir> --cli <volter executable> --integrity <sha512> --out <json> [--candidate <tgz>] | walk-consumer <pack dir> --prepared-consumer <json> [--out <file>] | assess <pack dir> --prepared-consumer <json> [--browser] [--out <file>] | --version');
|
|
28
28
|
process.exit(2);
|
|
29
29
|
};
|
|
30
30
|
|
package/src/conformance.ts
CHANGED
|
@@ -86,8 +86,9 @@ type Decision = { operation: string; mechanism: string; demand?: string; lane?:
|
|
|
86
86
|
* the vendor's own API's operation of that id, or, when its own API has none, the one lane's that has it. So an id two
|
|
87
87
|
* units share is two decisions, and a lane's operation its root serves stays the lane's gap (Clerk's Backend and
|
|
88
88
|
* Frontend APIs both name `ListOrganizationMemberships`). A demanded operation (journeys/demand.json, the vendor's
|
|
89
|
-
* backend calls) is the unit's a row naming no lane would decide.
|
|
90
|
-
|
|
89
|
+
* backend calls) is the unit's a row naming no lane would decide. Construction may leave unfinished operations as
|
|
90
|
+
* gaps; the default and conformance callers require completion. Both modes hold implemented operations to the table. */
|
|
91
|
+
export function decisionTableProblems(unit: string, byUnit: Record<string, Owners>, decisions: Decision[], demanded: string[] = [], doors: string[] = [], options: { building?: boolean } = {}): string[] {
|
|
91
92
|
const owners = { ...(byUnit[unit] ?? {}), ...Object.fromEntries(doors.map((id) => [`door:${id}`, 'door'])) };
|
|
92
93
|
const holders = (id: string): string[] => Object.keys(byUnit).filter((u) => id in byUnit[u]!);
|
|
93
94
|
const unnamedUnit = (id: string): string | undefined => (id in (byUnit[''] ?? {}) ? '' : holders(id).length === 1 ? holders(id)[0] : undefined);
|
|
@@ -103,9 +104,9 @@ export function decisionTableProblems(unit: string, byUnit: Record<string, Owner
|
|
|
103
104
|
}
|
|
104
105
|
for (const d of mine) {
|
|
105
106
|
if (!(d.operation in owners)) out.push(`${d.operation} is decided for ${unit || 'the root'}, which has no such operation`);
|
|
106
|
-
else if (owners[d.operation] === 'gap' && d.mechanism !== 'unmodeled') out.push(`${d.operation} is decided ${d.mechanism} and served by nothing`);
|
|
107
|
+
else if (!options.building && owners[d.operation] === 'gap' && d.mechanism !== 'unmodeled') out.push(`${d.operation} is decided ${d.mechanism} and served by nothing`);
|
|
107
108
|
}
|
|
108
|
-
for (const op of demanded) if (op && unnamedUnit(op) === unit && owners[op] === 'gap') out.push(`${op} is demanded (journeys/demand.json) and served by nothing`);
|
|
109
|
+
if (!options.building) for (const op of demanded) if (op && unnamedUnit(op) === unit && owners[op] === 'gap') out.push(`${op} is demanded (journeys/demand.json) and served by nothing`);
|
|
109
110
|
return out;
|
|
110
111
|
}
|
|
111
112
|
|
package/src/create-pack.ts
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
// vendor facts, the descriptor's hosts and the families
|
|
15
15
|
// bun scripts/create-pack.ts <unit> --index write src/semantics/index.ts, the handler map, and src/fetch.ts from the
|
|
16
16
|
// families' files (every exported function, by its name)
|
|
17
|
+
// bun scripts/create-pack.ts <unit> --index --building the same during construction; unfinished operations stay gaps,
|
|
18
|
+
// and implemented operations must match their decisions
|
|
17
19
|
// bun scripts/create-pack.ts <unit> --brief [--out f] the author's brief: every operation the derived core does not
|
|
18
20
|
// serve and no handler answers yet, by family, with what the
|
|
19
21
|
// vendored spec says of it (summary, description, parameters,
|
|
@@ -93,7 +95,7 @@ export function handlerIndex(dir: string): string {
|
|
|
93
95
|
* serves, the demand it answers and the mechanism that serves it (`handler` or `core`); an operation it does not serve
|
|
94
96
|
* is `unmodeled` in the manifest. The unit's owners are the vendor's dispatch's for that unit. A problem stops the index:
|
|
95
97
|
* six packs once shipped answering 404 to everything, and scope once grew past demand, both unseen by the tools. */
|
|
96
|
-
export async function decisionProblems(dir: string): Promise<string[]> {
|
|
98
|
+
export async function decisionProblems(dir: string, options: { building?: boolean } = {}): Promise<string[]> {
|
|
97
99
|
const out: string[] = [];
|
|
98
100
|
const journeys = existsSync(join(dir, 'journeys')) ? join(dir, 'journeys') : join(dir, '..', 'journeys');
|
|
99
101
|
const tablePath = join(journeys, 'decisions.json');
|
|
@@ -119,9 +121,9 @@ export async function decisionProblems(dir: string): Promise<string[]> {
|
|
|
119
121
|
// a refresh reads the vendor's list or retrieve, and a World rooted at a second World's twin of the pack reads it from
|
|
120
122
|
// that twin (pack-done's (g)): an operation a refresh scope names is one the pack serves
|
|
121
123
|
const { manifest } = await import(resolve(dir, 'src', 'manifest.ts')) as { manifest?: { doors?: Array<{ id: string }>; resources?: Record<string, { refresh?: { list?: string; detail?: string; get?: string } }> } };
|
|
122
|
-
out.push(...decisionTableProblems(unit, byUnit, table.decisions ?? [], demanded, manifest?.doors?.map((d) => d.id) ?? []));
|
|
124
|
+
out.push(...decisionTableProblems(unit, byUnit, table.decisions ?? [], demanded, manifest?.doors?.map((d) => d.id) ?? [], options));
|
|
123
125
|
for (const [name, decl] of Object.entries(manifest?.resources ?? {})) {
|
|
124
|
-
for (const id of [decl.refresh?.list ?? decl.refresh?.get, decl.refresh?.detail]) if (id && id in owners && owners[id] === 'gap') out.push(`${id}: ${name}'s refresh reads it, and nothing serves it (serve it, or declare refresh none with why)`);
|
|
126
|
+
for (const id of [decl.refresh?.list ?? decl.refresh?.get, decl.refresh?.detail]) if (!options.building && id && id in owners && owners[id] === 'gap') out.push(`${id}: ${name}'s refresh reads it, and nothing serves it (serve it, or declare refresh none with why)`);
|
|
125
127
|
}
|
|
126
128
|
return out;
|
|
127
129
|
}
|
|
@@ -447,7 +449,11 @@ export function laneInitFiles(vendor: string, lanes: string[]): Record<string, s
|
|
|
447
449
|
export async function createPack(args: string[]): Promise<void> {
|
|
448
450
|
const [unit, ...rest] = args;
|
|
449
451
|
if (!unit || (!rest.includes('--index') && !rest.includes('--brief') && !rest.includes('--init'))) {
|
|
450
|
-
console.error('usage: bun scripts/create-pack.ts <dir> --init <vendor> | <unit> --index | --brief [--out <file>]');
|
|
452
|
+
console.error('usage: bun scripts/create-pack.ts <dir> --init <vendor> | <unit> --index [--building] | --brief [--out <file>]');
|
|
453
|
+
process.exit(2);
|
|
454
|
+
}
|
|
455
|
+
if (rest.includes('--building') && (!rest.includes('--index') || rest.includes('--init') || rest.includes('--brief'))) {
|
|
456
|
+
console.error('create-pack: --building requires --index alone; it generates an unfinished pack, not completion evidence');
|
|
451
457
|
process.exit(2);
|
|
452
458
|
}
|
|
453
459
|
const dir = resolve(unit);
|
|
@@ -530,9 +536,10 @@ export async function createPack(args: string[]): Promise<void> {
|
|
|
530
536
|
}
|
|
531
537
|
if (odd) process.exit(1);
|
|
532
538
|
if (!lanesOnly) {
|
|
533
|
-
const problems = await decisionProblems(dir);
|
|
539
|
+
const problems = await decisionProblems(dir, { building: rest.includes('--building') });
|
|
534
540
|
if (problems.length) { console.error(`create-pack: ${basename(dir)}: ${problems.length} problem(s) with what the unit serves:\n ${problems.join('\n ')}`); process.exit(1); }
|
|
535
541
|
}
|
|
542
|
+
if (rest.includes('--building')) console.log('create-pack: construction index only; unfinished operations remain gaps; run ordinary --index before completion');
|
|
536
543
|
}
|
|
537
544
|
if (rest.includes('--brief')) {
|
|
538
545
|
const text = await brief(unit);
|