@sirenodeck/sirenodeck 0.1.1-alpha.0

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 (485) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +69 -0
  3. package/bin/_wrapper-shared.js +42 -0
  4. package/bin/dev.js +95 -0
  5. package/bin/sirenodeck.js +42 -0
  6. package/config/default-config.yml +117 -0
  7. package/config/starter-config.yml +54 -0
  8. package/dist/main.mjs +64702 -0
  9. package/package.json +140 -0
  10. package/src/action/executor.ts +156 -0
  11. package/src/action/index.ts +7 -0
  12. package/src/addon/api-types.ts +15 -0
  13. package/src/addon/api.ts +461 -0
  14. package/src/addon/check-runner.ts +84 -0
  15. package/src/addon/index.ts +5 -0
  16. package/src/addon/loader.ts +441 -0
  17. package/src/addon/registry.ts +230 -0
  18. package/src/addon/spec.ts +69 -0
  19. package/src/api/addon.ts +33 -0
  20. package/src/api/asset-url.ts +24 -0
  21. package/src/api/index.ts +50 -0
  22. package/src/api/protocol-internal.ts +656 -0
  23. package/src/api/protocol.ts +2 -0
  24. package/src/api/react/index.ts +11 -0
  25. package/src/api/react/registry.ts +91 -0
  26. package/src/api/react/use-addon-channel.ts +26 -0
  27. package/src/api/react/use-button-state.tsx +23 -0
  28. package/src/api/react/use-deck.ts +24 -0
  29. package/src/assets/logo.png +0 -0
  30. package/src/assets/logo72x72.png +0 -0
  31. package/src/assets/logoFull.png +0 -0
  32. package/src/builtin-addons/brightness/README.md +51 -0
  33. package/src/builtin-addons/brightness/buttons/brightness/backend.ts +13 -0
  34. package/src/builtin-addons/brightness/buttons/brightness/config.ts +10 -0
  35. package/src/builtin-addons/brightness/buttons/brightness/frontend.tsx +50 -0
  36. package/src/builtin-addons/brightness/domain/macos.ts +46 -0
  37. package/src/builtin-addons/brightness/index.d.ts +5 -0
  38. package/src/builtin-addons/brightness/index.ts +20 -0
  39. package/src/builtin-addons/brightness/poller.ts +27 -0
  40. package/src/builtin-addons/brightness/providers/darwin.ts +107 -0
  41. package/src/builtin-addons/brightness/providers/index.ts +81 -0
  42. package/src/builtin-addons/brightness/providers/linux.ts +108 -0
  43. package/src/builtin-addons/brightness/providers/windows.ts +77 -0
  44. package/src/builtin-addons/brightness/sirenodeck.json +6 -0
  45. package/src/builtin-addons/coding-agents/assets/claude-code.svg +1 -0
  46. package/src/builtin-addons/coding-agents/assets/opencode-dark-square.svg +1 -0
  47. package/src/builtin-addons/coding-agents/buttons/agent/backend.ts +82 -0
  48. package/src/builtin-addons/coding-agents/buttons/agent/config.ts +13 -0
  49. package/src/builtin-addons/coding-agents/buttons/agent/frontend.tsx +130 -0
  50. package/src/builtin-addons/coding-agents/buttons/agent/pages.tsx +150 -0
  51. package/src/builtin-addons/coding-agents/buttons/summary/backend.ts +48 -0
  52. package/src/builtin-addons/coding-agents/buttons/summary/config.ts +11 -0
  53. package/src/builtin-addons/coding-agents/buttons/summary/frontend.tsx +254 -0
  54. package/src/builtin-addons/coding-agents/decks/agents.ts +69 -0
  55. package/src/builtin-addons/coding-agents/global/backend.ts +402 -0
  56. package/src/builtin-addons/coding-agents/global-entry.ts +7 -0
  57. package/src/builtin-addons/coding-agents/hook/script.ts +123 -0
  58. package/src/builtin-addons/coding-agents/index.ts +15 -0
  59. package/src/builtin-addons/coding-agents/manifest.ts +115 -0
  60. package/src/builtin-addons/coding-agents/providers/claude-code.ts +470 -0
  61. package/src/builtin-addons/coding-agents/providers/claude-instances.ts +184 -0
  62. package/src/builtin-addons/coding-agents/providers/opencode-instances.ts +108 -0
  63. package/src/builtin-addons/coding-agents/providers/opencode.ts +636 -0
  64. package/src/builtin-addons/coding-agents/providers/registry.ts +120 -0
  65. package/src/builtin-addons/coding-agents/providers/spawn.ts +112 -0
  66. package/src/builtin-addons/coding-agents/shared/claude-hook-events.ts +55 -0
  67. package/src/builtin-addons/coding-agents/shared/claude-status.ts +236 -0
  68. package/src/builtin-addons/coding-agents/shared/live-count.ts +39 -0
  69. package/src/builtin-addons/coding-agents/shared/notifier.ts +58 -0
  70. package/src/builtin-addons/coding-agents/shared/opencode-status.ts +82 -0
  71. package/src/builtin-addons/coding-agents/shared/snapshot.ts +66 -0
  72. package/src/builtin-addons/coding-agents/shared/state.ts +72 -0
  73. package/src/builtin-addons/coding-agents/sirenodeck.json +6 -0
  74. package/src/builtin-addons/coding-agents/types/types.ts +33 -0
  75. package/src/builtin-addons/core/README.md +89 -0
  76. package/src/builtin-addons/core/buttons/action/backend.ts +7 -0
  77. package/src/builtin-addons/core/buttons/action/config.ts +13 -0
  78. package/src/builtin-addons/core/buttons/action/frontend.tsx +16 -0
  79. package/src/builtin-addons/core/buttons/change-deck/backend.ts +18 -0
  80. package/src/builtin-addons/core/buttons/change-deck/config.ts +15 -0
  81. package/src/builtin-addons/core/buttons/change-deck/frontend.tsx +18 -0
  82. package/src/builtin-addons/core/buttons/media-sample/backend.ts +8 -0
  83. package/src/builtin-addons/core/buttons/media-sample/config.ts +7 -0
  84. package/src/builtin-addons/core/buttons/media-sample/frontend.tsx +2 -0
  85. package/src/builtin-addons/core/buttons/page-nav/backend.ts +28 -0
  86. package/src/builtin-addons/core/buttons/page-nav/config.ts +9 -0
  87. package/src/builtin-addons/core/buttons/page-nav/frontend.tsx +68 -0
  88. package/src/builtin-addons/core/buttons/toggle/backend.ts +119 -0
  89. package/src/builtin-addons/core/buttons/toggle/config.ts +57 -0
  90. package/src/builtin-addons/core/buttons/toggle/frontend.tsx +57 -0
  91. package/src/builtin-addons/core/buttons/toggle/global-service.ts +174 -0
  92. package/src/builtin-addons/core/index.ts +120 -0
  93. package/src/builtin-addons/core/sirenodeck.json +6 -0
  94. package/src/builtin-addons/date-time/README.md +73 -0
  95. package/src/builtin-addons/date-time/buttons/analog-clock/backend.ts +10 -0
  96. package/src/builtin-addons/date-time/buttons/analog-clock/config.ts +4 -0
  97. package/src/builtin-addons/date-time/buttons/analog-clock/frontend.tsx +88 -0
  98. package/src/builtin-addons/date-time/buttons/custom/backend.ts +10 -0
  99. package/src/builtin-addons/date-time/buttons/custom/config.ts +9 -0
  100. package/src/builtin-addons/date-time/buttons/custom/frontend.tsx +26 -0
  101. package/src/builtin-addons/date-time/buttons/date/backend.ts +10 -0
  102. package/src/builtin-addons/date-time/buttons/date/config.ts +10 -0
  103. package/src/builtin-addons/date-time/buttons/date/frontend.tsx +42 -0
  104. package/src/builtin-addons/date-time/buttons/time/backend.ts +10 -0
  105. package/src/builtin-addons/date-time/buttons/time/config.ts +9 -0
  106. package/src/builtin-addons/date-time/buttons/time/frontend.tsx +29 -0
  107. package/src/builtin-addons/date-time/index.d.ts +5 -0
  108. package/src/builtin-addons/date-time/index.ts +30 -0
  109. package/src/builtin-addons/date-time/poller.ts +11 -0
  110. package/src/builtin-addons/date-time/shared/format-date.ts +36 -0
  111. package/src/builtin-addons/date-time/shared/format.ts +105 -0
  112. package/src/builtin-addons/date-time/shared/use-now.ts +10 -0
  113. package/src/builtin-addons/date-time/sirenodeck.json +6 -0
  114. package/src/builtin-addons/emoji-selector/README.md +45 -0
  115. package/src/builtin-addons/emoji-selector/assets/launcher.svg +10 -0
  116. package/src/builtin-addons/emoji-selector/assets/smileys.svg +8 -0
  117. package/src/builtin-addons/emoji-selector/buttons/category/backend.ts +14 -0
  118. package/src/builtin-addons/emoji-selector/buttons/category/config.ts +8 -0
  119. package/src/builtin-addons/emoji-selector/buttons/category/frontend.tsx +19 -0
  120. package/src/builtin-addons/emoji-selector/buttons/emoji/backend.ts +7 -0
  121. package/src/builtin-addons/emoji-selector/buttons/emoji/config.ts +9 -0
  122. package/src/builtin-addons/emoji-selector/buttons/emoji/frontend.tsx +11 -0
  123. package/src/builtin-addons/emoji-selector/buttons/launcher/backend.ts +13 -0
  124. package/src/builtin-addons/emoji-selector/buttons/launcher/config.ts +6 -0
  125. package/src/builtin-addons/emoji-selector/buttons/launcher/frontend.tsx +31 -0
  126. package/src/builtin-addons/emoji-selector/data/categories.ts +1084 -0
  127. package/src/builtin-addons/emoji-selector/decks/index.ts +137 -0
  128. package/src/builtin-addons/emoji-selector/index.d.ts +5 -0
  129. package/src/builtin-addons/emoji-selector/index.ts +29 -0
  130. package/src/builtin-addons/emoji-selector/sirenodeck.json +6 -0
  131. package/src/builtin-addons/emoji-selector/support.ts +49 -0
  132. package/src/builtin-addons/index.ts +13 -0
  133. package/src/builtin-addons/internal-settings/README.md +26 -0
  134. package/src/builtin-addons/internal-settings/backend.ts +58 -0
  135. package/src/builtin-addons/internal-settings/buttons/app-info/backend.ts +7 -0
  136. package/src/builtin-addons/internal-settings/buttons/app-info/config.ts +13 -0
  137. package/src/builtin-addons/internal-settings/buttons/app-info/frontend.tsx +18 -0
  138. package/src/builtin-addons/internal-settings/buttons/brightness/common/frontend.tsx +50 -0
  139. package/src/builtin-addons/internal-settings/buttons/brightness-down/backend.ts +16 -0
  140. package/src/builtin-addons/internal-settings/buttons/brightness-down/config.ts +6 -0
  141. package/src/builtin-addons/internal-settings/buttons/brightness-down/frontend.tsx +8 -0
  142. package/src/builtin-addons/internal-settings/buttons/brightness-up/backend.ts +16 -0
  143. package/src/builtin-addons/internal-settings/buttons/brightness-up/config.ts +6 -0
  144. package/src/builtin-addons/internal-settings/buttons/brightness-up/frontend.tsx +8 -0
  145. package/src/builtin-addons/internal-settings/decks/settings.ts +25 -0
  146. package/src/builtin-addons/internal-settings/index.d.ts +4 -0
  147. package/src/builtin-addons/internal-settings/index.ts +53 -0
  148. package/src/builtin-addons/internal-settings/logo72x72.png +0 -0
  149. package/src/builtin-addons/internal-settings/sirenodeck.json +6 -0
  150. package/src/builtin-addons/media/README.md +45 -0
  151. package/src/builtin-addons/media/backend.ts +143 -0
  152. package/src/builtin-addons/media/buttons/media-player/backend.ts +19 -0
  153. package/src/builtin-addons/media/buttons/media-player/components/MediaStatus.tsx +25 -0
  154. package/src/builtin-addons/media/buttons/media-player/components/MediaSurface.tsx +52 -0
  155. package/src/builtin-addons/media/buttons/media-player/components/ProgressBar.tsx +21 -0
  156. package/src/builtin-addons/media/buttons/media-player/components/status-meta.ts +36 -0
  157. package/src/builtin-addons/media/buttons/media-player/config.ts +4 -0
  158. package/src/builtin-addons/media/buttons/media-player/frontend.tsx +29 -0
  159. package/src/builtin-addons/media/buttons/volume/common/frontend.tsx +52 -0
  160. package/src/builtin-addons/media/buttons/volume/mute/backend.ts +13 -0
  161. package/src/builtin-addons/media/buttons/volume/mute/config.ts +4 -0
  162. package/src/builtin-addons/media/buttons/volume/mute/frontend.tsx +26 -0
  163. package/src/builtin-addons/media/buttons/volume/volume-down/backend.ts +14 -0
  164. package/src/builtin-addons/media/buttons/volume/volume-down/config.ts +6 -0
  165. package/src/builtin-addons/media/buttons/volume/volume-down/frontend.tsx +10 -0
  166. package/src/builtin-addons/media/buttons/volume/volume-up/backend.ts +14 -0
  167. package/src/builtin-addons/media/buttons/volume/volume-up/config.ts +6 -0
  168. package/src/builtin-addons/media/buttons/volume/volume-up/frontend.tsx +8 -0
  169. package/src/builtin-addons/media/checks.ts +104 -0
  170. package/src/builtin-addons/media/executor.ts +36 -0
  171. package/src/builtin-addons/media/index.d.ts +5 -0
  172. package/src/builtin-addons/media/index.ts +53 -0
  173. package/src/builtin-addons/media/progress.ts +16 -0
  174. package/src/builtin-addons/media/providers/darwin.ts +391 -0
  175. package/src/builtin-addons/media/providers/index.ts +27 -0
  176. package/src/builtin-addons/media/providers/linux.ts +196 -0
  177. package/src/builtin-addons/media/providers/types.ts +48 -0
  178. package/src/builtin-addons/media/providers/windows.ts +121 -0
  179. package/src/builtin-addons/media/sirenodeck.json +6 -0
  180. package/src/builtin-addons/media/state.ts +19 -0
  181. package/src/builtin-addons/register-builtins.ts +65 -0
  182. package/src/builtin-addons/session/README.md +14 -0
  183. package/src/builtin-addons/session/buttons/session-info/backend.ts +8 -0
  184. package/src/builtin-addons/session/buttons/session-info/config.ts +4 -0
  185. package/src/builtin-addons/session/buttons/session-info/frontend.tsx +2 -0
  186. package/src/builtin-addons/session/buttons/time/backend.ts +7 -0
  187. package/src/builtin-addons/session/buttons/time/config.ts +6 -0
  188. package/src/builtin-addons/session/buttons/time/frontend.tsx +2 -0
  189. package/src/builtin-addons/session/index.d.ts +5 -0
  190. package/src/builtin-addons/session/index.ts +18 -0
  191. package/src/builtin-addons/session/sirenodeck.json +6 -0
  192. package/src/builtin-addons/system-status/README.md +169 -0
  193. package/src/builtin-addons/system-status/buttons/_shared.ts +131 -0
  194. package/src/builtin-addons/system-status/buttons/system-status/frontend.tsx +224 -0
  195. package/src/builtin-addons/system-status/buttons/system-status/schemas.ts +67 -0
  196. package/src/builtin-addons/system-status/domain/catalog.ts +53 -0
  197. package/src/builtin-addons/system-status/domain/chart-sampler.ts +58 -0
  198. package/src/builtin-addons/system-status/domain/display-metrics.ts +128 -0
  199. package/src/builtin-addons/system-status/domain/index.ts +10 -0
  200. package/src/builtin-addons/system-status/domain/live-metrics.ts +1021 -0
  201. package/src/builtin-addons/system-status/domain/metric-ids.ts +0 -0
  202. package/src/builtin-addons/system-status/index.ts +16 -0
  203. package/src/builtin-addons/system-status/manifest.ts +105 -0
  204. package/src/builtin-addons/system-status/shared/metrics-catalog.ts +316 -0
  205. package/src/builtin-addons/system-status/sirenodeck.json +6 -0
  206. package/src/builtin-addons/test-buildin/decks/action-buttons-test.ts +56 -0
  207. package/src/builtin-addons/test-buildin/index.d.ts +5 -0
  208. package/src/builtin-addons/test-buildin/index.ts +16 -0
  209. package/src/builtin-addons/value-display/README.md +59 -0
  210. package/src/builtin-addons/value-display/backend.ts +86 -0
  211. package/src/builtin-addons/value-display/buttons/value-display/backend.ts +17 -0
  212. package/src/builtin-addons/value-display/buttons/value-display/config.ts +36 -0
  213. package/src/builtin-addons/value-display/buttons/value-display/frontend.tsx +54 -0
  214. package/src/builtin-addons/value-display/index.d.ts +5 -0
  215. package/src/builtin-addons/value-display/index.ts +23 -0
  216. package/src/builtin-addons/value-display/sirenodeck.json +6 -0
  217. package/src/builtin-addons/weather/README.md +46 -0
  218. package/src/builtin-addons/weather/backend.ts +121 -0
  219. package/src/builtin-addons/weather/buttons/weather/backend.ts +27 -0
  220. package/src/builtin-addons/weather/buttons/weather/config.ts +29 -0
  221. package/src/builtin-addons/weather/buttons/weather/frontend.tsx +63 -0
  222. package/src/builtin-addons/weather/buttons/weather/icons.tsx +88 -0
  223. package/src/builtin-addons/weather/buttons/weather/pages.tsx +109 -0
  224. package/src/builtin-addons/weather/domain/codes.ts +35 -0
  225. package/src/builtin-addons/weather/domain/fetch.ts +47 -0
  226. package/src/builtin-addons/weather/index.d.ts +5 -0
  227. package/src/builtin-addons/weather/index.ts +23 -0
  228. package/src/builtin-addons/weather/poller.ts +70 -0
  229. package/src/builtin-addons/weather/provider/city-key.ts +9 -0
  230. package/src/builtin-addons/weather/provider/codes.ts +35 -0
  231. package/src/builtin-addons/weather/provider/fetch.ts +97 -0
  232. package/src/builtin-addons/weather/provider/geocode.ts +67 -0
  233. package/src/builtin-addons/weather/provider/types.ts +42 -0
  234. package/src/builtin-addons/weather/sirenodeck.json +6 -0
  235. package/src/cli/coding-agents-onboarding.ts +434 -0
  236. package/src/cli/commands/addon-decks.ts +441 -0
  237. package/src/cli/commands/addon-registry.ts +639 -0
  238. package/src/cli/commands/emulator-mode.ts +289 -0
  239. package/src/cli/commands/install.ts +128 -0
  240. package/src/cli/commands/logs.ts +74 -0
  241. package/src/cli/commands/network-bind.ts +111 -0
  242. package/src/cli/commands/pipeline/helpers.ts +75 -0
  243. package/src/cli/commands/pipeline/preflight.ts +20 -0
  244. package/src/cli/commands/port-identity.ts +152 -0
  245. package/src/cli/commands/reload.ts +37 -0
  246. package/src/cli/commands/restart.ts +139 -0
  247. package/src/cli/commands/run.ts +2270 -0
  248. package/src/cli/commands/service/install.ts +121 -0
  249. package/src/cli/commands/service/render-template.ts +158 -0
  250. package/src/cli/commands/service-manager.ts +184 -0
  251. package/src/cli/commands/spawn-daemon.ts +251 -0
  252. package/src/cli/commands/start.ts +1061 -0
  253. package/src/cli/commands/status.ts +110 -0
  254. package/src/cli/commands/stop.ts +121 -0
  255. package/src/cli/commands/subprocess-supervisor.ts +203 -0
  256. package/src/cli/commands/system-requirements.ts +699 -0
  257. package/src/cli/cwd.ts +28 -0
  258. package/src/cli/first-run.ts +149 -0
  259. package/src/cli/http-server.ts +244 -0
  260. package/src/cli/index.ts +355 -0
  261. package/src/cli/install-root.ts +26 -0
  262. package/src/cli/main.ts +194 -0
  263. package/src/cli/package-manager.ts +175 -0
  264. package/src/cli/probe-deps.ts +45 -0
  265. package/src/cli/prompt.ts +22 -0
  266. package/src/cli/startup-display.ts +452 -0
  267. package/src/config/builtin-icons.ts +57 -0
  268. package/src/config/config-diff.ts +54 -0
  269. package/src/config/discovery.ts +64 -0
  270. package/src/config/icon-resolver.ts +90 -0
  271. package/src/config/include-resolver.ts +202 -0
  272. package/src/config/index.ts +9 -0
  273. package/src/config/loader.ts +187 -0
  274. package/src/config/mutation.ts +514 -0
  275. package/src/config/reference-expander.ts +84 -0
  276. package/src/config/schemas.ts +223 -0
  277. package/src/config/validation.ts +254 -0
  278. package/src/core/asset-id.ts +21 -0
  279. package/src/core/gesture-state.ts +344 -0
  280. package/src/core/icon-asset-registry.ts +127 -0
  281. package/src/core/icon-source.ts +86 -0
  282. package/src/core/index.ts +30 -0
  283. package/src/core/mime.ts +16 -0
  284. package/src/core/pagination.ts +90 -0
  285. package/src/core/pub-sub.ts +126 -0
  286. package/src/core/settings.ts +153 -0
  287. package/src/core/store.ts +88 -0
  288. package/src/core/watcher.ts +73 -0
  289. package/src/deck/addon-handler-bridge.ts +516 -0
  290. package/src/deck/deck-config.ts +271 -0
  291. package/src/deck/editor-surfaces.ts +87 -0
  292. package/src/deck/host-context.ts +18 -0
  293. package/src/deck/index.ts +102 -0
  294. package/src/deck/macro-parse.ts +182 -0
  295. package/src/deck/methods.ts +348 -0
  296. package/src/deck/paginate-deck.ts +98 -0
  297. package/src/deck/position-buttons.ts +57 -0
  298. package/src/deck/runtime/index.ts +5 -0
  299. package/src/deck/runtime/runtime.ts +1060 -0
  300. package/src/deck/runtime-subscriptions.ts +45 -0
  301. package/src/deck/system-back-injection.ts +62 -0
  302. package/src/deck/system-buttons/registry.tsx +97 -0
  303. package/src/deck/system-buttons/types.ts +14 -0
  304. package/src/device/black-frame.ts +73 -0
  305. package/src/device/index.ts +13 -0
  306. package/src/device/linux-udev.ts +36 -0
  307. package/src/device/models.ts +89 -0
  308. package/src/device/reconnecting.ts +193 -0
  309. package/src/device/registry.ts +38 -0
  310. package/src/device/stream-deck.ts +155 -0
  311. package/src/index.ts +74 -0
  312. package/src/outputClient/emulator.ts +357 -0
  313. package/src/outputClient/index.ts +26 -0
  314. package/src/outputClient/open-browser.ts +26 -0
  315. package/src/outputClient/real.ts +393 -0
  316. package/src/outputClient/types.ts +83 -0
  317. package/src/render/browser-renderer.ts +240 -0
  318. package/src/render/buffer-hash.ts +20 -0
  319. package/src/render/editor-handler.ts +197 -0
  320. package/src/render/emulator-server.ts +128 -0
  321. package/src/render/icon-source-resolver.ts +69 -0
  322. package/src/render/index.ts +16 -0
  323. package/src/render/protocol.ts +61 -0
  324. package/src/render/push-raw-image.ts +100 -0
  325. package/src/render/screenshot-cadence.ts +87 -0
  326. package/src/render/state-publisher.ts +110 -0
  327. package/src/render/vite-server.ts +138 -0
  328. package/src/render/ws-bridge.ts +315 -0
  329. package/src/system/device-selection.ts +80 -0
  330. package/src/system/glob-match.ts +74 -0
  331. package/src/system/providers/active-app/darwin.ts +117 -0
  332. package/src/system/providers/active-app/linux.ts +245 -0
  333. package/src/system/providers/active-app/wayland-gnome.ts +303 -0
  334. package/src/system/providers/active-app/windows.ts +94 -0
  335. package/src/system/providers/active-app.ts +97 -0
  336. package/src/system/providers/clipboard/darwin.ts +65 -0
  337. package/src/system/providers/clipboard/linux.ts +163 -0
  338. package/src/system/providers/clipboard/windows.ts +94 -0
  339. package/src/system/providers/clipboard.ts +84 -0
  340. package/src/system/providers/error.ts +16 -0
  341. package/src/system/providers/key-macro/darwin.ts +221 -0
  342. package/src/system/providers/key-macro/linux.ts +651 -0
  343. package/src/system/providers/key-macro/parser.ts +215 -0
  344. package/src/system/providers/key-macro/windows.ts +476 -0
  345. package/src/system/providers/key-macro.ts +79 -0
  346. package/src/system/providers/notification/darwin.ts +36 -0
  347. package/src/system/providers/notification/linux.ts +115 -0
  348. package/src/system/providers/notification/windows.ts +72 -0
  349. package/src/system/providers/notification.ts +75 -0
  350. package/src/system/providers/session/darwin.ts +82 -0
  351. package/src/system/providers/session/linux.ts +175 -0
  352. package/src/system/providers/session/windows.ts +73 -0
  353. package/src/system/providers/session.ts +89 -0
  354. package/src/system/providers/shared.ts +65 -0
  355. package/src/system/requirements.ts +235 -0
  356. package/src/system/setup-wizard/config-seed.ts +30 -0
  357. package/src/system/setup-wizard/format.ts +275 -0
  358. package/src/system/setup-wizard/index.ts +45 -0
  359. package/src/system/setup-wizard/open-settings.ts +26 -0
  360. package/src/system/setup-wizard/plan.ts +230 -0
  361. package/src/system/setup-wizard/probe-cache.ts +34 -0
  362. package/src/system/setup-wizard/probe.ts +486 -0
  363. package/src/system/setup-wizard/runtime-features.ts +85 -0
  364. package/src/system/setup-wizard/sudo.ts +178 -0
  365. package/src/system/setup-wizard/types.ts +133 -0
  366. package/src/system/virtual-stream-deck.ts +67 -0
  367. package/src/themes/css.ts +210 -0
  368. package/src/themes/default/assets/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  369. package/src/themes/default/assets/IBMPlexSans-VariableFont_wdth,wght.ttf +0 -0
  370. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Bold.ttf +0 -0
  371. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-BoldItalic.ttf +0 -0
  372. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ExtraLight.ttf +0 -0
  373. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ExtraLightItalic.ttf +0 -0
  374. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Italic.ttf +0 -0
  375. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Light.ttf +0 -0
  376. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-LightItalic.ttf +0 -0
  377. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Medium.ttf +0 -0
  378. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-MediumItalic.ttf +0 -0
  379. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Regular.ttf +0 -0
  380. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-SemiBold.ttf +0 -0
  381. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-SemiBoldItalic.ttf +0 -0
  382. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Thin.ttf +0 -0
  383. package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ThinItalic.ttf +0 -0
  384. package/src/themes/default/assets/IBM_Plex_Mono/OFL.txt +93 -0
  385. package/src/themes/default/assets/OFL.txt +93 -0
  386. package/src/themes/default/assets/README.txt +106 -0
  387. package/src/themes/default/assets/static/IBMPlexSans-Bold.ttf +0 -0
  388. package/src/themes/default/assets/static/IBMPlexSans-BoldItalic.ttf +0 -0
  389. package/src/themes/default/assets/static/IBMPlexSans-ExtraLight.ttf +0 -0
  390. package/src/themes/default/assets/static/IBMPlexSans-ExtraLightItalic.ttf +0 -0
  391. package/src/themes/default/assets/static/IBMPlexSans-Italic.ttf +0 -0
  392. package/src/themes/default/assets/static/IBMPlexSans-Light.ttf +0 -0
  393. package/src/themes/default/assets/static/IBMPlexSans-LightItalic.ttf +0 -0
  394. package/src/themes/default/assets/static/IBMPlexSans-Medium.ttf +0 -0
  395. package/src/themes/default/assets/static/IBMPlexSans-MediumItalic.ttf +0 -0
  396. package/src/themes/default/assets/static/IBMPlexSans-Regular.ttf +0 -0
  397. package/src/themes/default/assets/static/IBMPlexSans-SemiBold.ttf +0 -0
  398. package/src/themes/default/assets/static/IBMPlexSans-SemiBoldItalic.ttf +0 -0
  399. package/src/themes/default/assets/static/IBMPlexSans-Thin.ttf +0 -0
  400. package/src/themes/default/assets/static/IBMPlexSans-ThinItalic.ttf +0 -0
  401. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Bold.ttf +0 -0
  402. package/src/themes/default/assets/static/IBMPlexSans_Condensed-BoldItalic.ttf +0 -0
  403. package/src/themes/default/assets/static/IBMPlexSans_Condensed-ExtraLight.ttf +0 -0
  404. package/src/themes/default/assets/static/IBMPlexSans_Condensed-ExtraLightItalic.ttf +0 -0
  405. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Italic.ttf +0 -0
  406. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Light.ttf +0 -0
  407. package/src/themes/default/assets/static/IBMPlexSans_Condensed-LightItalic.ttf +0 -0
  408. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Medium.ttf +0 -0
  409. package/src/themes/default/assets/static/IBMPlexSans_Condensed-MediumItalic.ttf +0 -0
  410. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Regular.ttf +0 -0
  411. package/src/themes/default/assets/static/IBMPlexSans_Condensed-SemiBold.ttf +0 -0
  412. package/src/themes/default/assets/static/IBMPlexSans_Condensed-SemiBoldItalic.ttf +0 -0
  413. package/src/themes/default/assets/static/IBMPlexSans_Condensed-Thin.ttf +0 -0
  414. package/src/themes/default/assets/static/IBMPlexSans_Condensed-ThinItalic.ttf +0 -0
  415. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Bold.ttf +0 -0
  416. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-BoldItalic.ttf +0 -0
  417. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ExtraLight.ttf +0 -0
  418. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ExtraLightItalic.ttf +0 -0
  419. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Italic.ttf +0 -0
  420. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Light.ttf +0 -0
  421. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-LightItalic.ttf +0 -0
  422. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Medium.ttf +0 -0
  423. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-MediumItalic.ttf +0 -0
  424. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Regular.ttf +0 -0
  425. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-SemiBold.ttf +0 -0
  426. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
  427. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Thin.ttf +0 -0
  428. package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ThinItalic.ttf +0 -0
  429. package/src/themes/default/components.css +81 -0
  430. package/src/themes/default/sirenodeck.json +170 -0
  431. package/src/themes/index.ts +14 -0
  432. package/src/themes/light/assets/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  433. package/src/themes/light/assets/IBMPlexSans-VariableFont_wdth,wght.ttf +0 -0
  434. package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Bold.ttf +0 -0
  435. package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Medium.ttf +0 -0
  436. package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Regular.ttf +0 -0
  437. package/src/themes/light/assets/IBM_Plex_Mono/OFL.txt +93 -0
  438. package/src/themes/light/assets/OFL.txt +93 -0
  439. package/src/themes/light/assets/README.txt +106 -0
  440. package/src/themes/light/assets/static/IBMPlexSans-Light.ttf +0 -0
  441. package/src/themes/light/assets/static/IBMPlexSans-LightItalic.ttf +0 -0
  442. package/src/themes/light/assets/static/IBMPlexSans-Thin.ttf +0 -0
  443. package/src/themes/light/components.css +70 -0
  444. package/src/themes/light/sirenodeck.json +170 -0
  445. package/src/themes/loader.ts +343 -0
  446. package/src/themes/manifest.ts +164 -0
  447. package/src/themes/use-resolved-theme.tsx +32 -0
  448. package/src/ui/ButtonFrame.tsx +230 -0
  449. package/src/ui/contexts/AssetCacheContext.tsx +43 -0
  450. package/src/ui/index.ts +20 -0
  451. package/src/ui/primitives/Chip.tsx +53 -0
  452. package/src/ui/primitives/Icon.tsx +209 -0
  453. package/src/ui/primitives/Label.tsx +82 -0
  454. package/src/ui/primitives/ProgressBar.tsx +38 -0
  455. package/src/ui/primitives/TapIndicator.tsx +67 -0
  456. package/src/ui/primitives/Text.tsx +810 -0
  457. package/src/ui/primitives/index.ts +6 -0
  458. package/src/ui/surfaces/BarsSurface.tsx +211 -0
  459. package/src/ui/surfaces/IconLabelProgressSurface.tsx +133 -0
  460. package/src/ui/surfaces/IconLabelSurface.tsx +59 -0
  461. package/src/ui/surfaces/LabelValueListSurface.tsx +202 -0
  462. package/src/ui/surfaces/PaginatedSurface.tsx +164 -0
  463. package/src/ui/surfaces/SplitActionSurface.tsx +98 -0
  464. package/src/ui/surfaces/TemporaryErrorSurface.tsx +68 -0
  465. package/src/ui/surfaces/ValueChart.tsx +218 -0
  466. package/src/ui/surfaces/index.ts +32 -0
  467. package/src/ui/surfaces/paginated-schema.ts +22 -0
  468. package/src/ui/theme-presentation-builder.ts +184 -0
  469. package/src/ui/theme-presentation.tsx +146 -0
  470. package/src/ui/utils/cn.ts +12 -0
  471. package/src/util/cache-paths.ts +38 -0
  472. package/src/util/config-diff.ts +16 -0
  473. package/src/util/daemon-control.ts +85 -0
  474. package/src/util/daemon.ts +575 -0
  475. package/src/util/device-config.ts +56 -0
  476. package/src/util/errors.ts +13 -0
  477. package/src/util/log-reader.ts +114 -0
  478. package/src/util/log-tail.ts +103 -0
  479. package/src/util/logger.ts +417 -0
  480. package/src/util/process-title.ts +79 -0
  481. package/src/util/single-instance.ts +202 -0
  482. package/src/version.ts +10 -0
  483. package/src/vite/index.ts +6 -0
  484. package/src/vite/virtual-modules.ts +631 -0
  485. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,631 @@
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ readFileSync,
5
+ statSync,
6
+ writeFileSync,
7
+ } from "node:fs"
8
+ import { basename, dirname, join } from "node:path"
9
+ import type { Plugin, ViteDevServer } from "vite"
10
+
11
+ /**
12
+ * Asset base dir for browser-side `addon://` fetching, per configured
13
+ * addon. Mirrors buildExternalAddonDirs' dist-preferred logic on the CLI
14
+ * side: assets ship in <pkg>/dist/assets (post-build), source-only addons
15
+ * keep <pkg>/assets.
16
+ */
17
+ const addonAssetBaseDir = (frontendMain: string): string | null => {
18
+ const dir = dirname(frontendMain)
19
+ const base =
20
+ dirname(dir) === "" || dir === "."
21
+ ? null
22
+ : basename(dir) === "src" || basename(dir) === "dist"
23
+ ? dirname(dir)
24
+ : dir
25
+ if (base === null) return null
26
+ if (existsSync(join(base, "dist", "assets"))) return join(base, "dist")
27
+ if (existsSync(join(base, "assets"))) return base
28
+ return null
29
+ }
30
+
31
+ const ASSET_MIME: Record<string, string> = {
32
+ svg: "image/svg+xml",
33
+ png: "image/png",
34
+ jpg: "image/jpeg",
35
+ jpeg: "image/jpeg",
36
+ webp: "image/webp",
37
+ gif: "image/gif",
38
+ ogg: "audio/ogg",
39
+ mp3: "audio/mpeg",
40
+ wav: "audio/wav",
41
+ }
42
+
43
+ export interface SirenoVitePluginTheme {
44
+ name: string
45
+ manifestPath: string
46
+ uiOverridesPath: string | null
47
+ }
48
+
49
+ export interface SirenoVitePluginAddon {
50
+ name: string
51
+ frontend?: { main: string; styles?: string[] }
52
+ buttons?: ReadonlyArray<{ type: string }>
53
+ buttonTypes?: Readonly<Record<string, string>>
54
+ }
55
+
56
+ export interface SirenoVitePluginOptions {
57
+ token?: string
58
+ addons?: ReadonlyArray<SirenoVitePluginAddon>
59
+ theme?: SirenoVitePluginTheme
60
+ }
61
+
62
+ const TOKEN_VIRTUAL_ID = "virtual:sireno/token"
63
+ const TOKEN_RESOLVED_ID = "\0virtual:sireno/token"
64
+
65
+ const ADDONS_VIRTUAL_ID = "virtual:sireno/addons"
66
+ const ADDONS_RESOLVED_ID = "\0virtual:sireno/addons"
67
+
68
+ const ADDONS_REGISTRY_VIRTUAL_ID = "virtual:sireno/addons/registry"
69
+ const ADDONS_REGISTRY_RESOLVED_ID = "\0virtual:sireno/addons/registry"
70
+
71
+ const THEME_VIRTUAL_ID = "virtual:sireno/theme.css"
72
+ const THEME_RESOLVED_ID = "\0virtual:sireno/theme.css"
73
+
74
+ const THEMES_MANIFEST_VIRTUAL_ID = "virtual:sireno/themes/manifest"
75
+ const THEMES_MANIFEST_RESOLVED_ID = "\0virtual:sireno/themes/manifest"
76
+
77
+ /**
78
+ * Invalidate hooks registered by addon frontends. When a TSX file changes,
79
+ * the addon can register a cleanup that runs before HMR swaps the module,
80
+ * clearing intervals, animation frames, and event listeners that would
81
+ * otherwise fire on stale state.
82
+ *
83
+ * Addon code calls:
84
+ * `__SIRENO_ADDON_CLEANUP__(addonName, () => clearInterval(id))`
85
+ *
86
+ * The plugin collects these per addon and invokes them whenever the
87
+ * addon's main module is updated.
88
+ */
89
+ const cleanupRegistry = new Map<string, Set<() => void>>()
90
+
91
+ export const __SIRENO_ADDON_CLEANUP__ = (
92
+ addonName: string,
93
+ cleanup: () => void,
94
+ ): void => {
95
+ const set = cleanupRegistry.get(addonName) ?? new Set<() => void>()
96
+ set.add(cleanup)
97
+ cleanupRegistry.set(addonName, set)
98
+ }
99
+
100
+ export const __SIRENO_RUN_ADDON_CLEANUPS__ = (addonName: string): number => {
101
+ const set = cleanupRegistry.get(addonName)
102
+ if (set === undefined || set.size === 0) return 0
103
+ for (const cleanup of set) {
104
+ try {
105
+ cleanup()
106
+ } catch {
107
+ // cleanup may throw if the addon is in an intermediate state — ignore
108
+ }
109
+ }
110
+ set.clear()
111
+ return set.size
112
+ }
113
+
114
+ export const TOKEN_MODULE = (token: string): string =>
115
+ `export const token = ${JSON.stringify(token)};\n`
116
+
117
+ const sanitizeIdentifier = (name: string): string => {
118
+ const sanitized = name.replace(/[^a-zA-Z0-9_$]/g, "_")
119
+ return /^[a-zA-Z_$]/.test(sanitized) ? sanitized : `_${sanitized}`
120
+ }
121
+
122
+ export const buildAddonsImports = (
123
+ addons: ReadonlyArray<SirenoVitePluginAddon>,
124
+ ): string => {
125
+ const lines: string[] = []
126
+ for (const addon of addons) {
127
+ if (addon.frontend === undefined) continue
128
+ lines.push(
129
+ `import * as ${sanitizeIdentifier(addon.name)}_frontend from ${JSON.stringify(addon.frontend.main)};`,
130
+ )
131
+ }
132
+ lines.push(
133
+ `export const addons = ${JSON.stringify(
134
+ addons
135
+ .filter((a) => a.frontend !== undefined)
136
+ .map((a) => ({
137
+ name: a.name,
138
+ main: a.frontend!.main,
139
+ styles: a.frontend!.styles ?? [],
140
+ })),
141
+ )};`,
142
+ )
143
+ return lines.join("\n")
144
+ }
145
+
146
+ export const buildAddonsRegistryModule = (
147
+ addons: ReadonlyArray<SirenoVitePluginAddon>,
148
+ ): string => {
149
+ const lines: string[] = []
150
+ for (const addon of addons) {
151
+ if (addon.frontend === undefined) continue
152
+ if (addon.buttonTypes === undefined) continue
153
+ const modName = sanitizeIdentifier(addon.name) + "_manifest"
154
+ lines.push(
155
+ `import * as ${modName} from ${JSON.stringify(addon.frontend.main)};`,
156
+ )
157
+ }
158
+ lines.push("/* eslint-disable */")
159
+ for (const addon of addons) {
160
+ if (addon.frontend === undefined) continue
161
+ if (addon.buttonTypes === undefined) continue
162
+ const modName = sanitizeIdentifier(addon.name) + "_manifest"
163
+ lines.push(
164
+ `const ${modName}Obj = ${modName}.manifest ?? ${modName}.default;`,
165
+ )
166
+ }
167
+ lines.push("export const addonRegistry = {")
168
+ for (const addon of addons) {
169
+ if (addon.frontend === undefined) continue
170
+ if (addon.buttonTypes === undefined) continue
171
+ const modName = sanitizeIdentifier(addon.name) + "_manifest"
172
+ for (const [type, exportName] of Object.entries(addon.buttonTypes)) {
173
+ lines.push(
174
+ ` ${JSON.stringify(type)}: { addonName: ${JSON.stringify(addon.name)}, Component: (${modName}Obj.buttonTypes[${JSON.stringify(type)}] ?? ${modName}[${JSON.stringify(exportName)}] ?? ${modName}.default?.buttonTypes?.[${JSON.stringify(type)}])?.frontend, gestures: (${modName}Obj.buttonTypes[${JSON.stringify(type)}] ?? ${modName}[${JSON.stringify(exportName)}] ?? ${modName}.default?.buttonTypes?.[${JSON.stringify(type)}])?.backend?.gestureHandlers },`,
175
+ )
176
+ if (type === `${addon.name}:${addon.name}`) {
177
+ lines.push(
178
+ ` ${JSON.stringify(addon.name)}: { addonName: ${JSON.stringify(addon.name)}, Component: (${modName}Obj.buttonTypes[${JSON.stringify(type)}] ?? ${modName}[${JSON.stringify(exportName)}] ?? ${modName}.default?.buttonTypes?.[${JSON.stringify(type)}])?.frontend, gestures: (${modName}Obj.buttonTypes[${JSON.stringify(type)}] ?? ${modName}[${JSON.stringify(exportName)}] ?? ${modName}.default?.buttonTypes?.[${JSON.stringify(type)}])?.backend?.gestureHandlers },`,
179
+ )
180
+ }
181
+ }
182
+ }
183
+ lines.push("};")
184
+ return lines.join("\n")
185
+ }
186
+
187
+ export const readThemeCss = (themeDir: string | undefined): string => {
188
+ if (!themeDir) return ""
189
+ const cssPath = join(themeDir, ".sirenodeck", "theme.css")
190
+ try {
191
+ return readFileSync(cssPath, "utf8")
192
+ } catch {
193
+ return ""
194
+ }
195
+ }
196
+
197
+ export const buildThemesManifestModule = (
198
+ theme: SirenoVitePluginTheme | undefined,
199
+ ): string => {
200
+ if (!theme) {
201
+ return `export const activeTheme = null;\nexport const colorTokens = null;\nexport const typography = null;\n`
202
+ }
203
+
204
+ let manifestData = "{}"
205
+ try {
206
+ manifestData = readFileSync(theme.manifestPath, "utf8")
207
+ } catch {
208
+ // ignore — manifest may not exist in all setups
209
+ }
210
+
211
+ const uiOverridesImport = theme.uiOverridesPath
212
+ ? `import * as _uiOverrides from ${JSON.stringify(theme.uiOverridesPath)};`
213
+ : null
214
+
215
+ const uiOverridesBody =
216
+ theme.uiOverridesPath !== null
217
+ ? [
218
+ `const _components = _uiOverrides?.components ?? {};`,
219
+ `const _surfaces = _uiOverrides?.surfaces ?? {};`,
220
+ `const _primitives = _uiOverrides?.primitives ?? {};`,
221
+ `export const components = _components;`,
222
+ `export const surfaces = _surfaces;`,
223
+ `export const primitives = _primitives;`,
224
+ `export { _uiOverrides as uiOverrides };`,
225
+ ].join("\n")
226
+ : [
227
+ `export const components = null;`,
228
+ `export const surfaces = null;`,
229
+ `export const primitives = null;`,
230
+ `export const uiOverrides = null;`,
231
+ ].join("\n")
232
+
233
+ return [
234
+ uiOverridesImport,
235
+ `const _manifest = JSON.parse(${JSON.stringify(manifestData)})`,
236
+ `export const activeTheme = { name: ${JSON.stringify(theme.name)}, manifestPath: ${JSON.stringify(theme.manifestPath)}, uiOverridesPath: ${JSON.stringify(theme.uiOverridesPath)} };`,
237
+ `export const colorTokens = _manifest.colorTokens ?? null;`,
238
+ `export const typography = _manifest.typography ?? null;`,
239
+ uiOverridesBody,
240
+ ]
241
+ .filter(Boolean)
242
+ .join("\n")
243
+ }
244
+
245
+ /**
246
+ * Node-only modules that can appear in third-party addon SOURCE graphs
247
+ * served to the browser (the run pipeline hands vite each addon's
248
+ * `src/index.ts` when it exists — see browserFrontendMain in run.ts).
249
+ * Nothing on the render path invokes these; an inert stub keeps vite's
250
+ * transform and ESM link checks happy.
251
+ *
252
+ * Named exports must cover every named import across all addon sources
253
+ * (ESM link-time check fails on missing names). If a browser console
254
+ * reports "does not provide an export named 'X'", add X here.
255
+ */
256
+ const BROWSER_NODE_STUBS: ReadonlyArray<{
257
+ specifier: string
258
+ hasDefault: boolean
259
+ names: ReadonlyArray<string>
260
+ }> = [
261
+ { specifier: "chokidar", hasDefault: true, names: [] },
262
+ { specifier: "execa", hasDefault: false, names: ["execa"] },
263
+ {
264
+ specifier: "@opencode-ai/sdk",
265
+ hasDefault: false,
266
+ names: ["createOpencodeClient"],
267
+ },
268
+ {
269
+ specifier: "node:fs/promises",
270
+ hasDefault: false,
271
+ names: ["readFile", "readdir", "stat"],
272
+ },
273
+ { specifier: "node:path", hasDefault: false, names: ["join"] },
274
+ ]
275
+
276
+ const nodeStubSource = ({
277
+ hasDefault,
278
+ names,
279
+ }: {
280
+ hasDefault: boolean
281
+ names: ReadonlyArray<string>
282
+ }): string => {
283
+ const lines = [
284
+ "// Generated by the sirenoDeck2 vite plugin — inert browser stub for a",
285
+ "// Node-only dependency imported by third-party addon source graphs.",
286
+ "// Never call these: they exist only so module evaluation succeeds.",
287
+ "const noop = () => {}",
288
+ "const proxy = new Proxy(noop, { get: () => noop })",
289
+ ]
290
+ if (hasDefault) lines.push("export default noop")
291
+ for (const name of names) lines.push(`export const ${name} = noop`)
292
+ return `${lines.join("\n")}\n`
293
+ }
294
+
295
+ const writeNodeDepStubs = (dir: string): void => {
296
+ const stubDir = join(dir, "node-dep-stubs")
297
+ if (!existsSync(stubDir)) mkdirSync(stubDir, { recursive: true })
298
+ for (const stub of BROWSER_NODE_STUBS) {
299
+ const fileName = `${stub.specifier.replace(/[^a-zA-Z0-9]/g, "-")}.js`
300
+ writeFileSync(join(stubDir, fileName), nodeStubSource(stub), "utf8")
301
+ }
302
+ }
303
+
304
+ export const sirenoDeck2 = (options: SirenoVitePluginOptions = {}): Plugin => {
305
+ const token = options.token ?? ""
306
+ const addons = options.addons ?? []
307
+ const theme = options.theme
308
+ const themeDir = process.env["SIRENO_THEME_DIR"]
309
+ let themeCss = readThemeCss(themeDir)
310
+
311
+ return {
312
+ name: "sirenodeck",
313
+ resolveId: (id) => {
314
+ if (id === TOKEN_VIRTUAL_ID) return TOKEN_RESOLVED_ID
315
+ if (id === ADDONS_VIRTUAL_ID) return ADDONS_RESOLVED_ID
316
+ if (id === ADDONS_REGISTRY_VIRTUAL_ID) return ADDONS_REGISTRY_RESOLVED_ID
317
+ if (id === THEME_VIRTUAL_ID) return THEME_RESOLVED_ID
318
+ if (id === THEMES_MANIFEST_VIRTUAL_ID) return THEMES_MANIFEST_RESOLVED_ID
319
+ return null
320
+ },
321
+ load: (id) => {
322
+ if (id === TOKEN_RESOLVED_ID) return TOKEN_MODULE(token)
323
+ if (id === ADDONS_RESOLVED_ID) return buildAddonsImports(addons)
324
+ if (id === ADDONS_REGISTRY_RESOLVED_ID)
325
+ return buildAddonsRegistryModule(addons)
326
+ if (id === THEME_RESOLVED_ID) return readThemeCss(themeDir)
327
+ if (id === THEMES_MANIFEST_RESOLVED_ID)
328
+ return buildThemesManifestModule(theme)
329
+ return null
330
+ },
331
+ configResolved: (config) => {
332
+ const root = config.root ?? process.cwd()
333
+ const dir = join(root, ".sirenodeck")
334
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
335
+ writeNodeDepStubs(dir)
336
+ if (themeCss.length === 0) return
337
+ const filePath = join(dir, "theme.css")
338
+ // Sireno-deck serves a local Stream Deck UI over loopback — every
339
+ // byte ships through localhost, so Tailwind's "purge unused
340
+ // utilities" optimization costs nothing to undo. Scan the entire
341
+ // CLI package (frontend, emulator, shared ui, builtin addons)
342
+ // plus each third-party addon's frontend directory so addon
343
+ // authors can use any utility class without coordinating with
344
+ // the Vite plugin.
345
+ const cliPackage = join(root, "..")
346
+ const sourceLines = [`@source "${cliPackage}/**/*.{ts,tsx}";\n`]
347
+ const seenFrontendDirs = new Set<string>()
348
+ for (const addon of addons) {
349
+ const mainPath = addon.frontend?.main
350
+ if (typeof mainPath !== "string" || mainPath.length === 0) continue
351
+ const frontendDir = dirname(mainPath)
352
+ if (seenFrontendDirs.has(frontendDir)) continue
353
+ if (frontendDir.startsWith(cliPackage)) continue
354
+ seenFrontendDirs.add(frontendDir)
355
+ sourceLines.push(`@source "${frontendDir}/**/*.{ts,tsx}";\n`)
356
+ }
357
+ const sourceDirective = sourceLines.join("")
358
+ writeFileSync(filePath, sourceDirective + themeCss, "utf8")
359
+ },
360
+ config: (config) => {
361
+ // ponytail: never spread the incoming config — it carries `esbuild`
362
+ // and `optimizeDeps.esbuildOptions` from earlier plugins
363
+ // (`@vitejs/plugin-react`'s vite:react-babel hook). Re-emitting them
364
+ // via our `config` return makes Vite attribute the deprecation
365
+ // warnings to this plugin.
366
+ const root = config.root ?? process.cwd()
367
+ const dir = join(root, ".sirenodeck")
368
+ const alias = (config.resolve?.alias ?? []) as Array<{
369
+ find: string | RegExp
370
+ replacement: string
371
+ }>
372
+ const extra: Array<{ find: string | RegExp; replacement: string }> = [
373
+ ...BROWSER_NODE_STUBS.map(({ specifier }) => ({
374
+ find: new RegExp(
375
+ `^${specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`,
376
+ ),
377
+ replacement: join(
378
+ dir,
379
+ "node-dep-stubs",
380
+ `${specifier.replace(/[^a-zA-Z0-9]/g, "-")}.js`,
381
+ ),
382
+ })),
383
+ ]
384
+ if (themeCss.length > 0) {
385
+ extra.push({
386
+ find: /^sirenodeck-theme$/,
387
+ replacement: join(dir, "theme.css"),
388
+ })
389
+ }
390
+ return {
391
+ resolve: {
392
+ ...config.resolve,
393
+ alias: [...alias, ...extra],
394
+ },
395
+ }
396
+ },
397
+ configureServer: (server: ViteDevServer) => {
398
+ const signalReady = (): void => {
399
+ const addr = server.httpServer?.address()
400
+ const port = typeof addr === "object" && addr !== null ? addr.port : 0
401
+ process.stdout.write(`READY ${port}\n`)
402
+ }
403
+
404
+ // ponytail: --remote gates HTTP access with the daemon's token. The
405
+ // emulator/frontend vite is bound to 0.0.0.0 so any host on the LAN
406
+ // can reach it; without this gate, anyone with the URL could load
407
+ // the bundle and (via the bundle's baked token) replay commands on
408
+ // the WS bridge. Accept the token via `?token=` query OR a
409
+ // `sireno-token` cookie (the HTML page parses the URL token and sets
410
+ // the cookie on first load so subsequent module/HMR requests stay
411
+ // authenticated). Vite's HMR webSocket is exempt — it's a debug-only
412
+ // surface and tying it to token would break dev refresh.
413
+ const requiredToken = process.env["SIRENO_REQUIRE_TOKEN"]
414
+ if (requiredToken !== undefined && requiredToken.length > 0) {
415
+ const checkToken = (req: {
416
+ url?: string
417
+ headers?: Record<string, string | string[] | undefined>
418
+ }): boolean => {
419
+ const url = req.url ?? ""
420
+ if (url.startsWith("/@vite/") || url.startsWith("/__vite_ping")) {
421
+ return true
422
+ }
423
+ const queryIdx = url.indexOf("?")
424
+ if (queryIdx !== -1) {
425
+ const params = new URLSearchParams(url.slice(queryIdx + 1))
426
+ if (params.get("token") === requiredToken) return true
427
+ }
428
+ const cookieHeader = req.headers?.["cookie"]
429
+ if (typeof cookieHeader === "string") {
430
+ for (const part of cookieHeader.split(";")) {
431
+ const [rawName, ...rest] = part.trim().split("=")
432
+ if (
433
+ rawName === "sireno-token" &&
434
+ rest.join("=") === requiredToken
435
+ ) {
436
+ return true
437
+ }
438
+ }
439
+ }
440
+ return false
441
+ }
442
+ server.middlewares.use((req, res, next) => {
443
+ if (checkToken(req)) {
444
+ next()
445
+ return
446
+ }
447
+ res.statusCode = 403
448
+ res.end("token required")
449
+ })
450
+ }
451
+
452
+ const configPath = process.env["SIRENO_CONFIG_PATH"]
453
+ if (configPath !== undefined && configPath.length > 0) {
454
+ server.middlewares.use((req, res, next) => {
455
+ const url = req.url?.split("?", 1)[0]
456
+ if (url === "/api/config-path") {
457
+ const body = JSON.stringify({ path: configPath })
458
+ res.writeHead(200, {
459
+ "content-type": "application/json; charset=utf-8",
460
+ "content-length": Buffer.byteLength(body),
461
+ })
462
+ res.end(body)
463
+ return
464
+ }
465
+ if (url !== "/api/config" && url !== "/api/config/") {
466
+ next()
467
+ return
468
+ }
469
+ try {
470
+ const text = readFileSync(configPath, "utf8")
471
+ res.writeHead(200, {
472
+ "content-type": "text/plain; charset=utf-8",
473
+ "content-length": Buffer.byteLength(text),
474
+ })
475
+ res.end(text)
476
+ } catch {
477
+ res.writeHead(404, { "content-type": "text/plain; charset=utf-8" })
478
+ res.end("Config not available")
479
+ }
480
+ })
481
+ }
482
+
483
+ server.httpServer?.once("listening", () => {
484
+ // Wait for vite's dep optimizer to finish so the first browser
485
+ // request doesn't hit a 504 "Outdated Optimize Dep" while deps
486
+ // are still being pre-bundled.
487
+ const warmup = async (): Promise<void> => {
488
+ try {
489
+ await server.warmupRequest("/index.html")
490
+ } catch {
491
+ // ignore warmup failures
492
+ }
493
+ try {
494
+ await server.transformRequest("/src/main.tsx")
495
+ } catch {
496
+ // ignore transform failures
497
+ }
498
+ signalReady()
499
+ }
500
+ void warmup()
501
+ })
502
+
503
+ // ponytail: SPA <img src> cannot fetch `addon://` schemes. Serve
504
+ // addon assets over HTTP at /@sireno/addon-asset/<name>/<subPath>
505
+ // (see resolveAddonAssetSrc in src/api/asset-url.ts). Scoped to
506
+ // configured addons, path-traversal safe.
507
+ const assetDirs = new Map<string, string>()
508
+ for (const addon of addons) {
509
+ if (addon.frontend?.main === undefined) continue
510
+ const base = addonAssetBaseDir(addon.frontend.main)
511
+ if (base !== null) assetDirs.set(addon.name, base)
512
+ }
513
+ if (assetDirs.size > 0) {
514
+ server.middlewares.use((req, res, next) => {
515
+ const url = req.url ?? ""
516
+ const m = /^\/@sireno\/addon-asset\/([^/]+)\/(.+)$/.exec(
517
+ url.split("?")[0] ?? "",
518
+ )
519
+ if (m === null) {
520
+ next()
521
+ return
522
+ }
523
+ const [, name, subPathRaw] = m as unknown as [string, string, string]
524
+ const base = assetDirs.get(name)
525
+ if (base === undefined) {
526
+ res.statusCode = 404
527
+ res.end("unknown addon")
528
+ return
529
+ }
530
+ const safe = subPathRaw.replace(/^\/+/, "")
531
+ if (safe.includes("..") || safe.length === 0) {
532
+ res.statusCode = 400
533
+ res.end("bad path")
534
+ return
535
+ }
536
+ const filePath = join(base, safe)
537
+ if (!filePath.startsWith(base)) {
538
+ res.statusCode = 400
539
+ res.end("bad path")
540
+ return
541
+ }
542
+ try {
543
+ const body = readFileSync(filePath)
544
+ const ext = filePath.split(".").pop()?.toLowerCase() ?? ""
545
+ res.setHeader(
546
+ "Content-Type",
547
+ ASSET_MIME[ext] ?? "application/octet-stream",
548
+ )
549
+ res.setHeader("Cache-Control", "no-cache")
550
+ res.end(body)
551
+ } catch {
552
+ res.statusCode = 404
553
+ res.end("asset not found")
554
+ }
555
+ })
556
+ }
557
+
558
+ // Theme.css lives on disk and the daemon re-stages it when the
559
+ // active theme changes. Watch the staged file and reload the
560
+ // THEME_VIRTUAL_ID module so the browser gets the new CSS via
561
+ // vite's HMR without a full reload.
562
+ if (themeDir) {
563
+ const stagedCss = join(themeDir, ".sirenodeck", "theme.css")
564
+ if (existsSync(stagedCss)) {
565
+ server.watcher.add(stagedCss)
566
+ server.watcher.on("change", (file) => {
567
+ if (file !== stagedCss) return
568
+ themeCss = readThemeCss(themeDir)
569
+ const mod = server.moduleGraph.getModuleById(THEME_RESOLVED_ID)
570
+ if (mod) server.reloadModule(mod)
571
+ })
572
+ }
573
+ }
574
+ },
575
+ // ponytail: --remote requires a way to authenticate subsequent module
576
+ // requests after the HTML loads. Inject a small script that parses
577
+ // `?token=X` from the URL and stores it in `document.cookie` so vite's
578
+ // middleware accepts the cascade of `/src/...` and asset requests.
579
+ transformIndexHtml: {
580
+ order: "pre",
581
+ handler: (html, ctx) => {
582
+ // ponytail: third-party addon SOURCE served to the browser may
583
+ // reference Node globals (e.g. `process.env["HOME"]` in the
584
+ // coding-agents manifest probe). Vite doesn't polyfill `process`
585
+ // in dev; inject a minimal shim before any module executes so
586
+ // evaluation succeeds — values are irrelevant on the render path.
587
+ const processShim = `<script>window.process=window.process||{env:{}};</script>`
588
+ let injected = html.replace("<head>", `<head>${processShim}`)
589
+ if (process.env["SIRENO_REQUIRE_TOKEN"] === undefined) return injected
590
+ const script =
591
+ `<script>(function(){try{var p=new URLSearchParams(location.search);` +
592
+ `var t=p.get("token");if(t){document.cookie="sireno-token="+` +
593
+ `encodeURIComponent(t)+"; path=/; SameSite=Lax";` +
594
+ `p.delete("token");var q=p.toString();` +
595
+ `var u=location.pathname+(q?"?"+q:"")+location.hash;` +
596
+ `history.replaceState(null,"",u);}}catch(e){}})();</script>`
597
+ return injected.replace("<head>", `<head>${script}`)
598
+ },
599
+ },
600
+ handleHotUpdate: ({ file, server }) => {
601
+ // When an addon frontend file changes, run any registered cleanup
602
+ // hooks BEFORE Vite swaps in the new module. This lets setInterval,
603
+ // requestAnimationFrame, and event listeners be cleared in time to
604
+ // prevent the old instance from calling state setters on the new
605
+ // (or unmounted) React tree.
606
+ let totalCleanups = 0
607
+ for (const addon of addons) {
608
+ if (addon.frontend === undefined) continue
609
+ const frontendPath = addon.frontend.main
610
+ const normalizedFile = file.replace(/\\/g, "/")
611
+ const normalizedFrontend = frontendPath.replace(/\\/g, "/")
612
+ if (
613
+ !normalizedFile.endsWith(normalizedFrontend) &&
614
+ !normalizedFile.includes(normalizedFrontend)
615
+ )
616
+ continue
617
+ const cleared = __SIRENO_RUN_ADDON_CLEANUPS__(addon.name)
618
+ if (cleared > 0) {
619
+ server.config.logger.info(
620
+ `[sirenodeck] cleared ${cleared} cleanup(s) for addon "${addon.name}" before HMR of ${normalizedFile}`,
621
+ )
622
+ }
623
+ totalCleanups += cleared
624
+ }
625
+ // Always return undefined so Vite handles the HMR normally; the
626
+ // cleanups are side effects, not module replacements.
627
+ void totalCleanups
628
+ return undefined
629
+ },
630
+ }
631
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />