@grayhaven/nerve-cli 0.1.0 → 0.2.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/dist/index.js +281 -4
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -18,6 +18,30 @@ import {
|
|
|
18
18
|
assemblyInstructions,
|
|
19
19
|
boardSvg,
|
|
20
20
|
bomCsv,
|
|
21
|
+
bopCsv,
|
|
22
|
+
bopJson,
|
|
23
|
+
analysisCsv,
|
|
24
|
+
analysisJson,
|
|
25
|
+
analyzeHarness,
|
|
26
|
+
builtinAdapters,
|
|
27
|
+
buildRecordJson,
|
|
28
|
+
contractJson,
|
|
29
|
+
createBuildRecord,
|
|
30
|
+
createRedline,
|
|
31
|
+
createRelease,
|
|
32
|
+
formboardSheets,
|
|
33
|
+
releaseJson,
|
|
34
|
+
ReleaseBlockedError,
|
|
35
|
+
resolveRedline,
|
|
36
|
+
suggestPatch,
|
|
37
|
+
validateRedlineTarget,
|
|
38
|
+
exportConnectorContract,
|
|
39
|
+
findAdapter,
|
|
40
|
+
generateQuote,
|
|
41
|
+
importPinoutCsv,
|
|
42
|
+
quoteCsv,
|
|
43
|
+
quoteJson,
|
|
44
|
+
validateContract,
|
|
21
45
|
buildPacket,
|
|
22
46
|
canRelease,
|
|
23
47
|
cutListCsv,
|
|
@@ -92,9 +116,17 @@ Usage:
|
|
|
92
116
|
nerve init [dir]
|
|
93
117
|
nerve compile <file.harness.ts> [--out dir]
|
|
94
118
|
nerve validate <file.harness.ts>
|
|
95
|
-
nerve render <file.harness.ts> [--format svg] [--view schematic|board] [--out dir]
|
|
119
|
+
nerve render <file.harness.ts> [--format svg] [--view schematic|board|formboard] [--paper letter|a4] [--out dir]
|
|
96
120
|
nerve export <file.harness.ts> [--target manufacturing-packet|wireviz] [--out dir]
|
|
97
121
|
nerve import <file.yml> [--id harness-id] [--out dir] (WireViz YAML \u2192 HIR)
|
|
122
|
+
nerve quote <file.harness.ts> [--out dir] (requires costing in nerve.config.ts)
|
|
123
|
+
nerve analyze <file.harness.ts> [--out dir] (resistance, drop, bundle, weight \xA734)
|
|
124
|
+
nerve machine <adapter-id> <file.harness.ts> [--out dir] (shop-floor exports \xA731)
|
|
125
|
+
nerve contract <file.harness.ts> --connector <ref> [--against contract.json|pinout.csv] [--out dir]
|
|
126
|
+
nerve release <file.harness.ts> --eco <id> --reason <text> --date <iso> [--against release.json] [--out dir]
|
|
127
|
+
nerve record <file.harness.ts> --release <release.json> --serial <sn> --operator <name> --date <iso> --results <measurements.json> [--out dir]
|
|
128
|
+
nerve redline add <file.harness.ts> --target <hir-ref> --type <type> --description <text> [--value v] [--release id] [--serial sn]
|
|
129
|
+
nerve redline resolve <redlines.json> --id <id> --accept|--reject --reason <text> --date <iso>
|
|
98
130
|
nerve diff <revA> <revB> [--json] (each: harness.json, .harness.ts, or revision dir)
|
|
99
131
|
nerve inspect <harness.json>`;
|
|
100
132
|
var loadHirForDiff = async (path, io) => {
|
|
@@ -150,13 +182,22 @@ var run = async (argv, io = realIo) => {
|
|
|
150
182
|
return 2;
|
|
151
183
|
}
|
|
152
184
|
const view = flags["view"] ?? "schematic";
|
|
153
|
-
if (view !== "schematic" && view !== "board") {
|
|
154
|
-
io.err(`Unsupported render view: ${view} (supported: schematic, board)`);
|
|
185
|
+
if (view !== "schematic" && view !== "board" && view !== "formboard") {
|
|
186
|
+
io.err(`Unsupported render view: ${view} (supported: schematic, board, formboard)`);
|
|
155
187
|
return 2;
|
|
156
188
|
}
|
|
157
189
|
const result = await compileOrExit(file, io);
|
|
158
190
|
if (typeof result === "number") return result;
|
|
159
191
|
const outDir = resolve(flags["out"] ?? result.config.outputDir ?? "dist");
|
|
192
|
+
if (view === "formboard") {
|
|
193
|
+
const paper = flags["paper"] === "a4" ? "a4" : "letter";
|
|
194
|
+
const board = formboardSheets(result.hir, { paper });
|
|
195
|
+
for (const sheet of board.sheets) writeOut(outDir, sheet.name, sheet.svg, io);
|
|
196
|
+
io.out(
|
|
197
|
+
`formboard ${board.boardWidthMm}x${board.boardHeightMm} mm \u2192 ${board.rows}x${board.cols} ${paper} sheet(s) at 1:1. Print at 100% and verify the calibration ruler.`
|
|
198
|
+
);
|
|
199
|
+
return 0;
|
|
200
|
+
}
|
|
160
201
|
if (view === "board") {
|
|
161
202
|
writeOut(outDir, "board.svg", boardSvg(result.hir), io);
|
|
162
203
|
} else {
|
|
@@ -192,7 +233,10 @@ var run = async (argv, io = realIo) => {
|
|
|
192
233
|
}
|
|
193
234
|
const outDir = resolve(flags["out"] ?? result.config.outputDir ?? "dist");
|
|
194
235
|
const tolerance = result.config.defaultWireTolerance;
|
|
195
|
-
const options =
|
|
236
|
+
const options = {
|
|
237
|
+
...tolerance !== void 0 ? { defaultWireTolerance: tolerance } : {},
|
|
238
|
+
...result.config.costing !== void 0 ? { costing: result.config.costing } : {}
|
|
239
|
+
};
|
|
196
240
|
const plan = generateTestPlan(result.hir);
|
|
197
241
|
writeOut(outDir, "harness.json", JSON.stringify(result.hir, null, 2) + "\n", io);
|
|
198
242
|
writeOut(outDir, "schematic.svg", schematicSvg(result.hir), io);
|
|
@@ -200,9 +244,15 @@ var run = async (argv, io = realIo) => {
|
|
|
200
244
|
writeOut(outDir, "bom.csv", bomCsv(result.hir), io);
|
|
201
245
|
writeOut(outDir, "cut-list.csv", cutListCsv(result.hir, options), io);
|
|
202
246
|
writeOut(outDir, "labels.csv", labelScheduleCsv(result.hir), io);
|
|
247
|
+
writeOut(outDir, "bop.csv", bopCsv(result.hir), io);
|
|
248
|
+
writeOut(outDir, "bop.json", bopJson(result.hir), io);
|
|
203
249
|
writeOut(outDir, "tests.csv", testPlanCsv(plan), io);
|
|
204
250
|
writeOut(outDir, "test-plan.json", testPlanJson(result.hir), io);
|
|
205
251
|
writeOut(outDir, "assembly-instructions.txt", assemblyInstructions(result.hir), io);
|
|
252
|
+
if (result.config.costing !== void 0) {
|
|
253
|
+
writeOut(outDir, "quote.csv", quoteCsv(result.hir, result.config.costing), io);
|
|
254
|
+
writeOut(outDir, "quote.json", quoteJson(result.hir, result.config.costing), io);
|
|
255
|
+
}
|
|
206
256
|
writeOut(outDir, "manufacturing-packet.pdf", await manufacturingPacketPdf(result.hir, options), io);
|
|
207
257
|
writeOut(outDir, "manufacturing-packet.zip", (await buildPacket(result.hir, options)).zip, io);
|
|
208
258
|
io.out(summarize(result.hir));
|
|
@@ -232,6 +282,233 @@ var run = async (argv, io = realIo) => {
|
|
|
232
282
|
io.out(summarize(full));
|
|
233
283
|
return hasErrors(diagnostics) ? 1 : 0;
|
|
234
284
|
}
|
|
285
|
+
case "quote": {
|
|
286
|
+
const file = positional[0];
|
|
287
|
+
if (file === void 0) return usage(io);
|
|
288
|
+
const result = await compileOrExit(file, io);
|
|
289
|
+
if (typeof result === "number") return result;
|
|
290
|
+
const model = result.config.costing;
|
|
291
|
+
if (model === void 0) {
|
|
292
|
+
io.err("No costing model: add `costing: { laborRatePerHour, ... }` to nerve.config.ts (PRD \xA729).");
|
|
293
|
+
return 2;
|
|
294
|
+
}
|
|
295
|
+
const quote = generateQuote(result.hir, model);
|
|
296
|
+
const outDir = resolve(flags["out"] ?? result.config.outputDir ?? "dist");
|
|
297
|
+
writeOut(outDir, "quote.csv", quoteCsv(result.hir, model), io);
|
|
298
|
+
writeOut(outDir, "quote.json", quoteJson(result.hir, model), io);
|
|
299
|
+
io.out(
|
|
300
|
+
`${quote.harness.id} rev ${quote.harness.revision} \u2014 material ${quote.materialCost.toFixed(2)} + scrap ${quote.scrapCost.toFixed(2)} + labor ${quote.laborCost.toFixed(2)} = ${quote.totalCost.toFixed(2)} ${quote.currency} (${quote.perUnitCost.toFixed(2)}/unit @ ${(quote.assumptions.yield * 100).toFixed(0)}% yield)`
|
|
301
|
+
);
|
|
302
|
+
for (const mpn of quote.longLeadItems) io.out(`LONG-LEAD: ${mpn}`);
|
|
303
|
+
for (const mpn of quote.lifecycleRisks) io.out(`LIFECYCLE: ${mpn}`);
|
|
304
|
+
for (const item of quote.unpricedItems) io.out(`UNPRICED: ${item}`);
|
|
305
|
+
return 0;
|
|
306
|
+
}
|
|
307
|
+
case "analyze": {
|
|
308
|
+
const file = positional[0];
|
|
309
|
+
if (file === void 0) return usage(io);
|
|
310
|
+
const result = await compileOrExit(file, io);
|
|
311
|
+
if (typeof result === "number") return result;
|
|
312
|
+
const report = analyzeHarness(result.hir);
|
|
313
|
+
const outDir = resolve(flags["out"] ?? result.config.outputDir ?? "dist");
|
|
314
|
+
writeOut(outDir, "analysis.csv", analysisCsv(result.hir), io);
|
|
315
|
+
writeOut(outDir, "analysis.json", analysisJson(result.hir), io);
|
|
316
|
+
io.out(
|
|
317
|
+
`${report.harness.id} rev ${report.harness.revision} \u2014 ${report.totals.wireLengthM} m wire, ~${report.totals.estimatedWeightG} g, ${report.branches.map((b) => `${b.id}: \xD8${b.bundleDiameterMm}mm`).join(", ")}`
|
|
318
|
+
);
|
|
319
|
+
return 0;
|
|
320
|
+
}
|
|
321
|
+
case "machine": {
|
|
322
|
+
const [adapterId, file] = positional;
|
|
323
|
+
if (adapterId === void 0 || file === void 0) return usage(io);
|
|
324
|
+
const adapter = findAdapter(adapterId);
|
|
325
|
+
if (adapter === void 0) {
|
|
326
|
+
io.err(`Unknown adapter: ${adapterId}. Available: ${builtinAdapters.map((a) => a.id).join(", ")}`);
|
|
327
|
+
return 2;
|
|
328
|
+
}
|
|
329
|
+
const result = await compileOrExit(file, io);
|
|
330
|
+
if (typeof result === "number") return result;
|
|
331
|
+
const { files, diagnostics } = adapter.generate(result.hir);
|
|
332
|
+
printDiagnostics(diagnostics, io);
|
|
333
|
+
if (hasErrors(diagnostics)) return 1;
|
|
334
|
+
const outDir = resolve(flags["out"] ?? result.config.outputDir ?? "dist");
|
|
335
|
+
for (const [name, contents] of files) writeOut(outDir, name, contents, io);
|
|
336
|
+
return 0;
|
|
337
|
+
}
|
|
338
|
+
case "contract": {
|
|
339
|
+
const file = positional[0];
|
|
340
|
+
const connectorRef = flags["connector"];
|
|
341
|
+
if (file === void 0 || connectorRef === void 0) return usage(io);
|
|
342
|
+
const result = await compileOrExit(file, io);
|
|
343
|
+
if (typeof result === "number") return result;
|
|
344
|
+
const against = flags["against"];
|
|
345
|
+
if (against !== void 0) {
|
|
346
|
+
let contract2;
|
|
347
|
+
try {
|
|
348
|
+
const raw = readFileSync(resolve(against), "utf8");
|
|
349
|
+
contract2 = against.endsWith(".csv") ? importPinoutCsv(raw, { connector: connectorRef }) : JSON.parse(raw);
|
|
350
|
+
} catch (cause) {
|
|
351
|
+
io.err(`Failed to load contract ${against}: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
352
|
+
return 2;
|
|
353
|
+
}
|
|
354
|
+
const diagnostics = validateContract(result.hir, contract2);
|
|
355
|
+
printDiagnostics(diagnostics, io);
|
|
356
|
+
io.out(
|
|
357
|
+
diagnostics.length === 0 ? `Connector ${connectorRef} conforms to ${against}.` : `${diagnostics.length} contract issue(s) for ${connectorRef}.`
|
|
358
|
+
);
|
|
359
|
+
return hasErrors(diagnostics) ? 1 : 0;
|
|
360
|
+
}
|
|
361
|
+
const contract = exportConnectorContract(result.hir, connectorRef);
|
|
362
|
+
if (contract === void 0) {
|
|
363
|
+
io.err(`Connector ${connectorRef} not found in ${result.hir.harness.id}.`);
|
|
364
|
+
return 2;
|
|
365
|
+
}
|
|
366
|
+
const outDir = resolve(flags["out"] ?? result.config.outputDir ?? "dist");
|
|
367
|
+
writeOut(outDir, `contract-${connectorRef}.json`, contractJson(contract), io);
|
|
368
|
+
return 0;
|
|
369
|
+
}
|
|
370
|
+
case "release": {
|
|
371
|
+
const file = positional[0];
|
|
372
|
+
const eco = flags["eco"];
|
|
373
|
+
const reason = flags["reason"];
|
|
374
|
+
const date = flags["date"];
|
|
375
|
+
if (file === void 0 || eco === void 0 || reason === void 0 || date === void 0) return usage(io);
|
|
376
|
+
const result = await compileOrExit(file, io);
|
|
377
|
+
if (typeof result === "number") return result;
|
|
378
|
+
let previous;
|
|
379
|
+
if (flags["against"] !== void 0) {
|
|
380
|
+
try {
|
|
381
|
+
const prevRelease = JSON.parse(readFileSync(resolve(flags["against"]), "utf8"));
|
|
382
|
+
const prevDir = resolve(flags["against"], "..");
|
|
383
|
+
const prevHir = decodeHir(JSON.parse(readFileSync(join(prevDir, "harness.json"), "utf8")));
|
|
384
|
+
previous = { hir: prevHir, releaseId: prevRelease.releaseId };
|
|
385
|
+
} catch (cause) {
|
|
386
|
+
io.err(`Failed to load previous release: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
387
|
+
return 2;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
try {
|
|
391
|
+
const release = createRelease(result.hir, {
|
|
392
|
+
eco: { id: eco, reason, ...flags["author"] !== void 0 ? { author: flags["author"] } : {} },
|
|
393
|
+
createdAt: date,
|
|
394
|
+
...previous !== void 0 ? { previous } : {}
|
|
395
|
+
});
|
|
396
|
+
const outDir = resolve(flags["out"] ?? result.config.outputDir ?? "dist");
|
|
397
|
+
writeOut(outDir, "harness.json", JSON.stringify(result.hir, null, 2) + "\n", io);
|
|
398
|
+
writeOut(outDir, `release-${result.hir.harness.revision}.json`, releaseJson(release), io);
|
|
399
|
+
io.out(
|
|
400
|
+
`Release ${release.releaseId} (${eco}) \u2014 fingerprint ${release.hirFingerprint}` + (release.impact !== void 0 ? ` \u2014 impact: ${release.impact.riskScore} (${release.impact.risk}), ${release.impact.pinoutChanges} pinout / ${release.impact.wireChanges} wire change(s)` : "")
|
|
401
|
+
);
|
|
402
|
+
return 0;
|
|
403
|
+
} catch (cause) {
|
|
404
|
+
if (cause instanceof ReleaseBlockedError) {
|
|
405
|
+
io.err(cause.message);
|
|
406
|
+
return 1;
|
|
407
|
+
}
|
|
408
|
+
throw cause;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
case "record": {
|
|
412
|
+
const file = positional[0];
|
|
413
|
+
const releasePath = flags["release"];
|
|
414
|
+
const serial = flags["serial"];
|
|
415
|
+
const operator = flags["operator"];
|
|
416
|
+
const date = flags["date"];
|
|
417
|
+
const resultsPath = flags["results"];
|
|
418
|
+
if (file === void 0 || releasePath === void 0 || serial === void 0 || operator === void 0 || date === void 0 || resultsPath === void 0) {
|
|
419
|
+
return usage(io);
|
|
420
|
+
}
|
|
421
|
+
const result = await compileOrExit(file, io);
|
|
422
|
+
if (typeof result === "number") return result;
|
|
423
|
+
let release, measurements;
|
|
424
|
+
try {
|
|
425
|
+
release = JSON.parse(readFileSync(resolve(releasePath), "utf8"));
|
|
426
|
+
measurements = JSON.parse(readFileSync(resolve(resultsPath), "utf8"));
|
|
427
|
+
} catch (cause) {
|
|
428
|
+
io.err(`Failed to load inputs: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
429
|
+
return 2;
|
|
430
|
+
}
|
|
431
|
+
const record = createBuildRecord(result.hir, release, measurements, {
|
|
432
|
+
serial,
|
|
433
|
+
operator,
|
|
434
|
+
buildDate: date,
|
|
435
|
+
...flags["lot"] !== void 0 ? { lot: flags["lot"] } : {},
|
|
436
|
+
...flags["workstation"] !== void 0 ? { workstation: flags["workstation"] } : {}
|
|
437
|
+
});
|
|
438
|
+
const outDir = resolve(flags["out"] ?? result.config.outputDir ?? "dist");
|
|
439
|
+
writeOut(outDir, `build-record-${serial}.json`, buildRecordJson(record), io);
|
|
440
|
+
io.out(
|
|
441
|
+
`${serial}: ${record.summary.pass} pass / ${record.summary.fail} fail / ${record.summary.notRun} not run \u2192 ${record.summary.status.toUpperCase()}`
|
|
442
|
+
);
|
|
443
|
+
return record.summary.status === "fail" ? 1 : 0;
|
|
444
|
+
}
|
|
445
|
+
case "redline": {
|
|
446
|
+
const sub = positional[0];
|
|
447
|
+
if (sub === "add") {
|
|
448
|
+
const file = positional[1];
|
|
449
|
+
const target = flags["target"];
|
|
450
|
+
const type = flags["type"];
|
|
451
|
+
const description = flags["description"];
|
|
452
|
+
if (file === void 0 || target === void 0 || type === void 0 || description === void 0) return usage(io);
|
|
453
|
+
const result = await compileOrExit(file, io);
|
|
454
|
+
if (typeof result === "number") return result;
|
|
455
|
+
const invalid = validateRedlineTarget(result.hir, target);
|
|
456
|
+
if (invalid !== void 0) {
|
|
457
|
+
printDiagnostics([invalid], io);
|
|
458
|
+
return 1;
|
|
459
|
+
}
|
|
460
|
+
const redlinesPath = resolve(flags["file"] ?? "redlines.json");
|
|
461
|
+
const existing = existsSync(redlinesPath) ? JSON.parse(readFileSync(redlinesPath, "utf8")) : [];
|
|
462
|
+
const redline = createRedline({
|
|
463
|
+
id: `RL-${String(existing.length + 1).padStart(3, "0")}`,
|
|
464
|
+
target,
|
|
465
|
+
type,
|
|
466
|
+
description,
|
|
467
|
+
...flags["value"] !== void 0 ? { proposedValue: flags["value"] } : {},
|
|
468
|
+
release: flags["release"] ?? `${result.hir.harness.id}@${result.hir.harness.revision}`,
|
|
469
|
+
...flags["serial"] !== void 0 ? { serial: flags["serial"] } : {},
|
|
470
|
+
...flags["by"] !== void 0 ? { reportedBy: flags["by"] } : {}
|
|
471
|
+
});
|
|
472
|
+
writeFileSync(redlinesPath, JSON.stringify([...existing, redline], null, 2) + "\n");
|
|
473
|
+
io.out(`Recorded ${redline.id} against ${target} in ${redlinesPath}`);
|
|
474
|
+
return 0;
|
|
475
|
+
}
|
|
476
|
+
if (sub === "resolve") {
|
|
477
|
+
const redlinesPath = positional[1];
|
|
478
|
+
const id = flags["id"];
|
|
479
|
+
const reason = flags["reason"];
|
|
480
|
+
const date = flags["date"];
|
|
481
|
+
const accept = flags["accept"] === "true";
|
|
482
|
+
const reject = flags["reject"] === "true";
|
|
483
|
+
if (redlinesPath === void 0 || id === void 0 || reason === void 0 || date === void 0 || accept === reject) return usage(io);
|
|
484
|
+
const redlines = JSON.parse(readFileSync(resolve(redlinesPath), "utf8"));
|
|
485
|
+
const existing = redlines.find((r) => r.id === id);
|
|
486
|
+
const index = redlines.findIndex((r) => r.id === id);
|
|
487
|
+
if (existing === void 0) {
|
|
488
|
+
io.err(`Redline ${id} not found in ${redlinesPath}.`);
|
|
489
|
+
return 2;
|
|
490
|
+
}
|
|
491
|
+
const resolved = resolveRedline(existing, {
|
|
492
|
+
accept,
|
|
493
|
+
reason,
|
|
494
|
+
resolvedAt: date,
|
|
495
|
+
...flags["by"] !== void 0 ? { by: flags["by"] } : {}
|
|
496
|
+
});
|
|
497
|
+
const updated = [...redlines];
|
|
498
|
+
updated[index] = resolved;
|
|
499
|
+
writeFileSync(resolve(redlinesPath), JSON.stringify(updated, null, 2) + "\n");
|
|
500
|
+
io.out(`${id} ${resolved.status}.`);
|
|
501
|
+
if (resolved.status === "accepted") {
|
|
502
|
+
const patch = suggestPatch(resolved);
|
|
503
|
+
if (patch !== void 0) {
|
|
504
|
+
io.out("Structured patch (apply via variant() or edit the source):");
|
|
505
|
+
io.out(JSON.stringify(patch, null, 2));
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return 0;
|
|
509
|
+
}
|
|
510
|
+
return usage(io);
|
|
511
|
+
}
|
|
235
512
|
case "diff": {
|
|
236
513
|
const [pathA, pathB] = positional;
|
|
237
514
|
if (pathA === void 0 || pathB === void 0) return usage(io);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grayhaven/nerve-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Grayhaven Nerve CLI: compile, validate, render, and export harnesses locally and in CI.",
|
|
6
6
|
"bin": {
|
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"effect": "^3.16.0",
|
|
17
17
|
"jiti": "^2.4.2",
|
|
18
|
-
"@grayhaven/nerve
|
|
19
|
-
"@grayhaven/nerve": "0.
|
|
20
|
-
"@grayhaven/nerve-
|
|
21
|
-
"@grayhaven/nerve-compiler": "0.
|
|
18
|
+
"@grayhaven/nerve": "0.2.0",
|
|
19
|
+
"@grayhaven/nerve-wireviz": "0.2.0",
|
|
20
|
+
"@grayhaven/nerve-exporters": "0.2.0",
|
|
21
|
+
"@grayhaven/nerve-compiler": "0.2.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^25.9.2",
|
|
25
25
|
"typescript": "^5.8.3",
|
|
26
26
|
"vitest": "^3.2.4",
|
|
27
|
-
"@grayhaven/nerve-connectors": "0.
|
|
27
|
+
"@grayhaven/nerve-connectors": "0.2.0"
|
|
28
28
|
},
|
|
29
29
|
"license": "Apache-2.0",
|
|
30
30
|
"files": [
|