@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,97 @@
1
+ import type pino from "pino"
2
+
3
+ import {
4
+ logNull,
5
+ noopUnsubscribe,
6
+ type CommandExecutor,
7
+ type LinuxDbusBus,
8
+ } from "./shared"
9
+
10
+ export interface ActiveAppSnapshot {
11
+ name: string
12
+ windowTitle: string | null
13
+ processId: number | null
14
+ }
15
+
16
+ export interface ActiveAppProvider {
17
+ getActive(): Promise<ActiveAppSnapshot | null>
18
+ subscribe(handler: (snapshot: ActiveAppSnapshot | null) => void): () => void
19
+ stop(): Promise<void>
20
+ }
21
+
22
+ export const createNullActiveAppProvider = (
23
+ reason: string,
24
+ logger?: pino.Logger,
25
+ ): ActiveAppProvider => {
26
+ logNull(logger, "active-app", reason)
27
+ return {
28
+ async getActive() {
29
+ return null
30
+ },
31
+ subscribe() {
32
+ return noopUnsubscribe
33
+ },
34
+ async stop() {
35
+ return
36
+ },
37
+ }
38
+ }
39
+
40
+ export interface CreateActiveAppProviderOptions {
41
+ readonly platform?: NodeJS.Platform
42
+ readonly executor?: CommandExecutor
43
+ readonly dbus?: LinuxDbusBus
44
+ readonly logger: pino.Logger
45
+ }
46
+
47
+ export const createActiveAppProvider = async (
48
+ options: CreateActiveAppProviderOptions,
49
+ ): Promise<ActiveAppProvider> => {
50
+ const platform = options.platform ?? process.platform
51
+ if (platform === "linux") {
52
+ if (options.executor === undefined) {
53
+ return createNullActiveAppProvider(
54
+ "executor-not-injected",
55
+ options.logger,
56
+ )
57
+ }
58
+ const { createLinuxActiveAppProvider } = await import("./active-app/linux")
59
+ return createLinuxActiveAppProvider({
60
+ executor: options.executor,
61
+ ...(options.dbus !== undefined ? { dbus: options.dbus } : {}),
62
+ logger: options.logger,
63
+ })
64
+ }
65
+ if (platform === "darwin") {
66
+ if (options.executor === undefined) {
67
+ return createNullActiveAppProvider(
68
+ "executor-not-injected",
69
+ options.logger,
70
+ )
71
+ }
72
+ const { createDarwinActiveAppProvider } =
73
+ await import("./active-app/darwin")
74
+ return createDarwinActiveAppProvider({
75
+ executor: options.executor,
76
+ logger: options.logger,
77
+ })
78
+ }
79
+ if (platform === "win32") {
80
+ if (options.executor === undefined) {
81
+ return createNullActiveAppProvider(
82
+ "executor-not-injected",
83
+ options.logger,
84
+ )
85
+ }
86
+ const { createWindowsActiveAppProvider } =
87
+ await import("./active-app/windows")
88
+ return createWindowsActiveAppProvider({
89
+ executor: options.executor,
90
+ logger: options.logger,
91
+ })
92
+ }
93
+ return createNullActiveAppProvider(
94
+ `platform:${platform}-pending`,
95
+ options.logger,
96
+ )
97
+ }
@@ -0,0 +1,65 @@
1
+ import type pino from "pino"
2
+
3
+ import type { ClipboardProvider } from "../clipboard"
4
+ import { ProviderError } from "../error"
5
+ import type { CommandExecutor } from "../shared"
6
+ import { withTimeout } from "../shared"
7
+
8
+ export interface CreateDarwinClipboardProviderOptions {
9
+ readonly executor: CommandExecutor
10
+ readonly logger: pino.Logger
11
+ readonly timeoutMs?: number
12
+ }
13
+
14
+ export const createDarwinClipboardProvider = (
15
+ options: CreateDarwinClipboardProviderOptions,
16
+ ): ClipboardProvider => {
17
+ const { executor, logger, timeoutMs: timeoutMsOption } = options
18
+ const timeoutMs = timeoutMsOption ?? 500
19
+ let disposed = false
20
+ const stop = async (): Promise<void> => {
21
+ disposed = true
22
+ }
23
+
24
+ const writeText = async (text: string): Promise<void> => {
25
+ if (disposed) throw new Error("Clipboard provider is disposed")
26
+ const escaped = text.replace(/'/g, "'\\''")
27
+ try {
28
+ const result = await withTimeout(
29
+ executor.run("sh", ["-c", `printf '%s' '${escaped}' | pbcopy`], {
30
+ timeoutMs,
31
+ }),
32
+ timeoutMs + 500,
33
+ )
34
+ if (result.exitCode !== 0) {
35
+ logger.warn({ stderr: result.stderr }, "clipboard: pbcopy failed")
36
+ throw new ProviderError(
37
+ "EXEC_FAILED",
38
+ `clipboard write failed: ${result.stderr || "unknown error"}`,
39
+ )
40
+ }
41
+ } catch (err) {
42
+ if (err instanceof ProviderError) throw err
43
+ throw new ProviderError(
44
+ "EXEC_FAILED",
45
+ `clipboard write failed: ${err instanceof Error ? err.message : String(err)}`,
46
+ )
47
+ }
48
+ }
49
+
50
+ const readText = async (): Promise<string> => {
51
+ if (disposed) throw new Error("Clipboard provider is disposed")
52
+ try {
53
+ const result = await withTimeout(
54
+ executor.run("pbpaste", [], { timeoutMs }),
55
+ timeoutMs + 500,
56
+ )
57
+ if (result.exitCode === 0) return result.stdout
58
+ } catch {
59
+ // ignore
60
+ }
61
+ return ""
62
+ }
63
+
64
+ return { writeText, readText, stop }
65
+ }
@@ -0,0 +1,163 @@
1
+ import type pino from "pino"
2
+
3
+ import type { ClipboardProvider } from "../clipboard"
4
+ import { ProviderError } from "../error"
5
+ import type { CommandExecutor } from "../shared"
6
+ import { withTimeout } from "../shared"
7
+
8
+ export interface CreateLinuxClipboardProviderOptions {
9
+ readonly executor: CommandExecutor
10
+ readonly env?: Readonly<Record<string, string>>
11
+ readonly logger: pino.Logger
12
+ readonly timeoutMs?: number
13
+ readonly extraFsProbe?: (tool: string) => boolean
14
+ }
15
+
16
+ const WL_COPY_TOOL = "wl-copy"
17
+ const WL_PASTE_TOOL = "wl-paste"
18
+ const XCLIP_TOOL = "xclip"
19
+ const XSEL_TOOL = "xsel"
20
+
21
+ const probeTool = async (
22
+ executor: CommandExecutor,
23
+ tool: string,
24
+ extraFsProbe?: (tool: string) => boolean,
25
+ ): Promise<boolean> => {
26
+ const result = await executor.run("which", [tool])
27
+ if (result.exitCode === 0 && result.stdout.trim().length > 0) return true
28
+ // ponytail: fallback when CLI is launched with a stripped PATH (systemd,
29
+ // launchd, IDE runners) and `which` returns nothing even though wl-copy
30
+ // is installed at e.g. /usr/bin. Mirror of the requirements.ts probe and
31
+ // key-macro/linux.ts probeTool.
32
+ return extraFsProbe?.(tool) === true
33
+ }
34
+
35
+ const shellQuote = (value: string): string =>
36
+ `'${value.replace(/'/g, "'\\''")}'`
37
+
38
+ export const createLinuxClipboardProvider = (
39
+ options: CreateLinuxClipboardProviderOptions,
40
+ ): ClipboardProvider => {
41
+ const { executor, logger, timeoutMs: timeoutMsOption } = options
42
+ const timeoutMs = timeoutMsOption ?? 500
43
+ const isX11 =
44
+ options.env?.["WAYLAND_DISPLAY"] === undefined &&
45
+ options.env?.["DISPLAY"] !== undefined
46
+ const tools = isX11 ? [XCLIP_TOOL, XSEL_TOOL] : [WL_COPY_TOOL]
47
+ let selectedTool = tools[0]!
48
+ let disposed = false
49
+
50
+ const stop = async (): Promise<void> => {
51
+ disposed = true
52
+ }
53
+
54
+ const writeText = async (text: string): Promise<void> => {
55
+ if (disposed) throw new Error("Clipboard provider is disposed")
56
+ // ponytail: `-o` (paste-once) makes wl-copy exit after the next paste, so
57
+ // the surrounding `sh -c` pipeline does not wait on a long-lived wl-copy.
58
+ // The runtime's spawn() already drains stdio on 'exit', but the shell
59
+ // here waits for every child in the pipeline — without `-o`, wl-copy
60
+ // stays alive maintaining clipboard ownership and the pipe never closes.
61
+ const command = isX11
62
+ ? selectedTool === XSEL_TOOL
63
+ ? `printf '%s' ${shellQuote(text)} | ${XSEL_TOOL} --clipboard --input`
64
+ : `printf '%s' ${shellQuote(text)} | ${XCLIP_TOOL} -selection clipboard`
65
+ : `printf '%s' ${shellQuote(text)} | ${WL_COPY_TOOL} -o`
66
+ const startedAt = Date.now()
67
+ logger.info(
68
+ { step: "clipboard.writeText", command, textPreview: text.slice(0, 24) },
69
+ "clipboard: invoking tool",
70
+ )
71
+ const result = await withTimeout(
72
+ executor.run("sh", ["-c", command]),
73
+ timeoutMs + 2500,
74
+ )
75
+ logger.info(
76
+ {
77
+ step: "clipboard.writeText",
78
+ exitCode: result.exitCode,
79
+ stderr: result.stderr.trim(),
80
+ elapsedMs: Date.now() - startedAt,
81
+ },
82
+ "clipboard: tool returned",
83
+ )
84
+ if (result.exitCode !== 0) {
85
+ logger.warn({ stderr: result.stderr.trim() }, "clipboard: write failed")
86
+ throw new ProviderError(
87
+ "EXEC_FAILED",
88
+ `clipboard write failed with ${tools.join(" or ")}: ${result.stderr.trim() || "unknown error"}`,
89
+ )
90
+ }
91
+ }
92
+
93
+ const readText = async (): Promise<string> => {
94
+ if (disposed) throw new Error("Clipboard provider is disposed")
95
+ try {
96
+ if (!(await ensureProbed())) return ""
97
+ const startedAt = Date.now()
98
+ const result = await withTimeout(
99
+ isX11
100
+ ? selectedTool === XSEL_TOOL
101
+ ? executor.run(XSEL_TOOL, ["--clipboard", "--output"])
102
+ : executor.run(XCLIP_TOOL, ["-selection", "clipboard", "-out"])
103
+ : executor.run(WL_PASTE_TOOL, []),
104
+ timeoutMs + 2500,
105
+ )
106
+ logger.info(
107
+ {
108
+ step: "clipboard.readText",
109
+ exitCode: result.exitCode,
110
+ stdoutPreview: result.stdout.slice(0, 32),
111
+ elapsedMs: Date.now() - startedAt,
112
+ },
113
+ "clipboard: wl-paste returned",
114
+ )
115
+ if (result.exitCode !== 0) return ""
116
+ return result.stdout
117
+ } catch {
118
+ return ""
119
+ }
120
+ }
121
+
122
+ let _probeDone = false
123
+ let _probeOk = false
124
+ const ensureProbed = async (): Promise<boolean> => {
125
+ if (_probeDone) return _probeOk
126
+ _probeOk = false
127
+ for (const tool of tools) {
128
+ if (await probeTool(executor, tool, options.extraFsProbe)) {
129
+ selectedTool = tool
130
+ _probeOk = true
131
+ break
132
+ }
133
+ }
134
+ _probeDone = true
135
+ if (!_probeOk) {
136
+ logger.warn(
137
+ { tools },
138
+ "clipboard tool not found on PATH; clipboard will throw ProviderError",
139
+ )
140
+ }
141
+ return _probeOk
142
+ }
143
+
144
+ // Eager probe so a missing tool surfaces at init time.
145
+ void ensureProbed()
146
+
147
+ return {
148
+ writeText: async (text: string) => {
149
+ const ok = await ensureProbed()
150
+ if (!ok) {
151
+ throw new ProviderError(
152
+ "NOT_AVAILABLE",
153
+ isX11
154
+ ? "none of xclip or xsel found on PATH"
155
+ : "wl-copy not found on PATH; install the wl-clipboard package",
156
+ )
157
+ }
158
+ await writeText(text)
159
+ },
160
+ readText,
161
+ stop,
162
+ }
163
+ }
@@ -0,0 +1,94 @@
1
+ import type pino from "pino"
2
+
3
+ import type { ClipboardProvider } from "../clipboard"
4
+ import { ProviderError } from "../error"
5
+ import type { CommandExecutor } from "../shared"
6
+ import { withTimeout } from "../shared"
7
+
8
+ export interface CreateWindowsClipboardProviderOptions {
9
+ readonly executor: CommandExecutor
10
+ readonly logger: pino.Logger
11
+ readonly timeoutMs?: number
12
+ }
13
+
14
+ const encodePSCommand = (script: string): string => {
15
+ const buf = Buffer.from(script, "utf16le")
16
+ return buf.toString("base64")
17
+ }
18
+
19
+ const escapeForPowerShellSingleQuote = (s: string): string =>
20
+ s.replace(/'/g, "''")
21
+
22
+ const escapeForDoubleQuote = (s: string): string =>
23
+ s.replace(/[\\"$`]/g, "``$&")
24
+
25
+ export const createWindowsClipboardProvider = (
26
+ options: CreateWindowsClipboardProviderOptions,
27
+ ): ClipboardProvider => {
28
+ const { executor, logger, timeoutMs: timeoutMsOption } = options
29
+ const timeoutMs = timeoutMsOption ?? 500
30
+ let disposed = false
31
+ const stop = async (): Promise<void> => {
32
+ disposed = true
33
+ }
34
+
35
+ const runPS = async (script: string): Promise<string> => {
36
+ const result = await withTimeout(
37
+ executor.run("powershell", ["-NoProfile", "-Command", script], {
38
+ timeoutMs,
39
+ }),
40
+ timeoutMs + 500,
41
+ )
42
+ if (result.exitCode !== 0) {
43
+ logger.warn({ stderr: result.stderr }, "clipboard: powershell failed")
44
+ throw new ProviderError(
45
+ "EXEC_FAILED",
46
+ `clipboard write failed: ${result.stderr.trim() || "unknown error"}`,
47
+ )
48
+ }
49
+ return result.stdout
50
+ }
51
+
52
+ const writeText = async (text: string): Promise<void> => {
53
+ if (disposed) throw new Error("Clipboard provider is disposed")
54
+ // ponytail: -EncodedCommand avoids shell/interpolation entirely. The text
55
+ // is embedded inside a base64 UTF-16LE blob PowerShell parses as a script.
56
+ const escaped = escapeForDoubleQuote(text)
57
+ const psValue = `'${escapeForPowerShellSingleQuote(escaped)}'`
58
+ const script = `Set-Clipboard -Value ${psValue}`
59
+ const encoded = encodePSCommand(script)
60
+ const result = await withTimeout(
61
+ executor.run("powershell", ["-NoProfile", "-EncodedCommand", encoded], {
62
+ timeoutMs,
63
+ }),
64
+ timeoutMs + 500,
65
+ )
66
+ if (result.exitCode !== 0) {
67
+ logger.warn({ stderr: result.stderr }, "clipboard: powershell failed")
68
+ throw new ProviderError(
69
+ "EXEC_FAILED",
70
+ `clipboard write failed: ${result.stderr.trim() || "unknown error"}`,
71
+ )
72
+ }
73
+ }
74
+
75
+ const readText = async (): Promise<string> => {
76
+ if (disposed) throw new Error("Clipboard provider is disposed")
77
+ try {
78
+ const result = await withTimeout(
79
+ executor.run(
80
+ "powershell",
81
+ ["-NoProfile", "-Command", "Get-Clipboard"],
82
+ { timeoutMs },
83
+ ),
84
+ timeoutMs + 500,
85
+ )
86
+ if (result.exitCode === 0) return result.stdout
87
+ } catch {
88
+ // ignore
89
+ }
90
+ return ""
91
+ }
92
+
93
+ return { writeText, readText, stop }
94
+ }
@@ -0,0 +1,84 @@
1
+ import type pino from "pino"
2
+
3
+ import { ProviderError } from "./error"
4
+
5
+ import { createDarwinClipboardProvider } from "./clipboard/darwin"
6
+ import { createLinuxClipboardProvider } from "./clipboard/linux"
7
+ import { createWindowsClipboardProvider } from "./clipboard/windows"
8
+ import { type CommandExecutor } from "./shared"
9
+
10
+ export interface ClipboardProvider {
11
+ writeText(text: string): Promise<void>
12
+ readText(): Promise<string>
13
+ stop(): Promise<void>
14
+ }
15
+
16
+ export const createNullClipboardProvider = (
17
+ logger?: pino.Logger,
18
+ ): ClipboardProvider => {
19
+ if (logger) {
20
+ logger.warn(
21
+ { provider: "clipboard" },
22
+ "OS clipboard provider unavailable, using null provider",
23
+ )
24
+ }
25
+ return {
26
+ async writeText(_text: string): Promise<void> {
27
+ throw new ProviderError(
28
+ "NOT_AVAILABLE",
29
+ "Clipboard provider not available on this platform",
30
+ )
31
+ },
32
+ async readText(): Promise<string> {
33
+ throw new ProviderError(
34
+ "NOT_AVAILABLE",
35
+ "Clipboard provider not available on this platform",
36
+ )
37
+ },
38
+ async stop() {
39
+ return
40
+ },
41
+ }
42
+ }
43
+
44
+ export interface CreateClipboardProviderOptions {
45
+ readonly executor: CommandExecutor
46
+ readonly platform?: NodeJS.Platform
47
+ readonly env?: Readonly<Record<string, string>>
48
+ readonly logger: pino.Logger
49
+ readonly extraFsProbe?: (tool: string) => boolean
50
+ }
51
+
52
+ export const createClipboardProvider = (
53
+ options: CreateClipboardProviderOptions,
54
+ ): ClipboardProvider => {
55
+ const logger = options.logger.child({ component: "clipboard" })
56
+ const platform = options.platform ?? process.platform
57
+ if (platform === "linux") {
58
+ return createLinuxClipboardProvider({
59
+ executor: options.executor,
60
+ ...(options.env !== undefined ? { env: options.env } : {}),
61
+ logger: options.logger,
62
+ ...(options.extraFsProbe !== undefined
63
+ ? { extraFsProbe: options.extraFsProbe }
64
+ : {}),
65
+ })
66
+ }
67
+ if (platform === "darwin") {
68
+ return createDarwinClipboardProvider({
69
+ executor: options.executor,
70
+ logger: options.logger,
71
+ })
72
+ }
73
+ if (platform === "win32") {
74
+ return createWindowsClipboardProvider({
75
+ executor: options.executor,
76
+ logger: options.logger,
77
+ })
78
+ }
79
+ void createNullClipboardProvider(options.logger)
80
+ throw new ProviderError(
81
+ "UNSUPPORTED_PLATFORM",
82
+ `Clipboard provider not implemented for platform '${platform}' in this build`,
83
+ )
84
+ }
@@ -0,0 +1,16 @@
1
+ export type ProviderErrorCode =
2
+ | "NOT_AVAILABLE"
3
+ | "TIMEOUT"
4
+ | "EXEC_FAILED"
5
+ | "PARSE_ERROR"
6
+ | "UNSUPPORTED_PLATFORM"
7
+
8
+ export class ProviderError extends Error {
9
+ readonly code: ProviderErrorCode
10
+
11
+ constructor(code: ProviderErrorCode, message: string) {
12
+ super(message)
13
+ this.name = "ProviderError"
14
+ this.code = code
15
+ }
16
+ }