@fugood/bricks-ctor 2.25.0-beta.6 → 2.25.0-beta.60

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 (188) hide show
  1. package/compile/__tests__/config-diff.test.js +100 -0
  2. package/compile/__tests__/index.test.js +461 -0
  3. package/compile/__tests__/util.test.js +450 -0
  4. package/compile/action-name-map.ts +64 -0
  5. package/compile/config-diff.ts +155 -0
  6. package/compile/index.ts +668 -352
  7. package/compile/util.ts +134 -10
  8. package/package.json +7 -3
  9. package/skills/bricks-ctor/SKILL.md +23 -17
  10. package/skills/bricks-ctor/{rules → references}/animation.md +3 -2
  11. package/skills/bricks-ctor/{rules → references}/architecture-patterns.md +19 -0
  12. package/skills/bricks-ctor/{rules → references}/automations.md +11 -0
  13. package/skills/bricks-ctor/references/buttress.md +245 -0
  14. package/skills/bricks-ctor/references/data-calculation.md +252 -0
  15. package/skills/bricks-ctor/{rules → references}/media-flow.md +7 -0
  16. package/skills/bricks-ctor/references/simulator.md +132 -0
  17. package/skills/bricks-ctor/references/source-editing-tools.md +81 -0
  18. package/skills/bricks-ctor/references/verification-toolchain.md +200 -0
  19. package/skills/bricks-design/SKILL.md +150 -45
  20. package/skills/bricks-design/references/architecture-truths.md +132 -0
  21. package/skills/bricks-design/references/avoiding-complexity.md +91 -0
  22. package/skills/bricks-design/references/design-critique.md +195 -0
  23. package/skills/bricks-design/references/design-languages.md +265 -0
  24. package/skills/bricks-design/references/performance.md +116 -0
  25. package/skills/bricks-design/references/presentation-and-slideshow.md +137 -0
  26. package/skills/bricks-design/references/translating-inputs.md +152 -0
  27. package/skills/bricks-design/references/variations-and-tweaks.md +124 -0
  28. package/skills/bricks-design/references/when-the-brief-is-branded.md +284 -0
  29. package/skills/bricks-design/references/when-the-brief-is-vague.md +85 -0
  30. package/skills/bricks-design/references/workflow.md +134 -0
  31. package/skills/bricks-ux/SKILL.md +114 -0
  32. package/skills/bricks-ux/references/accessibility.md +162 -0
  33. package/skills/bricks-ux/references/flow-states.md +175 -0
  34. package/skills/bricks-ux/references/interaction-archetypes.md +189 -0
  35. package/skills/bricks-ux/references/monitoring-screens.md +153 -0
  36. package/skills/bricks-ux/references/pressable-composition.md +126 -0
  37. package/skills/bricks-ux/references/user-journey.md +168 -0
  38. package/skills/bricks-ux/references/ux-critique.md +256 -0
  39. package/tools/__tests__/_cli-error.test.ts +35 -0
  40. package/tools/__tests__/_mcp-config.test.ts +67 -0
  41. package/tools/__tests__/pull.test.ts +108 -0
  42. package/tools/_cli-error.ts +17 -0
  43. package/tools/_edits-log.ts +41 -0
  44. package/tools/_git-author.ts +10 -2
  45. package/tools/_last-pushed-commit.ts +28 -0
  46. package/tools/_mcp-config.ts +42 -0
  47. package/tools/_shell.ts +8 -1
  48. package/tools/deploy.ts +17 -6
  49. package/tools/mcp-env.ts +13 -0
  50. package/tools/mcp-server.ts +8 -0
  51. package/tools/mcp-tools/__tests__/data-calc-editing.test.js +516 -0
  52. package/tools/mcp-tools/__tests__/entry-editing.test.js +866 -0
  53. package/tools/mcp-tools/__tests__/huggingface.test.ts +49 -0
  54. package/tools/mcp-tools/__tests__/icons.test.ts +21 -0
  55. package/tools/mcp-tools/__tests__/mcp-env.test.js +19 -0
  56. package/tools/mcp-tools/_editing-helpers.ts +98 -0
  57. package/tools/mcp-tools/_verify.ts +50 -0
  58. package/tools/mcp-tools/compile.ts +21 -9
  59. package/tools/mcp-tools/data-calc-editing.ts +1311 -0
  60. package/tools/mcp-tools/entry-editing.ts +2297 -0
  61. package/tools/mcp-tools/huggingface.ts +23 -13
  62. package/tools/mcp-tools/icons.ts +23 -7
  63. package/tools/mcp-tools/media.ts +4 -1
  64. package/tools/postinstall.ts +95 -38
  65. package/tools/pull.ts +100 -23
  66. package/tools/push-config.ts +114 -0
  67. package/tools/{preview-main.mjs → simulator-main.mjs} +207 -12
  68. package/tools/simulator-preload.cjs +16 -0
  69. package/tools/{preview.ts → simulator.ts} +4 -4
  70. package/types/{animation.ts → animation.d.ts} +24 -8
  71. package/types/{automation.ts → automation.d.ts} +16 -20
  72. package/types/{brick-base.ts → brick-base.d.ts} +1 -1
  73. package/types/bricks/{Camera.ts → Camera.d.ts} +8 -8
  74. package/types/bricks/{Chart.ts → Chart.d.ts} +4 -4
  75. package/types/bricks/{GenerativeMedia.ts → GenerativeMedia.d.ts} +15 -15
  76. package/types/bricks/{Icon.ts → Icon.d.ts} +7 -7
  77. package/types/bricks/{Image.ts → Image.d.ts} +21 -9
  78. package/types/bricks/{Items.ts → Items.d.ts} +11 -7
  79. package/types/bricks/{Lottie.ts → Lottie.d.ts} +10 -10
  80. package/types/bricks/{Maps.ts → Maps.d.ts} +11 -11
  81. package/types/bricks/{QrCode.ts → QrCode.d.ts} +7 -7
  82. package/types/bricks/{Rect.ts → Rect.d.ts} +7 -7
  83. package/types/bricks/{RichText.ts → RichText.d.ts} +12 -9
  84. package/types/bricks/{Rive.ts → Rive.d.ts} +9 -9
  85. package/types/bricks/Scene3D.d.ts +676 -0
  86. package/types/bricks/{Sketch.ts → Sketch.d.ts} +10 -8
  87. package/types/bricks/{Slideshow.ts → Slideshow.d.ts} +7 -7
  88. package/types/bricks/{Svg.ts → Svg.d.ts} +7 -7
  89. package/types/bricks/{Text.ts → Text.d.ts} +9 -9
  90. package/types/bricks/{TextInput.ts → TextInput.d.ts} +10 -10
  91. package/types/bricks/{Video.ts → Video.d.ts} +80 -13
  92. package/types/bricks/{VideoStreaming.ts → VideoStreaming.d.ts} +10 -10
  93. package/types/bricks/{WebRtcStream.ts → WebRtcStream.d.ts} +1 -1
  94. package/types/bricks/{WebView.ts → WebView.d.ts} +4 -4
  95. package/types/bricks/{index.ts → index.d.ts} +1 -0
  96. package/types/{common.ts → common.d.ts} +3 -6
  97. package/types/data-calc-command/base.d.ts +57 -0
  98. package/types/data-calc-command/collection.d.ts +418 -0
  99. package/types/data-calc-command/color.d.ts +432 -0
  100. package/types/data-calc-command/constant.d.ts +50 -0
  101. package/types/data-calc-command/datetime.d.ts +147 -0
  102. package/types/data-calc-command/file.d.ts +129 -0
  103. package/types/data-calc-command/index.d.ts +13 -0
  104. package/types/data-calc-command/iteratee.d.ts +23 -0
  105. package/types/data-calc-command/logictype.d.ts +190 -0
  106. package/types/data-calc-command/math.d.ts +275 -0
  107. package/types/data-calc-command/object.d.ts +119 -0
  108. package/types/data-calc-command/sandbox.d.ts +66 -0
  109. package/types/data-calc-command/string.d.ts +407 -0
  110. package/types/{data-calc.ts → data-calc.d.ts} +1 -0
  111. package/types/{data.ts → data.d.ts} +4 -2
  112. package/types/generators/{Assistant.ts → Assistant.d.ts} +19 -0
  113. package/types/generators/{HttpServer.ts → HttpServer.d.ts} +56 -2
  114. package/types/generators/{LlmGgml.ts → LlmGgml.d.ts} +43 -1
  115. package/types/generators/{LlmMlx.ts → LlmMlx.d.ts} +1 -0
  116. package/types/generators/{RerankerGgml.ts → RerankerGgml.d.ts} +5 -1
  117. package/types/generators/{SoundRecorder.ts → SoundRecorder.d.ts} +10 -1
  118. package/types/generators/{SpeechToTextGgml.ts → SpeechToTextGgml.d.ts} +6 -1
  119. package/types/generators/{SttAppleBuiltin.ts → SttAppleBuiltin.d.ts} +27 -4
  120. package/types/generators/{ThermalPrinter.ts → ThermalPrinter.d.ts} +9 -7
  121. package/types/generators/{Tick.ts → Tick.d.ts} +1 -1
  122. package/types/generators/{VadGgml.ts → VadGgml.d.ts} +12 -2
  123. package/types/{subspace.ts → subspace.d.ts} +1 -1
  124. package/utils/__tests__/calc.test.js +25 -0
  125. package/utils/__tests__/id.test.js +154 -0
  126. package/utils/calc.ts +5 -1
  127. package/utils/data.ts +5 -7
  128. package/utils/event-props.ts +27 -1
  129. package/utils/id.ts +109 -56
  130. package/skills/bricks-ctor/rules/buttress.md +0 -156
  131. package/skills/bricks-ctor/rules/data-calculation.md +0 -209
  132. package/skills/bricks-design/LICENSE.txt +0 -180
  133. package/types/data-calc-command.ts +0 -7005
  134. /package/skills/bricks-ctor/{rules → references}/local-sync.md +0 -0
  135. /package/skills/bricks-ctor/{rules → references}/remote-data-bank.md +0 -0
  136. /package/skills/bricks-ctor/{rules → references}/standby-transition.md +0 -0
  137. /package/types/{canvas.ts → canvas.d.ts} +0 -0
  138. /package/types/{data-calc-script.ts → data-calc-script.d.ts} +0 -0
  139. /package/types/generators/{AlarmClock.ts → AlarmClock.d.ts} +0 -0
  140. /package/types/generators/{BleCentral.ts → BleCentral.d.ts} +0 -0
  141. /package/types/generators/{BlePeripheral.ts → BlePeripheral.d.ts} +0 -0
  142. /package/types/generators/{CanvasMap.ts → CanvasMap.d.ts} +0 -0
  143. /package/types/generators/{CastlesPay.ts → CastlesPay.d.ts} +0 -0
  144. /package/types/generators/{DataBank.ts → DataBank.d.ts} +0 -0
  145. /package/types/generators/{File.ts → File.d.ts} +0 -0
  146. /package/types/generators/{GraphQl.ts → GraphQl.d.ts} +0 -0
  147. /package/types/generators/{Http.ts → Http.d.ts} +0 -0
  148. /package/types/generators/{Information.ts → Information.d.ts} +0 -0
  149. /package/types/generators/{Intent.ts → Intent.d.ts} +0 -0
  150. /package/types/generators/{Iterator.ts → Iterator.d.ts} +0 -0
  151. /package/types/generators/{Keyboard.ts → Keyboard.d.ts} +0 -0
  152. /package/types/generators/{LlmAnthropicCompat.ts → LlmAnthropicCompat.d.ts} +0 -0
  153. /package/types/generators/{LlmAppleBuiltin.ts → LlmAppleBuiltin.d.ts} +0 -0
  154. /package/types/generators/{LlmMediaTekNeuroPilot.ts → LlmMediaTekNeuroPilot.d.ts} +0 -0
  155. /package/types/generators/{LlmOnnx.ts → LlmOnnx.d.ts} +0 -0
  156. /package/types/generators/{LlmOpenAiCompat.ts → LlmOpenAiCompat.d.ts} +0 -0
  157. /package/types/generators/{LlmQualcommAiEngine.ts → LlmQualcommAiEngine.d.ts} +0 -0
  158. /package/types/generators/{Mcp.ts → Mcp.d.ts} +0 -0
  159. /package/types/generators/{McpServer.ts → McpServer.d.ts} +0 -0
  160. /package/types/generators/{MediaFlow.ts → MediaFlow.d.ts} +0 -0
  161. /package/types/generators/{MqttBroker.ts → MqttBroker.d.ts} +0 -0
  162. /package/types/generators/{MqttClient.ts → MqttClient.d.ts} +0 -0
  163. /package/types/generators/{Question.ts → Question.d.ts} +0 -0
  164. /package/types/generators/{RealtimeTranscription.ts → RealtimeTranscription.d.ts} +0 -0
  165. /package/types/generators/{SerialPort.ts → SerialPort.d.ts} +0 -0
  166. /package/types/generators/{SoundPlayer.ts → SoundPlayer.d.ts} +0 -0
  167. /package/types/generators/{SpeechToTextOnnx.ts → SpeechToTextOnnx.d.ts} +0 -0
  168. /package/types/generators/{SpeechToTextPlatform.ts → SpeechToTextPlatform.d.ts} +0 -0
  169. /package/types/generators/{SqLite.ts → SqLite.d.ts} +0 -0
  170. /package/types/generators/{Step.ts → Step.d.ts} +0 -0
  171. /package/types/generators/{Tcp.ts → Tcp.d.ts} +0 -0
  172. /package/types/generators/{TcpServer.ts → TcpServer.d.ts} +0 -0
  173. /package/types/generators/{TextToSpeechAppleBuiltin.ts → TextToSpeechAppleBuiltin.d.ts} +0 -0
  174. /package/types/generators/{TextToSpeechGgml.ts → TextToSpeechGgml.d.ts} +0 -0
  175. /package/types/generators/{TextToSpeechOnnx.ts → TextToSpeechOnnx.d.ts} +0 -0
  176. /package/types/generators/{TextToSpeechOpenAiLike.ts → TextToSpeechOpenAiLike.d.ts} +0 -0
  177. /package/types/generators/{Udp.ts → Udp.d.ts} +0 -0
  178. /package/types/generators/{VadOnnx.ts → VadOnnx.d.ts} +0 -0
  179. /package/types/generators/{VadTraditional.ts → VadTraditional.d.ts} +0 -0
  180. /package/types/generators/{VectorStore.ts → VectorStore.d.ts} +0 -0
  181. /package/types/generators/{Watchdog.ts → Watchdog.d.ts} +0 -0
  182. /package/types/generators/{WebCrawler.ts → WebCrawler.d.ts} +0 -0
  183. /package/types/generators/{WebRtc.ts → WebRtc.d.ts} +0 -0
  184. /package/types/generators/{WebSocket.ts → WebSocket.d.ts} +0 -0
  185. /package/types/generators/{index.ts → index.d.ts} +0 -0
  186. /package/types/{index.ts → index.d.ts} +0 -0
  187. /package/types/{switch.ts → switch.d.ts} +0 -0
  188. /package/types/{system.ts → system.d.ts} +0 -0
package/compile/util.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { makeId } from '../utils/id'
1
+ import { makeSeededId } from '../utils/id'
2
2
 
3
3
  type ScriptConfig = {
4
4
  title?: string
@@ -18,30 +18,152 @@ const errorMsg = 'Not allow duplicate set property id between inputs / outputs /
18
18
  export const validateConfig = (config: ScriptConfig) => {
19
19
  // Skip input/output overlap validation in manual mode (allows same data node in both)
20
20
  if (config.trigger_mode === 'manual') return
21
- if (config.error && config.inputs[config.error]) {
21
+ // `inputs` is keyed by data-node id with the OBJECT_SET path as the *value*. The overlap
22
+ // check is "is this id also an input?" — a key-existence question — so test key presence,
23
+ // not the path's truthiness. An empty path ('' = OBJECT_SET's default "set whole object")
24
+ // is a legitimate input value that would otherwise slip the overlap guard.
25
+ const isInput = (id: string) => Object.prototype.hasOwnProperty.call(config.inputs, id)
26
+ if (config.error && isInput(config.error)) {
22
27
  throw new Error(`${errorMsg}. key: error`)
23
28
  }
24
- if (config.output && config.inputs[config.output]) {
29
+ if (config.output && isInput(config.output)) {
25
30
  throw new Error(`${errorMsg}. key: output`)
26
31
  }
27
- if (Object.values(config.outputs).some((value) => value.some((id) => config.inputs[id]))) {
32
+ if (Object.values(config.outputs).some((value) => value.some(isInput))) {
28
33
  throw new Error(`${errorMsg}. key: outputs`)
29
34
  }
35
+ // The same data-node id reused across the output-side targets (output / error / outputs)
36
+ // also collides: generateCalulationMap spreads their node objects last-wins, so the later
37
+ // one silently overwrites the earlier wiring (e.g. error == output drops the error change
38
+ // link). The checks above only compare against `inputs`, so guard the output-side pairs
39
+ // too. (The same id appearing in multiple `outputs` entries is a supported last-wins case
40
+ // and stays allowed.)
41
+ if (config.error && config.output && config.error === config.output) {
42
+ throw new Error(`${errorMsg}. key: error/output`)
43
+ }
44
+ const outputsIds = Object.values(config.outputs).flat()
45
+ if (config.output && outputsIds.includes(config.output)) {
46
+ throw new Error(`${errorMsg}. key: output/outputs`)
47
+ }
48
+ if (config.error && outputsIds.includes(config.error)) {
49
+ throw new Error(`${errorMsg}. key: error/outputs`)
50
+ }
30
51
  }
31
52
 
32
53
  const padding = 15
33
54
  const layerXInterval = 300
34
55
  const layerYInterval = 150
35
56
 
36
- export const generateCalulationMap = (config: ScriptConfig, opts?: { snapshotMode?: boolean }) => {
57
+ // Direction A first-class script node (see local-plan/PLAN-data-calculation-refactor.md).
58
+ //
59
+ // `generateCalulationMap` keeps emitting the legacy OBJECT_SET → SANDBOX_RUN_JAVASCRIPT →
60
+ // SANDBOX_GET_*/OBJECT_GET expansion in `map` (consumed by older runtimes). In parallel,
61
+ // `generateScriptMap` derives an equivalent single-node graph in `script_map` that a newer
62
+ // runtime prefers: one `command-node-script` node that assembles `inputs` from keyed input
63
+ // ports and fans out `error` / `output` / each `outputs` path directly to the target data
64
+ // nodes. Both are byte-stable functions of `script_config`, so codegen (which only reads
65
+ // `script_config`) round-trips losslessly and recompiling unchanged source re-derives an
66
+ // identical `script_map`.
67
+ //
68
+ // Trigger semantics stay edge-level (`disable_trigger_command` on the script node's input
69
+ // edges, same as the legacy OBJECT_SET inputs) so the circular-reference check and the
70
+ // manual-trigger overlay keep working unchanged — only the node count collapses N+3 → 1.
71
+ const generateScriptMap = (config: ScriptConfig, calcId: string) => {
72
+ const scriptId = makeSeededId('property_bank_command', `${calcId}:script`)
73
+ const map: Record<string, any> = {}
74
+
75
+ // Compiled run-time config for the script node. `inputs` maps each input port name (the
76
+ // source data-node id) to its OBJECT_SET path; `outputs` maps each fan-out port name to the
77
+ // OBJECT_GET path it extracts from the return value. The runtime reads these from
78
+ // properties.args, keeping the node `value` reserved purely for incoming input-port values.
79
+ const inputArgs: Record<string, string> = {}
80
+ const outputArgs: Record<string, string> = {}
81
+
82
+ const scriptIn: Record<string, any> = {}
83
+ const scriptOut: Record<string, any> = {}
84
+
85
+ // One in-port per input, keyed by the source data-node id (unique per input, so each input
86
+ // keeps its own `disable_trigger_command` flag — a shared path could not). Manual mode
87
+ // disables every input edge; auto mode honours per-key `disabled_triggers`.
88
+ Object.entries(config.inputs).forEach(([dataNodeId, path]) => {
89
+ inputArgs[dataNodeId] = path
90
+ scriptIn[dataNodeId] = [
91
+ {
92
+ id: dataNodeId,
93
+ port: 'value',
94
+ disable_trigger_command:
95
+ config.trigger_mode === 'manual' || config.disabled_triggers?.[dataNodeId] || undefined,
96
+ },
97
+ ]
98
+ map[dataNodeId] = {
99
+ type: 'data-node',
100
+ properties: {},
101
+ in: { change: null },
102
+ out: { value: [{ id: scriptId, port: dataNodeId }] },
103
+ }
104
+ })
105
+
106
+ // Wire an output target data-node's `change` port back from a script out-port, preserving
107
+ // any `out.value` it already has (a node reused as both input and output — allowed in manual
108
+ // mode). Mirrors the last-wins reuse handling in generateCalulationMap's outputs section.
109
+ const wireOutputTarget = (dataNodeId: string, scriptPort: string) => {
110
+ const existing = map[dataNodeId]
111
+ map[dataNodeId] = {
112
+ type: 'data-node',
113
+ properties: {},
114
+ in: { change: [{ id: scriptId, port: scriptPort }] },
115
+ out: { value: existing?.out?.value ?? null },
116
+ }
117
+ }
118
+
119
+ if (config.error) {
120
+ scriptOut.error = [{ id: config.error, port: 'change' }]
121
+ wireOutputTarget(config.error, 'error')
122
+ }
123
+ if (config.output) {
124
+ scriptOut.output = [{ id: config.output, port: 'change' }]
125
+ wireOutputTarget(config.output, 'output')
126
+ }
127
+ // `out:`-prefixed port names keep `outputs` paths from colliding with the reserved
128
+ // `error` / `output` ports (a path could legitimately be the string "output").
129
+ Object.entries(config.outputs).forEach(([path, pbList]) => {
130
+ const portName = `out:${path}`
131
+ outputArgs[portName] = path
132
+ scriptOut[portName] = pbList.map((pb) => ({ id: pb, port: 'change' }))
133
+ pbList.forEach((pb) => wireOutputTarget(pb, portName))
134
+ })
135
+
136
+ map[scriptId] = {
137
+ type: 'command-node-script',
138
+ title: 'Command: SCRIPT_RUN',
139
+ properties: {
140
+ command: 'SCRIPT_RUN',
141
+ args: {
142
+ code: config.code,
143
+ enable_async: config.enable_async,
144
+ inputs: inputArgs,
145
+ outputs: outputArgs,
146
+ },
147
+ },
148
+ in: scriptIn,
149
+ out: scriptOut,
150
+ }
151
+
152
+ return map
153
+ }
154
+
155
+ // `calcId` (the owning script calc's stable id) seeds every derived command-node id, so an
156
+ // unchanged calc recompiles to identical ids and editing one calc never shifts another's —
157
+ // keeping the compiled config byte-stable for change detection. See makeSeededId in utils/id.
158
+ export const generateCalulationMap = (config: ScriptConfig, calcId: string) => {
37
159
  validateConfig(config)
38
- const sandboxId = makeId('property_bank_command', opts)
39
- const sandboxErrorId = makeId('property_bank_command', opts)
40
- const sandboxResultId = makeId('property_bank_command', opts)
160
+ const sandboxId = makeSeededId('property_bank_command', `${calcId}:sandbox-run`)
161
+ const sandboxErrorId = makeSeededId('property_bank_command', `${calcId}:sandbox-error`)
162
+ const sandboxResultId = makeSeededId('property_bank_command', `${calcId}:sandbox-result`)
41
163
 
42
164
  const inputs = Object.entries(config.inputs).reduce(
43
165
  (acc, [key, value], index) => {
44
- const commandId = makeId('property_bank_command', opts)
166
+ const commandId = makeSeededId('property_bank_command', `${calcId}:input:${key}`)
45
167
  acc.map[key] = {
46
168
  type: 'data-node',
47
169
  properties: {},
@@ -123,7 +245,7 @@ export const generateCalulationMap = (config: ScriptConfig, opts?: { snapshotMod
123
245
  let y = 0
124
246
  const outputs = Object.entries(config.outputs).reduce(
125
247
  (acc, [key, pbList], index) => {
126
- const commandId = makeId('property_bank_command', opts)
248
+ const commandId = makeSeededId('property_bank_command', `${calcId}:output:${key}`)
127
249
  acc.commandIdList.push(commandId)
128
250
  acc.map[commandId] = {
129
251
  type: 'command-node-object',
@@ -195,6 +317,8 @@ export const generateCalulationMap = (config: ScriptConfig, opts?: { snapshotMod
195
317
  )
196
318
 
197
319
  return {
320
+ // Newer runtimes prefer this single-node graph; older runtimes fall back to `map` below.
321
+ script_map: generateScriptMap(config, calcId),
198
322
  map: {
199
323
  ...inputs.map,
200
324
  [sandboxId]: {
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@fugood/bricks-ctor",
3
- "version": "2.25.0-beta.6",
3
+ "version": "2.25.0-beta.60",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "typecheck": "tsc --noEmit",
7
7
  "build": "bun scripts/build.js"
8
8
  },
9
9
  "dependencies": {
10
- "@fugood/bricks-cli": "^2.25.0-beta.6",
10
+ "@babel/generator": "7.28.5",
11
+ "@babel/parser": "7.28.5",
12
+ "@babel/traverse": "7.28.5",
13
+ "@babel/types": "7.28.5",
14
+ "@fugood/bricks-cli": "^2.25.0-beta.60",
11
15
  "@huggingface/gguf": "^0.3.2",
12
16
  "@iarna/toml": "^3.0.0",
13
17
  "@modelcontextprotocol/sdk": "^1.15.0",
@@ -25,5 +29,5 @@
25
29
  "peerDependencies": {
26
30
  "oxfmt": "^0.36.0"
27
31
  },
28
- "gitHead": "d8a3bbd695e9118cf725b7226866d804bbe56bbe"
32
+ "gitHead": "c0dd2ea071f45382808ad55acfc9837217d683ca"
29
33
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: bricks-ctor
3
- description: Advanced BRICKS configuration knowledge. Covers Standby Transition, Automations (E2E testing), Data Calculation (JS sandbox libraries), Local Sync, Remote Data Bank, Media Flow, and Buttress (remote inference). Triggers on multi-device sync, cloud data, media assets, AI offloading, E2E testing, or canvas transitions.
3
+ description: Advanced BRICKS configuration knowledge. Covers Standby Transition, Automations (E2E testing), Data Calculation (JS sandbox libraries), Local Sync, Remote Data Bank, Media Flow, Buttress (remote inference), and the verification toolchain (compile, harness-specific preview tool, on-device DevTools, definition-of-done gate). Triggers on multi-device sync, cloud data, media assets, AI offloading, E2E testing, canvas transitions, or verifying project work before declaring done.
4
4
  ---
5
5
 
6
6
  # BRICKS Ctor - Advanced Features
@@ -11,22 +11,28 @@ This skill covers advanced BRICKS features not in the main project instructions.
11
11
 
12
12
  | Rule | Description |
13
13
  |------|-------------|
14
- | [Architecture Patterns](rules/architecture-patterns.md) | **Read first** — decompose flows and select patterns |
15
- | [Animation](rules/animation.md) | Animation system for brick transforms and opacity |
16
- | [Standby Transition](rules/standby-transition.md) | Canvas enter/exit animations |
17
- | [Automations](rules/automations.md) | E2E testing and scheduled tasks |
18
- | [Data Calculation](rules/data-calculation.md) | JS sandbox libraries (25+ available) |
19
- | [Local Sync](rules/local-sync.md) | LAN device synchronization |
20
- | [Remote Data Bank](rules/remote-data-bank.md) | Cloud data sync and API access |
21
- | [Media Flow](rules/media-flow.md) | Media asset management |
22
- | [Buttress](rules/buttress.md) | Remote inference for AI generators |
14
+ | [Architecture Patterns](references/architecture-patterns.md) | **Read first** — decompose flows and select patterns |
15
+ | [Source-Editing Tools](references/source-editing-tools.md) | MCP tools for editing entries and data-calcs by AST (new/edit/remove, value grammar, verify) |
16
+ | [Animation](references/animation.md) | Animation system for brick transforms and opacity |
17
+ | [Standby Transition](references/standby-transition.md) | Canvas enter/exit animations |
18
+ | [Automations](references/automations.md) | E2E testing and scheduled tasks |
19
+ | [Data Calculation](references/data-calculation.md) | Wiring contract, trigger semantics, JS sandbox (25+ libraries) |
20
+ | [Local Sync](references/local-sync.md) | LAN device synchronization |
21
+ | [Remote Data Bank](references/remote-data-bank.md) | Cloud data sync and API access |
22
+ | [Media Flow](references/media-flow.md) | Media asset management |
23
+ | [Buttress](references/buttress.md) | Remote inference for AI generators |
24
+ | [Verification Toolchain](references/verification-toolchain.md) | Definition of done, compile, preview tool selection, on-device DevTools, Path 1/2/3 decision rule |
25
+ | [Simulator](references/simulator.md) | Path 1 fidelity — Simulator feature support, fallbacks (Camera/LLM/STT/Vector Store, Buttress disabled), when a green run is enough vs. Path 2 |
23
26
 
24
27
  ## Quick Reference
25
28
 
26
- - **Complex flows**: See [Architecture Patterns](rules/architecture-patterns.md) for decomposing multi-step workflows
27
- - **Multi-device**: See [Local Sync](rules/local-sync.md) for LAN coordination
28
- - **Cloud data**: See [Remote Data Bank](rules/remote-data-bank.md) for sync and API access
29
- - **Media assets**: See [Media Flow](rules/media-flow.md) for centralized asset management
30
- - **AI offloading**: See [Buttress](rules/buttress.md) for GPU server delegation
31
- - **E2E testing**: See [Automations](rules/automations.md) for test automation
32
- - **Enter animations**: See [Standby Transition](rules/standby-transition.md) for canvas transitions
29
+ - **Complex flows**: See [Architecture Patterns](references/architecture-patterns.md) for decomposing multi-step workflows
30
+ - **Editing entries/data-calcs**: See [Source-Editing Tools](references/source-editing-tools.md) prefer the MCP editing tools over hand-editing `subspaces/**`
31
+ - **Multi-device**: See [Local Sync](references/local-sync.md) for LAN coordination
32
+ - **Cloud data**: See [Remote Data Bank](references/remote-data-bank.md) for sync and API access
33
+ - **Media assets**: See [Media Flow](references/media-flow.md) for centralized asset management
34
+ - **AI offloading**: See [Buttress](references/buttress.md) for GPU server delegation
35
+ - **E2E testing**: See [Automations](references/automations.md) for test automation
36
+ - **Enter animations**: See [Standby Transition](references/standby-transition.md) for canvas transitions
37
+ - **Verification before done**: See [Verification Toolchain](references/verification-toolchain.md) for the definition-of-done gate and Path 1/2/3 decision rule
38
+ - **Simulator fidelity**: See [Simulator](references/simulator.md) for what the Simulator fakes (Camera, AI generators, Buttress) and when to escalate to a real device
@@ -41,12 +41,13 @@ const bounce: AnimationDef = {
41
41
  toValue: 1,
42
42
  friction: 7,
43
43
  tension: 40,
44
- speed: 12,
45
- bounciness: 8,
46
44
  },
47
45
  }
48
46
  ```
49
47
 
48
+ Use one spring parameter family per config: `tension/friction`, `speed/bounciness`, or
49
+ `stiffness/damping/mass`.
50
+
50
51
  ### Decay Animation
51
52
  Velocity-based deceleration animation.
52
53
 
@@ -25,11 +25,20 @@ The primary way to orchestrate multi-step flows. A single event can contain an a
25
25
  - Use `waitAsync: true` to await async actions before the next step
26
26
  - Use `dataParams` + `mapping` to pass event data downstream
27
27
  - This is the "glue" that wires generators, state, and UI together
28
+ - For Generator result UI: await Generator, write done/version Data; route/current Data stays identity.
29
+
30
+ Sequential `PROPERTY_BANK` / `PROPERTY_BANK_EXPRESSION` actions in one chain read the data values that existed when the chain started. If a later action needs to read what an earlier action wrote, set `waitAsync: true` on the earlier action.
28
31
 
29
32
  ### System Actions (Priority 3)
30
33
  Built-in commands for direct state and UI changes.
31
34
  - **PROPERTY_BANK**: set data value
32
35
  - **PROPERTY_BANK_EXPRESSION**: inline JS expression for simple compute
36
+ - The expression engine folds statements into a single expression: only expression
37
+ statements, simple `const`/`let` declarations, and a final return/expression are
38
+ supported — **no `if`/`for`/`while`/`switch`** (use ternaries). The same limit
39
+ applies inside a zero-arg IIFE body. Unsupported statements fail at runtime with
40
+ the error visible only in a DevTools session, so prefer ternary chains or move the
41
+ logic to a DataCalculationScript.
33
42
  - **CHANGE_CANVAS**: navigate to another canvas
34
43
  - **DYNAMIC_ANIMATION**: trigger animation
35
44
  - **ALERT / MESSAGE**: system feedback
@@ -67,3 +76,13 @@ Only actual data derivation maps to Data Calculation:
67
76
 
68
77
  ### Step 4: Wire with Event Action Chains
69
78
  Connect the pieces through events on generators and bricks.
79
+
80
+ ## Recipe: user-driven state machine (calculator, form wizard, picker)
81
+
82
+ A brief like "state vars X, Y, Z; button A updates X from X+Y; button B resets" is a state machine. The shape:
83
+
84
+ - Each state variable is its own `Data` entity. Displays read it via `linkData(() => data.dFoo)`.
85
+ - Each button's `onPress` is an Event Action Chain. For every state var that changes, append one `PROPERTY_BANK_EXPRESSION` whose `expression` reads the current state and returns the new value.
86
+ - Use `Data Calculation` only for reusable pure derivations referenced as inputs to those expressions.
87
+
88
+ A 10-button calculator with 4 state vars is ~10 small chains of 1–4 inline expressions. If you find yourself writing a single auto-mode `DataCalculationScript` that consumes all state vars and emits all-new state through mirror `dFooResult` data — or pinging a `dLastInput` field to force an auto calc to re-run — the chain shape was the right answer.
@@ -118,6 +118,16 @@ const testLoginFlow: AutomationTest = {
118
118
  | `match_screenshot` | `[name, threshold?, maxRetry?]` | Screenshot compare |
119
119
  | `delay` | `[subspace?, property?, defaultValue?]` | Delay execution |
120
120
 
121
+ In project TypeScript source, pass entity getters for BRICKS entities:
122
+
123
+ ```typescript
124
+ run: ['brick_press', () => mainSubspace, () => bricks.bSubmitButton]
125
+ run: ['wait_until_canvas_change', () => mainSubspace, () => canvases.cDone, 5000]
126
+ run: ['assert_property', () => mainSubspace, () => data.dStep, 'done']
127
+ ```
128
+
129
+ The compiler resolves these getters to the current generated IDs.
130
+
121
131
  ### execute_action Params
122
132
 
123
133
  The `params` object in `execute_action` uses **runtime event property keys** from `event-props.ts`, NOT the action config `input` names from type definitions.
@@ -208,6 +218,7 @@ Automations work with Modules. Use Manual Run in Preview mode for module testing
208
218
 
209
219
  - **Automation map key**: Always use `'AUTOMATION_MAP_DEFAULT'` as the automation map ID (not `makeId()`). The preview test runner reads from `automationMap['AUTOMATION_MAP_DEFAULT']?.map`.
210
220
  - **Valid makeId types**: Use `'test'` for AutomationTest, `'test_case'` for TestCase, `'test_var'` for TestVariable. Do NOT use `'automation_test'` or `'automation_test_map'`.
221
+ - **Entity references in run arrays**: Use getter references (`() => subspace`, `() => bricks.bButton`, `() => data.dValue`) in TypeScript source so compile resolves fresh IDs.
211
222
  - **handler in execute_action**: Pass the entity's `.id` string (e.g., `bricks.bInput.id`), not a getter function.
212
223
 
213
224
  ## Best Practices
@@ -0,0 +1,245 @@
1
+ # Buttress (Remote Inference)
2
+
3
+ Backend system for offloading compute-intensive AI generator tasks from BRICKS devices to more powerful machines.
4
+
5
+ ## Purpose
6
+
7
+ When mobile devices or embedded systems lack hardware for local AI inference (LLM, speech-to-text), Buttress transparently delegates work to a server with appropriate resources (GPU).
8
+
9
+ ## How It Works
10
+
11
+ 1. **Capability Exchange**: Client and server share hardware capabilities
12
+ 2. **Strategy Selection**: System decides local vs. remote execution
13
+ 3. **Transparent Offloading**: Generator operates same way, execution happens remotely
14
+
15
+ ## Supported Generators
16
+
17
+ - LLM (GGML) (LlmMlx.ts) - Local Large Language Model inference with GGML
18
+ - LLM (MLX) (LlmGgml.ts) - Local Large Language Model inference with MLX
19
+ - Speech-to-Text (GGML) (SpeechToTextGgml.ts) - Local Speech-to-Text inference with GGML
20
+
21
+ ## Client Configuration
22
+
23
+ In generator properties, configure `buttressConnectionSettings`:
24
+
25
+ | Setting | Description |
26
+ |---------|-------------|
27
+ | `enabled` | Toggle Buttress offloading |
28
+ | `autoDiscoverType` | `auto` (LAN auto-discovery, recommended) or `manual` (typed URL) |
29
+ | `url` | Server URL (`manual` mode only — leave empty for `auto`) |
30
+ | `fallbackType` | If Buttress is unreachable: `use-local` runs locally, `no-op` blocks the load |
31
+ | `strategy` | Execution preference (see below) |
32
+
33
+ The launcher provides the access token automatically — there is **no user-facing access-token setting**. In `auto` mode the launcher discovers servers bound to the device's workspace and uses its workspace-scoped JWT; in `manual` mode it sends the same JWT only when the typed URL belongs to a server bound to the same workspace (verified against the server's `/buttress/info`).
34
+
35
+ ### `autoDiscoverType` options
36
+
37
+ | Mode | When to use | What the device does |
38
+ |------|-------------|----------------------|
39
+ | `auto` | LAN deployment with a workspace-bound buttress-server | Listens for UDP announcements and picks the strongest server bound to its workspace; reconnects automatically when the workspace flips |
40
+ | `manual` | Internet-reachable server, mixed networks, or a public/unbound server | Connects to the typed `url` directly; sends a token only when the server is bound to the same workspace |
41
+
42
+ ### Strategy options
43
+
44
+ | Strategy | Description |
45
+ |----------|-------------|
46
+ | `prefer-local` | Use local if capable, fallback to Buttress |
47
+ | `prefer-buttress` | Use Buttress if available, fallback to local |
48
+ | `prefer-best` | Auto-select based on capability comparison |
49
+
50
+ ## Generator Configuration Examples
51
+
52
+ ### Auto-discovery (recommended)
53
+
54
+ Workspace-bound launcher + workspace-bound buttress-server on the same LAN. No URL needed — discovery picks the highest-scoring server that runs the requested backend.
55
+
56
+ ```typescript
57
+ import { makeId } from 'bricks-ctor'
58
+
59
+ const llmGenerator: GeneratorLLM = {
60
+ __typename: 'Generator',
61
+ templateKey: 'GENERATOR_LLM',
62
+ id: makeId('generator'),
63
+ title: 'Chat LLM',
64
+ description: '',
65
+ property: {
66
+ modelUrl: 'https://huggingface.co/ggml-org/gemma-3-12b-it-qat-GGUF/resolve/main/gemma-3-12b-it-qat-q4_0.gguf',
67
+ contextSize: 8192,
68
+ buttressConnectionSettings: {
69
+ enabled: true,
70
+ autoDiscoverType: 'auto',
71
+ fallbackType: 'use-local',
72
+ strategy: 'prefer-best',
73
+ },
74
+ },
75
+ events: {},
76
+ switches: [],
77
+ }
78
+ ```
79
+
80
+ ### Manual URL
81
+
82
+ Use when the server isn't on the launcher's broadcast domain (cross-subnet, internet, behind a reverse proxy) or you want to point at a specific public/unbound server.
83
+
84
+ ```typescript
85
+ buttressConnectionSettings: {
86
+ enabled: true,
87
+ autoDiscoverType: 'manual',
88
+ url: 'http://192.168.1.100:2080',
89
+ fallbackType: 'use-local',
90
+ strategy: 'prefer-best',
91
+ }
92
+ ```
93
+
94
+ > ⚠️ With `fallbackType: 'no-op'` the generator refuses to load locally if Buttress is unreachable. Use `'use-local'` whenever the device can also run the model standalone.
95
+
96
+ ## Integrating a discovered buttress-server
97
+
98
+ The ctor-desktop "Local Devices" panel (and the `bricks buttress scan` CLI) can hand you a server's identity, workspace, and announced generator caps. When the user asks to integrate one:
99
+
100
+ 1. **Confirm workspace match.** The discovery message says whether the server's workspace matches this project's. If it doesn't, do NOT add the integration — instruct the user to run `bricks buttress unbind && bricks buttress bind` from this workspace's owner CLI on the server host. The launcher won't trust a cross-workspace server.
101
+
102
+ 2. **Map announced types to generator templates.** For each `generators[].type` in the announce, target the matching templateKey (create the generator if absent):
103
+
104
+ | Announced type | templateKey | Default model |
105
+ |----------------|------------------------------|---------------|
106
+ | `ggml-llm` | `GENERATOR_LLM` | `ggml-org/gemma-3-12b-it-qat-GGUF` (≥20 GB usable) or `ggml-org/gpt-oss-20b-GGUF` (~12 GB usable) |
107
+ | `mlx-llm` | `GENERATOR_MLX_LLM` | `mlx-community/Qwen3-4B-4bit` (or a larger 4-bit quant if `usableBytes` allows) |
108
+ | `ggml-stt` | `GENERATOR_SPEECH_INFERENCE` | `BricksDisplay/whisper-ggml` (filename `ggml-small-q8_0.bin`) |
109
+
110
+ Pick a model that fits the announced `usableBytes`. Set `contextSize` to match.
111
+
112
+ 3. **Use the canonical auto-discovery config** from "Auto-discovery (recommended)" above. Don't switch to manual mode just because the server is on the LAN — auto mode picks the workspace-bound server automatically and rebinds when the device's workspace flips.
113
+
114
+ ### Real-device caveat
115
+
116
+ Buttress LAN auto-discovery uses native UDP via `react-native-jsi-udp`. The iOS Simulator silently fails to bind UDP, so auto mode is a no-op there. With `fallbackType: 'use-local'` the generator falls back to local inference in the simulator — dev/test stays functional. **Validate the buttress path itself only when deploying to a real Foundation device; don't gate the build on simulator validation.**
117
+
118
+ ## Server Setup
119
+
120
+ ### Requirements
121
+ - [Bun](https://bun.sh) v1.3+
122
+ - GPU recommended for LLM/STT
123
+
124
+ ### Installation
125
+
126
+ ```bash
127
+ bun add -g @fugood/buttress-server
128
+ ```
129
+
130
+ ### Start Server
131
+
132
+ ```bash
133
+ bricks-buttress
134
+ # or with config
135
+ bricks-buttress --config ./config.toml
136
+ ```
137
+
138
+ ### CLI Options
139
+
140
+ | Option | Description |
141
+ |--------|-------------|
142
+ | `-p, --port` | Port (default: 2080) |
143
+ | `-c, --config` | TOML config file path |
144
+ | `-v, --version` | Show version |
145
+ | `-h, --help` | Show help |
146
+
147
+ ### Environment Variables
148
+
149
+ | Variable | Description |
150
+ |----------|-------------|
151
+ | `HF_TOKEN` | Hugging Face token for model downloads |
152
+ | `ENABLE_OPENAI_COMPAT_ENDPOINT` | Set to `1` for OpenAI-compatible API |
153
+ | `BRICKS_BUTTRESS_STATE_DIR` | Override the workspace state directory (default `~/.bricks-cli/buttress`) |
154
+
155
+ ## Bind the Server to a Workspace
156
+
157
+ To use `autoDiscoverType: 'auto'` — and to require workspace-scoped JWT auth — pair the buttress-server with a BRICKS workspace using the `bricks buttress` CLI commands. An unbound server stays in legacy public mode and accepts any LAN client.
158
+
159
+ ### One-time bind
160
+
161
+ ```bash
162
+ # Log into the cloud bricks-server with the workspace owner's account
163
+ bricks auth login
164
+
165
+ # Pair the buttress-server running on this machine with the active workspace
166
+ bricks buttress bind
167
+
168
+ # Restart bricks-buttress so it picks up the new state.json
169
+ ```
170
+
171
+ `bricks buttress bind` writes `~/.bricks-cli/buttress/state.json` containing the workspace id, the issuer's Ed25519 public key, and a stable `serverId` (defaults to `buttress-<machineId>`). Override location with `--state-dir` or `$BRICKS_BUTTRESS_STATE_DIR`. For headless setups, use `bricks buttress bind --print` to emit state.json to stdout.
172
+
173
+ ### Verify and manage
174
+
175
+ ```bash
176
+ # Show local binding + workspace-side bound list
177
+ bricks buttress status
178
+
179
+ # Discover buttress-servers on the LAN (with version, auth state, generator caps)
180
+ bricks buttress scan
181
+
182
+ # Issue a long-lived workspace access token (CI / ctor agents that already hold a workspace token)
183
+ bricks buttress issue-token --ttl 86400
184
+
185
+ # Detach this server from the workspace
186
+ bricks buttress unbind
187
+ ```
188
+
189
+ After binding, launchers in the same workspace will auto-discover this server; manual-mode generators will only send their access token if the server's reported workspace matches their own.
190
+
191
+ ## Server Configuration (TOML)
192
+
193
+ ```toml
194
+ [server]
195
+ port = 2080
196
+
197
+ [runtime]
198
+ cache_dir = "./.buttress-cache"
199
+ n_threads = 6
200
+ flash_attn_type = "on"
201
+ cache_type_k = "q8_0"
202
+ cache_type_v = "q8_0"
203
+
204
+ # LLM Generator
205
+ [[generators]]
206
+ type = "ggml-llm"
207
+ [generators.backend]
208
+ variant_preference = ["cuda", "vulkan", "default"]
209
+ gpu_memory_fraction = 0.95
210
+ [generators.model]
211
+ repo_id = "ggml-org/gemma-3-12b-it-qat-GGUF"
212
+ download = true
213
+ n_ctx = 8192
214
+
215
+ # STT Generator
216
+ [[generators]]
217
+ type = "ggml-stt"
218
+ [generators.backend]
219
+ variant_preference = ["cuda", "vulkan", "default"]
220
+ [generators.model]
221
+ repo_id = "BricksDisplay/whisper-ggml"
222
+ filename = "ggml-small-q8_0.bin"
223
+ download = true
224
+ use_gpu = true
225
+ ```
226
+
227
+ ## Use Cases
228
+
229
+ ### Resource-Constrained Devices
230
+ Digital signage with basic hardware offloads LLM to powerful server.
231
+
232
+ ### Shared GPU Resources
233
+ Multiple devices share single GPU server for inference.
234
+
235
+ ### Development Testing
236
+ Test AI features on lightweight dev machines by connecting to beefy server.
237
+
238
+ ## Best Practices
239
+
240
+ 1. **Network reliability**: Ensure stable LAN connection to Buttress server
241
+ 2. **Fallback strategy**: Configure appropriate fallback for critical features
242
+ 3. **Server monitoring**: Monitor Buttress server resource usage
243
+ 4. **Model consistency**: Ensure client and server use compatible models
244
+ 5. **Security**: Run Buttress on private network, not public internet
245
+ 6. **Latency awareness**: Account for network latency in UX design