@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,275 @@
1
+ import type {
2
+ CapabilityName,
3
+ CapabilityProbe,
4
+ InstallStep,
5
+ InstallStepResult,
6
+ SystemReport,
7
+ SystemReportSummary,
8
+ } from "./types"
9
+
10
+ const SESSION_LABEL: Readonly<Record<string, string>> = {
11
+ wayland: "Wayland",
12
+ x11: "X11",
13
+ unknown: "unknown",
14
+ }
15
+
16
+ // ponytail: ANSI helpers, no dep. `colorette` would pull in transitive types
17
+ // for a single use site — terminal escape sequences are stable. Returns the
18
+ // input untouched when stdout is not a TTY so piped output stays clean.
19
+ const useColor = (): boolean =>
20
+ Boolean(process.stdout.isTTY) && process.env["NO_COLOR"] === undefined
21
+
22
+ const ESC = "\u001b["
23
+ export const color = {
24
+ green: (s: string): string => (useColor() ? `${ESC}32m${s}${ESC}0m` : s),
25
+ red: (s: string): string => (useColor() ? `${ESC}31m${s}${ESC}0m` : s),
26
+ yellow: (s: string): string => (useColor() ? `${ESC}33m${s}${ESC}0m` : s),
27
+ dim: (s: string): string => (useColor() ? `${ESC}2m${s}${ESC}0m` : s),
28
+ }
29
+
30
+ export const stripAnsi = (s: string): string =>
31
+ s.replace(/\u001b\[[0-9;]*m/g, "")
32
+
33
+ export const summarizeReport = (report: SystemReport): SystemReportSummary => {
34
+ const missingCapabilities: Array<
35
+ "keyMacro" | "clipboard" | "notification" | "activeApp"
36
+ > = []
37
+ for (const [name, cap] of Object.entries(report.capabilities)) {
38
+ if (!cap.available) {
39
+ missingCapabilities.push(
40
+ name as "keyMacro" | "clipboard" | "notification" | "activeApp",
41
+ )
42
+ }
43
+ }
44
+
45
+ const lines: string[] = []
46
+ const ok =
47
+ missingCapabilities.length === 0 &&
48
+ report.udev.rulesInstalled &&
49
+ report.config.exists
50
+
51
+ lines.push(
52
+ `Platform: ${report.platform} (${SESSION_LABEL[report.session] ?? "unknown"} session)`,
53
+ )
54
+ lines.push(`Package manager: ${report.packageManager}`)
55
+ lines.push(`Config target: ${report.config.path}`)
56
+
57
+ if (missingCapabilities.length === 0) {
58
+ lines.push("Capabilities: all present")
59
+ } else {
60
+ // ponytail: same two problems the interactive panel had — internal keys
61
+ // ("keyMacro") instead of names a user recognises, and the full remedy
62
+ // repeated once per capability that happens to need the same grant. Lead
63
+ // with the capability and print each distinct remedy once.
64
+ lines.push("Missing capabilities:")
65
+ const remedies: string[] = []
66
+ for (const name of missingCapabilities) {
67
+ const cap = report.capabilities[name]
68
+ const label = CAPABILITY_LABEL[name]
69
+ const permission = cap.permission
70
+ const blocked =
71
+ cap.toolInstalled && permission !== null && !permission.granted
72
+ const state = blocked
73
+ ? `${cap.preferred} installed, ${permission.label} permission not granted`
74
+ : `${cap.preferred} not installed`
75
+ const remedy = blocked
76
+ ? `${permission.label}: ${permission.hint}`
77
+ : cap.reason
78
+ if (!remedies.includes(remedy)) remedies.push(remedy)
79
+ lines.push(` - ${label}: ${state} [${remedies.indexOf(remedy) + 1}]`)
80
+ }
81
+ remedies.forEach((remedy, index) => {
82
+ lines.push(` [${index + 1}] ${remedy}`)
83
+ })
84
+ }
85
+
86
+ if (report.platform === "linux") {
87
+ lines.push(
88
+ report.udev.rulesInstalled
89
+ ? "udev rules: installed at " + report.udev.rulesPath
90
+ : "udev rules: missing at " + report.udev.rulesPath,
91
+ )
92
+ lines.push(
93
+ report.udev.streamDeckConnected
94
+ ? "Stream Deck: connected (" +
95
+ report.udev.matchedProductIds.join(", ") +
96
+ ")"
97
+ : "Stream Deck: not detected",
98
+ )
99
+ }
100
+
101
+ lines.push(
102
+ report.config.exists
103
+ ? "Config: present"
104
+ : "Config: missing (run `sirenodeck system-requirements` to seed)",
105
+ )
106
+
107
+ return {
108
+ ok,
109
+ session: report.session,
110
+ packageManager: report.packageManager,
111
+ missingCapabilities,
112
+ udevMissing: !report.udev.rulesInstalled,
113
+ configMissing: !report.config.exists,
114
+ configPath: report.config.path,
115
+ streamDeckConnected: report.udev.streamDeckConnected,
116
+ lines,
117
+ }
118
+ }
119
+
120
+ // ponytail: the panel used to lead with the tool name, so macOS printed
121
+ // "osascript" three times — once per capability that happens to be driven by
122
+ // AppleScript — and repeated the same 200-character Accessibility hint twice.
123
+ // The capability is what the user cares about and the tool is an
124
+ // implementation detail, so capability now leads and the long hints collapse
125
+ // into numbered footnotes shared by every row that needs them.
126
+ const CAPABILITY_LABEL: Readonly<Record<CapabilityName, string>> = {
127
+ keyMacro: "Key macros",
128
+ clipboard: "Clipboard",
129
+ notification: "Notifications",
130
+ activeApp: "Active app",
131
+ }
132
+
133
+ const CAPABILITY_ORDER: ReadonlyArray<CapabilityName> = [
134
+ "keyMacro",
135
+ "clipboard",
136
+ "notification",
137
+ "activeApp",
138
+ ]
139
+
140
+ const FOOTNOTE_WIDTH = 64
141
+
142
+ const wrapText = (text: string, width: number): string[] => {
143
+ const words = text.split(/\s+/).filter((w) => w.length > 0)
144
+ const lines: string[] = []
145
+ let current = ""
146
+ for (const word of words) {
147
+ if (current.length === 0) {
148
+ current = word
149
+ } else if (current.length + 1 + word.length <= width) {
150
+ current = `${current} ${word}`
151
+ } else {
152
+ lines.push(current)
153
+ current = word
154
+ }
155
+ }
156
+ if (current.length > 0) lines.push(current)
157
+ return lines.length > 0 ? lines : [""]
158
+ }
159
+
160
+ interface CapabilityRow {
161
+ readonly mark: string
162
+ readonly label: string
163
+ readonly tool: string
164
+ readonly status: string
165
+ readonly note: number | null
166
+ }
167
+
168
+ /**
169
+ * Splits a capability into the two independent facts the old renderer
170
+ * conflated: whether the tool is installed, and whether it has the permission
171
+ * it needs. A denied grant is not "Not installed".
172
+ */
173
+ const describeCapability = (
174
+ cap: CapabilityProbe,
175
+ ): { status: string; detail: string | null } => {
176
+ if (!cap.toolInstalled) {
177
+ return { status: color.red("not installed"), detail: cap.reason }
178
+ }
179
+ const permission = cap.permission
180
+ if (permission === null) {
181
+ return { status: color.green("installed"), detail: null }
182
+ }
183
+ if (permission.granted) {
184
+ return {
185
+ status: `${color.green("installed")}${color.dim(" · ")}${color.green(`${permission.label} granted`)}`,
186
+ detail: null,
187
+ }
188
+ }
189
+ return {
190
+ status: `${color.green("installed")}${color.dim(" · ")}${color.red(`${permission.label} not granted`)}`,
191
+ detail: `${permission.label} permission — ${permission.hint}.`,
192
+ }
193
+ }
194
+
195
+ export const formatCapabilityPanel = (
196
+ capabilities: Readonly<Record<CapabilityName, CapabilityProbe>>,
197
+ ): string => {
198
+ const footnotes: string[] = []
199
+ const footnoteFor = (detail: string): number => {
200
+ const existing = footnotes.indexOf(detail)
201
+ if (existing !== -1) return existing + 1
202
+ footnotes.push(detail)
203
+ return footnotes.length
204
+ }
205
+
206
+ const rows: CapabilityRow[] = []
207
+ for (const name of CAPABILITY_ORDER) {
208
+ const cap = capabilities[name]
209
+ if (cap === undefined) continue
210
+ const { status, detail } = describeCapability(cap)
211
+ rows.push({
212
+ mark: cap.available ? color.green("●") : color.red("○"),
213
+ label: CAPABILITY_LABEL[name],
214
+ tool: cap.preferred,
215
+ status,
216
+ note: detail === null ? null : footnoteFor(detail),
217
+ })
218
+ }
219
+
220
+ const labelWidth = Math.max(...rows.map((r) => r.label.length))
221
+ const toolWidth = Math.max(...rows.map((r) => r.tool.length))
222
+
223
+ const lines = rows.map((r) => {
224
+ const label = `${r.label}:`.padEnd(labelWidth + 2)
225
+ const tool = color.dim(r.tool.padEnd(toolWidth))
226
+ const marker = r.note === null ? "" : color.dim(` [${r.note}]`)
227
+ return `${r.mark} ${label} ${tool} ${r.status}${marker}`
228
+ })
229
+
230
+ if (footnotes.length > 0) {
231
+ lines.push("")
232
+ footnotes.forEach((detail, index) => {
233
+ const wrapped = wrapText(detail, FOOTNOTE_WIDTH)
234
+ wrapped.forEach((line, lineIndex) => {
235
+ lines.push(
236
+ lineIndex === 0
237
+ ? `${color.dim(`[${index + 1}]`)} ${line}`
238
+ : ` ${line}`,
239
+ )
240
+ })
241
+ })
242
+ }
243
+
244
+ return lines.join("\n")
245
+ }
246
+
247
+ export const formatStepInstructions = (step: InstallStep): string => {
248
+ if (step.manualInstructions.length > 0) return step.manualInstructions
249
+ if (step.packageManager === "none") {
250
+ return `No package manager detected. Install manually: ${step.packages.join(", ")}.`
251
+ }
252
+ const verb =
253
+ step.packageManager === "brew"
254
+ ? "brew install"
255
+ : `sudo ${step.packageManager} install -y`
256
+ return `${verb} ${step.packages.join(" ")}`
257
+ }
258
+
259
+ export const formatSummaryLine = (line: string): string => line
260
+
261
+ export const formatResultLine = (
262
+ step: InstallStep,
263
+ result: InstallStepResult,
264
+ ): string => {
265
+ switch (result) {
266
+ case "installed":
267
+ return `${color.green("✓")} ${step.title}`
268
+ case "skipped":
269
+ return `${color.dim("·")} ${step.title} ${color.dim("(skipped)")}`
270
+ case "failed":
271
+ return `${color.red("✗")} ${step.title} ${color.red(`(failed — see ${step.manualInstructions.length > 0 ? "instructions below" : "logs"})`)}`
272
+ case "manual":
273
+ return `${color.dim("→")} ${step.title} ${color.dim(`(manual — ${step.manualInstructions.slice(0, 60)}…)`)}`
274
+ }
275
+ }
@@ -0,0 +1,45 @@
1
+ export {
2
+ type CapabilityName,
3
+ type CapabilityProbe,
4
+ type ConfigProbe,
5
+ type DesktopSession,
6
+ type InstallStep,
7
+ type InstallStepResult,
8
+ type PackageManager,
9
+ type ProbeDeps,
10
+ type SystemReport,
11
+ type SystemReportSummary,
12
+ type UdevProbe,
13
+ type WizardOutcome,
14
+ UDEV_RULES_PATH,
15
+ } from "./types"
16
+
17
+ export {
18
+ probeAll,
19
+ ACCESSIBILITY_HINT,
20
+ ACCESSIBILITY_SETTINGS_URL,
21
+ hasDarwinAccessibility,
22
+ } from "./probe"
23
+ export { probeAllCached, resetProbeCache } from "./probe-cache"
24
+ export {
25
+ probeMediaAccess,
26
+ probeCommandExecution,
27
+ probeInternetAccess,
28
+ type RuntimeFeatureProbe,
29
+ } from "./runtime-features"
30
+ export {
31
+ buildInstallPlan,
32
+ needsConfigSeed,
33
+ DARWIN_ACCESSIBILITY_STEP_ID,
34
+ } from "./plan"
35
+ export {
36
+ formatCapabilityPanel,
37
+ formatResultLine,
38
+ formatStepInstructions,
39
+ formatSummaryLine,
40
+ summarizeReport,
41
+ } from "./format"
42
+ export { openSettingsUrl } from "./open-settings"
43
+ export { defaultConfigSourcePath, seedDefaultConfig } from "./config-seed"
44
+ export { isSudoNopasswd, runWithSudo, capturePassword } from "./sudo"
45
+ export type { SudoRunResult } from "./sudo"
@@ -0,0 +1,26 @@
1
+ import type { CommandExecutor } from "../providers/shared"
2
+
3
+ /**
4
+ * Opens a System Settings pane by its `x-apple.systempreferences:` deep link.
5
+ *
6
+ * ponytail: the Accessibility grant is the one setup step no package manager
7
+ * can perform, and the old flow just printed a System Settings → Privacy &
8
+ * Security → Accessibility breadcrumb and left the user to navigate it. macOS
9
+ * can jump straight to the pane, so offer that instead of prose.
10
+ *
11
+ * Returns false (never throws) on non-darwin platforms or when `open` fails —
12
+ * the caller falls back to printing the manual instructions.
13
+ */
14
+ export const openSettingsUrl = async (
15
+ executor: CommandExecutor,
16
+ url: string,
17
+ platform: string = process.platform,
18
+ ): Promise<boolean> => {
19
+ if (platform !== "darwin") return false
20
+ try {
21
+ const result = await executor.run("open", [url])
22
+ return result.exitCode === 0
23
+ } catch {
24
+ return false
25
+ }
26
+ }
@@ -0,0 +1,230 @@
1
+ import { UDEV_RULES } from "../../device/linux-udev"
2
+ import { EXTENSION_INSTALL_URL } from "../providers/active-app/wayland-gnome"
3
+ import {
4
+ type CapabilityName,
5
+ type InstallStep,
6
+ type PackageManager,
7
+ type SystemReport,
8
+ ACCESSIBILITY_SETTINGS_URL,
9
+ UDEV_RULES_PATH,
10
+ } from "./types"
11
+
12
+ interface PackageMap {
13
+ readonly apt: ReadonlyArray<string>
14
+ readonly dnf: ReadonlyArray<string>
15
+ readonly pacman: ReadonlyArray<string>
16
+ readonly zypper: ReadonlyArray<string>
17
+ readonly brew: ReadonlyArray<string>
18
+ }
19
+
20
+ const KEYMACRO_PACKAGES: PackageMap = {
21
+ apt: ["ydotool"],
22
+ dnf: ["ydotool"],
23
+ pacman: ["ydotool"],
24
+ zypper: ["ydotool"],
25
+ brew: [],
26
+ }
27
+
28
+ const CLIPBOARD_WAYLAND_PACKAGES: PackageMap = {
29
+ apt: ["wl-clipboard"],
30
+ dnf: ["wl-clipboard"],
31
+ pacman: ["wl-clipboard"],
32
+ zypper: ["wl-clipboard"],
33
+ brew: [],
34
+ }
35
+
36
+ const CLIPBOARD_X11_PACKAGES: PackageMap = {
37
+ apt: ["xclip"],
38
+ dnf: ["xclip"],
39
+ pacman: ["xclip"],
40
+ zypper: ["xclip"],
41
+ brew: [],
42
+ }
43
+
44
+ const NOTIFICATION_PACKAGES: PackageMap = {
45
+ apt: ["libnotify-bin"],
46
+ dnf: ["libnotify"],
47
+ pacman: ["libnotify"],
48
+ zypper: ["libnotify"],
49
+ brew: [],
50
+ }
51
+
52
+ const ACTIVEAPP_X11_PACKAGES: PackageMap = {
53
+ apt: ["xdotool", "x11-utils"],
54
+ dnf: ["xdotool", "xorg-x11-utils"],
55
+ pacman: ["xdotool"],
56
+ zypper: ["xdotool"],
57
+ brew: [],
58
+ }
59
+
60
+ const resolvePackages = (
61
+ map: PackageMap,
62
+ pm: PackageManager,
63
+ ): ReadonlyArray<string> => {
64
+ if (pm === "none") return []
65
+ return map[pm]
66
+ }
67
+
68
+ const capId = (name: CapabilityName): string => `cap:${name}`
69
+
70
+ const udevInstruction = (rulesPath: string): string =>
71
+ `sudo tee ${rulesPath} > /dev/null <<'EOF'
72
+ ${UDEV_RULES.trimEnd()}
73
+ EOF
74
+ sudo udevadm control --reload-rules && sudo udevadm trigger`
75
+
76
+ const buildCapabilityStep = (
77
+ name: CapabilityName,
78
+ title: string,
79
+ description: string,
80
+ packages: ReadonlyArray<string>,
81
+ pm: PackageManager,
82
+ verifyCommand: string,
83
+ ): InstallStep => ({
84
+ id: capId(name),
85
+ capability: name,
86
+ title,
87
+ description,
88
+ packageManager: pm,
89
+ packages,
90
+ sudo: pm !== "brew",
91
+ manualOnly: packages.length === 0,
92
+ manualInstructions:
93
+ packages.length === 0
94
+ ? `No supported package manager found for this platform. Install ${name} manually for your distro.`
95
+ : pm === "none"
96
+ ? `No package manager detected. Install manually: ${packages.join(", ")}.`
97
+ : "",
98
+ ...(verifyCommand.length > 0 ? { verifyCommand } : {}),
99
+ })
100
+
101
+ const buildUdevStep = (streamDeckConnected: boolean): InstallStep => ({
102
+ id: "udev:rules",
103
+ capability: "udev",
104
+ title: "Stream Deck udev rules",
105
+ description: streamDeckConnected
106
+ ? "Stream Deck detected but udev rules are missing. Without them, the daemon can't access the device without sudo."
107
+ : "Install udev rules so a Stream Deck can be accessed without sudo when you plug one in.",
108
+ packageManager: "none",
109
+ packages: [],
110
+ sudo: true,
111
+ manualOnly: false,
112
+ manualInstructions: udevInstruction(UDEV_RULES_PATH),
113
+ verifyCommand: `test -f ${UDEV_RULES_PATH} && echo installed || echo missing`,
114
+ })
115
+
116
+ const buildGnomeExtensionStep = (): InstallStep => ({
117
+ id: "cap:activeApp:gnome-extension",
118
+ capability: "activeApp",
119
+ title: "GNOME 'Window Calls Extended' extension",
120
+ description: `Wayland GNOME requires this extension for active-app detection. Install from ${EXTENSION_INSTALL_URL}.`,
121
+ packageManager: "none",
122
+ packages: [],
123
+ sudo: false,
124
+ manualOnly: true,
125
+ manualInstructions: `Open ${EXTENSION_INSTALL_URL} in your browser, toggle the extension ON, then log out / log in (or restart GNOME Shell).`,
126
+ verifyCommand: undefined,
127
+ })
128
+
129
+ // ponytail: macOS UI scripting is gated by a TCC grant that no package manager
130
+ // can install, so this is manual-only. Before this step the wizard had no
131
+ // darwin branch at all — it printed "Capabilities: all present" while every
132
+ // keystroke silently failed with -1719.
133
+ export const DARWIN_ACCESSIBILITY_STEP_ID = "cap:darwin:accessibility"
134
+
135
+ const buildDarwinAccessibilityStep = (): InstallStep => ({
136
+ id: DARWIN_ACCESSIBILITY_STEP_ID,
137
+ capability: "keyMacro",
138
+ title: "macOS Accessibility permission",
139
+ description:
140
+ "Key macros and window-title detection drive the UI through AppleScript, which macOS gates behind Accessibility.",
141
+ packageManager: "none",
142
+ packages: [],
143
+ sudo: false,
144
+ manualOnly: true,
145
+ manualInstructions: `Open the pane directly: open "${ACCESSIBILITY_SETTINGS_URL}"
146
+ Then enable the app running sirenodeck (your terminal, or SirenoDeck.app) and restart it.
147
+ Verify with: osascript -e 'tell application "System Events" to get UI elements enabled' # should print true`,
148
+ verifyCommand: `osascript -e 'tell application "System Events" to get UI elements enabled'`,
149
+ })
150
+
151
+ export const buildInstallPlan = (report: SystemReport): InstallStep[] => {
152
+ const steps: InstallStep[] = []
153
+ const { platform, session, packageManager, capabilities, udev } = report
154
+
155
+ if (
156
+ platform === "darwin" &&
157
+ (!capabilities.keyMacro.available || !capabilities.activeApp.available)
158
+ ) {
159
+ steps.push(buildDarwinAccessibilityStep())
160
+ }
161
+
162
+ if (capabilities.keyMacro.missing.length > 0 && platform === "linux") {
163
+ steps.push(
164
+ buildCapabilityStep(
165
+ "keyMacro",
166
+ "Key macro tool (ydotool)",
167
+ capabilities.keyMacro.reason,
168
+ resolvePackages(KEYMACRO_PACKAGES, packageManager),
169
+ packageManager,
170
+ "which ydotool || which wtype || which xdotool",
171
+ ),
172
+ )
173
+ }
174
+
175
+ if (capabilities.clipboard.missing.length > 0 && platform === "linux") {
176
+ const map =
177
+ session === "wayland"
178
+ ? CLIPBOARD_WAYLAND_PACKAGES
179
+ : CLIPBOARD_X11_PACKAGES
180
+ steps.push(
181
+ buildCapabilityStep(
182
+ "clipboard",
183
+ session === "wayland" ? "wl-clipboard (wl-copy)" : "xclip",
184
+ capabilities.clipboard.reason,
185
+ resolvePackages(map, packageManager),
186
+ packageManager,
187
+ session === "wayland" ? "which wl-copy" : "which xclip",
188
+ ),
189
+ )
190
+ }
191
+
192
+ if (capabilities.notification.missing.length > 0 && platform === "linux") {
193
+ steps.push(
194
+ buildCapabilityStep(
195
+ "notification",
196
+ "Notification tool (notify-send)",
197
+ capabilities.notification.reason,
198
+ resolvePackages(NOTIFICATION_PACKAGES, packageManager),
199
+ packageManager,
200
+ "which notify-send",
201
+ ),
202
+ )
203
+ }
204
+
205
+ if (capabilities.activeApp.missing.length > 0 && platform === "linux") {
206
+ if (session === "wayland") {
207
+ steps.push(buildGnomeExtensionStep())
208
+ } else if (session === "x11") {
209
+ steps.push(
210
+ buildCapabilityStep(
211
+ "activeApp",
212
+ "Active-app detection (xdotool, xprop)",
213
+ capabilities.activeApp.reason,
214
+ resolvePackages(ACTIVEAPP_X11_PACKAGES, packageManager),
215
+ packageManager,
216
+ "which xdotool",
217
+ ),
218
+ )
219
+ }
220
+ }
221
+
222
+ if (platform === "linux" && !udev.rulesInstalled) {
223
+ steps.push(buildUdevStep(udev.streamDeckConnected))
224
+ }
225
+
226
+ return steps
227
+ }
228
+
229
+ export const needsConfigSeed = (report: SystemReport): boolean =>
230
+ !report.config.exists
@@ -0,0 +1,34 @@
1
+ import { probeAll, type ProbeDeps, type SystemReport } from "."
2
+
3
+ let cached: { report: SystemReport; at: number; key: string } | null = null
4
+ const DEFAULT_TTL_MS = 5_000
5
+
6
+ // ponytail: the config path is the one dep that differs between two call sites
7
+ // in the same run — the banner probes the XDG default while the first-run gate
8
+ // probes the explicit --config. Keying on it stops the first probe's
9
+ // "Config: missing" from being replayed to the second.
10
+ const cacheKey = (deps: ProbeDeps): string =>
11
+ `${deps.platform}\u0000${deps.xdgConfigHome}\u0000${deps.configPath ?? ""}`
12
+
13
+ // ponytail: single-slot TTL cache. probeAll runs subprocesses (which, sudo,
14
+ // lsusb, ...); the startup banner and runFirstRunCheckIfNeeded both probe the
15
+ // same machine within ms of each other — caching avoids paying for it twice.
16
+ // Single-slot because a CLI invocation probes one machine. Reset between runs
17
+ // via resetProbeCache() for tests.
18
+ export const probeAllCached = async (
19
+ deps: ProbeDeps,
20
+ ttlMs: number = DEFAULT_TTL_MS,
21
+ ): Promise<SystemReport> => {
22
+ const now = Date.now()
23
+ const key = cacheKey(deps)
24
+ if (cached !== null && cached.key === key && now - cached.at < ttlMs) {
25
+ return cached.report
26
+ }
27
+ const report = await probeAll(deps)
28
+ cached = { report, at: now, key }
29
+ return report
30
+ }
31
+
32
+ export const resetProbeCache = (): void => {
33
+ cached = null
34
+ }