fluidcad 0.0.34 → 0.0.35

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 (174) hide show
  1. package/README.md +69 -0
  2. package/bin/commands/login.js +120 -0
  3. package/bin/commands/pack.js +49 -0
  4. package/bin/commands/publish.js +136 -0
  5. package/bin/fluidcad.js +6 -0
  6. package/bin/lib/api-client.js +40 -0
  7. package/bin/lib/browser.js +16 -0
  8. package/bin/lib/config.js +39 -0
  9. package/bin/lib/model-config.js +38 -0
  10. package/bin/lib/workspace.js +57 -0
  11. package/lib/dist/common/shape-factory.d.ts +2 -1
  12. package/lib/dist/common/shape-factory.js +4 -0
  13. package/lib/dist/common/transformable-primitive.d.ts +6 -5
  14. package/lib/dist/common/transformable-primitive.js +8 -7
  15. package/lib/dist/common/vertex.js +0 -1
  16. package/lib/dist/core/2d/aline.d.ts +4 -3
  17. package/lib/dist/core/2d/aline.js +3 -2
  18. package/lib/dist/core/2d/arc.d.ts +3 -2
  19. package/lib/dist/core/2d/arc.js +4 -3
  20. package/lib/dist/core/2d/bezier.d.ts +8 -6
  21. package/lib/dist/core/2d/circle.d.ts +4 -3
  22. package/lib/dist/core/2d/circle.js +3 -2
  23. package/lib/dist/core/2d/ellipse.d.ts +5 -4
  24. package/lib/dist/core/2d/ellipse.js +5 -4
  25. package/lib/dist/core/2d/hline.d.ts +4 -3
  26. package/lib/dist/core/2d/hline.js +5 -3
  27. package/lib/dist/core/2d/line.js +1 -0
  28. package/lib/dist/core/2d/offset.d.ts +3 -2
  29. package/lib/dist/core/2d/offset.js +6 -5
  30. package/lib/dist/core/2d/polygon.d.ts +5 -4
  31. package/lib/dist/core/2d/polygon.js +10 -9
  32. package/lib/dist/core/2d/rect.d.ts +4 -3
  33. package/lib/dist/core/2d/rect.js +10 -9
  34. package/lib/dist/core/2d/slot.d.ts +14 -6
  35. package/lib/dist/core/2d/slot.js +19 -8
  36. package/lib/dist/core/2d/vline.d.ts +4 -3
  37. package/lib/dist/core/2d/vline.js +5 -3
  38. package/lib/dist/core/chamfer.d.ts +5 -4
  39. package/lib/dist/core/chamfer.js +7 -6
  40. package/lib/dist/core/color.d.ts +3 -2
  41. package/lib/dist/core/color.js +2 -1
  42. package/lib/dist/core/cut.d.ts +4 -3
  43. package/lib/dist/core/cut.js +5 -4
  44. package/lib/dist/core/cylinder.d.ts +2 -1
  45. package/lib/dist/core/cylinder.js +2 -1
  46. package/lib/dist/core/draft.d.ts +3 -2
  47. package/lib/dist/core/draft.js +3 -2
  48. package/lib/dist/core/extrude.d.ts +4 -3
  49. package/lib/dist/core/extrude.js +5 -4
  50. package/lib/dist/core/fillet.d.ts +5 -4
  51. package/lib/dist/core/fillet.js +6 -5
  52. package/lib/dist/core/index.d.ts +1 -0
  53. package/lib/dist/core/index.js +1 -0
  54. package/lib/dist/core/interfaces.d.ts +25 -24
  55. package/lib/dist/core/param.d.ts +74 -0
  56. package/lib/dist/core/param.js +147 -0
  57. package/lib/dist/core/repeat.d.ts +2 -1
  58. package/lib/dist/core/repeat.js +10 -8
  59. package/lib/dist/core/revolve.d.ts +2 -1
  60. package/lib/dist/core/revolve.js +3 -2
  61. package/lib/dist/core/rib.d.ts +3 -2
  62. package/lib/dist/core/rib.js +6 -2
  63. package/lib/dist/core/rotate.d.ts +5 -4
  64. package/lib/dist/core/rotate.js +4 -3
  65. package/lib/dist/core/shell.d.ts +3 -2
  66. package/lib/dist/core/shell.js +3 -2
  67. package/lib/dist/core/sphere.d.ts +3 -2
  68. package/lib/dist/core/sphere.js +2 -1
  69. package/lib/dist/core/translate.d.ts +7 -6
  70. package/lib/dist/core/translate.js +6 -5
  71. package/lib/dist/features/2d/arc.js +5 -5
  72. package/lib/dist/features/2d/bezier.js +16 -16
  73. package/lib/dist/features/2d/circle.js +4 -0
  74. package/lib/dist/features/2d/ellipse.js +4 -0
  75. package/lib/dist/features/2d/hline.d.ts +3 -0
  76. package/lib/dist/features/2d/hline.js +9 -2
  77. package/lib/dist/features/2d/line.d.ts +3 -0
  78. package/lib/dist/features/2d/line.js +11 -3
  79. package/lib/dist/features/2d/sketch.js +5 -1
  80. package/lib/dist/features/2d/slot.d.ts +5 -0
  81. package/lib/dist/features/2d/slot.js +52 -7
  82. package/lib/dist/features/2d/tarc-to-point-tangent.js +3 -0
  83. package/lib/dist/features/2d/tarc-to-point.js +3 -0
  84. package/lib/dist/features/2d/tarc-with-tangent.js +3 -0
  85. package/lib/dist/features/2d/tarc.js +3 -0
  86. package/lib/dist/features/2d/vline.d.ts +3 -0
  87. package/lib/dist/features/2d/vline.js +9 -2
  88. package/lib/dist/features/copy-circular.d.ts +4 -3
  89. package/lib/dist/features/copy-circular.js +16 -9
  90. package/lib/dist/features/copy-circular2d.js +16 -9
  91. package/lib/dist/features/copy-linear.d.ts +4 -3
  92. package/lib/dist/features/copy-linear.js +18 -12
  93. package/lib/dist/features/copy-linear2d.js +18 -12
  94. package/lib/dist/features/extrude-base.d.ts +4 -3
  95. package/lib/dist/features/extrude-base.js +10 -3
  96. package/lib/dist/features/mirror-shape2d.js +2 -2
  97. package/lib/dist/features/repeat-base.d.ts +13 -0
  98. package/lib/dist/features/repeat-base.js +21 -0
  99. package/lib/dist/features/repeat-circular.d.ts +6 -5
  100. package/lib/dist/features/repeat-circular.js +3 -6
  101. package/lib/dist/features/repeat-linear.d.ts +7 -7
  102. package/lib/dist/features/repeat-linear.js +3 -6
  103. package/lib/dist/index.d.ts +5 -0
  104. package/lib/dist/index.js +8 -1
  105. package/lib/dist/io/file-import.d.ts +7 -0
  106. package/lib/dist/io/file-import.js +30 -10
  107. package/lib/dist/math/lazy-matrix.d.ts +5 -0
  108. package/lib/dist/math/lazy-matrix.js +78 -10
  109. package/lib/dist/oc/boolean-ops.d.ts +2 -2
  110. package/lib/dist/param-registry.d.ts +34 -0
  111. package/lib/dist/param-registry.js +60 -0
  112. package/lib/dist/rendering/mesh-builder.js +2 -1
  113. package/lib/dist/tests/features/copy-circular.test.js +1 -1
  114. package/lib/dist/tests/features/copy-linear.test.js +10 -10
  115. package/lib/dist/tests/features/repeat-user-repro-cache.test.d.ts +1 -0
  116. package/lib/dist/tests/features/repeat-user-repro-cache.test.js +97 -0
  117. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  118. package/llm-docs/api/bezier.md +10 -11
  119. package/llm-docs/api/index.json +1 -1
  120. package/llm-docs/api/types/arc-points.md +2 -2
  121. package/llm-docs/api/types/cut.md +10 -10
  122. package/llm-docs/api/types/extrude.md +10 -10
  123. package/llm-docs/api/types/loft.md +6 -6
  124. package/llm-docs/api/types/revolve.md +6 -6
  125. package/llm-docs/api/types/rib.md +2 -2
  126. package/llm-docs/api/types/slot.md +2 -2
  127. package/llm-docs/api/types/sweep.md +10 -10
  128. package/llm-docs/api/types/transformable.md +14 -14
  129. package/llm-docs/index.json +12 -12
  130. package/mcp/dist/client.d.ts +1 -0
  131. package/mcp/dist/client.js +8 -1
  132. package/mcp/dist/server.js +14 -1
  133. package/mcp/dist/tools/engine.d.ts +16 -0
  134. package/mcp/dist/tools/engine.js +45 -0
  135. package/package.json +9 -3
  136. package/server/dist/api.d.ts +37 -0
  137. package/server/dist/api.js +44 -0
  138. package/server/dist/code-editor.d.ts +64 -0
  139. package/server/dist/code-editor.js +520 -2
  140. package/server/dist/fluidcad-server.d.ts +68 -1
  141. package/server/dist/fluidcad-server.js +224 -88
  142. package/server/dist/host/blocked-imports.d.ts +8 -0
  143. package/server/dist/host/blocked-imports.js +30 -0
  144. package/server/dist/{vite-manager.d.ts → host/local-scene-host.d.ts} +3 -1
  145. package/server/dist/{vite-manager.js → host/local-scene-host.js} +6 -26
  146. package/server/dist/host/scene-host.d.ts +19 -0
  147. package/server/dist/host/scene-host.js +1 -0
  148. package/server/dist/index.js +24 -117
  149. package/server/dist/model-package/capture-params.d.ts +19 -0
  150. package/server/dist/model-package/capture-params.js +42 -0
  151. package/server/dist/model-package/pack.d.ts +23 -0
  152. package/server/dist/model-package/pack.js +229 -0
  153. package/server/dist/model-package/types.d.ts +78 -0
  154. package/server/dist/model-package/types.js +17 -0
  155. package/server/dist/routes/hit-test.d.ts +3 -0
  156. package/server/dist/routes/hit-test.js +17 -0
  157. package/server/dist/routes/pack.d.ts +10 -0
  158. package/server/dist/routes/pack.js +47 -0
  159. package/server/dist/routes/params.d.ts +3 -0
  160. package/server/dist/routes/params.js +75 -0
  161. package/server/dist/routes/sketch-edits.d.ts +3 -0
  162. package/server/dist/routes/sketch-edits.js +542 -0
  163. package/server/dist/routes/timeline.d.ts +3 -0
  164. package/server/dist/routes/timeline.js +49 -0
  165. package/server/dist/server-core.d.ts +53 -0
  166. package/server/dist/server-core.js +147 -0
  167. package/server/dist/ws-protocol.d.ts +101 -2
  168. package/ui/dist/assets/index-CDJmUpFI.css +2 -0
  169. package/ui/dist/assets/index-MRqwG9Vh.js +5417 -0
  170. package/ui/dist/index.html +2 -2
  171. package/server/dist/routes/actions.d.ts +0 -3
  172. package/server/dist/routes/actions.js +0 -309
  173. package/ui/dist/assets/index-BdqrMDRu.js +0 -4946
  174. package/ui/dist/assets/index-DR7c2Qk9.css +0 -2
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  id: api/bezier
3
3
  title: bezier(...points)
4
- summary: Free-form bezier curve. The last point is the endpoint; preceding points are control points (degree = args − 1).
4
+ summary: Free-form bezier curve. The first point is the explicit start, the last is the endpoint; points in between are control points (degree = args − 1).
5
5
  tags: [api, 2d, primitive, curve]
6
6
  symbols: [bezier]
7
7
  seeAlso: [api/arc, api/line]
@@ -15,16 +15,15 @@ Imported from `fluidcad/core`.
15
15
  bezier(...points: Point2D[])
16
16
  ```
17
17
 
18
- The last argument is the endpoint; preceding arguments are control points.
18
+ The first argument is the explicit start, the last is the endpoint; any
19
+ arguments in between are control points. Sets the sketch cursor to the
20
+ endpoint.
19
21
 
20
- | Args | Degree | Shape |
21
- |------|----------|--------------------|
22
- | 1 | 1 (line) | straight segment |
23
- | 2 | 2 | quadratic bezier |
24
- | 3 | 3 | cubic bezier |
25
-
26
- The curve starts from the current cursor and ends at the final argument;
27
- intermediate arguments are control handles that shape the curve.
22
+ | Args | Degree | Shape |
23
+ |------|----------|--------------------------------------|
24
+ | 2 | 1 (line) | straight segment from start to end |
25
+ | 3 | 2 | quadratic bezier (start, ctrl, end) |
26
+ | 4 | 3 | cubic bezier (start, c1, c2, end) |
28
27
 
29
28
  ## Example
30
29
 
@@ -33,7 +32,7 @@ import { bezier, extrude, line, sketch } from "fluidcad/core";
33
32
 
34
33
  sketch("xy", () => {
35
34
  line([0, 0], [0, 40]);
36
- bezier([20, 80], [80, 80], [100, 40]); // cubic bezier
35
+ bezier([0, 40], [20, 80], [80, 80], [100, 40]); // cubic bezier
37
36
  line([0, 0]);
38
37
  });
39
38
  extrude(4);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-05-23T14:34:43.886Z",
3
+ "generatedAt": "2026-05-29T18:50:46.977Z",
4
4
  "symbols": {
5
5
  "arc": "api/arc",
6
6
  "axis": "api/axis",
@@ -10,7 +10,7 @@ seeAlso: [api/arc, api/types/extrudable-geometry]
10
10
 
11
11
  ```ts
12
12
  interface ArcPoints extends ExtrudableGeometry {
13
- radius(value: number): IArcRadius;
13
+ radius(value: NumberParam): IArcRadius;
14
14
  center(value: Point2DLike): IArcCenter;
15
15
  }
16
16
  ```
@@ -28,7 +28,7 @@ Positive = CCW, negative = CW.
28
28
 
29
29
  | Parameter | Type | Description |
30
30
  | --- | --- | --- |
31
- | `value` | `number` | The bulge radius. |
31
+ | `value` | `NumberParam` | The bulge radius. |
32
32
 
33
33
  ### `center()`
34
34
 
@@ -12,15 +12,15 @@ seeAlso: [api/cut, api/types/scene-object]
12
12
  interface Cut extends SceneObject {
13
13
  symmetric(): this;
14
14
  scope(...objects: SceneObject[]): this;
15
- draft(value: number | [number, number]): this;
16
- endOffset(value: number): this;
15
+ draft(value: NumberParam | [NumberParam, NumberParam]): this;
16
+ endOffset(value: NumberParam): this;
17
17
  startEdges(...args: (number | EdgeFilter)[]): SceneObject;
18
18
  endEdges(...args: (number | EdgeFilter)[]): SceneObject;
19
19
  internalEdges(...args: (number | EdgeFilter)[]): SceneObject;
20
20
  internalFaces(...args: (number | FaceFilter)[]): SceneObject;
21
21
  pick(...points: Point2DLike[]): this;
22
- thin(offset: number): this;
23
- thin(offset1: number, offset2: number): this;
22
+ thin(offset: NumberParam): this;
23
+ thin(offset1: NumberParam, offset2: NumberParam): this;
24
24
  }
25
25
  ```
26
26
 
@@ -47,7 +47,7 @@ Applies a draft (taper) angle to the cut walls.
47
47
 
48
48
  | Parameter | Type | Description |
49
49
  | --- | --- | --- |
50
- | `value` | `number` \| `[number, number]` | A single angle for uniform draft, or a `[start, end]` tuple for asymmetric draft. |
50
+ | `value` | `NumberParam` \| `[NumberParam, NumberParam]` | A single angle for uniform draft, or a `[start, end]` tuple for asymmetric draft. |
51
51
 
52
52
  ### `endOffset()`
53
53
 
@@ -55,7 +55,7 @@ Offsets the cut end face by a specified distance along the cut direction.
55
55
 
56
56
  | Parameter | Type | Description |
57
57
  | --- | --- | --- |
58
- | `value` | `number` | The offset distance. |
58
+ | `value` | `NumberParam` | The offset distance. |
59
59
 
60
60
  ### `startEdges()`
61
61
 
@@ -108,8 +108,8 @@ Restricts the cut to only the sketch regions containing the given points.
108
108
  ### `thin()`
109
109
 
110
110
  ```ts
111
- thin(offset: number): this
112
- thin(offset1: number, offset2: number): this
111
+ thin(offset: NumberParam): this
112
+ thin(offset1: NumberParam, offset2: NumberParam): this
113
113
  ```
114
114
 
115
115
  Enables thin cut mode — offsets the profile edges to cut a thin-walled shape
@@ -117,8 +117,8 @@ instead of cutting filled faces. Positive values offset outward, negative values
117
117
 
118
118
  | Parameter | Type | Description |
119
119
  | --- | --- | --- |
120
- | `offset1` | `number` | The first wall offset distance. Positive = outward, negative = inward. |
121
- | `offset2` | `number` | The second wall offset distance, in the opposite direction of offset1. |
120
+ | `offset1` | `NumberParam` | The first wall offset distance. Positive = outward, negative = inward. |
121
+ | `offset2` | `NumberParam` | The second wall offset distance, in the opposite direction of offset1. |
122
122
 
123
123
  ## Inherited
124
124
 
@@ -21,12 +21,12 @@ interface Extrude extends BooleanOperation {
21
21
  internalEdges(...args: (number | EdgeFilter)[]): SceneObject;
22
22
  capFaces(...args: (number | FaceFilter)[]): SceneObject;
23
23
  capEdges(...args: (number | EdgeFilter)[]): SceneObject;
24
- draft(value: number | [number, number]): this;
25
- endOffset(value: number): this;
24
+ draft(value: NumberParam | [NumberParam, NumberParam]): this;
25
+ endOffset(value: NumberParam): this;
26
26
  drill(value?: boolean): this;
27
27
  pick(...points: Point2DLike[]): this;
28
- thin(offset: number): this;
29
- thin(offset1: number, offset2: number): this;
28
+ thin(offset: NumberParam): this;
29
+ thin(offset1: NumberParam, offset2: NumberParam): this;
30
30
  }
31
31
  ```
32
32
 
@@ -145,7 +145,7 @@ Applies a draft (taper) angle to the extrusion walls.
145
145
 
146
146
  | Parameter | Type | Description |
147
147
  | --- | --- | --- |
148
- | `value` | `number` \| `[number, number]` | A single angle for uniform draft, or a `[start, end]` tuple for asymmetric draft. |
148
+ | `value` | `NumberParam` \| `[NumberParam, NumberParam]` | A single angle for uniform draft, or a `[start, end]` tuple for asymmetric draft. |
149
149
 
150
150
  ### `endOffset()`
151
151
 
@@ -153,7 +153,7 @@ Offsets the end face by a specified distance along the extrusion direction.
153
153
 
154
154
  | Parameter | Type | Description |
155
155
  | --- | --- | --- |
156
- | `value` | `number` | The offset distance. |
156
+ | `value` | `NumberParam` | The offset distance. |
157
157
 
158
158
  ### `drill()`
159
159
 
@@ -175,8 +175,8 @@ Restricts extrusion to only the sketch regions containing the given points.
175
175
  ### `thin()`
176
176
 
177
177
  ```ts
178
- thin(offset: number): this
179
- thin(offset1: number, offset2: number): this
178
+ thin(offset: NumberParam): this
179
+ thin(offset1: NumberParam, offset2: NumberParam): this
180
180
  ```
181
181
 
182
182
  Enables thin extrude mode — offsets the profile edges to create a thin-walled solid
@@ -184,8 +184,8 @@ instead of extruding filled faces. Positive values offset outward, negative valu
184
184
 
185
185
  | Parameter | Type | Description |
186
186
  | --- | --- | --- |
187
- | `offset1` | `number` | The first wall offset distance. Positive = outward, negative = inward. |
188
- | `offset2` | `number` | The second wall offset distance, in the opposite direction of offset1. |
187
+ | `offset1` | `NumberParam` | The first wall offset distance. Positive = outward, negative = inward. |
188
+ | `offset2` | `NumberParam` | The second wall offset distance, in the opposite direction of offset1. |
189
189
 
190
190
  ## Inherited
191
191
 
@@ -16,8 +16,8 @@ interface Loft extends BooleanOperation {
16
16
  startEdges(...args: (number | EdgeFilter)[]): SceneObject;
17
17
  endEdges(...args: (number | EdgeFilter)[]): SceneObject;
18
18
  sideEdges(...args: (number | EdgeFilter)[]): SceneObject;
19
- thin(offset: number): this;
20
- thin(offset1: number, offset2: number): this;
19
+ thin(offset: NumberParam): this;
20
+ thin(offset1: NumberParam, offset2: NumberParam): this;
21
21
  internalFaces(...args: (number | FaceFilter)[]): SceneObject;
22
22
  internalEdges(...args: (number | EdgeFilter)[]): SceneObject;
23
23
  capFaces(...args: (number | FaceFilter)[]): SceneObject;
@@ -92,8 +92,8 @@ Selects edges on the side faces, excluding edges shared with start/end faces.
92
92
  ### `thin()`
93
93
 
94
94
  ```ts
95
- thin(offset: number): this
96
- thin(offset1: number, offset2: number): this
95
+ thin(offset: NumberParam): this
96
+ thin(offset1: NumberParam, offset2: NumberParam): this
97
97
  ```
98
98
 
99
99
  Enables thin loft mode — offsets the profile edges of each section to create a
@@ -102,8 +102,8 @@ and share the same topology. Positive values offset outward, negative offsets in
102
102
 
103
103
  | Parameter | Type | Description |
104
104
  | --- | --- | --- |
105
- | `offset1` | `number` | The first wall offset distance. Positive = outward, negative = inward. |
106
- | `offset2` | `number` | The second wall offset distance, in the opposite direction of offset1. |
105
+ | `offset1` | `NumberParam` | The first wall offset distance. Positive = outward, negative = inward. |
106
+ | `offset2` | `NumberParam` | The second wall offset distance, in the opposite direction of offset1. |
107
107
 
108
108
  ### `internalFaces()`
109
109
 
@@ -12,8 +12,8 @@ seeAlso: [api/revolve, api/types/boolean-operation]
12
12
  interface Revolve extends BooleanOperation {
13
13
  symmetric(): this;
14
14
  pick(...points: Point2DLike[]): this;
15
- thin(offset: number): this;
16
- thin(offset1: number, offset2: number): this;
15
+ thin(offset: NumberParam): this;
16
+ thin(offset1: NumberParam, offset2: NumberParam): this;
17
17
  internalFaces(...args: (number | FaceFilter)[]): SceneObject;
18
18
  internalEdges(...args: (number | EdgeFilter)[]): SceneObject;
19
19
  capFaces(...args: (number | FaceFilter)[]): SceneObject;
@@ -40,8 +40,8 @@ Restricts the revolve to only the sketch regions containing the given points.
40
40
  ### `thin()`
41
41
 
42
42
  ```ts
43
- thin(offset: number): this
44
- thin(offset1: number, offset2: number): this
43
+ thin(offset: NumberParam): this
44
+ thin(offset1: NumberParam, offset2: NumberParam): this
45
45
  ```
46
46
 
47
47
  Enables thin revolve mode — offsets the profile edges to create a thin-walled
@@ -50,8 +50,8 @@ outward, negative values offset inward.
50
50
 
51
51
  | Parameter | Type | Description |
52
52
  | --- | --- | --- |
53
- | `offset1` | `number` | The first wall offset distance. Positive = outward, negative = inward. |
54
- | `offset2` | `number` | The second wall offset distance, in the opposite direction of offset1. |
53
+ | `offset1` | `NumberParam` | The first wall offset distance. Positive = outward, negative = inward. |
54
+ | `offset2` | `NumberParam` | The second wall offset distance, in the opposite direction of offset1. |
55
55
 
56
56
  ### `internalFaces()`
57
57
 
@@ -18,7 +18,7 @@ interface Rib extends BooleanOperation {
18
18
  endEdges(...args: (number | EdgeFilter)[]): SceneObject;
19
19
  sideEdges(...args: (number | EdgeFilter)[]): SceneObject;
20
20
  capEdges(...args: (number | EdgeFilter)[]): SceneObject;
21
- draft(value: number | [number, number]): this;
21
+ draft(value: NumberParam | [NumberParam, NumberParam]): this;
22
22
  parallel(): this;
23
23
  extend(): this;
24
24
  }
@@ -114,7 +114,7 @@ Applies a draft (taper) angle to the rib walls.
114
114
 
115
115
  | Parameter | Type | Description |
116
116
  | --- | --- | --- |
117
- | `value` | `number` \| `[number, number]` | A single angle for uniform draft, or a `[start, end]` tuple for asymmetric draft. |
117
+ | `value` | `NumberParam` \| `[NumberParam, NumberParam]` | A single angle for uniform draft, or a `[start, end]` tuple for asymmetric draft. |
118
118
 
119
119
  ### `parallel()`
120
120
 
@@ -11,7 +11,7 @@ seeAlso: [api/slot, api/types/extrudable-geometry]
11
11
  ```ts
12
12
  interface Slot extends ExtrudableGeometry {
13
13
  centered(value?: boolean): this;
14
- rotate(angle: number): this;
14
+ rotate(angle: NumberParam): this;
15
15
  }
16
16
  ```
17
17
 
@@ -34,7 +34,7 @@ Sets the rotation angle of the slot's primary axis.
34
34
 
35
35
  | Parameter | Type | Description |
36
36
  | --- | --- | --- |
37
- | `angle` | `number` | Rotation in degrees. |
37
+ | `angle` | `NumberParam` | Rotation in degrees. |
38
38
 
39
39
  ## Inherited
40
40
 
@@ -18,12 +18,12 @@ interface Sweep extends BooleanOperation {
18
18
  sideEdges(...args: (number | EdgeFilter)[]): SceneObject;
19
19
  internalFaces(...args: (number | FaceFilter)[]): SceneObject;
20
20
  internalEdges(...args: (number | EdgeFilter)[]): SceneObject;
21
- draft(value: number | [number, number]): this;
22
- endOffset(value: number): this;
21
+ draft(value: NumberParam | [NumberParam, NumberParam]): this;
22
+ endOffset(value: NumberParam): this;
23
23
  drill(value?: boolean): this;
24
24
  pick(...points: Point2DLike[]): this;
25
- thin(offset: number): this;
26
- thin(offset1: number, offset2: number): this;
25
+ thin(offset: NumberParam): this;
26
+ thin(offset1: NumberParam, offset2: NumberParam): this;
27
27
  capFaces(...args: (number | FaceFilter)[]): SceneObject;
28
28
  capEdges(...args: (number | EdgeFilter)[]): SceneObject;
29
29
  }
@@ -119,7 +119,7 @@ Applies a draft (taper) angle to the sweep walls.
119
119
 
120
120
  | Parameter | Type | Description |
121
121
  | --- | --- | --- |
122
- | `value` | `number` \| `[number, number]` | A single angle for uniform draft, or a `[start, end]` tuple for asymmetric draft. |
122
+ | `value` | `NumberParam` \| `[NumberParam, NumberParam]` | A single angle for uniform draft, or a `[start, end]` tuple for asymmetric draft. |
123
123
 
124
124
  ### `endOffset()`
125
125
 
@@ -127,7 +127,7 @@ Offsets the end face by a specified distance along the sweep direction.
127
127
 
128
128
  | Parameter | Type | Description |
129
129
  | --- | --- | --- |
130
- | `value` | `number` | The offset distance. |
130
+ | `value` | `NumberParam` | The offset distance. |
131
131
 
132
132
  ### `drill()`
133
133
 
@@ -148,8 +148,8 @@ Restricts the sweep to only the sketch regions containing the given points.
148
148
  ### `thin()`
149
149
 
150
150
  ```ts
151
- thin(offset: number): this
152
- thin(offset1: number, offset2: number): this
151
+ thin(offset: NumberParam): this
152
+ thin(offset1: NumberParam, offset2: NumberParam): this
153
153
  ```
154
154
 
155
155
  Enables thin sweep mode — offsets the profile edges to create a thin-walled
@@ -158,8 +158,8 @@ negative values offset inward.
158
158
 
159
159
  | Parameter | Type | Description |
160
160
  | --- | --- | --- |
161
- | `offset1` | `number` | The first wall offset distance. Positive = outward, negative = inward. |
162
- | `offset2` | `number` | The second wall offset distance, in the opposite direction of offset1. |
161
+ | `offset1` | `NumberParam` | The first wall offset distance. Positive = outward, negative = inward. |
162
+ | `offset2` | `NumberParam` | The second wall offset distance, in the opposite direction of offset1. |
163
163
 
164
164
  ### `capFaces()`
165
165
 
@@ -11,12 +11,12 @@ seeAlso: [api/types/scene-object, api/translate, api/rotate]
11
11
  ```ts
12
12
  interface Transformable extends SceneObject {
13
13
  transform(matrix: Matrix4): this;
14
- translate(x: number): this;
15
- translate(x: number, y: number): this;
16
- translate(x: number, y: number, z: number): this;
14
+ translate(x: NumberParam): this;
15
+ translate(x: NumberParam, y: NumberParam): this;
16
+ translate(x: NumberParam, y: NumberParam, z: NumberParam): this;
17
17
  translate(offset: PointLike): this;
18
- rotate(angle: number): this;
19
- rotate(axis: AxisLike, angle: number): this;
18
+ rotate(angle: NumberParam): this;
19
+ rotate(axis: AxisLike, angle: NumberParam): this;
20
20
  mirror(plane: PlaneLike): this;
21
21
  mirror(axis: AxisLike): this;
22
22
  }
@@ -47,9 +47,9 @@ object's own shapes after build. Chained calls compose left-to-right:
47
47
  ### `translate()`
48
48
 
49
49
  ```ts
50
- translate(x: number): this
51
- translate(x: number, y: number): this
52
- translate(x: number, y: number, z: number): this
50
+ translate(x: NumberParam): this
51
+ translate(x: NumberParam, y: NumberParam): this
52
+ translate(x: NumberParam, y: NumberParam, z: NumberParam): this
53
53
  translate(offset: PointLike): this
54
54
  ```
55
55
 
@@ -57,15 +57,15 @@ Translate along X.
57
57
 
58
58
  | Parameter | Type | Description |
59
59
  | --- | --- | --- |
60
- | `x` | `number` | |
61
- | `y` | `number` | |
62
- | `z` | `number` | |
60
+ | `x` | `NumberParam` | |
61
+ | `y` | `NumberParam` | |
62
+ | `z` | `NumberParam` | |
63
63
 
64
64
  ### `rotate()`
65
65
 
66
66
  ```ts
67
- rotate(angle: number): this
68
- rotate(axis: AxisLike, angle: number): this
67
+ rotate(angle: NumberParam): this
68
+ rotate(axis: AxisLike, angle: NumberParam): this
69
69
  ```
70
70
 
71
71
  Rotate by an angle around world Z through the origin.
@@ -73,7 +73,7 @@ Rotate by an angle around world Z through the origin.
73
73
  | Parameter | Type | Description |
74
74
  | --- | --- | --- |
75
75
  | `axis` | [[api/types/axis-like]] | The axis to rotate around. Use `local(...)` to reference a sketch-local axis. |
76
- | `angle` | `number` | Rotation in degrees. |
76
+ | `angle` | `NumberParam` | Rotation in degrees. |
77
77
 
78
78
  ### `mirror()`
79
79
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-05-23T14:34:43.886Z",
3
+ "generatedAt": "2026-05-29T18:50:46.977Z",
4
4
  "docs": [
5
5
  {
6
6
  "id": "api/arc",
@@ -47,7 +47,7 @@
47
47
  {
48
48
  "id": "api/bezier",
49
49
  "title": "bezier(...points)",
50
- "summary": "Free-form bezier curve. The last point is the endpoint; preceding points are control points (degree = args − 1).",
50
+ "summary": "Free-form bezier curve. The first point is the explicit start, the last is the endpoint; points in between are control points (degree = args − 1).",
51
51
  "tags": [
52
52
  "api",
53
53
  "2d",
@@ -62,7 +62,7 @@
62
62
  "api/line"
63
63
  ],
64
64
  "file": "api/bezier.md",
65
- "bodyLength": 788
65
+ "bodyLength": 826
66
66
  },
67
67
  {
68
68
  "id": "api/booleans",
@@ -986,7 +986,7 @@
986
986
  "api/types/extrudable-geometry"
987
987
  ],
988
988
  "file": "api/types/arc-points.md",
989
- "bodyLength": 840
989
+ "bodyLength": 850
990
990
  },
991
991
  {
992
992
  "id": "api/types/axis-like",
@@ -1106,7 +1106,7 @@
1106
1106
  "api/types/scene-object"
1107
1107
  ],
1108
1108
  "file": "api/types/cut.md",
1109
- "bodyLength": 3643
1109
+ "bodyLength": 3723
1110
1110
  },
1111
1111
  {
1112
1112
  "id": "api/types/draft",
@@ -1166,7 +1166,7 @@
1166
1166
  "api/types/boolean-operation"
1167
1167
  ],
1168
1168
  "file": "api/types/extrude.md",
1169
- "bodyLength": 6019
1169
+ "bodyLength": 6099
1170
1170
  },
1171
1171
  {
1172
1172
  "id": "api/types/geometry",
@@ -1243,7 +1243,7 @@
1243
1243
  "api/types/boolean-operation"
1244
1244
  ],
1245
1245
  "file": "api/types/loft.md",
1246
- "bodyLength": 4832
1246
+ "bodyLength": 4872
1247
1247
  },
1248
1248
  {
1249
1249
  "id": "api/types/mirror",
@@ -1443,7 +1443,7 @@
1443
1443
  "api/types/boolean-operation"
1444
1444
  ],
1445
1445
  "file": "api/types/revolve.md",
1446
- "bodyLength": 3040
1446
+ "bodyLength": 3080
1447
1447
  },
1448
1448
  {
1449
1449
  "id": "api/types/rib",
@@ -1463,7 +1463,7 @@
1463
1463
  "api/types/boolean-operation"
1464
1464
  ],
1465
1465
  "file": "api/types/rib.md",
1466
- "bodyLength": 3784
1466
+ "bodyLength": 3814
1467
1467
  },
1468
1468
  {
1469
1469
  "id": "api/types/scene-object",
@@ -1543,7 +1543,7 @@
1543
1543
  "api/types/extrudable-geometry"
1544
1544
  ],
1545
1545
  "file": "api/types/slot.md",
1546
- "bodyLength": 811
1546
+ "bodyLength": 821
1547
1547
  },
1548
1548
  {
1549
1549
  "id": "api/types/sweep",
@@ -1563,7 +1563,7 @@
1563
1563
  "api/types/boolean-operation"
1564
1564
  ],
1565
1565
  "file": "api/types/sweep.md",
1566
- "bodyLength": 5797
1566
+ "bodyLength": 5877
1567
1567
  },
1568
1568
  {
1569
1569
  "id": "api/types/tangent-arc-two-objects",
@@ -1604,7 +1604,7 @@
1604
1604
  "api/rotate"
1605
1605
  ],
1606
1606
  "file": "api/types/transformable.md",
1607
- "bodyLength": 2186
1607
+ "bodyLength": 2286
1608
1608
  },
1609
1609
  {
1610
1610
  "id": "api/types/trim",
@@ -39,6 +39,7 @@ export declare class FluidCadClient {
39
39
  statusCode: number;
40
40
  data: Buffer;
41
41
  contentType: string;
42
+ headers: Record<string, string>;
42
43
  }>;
43
44
  ensureWebSocket(): Promise<WebSocket>;
44
45
  /**
@@ -70,7 +70,14 @@ export class FluidCadClient {
70
70
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
71
71
  }
72
72
  const contentType = String(res.headers['content-type'] ?? 'application/octet-stream');
73
- return { statusCode: res.statusCode, data: Buffer.concat(chunks), contentType };
73
+ const headers = {};
74
+ for (const [k, v] of Object.entries(res.headers)) {
75
+ if (typeof v === 'string')
76
+ headers[k.toLowerCase()] = v;
77
+ else if (Array.isArray(v) && v.length > 0)
78
+ headers[k.toLowerCase()] = v[0];
79
+ }
80
+ return { statusCode: res.statusCode, data: Buffer.concat(chunks), contentType, headers };
74
81
  }
75
82
  async ensureWebSocket() {
76
83
  if (this.closed) {
@@ -15,7 +15,7 @@ import { getCompileError, getEdgeProperties, getFaceProperties, getSceneSummary,
15
15
  import { getCameraState, screenshot, screenshotMulti, screenshotShape, } from "./tools/screenshot.js";
16
16
  import { waitForIdle } from "./tools/coordination.js";
17
17
  import { editRange, listFluidFiles, readFile, writeFile, } from "./tools/source.js";
18
- import { addBreakpoint, clearBreakpoints, exportShapes, importStep, recompute, rollbackTo, } from "./tools/engine.js";
18
+ import { addBreakpoint, clearBreakpoints, exportShapes, importStep, packModel, recompute, rollbackTo, } from "./tools/engine.js";
19
19
  import { loadDocsIndex } from "./docs-index.js";
20
20
  import { registerDocResources } from "./resources.js";
21
21
  export const SERVER_NAME = 'FluidCAD';
@@ -441,6 +441,19 @@ export function buildServer(options = {}) {
441
441
  resolution,
442
442
  includeColors,
443
443
  })));
444
+ server.registerTool('pack_model', {
445
+ title: 'Package the current model into a shareable .fluidpkg archive',
446
+ description: 'Produces a self-contained .fluidpkg (zip) capturing the current `.fluid.js` entry as an esbuild ES module bundle, any STEP assets in the workspace (as raw bytes), the live param overrides, and the latest camera state. Inside the archive: `manifest.json`, `bundle.js`, optional `init.js`, and `assets/<path>` entries. Prefer `saveAsPath` — the binary payload can be multi-MB and shouldn\'t round-trip through the agent\'s context as base64. Returns `{ savedTo, bytesWritten, packageName }` when saved, or `{ mimeType, base64, bytes, packageName }` otherwise.',
447
+ inputSchema: {
448
+ ...workspaceArg,
449
+ name: z.string().optional().describe('Optional package name; defaults to the entry file basename. Becomes the file name when `saveAsPath` is omitted.'),
450
+ description: z.string().optional().describe('Optional human description embedded in manifest.json.'),
451
+ saveAsPath: z
452
+ .string()
453
+ .optional()
454
+ .describe('Absolute path to write the .fluidpkg to. If omitted, the package is returned inline (base64).'),
455
+ },
456
+ }, async ({ workspace, name, description, saveAsPath }) => toMcp(await packModel({ workspace, name, description, saveAsPath })));
444
457
  return server;
445
458
  }
446
459
  export async function runStdio() {
@@ -53,4 +53,20 @@ export type ExportBase64Output = {
53
53
  bytes: number;
54
54
  };
55
55
  export type ExportOutput = ExportSavedOutput | ExportBase64Output;
56
+ export type PackModelInput = WorkspaceArg & {
57
+ name?: string;
58
+ description?: string;
59
+ saveAsPath?: string;
60
+ };
61
+ export type PackModelOutput = {
62
+ savedTo: string;
63
+ bytesWritten: number;
64
+ packageName: string;
65
+ } | {
66
+ mimeType: string;
67
+ base64: string;
68
+ bytes: number;
69
+ packageName: string;
70
+ };
71
+ export declare function packModel(input: PackModelInput): Promise<ToolResult<PackModelOutput>>;
56
72
  export declare function exportShapes(input: ExportInput): Promise<ToolResult<ExportOutput>>;
@@ -72,6 +72,51 @@ export async function importStep(input) {
72
72
  const data = bytes.toString('base64');
73
73
  return callWithClient(input, (client) => client.postJson('/api/import-file', { fileName, data }));
74
74
  }
75
+ export async function packModel(input) {
76
+ if (input.saveAsPath !== undefined && typeof input.saveAsPath !== 'string') {
77
+ return err('invalid-input', '`saveAsPath` must be a string when provided.');
78
+ }
79
+ const resolved = resolveClient(input);
80
+ if (resolved.ok === false) {
81
+ return resolved;
82
+ }
83
+ const { client } = resolved.data;
84
+ try {
85
+ const raw = await client.postRaw('/api/pack', {
86
+ name: input.name,
87
+ description: input.description,
88
+ });
89
+ if (raw.statusCode >= 400) {
90
+ const text = raw.data.toString('utf8');
91
+ return err('http-error', `HTTP ${raw.statusCode}: ${text.slice(0, 200)}`, {
92
+ statusCode: raw.statusCode,
93
+ });
94
+ }
95
+ const packageName = raw.headers['x-fluidcad-package-name'] ?? 'model';
96
+ if (input.saveAsPath) {
97
+ const absPath = path.resolve(input.saveAsPath);
98
+ await fsp.writeFile(absPath, raw.data);
99
+ return ok({ savedTo: absPath, bytesWritten: raw.data.length, packageName });
100
+ }
101
+ return ok({
102
+ mimeType: raw.contentType,
103
+ base64: raw.data.toString('base64'),
104
+ bytes: raw.data.length,
105
+ packageName,
106
+ });
107
+ }
108
+ catch (e) {
109
+ if (e instanceof HttpError) {
110
+ return err('http-error', `HTTP ${e.statusCode}: ${e.body.slice(0, 200)}`, {
111
+ statusCode: e.statusCode,
112
+ });
113
+ }
114
+ return err('internal', e?.message ?? String(e));
115
+ }
116
+ finally {
117
+ await client.close().catch(() => { });
118
+ }
119
+ }
75
120
  export async function exportShapes(input) {
76
121
  if (input?.format !== 'step' && input?.format !== 'stl') {
77
122
  return err('invalid-input', '`format` is required and must be "step" or "stl".');