@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,355 @@
1
+ import { join } from "node:path"
2
+
3
+ import type { ArgumentsCamelCase, CommandModule } from "yargs"
4
+
5
+ import { createLogger } from "@/util/logger"
6
+ import { BIN_NAME, PACKAGE_NAME, VERSION } from "@/version"
7
+
8
+ import { logsCommand } from "./commands/logs"
9
+ import { reload, type ReloadOptions } from "./commands/reload"
10
+ import { restart, type RestartOptions } from "./commands/restart"
11
+ import start, { type StartOptions } from "./commands/start"
12
+ import { status, type StatusOptions } from "./commands/status"
13
+ import { stop, type StopOptions } from "./commands/stop"
14
+ import { systemRequirementsCommand } from "./commands/system-requirements"
15
+ import { snapshotDaemonLog } from "@/util/log-reader"
16
+ import {
17
+ buildStartupBanner,
18
+ printAddonCheckResults,
19
+ printDaemonEvents,
20
+ printDaemonUrl,
21
+ printStartupComplete,
22
+ printStartupFailed,
23
+ waitForFullStart,
24
+ type StartOutcome,
25
+ } from "./startup-display"
26
+ import { runBuiltinAddonChecks } from "@/addon/check-runner"
27
+ import { installPackage, type InstallOptions } from "./commands/install"
28
+ import type { PackageManager } from "./package-manager"
29
+ import { openBrowser } from "@/outputClient/open-browser"
30
+ import { requestDaemonToken } from "@/util/daemon-control"
31
+ import {
32
+ isRunning,
33
+ readPid,
34
+ readRuntimeState,
35
+ resolveDaemonPaths,
36
+ } from "@/util/daemon"
37
+ import { cancel, intro, log } from "./prompt"
38
+
39
+ export interface GlobalOptions {
40
+ verbose?: boolean
41
+ logLevel?: string
42
+ quiet?: boolean
43
+ json?: boolean
44
+ }
45
+
46
+ interface StartArgs extends GlobalOptions {
47
+ config?: string
48
+ port?: number
49
+ emulator?: boolean
50
+ remote?: boolean
51
+ deviceModel?: string
52
+ httpPort?: number
53
+ logs?: boolean
54
+ system?: boolean
55
+ autoopen?: boolean
56
+ }
57
+
58
+ interface StatusArgs extends GlobalOptions {}
59
+ interface StopArgs extends GlobalOptions {}
60
+ interface LogsArgs extends GlobalOptions {
61
+ follow?: boolean
62
+ lines?: number
63
+ }
64
+ interface ReloadArgs extends GlobalOptions {
65
+ logs?: boolean
66
+ }
67
+ interface RestartArgs extends GlobalOptions {
68
+ logs?: boolean
69
+ }
70
+
71
+ interface InstallArgs extends GlobalOptions {
72
+ packageName: string
73
+ config?: string
74
+ global?: boolean
75
+ packageManager?: PackageManager
76
+ }
77
+
78
+ export const buildLogger = (
79
+ argv: ArgumentsCamelCase<GlobalOptions>,
80
+ ): ReturnType<typeof createLogger> => {
81
+ const normalized = argv.logLevel === "none" ? "silent" : argv.logLevel
82
+ const level =
83
+ argv.quiet || normalized === "silent"
84
+ ? "silent"
85
+ : (normalized ?? (argv.verbose ? "debug" : "info"))
86
+ return createLogger({
87
+ verbose: argv.verbose,
88
+ json: argv.json ?? false,
89
+ level,
90
+ })
91
+ }
92
+
93
+ const startCommand: CommandModule<object, StartArgs> = {
94
+ command: "start",
95
+ describe: "Start the sirenodeck daemon",
96
+ builder: (yargs) =>
97
+ yargs
98
+ .option("config", { type: "string", description: "Path to config.yml" })
99
+ .option("port", {
100
+ type: "number",
101
+ description: "Port for the local server (0 = random free port)",
102
+ })
103
+ .option("emulator", {
104
+ type: "boolean",
105
+ default: false,
106
+ description:
107
+ "Run the emulator (full UI with sidebar, toolbar, device selector) at 127.0.0.1. Browser auto-opens unless --no-autoopen.",
108
+ })
109
+ .option("autoopen", {
110
+ type: "boolean",
111
+ default: true,
112
+ description:
113
+ "Auto-open the emulator URL in a browser after start. Pass --no-autoopen for headless / capture runs.",
114
+ })
115
+ .option("remote", {
116
+ type: "boolean",
117
+ default: false,
118
+ description:
119
+ "Open the emulator server to LAN clients. Implies --emulator. Binds 0.0.0.0; prints a QR for the deck-only view with token-gated HTTP.",
120
+ })
121
+ .option("device-model", {
122
+ type: "string",
123
+ description: "Emulator device model (mk2, plus, mini, xl)",
124
+ })
125
+ .option("http-port", {
126
+ type: "number",
127
+ default: 3939,
128
+ description:
129
+ "Port for the prod HTTP server that serves the bundled frontend with WS token injection",
130
+ })
131
+ .option("system", {
132
+ type: "boolean",
133
+ default: false,
134
+ description:
135
+ "Auto-install as system-wide service (requires root). Default: user-level.",
136
+ })
137
+ .option("logs", {
138
+ type: "boolean",
139
+ default: false,
140
+ description: "After start, follow the service log (Ctrl+C to exit)",
141
+ }),
142
+ handler: async (argv) => {
143
+ const logger = buildLogger(argv)
144
+ const options: StartOptions = {
145
+ logger,
146
+ ...(argv.config !== undefined ? { config: argv.config } : {}),
147
+ ...(argv.port !== undefined ? { port: argv.port } : {}),
148
+ ...(argv.emulator !== undefined ? { emulator: argv.emulator } : {}),
149
+ ...(argv.remote !== undefined ? { remote: argv.remote } : {}),
150
+ ...(argv.deviceModel !== undefined
151
+ ? { deviceModel: argv.deviceModel }
152
+ : {}),
153
+ ...(argv.httpPort !== undefined
154
+ ? { httpPort: argv.httpPort as number }
155
+ : {}),
156
+ ...(argv.system === true ? { system: true } : {}),
157
+ ...(argv.autoopen === false ? { noAutoOpen: true } : {}),
158
+ }
159
+ try {
160
+ await buildStartupBanner(
161
+ {
162
+ emulator: argv.emulator === true,
163
+ ...(argv.deviceModel !== undefined
164
+ ? { deviceModel: argv.deviceModel }
165
+ : {}),
166
+ ...(argv.port !== undefined ? { port: argv.port } : {}),
167
+ },
168
+ argv,
169
+ )
170
+ // ponytail: snapshot the daemon log size BEFORE the daemon's
171
+ // restart writes anything new. We use this to surface
172
+ // daemon-emitted warnings inline after the start completes,
173
+ // without reading the full log history.
174
+ const logSnapshot = snapshotDaemonLog()
175
+ const logPath = join(resolveDaemonPaths().runtimeDir, "service.log")
176
+ const startRequestedAt = Date.now()
177
+ const startPromise = start(options)
178
+ const outcome: StartOutcome = await waitForFullStart({
179
+ port: options.port ?? 52937,
180
+ tcpTimeoutMs: 30_000,
181
+ runtimeTimeoutMs: 30_000,
182
+ logPath,
183
+ logSnapshot,
184
+ notBefore: startRequestedAt,
185
+ })
186
+ // ponytail: print events the daemon emitted between the snapshot
187
+ // and now, BEFORE the success/failure line — operator's eye
188
+ // lands on each one even when stdout is a pipe. Silent on 0
189
+ // events.
190
+ printDaemonEvents(outcome.events)
191
+
192
+ if (outcome.runtimeReady && outcome.state !== null) {
193
+ await printDaemonUrl(outcome.state, outcome.token ?? "")
194
+ // ponytail: per-addon requirement checks (playerctl for media, etc.).
195
+ // Never blocks the daemon — failing checks are surfaced as warnings so
196
+ // the operator can act on them without digging into the log.
197
+ printAddonCheckResults(await runBuiltinAddonChecks())
198
+ printStartupComplete()
199
+ } else if (outcome.tcpReady) {
200
+ // ponytail: TCP is bound but the daemon didn't write runtime
201
+ // state in 30s. The daemon is alive but the runtime pipeline
202
+ // is still booting (or stuck). Fail loudly with whatever
203
+ // warnings the daemon emitted so the operator doesn't have to
204
+ // dig into the log file.
205
+ const message =
206
+ "daemon: TCP bound on 52937 but runtime state did not appear in 30s"
207
+ logger.warn(message)
208
+ process.exitCode = 1
209
+ printStartupFailed(message)
210
+ return
211
+ } else {
212
+ // ponytail: TCP never bound. Either the daemon crashed during
213
+ // boot, or port 52937 is held by an unrelated process. Either
214
+ // way, the start failed — the operator needs to know.
215
+ const message = `daemon: port ${options.port ?? 52937} did not accept connections in 30s`
216
+ logger.warn(message)
217
+ process.exitCode = 1
218
+ printStartupFailed(message)
219
+ return
220
+ }
221
+ await startPromise
222
+ } catch (err) {
223
+ printStartupFailed(err)
224
+ const e = err as { issues?: unknown; message?: unknown }
225
+ const message =
226
+ e &&
227
+ typeof e === "object" &&
228
+ "message" in e &&
229
+ typeof e.message === "string"
230
+ ? e.message
231
+ : "command failed"
232
+ logger.error({ err }, message)
233
+ process.exitCode = 1
234
+ }
235
+ },
236
+ }
237
+
238
+ const stopCommand: CommandModule<object, StopArgs> = {
239
+ command: "stop",
240
+ describe: "Stop the running sirenodeck daemon",
241
+ handler: async (argv) => {
242
+ const logger = buildLogger(argv)
243
+ const options: StopOptions = { logger }
244
+ await stop(options)
245
+ },
246
+ }
247
+
248
+ const statusCommand: CommandModule<object, StatusArgs> = {
249
+ command: "status",
250
+ describe: "Check sirenodeck daemon status",
251
+ handler: async (argv) => {
252
+ const logger = buildLogger(argv)
253
+ const options: StatusOptions = { logger }
254
+ await status(options)
255
+ },
256
+ }
257
+
258
+ const configUiCommand: CommandModule<object, GlobalOptions> = {
259
+ command: "config-ui",
260
+ describe: "Open the visual configuration editor",
261
+ handler: async (argv) => {
262
+ const logger = buildLogger(argv)
263
+ const paths = resolveDaemonPaths()
264
+ const pid = readPid(paths)
265
+ const state =
266
+ pid !== null && isRunning(pid) ? readRuntimeState(paths) : null
267
+ if (state === null) {
268
+ intro("sirenodeck config-ui")
269
+ log.error("Daemon is not running")
270
+ cancel("✗ Run `sirenodeck start` first")
271
+ return
272
+ }
273
+ const token = await requestDaemonToken(paths)
274
+ if (token === null) {
275
+ throw new Error("Could not retrieve the daemon token")
276
+ }
277
+ const base = state.configUiUrl
278
+ const url = `${base}${base.includes("?") ? "&" : "?"}token=${encodeURIComponent(token)}#/config`
279
+ log.info(`Config UI: ${url}`)
280
+ openBrowser(url, logger)
281
+ },
282
+ }
283
+
284
+ const reloadCommand: CommandModule<object, ReloadArgs> = {
285
+ command: "reload",
286
+ describe: "Send SIGUSR1 to the daemon (in-place reload)",
287
+ handler: async (argv) => {
288
+ const logger = buildLogger(argv)
289
+ const options: ReloadOptions = { logger }
290
+ await reload(options)
291
+ },
292
+ }
293
+
294
+ const restartCommand: CommandModule<object, RestartArgs> = {
295
+ command: "restart",
296
+ describe: "Restart the sirenodeck daemon",
297
+ handler: async (argv) => {
298
+ const logger = buildLogger(argv)
299
+ const options: RestartOptions = { logger }
300
+ await restart(options)
301
+ },
302
+ }
303
+
304
+ const installCommand: CommandModule<object, InstallArgs> = {
305
+ command: "install <packageName>",
306
+ describe: "Install and configure an addon or theme",
307
+ builder: (yargs) =>
308
+ yargs
309
+ .positional("packageName", {
310
+ type: "string",
311
+ demandOption: true,
312
+ description: "npm package name",
313
+ })
314
+ .option("config", { type: "string", description: "Path to config.yml" })
315
+ .option("global", {
316
+ type: "boolean",
317
+ default: false,
318
+ description: "Install globally",
319
+ })
320
+ .option("package-manager", { choices: ["pnpm", "npm", "yarn"] as const }),
321
+ handler: async (argv) => {
322
+ await installPackage({
323
+ packageName: argv.packageName,
324
+ ...(argv.config !== undefined ? { config: argv.config } : {}),
325
+ ...(argv.global === true ? { global: true } : {}),
326
+ ...(argv.packageManager !== undefined
327
+ ? { packageManager: argv.packageManager }
328
+ : {}),
329
+ } satisfies InstallOptions)
330
+ },
331
+ }
332
+
333
+ export const buildCli = async (): Promise<{
334
+ scriptName: string
335
+ commands: CommandModule<object, GlobalOptions>[]
336
+ packageName: string
337
+ version: string
338
+ }> => {
339
+ return {
340
+ scriptName: BIN_NAME,
341
+ commands: [
342
+ configUiCommand,
343
+ startCommand,
344
+ stopCommand,
345
+ statusCommand,
346
+ restartCommand,
347
+ reloadCommand,
348
+ logsCommand as CommandModule<object, GlobalOptions>,
349
+ systemRequirementsCommand as CommandModule<object, GlobalOptions>,
350
+ installCommand as CommandModule<object, GlobalOptions>,
351
+ ],
352
+ packageName: PACKAGE_NAME,
353
+ version: VERSION,
354
+ }
355
+ }
@@ -0,0 +1,26 @@
1
+ import { existsSync } from "node:fs"
2
+ import { dirname, join } from "node:path"
3
+ import { fileURLToPath } from "node:url"
4
+
5
+ /**
6
+ * Resolve the install root of the distributed bundle:
7
+ * <root>/sirenodeck, <root>/lib/cli/main.mjs, <root>/frontend/dist, <root>/etc/install.json
8
+ *
9
+ * Priority: SIRENO_INSTALL_ROOT (set by the launcher/installer) → walk up from
10
+ * this module looking for the install markers (`etc/install.json`,
11
+ * `frontend/dist/index.html`). Returns null in the dev checkout so callers
12
+ * fall back to the monorepo layout.
13
+ */
14
+ export const resolveInstallRoot = (): string | null => {
15
+ const env = process.env["SIRENO_INSTALL_ROOT"]
16
+ if (env !== undefined && env.length > 0) return env
17
+ let dir = dirname(fileURLToPath(import.meta.url))
18
+ for (let i = 0; i < 8; i += 1) {
19
+ if (existsSync(join(dir, "etc", "install.json"))) return dir
20
+ if (existsSync(join(dir, "frontend", "dist", "index.html"))) return dir
21
+ const parent = dirname(dir)
22
+ if (parent === dir) break
23
+ dir = parent
24
+ }
25
+ return null
26
+ }
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from "node:url"
3
+
4
+ import yargs from "yargs"
5
+ import { hideBin } from "yargs/helpers"
6
+
7
+ import { buildCli } from "./index"
8
+ import { createLogger } from "@/util/logger"
9
+ import { terminateChildren } from "@/util/daemon"
10
+ import { holdsInstanceLock } from "@/util/single-instance"
11
+ import {
12
+ DAEMON_TITLE,
13
+ FOREGROUND_TITLE,
14
+ setProcessTitle,
15
+ startParentDeathWatchdog,
16
+ } from "@/util/process-title"
17
+
18
+ // ponytail: process name shows up in `ps`/`top`/`pstree` so operators can
19
+ // tell which role is holding a port. The detached daemon is `sirenodeck:dm`
20
+ // (12 chars, fits Linux 15-char `comm`); foreground CLI invocations are
21
+ // `sirenodeck:cli`. Naming is diagnostic only — the kill identity gate
22
+ // still relies on cmdline + port + tracked PIDs.
23
+ const isDaemon = process.env["SIRENO_DAEMON_CHILD"] === "1"
24
+ setProcessTitle(isDaemon ? DAEMON_TITLE : FOREGROUND_TITLE)
25
+
26
+ /**
27
+ * Install global handlers for uncaught exceptions and unhandled promise
28
+ * rejections BEFORE any command runs. Without these, an error in an
29
+ * async addon poller or a stray promise rejection in the deck runtime
30
+ * would kill the process with no log output and no exit code.
31
+ *
32
+ * Handlers log at `fatal` level, then exit 1 so supervisors (systemd,
33
+ * launchd, the bin/sirenodeck.js wrapper) can detect the failure.
34
+ *
35
+ * `processExitInProgress` guards against double-exit when both `exit`
36
+ * events fire in sequence (common when child processes are still alive).
37
+ */
38
+ let processExitInProgress = false
39
+
40
+ const killChildrenAndExit = (
41
+ logger: ReturnType<typeof createLogger>,
42
+ code: number,
43
+ ): void => {
44
+ if (processExitInProgress) return
45
+ processExitInProgress = true
46
+ // Only the daemon that holds the instance lock owns the tracked children.
47
+ // Without this, a CLI whose start was refused because a daemon was already
48
+ // running would still run this cleanup on its way out — reading the shared
49
+ // children file and killing the RUNNING daemon's frontend. The start had
50
+ // changed nothing, but the deck went dark for as long as the daemon took to
51
+ // notice and respawn it.
52
+ if (!holdsInstanceLock()) {
53
+ process.exit(code)
54
+ return
55
+ }
56
+ // ponytail: 5s grace matches the run-pipeline's pushBlackFrame + drain delay
57
+ // so the SIGINT handler doesn't cut off the in-flight device clear writes.
58
+ void terminateChildren({ logger, timeoutMs: 5_000 })
59
+ .catch((err: unknown) => {
60
+ logger.warn({ err }, "main: terminate children failed")
61
+ })
62
+ .finally(() => {
63
+ process.exit(code)
64
+ })
65
+ }
66
+
67
+ const installProcessGuards = (
68
+ logger: ReturnType<typeof createLogger>,
69
+ ): void => {
70
+ process.on("uncaughtException", (err, origin) => {
71
+ logger.fatal(
72
+ { err, origin },
73
+ "uncaughtException — exiting to surface the failure",
74
+ )
75
+ killChildrenAndExit(logger, 1)
76
+ })
77
+ process.on("unhandledRejection", (reason, promise) => {
78
+ // ponytail: pino's `{ reason }` field serializes empty objects as `{}`,
79
+ // which hides the actual rejection value during operator triage. The
80
+ // `errSerializer` only fires on the literal key `err`, so dump the
81
+ // reason as a string fallback (so a thrown Error, a string, or a
82
+ // bare object all show up) and log the promise's stack when we can.
83
+ const reasonDump =
84
+ reason instanceof Error
85
+ ? { type: reason.name, message: reason.message, stack: reason.stack }
86
+ : { value: String(reason) }
87
+ const stackDump =
88
+ promise &&
89
+ typeof (promise as unknown as { stack?: unknown }).stack === "string"
90
+ ? (promise as unknown as { stack: string }).stack
91
+ : undefined
92
+ logger.fatal(
93
+ {
94
+ reason: reasonDump,
95
+ ...(stackDump !== undefined ? { stack: stackDump } : {}),
96
+ },
97
+ "unhandledRejection — exiting to surface the failure",
98
+ )
99
+ killChildrenAndExit(logger, 1)
100
+ })
101
+ // ponytail: SIGINT/SIGTERM also reap children — supervisors (systemd,
102
+ // launchd, our `stop`) send SIGTERM and we don't want orphaned vite/playwright.
103
+ process.on("SIGTERM", () => {
104
+ logger.info("main: SIGTERM received")
105
+ killChildrenAndExit(logger, 0)
106
+ })
107
+ process.on("SIGINT", () => {
108
+ logger.info("main: SIGINT received")
109
+ killChildrenAndExit(logger, 0)
110
+ })
111
+ // ponytail: parent-death watchdog — when the dev wrapper dies (tsx watcher
112
+ // killed, `bin/dev.js` SIGKILL'd, ...) the daemon is reparented to init and
113
+ // its vite children keep their ports. The 1s ppid poll catches that and
114
+ // routes through the same teardown as a graceful shutdown. Linux-only and
115
+ // inactive when the daemon already runs under systemd (ppid=1 from boot).
116
+ if (isDaemon) {
117
+ startParentDeathWatchdog({
118
+ logger,
119
+ onOrphan: () => killChildrenAndExit(logger, 0),
120
+ })
121
+ }
122
+ }
123
+
124
+ const main = async (): Promise<void> => {
125
+ const args = process.argv
126
+ const isJson = args.indexOf("--json") !== -1
127
+ const isVerbose =
128
+ args.indexOf("--verbose") !== -1 || args.indexOf("-v") !== -1
129
+ const logger = createLogger({ verbose: isVerbose, json: isJson })
130
+ installProcessGuards(logger)
131
+ const { scriptName, commands, packageName, version } = await buildCli()
132
+
133
+ const parser = yargs(hideBin(process.argv))
134
+ .scriptName(scriptName)
135
+ .usage("$0 <command> [options]")
136
+ .option("verbose", {
137
+ alias: "v",
138
+ type: "boolean",
139
+ description: "Enable verbose debug logging",
140
+ default: false,
141
+ })
142
+ .option("json", {
143
+ type: "boolean",
144
+ description: "Emit logs as JSON (default: human-friendly)",
145
+ default: false,
146
+ })
147
+ .option("log-level", {
148
+ type: "string",
149
+ description:
150
+ "Override log level (trace, debug, info, warn, error, fatal, silent, none)",
151
+ })
152
+ .option("quiet", {
153
+ alias: "q",
154
+ type: "boolean",
155
+ default: false,
156
+ description: "Suppress all logs and the startup banner (silent level)",
157
+ })
158
+ // ponytail: yargs only expands $0 in usage/command strings, not in the
159
+ // demandCommand message — it printed the literal "Run 0 --help". Build the
160
+ // sentence from scriptName instead.
161
+ .demandCommand(1, `Run ${scriptName} --help to see available commands.`)
162
+ .strict()
163
+ .help()
164
+ .alias("help", "h")
165
+ .version(`${packageName} ${version}`)
166
+ .alias("version", "V")
167
+
168
+ for (const cmd of commands) {
169
+ parser.command(cmd)
170
+ }
171
+
172
+ try {
173
+ await parser.parseAsync()
174
+ } catch (error) {
175
+ const e = error as { issues?: unknown; message?: string }
176
+ const message =
177
+ e &&
178
+ typeof e === "object" &&
179
+ "message" in e &&
180
+ typeof e.message === "string"
181
+ ? e.message
182
+ : "command failed"
183
+ if (e && Array.isArray(e.issues)) {
184
+ logger.error({ err: error }, message)
185
+ } else {
186
+ logger.error({ err: error }, "command failed")
187
+ }
188
+ process.exitCode = 1
189
+ }
190
+ }
191
+
192
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
193
+ void main()
194
+ }