@knolo/cli 5.5.1 → 6.0.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.
@@ -0,0 +1,469 @@
1
+ import { mkdirSync, writeFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { KAR_CLI_MAX_IMAGE_BYTES, readBounded, readBoundedJson, readBoundedText } from './kar-limits.mjs';
4
+ import { fileURLToPath, pathToFileURL } from 'node:url';
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ const decoder = new TextDecoder();
8
+
9
+ const GRAPH_USAGE = `Usage: knolo kar graph <command> [options]
10
+
11
+ Author, compile, and review a Committed Evidence Graph. Retrieval stays kar-1-research-1.
12
+
13
+ knolo kar graph lint knowledge.ceg.yaml [--image knowledge.knolo] [--plan plan.json]
14
+ knolo kar graph review knowledge.ceg.yaml [--image knowledge.knolo]
15
+ knolo kar graph build --image knowledge.knolo --source knowledge.ceg.yaml --out knowledge.kar.json [--plan plan.json]
16
+ knolo kar graph validate --image knowledge.knolo --graph knowledge.kar.json [--build knowledge.kar.build.json]
17
+ knolo kar graph inspect knowledge.kar.json [--image knowledge.knolo] [--plan plan.json]
18
+ knolo kar graph diff old.kar.json new.kar.json
19
+ knolo kar graph check --image knowledge.knolo --graph knowledge.kar.json
20
+ knolo kar graph rebuild --image knowledge.knolo --source knowledge.ceg.yaml --previous old.kar.json --out new.kar.json
21
+ knolo kar graph import claim-graph --image knowledge.knolo --out imported.ceg.yaml [--claim claim.json] [--block-map map.json]
22
+ knolo kar graph import json --input ontology.json --mapping mapping.json --out knowledge.ceg.yaml
23
+
24
+ --json Print machine-readable output
25
+ `;
26
+
27
+ const PACKAGE_USAGE = `Usage: knolo kar package --image knowledge.knolo --graph knowledge.kar.json --out dist/my-knowledge-kar/
28
+ knolo kar package verify dist/my-knowledge-kar/
29
+
30
+ The directory is the distribution. The manifest pairs one Knowledge Image with one CEG.
31
+ `;
32
+
33
+ export async function runKarAuthoring(command, positionals, flags) {
34
+ if (command === 'package') return runPackage(positionals, flags);
35
+ return runGraph(positionals, flags);
36
+ }
37
+
38
+ async function runGraph(positionals, flags) {
39
+ const sub = positionals[0] ?? '';
40
+ if (!sub || sub === 'help') {
41
+ console.log(GRAPH_USAGE.trim());
42
+ return;
43
+ }
44
+ const authoring = await loadAuthoring();
45
+ if (sub === 'lint') return lintCommand(authoring, positionals, flags);
46
+ if (sub === 'review') return reviewCommand(authoring, positionals, flags);
47
+ if (sub === 'build') return buildCommand(authoring, flags, false);
48
+ if (sub === 'rebuild') return buildCommand(authoring, flags, true);
49
+ if (sub === 'validate') return validateCommand(authoring, flags);
50
+ if (sub === 'inspect') return inspectCommand(authoring, positionals, flags);
51
+ if (sub === 'diff') return diffCommand(authoring, positionals, flags);
52
+ if (sub === 'check') return checkCommand(authoring, flags);
53
+ if (sub === 'import') return importCommand(authoring, positionals, flags);
54
+ throw new Error(`Unknown kar graph command "${sub}".\n${GRAPH_USAGE}`);
55
+ }
56
+
57
+ async function runPackage(positionals, flags) {
58
+ const sub = positionals[0] ?? '';
59
+ if (sub === 'help') {
60
+ console.log(PACKAGE_USAGE.trim());
61
+ return;
62
+ }
63
+ const authoring = await loadAuthoring();
64
+ if (sub === 'verify') return verifyPackage(authoring, positionals[1]);
65
+ if (sub) throw new Error(`Unknown kar package command "${sub}".\n${PACKAGE_USAGE}`);
66
+ return packageCommand(authoring, flags);
67
+ }
68
+
69
+ async function lintCommand(authoring, positionals, flags) {
70
+ const parsed = readSource(authoring, positionals[1] ?? flags.source);
71
+ if (!parsed.ok) return fail(flags, parsed.diagnostics);
72
+ const catalog = await maybeCatalog(authoring, flags.image);
73
+ if (catalog && !catalog.ok) return fail(flags, catalog.diagnostics);
74
+ const diagnostics = authoring.lintCegSource(parsed.source, {
75
+ catalog: catalog ? catalog.catalog : undefined,
76
+ plan: flags.plan ? readJson(flags.plan) : undefined,
77
+ });
78
+ const errors = diagnostics.filter((item) => item.severity === 'error');
79
+ if (flags.json) {
80
+ console.log(JSON.stringify({ ok: errors.length === 0, diagnostics }, null, 2));
81
+ } else if (diagnostics.length === 0) {
82
+ console.log('CEG LINT\n\nno findings');
83
+ } else {
84
+ console.log('CEG LINT\n');
85
+ printDiagnostics(diagnostics);
86
+ }
87
+ if (errors.length > 0) process.exitCode = 1;
88
+ }
89
+
90
+ async function reviewCommand(authoring, positionals, flags) {
91
+ const parsed = readSource(authoring, positionals[1] ?? flags.source);
92
+ if (!parsed.ok) return fail(flags, parsed.diagnostics);
93
+ const catalog = await maybeCatalog(authoring, flags.image);
94
+ if (catalog && !catalog.ok) return fail(flags, catalog.diagnostics);
95
+ const review = authoring.reviewCegSource(parsed.source, catalog ? catalog.catalog : undefined);
96
+ if (flags.json) console.log(JSON.stringify(review, null, 2));
97
+ else console.log(authoring.renderCegReview(review));
98
+ if (review.diagnostics.some((item) => item.severity === 'error')) process.exitCode = 1;
99
+ }
100
+
101
+ async function buildCommand(authoring, flags, rebuild) {
102
+ const parsed = readSource(authoring, required(flags, 'source'));
103
+ if (!parsed.ok) return fail(flags, parsed.diagnostics);
104
+ const image = readBytes(required(flags, 'image'));
105
+ const out = required(flags, 'out');
106
+ const plan = flags.plan ? readJson(flags.plan) : undefined;
107
+ const compiled = authoring.compileCegSource({ source: parsed.source, image, plan });
108
+ if (!compiled.ok) return fail(flags, compiled.diagnostics);
109
+ const buildFile = buildPathFor(out);
110
+ writeFileSync(resolve(out), compiled.bytes);
111
+ writeFileSync(resolve(buildFile), authoring.serializeCegBuild(compiled.buildInfo));
112
+ let diff = null;
113
+ if (rebuild) {
114
+ if (!flags.previous) throw new Error('Missing --previous');
115
+ diff = authoring.diffCegGraphs(readJson(flags.previous), compiled.sidecar);
116
+ }
117
+ const warnings = compiled.diagnostics.filter((item) => item.severity === 'warning');
118
+ const report = {
119
+ ok: true,
120
+ stateRoot: compiled.sidecar.stateRoot,
121
+ knowledgeRoot: compiled.sidecar.knowledgeRoot,
122
+ semanticRoot: compiled.semanticRoot,
123
+ objectRoot: compiled.sidecar.objectRoot,
124
+ commitDigest: compiled.sidecar.commitDigest,
125
+ nodes: compiled.sidecar.graph.nodes.length,
126
+ relations: compiled.sidecar.graph.relations.length,
127
+ bindings: compiled.sidecar.graph.bindings.length,
128
+ warnings,
129
+ output: out,
130
+ build: buildFile,
131
+ diff,
132
+ };
133
+ if (flags.json) {
134
+ console.log(JSON.stringify(report, null, 2));
135
+ return;
136
+ }
137
+ const lines = [
138
+ rebuild ? 'CEG REBUILT' : 'CEG COMPILED',
139
+ '',
140
+ `Knowledge state root ${report.stateRoot}`,
141
+ `Knowledge root ${report.knowledgeRoot}`,
142
+ `Semantic root ${report.semanticRoot}`,
143
+ `Nodes ${report.nodes}`,
144
+ `Relations ${report.relations}`,
145
+ `Bindings ${report.bindings}`,
146
+ `Warnings ${warnings.length}`,
147
+ `Output file ${out}`,
148
+ `Build record ${buildFile}`,
149
+ ];
150
+ if (warnings.length > 0) {
151
+ lines.push('');
152
+ for (const item of warnings) lines.push(`${item.code} ${item.message}`);
153
+ }
154
+ if (diff) {
155
+ lines.push('');
156
+ lines.push(...diffLines(diff));
157
+ }
158
+ console.log(lines.join('\n'));
159
+ }
160
+
161
+ async function validateCommand(authoring, flags) {
162
+ const image = readBytes(required(flags, 'image'));
163
+ const graph = readJson(required(flags, 'graph'));
164
+ const checked = flags.build
165
+ ? authoring.validateCegBuild({ image, graph, build: readJson(flags.build) })
166
+ : authoring.checkCegFreshness({ image, graph });
167
+ if (!checked.ok) return fail(flags, checked.diagnostics);
168
+ const quality = authoring.inspectCegQuality(graph);
169
+ if (!quality.ok) return fail(flags, quality.diagnostics);
170
+ const report = {
171
+ ok: true,
172
+ semanticRoot: checked.semanticRoot,
173
+ nodes: quality.quality.nodeCount,
174
+ relations: quality.quality.relationCount,
175
+ bindings: quality.quality.bindingCount,
176
+ };
177
+ if (flags.json) console.log(JSON.stringify(report, null, 2));
178
+ else {
179
+ console.log([
180
+ 'CEG VALID',
181
+ '',
182
+ `Semantic root ${report.semanticRoot}`,
183
+ `Nodes ${report.nodes}`,
184
+ `Relations ${report.relations}`,
185
+ `Bindings ${report.bindings}`,
186
+ ].join('\n'));
187
+ }
188
+ }
189
+
190
+ async function inspectCommand(authoring, positionals, flags) {
191
+ const file = positionals[1] ?? flags.graph;
192
+ if (!file) throw new Error('Missing graph path');
193
+ const graph = readJson(file);
194
+ const quality = authoring.inspectCegQuality(graph);
195
+ if (!quality.ok) return fail(flags, quality.diagnostics);
196
+ const committed = graph.graph ?? graph;
197
+ const bindings = Array.isArray(committed.bindings) ? committed.bindings : [];
198
+ const relations = Array.isArray(committed.relations) ? committed.relations : [];
199
+ const authorities = [];
200
+ for (const binding of bindings) {
201
+ if (Number.isInteger(binding.authority)) authorities.push(binding.authority);
202
+ }
203
+ const froms = bindings.map((binding) => binding.validFrom).filter((value) => typeof value === 'string').sort();
204
+ const untils = bindings.map((binding) => binding.validUntil).filter((value) => typeof value === 'string').sort();
205
+ const requirements = [...new Set(bindings.flatMap((binding) => binding.requirements ?? []))].sort();
206
+ const symbols = [...new Set(relations.map((relation) => relation.relation))].sort();
207
+ let freshness = null;
208
+ if (flags.image) {
209
+ freshness = authoring.checkCegFreshness({ image: readBytes(flags.image), graph });
210
+ }
211
+ let readiness = null;
212
+ if (flags.plan) {
213
+ readiness = authoring.inspectKarReadiness(graph, [readJson(flags.plan)]);
214
+ }
215
+ const report = {
216
+ producer: committed.provenance?.producer ?? null,
217
+ knowledgeRoot: quality.quality ? committed.knowledgeRoot : null,
218
+ semanticRoot: quality.quality.semanticRoot,
219
+ nodes: quality.quality.nodeCount,
220
+ relations: quality.quality.relationCount,
221
+ bindings: quality.quality.bindingCount,
222
+ relationSymbols: symbols,
223
+ requirements,
224
+ authority: authorityRange(authorities),
225
+ validity: { from: froms[0] ?? null, until: untils[untils.length - 1] ?? null },
226
+ components: quality.quality.componentCount,
227
+ largestComponent: quality.quality.largestComponent,
228
+ unboundNodes: quality.quality.unboundNodes,
229
+ image: freshness,
230
+ readiness: readiness && readiness.ok ? readiness.readiness : readiness,
231
+ };
232
+ if (flags.json) console.log(JSON.stringify(report, null, 2));
233
+ else {
234
+ const lines = [
235
+ 'CEG INSPECT',
236
+ '',
237
+ `producer ${report.producer ?? '(none)'}`,
238
+ `knowledgeRoot ${committed.knowledgeRoot ?? '(none)'}`,
239
+ `semanticRoot ${report.semanticRoot}`,
240
+ `node count ${report.nodes}`,
241
+ `relation count ${report.relations}`,
242
+ `binding count ${report.bindings}`,
243
+ `relation symbols ${symbols.join(', ') || '(none)'}`,
244
+ `requirements ${requirements.join(', ') || '(none)'}`,
245
+ `authority range ${report.authority ? `${report.authority.min}..${report.authority.max}` : '(none)'}`,
246
+ `validity range ${report.validity.from || report.validity.until ? `${report.validity.from ?? ''}..${report.validity.until ?? ''}` : '(none)'}`,
247
+ `connected components ${report.components}`,
248
+ `largest component ${report.largestComponent}`,
249
+ `unbound nodes ${report.unboundNodes}`,
250
+ ];
251
+ if (freshness) {
252
+ lines.push(`image binding ${freshness.ok ? 'bound' : freshness.diagnostics.map((item) => item.code).join(', ')}`);
253
+ }
254
+ console.log(lines.join('\n'));
255
+ }
256
+ if (freshness && !freshness.ok) process.exitCode = 1;
257
+ }
258
+
259
+ function diffCommand(authoring, positionals, flags) {
260
+ const before = positionals[1] ?? flags.old;
261
+ const after = positionals[2] ?? flags.new;
262
+ if (!before || !after) throw new Error('Usage: knolo kar graph diff <old.kar.json> <new.kar.json>');
263
+ const diff = authoring.diffCegGraphs(readJson(before), readJson(after));
264
+ if (!diff.ok) return fail(flags, diff.diagnostics);
265
+ if (flags.json) console.log(JSON.stringify(diff, null, 2));
266
+ else console.log(['CEG DIFF', '', ...diffLines(diff)].join('\n'));
267
+ }
268
+
269
+ function checkCommand(authoring, flags) {
270
+ const checked = authoring.checkCegFreshness({
271
+ image: readBytes(required(flags, 'image')),
272
+ graph: readJson(required(flags, 'graph')),
273
+ });
274
+ if (!checked.ok) return fail(flags, checked.diagnostics);
275
+ const report = { ok: true, semanticRoot: checked.semanticRoot };
276
+ if (flags.json) console.log(JSON.stringify(report, null, 2));
277
+ else console.log(`CEG BOUND\n\nSemantic root ${checked.semanticRoot}`);
278
+ }
279
+
280
+ function importCommand(authoring, positionals, flags) {
281
+ const kind = positionals[1];
282
+ const out = required(flags, 'out');
283
+ if (kind === 'claim-graph') return importClaim(authoring, flags, out);
284
+ if (kind === 'json') return importJson(authoring, flags, out);
285
+ throw new Error('Usage: knolo kar graph import <claim-graph|json> ...');
286
+ }
287
+
288
+ function importClaim(authoring, flags, out) {
289
+ const image = readBytes(required(flags, 'image'));
290
+ const claim = flags.claim
291
+ ? readJson(flags.claim)
292
+ : authoring.readImageClaimGraph(image);
293
+ const graph = flags.claim ? { ok: true, claim } : claim;
294
+ if (!graph.ok) return fail(flags, graph.diagnostics);
295
+ const blockEvidence = flags['block-map'] ? readJson(flags['block-map']) : undefined;
296
+ const imported = authoring.importClaimGraph(graph.claim, blockEvidence ? { blockEvidence } : {});
297
+ if (!imported.ok) return fail(flags, imported.diagnostics);
298
+ writeSource(authoring, out, imported.source);
299
+ finishImport(flags, out, imported.diagnostics);
300
+ }
301
+
302
+ function importJson(authoring, flags, out) {
303
+ const imported = authoring.importJsonGraph(readJson(required(flags, 'input')), readJson(required(flags, 'mapping')));
304
+ if (!imported.ok) return fail(flags, imported.diagnostics);
305
+ writeSource(authoring, out, imported.source);
306
+ finishImport(flags, out, imported.diagnostics);
307
+ }
308
+
309
+ function finishImport(flags, out, diagnostics) {
310
+ if (flags.json) console.log(JSON.stringify({ ok: true, output: out, diagnostics }, null, 2));
311
+ else {
312
+ console.log(`CEG IMPORTED\n\nOutput file ${out}`);
313
+ if (diagnostics.length > 0) {
314
+ console.log('');
315
+ printDiagnostics(diagnostics);
316
+ }
317
+ }
318
+ }
319
+
320
+ function packageCommand(authoring, flags) {
321
+ const image = readBytes(required(flags, 'image'));
322
+ const graphFile = required(flags, 'graph');
323
+ const graphBytes = readBytes(graphFile);
324
+ const graph = JSON.parse(decoder.decode(graphBytes));
325
+ const packed = authoring.buildDistributionFiles({ image, graphBytes, graph });
326
+ if (!packed.ok) return fail(flags, packed.diagnostics);
327
+ const out = resolve(required(flags, 'out'));
328
+ mkdirSync(out, { recursive: true });
329
+ for (const [name, bytes] of Object.entries(packed.files)) {
330
+ writeFileSync(path.join(out, name), bytes);
331
+ }
332
+ const report = { ok: true, output: out, manifest: packed.manifest };
333
+ if (flags.json) console.log(JSON.stringify(report, null, 2));
334
+ else {
335
+ console.log([
336
+ 'KAR PACKAGED',
337
+ '',
338
+ `Output directory ${out}`,
339
+ `Semantic root ${packed.manifest.kar.semanticRoot}`,
340
+ `Knowledge root ${packed.manifest.image.knowledgeRoot}`,
341
+ `Image sha256 ${packed.manifest.image.sha256}`,
342
+ `CEG sha256 ${packed.manifest.kar.sha256}`,
343
+ ].join('\n'));
344
+ }
345
+ }
346
+
347
+ function verifyPackage(authoring, directory) {
348
+ if (!directory) throw new Error('Usage: knolo kar package verify <directory>');
349
+ const dir = resolve(directory);
350
+ const image = readBytes(path.join(dir, 'knowledge.knolo'));
351
+ const graphBytes = readBytes(path.join(dir, 'knowledge.kar.json'));
352
+ const manifest = readJson(path.join(dir, 'kar-manifest.json'));
353
+ const graph = JSON.parse(decoder.decode(graphBytes));
354
+ const loaded = authoring.loadKarBundle({ image, graph, manifest, graphBytes });
355
+ if (!loaded.ok) return fail({ json: false }, loaded.diagnostics);
356
+ console.log(`KAR BUNDLE VERIFIED\n\nSemantic root ${loaded.semanticRoot}`);
357
+ }
358
+
359
+ function authorityRange(values) {
360
+ if (values.length === 0) return null;
361
+ let min = values[0];
362
+ let max = values[0];
363
+ for (const value of values) {
364
+ if (value < min) min = value;
365
+ if (value > max) max = value;
366
+ }
367
+ return { min, max };
368
+ }
369
+
370
+ function diffLines(diff) {
371
+ if (!diff.ok) return diff.diagnostics.map((item) => `${item.code} ${item.message}`);
372
+ return [
373
+ `old SemanticRoot ${diff.semanticRoot.before}`,
374
+ `new SemanticRoot ${diff.semanticRoot.after}`,
375
+ `nodes added ${diff.nodes.added.length}`,
376
+ `nodes removed ${diff.nodes.removed.length}`,
377
+ `relations added ${diff.relations.added.length}`,
378
+ `relations removed ${diff.relations.removed.length}`,
379
+ `bindings added ${diff.bindings.added.length}`,
380
+ `bindings removed ${diff.bindings.removed.length}`,
381
+ `authority changes ${diff.authority.length}`,
382
+ `validity changes ${diff.validity.length}`,
383
+ `requirement changes ${diff.requirements.length}`,
384
+ ];
385
+ }
386
+
387
+ function writeSource(authoring, file, source) {
388
+ const text = file.endsWith('.json') ? authoring.emitCegSourceJson(source) : authoring.emitCegSourceYaml(source);
389
+ writeFileSync(resolve(file), text);
390
+ }
391
+
392
+ function readSource(authoring, file) {
393
+ if (!file) throw new Error('Missing CEG source path');
394
+ return authoring.parseCegSource(readBoundedText(file));
395
+ }
396
+
397
+ async function maybeCatalog(authoring, image) {
398
+ if (!image) return null;
399
+ const opened = authoring.openEvidenceCatalog
400
+ ? authoring.openEvidenceCatalog(readBytes(image))
401
+ : null;
402
+ if (opened) return opened;
403
+ const catalog = await loadCatalog();
404
+ return catalog.openEvidenceCatalog(readBytes(image));
405
+ }
406
+
407
+ function fail(flags, diagnostics) {
408
+ if (flags.json) console.log(JSON.stringify({ ok: false, diagnostics }, null, 2));
409
+ else printDiagnostics(diagnostics);
410
+ process.exitCode = 1;
411
+ }
412
+
413
+ function printDiagnostics(diagnostics) {
414
+ for (const item of diagnostics) {
415
+ console.log(`${item.severity} ${item.code} ${item.path} ${item.message}`);
416
+ }
417
+ }
418
+
419
+ function buildPathFor(out) {
420
+ if (out.endsWith('.json')) return `${out.slice(0, -'.json'.length)}.build.json`;
421
+ return `${out}.build.json`;
422
+ }
423
+
424
+ function required(flags, name) {
425
+ if (!flags[name]) throw new Error(`Missing --${name}`);
426
+ return flags[name];
427
+ }
428
+
429
+ function resolve(file) {
430
+ return path.resolve(process.cwd(), file);
431
+ }
432
+
433
+ function readBytes(file) {
434
+ return readBounded(file, KAR_CLI_MAX_IMAGE_BYTES);
435
+ }
436
+
437
+ function readJson(file) {
438
+ return readBoundedJson(file);
439
+ }
440
+
441
+ async function loadAuthoring() {
442
+ const loaded = await loadFirst([
443
+ path.resolve(__dirname, '../../core/dist/experimental/kar/authoring/index.js'),
444
+ '@knolo/core/experimental/kar/authoring',
445
+ ], (mod) => mod.compileCegSource && mod.parseCegSource);
446
+ if (!loaded) throw new Error('Could not load @knolo/core/experimental/kar/authoring. Build packages/core first.');
447
+ return loaded;
448
+ }
449
+
450
+ async function loadCatalog() {
451
+ const loaded = await loadFirst([
452
+ path.resolve(__dirname, '../../core/dist/experimental/kar/authoring/catalog.js'),
453
+ ], (mod) => mod.openEvidenceCatalog);
454
+ if (!loaded) throw new Error('Could not load the CEG evidence catalog. Build packages/core first.');
455
+ return loaded;
456
+ }
457
+
458
+ async function loadFirst(candidates, accept) {
459
+ for (const candidate of candidates) {
460
+ try {
461
+ const href = candidate.startsWith('@') ? candidate : pathToFileURL(candidate).href;
462
+ const loaded = await import(href);
463
+ if (accept(loaded)) return loaded;
464
+ } catch {
465
+ // Try the next candidate.
466
+ }
467
+ }
468
+ return null;
469
+ }
@@ -0,0 +1,36 @@
1
+ import { readFileSync, statSync } from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ /** Matches the CEG Source byte limit. JSON and YAML reads use this cap. */
5
+ export const KAR_CLI_MAX_TEXT_BYTES = 32 * 1024 * 1024;
6
+
7
+ /** Knowledge Image reads. Larger than source text, still bounded. */
8
+ export const KAR_CLI_MAX_IMAGE_BYTES = 512 * 1024 * 1024;
9
+
10
+ export function resolveUserPath(file) {
11
+ return path.resolve(process.cwd(), file);
12
+ }
13
+
14
+ /**
15
+ * Read a user-supplied CLI path after checking it is a regular file under the cap.
16
+ * The caller chooses the path. This does not sandbox `..`. It refuses oversized inputs.
17
+ */
18
+ export function readBounded(file, maxBytes) {
19
+ const resolved = resolveUserPath(file);
20
+ const stat = statSync(resolved);
21
+ if (!stat.isFile()) throw new Error(`KAR_INPUT_NOT_A_FILE: ${file}`);
22
+ if (stat.size > maxBytes) {
23
+ throw new Error(
24
+ `KAR_INPUT_TOO_LARGE: ${file} is ${stat.size} bytes; limit is ${maxBytes}.`
25
+ );
26
+ }
27
+ return readFileSync(resolved);
28
+ }
29
+
30
+ export function readBoundedText(file, maxBytes = KAR_CLI_MAX_TEXT_BYTES) {
31
+ return new TextDecoder().decode(readBounded(file, maxBytes));
32
+ }
33
+
34
+ export function readBoundedJson(file, maxBytes = KAR_CLI_MAX_TEXT_BYTES) {
35
+ return JSON.parse(readBoundedText(file, maxBytes));
36
+ }