@vcad/mcp 0.9.4-main.26 → 0.9.4-main.28
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 +300 -37
- 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 301abfbddc2a05c29fabc9f655031e554eab1d04 at 2026-07-26T00:32:29.039Z. Source: https://github.com/ecto/vcad
|
package/index.mjs
CHANGED
|
@@ -10367,6 +10367,20 @@ async function airgapFluxDensity(spec) {
|
|
|
10367
10367
|
return null;
|
|
10368
10368
|
}
|
|
10369
10369
|
}
|
|
10370
|
+
async function airgapSolve(spec) {
|
|
10371
|
+
const wasm3 = await loadEcadWasm();
|
|
10372
|
+
if (!wasm3)
|
|
10373
|
+
return null;
|
|
10374
|
+
try {
|
|
10375
|
+
const fn = wasm3.ecadAirgapSolve;
|
|
10376
|
+
if (typeof fn !== "function")
|
|
10377
|
+
return null;
|
|
10378
|
+
return fn(JSON.stringify(spec));
|
|
10379
|
+
} catch (e) {
|
|
10380
|
+
console.warn("[ECAD] airgapSolve failed:", e);
|
|
10381
|
+
return null;
|
|
10382
|
+
}
|
|
10383
|
+
}
|
|
10370
10384
|
async function generateNetlist(sheet) {
|
|
10371
10385
|
const wasm3 = await loadEcadWasm();
|
|
10372
10386
|
if (!wasm3)
|
|
@@ -11229,6 +11243,7 @@ __export(dist_exports, {
|
|
|
11229
11243
|
PhysicsEnv: () => PhysicsEnv,
|
|
11230
11244
|
SolidCache: () => SolidCache,
|
|
11231
11245
|
airgapFluxDensity: () => airgapFluxDensity,
|
|
11246
|
+
airgapSolve: () => airgapSolve,
|
|
11232
11247
|
applyForwardKinematics: () => applyForwardKinematics,
|
|
11233
11248
|
buildMoleculeReceipt: () => buildReceipt2,
|
|
11234
11249
|
buildPartDocument: () => buildPartDocument,
|
|
@@ -40898,6 +40913,26 @@ var init_CHANGELOG = __esm({
|
|
|
40898
40913
|
"sheet_metal_create"
|
|
40899
40914
|
]
|
|
40900
40915
|
},
|
|
40916
|
+
{
|
|
40917
|
+
id: "2026-07-25-shell-and-booleans-on-filleted-solids",
|
|
40918
|
+
version: "0.9.4",
|
|
40919
|
+
date: "2026-07-25",
|
|
40920
|
+
category: "fix",
|
|
40921
|
+
title: "Shelling and cutting a filleted solid no longer leaks",
|
|
40922
|
+
summary: "Hollowing a filleted part scattered its cavity and a pocket cut collapsed the neighboring blends flat; both are now exact and watertight, and the kernel is reproducible run to run.",
|
|
40923
|
+
features: [
|
|
40924
|
+
"kernel",
|
|
40925
|
+
"fillet",
|
|
40926
|
+
"shell",
|
|
40927
|
+
"booleans",
|
|
40928
|
+
"tessellation"
|
|
40929
|
+
],
|
|
40930
|
+
mcpTools: [
|
|
40931
|
+
"create_cad_loon",
|
|
40932
|
+
"inspect_cad",
|
|
40933
|
+
"export_cad"
|
|
40934
|
+
]
|
|
40935
|
+
},
|
|
40901
40936
|
{
|
|
40902
40937
|
id: "2026-07-25-router-window-repair-commit-parity",
|
|
40903
40938
|
version: "0.9.4",
|
|
@@ -41079,6 +41114,28 @@ var init_CHANGELOG = __esm({
|
|
|
41079
41114
|
"build_receipt"
|
|
41080
41115
|
]
|
|
41081
41116
|
},
|
|
41117
|
+
{
|
|
41118
|
+
id: "2026-07-25-gym-session-first",
|
|
41119
|
+
version: "0.9.4",
|
|
41120
|
+
date: "2026-07-25",
|
|
41121
|
+
category: "feat",
|
|
41122
|
+
title: "Sim from a session id, not a re-sent document",
|
|
41123
|
+
summary: "create_robot_env and batch_create_envs now take document_id, binding the env to the live session instead of a re-sent IR copy, and observations come back keyed by joint / instance id.",
|
|
41124
|
+
features: [
|
|
41125
|
+
"physics",
|
|
41126
|
+
"simulation",
|
|
41127
|
+
"mcp"
|
|
41128
|
+
],
|
|
41129
|
+
mcpTools: [
|
|
41130
|
+
"create_robot_env",
|
|
41131
|
+
"batch_create_envs",
|
|
41132
|
+
"gym_reset",
|
|
41133
|
+
"gym_observe",
|
|
41134
|
+
"gym_step",
|
|
41135
|
+
"batch_reset",
|
|
41136
|
+
"batch_step"
|
|
41137
|
+
]
|
|
41138
|
+
},
|
|
41082
41139
|
{
|
|
41083
41140
|
id: "2026-07-25-gerber-pad-rotation",
|
|
41084
41141
|
version: "0.9.4",
|
|
@@ -41147,6 +41204,22 @@ var init_CHANGELOG = __esm({
|
|
|
41147
41204
|
"quote_manufacturing"
|
|
41148
41205
|
]
|
|
41149
41206
|
},
|
|
41207
|
+
{
|
|
41208
|
+
id: "2026-07-25-calc-motor-tooth-saturation",
|
|
41209
|
+
version: "0.9.4",
|
|
41210
|
+
date: "2026-07-25",
|
|
41211
|
+
category: "fix",
|
|
41212
|
+
title: "calc_motor sees saturating stator teeth",
|
|
41213
|
+
summary: "calc_motor's MEC model treated iron as linear and silently over-predicted Kt on machines whose teeth saturate; it now reports tooth flux, warns past the knee, and can solve the saturating network.",
|
|
41214
|
+
features: [
|
|
41215
|
+
"motor",
|
|
41216
|
+
"magnetics",
|
|
41217
|
+
"simulation"
|
|
41218
|
+
],
|
|
41219
|
+
mcpTools: [
|
|
41220
|
+
"calc_motor"
|
|
41221
|
+
]
|
|
41222
|
+
},
|
|
41150
41223
|
{
|
|
41151
41224
|
id: "2026-07-24-visionos-app",
|
|
41152
41225
|
version: "0.9.4",
|
|
@@ -64103,6 +64176,7 @@ async function calcMotor(args) {
|
|
|
64103
64176
|
let bGap = num2(args.airgap_flux_tesla);
|
|
64104
64177
|
let bGapSource = "supplied";
|
|
64105
64178
|
let magnetSpec;
|
|
64179
|
+
let solution = null;
|
|
64106
64180
|
let fringing;
|
|
64107
64181
|
if (!Number.isFinite(bGap)) {
|
|
64108
64182
|
const m = args.magnet ?? {};
|
|
@@ -64116,15 +64190,55 @@ async function calcMotor(args) {
|
|
|
64116
64190
|
gapAreaMm2: mnum(m.gap_area_mm2, 1),
|
|
64117
64191
|
ironMuRel: typeof m.iron_mu_rel === "number" ? m.iron_mu_rel : null,
|
|
64118
64192
|
ironPathMm: mnum(m.iron_path_mm, 0),
|
|
64119
|
-
ironAreaMm2: mnum(m.iron_area_mm2, 1)
|
|
64120
|
-
|
|
64121
|
-
|
|
64122
|
-
if (
|
|
64123
|
-
|
|
64124
|
-
|
|
64125
|
-
);
|
|
64193
|
+
ironAreaMm2: mnum(m.iron_area_mm2, 1),
|
|
64194
|
+
ironJsT: typeof m.iron_js_t === "number" ? m.iron_js_t : null
|
|
64195
|
+
};
|
|
64196
|
+
if (m.slots !== void 0) {
|
|
64197
|
+
const slots = num2(m.slots);
|
|
64198
|
+
if (!(slots > 0)) return fail2("magnet.slots must be > 0");
|
|
64199
|
+
const meanR = Number.isFinite(num2(m.mean_radius_mm)) ? num2(m.mean_radius_mm) : (innerR + outerR) / 2;
|
|
64200
|
+
const pitch = 2 * Math.PI * meanR / slots;
|
|
64201
|
+
let toothW = num2(m.tooth_width_mm);
|
|
64202
|
+
if (!Number.isFinite(toothW)) {
|
|
64203
|
+
const frac = num2(m.tooth_fraction);
|
|
64204
|
+
if (!Number.isFinite(frac)) {
|
|
64205
|
+
return fail2("magnet.slots requires magnet.tooth_width_mm or magnet.tooth_fraction");
|
|
64206
|
+
}
|
|
64207
|
+
if (!(frac > 0 && frac <= 1)) return fail2("magnet.tooth_fraction must be in (0, 1]");
|
|
64208
|
+
toothW = frac * pitch;
|
|
64209
|
+
}
|
|
64210
|
+
if (!(toothW > 0)) return fail2("magnet.tooth_width_mm must be > 0");
|
|
64211
|
+
magnetSpec.teeth = {
|
|
64212
|
+
slots,
|
|
64213
|
+
toothWidthMm: toothW,
|
|
64214
|
+
meanRadiusMm: meanR,
|
|
64215
|
+
toothPathMm: mnum(m.tooth_path_mm, 0)
|
|
64216
|
+
};
|
|
64217
|
+
}
|
|
64218
|
+
solution = await airgapSolve(magnetSpec);
|
|
64219
|
+
if (solution == null) {
|
|
64220
|
+
const computed = await airgapFluxDensity(magnetSpec);
|
|
64221
|
+
if (computed == null) {
|
|
64222
|
+
return fail2(
|
|
64223
|
+
"air-gap flux is required: pass airgap_flux_tesla, or `magnet` params (ECAD WASM must be available to compute B_gap)."
|
|
64224
|
+
);
|
|
64225
|
+
}
|
|
64226
|
+
const t2 = magnetSpec.teeth;
|
|
64227
|
+
const k = t2 ? Math.max(1, 2 * Math.PI * t2.meanRadiusMm / t2.slots / t2.toothWidthMm) : null;
|
|
64228
|
+
solution = {
|
|
64229
|
+
bGapTesla: computed,
|
|
64230
|
+
bToothTesla: k == null ? null : computed * k,
|
|
64231
|
+
bIronTesla: null,
|
|
64232
|
+
toothConcentration: k,
|
|
64233
|
+
nonlinear: false,
|
|
64234
|
+
iterations: 0,
|
|
64235
|
+
converged: true,
|
|
64236
|
+
warnings: k != null && computed * k > SILICON_STEEL_KNEE_T ? [
|
|
64237
|
+
`tooth flux density ${(computed * k).toFixed(2)} T exceeds the ${SILICON_STEEL_KNEE_T.toFixed(2)} T knee \u2014 the LINEAR iron model over-predicts B_gap and Kt here; pass magnet.iron_js_t to solve the saturating network`
|
|
64238
|
+
] : []
|
|
64239
|
+
};
|
|
64126
64240
|
}
|
|
64127
|
-
bGap =
|
|
64241
|
+
bGap = solution.bGapTesla;
|
|
64128
64242
|
bGapSource = "computed";
|
|
64129
64243
|
if (m.pole_width_mm !== void 0) {
|
|
64130
64244
|
const poleW = num2(m.pole_width_mm);
|
|
@@ -64196,6 +64310,24 @@ async function calcMotor(args) {
|
|
|
64196
64310
|
})
|
|
64197
64311
|
);
|
|
64198
64312
|
}
|
|
64313
|
+
if (solution?.bToothTesla != null && magnetSpec.teeth) {
|
|
64314
|
+
claims.push(
|
|
64315
|
+
emClaim(
|
|
64316
|
+
"tooth_flux_density",
|
|
64317
|
+
r4(solution.bToothTesla),
|
|
64318
|
+
"T",
|
|
64319
|
+
solution.nonlinear ? "mec-saturating-iron" : "mec-tooth-concentration",
|
|
64320
|
+
{
|
|
64321
|
+
...mecInputs,
|
|
64322
|
+
slots: magnetSpec.teeth.slots,
|
|
64323
|
+
tooth_width_mm: r4(magnetSpec.teeth.toothWidthMm),
|
|
64324
|
+
mean_radius_mm: r4(magnetSpec.teeth.meanRadiusMm),
|
|
64325
|
+
tooth_concentration: r4(solution.toothConcentration ?? 1),
|
|
64326
|
+
...magnetSpec.ironJsT != null ? { iron_js_t: magnetSpec.ironJsT } : {}
|
|
64327
|
+
}
|
|
64328
|
+
)
|
|
64329
|
+
);
|
|
64330
|
+
}
|
|
64199
64331
|
}
|
|
64200
64332
|
return {
|
|
64201
64333
|
content: [
|
|
@@ -64211,6 +64343,16 @@ async function calcMotor(args) {
|
|
|
64211
64343
|
fringing_derate: r4(fringing.derate),
|
|
64212
64344
|
fringing_note: "Carter-like first-order derate: B under the pole drops by w/(w+2g) as flux fringes ~one gap length past each pole edge. Kt/Ke and the curve use the DERATED flux. Honest for pole width \u2273 2\xD7 gap; below that treat it as a lower bound."
|
|
64213
64345
|
} : {},
|
|
64346
|
+
...solution?.bToothTesla != null ? {
|
|
64347
|
+
tooth_flux_tesla: r4(solution.bToothTesla),
|
|
64348
|
+
tooth_concentration: r4(solution.toothConcentration ?? 1),
|
|
64349
|
+
// Fringing redistributes flux under the pole; it does not remove
|
|
64350
|
+
// it, so the tooth carries the RAW gap flux either way.
|
|
64351
|
+
tooth_note: "B_tooth = raw B_gap \xB7 (tooth pitch / tooth width) \u2014 the gap flux of a whole tooth pitch funnels into one tooth body. Compare against your steel's knee (~1.5-1.7 T for M19-class silicon steel)."
|
|
64352
|
+
} : {},
|
|
64353
|
+
...solution?.bIronTesla != null ? { yoke_flux_tesla: r4(solution.bIronTesla) } : {},
|
|
64354
|
+
...solution?.nonlinear ? { iron_model: "saturating (arctangent B-H)" } : {},
|
|
64355
|
+
...solution && solution.warnings.length > 0 ? { warnings: solution.warnings } : {},
|
|
64214
64356
|
winding_factor: windingFactor,
|
|
64215
64357
|
kt_nm_per_a: r4(perf.ktNmPerA),
|
|
64216
64358
|
ke_v_s_per_rad: r4(perf.keVSPerRad),
|
|
@@ -64221,7 +64363,7 @@ async function calcMotor(args) {
|
|
|
64221
64363
|
speed_rad_s: r4(p.speedRadS),
|
|
64222
64364
|
torque_nm: r4(p.torqueNm)
|
|
64223
64365
|
})),
|
|
64224
|
-
note:
|
|
64366
|
+
note: "First-order steady-state estimate (no slotting, no losses)" + (fringing ? "; pole-edge fringing derated via magnet.pole_width_mm" : "; pass magnet.pole_width_mm to derate for fringing") + (solution?.nonlinear ? "; iron solved with its saturating B-H law" : "; iron is LINEAR, so Kt is OPTIMISTIC on a machine whose teeth saturate \u2014 " + (solution?.bToothTesla != null ? "see tooth_flux_tesla, and pass magnet.iron_js_t to solve the saturating network" : "pass magnet.slots + tooth_width_mm to see the tooth field")) + ".",
|
|
64225
64367
|
claims
|
|
64226
64368
|
})
|
|
64227
64369
|
}
|
|
@@ -65693,7 +65835,7 @@ async function fixDrc(args) {
|
|
|
65693
65835
|
};
|
|
65694
65836
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
65695
65837
|
}
|
|
65696
|
-
var PCB_LAYER_SET, COPPER_LAYERS, COPPER_LAYER_SET, POSITION_TOLERANCE, PIN_SEP, pinKey, createSchematicSchema, docInputProperties, placeComponentsSchema, routeNetsSchema, runDrcSchema, runErcSchema, critiqueRouteSchema, routeDiffPairSchema, lengthMatchTracesSchema, exportGerberSchema, validateForFabSchema, fabPrepSchema, exportKicadSchema, addCoilSchema, boardFromSolidSchema, addCoilArraySchema, windingLayoutSchema, calcImpedanceSchema, sizeImpedanceSchema, sizePdnSchema, calcCoilSchema, sizeCoilSchema, calcRfSchema, COIL_GEOMETRY, MU0, RAIL_PIN_NAMES, PIN_TYPES, CHIP_SIZE_CODES, CONNECTIVITY_GUARD_MAX_NETS, POWER_NET_RE, DRC_CATEGORY, DRC_DELTA_SAMPLE_CAP, DRC_DELTA_FULL_BOARD_MAX, LINT_CRYSTAL_MAX_MM, LINT_DECAP_MAX_MM, LINT_CONNECTOR_EDGE_MAX_MM, LINT_HIGH_CURRENT_SEP_MIN_MM, LINT_GROUND_NET_RE, LINT_SUPPLY_NET_RE, SENSITIVE_NET_RE, HIGH_CURRENT_CLASS_RE, refPrefix, dist2d, round2, sumSq, round33, vec2Schema, setBoardOutlineSchema, FOOTPRINT_FAMILIES, listFootprintsSchema, searchFootprintsSchema, getPadPositionsSchema, getFootprintSchema, ROTATION_CONVENTION, describePcbSchema, DESCRIBE_NET_NAME_CAP, addTraceSchema, addViaSchema, OZ_TO_MM, setStackupSchema, setPlacementSchema, addZoneSchema, deleteElementProps, deleteZoneSchema, deleteTraceSchema, deleteViaSchema, COPPER_KINDS, GET_COPPER_CAP, getCopperSchema, TIE_POSITION_TOL, addNetTieSchema, deleteNetTieSchema, undoSchema, setDesignRulesSchema, sizeTraceForCurrentSchema, addViaArraySchema, MAX_VIA_ARRAY, addMotorWindingSchema, calcMotorSchema, sig6, BEARING_FRICTION_MNM, checkSelfStartSchema, solidFromBoardSchema, FR4_DENSITY_KG_M3, COPPER_DENSITY_KG_M3, DEFAULT_COPPER_THICKNESS_MM, loopArea, MOUNTING_FP_RE, KEEPOUT_ECHO_CAP, INLINE_DOC_ECHO_CAP, searchElectronicPartsSchema, resolvePartSchema, findAlternativesSchema, verifySubstitutionSchema, buildReceiptSchema, verifyReceiptSchema, FIX_EPS, FIX_DRC_NEVER, fixDrcSchema, toolDefs20;
|
|
65838
|
+
var PCB_LAYER_SET, COPPER_LAYERS, COPPER_LAYER_SET, POSITION_TOLERANCE, PIN_SEP, pinKey, createSchematicSchema, docInputProperties, placeComponentsSchema, routeNetsSchema, runDrcSchema, runErcSchema, critiqueRouteSchema, routeDiffPairSchema, lengthMatchTracesSchema, exportGerberSchema, validateForFabSchema, fabPrepSchema, exportKicadSchema, addCoilSchema, boardFromSolidSchema, addCoilArraySchema, windingLayoutSchema, calcImpedanceSchema, sizeImpedanceSchema, sizePdnSchema, calcCoilSchema, sizeCoilSchema, calcRfSchema, COIL_GEOMETRY, MU0, RAIL_PIN_NAMES, PIN_TYPES, CHIP_SIZE_CODES, CONNECTIVITY_GUARD_MAX_NETS, POWER_NET_RE, DRC_CATEGORY, DRC_DELTA_SAMPLE_CAP, DRC_DELTA_FULL_BOARD_MAX, LINT_CRYSTAL_MAX_MM, LINT_DECAP_MAX_MM, LINT_CONNECTOR_EDGE_MAX_MM, LINT_HIGH_CURRENT_SEP_MIN_MM, LINT_GROUND_NET_RE, LINT_SUPPLY_NET_RE, SENSITIVE_NET_RE, HIGH_CURRENT_CLASS_RE, refPrefix, dist2d, round2, sumSq, round33, vec2Schema, setBoardOutlineSchema, FOOTPRINT_FAMILIES, listFootprintsSchema, searchFootprintsSchema, getPadPositionsSchema, getFootprintSchema, ROTATION_CONVENTION, describePcbSchema, DESCRIBE_NET_NAME_CAP, addTraceSchema, addViaSchema, OZ_TO_MM, setStackupSchema, setPlacementSchema, addZoneSchema, deleteElementProps, deleteZoneSchema, deleteTraceSchema, deleteViaSchema, COPPER_KINDS, GET_COPPER_CAP, getCopperSchema, TIE_POSITION_TOL, addNetTieSchema, deleteNetTieSchema, undoSchema, setDesignRulesSchema, sizeTraceForCurrentSchema, addViaArraySchema, MAX_VIA_ARRAY, addMotorWindingSchema, calcMotorSchema, SILICON_STEEL_KNEE_T, sig6, BEARING_FRICTION_MNM, checkSelfStartSchema, solidFromBoardSchema, FR4_DENSITY_KG_M3, COPPER_DENSITY_KG_M3, DEFAULT_COPPER_THICKNESS_MM, loopArea, MOUNTING_FP_RE, KEEPOUT_ECHO_CAP, INLINE_DOC_ECHO_CAP, searchElectronicPartsSchema, resolvePartSchema, findAlternativesSchema, verifySubstitutionSchema, buildReceiptSchema, verifyReceiptSchema, FIX_EPS, FIX_DRC_NEVER, fixDrcSchema, toolDefs20;
|
|
65697
65839
|
var init_ecad2 = __esm({
|
|
65698
65840
|
"src/tools/ecad.ts"() {
|
|
65699
65841
|
"use strict";
|
|
@@ -67105,7 +67247,7 @@ var init_ecad2 = __esm({
|
|
|
67105
67247
|
},
|
|
67106
67248
|
magnet: {
|
|
67107
67249
|
type: "object",
|
|
67108
|
-
description: "PM mode: optional magnet/geometry to compute B_gap when airgap_flux_tesla is omitted (NdFeB defaults). Fields: remanence_tesla, magnet_thickness_mm, airgap_mm, recoil_mu_rel, magnet_area_mm2, gap_area_mm2, iron_mu_rel. Add pole_width_mm (magnet pole face width across the fringing direction) to also apply the first-order Carter-like fringing derate w/(w+2g) \u2014 the tool then reports raw AND derated B and uses the derated value for Kt."
|
|
67250
|
+
description: "PM mode: optional magnet/geometry to compute B_gap when airgap_flux_tesla is omitted (NdFeB defaults). Fields: remanence_tesla, magnet_thickness_mm, airgap_mm, recoil_mu_rel, magnet_area_mm2, gap_area_mm2, iron_mu_rel. Add pole_width_mm (magnet pole face width across the fringing direction) to also apply the first-order Carter-like fringing derate w/(w+2g) \u2014 the tool then reports raw AND derated B and uses the derated value for Kt. SATURATION: the MEC iron is LINEAR unless you say otherwise, so it over-predicts Kt on a machine whose teeth saturate. Pass slots + (tooth_width_mm or tooth_fraction) to make the teeth visible \u2014 the tool then reports tooth_flux_density (B_gap \xB7 pitch/width, routinely 2x B_gap) and warns when it passes the ~1.5 T silicon-steel knee. Add iron_js_t (saturation polarization, silicon steel \u2248 2.0 T) with iron_mu_rel to actually SOLVE the saturating network instead of merely warning; tooth_path_mm puts the teeth in the reluctance loop."
|
|
67109
67251
|
},
|
|
67110
67252
|
phase_current_a: { type: "number", description: "Induction mode (required): phase current, A RMS (balanced 3-phase drive)." },
|
|
67111
67253
|
electrical_freq_hz: { type: "number", description: "Induction mode (required): electrical drive frequency, Hz." },
|
|
@@ -67124,6 +67266,7 @@ var init_ecad2 = __esm({
|
|
|
67124
67266
|
},
|
|
67125
67267
|
required: ["pole_pairs", "turns_per_phase"]
|
|
67126
67268
|
};
|
|
67269
|
+
SILICON_STEEL_KNEE_T = 1.5;
|
|
67127
67270
|
sig6 = (v) => v === 0 || !Number.isFinite(v) ? v : Number(v.toPrecision(6));
|
|
67128
67271
|
BEARING_FRICTION_MNM = {
|
|
67129
67272
|
"608-2RS": {
|
|
@@ -74088,6 +74231,41 @@ function getSimulation(envId) {
|
|
|
74088
74231
|
function getEnvRecord(envId) {
|
|
74089
74232
|
return envRecords.get(envId) ?? null;
|
|
74090
74233
|
}
|
|
74234
|
+
function resolveEnvDocument(args) {
|
|
74235
|
+
const id = typeof args.document_id === "string" ? args.document_id : "";
|
|
74236
|
+
const inline = args.document && typeof args.document === "object" ? args.document : null;
|
|
74237
|
+
if (id && inline) {
|
|
74238
|
+
throw new Error(
|
|
74239
|
+
`Pass either \`document_id\` or \`document\`, not both \u2014 they resolve to different sessions and the inline copy may be stale. Drop \`document\` to simulate session "${id}" in place.`
|
|
74240
|
+
);
|
|
74241
|
+
}
|
|
74242
|
+
if (!id && !inline) {
|
|
74243
|
+
throw new Error(
|
|
74244
|
+
"Pass `document_id` (from open_document) to simulate a resident session \u2014 or an inline `document` object for the stateless flow. Exactly one is required."
|
|
74245
|
+
);
|
|
74246
|
+
}
|
|
74247
|
+
if (id) {
|
|
74248
|
+
return { doc: getSession(id), documentId: id, source: "session" };
|
|
74249
|
+
}
|
|
74250
|
+
return { doc: inline, documentId: null, source: "inline" };
|
|
74251
|
+
}
|
|
74252
|
+
function labelObservation(obs, jointIds, endEffectorIds) {
|
|
74253
|
+
const labeled = { ...obs, joint_ids: jointIds };
|
|
74254
|
+
if (jointIds && jointIds.length === obs.joint_positions.length) {
|
|
74255
|
+
labeled.joints = jointIds.map((id, i) => ({
|
|
74256
|
+
id,
|
|
74257
|
+
position: obs.joint_positions[i],
|
|
74258
|
+
velocity: obs.joint_velocities[i]
|
|
74259
|
+
}));
|
|
74260
|
+
}
|
|
74261
|
+
if (endEffectorIds.length === obs.end_effector_poses.length) {
|
|
74262
|
+
labeled.end_effectors = endEffectorIds.map((id, i) => ({
|
|
74263
|
+
id,
|
|
74264
|
+
pose: obs.end_effector_poses[i]
|
|
74265
|
+
}));
|
|
74266
|
+
}
|
|
74267
|
+
return labeled;
|
|
74268
|
+
}
|
|
74091
74269
|
async function createRobotEnv(input) {
|
|
74092
74270
|
const args = input;
|
|
74093
74271
|
const available = await isPhysicsAvailable2();
|
|
@@ -74106,17 +74284,19 @@ async function createRobotEnv(input) {
|
|
|
74106
74284
|
}
|
|
74107
74285
|
try {
|
|
74108
74286
|
const envId = `sim_${randomBytes4(12).toString("base64url")}`;
|
|
74109
|
-
const
|
|
74287
|
+
const { doc, documentId: sessionId, source } = resolveEnvDocument(args);
|
|
74288
|
+
const env = await PhysicsEnv.create(doc, {
|
|
74110
74289
|
endEffectorIds: args.end_effector_ids,
|
|
74111
74290
|
dt: args.dt,
|
|
74112
74291
|
substeps: args.substeps,
|
|
74113
74292
|
maxSteps: args.max_steps
|
|
74114
74293
|
});
|
|
74115
74294
|
simulations.set(envId, env);
|
|
74116
|
-
const documentId = registerSession(
|
|
74295
|
+
const documentId = sessionId ?? registerSession(doc);
|
|
74117
74296
|
envRecords.set(envId, {
|
|
74118
|
-
document:
|
|
74297
|
+
document: doc,
|
|
74119
74298
|
documentId,
|
|
74299
|
+
endEffectorIds: args.end_effector_ids,
|
|
74120
74300
|
trajectory: [],
|
|
74121
74301
|
rewards: [],
|
|
74122
74302
|
dones: [],
|
|
@@ -74128,7 +74308,19 @@ async function createRobotEnv(input) {
|
|
|
74128
74308
|
});
|
|
74129
74309
|
const info = {
|
|
74130
74310
|
env_id: envId,
|
|
74311
|
+
// Binding contract, spelled out because two handles come back:
|
|
74312
|
+
// · env_id → gym_step / gym_reset / gym_observe / gym_close
|
|
74313
|
+
// · document_id → the replay viewer, get_preview_glb, and every other
|
|
74314
|
+
// session tool (render_view, inspect_cad, update, …)
|
|
74315
|
+
// On the session path document_id IS the id you passed — the env and the
|
|
74316
|
+
// document you keep editing are the same session. On the inline path it
|
|
74317
|
+
// is a NEW session minted from the IR you sent.
|
|
74131
74318
|
document_id: documentId,
|
|
74319
|
+
document_source: source,
|
|
74320
|
+
binds: {
|
|
74321
|
+
env_id: "gym_step, gym_reset, gym_observe, gym_close",
|
|
74322
|
+
document_id: "replay viewer, get_preview_glb, and session tools (render_view, inspect_cad, update)"
|
|
74323
|
+
},
|
|
74132
74324
|
num_joints: env.numJoints,
|
|
74133
74325
|
// Observation ordering contract: joint_positions[i] and
|
|
74134
74326
|
// joint_velocities[i] refer to joint_ids[i]. Null when the loaded
|
|
@@ -74185,8 +74377,16 @@ function gymStep(input) {
|
|
|
74185
74377
|
record2.dones.shift();
|
|
74186
74378
|
}
|
|
74187
74379
|
}
|
|
74380
|
+
const labeled = {
|
|
74381
|
+
...result,
|
|
74382
|
+
observation: labelObservation(
|
|
74383
|
+
result.observation,
|
|
74384
|
+
env.jointIds,
|
|
74385
|
+
record2?.endEffectorIds ?? []
|
|
74386
|
+
)
|
|
74387
|
+
};
|
|
74188
74388
|
return {
|
|
74189
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
74389
|
+
content: [{ type: "text", text: JSON.stringify(labeled, null, 2) }]
|
|
74190
74390
|
};
|
|
74191
74391
|
} catch (err7) {
|
|
74192
74392
|
const message = err7 instanceof Error ? err7.message : String(err7);
|
|
@@ -74218,7 +74418,20 @@ function gymReset(input) {
|
|
|
74218
74418
|
record2.resetEpoch += 1;
|
|
74219
74419
|
}
|
|
74220
74420
|
return {
|
|
74221
|
-
content: [
|
|
74421
|
+
content: [
|
|
74422
|
+
{
|
|
74423
|
+
type: "text",
|
|
74424
|
+
text: JSON.stringify(
|
|
74425
|
+
labelObservation(
|
|
74426
|
+
observation,
|
|
74427
|
+
env.jointIds,
|
|
74428
|
+
record2?.endEffectorIds ?? []
|
|
74429
|
+
),
|
|
74430
|
+
null,
|
|
74431
|
+
2
|
|
74432
|
+
)
|
|
74433
|
+
}
|
|
74434
|
+
]
|
|
74222
74435
|
};
|
|
74223
74436
|
} catch (err7) {
|
|
74224
74437
|
const message = err7 instanceof Error ? err7.message : String(err7);
|
|
@@ -74241,8 +74454,22 @@ function gymObserve(input) {
|
|
|
74241
74454
|
}
|
|
74242
74455
|
try {
|
|
74243
74456
|
const observation = env.observe();
|
|
74457
|
+
const record2 = envRecords.get(args.env_id);
|
|
74244
74458
|
return {
|
|
74245
|
-
content: [
|
|
74459
|
+
content: [
|
|
74460
|
+
{
|
|
74461
|
+
type: "text",
|
|
74462
|
+
text: JSON.stringify(
|
|
74463
|
+
labelObservation(
|
|
74464
|
+
observation,
|
|
74465
|
+
env.jointIds,
|
|
74466
|
+
record2?.endEffectorIds ?? []
|
|
74467
|
+
),
|
|
74468
|
+
null,
|
|
74469
|
+
2
|
|
74470
|
+
)
|
|
74471
|
+
}
|
|
74472
|
+
]
|
|
74246
74473
|
};
|
|
74247
74474
|
} catch (err7) {
|
|
74248
74475
|
const message = err7 instanceof Error ? err7.message : String(err7);
|
|
@@ -74288,6 +74515,7 @@ async function batchCreateEnvs(input) {
|
|
|
74288
74515
|
}
|
|
74289
74516
|
try {
|
|
74290
74517
|
const batchId = `batch_${randomBytes4(12).toString("base64url")}`;
|
|
74518
|
+
const { doc, documentId, source } = resolveEnvDocument(args);
|
|
74291
74519
|
const envOptions = {
|
|
74292
74520
|
endEffectorIds: args.end_effector_ids,
|
|
74293
74521
|
dt: args.dt,
|
|
@@ -74296,22 +74524,33 @@ async function batchCreateEnvs(input) {
|
|
|
74296
74524
|
};
|
|
74297
74525
|
const envPromises = Array.from(
|
|
74298
74526
|
{ length: args.n_envs },
|
|
74299
|
-
() => PhysicsEnv.create(
|
|
74527
|
+
() => PhysicsEnv.create(doc, envOptions)
|
|
74300
74528
|
);
|
|
74301
74529
|
const envs = await Promise.all(envPromises);
|
|
74302
74530
|
const actionDim = envs[0].actionDim;
|
|
74303
74531
|
batchGroups.set(batchId, {
|
|
74304
74532
|
envs,
|
|
74305
74533
|
actionDim,
|
|
74306
|
-
document:
|
|
74307
|
-
options: envOptions
|
|
74534
|
+
document: doc,
|
|
74535
|
+
options: envOptions,
|
|
74536
|
+
jointIds: envs[0].jointIds
|
|
74308
74537
|
});
|
|
74309
74538
|
const info = {
|
|
74310
74539
|
batch_id: batchId,
|
|
74540
|
+
// batch_step / batch_reset bind to batch_id. No document session is
|
|
74541
|
+
// minted here — a batch has no viewer; `document_id` echoes the session
|
|
74542
|
+
// read from, and is null on the inline path.
|
|
74543
|
+
document_id: documentId,
|
|
74544
|
+
document_source: source,
|
|
74311
74545
|
n_envs: args.n_envs,
|
|
74312
74546
|
action_dim: actionDim,
|
|
74313
74547
|
observation_dim: envs[0].observationDim,
|
|
74314
|
-
num_joints: envs[0].numJoints
|
|
74548
|
+
num_joints: envs[0].numJoints,
|
|
74549
|
+
// Observation ordering contract, echoed so batch callers get the same
|
|
74550
|
+
// labeling the single-env path returns per observation.
|
|
74551
|
+
joint_ids: envs[0].jointIds,
|
|
74552
|
+
actuated_joint_ids: envs[0].actuatedJointIds,
|
|
74553
|
+
end_effector_ids: args.end_effector_ids
|
|
74315
74554
|
};
|
|
74316
74555
|
return {
|
|
74317
74556
|
content: [{ type: "text", text: JSON.stringify(info, null, 2) }]
|
|
@@ -74353,7 +74592,13 @@ function batchStep(input) {
|
|
|
74353
74592
|
(env, i) => env.step(args.action_type, args.actions[i])
|
|
74354
74593
|
);
|
|
74355
74594
|
const summary = {
|
|
74356
|
-
observations: results.map(
|
|
74595
|
+
observations: results.map(
|
|
74596
|
+
(r) => labelObservation(
|
|
74597
|
+
r.observation,
|
|
74598
|
+
group.jointIds,
|
|
74599
|
+
group.options.endEffectorIds
|
|
74600
|
+
)
|
|
74601
|
+
),
|
|
74357
74602
|
rewards: results.map((r) => r.reward),
|
|
74358
74603
|
dones: results.map((r) => r.done)
|
|
74359
74604
|
};
|
|
@@ -74380,7 +74625,13 @@ function batchReset(input) {
|
|
|
74380
74625
|
};
|
|
74381
74626
|
}
|
|
74382
74627
|
try {
|
|
74383
|
-
const observations = group.envs.map(
|
|
74628
|
+
const observations = group.envs.map(
|
|
74629
|
+
(env) => labelObservation(
|
|
74630
|
+
env.reset(),
|
|
74631
|
+
group.jointIds,
|
|
74632
|
+
group.options.endEffectorIds
|
|
74633
|
+
)
|
|
74634
|
+
);
|
|
74384
74635
|
return {
|
|
74385
74636
|
content: [{ type: "text", text: JSON.stringify({ observations }, null, 2) }]
|
|
74386
74637
|
};
|
|
@@ -74392,7 +74643,7 @@ function batchReset(input) {
|
|
|
74392
74643
|
};
|
|
74393
74644
|
}
|
|
74394
74645
|
}
|
|
74395
|
-
var simulations, MAX_TRAJECTORY, envRecords, batchGroups, createRobotEnvSchema, gymStepSchema, gymResetSchema, gymObserveSchema, gymCloseSchema, batchCreateEnvsSchema, batchStepSchema, batchResetSchema, toolDefs45;
|
|
74646
|
+
var simulations, MAX_TRAJECTORY, envRecords, batchGroups, INLINE_DOC_DESC, SESSION_DOC_DESC, createRobotEnvSchema, gymStepSchema, gymResetSchema, gymObserveSchema, gymCloseSchema, batchCreateEnvsSchema, batchStepSchema, batchResetSchema, toolDefs45;
|
|
74396
74647
|
var init_gym = __esm({
|
|
74397
74648
|
"src/tools/gym.ts"() {
|
|
74398
74649
|
"use strict";
|
|
@@ -74403,12 +74654,18 @@ var init_gym = __esm({
|
|
|
74403
74654
|
MAX_TRAJECTORY = 600;
|
|
74404
74655
|
envRecords = /* @__PURE__ */ new Map();
|
|
74405
74656
|
batchGroups = /* @__PURE__ */ new Map();
|
|
74657
|
+
INLINE_DOC_DESC = "Inline Document IR describing the robot assembly, used instead of a session. Use this stateless path when no `document_id` is resident (e.g. a cold serverless instance). Exactly one of `document_id` or `document` must be given.";
|
|
74658
|
+
SESSION_DOC_DESC = "Session id of the assembly to simulate (from open_document / create_cad_loon). Preferred: the env binds to this same session, so no IR round-trip is needed and the sim can never run against a stale copy.";
|
|
74406
74659
|
createRobotEnvSchema = {
|
|
74407
74660
|
type: "object",
|
|
74408
74661
|
properties: {
|
|
74662
|
+
document_id: {
|
|
74663
|
+
type: "string",
|
|
74664
|
+
description: SESSION_DOC_DESC
|
|
74665
|
+
},
|
|
74409
74666
|
document: {
|
|
74410
74667
|
type: "object",
|
|
74411
|
-
description:
|
|
74668
|
+
description: INLINE_DOC_DESC
|
|
74412
74669
|
},
|
|
74413
74670
|
end_effector_ids: {
|
|
74414
74671
|
type: "array",
|
|
@@ -74428,7 +74685,7 @@ var init_gym = __esm({
|
|
|
74428
74685
|
description: "Maximum episode length (default: 1000)"
|
|
74429
74686
|
}
|
|
74430
74687
|
},
|
|
74431
|
-
required: ["
|
|
74688
|
+
required: ["end_effector_ids"]
|
|
74432
74689
|
};
|
|
74433
74690
|
gymStepSchema = {
|
|
74434
74691
|
type: "object",
|
|
@@ -74483,9 +74740,13 @@ var init_gym = __esm({
|
|
|
74483
74740
|
batchCreateEnvsSchema = {
|
|
74484
74741
|
type: "object",
|
|
74485
74742
|
properties: {
|
|
74743
|
+
document_id: {
|
|
74744
|
+
type: "string",
|
|
74745
|
+
description: SESSION_DOC_DESC
|
|
74746
|
+
},
|
|
74486
74747
|
document: {
|
|
74487
74748
|
type: "object",
|
|
74488
|
-
description:
|
|
74749
|
+
description: INLINE_DOC_DESC
|
|
74489
74750
|
},
|
|
74490
74751
|
n_envs: {
|
|
74491
74752
|
type: "number",
|
|
@@ -74509,7 +74770,7 @@ var init_gym = __esm({
|
|
|
74509
74770
|
description: "Maximum episode length (default: 1000)"
|
|
74510
74771
|
}
|
|
74511
74772
|
},
|
|
74512
|
-
required: ["
|
|
74773
|
+
required: ["n_envs", "end_effector_ids"]
|
|
74513
74774
|
};
|
|
74514
74775
|
batchStepSchema = {
|
|
74515
74776
|
type: "object",
|
|
@@ -74548,7 +74809,7 @@ var init_gym = __esm({
|
|
|
74548
74809
|
{
|
|
74549
74810
|
name: "create_robot_env",
|
|
74550
74811
|
pack: "physics",
|
|
74551
|
-
description: "Create a physics simulation environment from a vcad assembly.
|
|
74812
|
+
description: "Create a physics simulation environment from a vcad assembly. Session-first: pass the `document_id` of the assembly already open \u2014 the env binds to that same session, so there is no IR round-trip and the sim can't run against a stale copy. Inline `document` IR is the stateless fallback; exactly one of the two is required. Returns env_id (for gym_step / gym_reset / gym_observe / gym_close) and document_id (what the replay viewer and the other session tools bind to). Mounts the inline 3D viewer with a play button \u2014 gym_step rollouts replay right in the chat.",
|
|
74552
74813
|
inputSchema: createRobotEnvSchema,
|
|
74553
74814
|
handler: (a) => createRobotEnv(a),
|
|
74554
74815
|
// writesDoc: the registered session must persist durably (signed-in hosted
|
|
@@ -74559,7 +74820,7 @@ var init_gym = __esm({
|
|
|
74559
74820
|
{
|
|
74560
74821
|
name: "gym_step",
|
|
74561
74822
|
pack: "physics",
|
|
74562
|
-
description: "Step the physics simulation with an action. action_type can be 'torque' (Nm), 'position' (degrees/mm), or 'velocity' (deg/s or mm/s). Returns observation (joint positions/velocities, end effector poses), reward, and done flag.",
|
|
74823
|
+
description: "Step the physics simulation with an action. action_type can be 'torque' (Nm), 'position' (degrees/mm), or 'velocity' (deg/s or mm/s). Returns observation (joint positions/velocities, end effector poses), reward, and done flag. The observation carries both the bare positional arrays and id-keyed views: `joints[i] = {id, position, velocity}` and `end_effectors[i] = {id, pose}`, so no ordering has to be remembered.",
|
|
74563
74824
|
inputSchema: gymStepSchema,
|
|
74564
74825
|
handler: (a) => gymStep(a),
|
|
74565
74826
|
behavior: behavior({})
|
|
@@ -74567,7 +74828,7 @@ var init_gym = __esm({
|
|
|
74567
74828
|
{
|
|
74568
74829
|
name: "gym_reset",
|
|
74569
74830
|
pack: "physics",
|
|
74570
|
-
description: "Reset the simulation environment to its initial state. Returns the initial observation.",
|
|
74831
|
+
description: "Reset the simulation environment to its initial state. Returns the initial observation, with joint values keyed by joint id (`joints`) and end effector poses keyed by instance id (`end_effectors`) alongside the bare arrays.",
|
|
74571
74832
|
inputSchema: gymResetSchema,
|
|
74572
74833
|
handler: (a) => gymReset(a),
|
|
74573
74834
|
behavior: behavior({})
|
|
@@ -74575,7 +74836,7 @@ var init_gym = __esm({
|
|
|
74575
74836
|
{
|
|
74576
74837
|
name: "gym_observe",
|
|
74577
74838
|
pack: "physics",
|
|
74578
|
-
description: "Get the current observation from the simulation without stepping. Returns joint positions, velocities, and end effector poses.",
|
|
74839
|
+
description: "Get the current observation from the simulation without stepping. Returns joint positions, velocities, and end effector poses \u2014 both as bare arrays and keyed by joint / instance id.",
|
|
74579
74840
|
inputSchema: gymObserveSchema,
|
|
74580
74841
|
handler: (a) => gymObserve(a),
|
|
74581
74842
|
behavior: behavior({})
|
|
@@ -74591,7 +74852,7 @@ var init_gym = __esm({
|
|
|
74591
74852
|
{
|
|
74592
74853
|
name: "batch_create_envs",
|
|
74593
74854
|
pack: "physics",
|
|
74594
|
-
description: "Create N parallel simulation environments from a single robot assembly. Returns a batch_id for use with batch_step and batch_reset. Enables parallel RL training across multiple environments.",
|
|
74855
|
+
description: "Create N parallel simulation environments from a single robot assembly. Session-first: pass the `document_id` of the open assembly, or inline `document` IR as the stateless fallback \u2014 exactly one is required. Returns a batch_id for use with batch_step and batch_reset. Enables parallel RL training across multiple environments.",
|
|
74595
74856
|
inputSchema: batchCreateEnvsSchema,
|
|
74596
74857
|
handler: (a) => batchCreateEnvs(a),
|
|
74597
74858
|
behavior: behavior({})
|
|
@@ -77878,6 +78139,8 @@ function buildInstructions(kernelPrompt) {
|
|
|
77878
78139
|
"",
|
|
77879
78140
|
"Sessions and server restarts: a `document_id` is a handle to server-side state, NOT storage. If ANY preview goes dark, a widget stops updating, or a call fails with an unknown `document_id`, check `server_info` FIRST \u2014 a low `uptime_s` or a changed `instance_id` means the SERVER RESTARTED and the feature is fine. That is the fastest way to tell a restart from a bug, and it should be the first check for any preview-shaped complaint. On a restart every live document_id dies at once, so previously-working widgets all go dark together \u2014 that is not a renderer bug. When `server_info` reports `durable:false`, sessions are in-memory only and cannot be recovered: keep the authoring source (your `create_cad_loon` call or record of edits) so a document can be rebuilt, and snapshot long-lived work with `checkpoint_document` / `save_document`. Every tool result also carries `_meta['io.vcad/build']` with `boot_token` + `instance_id`, so a changed value flags a restart without polling.",
|
|
77880
78141
|
"",
|
|
78142
|
+
"Physics/RL workflow: `create_robot_env` (or `batch_create_envs`) \u2192 `gym_reset` \u2192 `gym_step` \u2192 `gym_close`. Both env creators are session-first like everything else: pass the `document_id` of the open assembly \u2014 never re-send its IR. `create_robot_env` binds the env to that same session, so a `document_id` env and the document you keep editing can't drift apart; inline `document` IR stays available as the stateless fallback (it mints a NEW session, returned as `document_id`) and passing both is refused. `env_id` drives gym_step/gym_reset/gym_observe/gym_close; `document_id` is what the replay viewer, `get_preview_glb`, and the other session tools bind to. Observations come back both as bare positional arrays and keyed by id (`joints[i] = {id, position, velocity}`, `end_effectors[i] = {id, pose}`) \u2014 read the keyed views and no ordering has to be remembered.",
|
|
78143
|
+
"",
|
|
77881
78144
|
"PCB workflow: `create_schematic` (declare connectivity as data via `nets`) \u2192 `place_components` \u2192 `route_nets` / `add_coil` / `add_coil_array` \u2192 `run_drc` \u2192 `validate_for_fab` \u2192 `export_gerber`. All take the `document_id` from create_schematic and mutate that session \u2014 never re-send the document. `validate_for_fab` is the single 'is this board ready?' gate (DRC + renderability + Gerber serialization + blockers, all fail-closed); `export_gerber` enforces a clean DRC by default and blocks a dirty board. `board_from_solid` turns a solid part (e.g. an enclosure or stator disc in a CAD session) into an outline polygon for `place_components`. For motors, plan the winding first with `winding_layout` (slots + poles \u2192 per-coil phase/polarity/winding-factor, as data \u2014 it touches no board), then realize it with `add_coil_array`. `run_drc` returns a summary by default (counts by rule + net-pair, worst clearance, a capped sample); pass `detail:'full'` for every violation. Surgical copper edits: `get_copper` lists existing traces/vias/zones (filtered by layer/net/bbox/kind) with the same indices `delete_trace`/`delete_via`/`delete_zone` accept \u2014 discover, then delete, without exporting the document. Where two nets must touch on purpose (wye neutral, split ground, shunt tap), declare it with `add_net_tie` \u2014 prefer a region-scoped tie (position+radius) so DRC stays honest away from the junction; `delete_net_tie` takes it back."
|
|
77882
78145
|
].join("\n");
|
|
77883
78146
|
if (!kernelPrompt) return header;
|
|
@@ -78727,8 +78990,8 @@ var init_server3 = __esm({
|
|
|
78727
78990
|
init_order_feed();
|
|
78728
78991
|
init_animate();
|
|
78729
78992
|
PKG_VERSION = (() => {
|
|
78730
|
-
if ("0.9.4-main.
|
|
78731
|
-
return "0.9.4-main.
|
|
78993
|
+
if ("0.9.4-main.28") {
|
|
78994
|
+
return "0.9.4-main.28";
|
|
78732
78995
|
}
|
|
78733
78996
|
try {
|
|
78734
78997
|
const req = createRequire2(import.meta.url);
|
|
@@ -78737,8 +79000,8 @@ var init_server3 = __esm({
|
|
|
78737
79000
|
return "0.0.0";
|
|
78738
79001
|
}
|
|
78739
79002
|
})();
|
|
78740
|
-
BUILD_SHA = "
|
|
78741
|
-
BUILD_TIME = "2026-07-
|
|
79003
|
+
BUILD_SHA = "301abfbddc2a05c29fabc9f655031e554eab1d04";
|
|
79004
|
+
BUILD_TIME = "2026-07-26T00:32:29.039Z";
|
|
78742
79005
|
SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
|
|
78743
79006
|
VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
|
|
78744
79007
|
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.28",
|
|
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": "301abfbddc2a05c29fabc9f655031e554eab1d04",
|
|
23
|
+
"builtAt": "2026-07-26T00:32:29.039Z"
|
|
24
24
|
}
|
|
25
25
|
}
|
package/vcad_kernel_wasm_bg.wasm
CHANGED
|
Binary file
|