@zaflun/lumio-agent-skill 0.4.0 → 0.5.3

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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  installSkillFiles
4
- } from "../chunk-SSYJXSJX.js";
4
+ } from "../chunk-TKNV5K6F.js";
5
5
 
6
6
  // src/bin/lumio-skills.ts
7
7
  import { resolve } from "path";
@@ -14,8 +14,8 @@ Lumio Extensions are sandboxed React apps that run inside a 3-layer iframe sandb
14
14
  \`\`\`
15
15
  3-Layer Sandbox:
16
16
  Host (lumio.vision / browser source)
17
- \u2514\u2500\u2500 Supervisor iframe (supervisor.extension.lumio.vision)
18
- \u2514\u2500\u2500 Extension Runtime iframe (ext.lumio.vision)
17
+ \u2514\u2500\u2500 Supervisor iframe (supervisor.ext.lumio.vision)
18
+ \u2514\u2500\u2500 Extension Runtime iframe (<label>.runtime.ext.lumio.vision)
19
19
  \u2514\u2500\u2500 Web Worker (server function execution)
20
20
  \`\`\`
21
21
 
@@ -30,7 +30,7 @@ Extension Project:
30
30
  \`\`\`
31
31
 
32
32
  **Runtime flow:**
33
- 1. Extension Runtime iframe loads from \`ext.lumio.vision/ext/{slug}/layer?token=lm_ext_xxx&install_id=uuid\`
33
+ 1. Extension Runtime iframe loads on the extension's own origin \`<label>.runtime.ext.lumio.vision\` (\`<label>\` = extension UUID as 32 lowercase hex)
34
34
  2. \`Lumio.render(<Component />, { target: "layer" })\` must be called within 10 seconds
35
35
  3. The custom React reconciler serializes the tree to \`LumioComponentNode[]\` via postMessage to the Supervisor
36
36
  4. The Supervisor validates and forwards the node tree to the Host for rendering
@@ -76,8 +76,13 @@ All three surfaces share storage via \`useExtensionStorage()\` \u2014 changes in
76
76
  },
77
77
  "visibility": "public",
78
78
  "pricing": { "type": "free" },
79
+ "sounds": [
80
+ { "file": "alert.mp3", "name": "Alert" },
81
+ { "file": "chime.wav", "name": "Chime", "default_volume": 0.8 }
82
+ ],
79
83
  "config_schema": [
80
- { "key": "title", "type": "string", "label": "Widget Title", "default_value": "My Widget", "required": true },
84
+ { "key": "title", "type": "string", "label": "Widget Title", "default_value": "My Widget", "required": true, "placeholder": "Enter title..." },
85
+ { "key": "fontSize", "type": "number", "label": "Font Size", "default_value": 16, "required": false, "min": 8, "max": 200, "unit": "px" },
81
86
  { "key": "showBorder", "type": "boolean", "label": "Show Border", "default_value": true, "required": false }
82
87
  ]
83
88
  }
@@ -94,6 +99,8 @@ All three surfaces share storage via \`useExtensionStorage()\` \u2014 changes in
94
99
  - \`"visibility": "unlisted"\` \u2014 accessible by direct link only
95
100
  - Private invite: grant \`free_access\` to specific accounts via dashboard
96
101
 
102
+ **Bundled sounds:** Place audio files in \`dist/sounds/\` and declare them in the \`sounds\` array. Supported: MP3, WAV, OGG, WebM, M4A, AAC, FLAC. Default limits: 50 sounds per extension, 10 MB per file, 200 MB total storage (all admin-configurable). Developers can view limits at \`/account/developer/limits\` and request increases via a 4-step wizard. Requests are reviewed by admins in the admin app. Users cannot delete bundled sounds \u2014 uninstalling the extension removes them.
103
+
97
104
  **Revenue split:** Configurable per developer (default 70/30 developer/Lumio).
98
105
 
99
106
  **Feature gates:** The following Lumio plan features must be enabled for extension functionality:
@@ -136,11 +143,11 @@ lumio deploy
136
143
 
137
144
  ## Components
138
145
 
139
- 15 SDK components (all accept \`style: VisionStyle\`):
146
+ 17 SDK components (all accept \`style: VisionStyle\`):
140
147
 
141
148
  **Layout:** \`Box\`, \`VStack\`, \`HStack\`, \`CompactView\`, \`List\`
142
- **Display:** \`Text\` (variant: heading/muted/default), \`Image\` (objectFit), \`Animated\`
143
- **Input:** \`Button\` (variant: default/destructive), \`Toggle\`, \`TextField\`, \`TextArea\`, \`NumberField\`, \`Dropdown\`, \`Slider\`
149
+ **Display:** \`Text\` (variant: heading/muted/default), \`Image\` (objectFit), \`Animated\`, \`IconDisplay\`
150
+ **Input:** \`Button\` (variant: default/destructive), \`Toggle\`, \`TextField\`, \`TextArea\`, \`NumberField\`, \`Dropdown\`, \`Slider\`, \`IconPicker\`
144
151
 
145
152
  **VisionStyle** is a typed CSS subset (allowed: flexbox, grid, \`position: relative\` / \`absolute\`, sizing, spacing, colors, border, typography, transitions; blocked: \`fixed\`, \`sticky\`, CSS expressions).
146
153
 
@@ -150,7 +157,7 @@ See \`components.md\` for full props and examples.
150
157
 
151
158
  ## Hooks
152
159
 
153
- 11 SDK hooks:
160
+ 12 SDK hooks:
154
161
 
155
162
  | Hook | Returns | Purpose |
156
163
  |------|---------|---------|
@@ -165,6 +172,7 @@ See \`components.md\` for full props and examples.
165
172
  | \`useQuery(fn, params?)\` | \`{ data, isLoading, error, refetch }\` | Call server query |
166
173
  | \`useMutation(fn)\` | \`{ mutate, mutateAsync, data, isLoading, error }\` | Call server mutation/action |
167
174
  | \`useAnimation(preset, options?)\` | \`animationProps\` | SDK animation presets for \`Animated\` component |
175
+ | \`useIconPicker(options?)\` | \`{ isOpen, selected, open, close, select, clear, sources }\` | Headless state for custom icon picker UIs |
168
176
 
169
177
  **Important:** \`setStorage\` must always spread: \`setStorage({ ...storage, key: value })\`
170
178
 
@@ -194,26 +202,17 @@ export const fetchScores = action({
194
202
  });
195
203
  \`\`\`
196
204
 
197
- **Extension Actions** via \`useLumioAction()\` (21 total, \`action\` is \`LumioActionType\`):
205
+ **Extension Actions** via \`useLumioAction()\` (dispatched by the Lumio API; \`action\` is \`LumioActionType\`):
198
206
  \`\`\`tsx
199
207
  // Trigger platform actions (requires matching permission in lumio.config.json)
200
208
  const { execute: sendChat } = useLumioAction("chat:send");
201
- const { execute: banUser } = useLumioAction("chat:ban");
202
- const { execute: unbanUser } = useLumioAction("chat:unban");
203
- const { execute: timeoutUser } = useLumioAction("chat:timeout");
209
+ const { execute: deleteChat } = useLumioAction("chat:delete");
204
210
  const { execute: setScene } = useLumioAction("obs:set_scene");
205
- const { execute: startRecording } = useLumioAction("obs:start_recording");
206
- const { execute: stopRecording } = useLumioAction("obs:stop_recording");
211
+ const { execute: setSourceVisible } = useLumioAction("obs:set_source_visible");
212
+ const { execute: startStream } = useLumioAction("obs:start_stream");
213
+ const { execute: stopStream } = useLumioAction("obs:stop_stream");
207
214
  const { execute: emitEvent } = useLumioAction("events:emit");
208
215
  const { execute: updateLayer } = useLumioAction("overlay:update_layer");
209
- const { execute: spotifyPlay } = useLumioAction("spotify:play");
210
- const { execute: spotifyPause } = useLumioAction("spotify:pause");
211
- const { execute: spotifySkip } = useLumioAction("spotify:skip");
212
- const { execute: spotifySeek } = useLumioAction("spotify:seek");
213
- const { execute: spotifyVolume } = useLumioAction("spotify:volume");
214
- const { execute: spotifyShuffle } = useLumioAction("spotify:shuffle");
215
- const { execute: spotifyRepeat } = useLumioAction("spotify:repeat");
216
- const { execute: spotifyQueueAdd } = useLumioAction("spotify:queue_add");
217
216
  \`\`\`
218
217
 
219
218
  See \`server-functions.md\` for schema definition, storage scoping, and advanced patterns.
@@ -373,22 +372,26 @@ return <Text content={storage.text} />;
373
372
 
374
373
  ### Live events
375
374
  \`\`\`tsx
376
- // eventType is LumioEventType \u2014 autocomplete shows all 49 valid event strings
375
+ // eventType is LumioEventType \u2014 autocomplete shows all 58 valid event strings
377
376
  const event = useLumioEvent("twitch:follower");
378
377
  // event updates automatically when a new follower event arrives
379
378
  \`\`\`
380
379
 
381
- **All 49 event types** (\`LumioEventType\`):
380
+ **All 58 event types** (\`LumioEventType\`):
381
+
382
+ Twitch (23): \`twitch:follower\`, \`twitch:subscribe\`, \`twitch:gift_sub\`, \`twitch:resub\`, \`twitch:cheer\`, \`twitch:raid\`, \`twitch:reward\`, \`twitch:hype_train\`, \`twitch:hype_train_end\`, \`twitch:poll\`, \`twitch:poll_end\`, \`twitch:prediction\`, \`twitch:prediction_lock\`, \`twitch:prediction_end\`, \`twitch:goal\`, \`twitch:goal_end\`, \`twitch:ad_break\`, \`twitch:ban\`, \`twitch:unban\`, \`twitch:stream_online\`, \`twitch:stream_offline\`, \`twitch:stream_update\`, \`twitch:chat\`
383
+
384
+ YouTube (10): \`youtube:subscribe\`, \`youtube:member\`, \`youtube:superchat\`, \`youtube:supersticker\`, \`youtube:gift_membership\`, \`youtube:gift_membership_received\`, \`youtube:poll\`, \`youtube:stream_online\`, \`youtube:stream_offline\`, \`youtube:chat\`
382
385
 
383
- Twitch (22): \`twitch:follower\`, \`twitch:subscriber\`, \`twitch:subscribe\`, \`twitch:gift_sub\`, \`twitch:resub\`, \`twitch:cheer\`, \`twitch:raid\`, \`twitch:redemption\`, \`twitch:ban\`, \`twitch:unban\`, \`twitch:hype_train\`, \`twitch:poll\`, \`twitch:prediction\`, \`twitch:goal\`, \`twitch:ad_break\`, \`twitch:stream_update\`, \`twitch:chat\`
386
+ Kick (11): \`kick:follower\`, \`kick:subscribe\`, \`kick:resub\`, \`kick:gift\`, \`kick:kicks\`, \`kick:reward\`, \`kick:reward_status\`, \`kick:stream_online\`, \`kick:stream_offline\`, \`kick:stream_update\`, \`kick:chat\`
384
387
 
385
- YouTube (7): \`youtube:subscriber\`, \`youtube:superchat\`, \`youtube:supersticker\`, \`youtube:gift_membership\`, \`youtube:gift_membership_received\`, \`youtube:poll\`, \`youtube:chat\`
388
+ Spotify (12): \`spotify:track\`, \`spotify:play\`, \`spotify:pause\`, \`spotify:skip\`, \`spotify:volume\`, \`spotify:queue_add\`, \`spotify:device\`, \`spotify:playlist_add\`, \`spotify:playlist_remove\`, \`spotify:playlist_create\`, \`spotify:playlist_edit\`, \`spotify:playlist_delete\`
386
389
 
387
- Kick (6): \`kick:follower\`, \`kick:subscribe\`, \`kick:gift\`, \`kick:stream_online\`, \`kick:stream_offline\`, \`kick:chat\`
390
+ StreamElements (1): \`streamelements:tip\`
388
391
 
389
- Trovo (3): \`trovo:subscribe\`, \`trovo:spell\`, \`trovo:chat\`
392
+ Cross-platform chat (1, fires for every platform): \`chat:message\`
390
393
 
391
- Spotify (11): \`spotify:track\`, \`spotify:play\`, \`spotify:pause\`, \`spotify:skip\`, \`spotify:volume\`, \`spotify:queue_add\`, \`spotify:device\`, \`spotify:playlist_add\`, \`spotify:playlist_remove\`, \`spotify:playlist_create\`, \`spotify:playlist_delete\`
394
+ Kick events carry canonical top-level fields (\`user_id\`, \`user_login\`, \`user_name\`, \`avatarUrl\`, \`is_anonymous\`) plus the original Kick payload. \`kick:kicks\` has \`kicks\` (amount, like Twitch \`bits\`) and \`message\`; \`kick:reward\` / \`kick:reward_status\` have \`status\`, \`user_input\` and \`reward { id, title, cost, prompt }\`; \`kick:resub\` has \`cumulative_months\`.
392
395
 
393
396
  ### Server queries
394
397
  \`\`\`tsx
@@ -419,7 +422,7 @@ const anim = useAnimation("slide-in", { duration: 300 });
419
422
  | \`EGRESS_BLOCKED\` | Fetch to undeclared host | Add host to \`egress.allowHosts\` in config |
420
423
  | \`STORAGE_QUOTA\` | Exceeded storage limit | Reduce data or request limit increase |
421
424
  | \`EDITOR_ONLY\` | Mutation called from layer/interactive | Remove \`editorOnly\` or restrict to editor |
422
- | \`SUPERVISOR_BLOCKED\` | Supervisor rejected a postMessage | Check message origin matches ext.lumio.vision |
425
+ | \`SUPERVISOR_BLOCKED\` | Supervisor rejected a postMessage | Check message origin matches the extension's runtime origin (\`<label>.runtime.ext.lumio.vision\`) |
423
426
  | \`WORKER_CREATION_FAILED\` | Web Worker could not be created | Verify server functions bundle is valid JS |
424
427
  | \`STORAGE_DOM_EXCEPTION\` | localStorage/sessionStorage access | Use \`useExtensionStorage()\` instead (storage blocked) |
425
428
 
@@ -429,7 +432,59 @@ See \`troubleshooting.md\` for the full error reference.
429
432
  // src/content/components.ts
430
433
  var componentsContent = `# Lumio SDK Components
431
434
 
432
- All 15 components available in \`@zaflun/lumio-sdk\`. Every component accepts an optional \`style: VisionStyle\` prop.
435
+ All 31 components available in \`@zaflun/lumio-sdk\`. Every component accepts an optional \`style: VisionStyle\` prop.
436
+
437
+ ## Editor Components
438
+
439
+ 14 components for building settings UIs in \`editor.tsx\` and \`designer.tsx\`. They serialize via the Reconciler and render as \`@lumio/ui\` components on the host \u2014 matching the dashboard design system.
440
+
441
+ \`\`\`tsx
442
+ import {
443
+ EditorInput, EditorTextarea, EditorSelect, EditorCheckbox,
444
+ EditorSlider, EditorColorPicker, EditorGroup, EditorLabel,
445
+ EditorDivider, EditorInfo, EditorFontRow, EditorButton,
446
+ EditorFontPicker, EditorChannelRow, useEditorAction,
447
+ } from "@zaflun/lumio-sdk";
448
+
449
+ // Text/number input with label, placeholder, min/max, unit
450
+ <EditorInput label="Font Size" type="number" value="16" onChange={setSize} min={8} max={200} unit="px" />
451
+
452
+ // Select dropdown
453
+ <EditorSelect label="Theme" value={theme} onChange={setTheme} options={[{ value: "dark", label: "Dark" }]} />
454
+
455
+ // Checkbox toggle
456
+ <EditorCheckbox label="Show Badges" checked={show} onChange={setShow} />
457
+
458
+ // Slider with range
459
+ <EditorSlider label="Opacity" value={100} onChange={setOpacity} min={0} max={100} unit="%" />
460
+
461
+ // Color picker (HSV canvas + hex input)
462
+ <EditorColorPicker label="Background" value="#000000" onChange={setColor} />
463
+
464
+ // Compact font row: [B] [size px] [color]
465
+ <EditorFontRow bold={isBold} onBoldChange={setBold} fontSize={16} onFontSizeChange={setSize} fontColor="#fff" onFontColorChange={setColor} />
466
+
467
+ // Full-width button (primary/default/destructive)
468
+ <EditorButton label="Open Designer" variant="primary" onClick={openDesigner} />
469
+
470
+ // Collapsible section
471
+ <EditorGroup label="Advanced"><EditorCheckbox ... /></EditorGroup>
472
+
473
+ // Font picker with weight dropdown (opens NavigationStack page)
474
+ <EditorFontPicker label="Font Family" value="Lexend" onChange={setFont} weight="400" onWeightChange={setWeight} />
475
+
476
+ // Channel row: platform + name + color + delete in one line
477
+ <EditorChannelRow platform="twitch" onPlatformChange={setPlatform} channel="username" onChannelChange={setChannel} color="#9146FF" onColorChange={setColor} onRemove={remove} />
478
+
479
+ // Dispatch UI actions (e.g. open designer)
480
+ const { dispatch } = useEditorAction();
481
+ <EditorButton label="Open Designer" variant="primary" onClick={() => dispatch("open-designer")} />
482
+
483
+ // Section label, divider, info text
484
+ <EditorLabel text="Settings" />
485
+ <EditorDivider />
486
+ <EditorInfo content="Configure your widget here." />
487
+ \`\`\`
433
488
 
434
489
  > **Note:** There is no direct DOM access from extension code. The custom React reconciler serializes component trees to \`LumioComponentNode[]\` via postMessage \u2014 you must use SDK components exclusively.
435
490
 
@@ -750,6 +805,69 @@ const exitAnim = useAnimation("fade-out", { duration: 200 });
750
805
  - Keep animation durations short on the layer surface (< 500ms) to avoid frame drops
751
806
  - Avoid animating layout-affecting properties (\`width\`, \`height\`) on the layer; prefer \`opacity\` and \`transform\`
752
807
 
808
+ ## Picker Components
809
+
810
+ ### IconPicker
811
+
812
+ Universal icon/emoji/emote picker with 4 tabs: Lucide icons (1960), Unicode emojis (1584), platform emotes (via API), and file upload. **Editor-only** \u2014 use \`IconDisplay\` to render the selected icon on the layer surface.
813
+
814
+ \`\`\`tsx
815
+ import { IconPicker, IconDisplay } from "@zaflun/lumio-sdk";
816
+ import type { IconValue } from "@zaflun/lumio-sdk";
817
+
818
+ // In editor.tsx \u2014 let the user pick an icon
819
+ const [storage, setStorage] = useExtensionStorage();
820
+
821
+ <IconPicker
822
+ value={storage.icon ?? null}
823
+ onChange={(icon) => setStorage({ ...storage, icon })}
824
+ sources={["lucide", "emoji", "emote", "upload"]}
825
+ size="md"
826
+ placeholder="Choose an icon..."
827
+ />
828
+
829
+ // In layer.tsx \u2014 render the selected icon
830
+ <IconDisplay icon={storage.icon ?? null} size={32} />
831
+ \`\`\`
832
+
833
+ **IconPickerProps:**
834
+ - \`value: IconValue | null\` \u2014 currently selected icon
835
+ - \`onChange: (icon: IconValue | null) => void\` \u2014 selection callback
836
+ - \`sources?: IconSource[]\` \u2014 tabs to show (default: all four: \`["lucide", "emoji", "emote", "upload"]\`)
837
+ - \`size?: "sm" | "md" | "lg"\` \u2014 picker trigger button size
838
+ - \`placeholder?: string\` \u2014 placeholder text when no icon selected
839
+
840
+ **IconDisplayProps:**
841
+ - \`icon: IconValue | null\` \u2014 icon to render (null renders nothing)
842
+ - \`size?: number\` \u2014 pixel size
843
+ - \`className?: string\`
844
+
845
+ **IconValue type:**
846
+ \`\`\`typescript
847
+ type IconSource = "lucide" | "emoji" | "emote" | "upload";
848
+
849
+ interface IconValue {
850
+ type: IconSource; // which tab/source the icon came from
851
+ value: string; // icon name (lucide), Unicode char (emoji), CDN URL (emote), upload URL (upload)
852
+ label?: string; // human-readable label
853
+ platform?: string; // platform identifier for emotes (e.g. "twitch", "7tv")
854
+ }
855
+ \`\`\`
856
+
857
+ **\`useIconPicker()\` hook** \u2014 headless state management for custom picker UIs:
858
+ \`\`\`tsx
859
+ import { useIconPicker } from "@zaflun/lumio-sdk";
860
+
861
+ const { isOpen, selected, open, close, select, clear, sources } = useIconPicker({
862
+ sources: ["lucide", "emoji"],
863
+ onSelect: (icon) => console.log("Selected:", icon),
864
+ });
865
+ \`\`\`
866
+
867
+ Returns: \`{ isOpen, selected, open, close, select, clear, sources }\`
868
+
869
+ **Upload tab** requires the extension to declare \`egress.allowHosts\` including the Lumio API. Uploaded files are stored per-extension with quota tracking via the \`extension_uploads\` table.
870
+
753
871
  ## VisionStyle
754
872
 
755
873
  Typed CSS subset accepted by all components. Sanitized by the host \u2014 only safe properties are allowed.
@@ -946,49 +1064,126 @@ import { useLumioAction } from "@zaflun/lumio-sdk";
946
1064
 
947
1065
  // Send a chat message on the connected platform
948
1066
  const { execute: sendChat, isLoading, error } = useLumioAction("chat:send");
949
- sendChat({ message: "Hello from the extension!" });
1067
+ sendChat({
1068
+ platform: "twitch",
1069
+ channel_id: "primary",
1070
+ message: "Hello from the extension!"
1071
+ });
950
1072
 
951
1073
  // Switch OBS scene (requires OBS WebSocket integration)
952
1074
  const { execute: setScene } = useLumioAction("obs:set_scene");
953
- setScene({ sceneName: "Main Scene" });
1075
+ setScene({ scene_name: "Main Scene" });
954
1076
 
955
1077
  // Emit a custom event into the Lumio event bus
956
1078
  const { execute: emitEvent } = useLumioAction("events:emit");
957
- emitEvent({ type: "extension:custom", payload: { score: 42 } });
1079
+ emitEvent({ event_type: "extension:custom", data: { score: 42 } });
958
1080
 
959
1081
  // Update a specific layer in the overlay
960
1082
  const { execute: updateLayer } = useLumioAction("overlay:update_layer");
961
- updateLayer({ layerId: "uuid", visible: true });
1083
+ updateLayer({
1084
+ overlay_id: "overlay-uuid",
1085
+ layer_id: "layer-uuid",
1086
+ config: { visible: true }
1087
+ });
962
1088
  \`\`\`
963
1089
 
964
- **Available actions (21 total, \`action\` is \`LumioActionType\`):**
1090
+ **Available dispatched actions (\`action\` is \`LumioActionType\`):**
965
1091
 
966
1092
  | Action | Permission required | Description |
967
1093
  |--------|--------------------|----|
968
1094
  | \`chat:send\` | \`chat:send\` | Send chat message on connected platform |
969
1095
  | \`chat:delete\` | \`chat:delete\` | Delete a chat message |
970
- | \`chat:ban\` | \`chat:ban\` | Ban a user from chat |
971
- | \`chat:unban\` | \`chat:ban\` | Unban a user from chat |
972
- | \`chat:timeout\` | \`chat:ban\` | Timeout a user from chat |
973
- | \`obs:set_scene\` | \`obs:control\` | Switch OBS scene |
974
- | \`obs:set_source_visible\` | \`obs:control\` | Show or hide an OBS source |
975
- | \`obs:start_stream\` | \`obs:control\` | Start OBS streaming |
976
- | \`obs:stop_stream\` | \`obs:control\` | Stop OBS streaming |
977
- | \`obs:start_recording\` | \`obs:control\` | Start OBS recording |
978
- | \`obs:stop_recording\` | \`obs:control\` | Stop OBS recording |
979
- | \`events:emit\` | \`events:write\` | Emit event into Lumio event bus |
980
- | \`overlay:update_layer\` | \`overlays:edit\` | Update overlay layer properties |
981
- | \`spotify:play\` | \`spotify:control\` | Resume Spotify playback |
982
- | \`spotify:pause\` | \`spotify:control\` | Pause Spotify playback |
983
- | \`spotify:skip\` | \`spotify:control\` | Skip to next track |
984
- | \`spotify:seek\` | \`spotify:control\` | Seek to position in current track |
985
- | \`spotify:volume\` | \`spotify:control\` | Set Spotify volume |
986
- | \`spotify:shuffle\` | \`spotify:control\` | Toggle shuffle mode |
987
- | \`spotify:repeat\` | \`spotify:control\` | Set repeat mode |
988
- | \`spotify:queue_add\` | \`spotify:control\` | Add track to Spotify queue |
1096
+ | \`obs:set_scene\` | \`obs:set_scene\` | Switch OBS scene |
1097
+ | \`obs:set_source_visible\` | \`obs:set_source_visible\` | Show or hide an OBS source |
1098
+ | \`obs:start_stream\` | \`obs:start_stream\` | Start OBS streaming |
1099
+ | \`obs:stop_stream\` | \`obs:stop_stream\` | Stop OBS streaming |
1100
+ | \`events:emit\` | \`events:emit\` | Emit event into Lumio event bus |
1101
+ | \`overlay:update_layer\` | \`overlay:update_layer\` | Update overlay layer properties |
989
1102
 
990
1103
  Actions are proxied through the Lumio API \u2014 the extension never accesses OAuth tokens or platform credentials directly.
991
1104
 
1105
+ ## Automation Node Handlers
1106
+
1107
+ Extensions with category \`automation_node\` export a single handler in \`server/handler.ts\`. The handler type depends on the \`node_type\` declared in \`lumio.config.json\`.
1108
+
1109
+ ### \`defineAutomationAction(handler)\`
1110
+
1111
+ Action nodes execute when triggered by an upstream node in the automation workflow. They receive input data from the previous node and can perform side effects (HTTP calls, DB writes).
1112
+
1113
+ \`\`\`typescript
1114
+ import { defineAutomationAction } from "@zaflun/lumio-sdk/server";
1115
+
1116
+ export default defineAutomationAction({
1117
+ handler: async (ctx) => {
1118
+ const webhookUrl = ctx.nodeConfig.webhook_url;
1119
+ const res = await ctx.fetch(webhookUrl, {
1120
+ method: "POST",
1121
+ headers: { "Content-Type": "application/json" },
1122
+ body: JSON.stringify(ctx.inputData),
1123
+ });
1124
+ return { status: res.status, body: await res.json() };
1125
+ },
1126
+ });
1127
+ \`\`\`
1128
+
1129
+ ### \`defineAutomationTrigger({ onWebhook?, onPoll? })\`
1130
+
1131
+ Trigger nodes start automation workflows. They support two modes: webhook (receives external HTTP calls) and poll (periodic checks for new data).
1132
+
1133
+ \`\`\`typescript
1134
+ import { defineAutomationTrigger } from "@zaflun/lumio-sdk/server";
1135
+
1136
+ export default defineAutomationTrigger({
1137
+ onWebhook: async (ctx, request) => {
1138
+ const body = await request.json();
1139
+ if (body.event === "order.created") {
1140
+ return { trigger: true, outputData: { orderId: body.id, total: body.total } };
1141
+ }
1142
+ return { trigger: false };
1143
+ },
1144
+ onPoll: async (ctx) => {
1145
+ const lastCheck = await ctx.cache.get("last_check") ?? "1970-01-01T00:00:00Z";
1146
+ const res = await ctx.fetch(
1147
+ \`https://api.example.com/events?since=\${lastCheck}\`,
1148
+ { headers: { "Authorization": \`Bearer \${ctx.secrets.get("API_KEY")}\` } }
1149
+ );
1150
+ const events = await res.json();
1151
+ await ctx.cache.set("last_check", new Date().toISOString());
1152
+ return events.map((e: any) => ({ trigger: true, outputData: e }));
1153
+ },
1154
+ });
1155
+ \`\`\`
1156
+
1157
+ ### \`defineAutomationLogic(handler)\`
1158
+
1159
+ Logic nodes evaluate conditions and route the workflow to different branches. They return a branch key that determines which downstream path executes.
1160
+
1161
+ \`\`\`typescript
1162
+ import { defineAutomationLogic } from "@zaflun/lumio-sdk/server";
1163
+
1164
+ export default defineAutomationLogic({
1165
+ handler: async (ctx) => {
1166
+ const amount = ctx.inputData.total;
1167
+ if (amount >= 100) return { branch: "high_value" };
1168
+ if (amount >= 25) return { branch: "medium_value" };
1169
+ return { branch: "low_value" };
1170
+ },
1171
+ });
1172
+ \`\`\`
1173
+
1174
+ ### Automation Handler Context (\`ctx\`)
1175
+
1176
+ | Property | Available in | Description |
1177
+ |----------|-------------|-------------|
1178
+ | \`ctx.nodeConfig\` | All | Node configuration from the user's config panel |
1179
+ | \`ctx.inputData\` | Action, Logic | Data from the upstream node |
1180
+ | \`ctx.fetch(url, options?)\` | All | HTTP request (egress allowlist enforced) |
1181
+ | \`ctx.cache.get(key)\` / \`ctx.cache.set(key, value, ttl?)\` | All | Per-installation KV cache (Redis, 1h default TTL) |
1182
+ | \`ctx.secrets.get(key)\` | All | Server-side secret |
1183
+ | \`ctx.db\` | All | Extension DB schema access |
1184
+ | \`ctx.auth\` | All | \`{ accountId }\` \u2014 account identity |
1185
+ | \`ctx.defer()\` | Action | Mark execution as async (returns 202, completes in background) |
1186
+
992
1187
  ## Bot Module Handlers
993
1188
 
994
1189
  Extensions with category \`bot_module\` export handler wrappers that register triggers. Each wrapper defines when the handler fires and what context it receives.
@@ -1087,6 +1282,37 @@ export const toxicFilter = moderate({
1087
1282
  });
1088
1283
  \`\`\`
1089
1284
 
1285
+ ### \`defineModerate(handler)\`
1286
+
1287
+ Convenience wrapper for system-style moderation handlers. The handler receives a unified context with the chat message and returns a moderation action.
1288
+
1289
+ \`\`\`typescript
1290
+ import { defineModerate } from "@zaflun/lumio-sdk/server";
1291
+
1292
+ export default defineModerate(async (ctx) => {
1293
+ if (ctx.message.text.includes("spam")) {
1294
+ return { action: "delete" };
1295
+ }
1296
+ return { action: "allow" };
1297
+ });
1298
+ \`\`\`
1299
+
1300
+ ### \`defineTimer(handler)\`
1301
+
1302
+ Convenience wrapper for timer-based handlers (e.g., timed messages). Fires on a recurring interval configured in the extension's install config.
1303
+
1304
+ \`\`\`typescript
1305
+ import { defineTimer } from "@zaflun/lumio-sdk/server";
1306
+
1307
+ export default defineTimer(async (ctx) => {
1308
+ const messages = (ctx.installConfig.messages as string || "").split("\\n").filter(Boolean);
1309
+ if (messages.length === 0) return null;
1310
+ const idx = parseInt(await ctx.cache.get("idx") || "0", 10);
1311
+ await ctx.cache.set("idx", String((idx + 1) % messages.length));
1312
+ return { reply: messages[idx] };
1313
+ });
1314
+ \`\`\`
1315
+
1090
1316
  ### Handler Return Actions
1091
1317
 
1092
1318
  All bot module handlers return an action object:
@@ -1110,7 +1336,7 @@ The extension must declare the matching permission in \`lumio.config.json\`. For
1110
1336
  | \`chat:delete\` | Delete chat messages | Enhanced |
1111
1337
  | \`chat:ban\` | Ban, unban, and timeout users | Enhanced |
1112
1338
  | \`events:read\` | Subscribe to platform events | Standard |
1113
- | \`events:write\` | Emit custom events | Standard |
1339
+ | \`events:emit\` | Emit custom events | Standard |
1114
1340
  `;
1115
1341
 
1116
1342
  // src/content/cli-workflows.ts
@@ -1170,15 +1396,14 @@ lumio init my-widget
1170
1396
  # ? Category: widget
1171
1397
  # ? Targets: editor, layer
1172
1398
  # ? Server functions: yes
1173
- # ? Template: Widget (editor + layer + storage)
1399
+ # ? How should the editor UI be built? config_schema only / editor.tsx only / Both
1174
1400
  # ? Owner: My Stream (personal) / Awesome Team <- team selection if applicable
1175
1401
  #
1176
1402
  # Creating extension on Lumio...
1177
1403
  # Extension-ID: 88b2478a-39c7-468e-bebd-14e52fd97ac0
1178
1404
  # Slug: k8f2x-my-widget
1179
1405
  #
1180
- # Writing lumio.config.json...
1181
- # Writing src/editor.tsx...
1406
+ # Writing lumio.config.json (with config_schema)...
1182
1407
  # Writing src/layer.tsx...
1183
1408
  # Writing server/schema.ts...
1184
1409
  # Writing server/functions.ts...
@@ -1186,6 +1411,32 @@ lumio init my-widget
1186
1411
  # Done! Run \`lumio dev\` to start developing.
1187
1412
  \`\`\`
1188
1413
 
1414
+ ### Automation Node Example
1415
+
1416
+ \`\`\`bash
1417
+ lumio init my-webhook-action
1418
+
1419
+ # Interactive prompts:
1420
+ # ? Extension name: My Webhook Action
1421
+ # ? Description: Posts data to a webhook
1422
+ # ? Category: automation_node
1423
+ # ? Node type: action
1424
+ # ? Server functions: yes (required for automation nodes)
1425
+ # ? Owner: My Stream (personal) / Awesome Team
1426
+ #
1427
+ # Creating extension on Lumio...
1428
+ # Extension-ID: a1b2c3d4-...
1429
+ # Slug: x9k1z-my-webhook-action
1430
+ #
1431
+ # Writing lumio.config.json...
1432
+ # Writing src/editor.tsx...
1433
+ # Writing server/handler.ts...
1434
+ #
1435
+ # Done! Run \`lumio dev\` to start developing.
1436
+ \`\`\`
1437
+
1438
+ Automation nodes always generate \`server/handler.ts\` (not \`server/functions.ts\`) and \`src/editor.tsx\` for the config panel. The \`node_type\` can be \`action\`, \`trigger\`, or \`logic\`.
1439
+
1189
1440
  ### Templates
1190
1441
 
1191
1442
  | Template | Targets | Server | Description |
@@ -1195,6 +1446,9 @@ lumio init my-widget
1195
1446
  | Full-Stack | editor + layer | Yes | Complete extension with server functions |
1196
1447
  | Interactive Game | editor + layer + interactive | Yes | Viewer interaction game |
1197
1448
  | Bot Module | server-only | Yes | Chat command module |
1449
+ | Automation Action | server + editor | Yes | Automation workflow action node |
1450
+ | Automation Trigger | server + editor | Yes | Automation workflow trigger node |
1451
+ | Automation Logic | server + editor | Yes | Automation workflow logic/branching node |
1198
1452
  | Integration | server | Yes | External API integration |
1199
1453
  | Theme | editor | No | Style variables only |
1200
1454
 
@@ -1215,27 +1469,59 @@ lumio dev
1215
1469
  # Press 'q' to quit.
1216
1470
  \`\`\`
1217
1471
 
1472
+ ### Automation Node Development
1473
+
1474
+ For automation node extensions, the dev server auto-detects \`category: "automation_node"\` and launches the automation simulator:
1475
+
1476
+ \`\`\`bash
1477
+ lumio dev
1478
+ # or explicitly:
1479
+ lumio dev --automation-node
1480
+
1481
+ # Output:
1482
+ # Lumio Dev Server running at http://localhost:5173
1483
+ # Config Panel: http://localhost:5173/editor
1484
+ #
1485
+ # Automation Simulator ready. Commands:
1486
+ # /execute [JSON] \u2014 Run the action handler with input data
1487
+ # /webhook [JSON] \u2014 Simulate an incoming webhook (trigger nodes)
1488
+ # /poll \u2014 Trigger a poll cycle (trigger nodes)
1489
+ # /config [JSON] \u2014 Update node config
1490
+ # /quit \u2014 Stop the dev server
1491
+ \`\`\`
1492
+
1218
1493
  Features:
1219
1494
  - Hot reload on file changes
1220
1495
  - Mock event system (simulated chat, follows, subs)
1221
1496
  - Sandbox environment matching production constraints
1222
1497
  - Server function execution with local DB
1498
+ - Automation simulator with /execute, /webhook, /poll, /config commands
1223
1499
 
1224
1500
  ## lumio build
1225
1501
 
1226
- Bundle the extension as standalone HTML and create source archive.
1502
+ Bundle the extension and create source archive.
1227
1503
 
1228
1504
  \`\`\`bash
1229
1505
  lumio build
1230
1506
 
1231
- # Output:
1232
- # Building extension...
1233
- # Bundling editor.tsx -> dist/editor.html (42 KB)
1234
- # Bundling layer.tsx -> dist/layer.html (38 KB)
1235
- # Creating source archive -> dist/source.tar.gz
1236
- # Build complete!
1507
+ # Output for widgets:
1508
+ # Built: dist/editor.js
1509
+ # Built: dist/layer.js
1510
+ # Compiled: dist/styles.css (if Tailwind detected)
1511
+ # Archive: dist/source.tar.gz
1512
+ #
1513
+ # Output for bot modules (server: true):
1514
+ # Bot module \u2014 no UI targets to bundle.
1515
+ # Built: dist/server.js
1516
+ # Archive: dist/source.tar.gz
1237
1517
  \`\`\`
1238
1518
 
1519
+ Build steps:
1520
+ 1. UI targets (\`editor\`, \`layer\`, \`interactive\`, \`designer\`) \u2014 esbuild IIFE bundles
1521
+ 2. Server handler (\`server: true\`) \u2014 compiles \`server/handler.ts\` to \`dist/server.js\` (ESM)
1522
+ 3. CSS \u2014 Tailwind v4 auto-detected and compiled, or plain CSS copied
1523
+ 4. Source archive \u2014 \`dist/source.tar.gz\`
1524
+
1239
1525
  Warns if \`CHANGELOG.md\` is missing.
1240
1526
 
1241
1527
  ## lumio deploy
@@ -1259,6 +1545,11 @@ lumio deploy --breaking -v 2.0.0
1259
1545
  lumio deploy --draft
1260
1546
  \`\`\`
1261
1547
 
1548
+ Pre-flight checks before upload:
1549
+ - Sound files validated (format, existence in \`dist/sounds/\`, per-file size against limit)
1550
+ - Sound count, per-file size, and total storage checked against resolved limits from API
1551
+ - Manifest validated against schema
1552
+
1262
1553
  After deploy:
1263
1554
  1. Version status = \`draft\`
1264
1555
  2. Submit for review via dashboard (\`/dashboard/extensions/{slug}/submit\`)
@@ -1427,7 +1718,7 @@ AuthContext::ExtensionToken {
1427
1718
  ### Token Lifecycle
1428
1719
 
1429
1720
  1. User opens extension in dashboard/overlay \u2014 Lumio server generates \`lm_ext_*\` token
1430
- 2. Token passed to Extension Runtime iframe: \`ext.lumio.vision/ext/{slug}/layer?token=lm_ext_xxx&install_id=uuid\` where \`slug\` includes the \`short_id\` prefix (e.g. \`k8f2x-my-extension\`); the store URL is \`/store/k8f2x-my-extension\`
1721
+ 2. Token handed to the extension inside the sandbox; the Extension Runtime iframe runs on the extension's own origin \`<label>.runtime.ext.lumio.vision\` (\`<label>\` = extension UUID as 32 lowercase hex). The extension \`slug\` includes the \`short_id\` prefix (e.g. \`k8f2x-my-extension\`); the store URL is \`/store/k8f2x-my-extension\`
1431
1722
  3. SDK uses token for all API calls (server functions, storage, events)
1432
1723
  4. Token auto-refreshes via postMessage every 12 minutes (before 15-min expiry) \u2014 the Supervisor mediates the refresh
1433
1724
  5. On page reload, a new token is generated
@@ -1504,11 +1795,11 @@ Extensions run inside a 3-layer iframe sandbox. Understanding the layers is impo
1504
1795
 
1505
1796
  \`\`\`
1506
1797
  Host (lumio.vision / browser source)
1507
- \u2514\u2500\u2500 Supervisor iframe (supervisor.extension.lumio.vision)
1798
+ \u2514\u2500\u2500 Supervisor iframe (supervisor.ext.lumio.vision)
1508
1799
  Purpose: validates postMessage traffic, enforces egress allowlist,
1509
1800
  mediates token refresh, proxies LumioComponentNode[] to host
1510
- \u2514\u2500\u2500 Extension Runtime iframe (ext.lumio.vision)
1511
- Purpose: runs extension code on a separate origin
1801
+ \u2514\u2500\u2500 Extension Runtime iframe (<label>.runtime.ext.lumio.vision)
1802
+ Purpose: runs extension code on its own origin (one per extension)
1512
1803
  Storage: localStorage/sessionStorage BLOCKED (cross-origin isolation)
1513
1804
  \u2514\u2500\u2500 Web Worker
1514
1805
  Purpose: V8 isolate for server function execution
@@ -1521,8 +1812,9 @@ Host (lumio.vision / browser source)
1521
1812
  - Mediates \`lm_ext_*\` token refresh (12 min interval, before 15 min expiry)
1522
1813
  - Forwards serialized \`LumioComponentNode[]\` to the Host renderer
1523
1814
 
1524
- **Extension Runtime (ext.lumio.vision) origin isolation:**
1815
+ **Extension Runtime (<label>.runtime.ext.lumio.vision) origin isolation:**
1525
1816
  - Runs on a separate origin from the Host \u2014 this prevents direct DOM access and localStorage sharing
1817
+ - Every extension gets its own origin (\`<label>\` = extension UUID as 32 lowercase hex), so two extensions never share browser state
1526
1818
  - \`localStorage\` and \`sessionStorage\` throw \`DOMException: Access is denied\` \u2014 use \`useExtensionStorage()\` instead
1527
1819
  - \`document.cookie\` is unavailable
1528
1820
  - Cross-origin postMessage to the Host goes through the Supervisor
@@ -1697,22 +1989,46 @@ Lumio.render(<Overlay />, { target: "layer" });
1697
1989
 
1698
1990
  ## Config Schema
1699
1991
 
1700
- Use \`config_schema\` in \`lumio.config.json\` for user-customizable settings. The Lumio dashboard renders these as a form in the Widget Gallery install flow \u2014 the user fills them in without needing to open the editor surface.
1992
+ Use \`config_schema\` in \`lumio.config.json\` for user-customizable settings. The widget editor auto-generates form fields from the schema \u2014 no custom \`editor.tsx\` needed for simple settings. For advanced UIs, use \`editor.tsx\` alongside (or instead of) config_schema.
1993
+
1994
+ **Three valid combos:**
1995
+ - **config_schema only** \u2014 auto-generated editor form, \`targets: ["layer"]\`
1996
+ - **editor.tsx only** \u2014 fully custom React editor UI, \`targets: ["layer", "editor"]\`
1997
+ - **Both** \u2014 schema fields render first, editor.tsx renders below for advanced controls
1998
+
1999
+ **Supported field types:** \`string\`, \`number\`, \`boolean\`, \`color\`, \`select\`, \`textarea\`, \`slider\`, \`font\`, \`icon\`, \`image\`, \`sound\`, \`multiselect\`, \`group\`, \`divider\`, \`info\`, \`channel\`, \`designer\`, \`presets\`
1701
2000
 
1702
2001
  \`\`\`json
1703
2002
  {
1704
2003
  "config_schema": [
1705
- { "key": "title", "type": "string", "label": "Widget Title", "default_value": "My Widget", "required": true },
1706
- { "key": "maxItems", "type": "number", "label": "Max Items", "default_value": 10, "required": false },
2004
+ { "key": "title", "type": "string", "label": "Widget Title", "default_value": "My Widget", "required": true, "placeholder": "Enter title..." },
2005
+ { "key": "fontSize", "type": "number", "label": "Font Size", "default_value": 16, "required": false, "min": 8, "max": 200, "unit": "px" },
2006
+ { "key": "volume", "type": "slider", "label": "Volume", "default_value": 50, "required": false, "min": 0, "max": 100, "unit": "%" },
2007
+ { "key": "theme", "type": "select", "label": "Color Theme", "options": ["dark", "light"], "default_value": "dark", "required": false },
2008
+ { "key": "primaryColor", "type": "color", "label": "Primary Color", "default_value": "#43F7CD", "required": false },
1707
2009
  { "key": "showBorder", "type": "boolean", "label": "Show Border", "default_value": true, "required": false },
2010
+ { "key": "targetDate", "type": "string", "label": "Target Date", "default_value": "", "required": false, "visible_when": { "key": "mode", "equals": "countdown" } },
1708
2011
  {
1709
- "key": "theme",
1710
- "type": "select",
1711
- "label": "Color Theme",
1712
- "options": ["dark", "light"],
1713
- "default_value": "dark",
1714
- "required": false
1715
- }
2012
+ "key": "twitch_events", "type": "group", "label": "Twitch Events", "required": false, "collapsed": true,
2013
+ "fields": [
2014
+ { "key": "includeFollowers", "type": "boolean", "label": "Followers", "default_value": true, "required": false },
2015
+ { "key": "includeSubs", "type": "boolean", "label": "Subscriptions", "default_value": true, "required": false }
2016
+ ]
2017
+ },
2018
+ { "key": "separator", "type": "divider", "label": "", "required": false },
2019
+ { "key": "help_text", "type": "info", "label": "", "required": false, "content": "Configure the events you want to display." }
2020
+ ]
2021
+ }
2022
+ \`\`\`
2023
+
2024
+ **Designer Schema:** Extensions can define a \`designer_schema\` alongside \`config_schema\` for a fullpage visual design editor. The designer opens as a fullpage overlay with the schema fields on the left and a live preview on the right. Designer values are stored under \`config.design\`.
2025
+
2026
+ \`\`\`json
2027
+ {
2028
+ "designer_schema": [
2029
+ { "key": "bubbleColor", "type": "color", "label": "Chat Bubble Color", "default_value": "#9146FF", "required": false },
2030
+ { "key": "nameFont", "type": "font", "label": "Username Font", "default_value": "Inter", "required": false },
2031
+ { "key": "badgeSize", "type": "slider", "label": "Badge Size", "default_value": 18, "required": false, "min": 12, "max": 32, "unit": "px" }
1716
2032
  ]
1717
2033
  }
1718
2034
  \`\`\`
@@ -1722,7 +2038,7 @@ Read config values via \`useLumioConfig()\`:
1722
2038
  \`\`\`tsx
1723
2039
  const [config] = useLumioConfig();
1724
2040
  const title = config.title ?? "My Widget";
1725
- const maxItems = config.maxItems ?? 10;
2041
+ const fontSize = config.fontSize ?? 16;
1726
2042
  \`\`\`
1727
2043
 
1728
2044
  **When to use config_schema vs storage:**
@@ -1770,7 +2086,7 @@ For beta testing before public launch:
1770
2086
  | Large inline images | Use URL-based images via \`Image\` component |
1771
2087
  | Missing \`Lumio.render()\` | Must be called within 10s of page load |
1772
2088
  | Hardcoded strings | Use \`config_schema\` fields for user-customizable text |
1773
- | Using localStorage directly | Use \`useExtensionStorage()\` (localStorage blocked at ext.lumio.vision) |
2089
+ | Using localStorage directly | Use \`useExtensionStorage()\` (localStorage blocked on the Extension Runtime origin) |
1774
2090
  | Raw CSS \`@keyframes\` animations | Use \`useAnimation()\` + \`<Animated>\` SDK presets |
1775
2091
 
1776
2092
  ## Bot Module Security
@@ -1914,7 +2230,7 @@ var troubleshootingContent = `# Troubleshooting
1914
2230
  | \`BUNDLE_TOO_LARGE\` | Bundle exceeds size limit | Built bundle exceeds \`max_bundle_size_mb\` | Reduce bundle size or request limit increase |
1915
2231
  | \`SUPERVISOR_BLOCKED\` | Supervisor rejected postMessage | Message origin or schema invalid | Ensure you are in the correct iframe layer; do not post to Host directly |
1916
2232
  | \`WORKER_CREATION_FAILED\` | Web Worker could not start | Server functions bundle is invalid or too large | Check for syntax errors in server functions; run \`lumio build\` locally |
1917
- | \`STORAGE_DOM_EXCEPTION\` | localStorage/sessionStorage access attempt | Extension Runtime (ext.lumio.vision) blocks storage APIs | Replace \`localStorage.setItem\` with \`useExtensionStorage()\` |
2233
+ | \`STORAGE_DOM_EXCEPTION\` | localStorage/sessionStorage access attempt | Extension Runtime (<label>.runtime.ext.lumio.vision) blocks storage APIs | Replace \`localStorage.setItem\` with \`useExtensionStorage()\` |
1918
2234
  | \`SLUG_FORMAT_INVALID\` | Slug does not match short-ID format | Manually edited slug in \`lumio.config.json\` | Use auto-generated slug (\`k8f2x-my-extension\` format) or run \`lumio init\` |
1919
2235
 
1920
2236
  ## Common Issues
@@ -1952,7 +2268,7 @@ The SDK auto-refreshes \`lm_ext_*\` tokens via postMessage. The Supervisor media
1952
2268
  ### Supervisor blocked messages (\`SUPERVISOR_BLOCKED\`)
1953
2269
  The Supervisor validates all postMessage traffic between Extension Runtime and Host. If you see this error:
1954
2270
  1. Do not post messages directly from extension code to \`window.parent.parent\` (Host) \u2014 always use SDK hooks
1955
- 2. Verify the extension iframe is loaded inside \`supervisor.extension.lumio.vision\`, not directly in the Host
2271
+ 2. Verify the extension iframe is loaded inside \`supervisor.ext.lumio.vision\`, not directly in the Host
1956
2272
  3. Check that the message schema matches the \`@zaflun/lumio-protocol\` wire format (SDK handles this automatically)
1957
2273
 
1958
2274
  ### Worker creation failures (\`WORKER_CREATION_FAILED\`)
@@ -1962,7 +2278,7 @@ The V8 isolate Web Worker for server functions failed to start:
1962
2278
  3. Check \`lumio logs\` for the underlying JS error from the Worker initializer
1963
2279
 
1964
2280
  ### Storage DOMException errors (\`STORAGE_DOM_EXCEPTION\`)
1965
- \`localStorage\` and \`sessionStorage\` are blocked on the Extension Runtime origin (\`ext.lumio.vision\`):
2281
+ \`localStorage\` and \`sessionStorage\` are blocked on the Extension Runtime origin (\`<label>.runtime.ext.lumio.vision\`):
1966
2282
  1. Replace all \`localStorage.setItem\` / \`localStorage.getItem\` calls with \`useExtensionStorage()\`
1967
2283
  2. Replace all \`sessionStorage\` usage with \`useVisionState()\` for transient values
1968
2284
  3. For server-side persistence, use \`ctx.db\` in server functions
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  ROOT_FILES,
3
3
  SKILL_FILES,
4
4
  installSkillFiles
5
- } from "./chunk-SSYJXSJX.js";
5
+ } from "./chunk-TKNV5K6F.js";
6
6
  export {
7
7
  ROOT_FILES,
8
8
  SKILL_FILES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zaflun/lumio-agent-skill",
3
- "version": "0.4.0",
3
+ "version": "0.5.3",
4
4
  "description": "AI agent skill files for building Lumio Extensions",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -27,10 +27,10 @@
27
27
  },
28
28
  "license": "MIT",
29
29
  "devDependencies": {
30
- "@types/node": "^22.0.0",
30
+ "@types/node": "^26.1.2",
31
31
  "tsup": "^8.4.0",
32
- "typescript": "^5.8.3",
33
- "vitest": "^3.1.4"
32
+ "typescript": "^6.0.3",
33
+ "vitest": "^4.1.10"
34
34
  },
35
35
  "repository": {
36
36
  "type": "git",