forgecad 0.10.3 → 0.10.4

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.
Files changed (99) hide show
  1. package/dist/assets/{AdminPage-CK7ObBz3.js → AdminPage-B3L3W1Uo.js} +1 -1
  2. package/dist/assets/{BenchmarkPage-Ds7Z2doN.js → BenchmarkPage-DXKVXMrJ.js} +2 -2
  3. package/dist/assets/{BlogPage-DlPbpt6A.js → BlogPage-B7BWxOCg.js} +1 -1
  4. package/dist/assets/{DocsPage-vZb3b3Y0.js → DocsPage-BPGGwht1.js} +28 -43
  5. package/dist/assets/{EditorApp-HLoKfe15.js → EditorApp-BWUGCdD5.js} +49 -16
  6. package/dist/assets/{EmbedViewer--KnqBKrJ.js → EmbedViewer-DygByZS2.js} +2 -2
  7. package/dist/assets/{LandingPageProofDriven-C_LssmnA.js → LandingPageProofDriven-BoVE7JGY.js} +54 -36
  8. package/dist/assets/{LegalPage-DGsyo4n1.js → LegalPage-Din8wv8d.js} +2 -2
  9. package/dist/assets/{PricingPage-BOE27B-R.js → PricingPage-C2PMzmDc.js} +2 -2
  10. package/dist/assets/{SettingsPage-f47cnk39.js → SettingsPage-BlJDCRe8.js} +1 -1
  11. package/dist/assets/{app-D6ccu2Xx.js → app-BsRYSfxY.js} +238 -3714
  12. package/dist/assets/{backendInit-DbTkQN9J.js → backendInit-6C0DLgH0.js} +5972 -1566
  13. package/dist/assets/cli/{render-BsngirjC.js → render-XXol_ET7.js} +724 -112
  14. package/dist/assets/{constructionHistoryWorker-PCwXrTDB.js → constructionHistoryWorker-cTHWRJEi.js} +528 -252
  15. package/dist/assets/{evalWorker-CS63PfZu.js → evalWorker-BssDYW9u.js} +1453 -902
  16. package/dist/assets/{inspectWorker-Y4cOzNyA.js → inspectWorker-ymhBV4Ll.js} +2635 -1024
  17. package/dist/assets/{jointPose-AMvCywzS.js → jointPose-B0blBj9A.js} +1 -1
  18. package/dist/assets/{landing-proof-driven-ORyigZ6p.css → landing-proof-driven-Cpf-MIbI.css} +73 -13
  19. package/dist/assets/{manifold-Crd_F2qx.js → manifold-B_7QXpGB.js} +1 -1
  20. package/dist/assets/{manifold-k2kRcc85.js → manifold-CNShmpEJ.js} +1 -1
  21. package/dist/assets/{manifold-CBry38ly.js → manifold-CYlIm-M6.js} +2 -2
  22. package/dist/assets/{reportWorker-CWvn0CEv.js → reportWorker-Cb5eyM7D.js} +1407 -892
  23. package/dist/cli/render.html +1 -1
  24. package/dist/docs/index.html +2 -2
  25. package/dist/docs-raw/AI/usage.md +17 -15
  26. package/dist/docs-raw/component-model.md +2 -2
  27. package/dist/docs-raw/generated/concepts.md +5 -1
  28. package/dist/docs-raw/generated/core.md +26 -0
  29. package/dist/docs-raw/generated/runtime-names.md +1 -1
  30. package/dist/docs-raw/guides/inspection-bundles.md +1 -1
  31. package/dist/docs-raw/simulation-workflow.md +1 -1
  32. package/dist/docs-raw/skills/{forgecad-make-a-model.md → forgecad-build-model.md} +18 -8
  33. package/dist/docs-raw/skills/{forgecad-spec-by-walking-through-it.md → forgecad-design-spec.md} +6 -6
  34. package/dist/docs-raw/skills/{forgecad-model-grader.md → forgecad-grade-model.md} +8 -6
  35. package/{dist-skill/website/skills/forgecad-visual-spec.md → dist/docs-raw/skills/forgecad-image-prompt.md} +7 -7
  36. package/dist/docs-raw/skills/{forgecad-render-inspect.md → forgecad-inspect-model.md} +6 -6
  37. package/{dist-skill/website/skills/forgecad-project.md → dist/docs-raw/skills/forgecad-project-sync.md} +5 -5
  38. package/dist/docs-raw/skills/{forgecad-3d-reconstruction.md → forgecad-reconstruct-cad-file.md} +7 -7
  39. package/dist/docs-raw/skills/{forgecad-image-replicator.md → forgecad-reconstruct-from-images.md} +12 -12
  40. package/dist/docs-raw/skills/{forgecad-mujoco-verify.md → forgecad-verify-mujoco.md} +6 -6
  41. package/dist/docs-raw/skills/index.md +9 -12
  42. package/dist/index.html +9 -9
  43. package/dist/llms.txt +7 -7
  44. package/dist/sitemap.xml +16 -16
  45. package/dist-cli/{check-compiler-HPF2T2FS.js → check-compiler-4RPB6SB5.js} +1 -1
  46. package/dist-cli/{check-query-propagation-HYSLTXAB.js → check-query-propagation-KN3DFQTX.js} +1 -1
  47. package/dist-cli/{chunk-WLUKAW3H.js → chunk-UHBRMYA6.js} +28802 -28152
  48. package/dist-cli/forgecad.js +660 -9
  49. package/dist-skill/CONTEXT.md +27 -1
  50. package/dist-skill/docs/generated/core.md +26 -0
  51. package/dist-skill/docs/generated/runtime-names.md +1 -1
  52. package/dist-skill/docs/guides/inspection-bundles.md +1 -1
  53. package/dist-skill/library/README.md +9 -12
  54. package/dist-skill/library/{forgecad-make-a-model → forgecad-build-model}/SKILL.md +16 -6
  55. package/dist-skill/library/{forgecad-spec-by-walking-through-it → forgecad-design-spec}/SKILL.md +4 -4
  56. package/dist-skill/library/{forgecad-spec-by-walking-through-it → forgecad-design-spec}/references/master-prompt.md +1 -1
  57. package/dist-skill/library/{forgecad-model-grader → forgecad-grade-model}/SKILL.md +6 -4
  58. package/dist-skill/library/forgecad-grade-model/agents/openai.yaml +4 -0
  59. package/dist-skill/library/{forgecad-visual-spec → forgecad-image-prompt}/SKILL.md +5 -5
  60. package/dist-skill/library/forgecad-image-prompt/agents/openai.yaml +4 -0
  61. package/dist-skill/library/{forgecad-render-inspect → forgecad-inspect-model}/SKILL.md +4 -4
  62. package/dist-skill/library/{forgecad-project → forgecad-project-sync}/SKILL.md +3 -3
  63. package/dist-skill/library/{forgecad-3d-reconstruction → forgecad-reconstruct-cad-file}/SKILL.md +5 -5
  64. package/dist-skill/library/forgecad-reconstruct-cad-file/agents/openai.yaml +4 -0
  65. package/dist-skill/library/{forgecad-image-replicator → forgecad-reconstruct-from-images}/SKILL.md +10 -10
  66. package/dist-skill/library/forgecad-reconstruct-from-images/agents/openai.yaml +4 -0
  67. package/dist-skill/library/{forgecad-mujoco-verify → forgecad-verify-mujoco}/SKILL.md +4 -4
  68. package/dist-skill/website/skills/{forgecad-make-a-model.md → forgecad-build-model.md} +18 -8
  69. package/dist-skill/website/skills/{forgecad-spec-by-walking-through-it.md → forgecad-design-spec.md} +6 -6
  70. package/dist-skill/website/skills/{forgecad-model-grader.md → forgecad-grade-model.md} +8 -6
  71. package/{dist/docs-raw/skills/forgecad-visual-spec.md → dist-skill/website/skills/forgecad-image-prompt.md} +7 -7
  72. package/dist-skill/website/skills/{forgecad-render-inspect.md → forgecad-inspect-model.md} +6 -6
  73. package/{dist/docs-raw/skills/forgecad-project.md → dist-skill/website/skills/forgecad-project-sync.md} +5 -5
  74. package/dist-skill/website/skills/{forgecad-3d-reconstruction.md → forgecad-reconstruct-cad-file.md} +7 -7
  75. package/dist-skill/website/skills/{forgecad-image-replicator.md → forgecad-reconstruct-from-images.md} +12 -12
  76. package/dist-skill/website/skills/{forgecad-mujoco-verify.md → forgecad-verify-mujoco.md} +6 -6
  77. package/dist-skill/website/skills/index.md +9 -12
  78. package/examples/api/texture-projection.forge.js +75 -0
  79. package/examples/assets/uv-grid.png +0 -0
  80. package/package.json +1 -1
  81. package/dist/docs-raw/skills/forgecad-blockout-model.md +0 -49
  82. package/dist/docs-raw/skills/forgecad-component-model.md +0 -53
  83. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +0 -60
  84. package/dist-skill/library/forgecad-3d-reconstruction/agents/openai.yaml +0 -4
  85. package/dist-skill/library/forgecad-blockout-model/SKILL.md +0 -42
  86. package/dist-skill/library/forgecad-component-model/SKILL.md +0 -46
  87. package/dist-skill/library/forgecad-image-replicator/agents/openai.yaml +0 -4
  88. package/dist-skill/library/forgecad-model-grader/agents/openai.yaml +0 -4
  89. package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +0 -48
  90. package/dist-skill/library/forgecad-reconstruction-benchmark/agents/openai.yaml +0 -4
  91. package/dist-skill/library/forgecad-visual-spec/agents/openai.yaml +0 -4
  92. package/dist-skill/website/skills/forgecad-blockout-model.md +0 -49
  93. package/dist-skill/website/skills/forgecad-component-model.md +0 -53
  94. package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +0 -60
  95. /package/dist/assets/{landing-proof-driven-DiGqdtWa.js → landing-proof-driven-BxZZh5r5.js} +0 -0
  96. /package/dist-skill/library/{forgecad-spec-by-walking-through-it → forgecad-design-spec}/references/default-profiles.md +0 -0
  97. /package/dist-skill/library/{forgecad-render-inspect → forgecad-inspect-model}/summarize_manifest.py +0 -0
  98. /package/dist-skill/library/{forgecad-image-replicator → forgecad-reconstruct-from-images}/scripts/compare_images.py +0 -0
  99. /package/dist-skill/library/{forgecad-mujoco-verify → forgecad-verify-mujoco}/scripts/mujoco_verify.py +0 -0
@@ -37,6 +37,8 @@ import {
37
37
  circle2d,
38
38
  collectProjectFiles,
39
39
  collectSimulationModel,
40
+ compileProjection,
41
+ computeGeometryArrays,
40
42
  constrainedSketch,
41
43
  cylinder,
42
44
  describeFaceQueryRef,
@@ -134,11 +136,12 @@ import {
134
136
  updateConstraintValue,
135
137
  validateSimulationModel,
136
138
  wrapOCCTShapeBackend
137
- } from "./chunk-WLUKAW3H.js";
139
+ } from "./chunk-UHBRMYA6.js";
138
140
 
139
141
  // cli/forgecad.ts
140
142
  import { Command, Flags, Help, flush as flushOclif, handle as handleOclif, run as runOclif } from "@oclif/core";
141
- import { readFileSync as readFileSync28 } from "fs";
143
+ import { readFileSync as readFileSync28, writeFileSync as writeFileSync22 } from "fs";
144
+ import { extname as extname15, resolve as resolvePath } from "path";
142
145
 
143
146
  // cli/check-api-contracts.ts
144
147
  import assert from "assert/strict";
@@ -5667,7 +5670,7 @@ function computeMeshInertia(mesh, massKg) {
5667
5670
  }
5668
5671
 
5669
5672
  // src/studio/project-files/importGraph.ts
5670
- var FORGE_IMPORT_RE = /\b(?:importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.svgSketch|Import\.dxfSketch|compareWith)\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
5673
+ var FORGE_IMPORT_RE = /\b(?:importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.image|Import\.svgSketch|Import\.dxfSketch|compareWith)\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
5671
5674
  var REQUIRE_RE = /\brequire\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
5672
5675
  var ES_IMPORT_RE = /\bfrom\s+(?:"([^"]+)"|'([^']+)')/g;
5673
5676
  var VIRTUAL_MODULES = /* @__PURE__ */ new Set(["forgecad", "@forge/runtime", "@forgecad/runtime"]);
@@ -5686,6 +5689,7 @@ function extractImports(code) {
5686
5689
  importSvgSketch: "forgeSvg",
5687
5690
  "Import.mesh": "forgeMesh",
5688
5691
  "Import.step": "forgeMesh",
5692
+ "Import.image": "forgeMesh",
5689
5693
  "Import.svgSketch": "forgeSvg",
5690
5694
  "Import.dxfSketch": "forgeDxf",
5691
5695
  compareWith: "compareWith"
@@ -5695,7 +5699,7 @@ function extractImports(code) {
5695
5699
  while ((m = forgeRe.exec(code)) !== null) {
5696
5700
  const path5 = m[1] ?? m[2];
5697
5701
  const fn = m[0].match(
5698
- /\b(importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.svgSketch|Import\.dxfSketch|compareWith)/
5702
+ /\b(importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.image|Import\.svgSketch|Import\.dxfSketch|compareWith)/
5699
5703
  )?.[1];
5700
5704
  add2(path5, kindMap[fn] ?? "forgeMesh");
5701
5705
  }
@@ -7563,6 +7567,13 @@ function parsePositiveInteger2(value, flag) {
7563
7567
  }
7564
7568
  return parsed;
7565
7569
  }
7570
+ function parseNonNegativeInteger(value, flag) {
7571
+ const parsed = Number.parseInt(value, 10);
7572
+ if (!Number.isInteger(parsed) || parsed < 0) {
7573
+ throw new Error(`${flag} must be a non-negative integer (got '${value}')`);
7574
+ }
7575
+ return parsed;
7576
+ }
7566
7577
  function parseScanGranularity(value) {
7567
7578
  const numeric = parsePositiveInteger2(value, "--scan-granularity");
7568
7579
  if (numeric < SCAN_GRANULARITY_MIN || numeric > SCAN_GRANULARITY_MAX) {
@@ -8133,6 +8144,7 @@ function parseInspectCli(argv, config = {}) {
8133
8144
  let compareToleranceMm;
8134
8145
  const thickness = {};
8135
8146
  const roughness = {};
8147
+ const scalarFieldParams = {};
8136
8148
  const sectionPlan = { planes: [] };
8137
8149
  let sectionOptionsUsed = false;
8138
8150
  let sectionOffset;
@@ -8277,6 +8289,22 @@ function parseInspectCli(argv, config = {}) {
8277
8289
  i += 1;
8278
8290
  continue;
8279
8291
  }
8292
+ if (arg === "--bands" || arg === "--iso" || arg === "--iso-width") {
8293
+ const isScalarFieldChannel = channels?.includes("thickness") || channels?.includes("roughness");
8294
+ if (!isScalarFieldChannel) {
8295
+ throw new Error(`${arg} only works with \`forgecad inspect surface thickness\` or \`... roughness\`.`);
8296
+ }
8297
+ if (arg === "--bands") {
8298
+ scalarFieldParams.quantizeBands = parseNonNegativeInteger(readValue2(argv, i, arg), arg);
8299
+ i += 1;
8300
+ } else if (arg === "--iso-width") {
8301
+ scalarFieldParams.isoLineWidthPx = parsePositiveNumber(readValue2(argv, i, arg), arg);
8302
+ i += 1;
8303
+ } else {
8304
+ scalarFieldParams.isoEnabled = true;
8305
+ }
8306
+ continue;
8307
+ }
8280
8308
  if (arg === "--plane" && channels?.includes("section")) {
8281
8309
  addSectionPlaneRequests(sectionPlan, readValue2(argv, i, arg), arg);
8282
8310
  sectionOptionsUsed = true;
@@ -8524,6 +8552,10 @@ function parseInspectCli(argv, config = {}) {
8524
8552
  freshServer,
8525
8553
  thickness,
8526
8554
  roughness,
8555
+ // Only forward heatmap overrides when a flag actually set one; an empty object
8556
+ // would still merge to the viewport default, but undefined keeps the payload and
8557
+ // the inspect bundle byte-identical to the pre-flag behavior.
8558
+ scalarFieldParams: Object.keys(scalarFieldParams).length > 0 ? scalarFieldParams : void 0,
8527
8559
  sectionPlan: sectionOptionsUsed ? {
8528
8560
  ...sectionPlan.planes.length > 0 ? { planes: sectionPlan.planes } : {},
8529
8561
  ...sectionPlan.offsets != null ? { offsets: sectionPlan.offsets } : {},
@@ -9772,6 +9804,11 @@ async function runInspectBundleInput(options) {
9772
9804
  });
9773
9805
  try {
9774
9806
  const page = await browser.newPage();
9807
+ if (process.env.FORGECAD_INSPECT_PROFILE === "1") {
9808
+ page.on("console", (message) => {
9809
+ console.log(`[browser:${message.type()}] ${message.text()}`);
9810
+ });
9811
+ }
9775
9812
  const url = `http://127.0.0.1:${activePort}/cli/render.html`;
9776
9813
  await page.goto(url, { waitUntil: "networkidle0", timeout: 15e3 });
9777
9814
  await page.waitForFunction("window.__forgeReady === true", { timeout: RENDER_READY_TIMEOUT_MS });
@@ -9780,6 +9817,7 @@ async function runInspectBundleInput(options) {
9780
9817
  cameras: options.cameras,
9781
9818
  size: options.size,
9782
9819
  quality: options.quality,
9820
+ debug: process.env.FORGECAD_INSPECT_PROFILE === "1",
9783
9821
  binaryFiles,
9784
9822
  activeBackend,
9785
9823
  cameraSpec: options.cameraSpec || null,
@@ -9798,6 +9836,7 @@ async function runInspectBundleInput(options) {
9798
9836
  cameras: browserRenderOptions.cameras,
9799
9837
  size: browserRenderOptions.size,
9800
9838
  quality: browserRenderOptions.quality,
9839
+ debug: browserRenderOptions.debug,
9801
9840
  channels: browserRenderOptions.channels,
9802
9841
  allFiles: files,
9803
9842
  fileName: scriptName,
@@ -9811,6 +9850,7 @@ async function runInspectBundleInput(options) {
9811
9850
  hide: browserRenderOptions.hide,
9812
9851
  thickness: browserRenderOptions.thickness,
9813
9852
  roughness: browserRenderOptions.roughness,
9853
+ scalarFieldParams: browserRenderOptions.scalarFieldParams,
9814
9854
  sectionPlan: browserRenderOptions.sectionPlan,
9815
9855
  cutaway: browserRenderOptions.cutaway,
9816
9856
  comparison: browserRenderOptions.comparison,
@@ -9833,6 +9873,7 @@ async function runInspectBundleInput(options) {
9833
9873
  channels: options.channels,
9834
9874
  thickness: options.thickness,
9835
9875
  roughness: options.roughness,
9876
+ scalarFieldParams: options.scalarFieldParams,
9836
9877
  sectionPlan: options.sectionPlan,
9837
9878
  cutaway: options.cutaway,
9838
9879
  jointOverrides: options.jointOverrides,
@@ -22284,6 +22325,361 @@ async function exportMeshInput(format, scriptPath, outputPath, quality, backend,
22284
22325
  stats.forEach((line) => console.log(line));
22285
22326
  }
22286
22327
 
22328
+ // src/forge/mesh/textureProjection.ts
22329
+ function bakeProjectedUv(positions, spec) {
22330
+ if (!(positions instanceof Float32Array)) {
22331
+ throw new TypeError("bakeProjectedUv: positions must be a Float32Array.");
22332
+ }
22333
+ if (positions.length % 3 !== 0) {
22334
+ throw new RangeError(
22335
+ `bakeProjectedUv: positions length must be a multiple of 3, got ${positions.length}.`
22336
+ );
22337
+ }
22338
+ const project = compileProjection(spec);
22339
+ const vertexCount = positions.length / 3;
22340
+ const uvs = new Float32Array(vertexCount * 2);
22341
+ for (let i = 0; i < vertexCount; i++) {
22342
+ const x = positions[i * 3];
22343
+ const y = positions[i * 3 + 1];
22344
+ const z = positions[i * 3 + 2];
22345
+ const [u, v] = project(x, y, z);
22346
+ uvs[i * 2] = u;
22347
+ uvs[i * 2 + 1] = v;
22348
+ }
22349
+ return uvs;
22350
+ }
22351
+
22352
+ // src/forge/export/exportGltf.ts
22353
+ var GLTF_FLOAT = 5126;
22354
+ var GLTF_UNSIGNED_INT = 5125;
22355
+ var GLTF_ARRAY_BUFFER = 34962;
22356
+ var GLTF_ELEMENT_ARRAY_BUFFER = 34963;
22357
+ var GLTF_WRAP_REPEAT = 10497;
22358
+ var GLTF_FILTER_LINEAR = 9729;
22359
+ var GLTF_FILTER_LINEAR_MIPMAP_LINEAR = 9987;
22360
+ function parseHexColor01(hex) {
22361
+ if (!hex) return null;
22362
+ const value = hex.trim();
22363
+ if (!value.startsWith("#")) return null;
22364
+ let r;
22365
+ let g;
22366
+ let b;
22367
+ if (value.length === 7) {
22368
+ r = Number.parseInt(value.slice(1, 3), 16);
22369
+ g = Number.parseInt(value.slice(3, 5), 16);
22370
+ b = Number.parseInt(value.slice(5, 7), 16);
22371
+ } else if (value.length === 4) {
22372
+ r = Number.parseInt(value[1] + value[1], 16);
22373
+ g = Number.parseInt(value[2] + value[2], 16);
22374
+ b = Number.parseInt(value[3] + value[3], 16);
22375
+ } else {
22376
+ return null;
22377
+ }
22378
+ if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return null;
22379
+ return { r: srgbToLinear(r / 255), g: srgbToLinear(g / 255), b: srgbToLinear(b / 255) };
22380
+ }
22381
+ function srgbToLinear(c) {
22382
+ return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
22383
+ }
22384
+ function decodeDataUri(dataUri) {
22385
+ const match = /^data:([^;,]+)(;base64)?,(.*)$/s.exec(dataUri);
22386
+ if (!match) {
22387
+ throw new Error(
22388
+ "glTF export: texture image is not a data: URI. Expected the self-contained data URI produced by Import.image(...)."
22389
+ );
22390
+ }
22391
+ const mimeType = match[1] || "application/octet-stream";
22392
+ const isBase64 = match[2] === ";base64";
22393
+ const payload = match[3];
22394
+ if (!isBase64) {
22395
+ throw new Error("glTF export: only base64-encoded texture data URIs are supported.");
22396
+ }
22397
+ let bytes;
22398
+ if (typeof Buffer !== "undefined") {
22399
+ bytes = new Uint8Array(Buffer.from(payload, "base64"));
22400
+ } else {
22401
+ const binary = atob(payload);
22402
+ bytes = new Uint8Array(binary.length);
22403
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
22404
+ }
22405
+ return { mimeType, bytes };
22406
+ }
22407
+ function extractShapeGeometry(obj) {
22408
+ const mesh = obj.shape.getMesh();
22409
+ const geo = computeGeometryArrays(mesh, { skipEdges: true });
22410
+ const positions = geo.positions;
22411
+ const normals = geo.normals;
22412
+ const materialProps = obj.shape.materialProps;
22413
+ const textureDescriptor = materialProps?.texture;
22414
+ let uvs = null;
22415
+ let texture = null;
22416
+ if (geo.uvs) {
22417
+ uvs = geo.uvs;
22418
+ if (textureDescriptor) {
22419
+ texture = decodeDataUri(textureDescriptor.image);
22420
+ }
22421
+ } else if (textureDescriptor) {
22422
+ uvs = bakeProjectedUv(positions, textureDescriptor.projection);
22423
+ texture = decodeDataUri(textureDescriptor.image);
22424
+ }
22425
+ const numVerts = positions.length / 3;
22426
+ const indices = new Uint32Array(numVerts);
22427
+ for (let i = 0; i < numVerts; i++) indices[i] = i;
22428
+ const metalness = typeof materialProps?.metalness === "number" ? materialProps.metalness : 0.05;
22429
+ const roughness = typeof materialProps?.roughness === "number" ? materialProps.roughness : 0.35;
22430
+ const opacity = typeof materialProps?.opacity === "number" ? materialProps.opacity : 1;
22431
+ return {
22432
+ positions,
22433
+ normals,
22434
+ uvs,
22435
+ indices,
22436
+ texture,
22437
+ baseColor: parseHexColor01(obj.color),
22438
+ metalness,
22439
+ roughness,
22440
+ opacity,
22441
+ name: obj.name || "Object"
22442
+ };
22443
+ }
22444
+ function align4(n) {
22445
+ return n + 3 & ~3;
22446
+ }
22447
+ function vec3MinMax(arr) {
22448
+ const min = [Infinity, Infinity, Infinity];
22449
+ const max = [-Infinity, -Infinity, -Infinity];
22450
+ for (let i = 0; i < arr.length; i += 3) {
22451
+ for (let c = 0; c < 3; c++) {
22452
+ const val = arr[i + c];
22453
+ if (val < min[c]) min[c] = val;
22454
+ if (val > max[c]) max[c] = val;
22455
+ }
22456
+ }
22457
+ return { min, max };
22458
+ }
22459
+ function buildGltf(objects) {
22460
+ if (objects.length === 0) {
22461
+ throw new Error("No shapes provided for glTF export.");
22462
+ }
22463
+ const geometries = objects.map(extractShapeGeometry);
22464
+ const bufferViews = [];
22465
+ const accessors = [];
22466
+ const meshes = [];
22467
+ const nodes = [];
22468
+ const materials = [];
22469
+ const textures = [];
22470
+ const images = [];
22471
+ const samplers = [];
22472
+ const chunks = [];
22473
+ let byteOffset = 0;
22474
+ function pushBufferView(data, target) {
22475
+ const padded = align4(data.byteLength);
22476
+ const view = {
22477
+ buffer: 0,
22478
+ byteOffset,
22479
+ byteLength: data.byteLength
22480
+ };
22481
+ if (target !== void 0) view.target = target;
22482
+ bufferViews.push(view);
22483
+ const chunk = new Uint8Array(padded);
22484
+ chunk.set(data);
22485
+ chunks.push(chunk);
22486
+ byteOffset += padded;
22487
+ return bufferViews.length - 1;
22488
+ }
22489
+ function typedArrayBytes(arr) {
22490
+ return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
22491
+ }
22492
+ const imageIndexByKey = /* @__PURE__ */ new Map();
22493
+ const samplerIndexByKey = /* @__PURE__ */ new Map();
22494
+ function ensureSampler() {
22495
+ const key = "repeat-linear-mip";
22496
+ const existing = samplerIndexByKey.get(key);
22497
+ if (existing !== void 0) return existing;
22498
+ samplers.push({
22499
+ wrapS: GLTF_WRAP_REPEAT,
22500
+ wrapT: GLTF_WRAP_REPEAT,
22501
+ magFilter: GLTF_FILTER_LINEAR,
22502
+ minFilter: GLTF_FILTER_LINEAR_MIPMAP_LINEAR
22503
+ });
22504
+ const idx = samplers.length - 1;
22505
+ samplerIndexByKey.set(key, idx);
22506
+ return idx;
22507
+ }
22508
+ function ensureTexture(image) {
22509
+ const key = `${image.mimeType}|${image.bytes.byteLength}`;
22510
+ const fpKey = `${key}|${fingerprint2(image.bytes)}`;
22511
+ const existing = imageIndexByKey.get(fpKey);
22512
+ let imageIdx;
22513
+ if (existing !== void 0) {
22514
+ imageIdx = existing;
22515
+ } else {
22516
+ const bufferViewIdx = pushBufferView(image.bytes);
22517
+ images.push({ mimeType: image.mimeType, bufferView: bufferViewIdx });
22518
+ imageIdx = images.length - 1;
22519
+ imageIndexByKey.set(fpKey, imageIdx);
22520
+ }
22521
+ const samplerIdx = ensureSampler();
22522
+ textures.push({ sampler: samplerIdx, source: imageIdx });
22523
+ return textures.length - 1;
22524
+ }
22525
+ for (const geo of geometries) {
22526
+ const attributes = {};
22527
+ const posView = pushBufferView(typedArrayBytes(geo.positions), GLTF_ARRAY_BUFFER);
22528
+ const { min, max } = vec3MinMax(geo.positions);
22529
+ accessors.push({
22530
+ bufferView: posView,
22531
+ componentType: GLTF_FLOAT,
22532
+ count: geo.positions.length / 3,
22533
+ type: "VEC3",
22534
+ min,
22535
+ max
22536
+ });
22537
+ attributes.POSITION = accessors.length - 1;
22538
+ const nView = pushBufferView(typedArrayBytes(geo.normals), GLTF_ARRAY_BUFFER);
22539
+ accessors.push({
22540
+ bufferView: nView,
22541
+ componentType: GLTF_FLOAT,
22542
+ count: geo.normals.length / 3,
22543
+ type: "VEC3"
22544
+ });
22545
+ attributes.NORMAL = accessors.length - 1;
22546
+ if (geo.uvs) {
22547
+ const uvView = pushBufferView(typedArrayBytes(geo.uvs), GLTF_ARRAY_BUFFER);
22548
+ accessors.push({
22549
+ bufferView: uvView,
22550
+ componentType: GLTF_FLOAT,
22551
+ count: geo.uvs.length / 2,
22552
+ type: "VEC2"
22553
+ });
22554
+ attributes.TEXCOORD_0 = accessors.length - 1;
22555
+ }
22556
+ const idxView = pushBufferView(typedArrayBytes(geo.indices), GLTF_ELEMENT_ARRAY_BUFFER);
22557
+ accessors.push({
22558
+ bufferView: idxView,
22559
+ componentType: GLTF_UNSIGNED_INT,
22560
+ count: geo.indices.length,
22561
+ type: "SCALAR"
22562
+ });
22563
+ const indicesAccessor = accessors.length - 1;
22564
+ const color = geo.baseColor;
22565
+ const pbr = {
22566
+ metallicFactor: geo.metalness,
22567
+ roughnessFactor: geo.roughness
22568
+ };
22569
+ if (color) {
22570
+ pbr.baseColorFactor = [color.r, color.g, color.b, geo.opacity];
22571
+ } else if (geo.opacity !== 1) {
22572
+ pbr.baseColorFactor = [1, 1, 1, geo.opacity];
22573
+ }
22574
+ if (geo.texture) {
22575
+ const texIdx = ensureTexture(geo.texture);
22576
+ pbr.baseColorTexture = { index: texIdx, texCoord: 0 };
22577
+ }
22578
+ const material = {
22579
+ name: `${geo.name} material`,
22580
+ pbrMetallicRoughness: pbr
22581
+ };
22582
+ if (geo.opacity !== 1) {
22583
+ material.alphaMode = "BLEND";
22584
+ }
22585
+ materials.push(material);
22586
+ const materialIdx = materials.length - 1;
22587
+ meshes.push({
22588
+ name: geo.name,
22589
+ primitives: [
22590
+ {
22591
+ attributes,
22592
+ indices: indicesAccessor,
22593
+ material: materialIdx,
22594
+ mode: 4
22595
+ // TRIANGLES
22596
+ }
22597
+ ]
22598
+ });
22599
+ nodes.push({ name: geo.name, mesh: meshes.length - 1 });
22600
+ }
22601
+ const totalBytes = byteOffset;
22602
+ const binary = new Uint8Array(totalBytes);
22603
+ let cursor = 0;
22604
+ for (const chunk of chunks) {
22605
+ binary.set(chunk, cursor);
22606
+ cursor += chunk.byteLength;
22607
+ }
22608
+ const json = {
22609
+ asset: { version: "2.0", generator: "ForgeCAD glTF Export" },
22610
+ scene: 0,
22611
+ scenes: [{ nodes: nodes.map((_, i) => i) }],
22612
+ nodes,
22613
+ meshes,
22614
+ accessors,
22615
+ bufferViews,
22616
+ materials,
22617
+ buffers: [{ byteLength: totalBytes }]
22618
+ };
22619
+ if (samplers.length > 0) json.samplers = samplers;
22620
+ if (textures.length > 0) json.textures = textures;
22621
+ if (images.length > 0) json.images = images;
22622
+ return { json, binary };
22623
+ }
22624
+ function fingerprint2(bytes) {
22625
+ let h = 2166136261 >>> 0;
22626
+ const step = Math.max(1, Math.floor(bytes.byteLength / 256));
22627
+ for (let i = 0; i < bytes.byteLength; i += step) {
22628
+ h ^= bytes[i];
22629
+ h = Math.imul(h, 16777619) >>> 0;
22630
+ }
22631
+ return h ^ bytes.byteLength;
22632
+ }
22633
+ function buildGltfJson(objects) {
22634
+ const { json, binary } = buildGltf(objects);
22635
+ let base64;
22636
+ if (typeof Buffer !== "undefined") {
22637
+ base64 = Buffer.from(binary).toString("base64");
22638
+ } else {
22639
+ let binaryStr = "";
22640
+ for (let i = 0; i < binary.length; i++) binaryStr += String.fromCharCode(binary[i]);
22641
+ base64 = btoa(binaryStr);
22642
+ }
22643
+ json.buffers[0].uri = `data:application/octet-stream;base64,${base64}`;
22644
+ return JSON.stringify(json);
22645
+ }
22646
+ function buildGlb(objects) {
22647
+ const { json, binary } = buildGltf(objects);
22648
+ const jsonStr = JSON.stringify(json);
22649
+ const jsonBytes = utf8Encode(jsonStr);
22650
+ const jsonPadded = align4(jsonBytes.byteLength);
22651
+ const jsonChunk = new Uint8Array(jsonPadded);
22652
+ jsonChunk.set(jsonBytes);
22653
+ jsonChunk.fill(32, jsonBytes.byteLength);
22654
+ const binPadded = align4(binary.byteLength);
22655
+ const binChunk = new Uint8Array(binPadded);
22656
+ binChunk.set(binary);
22657
+ const GLB_HEADER = 12;
22658
+ const CHUNK_HEADER = 8;
22659
+ const totalLength = GLB_HEADER + CHUNK_HEADER + jsonChunk.byteLength + CHUNK_HEADER + binChunk.byteLength;
22660
+ const out = new ArrayBuffer(totalLength);
22661
+ const view = new DataView(out);
22662
+ const bytes = new Uint8Array(out);
22663
+ view.setUint32(0, 1179937895, true);
22664
+ view.setUint32(4, 2, true);
22665
+ view.setUint32(8, totalLength, true);
22666
+ let offset = GLB_HEADER;
22667
+ view.setUint32(offset, jsonChunk.byteLength, true);
22668
+ view.setUint32(offset + 4, 1313821514, true);
22669
+ bytes.set(jsonChunk, offset + CHUNK_HEADER);
22670
+ offset += CHUNK_HEADER + jsonChunk.byteLength;
22671
+ view.setUint32(offset, binChunk.byteLength, true);
22672
+ view.setUint32(offset + 4, 5130562, true);
22673
+ bytes.set(binChunk, offset + CHUNK_HEADER);
22674
+ return out;
22675
+ }
22676
+ function utf8Encode(str) {
22677
+ if (typeof TextEncoder !== "undefined") {
22678
+ return new TextEncoder().encode(str);
22679
+ }
22680
+ return new Uint8Array(Buffer.from(str, "utf-8"));
22681
+ }
22682
+
22287
22683
  // cli/forge-mjcf.ts
22288
22684
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "fs";
22289
22685
  import { dirname as dirname6, extname as extname7, resolve as resolve23 } from "path";
@@ -27456,6 +27852,27 @@ var INSTALL_TARGETS = {
27456
27852
  };
27457
27853
  var ALL_INSTALL_TARGETS = Object.keys(INSTALL_TARGETS);
27458
27854
  var DEFAULT_INSTALL_TARGET = "agents";
27855
+ var RETIRED_COMPANION_SKILL_NAMES = [
27856
+ "forgecad-3d-reconstruction",
27857
+ "forgecad-assembly-contract",
27858
+ "forgecad-benchmark-reconstruction",
27859
+ "forgecad-blockout",
27860
+ "forgecad-blockout-model",
27861
+ "forgecad-component-model",
27862
+ "forgecad-high-level-spec",
27863
+ "forgecad-image-replicator",
27864
+ "forgecad-lld",
27865
+ "forgecad-make-a-model",
27866
+ "forgecad-model-grader",
27867
+ "forgecad-mujoco-verify",
27868
+ "forgecad-prepare-prompt",
27869
+ "forgecad-project",
27870
+ "forgecad-reconstruction-benchmark",
27871
+ "forgecad-render-inspect",
27872
+ "forgecad-spec-by-walking-through-it",
27873
+ "forgecad-visual-prompt",
27874
+ "forgecad-visual-spec"
27875
+ ];
27459
27876
  function installUsage() {
27460
27877
  return [
27461
27878
  "Usage: forgecad skill install [--target agents|claude|codex|opencode|all] [--core-only]",
@@ -27555,6 +27972,9 @@ If you are running from a source checkout, run: npm run build:skill:forgecad`
27555
27972
  );
27556
27973
  }
27557
27974
  mkdirSync6(targetRoot, { recursive: true });
27975
+ for (const name of RETIRED_COMPANION_SKILL_NAMES) {
27976
+ rmSync3(join9(targetRoot, name), { recursive: true, force: true });
27977
+ }
27558
27978
  const installed = [];
27559
27979
  for (const entry of readdirSync6(srcLibrary, { withFileTypes: true })) {
27560
27980
  if (!entry.isDirectory()) continue;
@@ -28386,15 +28806,27 @@ async function fetchRemoteFiles(projectId) {
28386
28806
  const entries = await res.json();
28387
28807
  return remoteFilesFromInit(entries);
28388
28808
  }
28809
+ var SAVE_BODY_LIMIT_BYTES = 10 * 1024 * 1024;
28810
+ var formatMiB = (bytes) => `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
28389
28811
  async function saveRemoteFile(projectId, filename, content) {
28812
+ const body = JSON.stringify({ filename, content });
28813
+ const bodyBytes = Buffer.byteLength(body, "utf-8");
28814
+ if (bodyBytes > SAVE_BODY_LIMIT_BYTES) {
28815
+ const contentBytes = Buffer.byteLength(content, "utf-8");
28816
+ throw new Error(
28817
+ `Cannot push "${filename}": its upload is ${formatMiB(bodyBytes)} (file content ${formatMiB(contentBytes)}), over the server's ${formatMiB(SAVE_BODY_LIMIT_BYTES)} per-file limit.
28818
+ Each file is uploaded in its own request, so this is a single oversized file, not the total push size.
28819
+ Fixes: split the file into smaller pieces, trim generated/embedded data (e.g. inline images in SVGs), or keep it out of the project (add it to .gitignore-style skips) if it doesn't need to live on the server.`
28820
+ );
28821
+ }
28390
28822
  const res = await authenticatedFetch(`/api/projects/${projectId}/save`, {
28391
28823
  method: "POST",
28392
28824
  headers: { "Content-Type": "application/json" },
28393
- body: JSON.stringify({ filename, content })
28825
+ body
28394
28826
  });
28395
28827
  if (!res.ok) {
28396
- const body = await res.json().catch(() => ({}));
28397
- throw new Error(body.error ?? `Failed to save ${filename} (${res.status})`);
28828
+ const body2 = await res.json().catch(() => ({}));
28829
+ throw new Error(body2.error ?? `Failed to save ${filename} (${res.status})`);
28398
28830
  }
28399
28831
  }
28400
28832
  async function deleteRemoteFile(projectId, filename) {
@@ -37876,6 +38308,135 @@ var PARAM_OPTIONS = [
37876
38308
  var PARAM_OVERRIDE_OPTIONS = PARAM_OPTIONS.filter(
37877
38309
  (option) => option.name === "--param" || option.name === "-p"
37878
38310
  );
38311
+ function parseGltfExportArgs(format, argv) {
38312
+ const inputPaths = [];
38313
+ let outputPath;
38314
+ let quality;
38315
+ let backend;
38316
+ const paramOverrides = {};
38317
+ const jointOverrides = {};
38318
+ for (let i = 0; i < argv.length; i += 1) {
38319
+ const arg = argv[i];
38320
+ if (arg === "--output" || arg === "-o") {
38321
+ outputPath = argv[i + 1];
38322
+ if (!outputPath) throw new Error("--output requires a file path");
38323
+ i += 1;
38324
+ continue;
38325
+ }
38326
+ if (arg === "--quality" || arg === "-q") {
38327
+ const val = argv[i + 1];
38328
+ if (val !== "default" && val !== "live" && val !== "high") {
38329
+ throw new Error("--quality must be default, live, or high");
38330
+ }
38331
+ quality = val;
38332
+ i += 1;
38333
+ continue;
38334
+ }
38335
+ if (arg === "--backend") {
38336
+ const val = argv[i + 1];
38337
+ if (val !== "manifold" && val !== "occt" && val !== "truck" && val !== "sdf") {
38338
+ throw new Error("--backend must be manifold, occt, truck, or sdf");
38339
+ }
38340
+ backend = val;
38341
+ i += 1;
38342
+ continue;
38343
+ }
38344
+ if (arg === "--joint") {
38345
+ const value = argv[i + 1];
38346
+ if (!value || value.startsWith("--")) throw new Error("--joint requires JointName=Value");
38347
+ addCliJointOverride(value, jointOverrides);
38348
+ i += 1;
38349
+ continue;
38350
+ }
38351
+ if (arg === "--param" || arg === "-p") {
38352
+ const value = argv[i + 1];
38353
+ if (!value || value.startsWith("--")) throw new Error(`${arg} requires Key=Value`);
38354
+ addCliParamOverride(value, paramOverrides);
38355
+ i += 1;
38356
+ continue;
38357
+ }
38358
+ if (arg.startsWith("--")) {
38359
+ throw new Error(`Unknown flag: ${arg}`);
38360
+ }
38361
+ inputPaths.push(arg);
38362
+ }
38363
+ requireInputPaths(
38364
+ inputPaths,
38365
+ `Usage: forgecad export ${format} <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [input ...] [--output path] [--quality default|live|high] [--backend manifold|occt|truck|sdf] [--param Key=Value] [--joint JointName=Value]`
38366
+ );
38367
+ requireRenderableInputPaths(inputPaths);
38368
+ requireSingleInputForOutputPath(inputPaths, outputPath);
38369
+ return { inputPaths, outputPath, quality, backend, paramOverrides, jointOverrides };
38370
+ }
38371
+ function defaultGltfOutputPath(scriptPath, sourcePath, format) {
38372
+ const abs = resolvePath(scriptPath);
38373
+ const base = abs.slice(0, abs.length - extname15(abs).length);
38374
+ const stem = base.endsWith(".forge") ? base.slice(0, -6) : base;
38375
+ const target = `${stem}.${format}`;
38376
+ return resolvePath(target) === resolvePath(sourcePath) ? `${stem}.forgecad.${format}` : target;
38377
+ }
38378
+ function extractGltfMeshObjects(result) {
38379
+ return result.objects.filter((obj) => obj.shape).map((obj) => ({
38380
+ name: obj.name,
38381
+ shape: obj.shape,
38382
+ color: obj.color
38383
+ }));
38384
+ }
38385
+ async function exportGltfInput(format, scriptPath, outputPath, quality, backend, paramOverrides, jointOverrides) {
38386
+ const input = loadCliScriptInput(scriptPath);
38387
+ await initCliBackend(resolveCliBackend(backend, input) ?? CLI_DEFAULT_BACKEND);
38388
+ const qualityPreset = quality && quality !== "default" ? quality : void 0;
38389
+ setParamOverrides(paramOverrides);
38390
+ const runResult = runScript(input.code, input.fileName, input.allFiles, {
38391
+ ...qualityPreset ? { quality: qualityPreset } : {},
38392
+ readBinaryFile: input.readBinaryFile,
38393
+ assemblyState: jointOverrides
38394
+ });
38395
+ const result = applyCliJointOverrides(runResult, jointOverrides);
38396
+ if (result.error) {
38397
+ throw new Error(`ERROR: ${result.error}`);
38398
+ }
38399
+ const meshObjects = extractGltfMeshObjects(result);
38400
+ if (meshObjects.length === 0) {
38401
+ throw new Error("No 3D shapes found in the script output.");
38402
+ }
38403
+ if (outputPath && resolvePath(outputPath) === input.sourcePath) {
38404
+ throw new Error("Output path would overwrite the input file. Pass a different --output path.");
38405
+ }
38406
+ const target = resolvePath(outputPath ?? defaultGltfOutputPath(scriptPath, input.sourcePath, format));
38407
+ if (format === "glb") {
38408
+ const buffer = buildGlb(meshObjects);
38409
+ writeFileSync22(target, Buffer.from(buffer));
38410
+ } else {
38411
+ const json = buildGltfJson(meshObjects);
38412
+ writeFileSync22(target, json, "utf-8");
38413
+ }
38414
+ const stats = meshObjects.map((obj) => {
38415
+ const mesh = obj.shape.getMesh();
38416
+ const textured = mesh.numProp === 8 || Boolean(obj.shape.materialProps?.texture);
38417
+ return ` ${obj.name}: ${mesh.numTri.toLocaleString()} triangles${textured ? " (textured)" : ""}`;
38418
+ });
38419
+ console.log(`\u2713 Exported ${format.toUpperCase()} to ${target}`);
38420
+ console.log(` ${meshObjects.length} object(s)${quality ? ` [quality: ${quality}]` : ""}`);
38421
+ stats.forEach((line) => console.log(line));
38422
+ }
38423
+ async function runGltfExportCli(format, argv) {
38424
+ const { inputPaths, outputPath, quality, backend, paramOverrides, jointOverrides } = parseGltfExportArgs(format, argv);
38425
+ requireExistingInputPaths(inputPaths);
38426
+ let failures = 0;
38427
+ for (const [index, scriptPath] of inputPaths.entries()) {
38428
+ printBatchHeader(scriptPath, index, inputPaths.length);
38429
+ try {
38430
+ await exportGltfInput(format, scriptPath, outputPath, quality, backend, paramOverrides, jointOverrides);
38431
+ } catch (error) {
38432
+ failures += 1;
38433
+ console.error(error instanceof Error ? error.message : String(error));
38434
+ }
38435
+ }
38436
+ if (failures > 0) {
38437
+ process.exit(1);
38438
+ }
38439
+ }
37879
38440
  var RENDER_OPTIONS = [
37880
38441
  ...PARAM_OPTIONS,
37881
38442
  {
@@ -38097,6 +38658,19 @@ var INSPECT_EVIDENCE_OPTIONS = [
38097
38658
  argument: "required",
38098
38659
  valueLabel: "<n>"
38099
38660
  },
38661
+ {
38662
+ name: "--bands",
38663
+ description: "Quantize the heatmap fill into N discrete contour bands (0 = smooth ramp)",
38664
+ argument: "required",
38665
+ valueLabel: "<n>"
38666
+ },
38667
+ { name: "--iso", description: "Draw anti-aliased isolines on the heatmap surface" },
38668
+ {
38669
+ name: "--iso-width",
38670
+ description: "Isoline width in screen pixels (default 1.5)",
38671
+ argument: "required",
38672
+ valueLabel: "<px>"
38673
+ },
38100
38674
  {
38101
38675
  name: "--with",
38102
38676
  description: "Reference model for comparison evidence",
@@ -38209,6 +38783,11 @@ function inspectEvidenceOptions(definition) {
38209
38783
  if (definition.channel === "roughness") {
38210
38784
  names.add("--roughness-samples");
38211
38785
  }
38786
+ if (definition.channel === "thickness" || definition.channel === "roughness") {
38787
+ for (const name of ["--bands", "--iso", "--iso-width"]) {
38788
+ names.add(name);
38789
+ }
38790
+ }
38212
38791
  if (definition.channel === "collisions") {
38213
38792
  names.add("--joint-sweep");
38214
38793
  }
@@ -39701,6 +40280,78 @@ var commands = [
39701
40280
  },
39702
40281
  run: (args) => runMeshExportCli("stl", args)
39703
40282
  },
40283
+ {
40284
+ group: "Export",
40285
+ path: ["export", "glb"],
40286
+ summary: "Export a Forge script to binary glTF (.glb) with PBR materials and embedded textures.",
40287
+ usage: [
40288
+ "forgecad export glb <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [input ...] [--output path] [--quality default|live|high] [--backend manifold|occt|truck|sdf] [--param Key=Value] [--joint JointName=Value]"
40289
+ ],
40290
+ examples: [
40291
+ "forgecad export glb examples/products/cup.forge.js",
40292
+ "forgecad export glb examples/products/cup.forge.js --output out/cup.glb",
40293
+ "forgecad export glb examples/products/cup.forge.js examples/api/boolean-operations.forge.js",
40294
+ "forgecad export glb examples/products/cup.forge.js --quality high"
40295
+ ],
40296
+ completion: {
40297
+ options: [
40298
+ ...PARAM_OPTIONS,
40299
+ { name: "--output", description: "Output GLB path", argument: "required", valueLabel: "<path>", valueKind: "path" },
40300
+ {
40301
+ name: "--quality",
40302
+ description: "Forge quality preset",
40303
+ argument: "required",
40304
+ valueLabel: "<default|live|high>",
40305
+ values: QUALITY_VALUES
40306
+ },
40307
+ {
40308
+ name: "--backend",
40309
+ description: "Geometry backend (default: manifold)",
40310
+ argument: "required",
40311
+ valueLabel: "<manifold|occt|truck|sdf>",
40312
+ values: BACKEND_VALUES
40313
+ }
40314
+ ],
40315
+ positionals: [{ description: "Forge script or CAD asset", valueKind: "renderable", repeatable: true }]
40316
+ },
40317
+ run: (args) => runGltfExportCli("glb", args)
40318
+ },
40319
+ {
40320
+ group: "Export",
40321
+ path: ["export", "gltf"],
40322
+ summary: "Export a Forge script to text glTF (.gltf) with PBR materials and embedded textures.",
40323
+ usage: [
40324
+ "forgecad export gltf <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [input ...] [--output path] [--quality default|live|high] [--backend manifold|occt|truck|sdf] [--param Key=Value] [--joint JointName=Value]"
40325
+ ],
40326
+ examples: [
40327
+ "forgecad export gltf examples/products/cup.forge.js",
40328
+ "forgecad export gltf examples/products/cup.forge.js --output out/cup.gltf",
40329
+ "forgecad export gltf examples/products/cup.forge.js examples/api/boolean-operations.forge.js",
40330
+ "forgecad export gltf examples/products/cup.forge.js --quality high"
40331
+ ],
40332
+ completion: {
40333
+ options: [
40334
+ ...PARAM_OPTIONS,
40335
+ { name: "--output", description: "Output glTF path", argument: "required", valueLabel: "<path>", valueKind: "path" },
40336
+ {
40337
+ name: "--quality",
40338
+ description: "Forge quality preset",
40339
+ argument: "required",
40340
+ valueLabel: "<default|live|high>",
40341
+ values: QUALITY_VALUES
40342
+ },
40343
+ {
40344
+ name: "--backend",
40345
+ description: "Geometry backend (default: manifold)",
40346
+ argument: "required",
40347
+ valueLabel: "<manifold|occt|truck|sdf>",
40348
+ values: BACKEND_VALUES
40349
+ }
40350
+ ],
40351
+ positionals: [{ description: "Forge script or CAD asset", valueKind: "renderable", repeatable: true }]
40352
+ },
40353
+ run: (args) => runGltfExportCli("gltf", args)
40354
+ },
39704
40355
  {
39705
40356
  group: "Export",
39706
40357
  path: ["export", "gcode"],
@@ -41194,7 +41845,7 @@ var commands = [
41194
41845
  { name: "--update", description: "Regenerate compiler snapshots" }
41195
41846
  ]
41196
41847
  },
41197
- run: async (args) => (await import("./check-compiler-HPF2T2FS.js")).runCheckCompilerCli(args)
41848
+ run: async (args) => (await import("./check-compiler-4RPB6SB5.js")).runCheckCompilerCli(args)
41198
41849
  },
41199
41850
  {
41200
41851
  group: "Checks",
@@ -41217,7 +41868,7 @@ var commands = [
41217
41868
  { name: "--update", description: "Regenerate query-propagation snapshots" }
41218
41869
  ]
41219
41870
  },
41220
- run: async (args) => (await import("./check-query-propagation-HYSLTXAB.js")).runCheckQueryPropagationCli(args)
41871
+ run: async (args) => (await import("./check-query-propagation-KN3DFQTX.js")).runCheckQueryPropagationCli(args)
41221
41872
  },
41222
41873
  {
41223
41874
  group: "Checks",