@vcad/mcp 0.9.4-main.40 → 0.9.4-main.42
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 +370 -76
- 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 5288a75b111969d99b834a3df1c88e017376b921 at 2026-07-26T14:46:06.764Z. Source: https://github.com/ecto/vcad
|
package/index.mjs
CHANGED
|
@@ -8996,6 +8996,38 @@ function costSheetMetalChain(chain, kernel2, rates, quantity2 = 1) {
|
|
|
8996
8996
|
}
|
|
8997
8997
|
return { breakdown: parsed.breakdown, rates: parsed.rates };
|
|
8998
8998
|
}
|
|
8999
|
+
function flattenSolidToSheetMetal(mesh, kernel2, options = {}) {
|
|
9000
|
+
if (!kernel2.flattenSolidToSheetMetal) {
|
|
9001
|
+
throw new Error("kernel.flattenSolidToSheetMetal not available \u2014 rebuild @vcad/kernel-wasm");
|
|
9002
|
+
}
|
|
9003
|
+
const request = {
|
|
9004
|
+
positions: Array.from(mesh.positions),
|
|
9005
|
+
indices: Array.from(mesh.indices),
|
|
9006
|
+
material: options.material,
|
|
9007
|
+
manualK: options.manualK,
|
|
9008
|
+
shopProfile: options.shopProfile,
|
|
9009
|
+
volumeTolFrac: options.volumeTolFrac,
|
|
9010
|
+
planeAngleTolDeg: options.planeAngleTolDeg,
|
|
9011
|
+
planeOffsetTol: options.planeOffsetTol
|
|
9012
|
+
};
|
|
9013
|
+
const parsed = JSON.parse(kernel2.flattenSolidToSheetMetal(JSON.stringify(request)));
|
|
9014
|
+
if (parsed.error) {
|
|
9015
|
+
throw new Error(parsed.error);
|
|
9016
|
+
}
|
|
9017
|
+
return {
|
|
9018
|
+
flatPattern: parsed.flat_pattern,
|
|
9019
|
+
model: parsed.model,
|
|
9020
|
+
dxf: parsed.dxf,
|
|
9021
|
+
violations: parsed.violations,
|
|
9022
|
+
thickness: parsed.thickness,
|
|
9023
|
+
panels: parsed.panels,
|
|
9024
|
+
bends: parsed.bends,
|
|
9025
|
+
solidVolumeMm3: parsed.solid_volume_mm3,
|
|
9026
|
+
recoveredVolumeMm3: parsed.recovered_volume_mm3,
|
|
9027
|
+
volumeErrorFrac: parsed.volume_error_frac,
|
|
9028
|
+
warnings: parsed.warnings ?? []
|
|
9029
|
+
};
|
|
9030
|
+
}
|
|
8999
9031
|
var DEFAULT_SHOP_PROFILE, DEFAULT_COST_RATES, DEFAULT_NESTING_PARAMS;
|
|
9000
9032
|
var init_sheet_metal = __esm({
|
|
9001
9033
|
"../engine/dist/sheet-metal.js"() {
|
|
@@ -11564,6 +11596,7 @@ var init_dist = __esm({
|
|
|
11564
11596
|
getSheetMetalBendTable: wasmModule7.getSheetMetalBendTable,
|
|
11565
11597
|
getSheetMetalShopCatalog: wasmModule7.getSheetMetalShopCatalog,
|
|
11566
11598
|
sheetMetalFoldedStep: wasmModule7.sheetMetalFoldedStep,
|
|
11599
|
+
flattenSolidToSheetMetal: wasmModule7.flattenSolidToSheetMetal,
|
|
11567
11600
|
mesh_clearance: wasmModule7.mesh_clearance,
|
|
11568
11601
|
topologyOptimizeBox: wasmModule7.topologyOptimizeBox,
|
|
11569
11602
|
topologyOptimizeMesh: wasmModule7.topologyOptimizeMesh,
|
|
@@ -12140,6 +12173,12 @@ var init_dist = __esm({
|
|
|
12140
12173
|
nestSheetMetalParts(parts, params) {
|
|
12141
12174
|
return nestSheetMetalParts2(parts, this.kernel, params);
|
|
12142
12175
|
}
|
|
12176
|
+
/** Recover a flat pattern (panels, bends, DXF) from a solid part's mesh —
|
|
12177
|
+
* the mechanical counterpart of `board_from_solid`. Throws when the solid
|
|
12178
|
+
* is not constant-thickness sheet. */
|
|
12179
|
+
flattenSolidToSheetMetal(mesh, options) {
|
|
12180
|
+
return flattenSolidToSheetMetal(mesh, this.kernel, options);
|
|
12181
|
+
}
|
|
12143
12182
|
/** Compute a feasible bend sequence (outermost-first) for the
|
|
12144
12183
|
* sheet-metal part in `doc`. Returns `null` if there is none. */
|
|
12145
12184
|
sheetMetalSequence(doc) {
|
|
@@ -41105,6 +41144,39 @@ var init_CHANGELOG = __esm({
|
|
|
41105
41144
|
"export_cad"
|
|
41106
41145
|
]
|
|
41107
41146
|
},
|
|
41147
|
+
{
|
|
41148
|
+
id: "2026-07-26-isometric-handedness",
|
|
41149
|
+
version: "0.9.4",
|
|
41150
|
+
date: "2026-07-26",
|
|
41151
|
+
category: "fix",
|
|
41152
|
+
title: "Isometric renders are no longer mirrored",
|
|
41153
|
+
summary: "The default isometric view used a left-handed screen basis, so every static render was a mirror image of the app viewport; it now matches.",
|
|
41154
|
+
features: [
|
|
41155
|
+
"rendering",
|
|
41156
|
+
"drafting"
|
|
41157
|
+
],
|
|
41158
|
+
mcpTools: [
|
|
41159
|
+
"render_view"
|
|
41160
|
+
]
|
|
41161
|
+
},
|
|
41162
|
+
{
|
|
41163
|
+
id: "2026-07-26-flat-pattern-from-solid",
|
|
41164
|
+
version: "0.9.4",
|
|
41165
|
+
date: "2026-07-26",
|
|
41166
|
+
category: "feat",
|
|
41167
|
+
title: "Flat patterns from parts you already modelled",
|
|
41168
|
+
summary: "flat_pattern_from_solid turns any constant-thickness solid into a fab-ready DXF plus a bend table \u2014 no sheet-metal re-authoring \u2014 batching a whole assembly into unique patterns x quantity.",
|
|
41169
|
+
features: [
|
|
41170
|
+
"sheet-metal",
|
|
41171
|
+
"manufacturing",
|
|
41172
|
+
"mcp"
|
|
41173
|
+
],
|
|
41174
|
+
mcpTools: [
|
|
41175
|
+
"flat_pattern_from_solid",
|
|
41176
|
+
"sheet_metal_nest",
|
|
41177
|
+
"sheet_metal_cost"
|
|
41178
|
+
]
|
|
41179
|
+
},
|
|
41108
41180
|
{
|
|
41109
41181
|
id: "2026-07-26-fastener-forms",
|
|
41110
41182
|
version: "0.9.4",
|
|
@@ -47931,13 +48003,13 @@ function computeIntegrity(doc, engine) {
|
|
|
47931
48003
|
cx += v * (p1[0] + p2[0] + p3[0]) / 4;
|
|
47932
48004
|
cy += v * (p1[1] + p2[1] + p3[1]) / 4;
|
|
47933
48005
|
cz += v * (p1[2] + p2[2] + p3[2]) / 4;
|
|
47934
|
-
for (const
|
|
47935
|
-
bbox.min.x = Math.min(bbox.min.x,
|
|
47936
|
-
bbox.min.y = Math.min(bbox.min.y,
|
|
47937
|
-
bbox.min.z = Math.min(bbox.min.z,
|
|
47938
|
-
bbox.max.x = Math.max(bbox.max.x,
|
|
47939
|
-
bbox.max.y = Math.max(bbox.max.y,
|
|
47940
|
-
bbox.max.z = Math.max(bbox.max.z,
|
|
48006
|
+
for (const q2 of [p1, p2, p3]) {
|
|
48007
|
+
bbox.min.x = Math.min(bbox.min.x, q2[0]);
|
|
48008
|
+
bbox.min.y = Math.min(bbox.min.y, q2[1]);
|
|
48009
|
+
bbox.min.z = Math.min(bbox.min.z, q2[2]);
|
|
48010
|
+
bbox.max.x = Math.max(bbox.max.x, q2[0]);
|
|
48011
|
+
bbox.max.y = Math.max(bbox.max.y, q2[1]);
|
|
48012
|
+
bbox.max.z = Math.max(bbox.max.z, q2[2]);
|
|
47941
48013
|
}
|
|
47942
48014
|
}
|
|
47943
48015
|
const label = labels[partIndex] ?? `part ${partIndex + 1}`;
|
|
@@ -50352,8 +50424,8 @@ function buildPartLabels(doc) {
|
|
|
50352
50424
|
});
|
|
50353
50425
|
}
|
|
50354
50426
|
function eulerXyzDegToQuat2(rotation) {
|
|
50355
|
-
const
|
|
50356
|
-
return [
|
|
50427
|
+
const q2 = exportKernel().eulerXyzDegToQuat(rotation.x, rotation.y, rotation.z);
|
|
50428
|
+
return [q2[0], q2[1], q2[2], q2[3]];
|
|
50357
50429
|
}
|
|
50358
50430
|
function buildGlb(inputMeshes, name, animation) {
|
|
50359
50431
|
let f32Len = 0;
|
|
@@ -51182,9 +51254,9 @@ var init_store = __esm({
|
|
|
51182
51254
|
async listOrders(userId, filter) {
|
|
51183
51255
|
try {
|
|
51184
51256
|
const limit = typeof filter.limit === "number" ? filter.limit : 50;
|
|
51185
|
-
let
|
|
51186
|
-
if (filter.status)
|
|
51187
|
-
const res = await fabricateFetch(this.url("orders",
|
|
51257
|
+
let q2 = `?user_id=eq.${encodeURIComponent(userId)}&order=created_at.desc&limit=${limit}`;
|
|
51258
|
+
if (filter.status) q2 += `&state=eq.${encodeURIComponent(filter.status)}`;
|
|
51259
|
+
const res = await fabricateFetch(this.url("orders", q2), {
|
|
51188
51260
|
method: "GET",
|
|
51189
51261
|
headers: this.headers()
|
|
51190
51262
|
});
|
|
@@ -51388,8 +51460,8 @@ var init_store = __esm({
|
|
|
51388
51460
|
* can retry before deciding what to say. Never throws. */
|
|
51389
51461
|
async postRow(table, row, onConflict) {
|
|
51390
51462
|
try {
|
|
51391
|
-
const
|
|
51392
|
-
const res = await fabricateFetch(this.url(table,
|
|
51463
|
+
const q2 = onConflict ? `?on_conflict=${onConflict}` : "";
|
|
51464
|
+
const res = await fabricateFetch(this.url(table, q2), {
|
|
51393
51465
|
method: "POST",
|
|
51394
51466
|
headers: this.headers({
|
|
51395
51467
|
Prefer: onConflict ? "resolution=merge-duplicates,return=minimal" : "return=minimal"
|
|
@@ -53435,6 +53507,7 @@ var init_tool_metadata = __esm({
|
|
|
53435
53507
|
// ── Sheet metal ────────────────────────────────────────────────────────
|
|
53436
53508
|
sheet_metal_create: { title: "Create Sheet Metal", annotations: RW },
|
|
53437
53509
|
sheet_metal_unfold: { title: "Unfold Sheet Metal", annotations: RO },
|
|
53510
|
+
flat_pattern_from_solid: { title: "Flat Pattern From Solid", annotations: RO },
|
|
53438
53511
|
sheet_metal_check: { title: "Check Sheet Metal", annotations: RO },
|
|
53439
53512
|
sheet_metal_materials: { title: "Sheet Metal Materials", annotations: RO },
|
|
53440
53513
|
sheet_metal_bend_table: { title: "Sheet Metal Bend Table", annotations: RO },
|
|
@@ -54750,34 +54823,34 @@ var init_broker = __esm({
|
|
|
54750
54823
|
);
|
|
54751
54824
|
const built = [];
|
|
54752
54825
|
for (const adapter of eligible) {
|
|
54753
|
-
let
|
|
54826
|
+
let q2;
|
|
54754
54827
|
try {
|
|
54755
|
-
|
|
54828
|
+
q2 = await adapter.quote(req);
|
|
54756
54829
|
} catch {
|
|
54757
|
-
|
|
54830
|
+
q2 = null;
|
|
54758
54831
|
}
|
|
54759
|
-
if (!
|
|
54832
|
+
if (!q2) continue;
|
|
54760
54833
|
const landed2 = estimateLandedCost({
|
|
54761
54834
|
region: adapter.region,
|
|
54762
54835
|
supportsDdp: adapter.supportsDdp
|
|
54763
54836
|
});
|
|
54764
|
-
const markedUp = applyMargin(
|
|
54837
|
+
const markedUp = applyMargin(q2.fab_cost_minor);
|
|
54765
54838
|
const totalMinor = markedUp + landed2.shipping_minor + landed2.duty_minor;
|
|
54766
|
-
const orderable = CONTRACTED_FABS.has(adapter.key) &&
|
|
54839
|
+
const orderable = CONTRACTED_FABS.has(adapter.key) && q2.pricing_basis === "binding";
|
|
54767
54840
|
built.push({
|
|
54768
|
-
fabCost:
|
|
54841
|
+
fabCost: q2.fab_cost_minor,
|
|
54769
54842
|
option: {
|
|
54770
54843
|
fab: adapter.key,
|
|
54771
54844
|
fab_label: adapter.label,
|
|
54772
54845
|
region: adapter.region,
|
|
54773
54846
|
unit_price_minor: Math.round(totalMinor / req.quantity),
|
|
54774
54847
|
total_minor: totalMinor,
|
|
54775
|
-
lead_time_days:
|
|
54776
|
-
in_spec:
|
|
54777
|
-
pricing_basis:
|
|
54848
|
+
lead_time_days: q2.lead_time_days,
|
|
54849
|
+
in_spec: q2.in_spec,
|
|
54850
|
+
pricing_basis: q2.pricing_basis,
|
|
54778
54851
|
supports_ddp: adapter.supportsDdp,
|
|
54779
54852
|
orderable,
|
|
54780
|
-
notes:
|
|
54853
|
+
notes: q2.notes
|
|
54781
54854
|
}
|
|
54782
54855
|
});
|
|
54783
54856
|
}
|
|
@@ -55967,7 +56040,7 @@ function measureGroups(engine, candidates, groupA, groupB) {
|
|
|
55967
56040
|
error: `No part with id or name ${missing.map((m) => `"${m}"`).join(", ")}. Available: ${available || "none"}`
|
|
55968
56041
|
};
|
|
55969
56042
|
}
|
|
55970
|
-
const overlap = a.parts.filter((p) => b.parts.some((
|
|
56043
|
+
const overlap = a.parts.filter((p) => b.parts.some((q2) => q2.id === p.id));
|
|
55971
56044
|
if (overlap.length > 0) {
|
|
55972
56045
|
return {
|
|
55973
56046
|
error: `Parts cannot appear in both groups: ${overlap.map((p) => p.id).join(", ")}`
|
|
@@ -60127,10 +60200,10 @@ function detectStaleNets(pcb) {
|
|
|
60127
60200
|
const onSegBody = (p, t2) => segDist(p, t2) <= t2.width / 2 + 0.02;
|
|
60128
60201
|
const anchored = (p, selfIdx, halfW) => {
|
|
60129
60202
|
if (pads.some(
|
|
60130
|
-
(
|
|
60203
|
+
(q2) => Math.hypot(p.x - q2.pos.x, p.y - q2.pos.y) <= q2.halfExtent + halfW + 0.02
|
|
60131
60204
|
))
|
|
60132
60205
|
return true;
|
|
60133
|
-
if (vias.some((
|
|
60206
|
+
if (vias.some((q2) => Math.hypot(p.x - q2.x, p.y - q2.y) <= halfW + 0.42)) return true;
|
|
60134
60207
|
for (let j = 0; j < traces.length; j++) {
|
|
60135
60208
|
if (j === selfIdx) continue;
|
|
60136
60209
|
if (segDist(p, traces[j]) <= traces[j].width / 2 + halfW + 0.02) return true;
|
|
@@ -61930,7 +62003,7 @@ function seedScan(residual, lo, hi, perDim = 12) {
|
|
|
61930
62003
|
const idx = new Array(n).fill(0);
|
|
61931
62004
|
const total = Math.pow(perDim, n);
|
|
61932
62005
|
for (let c = 0; c < total; c++) {
|
|
61933
|
-
const x = idx.map((
|
|
62006
|
+
const x = idx.map((q2, d) => lo[d] + (hi[d] - lo[d]) * q2 / (perDim - 1));
|
|
61934
62007
|
const cost = sumSq(residual(x));
|
|
61935
62008
|
if (cost < best.cost) {
|
|
61936
62009
|
best.cost = cost;
|
|
@@ -62471,7 +62544,7 @@ function calcRf(args) {
|
|
|
62471
62544
|
return fail2("topology must be 'series_rlc' or 'parallel_rlc'");
|
|
62472
62545
|
}
|
|
62473
62546
|
const f0 = 1 / (2 * Math.PI * Math.sqrt(l * c));
|
|
62474
|
-
const
|
|
62547
|
+
const q2 = topology === "series_rlc" ? r > 0 ? 1 / r * Math.sqrt(l / c) : Infinity : r * Math.sqrt(c / l);
|
|
62475
62548
|
const fStart = args.f_start_hz ?? f0 * 0.1;
|
|
62476
62549
|
const fStop = args.f_stop_hz ?? f0 * 10;
|
|
62477
62550
|
const points = Math.min(256, Math.max(3, Math.round(args.points ?? 21)));
|
|
@@ -62517,7 +62590,7 @@ function calcRf(args) {
|
|
|
62517
62590
|
success: true,
|
|
62518
62591
|
topology,
|
|
62519
62592
|
resonance_hz: Math.round(f0),
|
|
62520
|
-
q_factor: Number.isFinite(
|
|
62593
|
+
q_factor: Number.isFinite(q2) ? r3(q2) : 999999,
|
|
62521
62594
|
z_at_resonance_ohm: r3(mag(z0re, z0im)),
|
|
62522
62595
|
best_match: {
|
|
62523
62596
|
f_hz: best.f_hz,
|
|
@@ -62532,8 +62605,8 @@ function calcRf(args) {
|
|
|
62532
62605
|
l_henry: l,
|
|
62533
62606
|
c_farad: c
|
|
62534
62607
|
}),
|
|
62535
|
-
...Number.isFinite(
|
|
62536
|
-
emClaim("q_factor", r3(
|
|
62608
|
+
...Number.isFinite(q2) ? [
|
|
62609
|
+
emClaim("q_factor", r3(q2), "dimensionless", "rlc-analytic", {
|
|
62537
62610
|
topology,
|
|
62538
62611
|
r_ohm: r,
|
|
62539
62612
|
l_henry: l,
|
|
@@ -63112,17 +63185,17 @@ async function setBoardOutline(args) {
|
|
|
63112
63185
|
};
|
|
63113
63186
|
}
|
|
63114
63187
|
function scoreFootprintFamily(query, fam) {
|
|
63115
|
-
const
|
|
63116
|
-
if (!
|
|
63188
|
+
const q2 = query.toLowerCase().trim();
|
|
63189
|
+
if (!q2) return 0;
|
|
63117
63190
|
const cands = [fam.family, fam.label, ...fam.aliases].map((s) => s.toLowerCase());
|
|
63118
63191
|
let best = 0;
|
|
63119
63192
|
for (const c of cands) {
|
|
63120
|
-
if (c ===
|
|
63193
|
+
if (c === q2) {
|
|
63121
63194
|
best = Math.max(best, 1);
|
|
63122
|
-
} else if (c.includes(
|
|
63195
|
+
} else if (c.includes(q2) || q2.includes(c)) {
|
|
63123
63196
|
best = Math.max(best, 0.75);
|
|
63124
63197
|
} else {
|
|
63125
|
-
const sim = 1 - editDistance(
|
|
63198
|
+
const sim = 1 - editDistance(q2, c) / Math.max(q2.length, c.length);
|
|
63126
63199
|
best = Math.max(best, sim * 0.6);
|
|
63127
63200
|
}
|
|
63128
63201
|
}
|
|
@@ -64755,16 +64828,16 @@ function windSegClosestPt(a, b, c, d) {
|
|
|
64755
64828
|
};
|
|
64756
64829
|
let best = [a, clamp(a, c, d)];
|
|
64757
64830
|
let bestD = Infinity;
|
|
64758
|
-
for (const [p,
|
|
64831
|
+
for (const [p, q2] of [
|
|
64759
64832
|
[a, clamp(a, c, d)],
|
|
64760
64833
|
[b, clamp(b, c, d)],
|
|
64761
64834
|
[c, clamp(c, a, b)],
|
|
64762
64835
|
[d, clamp(d, a, b)]
|
|
64763
64836
|
]) {
|
|
64764
|
-
const dd = Math.hypot(p.x -
|
|
64837
|
+
const dd = Math.hypot(p.x - q2.x, p.y - q2.y);
|
|
64765
64838
|
if (dd < bestD) {
|
|
64766
64839
|
bestD = dd;
|
|
64767
|
-
best = [p,
|
|
64840
|
+
best = [p, q2];
|
|
64768
64841
|
}
|
|
64769
64842
|
}
|
|
64770
64843
|
return { x: (best[0].x + best[1].x) / 2, y: (best[0].y + best[1].y) / 2 };
|
|
@@ -65382,7 +65455,7 @@ function calcMotorInduction(args, polePairs, turnsPerPhase, windingFactor, inner
|
|
|
65382
65455
|
outer_radius_mm: outerR,
|
|
65383
65456
|
end_effect_factor: endEffect
|
|
65384
65457
|
};
|
|
65385
|
-
const claim2 = (
|
|
65458
|
+
const claim2 = (q2, v, unit) => emClaim(q2, sig6(v), unit, "thin-sheet-induction", inductionInputs);
|
|
65386
65459
|
return {
|
|
65387
65460
|
content: [
|
|
65388
65461
|
{
|
|
@@ -65810,11 +65883,11 @@ function polygonCentroid(poly) {
|
|
|
65810
65883
|
let cy = 0;
|
|
65811
65884
|
for (let i = 0; i < poly.length; i++) {
|
|
65812
65885
|
const p = poly[i];
|
|
65813
|
-
const
|
|
65814
|
-
const cross = p.x *
|
|
65886
|
+
const q2 = poly[(i + 1) % poly.length];
|
|
65887
|
+
const cross = p.x * q2.y - q2.x * p.y;
|
|
65815
65888
|
a += cross;
|
|
65816
|
-
cx += (p.x +
|
|
65817
|
-
cy += (p.y +
|
|
65889
|
+
cx += (p.x + q2.x) * cross;
|
|
65890
|
+
cy += (p.y + q2.y) * cross;
|
|
65818
65891
|
}
|
|
65819
65892
|
if (Math.abs(a) < 1e-12) {
|
|
65820
65893
|
const n = poly.length || 1;
|
|
@@ -74210,8 +74283,8 @@ function normaliseEngravings(raw) {
|
|
|
74210
74283
|
if (Array.isArray(p) && p.length >= 2)
|
|
74211
74284
|
return { x: Number(p[0]), y: Number(p[1]) };
|
|
74212
74285
|
if (p && typeof p === "object" && "x" in p && "y" in p) {
|
|
74213
|
-
const
|
|
74214
|
-
return { x: Number(
|
|
74286
|
+
const q2 = p;
|
|
74287
|
+
return { x: Number(q2.x), y: Number(q2.y) };
|
|
74215
74288
|
}
|
|
74216
74289
|
return null;
|
|
74217
74290
|
}).filter((p) => p !== null) : [];
|
|
@@ -74876,6 +74949,222 @@ var init_sheet_metal2 = __esm({
|
|
|
74876
74949
|
}
|
|
74877
74950
|
});
|
|
74878
74951
|
|
|
74952
|
+
// src/tools/flat-pattern.ts
|
|
74953
|
+
function q(v, dp = 3) {
|
|
74954
|
+
const f = 10 ** dp;
|
|
74955
|
+
const r = Math.round(v * f) / f;
|
|
74956
|
+
return r === 0 ? 0 : r;
|
|
74957
|
+
}
|
|
74958
|
+
function ringSignature(ring) {
|
|
74959
|
+
const n = ring.length;
|
|
74960
|
+
if (n < 3) return "";
|
|
74961
|
+
const terms = [];
|
|
74962
|
+
for (let i = 0; i < n; i++) {
|
|
74963
|
+
const a = ring[(i + n - 1) % n];
|
|
74964
|
+
const b = ring[i];
|
|
74965
|
+
const c = ring[(i + 1) % n];
|
|
74966
|
+
const e1 = [b[0] - a[0], b[1] - a[1]];
|
|
74967
|
+
const e2 = [c[0] - b[0], c[1] - b[1]];
|
|
74968
|
+
const len = Math.hypot(e2[0], e2[1]);
|
|
74969
|
+
const turn = Math.atan2(
|
|
74970
|
+
e1[0] * e2[1] - e1[1] * e2[0],
|
|
74971
|
+
e1[0] * e2[0] + e1[1] * e2[1]
|
|
74972
|
+
);
|
|
74973
|
+
terms.push(`${q(len, 2)}:${q(turn, 3)}`);
|
|
74974
|
+
}
|
|
74975
|
+
let best = null;
|
|
74976
|
+
for (let r = 0; r < n; r++) {
|
|
74977
|
+
const cand = terms.slice(r).concat(terms.slice(0, r)).join(",");
|
|
74978
|
+
if (best === null || cand < best) best = cand;
|
|
74979
|
+
}
|
|
74980
|
+
return best ?? "";
|
|
74981
|
+
}
|
|
74982
|
+
function patternSignature(r) {
|
|
74983
|
+
const sil = r.flatPattern.silhouette_2d ?? [];
|
|
74984
|
+
const outer = ringSignature(sil[0] ?? []);
|
|
74985
|
+
const holes = sil.slice(1).map((h) => ringSignature(h)).sort().join("|");
|
|
74986
|
+
const bends = r.bends.map((b) => `${q(b.angle_deg, 2)}/${q(b.radius, 3)}/${q(b.length, 2)}/${b.direction}`).sort().join("|");
|
|
74987
|
+
return `t${q(r.thickness)}|${outer}|H:${holes}|B:${bends}`;
|
|
74988
|
+
}
|
|
74989
|
+
function solidParts(doc, engine) {
|
|
74990
|
+
const scene = engine.evaluate(doc);
|
|
74991
|
+
const visibleRoots = doc.roots.filter((e) => e.visible !== false);
|
|
74992
|
+
const out = [];
|
|
74993
|
+
for (let i = 0; i < scene.parts.length && i < visibleRoots.length; i++) {
|
|
74994
|
+
const rootId = visibleRoots[i].root;
|
|
74995
|
+
const node = doc.nodes[String(rootId)];
|
|
74996
|
+
const mesh = scene.parts[i].mesh;
|
|
74997
|
+
if (!mesh || mesh.positions.length === 0) continue;
|
|
74998
|
+
out.push({ rootId, name: node?.name ?? void 0, mesh });
|
|
74999
|
+
}
|
|
75000
|
+
return out;
|
|
75001
|
+
}
|
|
75002
|
+
function flatPatternFromSolid(input, engine) {
|
|
75003
|
+
const a = input ?? {};
|
|
75004
|
+
const doc = getSession(String(a.document_id ?? ""));
|
|
75005
|
+
const includeDxf = a.include_dxf !== false;
|
|
75006
|
+
const options = {
|
|
75007
|
+
material: typeof a.material === "string" ? a.material : void 0,
|
|
75008
|
+
manualK: typeof a.manual_k === "number" ? a.manual_k : void 0,
|
|
75009
|
+
shopProfile: typeof a.shop_profile === "string" ? a.shop_profile : void 0,
|
|
75010
|
+
volumeTolFrac: typeof a.volume_tolerance === "number" ? a.volume_tolerance : void 0
|
|
75011
|
+
};
|
|
75012
|
+
const all = solidParts(doc, engine);
|
|
75013
|
+
if (all.length === 0) {
|
|
75014
|
+
throw new Error("Document has no solid parts to flatten");
|
|
75015
|
+
}
|
|
75016
|
+
const partId = a.part_id !== void 0 ? String(a.part_id) : void 0;
|
|
75017
|
+
const targets = partId ? all.filter((p) => String(p.rootId) === partId) : all;
|
|
75018
|
+
if (targets.length === 0) {
|
|
75019
|
+
throw new Error(
|
|
75020
|
+
`No part with id "${partId}". Available: ${all.map((c) => `${c.rootId}${c.name ? ` (${c.name})` : ""}`).join(", ")}`
|
|
75021
|
+
);
|
|
75022
|
+
}
|
|
75023
|
+
const groups = /* @__PURE__ */ new Map();
|
|
75024
|
+
const failed = [];
|
|
75025
|
+
for (const part of targets) {
|
|
75026
|
+
let r;
|
|
75027
|
+
try {
|
|
75028
|
+
r = engine.flattenSolidToSheetMetal(part.mesh, options);
|
|
75029
|
+
} catch (e) {
|
|
75030
|
+
failed.push({
|
|
75031
|
+
root_id: part.rootId,
|
|
75032
|
+
...part.name ? { name: part.name } : {},
|
|
75033
|
+
reason: e instanceof Error ? e.message : String(e)
|
|
75034
|
+
});
|
|
75035
|
+
continue;
|
|
75036
|
+
}
|
|
75037
|
+
const sig = patternSignature(r);
|
|
75038
|
+
const existing = groups.get(sig);
|
|
75039
|
+
if (existing) {
|
|
75040
|
+
existing.quantity += 1;
|
|
75041
|
+
existing.parts.push({
|
|
75042
|
+
root_id: part.rootId,
|
|
75043
|
+
...part.name ? { name: part.name } : {}
|
|
75044
|
+
});
|
|
75045
|
+
continue;
|
|
75046
|
+
}
|
|
75047
|
+
const [minX, minY, maxX, maxY] = r.flatPattern.bbox;
|
|
75048
|
+
const creases = r.flatPattern.creases ?? [];
|
|
75049
|
+
groups.set(sig, {
|
|
75050
|
+
pattern_id: `pattern-${groups.size + 1}`,
|
|
75051
|
+
quantity: 1,
|
|
75052
|
+
parts: [{ root_id: part.rootId, ...part.name ? { name: part.name } : {} }],
|
|
75053
|
+
thickness_mm: r.thickness,
|
|
75054
|
+
flat: {
|
|
75055
|
+
bbox: r.flatPattern.bbox,
|
|
75056
|
+
area_mm2: r.flatPattern.area_mm2,
|
|
75057
|
+
width_mm: Math.abs(maxX - minX),
|
|
75058
|
+
height_mm: Math.abs(maxY - minY)
|
|
75059
|
+
},
|
|
75060
|
+
panels: r.panels,
|
|
75061
|
+
bend_table: r.bends.map((b, i) => ({
|
|
75062
|
+
bend: b.bend,
|
|
75063
|
+
angle_deg: b.angle_deg,
|
|
75064
|
+
radius_mm: b.radius,
|
|
75065
|
+
length_mm: b.length,
|
|
75066
|
+
direction: b.direction,
|
|
75067
|
+
k_factor: b.k_factor,
|
|
75068
|
+
allowance_mm: b.angle_deg * Math.PI / 180 * (b.radius + b.k_factor * r.thickness),
|
|
75069
|
+
line: creases[i] ? creases[i].line : []
|
|
75070
|
+
})),
|
|
75071
|
+
verification: {
|
|
75072
|
+
solid_volume_mm3: r.solidVolumeMm3,
|
|
75073
|
+
recovered_volume_mm3: r.recoveredVolumeMm3,
|
|
75074
|
+
error_frac: r.volumeErrorFrac,
|
|
75075
|
+
status: "verified"
|
|
75076
|
+
},
|
|
75077
|
+
violations: r.violations,
|
|
75078
|
+
warnings: r.warnings,
|
|
75079
|
+
...includeDxf ? { dxf: r.dxf } : {}
|
|
75080
|
+
});
|
|
75081
|
+
}
|
|
75082
|
+
const patterns = [...groups.values()];
|
|
75083
|
+
if (patterns.length === 0) {
|
|
75084
|
+
throw new Error(
|
|
75085
|
+
`No part in this document is constant-thickness sheet. ${failed.map((f) => `${f.name ?? f.root_id}: ${f.reason}`).join("; ")}`
|
|
75086
|
+
);
|
|
75087
|
+
}
|
|
75088
|
+
const totalParts = patterns.reduce((s, p) => s + p.quantity, 0);
|
|
75089
|
+
const bends = patterns.reduce((s, p) => s + p.bend_table.length * p.quantity, 0);
|
|
75090
|
+
return {
|
|
75091
|
+
content: [
|
|
75092
|
+
{
|
|
75093
|
+
type: "text",
|
|
75094
|
+
text: JSON.stringify(
|
|
75095
|
+
{
|
|
75096
|
+
success: true,
|
|
75097
|
+
patterns,
|
|
75098
|
+
...failed.length > 0 ? { not_sheet_metal: failed } : {},
|
|
75099
|
+
nest_input: patterns.map((p) => ({
|
|
75100
|
+
name: p.pattern_id,
|
|
75101
|
+
width_mm: p.flat.width_mm,
|
|
75102
|
+
height_mm: p.flat.height_mm,
|
|
75103
|
+
quantity: p.quantity
|
|
75104
|
+
})),
|
|
75105
|
+
summary: `${patterns.length} unique pattern(s) covering ${totalParts} part(s), ${bends} bend(s) total${failed.length > 0 ? `; ${failed.length} part(s) are not sheet metal` : ""}. Every pattern is volume-verified against its solid.`,
|
|
75106
|
+
note: "Each DXF is a fab-ready merged silhouette in millimetres: one closed exterior polyline + hole loops on CUT, DASHED bend centerlines on BEND_UP/BEND_DOWN. DXF carries no bend angles \u2014 use `bend_table` (angle, inside radius, direction, allowance, crease line) when entering them in the fab's UI. Hand `nest_input` to sheet_metal_nest, then sheet_metal_cost, for a quote."
|
|
75107
|
+
},
|
|
75108
|
+
null,
|
|
75109
|
+
2
|
|
75110
|
+
)
|
|
75111
|
+
}
|
|
75112
|
+
]
|
|
75113
|
+
};
|
|
75114
|
+
}
|
|
75115
|
+
var flatPatternFromSolidSchema, toolDefs41;
|
|
75116
|
+
var init_flat_pattern = __esm({
|
|
75117
|
+
"src/tools/flat-pattern.ts"() {
|
|
75118
|
+
"use strict";
|
|
75119
|
+
init_session2();
|
|
75120
|
+
init_tool_def();
|
|
75121
|
+
flatPatternFromSolidSchema = {
|
|
75122
|
+
type: "object",
|
|
75123
|
+
properties: {
|
|
75124
|
+
document_id: {
|
|
75125
|
+
type: "string",
|
|
75126
|
+
description: "CAD session id (open_document / create_cad_loon) holding the solid(s)."
|
|
75127
|
+
},
|
|
75128
|
+
part_id: {
|
|
75129
|
+
type: "string",
|
|
75130
|
+
description: "Root id of a single part to flatten (from `read`). Omit to flatten every solid part in the document and group identical parts into one pattern \xD7 quantity."
|
|
75131
|
+
},
|
|
75132
|
+
material: {
|
|
75133
|
+
type: "string",
|
|
75134
|
+
description: 'Material key for K-factor lookup (e.g. "al-soft", "steel-mild"). Default "al-soft". See sheet_metal_materials.'
|
|
75135
|
+
},
|
|
75136
|
+
manual_k: {
|
|
75137
|
+
type: "number",
|
|
75138
|
+
description: "Override the K-factor for every recovered bend (skips the bend table)."
|
|
75139
|
+
},
|
|
75140
|
+
shop_profile: {
|
|
75141
|
+
type: "string",
|
|
75142
|
+
description: 'Fab-service catalog id (e.g. "sendcutsend") to run manufacturability against. Default: generic shop.'
|
|
75143
|
+
},
|
|
75144
|
+
volume_tolerance: {
|
|
75145
|
+
type: "number",
|
|
75146
|
+
description: "Max relative volume error the round-trip check accepts (default 0.02). Re-extruding the emitted profile by the detected thickness must reproduce the solid's volume; a mismatch fails the part."
|
|
75147
|
+
},
|
|
75148
|
+
include_dxf: {
|
|
75149
|
+
type: "boolean",
|
|
75150
|
+
description: "Include the DXF string per pattern. Default true."
|
|
75151
|
+
}
|
|
75152
|
+
},
|
|
75153
|
+
required: ["document_id"]
|
|
75154
|
+
};
|
|
75155
|
+
toolDefs41 = [
|
|
75156
|
+
{
|
|
75157
|
+
name: "flat_pattern_from_solid",
|
|
75158
|
+
pack: "sheet_metal",
|
|
75159
|
+
description: "Emit the flat pattern (DXF + bend table) for a part that was modelled as an ordinary solid \u2014 extruded sketch, boolean, imported STEP \u2014 with no sheet-metal authoring required. The mechanical counterpart of board_from_solid: it detects the constant-thickness walls and the cylindrical bends between them, returns the cut profile (outer boundary + every interior hole) with the detected thickness, and a bend table (line positions, angles, direction, allowance) for bent parts. Called without part_id it batches the whole document and groups identical parts into one pattern \xD7 quantity (mirror images stay separate). Fails closed: re-extruding the emitted profile by the detected thickness must reproduce the solid's volume, so a part that isn't really constant-thickness sheet is reported as such instead of yielding a wrong outline.",
|
|
75160
|
+
inputSchema: flatPatternFromSolidSchema,
|
|
75161
|
+
handler: (a, c) => flatPatternFromSolid(a, c.engine),
|
|
75162
|
+
behavior: behavior({ geometry: true })
|
|
75163
|
+
}
|
|
75164
|
+
];
|
|
75165
|
+
}
|
|
75166
|
+
});
|
|
75167
|
+
|
|
74879
75168
|
// src/tools/acoustics.ts
|
|
74880
75169
|
import { writeFileSync as writeFileSync5 } from "node:fs";
|
|
74881
75170
|
function lookupMaterial(engine, name) {
|
|
@@ -75044,7 +75333,7 @@ function simulateStrike(input, engine) {
|
|
|
75044
75333
|
}
|
|
75045
75334
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
75046
75335
|
}
|
|
75047
|
-
var cents, MATERIAL_ALIASES, simulateStrikeSchema, round23, round4,
|
|
75336
|
+
var cents, MATERIAL_ALIASES, simulateStrikeSchema, round23, round4, toolDefs42;
|
|
75048
75337
|
var init_acoustics = __esm({
|
|
75049
75338
|
"src/tools/acoustics.ts"() {
|
|
75050
75339
|
"use strict";
|
|
@@ -75118,7 +75407,7 @@ var init_acoustics = __esm({
|
|
|
75118
75407
|
};
|
|
75119
75408
|
round23 = (x) => Math.round(x * 100) / 100;
|
|
75120
75409
|
round4 = (x) => Math.round(x * 1e4) / 1e4;
|
|
75121
|
-
|
|
75410
|
+
toolDefs42 = [
|
|
75122
75411
|
{
|
|
75123
75412
|
name: "simulate_strike",
|
|
75124
75413
|
pack: "sheet_metal",
|
|
@@ -75269,7 +75558,7 @@ function importStep(input, engine) {
|
|
|
75269
75558
|
]
|
|
75270
75559
|
};
|
|
75271
75560
|
}
|
|
75272
|
-
var MAX_STEP_BYTES, importStepSchema,
|
|
75561
|
+
var MAX_STEP_BYTES, importStepSchema, toolDefs43;
|
|
75273
75562
|
var init_import = __esm({
|
|
75274
75563
|
"src/tools/import.ts"() {
|
|
75275
75564
|
"use strict";
|
|
@@ -75301,7 +75590,7 @@ var init_import = __esm({
|
|
|
75301
75590
|
}
|
|
75302
75591
|
}
|
|
75303
75592
|
};
|
|
75304
|
-
|
|
75593
|
+
toolDefs43 = [
|
|
75305
75594
|
{
|
|
75306
75595
|
name: "import_step",
|
|
75307
75596
|
pack: null,
|
|
@@ -75429,7 +75718,7 @@ function importEagle(_input) {
|
|
|
75429
75718
|
"Eagle (.brd) import is not yet supported. Export your board from Eagle as KiCad (File > Export > KiCad .kicad_pcb) and use import_kicad instead."
|
|
75430
75719
|
);
|
|
75431
75720
|
}
|
|
75432
|
-
var MAX_PCB_BYTES, importKicadSchema, importEagleSchema,
|
|
75721
|
+
var MAX_PCB_BYTES, importKicadSchema, importEagleSchema, toolDefs44;
|
|
75433
75722
|
var init_import_pcb = __esm({
|
|
75434
75723
|
"src/tools/import-pcb.ts"() {
|
|
75435
75724
|
"use strict";
|
|
@@ -75470,7 +75759,7 @@ var init_import_pcb = __esm({
|
|
|
75470
75759
|
}
|
|
75471
75760
|
}
|
|
75472
75761
|
};
|
|
75473
|
-
|
|
75762
|
+
toolDefs44 = [
|
|
75474
75763
|
{
|
|
75475
75764
|
name: "import_kicad",
|
|
75476
75765
|
pack: "ecad",
|
|
@@ -75566,7 +75855,7 @@ Compressed URL param: ${encoded.length} bytes`
|
|
|
75566
75855
|
]
|
|
75567
75856
|
};
|
|
75568
75857
|
}
|
|
75569
|
-
var openInBrowserSchema,
|
|
75858
|
+
var openInBrowserSchema, toolDefs45;
|
|
75570
75859
|
var init_share = __esm({
|
|
75571
75860
|
"src/tools/share.ts"() {
|
|
75572
75861
|
"use strict";
|
|
@@ -75590,7 +75879,7 @@ var init_share = __esm({
|
|
|
75590
75879
|
}
|
|
75591
75880
|
}
|
|
75592
75881
|
};
|
|
75593
|
-
|
|
75882
|
+
toolDefs45 = [
|
|
75594
75883
|
{
|
|
75595
75884
|
name: "open_in_browser",
|
|
75596
75885
|
pack: null,
|
|
@@ -76023,7 +76312,7 @@ function batchReset(input) {
|
|
|
76023
76312
|
};
|
|
76024
76313
|
}
|
|
76025
76314
|
}
|
|
76026
|
-
var simulations, MAX_TRAJECTORY, envRecords, batchGroups, INLINE_DOC_DESC, SESSION_DOC_DESC, createRobotEnvSchema, gymStepSchema, gymResetSchema, gymObserveSchema, gymCloseSchema, batchCreateEnvsSchema, batchStepSchema, batchResetSchema,
|
|
76315
|
+
var simulations, MAX_TRAJECTORY, envRecords, batchGroups, INLINE_DOC_DESC, SESSION_DOC_DESC, createRobotEnvSchema, gymStepSchema, gymResetSchema, gymObserveSchema, gymCloseSchema, batchCreateEnvsSchema, batchStepSchema, batchResetSchema, toolDefs46;
|
|
76027
76316
|
var init_gym = __esm({
|
|
76028
76317
|
"src/tools/gym.ts"() {
|
|
76029
76318
|
"use strict";
|
|
@@ -76185,7 +76474,7 @@ var init_gym = __esm({
|
|
|
76185
76474
|
},
|
|
76186
76475
|
required: ["batch_id"]
|
|
76187
76476
|
};
|
|
76188
|
-
|
|
76477
|
+
toolDefs46 = [
|
|
76189
76478
|
{
|
|
76190
76479
|
name: "create_robot_env",
|
|
76191
76480
|
pack: "physics",
|
|
@@ -76543,7 +76832,7 @@ async function renderMolecule(input) {
|
|
|
76543
76832
|
return fail(err7);
|
|
76544
76833
|
}
|
|
76545
76834
|
}
|
|
76546
|
-
var loadStructureSchema, inspectMoleculeSchema, configSchema, minimizeEnergySchema, mdRunSchema, designMaterialSchema, homogenizeMaterialSchema, renderMoleculeSchema, CPK, DEFAULT_RADIUS,
|
|
76835
|
+
var loadStructureSchema, inspectMoleculeSchema, configSchema, minimizeEnergySchema, mdRunSchema, designMaterialSchema, homogenizeMaterialSchema, renderMoleculeSchema, CPK, DEFAULT_RADIUS, toolDefs47;
|
|
76547
76836
|
var init_atoms2 = __esm({
|
|
76548
76837
|
"src/tools/atoms.ts"() {
|
|
76549
76838
|
"use strict";
|
|
@@ -76678,7 +76967,7 @@ var init_atoms2 = __esm({
|
|
|
76678
76967
|
S: 1.05,
|
|
76679
76968
|
P: 1.07
|
|
76680
76969
|
};
|
|
76681
|
-
|
|
76970
|
+
toolDefs47 = [
|
|
76682
76971
|
{
|
|
76683
76972
|
name: "load_structure",
|
|
76684
76973
|
pack: "atoms",
|
|
@@ -77886,7 +78175,7 @@ function errorResult(text) {
|
|
|
77886
78175
|
isError: true
|
|
77887
78176
|
};
|
|
77888
78177
|
}
|
|
77889
|
-
var recordSimulationSchema, SVG_SCALE2, MAX_STEPS, MIN_STEPS, DEFAULT_WIDTH_PX2, MIN_WIDTH_PX, MAX_WIDTH_PX, DEFAULT_FPS, MIN_FPS, MAX_FPS,
|
|
78178
|
+
var recordSimulationSchema, SVG_SCALE2, MAX_STEPS, MIN_STEPS, DEFAULT_WIDTH_PX2, MIN_WIDTH_PX, MAX_WIDTH_PX, DEFAULT_FPS, MIN_FPS, MAX_FPS, toolDefs48;
|
|
77890
78179
|
var init_record2 = __esm({
|
|
77891
78180
|
"src/tools/record.ts"() {
|
|
77892
78181
|
"use strict";
|
|
@@ -77950,7 +78239,7 @@ var init_record2 = __esm({
|
|
|
77950
78239
|
DEFAULT_FPS = 30;
|
|
77951
78240
|
MIN_FPS = 1;
|
|
77952
78241
|
MAX_FPS = 60;
|
|
77953
|
-
|
|
78242
|
+
toolDefs48 = [
|
|
77954
78243
|
{
|
|
77955
78244
|
name: "record_simulation",
|
|
77956
78245
|
pack: "physics",
|
|
@@ -78043,7 +78332,7 @@ ${text}`
|
|
|
78043
78332
|
]
|
|
78044
78333
|
};
|
|
78045
78334
|
}
|
|
78046
|
-
var getChangelogSchema,
|
|
78335
|
+
var getChangelogSchema, toolDefs49;
|
|
78047
78336
|
var init_changelog2 = __esm({
|
|
78048
78337
|
"src/tools/changelog.ts"() {
|
|
78049
78338
|
"use strict";
|
|
@@ -78083,7 +78372,7 @@ var init_changelog2 = __esm({
|
|
|
78083
78372
|
}
|
|
78084
78373
|
}
|
|
78085
78374
|
};
|
|
78086
|
-
|
|
78375
|
+
toolDefs49 = [
|
|
78087
78376
|
{
|
|
78088
78377
|
name: "get_changelog",
|
|
78089
78378
|
pack: null,
|
|
@@ -78232,7 +78521,7 @@ function getSimVersion(input) {
|
|
|
78232
78521
|
]
|
|
78233
78522
|
};
|
|
78234
78523
|
}
|
|
78235
|
-
var getSimReplaySchema, getSimVersionSchema,
|
|
78524
|
+
var getSimReplaySchema, getSimVersionSchema, toolDefs50;
|
|
78236
78525
|
var init_sim_replay = __esm({
|
|
78237
78526
|
"src/tools/sim-replay.ts"() {
|
|
78238
78527
|
"use strict";
|
|
@@ -78260,7 +78549,7 @@ var init_sim_replay = __esm({
|
|
|
78260
78549
|
},
|
|
78261
78550
|
required: ["env_id"]
|
|
78262
78551
|
};
|
|
78263
|
-
|
|
78552
|
+
toolDefs50 = [
|
|
78264
78553
|
{
|
|
78265
78554
|
name: "get_sim_replay",
|
|
78266
78555
|
pack: null,
|
|
@@ -78379,7 +78668,7 @@ async function getOrderFeed(input, store, user) {
|
|
|
78379
78668
|
version: version2
|
|
78380
78669
|
});
|
|
78381
78670
|
}
|
|
78382
|
-
var toUsd4, getOrderFeedSchema,
|
|
78671
|
+
var toUsd4, getOrderFeedSchema, toolDefs51;
|
|
78383
78672
|
var init_order_feed = __esm({
|
|
78384
78673
|
"src/tools/order-feed.ts"() {
|
|
78385
78674
|
"use strict";
|
|
@@ -78398,7 +78687,7 @@ var init_order_feed = __esm({
|
|
|
78398
78687
|
},
|
|
78399
78688
|
required: ["document_id"]
|
|
78400
78689
|
};
|
|
78401
|
-
|
|
78690
|
+
toolDefs51 = [
|
|
78402
78691
|
{
|
|
78403
78692
|
name: "get_order_feed",
|
|
78404
78693
|
pack: "fabricate",
|
|
@@ -79300,7 +79589,7 @@ ${issues.map((i) => i.problem).join("\n")}`
|
|
|
79300
79589
|
note: "Rollout compiled to the document timeline. Preview with render_sequence, ship with export_video."
|
|
79301
79590
|
});
|
|
79302
79591
|
}
|
|
79303
|
-
var MAX_FRAMES, MAX_GEO_SAMPLES, MAX_VERIFY_FRAMES, INLINE_GLB_MAX_BASE64, DEFAULT_WIDTH_PX3, MIN_WIDTH_PX2, MAX_WIDTH_PX2, SVG_SCALE3, KEYFRAMES_DESCRIPTION, animateSchema, renderSequenceSchema, CAMERA_NODE, exportVideoSchema, ffmpegAvailable, timelineFromSimulationSchema,
|
|
79592
|
+
var MAX_FRAMES, MAX_GEO_SAMPLES, MAX_VERIFY_FRAMES, INLINE_GLB_MAX_BASE64, DEFAULT_WIDTH_PX3, MIN_WIDTH_PX2, MAX_WIDTH_PX2, SVG_SCALE3, KEYFRAMES_DESCRIPTION, animateSchema, renderSequenceSchema, CAMERA_NODE, exportVideoSchema, ffmpegAvailable, timelineFromSimulationSchema, toolDefs52;
|
|
79304
79593
|
var init_animate = __esm({
|
|
79305
79594
|
"src/tools/animate.ts"() {
|
|
79306
79595
|
"use strict";
|
|
@@ -79452,7 +79741,7 @@ var init_animate = __esm({
|
|
|
79452
79741
|
},
|
|
79453
79742
|
required: ["env_id", "document_id"]
|
|
79454
79743
|
};
|
|
79455
|
-
|
|
79744
|
+
toolDefs52 = [
|
|
79456
79745
|
{
|
|
79457
79746
|
name: "animate",
|
|
79458
79747
|
pack: null,
|
|
@@ -79514,6 +79803,7 @@ function buildInstructions(kernelPrompt) {
|
|
|
79514
79803
|
"- See your work with `render_view` (isometric PNG); measure with `inspect_cad` (whole-document volume, area, bbox, center of mass), `inspect_part` / `describe_scene` (per-part bbox, size, center, material), and `measure` (min distance/overlap between two parts, or one part's bbox+volume+center of mass).",
|
|
79515
79804
|
"- Ship with `export_cad` (STL/GLB/STEP) or `open_in_browser` (vcad.io deep link).",
|
|
79516
79805
|
"- Fix in place: when geometry is wrong, prefer `update` on the offending node over deleting parts and starting over.",
|
|
79806
|
+
"- Cut sheet parts you already modelled: `flat_pattern_from_solid` turns any constant-thickness solid \u2014 extruded sketch, boolean, imported STEP \u2014 into a fab-ready DXF plus a bend table, no sheet-metal re-authoring. Called without `part_id` it does the whole document and groups identical parts into one pattern \xD7 quantity; hand its `nest_input` to `sheet_metal_nest`, then `sheet_metal_cost`, for a quote. It fails closed: the emitted profile re-extruded by the detected thickness must reproduce the solid's volume.",
|
|
79517
79807
|
"- Deliver the project bill of materials with `bom_create` \u2192 `bom_export` (markdown/CSV/JSON with landed-cost totals): link quote_manufacturing quotes on manufactured lines, and source COTS hardware (bearings, shafts, standoffs, screws, ferrite magnets) with `search_mechanical_parts`. All BOM prices are estimates and flagged as such.",
|
|
79518
79808
|
"",
|
|
79519
79809
|
"",
|
|
@@ -80130,6 +80420,7 @@ async function createServer(existingEngine, context = { user: null }) {
|
|
|
80130
80420
|
function slimPreviewForInlineUi(result, docId, toolName, clientHasInlineUi) {
|
|
80131
80421
|
if (!clientHasInlineUi) return;
|
|
80132
80422
|
if (toolName === "sheet_metal_unfold") return;
|
|
80423
|
+
if (toolName === "flat_pattern_from_solid") return;
|
|
80133
80424
|
if (PURE_JSON_RESULT_TOOLS.has(toolName)) return;
|
|
80134
80425
|
const alwaysSlim = toolName === "create_cad_loon";
|
|
80135
80426
|
const totalChars = result.content.reduce(
|
|
@@ -80368,6 +80659,7 @@ var init_server3 = __esm({
|
|
|
80368
80659
|
init_loon_macros();
|
|
80369
80660
|
init_dfm2();
|
|
80370
80661
|
init_sheet_metal2();
|
|
80662
|
+
init_flat_pattern();
|
|
80371
80663
|
init_acoustics();
|
|
80372
80664
|
init_import();
|
|
80373
80665
|
init_import_pcb();
|
|
@@ -80382,8 +80674,8 @@ var init_server3 = __esm({
|
|
|
80382
80674
|
init_order_feed();
|
|
80383
80675
|
init_animate();
|
|
80384
80676
|
PKG_VERSION = (() => {
|
|
80385
|
-
if ("0.9.4-main.
|
|
80386
|
-
return "0.9.4-main.
|
|
80677
|
+
if ("0.9.4-main.42") {
|
|
80678
|
+
return "0.9.4-main.42";
|
|
80387
80679
|
}
|
|
80388
80680
|
try {
|
|
80389
80681
|
const req = createRequire2(import.meta.url);
|
|
@@ -80392,8 +80684,8 @@ var init_server3 = __esm({
|
|
|
80392
80684
|
return "0.0.0";
|
|
80393
80685
|
}
|
|
80394
80686
|
})();
|
|
80395
|
-
BUILD_SHA = "
|
|
80396
|
-
BUILD_TIME = "2026-07-26T14:
|
|
80687
|
+
BUILD_SHA = "5288a75b111969d99b834a3df1c88e017376b921";
|
|
80688
|
+
BUILD_TIME = "2026-07-26T14:46:06.764Z";
|
|
80397
80689
|
SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
|
|
80398
80690
|
VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
|
|
80399
80691
|
INSTANCE_ID = randomUUID6().slice(0, 8);
|
|
@@ -80466,11 +80758,12 @@ var init_server3 = __esm({
|
|
|
80466
80758
|
...toolDefs46,
|
|
80467
80759
|
...toolDefs47,
|
|
80468
80760
|
...toolDefs48,
|
|
80761
|
+
...toolDefs49,
|
|
80469
80762
|
...toolDefs20,
|
|
80470
80763
|
...toolDefs19,
|
|
80471
|
-
...toolDefs49,
|
|
80472
80764
|
...toolDefs50,
|
|
80473
|
-
...toolDefs51
|
|
80765
|
+
...toolDefs51,
|
|
80766
|
+
...toolDefs52
|
|
80474
80767
|
];
|
|
80475
80768
|
LIST_TOOL_ORDER = [
|
|
80476
80769
|
// ── Session lifecycle ──────────────────────────────────────
|
|
@@ -80577,6 +80870,7 @@ var init_server3 = __esm({
|
|
|
80577
80870
|
"sheet_metal_suggest_fix",
|
|
80578
80871
|
"sheet_metal_sequence",
|
|
80579
80872
|
"sheet_metal_nest",
|
|
80873
|
+
"flat_pattern_from_solid",
|
|
80580
80874
|
"simulate_strike",
|
|
80581
80875
|
// ── Import + share ─────────────────────────────────────────
|
|
80582
80876
|
"import_step",
|
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.42",
|
|
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-26T14:
|
|
22
|
+
"sha": "5288a75b111969d99b834a3df1c88e017376b921",
|
|
23
|
+
"builtAt": "2026-07-26T14:46:06.764Z"
|
|
24
24
|
}
|
|
25
25
|
}
|