@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,486 @@
1
+ import { execFileSync } from "node:child_process"
2
+
3
+ import {
4
+ EXTENSION_INSTALL_URL,
5
+ hasWaylandGnomeSession,
6
+ } from "../providers/active-app/wayland-gnome"
7
+ import type { CommandExecutor } from "../providers/shared"
8
+ import { ACCESSIBILITY_SETTINGS_URL, UDEV_RULES_PATH } from "./types"
9
+
10
+ export { ACCESSIBILITY_SETTINGS_URL } from "./types"
11
+ import {
12
+ type CapabilityName,
13
+ type CapabilityProbe,
14
+ type DesktopSession,
15
+ type PackageManager,
16
+ type ProbeDeps,
17
+ type SystemReport,
18
+ type UdevProbe,
19
+ type ConfigProbe,
20
+ } from "./types"
21
+
22
+ const STREAM_DECK_PRODUCT_IDS = [
23
+ "0060",
24
+ "006c",
25
+ "006d",
26
+ "006e",
27
+ "0080",
28
+ "0084",
29
+ "0086",
30
+ "0090",
31
+ ] as const
32
+
33
+ const PCMAN_PM_BINS: ReadonlyArray<{
34
+ readonly pm: PackageManager
35
+ readonly probe: ReadonlyArray<string>
36
+ }> = [
37
+ { pm: "apt", probe: ["apt-get", "apt"] },
38
+ { pm: "dnf", probe: ["dnf"] },
39
+ { pm: "pacman", probe: ["pacman"] },
40
+ { pm: "zypper", probe: ["zypper"] },
41
+ ]
42
+
43
+ const detectPackageManager = async (
44
+ platform: string,
45
+ executor: CommandExecutor,
46
+ extraFsProbe?: (command: string) => boolean,
47
+ ): Promise<PackageManager> => {
48
+ if (platform === "darwin") {
49
+ const brew = await probeCommand(executor, "brew", extraFsProbe)
50
+ return brew ? "brew" : "none"
51
+ }
52
+ if (platform !== "linux") return "none"
53
+ for (const { pm, probe } of PCMAN_PM_BINS) {
54
+ for (const bin of probe) {
55
+ if (await probeCommand(executor, bin, extraFsProbe)) return pm
56
+ }
57
+ }
58
+ return "none"
59
+ }
60
+
61
+ const detectSession = (env: NodeJS.ProcessEnv): DesktopSession => {
62
+ if (env["XDG_SESSION_TYPE"] === "wayland") return "wayland"
63
+ if (env["XDG_SESSION_TYPE"] === "x11") return "x11"
64
+ if (env["WAYLAND_DISPLAY"] !== undefined && env["WAYLAND_DISPLAY"] !== "") {
65
+ return "wayland"
66
+ }
67
+ if (env["DISPLAY"] !== undefined && env["DISPLAY"] !== "") return "x11"
68
+ return "unknown"
69
+ }
70
+
71
+ const detectStreamDeck = (
72
+ platforms: ReadonlyArray<string>,
73
+ syncRunner: (cmd: string) => string,
74
+ ): { connected: boolean; matched: ReadonlyArray<string> } => {
75
+ if (!platforms.includes("linux")) return { connected: false, matched: [] }
76
+ const candidates = ["lsusb", "/usr/bin/lsusb", "/usr/sbin/lsusb"]
77
+ let raw = ""
78
+ for (const bin of candidates) {
79
+ try {
80
+ raw = syncRunner(bin)
81
+ if (raw.length > 0) break
82
+ } catch {
83
+ // try next candidate
84
+ }
85
+ }
86
+ if (raw.length === 0) return { connected: false, matched: [] }
87
+ const matched: string[] = []
88
+ for (const pid of STREAM_DECK_PRODUCT_IDS) {
89
+ const re = new RegExp(`0fd9:${pid}\\b`, "i")
90
+ if (re.test(raw)) matched.push(pid)
91
+ }
92
+ return { connected: matched.length > 0, matched }
93
+ }
94
+
95
+ const probeViaCommandV = async (
96
+ executor: CommandExecutor,
97
+ command: string,
98
+ ): Promise<boolean> => {
99
+ // ponytail: `command` is a POSIX shell builtin, not a binary.
100
+ // execFile can't run builtins directly — wrap in sh -c so the call
101
+ // works under real executors as well as test mocks. Argument is
102
+ // shell-quoted to keep the call single-token.
103
+ const quoted = `'${command.replaceAll(`'`, `'\\''`)}'`
104
+ const result = await executor.run("sh", ["-c", `command -v ${quoted}`])
105
+ return result.exitCode === 0 && result.stdout.trim().length > 0
106
+ }
107
+
108
+ const probeViaVersion = async (
109
+ executor: CommandExecutor,
110
+ command: string,
111
+ ): Promise<boolean> => {
112
+ try {
113
+ const result = await executor.run(command, ["--version"], {
114
+ timeoutMs: 1_000,
115
+ })
116
+ return result.exitCode === 0
117
+ } catch {
118
+ return false
119
+ }
120
+ }
121
+
122
+ const probeCommand = async (
123
+ executor: CommandExecutor,
124
+ command: string,
125
+ extraFsProbe?: (command: string) => boolean,
126
+ ): Promise<boolean> => {
127
+ if (await probeViaCommandV(executor, command)) return true
128
+ if (extraFsProbe?.(command) === true) return true
129
+ return await probeViaVersion(executor, command)
130
+ }
131
+
132
+ const probeCapability = async (
133
+ name: CapabilityName,
134
+ candidates: ReadonlyArray<string>,
135
+ preferred: string,
136
+ reason: string,
137
+ executor: CommandExecutor,
138
+ extraFsProbe?: (command: string) => boolean,
139
+ ): Promise<CapabilityProbe> => {
140
+ const availability = await Promise.all(
141
+ candidates.map((cmd) => probeCommand(executor, cmd, extraFsProbe)),
142
+ )
143
+ const found = candidates.filter((_, idx) => availability[idx])
144
+ const missing = candidates.filter((_, idx) => !availability[idx])
145
+ return {
146
+ name,
147
+ available: found.length > 0,
148
+ toolInstalled: found.length > 0,
149
+ permission: null,
150
+ missing,
151
+ preferred,
152
+ reason,
153
+ }
154
+ }
155
+
156
+ // ponytail: on macOS the mere presence of `osascript` proves nothing — it ships
157
+ // with the OS, so the capability probes always said "all present" while every
158
+ // UI-scripting call failed at runtime with "osascript is not allowed assistive
159
+ // access (-1719)". Key macros (`keystroke`/`key code`) and window-title reads
160
+ // both need an Accessibility (TCC) grant for whichever binary hosts the script.
161
+ // `UI elements enabled` is the documented, side-effect-free way to ask.
162
+ export const ACCESSIBILITY_HINT =
163
+ "grant Accessibility to your terminal (or the SirenoDeck app) in System Settings → Privacy & Security → Accessibility, then restart it"
164
+
165
+ const UI_ELEMENTS_SCRIPT = `tell application "System Events" to get UI elements enabled`
166
+
167
+ export const hasDarwinAccessibility = async (
168
+ executor: CommandExecutor,
169
+ ): Promise<boolean> => {
170
+ try {
171
+ const result = await executor.run("osascript", ["-e", UI_ELEMENTS_SCRIPT], {
172
+ timeoutMs: 2_000,
173
+ })
174
+ return (
175
+ result.exitCode === 0 && result.stdout.trim().toLowerCase() === "true"
176
+ )
177
+ } catch {
178
+ return false
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Wraps a darwin capability that needs UI scripting: the binary must exist AND
184
+ * the Accessibility grant must be in place, otherwise the capability is
185
+ * reported missing with an actionable reason instead of a false "present".
186
+ */
187
+ const probeDarwinUiScripting = async (
188
+ name: CapabilityName,
189
+ reason: string,
190
+ executor: CommandExecutor,
191
+ extraFsProbe?: (command: string) => boolean,
192
+ ): Promise<CapabilityProbe> => {
193
+ const base = await probeCapability(
194
+ name,
195
+ ["osascript"],
196
+ "osascript",
197
+ reason,
198
+ executor,
199
+ extraFsProbe,
200
+ )
201
+ if (!base.available) return base
202
+ const granted = await hasDarwinAccessibility(executor)
203
+ const permission = {
204
+ label: "Accessibility",
205
+ granted,
206
+ hint: ACCESSIBILITY_HINT,
207
+ settingsUrl: ACCESSIBILITY_SETTINGS_URL,
208
+ }
209
+ if (granted) return { ...base, permission }
210
+ return {
211
+ ...base,
212
+ available: false,
213
+ // toolInstalled stays true: osascript ships with macOS. Only the grant is
214
+ // missing, and `missing` keeps the sentinel so existing callers that gate
215
+ // on it (the install plan) behave as before.
216
+ permission,
217
+ missing: ["accessibility-permission"],
218
+ reason: `osascript is installed but has no Accessibility permission — ${ACCESSIBILITY_HINT}`,
219
+ }
220
+ }
221
+
222
+ const probeKeyMacro = (
223
+ platform: string,
224
+ executor: CommandExecutor,
225
+ extraFsProbe?: (command: string) => boolean,
226
+ ): Promise<CapabilityProbe> => {
227
+ if (platform === "darwin") {
228
+ return probeDarwinUiScripting(
229
+ "keyMacro",
230
+ "macOS uses osascript for key macros.",
231
+ executor,
232
+ extraFsProbe,
233
+ )
234
+ }
235
+ if (platform === "win32") {
236
+ return probeCapability(
237
+ "keyMacro",
238
+ ["powershell"],
239
+ "powershell",
240
+ "Windows uses PowerShell SendInput for key macros.",
241
+ executor,
242
+ extraFsProbe,
243
+ )
244
+ }
245
+ return probeCapability(
246
+ "keyMacro",
247
+ ["ydotool", "wtype", "xdotool", "dotool"],
248
+ "ydotool",
249
+ "Install ydotool (most compositors) or wtype (wlroots only).",
250
+ executor,
251
+ extraFsProbe,
252
+ )
253
+ }
254
+
255
+ const probeClipboard = (
256
+ platform: string,
257
+ executor: CommandExecutor,
258
+ extraFsProbe?: (command: string) => boolean,
259
+ ): Promise<CapabilityProbe> => {
260
+ if (platform === "darwin") {
261
+ return probeCapability(
262
+ "clipboard",
263
+ ["pbcopy"],
264
+ "pbcopy",
265
+ "macOS ships pbcopy.",
266
+ executor,
267
+ extraFsProbe,
268
+ )
269
+ }
270
+ if (platform === "win32") {
271
+ return probeCapability(
272
+ "clipboard",
273
+ ["clip"],
274
+ "clip",
275
+ "Windows ships clip.exe.",
276
+ executor,
277
+ extraFsProbe,
278
+ )
279
+ }
280
+ return probeCapability(
281
+ "clipboard",
282
+ ["wl-copy"],
283
+ "wl-copy",
284
+ "Install wl-clipboard (provides wl-copy).",
285
+ executor,
286
+ extraFsProbe,
287
+ )
288
+ }
289
+
290
+ const probeNotification = (
291
+ platform: string,
292
+ executor: CommandExecutor,
293
+ extraFsProbe?: (command: string) => boolean,
294
+ ): Promise<CapabilityProbe> => {
295
+ if (platform === "darwin") {
296
+ return probeCapability(
297
+ "notification",
298
+ ["osascript"],
299
+ "osascript",
300
+ "macOS uses osascript for notifications.",
301
+ executor,
302
+ extraFsProbe,
303
+ )
304
+ }
305
+ if (platform === "win32") {
306
+ return probeCapability(
307
+ "notification",
308
+ ["powershell"],
309
+ "powershell",
310
+ "Windows uses PowerShell NotifyIcon.",
311
+ executor,
312
+ extraFsProbe,
313
+ )
314
+ }
315
+ return probeCapability(
316
+ "notification",
317
+ ["notify-send"],
318
+ "notify-send",
319
+ "Install libnotify (provides notify-send).",
320
+ executor,
321
+ extraFsProbe,
322
+ )
323
+ }
324
+
325
+ const probeActiveApp = async (
326
+ platform: string,
327
+ env: NodeJS.ProcessEnv,
328
+ executor: CommandExecutor,
329
+ extraFsProbe?: (command: string) => boolean,
330
+ ): Promise<CapabilityProbe> => {
331
+ if (platform === "darwin") {
332
+ // Without the grant the provider still reports the app name and pid (those
333
+ // need no permission) but never a window title, so this is a soft miss.
334
+ return probeDarwinUiScripting(
335
+ "activeApp",
336
+ "macOS uses AppleScript for active-app detection; window titles need Accessibility.",
337
+ executor,
338
+ extraFsProbe,
339
+ )
340
+ }
341
+ if (platform === "win32") {
342
+ return probeCapability(
343
+ "activeApp",
344
+ ["powershell"],
345
+ "powershell",
346
+ "Windows uses PowerShell UIAutomation for active-app detection.",
347
+ executor,
348
+ extraFsProbe,
349
+ )
350
+ }
351
+ const session = detectSession(env)
352
+ if (await hasWaylandGnomeSession({ env, executor })) {
353
+ return {
354
+ name: "activeApp",
355
+ available: true,
356
+ toolInstalled: true,
357
+ permission: null,
358
+ missing: [],
359
+ preferred: "gnome-shell-extension",
360
+ reason: `Wayland GNOME requires the 'Window Calls Extended' extension (${EXTENSION_INSTALL_URL}). The wizard can detect this — install it from the GNOME extensions website and re-run.`,
361
+ }
362
+ }
363
+ if (session === "x11") {
364
+ return probeCapability(
365
+ "activeApp",
366
+ ["xdotool", "xprop"],
367
+ "xdotool",
368
+ "Install xdotool and xprop for active-app detection on X11.",
369
+ executor,
370
+ extraFsProbe,
371
+ )
372
+ }
373
+ return probeCapability(
374
+ "activeApp",
375
+ ["xdotool"],
376
+ "xdotool",
377
+ "No active-app provider detected for this session. Install xdotool or switch to a supported session.",
378
+ executor,
379
+ extraFsProbe,
380
+ )
381
+ }
382
+
383
+ const probeUdev = (
384
+ platform: string,
385
+ rulesPath: string,
386
+ fileExists: (path: string) => boolean,
387
+ readFile: (path: string) => string | null,
388
+ streamDeck: { connected: boolean; matched: ReadonlyArray<string> },
389
+ ): UdevProbe => {
390
+ if (platform !== "linux") {
391
+ return {
392
+ rulesInstalled: true,
393
+ rulesPath,
394
+ streamDeckConnected: false,
395
+ matchedProductIds: [],
396
+ }
397
+ }
398
+ const contents = fileExists(rulesPath) ? readFile(rulesPath) : null
399
+ return {
400
+ rulesInstalled:
401
+ contents !== null &&
402
+ contents.includes('SUBSYSTEM=="usb"') &&
403
+ contents.includes('ATTRS{idVendor}=="0fd9"'),
404
+ rulesPath,
405
+ streamDeckConnected: streamDeck.connected,
406
+ matchedProductIds: streamDeck.matched,
407
+ }
408
+ }
409
+
410
+ // ponytail: an explicit `--config` is the config for this run — report on THAT
411
+ // file, not on the XDG default the user never asked for. The XDG path stays the
412
+ // answer (and the seed target) when no explicit path was given.
413
+ const probeConfig = (
414
+ xdgConfigHome: string,
415
+ fileExists: (path: string) => boolean,
416
+ configPath?: string,
417
+ ): ConfigProbe => {
418
+ const path =
419
+ configPath !== undefined && configPath !== ""
420
+ ? configPath
421
+ : `${xdgConfigHome}/sirenodeck/config.yml`
422
+ return { exists: fileExists(path), path }
423
+ }
424
+
425
+ export const probeAll = async (deps: ProbeDeps): Promise<SystemReport> => {
426
+ const {
427
+ platform,
428
+ homeDir,
429
+ xdgConfigHome,
430
+ env,
431
+ executor,
432
+ extraFsProbe,
433
+ fileExists,
434
+ readFile,
435
+ configPath,
436
+ } = deps
437
+
438
+ const session = detectSession(env)
439
+ const packageManager = await detectPackageManager(
440
+ platform,
441
+ executor,
442
+ extraFsProbe,
443
+ )
444
+
445
+ const lsusbSync = (binary: string): string => {
446
+ try {
447
+ const out = execFileSync(binary, [], {
448
+ encoding: "utf8",
449
+ timeout: 2_000,
450
+ stdio: ["ignore", "pipe", "ignore"],
451
+ })
452
+ return out
453
+ } catch {
454
+ return ""
455
+ }
456
+ }
457
+
458
+ const streamDeck = detectStreamDeck([platform], lsusbSync)
459
+
460
+ const capabilities = {
461
+ keyMacro: await probeKeyMacro(platform, executor, extraFsProbe),
462
+ clipboard: await probeClipboard(platform, executor, extraFsProbe),
463
+ notification: await probeNotification(platform, executor, extraFsProbe),
464
+ activeApp: await probeActiveApp(platform, env, executor, extraFsProbe),
465
+ } as const
466
+
467
+ const udev = probeUdev(
468
+ platform,
469
+ UDEV_RULES_PATH,
470
+ fileExists,
471
+ readFile,
472
+ streamDeck,
473
+ )
474
+ const config = probeConfig(xdgConfigHome, fileExists, configPath)
475
+
476
+ return {
477
+ platform,
478
+ homeDir,
479
+ xdgConfigHome,
480
+ session,
481
+ packageManager,
482
+ capabilities,
483
+ udev,
484
+ config,
485
+ }
486
+ }
@@ -0,0 +1,85 @@
1
+ import { existsSync, readdirSync } from "node:fs"
2
+ import { homedir } from "node:os"
3
+ import { join } from "node:path"
4
+
5
+ export interface RuntimeFeatureProbe {
6
+ readonly available: boolean
7
+ readonly reason?: string
8
+ }
9
+
10
+ // ponytail: lightweight runtime probes for the startup banner. Unlike the
11
+ // full system probe (`probeAll`) these don't run subprocesses for every
12
+ // capability — they just sanity-check that the resources exist. 2s budget
13
+ // per probe; failures fall back to ✗ with a short reason.
14
+
15
+ const REASON_MAX = 40
16
+
17
+ const truncate = (s: string): string =>
18
+ s.length <= REASON_MAX ? s : `${s.slice(0, REASON_MAX - 1)}…`
19
+
20
+ // ponytail: chromium can legitimately live in either of two places. The
21
+ // first-run bootstrap pins PLAYWRIGHT_BROWSERS_PATH to
22
+ // ~/.cache/sirenodeck/playwright, but a `playwright install` run by hand — the
23
+ // documented fix, and what a dev checkout needs — puts it in Playwright's own
24
+ // per-OS default. Checking only the sirenodeck path reported "not installed"
25
+ // on a machine whose renderer was working fine.
26
+ const playwrightDefaultBrowsersDir = (): string => {
27
+ switch (process.platform) {
28
+ case "darwin":
29
+ return join(homedir(), "Library", "Caches", "ms-playwright")
30
+ case "win32":
31
+ return join(process.env["LOCALAPPDATA"] ?? homedir(), "ms-playwright")
32
+ default:
33
+ return join(homedir(), ".cache", "ms-playwright")
34
+ }
35
+ }
36
+
37
+ const hasChromium = (dir: string): boolean => {
38
+ if (!existsSync(dir)) return false
39
+ try {
40
+ return readdirSync(dir).some((e) => e.startsWith("chromium"))
41
+ } catch {
42
+ return false
43
+ }
44
+ }
45
+
46
+ export const probeMediaAccess = async (): Promise<RuntimeFeatureProbe> => {
47
+ const explicit = process.env["PLAYWRIGHT_BROWSERS_PATH"]
48
+ const candidates =
49
+ explicit !== undefined && explicit !== ""
50
+ ? [explicit]
51
+ : [
52
+ join(homedir(), ".cache", "sirenodeck", "playwright"),
53
+ playwrightDefaultBrowsersDir(),
54
+ ]
55
+ if (candidates.some(hasChromium)) return { available: true }
56
+ return { available: false, reason: truncate("chromium not installed") }
57
+ }
58
+
59
+ export const probeCommandExecution = async (): Promise<RuntimeFeatureProbe> => {
60
+ const shell =
61
+ process.platform === "win32"
62
+ ? `${process.env["SystemRoot"] ?? "C:\\Windows"}\\System32\\cmd.exe`
63
+ : "/bin/sh"
64
+ if (!existsSync(shell)) {
65
+ return { available: false, reason: truncate(`shell missing: ${shell}`) }
66
+ }
67
+ return { available: true }
68
+ }
69
+
70
+ export const probeInternetAccess = async (): Promise<RuntimeFeatureProbe> => {
71
+ if (typeof fetch !== "function") {
72
+ return { available: false, reason: "fetch unavailable" }
73
+ }
74
+ try {
75
+ const res = await fetch("https://1.1.1.1/", {
76
+ method: "HEAD",
77
+ signal: AbortSignal.timeout(2_000),
78
+ })
79
+ return res.ok || (res.status >= 200 && res.status < 500)
80
+ ? { available: true }
81
+ : { available: false, reason: truncate(`HTTP ${res.status}`) }
82
+ } catch (err) {
83
+ return { available: false, reason: truncate(`unreachable: ${String(err)}`) }
84
+ }
85
+ }