@vcad/mcp 0.10.0-main.132 → 0.10.0-main.134
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 +157 -47
- 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 50d4e0c1c282745ef88e456dca033ceabdee9c31 at 2026-10-21T17:58:04.042Z. Source: https://github.com/ecto/vcad
|
package/index.mjs
CHANGED
|
@@ -10887,6 +10887,15 @@ function handleEngineRequest(host, req) {
|
|
|
10887
10887
|
const engine2 = host.engine;
|
|
10888
10888
|
if (!engine2)
|
|
10889
10889
|
return { ok: false, message: "engine not initialized" };
|
|
10890
|
+
if ((req.op === "apply" || req.op === "applyTool") && req.expectHash !== void 0 && engine2.stateHash() !== req.expectHash) {
|
|
10891
|
+
return {
|
|
10892
|
+
ok: true,
|
|
10893
|
+
stale: true,
|
|
10894
|
+
hash: engine2.stateHash(),
|
|
10895
|
+
canUndo: engine2.can_undo(),
|
|
10896
|
+
canRedo: engine2.can_redo()
|
|
10897
|
+
};
|
|
10898
|
+
}
|
|
10890
10899
|
if (req.op === "apply")
|
|
10891
10900
|
envelope = engine2.applyDelta(JSON.stringify(req.command));
|
|
10892
10901
|
else if (req.op === "applyTool")
|
|
@@ -10920,6 +10929,19 @@ function inProcessTransport(Engine2) {
|
|
|
10920
10929
|
}
|
|
10921
10930
|
};
|
|
10922
10931
|
}
|
|
10932
|
+
function sharedEngineTransport(getEngine) {
|
|
10933
|
+
const ownerOnly = function() {
|
|
10934
|
+
throw new Error("shared engine: the owner constructs engines");
|
|
10935
|
+
};
|
|
10936
|
+
return {
|
|
10937
|
+
request: async (req) => {
|
|
10938
|
+
const host = { Engine: ownerOnly, engine: getEngine() };
|
|
10939
|
+
const r = req.op === "init" || req.op === "load" ? { op: "state" } : req;
|
|
10940
|
+
return handleEngineRequest(host, r);
|
|
10941
|
+
},
|
|
10942
|
+
dispose: () => void 0
|
|
10943
|
+
};
|
|
10944
|
+
}
|
|
10923
10945
|
function workerTransport(worker) {
|
|
10924
10946
|
let next = 0;
|
|
10925
10947
|
const pending2 = /* @__PURE__ */ new Map();
|
|
@@ -11009,10 +11031,17 @@ var init_engine_host = __esm({
|
|
|
11009
11031
|
}
|
|
11010
11032
|
delta(req) {
|
|
11011
11033
|
return this.enqueue(async () => {
|
|
11012
|
-
|
|
11013
|
-
if (!base)
|
|
11034
|
+
if (!this.view)
|
|
11014
11035
|
throw new EngineHostError("engine not initialized");
|
|
11015
|
-
|
|
11036
|
+
let r = this.check(await this.transport.request({ ...req, expectHash: this.flags.hash }));
|
|
11037
|
+
if (r.stale) {
|
|
11038
|
+
const full = this.check(await this.transport.request({ op: "state" }));
|
|
11039
|
+
this.view = stateFromEnvelope(full.envelope);
|
|
11040
|
+
r = this.check(await this.transport.request({ ...req, expectHash: this.flags.hash }));
|
|
11041
|
+
if (r.stale)
|
|
11042
|
+
throw new EngineHostError("engine changed during resync");
|
|
11043
|
+
}
|
|
11044
|
+
const base = this.view;
|
|
11016
11045
|
const delta = parseDeltaEnvelope(r.envelope);
|
|
11017
11046
|
const { state, changes } = applyEngineDelta(base, delta);
|
|
11018
11047
|
this.view = state;
|
|
@@ -12310,6 +12339,7 @@ __export(dist_exports, {
|
|
|
12310
12339
|
searchEcadParts: () => searchEcadParts,
|
|
12311
12340
|
searchParts: () => searchParts,
|
|
12312
12341
|
semanticDiff: () => semanticDiff,
|
|
12342
|
+
sharedEngineTransport: () => sharedEngineTransport,
|
|
12313
12343
|
solveDesignConstraints: () => solveDesignConstraints2,
|
|
12314
12344
|
solveForwardKinematics: () => solveForwardKinematics2,
|
|
12315
12345
|
stateFromEnvelope: () => stateFromEnvelope,
|
|
@@ -31847,6 +31877,7 @@ var init_document_store = __esm({
|
|
|
31847
31877
|
init_esm2();
|
|
31848
31878
|
init_wasm_singleton2();
|
|
31849
31879
|
init_engine_command();
|
|
31880
|
+
init_dist();
|
|
31850
31881
|
init_sketch_constraint_persist();
|
|
31851
31882
|
init_dist2();
|
|
31852
31883
|
init_dist2();
|
|
@@ -31873,6 +31904,27 @@ var init_document_store = __esm({
|
|
|
31873
31904
|
// CRDT bridge state
|
|
31874
31905
|
_crdtEngine: null,
|
|
31875
31906
|
_crdtEngineClass: null,
|
|
31907
|
+
_engineClient: new DocumentEngineClient(sharedEngineTransport(() => get()._crdtEngine)),
|
|
31908
|
+
dispatch: async (command) => {
|
|
31909
|
+
const readOnly = useUiStore.getState().readOnlyShare;
|
|
31910
|
+
if (readOnly) {
|
|
31911
|
+
if (typeof window !== "undefined") {
|
|
31912
|
+
window.dispatchEvent(new CustomEvent("vcad:fork-prompt", { detail: readOnly }));
|
|
31913
|
+
}
|
|
31914
|
+
return void 0;
|
|
31915
|
+
}
|
|
31916
|
+
const client = get()._engineClient;
|
|
31917
|
+
if (!client.state)
|
|
31918
|
+
await client.init();
|
|
31919
|
+
const outcome = await client.apply(command);
|
|
31920
|
+
for (const w of outcome.warnings)
|
|
31921
|
+
console.warn(`[vcad] ${w}`);
|
|
31922
|
+
set(applyLegacyResult({
|
|
31923
|
+
document: outcome.state.document,
|
|
31924
|
+
parts: outcome.state.parts
|
|
31925
|
+
}));
|
|
31926
|
+
return outcome.createdFeatureId;
|
|
31927
|
+
},
|
|
31876
31928
|
_initCrdt: (EngineClass) => {
|
|
31877
31929
|
const existing = get()._crdtEngine;
|
|
31878
31930
|
const existingClass = get()._crdtEngineClass;
|
|
@@ -31977,10 +32029,8 @@ var init_document_store = __esm({
|
|
|
31977
32029
|
return "";
|
|
31978
32030
|
}
|
|
31979
32031
|
},
|
|
31980
|
-
removePart: (partId) => {
|
|
31981
|
-
|
|
31982
|
-
const result = applyCommand(engine, { type: "delete_feature", id: partId });
|
|
31983
|
-
set(applyApiResult(result));
|
|
32032
|
+
removePart: async (partId) => {
|
|
32033
|
+
await get().dispatch({ type: "delete_feature", id: partId });
|
|
31984
32034
|
},
|
|
31985
32035
|
setTranslation: (partId, offset) => {
|
|
31986
32036
|
const engine = get()._crdtEngine;
|
|
@@ -32064,10 +32114,8 @@ var init_document_store = __esm({
|
|
|
32064
32114
|
const result = applyCommand(engine, { type: "set_param", id: partId, key: key3, value });
|
|
32065
32115
|
set(applyLegacyResult(result));
|
|
32066
32116
|
},
|
|
32067
|
-
updateBooleanType: (partId, newType) => {
|
|
32068
|
-
|
|
32069
|
-
const result = applyCommand(engine, { type: "set_param", id: partId, key: "boolean_type", value: crdtStr(newType) });
|
|
32070
|
-
set(applyLegacyResult(result));
|
|
32117
|
+
updateBooleanType: async (partId, newType) => {
|
|
32118
|
+
await get().dispatch({ type: "set_param", id: partId, key: "boolean_type", value: crdtStr(newType) });
|
|
32071
32119
|
},
|
|
32072
32120
|
applyBoolean: (type, partIdA, partIdB) => {
|
|
32073
32121
|
const engine = get()._crdtEngine;
|
|
@@ -32341,9 +32389,8 @@ var init_document_store = __esm({
|
|
|
32341
32389
|
return null;
|
|
32342
32390
|
}
|
|
32343
32391
|
},
|
|
32344
|
-
setThreadColor: (nodeId, threadIdx, color) => {
|
|
32392
|
+
setThreadColor: async (nodeId, threadIdx, color) => {
|
|
32345
32393
|
const state = get();
|
|
32346
|
-
const engine = state._crdtEngine;
|
|
32347
32394
|
const part = state.parts.find((p) => p.kind === "embroidery-pattern" && p.patternNodeId === nodeId);
|
|
32348
32395
|
if (!part)
|
|
32349
32396
|
return;
|
|
@@ -32353,12 +32400,10 @@ var init_document_store = __esm({
|
|
|
32353
32400
|
const d = structuredClone(design);
|
|
32354
32401
|
if (d.threads[threadIdx])
|
|
32355
32402
|
d.threads[threadIdx].color = color;
|
|
32356
|
-
|
|
32357
|
-
set(applyLegacyResult(result));
|
|
32403
|
+
await get().dispatch({ type: "set_param", id: part.id, key: "design", value: crdtStr(JSON.stringify(d)) });
|
|
32358
32404
|
},
|
|
32359
|
-
setThreadName: (nodeId, threadIdx, name) => {
|
|
32405
|
+
setThreadName: async (nodeId, threadIdx, name) => {
|
|
32360
32406
|
const state = get();
|
|
32361
|
-
const engine = state._crdtEngine;
|
|
32362
32407
|
const part = state.parts.find((p) => p.kind === "embroidery-pattern" && p.patternNodeId === nodeId);
|
|
32363
32408
|
if (!part)
|
|
32364
32409
|
return;
|
|
@@ -32368,8 +32413,7 @@ var init_document_store = __esm({
|
|
|
32368
32413
|
const d = structuredClone(design);
|
|
32369
32414
|
if (d.threads[threadIdx])
|
|
32370
32415
|
d.threads[threadIdx].name = name;
|
|
32371
|
-
|
|
32372
|
-
set(applyLegacyResult(result));
|
|
32416
|
+
await get().dispatch({ type: "set_param", id: part.id, key: "design", value: crdtStr(JSON.stringify(d)) });
|
|
32373
32417
|
},
|
|
32374
32418
|
setStitchGroupFillParams: (nodeId, groupIdx, params) => {
|
|
32375
32419
|
const state = get();
|
|
@@ -32604,15 +32648,11 @@ var init_document_store = __esm({
|
|
|
32604
32648
|
set(applyApiResult(result));
|
|
32605
32649
|
return result.createdFeatureId ?? null;
|
|
32606
32650
|
},
|
|
32607
|
-
setPartMaterial: (partId, materialKey) => {
|
|
32608
|
-
|
|
32609
|
-
const result = applyCommand(engine, { type: "set_material", id: partId, material: materialKey });
|
|
32610
|
-
set(applyApiResult(result));
|
|
32651
|
+
setPartMaterial: async (partId, materialKey) => {
|
|
32652
|
+
await get().dispatch({ type: "set_material", id: partId, material: materialKey });
|
|
32611
32653
|
},
|
|
32612
|
-
renamePart: (partId, name) => {
|
|
32613
|
-
|
|
32614
|
-
const result = applyCommand(engine, { type: "rename", id: partId, name });
|
|
32615
|
-
set(applyApiResult(result));
|
|
32654
|
+
renamePart: async (partId, name) => {
|
|
32655
|
+
await get().dispatch({ type: "rename", id: partId, name });
|
|
32616
32656
|
},
|
|
32617
32657
|
undo: () => {
|
|
32618
32658
|
const engine = get()._crdtEngine;
|
|
@@ -32645,10 +32685,8 @@ var init_document_store = __esm({
|
|
|
32645
32685
|
const result = applyCommand(engine, { type: "set_param", id: instanceId, key: "transform", value: crdtStr(JSON.stringify(transform2)) });
|
|
32646
32686
|
set(applyLegacyResult(result));
|
|
32647
32687
|
},
|
|
32648
|
-
setInstanceMaterial: (instanceId, materialKey) => {
|
|
32649
|
-
|
|
32650
|
-
const result = applyCommand(engine, { type: "set_param", id: instanceId, key: "material", value: crdtStr(materialKey) });
|
|
32651
|
-
set(applyLegacyResult(result));
|
|
32688
|
+
setInstanceMaterial: async (instanceId, materialKey) => {
|
|
32689
|
+
await get().dispatch({ type: "set_param", id: instanceId, key: "material", value: crdtStr(materialKey) });
|
|
32652
32690
|
},
|
|
32653
32691
|
setJointState: (jointId, jointState) => {
|
|
32654
32692
|
const engine = get()._crdtEngine;
|
|
@@ -32719,10 +32757,8 @@ var init_document_store = __esm({
|
|
|
32719
32757
|
const result = applyCommand(engine, { type: "delete_feature", id: instanceId });
|
|
32720
32758
|
set(applyApiResult(result));
|
|
32721
32759
|
},
|
|
32722
|
-
deleteJoint: (jointId) => {
|
|
32723
|
-
|
|
32724
|
-
const result = applyCommand(engine, { type: "delete_feature", id: jointId });
|
|
32725
|
-
set(applyApiResult(result));
|
|
32760
|
+
deleteJoint: async (jointId) => {
|
|
32761
|
+
await get().dispatch({ type: "delete_feature", id: jointId });
|
|
32726
32762
|
},
|
|
32727
32763
|
setGroundInstance: (instanceId) => {
|
|
32728
32764
|
const state = get();
|
|
@@ -32738,10 +32774,8 @@ var init_document_store = __esm({
|
|
|
32738
32774
|
const result = applyCommand(engine, { type: "set_param", id: instanceId, key: "is_ground", value: crdtBool(true) });
|
|
32739
32775
|
set(applyLegacyResult(result));
|
|
32740
32776
|
},
|
|
32741
|
-
renameInstance: (instanceId, name) => {
|
|
32742
|
-
|
|
32743
|
-
const result = applyCommand(engine, { type: "set_param", id: instanceId, key: "name", value: crdtStr(name) });
|
|
32744
|
-
set(applyLegacyResult(result));
|
|
32777
|
+
renameInstance: async (instanceId, name) => {
|
|
32778
|
+
await get().dispatch({ type: "set_param", id: instanceId, key: "name", value: crdtStr(name) });
|
|
32745
32779
|
},
|
|
32746
32780
|
setDocumentMeta: (id, name) => {
|
|
32747
32781
|
set({ documentId: id, documentName: name });
|
|
@@ -32800,10 +32834,8 @@ var init_document_store = __esm({
|
|
|
32800
32834
|
set(applyApiResult(envelope.result));
|
|
32801
32835
|
return envelope;
|
|
32802
32836
|
},
|
|
32803
|
-
setPartVisible: (partId, visible) => {
|
|
32804
|
-
|
|
32805
|
-
const result = applyCommand(engine, { type: "set_visible", id: partId, visible });
|
|
32806
|
-
set(applyApiResult(result));
|
|
32837
|
+
setPartVisible: async (partId, visible) => {
|
|
32838
|
+
await get().dispatch({ type: "set_visible", id: partId, visible });
|
|
32807
32839
|
},
|
|
32808
32840
|
reorderPart: (partId, newIndex) => {
|
|
32809
32841
|
const state = get();
|
|
@@ -41664,6 +41696,31 @@ var init_CHANGELOG = __esm({
|
|
|
41664
41696
|
CHANGELOG_default = {
|
|
41665
41697
|
$schema: "./changelog.schema.json",
|
|
41666
41698
|
entries: [
|
|
41699
|
+
{
|
|
41700
|
+
id: "2026-10-21-vcad-printer-cli",
|
|
41701
|
+
version: "0.10.0",
|
|
41702
|
+
date: "2026-10-21",
|
|
41703
|
+
category: "feat",
|
|
41704
|
+
title: "vcad printer: LAN control of a Bambu P1S",
|
|
41705
|
+
summary: "New `vcad printer` CLI: discover, status, camera snapshot and upload test over LAN; printing refuses without --confirm-print and a passing printcheck and file/printer match.",
|
|
41706
|
+
features: [
|
|
41707
|
+
"printing",
|
|
41708
|
+
"bambu",
|
|
41709
|
+
"cli"
|
|
41710
|
+
]
|
|
41711
|
+
},
|
|
41712
|
+
{
|
|
41713
|
+
id: "2026-10-21-slicer-first-layer-z",
|
|
41714
|
+
version: "0.10.0",
|
|
41715
|
+
date: "2026-10-21",
|
|
41716
|
+
category: "fix",
|
|
41717
|
+
title: "Slicer: layers print at full height, not half",
|
|
41718
|
+
summary: "Sliced G-code sent the nozzle to each layer's mid-plane, so the first layer was squashed to half height into the plate. Layers now print at their top.",
|
|
41719
|
+
features: [
|
|
41720
|
+
"printing",
|
|
41721
|
+
"slicer"
|
|
41722
|
+
]
|
|
41723
|
+
},
|
|
41667
41724
|
{
|
|
41668
41725
|
id: "2026-10-21-planar-cap-normals",
|
|
41669
41726
|
version: "0.10.0",
|
|
@@ -41690,6 +41747,19 @@ var init_CHANGELOG = __esm({
|
|
|
41690
41747
|
"lighting"
|
|
41691
41748
|
]
|
|
41692
41749
|
},
|
|
41750
|
+
{
|
|
41751
|
+
id: "2026-10-21-photoreal-look-textures",
|
|
41752
|
+
version: "0.10.0",
|
|
41753
|
+
date: "2026-10-21",
|
|
41754
|
+
category: "feat",
|
|
41755
|
+
title: "Material looks and wood/fabric textures in photoreal",
|
|
41756
|
+
summary: "--look applies render-time material overrides by material or part (colours, roughness, emission) plus procedural oak, fabric and triplanar image textures.",
|
|
41757
|
+
features: [
|
|
41758
|
+
"photoreal",
|
|
41759
|
+
"render",
|
|
41760
|
+
"materials"
|
|
41761
|
+
]
|
|
41762
|
+
},
|
|
41693
41763
|
{
|
|
41694
41764
|
id: "2026-10-21-photoreal-focus-dist",
|
|
41695
41765
|
version: "0.10.0",
|
|
@@ -41715,6 +41785,20 @@ var init_CHANGELOG = __esm({
|
|
|
41715
41785
|
"render"
|
|
41716
41786
|
]
|
|
41717
41787
|
},
|
|
41788
|
+
{
|
|
41789
|
+
id: "2026-10-21-mac-welcome",
|
|
41790
|
+
version: "0.10.0",
|
|
41791
|
+
date: "2026-10-21",
|
|
41792
|
+
category: "feat",
|
|
41793
|
+
title: "Mac app welcome window with samples and a loon tutorial",
|
|
41794
|
+
summary: "First launch opens a welcome window with four sample projects bundled in the app and a short tutorial, \u201Cyour first part in loon\u201D. Help \u25B8 Welcome to vcad reopens it.",
|
|
41795
|
+
features: [
|
|
41796
|
+
"desktop",
|
|
41797
|
+
"mac",
|
|
41798
|
+
"onboarding",
|
|
41799
|
+
"loon"
|
|
41800
|
+
]
|
|
41801
|
+
},
|
|
41718
41802
|
{
|
|
41719
41803
|
id: "2026-10-21-mac-sim-teardown-race",
|
|
41720
41804
|
version: "0.10.0",
|
|
@@ -41752,6 +41836,19 @@ var init_CHANGELOG = __esm({
|
|
|
41752
41836
|
"file-io"
|
|
41753
41837
|
]
|
|
41754
41838
|
},
|
|
41839
|
+
{
|
|
41840
|
+
id: "2026-10-21-mac-loon-loop",
|
|
41841
|
+
version: "0.10.0",
|
|
41842
|
+
date: "2026-10-21",
|
|
41843
|
+
category: "feat",
|
|
41844
|
+
title: "Mac app: loon errors inline, last good model kept",
|
|
41845
|
+
summary: "Open a .loon file or project folder and every save rebuilds; a broken edit shows file:line with Go to Line and keeps the last good model on screen. Module edits rebuild too.",
|
|
41846
|
+
features: [
|
|
41847
|
+
"desktop",
|
|
41848
|
+
"mac",
|
|
41849
|
+
"loon"
|
|
41850
|
+
]
|
|
41851
|
+
},
|
|
41755
41852
|
{
|
|
41756
41853
|
id: "2026-10-21-mac-labs-toggle",
|
|
41757
41854
|
version: "0.10.0",
|
|
@@ -41804,6 +41901,19 @@ var init_CHANGELOG = __esm({
|
|
|
41804
41901
|
"cli"
|
|
41805
41902
|
]
|
|
41806
41903
|
},
|
|
41904
|
+
{
|
|
41905
|
+
id: "2026-10-21-incremental-eval-hosts",
|
|
41906
|
+
version: "0.10.0",
|
|
41907
|
+
date: "2026-10-21",
|
|
41908
|
+
category: "perf",
|
|
41909
|
+
title: "Parameter scrubs re-evaluate only what changed",
|
|
41910
|
+
summary: "The Mac app and web eval worker keep an incremental evaluation session per document, so scrubbing a parameter rebuilds only the features downstream of it.",
|
|
41911
|
+
features: [
|
|
41912
|
+
"engine",
|
|
41913
|
+
"evaluation",
|
|
41914
|
+
"performance"
|
|
41915
|
+
]
|
|
41916
|
+
},
|
|
41807
41917
|
{
|
|
41808
41918
|
id: "2026-10-21-gpu-photoreal-limits",
|
|
41809
41919
|
version: "0.10.0",
|
|
@@ -87061,8 +87171,8 @@ var init_server3 = __esm({
|
|
|
87061
87171
|
init_order_feed();
|
|
87062
87172
|
init_animate();
|
|
87063
87173
|
PKG_VERSION = (() => {
|
|
87064
|
-
if ("0.10.0-main.
|
|
87065
|
-
return "0.10.0-main.
|
|
87174
|
+
if ("0.10.0-main.134") {
|
|
87175
|
+
return "0.10.0-main.134";
|
|
87066
87176
|
}
|
|
87067
87177
|
try {
|
|
87068
87178
|
const req = createRequire2(import.meta.url);
|
|
@@ -87071,8 +87181,8 @@ var init_server3 = __esm({
|
|
|
87071
87181
|
return "0.0.0";
|
|
87072
87182
|
}
|
|
87073
87183
|
})();
|
|
87074
|
-
BUILD_SHA = "
|
|
87075
|
-
BUILD_TIME = "2026-10-21T17:
|
|
87184
|
+
BUILD_SHA = "50d4e0c1c282745ef88e456dca033ceabdee9c31";
|
|
87185
|
+
BUILD_TIME = "2026-10-21T17:58:04.042Z";
|
|
87076
87186
|
SHORT_SHA = BUILD_SHA === "unknown" ? "unknown" : BUILD_SHA.slice(0, 7);
|
|
87077
87187
|
VERSION_WITH_BUILD = SHORT_SHA === "unknown" ? PKG_VERSION : `${PKG_VERSION}+${SHORT_SHA}`;
|
|
87078
87188
|
INSTANCE_ID = randomUUID7().slice(0, 8);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vcad/mcp",
|
|
3
|
-
"version": "0.10.0-main.
|
|
3
|
+
"version": "0.10.0-main.134",
|
|
4
4
|
"description": "vcad MCP server — parametric CAD + PCB design tools for AI agents (self-contained: bundled server + kernel WASM)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"homepage": "https://vcad.io",
|
|
21
21
|
"vcadBuild": {
|
|
22
|
-
"sha": "
|
|
23
|
-
"builtAt": "2026-10-21T17:
|
|
22
|
+
"sha": "50d4e0c1c282745ef88e456dca033ceabdee9c31",
|
|
23
|
+
"builtAt": "2026-10-21T17:58:04.042Z"
|
|
24
24
|
}
|
|
25
25
|
}
|