@vcad/mcp 0.11.0-main.162 → 0.11.0-main.164
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 +248 -93
- package/package.json +3 -3
- package/skill/SKILL.md +22 -0
package/README.md
CHANGED
|
@@ -9,4 +9,4 @@ vcad's MCP server as a self-contained bundle (server + BRep kernel WASM).
|
|
|
9
9
|
Or install vcad.app, which registers this server with your agents on first launch.
|
|
10
10
|
The agent skill ships at `skill/SKILL.md`.
|
|
11
11
|
|
|
12
|
-
Built from
|
|
12
|
+
Built from f36361fa47f1a47d196b4d9a8bcdb421c1cc785f at 2026-11-11T21:56:09.744Z. Source: https://github.com/muni-works/vcad
|
package/index.mjs
CHANGED
|
@@ -11312,9 +11312,9 @@ function workerTransport(worker) {
|
|
|
11312
11312
|
pending.delete(id);
|
|
11313
11313
|
});
|
|
11314
11314
|
return {
|
|
11315
|
-
request: (req) => new Promise((
|
|
11315
|
+
request: (req) => new Promise((resolve9) => {
|
|
11316
11316
|
const id = next++;
|
|
11317
|
-
pending.set(id,
|
|
11317
|
+
pending.set(id, resolve9);
|
|
11318
11318
|
worker.postMessage({ id, req });
|
|
11319
11319
|
}),
|
|
11320
11320
|
dispose: () => worker.terminate()
|
|
@@ -12193,11 +12193,11 @@ var init_analyze = __esm({
|
|
|
12193
12193
|
type: "module"
|
|
12194
12194
|
});
|
|
12195
12195
|
this.worker = worker;
|
|
12196
|
-
this.ready = new Promise((
|
|
12196
|
+
this.ready = new Promise((resolve9, reject) => {
|
|
12197
12197
|
const onMessage = (e) => {
|
|
12198
12198
|
const { type, id, message, analysis } = e.data;
|
|
12199
12199
|
if (type === "ready") {
|
|
12200
|
-
|
|
12200
|
+
resolve9();
|
|
12201
12201
|
return;
|
|
12202
12202
|
}
|
|
12203
12203
|
if (type === "error" && id === null) {
|
|
@@ -12221,9 +12221,9 @@ var init_analyze = __esm({
|
|
|
12221
12221
|
return this.ready;
|
|
12222
12222
|
}
|
|
12223
12223
|
request(msg, transfer = []) {
|
|
12224
|
-
return this.ensureWorker().then(() => new Promise((
|
|
12224
|
+
return this.ensureWorker().then(() => new Promise((resolve9, reject) => {
|
|
12225
12225
|
const id = this.nextId++;
|
|
12226
|
-
this.pending.set(id, { resolve:
|
|
12226
|
+
this.pending.set(id, { resolve: resolve9, reject });
|
|
12227
12227
|
this.worker.postMessage({ ...msg, id }, transfer);
|
|
12228
12228
|
}));
|
|
12229
12229
|
}
|
|
@@ -12840,11 +12840,11 @@ var init_dist = __esm({
|
|
|
12840
12840
|
return;
|
|
12841
12841
|
try {
|
|
12842
12842
|
const worker = new Worker(new URL("./eval-worker.js", import.meta.url), { type: "module" });
|
|
12843
|
-
this.workerReady = new Promise((
|
|
12843
|
+
this.workerReady = new Promise((resolve9, reject) => {
|
|
12844
12844
|
const onMessage = (e) => {
|
|
12845
12845
|
if (e.data.type === "ready") {
|
|
12846
12846
|
worker.removeEventListener("message", onMessage);
|
|
12847
|
-
|
|
12847
|
+
resolve9();
|
|
12848
12848
|
} else if (e.data.type === "error" && e.data.id === null) {
|
|
12849
12849
|
worker.removeEventListener("message", onMessage);
|
|
12850
12850
|
console.warn("[ENGINE] Worker WASM init failed:", e.data.message);
|
|
@@ -13067,7 +13067,7 @@ var init_dist = __esm({
|
|
|
13067
13067
|
const worker = this.worker;
|
|
13068
13068
|
const id = Math.random().toString(36).slice(2);
|
|
13069
13069
|
const skipClash = options.skipClashDetection ?? false;
|
|
13070
|
-
return new Promise((
|
|
13070
|
+
return new Promise((resolve9, reject) => {
|
|
13071
13071
|
const onMessage = (e) => {
|
|
13072
13072
|
if (e.data.id !== id)
|
|
13073
13073
|
return;
|
|
@@ -13077,7 +13077,7 @@ var init_dist = __esm({
|
|
|
13077
13077
|
if (scene.timing && _Engine.DEV) {
|
|
13078
13078
|
_Engine.logTiming(scene.timing, e.data.workerTotalMs);
|
|
13079
13079
|
}
|
|
13080
|
-
|
|
13080
|
+
resolve9(scene);
|
|
13081
13081
|
} else if (e.data.type === "error") {
|
|
13082
13082
|
reject(new Error(e.data.message));
|
|
13083
13083
|
}
|
|
@@ -17365,7 +17365,7 @@ function isRef(value) {
|
|
|
17365
17365
|
function cloneIssues(issues) {
|
|
17366
17366
|
return issues.map((iss) => iss.path ? { ...iss, path: iss.path.slice() } : { ...iss });
|
|
17367
17367
|
}
|
|
17368
|
-
function isRecursive(inst, stack,
|
|
17368
|
+
function isRecursive(inst, stack, resolve9) {
|
|
17369
17369
|
const cached3 = recursive.get(inst);
|
|
17370
17370
|
if (cached3 !== void 0)
|
|
17371
17371
|
return cached3 ? PROVEN : NONE;
|
|
@@ -17375,7 +17375,7 @@ function isRecursive(inst, stack, resolve8) {
|
|
|
17375
17375
|
let result = NONE;
|
|
17376
17376
|
const check = (child) => {
|
|
17377
17377
|
if (result !== PROVEN && child?._zod) {
|
|
17378
|
-
const answer = isRecursive(child, stack,
|
|
17378
|
+
const answer = isRecursive(child, stack, resolve9);
|
|
17379
17379
|
if (answer > result)
|
|
17380
17380
|
result = answer;
|
|
17381
17381
|
}
|
|
@@ -17386,7 +17386,7 @@ function isRecursive(inst, stack, resolve8) {
|
|
|
17386
17386
|
const desc = Object.getOwnPropertyDescriptor(sh, key3);
|
|
17387
17387
|
if (spread && !desc.enumerable)
|
|
17388
17388
|
continue;
|
|
17389
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
17389
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve9) : NONE;
|
|
17390
17390
|
if (child > answer)
|
|
17391
17391
|
answer = child;
|
|
17392
17392
|
}
|
|
@@ -17450,7 +17450,7 @@ function isRecursive(inst, stack, resolve8) {
|
|
|
17450
17450
|
break;
|
|
17451
17451
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
17452
17452
|
case "lazy": {
|
|
17453
|
-
const inner = def._cachedInner ?? (
|
|
17453
|
+
const inner = def._cachedInner ?? (resolve9 ? inst._zod.innerType : void 0);
|
|
17454
17454
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
17455
17455
|
break;
|
|
17456
17456
|
}
|
|
@@ -22204,12 +22204,12 @@ var init_stdio2 = __esm({
|
|
|
22204
22204
|
this.onclose?.();
|
|
22205
22205
|
}
|
|
22206
22206
|
send(message) {
|
|
22207
|
-
return new Promise((
|
|
22207
|
+
return new Promise((resolve9) => {
|
|
22208
22208
|
const json = serializeMessage(message);
|
|
22209
22209
|
if (this._stdout.write(json)) {
|
|
22210
|
-
|
|
22210
|
+
resolve9();
|
|
22211
22211
|
} else {
|
|
22212
|
-
this._stdout.once("drain",
|
|
22212
|
+
this._stdout.once("drain", resolve9);
|
|
22213
22213
|
}
|
|
22214
22214
|
});
|
|
22215
22215
|
}
|
|
@@ -23246,7 +23246,7 @@ var init_protocol = __esm({
|
|
|
23246
23246
|
return;
|
|
23247
23247
|
}
|
|
23248
23248
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
23249
|
-
await new Promise((
|
|
23249
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
|
|
23250
23250
|
options?.signal?.throwIfAborted();
|
|
23251
23251
|
}
|
|
23252
23252
|
} catch (error2) {
|
|
@@ -23263,7 +23263,7 @@ var init_protocol = __esm({
|
|
|
23263
23263
|
*/
|
|
23264
23264
|
request(request, resultSchema, options) {
|
|
23265
23265
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
23266
|
-
return new Promise((
|
|
23266
|
+
return new Promise((resolve9, reject) => {
|
|
23267
23267
|
const earlyReject = (error2) => {
|
|
23268
23268
|
reject(error2);
|
|
23269
23269
|
};
|
|
@@ -23341,7 +23341,7 @@ var init_protocol = __esm({
|
|
|
23341
23341
|
if (!parseResult.success) {
|
|
23342
23342
|
reject(parseResult.error);
|
|
23343
23343
|
} else {
|
|
23344
|
-
|
|
23344
|
+
resolve9(parseResult.data);
|
|
23345
23345
|
}
|
|
23346
23346
|
} catch (error2) {
|
|
23347
23347
|
reject(error2);
|
|
@@ -23602,12 +23602,12 @@ var init_protocol = __esm({
|
|
|
23602
23602
|
}
|
|
23603
23603
|
} catch {
|
|
23604
23604
|
}
|
|
23605
|
-
return new Promise((
|
|
23605
|
+
return new Promise((resolve9, reject) => {
|
|
23606
23606
|
if (signal.aborted) {
|
|
23607
23607
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
23608
23608
|
return;
|
|
23609
23609
|
}
|
|
23610
|
-
const timeoutId = setTimeout(
|
|
23610
|
+
const timeoutId = setTimeout(resolve9, interval);
|
|
23611
23611
|
signal.addEventListener("abort", () => {
|
|
23612
23612
|
clearTimeout(timeoutId);
|
|
23613
23613
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -26634,7 +26634,7 @@ var require_compile = __commonJS({
|
|
|
26634
26634
|
const schOrFunc = root.refs[ref];
|
|
26635
26635
|
if (schOrFunc)
|
|
26636
26636
|
return schOrFunc;
|
|
26637
|
-
let _sch =
|
|
26637
|
+
let _sch = resolve9.call(this, root, ref);
|
|
26638
26638
|
if (_sch === void 0) {
|
|
26639
26639
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
26640
26640
|
const { schemaId } = this.opts;
|
|
@@ -26661,7 +26661,7 @@ var require_compile = __commonJS({
|
|
|
26661
26661
|
function sameSchemaEnv(s1, s2) {
|
|
26662
26662
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
26663
26663
|
}
|
|
26664
|
-
function
|
|
26664
|
+
function resolve9(root, ref) {
|
|
26665
26665
|
let sch;
|
|
26666
26666
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
26667
26667
|
ref = sch;
|
|
@@ -27236,7 +27236,7 @@ var require_fast_uri = __commonJS({
|
|
|
27236
27236
|
}
|
|
27237
27237
|
return uri;
|
|
27238
27238
|
}
|
|
27239
|
-
function
|
|
27239
|
+
function resolve9(baseURI, relativeURI, options) {
|
|
27240
27240
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
27241
27241
|
const resolved = resolveComponent(parse5(baseURI, schemelessOptions), parse5(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
27242
27242
|
schemelessOptions.skipEscape = true;
|
|
@@ -27463,7 +27463,7 @@ var require_fast_uri = __commonJS({
|
|
|
27463
27463
|
var fastUri = {
|
|
27464
27464
|
SCHEMES,
|
|
27465
27465
|
normalize: normalize2,
|
|
27466
|
-
resolve:
|
|
27466
|
+
resolve: resolve9,
|
|
27467
27467
|
resolveComponent,
|
|
27468
27468
|
equal,
|
|
27469
27469
|
serialize,
|
|
@@ -33635,21 +33635,21 @@ var require_react_development = __commonJS({
|
|
|
33635
33635
|
);
|
|
33636
33636
|
actScopeDepth = prevActScopeDepth;
|
|
33637
33637
|
}
|
|
33638
|
-
function recursivelyFlushAsyncActWork(returnValue,
|
|
33638
|
+
function recursivelyFlushAsyncActWork(returnValue, resolve9, reject) {
|
|
33639
33639
|
var queue = ReactSharedInternals.actQueue;
|
|
33640
33640
|
if (null !== queue)
|
|
33641
33641
|
if (0 !== queue.length)
|
|
33642
33642
|
try {
|
|
33643
33643
|
flushActQueue(queue);
|
|
33644
33644
|
enqueueTask(function() {
|
|
33645
|
-
return recursivelyFlushAsyncActWork(returnValue,
|
|
33645
|
+
return recursivelyFlushAsyncActWork(returnValue, resolve9, reject);
|
|
33646
33646
|
});
|
|
33647
33647
|
return;
|
|
33648
33648
|
} catch (error2) {
|
|
33649
33649
|
ReactSharedInternals.thrownErrors.push(error2);
|
|
33650
33650
|
}
|
|
33651
33651
|
else ReactSharedInternals.actQueue = null;
|
|
33652
|
-
0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) :
|
|
33652
|
+
0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve9(returnValue);
|
|
33653
33653
|
}
|
|
33654
33654
|
function flushActQueue(queue) {
|
|
33655
33655
|
if (!isFlushing) {
|
|
@@ -33836,7 +33836,7 @@ var require_react_development = __commonJS({
|
|
|
33836
33836
|
));
|
|
33837
33837
|
});
|
|
33838
33838
|
return {
|
|
33839
|
-
then: function(
|
|
33839
|
+
then: function(resolve9, reject) {
|
|
33840
33840
|
didAwaitActCall = true;
|
|
33841
33841
|
thenable.then(
|
|
33842
33842
|
function(returnValue) {
|
|
@@ -33846,7 +33846,7 @@ var require_react_development = __commonJS({
|
|
|
33846
33846
|
flushActQueue(queue), enqueueTask(function() {
|
|
33847
33847
|
return recursivelyFlushAsyncActWork(
|
|
33848
33848
|
returnValue,
|
|
33849
|
-
|
|
33849
|
+
resolve9,
|
|
33850
33850
|
reject
|
|
33851
33851
|
);
|
|
33852
33852
|
});
|
|
@@ -33860,7 +33860,7 @@ var require_react_development = __commonJS({
|
|
|
33860
33860
|
ReactSharedInternals.thrownErrors.length = 0;
|
|
33861
33861
|
reject(_thrownError);
|
|
33862
33862
|
}
|
|
33863
|
-
} else
|
|
33863
|
+
} else resolve9(returnValue);
|
|
33864
33864
|
},
|
|
33865
33865
|
function(error2) {
|
|
33866
33866
|
popActScope(prevActQueue, prevActScopeDepth);
|
|
@@ -33882,15 +33882,15 @@ var require_react_development = __commonJS({
|
|
|
33882
33882
|
if (0 < ReactSharedInternals.thrownErrors.length)
|
|
33883
33883
|
throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
|
|
33884
33884
|
return {
|
|
33885
|
-
then: function(
|
|
33885
|
+
then: function(resolve9, reject) {
|
|
33886
33886
|
didAwaitActCall = true;
|
|
33887
33887
|
0 === prevActScopeDepth ? (ReactSharedInternals.actQueue = queue, enqueueTask(function() {
|
|
33888
33888
|
return recursivelyFlushAsyncActWork(
|
|
33889
33889
|
returnValue$jscomp$0,
|
|
33890
|
-
|
|
33890
|
+
resolve9,
|
|
33891
33891
|
reject
|
|
33892
33892
|
);
|
|
33893
|
-
})) :
|
|
33893
|
+
})) : resolve9(returnValue$jscomp$0);
|
|
33894
33894
|
}
|
|
33895
33895
|
};
|
|
33896
33896
|
};
|
|
@@ -44853,6 +44853,19 @@ var init_CHANGELOG = __esm({
|
|
|
44853
44853
|
"mac-app"
|
|
44854
44854
|
]
|
|
44855
44855
|
},
|
|
44856
|
+
{
|
|
44857
|
+
id: "2026-11-11-per-instance-agent-discovery",
|
|
44858
|
+
version: "undefined",
|
|
44859
|
+
date: "2026-11-11",
|
|
44860
|
+
category: "fix",
|
|
44861
|
+
title: "Agents no longer drive the wrong vcad.app window",
|
|
44862
|
+
summary: "Each vcad.app instance registers its own discovery file; clients pick one via VCAD_APP_PID/URL/DOCUMENT, every bridge result names its window, and a relaunch is reported.",
|
|
44863
|
+
features: [
|
|
44864
|
+
"agent-bridge",
|
|
44865
|
+
"mcp",
|
|
44866
|
+
"mac-app"
|
|
44867
|
+
]
|
|
44868
|
+
},
|
|
44856
44869
|
{
|
|
44857
44870
|
id: "2026-11-11-one-click-drawings",
|
|
44858
44871
|
version: "0.11.0",
|
|
@@ -44919,6 +44932,34 @@ var init_CHANGELOG = __esm({
|
|
|
44919
44932
|
"ask_user"
|
|
44920
44933
|
]
|
|
44921
44934
|
},
|
|
44935
|
+
{
|
|
44936
|
+
id: "2026-11-11-live-qa-round-2",
|
|
44937
|
+
version: "0.11.0",
|
|
44938
|
+
date: "2026-11-11",
|
|
44939
|
+
category: "fix",
|
|
44940
|
+
title: "Mac app: Find works on a new file; print orient fits bed",
|
|
44941
|
+
summary: "Find offers Sketch/Extrude/Fillet before the first edit and closes on click-outside, Esc, or a mode switch; Orient never defaults to a pose that overflows the printer.",
|
|
44942
|
+
features: [
|
|
44943
|
+
"mac-app",
|
|
44944
|
+
"command-palette",
|
|
44945
|
+
"3d-print"
|
|
44946
|
+
]
|
|
44947
|
+
},
|
|
44948
|
+
{
|
|
44949
|
+
id: "2026-11-11-huge-assembly-open",
|
|
44950
|
+
version: "0.11.0",
|
|
44951
|
+
date: "2026-11-11",
|
|
44952
|
+
category: "perf",
|
|
44953
|
+
title: "Huge STEP assemblies open in seconds",
|
|
44954
|
+
summary: "Identical imported parts tessellate once and are instanced, parts stream in as they finish, and big assemblies draw a quick preview first, then refine. Voron 2.4: first part 425 s \u2192 1.4 s.",
|
|
44955
|
+
features: [
|
|
44956
|
+
"step",
|
|
44957
|
+
"import",
|
|
44958
|
+
"assembly",
|
|
44959
|
+
"native-app",
|
|
44960
|
+
"performance"
|
|
44961
|
+
]
|
|
44962
|
+
},
|
|
44922
44963
|
{
|
|
44923
44964
|
id: "2026-11-11-history-edits-keep-references",
|
|
44924
44965
|
version: "undefined",
|
|
@@ -44932,6 +44973,19 @@ var init_CHANGELOG = __esm({
|
|
|
44932
44973
|
"history"
|
|
44933
44974
|
]
|
|
44934
44975
|
},
|
|
44976
|
+
{
|
|
44977
|
+
id: "2026-11-11-fusion-model-edges",
|
|
44978
|
+
version: "1.0.0",
|
|
44979
|
+
date: "2026-11-11",
|
|
44980
|
+
category: "feat",
|
|
44981
|
+
title: "Model edges drawn like Fusion 360",
|
|
44982
|
+
summary: "The Mac app now outlines every B-rep face boundary, tangent fillet edges included, and draws outlines on assembly instances too. Mesh-only parts keep crease edges.",
|
|
44983
|
+
features: [
|
|
44984
|
+
"viewport",
|
|
44985
|
+
"edges",
|
|
44986
|
+
"assembly"
|
|
44987
|
+
]
|
|
44988
|
+
},
|
|
44935
44989
|
{
|
|
44936
44990
|
id: "2026-11-11-direct-manipulation",
|
|
44937
44991
|
version: "undefined",
|
|
@@ -57378,10 +57432,86 @@ var init_source_provenance = __esm({
|
|
|
57378
57432
|
}
|
|
57379
57433
|
});
|
|
57380
57434
|
|
|
57381
|
-
// src/app-
|
|
57382
|
-
import { readFileSync as readFileSync5 } from "node:fs";
|
|
57435
|
+
// src/app-discovery.ts
|
|
57436
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "node:fs";
|
|
57383
57437
|
import { homedir } from "node:os";
|
|
57384
|
-
import {
|
|
57438
|
+
import { join as join3, resolve as resolve3 } from "node:path";
|
|
57439
|
+
function isAlive(pid) {
|
|
57440
|
+
if (typeof pid !== "number" || pid <= 0) return false;
|
|
57441
|
+
try {
|
|
57442
|
+
process.kill(pid, 0);
|
|
57443
|
+
return true;
|
|
57444
|
+
} catch (e) {
|
|
57445
|
+
return e.code === "EPERM";
|
|
57446
|
+
}
|
|
57447
|
+
}
|
|
57448
|
+
function describeInstance(r) {
|
|
57449
|
+
return `pid ${r.pid} (${r.document_name ?? r.document_path ?? "no document"})`;
|
|
57450
|
+
}
|
|
57451
|
+
function listInstances({ home = homedir(), alive = isAlive } = {}) {
|
|
57452
|
+
const root = join3(home, ".vcad");
|
|
57453
|
+
const read = (p) => {
|
|
57454
|
+
try {
|
|
57455
|
+
return JSON.parse(readFileSync5(p, "utf8"));
|
|
57456
|
+
} catch {
|
|
57457
|
+
return null;
|
|
57458
|
+
}
|
|
57459
|
+
};
|
|
57460
|
+
let records = [];
|
|
57461
|
+
try {
|
|
57462
|
+
const dir = join3(root, "instances");
|
|
57463
|
+
records = readdirSync2(dir).filter((f) => f.endsWith(".json")).map((f) => read(join3(dir, f))).filter((r) => r !== null);
|
|
57464
|
+
} catch {
|
|
57465
|
+
}
|
|
57466
|
+
if (records.length === 0) {
|
|
57467
|
+
const legacy = read(join3(root, "app.json"));
|
|
57468
|
+
if (legacy) records = [legacy];
|
|
57469
|
+
}
|
|
57470
|
+
return records.filter((r) => alive(r.pid)).sort((a, b) => String(b.started ?? "").localeCompare(String(a.started ?? "")));
|
|
57471
|
+
}
|
|
57472
|
+
function selectInstance({
|
|
57473
|
+
env = process.env,
|
|
57474
|
+
instances,
|
|
57475
|
+
documentPath
|
|
57476
|
+
}) {
|
|
57477
|
+
if (env.VCAD_APP_URL) {
|
|
57478
|
+
const url = env.VCAD_APP_URL;
|
|
57479
|
+
return { url, instance: instances.find((r) => urlOf(r) === url) ?? null };
|
|
57480
|
+
}
|
|
57481
|
+
if (env.VCAD_APP_PID) {
|
|
57482
|
+
const pid = Number(env.VCAD_APP_PID);
|
|
57483
|
+
const instance2 = instances.find((r) => r.pid === pid);
|
|
57484
|
+
if (!instance2) {
|
|
57485
|
+
const others2 = instances.length ? ` Running: ${instances.map(describeInstance).join(", ")}.` : "";
|
|
57486
|
+
return { error: `VCAD_APP_PID=${env.VCAD_APP_PID}: no running vcad.app with that pid.${others2}` };
|
|
57487
|
+
}
|
|
57488
|
+
return { url: urlOf(instance2), instance: instance2 };
|
|
57489
|
+
}
|
|
57490
|
+
if (instances.length === 0) return { error: "no running vcad.app found (~/.vcad/instances is empty)" };
|
|
57491
|
+
const wanted = documentPath ?? env.VCAD_APP_DOCUMENT;
|
|
57492
|
+
if (wanted) {
|
|
57493
|
+
const abs = resolve3(wanted);
|
|
57494
|
+
const instance2 = instances.find((r) => r.document_path && resolve3(r.document_path) === abs);
|
|
57495
|
+
if (instance2) return { url: urlOf(instance2), instance: instance2 };
|
|
57496
|
+
}
|
|
57497
|
+
const [instance, ...others] = instances;
|
|
57498
|
+
if (!others.length) return { url: urlOf(instance), instance };
|
|
57499
|
+
return {
|
|
57500
|
+
url: urlOf(instance),
|
|
57501
|
+
instance,
|
|
57502
|
+
warning: `${instances.length} vcad.app instances are running; using the newest, ${describeInstance(instance)}. Others: ${others.map(describeInstance).join(", ")}. Set VCAD_APP_PID to choose.`
|
|
57503
|
+
};
|
|
57504
|
+
}
|
|
57505
|
+
var urlOf;
|
|
57506
|
+
var init_app_discovery = __esm({
|
|
57507
|
+
"src/app-discovery.ts"() {
|
|
57508
|
+
"use strict";
|
|
57509
|
+
urlOf = (r) => r.url ?? `http://127.0.0.1:${r.port}/mcp`;
|
|
57510
|
+
}
|
|
57511
|
+
});
|
|
57512
|
+
|
|
57513
|
+
// src/app-link.ts
|
|
57514
|
+
import { dirname, isAbsolute as isAbsolute4 } from "node:path";
|
|
57385
57515
|
function noteDocumentFolder(documentId, folder) {
|
|
57386
57516
|
folders.set(documentId, folder);
|
|
57387
57517
|
}
|
|
@@ -57393,13 +57523,27 @@ function documentFolder(documentId, document) {
|
|
|
57393
57523
|
}
|
|
57394
57524
|
function endpoint() {
|
|
57395
57525
|
if (process.env.VCAD_APP === "0") return null;
|
|
57396
|
-
|
|
57397
|
-
|
|
57398
|
-
|
|
57399
|
-
|
|
57400
|
-
|
|
57401
|
-
|
|
57526
|
+
const instances = listInstances();
|
|
57527
|
+
if (pinned && (process.env.VCAD_APP_URL || instances.some((r) => r.pid === pinned?.instance?.pid))) {
|
|
57528
|
+
return pinned.url;
|
|
57529
|
+
}
|
|
57530
|
+
const pick2 = selectInstance({ instances });
|
|
57531
|
+
if (pick2.error !== void 0) return null;
|
|
57532
|
+
if (pick2.warning && pick2.warning !== warned) {
|
|
57533
|
+
warned = pick2.warning;
|
|
57534
|
+
process.stderr.write(`[vcad-mcp] ${pick2.warning}
|
|
57535
|
+
`);
|
|
57536
|
+
}
|
|
57537
|
+
const prev = pinned?.instance;
|
|
57538
|
+
if (prev && pick2.instance && prev.instance_id !== pick2.instance.instance_id) {
|
|
57539
|
+
appChanged(prev, pick2.instance);
|
|
57402
57540
|
}
|
|
57541
|
+
pinned = { url: pick2.url, instance: pick2.instance };
|
|
57542
|
+
return pick2.url;
|
|
57543
|
+
}
|
|
57544
|
+
function appChanged(prev, next) {
|
|
57545
|
+
const why = `The vcad app changed: ${describeInstance(prev)} is gone, now linked to ${describeInstance(next)} (it relaunched, or another window took over). Edits stay in this session. To show it there, call show_in_app.`;
|
|
57546
|
+
for (const id of links.keys()) loseWindow(id, why);
|
|
57403
57547
|
}
|
|
57404
57548
|
async function reconcileBeforeWrite(documentId, documents2, call = callApp) {
|
|
57405
57549
|
const link = links.get(documentId);
|
|
@@ -57613,14 +57757,17 @@ function pushPose(documentId, joints, call = callApp, now = Date.now(), base) {
|
|
|
57613
57757
|
sendPose(frame, now);
|
|
57614
57758
|
return true;
|
|
57615
57759
|
}
|
|
57616
|
-
var links, folders, callApp, PUSH_WAIT_MS, RECONCILE_WAIT_MS, wait, pushes, lateNotes, LOST_WINDOW, OPENING_TOOLS, toolDefs3, POSE_INTERVAL_MS, poseBusy, lastPose, pendingRestart, pendingLatest, poseTimer;
|
|
57760
|
+
var links, folders, pinned, warned, callApp, PUSH_WAIT_MS, RECONCILE_WAIT_MS, wait, pushes, lateNotes, LOST_WINDOW, OPENING_TOOLS, toolDefs3, POSE_INTERVAL_MS, poseBusy, lastPose, pendingRestart, pendingLatest, poseTimer;
|
|
57617
57761
|
var init_app_link = __esm({
|
|
57618
57762
|
"src/app-link.ts"() {
|
|
57619
57763
|
"use strict";
|
|
57764
|
+
init_app_discovery();
|
|
57620
57765
|
init_session_core();
|
|
57621
57766
|
init_tool_def();
|
|
57622
57767
|
links = /* @__PURE__ */ new Map();
|
|
57623
57768
|
folders = /* @__PURE__ */ new Map();
|
|
57769
|
+
pinned = null;
|
|
57770
|
+
warned = "";
|
|
57624
57771
|
callApp = async (tool, args) => {
|
|
57625
57772
|
const url = endpoint();
|
|
57626
57773
|
if (!url) return null;
|
|
@@ -57690,7 +57837,7 @@ var init_app_link = __esm({
|
|
|
57690
57837
|
|
|
57691
57838
|
// src/tools/session.ts
|
|
57692
57839
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync6 } from "node:fs";
|
|
57693
|
-
import { dirname as dirname2, resolve as
|
|
57840
|
+
import { dirname as dirname2, resolve as resolve4 } from "node:path";
|
|
57694
57841
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
57695
57842
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
57696
57843
|
function durabilityWarning() {
|
|
@@ -57716,8 +57863,16 @@ function runInSessionScope(user, fn) {
|
|
|
57716
57863
|
return out;
|
|
57717
57864
|
}
|
|
57718
57865
|
async function hydrateSession(store, documentId) {
|
|
57719
|
-
|
|
57866
|
+
const proxied = process.env.VCAD_MCP_PROXIED === "1";
|
|
57867
|
+
if (documents.has(documentId) && !proxied) return true;
|
|
57720
57868
|
const doc = await store.load(documentId);
|
|
57869
|
+
if (doc && proxied && documents.has(documentId)) {
|
|
57870
|
+
if (JSON.stringify(documents.get(documentId)) !== JSON.stringify(doc)) {
|
|
57871
|
+
documents.set(documentId, doc);
|
|
57872
|
+
runDocumentEnterHooks(doc);
|
|
57873
|
+
}
|
|
57874
|
+
return true;
|
|
57875
|
+
}
|
|
57721
57876
|
if (doc) {
|
|
57722
57877
|
documents.set(documentId, doc);
|
|
57723
57878
|
runDocumentEnterHooks(doc);
|
|
@@ -57762,7 +57917,7 @@ function openDocument(args) {
|
|
|
57762
57917
|
};
|
|
57763
57918
|
}
|
|
57764
57919
|
function writeLocalSnapshot(id, json) {
|
|
57765
|
-
const dir =
|
|
57920
|
+
const dir = resolve4(stateRoot(), "get_document");
|
|
57766
57921
|
mkdirSync2(dir, { recursive: true });
|
|
57767
57922
|
const path = resolveWithinRoot(`${id}.vcad`, dir);
|
|
57768
57923
|
writeFileSync2(path, json);
|
|
@@ -57975,7 +58130,7 @@ async function loadDocumentFromPath(path, store, engine) {
|
|
|
57975
58130
|
"`path` loads are local/stdio only \u2014 this server's filesystem is ephemeral. Pass the source inline to create_cad_loon, or save with save_document and reopen by `name`."
|
|
57976
58131
|
);
|
|
57977
58132
|
}
|
|
57978
|
-
const abs =
|
|
58133
|
+
const abs = resolve4(path);
|
|
57979
58134
|
let raw;
|
|
57980
58135
|
try {
|
|
57981
58136
|
raw = readFileSync6(abs, "utf8");
|
|
@@ -61780,8 +61935,8 @@ async function openBridge(createServer2, clientInfo, clientCapabilities, hooks)
|
|
|
61780
61935
|
await server.connect(serverSide);
|
|
61781
61936
|
const rpc = (method, params) => {
|
|
61782
61937
|
const id = nextId3++;
|
|
61783
|
-
return new Promise((
|
|
61784
|
-
pending.set(id, (m) =>
|
|
61938
|
+
return new Promise((resolve9) => {
|
|
61939
|
+
pending.set(id, (m) => resolve9(m));
|
|
61785
61940
|
void clientSide.send({
|
|
61786
61941
|
jsonrpc: "2.0",
|
|
61787
61942
|
id,
|
|
@@ -62118,8 +62273,8 @@ function taskElicitation(bridge, reqParams) {
|
|
|
62118
62273
|
method: "elicitation/create",
|
|
62119
62274
|
params: cleanParams
|
|
62120
62275
|
};
|
|
62121
|
-
return new Promise((
|
|
62122
|
-
record2.pendingInputs.set(key3,
|
|
62276
|
+
return new Promise((resolve9) => {
|
|
62277
|
+
record2.pendingInputs.set(key3, resolve9);
|
|
62123
62278
|
touchTask(record2, "input_required", "waiting for client input");
|
|
62124
62279
|
});
|
|
62125
62280
|
}
|
|
@@ -62144,11 +62299,11 @@ function handleTaskMethod(id, method, params) {
|
|
|
62144
62299
|
if (method === "tasks/update") {
|
|
62145
62300
|
const responses = params.inputResponses && typeof params.inputResponses === "object" ? params.inputResponses : {};
|
|
62146
62301
|
for (const [key3, response] of Object.entries(responses)) {
|
|
62147
|
-
const
|
|
62148
|
-
if (!
|
|
62302
|
+
const resolve9 = record2.pendingInputs.get(key3);
|
|
62303
|
+
if (!resolve9) continue;
|
|
62149
62304
|
record2.pendingInputs.delete(key3);
|
|
62150
62305
|
delete record2.inputRequests[key3];
|
|
62151
|
-
|
|
62306
|
+
resolve9(response);
|
|
62152
62307
|
}
|
|
62153
62308
|
if (record2.status === "input_required" && Object.keys(record2.inputRequests).length === 0) {
|
|
62154
62309
|
touchTask(record2, "working", `running ${record2.toolName}`);
|
|
@@ -62160,10 +62315,10 @@ function handleTaskMethod(id, method, params) {
|
|
|
62160
62315
|
}
|
|
62161
62316
|
record2.cancelRequested = true;
|
|
62162
62317
|
if (record2.status === "working" || record2.status === "input_required") {
|
|
62163
|
-
for (const [key3,
|
|
62318
|
+
for (const [key3, resolve9] of record2.pendingInputs) {
|
|
62164
62319
|
record2.pendingInputs.delete(key3);
|
|
62165
62320
|
delete record2.inputRequests[key3];
|
|
62166
|
-
|
|
62321
|
+
resolve9({ action: "cancel" });
|
|
62167
62322
|
}
|
|
62168
62323
|
touchTask(record2, "cancelled", "cancelled by client");
|
|
62169
62324
|
}
|
|
@@ -62841,7 +62996,7 @@ function normalizeOp(op, index) {
|
|
|
62841
62996
|
return { name, args };
|
|
62842
62997
|
}
|
|
62843
62998
|
function resolveSymbolicRefs(args, createdIds, index) {
|
|
62844
|
-
const
|
|
62999
|
+
const resolve9 = (v) => {
|
|
62845
63000
|
if (typeof v !== "string") return v;
|
|
62846
63001
|
const m = SYMBOLIC_REF.exec(v);
|
|
62847
63002
|
if (!m) return v;
|
|
@@ -62861,15 +63016,15 @@ function resolveSymbolicRefs(args, createdIds, index) {
|
|
|
62861
63016
|
};
|
|
62862
63017
|
const out = { ...args };
|
|
62863
63018
|
for (const key3 of ["part_id", "node_id"]) {
|
|
62864
|
-
if (key3 in out) out[key3] =
|
|
63019
|
+
if (key3 in out) out[key3] = resolve9(out[key3]);
|
|
62865
63020
|
}
|
|
62866
63021
|
if (out.params && typeof out.params === "object" && !Array.isArray(out.params)) {
|
|
62867
63022
|
const params = { ...out.params };
|
|
62868
63023
|
for (const key3 of ["child", "left", "right", "sketch"]) {
|
|
62869
|
-
if (key3 in params) params[key3] =
|
|
63024
|
+
if (key3 in params) params[key3] = resolve9(params[key3]);
|
|
62870
63025
|
}
|
|
62871
63026
|
if (Array.isArray(params.sketches)) {
|
|
62872
|
-
params.sketches = params.sketches.map(
|
|
63027
|
+
params.sketches = params.sketches.map(resolve9);
|
|
62873
63028
|
}
|
|
62874
63029
|
out.params = params;
|
|
62875
63030
|
}
|
|
@@ -67532,7 +67687,7 @@ async function flattenSchematic(sheet, netlister) {
|
|
|
67532
67687
|
if (def.body.hierarchy) {
|
|
67533
67688
|
err9(`Sheet '${def.name}' body has its own hierarchy \u2014 nest sheets with \`instances\` instead (ignored)`);
|
|
67534
67689
|
}
|
|
67535
|
-
const
|
|
67690
|
+
const resolve9 = (name) => {
|
|
67536
67691
|
if (ports.has(name)) {
|
|
67537
67692
|
const bound = binding.get(name);
|
|
67538
67693
|
if (bound !== void 0) return bound;
|
|
@@ -67560,7 +67715,7 @@ async function flattenSchematic(sheet, netlister) {
|
|
|
67560
67715
|
if (pins.length === 0) continue;
|
|
67561
67716
|
if (isAutoName(name) && pins.length < 2) continue;
|
|
67562
67717
|
usedNames.add(name);
|
|
67563
|
-
const flat =
|
|
67718
|
+
const flat = resolve9(name);
|
|
67564
67719
|
if (!ports.has(name) && !globals.has(name) && rootNames.has(flat)) {
|
|
67565
67720
|
err9(
|
|
67566
67721
|
`Net scope collision: local net '${name}' of sheet instance '${path}' flattens to '${flat}', which the root sheet also uses`
|
|
@@ -67584,9 +67739,9 @@ async function flattenSchematic(sheet, netlister) {
|
|
|
67584
67739
|
warn(`Hierarchical port '${port}' of sheet '${def.name}' connects to nothing inside the sheet`);
|
|
67585
67740
|
}
|
|
67586
67741
|
}
|
|
67587
|
-
for (const flag of def.body.power_flags ?? []) outFlags.push({ ...flag, net:
|
|
67742
|
+
for (const flag of def.body.power_flags ?? []) outFlags.push({ ...flag, net: resolve9(flag.net) });
|
|
67588
67743
|
instances.push({ path, sheet: def.name, refs });
|
|
67589
|
-
await elaborateAll(def.instances ?? [], path, offset,
|
|
67744
|
+
await elaborateAll(def.instances ?? [], path, offset, resolve9, stack);
|
|
67590
67745
|
};
|
|
67591
67746
|
await elaborateAll(h.instances ?? [], "", { x: 0, y: 0 }, (n) => n, []);
|
|
67592
67747
|
const nets = {};
|
|
@@ -69141,12 +69296,12 @@ var init_enclosure = __esm({
|
|
|
69141
69296
|
// src/wasm/ecad-diff.ts
|
|
69142
69297
|
import { createRequire } from "node:module";
|
|
69143
69298
|
import { fileURLToPath } from "node:url";
|
|
69144
|
-
import { dirname as dirname3, resolve as
|
|
69299
|
+
import { dirname as dirname3, resolve as resolve5 } from "node:path";
|
|
69145
69300
|
function load() {
|
|
69146
69301
|
if (cached2 !== void 0) return cached2;
|
|
69147
69302
|
try {
|
|
69148
69303
|
const here = dirname3(fileURLToPath(import.meta.url));
|
|
69149
|
-
const pkg =
|
|
69304
|
+
const pkg = resolve5(here, "../../../../crates/vcad-ecad-diff-wasm/pkg/vcad_ecad_diff_wasm.js");
|
|
69150
69305
|
const req = createRequire(import.meta.url);
|
|
69151
69306
|
cached2 = req(pkg);
|
|
69152
69307
|
} catch {
|
|
@@ -83028,7 +83183,7 @@ import { execFile } from "child_process";
|
|
|
83028
83183
|
import {
|
|
83029
83184
|
existsSync as existsSync4,
|
|
83030
83185
|
mkdtempSync as mkdtempSync2,
|
|
83031
|
-
readdirSync as
|
|
83186
|
+
readdirSync as readdirSync3,
|
|
83032
83187
|
readFileSync as readFileSync8,
|
|
83033
83188
|
rmSync,
|
|
83034
83189
|
writeFileSync as writeFileSync5
|
|
@@ -83086,7 +83241,7 @@ function listEvalTasks(args) {
|
|
|
83086
83241
|
const tasksDir = join6(mechevalRoot, "tasks");
|
|
83087
83242
|
const suiteFilter = args.suite ? String(args.suite).toUpperCase() : null;
|
|
83088
83243
|
const tasks2 = [];
|
|
83089
|
-
for (const file of
|
|
83244
|
+
for (const file of readdirSync3(tasksDir).sort()) {
|
|
83090
83245
|
if (!file.endsWith(".json")) continue;
|
|
83091
83246
|
try {
|
|
83092
83247
|
const raw = JSON.parse(readFileSync8(join6(tasksDir, file), "utf8"));
|
|
@@ -87474,14 +87629,14 @@ var init_acoustics = __esm({
|
|
|
87474
87629
|
// src/tools/import.ts
|
|
87475
87630
|
import { createHash as createHash6 } from "node:crypto";
|
|
87476
87631
|
import { readFileSync as readFileSync9, existsSync as existsSync5, statSync as statSync2, mkdirSync as mkdirSync3, writeFileSync as writeFileSync7 } from "node:fs";
|
|
87477
|
-
import { basename, join as join7, resolve as
|
|
87632
|
+
import { basename, join as join7, resolve as resolve6, sep as sep3 } from "node:path";
|
|
87478
87633
|
function exportRoot() {
|
|
87479
87634
|
return process.env.VCAD_MCP_EXPORT_DIR ?? process.cwd();
|
|
87480
87635
|
}
|
|
87481
87636
|
function isWithinExportRoot(path) {
|
|
87482
87637
|
if (path.includes("\0")) return false;
|
|
87483
|
-
const root =
|
|
87484
|
-
const abs =
|
|
87638
|
+
const root = resolve6(exportRoot());
|
|
87639
|
+
const abs = resolve6(path);
|
|
87485
87640
|
return abs === root || abs.startsWith(root + sep3);
|
|
87486
87641
|
}
|
|
87487
87642
|
function stepNodePath(bytes, diskPath, label) {
|
|
@@ -87492,7 +87647,7 @@ function stepNodePath(bytes, diskPath, label) {
|
|
|
87492
87647
|
mkdirSync3(dir, { recursive: true });
|
|
87493
87648
|
const file = join7(dir, `${digest}.step`);
|
|
87494
87649
|
if (!existsSync5(file)) writeFileSync7(file, bytes);
|
|
87495
|
-
return { path:
|
|
87650
|
+
return { path: resolve6(file), portable: true };
|
|
87496
87651
|
} catch {
|
|
87497
87652
|
return { path: `step:${digest}/${label}`, portable: false };
|
|
87498
87653
|
}
|
|
@@ -87568,7 +87723,7 @@ function readStepInput(input) {
|
|
|
87568
87723
|
if (stat.size > MAX_STEP_BYTES) {
|
|
87569
87724
|
throw new Error(`STEP file exceeds ${MAX_STEP_BYTES} byte limit`);
|
|
87570
87725
|
}
|
|
87571
|
-
return { bytes: readFileSync9(filepath), diskPath:
|
|
87726
|
+
return { bytes: readFileSync9(filepath), diskPath: resolve6(filepath) };
|
|
87572
87727
|
}
|
|
87573
87728
|
function skippedFaceSummary(report, warnings) {
|
|
87574
87729
|
const skipped = report.reduce((sum, s) => sum + s.skipped_faces.length, 0);
|
|
@@ -87741,11 +87896,11 @@ var init_import = __esm({
|
|
|
87741
87896
|
|
|
87742
87897
|
// src/tools/import-urdf.ts
|
|
87743
87898
|
import { readFileSync as readFileSync10, existsSync as existsSync6, statSync as statSync3 } from "node:fs";
|
|
87744
|
-
import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute5, resolve as
|
|
87899
|
+
import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute5, resolve as resolve7, sep as sep4 } from "node:path";
|
|
87745
87900
|
function resolveMeshPath(filename, urdfDir, packageRoots, confineRoot) {
|
|
87746
87901
|
const confined = (p) => {
|
|
87747
|
-
const abs =
|
|
87748
|
-
const root =
|
|
87902
|
+
const abs = resolve7(p);
|
|
87903
|
+
const root = resolve7(confineRoot);
|
|
87749
87904
|
const rootSep = root.endsWith(sep4) ? root : root + sep4;
|
|
87750
87905
|
if (abs !== root && !abs.startsWith(rootSep)) return null;
|
|
87751
87906
|
return existsSync6(abs) && statSync3(abs).isFile() ? abs : null;
|
|
@@ -87754,14 +87909,14 @@ function resolveMeshPath(filename, urdfDir, packageRoots, confineRoot) {
|
|
|
87754
87909
|
const pkgMatch = filename.match(/^package:\/\/([^/]+)\/?(.*)$/);
|
|
87755
87910
|
if (pkgMatch) {
|
|
87756
87911
|
for (const root of packageRoots) {
|
|
87757
|
-
const hit = confined(
|
|
87912
|
+
const hit = confined(resolve7(root, pkgMatch[1], pkgMatch[2]));
|
|
87758
87913
|
if (hit) return hit;
|
|
87759
87914
|
}
|
|
87760
87915
|
return null;
|
|
87761
87916
|
}
|
|
87762
87917
|
const stripped = filename.startsWith("file://") ? filename.slice("file://".length) : filename;
|
|
87763
87918
|
if (isAbsolute5(stripped)) return confined(stripped);
|
|
87764
|
-
if (urdfDir) return confined(
|
|
87919
|
+
if (urdfDir) return confined(resolve7(urdfDir, stripped));
|
|
87765
87920
|
return null;
|
|
87766
87921
|
}
|
|
87767
87922
|
function inlineMeshImports2(doc, engine, urdfDir, packageRoots, confineRoot) {
|
|
@@ -91004,13 +91159,13 @@ ${issues.map((i) => i.problem).join("\n")}`
|
|
|
91004
91159
|
}
|
|
91005
91160
|
async function hasFfmpeg() {
|
|
91006
91161
|
if (ffmpegAvailable !== null) return ffmpegAvailable;
|
|
91007
|
-
ffmpegAvailable = await new Promise((
|
|
91162
|
+
ffmpegAvailable = await new Promise((resolve9) => {
|
|
91008
91163
|
try {
|
|
91009
91164
|
const p = spawn("ffmpeg", ["-version"], { stdio: "ignore" });
|
|
91010
|
-
p.on("error", () =>
|
|
91011
|
-
p.on("exit", (code) =>
|
|
91165
|
+
p.on("error", () => resolve9(false));
|
|
91166
|
+
p.on("exit", (code) => resolve9(code === 0));
|
|
91012
91167
|
} catch {
|
|
91013
|
-
|
|
91168
|
+
resolve9(false);
|
|
91014
91169
|
}
|
|
91015
91170
|
});
|
|
91016
91171
|
return ffmpegAvailable;
|
|
@@ -91049,13 +91204,13 @@ function startMp4Encoder(width, height, fps) {
|
|
|
91049
91204
|
const exitError = (code) => new Error(
|
|
91050
91205
|
`ffmpeg exited ${code}: ${Buffer.concat(errChunks).toString().slice(-400)}`
|
|
91051
91206
|
);
|
|
91052
|
-
const done = new Promise((
|
|
91207
|
+
const done = new Promise((resolve9, reject) => {
|
|
91053
91208
|
p.on("error", (e) => {
|
|
91054
91209
|
failure2 = e;
|
|
91055
91210
|
reject(e);
|
|
91056
91211
|
});
|
|
91057
91212
|
p.on("close", (code) => {
|
|
91058
|
-
if (code === 0)
|
|
91213
|
+
if (code === 0) resolve9(Buffer.concat(out));
|
|
91059
91214
|
else {
|
|
91060
91215
|
failure2 ??= exitError(code);
|
|
91061
91216
|
reject(failure2);
|
|
@@ -91066,15 +91221,15 @@ function startMp4Encoder(width, height, fps) {
|
|
|
91066
91221
|
});
|
|
91067
91222
|
return {
|
|
91068
91223
|
writeFrame(rgba) {
|
|
91069
|
-
return new Promise((
|
|
91224
|
+
return new Promise((resolve9, reject) => {
|
|
91070
91225
|
if (failure2) return reject(failure2);
|
|
91071
91226
|
if (p.exitCode !== null || p.stdin.destroyed) {
|
|
91072
91227
|
return reject(failure2 ?? exitError(p.exitCode));
|
|
91073
91228
|
}
|
|
91074
|
-
if (p.stdin.write(rgba)) return
|
|
91229
|
+
if (p.stdin.write(rgba)) return resolve9();
|
|
91075
91230
|
const onDrain = () => {
|
|
91076
91231
|
p.removeListener("close", onClose);
|
|
91077
|
-
|
|
91232
|
+
resolve9();
|
|
91078
91233
|
};
|
|
91079
91234
|
const onClose = () => {
|
|
91080
91235
|
p.stdin.removeListener("drain", onDrain);
|
|
@@ -92634,8 +92789,8 @@ var init_server3 = __esm({
|
|
|
92634
92789
|
resolveLoonAssets(doc, { base_dir }, engine);
|
|
92635
92790
|
});
|
|
92636
92791
|
PKG_VERSION = (() => {
|
|
92637
|
-
if ("0.11.0-main.
|
|
92638
|
-
return "0.11.0-main.
|
|
92792
|
+
if ("0.11.0-main.164") {
|
|
92793
|
+
return "0.11.0-main.164";
|
|
92639
92794
|
}
|
|
92640
92795
|
try {
|
|
92641
92796
|
const req = createRequire3(import.meta.url);
|
|
@@ -92644,8 +92799,8 @@ var init_server3 = __esm({
|
|
|
92644
92799
|
return "0.0.0";
|
|
92645
92800
|
}
|
|
92646
92801
|
})();
|
|
92647
|
-
BUILD_SHA = "
|
|
92648
|
-
BUILD_TIME = "2026-11-
|
|
92802
|
+
BUILD_SHA = "f36361fa47f1a47d196b4d9a8bcdb421c1cc785f";
|
|
92803
|
+
BUILD_TIME = "2026-11-11T21:56:09.744Z";
|
|
92649
92804
|
SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
|
|
92650
92805
|
VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
|
|
92651
92806
|
INSTANCE_ID = randomUUID5().slice(0, 8);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vcad/mcp",
|
|
3
|
-
"version": "0.11.0-main.
|
|
3
|
+
"version": "0.11.0-main.164",
|
|
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://github.com/muni-works/vcad",
|
|
21
21
|
"vcadBuild": {
|
|
22
|
-
"sha": "
|
|
23
|
-
"builtAt": "2026-11-
|
|
22
|
+
"sha": "f36361fa47f1a47d196b4d9a8bcdb421c1cc785f",
|
|
23
|
+
"builtAt": "2026-11-11T21:56:09.744Z"
|
|
24
24
|
}
|
|
25
25
|
}
|
package/skill/SKILL.md
CHANGED
|
@@ -81,6 +81,28 @@ Call `server_info` first.
|
|
|
81
81
|
Tell the user and suggest restarting their agent (or updating vcad.app).
|
|
82
82
|
- `durable: false` → sessions won't survive a restart; keep the loon source.
|
|
83
83
|
|
|
84
|
+
## More than one vcad.app
|
|
85
|
+
|
|
86
|
+
Each running vcad.app registers itself in `~/.vcad/instances/<pid>.json`
|
|
87
|
+
(pid, port, open document, launch time); `~/.vcad/app.json` only points at
|
|
88
|
+
the newest. Every app-bridge result ends with `[vcad.app pid N · document]`
|
|
89
|
+
and carries `_meta["io.vcad/instance"]`: check it names the window you mean.
|
|
90
|
+
A `⚠ vcad.app CHANGED` notice means the app relaunched or another window took
|
|
91
|
+
over — re-check the open document before trusting earlier state.
|
|
92
|
+
|
|
93
|
+
Pick the window explicitly when more than one runs:
|
|
94
|
+
|
|
95
|
+
- `VCAD_APP_PID=<pid>` — that instance; fails loudly if it's gone.
|
|
96
|
+
- `VCAD_APP_URL=http://127.0.0.1:<port>/mcp` — that endpoint, verbatim.
|
|
97
|
+
- `VCAD_APP_DOCUMENT=<path>` — the instance with that file open.
|
|
98
|
+
- otherwise the newest, with a warning listing the others. The choice is
|
|
99
|
+
pinned for the client's lifetime; a later launch never steals it.
|
|
100
|
+
- `VCAD_APP=0` — `@vcad/mcp` doesn't link to any app.
|
|
101
|
+
|
|
102
|
+
**Never `pkill`/`killall` vcad.app by pattern.** Other sessions may be driving
|
|
103
|
+
other windows, and to them it looks like a crash. Quit only an instance you
|
|
104
|
+
launched, by its pid (`kill <pid>`, from `~/.vcad/instances/`).
|
|
105
|
+
|
|
84
106
|
## Ground rules
|
|
85
107
|
|
|
86
108
|
- vcad has no hosted services. Never send the user to a website to view,
|