@fugood/bricks-project 2.25.0-beta.3 → 2.25.0-beta.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compile/action-name-map.ts +61 -0
- package/compile/index.ts +205 -17
- package/package.json +2 -2
- package/package.json.bak +2 -2
- package/skills/bricks-ctor/SKILL.md +3 -1
- package/skills/bricks-ctor/rules/animation.md +3 -2
- package/skills/bricks-ctor/rules/architecture-patterns.md +12 -0
- package/skills/bricks-ctor/rules/automations.md +11 -0
- package/skills/bricks-ctor/rules/buttress.md +97 -8
- package/skills/bricks-ctor/rules/data-calculation.md +5 -5
- package/skills/bricks-ctor/rules/verification-toolchain.md +177 -0
- package/skills/bricks-design/SKILL.md +160 -45
- package/skills/bricks-design/references/architecture-truths.md +132 -0
- package/skills/bricks-design/references/avoiding-complexity.md +91 -0
- package/skills/bricks-design/references/design-critique.md +195 -0
- package/skills/bricks-design/references/design-languages.md +265 -0
- package/skills/bricks-design/references/performance.md +116 -0
- package/skills/bricks-design/references/presentation-and-slideshow.md +137 -0
- package/skills/bricks-design/references/translating-inputs.md +152 -0
- package/skills/bricks-design/references/variations-and-tweaks.md +124 -0
- package/skills/bricks-design/references/when-the-brief-is-branded.md +284 -0
- package/skills/bricks-design/references/when-the-brief-is-vague.md +85 -0
- package/skills/bricks-design/references/workflow.md +134 -0
- package/skills/bricks-ux/SKILL.md +120 -0
- package/skills/bricks-ux/references/accessibility.md +162 -0
- package/skills/bricks-ux/references/flow-states.md +175 -0
- package/skills/bricks-ux/references/interaction-archetypes.md +189 -0
- package/skills/bricks-ux/references/monitoring-screens.md +153 -0
- package/skills/bricks-ux/references/pressable-composition.md +126 -0
- package/skills/bricks-ux/references/user-journey.md +168 -0
- package/skills/bricks-ux/references/ux-critique.md +256 -0
- package/tools/_git-author.ts +10 -2
- package/tools/_last-pushed-commit.ts +28 -0
- package/tools/_shell.ts +8 -1
- package/tools/deploy.ts +15 -0
- package/tools/mcp-tools/compile.ts +17 -7
- package/tools/mcp-tools/media.ts +4 -1
- package/tools/preview-main.mjs +18 -5
- package/tools/pull.ts +91 -16
- package/tools/push-config.ts +118 -0
- package/types/{animation.ts → animation.d.ts} +24 -8
- package/types/{automation.ts → automation.d.ts} +16 -20
- package/types/{brick-base.ts → brick-base.d.ts} +1 -1
- package/types/bricks/{Camera.ts → Camera.d.ts} +8 -8
- package/types/bricks/{Chart.ts → Chart.d.ts} +4 -4
- package/types/bricks/{GenerativeMedia.ts → GenerativeMedia.d.ts} +15 -15
- package/types/bricks/{Icon.ts → Icon.d.ts} +7 -7
- package/types/bricks/{Image.ts → Image.d.ts} +21 -9
- package/types/bricks/{Items.ts → Items.d.ts} +7 -7
- package/types/bricks/{Lottie.ts → Lottie.d.ts} +10 -10
- package/types/bricks/{Maps.ts → Maps.d.ts} +11 -11
- package/types/bricks/{QrCode.ts → QrCode.d.ts} +7 -7
- package/types/bricks/{Rect.ts → Rect.d.ts} +7 -7
- package/types/bricks/{RichText.ts → RichText.d.ts} +12 -9
- package/types/bricks/{Rive.ts → Rive.d.ts} +9 -9
- package/types/bricks/Scene3D.d.ts +596 -0
- package/types/bricks/{Sketch.ts → Sketch.d.ts} +11 -9
- package/types/bricks/{Slideshow.ts → Slideshow.d.ts} +7 -7
- package/types/bricks/{Svg.ts → Svg.d.ts} +7 -7
- package/types/bricks/{Text.ts → Text.d.ts} +9 -9
- package/types/bricks/{TextInput.ts → TextInput.d.ts} +10 -10
- package/types/bricks/{Video.ts → Video.d.ts} +12 -12
- package/types/bricks/{VideoStreaming.ts → VideoStreaming.d.ts} +10 -10
- package/types/bricks/{WebRtcStream.ts → WebRtcStream.d.ts} +1 -1
- package/types/bricks/{WebView.ts → WebView.d.ts} +4 -4
- package/types/bricks/{index.ts → index.d.ts} +1 -0
- package/types/{common.ts → common.d.ts} +3 -6
- package/types/data-calc-command/base.d.ts +57 -0
- package/types/data-calc-command/collection.d.ts +418 -0
- package/types/data-calc-command/color.d.ts +432 -0
- package/types/data-calc-command/constant.d.ts +50 -0
- package/types/data-calc-command/datetime.d.ts +147 -0
- package/types/data-calc-command/file.d.ts +129 -0
- package/types/data-calc-command/index.d.ts +13 -0
- package/types/data-calc-command/iteratee.d.ts +23 -0
- package/types/data-calc-command/logictype.d.ts +190 -0
- package/types/data-calc-command/math.d.ts +275 -0
- package/types/data-calc-command/object.d.ts +119 -0
- package/types/data-calc-command/sandbox.d.ts +58 -0
- package/types/data-calc-command/string.d.ts +407 -0
- package/types/{data-calc.ts → data-calc.d.ts} +1 -0
- package/types/{data.ts → data.d.ts} +4 -2
- package/types/generators/{Assistant.ts → Assistant.d.ts} +19 -0
- package/types/generators/{LlmGgml.ts → LlmGgml.d.ts} +38 -0
- package/types/generators/{LlmMlx.ts → LlmMlx.d.ts} +1 -0
- package/types/generators/{SpeechToTextGgml.ts → SpeechToTextGgml.d.ts} +1 -0
- package/types/generators/{ThermalPrinter.ts → ThermalPrinter.d.ts} +9 -7
- package/types/{subspace.ts → subspace.d.ts} +1 -1
- package/utils/data.ts +2 -2
- package/utils/event-props.ts +17 -0
- package/utils/id.ts +78 -27
- package/skills/bricks-design/LICENSE.txt +0 -180
- package/types/data-calc-command.ts +0 -7005
- /package/types/{canvas.ts → canvas.d.ts} +0 -0
- /package/types/{data-calc-script.ts → data-calc-script.d.ts} +0 -0
- /package/types/generators/{AlarmClock.ts → AlarmClock.d.ts} +0 -0
- /package/types/generators/{BleCentral.ts → BleCentral.d.ts} +0 -0
- /package/types/generators/{BlePeripheral.ts → BlePeripheral.d.ts} +0 -0
- /package/types/generators/{CanvasMap.ts → CanvasMap.d.ts} +0 -0
- /package/types/generators/{CastlesPay.ts → CastlesPay.d.ts} +0 -0
- /package/types/generators/{DataBank.ts → DataBank.d.ts} +0 -0
- /package/types/generators/{File.ts → File.d.ts} +0 -0
- /package/types/generators/{GraphQl.ts → GraphQl.d.ts} +0 -0
- /package/types/generators/{Http.ts → Http.d.ts} +0 -0
- /package/types/generators/{HttpServer.ts → HttpServer.d.ts} +0 -0
- /package/types/generators/{Information.ts → Information.d.ts} +0 -0
- /package/types/generators/{Intent.ts → Intent.d.ts} +0 -0
- /package/types/generators/{Iterator.ts → Iterator.d.ts} +0 -0
- /package/types/generators/{Keyboard.ts → Keyboard.d.ts} +0 -0
- /package/types/generators/{LlmAnthropicCompat.ts → LlmAnthropicCompat.d.ts} +0 -0
- /package/types/generators/{LlmAppleBuiltin.ts → LlmAppleBuiltin.d.ts} +0 -0
- /package/types/generators/{LlmMediaTekNeuroPilot.ts → LlmMediaTekNeuroPilot.d.ts} +0 -0
- /package/types/generators/{LlmOnnx.ts → LlmOnnx.d.ts} +0 -0
- /package/types/generators/{LlmOpenAiCompat.ts → LlmOpenAiCompat.d.ts} +0 -0
- /package/types/generators/{LlmQualcommAiEngine.ts → LlmQualcommAiEngine.d.ts} +0 -0
- /package/types/generators/{Mcp.ts → Mcp.d.ts} +0 -0
- /package/types/generators/{McpServer.ts → McpServer.d.ts} +0 -0
- /package/types/generators/{MediaFlow.ts → MediaFlow.d.ts} +0 -0
- /package/types/generators/{MqttBroker.ts → MqttBroker.d.ts} +0 -0
- /package/types/generators/{MqttClient.ts → MqttClient.d.ts} +0 -0
- /package/types/generators/{Question.ts → Question.d.ts} +0 -0
- /package/types/generators/{RealtimeTranscription.ts → RealtimeTranscription.d.ts} +0 -0
- /package/types/generators/{RerankerGgml.ts → RerankerGgml.d.ts} +0 -0
- /package/types/generators/{SerialPort.ts → SerialPort.d.ts} +0 -0
- /package/types/generators/{SoundPlayer.ts → SoundPlayer.d.ts} +0 -0
- /package/types/generators/{SoundRecorder.ts → SoundRecorder.d.ts} +0 -0
- /package/types/generators/{SpeechToTextOnnx.ts → SpeechToTextOnnx.d.ts} +0 -0
- /package/types/generators/{SpeechToTextPlatform.ts → SpeechToTextPlatform.d.ts} +0 -0
- /package/types/generators/{SqLite.ts → SqLite.d.ts} +0 -0
- /package/types/generators/{Step.ts → Step.d.ts} +0 -0
- /package/types/generators/{SttAppleBuiltin.ts → SttAppleBuiltin.d.ts} +0 -0
- /package/types/generators/{Tcp.ts → Tcp.d.ts} +0 -0
- /package/types/generators/{TcpServer.ts → TcpServer.d.ts} +0 -0
- /package/types/generators/{TextToSpeechAppleBuiltin.ts → TextToSpeechAppleBuiltin.d.ts} +0 -0
- /package/types/generators/{TextToSpeechGgml.ts → TextToSpeechGgml.d.ts} +0 -0
- /package/types/generators/{TextToSpeechOnnx.ts → TextToSpeechOnnx.d.ts} +0 -0
- /package/types/generators/{TextToSpeechOpenAiLike.ts → TextToSpeechOpenAiLike.d.ts} +0 -0
- /package/types/generators/{Tick.ts → Tick.d.ts} +0 -0
- /package/types/generators/{Udp.ts → Udp.d.ts} +0 -0
- /package/types/generators/{VadGgml.ts → VadGgml.d.ts} +0 -0
- /package/types/generators/{VadOnnx.ts → VadOnnx.d.ts} +0 -0
- /package/types/generators/{VadTraditional.ts → VadTraditional.d.ts} +0 -0
- /package/types/generators/{VectorStore.ts → VectorStore.d.ts} +0 -0
- /package/types/generators/{Watchdog.ts → Watchdog.d.ts} +0 -0
- /package/types/generators/{WebCrawler.ts → WebCrawler.d.ts} +0 -0
- /package/types/generators/{WebRtc.ts → WebRtc.d.ts} +0 -0
- /package/types/generators/{WebSocket.ts → WebSocket.d.ts} +0 -0
- /package/types/generators/{index.ts → index.d.ts} +0 -0
- /package/types/{index.ts → index.d.ts} +0 -0
- /package/types/{switch.ts → switch.d.ts} +0 -0
- /package/types/{system.ts → system.d.ts} +0 -0
|
@@ -350,6 +350,62 @@ export const templateActionNameMap = {
|
|
|
350
350
|
strokeWidth: 'BRICK_SKETCH_STROKE_WIDTH',
|
|
351
351
|
},
|
|
352
352
|
},
|
|
353
|
+
BRICK_SCENE_3D: {
|
|
354
|
+
BRICK_SCENE_3D_ADD_OBJECT: {
|
|
355
|
+
objectId: 'BRICK_SCENE_3D_OBJECT_ID',
|
|
356
|
+
objectType: 'BRICK_SCENE_3D_OBJECT_TYPE',
|
|
357
|
+
objectUrl: 'BRICK_SCENE_3D_OBJECT_URL',
|
|
358
|
+
objectMd5: 'BRICK_SCENE_3D_OBJECT_MD5',
|
|
359
|
+
objectPosition: 'BRICK_SCENE_3D_OBJECT_POSITION',
|
|
360
|
+
objectRotation: 'BRICK_SCENE_3D_OBJECT_ROTATION',
|
|
361
|
+
objectScale: 'BRICK_SCENE_3D_OBJECT_SCALE',
|
|
362
|
+
objectColor: 'BRICK_SCENE_3D_OBJECT_COLOR',
|
|
363
|
+
},
|
|
364
|
+
BRICK_SCENE_3D_REMOVE_OBJECT: {
|
|
365
|
+
objectId: 'BRICK_SCENE_3D_OBJECT_ID',
|
|
366
|
+
},
|
|
367
|
+
BRICK_SCENE_3D_UPDATE_OBJECT: {
|
|
368
|
+
objectId: 'BRICK_SCENE_3D_OBJECT_ID',
|
|
369
|
+
objectPosition: 'BRICK_SCENE_3D_OBJECT_POSITION',
|
|
370
|
+
objectRotation: 'BRICK_SCENE_3D_OBJECT_ROTATION',
|
|
371
|
+
objectScale: 'BRICK_SCENE_3D_OBJECT_SCALE',
|
|
372
|
+
objectVisible: 'BRICK_SCENE_3D_OBJECT_VISIBLE',
|
|
373
|
+
objectColor: 'BRICK_SCENE_3D_OBJECT_COLOR',
|
|
374
|
+
},
|
|
375
|
+
BRICK_SCENE_3D_SET_CAMERA: {
|
|
376
|
+
cameraPosition: 'BRICK_SCENE_3D_CAMERA_POSITION',
|
|
377
|
+
cameraTarget: 'BRICK_SCENE_3D_CAMERA_TARGET',
|
|
378
|
+
cameraFov: 'BRICK_SCENE_3D_CAMERA_FOV',
|
|
379
|
+
cameraAnimateMs: 'BRICK_SCENE_3D_CAMERA_ANIMATE_MS',
|
|
380
|
+
},
|
|
381
|
+
BRICK_SCENE_3D_LOOK_AT: {
|
|
382
|
+
objectId: 'BRICK_SCENE_3D_OBJECT_ID',
|
|
383
|
+
},
|
|
384
|
+
BRICK_SCENE_3D_PLAY_ANIMATION: {
|
|
385
|
+
objectId: 'BRICK_SCENE_3D_OBJECT_ID',
|
|
386
|
+
animationName: 'BRICK_SCENE_3D_ANIMATION_NAME',
|
|
387
|
+
animationLoop: 'BRICK_SCENE_3D_ANIMATION_LOOP',
|
|
388
|
+
animationSpeed: 'BRICK_SCENE_3D_ANIMATION_SPEED',
|
|
389
|
+
},
|
|
390
|
+
BRICK_SCENE_3D_STOP_ANIMATION: {
|
|
391
|
+
objectId: 'BRICK_SCENE_3D_OBJECT_ID',
|
|
392
|
+
animationName: 'BRICK_SCENE_3D_ANIMATION_NAME',
|
|
393
|
+
},
|
|
394
|
+
BRICK_SCENE_3D_SET_BACKGROUND: {
|
|
395
|
+
backgroundColor: 'BRICK_SCENE_3D_BACKGROUND_COLOR',
|
|
396
|
+
backgroundHdrUrl: 'BRICK_SCENE_3D_BACKGROUND_HDR_URL',
|
|
397
|
+
backgroundMd5: 'BRICK_SCENE_3D_BACKGROUND_MD5',
|
|
398
|
+
},
|
|
399
|
+
BRICK_SCENE_3D_SET_CONTROLS: {
|
|
400
|
+
controlsEnabled: 'BRICK_SCENE_3D_CONTROLS_ENABLED',
|
|
401
|
+
controlsAutoRotate: 'BRICK_SCENE_3D_CONTROLS_AUTO_ROTATE',
|
|
402
|
+
controlsAutoRotateSpeed: 'BRICK_SCENE_3D_CONTROLS_AUTO_ROTATE_SPEED',
|
|
403
|
+
},
|
|
404
|
+
BRICK_SCENE_3D_SCREENSHOT: {
|
|
405
|
+
screenshotFormat: 'BRICK_SCENE_3D_SCREENSHOT_FORMAT',
|
|
406
|
+
screenshotQuality: 'BRICK_SCENE_3D_SCREENSHOT_QUALITY',
|
|
407
|
+
},
|
|
408
|
+
},
|
|
353
409
|
|
|
354
410
|
GENERATOR_FILE: {
|
|
355
411
|
GENERATOR_FILE_READ_CONTENT: {
|
|
@@ -782,6 +838,11 @@ export const templateActionNameMap = {
|
|
|
782
838
|
seed: 'GENERATOR_LLM_SEED',
|
|
783
839
|
typicalP: 'GENERATOR_LLM_TYPICAL_P',
|
|
784
840
|
ignoreEos: 'GENERATOR_LLM_IGNORE_EOS',
|
|
841
|
+
mtpSpeculativeDecoding: 'GENERATOR_LLM_MTP_SPECULATIVE_DECODING',
|
|
842
|
+
mtpDraftTokens: 'GENERATOR_LLM_MTP_DRAFT_TOKENS',
|
|
843
|
+
mtpDraftMinTokens: 'GENERATOR_LLM_MTP_DRAFT_MIN_TOKENS',
|
|
844
|
+
mtpDraftMinProbability: 'GENERATOR_LLM_MTP_DRAFT_MIN_PROBABILITY',
|
|
845
|
+
mtpDraftSplitProbability: 'GENERATOR_LLM_MTP_DRAFT_SPLIT_PROBABILITY',
|
|
785
846
|
functionCallEnabled: 'GENERATOR_LLM_FUNCTION_CALL_ENABLED',
|
|
786
847
|
functionCallSchema: 'GENERATOR_LLM_FUNCTION_CALL_SCHEMA',
|
|
787
848
|
},
|
package/compile/index.ts
CHANGED
|
@@ -154,11 +154,13 @@ const basicAnimationEvents = ['show', 'standby', 'breatheStart']
|
|
|
154
154
|
|
|
155
155
|
const compileAnimations = (
|
|
156
156
|
templateKey: string,
|
|
157
|
-
animations: { [key: string]: Animation },
|
|
157
|
+
animations: { [key: string]: Animation | (() => Animation) },
|
|
158
158
|
errorReference: string,
|
|
159
159
|
) =>
|
|
160
160
|
Object.entries(animations).reduce((acc, [key, animation]) => {
|
|
161
|
-
|
|
161
|
+
// Animation events accept either a direct Animation or a getter; unwrap.
|
|
162
|
+
const resolved = typeof animation === 'function' ? animation() : animation
|
|
163
|
+
const animationId = assertEntryId(resolved?.id, 'ANIMATION', errorReference)
|
|
162
164
|
acc[convertEventKey(basicAnimationEvents.includes(key) ? 'BRICK' : templateKey, key)] =
|
|
163
165
|
`ANIMATION#${animationId}`
|
|
164
166
|
return acc
|
|
@@ -301,6 +303,162 @@ const animationTypeMap = {
|
|
|
301
303
|
AnimationTimingConfig: 'timing',
|
|
302
304
|
AnimationSpringConfig: 'spring',
|
|
303
305
|
AnimationDecayConfig: 'decay',
|
|
306
|
+
} as const
|
|
307
|
+
|
|
308
|
+
type CompiledAnimationType = (typeof animationTypeMap)[keyof typeof animationTypeMap]
|
|
309
|
+
type WarningMetadata = Record<string, unknown>
|
|
310
|
+
|
|
311
|
+
const animationProperties = new Set([
|
|
312
|
+
'transform.translateX',
|
|
313
|
+
'transform.translateY',
|
|
314
|
+
'transform.scale',
|
|
315
|
+
'transform.scaleX',
|
|
316
|
+
'transform.scaleY',
|
|
317
|
+
'transform.rotate',
|
|
318
|
+
'transform.rotateX',
|
|
319
|
+
'transform.rotateY',
|
|
320
|
+
'opacity',
|
|
321
|
+
])
|
|
322
|
+
|
|
323
|
+
const animationComposeTypes = new Set(['parallel', 'sequence'])
|
|
324
|
+
const springConfigFamilies = [
|
|
325
|
+
['stiffness', 'damping', 'mass'],
|
|
326
|
+
['tension', 'friction'],
|
|
327
|
+
['bounciness', 'speed'],
|
|
328
|
+
]
|
|
329
|
+
const springConfigFamilyKeys = new Set(springConfigFamilies.flat())
|
|
330
|
+
|
|
331
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
332
|
+
Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
|
333
|
+
|
|
334
|
+
const hasDefinedConfigValue = (config: Record<string, unknown>, key: string) =>
|
|
335
|
+
config[key] !== undefined
|
|
336
|
+
|
|
337
|
+
const assertConfigValue = (
|
|
338
|
+
config: Record<string, unknown>,
|
|
339
|
+
key: string,
|
|
340
|
+
errorReference: string,
|
|
341
|
+
) => {
|
|
342
|
+
if (!hasDefinedConfigValue(config, key)) {
|
|
343
|
+
throw new Error(`Invalid animation config ${errorReference}: missing "${key}"`)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const assertAnimationProperty = (property: unknown, errorReference: string) => {
|
|
348
|
+
if (typeof property !== 'string' || !animationProperties.has(property)) {
|
|
349
|
+
throw new Error(
|
|
350
|
+
`Invalid animation property${errorReference ? ` ${errorReference}` : ''}: ${String(
|
|
351
|
+
property,
|
|
352
|
+
)}`,
|
|
353
|
+
)
|
|
354
|
+
}
|
|
355
|
+
return property
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const getAnimationType = (config: unknown, errorReference: string): CompiledAnimationType => {
|
|
359
|
+
if (!isRecord(config)) {
|
|
360
|
+
throw new Error(`Invalid animation config ${errorReference}: config must be an object`)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const animationType = animationTypeMap[config.__type as keyof typeof animationTypeMap]
|
|
364
|
+
if (!animationType) {
|
|
365
|
+
throw new Error(`Invalid animation config type ${errorReference}: ${String(config.__type)}`)
|
|
366
|
+
}
|
|
367
|
+
return animationType
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const assertAnimationComposeType = (composeType: unknown, errorReference: string) => {
|
|
371
|
+
if (typeof composeType !== 'string' || !animationComposeTypes.has(composeType)) {
|
|
372
|
+
throw new Error(`Invalid animation compose type ${errorReference}: ${String(composeType)}`)
|
|
373
|
+
}
|
|
374
|
+
return composeType
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const pickDefinedConfigValues = (config: Record<string, unknown>, keys: string[]) =>
|
|
378
|
+
keys.reduce((acc, key) => {
|
|
379
|
+
if (hasDefinedConfigValue(config, key)) acc[key] = config[key]
|
|
380
|
+
return acc
|
|
381
|
+
}, {})
|
|
382
|
+
|
|
383
|
+
const getDefinedConfigKeys = (config: Record<string, unknown>, keys: string[]) =>
|
|
384
|
+
keys.filter((key) => hasDefinedConfigValue(config, key))
|
|
385
|
+
|
|
386
|
+
const formatWarningMetadata = (metadata: WarningMetadata = {}) =>
|
|
387
|
+
Object.entries(metadata)
|
|
388
|
+
.filter(([, value]) => value !== undefined && value !== null && value !== '')
|
|
389
|
+
.map(([key, value]) => `${key}: ${String(value)}`)
|
|
390
|
+
.join(', ')
|
|
391
|
+
|
|
392
|
+
const formatWarningReference = (metadata?: WarningMetadata) => {
|
|
393
|
+
const metadataText = formatWarningMetadata(metadata)
|
|
394
|
+
return metadataText ? ` [${metadataText}]` : ''
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const normalizeSpringConfig = (
|
|
398
|
+
config: Record<string, unknown>,
|
|
399
|
+
errorReference: string,
|
|
400
|
+
warningMetadata?: WarningMetadata,
|
|
401
|
+
): Record<string, unknown> => {
|
|
402
|
+
assertConfigValue(config, 'toValue', errorReference)
|
|
403
|
+
|
|
404
|
+
const usedFamilies = springConfigFamilies.filter((keys) =>
|
|
405
|
+
keys.some((key) => hasDefinedConfigValue(config, key)),
|
|
406
|
+
)
|
|
407
|
+
if (usedFamilies.length <= 1) return config
|
|
408
|
+
|
|
409
|
+
const configWithoutSpringFamily = Object.entries(config).reduce((acc, [key, value]) => {
|
|
410
|
+
if (!springConfigFamilyKeys.has(key)) acc[key] = value
|
|
411
|
+
return acc
|
|
412
|
+
}, {})
|
|
413
|
+
|
|
414
|
+
// Match runtime normalization: physical spring values are most explicit,
|
|
415
|
+
// otherwise preserve BRICKS' historical tension/friction controls.
|
|
416
|
+
const resolvedFamily =
|
|
417
|
+
usedFamilies.find((keys) => keys.includes('stiffness')) ||
|
|
418
|
+
usedFamilies.find((keys) => keys.includes('tension')) ||
|
|
419
|
+
usedFamilies[0]
|
|
420
|
+
const resolvedFamilyKeys = getDefinedConfigKeys(config, resolvedFamily)
|
|
421
|
+
const droppedFamilyKeys = usedFamilies
|
|
422
|
+
.filter((keys) => keys !== resolvedFamily)
|
|
423
|
+
.flatMap((keys) => getDefinedConfigKeys(config, keys))
|
|
424
|
+
|
|
425
|
+
console.warn(
|
|
426
|
+
`[Warning] Resolved animation spring config${formatWarningReference(
|
|
427
|
+
warningMetadata,
|
|
428
|
+
)}: using ${resolvedFamilyKeys.join('/')}, dropping ${droppedFamilyKeys.join('/')}`,
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
return {
|
|
432
|
+
...configWithoutSpringFamily,
|
|
433
|
+
...pickDefinedConfigValues(config, resolvedFamily),
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const compileAnimationConfig = (
|
|
438
|
+
animationType: CompiledAnimationType,
|
|
439
|
+
config: unknown,
|
|
440
|
+
errorReference: string,
|
|
441
|
+
warningMetadata?: WarningMetadata,
|
|
442
|
+
) => {
|
|
443
|
+
if (!isRecord(config)) {
|
|
444
|
+
throw new Error(`Invalid animation config ${errorReference}: config must be an object`)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const compiledConfig = compileProperty(omit(config, '__type'), errorReference)
|
|
448
|
+
|
|
449
|
+
if (!isRecord(compiledConfig)) {
|
|
450
|
+
throw new Error(`Invalid animation config ${errorReference}: config must compile to an object`)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (animationType === 'timing') {
|
|
454
|
+
assertConfigValue(compiledConfig, 'toValue', errorReference)
|
|
455
|
+
} else if (animationType === 'spring') {
|
|
456
|
+
return normalizeSpringConfig(compiledConfig, errorReference, warningMetadata)
|
|
457
|
+
} else if (animationType === 'decay') {
|
|
458
|
+
assertConfigValue(compiledConfig, 'velocity', errorReference)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return compiledConfig
|
|
304
462
|
}
|
|
305
463
|
|
|
306
464
|
const compileFrame = (frame: Canvas['items'][number]['frame']) => ({
|
|
@@ -327,6 +485,8 @@ const preloadTypes = [
|
|
|
327
485
|
'ggml-model-asset',
|
|
328
486
|
'gguf-model-asset',
|
|
329
487
|
'binary-asset',
|
|
488
|
+
'mlx-model-asset',
|
|
489
|
+
'scene3d-objects',
|
|
330
490
|
]
|
|
331
491
|
|
|
332
492
|
const compileKind = (kind: Data['kind']) => {
|
|
@@ -485,6 +645,7 @@ const compileAutomationTest = (
|
|
|
485
645
|
|
|
486
646
|
return {
|
|
487
647
|
id: testId,
|
|
648
|
+
alias: test.alias,
|
|
488
649
|
title: test.title,
|
|
489
650
|
hide_short_ref: test.hideShortRef,
|
|
490
651
|
timeout: test.timeout,
|
|
@@ -629,39 +790,63 @@ export const compile = async (app: Application) => {
|
|
|
629
790
|
`(animation index: ${animationIndex}, subspace: ${subspaceId})`,
|
|
630
791
|
)
|
|
631
792
|
|
|
632
|
-
|
|
793
|
+
const animationTypename = animation.__typename
|
|
794
|
+
if (animationTypename === 'Animation') {
|
|
633
795
|
const animationDef = animation as AnimationDef
|
|
796
|
+
const animationErrorReference = `(animation: ${animationId}, subspace ${subspaceId})`
|
|
797
|
+
const animationWarningMetadata = {
|
|
798
|
+
animationIndex,
|
|
799
|
+
animationTitle: animationDef.title,
|
|
800
|
+
animationAlias: animationDef.alias,
|
|
801
|
+
animationProperty: animationDef.property,
|
|
802
|
+
subspaceIndex,
|
|
803
|
+
subspaceTitle: subspace.title,
|
|
804
|
+
}
|
|
805
|
+
const animationType = getAnimationType(animationDef.config, animationErrorReference)
|
|
634
806
|
map[animationId] = {
|
|
635
807
|
alias: animationDef.alias,
|
|
636
808
|
title: animationDef.title,
|
|
637
809
|
description: animationDef.description,
|
|
638
810
|
hide_short_ref: animationDef.hideShortRef,
|
|
639
811
|
animationRunType: animationDef.runType,
|
|
640
|
-
property: animationDef.property,
|
|
641
|
-
type:
|
|
642
|
-
config:
|
|
643
|
-
|
|
644
|
-
|
|
812
|
+
property: assertAnimationProperty(animationDef.property, animationErrorReference),
|
|
813
|
+
type: animationType,
|
|
814
|
+
config: compileAnimationConfig(
|
|
815
|
+
animationType,
|
|
816
|
+
animationDef.config,
|
|
817
|
+
animationErrorReference,
|
|
818
|
+
animationWarningMetadata,
|
|
645
819
|
),
|
|
646
820
|
}
|
|
647
|
-
} else if (
|
|
821
|
+
} else if (animationTypename === 'AnimationCompose') {
|
|
648
822
|
const animationDef = animation as AnimationComposeDef
|
|
823
|
+
const animationErrorReference = `(animation: ${animationId}, subspace ${subspaceId})`
|
|
649
824
|
map[animationId] = {
|
|
650
825
|
alias: animationDef.alias,
|
|
651
826
|
title: animationDef.title,
|
|
652
827
|
description: animationDef.description,
|
|
653
828
|
hide_short_ref: animationDef.hideShortRef,
|
|
654
829
|
animationRunType: animationDef.runType,
|
|
655
|
-
compose_type:
|
|
830
|
+
compose_type: assertAnimationComposeType(
|
|
831
|
+
animationDef.composeType,
|
|
832
|
+
animationErrorReference,
|
|
833
|
+
),
|
|
656
834
|
item_list: animationDef.items.map((item, index) => {
|
|
657
835
|
const innerAnimation = item()
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
)
|
|
662
|
-
|
|
836
|
+
const innerAnimationId = assertEntryId(
|
|
837
|
+
innerAnimation?.id,
|
|
838
|
+
'ANIMATION',
|
|
839
|
+
`(animation item index: ${index}, animation: ${animationId}, subspace ${subspaceId})`,
|
|
840
|
+
)
|
|
841
|
+
return { animation_id: innerAnimationId }
|
|
663
842
|
}),
|
|
664
843
|
}
|
|
844
|
+
} else {
|
|
845
|
+
throw new Error(
|
|
846
|
+
`Invalid animation typename (animation: ${animationId}, subspace ${subspaceId}): ${String(
|
|
847
|
+
animationTypename,
|
|
848
|
+
)}`,
|
|
849
|
+
)
|
|
665
850
|
}
|
|
666
851
|
return map
|
|
667
852
|
}, {}),
|
|
@@ -1009,7 +1194,7 @@ export const compile = async (app: Application) => {
|
|
|
1009
1194
|
...compileRemoteUpdate(data.remoteUpdate),
|
|
1010
1195
|
routing: data.routing,
|
|
1011
1196
|
schema: data.schema,
|
|
1012
|
-
type: data.type,
|
|
1197
|
+
type: data.type === 'boolean' ? 'bool' : data.type,
|
|
1013
1198
|
...compileKind(data.kind),
|
|
1014
1199
|
value: compileProperty(data.value, `(data: ${dataId}, subspace ${subspaceId})`),
|
|
1015
1200
|
event_map: compileEvents('PROPERTY_BANK', data.events || {}, {
|
|
@@ -1029,6 +1214,7 @@ export const compile = async (app: Application) => {
|
|
|
1029
1214
|
)
|
|
1030
1215
|
|
|
1031
1216
|
const calc: any = {
|
|
1217
|
+
alias: dataCalc.alias,
|
|
1032
1218
|
title: dataCalc.title,
|
|
1033
1219
|
description: dataCalc.description,
|
|
1034
1220
|
hide_short_ref: dataCalc.hideShortRef,
|
|
@@ -1274,5 +1460,7 @@ export const compile = async (app: Application) => {
|
|
|
1274
1460
|
|
|
1275
1461
|
export const checkConfig = async (configPath: string) => {
|
|
1276
1462
|
const { sh } = await import('../tools/_shell')
|
|
1277
|
-
|
|
1463
|
+
// --validate-automation surfaces broken automation_map / test_map refs early,
|
|
1464
|
+
// which catches agent-authored automations that reference deleted bricks.
|
|
1465
|
+
await sh`bricks app check-config --validate-automation ${configPath}`
|
|
1278
1466
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fugood/bricks-project",
|
|
3
|
-
"version": "2.25.0-beta.
|
|
3
|
+
"version": "2.25.0-beta.30",
|
|
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.
|
|
10
|
+
"@fugood/bricks-cli": "^2.25.0-beta.30",
|
|
11
11
|
"@huggingface/gguf": "^0.3.2",
|
|
12
12
|
"@iarna/toml": "^3.0.0",
|
|
13
13
|
"@modelcontextprotocol/sdk": "^1.15.0",
|
package/package.json.bak
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fugood/bricks-ctor",
|
|
3
|
-
"version": "2.25.0-beta.
|
|
3
|
+
"version": "2.25.0-beta.30",
|
|
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.
|
|
10
|
+
"@fugood/bricks-cli": "^2.25.0-beta.30",
|
|
11
11
|
"@huggingface/gguf": "^0.3.2",
|
|
12
12
|
"@iarna/toml": "^3.0.0",
|
|
13
13
|
"@modelcontextprotocol/sdk": "^1.15.0",
|
|
@@ -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,
|
|
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
|
|
@@ -20,6 +20,7 @@ This skill covers advanced BRICKS features not in the main project instructions.
|
|
|
20
20
|
| [Remote Data Bank](rules/remote-data-bank.md) | Cloud data sync and API access |
|
|
21
21
|
| [Media Flow](rules/media-flow.md) | Media asset management |
|
|
22
22
|
| [Buttress](rules/buttress.md) | Remote inference for AI generators |
|
|
23
|
+
| [Verification Toolchain](rules/verification-toolchain.md) | Definition of done, compile, preview tool selection, on-device DevTools, Path 1/2/3 decision rule |
|
|
23
24
|
|
|
24
25
|
## Quick Reference
|
|
25
26
|
|
|
@@ -30,3 +31,4 @@ This skill covers advanced BRICKS features not in the main project instructions.
|
|
|
30
31
|
- **AI offloading**: See [Buttress](rules/buttress.md) for GPU server delegation
|
|
31
32
|
- **E2E testing**: See [Automations](rules/automations.md) for test automation
|
|
32
33
|
- **Enter animations**: See [Standby Transition](rules/standby-transition.md) for canvas transitions
|
|
34
|
+
- **Verification before done**: See [Verification Toolchain](rules/verification-toolchain.md) for the definition-of-done gate and Path 1/2/3 decision rule
|
|
@@ -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
|
|
|
@@ -26,6 +26,8 @@ The primary way to orchestrate multi-step flows. A single event can contain an a
|
|
|
26
26
|
- Use `dataParams` + `mapping` to pass event data downstream
|
|
27
27
|
- This is the "glue" that wires generators, state, and UI together
|
|
28
28
|
|
|
29
|
+
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.
|
|
30
|
+
|
|
29
31
|
### System Actions (Priority 3)
|
|
30
32
|
Built-in commands for direct state and UI changes.
|
|
31
33
|
- **PROPERTY_BANK**: set data value
|
|
@@ -67,3 +69,13 @@ Only actual data derivation maps to Data Calculation:
|
|
|
67
69
|
|
|
68
70
|
### Step 4: Wire with Event Action Chains
|
|
69
71
|
Connect the pieces through events on generators and bricks.
|
|
72
|
+
|
|
73
|
+
## Recipe: user-driven state machine (calculator, form wizard, picker)
|
|
74
|
+
|
|
75
|
+
A brief like "state vars X, Y, Z; button A updates X from X+Y; button B resets" is a state machine. The shape:
|
|
76
|
+
|
|
77
|
+
- Each state variable is its own `Data` entity. Displays read it via `linkData(() => data.dFoo)`.
|
|
78
|
+
- 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.
|
|
79
|
+
- Use `Data Calculation` only for reusable pure derivations referenced as inputs to those expressions.
|
|
80
|
+
|
|
81
|
+
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
|
|
@@ -20,16 +20,26 @@ When mobile devices or embedded systems lack hardware for local AI inference (LL
|
|
|
20
20
|
|
|
21
21
|
## Client Configuration
|
|
22
22
|
|
|
23
|
-
In generator properties, configure
|
|
23
|
+
In generator properties, configure `buttressConnectionSettings`:
|
|
24
24
|
|
|
25
25
|
| Setting | Description |
|
|
26
26
|
|---------|-------------|
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
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) |
|
|
31
32
|
|
|
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
|
|
33
43
|
|
|
34
44
|
| Strategy | Description |
|
|
35
45
|
|----------|-------------|
|
|
@@ -37,7 +47,11 @@ In generator properties, configure Buttress settings:
|
|
|
37
47
|
| `prefer-buttress` | Use Buttress if available, fallback to local |
|
|
38
48
|
| `prefer-best` | Auto-select based on capability comparison |
|
|
39
49
|
|
|
40
|
-
## Generator Configuration
|
|
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.
|
|
41
55
|
|
|
42
56
|
```typescript
|
|
43
57
|
import { makeId } from 'bricks-ctor'
|
|
@@ -53,7 +67,7 @@ const llmGenerator: GeneratorLLM = {
|
|
|
53
67
|
contextSize: 8192,
|
|
54
68
|
buttressConnectionSettings: {
|
|
55
69
|
enabled: true,
|
|
56
|
-
|
|
70
|
+
autoDiscoverType: 'auto',
|
|
57
71
|
fallbackType: 'use-local',
|
|
58
72
|
strategy: 'prefer-best',
|
|
59
73
|
},
|
|
@@ -63,6 +77,44 @@ const llmGenerator: GeneratorLLM = {
|
|
|
63
77
|
}
|
|
64
78
|
```
|
|
65
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
|
+
|
|
66
118
|
## Server Setup
|
|
67
119
|
|
|
68
120
|
### Requirements
|
|
@@ -98,6 +150,43 @@ bricks-buttress --config ./config.toml
|
|
|
98
150
|
|----------|-------------|
|
|
99
151
|
| `HF_TOKEN` | Hugging Face token for model downloads |
|
|
100
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.
|
|
101
190
|
|
|
102
191
|
## Server Configuration (TOML)
|
|
103
192
|
|