forgecad 0.9.14 → 0.9.15

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 (219) hide show
  1. package/LICENSE +6 -4
  2. package/README.md +8 -4
  3. package/dist/assets/{AdminPage-eWGs2K6H.js → AdminPage-CDyGUinA.js} +2 -2
  4. package/dist/assets/{BenchmarkPage-CTrLKfpo.js → BenchmarkPage-DfPMY_-d.js} +4 -15
  5. package/dist/assets/{BlogPage-5nPesyds.js → BlogPage-kF0fkdJT.js} +2 -2
  6. package/dist/assets/{DocsPage-C4Y3nbYc.js → DocsPage-B954L3YN.js} +9 -3
  7. package/dist/assets/EditorApp-Beb-IZ0y.js +14014 -0
  8. package/dist/assets/{EditorApp-BAnckbsk.css → EditorApp-CuDLxKqL.css} +698 -0
  9. package/dist/assets/{EmbedViewer-C8fB4n5U.js → EmbedViewer-C77B-TrF.js} +3 -3
  10. package/dist/assets/{LandingPageProofDriven-jSz0LaMM.js → LandingPageProofDriven-Cr6fXMDj.js} +35 -37
  11. package/dist/assets/LegalPage-BRlScr9A.css +91 -0
  12. package/dist/assets/LegalPage-Dzklqmmg.js +39 -0
  13. package/dist/assets/{PricingPage-BMedqFef.css → PricingPage-BPF6HKyO.css} +25 -0
  14. package/dist/assets/{PricingPage-B83B90zh.js → PricingPage-zWXkvlwl.js} +19 -19
  15. package/dist/assets/{SettingsPage-DY889pcu.js → SettingsPage-Bz0of4KQ.js} +2 -2
  16. package/dist/assets/app-CE3sYcV7.css +3890 -0
  17. package/dist/assets/{app-bEww1ic4.js → app-D3kDkggg.js} +2293 -946
  18. package/dist/assets/cli/{render-Cho2uKG_.js → render-DSY3mMQa.js} +337 -7
  19. package/dist/assets/{constructionHistoryWorker-HYwzJY4m.js → constructionHistoryWorker-gpDo-uH2.js} +927 -243
  20. package/dist/assets/{evalWorker-CjQwJSE-.js → evalWorker-CU0Ke6DP.js} +7800 -4164
  21. package/dist/assets/{forgecad_geometry-CH2nvuLA.js → forgecad_geometry-Dgceylq9.js} +43 -1
  22. package/dist/assets/forgecad_geometry_bg-dD4RNQF1.wasm +0 -0
  23. package/dist/assets/{inspectWorker-DeRnMVv1.js → inspectWorker-COyp8XXA.js} +927 -243
  24. package/dist/assets/{javascript-70-4uGcz.js → javascript-1kQXfVaz.js} +1 -1
  25. package/dist/assets/landing-proof-driven-DiGqdtWa.js +18 -0
  26. package/dist/assets/{landing-proof-driven-oFYW6mjz.css → landing-proof-driven-ORyigZ6p.css} +13 -7
  27. package/dist/assets/legalContent-ZfFGMmi4.js +251 -0
  28. package/dist/assets/{manifold-CG9Fokx-.js → manifold-BRI5prcH.js} +1 -1
  29. package/dist/assets/{manifold-uRzgk5O8.js → manifold-C-3h2M7p.js} +2 -2
  30. package/dist/assets/{manifold-rmfAcdwF.js → manifold-DNkrUWpA.js} +1 -1
  31. package/dist/assets/{reportWorker-4cW_ZpoS.js → reportWorker-CdBz5bNg.js} +7538 -10857
  32. package/dist/assets/{scalar-sampling-budget-CfDiFvh7.js → scalar-sampling-budget-wJF98aY9.js} +6935 -4331
  33. package/dist/assets/{scanProxyWorker-Bs2TDgLw.js → scanProxyWorker-B-9VbLIs.js} +32 -1
  34. package/dist/assets/{solver-DuJAO8S6.js → solver-BZ9LPTHs.js} +1 -1
  35. package/dist/assets/solver_bg-DAHZJ_rw.wasm +0 -0
  36. package/dist/assets/{targets-D6PWsv6X.js → targets-B9sGB5nB.js} +1 -1
  37. package/dist/assets/{vendor-react-Da3A2QmU.js → vendor-react-6j1Kke-Y.js} +6 -5
  38. package/dist/cli/render.html +1 -1
  39. package/dist/docs/index.html +2 -2
  40. package/dist/docs-raw/AI/ai-native-cad.md +50 -0
  41. package/dist/docs-raw/AI/usage.md +3 -12
  42. package/dist/docs-raw/CLI.md +30 -10
  43. package/dist/docs-raw/component-model.md +27 -11
  44. package/dist/docs-raw/generated/assembly.md +301 -212
  45. package/dist/docs-raw/generated/concepts.md +235 -237
  46. package/dist/docs-raw/generated/core.md +283 -6
  47. package/dist/docs-raw/generated/curves.md +274 -361
  48. package/dist/docs-raw/generated/lib.md +7 -1
  49. package/dist/docs-raw/generated/output.md +19 -4
  50. package/dist/docs-raw/generated/runtime-names.md +41 -0
  51. package/dist/docs-raw/generated/sdf.md +31 -0
  52. package/dist/docs-raw/generated/sheet-metal.md +9 -0
  53. package/dist/docs-raw/generated/sketch.md +44 -1
  54. package/dist/docs-raw/generated/viewport.md +11 -3
  55. package/dist/docs-raw/guides/coordinate-system.md +20 -16
  56. package/dist/docs-raw/guides/geometry-conventions.md +2 -2
  57. package/dist/docs-raw/guides/inspection-bundles.md +2 -1
  58. package/dist/docs-raw/guides/joint-design.md +24 -0
  59. package/dist/docs-raw/guides/positioning.md +13 -3
  60. package/dist/docs-raw/legal/privacy.md +63 -0
  61. package/dist/docs-raw/legal/software-license.md +55 -0
  62. package/dist/docs-raw/legal/terms.md +87 -0
  63. package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +1 -1
  64. package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -1
  65. package/dist/docs-raw/skills/forgecad-component-model.md +11 -2
  66. package/dist/docs-raw/skills/forgecad-high-level-spec.md +1 -1
  67. package/dist/docs-raw/skills/forgecad-image-replicator.md +8 -8
  68. package/dist/docs-raw/skills/forgecad-lld.md +1 -1
  69. package/dist/docs-raw/skills/forgecad-make-a-model.md +1 -1
  70. package/dist/docs-raw/skills/forgecad-model-grader.md +2 -2
  71. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +2 -2
  72. package/dist/docs-raw/skills/forgecad-project.md +1 -1
  73. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +1 -1
  74. package/dist/docs-raw/skills/forgecad-render-inspect.md +4 -2
  75. package/dist/docs-raw/skills/forgecad-visual-spec.md +1 -1
  76. package/dist/docs-raw/skills/forgecad.md +4 -3
  77. package/dist/index.html +40 -12
  78. package/dist/llms.txt +8 -0
  79. package/dist/site.webmanifest +1 -1
  80. package/dist/sitemap.xml +49 -13
  81. package/dist-cli/{check-compiler-U5SOPN7X.js → check-compiler-SDX5QIXI.js} +1 -2
  82. package/dist-cli/{check-query-propagation-XOKNSSYU.js → check-query-propagation-EAYEFT77.js} +1 -2
  83. package/dist-cli/{chunk-EXWGNL6K.js → chunk-N4O47JLF.js} +12540 -9046
  84. package/dist-cli/forgecad.js +1786 -679
  85. package/dist-cli/{forgecad_geometry-GYVNKPIE.js → forgecad_geometry-QOQIIP53.js} +42 -1
  86. package/dist-cli/forgecad_geometry_bg.wasm +0 -0
  87. package/dist-cli/{solver-46FFSK2U.js → solver-OK4HECRH.js} +0 -1
  88. package/dist-cli/solver_bg.wasm +0 -0
  89. package/dist-skill/CONTEXT.md +1117 -721
  90. package/dist-skill/SKILL.md +3 -2
  91. package/dist-skill/docs/API/core/concepts.md +64 -1
  92. package/dist-skill/docs/CLI.md +30 -10
  93. package/dist-skill/docs/generated/assembly.md +277 -229
  94. package/dist-skill/docs/generated/core.md +283 -6
  95. package/dist-skill/docs/generated/curves.md +272 -362
  96. package/dist-skill/docs/generated/lib.md +7 -1
  97. package/dist-skill/docs/generated/output.md +19 -4
  98. package/dist-skill/docs/generated/runtime-names.md +41 -0
  99. package/dist-skill/docs/generated/sdf.md +31 -0
  100. package/dist-skill/docs/generated/sheet-metal.md +9 -0
  101. package/dist-skill/docs/generated/sketch.md +44 -2
  102. package/dist-skill/docs/generated/viewport.md +2 -87
  103. package/dist-skill/docs/guides/coordinate-system.md +20 -16
  104. package/dist-skill/docs/guides/geometry-conventions.md +2 -2
  105. package/dist-skill/docs/guides/inspection-bundles.md +2 -1
  106. package/dist-skill/docs/guides/joint-design.md +24 -0
  107. package/dist-skill/docs/guides/positioning.md +13 -3
  108. package/dist-skill/library/forgecad-component-model/SKILL.md +10 -1
  109. package/dist-skill/library/forgecad-image-replicator/SKILL.md +6 -6
  110. package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.py +166 -0
  111. package/dist-skill/library/forgecad-model-grader/SKILL.md +1 -1
  112. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +1 -1
  113. package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
  114. package/examples/api/assembly-kinematics-foundation.forge.js +65 -0
  115. package/examples/api/assembly-kinematics-four-bar.forge.js +115 -0
  116. package/examples/api/assembly-kinematics-limb.forge.js +116 -0
  117. package/examples/api/connector-frame-rig-chain.forge.js +102 -0
  118. package/examples/api/exact-sheet-shell-assembly.forge.js +0 -2
  119. package/examples/api/exact-surface-studio.forge.js +6 -8
  120. package/examples/api/helix-basics.forge.js +6 -6
  121. package/examples/api/lean-foundations/README.md +12 -0
  122. package/examples/api/lean-foundations/curve-blend-exact.forge.js +22 -0
  123. package/examples/api/lean-foundations/curve-fit-interpolation.forge.js +18 -0
  124. package/examples/api/lean-foundations/curve-helix-canonicalization.forge.js +27 -0
  125. package/examples/api/lean-foundations/curve-route-canonicalization.forge.js +16 -0
  126. package/examples/api/lean-foundations/curve-trim-reverse.forge.js +24 -0
  127. package/examples/api/lean-foundations/exact-curve-arc.forge.js +36 -0
  128. package/examples/api/mixed-edge-finishes-proof.forge.js +8 -11
  129. package/examples/api/route3d-elbow.forge.js +68 -0
  130. package/examples/api/transition-curves.forge.js +44 -15
  131. package/examples/api/y-blend-corner-showcase.forge.js +0 -2
  132. package/examples/generative/coral-vase.forge.js +1 -1
  133. package/examples/nurbs-tube.forge.js +1 -1
  134. package/package.json +14 -13
  135. package/dist/assets/EditorApp-lXv53A1m.js +0 -13610
  136. package/dist/assets/app-CsHnaBWt.css +0 -1789
  137. package/dist/assets/forgecad_geometry_bg-C5_E9Oa9.wasm +0 -0
  138. package/dist/assets/solver_bg-CWvv4lnN.wasm +0 -0
  139. package/dist/docs-raw/API/README.md +0 -16
  140. package/dist/docs-raw/API/core/concepts.md +0 -118
  141. package/dist/docs-raw/INDEX.md +0 -138
  142. package/dist/docs-raw/RELEASING.md +0 -87
  143. package/dist/docs-raw/agent-native-api.md +0 -27
  144. package/dist/docs-raw/beta-deployment.md +0 -304
  145. package/dist/docs-raw/beta-operations.md +0 -325
  146. package/dist/docs-raw/blueprint-first.md +0 -145
  147. package/dist/docs-raw/cli-monetization.md +0 -112
  148. package/dist/docs-raw/coding-best-practices.md +0 -120
  149. package/dist/docs-raw/coding.md +0 -340
  150. package/dist/docs-raw/deployment.md +0 -374
  151. package/dist/docs-raw/guides/skill-maintenance.md +0 -161
  152. package/dist/docs-raw/guides/surface-members.md +0 -82
  153. package/dist/docs-raw/harbor-cli.md +0 -854
  154. package/dist/docs-raw/internals/backend-vocabulary.md +0 -35
  155. package/dist/docs-raw/internals/compiler.md +0 -307
  156. package/dist/docs-raw/internals/constraint-solver-quality.md +0 -161
  157. package/dist/docs-raw/internals/constraint-solver.md +0 -176
  158. package/dist/docs-raw/internals/shape-from-slices.md +0 -152
  159. package/dist/docs-raw/internals/sketch-2d-pipeline.md +0 -108
  160. package/dist/docs-raw/platform/admin.md +0 -45
  161. package/dist/docs-raw/platform/architecture.md +0 -82
  162. package/dist/docs-raw/platform/auth.md +0 -139
  163. package/dist/docs-raw/platform/email.md +0 -67
  164. package/dist/docs-raw/platform/google-oauth-setup.md +0 -88
  165. package/dist/docs-raw/platform/observability.md +0 -197
  166. package/dist/docs-raw/platform/projects.md +0 -111
  167. package/dist/docs-raw/platform/sharing.md +0 -90
  168. package/dist/docs-raw/product/README.md +0 -39
  169. package/dist/docs-raw/product/api-as-product-language.md +0 -13
  170. package/dist/docs-raw/product/business-model.md +0 -15
  171. package/dist/docs-raw/product/competitive-positioning.md +0 -17
  172. package/dist/docs-raw/product/creative-manufacturing.md +0 -15
  173. package/dist/docs-raw/product/founder-story.md +0 -11
  174. package/dist/docs-raw/product/manufacturing-workflows.md +0 -15
  175. package/dist/docs-raw/product/onboarding-first-experience.md +0 -256
  176. package/dist/docs-raw/product/product-loop.md +0 -17
  177. package/dist/docs-raw/product/strategic-decisions.md +0 -22
  178. package/dist/docs-raw/product/user-outreach-email-templates.md +0 -161
  179. package/dist/docs-raw/product/user-segments.md +0 -15
  180. package/dist/docs-raw/product/vision.md +0 -26
  181. package/dist/docs-raw/rl-environments.md +0 -350
  182. package/dist/docs-raw/runbook.md +0 -611
  183. package/dist-cli/check-compiler-U5SOPN7X.js.map +0 -1
  184. package/dist-cli/check-query-propagation-XOKNSSYU.js.map +0 -1
  185. package/dist-cli/chunk-EXWGNL6K.js.map +0 -1
  186. package/dist-cli/forgecad.js.map +0 -1
  187. package/dist-cli/forgecad_geometry-GYVNKPIE.js.map +0 -1
  188. package/dist-cli/solver-46FFSK2U.js.map +0 -1
  189. package/dist-skill/SKILL-dev.md +0 -145
  190. package/dist-skill/docs-dev/API/core/concepts.md +0 -118
  191. package/dist-skill/docs-dev/CLI.md +0 -677
  192. package/dist-skill/docs-dev/agent-native-api.md +0 -27
  193. package/dist-skill/docs-dev/blueprint-first.md +0 -145
  194. package/dist-skill/docs-dev/coding-best-practices.md +0 -120
  195. package/dist-skill/docs-dev/coding.md +0 -340
  196. package/dist-skill/docs-dev/component-model.md +0 -164
  197. package/dist-skill/docs-dev/generated/assembly.md +0 -794
  198. package/dist-skill/docs-dev/generated/core.md +0 -2117
  199. package/dist-skill/docs-dev/generated/curves.md +0 -2583
  200. package/dist-skill/docs-dev/generated/lib.md +0 -169
  201. package/dist-skill/docs-dev/generated/output.md +0 -247
  202. package/dist-skill/docs-dev/generated/sdf.md +0 -446
  203. package/dist-skill/docs-dev/generated/sheet-metal.md +0 -504
  204. package/dist-skill/docs-dev/generated/sketch.md +0 -1811
  205. package/dist-skill/docs-dev/generated/viewport.md +0 -585
  206. package/dist-skill/docs-dev/generated/wood.md +0 -108
  207. package/dist-skill/docs-dev/guides/coordinate-system.md +0 -46
  208. package/dist-skill/docs-dev/guides/geometry-conventions.md +0 -52
  209. package/dist-skill/docs-dev/guides/inspection-bundles.md +0 -485
  210. package/dist-skill/docs-dev/guides/joint-design.md +0 -78
  211. package/dist-skill/docs-dev/guides/modeling-recipes.md +0 -78
  212. package/dist-skill/docs-dev/guides/positioning.md +0 -161
  213. package/dist-skill/docs-dev/guides/skill-maintenance.md +0 -161
  214. package/dist-skill/docs-dev/internals/backend-vocabulary.md +0 -35
  215. package/dist-skill/docs-dev/internals/compiler.md +0 -307
  216. package/dist-skill/docs-dev/internals/constraint-solver-quality.md +0 -161
  217. package/dist-skill/docs-dev/internals/constraint-solver.md +0 -176
  218. package/dist-skill/docs-dev/internals/sketch-2d-pipeline.md +0 -108
  219. package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.mjs +0 -289
@@ -1,374 +0,0 @@
1
- ---
2
- ---
3
-
4
- # ForgeCAD Deployment & Environment
5
-
6
- See also the **[Runbook](runbook.md)** for day-to-day operations, testing, troubleshooting, and API reference.
7
- See **[Observability](platform/observability.md)** for the incident debugging workflow, dashboards, logs, metrics, and stack access.
8
-
9
- ForgeCAD runs on a **Hetzner VPS** deployed via **[Kamal 2](https://kamal-deploy.org)**. Kamal handles zero-downtime rolling deploys, TLS, and container orchestration.
10
-
11
- **Environments:**
12
-
13
- | Env | URL | Config | Postgres accessory | Purpose |
14
- |-----|-----|--------|--------------------|---------|
15
- | **production** | https://forgecad.io | `config/deploy.yml` | `forgecad-postgres` | Real users |
16
- | **beta** | https://beta.forgecad.io | `config/deploy.beta.yml` | `forgecad-postgres-beta` | Staging; every change lands here before prod |
17
-
18
- Beta runs on the same VPS as prod, fully isolated at the process level —
19
- separate containers, separate volumes, separate secrets, separate JWT keys.
20
- See [beta-operations.md](beta-operations.md) for the full runbook.
21
-
22
- **Kamal configs, Docker images:**
23
-
24
- | Config | Dockerfile | Service | Image |
25
- |--------|-----------|---------|-------|
26
- | `config/deploy.yml` | `Dockerfile` | `forgecad` (web, prod) | `ghcr.io/kostard/forgecad-web` |
27
- | `config/deploy.beta.yml` | `Dockerfile` | `forgecad-beta` (web, beta) | `ghcr.io/kostard/forgecad-web` |
28
- | `config/deploy.backend.yml` | `Dockerfile.backend` | `forgecad-backend` (compute) | `ghcr.io/kostard/forgecad-backend` |
29
-
30
- **Architecture:**
31
-
32
- ```
33
- Cloudflare (TLS termination, "Full Strict" + Origin Certificate)
34
-
35
- └─ kamal-proxy (:443/:80) ← TLS with Cloudflare Origin Cert
36
-
37
- ├─ forgecad web container ← Fastify app (Node.js) on :5173
38
- │ │
39
- │ ├─ Serves SPA (dist/) ← Vite-built React frontend
40
- │ ├─ API routes (/api/*) ← Auth, projects, files
41
- │ ├─ Metrics listener ← Internal-only Prometheus scrape on :9464
42
- │ └─ connects to ──────────► postgresql accessory
43
-
44
- └─ forgecad-backend container ← Compute server on :4510
45
- └─ ForgeCAD kernel (Node.js + WASM)
46
-
47
- Volume: forgecad-project-data ← User project files on disk
48
- ```
49
-
50
- All containers share the `kamal` Docker network. The web container reaches the backend via the network alias `forgecad-backend`. Postgres is a Kamal accessory — never restarted on app deploys.
51
-
52
- ---
53
-
54
- ## Deploying
55
-
56
- ```bash
57
- # Deploy prod web app (zero-downtime rolling update)
58
- npm run prod:deploy
59
-
60
- # Deploy prod backend compute server
61
- npm run prod:deploy:backend
62
-
63
- # Deploy both prod services
64
- npm run prod:deploy:all
65
-
66
- # Deploy the same commit to beta (run BEFORE prod)
67
- npm run beta:deploy
68
-
69
- # Open the environment after a deploy
70
- npm run beta:smoke
71
- npm run prod:smoke
72
- ```
73
-
74
- Recommended flow for a change: merge to `mainline` → `npm run beta:deploy`
75
- → verify on `beta.forgecad.io` → `npm run prod:deploy`.
76
- See [beta-operations.md](beta-operations.md) for the full beta runbook.
77
-
78
- Kamal builds the Docker image on the server via SSH (`builder: remote:`), pushes to GHCR, pulls on the server, starts the new container, waits for healthcheck, switches traffic, stops old container. Zero downtime.
79
-
80
- ### Auto-deploy via CI
81
-
82
- Push to `mainline` triggers `.github/workflows/deploy.yml` which runs `kamal deploy` (and optionally the backend). The GitHub Action SSHs into Hetzner and orchestrates the build there — no GitHub Actions compute used for building.
83
-
84
- ### Rollback
85
-
86
- ```bash
87
- # Some Kamal installs require an explicit version argument.
88
- # List recent prod web versions on Hetzner:
89
- ssh hetzner 'docker ps -a --format "{{.Names}}\t{{.Status}}" | grep "^forgecad-web-" | sed -E "s/^forgecad-web-([a-f0-9]+)(_replaced_[^[:space:]]+)?\t/\1\t/"'
90
-
91
- # Roll the web app back to the last known-good version:
92
- npm run prod:rollback -- <VERSION>
93
-
94
- # Backend rollback:
95
- npm run prod:rollback:backend -- <VERSION>
96
- ```
97
-
98
- Example:
99
-
100
- ```bash
101
- npm run prod:rollback -- db9c49b058aaa379d9d622456cb249f9b0ddc335
102
- ```
103
-
104
- Use the most recent healthy version from the `docker ps -a` output above. The
105
- version is the hex suffix in the Kamal container name, e.g.
106
- `forgecad-web-<VERSION>`.
107
-
108
- ---
109
-
110
- ## Configuration Files
111
-
112
- | File | Purpose | Gitignored? |
113
- |------|---------|-------------|
114
- | `config/deploy.yml` | Kamal config for prod web app + postgres accessory | No |
115
- | `config/deploy.beta.yml` | Kamal config for beta web app + its own postgres accessory | No |
116
- | `config/deploy.backend.yml` | Kamal config for backend compute (prod) | No |
117
- | `.kamal/secrets` | Production secrets (DB, JWT, OAuth, email, registry) | Yes |
118
- | `.kamal/secrets.beta` | Beta secrets — must differ from prod | Yes |
119
- | `.kamal/secrets.beta.example` | Template for `.kamal/secrets.beta` | No |
120
- | `.kamal/certs/origin-cert.pem` | Cloudflare Origin Certificate (shared prod + beta, needs `*.forgecad.io` SAN) | Yes |
121
- | `.kamal/certs/origin-key.pem` | Cloudflare Origin Private Key | Yes |
122
- | `.github/workflows/deploy.yml` | CI deploy pipeline (prod only — beta is manual) | No |
123
-
124
- ---
125
-
126
- ## Operator Shortcuts
127
-
128
- Routine production and beta admin work is wrapped by repo-local scripts so the
129
- flow stays safe and memorable without changing the public ForgeCAD user CLI:
130
-
131
- ```bash
132
- npm run prod:deploy
133
- npm run prod:smoke
134
- npm run prod:deploy:backend
135
- npm run prod:deploy:all
136
- npm run prod:rollback -- <VERSION>
137
- npm run prod:logs
138
- npm run prod:logs:backend
139
- npm run prod:shell
140
- npm run prod:db
141
- npm run prod:health
142
- npm run prod:errors -- web 1000
143
-
144
- npm run beta:setup
145
- npm run beta:deploy
146
- npm run beta:smoke
147
- npm run beta:logs
148
- npm run beta:refresh
149
- npm run beta:local
150
- ```
151
-
152
- `beta:local` is the local-only smoke path: it launches a hosted frontend plus
153
- `FORGE_ENVIRONMENT=beta` backend on localhost, so you can open the browser and
154
- judge the UX without touching Cloudflare.
155
-
156
- These package scripts are thin wrappers around `scripts/prod.sh`,
157
- `scripts/beta.sh`, and Kamal. Use raw Kamal commands when debugging Kamal
158
- itself; otherwise prefer the environment-prefixed shortcuts.
159
-
160
- ---
161
-
162
- ## Environment Variables
163
-
164
- ### Server Core
165
-
166
- | Variable | Required | Default | Purpose |
167
- |----------|----------|---------|---------|
168
- | `NODE_ENV` | no | `development` | Set to `production` for deployed instances |
169
- | `FORGE_ENVIRONMENT` | no | `production` | `production`, `beta`, or `development`. Drives the BETA banner in the UI and the `/api/health` response. Independent of `NODE_ENV`. |
170
- | `FORGE_PORT` | no | `5173` | HTTP port the server listens on |
171
- | `FORGE_HOST` | no | `127.0.0.1` | Bind address. Use `0.0.0.0` in Docker |
172
- | `FORGE_METRICS_PORT` | no | `9464` | Internal Prometheus metrics listener port |
173
- | `FORGE_METRICS_HOST` | no | `127.0.0.1` | Bind address for the internal metrics listener |
174
- | `FORGE_DIST_DIR` | no | — | Path to Vite build output. In Docker: `/app/dist` |
175
-
176
- ### Database
177
-
178
- ForgeCAD uses PostgreSQL 16 for users, projects, sessions, and audit logs. The database schema is managed by Drizzle ORM with auto-migrations on server startup.
179
-
180
- | Variable | Required | Default | Purpose |
181
- |----------|----------|---------|---------|
182
- | `FORGE_DB_URL` | **yes** | — | PostgreSQL connection string |
183
- | `FORGE_DB_SLOW_QUERY_MS` | no | `250` | Slow-query threshold for DB warning logs and metrics |
184
-
185
- In Kamal, Postgres runs as an accessory with the hostname `forgecad-postgres` on the `kamal` Docker network:
186
- ```
187
- FORGE_DB_URL=postgresql://<user>:<pass>@forgecad-postgres:5432/forgecad
188
- ```
189
-
190
- ### Authentication
191
-
192
- | Variable | Required | Default | Purpose |
193
- |----------|----------|---------|---------|
194
- | `FORGE_JWT_SECRET` | **yes** | — | HMAC key for signing JWT tokens. Generate: `openssl rand -base64 32` |
195
- | `FORGE_JWT_ACCESS_EXPIRES` | no | `15m` | Access token lifetime |
196
- | `FORGE_JWT_REFRESH_EXPIRES` | no | `7d` | Refresh token lifetime |
197
-
198
- ### CORS & Public URL
199
-
200
- | Variable | Required | Default | Purpose |
201
- |----------|----------|---------|---------|
202
- | `FORGE_ALLOWED_ORIGINS` | no | `http://localhost:5173` | Comma-separated CORS origins |
203
- | `FORGE_APP_URL` | no | `http://localhost:5173` | Public base URL for email links |
204
-
205
- ### OAuth Providers
206
-
207
- | Variable | Required | Default | Purpose |
208
- |----------|----------|---------|---------|
209
- | `GITHUB_CLIENT_ID` | no | — | GitHub OAuth app client ID |
210
- | `GITHUB_CLIENT_SECRET` | no | — | GitHub OAuth app client secret |
211
- | `GOOGLE_CLIENT_ID` | no | — | Google OAuth client ID |
212
- | `GOOGLE_CLIENT_SECRET` | no | — | Google OAuth client secret |
213
-
214
- ### Email Delivery
215
-
216
- | Variable | Required | Default | Purpose |
217
- |----------|----------|---------|---------|
218
- | `RESEND_API_KEY` | no | — | API key from resend.com. Without it, emails logged to stdout |
219
- | `FORGE_EMAIL_FROM` | no | `ForgeCAD <noreply@forgecad.io>` | Sender address |
220
-
221
- ### File Storage
222
-
223
- | Variable | Required | Default | Purpose |
224
- |----------|----------|---------|---------|
225
- | `FORGE_STORAGE_ROOT` | no | `/data/projects` | Root directory for user project files |
226
-
227
- In Docker, this is backed by the `forgecad-project-data` volume.
228
-
229
- ### Backend Compute
230
-
231
- | Variable | Required | Default | Purpose |
232
- |----------|----------|---------|---------|
233
- | `FORGE_COMPUTE_URL` | no | `http://localhost:4510` | Backend server address (from Fastify's perspective) |
234
- | `FORGE_BACKEND_PORT` | no | `4510` | Backend server listen port |
235
-
236
- In Kamal, the web app reaches the backend via `http://forgecad-backend:4510` (Docker network alias).
237
-
238
- ---
239
-
240
- ## Secrets Management
241
-
242
- Secrets are stored in `.kamal/secrets` (gitignored). Kamal reads this file as a shell script — you can use `$(cat ...)` for multi-line values like TLS certificates:
243
-
244
- ```bash
245
- FORGE_JWT_SECRET=your-secret-here
246
- FORGE_DB_URL=postgresql://user:pass@forgecad-postgres:5432/forgecad
247
- CLOUDFLARE_ORIGIN_CERT=$(cat .kamal/certs/origin-cert.pem)
248
- CLOUDFLARE_ORIGIN_KEY=$(cat .kamal/certs/origin-key.pem)
249
- KAMAL_REGISTRY_PASSWORD=ghp_your_github_pat
250
- ```
251
-
252
- For CI deploys, secrets are passed as GitHub Actions secrets (see `.github/workflows/deploy.yml`).
253
-
254
- ---
255
-
256
- ## TLS / SSL
257
-
258
- TLS is handled by **Cloudflare** (edge termination) + **Cloudflare Origin Certificate** (origin authentication).
259
-
260
- - Cloudflare SSL mode: **Full (Strict)**
261
- - Origin cert stored in `.kamal/certs/` (gitignored, 15-year validity)
262
- - kamal-proxy serves the Origin Certificate to Cloudflare
263
- - `forward_headers: true` in proxy config passes `X-Forwarded-For` from Cloudflare
264
-
265
- To regenerate the Origin Certificate: Cloudflare dashboard → SSL/TLS → Origin Server → Create Certificate (PEM format, hostnames: `*.forgecad.io` and `forgecad.io`).
266
-
267
- ---
268
-
269
- ## Database Migrations
270
-
271
- Migrations run automatically on server startup (`server/db/migrate.ts`). No manual database setup needed — the ForgeCAD server creates all tables on first boot.
272
-
273
- ---
274
-
275
- ## Debugging Production
276
-
277
- Use the observability stack first for incidents:
278
-
279
- - [runbook.md](runbook.md#observability-first-incident-workflow)
280
- - [platform/observability.md](platform/observability.md)
281
-
282
- SSH access: `ssh hetzner` (see `~/.ssh/config`).
283
-
284
- ```bash
285
- # Kamal-backed commands (run from project root locally)
286
- npm run prod:logs # web app logs
287
- npm run prod:logs:backend # backend logs
288
- npm run prod:shell # shell into web container
289
- kamal proxy details # proxy routing info
290
-
291
- # Direct SSH
292
- ssh hetzner "docker ps --format 'table {{.Names}}\t{{.Status}}' | grep -E 'forgecad|kamal'"
293
- ssh hetzner "df -h / && free -h"
294
- ```
295
-
296
- ### Production scripts
297
-
298
- ```bash
299
- npm run prod:health # containers, disk, memory
300
- npm run prod:errors -- web 1000 # error logs only
301
- npm run prod:db # interactive psql via Kamal
302
- bash scripts/prod/db.sh "SELECT ..." # one-shot SQL query
303
- ```
304
-
305
- ### Email not delivered
306
-
307
- If `RESEND_API_KEY` is empty, emails are logged to stdout instead of sent. Check:
308
- ```bash
309
- kamal app exec "env | grep RESEND"
310
- ```
311
-
312
- ---
313
-
314
- ## Local Development
315
-
316
- ### Quick start (editor only, no auth)
317
-
318
- ```bash
319
- npm run dev
320
- ```
321
-
322
- No database, no login. For geometry, sketches, viewer work.
323
-
324
- ### Full stack (auth, database, landing page, docs, blog)
325
-
326
- ```bash
327
- # Terminal 1: start PostgreSQL
328
- npm run dev:db
329
-
330
- # Terminal 2: start Fastify API server (port 5174)
331
- npm run dev:server
332
-
333
- # Terminal 3: start Vite dev server (port 5173, proxies /api to 5174)
334
- npm run dev:studio
335
- ```
336
-
337
- To stop the database: `npm run dev:db:stop`
338
-
339
- **Dev credentials** (hardcoded in `dev:server`, never used in production):
340
- - Database: `forgecad:forgecad@localhost:5432/forgecad`
341
- - JWT secret: `dev-secret-do-not-use-in-production`
342
-
343
- ### Production build locally
344
-
345
- ```bash
346
- npm run dev:db # start PostgreSQL (once)
347
- npm run dev:prod # tsc + vite build + run Fastify server
348
- ```
349
-
350
- ---
351
-
352
- ## Kamal Setup Notes
353
-
354
- **Prerequisites for deploying from a new machine:**
355
- 1. Ruby (3.1+): `brew install ruby`
356
- 2. Kamal: `gem install kamal`
357
- 3. Docker + buildx: `brew install docker` + buildx plugin
358
- 4. Colima (macOS): `brew install colima && colima start`
359
- 5. SSH access to Hetzner: `ssh hetzner` must work
360
- 6. `.kamal/secrets` populated with production values
361
- 7. `.kamal/certs/` with Cloudflare Origin Certificate PEM files
362
-
363
- **Kamal paths (Homebrew Ruby on macOS):**
364
- ```
365
- /opt/homebrew/opt/ruby/bin
366
- /opt/homebrew/lib/ruby/gems/4.0.0/bin/kamal
367
- ```
368
-
369
- Add to fish: `fish_add_path /opt/homebrew/opt/ruby/bin /opt/homebrew/lib/ruby/gems/4.0.0/bin`
370
-
371
- **Server requirements:**
372
- - Docker installed
373
- - Login shell must be bash (not fish): `sudo chsh -s /bin/bash <user>`
374
- - Docker logged into GHCR: `docker login ghcr.io -u KoStard`
@@ -1,161 +0,0 @@
1
- ---
2
- skill-group: dev-skill
3
- skill-order: 1
4
- ---
5
-
6
- # Skill System Maintenance
7
-
8
- ForgeCAD ships an AI agent skill that teaches language models how to author `.forge.js` models. The skill has two variants:
9
-
10
- - **Standard** (`forgecad skill install`) — model-authoring docs only. Shipped to users.
11
- - **Dev** (`forgecad skill install --dev`) — everything above plus internals (compiler, solver, sketch pipeline, coding conventions, full CLI). For developers building ForgeCAD itself.
12
-
13
- ## Architecture
14
-
15
- ```
16
- docs/permanent/ ← source of truth for all docs
17
- scripts/build-forgecad-skill.mjs ← reads docs, emits skill artifacts
18
- dist-skill/
19
- SKILL.md ← standard skill ({{SKILL_DIR}} placeholders)
20
- SKILL-dev.md ← dev skill (same, with extra sections)
21
- CONTEXT.md ← one-file paste target for chat UIs
22
- docs/ ← full docs tree, copied from docs/permanent/
23
- library/ ← namespaced companion skills for `skill install`
24
- docs/permanent/skills/ ← generated website pages for installable skills
25
- src/pages/generatedSkillSections.ts ← generated docs sidebar/search manifest
26
- skills/forgecad/SKILL.md ← in-repo dev SKILL.md (relative paths)
27
- skills/*/SKILL.md ← companion workflow skills, tagged for public export
28
- ```
29
-
30
- At install time (`forge-skill.ts`), the `{{SKILL_DIR}}` placeholder is replaced with the actual install directory (for example `~/.agents/skills/forgecad`), and the docs tree is copied alongside.
31
-
32
- ## Adding a New Doc to the Skill
33
-
34
- 1. Write the doc under `docs/permanent/` (e.g. `docs/permanent/API/core/my-feature.md`).
35
- 2. Add YAML frontmatter at the top declaring which skill group it belongs to:
36
- ```yaml
37
- ---
38
- skill-group: core
39
- skill-order: 5
40
- ---
41
- ```
42
- 3. Run `npm run refresh` — this rebuilds CLI, types, docs, and skill in the correct order.
43
- 4. Run `node dist-cli/forgecad.js skill install` (or `--dev`) to install locally.
44
-
45
- `skill install` copies the already-built `dist-skill` artifacts. It does not rebuild them. If you are working from a checkout, run `npm run build:skill:forgecad` or `npm run refresh` before installing.
46
-
47
- `forgecad skill flattened-files <output-folder>` also reads the already-built `dist-skill` artifacts. It writes `forgecad.md` plus one Markdown file per bundled companion skill, appending extra files under filename-prefixed sections so each skill can be moved around as a single file.
48
-
49
- The build script discovers docs by scanning `docs/permanent/` for files with `skill-group` frontmatter. No hardcoded file lists to update.
50
-
51
- ### Frontmatter fields
52
-
53
- | Field | Required | Default | Description |
54
- |-------|----------|---------|-------------|
55
- | `skill-group` | Yes | — | Group name (must match `GROUP_REGISTRY` in build script) |
56
- | `skill-order` | No | 50 | Sort order within the group |
57
-
58
- ### Inclusion rules
59
-
60
- - **`skill-group: <name>`** — included in SKILL.md (index) and CONTEXT.md (inlined).
61
- - **`skill-group: dev-<name>`** — dev-only, appears in SKILL-dev.md only.
62
- - **No `skill-group`** — not part of the skill at all.
63
-
64
- ### Adding a new group
65
-
66
- If you need a new group, add it to `GROUP_REGISTRY` in `scripts/build-forgecad-skill.mjs` with a title, guidance, and order number.
67
-
68
- ## Companion Skill Public Export
69
-
70
- ForgeCAD also keeps companion workflow skills under `skills/`. These are the prompts and operating procedures used around the core modeling skill: build-brief preparation, model authoring, render inspection, print-time optimization, public project workflows, and similar.
71
-
72
- Each companion skill declares whether it can be mirrored to the public repository:
73
-
74
- ```yaml
75
- ---
76
- name: forgecad-make-a-model
77
- description: Create new ForgeCAD (.forge.js) models...
78
- forgecad-public: true
79
- ---
80
- ```
81
-
82
- Use `forgecad-public: true` for skills that help people reproduce ForgeCAD modeling workflows. Use `forgecad-public: false` for operational, private, or personal-process skills.
83
-
84
- To list the public set:
85
-
86
- ```bash
87
- npm run sync:public-skills -- --list
88
- ```
89
-
90
- To copy the public README source, rebuild the generated `forgecad` skill, build the installable companion library, and copy all public skills into the sibling public repo:
91
-
92
- ```bash
93
- npm run sync:public-skills
94
- ```
95
-
96
- End users install the core skill and namespaced companion library from the npm package with:
97
-
98
- ```bash
99
- forgecad skill install
100
- ```
101
-
102
- Public companion skill folder names are the installed names. Use the `forgecad-*` prefix directly in `skills/` for public skills, for example `skills/forgecad-prepare-prompt/`, `skills/forgecad-make-a-model/`, and `skills/forgecad-lld/`. The build step copies these folders into `dist-skill/library/` without renaming them.
103
-
104
- The sync script writes:
105
-
106
- - `README.md` in the public repo from this repo's `public-kit/README.md`
107
- - `skills/README.md` in this repo, listing public and private/internal skills
108
- - `skills/README.md` in the public repo, listing only exported skills
109
- - `skills/forgecad/` in the public repo from `dist-skill/`, so `forgecad skill install --core-only` remains available as the simple generated skill path
110
- - every other public skill from `skills/<skill-name>/`
111
-
112
- The skill build also writes the website-facing skill docs:
113
-
114
- - `docs/permanent/skills/index.md`
115
- - one page per installable skill under `docs/permanent/skills/<skill-name>.md`
116
- - `src/pages/generatedSkillSections.ts`, which powers the React docs sidebar, index cards, keyboard navigation, and search list
117
-
118
- Those pages are generated from the same `forgecad-public: true` skill set used for `dist-skill/library/`, so a public companion skill should never be installable without also appearing in the docs.
119
-
120
- ## Adding Dev-Only Docs
121
-
122
- Use a `skill-group` with a `dev-` prefix (e.g. `dev-compiler`, `dev-solver`, `dev-conventions`). These appear in `SKILL-dev.md` but not in `SKILL.md` or `CONTEXT.md`. Use this for:
123
-
124
- - Compiler/solver internals
125
- - Coding conventions and PR guidelines
126
- - Full CLI reference (vs the slim user-facing docs)
127
-
128
- ## Deciding Standard vs Dev
129
-
130
- | Question | If yes → |
131
- |----------|----------|
132
- | Does a model author need this to build `.forge.js` files? | Standard |
133
- | Does it describe internal architecture (compiler, solver, pipeline)? | Dev only |
134
- | Does it cover team process (coding standards, releases, CI)? | Dev only |
135
- | Is it repo-only developer tooling (`npm run check:suite`, `sdf`, debug flags)? | Dev only |
136
-
137
- ## Orphan files
138
-
139
- Current `skill install` removes the installed `~/.agents/skills/forgecad/docs/` directory before copying the built docs tree, so normal installs should not leave doc orphans. If a machine used an older installer or files were edited manually, stale copies can still exist and should be deleted.
140
-
141
- To check for orphans:
142
- ```bash
143
- for f in $(find ~/.agents/skills/forgecad/docs -name '*.md'); do
144
- echo "$f" | grep -q '/generated/' && continue
145
- base=$(echo "$f" | sed "s|$HOME/.agents/skills/forgecad/docs/|docs/permanent/|")
146
- [ -f "$base" ] || echo "ORPHAN: $f"
147
- done
148
- ```
149
-
150
- Delete any orphans found. They're stale copies that can mislead agents with outdated information.
151
-
152
- ## Verifying
153
-
154
- After changes, check that the build succeeds and the doc counts look right:
155
-
156
- ```bash
157
- npm run refresh
158
- node dist-cli/forgecad.js skill install --dev # verify dev install
159
- ```
160
-
161
- The build script prints the number of indexed source files. Verify new docs are counted.
@@ -1,82 +0,0 @@
1
- # Surface Members: Straps, Inlays, Guards, And Structural Bands
2
-
3
- Surface members are for physical material that follows a carrier surface: bottle-cage arms, razor grip inlays, brace ribs, prop guards, helmet vents, and similar curved product structures.
4
-
5
- Use this layer when the model has:
6
-
7
- - a carrier surface such as a cylinder, plane, or `ProductSkin`
8
- - paths or bands that live in carrier-local coordinates
9
- - member-local section thickness, edge radius, slots, cutouts, lips, cups, or ribs
10
- - explicit joins between named members
11
-
12
- Do not use it for ordinary boxes, simple extrusions, sheet-metal bends, exact machined faces, or free-floating assemblies with normal connector placement.
13
-
14
- ## Core Vocabulary
15
-
16
- `Carrier` owns surface coordinates and frames. The first carriers are `Carrier.cylinder(name)`, `Carrier.plane(name)`, and `Carrier.productSkin(skin)`. `Carrier.mesh(name)` and `Carrier.scan(name)` are reserved stubs that fail with direct guidance until arbitrary mesh/scan parameterization exists.
17
-
18
- `SurfaceBody(name)` is the main public builder for this layer. `SurfaceMembers.Body(name)` is the namespaced alias for the same builder; use the direct `SurfaceBody` form in examples where the model is mainly a surface-member graph. There is intentionally no lowercase `ribbonBody()` global in this primitive layer.
19
-
20
- `SurfacePath` samples carrier-local coordinates into continuous 3D frames. Cylinder paths use degrees and handle seam wrapping without user trigonometry. ProductSkin paths stay on one side; use `Carrier.productSkin(skin).sideTransition(fromSide, toSide, { v, name })` for one adjacent side boundary, `sideTransitionChain([from, middle, to], { v, name })` for named boundary pairs, or `sideRoute({ sides, from, to, v, name })` to generate ready-to-use per-side segment endpoints. Transition `v` is normalized from 0 to 1; names must be non-empty when provided; returned boundary coordinates are verified as coincident. These helpers create matching side-local coordinates so you can split the detail into one member per side and join named transition anchors through `SurfaceBody.join(...)`.
21
-
22
- `SurfaceBand` is a widened path footprint. It supports constant, station, eased, and function width profiles, records member-local hole/cut-region intent through `.withHole(name, { length, width, along, across })`, and exposes boundaries and cap intent before geometry lowering.
23
-
24
- `SurfaceBody` owns named members and joins. Members can be `band()` or `plate()`. Features are attached to members before lowering, so a slot or cutout belongs to a member-local coordinate system instead of a global boolean recipe.
25
-
26
- `buildWithDiagnostics()` returns a serializable member graph with endpoint, sampled landing, sampled band-boundary, named rounded slot/cutout feature-center, and explicit named surface-coordinate anchors from `.anchorAt(name, coordinateOrAnchor)`. Named anchors must be unique within a member. Diagnostics include a stable `code` field for agent repair loops; inspect `code` rather than matching prose. Joins must reference existing, distinct member names; invalid sources, targets, self-joins, raw wildcard joins, and conflicting duplicate radii fail directly. Join diagnostics report the nearest endpoint pair, landing candidate, or selected named anchor pair and distance, plus conflict warnings for too-small spans, too-large radii, insufficient overlap, and opposed normals. Close endpoint-to-endpoint joins and close `.join(a, b).betweenAnchors(fromAnchor, toAnchor)` joins lower into limited round connector geometry. Endpoint-to-band and endpoint-to-plate landing joins lower into surface-oriented pad connector geometry. `autoJoinAtSharedAnchors()` lowers only unambiguous shared endpoint pairs and warns when more than two members share a point. Farther, missing-anchor, or ambiguous joins remain diagnostic-only until topology-preserving trimming and blends exist.
27
-
28
- `buildDebug()` returns the normal member shapes plus visible anchor markers, join connection/radius markers, band centerlines, band boundary rails, sampled frame axes, rounded slot/cutout feature outlines, rib-pattern crossbars, and lip/cup profile rails, alongside the same diagnostics and IR.
29
-
30
- Bands report region diagnostics when center coordinates exceed carrier bounds, when ProductSkin side boundaries are crossed, when cylinder band width crosses z bounds, and when simple plane/cylinder boundary polylines self-intersect. These warnings use repair-oriented codes such as `region.centerOutOfBounds`, `region.productSkinBoundaryOutOfBounds`, `region.zBoundaryOutOfBounds`, and `region.boundarySelfIntersection` instead of forcing agents to parse English. The compiler reports these warnings instead of silently treating clipped or crossing geometry as valid.
31
-
32
- `Product.ribbon()` and `Product.surface(...).ribbon(...)` remain the compatibility path for simple one-side conformal NURBS ribbons on a `ProductSkin`. Use `Carrier.productSkin(skin)` plus `SurfaceBody` when the same skin detail needs member-local features, repeated ribs, explicit joins, mirrored members, or the same primitive vocabulary as bottle cages and guards.
33
-
34
- ## Example
35
-
36
- For a minimal guard, use a cylinder carrier, one authored strut, its mirror, and an explicit join to a front hoop:
37
-
38
- ```js
39
- const envelope = Carrier.cylinder("guard-envelope").diameter(84).height(36).clearance(2);
40
-
41
- const guard = SurfaceBody("simple-guard")
42
- .carrier(envelope)
43
- .member("left-strut")
44
- .band()
45
- .path(envelope.path().from({ angle: -132, z: 6 }).to({ angle: -58, z: 18 }))
46
- .section({ width: 5.5, thickness: 2.8, edgeRadius: 0.6 })
47
- .member("right-strut")
48
- .mirrorOf("left-strut")
49
- .member("front-hoop")
50
- .band()
51
- .path(envelope.path().around({ z: 18, fromAngle: -58, toAngle: 58 }))
52
- .section({ width: 6.2, thickness: 3, edgeRadius: 0.7 })
53
- .join("left-strut", "front-hoop").blend({ radius: 3.2 })
54
- .join("right-strut", "front-hoop").blend({ radius: 3.2 })
55
- .build();
56
- ```
57
-
58
- ```js
59
- const bottle = Carrier.cylinder("bottle").diameter(74).height(170).clearance(1.5);
60
-
61
- const cage = SurfaceBody("cage")
62
- .carrier(bottle)
63
- .member("left-arm")
64
- .band()
65
- .path(bottle.path()
66
- .from({ angle: -145, z: 18 })
67
- .through({ angle: -80, z: 72 })
68
- .to({ angle: -34, z: 112 }))
69
- .section({ width: 10, thickness: 5, edgeRadius: 1 })
70
- .member("right-arm")
71
- .mirrorOf("left-arm")
72
- .join("left-arm", "right-arm").blend({ radius: 5, style: "structural" })
73
- .build();
74
-
75
- return cage;
76
- ```
77
-
78
- The API is intentionally blueprint-first: common paths should not require `Math.sin`, `Math.cos`, raw matrices, or hand-built overlap solids.
79
-
80
- ## Current Limits
81
-
82
- The first compiler lowers members as deterministic sampled mesh solids. Bands support square, rounded, and tapered end caps; extended-to-junction caps remain explicit join intent until the junction compiler can trim/extend members. Plate-local rounded slots/cutouts, plate counterbores, band-local rounded cutouts, band rib patterns, lip/cup profile rails, and cup cradle webs are lowered as real mesh features, including variable-width and variable-thickness band footprints. Close endpoint junctions, unambiguous auto shared-endpoint junctions, and selected named-anchor junctions are lowered as small round connector solids at actual anchors; sampled band/plate landing junctions lower as surface-oriented pad connector solids. More advanced cup retention shapes, member trimming, and topology-preserving junction blends are still graph/diagnostic intent, so unsupported high-order transitions should be decomposed or left as future work rather than hidden behind silent fallbacks.