@uwmd/core 1.1.2 → 1.3.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 +3 -3
- package/dist/agents/bancroft.d.ts +22 -1
- package/dist/agents/bancroft.d.ts.map +1 -1
- package/dist/agents/bancroft.js +86 -60
- package/dist/agents/bancroft.js.map +1 -1
- package/dist/agents/provider.d.ts +62 -0
- package/dist/agents/provider.d.ts.map +1 -0
- package/dist/agents/provider.js +28 -0
- package/dist/agents/provider.js.map +1 -0
- package/dist/agents/providers/anthropic.d.ts +20 -0
- package/dist/agents/providers/anthropic.d.ts.map +1 -0
- package/dist/agents/providers/anthropic.js +104 -0
- package/dist/agents/providers/anthropic.js.map +1 -0
- package/dist/agents/providers/replay.d.ts +46 -0
- package/dist/agents/providers/replay.d.ts.map +1 -0
- package/dist/agents/providers/replay.js +162 -0
- package/dist/agents/providers/replay.js.map +1 -0
- package/dist/agents/schemas.d.ts +3 -3
- package/dist/agents/schemas.d.ts.map +1 -1
- package/dist/agents/schemas.js.map +1 -1
- package/dist/browser.d.ts +26 -3
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +23 -3
- package/dist/browser.js.map +1 -1
- package/dist/calc/builtins.d.ts +10 -0
- package/dist/calc/builtins.d.ts.map +1 -1
- package/dist/calc/builtins.js +64 -42
- package/dist/calc/builtins.js.map +1 -1
- package/dist/calc/errors.js +1 -1
- package/dist/calc/evaluator.js +6 -2
- package/dist/calc/evaluator.js.map +1 -1
- package/dist/calc/index.d.ts +7 -0
- package/dist/calc/index.d.ts.map +1 -1
- package/dist/calc/index.js +13 -1
- package/dist/calc/index.js.map +1 -1
- package/dist/calc/quantize.d.ts +56 -0
- package/dist/calc/quantize.d.ts.map +1 -0
- package/dist/calc/quantize.js +116 -0
- package/dist/calc/quantize.js.map +1 -0
- package/dist/cli-packages.d.ts +11 -0
- package/dist/cli-packages.d.ts.map +1 -0
- package/dist/cli-packages.js +177 -0
- package/dist/cli-packages.js.map +1 -0
- package/dist/cli.js +424 -28
- package/dist/cli.js.map +1 -1
- package/dist/context.js +23 -23
- package/dist/deal-package-context.d.ts +52 -0
- package/dist/deal-package-context.d.ts.map +1 -0
- package/dist/deal-package-context.js +132 -0
- package/dist/deal-package-context.js.map +1 -0
- package/dist/deal-package-zip.d.ts +36 -0
- package/dist/deal-package-zip.d.ts.map +1 -0
- package/dist/deal-package-zip.js +132 -0
- package/dist/deal-package-zip.js.map +1 -0
- package/dist/deal-package.d.ts +82 -0
- package/dist/deal-package.d.ts.map +1 -0
- package/dist/deal-package.js +189 -0
- package/dist/deal-package.js.map +1 -0
- package/dist/defaults.d.ts +48 -0
- package/dist/defaults.d.ts.map +1 -1
- package/dist/defaults.js +512 -0
- package/dist/defaults.js.map +1 -1
- package/dist/index.d.ts +32 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -3
- package/dist/index.js.map +1 -1
- package/dist/init.js +1 -1
- package/dist/init.js.map +1 -1
- package/dist/integrity.d.ts +9 -0
- package/dist/integrity.d.ts.map +1 -1
- package/dist/integrity.js +12 -2
- package/dist/integrity.js.map +1 -1
- package/dist/lease-abstract.d.ts +82 -0
- package/dist/lease-abstract.d.ts.map +1 -0
- package/dist/lease-abstract.js +183 -0
- package/dist/lease-abstract.js.map +1 -0
- package/dist/lite-bridge.d.ts +58 -0
- package/dist/lite-bridge.d.ts.map +1 -0
- package/dist/lite-bridge.js +525 -0
- package/dist/lite-bridge.js.map +1 -0
- package/dist/lite.d.ts +63 -0
- package/dist/lite.d.ts.map +1 -0
- package/dist/lite.js +362 -0
- package/dist/lite.js.map +1 -0
- package/dist/modules.d.ts.map +1 -1
- package/dist/modules.js +278 -13
- package/dist/modules.js.map +1 -1
- package/dist/packs/excel-emit.d.ts +18 -1
- package/dist/packs/excel-emit.d.ts.map +1 -1
- package/dist/packs/excel-emit.js +18 -1
- package/dist/packs/excel-emit.js.map +1 -1
- package/dist/packs/hospitality.d.ts +3 -0
- package/dist/packs/hospitality.d.ts.map +1 -0
- package/dist/packs/hospitality.js +122 -0
- package/dist/packs/hospitality.js.map +1 -0
- package/dist/packs/index.d.ts +5 -1
- package/dist/packs/index.d.ts.map +1 -1
- package/dist/packs/index.js +13 -1
- package/dist/packs/index.js.map +1 -1
- package/dist/packs/land.d.ts +3 -0
- package/dist/packs/land.d.ts.map +1 -0
- package/dist/packs/land.js +124 -0
- package/dist/packs/land.js.map +1 -0
- package/dist/packs/senior-housing.d.ts +3 -0
- package/dist/packs/senior-housing.d.ts.map +1 -0
- package/dist/packs/senior-housing.js +123 -0
- package/dist/packs/senior-housing.js.map +1 -0
- package/dist/packs/student-housing.d.ts +3 -0
- package/dist/packs/student-housing.d.ts.map +1 -0
- package/dist/packs/student-housing.js +124 -0
- package/dist/packs/student-housing.js.map +1 -0
- package/dist/parser.d.ts +9 -0
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +40 -6
- package/dist/parser.js.map +1 -1
- package/dist/protocol.d.ts +69 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +109 -1
- package/dist/protocol.js.map +1 -1
- package/dist/receipts.d.ts +216 -0
- package/dist/receipts.d.ts.map +1 -0
- package/dist/receipts.js +554 -0
- package/dist/receipts.js.map +1 -0
- package/dist/report.js +165 -165
- package/dist/runner.d.ts +13 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +5 -5
- package/dist/runner.js.map +1 -1
- package/dist/source-representation.d.ts +40 -0
- package/dist/source-representation.d.ts.map +1 -0
- package/dist/source-representation.js +114 -0
- package/dist/source-representation.js.map +1 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +34 -0
- package/dist/types.js.map +1 -1
- package/dist/uwcsv.d.ts.map +1 -1
- package/dist/uwcsv.js +10 -78
- package/dist/uwcsv.js.map +1 -1
- package/dist/uwxml.d.ts.map +1 -1
- package/dist/uwxml.js +10 -2
- package/dist/uwxml.js.map +1 -1
- package/dist/validator.js +10 -10
- package/dist/validator.js.map +1 -1
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +8 -0
- package/dist/version.js.map +1 -0
- package/dist/zip-safety.d.ts +15 -0
- package/dist/zip-safety.d.ts.map +1 -0
- package/dist/zip-safety.js +109 -0
- package/dist/zip-safety.js.map +1 -0
- package/package.json +12 -3
package/dist/cli.js
CHANGED
|
@@ -10,12 +10,14 @@ import { compact, diff } from './compactor.js';
|
|
|
10
10
|
import { generateBlankUWFile } from './init.js';
|
|
11
11
|
import { render } from './renderer.js';
|
|
12
12
|
import { renderReportHtml } from './report.js';
|
|
13
|
-
import {
|
|
14
|
-
import { toUWEnvelope } from './envelope.js';
|
|
13
|
+
import { stringifyUWEnvelope } from './uwjson.js';
|
|
14
|
+
import { stampEnvelopeDigest, toUWEnvelope, } from './envelope.js';
|
|
15
15
|
import { CORE_CODEC_REGISTRY } from './codecs.js';
|
|
16
16
|
import { writeAgentBlock } from './runner.js';
|
|
17
17
|
import { applyEdit } from './editor.js';
|
|
18
18
|
import { verifyChain, verifyProvenance } from './integrity.js';
|
|
19
|
+
import { loadModuleManifest, createModuleRegistry, ModuleRegistryError } from './modules.js';
|
|
20
|
+
import { cmdLeaseValidate, cmdLeaseProject, cmdPackageCreate, cmdPackageVerify, cmdPackageList, cmdPackageToContext, cmdPackageContextValidate, cmdPackageEdges, } from './cli-packages.js';
|
|
19
21
|
import { evaluateCalc } from './calc/index.js';
|
|
20
22
|
import { buildAgentContext, buildAgentPrompt, isContextReady, BANCROFT_LAYERS } from './context.js';
|
|
21
23
|
import { runBancroftAgent } from './agents/bancroft.js';
|
|
@@ -24,6 +26,12 @@ import { rankGaps } from './refinement.js';
|
|
|
24
26
|
import { resolveValue } from './cascade.js';
|
|
25
27
|
import { getAssetClassDefaults } from './defaults.js';
|
|
26
28
|
import { MULTIFAMILY_PACK, getPackForAssetClass } from './packs/index.js';
|
|
29
|
+
import { issueReceipt, verifyReceipt, assertUWReceipt } from './receipts.js';
|
|
30
|
+
import { CORE_VERSION } from './version.js';
|
|
31
|
+
import { detectUWSourceRepresentation, UWX_REPRESENTATION_ID, UW_LITE_SOURCE_DESCRIPTOR, UWX_SOURCE_DESCRIPTOR, UWX_EXTENSION, } from './source-representation.js';
|
|
32
|
+
import { parseUWLite } from './lite.js';
|
|
33
|
+
import { compileUWLite, projectUWEnvelopeToLite, stringifyUWX, } from './lite-bridge.js';
|
|
34
|
+
import { UW_LITE_REPRESENTATION_ID } from './source-representation.js';
|
|
27
35
|
const [, , command, ...args] = process.argv;
|
|
28
36
|
function readFile(path) {
|
|
29
37
|
const resolved = resolve(path);
|
|
@@ -71,6 +79,20 @@ function parseFlags(rawArgs) {
|
|
|
71
79
|
// ─── Commands ─────────────────────────────────────────────────────────────────
|
|
72
80
|
function cmdParse(file, flags) {
|
|
73
81
|
const content = readFile(file);
|
|
82
|
+
const detection = detectUWSourceRepresentation(content, file);
|
|
83
|
+
if (detection.representation !== UWX_REPRESENTATION_ID) {
|
|
84
|
+
const lite = parseUWLite(content);
|
|
85
|
+
console.log(JSON.stringify({
|
|
86
|
+
representation: lite.representation,
|
|
87
|
+
representation_version: lite.representation_version,
|
|
88
|
+
frontmatter: lite.frontmatter,
|
|
89
|
+
fields: lite.fields,
|
|
90
|
+
issues: lite.issues,
|
|
91
|
+
}, null, flags['compact'] ? 0 : 2));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
for (const warning of detection.warnings)
|
|
95
|
+
console.warn(`Warning: ${warning}`);
|
|
74
96
|
const parsed = parseUWFile(content, { strict: flags['strict'] === true });
|
|
75
97
|
const output = {
|
|
76
98
|
frontmatter: parsed.frontmatter,
|
|
@@ -83,6 +105,38 @@ function cmdParse(file, flags) {
|
|
|
83
105
|
}
|
|
84
106
|
function cmdValidate(file, flags) {
|
|
85
107
|
const content = readFile(file);
|
|
108
|
+
const detection = detectUWSourceRepresentation(content, file);
|
|
109
|
+
if (detection.representation !== UWX_REPRESENTATION_ID) {
|
|
110
|
+
const lite = parseUWLite(content);
|
|
111
|
+
const errors = lite.issues.filter((issue) => issue.severity === 'error');
|
|
112
|
+
const warnings = lite.issues.filter((issue) => issue.severity === 'warning');
|
|
113
|
+
const result = {
|
|
114
|
+
representation: lite.representation,
|
|
115
|
+
overall_status: errors.length > 0 ? 'errors' : warnings.length > 0 ? 'warnings' : 'clean',
|
|
116
|
+
issues: lite.issues,
|
|
117
|
+
errors,
|
|
118
|
+
warnings,
|
|
119
|
+
};
|
|
120
|
+
if (flags['json']) {
|
|
121
|
+
console.log(JSON.stringify(result, null, 2));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
console.log(`\n${result.overall_status.toUpperCase()} - ${basename(file)}\n`);
|
|
125
|
+
if (result.issues.length === 0) {
|
|
126
|
+
console.log('No Lite syntax issues found.');
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
for (const issue of result.issues) {
|
|
130
|
+
const location = issue.line ? ` line ${issue.line}` : '';
|
|
131
|
+
const field = issue.field_path ? ` [${issue.field_path}]` : '';
|
|
132
|
+
console.log(` [${issue.severity.toUpperCase()}]${location}${field} ${issue.code}: ${issue.message}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
console.log('');
|
|
136
|
+
process.exit(errors.length > 0 ? 1 : 0);
|
|
137
|
+
}
|
|
138
|
+
for (const warning of detection.warnings)
|
|
139
|
+
console.warn(`Warning: ${warning}`);
|
|
86
140
|
const parsed = parseUWFile(content);
|
|
87
141
|
const instCfg = loadInstitutionConfig(flags['institution']);
|
|
88
142
|
const result = validateUWFile(parsed, instCfg?.thresholds);
|
|
@@ -229,8 +283,13 @@ function cmdInit(flags) {
|
|
|
229
283
|
dealStage: flags['stage'],
|
|
230
284
|
tier: flags['tier'] ?? 'screener',
|
|
231
285
|
});
|
|
286
|
+
// generateBlankUWFile() emits structured UWX content, so the default filename
|
|
287
|
+
// must be .uwx.md. Writing it as .uw.md produced a file the format spec
|
|
288
|
+
// forbids and detectUWSourceRepresentation() flags as legacy on the next load.
|
|
232
289
|
const filename = flags['output']
|
|
233
|
-
?? (flags['name']
|
|
290
|
+
?? (flags['name']
|
|
291
|
+
? `${flags['name'].replace(/\s+/g, '-').toLowerCase()}${UWX_EXTENSION}`
|
|
292
|
+
: `new-deal${UWX_EXTENSION}`);
|
|
234
293
|
const outPath = resolve(filename);
|
|
235
294
|
writeFileSync(outPath, content, 'utf-8');
|
|
236
295
|
console.log(`Created: ${outPath}`);
|
|
@@ -269,16 +328,14 @@ function cmdSummary(file) {
|
|
|
269
328
|
`);
|
|
270
329
|
}
|
|
271
330
|
async function cmdExport(file, flags) {
|
|
272
|
-
const
|
|
273
|
-
const
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
includeSuperseded: !(flags['no-superseded'] === true || flags['compact'] === true),
|
|
277
|
-
});
|
|
331
|
+
const includeSuperseded = !(flags['no-superseded'] === true || flags['compact'] === true);
|
|
332
|
+
const loaded = await loadEnvelope(file);
|
|
333
|
+
const envelope = includeSuperseded ? loaded : { ...loaded, superseded: {} };
|
|
334
|
+
const text = stringifyUWEnvelope(await stampEnvelopeDigest(envelope));
|
|
278
335
|
// Default output path: swap the .uw.md suffix for .uw.json (fall back to appending).
|
|
279
336
|
const outPath = flags['output']
|
|
280
337
|
? resolve(flags['output'])
|
|
281
|
-
: resolve(
|
|
338
|
+
: resolve(replaceUWExtension(file, '.uw.json'));
|
|
282
339
|
if (flags['stdout']) {
|
|
283
340
|
process.stdout.write(text);
|
|
284
341
|
return;
|
|
@@ -292,12 +349,33 @@ const FORMAT_ALIASES = {
|
|
|
292
349
|
csv: 'uw-csv-bundle',
|
|
293
350
|
'csv-bundle': 'uw-csv-bundle',
|
|
294
351
|
'uw-json': 'uw-json',
|
|
352
|
+
lite: 'uw-lite-markdown',
|
|
353
|
+
uw: 'uw-lite-markdown',
|
|
354
|
+
uwx: 'uwx-markdown',
|
|
355
|
+
'uw-lite': 'uw-lite-markdown',
|
|
356
|
+
'uw-lite-markdown': 'uw-lite-markdown',
|
|
357
|
+
'uwx-markdown': 'uwx-markdown',
|
|
295
358
|
'uw-xml': 'uw-xml',
|
|
296
359
|
'uw-csv-bundle': 'uw-csv-bundle',
|
|
297
360
|
};
|
|
298
361
|
async function loadEnvelope(file) {
|
|
299
|
-
|
|
300
|
-
|
|
362
|
+
const lower = file.toLowerCase();
|
|
363
|
+
if (lower.endsWith('.uw.md') || lower.endsWith(UWX_EXTENSION)) {
|
|
364
|
+
const content = readFile(file);
|
|
365
|
+
const detection = detectUWSourceRepresentation(content, file);
|
|
366
|
+
if (detection.representation !== UWX_REPRESENTATION_ID) {
|
|
367
|
+
const compilation = compileUWLite(parseUWLite(content));
|
|
368
|
+
if (!compilation.ok) {
|
|
369
|
+
const issues = compilation.report.issues
|
|
370
|
+
.map((issue) => `${issue.code}: ${issue.message}`)
|
|
371
|
+
.join('; ');
|
|
372
|
+
throw new Error(`UW Lite compilation failed: ${issues}`);
|
|
373
|
+
}
|
|
374
|
+
return compilation.envelope;
|
|
375
|
+
}
|
|
376
|
+
for (const warning of detection.warnings)
|
|
377
|
+
console.warn(`Warning: ${warning}`);
|
|
378
|
+
return toUWEnvelope(parseUWFile(content));
|
|
301
379
|
}
|
|
302
380
|
const sourceCodec = CORE_CODEC_REGISTRY.findByFileName(file);
|
|
303
381
|
if (!sourceCodec || !sourceCodec.descriptor.directions.includes('read')) {
|
|
@@ -308,9 +386,17 @@ async function loadEnvelope(file) {
|
|
|
308
386
|
: readFile(file);
|
|
309
387
|
return sourceCodec.decode(input);
|
|
310
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* Swap a known UW source extension for `extension`, appending if none matches.
|
|
391
|
+
*
|
|
392
|
+
* `.uwx.md` must be in this list. Without it a UWX input fell through to the
|
|
393
|
+
* append branch, so `uwmd export deal.uwx.md` wrote `deal.uwx.md.uw.json`
|
|
394
|
+
* rather than `deal.uw.json`. (`.uw.md` does not match a `.uwx.md` filename —
|
|
395
|
+
* the suffixes genuinely differ — so it was silently a no-op, not a mis-match.)
|
|
396
|
+
*/
|
|
311
397
|
function replaceUWExtension(file, extension) {
|
|
312
398
|
const lower = file.toLowerCase();
|
|
313
|
-
for (const suffix of ['.uw.csv.zip', '.uw.md', '.uw.json', '.uw.xml']) {
|
|
399
|
+
for (const suffix of [UWX_EXTENSION, '.uw.csv.zip', '.uw.md', '.uw.json', '.uw.xml']) {
|
|
314
400
|
if (lower.endsWith(suffix))
|
|
315
401
|
return `${file.slice(0, -suffix.length)}${extension}`;
|
|
316
402
|
}
|
|
@@ -319,15 +405,39 @@ function replaceUWExtension(file, extension) {
|
|
|
319
405
|
async function cmdConvert(file, flags) {
|
|
320
406
|
const requested = flags['to'];
|
|
321
407
|
if (typeof requested !== 'string') {
|
|
322
|
-
throw new Error('convert requires --to uw-json|uw-xml|uw-csv-bundle.');
|
|
408
|
+
throw new Error('convert requires --to uw-lite-markdown|uwx-markdown|uw-json|uw-xml|uw-csv-bundle.');
|
|
323
409
|
}
|
|
324
410
|
const targetId = FORMAT_ALIASES[requested.toLowerCase()] ?? requested;
|
|
325
|
-
const target = CORE_CODEC_REGISTRY.get(targetId);
|
|
326
|
-
if (!target.descriptor.directions.includes('write')) {
|
|
327
|
-
throw new Error(`Representation ${targetId} is not writable.`);
|
|
328
|
-
}
|
|
329
411
|
const envelope = await loadEnvelope(file);
|
|
330
|
-
|
|
412
|
+
let encoded;
|
|
413
|
+
let extension;
|
|
414
|
+
if (targetId === UWX_REPRESENTATION_ID) {
|
|
415
|
+
encoded = stringifyUWX(envelope);
|
|
416
|
+
extension = UWX_EXTENSION;
|
|
417
|
+
}
|
|
418
|
+
else if (targetId === UW_LITE_REPRESENTATION_ID) {
|
|
419
|
+
const projection = projectUWEnvelopeToLite(envelope);
|
|
420
|
+
encoded = projection.content;
|
|
421
|
+
extension = '.uw.md';
|
|
422
|
+
if (projection.report.lossy) {
|
|
423
|
+
console.warn(`Warning: Lite projection omitted ${projection.report.omitted_paths.length} advanced path(s).`);
|
|
424
|
+
}
|
|
425
|
+
if (typeof flags['projection-report'] === 'string') {
|
|
426
|
+
writeFileSync(resolve(flags['projection-report']), JSON.stringify(projection.report, null, 2), 'utf-8');
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
const target = CORE_CODEC_REGISTRY.get(targetId);
|
|
431
|
+
if (!target.descriptor.directions.includes('write')) {
|
|
432
|
+
throw new Error(`Representation ${targetId} is not writable.`);
|
|
433
|
+
}
|
|
434
|
+
const targetEncoded = await target.encode(envelope);
|
|
435
|
+
if (typeof targetEncoded !== 'string' && !(targetEncoded instanceof Uint8Array)) {
|
|
436
|
+
throw new Error(`Representation ${targetId} did not produce file-compatible output.`);
|
|
437
|
+
}
|
|
438
|
+
encoded = targetEncoded;
|
|
439
|
+
extension = target.descriptor.file_extensions[0];
|
|
440
|
+
}
|
|
331
441
|
if (typeof encoded !== 'string' && !(encoded instanceof Uint8Array)) {
|
|
332
442
|
throw new Error(`Representation ${targetId} did not produce file-compatible output.`);
|
|
333
443
|
}
|
|
@@ -335,15 +445,174 @@ async function cmdConvert(file, flags) {
|
|
|
335
445
|
process.stdout.write(typeof encoded === 'string' ? encoded : Buffer.from(encoded));
|
|
336
446
|
return;
|
|
337
447
|
}
|
|
338
|
-
const extension = target.descriptor.file_extensions[0];
|
|
339
448
|
const outPath = flags['output']
|
|
340
449
|
? resolve(flags['output'])
|
|
341
450
|
: resolve(replaceUWExtension(file, extension));
|
|
342
451
|
writeFileSync(outPath, encoded, 'utf-8');
|
|
343
452
|
console.log(`Converted ${basename(file)} → ${basename(outPath)} (${targetId})`);
|
|
344
453
|
}
|
|
454
|
+
// ─── Receipts (RFC 0016 / spec/UW_RECEIPT_v1.md) ─────────────────────────────
|
|
455
|
+
async function cmdReceiptIssue(file, flags) {
|
|
456
|
+
const receipt = await issueReceipt(readFile(file), {
|
|
457
|
+
filename: file,
|
|
458
|
+
issuer: `uwmd-cli@${CORE_VERSION}`,
|
|
459
|
+
...(typeof flags['issued-at'] === 'string' ? { issued_at: flags['issued-at'] } : {}),
|
|
460
|
+
});
|
|
461
|
+
const serialized = `${JSON.stringify(receipt, null, 2)}\n`;
|
|
462
|
+
if (flags['stdout']) {
|
|
463
|
+
process.stdout.write(serialized);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
const outPath = flags['output']
|
|
467
|
+
? resolve(flags['output'])
|
|
468
|
+
: resolve(`${file.replace(/\.(uw|uwx)\.md$/i, '')}.receipt.json`);
|
|
469
|
+
writeFileSync(outPath, serialized, 'utf-8');
|
|
470
|
+
const computed = receipt.computation.results.filter((r) => r.computed).length;
|
|
471
|
+
console.log(`Issued receipt for ${basename(file)} → ${basename(outPath)} ` +
|
|
472
|
+
`(${receipt.computation.pack}@${receipt.computation.pack_version}, ` +
|
|
473
|
+
`${computed}/${receipt.computation.results.length} outputs computed)`);
|
|
474
|
+
console.log('A receipt attests that these outputs follow from this record. It does not attest that the inputs are true.');
|
|
475
|
+
}
|
|
476
|
+
// ─── Modules ──────────────────────────────────────────────────────────────────
|
|
477
|
+
function readManifestFile(path) {
|
|
478
|
+
try {
|
|
479
|
+
return { ok: true, manifest: JSON.parse(readFileSync(resolve(path), 'utf-8')) };
|
|
480
|
+
}
|
|
481
|
+
catch (e) {
|
|
482
|
+
return { ok: false, message: e instanceof Error ? e.message : String(e) };
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
function hostTierFlag(flags) {
|
|
486
|
+
const tier = flags['tier'];
|
|
487
|
+
return typeof tier === 'string' ? tier : undefined;
|
|
488
|
+
}
|
|
489
|
+
function cmdModulesValidate(files, flags) {
|
|
490
|
+
const hostTier = hostTierFlag(flags);
|
|
491
|
+
const reports = files.map((file) => {
|
|
492
|
+
const read = readManifestFile(file);
|
|
493
|
+
if (!read.ok) {
|
|
494
|
+
return { file, ok: false, errors: [{ category: 'module', code: 'CLI-MOD-READ', message: read.message }] };
|
|
495
|
+
}
|
|
496
|
+
const result = loadModuleManifest(read.manifest, hostTier ? { hostTier } : {});
|
|
497
|
+
return { file, ok: result.ok, errors: result.errors };
|
|
498
|
+
});
|
|
499
|
+
if (flags['json']) {
|
|
500
|
+
console.log(JSON.stringify({ modules: reports }, null, 2));
|
|
501
|
+
process.exit(reports.every((r) => r.ok) ? 0 : 1);
|
|
502
|
+
}
|
|
503
|
+
for (const report of reports) {
|
|
504
|
+
if (report.ok) {
|
|
505
|
+
console.log(`\nOK - ${basename(report.file)}`);
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
console.log(`\nERRORS - ${basename(report.file)}`);
|
|
509
|
+
for (const error of report.errors) {
|
|
510
|
+
// The pointer is what makes this actionable: it names the exact
|
|
511
|
+
// declaration that failed, not just the manifest.
|
|
512
|
+
const at = 'pointer' in error && error.pointer ? ` [${error.pointer}]` : '';
|
|
513
|
+
console.log(` ${error.code}${at} ${error.message}`);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
const failed = reports.filter((r) => !r.ok).length;
|
|
517
|
+
console.log(`\n${reports.length - failed}/${reports.length} manifest(s) loaded${hostTier ? ` against ${hostTier}` : ''}.\n`);
|
|
518
|
+
process.exit(failed > 0 ? 1 : 0);
|
|
519
|
+
}
|
|
520
|
+
function cmdModulesList(files, flags) {
|
|
521
|
+
const hostTier = hostTierFlag(flags);
|
|
522
|
+
const manifests = [];
|
|
523
|
+
for (const file of files) {
|
|
524
|
+
const read = readManifestFile(file);
|
|
525
|
+
if (!read.ok) {
|
|
526
|
+
console.error(`Cannot read ${basename(file)}: ${read.message}`);
|
|
527
|
+
process.exit(1);
|
|
528
|
+
}
|
|
529
|
+
manifests.push(read.manifest);
|
|
530
|
+
}
|
|
531
|
+
let registry;
|
|
532
|
+
try {
|
|
533
|
+
registry = createModuleRegistry({ modules: manifests, ...(hostTier ? { hostTier } : {}) });
|
|
534
|
+
}
|
|
535
|
+
catch (e) {
|
|
536
|
+
// Registry construction is all-or-nothing: a bad manifest means there is no
|
|
537
|
+
// registry to list, so report why rather than printing a partial one.
|
|
538
|
+
if (e instanceof ModuleRegistryError) {
|
|
539
|
+
console.error('Registry refused to load:');
|
|
540
|
+
for (const error of e.errors)
|
|
541
|
+
console.error(` ${error.code} ${error.message}`);
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
console.error(e instanceof Error ? e.message : String(e));
|
|
545
|
+
}
|
|
546
|
+
process.exit(1);
|
|
547
|
+
}
|
|
548
|
+
const rows = registry.modules.map((m) => ({
|
|
549
|
+
id: m.id,
|
|
550
|
+
name: m.name,
|
|
551
|
+
version: m.version,
|
|
552
|
+
requires_tier: m.requires_tier,
|
|
553
|
+
asset_classes: m.asset_classes ?? [],
|
|
554
|
+
calculations: (m.calculations ?? []).length,
|
|
555
|
+
validations: (m.validations ?? []).length,
|
|
556
|
+
sections: (m.sections ?? []).length,
|
|
557
|
+
agent_layers: (m.agent_layers ?? []).length,
|
|
558
|
+
}));
|
|
559
|
+
if (flags['json']) {
|
|
560
|
+
console.log(JSON.stringify({ modules: rows }, null, 2));
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
console.log(`\n${rows.length} module(s) loaded\n`);
|
|
564
|
+
for (const row of rows) {
|
|
565
|
+
console.log(` ${row.id}@${row.version} — ${row.name}`);
|
|
566
|
+
console.log(` tier: ${row.requires_tier}`);
|
|
567
|
+
console.log(` asset classes: ${row.asset_classes.length ? row.asset_classes.join(', ') : 'class-agnostic'}`);
|
|
568
|
+
console.log(` contributes: ${row.calculations} calc, ${row.validations} validation, ` +
|
|
569
|
+
`${row.sections} section, ${row.agent_layers} agent layer`);
|
|
570
|
+
}
|
|
571
|
+
console.log('');
|
|
572
|
+
}
|
|
573
|
+
async function cmdReceiptVerify(file, receiptPath, flags) {
|
|
574
|
+
let receipt;
|
|
575
|
+
try {
|
|
576
|
+
const raw = JSON.parse(readFileSync(resolve(receiptPath), 'utf-8'));
|
|
577
|
+
assertUWReceipt(raw);
|
|
578
|
+
receipt = raw;
|
|
579
|
+
}
|
|
580
|
+
catch (e) {
|
|
581
|
+
console.error(`Not a readable receipt: ${e instanceof Error ? e.message : String(e)}`);
|
|
582
|
+
process.exit(2);
|
|
583
|
+
}
|
|
584
|
+
const result = await verifyReceipt(receipt, readFile(file), { filename: file });
|
|
585
|
+
if (flags['json']) {
|
|
586
|
+
console.log(JSON.stringify(result, null, 2));
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
console.log(`Verdict: ${result.verdict.toUpperCase()}`);
|
|
590
|
+
for (const issue of result.issues) {
|
|
591
|
+
const detail = issue.expected !== undefined || issue.actual !== undefined
|
|
592
|
+
? ` (expected ${issue.expected ?? '—'}, got ${issue.actual ?? '—'})`
|
|
593
|
+
: '';
|
|
594
|
+
console.log(` [${issue.code}] ${issue.message}${detail}`);
|
|
595
|
+
}
|
|
596
|
+
if (result.verdict === 'verified') {
|
|
597
|
+
console.log('\nThis record is unchanged since issuance and its stated outputs follow deterministically\n' +
|
|
598
|
+
'from its contents. It is NOT a statement that the inputs are true, complete, or approved.');
|
|
599
|
+
}
|
|
600
|
+
else if (result.verdict === 'unverifiable') {
|
|
601
|
+
console.log('\nThis verifier could not decide. That is not a negative result.');
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
// 0 verified, 1 failed, 3 unverifiable — so scripts can tell the third state apart.
|
|
605
|
+
if (result.verdict === 'failed')
|
|
606
|
+
process.exit(1);
|
|
607
|
+
if (result.verdict === 'unverifiable')
|
|
608
|
+
process.exit(3);
|
|
609
|
+
}
|
|
345
610
|
function cmdFormats(flags) {
|
|
346
|
-
const descriptors =
|
|
611
|
+
const descriptors = [
|
|
612
|
+
UW_LITE_SOURCE_DESCRIPTOR,
|
|
613
|
+
UWX_SOURCE_DESCRIPTOR,
|
|
614
|
+
...CORE_CODEC_REGISTRY.list(),
|
|
615
|
+
];
|
|
347
616
|
if (flags['json']) {
|
|
348
617
|
console.log(JSON.stringify(descriptors, null, 2));
|
|
349
618
|
return;
|
|
@@ -360,10 +629,12 @@ function cmdReport(file, flags) {
|
|
|
360
629
|
tier: flags['tier'],
|
|
361
630
|
preparedBy: flags['prepared-by'],
|
|
362
631
|
});
|
|
363
|
-
// Default output path: swap the
|
|
632
|
+
// Default output path: swap the source extension for .report.html. Routed
|
|
633
|
+
// through replaceUWExtension so every UW source extension is handled in one
|
|
634
|
+
// place — the inline .uw.md check this replaced missed .uwx.md entirely.
|
|
364
635
|
const outPath = flags['output']
|
|
365
636
|
? resolve(flags['output'])
|
|
366
|
-
: resolve(
|
|
637
|
+
: resolve(replaceUWExtension(file, '.report.html'));
|
|
367
638
|
if (flags['stdout']) {
|
|
368
639
|
process.stdout.write(result.html);
|
|
369
640
|
return;
|
|
@@ -759,7 +1030,7 @@ for (let _i = 0; _i < args.length; _i++) {
|
|
|
759
1030
|
break;
|
|
760
1031
|
case 'convert':
|
|
761
1032
|
if (!positional[0]) {
|
|
762
|
-
console.error('Usage: uwmd convert <file.uw.md|file.uw.json|file.uw.xml|file.uw.csv.zip> --to uw-json|uw-xml|uw-csv-bundle [--output <file>] [--stdout]');
|
|
1033
|
+
console.error('Usage: uwmd convert <file.uw.md|file.uwx.md|file.uw.json|file.uw.xml|file.uw.csv.zip> --to lite|uwx|uw-json|uw-xml|uw-csv-bundle [--projection-report <file>] [--output <file>] [--stdout]');
|
|
763
1034
|
process.exit(1);
|
|
764
1035
|
}
|
|
765
1036
|
await cmdConvert(positional[0], flags);
|
|
@@ -785,9 +1056,127 @@ for (let _i = 0; _i < args.length; _i++) {
|
|
|
785
1056
|
}
|
|
786
1057
|
cmdRefine(positional[0], flags);
|
|
787
1058
|
break;
|
|
1059
|
+
case 'lease': {
|
|
1060
|
+
const sub = positional[0];
|
|
1061
|
+
if (sub === 'validate') {
|
|
1062
|
+
if (!positional[1]) {
|
|
1063
|
+
console.error('Usage: uwmd lease validate <abstract.json> [--json]');
|
|
1064
|
+
process.exit(1);
|
|
1065
|
+
}
|
|
1066
|
+
cmdLeaseValidate(positional[1], flags);
|
|
1067
|
+
}
|
|
1068
|
+
else if (sub === 'project') {
|
|
1069
|
+
if (!positional[1]) {
|
|
1070
|
+
console.error('Usage: uwmd lease project <abstract.json> [--compact]');
|
|
1071
|
+
process.exit(1);
|
|
1072
|
+
}
|
|
1073
|
+
cmdLeaseProject(positional[1], flags);
|
|
1074
|
+
}
|
|
1075
|
+
else {
|
|
1076
|
+
console.error('Usage: uwmd lease <validate|project> <abstract.json>');
|
|
1077
|
+
process.exit(1);
|
|
1078
|
+
}
|
|
1079
|
+
break;
|
|
1080
|
+
}
|
|
1081
|
+
case 'package': {
|
|
1082
|
+
const sub = positional[0];
|
|
1083
|
+
const target = positional[1];
|
|
1084
|
+
if (sub === 'create') {
|
|
1085
|
+
if (!target) {
|
|
1086
|
+
console.error('Usage: uwmd package create <manifest.json> [--output <file.uwpkg.zip>]');
|
|
1087
|
+
process.exit(1);
|
|
1088
|
+
}
|
|
1089
|
+
await cmdPackageCreate(target, flags);
|
|
1090
|
+
}
|
|
1091
|
+
else if (sub === 'verify') {
|
|
1092
|
+
if (!target) {
|
|
1093
|
+
console.error('Usage: uwmd package verify <file.uwpkg.zip> [--json]');
|
|
1094
|
+
process.exit(1);
|
|
1095
|
+
}
|
|
1096
|
+
await cmdPackageVerify(target, flags);
|
|
1097
|
+
}
|
|
1098
|
+
else if (sub === 'list') {
|
|
1099
|
+
if (!target) {
|
|
1100
|
+
console.error('Usage: uwmd package list <file.uwpkg.zip> [--json]');
|
|
1101
|
+
process.exit(1);
|
|
1102
|
+
}
|
|
1103
|
+
cmdPackageList(target, flags);
|
|
1104
|
+
}
|
|
1105
|
+
else if (sub === 'to-context') {
|
|
1106
|
+
if (!target) {
|
|
1107
|
+
console.error('Usage: uwmd package to-context <file.uwpkg.zip> [--output <file.json>] [--stdout]');
|
|
1108
|
+
process.exit(1);
|
|
1109
|
+
}
|
|
1110
|
+
cmdPackageToContext(target, flags);
|
|
1111
|
+
}
|
|
1112
|
+
else if (sub === 'validate-context') {
|
|
1113
|
+
if (!target) {
|
|
1114
|
+
console.error('Usage: uwmd package validate-context <file.uwpkg.context.json> [--json]');
|
|
1115
|
+
process.exit(1);
|
|
1116
|
+
}
|
|
1117
|
+
cmdPackageContextValidate(target, flags);
|
|
1118
|
+
}
|
|
1119
|
+
else if (sub === 'edges') {
|
|
1120
|
+
if (!target) {
|
|
1121
|
+
console.error('Usage: uwmd package edges <file.uwpkg.zip>');
|
|
1122
|
+
process.exit(1);
|
|
1123
|
+
}
|
|
1124
|
+
cmdPackageEdges(target);
|
|
1125
|
+
}
|
|
1126
|
+
else {
|
|
1127
|
+
console.error('Usage: uwmd package <create|verify|list|to-context|validate-context|edges> ...');
|
|
1128
|
+
process.exit(1);
|
|
1129
|
+
}
|
|
1130
|
+
break;
|
|
1131
|
+
}
|
|
1132
|
+
case 'modules': {
|
|
1133
|
+
const sub = positional[0];
|
|
1134
|
+
const files = positional.slice(1);
|
|
1135
|
+
if (sub === 'validate') {
|
|
1136
|
+
if (files.length === 0) {
|
|
1137
|
+
console.error('Usage: uwmd modules validate <manifest.json...> [--tier <viewer-tier>] [--json]');
|
|
1138
|
+
process.exit(1);
|
|
1139
|
+
}
|
|
1140
|
+
cmdModulesValidate(files, flags);
|
|
1141
|
+
}
|
|
1142
|
+
else if (sub === 'list') {
|
|
1143
|
+
if (files.length === 0) {
|
|
1144
|
+
console.error('Usage: uwmd modules list <manifest.json...> [--tier <viewer-tier>] [--json]');
|
|
1145
|
+
process.exit(1);
|
|
1146
|
+
}
|
|
1147
|
+
cmdModulesList(files, flags);
|
|
1148
|
+
}
|
|
1149
|
+
else {
|
|
1150
|
+
console.error('Usage: uwmd modules <validate|list> <manifest.json...>');
|
|
1151
|
+
process.exit(1);
|
|
1152
|
+
}
|
|
1153
|
+
break;
|
|
1154
|
+
}
|
|
1155
|
+
case 'receipt': {
|
|
1156
|
+
const sub = positional[0];
|
|
1157
|
+
if (sub === 'issue') {
|
|
1158
|
+
if (!positional[1]) {
|
|
1159
|
+
console.error('Usage: uwmd receipt issue <file> [--output <file.receipt.json>] [--issued-at <iso>] [--stdout]');
|
|
1160
|
+
process.exit(1);
|
|
1161
|
+
}
|
|
1162
|
+
await cmdReceiptIssue(positional[1], flags);
|
|
1163
|
+
}
|
|
1164
|
+
else if (sub === 'verify') {
|
|
1165
|
+
if (!positional[1] || !positional[2]) {
|
|
1166
|
+
console.error('Usage: uwmd receipt verify <file> <receipt.json> [--json]');
|
|
1167
|
+
process.exit(1);
|
|
1168
|
+
}
|
|
1169
|
+
await cmdReceiptVerify(positional[1], positional[2], flags);
|
|
1170
|
+
}
|
|
1171
|
+
else {
|
|
1172
|
+
console.error('Usage: uwmd receipt <issue|verify> ...');
|
|
1173
|
+
process.exit(1);
|
|
1174
|
+
}
|
|
1175
|
+
break;
|
|
1176
|
+
}
|
|
788
1177
|
default:
|
|
789
1178
|
console.log(`
|
|
790
|
-
uwmd —
|
|
1179
|
+
uwmd — UW Markdown underwriting file toolkit
|
|
791
1180
|
|
|
792
1181
|
Commands:
|
|
793
1182
|
parse <file> Parse file and output JSON
|
|
@@ -799,14 +1188,18 @@ Commands:
|
|
|
799
1188
|
compact <file> Strip superseded blocks
|
|
800
1189
|
diff <file-a> <file-b> Compare two files section by section
|
|
801
1190
|
calc <file> <calc.json> Evaluate a calc declaration or inline formula (Tier-3)
|
|
802
|
-
init Generate a blank .
|
|
1191
|
+
init Generate a blank .uwx.md file
|
|
803
1192
|
summary <file> Print quick metrics to terminal
|
|
804
1193
|
export <file> Export a digested UW JSON 1.0 document
|
|
805
1194
|
formats List registered machine representations
|
|
806
|
-
convert <file> Convert
|
|
1195
|
+
convert <file> Convert Lite/UWX/JSON/XML/CSV bundle representations
|
|
807
1196
|
report <file> Render the lender package / credit memo HTML (§7.1/§7.2)
|
|
808
1197
|
scope <file> Resolve every required input via the fallback cascade and emit a triage view
|
|
809
1198
|
refine <file> Rank gaps by value-of-information for stated calc targets
|
|
1199
|
+
receipt issue|verify Issue or verify a detached verification receipt (RFC 0016)
|
|
1200
|
+
modules validate|list Validate module manifest files, or list the registry they form
|
|
1201
|
+
lease validate|project Validate a lease abstract, or project it to a rent-roll row (RFC 0018)
|
|
1202
|
+
package create|verify|... Build, verify, list, or project a UW Deal Package (RFC 0018)
|
|
810
1203
|
layers List Bancroft agent layers
|
|
811
1204
|
|
|
812
1205
|
Options:
|
|
@@ -817,7 +1210,10 @@ Options:
|
|
|
817
1210
|
--format <f> Render format: json|csv|chat|summary (render, default: summary)
|
|
818
1211
|
--no-superseded Drop append-only history from the .uw.json export (export)
|
|
819
1212
|
--stdout Write export/convert output to stdout
|
|
820
|
-
--to <format> Target
|
|
1213
|
+
--to <format> Target: lite|uwx|uw-json|uw-xml|uw-csv-bundle (convert)
|
|
1214
|
+
--projection-report Write the UWX-to-Lite omission report as JSON (convert)
|
|
1215
|
+
--issued-at <iso> Fix the receipt issuance timestamp (receipt issue)
|
|
1216
|
+
--tier <tier> Host viewer tier to load modules against (modules)
|
|
821
1217
|
--max-tokens <n> Max tokens for chat render (default: 12000)
|
|
822
1218
|
--institution <f> Path to .uw.institution.json config (validate)
|
|
823
1219
|
--name <n> Deal name (init)
|