@vcad/mcp 0.9.4-main.7 → 0.9.4-main.9
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 +63 -218
- package/package.json +3 -3
- package/vcad_kernel_wasm_bg.wasm +0 -0
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 c3ef1dae913d41fab4da6f029c6d6b347bb08395 at 2026-07-24T16:14:27.739Z. Source: https://github.com/ecto/vcad
|
package/index.mjs
CHANGED
|
@@ -10077,120 +10077,22 @@ var init_diff = __esm({
|
|
|
10077
10077
|
});
|
|
10078
10078
|
|
|
10079
10079
|
// ../engine/dist/sequence.js
|
|
10080
|
-
function
|
|
10081
|
-
const
|
|
10082
|
-
if (
|
|
10083
|
-
|
|
10084
|
-
const first = keys[0];
|
|
10085
|
-
if (t2 <= first.t)
|
|
10086
|
-
return first.value;
|
|
10087
|
-
const last = keys[keys.length - 1];
|
|
10088
|
-
if (t2 >= last.t)
|
|
10089
|
-
return last.value;
|
|
10090
|
-
const idx = keys.findIndex((k) => k.t > t2);
|
|
10091
|
-
const a = keys[idx - 1];
|
|
10092
|
-
const b = keys[idx];
|
|
10093
|
-
const span = b.t - a.t;
|
|
10094
|
-
let u = span <= 0 ? 1 : (t2 - a.t) / span;
|
|
10095
|
-
switch (b.ease ?? "linear") {
|
|
10096
|
-
case "step":
|
|
10097
|
-
u = u >= 1 ? 1 : 0;
|
|
10098
|
-
break;
|
|
10099
|
-
case "ease-in-out":
|
|
10100
|
-
u = u * u * (3 - 2 * u);
|
|
10101
|
-
break;
|
|
10102
|
-
default:
|
|
10103
|
-
break;
|
|
10104
|
-
}
|
|
10105
|
-
return a.value + (b.value - a.value) * u;
|
|
10106
|
-
}
|
|
10107
|
-
function cameraPoseAt(shots, t2, prevPose) {
|
|
10108
|
-
let active;
|
|
10109
|
-
for (const shot of shots) {
|
|
10110
|
-
if (t2 >= shot.startS && t2 < shot.endS)
|
|
10111
|
-
active = shot;
|
|
10112
|
-
}
|
|
10113
|
-
if (!active)
|
|
10114
|
-
return prevPose;
|
|
10115
|
-
const span = active.endS - active.startS;
|
|
10116
|
-
const u = span <= 0 ? 1 : (t2 - active.startS) / span;
|
|
10117
|
-
const kind = active.kind;
|
|
10118
|
-
switch (kind.type) {
|
|
10119
|
-
case "Turntable":
|
|
10120
|
-
return {
|
|
10121
|
-
azimuthDeg: kind.degrees * u,
|
|
10122
|
-
elevationDeg: kind.elevationDeg,
|
|
10123
|
-
dolly: 1
|
|
10124
|
-
};
|
|
10125
|
-
case "Orbit":
|
|
10126
|
-
return {
|
|
10127
|
-
azimuthDeg: kind.from[0] + (kind.to[0] - kind.from[0]) * u,
|
|
10128
|
-
elevationDeg: kind.from[1] + (kind.to[1] - kind.from[1]) * u,
|
|
10129
|
-
dolly: 1
|
|
10130
|
-
};
|
|
10131
|
-
case "Focus":
|
|
10132
|
-
return {
|
|
10133
|
-
azimuthDeg: prevPose.azimuthDeg,
|
|
10134
|
-
elevationDeg: prevPose.elevationDeg,
|
|
10135
|
-
dolly: 1 + (kind.dolly - 1) * u,
|
|
10136
|
-
target: kind.target
|
|
10137
|
-
};
|
|
10138
|
-
case "Static":
|
|
10139
|
-
return { ...DEFAULT_POSE };
|
|
10080
|
+
function requireWasm() {
|
|
10081
|
+
const mod = getKernelWasmSync();
|
|
10082
|
+
if (!mod || typeof mod.sample_timeline_sequence !== "function" || typeof mod.sample_timeline_track !== "function") {
|
|
10083
|
+
throw new Error("kernel WASM not initialized (or too old for timeline sampling) \u2014 await getKernelWasm() before sampling sequences");
|
|
10140
10084
|
}
|
|
10085
|
+
return mod;
|
|
10086
|
+
}
|
|
10087
|
+
function sampleTrackValue(track, t2) {
|
|
10088
|
+
return requireWasm().sample_timeline_track(JSON.stringify(track), t2);
|
|
10141
10089
|
}
|
|
10142
10090
|
function sampleSequence(doc, timelineOverride) {
|
|
10143
10091
|
const timeline = timelineOverride ?? doc.timeline;
|
|
10144
10092
|
if (!timeline)
|
|
10145
10093
|
return [];
|
|
10146
|
-
const
|
|
10147
|
-
|
|
10148
|
-
const tracks = timeline.tracks ?? [];
|
|
10149
|
-
const shots = timeline.camera ?? [];
|
|
10150
|
-
const hasParamTracks2 = tracks.some((tr) => tr.target.type === "Parameter");
|
|
10151
|
-
const frames = [];
|
|
10152
|
-
let prevParams;
|
|
10153
|
-
let prevPose = { ...DEFAULT_POSE };
|
|
10154
|
-
for (let index = 0; index < frameCount; index++) {
|
|
10155
|
-
const t2 = index / fps;
|
|
10156
|
-
const params = {};
|
|
10157
|
-
const joints = {};
|
|
10158
|
-
const visibility = {};
|
|
10159
|
-
let explode = 0;
|
|
10160
|
-
for (const track of tracks) {
|
|
10161
|
-
const value = sampleTrackValue(track, t2);
|
|
10162
|
-
const target = track.target;
|
|
10163
|
-
switch (target.type) {
|
|
10164
|
-
case "Parameter":
|
|
10165
|
-
params[target.name] = value;
|
|
10166
|
-
break;
|
|
10167
|
-
case "Joint":
|
|
10168
|
-
joints[target.jointId] = value;
|
|
10169
|
-
break;
|
|
10170
|
-
case "Visibility":
|
|
10171
|
-
visibility[target.instanceId] = value > 0.5;
|
|
10172
|
-
break;
|
|
10173
|
-
case "Explode":
|
|
10174
|
-
explode = value;
|
|
10175
|
-
break;
|
|
10176
|
-
}
|
|
10177
|
-
}
|
|
10178
|
-
const camera = cameraPoseAt(shots, t2, prevPose);
|
|
10179
|
-
prevPose = camera;
|
|
10180
|
-
const geometryDirty = prevParams === void 0 ? hasParamTracks2 : Object.keys(params).some((name) => params[name] !== prevParams[name]);
|
|
10181
|
-
prevParams = params;
|
|
10182
|
-
frames.push({
|
|
10183
|
-
index,
|
|
10184
|
-
t: t2,
|
|
10185
|
-
params,
|
|
10186
|
-
joints,
|
|
10187
|
-
visibility,
|
|
10188
|
-
explode,
|
|
10189
|
-
camera,
|
|
10190
|
-
geometryDirty
|
|
10191
|
-
});
|
|
10192
|
-
}
|
|
10193
|
-
return frames;
|
|
10094
|
+
const json = requireWasm().sample_timeline_sequence(JSON.stringify(timeline));
|
|
10095
|
+
return JSON.parse(json);
|
|
10194
10096
|
}
|
|
10195
10097
|
function poseDocument(doc, frame) {
|
|
10196
10098
|
const posed = structuredClone(doc);
|
|
@@ -10212,11 +10114,10 @@ function poseDocument(doc, frame) {
|
|
|
10212
10114
|
}
|
|
10213
10115
|
return posed;
|
|
10214
10116
|
}
|
|
10215
|
-
var DEFAULT_POSE;
|
|
10216
10117
|
var init_sequence = __esm({
|
|
10217
10118
|
"../engine/dist/sequence.js"() {
|
|
10218
10119
|
"use strict";
|
|
10219
|
-
|
|
10120
|
+
init_wasm_singleton();
|
|
10220
10121
|
}
|
|
10221
10122
|
});
|
|
10222
10123
|
|
|
@@ -41632,6 +41533,24 @@ var init_CHANGELOG = __esm({
|
|
|
41632
41533
|
CHANGELOG_default = {
|
|
41633
41534
|
$schema: "./changelog.schema.json",
|
|
41634
41535
|
entries: [
|
|
41536
|
+
{
|
|
41537
|
+
id: "2026-07-24-router-keepout-edge-clearance",
|
|
41538
|
+
version: "0.9.4",
|
|
41539
|
+
date: "2026-07-24",
|
|
41540
|
+
category: "fix",
|
|
41541
|
+
title: "Autorouter respects keepouts and board-edge clearance",
|
|
41542
|
+
summary: "route_nets' legality oracle now indexes no-tracks keepouts and the board outline (incl. cutouts), so routes never cross keepouts or hug the edge inside edge_clearance; multi-round negotiation can no longer finish below the single-round baseline.",
|
|
41543
|
+
features: [
|
|
41544
|
+
"pcb",
|
|
41545
|
+
"autorouter",
|
|
41546
|
+
"drc"
|
|
41547
|
+
],
|
|
41548
|
+
mcpTools: [
|
|
41549
|
+
"route_nets",
|
|
41550
|
+
"run_drc",
|
|
41551
|
+
"validate_for_fab"
|
|
41552
|
+
]
|
|
41553
|
+
},
|
|
41635
41554
|
{
|
|
41636
41555
|
id: "2026-07-23-router-post-route-legalization",
|
|
41637
41556
|
version: "0.9.4",
|
|
@@ -50968,83 +50887,26 @@ var init_store = __esm({
|
|
|
50968
50887
|
});
|
|
50969
50888
|
|
|
50970
50889
|
// src/tools/inspect.ts
|
|
50971
|
-
function signedVolumeOfTriangle(p1, p2, p3) {
|
|
50972
|
-
return (p1[0] * (p2[1] * p3[2] - p3[1] * p2[2]) - p2[0] * (p1[1] * p3[2] - p3[1] * p1[2]) + p3[0] * (p1[1] * p2[2] - p2[1] * p1[2])) / 6;
|
|
50973
|
-
}
|
|
50974
|
-
function triangleArea2(p1, p2, p3) {
|
|
50975
|
-
const ax = p2[0] - p1[0];
|
|
50976
|
-
const ay = p2[1] - p1[1];
|
|
50977
|
-
const az = p2[2] - p1[2];
|
|
50978
|
-
const bx = p3[0] - p1[0];
|
|
50979
|
-
const by = p3[1] - p1[1];
|
|
50980
|
-
const bz = p3[2] - p1[2];
|
|
50981
|
-
const cx = ay * bz - az * by;
|
|
50982
|
-
const cy = az * bx - ax * bz;
|
|
50983
|
-
const cz = ax * by - ay * bx;
|
|
50984
|
-
return Math.sqrt(cx * cx + cy * cy + cz * cz) / 2;
|
|
50985
|
-
}
|
|
50986
|
-
function getVertex(mesh, index) {
|
|
50987
|
-
const i = index * 3;
|
|
50988
|
-
return [mesh.positions[i], mesh.positions[i + 1], mesh.positions[i + 2]];
|
|
50989
|
-
}
|
|
50990
50890
|
function computeMeshProperties(mesh) {
|
|
50991
|
-
const
|
|
50992
|
-
|
|
50993
|
-
|
|
50994
|
-
let cx = 0, cy = 0, cz = 0;
|
|
50995
|
-
let acx = 0, acy = 0, acz = 0;
|
|
50996
|
-
const bbox = {
|
|
50997
|
-
min: { x: Infinity, y: Infinity, z: Infinity },
|
|
50998
|
-
max: { x: -Infinity, y: -Infinity, z: -Infinity }
|
|
50999
|
-
};
|
|
51000
|
-
for (let t2 = 0; t2 < numTriangles; t2++) {
|
|
51001
|
-
const i0 = mesh.indices[t2 * 3];
|
|
51002
|
-
const i1 = mesh.indices[t2 * 3 + 1];
|
|
51003
|
-
const i2 = mesh.indices[t2 * 3 + 2];
|
|
51004
|
-
const p1 = getVertex(mesh, i0);
|
|
51005
|
-
const p2 = getVertex(mesh, i1);
|
|
51006
|
-
const p3 = getVertex(mesh, i2);
|
|
51007
|
-
const v = signedVolumeOfTriangle(p1, p2, p3);
|
|
51008
|
-
volume += v;
|
|
51009
|
-
const a = triangleArea2(p1, p2, p3);
|
|
51010
|
-
area += a;
|
|
51011
|
-
cx += v * (p1[0] + p2[0] + p3[0]) / 4;
|
|
51012
|
-
cy += v * (p1[1] + p2[1] + p3[1]) / 4;
|
|
51013
|
-
cz += v * (p1[2] + p2[2] + p3[2]) / 4;
|
|
51014
|
-
acx += a * (p1[0] + p2[0] + p3[0]) / 3;
|
|
51015
|
-
acy += a * (p1[1] + p2[1] + p3[1]) / 3;
|
|
51016
|
-
acz += a * (p1[2] + p2[2] + p3[2]) / 3;
|
|
51017
|
-
for (const p of [p1, p2, p3]) {
|
|
51018
|
-
bbox.min.x = Math.min(bbox.min.x, p[0]);
|
|
51019
|
-
bbox.min.y = Math.min(bbox.min.y, p[1]);
|
|
51020
|
-
bbox.min.z = Math.min(bbox.min.z, p[2]);
|
|
51021
|
-
bbox.max.x = Math.max(bbox.max.x, p[0]);
|
|
51022
|
-
bbox.max.y = Math.max(bbox.max.y, p[1]);
|
|
51023
|
-
bbox.max.z = Math.max(bbox.max.z, p[2]);
|
|
51024
|
-
}
|
|
51025
|
-
}
|
|
51026
|
-
const absVolume = Math.abs(volume);
|
|
51027
|
-
let centroid = null;
|
|
51028
|
-
if (absVolume > 1e-10) {
|
|
51029
|
-
centroid = { x: cx / volume, y: cy / volume, z: cz / volume };
|
|
51030
|
-
const eps = 1e-9 * Math.max(
|
|
51031
|
-
bbox.max.x - bbox.min.x,
|
|
51032
|
-
bbox.max.y - bbox.min.y,
|
|
51033
|
-
bbox.max.z - bbox.min.z,
|
|
51034
|
-
1
|
|
51035
|
-
);
|
|
51036
|
-
const inBbox = centroid.x >= bbox.min.x - eps && centroid.x <= bbox.max.x + eps && centroid.y >= bbox.min.y - eps && centroid.y <= bbox.max.y + eps && centroid.z >= bbox.min.z - eps && centroid.z <= bbox.max.z + eps;
|
|
51037
|
-
if (!inBbox) centroid = null;
|
|
50891
|
+
const wasm2 = getKernelWasmSync();
|
|
50892
|
+
if (!wasm2) {
|
|
50893
|
+
throw new Error("kernel WASM is not initialized \u2014 call Engine.init first");
|
|
51038
50894
|
}
|
|
51039
|
-
if (
|
|
51040
|
-
|
|
50895
|
+
if (typeof wasm2.computeMeshProperties !== "function") {
|
|
50896
|
+
throw new Error(
|
|
50897
|
+
"computeMeshProperties is not in this kernel build \u2014 rebuild vcad-kernel-wasm"
|
|
50898
|
+
);
|
|
51041
50899
|
}
|
|
50900
|
+
const props = wasm2.computeMeshProperties(
|
|
50901
|
+
mesh.positions instanceof Float32Array ? mesh.positions : new Float32Array(mesh.positions),
|
|
50902
|
+
mesh.indices instanceof Uint32Array ? mesh.indices : new Uint32Array(mesh.indices)
|
|
50903
|
+
);
|
|
51042
50904
|
return {
|
|
51043
|
-
volume:
|
|
51044
|
-
area,
|
|
51045
|
-
bbox,
|
|
51046
|
-
centroid,
|
|
51047
|
-
triangles:
|
|
50905
|
+
volume: props.volume,
|
|
50906
|
+
area: props.area,
|
|
50907
|
+
bbox: props.bbox,
|
|
50908
|
+
centroid: props.centerOfMass,
|
|
50909
|
+
triangles: props.triangles
|
|
51048
50910
|
};
|
|
51049
50911
|
}
|
|
51050
50912
|
function computeInspection(ir, engine) {
|
|
@@ -53665,21 +53527,6 @@ var init_continue_doc = __esm({
|
|
|
53665
53527
|
});
|
|
53666
53528
|
|
|
53667
53529
|
// src/fabricate/geometry.ts
|
|
53668
|
-
function vertex(mesh, index) {
|
|
53669
|
-
const i = index * 3;
|
|
53670
|
-
return [mesh.positions[i], mesh.positions[i + 1], mesh.positions[i + 2]];
|
|
53671
|
-
}
|
|
53672
|
-
function signedTetVolume(p1, p2, p3) {
|
|
53673
|
-
return (p1[0] * (p2[1] * p3[2] - p3[1] * p2[2]) - p2[0] * (p1[1] * p3[2] - p3[1] * p1[2]) + p3[0] * (p1[1] * p2[2] - p2[1] * p1[2])) / 6;
|
|
53674
|
-
}
|
|
53675
|
-
function triArea(p1, p2, p3) {
|
|
53676
|
-
const ax = p2[0] - p1[0], ay = p2[1] - p1[1], az = p2[2] - p1[2];
|
|
53677
|
-
const bx = p3[0] - p1[0], by = p3[1] - p1[1], bz = p3[2] - p1[2];
|
|
53678
|
-
const cx = ay * bz - az * by;
|
|
53679
|
-
const cy = az * bx - ax * bz;
|
|
53680
|
-
const cz = ax * by - ay * bx;
|
|
53681
|
-
return Math.sqrt(cx * cx + cy * cy + cz * cz) / 2;
|
|
53682
|
-
}
|
|
53683
53530
|
function measureDocument(ir, engine) {
|
|
53684
53531
|
let scene;
|
|
53685
53532
|
try {
|
|
@@ -53693,21 +53540,15 @@ function measureDocument(ir, engine) {
|
|
|
53693
53540
|
const min = [Infinity, Infinity, Infinity];
|
|
53694
53541
|
const max = [-Infinity, -Infinity, -Infinity];
|
|
53695
53542
|
for (const part of scene.parts) {
|
|
53696
|
-
const
|
|
53697
|
-
|
|
53698
|
-
|
|
53699
|
-
|
|
53700
|
-
|
|
53701
|
-
|
|
53702
|
-
|
|
53703
|
-
|
|
53704
|
-
|
|
53705
|
-
for (let k = 0; k < 3; k++) {
|
|
53706
|
-
if (p[k] < min[k]) min[k] = p[k];
|
|
53707
|
-
if (p[k] > max[k]) max[k] = p[k];
|
|
53708
|
-
}
|
|
53709
|
-
}
|
|
53710
|
-
}
|
|
53543
|
+
const props = computeMeshProperties(part.mesh);
|
|
53544
|
+
volume += props.volume;
|
|
53545
|
+
area += props.area;
|
|
53546
|
+
min[0] = Math.min(min[0], props.bbox.min.x);
|
|
53547
|
+
min[1] = Math.min(min[1], props.bbox.min.y);
|
|
53548
|
+
min[2] = Math.min(min[2], props.bbox.min.z);
|
|
53549
|
+
max[0] = Math.max(max[0], props.bbox.max.x);
|
|
53550
|
+
max[1] = Math.max(max[1], props.bbox.max.y);
|
|
53551
|
+
max[2] = Math.max(max[2], props.bbox.max.z);
|
|
53711
53552
|
}
|
|
53712
53553
|
if (!Number.isFinite(min[0])) return { ...EMPTY };
|
|
53713
53554
|
const dx = max[0] - min[0];
|
|
@@ -53716,7 +53557,7 @@ function measureDocument(ir, engine) {
|
|
|
53716
53557
|
return {
|
|
53717
53558
|
ok: true,
|
|
53718
53559
|
parts: scene.parts.length,
|
|
53719
|
-
volume_mm3:
|
|
53560
|
+
volume_mm3: volume,
|
|
53720
53561
|
surface_area_mm2: area,
|
|
53721
53562
|
footprint_mm2: dx * dy,
|
|
53722
53563
|
max_dim_mm: Math.max(dx, dy, dz),
|
|
@@ -53727,6 +53568,7 @@ var EMPTY;
|
|
|
53727
53568
|
var init_geometry2 = __esm({
|
|
53728
53569
|
"src/fabricate/geometry.ts"() {
|
|
53729
53570
|
"use strict";
|
|
53571
|
+
init_inspect();
|
|
53730
53572
|
EMPTY = {
|
|
53731
53573
|
ok: false,
|
|
53732
53574
|
parts: 0,
|
|
@@ -77183,6 +77025,7 @@ ${issues.map((i) => i.track >= 0 ? `track[${i.track}]: ${i.problem}` : i.problem
|
|
|
77183
77025
|
);
|
|
77184
77026
|
}
|
|
77185
77027
|
doc.timeline = timeline;
|
|
77028
|
+
await getKernelWasm();
|
|
77186
77029
|
const frames = sampleSequence(doc);
|
|
77187
77030
|
return ok({
|
|
77188
77031
|
duration_s: timeline.durationS,
|
|
@@ -77453,6 +77296,7 @@ async function renderSequence(args, ctx) {
|
|
|
77453
77296
|
${issues.map((i) => i.problem).join("\n")}`
|
|
77454
77297
|
);
|
|
77455
77298
|
}
|
|
77299
|
+
await getKernelWasm();
|
|
77456
77300
|
const frames = sampleSequence(doc, timeline);
|
|
77457
77301
|
if (frames.length > MAX_FRAMES) {
|
|
77458
77302
|
return err2(
|
|
@@ -77637,6 +77481,7 @@ async function exportVideo(args, ctx) {
|
|
|
77637
77481
|
return err2(`timeline invalid:
|
|
77638
77482
|
${issues.map((i) => i.problem).join("\n")}`);
|
|
77639
77483
|
}
|
|
77484
|
+
await getKernelWasm();
|
|
77640
77485
|
const frames = sampleSequence(doc, timeline);
|
|
77641
77486
|
if (frames.length > MAX_FRAMES) {
|
|
77642
77487
|
return err2(
|
|
@@ -78903,8 +78748,8 @@ var init_server3 = __esm({
|
|
|
78903
78748
|
init_order_feed();
|
|
78904
78749
|
init_animate();
|
|
78905
78750
|
PKG_VERSION = (() => {
|
|
78906
|
-
if ("0.9.4-main.
|
|
78907
|
-
return "0.9.4-main.
|
|
78751
|
+
if ("0.9.4-main.9") {
|
|
78752
|
+
return "0.9.4-main.9";
|
|
78908
78753
|
}
|
|
78909
78754
|
try {
|
|
78910
78755
|
const req = createRequire2(import.meta.url);
|
|
@@ -78913,8 +78758,8 @@ var init_server3 = __esm({
|
|
|
78913
78758
|
return "0.0.0";
|
|
78914
78759
|
}
|
|
78915
78760
|
})();
|
|
78916
|
-
BUILD_SHA = "
|
|
78917
|
-
BUILD_TIME = "2026-07-
|
|
78761
|
+
BUILD_SHA = "c3ef1dae913d41fab4da6f029c6d6b347bb08395";
|
|
78762
|
+
BUILD_TIME = "2026-07-24T16:14:27.739Z";
|
|
78918
78763
|
SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
|
|
78919
78764
|
VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
|
|
78920
78765
|
INSTANCE_ID = randomUUID6().slice(0, 8);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vcad/mcp",
|
|
3
|
-
"version": "0.9.4-main.
|
|
3
|
+
"version": "0.9.4-main.9",
|
|
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-07-
|
|
22
|
+
"sha": "c3ef1dae913d41fab4da6f029c6d6b347bb08395",
|
|
23
|
+
"builtAt": "2026-07-24T16:14:27.739Z"
|
|
24
24
|
}
|
|
25
25
|
}
|
package/vcad_kernel_wasm_bg.wasm
CHANGED
|
Binary file
|