@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,73 @@
1
+ import type pino from "pino"
2
+
3
+ import type { CommandExecutor } from "../shared"
4
+ import { type SessionProvider, type SessionState } from "../session"
5
+
6
+ export interface WindowsSessionDeps {
7
+ readonly executor: CommandExecutor
8
+ readonly logger: pino.Logger
9
+ readonly pollIntervalMs?: number
10
+ }
11
+
12
+ const toState = (locked: boolean): SessionState =>
13
+ locked ? "locked" : "unlocked"
14
+
15
+ const PS_GET_LOGONUI = `if (Get-Process logonui -ErrorAction SilentlyContinue) { 'true' } else { 'false' }`
16
+
17
+ const parseLockResult = (raw: string): boolean =>
18
+ raw.trim().toLowerCase() === "true"
19
+
20
+ export const createWindowsSessionProvider = async (
21
+ deps: WindowsSessionDeps,
22
+ ): Promise<SessionProvider> => {
23
+ const listeners = new Set<(s: SessionState) => void>()
24
+ let state: SessionState = "unknown"
25
+ let stopped = false
26
+ const pollMs = deps.pollIntervalMs ?? 5_000
27
+
28
+ const tick = async (): Promise<SessionState> => {
29
+ try {
30
+ const result = await deps.executor.run(
31
+ "powershell",
32
+ ["-NoProfile", "-Command", PS_GET_LOGONUI],
33
+ { timeoutMs: 2_000 },
34
+ )
35
+ if (result.exitCode !== 0) return state
36
+ return toState(parseLockResult(result.stdout))
37
+ } catch (err) {
38
+ deps.logger.debug({ err }, "session: powershell failed")
39
+ return state
40
+ }
41
+ }
42
+
43
+ state = await tick()
44
+ let interval: ReturnType<typeof setInterval> | null = setInterval(() => {
45
+ if (stopped) return
46
+ void tick().then((s) => {
47
+ if (stopped) return
48
+ if (s !== state) {
49
+ state = s
50
+ for (const l of listeners) l(state)
51
+ }
52
+ })
53
+ }, pollMs)
54
+
55
+ return {
56
+ getState() {
57
+ return state
58
+ },
59
+ subscribe(handler) {
60
+ listeners.add(handler)
61
+ return () => {
62
+ listeners.delete(handler)
63
+ }
64
+ },
65
+ async stop() {
66
+ stopped = true
67
+ if (interval !== null) {
68
+ clearInterval(interval)
69
+ interval = null
70
+ }
71
+ },
72
+ }
73
+ }
@@ -0,0 +1,89 @@
1
+ import type pino from "pino"
2
+
3
+ import { noopUnsubscribe } from "./shared"
4
+
5
+ export type SessionState = "locked" | "unlocked" | "unknown"
6
+
7
+ export interface SessionProvider {
8
+ getState(): SessionState
9
+ subscribe(handler: (state: SessionState) => void): () => void
10
+ stop(): Promise<void>
11
+ }
12
+
13
+ export const createNullSessionProvider = (
14
+ logger?: pino.Logger,
15
+ ): SessionProvider => {
16
+ if (logger) {
17
+ logger.warn(
18
+ { provider: "session" },
19
+ "OS session provider unavailable, using null provider",
20
+ )
21
+ }
22
+ return {
23
+ getState() {
24
+ return "unknown"
25
+ },
26
+ subscribe() {
27
+ return noopUnsubscribe
28
+ },
29
+ async stop() {
30
+ return
31
+ },
32
+ }
33
+ }
34
+
35
+ export interface CreateSessionProviderOptions {
36
+ readonly platform?: NodeJS.Platform
37
+ readonly dbus?: import("./shared").LinuxDbusBus
38
+ readonly executor?: import("./shared").CommandExecutor
39
+ readonly logger: pino.Logger
40
+ readonly idleMs?: number
41
+ }
42
+
43
+ export const createSessionProvider = async (
44
+ options: CreateSessionProviderOptions,
45
+ ): Promise<SessionProvider> => {
46
+ // ponytail: capture mode — force unlocked so screenshots show the real
47
+ // decks instead of the lock screen. Set by capture-decks.mjs.
48
+ if (process.env["SIRENO_CAPTURE_UNLOCKED"] === "1") {
49
+ return {
50
+ getState: () => "unlocked" as const,
51
+ subscribe: () => noopUnsubscribe,
52
+ stop: async () => {},
53
+ }
54
+ }
55
+ const platform = options.platform ?? process.platform
56
+ if (platform === "linux") {
57
+ // ponytail: let createLinuxSessionProvider self-own a dbus session bus when
58
+ // none is injected (mirrors active-app/wayland-gnome.ts:62-74). Returning a
59
+ // null provider here turned every Linux run into a silent no-op, which is
60
+ // why core:lock never fired.
61
+ const { createLinuxSessionProvider } = await import("./session/linux")
62
+ return createLinuxSessionProvider({
63
+ ...(options.dbus !== undefined ? { dbus: options.dbus } : {}),
64
+ logger: options.logger,
65
+ ...(options.idleMs !== undefined ? { idleMs: options.idleMs } : {}),
66
+ })
67
+ }
68
+ if (platform === "darwin") {
69
+ if (options.executor === undefined) {
70
+ return createNullSessionProvider(options.logger)
71
+ }
72
+ const { createDarwinSessionProvider } = await import("./session/darwin")
73
+ return createDarwinSessionProvider({
74
+ executor: options.executor,
75
+ logger: options.logger,
76
+ })
77
+ }
78
+ if (platform === "win32") {
79
+ if (options.executor === undefined) {
80
+ return createNullSessionProvider(options.logger)
81
+ }
82
+ const { createWindowsSessionProvider } = await import("./session/windows")
83
+ return createWindowsSessionProvider({
84
+ executor: options.executor,
85
+ logger: options.logger,
86
+ })
87
+ }
88
+ return createNullSessionProvider(options.logger)
89
+ }
@@ -0,0 +1,65 @@
1
+ import type pino from "pino"
2
+
3
+ import { ProviderError } from "./error"
4
+
5
+ export interface CommandExecutor {
6
+ run(
7
+ command: string,
8
+ args: ReadonlyArray<string>,
9
+ options?: { timeoutMs?: number },
10
+ ): Promise<{ exitCode: number; stdout: string; stderr: string }>
11
+ }
12
+
13
+ export interface LinuxDbusBus {
14
+ getProxyObject(
15
+ serviceName: string,
16
+ objectPath: string,
17
+ ): Promise<LinuxDbusProxyObject>
18
+ disconnect?(): void
19
+ }
20
+
21
+ export interface LinuxDbusProxyObject {
22
+ getInterface(name: string): LinuxDbusInterface
23
+ }
24
+
25
+ export interface LinuxDbusInterface {
26
+ Eval?(script: string): Promise<unknown>
27
+ GetActive?(): Promise<boolean>
28
+ GetIdletime?(): Promise<number>
29
+ FocusClass?(): Promise<string>
30
+ FocusTitle?(): Promise<string>
31
+ on?(event: string, handler: (...args: unknown[]) => void): void
32
+ off?(event: string, handler: (...args: unknown[]) => void): void
33
+ }
34
+
35
+ export const withTimeout = async <T>(
36
+ promise: Promise<T>,
37
+ ms: number,
38
+ ): Promise<T> => {
39
+ let timer: ReturnType<typeof setTimeout> | null = null
40
+ const timeout = new Promise<never>((_resolve, reject) => {
41
+ timer = setTimeout(() => {
42
+ reject(new ProviderError("TIMEOUT", `Operation timed out after ${ms}ms`))
43
+ }, ms)
44
+ })
45
+ try {
46
+ return await Promise.race([promise, timeout])
47
+ } finally {
48
+ if (timer !== null) clearTimeout(timer)
49
+ }
50
+ }
51
+
52
+ export const noopUnsubscribe = (): void => undefined
53
+
54
+ export const logNull = (
55
+ logger: pino.Logger | undefined,
56
+ name: string,
57
+ reason: string,
58
+ ): void => {
59
+ if (logger) {
60
+ logger.warn(
61
+ { provider: name, reason },
62
+ "OS provider unavailable, using null provider",
63
+ )
64
+ }
65
+ }
@@ -0,0 +1,235 @@
1
+ import type { CommandExecutor } from "./providers/shared"
2
+ import {
3
+ ACCESSIBILITY_HINT,
4
+ hasDarwinAccessibility,
5
+ } from "./setup-wizard/probe"
6
+
7
+ export type SystemCapability = "keyMacro" | "clipboard" | "notification"
8
+
9
+ /**
10
+ * Stand-in used in `missingCommands` when the capability is blocked by an OS
11
+ * permission rather than a missing binary — there is nothing to install.
12
+ */
13
+ export const PERMISSION_SENTINEL = "accessibility-permission"
14
+
15
+ export interface CapabilityRequirement {
16
+ readonly name: SystemCapability
17
+ readonly commands: ReadonlyArray<string>
18
+ readonly reason: string
19
+ }
20
+
21
+ export interface CapabilityStatus {
22
+ readonly available: boolean
23
+ readonly commands: ReadonlyArray<string>
24
+ readonly missingCommands: ReadonlyArray<string>
25
+ readonly reason: string
26
+ readonly preferred: string
27
+ }
28
+
29
+ export type RequirementsCheckResult = Readonly<
30
+ Record<SystemCapability, CapabilityStatus>
31
+ >
32
+
33
+ export interface RequirementsCheckDeps {
34
+ readonly platform: string
35
+ readonly executor: CommandExecutor
36
+ readonly env?: NodeJS.ProcessEnv
37
+ // ponytail: fallback probe for stripped-PATH environments
38
+ // (systemd/launchd/IDE). `which` may return nothing even when the binary
39
+ // exists on disk at a non-default bin dir. The caller wires this with a
40
+ // statSync-based probe over `/usr/local/bin`, `~/.local/bin`, etc.
41
+ readonly extraFsProbe?: (command: string) => boolean
42
+ }
43
+
44
+ const capabilityConfig: Readonly<
45
+ Record<
46
+ SystemCapability,
47
+ {
48
+ readonly commands: ReadonlyArray<string>
49
+ readonly reason: string
50
+ readonly preferred: (platform: string, env: NodeJS.ProcessEnv) => string
51
+ }
52
+ >
53
+ > = {
54
+ keyMacro: {
55
+ commands: [
56
+ "ydotool",
57
+ "wtype",
58
+ "xdotool",
59
+ "dotool",
60
+ "osascript",
61
+ "powershell",
62
+ ],
63
+ reason:
64
+ "type:// keystrokes need a key input tool. Install ydotool (works on most compositors via uinput) or wtype (wlroots compositors only). macOS uses osascript; Windows uses PowerShell with Win32 SendInput.",
65
+ preferred: (platform) => {
66
+ if (platform === "darwin") return "osascript"
67
+ if (platform === "win32") return "powershell"
68
+ return "ydotool"
69
+ },
70
+ },
71
+ clipboard: {
72
+ commands: ["wl-copy", "xclip", "xsel", "pbcopy"],
73
+ reason:
74
+ "non-ASCII literal text (emoji, accented letters, CJK) needs a clipboard tool because ydotool's `type` does not handle non-BMP characters. Install the wl-clipboard package (provides wl-copy) on Wayland; xclip / xsel work on X11; macOS ships pbcopy.",
75
+ preferred: (platform, env) => {
76
+ if (platform === "darwin") return "pbcopy"
77
+ return env["WAYLAND_DISPLAY"] === undefined &&
78
+ env["DISPLAY"] !== undefined
79
+ ? "xclip"
80
+ : "wl-copy"
81
+ },
82
+ },
83
+ notification: {
84
+ commands: ["notify-send", "osascript", "powershell"],
85
+ reason:
86
+ "OS-level notifications (toast/notification-center) need a host tool. Linux uses notify-send (libnotify); macOS uses osascript; Windows uses PowerShell with System.Windows.Forms.NotifyIcon.",
87
+ preferred: (platform) => {
88
+ if (platform === "darwin") return "osascript"
89
+ if (platform === "win32") return "powershell"
90
+ return "notify-send"
91
+ },
92
+ },
93
+ }
94
+
95
+ // ponytail: `command` is a POSIX shell builtin, not a binary — execFile can't
96
+ // spawn it, so the old `executor.run("command", ["-v", x])` failed for EVERY
97
+ // candidate and the probe silently degraded to the `--version` fallback (which
98
+ // osascript, pbcopy and clip don't even support). Wrap in `sh -c`, matching
99
+ // setup-wizard/probe.ts, and shell-quote so the argument stays one token.
100
+ const probeCommandV = async (
101
+ executor: CommandExecutor,
102
+ command: string,
103
+ ): Promise<boolean> => {
104
+ const quoted = `'${command.replaceAll(`'`, `'\\''`)}'`
105
+ const result = await executor.run("sh", ["-c", `command -v ${quoted}`])
106
+ return result.exitCode === 0 && result.stdout.trim().length > 0
107
+ }
108
+
109
+ const probeVersion = async (
110
+ executor: CommandExecutor,
111
+ command: string,
112
+ ): Promise<boolean> => {
113
+ try {
114
+ const result = await executor.run(command, ["--version"], {
115
+ timeoutMs: 1_000,
116
+ })
117
+ return result.exitCode === 0
118
+ } catch {
119
+ return false
120
+ }
121
+ }
122
+
123
+ const probeCommand = async (
124
+ executor: CommandExecutor,
125
+ command: string,
126
+ extraFsProbe?: (command: string) => boolean,
127
+ ): Promise<boolean> => {
128
+ if (await probeCommandV(executor, command)) return true
129
+ if (extraFsProbe?.(command) === true) return true
130
+ return await probeVersion(executor, command)
131
+ }
132
+
133
+ // ponytail: the clipboard candidate list is session-dependent on Linux, so it
134
+ // can't just be `config.commands`. The old ternary keyed off `preferred ===
135
+ // "xclip"` and sent every other platform down the `["wl-copy"]` branch — so on
136
+ // macOS the probe looked for a Wayland tool, never for pbcopy, and every run
137
+ // logged "clipboard: none of wl-copy found" on a machine that ships pbcopy.
138
+ const clipboardCandidates = (
139
+ platform: string,
140
+ preferred: string,
141
+ ): ReadonlyArray<string> => {
142
+ if (platform === "darwin") return ["pbcopy"]
143
+ if (platform === "win32") return ["clip"]
144
+ return preferred === "xclip" ? ["xclip", "xsel"] : ["wl-copy"]
145
+ }
146
+
147
+ export const checkRequirements = async ({
148
+ platform,
149
+ executor,
150
+ env = process.env,
151
+ extraFsProbe,
152
+ }: RequirementsCheckDeps): Promise<RequirementsCheckResult> => {
153
+ const result: Partial<Record<SystemCapability, CapabilityStatus>> = {}
154
+
155
+ for (const [name, config] of Object.entries(capabilityConfig)) {
156
+ const commands =
157
+ name === "clipboard"
158
+ ? clipboardCandidates(platform, config.preferred(platform, env))
159
+ : config.commands
160
+ const availability = await Promise.all(
161
+ commands.map((command) => probeCommand(executor, command, extraFsProbe)),
162
+ )
163
+ const found = commands.filter((_, index) => availability[index])
164
+ const missing = commands.filter((_, index) => !availability[index])
165
+ result[name as SystemCapability] = {
166
+ available: found.length > 0,
167
+ commands: found,
168
+ missingCommands: missing,
169
+ reason: config.reason,
170
+ preferred: config.preferred(platform, env),
171
+ }
172
+ }
173
+
174
+ // ponytail: darwin-only. `osascript` ships with macOS, so probing for the
175
+ // binary always succeeds and the daemon started reporting keyMacro as
176
+ // available while every keystroke failed with -1719/1002. The setup wizard
177
+ // already checks the Accessibility grant; without the same check here the
178
+ // runtime never warned at boot, and a tapped macro button showed a generic
179
+ // "action-failed" tile instead of being pre-empted with the real reason.
180
+ // Linux and Windows are untouched — they return before this block.
181
+ if (platform === "darwin") {
182
+ const keyMacro = result.keyMacro
183
+ if (
184
+ keyMacro?.available === true &&
185
+ !(await hasDarwinAccessibility(executor))
186
+ ) {
187
+ result.keyMacro = {
188
+ ...keyMacro,
189
+ available: false,
190
+ missingCommands: [PERMISSION_SENTINEL],
191
+ reason: `osascript is installed but has no Accessibility permission — ${ACCESSIBILITY_HINT}`,
192
+ }
193
+ }
194
+ }
195
+
196
+ return result as RequirementsCheckResult
197
+ }
198
+
199
+ const CAPABILITY_LABEL: Readonly<Record<SystemCapability, string>> = {
200
+ keyMacro: "Key macros",
201
+ clipboard: "Clipboard",
202
+ notification: "Notifications",
203
+ }
204
+
205
+ export const formatCapabilityWarning = (
206
+ name: SystemCapability,
207
+ status: CapabilityStatus,
208
+ ): string => {
209
+ const label = CAPABILITY_LABEL[name]
210
+ if (status.available) {
211
+ const missingPreferred =
212
+ status.preferred.length > 0 &&
213
+ !status.commands.includes(status.preferred) &&
214
+ status.missingCommands.length > 0
215
+ if (missingPreferred) {
216
+ return `${label}: using ${status.commands.join(", ")} as fallback; preferred ${status.preferred} is missing — ${status.reason}`
217
+ }
218
+ return ""
219
+ }
220
+ // ponytail: "none of accessibility-permission found" reads like a missing
221
+ // binary. A denied OS permission is not something the user can install, so
222
+ // state it as a permission and let the reason carry the remedy.
223
+ if (status.missingCommands.includes(PERMISSION_SENTINEL)) {
224
+ return `${label}: ${status.reason}`
225
+ }
226
+ return `${label}: none of ${status.missingCommands.join(", ")} found — ${status.reason}`
227
+ }
228
+
229
+ export const getRequiredCapability = (
230
+ action: string,
231
+ ): SystemCapability | null => {
232
+ if (action.startsWith("type://")) return "keyMacro"
233
+ if (action.startsWith("macro://")) return "keyMacro"
234
+ return null
235
+ }
@@ -0,0 +1,30 @@
1
+ import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
2
+ import { dirname, join } from "node:path"
3
+ import { fileURLToPath } from "node:url"
4
+
5
+ export interface ConfigSeedResult {
6
+ readonly seeded: boolean
7
+ readonly targetPath: string
8
+ readonly sourcePath: string
9
+ }
10
+
11
+ const here = (): string => dirname(fileURLToPath(import.meta.url))
12
+
13
+ // ponytail: seed `starter-config.yml`, NOT `default-config.yml`. The latter is
14
+ // the repo's own dev config — it carries `!include demos/*.yml` and
15
+ // `src: ./packages/addons/*`, both resolved relative to the config file. Copied
16
+ // to ~/.config/sirenodeck/ those paths point at directories that don't exist,
17
+ // so the very first `sirenodeck start` after the wizard died with
18
+ // "Included file not found". The starter config is self-contained by design.
19
+ export const defaultConfigSourcePath = (): string =>
20
+ join(here(), "..", "..", "..", "config", "starter-config.yml")
21
+
22
+ export const seedDefaultConfig = (targetPath: string): ConfigSeedResult => {
23
+ const sourcePath = defaultConfigSourcePath()
24
+ mkdirSync(dirname(targetPath), { recursive: true })
25
+ copyFileSync(sourcePath, targetPath)
26
+ writeFileSync(targetPath, readFileSync(targetPath, "utf8"), {
27
+ mode: 0o600,
28
+ })
29
+ return { seeded: true, targetPath, sourcePath }
30
+ }