@vcad/mcp 0.10.0-main.130 → 0.10.0-main.132
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 -1
- package/index.mjs +275 -90
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -6,4 +6,4 @@ vcad's MCP server as a self-contained bundle (server + BRep kernel WASM).
|
|
|
6
6
|
{ "mcpServers": { "vcad": { "command": "npx", "args": ["-y", "@vcad/mcp"] } } }
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
Built from
|
|
9
|
+
Built from 362c46b798588ed58f200c9d529b3d11b2b2ff73 at 2026-10-21T17:31:51.205Z. Source: https://github.com/ecto/vcad
|
package/index.mjs
CHANGED
|
@@ -10867,6 +10867,173 @@ var init_delta = __esm({
|
|
|
10867
10867
|
}
|
|
10868
10868
|
});
|
|
10869
10869
|
|
|
10870
|
+
// ../engine/dist/engine-host.js
|
|
10871
|
+
function handleEngineRequest(host, req) {
|
|
10872
|
+
try {
|
|
10873
|
+
let envelope;
|
|
10874
|
+
let bytes;
|
|
10875
|
+
switch (req.op) {
|
|
10876
|
+
case "init":
|
|
10877
|
+
host.engine?.free();
|
|
10878
|
+
host.engine = req.replica !== void 0 && host.Engine.withReplica ? host.Engine.withReplica(req.replica) : new host.Engine();
|
|
10879
|
+
envelope = host.engine.stateEnvelope();
|
|
10880
|
+
break;
|
|
10881
|
+
case "load":
|
|
10882
|
+
host.engine?.free();
|
|
10883
|
+
host.engine = host.Engine.load(req.bytes);
|
|
10884
|
+
envelope = host.engine.stateEnvelope();
|
|
10885
|
+
break;
|
|
10886
|
+
default: {
|
|
10887
|
+
const engine2 = host.engine;
|
|
10888
|
+
if (!engine2)
|
|
10889
|
+
return { ok: false, message: "engine not initialized" };
|
|
10890
|
+
if (req.op === "apply")
|
|
10891
|
+
envelope = engine2.applyDelta(JSON.stringify(req.command));
|
|
10892
|
+
else if (req.op === "applyTool")
|
|
10893
|
+
envelope = engine2.applyToolDelta(req.tool, JSON.stringify(req.args));
|
|
10894
|
+
else if (req.op === "state")
|
|
10895
|
+
envelope = engine2.stateEnvelope();
|
|
10896
|
+
else
|
|
10897
|
+
bytes = engine2.save();
|
|
10898
|
+
}
|
|
10899
|
+
}
|
|
10900
|
+
const engine = host.engine;
|
|
10901
|
+
return {
|
|
10902
|
+
ok: true,
|
|
10903
|
+
envelope,
|
|
10904
|
+
bytes,
|
|
10905
|
+
hash: engine.stateHash(),
|
|
10906
|
+
canUndo: engine.can_undo(),
|
|
10907
|
+
canRedo: engine.can_redo()
|
|
10908
|
+
};
|
|
10909
|
+
} catch (e) {
|
|
10910
|
+
return { ok: false, message: e instanceof Error ? e.message : String(e) };
|
|
10911
|
+
}
|
|
10912
|
+
}
|
|
10913
|
+
function inProcessTransport(Engine2) {
|
|
10914
|
+
const host = { Engine: Engine2, engine: null };
|
|
10915
|
+
return {
|
|
10916
|
+
request: async (req) => handleEngineRequest(host, req),
|
|
10917
|
+
dispose: () => {
|
|
10918
|
+
host.engine?.free();
|
|
10919
|
+
host.engine = null;
|
|
10920
|
+
}
|
|
10921
|
+
};
|
|
10922
|
+
}
|
|
10923
|
+
function workerTransport(worker) {
|
|
10924
|
+
let next = 0;
|
|
10925
|
+
const pending2 = /* @__PURE__ */ new Map();
|
|
10926
|
+
worker.addEventListener("message", (e) => {
|
|
10927
|
+
const { id, response } = e.data;
|
|
10928
|
+
pending2.get(id)?.(response);
|
|
10929
|
+
pending2.delete(id);
|
|
10930
|
+
});
|
|
10931
|
+
return {
|
|
10932
|
+
request: (req) => new Promise((resolve7) => {
|
|
10933
|
+
const id = next++;
|
|
10934
|
+
pending2.set(id, resolve7);
|
|
10935
|
+
worker.postMessage({ id, req });
|
|
10936
|
+
}),
|
|
10937
|
+
dispose: () => worker.terminate()
|
|
10938
|
+
};
|
|
10939
|
+
}
|
|
10940
|
+
var EngineHostError, DocumentEngineClient;
|
|
10941
|
+
var init_engine_host = __esm({
|
|
10942
|
+
"../engine/dist/engine-host.js"() {
|
|
10943
|
+
"use strict";
|
|
10944
|
+
init_delta();
|
|
10945
|
+
EngineHostError = class extends Error {
|
|
10946
|
+
constructor(message) {
|
|
10947
|
+
super(message);
|
|
10948
|
+
this.name = "EngineHostError";
|
|
10949
|
+
}
|
|
10950
|
+
};
|
|
10951
|
+
DocumentEngineClient = class {
|
|
10952
|
+
transport;
|
|
10953
|
+
view = null;
|
|
10954
|
+
queue = Promise.resolve();
|
|
10955
|
+
flags = { hash: "", canUndo: false, canRedo: false };
|
|
10956
|
+
constructor(transport) {
|
|
10957
|
+
this.transport = transport;
|
|
10958
|
+
}
|
|
10959
|
+
/** The current view (null before `init`/`load`). */
|
|
10960
|
+
get state() {
|
|
10961
|
+
return this.view;
|
|
10962
|
+
}
|
|
10963
|
+
get stateHash() {
|
|
10964
|
+
return this.flags.hash;
|
|
10965
|
+
}
|
|
10966
|
+
get canUndo() {
|
|
10967
|
+
return this.flags.canUndo;
|
|
10968
|
+
}
|
|
10969
|
+
get canRedo() {
|
|
10970
|
+
return this.flags.canRedo;
|
|
10971
|
+
}
|
|
10972
|
+
/** Start an empty document. */
|
|
10973
|
+
init(replica) {
|
|
10974
|
+
return this.full({ op: "init", replica });
|
|
10975
|
+
}
|
|
10976
|
+
/** Restore a `save()` snapshot. */
|
|
10977
|
+
load(bytes) {
|
|
10978
|
+
return this.full({ op: "load", bytes });
|
|
10979
|
+
}
|
|
10980
|
+
/** Re-read the whole state (resynchronizes the view). */
|
|
10981
|
+
refresh() {
|
|
10982
|
+
return this.full({ op: "state" });
|
|
10983
|
+
}
|
|
10984
|
+
/** Apply a command; rejects with `EngineRefusal` when the engine refuses. */
|
|
10985
|
+
apply(command) {
|
|
10986
|
+
return this.delta({ op: "apply", command });
|
|
10987
|
+
}
|
|
10988
|
+
/** Lower and apply an AI tool call. */
|
|
10989
|
+
applyTool(tool, args) {
|
|
10990
|
+
return this.delta({ op: "applyTool", tool, args });
|
|
10991
|
+
}
|
|
10992
|
+
/** Snapshot the engine (document + history). */
|
|
10993
|
+
save() {
|
|
10994
|
+
return this.enqueue(async () => {
|
|
10995
|
+
const r = this.check(await this.transport.request({ op: "save" }));
|
|
10996
|
+
return r.bytes ?? new Uint8Array();
|
|
10997
|
+
});
|
|
10998
|
+
}
|
|
10999
|
+
dispose() {
|
|
11000
|
+
this.transport.dispose();
|
|
11001
|
+
this.view = null;
|
|
11002
|
+
}
|
|
11003
|
+
full(req) {
|
|
11004
|
+
return this.enqueue(async () => {
|
|
11005
|
+
const r = this.check(await this.transport.request(req));
|
|
11006
|
+
this.view = stateFromEnvelope(r.envelope);
|
|
11007
|
+
return this.view;
|
|
11008
|
+
});
|
|
11009
|
+
}
|
|
11010
|
+
delta(req) {
|
|
11011
|
+
return this.enqueue(async () => {
|
|
11012
|
+
const base = this.view;
|
|
11013
|
+
if (!base)
|
|
11014
|
+
throw new EngineHostError("engine not initialized");
|
|
11015
|
+
const r = this.check(await this.transport.request(req));
|
|
11016
|
+
const delta = parseDeltaEnvelope(r.envelope);
|
|
11017
|
+
const { state, changes } = applyEngineDelta(base, delta);
|
|
11018
|
+
this.view = state;
|
|
11019
|
+
return { state, changes, createdFeatureId: delta.createdFeatureId, warnings: delta.warnings };
|
|
11020
|
+
});
|
|
11021
|
+
}
|
|
11022
|
+
check(r) {
|
|
11023
|
+
if (!r.ok)
|
|
11024
|
+
throw new EngineHostError(r.message);
|
|
11025
|
+
this.flags = { hash: r.hash, canUndo: r.canUndo, canRedo: r.canRedo };
|
|
11026
|
+
return r;
|
|
11027
|
+
}
|
|
11028
|
+
enqueue(f) {
|
|
11029
|
+
const run = this.queue.then(f, f);
|
|
11030
|
+
this.queue = run.catch(() => void 0);
|
|
11031
|
+
return run;
|
|
11032
|
+
}
|
|
11033
|
+
};
|
|
11034
|
+
}
|
|
11035
|
+
});
|
|
11036
|
+
|
|
10870
11037
|
// ../engine/dist/sequence.js
|
|
10871
11038
|
function requireWasm2() {
|
|
10872
11039
|
const mod = getKernelWasmSync();
|
|
@@ -12023,7 +12190,9 @@ __export(dist_exports, {
|
|
|
12023
12190
|
DEFAULT_NESTING_PARAMS: () => DEFAULT_NESTING_PARAMS,
|
|
12024
12191
|
DEFAULT_SHOP_PROFILE: () => DEFAULT_SHOP_PROFILE,
|
|
12025
12192
|
DependencyGraph: () => DependencyGraph,
|
|
12193
|
+
DocumentEngineClient: () => DocumentEngineClient,
|
|
12026
12194
|
Engine: () => Engine,
|
|
12195
|
+
EngineHostError: () => EngineHostError,
|
|
12027
12196
|
EngineRefusal: () => EngineRefusal,
|
|
12028
12197
|
ExpressionEvalError: () => EvalError,
|
|
12029
12198
|
ExpressionParseError: () => ParseError,
|
|
@@ -12055,6 +12224,7 @@ __export(dist_exports, {
|
|
|
12055
12224
|
computeRatsnest: () => computeRatsnest,
|
|
12056
12225
|
connectorsFromPcb: () => connectorsFromPcb,
|
|
12057
12226
|
createCircuitSim: () => createCircuitSim,
|
|
12227
|
+
createEngineWorker: () => createEngineWorker,
|
|
12058
12228
|
critiqueRoute: () => critiqueRoute,
|
|
12059
12229
|
decimateMeshGpu: () => decimateMeshGpu2,
|
|
12060
12230
|
defaultParamsFor: () => defaultParamsFor,
|
|
@@ -12081,8 +12251,10 @@ __export(dist_exports, {
|
|
|
12081
12251
|
getKernelWasmSync: () => getKernelWasmSync,
|
|
12082
12252
|
getPcbDfmPack: () => getPcbDfmPack,
|
|
12083
12253
|
getRayTracer: () => getRayTracer,
|
|
12254
|
+
handleEngineRequest: () => handleEngineRequest,
|
|
12084
12255
|
hashCsgOp: () => hashCsgOp,
|
|
12085
12256
|
homogenizeMaterial: () => homogenizeMaterial,
|
|
12257
|
+
inProcessTransport: () => inProcessTransport,
|
|
12086
12258
|
initializeGpu: () => initializeGpu,
|
|
12087
12259
|
initializeRayTracer: () => initializeRayTracer,
|
|
12088
12260
|
inspectMolecule: () => inspectMolecule,
|
|
@@ -12150,8 +12322,12 @@ __export(dist_exports, {
|
|
|
12150
12322
|
tryRunDrc: () => tryRunDrc,
|
|
12151
12323
|
verifyReceipt: () => verifyReceipt,
|
|
12152
12324
|
verifySubstitution: () => verifySubstitution,
|
|
12325
|
+
workerTransport: () => workerTransport,
|
|
12153
12326
|
writeXyz: () => writeXyz
|
|
12154
12327
|
});
|
|
12328
|
+
function createEngineWorker() {
|
|
12329
|
+
return new Worker(new URL("./engine-worker.js", import.meta.url), { type: "module" });
|
|
12330
|
+
}
|
|
12155
12331
|
var SCENE_CACHE_MAX, Engine;
|
|
12156
12332
|
var init_dist = __esm({
|
|
12157
12333
|
"../engine/dist/index.js"() {
|
|
@@ -12168,6 +12344,7 @@ var init_dist = __esm({
|
|
|
12168
12344
|
init_diff();
|
|
12169
12345
|
init_solid_cache();
|
|
12170
12346
|
init_delta();
|
|
12347
|
+
init_engine_host();
|
|
12171
12348
|
init_mesh_cache();
|
|
12172
12349
|
init_dependency_graph();
|
|
12173
12350
|
init_evaluate();
|
|
@@ -12315,6 +12492,7 @@ var init_dist = __esm({
|
|
|
12315
12492
|
evaluateDocument: wasmModule7.evaluateDocument,
|
|
12316
12493
|
evalVcadSource: wasmModule7.evalVcadSource,
|
|
12317
12494
|
evalVcadSourceWithModules: wasmModule7.evalVcadSourceWithModules,
|
|
12495
|
+
evalLoonDocument: wasmModule7.evalLoonDocument,
|
|
12318
12496
|
evalVcadSourceParametric: wasmModule7.evalVcadSourceParametric,
|
|
12319
12497
|
documentParameterGradient: wasmModule7.documentParameterGradient,
|
|
12320
12498
|
documentSensitivities: wasmModule7.documentSensitivities,
|
|
@@ -13217,6 +13395,22 @@ var init_dist = __esm({
|
|
|
13217
13395
|
* With an empty map this is exactly {@link evalVcadSource}. Returns null
|
|
13218
13396
|
* if the kernel doesn't support module-aware loon evaluation.
|
|
13219
13397
|
*/
|
|
13398
|
+
/**
|
|
13399
|
+
* Evaluate loon source to a session document stamped with its provenance
|
|
13400
|
+
* (`doc.source`: program, modules, origin dir/file, SHA-256), in the kernel.
|
|
13401
|
+
* Null on kernels predating the export.
|
|
13402
|
+
*/
|
|
13403
|
+
evalLoonDocument(source, modules, origin = {}) {
|
|
13404
|
+
if (typeof this.kernel.evalLoonDocument !== "function")
|
|
13405
|
+
return null;
|
|
13406
|
+
const doc = JSON.parse(this.kernel.evalLoonDocument(source, JSON.stringify(modules), origin.baseDir, origin.path));
|
|
13407
|
+
if (doc.source) {
|
|
13408
|
+
doc.source.diverged ??= false;
|
|
13409
|
+
doc.source.diverged_by ??= [];
|
|
13410
|
+
doc.source.modules ??= {};
|
|
13411
|
+
}
|
|
13412
|
+
return doc;
|
|
13413
|
+
}
|
|
13220
13414
|
evalVcadSourceWithModules(source, modules) {
|
|
13221
13415
|
if (!Object.keys(modules).length)
|
|
13222
13416
|
return this.evalVcadSource(source);
|
|
@@ -41509,6 +41703,18 @@ var init_CHANGELOG = __esm({
|
|
|
41509
41703
|
"cli"
|
|
41510
41704
|
]
|
|
41511
41705
|
},
|
|
41706
|
+
{
|
|
41707
|
+
id: "2026-10-21-photoreal-cyclorama",
|
|
41708
|
+
version: "0.10.0",
|
|
41709
|
+
date: "2026-10-21",
|
|
41710
|
+
category: "feat",
|
|
41711
|
+
title: "Seamless cyclorama backdrop for photoreal renders",
|
|
41712
|
+
summary: "--backdrop cyclorama (vcad render --cyclorama) puts the subject on a matte floor that curves into a back wall, with no horizon line or edge in frame.",
|
|
41713
|
+
features: [
|
|
41714
|
+
"photoreal",
|
|
41715
|
+
"render"
|
|
41716
|
+
]
|
|
41717
|
+
},
|
|
41512
41718
|
{
|
|
41513
41719
|
id: "2026-10-21-mac-sim-teardown-race",
|
|
41514
41720
|
version: "0.10.0",
|
|
@@ -51528,79 +51734,8 @@ var init_loon_assets = __esm({
|
|
|
51528
51734
|
}
|
|
51529
51735
|
});
|
|
51530
51736
|
|
|
51531
|
-
// src/tools/source-provenance.ts
|
|
51532
|
-
import { createHash } from "node:crypto";
|
|
51533
|
-
import { readFileSync as readFileSync3 } from "node:fs";
|
|
51534
|
-
function sourceHash(text) {
|
|
51535
|
-
return createHash("sha256").update(text, "utf8").digest("hex");
|
|
51536
|
-
}
|
|
51537
|
-
function attachLoonSource(doc, { text, modules, base_dir, path }) {
|
|
51538
|
-
const source = {
|
|
51539
|
-
language: "loon",
|
|
51540
|
-
text,
|
|
51541
|
-
modules: modules && Object.keys(modules).length ? { ...modules } : {},
|
|
51542
|
-
hash: sourceHash(text),
|
|
51543
|
-
diverged: false,
|
|
51544
|
-
diverged_by: []
|
|
51545
|
-
};
|
|
51546
|
-
if (base_dir) source.base_dir = base_dir;
|
|
51547
|
-
if (path) source.path = path;
|
|
51548
|
-
doc.source = source;
|
|
51549
|
-
}
|
|
51550
|
-
function markSourceDiverged(documentId, tool) {
|
|
51551
|
-
const src = documents.get(documentId)?.source;
|
|
51552
|
-
if (!src) return false;
|
|
51553
|
-
const firstTime = !src.diverged;
|
|
51554
|
-
src.diverged = true;
|
|
51555
|
-
const by = src.diverged_by ??= [];
|
|
51556
|
-
if (by[by.length - 1] !== tool) by.push(tool);
|
|
51557
|
-
if (by.length > MAX_DIVERGED_BY) by.splice(0, by.length - MAX_DIVERGED_BY);
|
|
51558
|
-
return firstTime;
|
|
51559
|
-
}
|
|
51560
|
-
function sourceStatus(doc) {
|
|
51561
|
-
const src = doc?.source;
|
|
51562
|
-
if (!src) return { source_stale: false };
|
|
51563
|
-
const path = src.path;
|
|
51564
|
-
if (path) {
|
|
51565
|
-
let onDisk = null;
|
|
51566
|
-
try {
|
|
51567
|
-
onDisk = readFileSync3(path, "utf8");
|
|
51568
|
-
} catch {
|
|
51569
|
-
return {
|
|
51570
|
-
source_stale: true,
|
|
51571
|
-
source_path: path,
|
|
51572
|
-
reason: `The source file ${path} can no longer be read \u2014 this session is no longer backed by the file it was loaded from.`
|
|
51573
|
-
};
|
|
51574
|
-
}
|
|
51575
|
-
if (sourceHash(onDisk) !== src.hash) {
|
|
51576
|
-
return {
|
|
51577
|
-
source_stale: true,
|
|
51578
|
-
source_path: path,
|
|
51579
|
-
reason: `${path} changed on disk since this session was evaluated from it \u2014 the session's geometry is from the OLD source. Re-load the file to pick up the edits.`
|
|
51580
|
-
};
|
|
51581
|
-
}
|
|
51582
|
-
}
|
|
51583
|
-
if (src.diverged) {
|
|
51584
|
-
const by = src.diverged_by ?? [];
|
|
51585
|
-
return {
|
|
51586
|
-
source_stale: true,
|
|
51587
|
-
...path ? { source_path: path } : {},
|
|
51588
|
-
reason: `This session has been edited directly (${by.join(", ") || "mutation"}), and those edits cannot round-trip back to loon. The stored source describes the document's ORIGIN, not its current state` + (path ? ` \u2014 saving would not reproduce ${path}, and re-evaluating ${path} would discard the edits.` : `.`)
|
|
51589
|
-
};
|
|
51590
|
-
}
|
|
51591
|
-
return { source_stale: false, ...path ? { source_path: path } : {} };
|
|
51592
|
-
}
|
|
51593
|
-
var MAX_DIVERGED_BY;
|
|
51594
|
-
var init_source_provenance = __esm({
|
|
51595
|
-
"src/tools/source-provenance.ts"() {
|
|
51596
|
-
"use strict";
|
|
51597
|
-
init_session_core();
|
|
51598
|
-
MAX_DIVERGED_BY = 8;
|
|
51599
|
-
}
|
|
51600
|
-
});
|
|
51601
|
-
|
|
51602
51737
|
// src/tools/loon.ts
|
|
51603
|
-
import { readFileSync as
|
|
51738
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
51604
51739
|
import { delimiter, isAbsolute as isAbsolute3, join as join2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
51605
51740
|
function composeLoonProgram(input) {
|
|
51606
51741
|
const { source, use_loons, loons } = input;
|
|
@@ -51626,7 +51761,7 @@ function readModule(base, name) {
|
|
|
51626
51761
|
const file = resolve2(join2(root, ...name.split(".")) + ext);
|
|
51627
51762
|
if (file !== root && !file.startsWith(root + sep2)) continue;
|
|
51628
51763
|
try {
|
|
51629
|
-
return
|
|
51764
|
+
return readFileSync3(file, "utf8");
|
|
51630
51765
|
} catch {
|
|
51631
51766
|
}
|
|
51632
51767
|
}
|
|
@@ -51693,11 +51828,10 @@ function evalCadLoon(input, engine) {
|
|
|
51693
51828
|
const modules = composeLoonModules(input);
|
|
51694
51829
|
let doc;
|
|
51695
51830
|
try {
|
|
51696
|
-
doc = engine.
|
|
51831
|
+
doc = engine.evalLoonDocument(source, modules, { baseDir: base_dir });
|
|
51697
51832
|
} catch (e) {
|
|
51698
51833
|
throw explainEvalError(e, parsed, missingModules(source, modules));
|
|
51699
51834
|
}
|
|
51700
|
-
if (doc) attachLoonSource(doc, { text: source, modules, base_dir });
|
|
51701
51835
|
if (!doc) {
|
|
51702
51836
|
const text2 = Object.keys(modules).length ? "Error: this kernel build cannot resolve loon modules ([use ...]) \u2014 update the kernel, or inline the modules into `source`" : "Error: Loon evaluation not supported by this engine build";
|
|
51703
51837
|
return { result: { content: [{ type: "text", text: text2 }] }, doc: null };
|
|
@@ -51771,7 +51905,6 @@ var init_loon = __esm({
|
|
|
51771
51905
|
init_session_core();
|
|
51772
51906
|
init_loon_assets();
|
|
51773
51907
|
init_remote();
|
|
51774
|
-
init_source_provenance();
|
|
51775
51908
|
init_tool_def();
|
|
51776
51909
|
createCadLoonSchema = {
|
|
51777
51910
|
type: "object",
|
|
@@ -52427,7 +52560,7 @@ var init_session_store = __esm({
|
|
|
52427
52560
|
});
|
|
52428
52561
|
|
|
52429
52562
|
// src/tools/artifact-store.ts
|
|
52430
|
-
import { createHash
|
|
52563
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
52431
52564
|
function ttlMs() {
|
|
52432
52565
|
const raw = process.env.MCP_ARTIFACT_TTL_MS;
|
|
52433
52566
|
const n = raw ? parseInt(raw, 10) : NaN;
|
|
@@ -52523,7 +52656,7 @@ function storeArtifact(files) {
|
|
|
52523
52656
|
const manifest = stored.map((f) => ({
|
|
52524
52657
|
file: f.name,
|
|
52525
52658
|
bytes: f.buf.length,
|
|
52526
|
-
sha256:
|
|
52659
|
+
sha256: createHash("sha256").update(f.buf).digest("hex")
|
|
52527
52660
|
}));
|
|
52528
52661
|
const bytes = stored.reduce((n, f) => n + f.buf.length, 0);
|
|
52529
52662
|
const expiresAt = now + ttlMs();
|
|
@@ -52658,7 +52791,7 @@ async function loadDurable(id) {
|
|
|
52658
52791
|
manifest: Array.isArray(row.manifest) ? row.manifest : files.map((f) => ({
|
|
52659
52792
|
file: f.name,
|
|
52660
52793
|
bytes: f.buf.length,
|
|
52661
|
-
sha256:
|
|
52794
|
+
sha256: createHash("sha256").update(f.buf).digest("hex")
|
|
52662
52795
|
})),
|
|
52663
52796
|
bytes: typeof row.bytes === "number" ? row.bytes : files.reduce((n, f) => n + f.buf.length, 0),
|
|
52664
52797
|
createdAt: row.created_at ? Date.parse(row.created_at) : Date.now(),
|
|
@@ -52718,6 +52851,64 @@ var init_artifact_store = __esm({
|
|
|
52718
52851
|
}
|
|
52719
52852
|
});
|
|
52720
52853
|
|
|
52854
|
+
// src/tools/source-provenance.ts
|
|
52855
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
52856
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
52857
|
+
function sourceHash(text) {
|
|
52858
|
+
return createHash2("sha256").update(text, "utf8").digest("hex");
|
|
52859
|
+
}
|
|
52860
|
+
function markSourceDiverged(documentId, tool) {
|
|
52861
|
+
const src = documents.get(documentId)?.source;
|
|
52862
|
+
if (!src) return false;
|
|
52863
|
+
const firstTime = !src.diverged;
|
|
52864
|
+
src.diverged = true;
|
|
52865
|
+
const by = src.diverged_by ??= [];
|
|
52866
|
+
if (by[by.length - 1] !== tool) by.push(tool);
|
|
52867
|
+
if (by.length > MAX_DIVERGED_BY) by.splice(0, by.length - MAX_DIVERGED_BY);
|
|
52868
|
+
return firstTime;
|
|
52869
|
+
}
|
|
52870
|
+
function sourceStatus(doc) {
|
|
52871
|
+
const src = doc?.source;
|
|
52872
|
+
if (!src) return { source_stale: false };
|
|
52873
|
+
const path = src.path;
|
|
52874
|
+
if (path) {
|
|
52875
|
+
let onDisk = null;
|
|
52876
|
+
try {
|
|
52877
|
+
onDisk = readFileSync4(path, "utf8");
|
|
52878
|
+
} catch {
|
|
52879
|
+
return {
|
|
52880
|
+
source_stale: true,
|
|
52881
|
+
source_path: path,
|
|
52882
|
+
reason: `The source file ${path} can no longer be read \u2014 this session is no longer backed by the file it was loaded from.`
|
|
52883
|
+
};
|
|
52884
|
+
}
|
|
52885
|
+
if (sourceHash(onDisk) !== src.hash) {
|
|
52886
|
+
return {
|
|
52887
|
+
source_stale: true,
|
|
52888
|
+
source_path: path,
|
|
52889
|
+
reason: `${path} changed on disk since this session was evaluated from it \u2014 the session's geometry is from the OLD source. Re-load the file to pick up the edits.`
|
|
52890
|
+
};
|
|
52891
|
+
}
|
|
52892
|
+
}
|
|
52893
|
+
if (src.diverged) {
|
|
52894
|
+
const by = src.diverged_by ?? [];
|
|
52895
|
+
return {
|
|
52896
|
+
source_stale: true,
|
|
52897
|
+
...path ? { source_path: path } : {},
|
|
52898
|
+
reason: `This session has been edited directly (${by.join(", ") || "mutation"}), and those edits cannot round-trip back to loon. The stored source describes the document's ORIGIN, not its current state` + (path ? ` \u2014 saving would not reproduce ${path}, and re-evaluating ${path} would discard the edits.` : `.`)
|
|
52899
|
+
};
|
|
52900
|
+
}
|
|
52901
|
+
return { source_stale: false, ...path ? { source_path: path } : {} };
|
|
52902
|
+
}
|
|
52903
|
+
var MAX_DIVERGED_BY;
|
|
52904
|
+
var init_source_provenance = __esm({
|
|
52905
|
+
"src/tools/source-provenance.ts"() {
|
|
52906
|
+
"use strict";
|
|
52907
|
+
init_session_core();
|
|
52908
|
+
MAX_DIVERGED_BY = 8;
|
|
52909
|
+
}
|
|
52910
|
+
});
|
|
52911
|
+
|
|
52721
52912
|
// src/tools/session.ts
|
|
52722
52913
|
import { writeFileSync as writeFileSync2, readFileSync as readFileSync5 } from "node:fs";
|
|
52723
52914
|
import { dirname, resolve as resolve3 } from "node:path";
|
|
@@ -53034,18 +53225,12 @@ async function loadDocumentFromPath(path, store, engine) {
|
|
|
53034
53225
|
const modules = composeLoonModules({ source: raw, base_dir: baseDir });
|
|
53035
53226
|
let doc;
|
|
53036
53227
|
try {
|
|
53037
|
-
doc = engine.
|
|
53228
|
+
doc = engine.evalLoonDocument(raw, modules, { baseDir, path: abs });
|
|
53038
53229
|
} catch (e) {
|
|
53039
53230
|
return fail2(`Loon evaluation of ${abs} failed: ${e.message}`);
|
|
53040
53231
|
}
|
|
53041
53232
|
if (!doc) return fail2(`Loon evaluation of ${abs} produced no document.`);
|
|
53042
53233
|
const imports = assetNote(resolveLoonAssets(doc, { base_dir: baseDir }, engine));
|
|
53043
|
-
attachLoonSource(doc, {
|
|
53044
|
-
text: raw,
|
|
53045
|
-
modules,
|
|
53046
|
-
base_dir: baseDir,
|
|
53047
|
-
path: abs
|
|
53048
|
-
});
|
|
53049
53234
|
const id = registerSession(doc);
|
|
53050
53235
|
return {
|
|
53051
53236
|
content: [
|
|
@@ -86876,8 +87061,8 @@ var init_server3 = __esm({
|
|
|
86876
87061
|
init_order_feed();
|
|
86877
87062
|
init_animate();
|
|
86878
87063
|
PKG_VERSION = (() => {
|
|
86879
|
-
if ("0.10.0-main.
|
|
86880
|
-
return "0.10.0-main.
|
|
87064
|
+
if ("0.10.0-main.132") {
|
|
87065
|
+
return "0.10.0-main.132";
|
|
86881
87066
|
}
|
|
86882
87067
|
try {
|
|
86883
87068
|
const req = createRequire2(import.meta.url);
|
|
@@ -86886,8 +87071,8 @@ var init_server3 = __esm({
|
|
|
86886
87071
|
return "0.0.0";
|
|
86887
87072
|
}
|
|
86888
87073
|
})();
|
|
86889
|
-
BUILD_SHA = "
|
|
86890
|
-
BUILD_TIME = "2026-10-
|
|
87074
|
+
BUILD_SHA = "362c46b798588ed58f200c9d529b3d11b2b2ff73";
|
|
87075
|
+
BUILD_TIME = "2026-10-21T17:31:51.205Z";
|
|
86891
87076
|
SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
|
|
86892
87077
|
VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
|
|
86893
87078
|
INSTANCE_ID = randomUUID7().slice(0, 8);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vcad/mcp",
|
|
3
|
-
"version": "0.10.0-main.
|
|
3
|
+
"version": "0.10.0-main.132",
|
|
4
4
|
"description": "vcad MCP server — parametric CAD + PCB design tools for AI agents (self-contained: bundled server + kernel WASM)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"homepage": "https://vcad.io",
|
|
21
21
|
"vcadBuild": {
|
|
22
|
-
"sha": "
|
|
23
|
-
"builtAt": "2026-10-
|
|
22
|
+
"sha": "362c46b798588ed58f200c9d529b3d11b2b2ff73",
|
|
23
|
+
"builtAt": "2026-10-21T17:31:51.205Z"
|
|
24
24
|
}
|
|
25
25
|
}
|