@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,108 @@
1
+ import type pino from "pino"
2
+
3
+ import { type CommandExecutor } from "@/system/providers/shared"
4
+
5
+ import {
6
+ createNullBrightnessProvider,
7
+ type BrightnessProvider,
8
+ type BrightnessReading,
9
+ } from "./index"
10
+
11
+ export interface CreateLinuxBrightnessProviderOptions {
12
+ readonly executor: CommandExecutor
13
+ readonly env?: Readonly<Record<string, string>>
14
+ readonly logger: pino.Logger
15
+ }
16
+
17
+ const parseXrandr = (stdout: string): BrightnessReading | null => {
18
+ const m = stdout.match(/Brightness:\s*([\d.]+)\s*\/\s*([\d.]+)/)
19
+ if (m === null) return null
20
+ const current = Number.parseFloat(m[1] ?? "")
21
+ const max = Number.parseFloat(m[2] ?? "")
22
+ if (!Number.isFinite(current) || !Number.isFinite(max) || max <= 0)
23
+ return null
24
+ return { value: Math.round((current / max) * 100), max: 100 }
25
+ }
26
+
27
+ const parseBrightnessCtl = (stdout: string): BrightnessReading | null => {
28
+ const v = Number.parseInt(stdout.trim(), 10)
29
+ if (!Number.isFinite(v)) return null
30
+ return { value: v, max: 100 }
31
+ }
32
+
33
+ export const createLinuxBrightnessProvider = (
34
+ options: CreateLinuxBrightnessProviderOptions,
35
+ ): BrightnessProvider => {
36
+ const { executor, env, logger } = options
37
+ const nullProvider = createNullBrightnessProvider(logger)
38
+ let disposed = false
39
+ const stop = async (): Promise<void> => {
40
+ disposed = true
41
+ }
42
+
43
+ const getCurrent = async (): Promise<BrightnessReading> => {
44
+ if (disposed) {
45
+ throw new Error("Brightness provider is disposed")
46
+ }
47
+ const envWayland = (env ?? process.env)["WAYLAND_DISPLAY"]
48
+ const isWayland =
49
+ envWayland !== undefined && envWayland.length > 0 && envWayland !== "0"
50
+ if (isWayland) {
51
+ const r = await executor.run("brightnessctl get", [])
52
+ if (r.exitCode === 0) {
53
+ const parsed = parseBrightnessCtl(r.stdout)
54
+ if (parsed !== null) return parsed
55
+ }
56
+ }
57
+ const r = await executor.run("xrandr --query", [])
58
+ if (r.exitCode !== 0) {
59
+ const fallback = await executor.run("brightnessctl get", [])
60
+ if (fallback.exitCode === 0) {
61
+ const parsed = parseBrightnessCtl(fallback.stdout)
62
+ if (parsed !== null) return parsed
63
+ }
64
+ logger.warn(
65
+ { exitCode: r.exitCode, stderr: r.stderr },
66
+ "brightness: xrandr + brightnessctl failed",
67
+ )
68
+ return nullProvider.getCurrent()
69
+ }
70
+ const parsed = parseXrandr(r.stdout)
71
+ if (parsed === null) {
72
+ logger.warn(
73
+ { stdout: r.stdout.slice(0, 200) },
74
+ "brightness: xrandr output not parseable",
75
+ )
76
+ return nullProvider.getCurrent()
77
+ }
78
+ return parsed
79
+ }
80
+
81
+ const setBrightness = async (value: number): Promise<void> => {
82
+ if (disposed) throw new Error("Brightness provider is disposed")
83
+ const clamped = Math.max(0, Math.min(100, Math.round(value)))
84
+ const setEnvWayland = (env ?? process.env)["WAYLAND_DISPLAY"]
85
+ const isWayland =
86
+ setEnvWayland !== undefined &&
87
+ setEnvWayland.length > 0 &&
88
+ setEnvWayland !== "0"
89
+ if (isWayland) {
90
+ const r = await executor.run("brightnessctl set", [`${clamped}%`])
91
+ if (r.exitCode === 0) return
92
+ logger.warn({ stderr: r.stderr }, "brightness: brightnessctl set failed")
93
+ return
94
+ }
95
+ const r = await executor.run("xrandr --output", [
96
+ "--brightness",
97
+ `${clamped / 100}`,
98
+ ])
99
+ if (r.exitCode !== 0) {
100
+ logger.warn(
101
+ { exitCode: r.exitCode, stderr: r.stderr },
102
+ "brightness: xrandr --brightness failed",
103
+ )
104
+ }
105
+ }
106
+
107
+ return { getCurrent, setBrightness, stop }
108
+ }
@@ -0,0 +1,77 @@
1
+ import type pino from "pino"
2
+
3
+ import { type CommandExecutor } from "@/system/providers/shared"
4
+
5
+ import {
6
+ createNullBrightnessProvider,
7
+ type BrightnessProvider,
8
+ type BrightnessReading,
9
+ } from "./index"
10
+
11
+ export interface CreateWindowsBrightnessProviderOptions {
12
+ readonly executor: CommandExecutor
13
+ readonly logger: pino.Logger
14
+ }
15
+
16
+ const parseWmi = (stdout: string): BrightnessReading | null => {
17
+ const v = Number.parseInt(stdout.trim(), 10)
18
+ if (!Number.isFinite(v)) return null
19
+ return { value: v, max: 100 }
20
+ }
21
+
22
+ export const createWindowsBrightnessProvider = (
23
+ options: CreateWindowsBrightnessProviderOptions,
24
+ ): BrightnessProvider => {
25
+ const { executor, logger } = options
26
+ const nullProvider = createNullBrightnessProvider(logger)
27
+ let disposed = false
28
+ const stop = async (): Promise<void> => {
29
+ disposed = true
30
+ }
31
+
32
+ const getCommand =
33
+ "(Get-WmiObject -Namespace root/WMI -ClassName WmiMonitorBrightness).CurrentBrightness"
34
+
35
+ const getCurrent = async (): Promise<BrightnessReading> => {
36
+ if (disposed) throw new Error("Brightness provider is disposed")
37
+ const r = await executor.run("powershell", [
38
+ "-NoProfile",
39
+ "-Command",
40
+ getCommand,
41
+ ])
42
+ if (r.exitCode !== 0) {
43
+ logger.warn(
44
+ { exitCode: r.exitCode, stderr: r.stderr },
45
+ "brightness: powershell get failed",
46
+ )
47
+ return nullProvider.getCurrent()
48
+ }
49
+ const parsed = parseWmi(r.stdout)
50
+ if (parsed === null) {
51
+ logger.warn(
52
+ { stdout: r.stdout.slice(0, 200) },
53
+ "brightness: powershell output not parseable",
54
+ )
55
+ return nullProvider.getCurrent()
56
+ }
57
+ return parsed
58
+ }
59
+
60
+ const setBrightness = async (value: number): Promise<void> => {
61
+ if (disposed) throw new Error("Brightness provider is disposed")
62
+ const clamped = Math.max(0, Math.min(100, Math.round(value)))
63
+ const r = await executor.run("powershell", [
64
+ "-NoProfile",
65
+ "-Command",
66
+ `(Get-WmiObject -Namespace root/WMI -ClassName WmiMonitorBrightness).CurrentBrightness = ${clamped}`,
67
+ ])
68
+ if (r.exitCode !== 0) {
69
+ logger.warn(
70
+ { exitCode: r.exitCode, stderr: r.stderr },
71
+ "brightness: powershell set failed",
72
+ )
73
+ }
74
+ }
75
+
76
+ return { getCurrent, setBrightness, stop }
77
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "kind": "addon",
3
+ "apiVersion": 1,
4
+ "name": "brightness",
5
+ "entry": "index.ts"
6
+ }
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="#d97706" d="m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z"/></svg>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><svg width="800px" height="800px" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><rect width="512" height="512" fill="#131010"/><path d="M320 224V352H192V224H320Z" fill="#b4afaf"/><path fill-rule="evenodd" clip-rule="evenodd" d="M384 416H128V96H384V416ZM320 160H192V352H320V160Z" fill="white"/></svg>
@@ -0,0 +1,82 @@
1
+ // ponytail: see packages/addons/app-shortcuts/src/index.ts for context.
2
+ import type {
3
+ AddonButtonServiceContext,
4
+ AddonButtonTypeService,
5
+ } from "../../types/types.js"
6
+ import { configSchema, type AgentConfig } from "./config.js"
7
+ import { agentAtSlot } from "../../shared/snapshot.js"
8
+ import { agentKey } from "../../shared/state.js"
9
+ import type { AgentsSnapshot, ProviderId } from "../../shared/state.js"
10
+
11
+ const shq = (value: string): string => `'${value.replaceAll(`'`, `'\\''`)}'`
12
+
13
+ // ponytail: focus can't reliably foreground the exact terminal/tmux pane an
14
+ // agent runs in (no window-activation CLI on GNOME Wayland), so we spawn a
15
+ // terminal in the session's project dir resuming it. claude --resume and
16
+ // opencode --session N both support this. The launcher picks xdg-terminal-exec
17
+ // then gnome-terminal; both accept `-- <cmd>`.
18
+ const runFocus = async (
19
+ ctx: AddonButtonServiceContext<AgentConfig>,
20
+ agent: { providerId: ProviderId; sessionId: string; directory?: string },
21
+ ): Promise<void> => {
22
+ if (agent.sessionId.startsWith("instance:")) return
23
+ const dir = agent.directory
24
+ const prefix = dir && dir.length > 0 ? `cd ${shq(dir)} && ` : ""
25
+ const resume =
26
+ agent.providerId === "opencode"
27
+ ? `${prefix}opencode --session ${shq(agent.sessionId)}`
28
+ : `${prefix}claude --resume ${shq(agent.sessionId)}`
29
+ const cmd =
30
+ `t=$(command -v xdg-terminal-exec || command -v gnome-terminal); ` +
31
+ `[ -n "$t" ] && nohup "$t" -- bash -c ${shq(resume)} >/dev/null 2>&1 &`
32
+ await ctx.executor.run(cmd)
33
+ }
34
+
35
+ const methodsFor = (ctx: AddonButtonServiceContext<AgentConfig>) =>
36
+ ctx.methods as Readonly<Record<string, (...args: unknown[]) => unknown>>
37
+
38
+ const resolveSlotAgent = (
39
+ ctx: AddonButtonServiceContext<AgentConfig>,
40
+ ): { providerId: ProviderId; sessionId: string; directory?: string } | null => {
41
+ const get = methodsFor(ctx)["coding-agents:getSnapshot"]
42
+ if (typeof get !== "function") return null
43
+ const snapshot = get() as AgentsSnapshot | undefined
44
+ if (!snapshot) return null
45
+ const agent = agentAtSlot(snapshot, ctx.config?.slot ?? 0)
46
+ return agent
47
+ ? {
48
+ providerId: agent.providerId,
49
+ sessionId: agent.sessionId,
50
+ directory: agent.directory,
51
+ }
52
+ : null
53
+ }
54
+
55
+ const callDismiss = (ctx: AddonButtonServiceContext<AgentConfig>): void => {
56
+ const dismiss = methodsFor(ctx)["coding-agents:dismissAttention"]
57
+ if (typeof dismiss === "function") {
58
+ const agent = resolveSlotAgent(ctx)
59
+ if (agent) {
60
+ ;(dismiss as (a: string) => unknown)(agentKey(agent))
61
+ }
62
+ }
63
+ }
64
+
65
+ // ponytail: tap is reserved for the frontend's PaginatedSurface page
66
+ // cycling; hold opens the session, dbl-tap dismisses the attention notice.
67
+ export default {
68
+ configSchema,
69
+ gestureHandlers: ["tap", "hold", "dbl-tap"] as const,
70
+ onMount: (_ctx: AddonButtonServiceContext<AgentConfig>): void => {},
71
+ onTap: (_ctx: AddonButtonServiceContext<AgentConfig>): void => {},
72
+ onHold: async (
73
+ ctx: AddonButtonServiceContext<AgentConfig>,
74
+ ): Promise<void> => {
75
+ const agent = resolveSlotAgent(ctx)
76
+ if (agent) await runFocus(ctx, agent)
77
+ },
78
+ onDblTap: (ctx: AddonButtonServiceContext<AgentConfig>): void => {
79
+ callDismiss(ctx)
80
+ },
81
+ dispose: (_ctx: AddonButtonServiceContext<AgentConfig>): void => {},
82
+ } satisfies AddonButtonTypeService<AgentConfig>
@@ -0,0 +1,13 @@
1
+ import { z } from "zod"
2
+
3
+ export const configSchema = z
4
+ .object({
5
+ // ponytail: slot tiles are materialized once; which agent a slot shows
6
+ // is resolved from the live snapshot (agentAtSlot). Explicit
7
+ // providerId/sessionId pinning was never reachable (button ids are
8
+ // positional), so it's gone.
9
+ slot: z.number().int().min(0).default(0),
10
+ })
11
+ .strict()
12
+
13
+ export type AgentConfig = z.infer<typeof configSchema>
@@ -0,0 +1,130 @@
1
+ import { PaginatedSurface, type PaginatedPage } from "@/ui"
2
+
3
+ import type { AddonFrontendButtonProps } from "../../types/types.js"
4
+
5
+ import type { AgentConfig } from "./config.js"
6
+ import {
7
+ AgentContextPage,
8
+ AgentDetailsPage,
9
+ AgentMetricsPage,
10
+ CurrentAgentPage,
11
+ } from "./pages.js"
12
+
13
+ const AUTO_RETURN_MS = 10_000
14
+
15
+ const CHANNEL_NAME = "coding-agents:agents"
16
+
17
+ declare global {
18
+ // ponytail: see summary/frontend.tsx — host-injected channel hook,
19
+ // looked up lazily so test environments without it still typecheck.
20
+ // eslint-disable-next-line no-var
21
+ var __codingAgentsUseAddonChannel:
22
+ | (<T>(channel: string) => { data: T | undefined })
23
+ | undefined
24
+ }
25
+
26
+ const useAddonChannel = <T,>(channel: string): { data: T | undefined } => {
27
+ const hook = globalThis.__codingAgentsUseAddonChannel
28
+ return hook ? hook<T>(channel) : { data: undefined }
29
+ }
30
+
31
+ interface AgentLike {
32
+ sessionId: string
33
+ providerId: string
34
+ title: string
35
+ status: string
36
+ directory?: string
37
+ cost?: number
38
+ contextTokens?: number
39
+ contextPercent?: number
40
+ createdAt?: number
41
+ updatedAt?: number
42
+ lastMessagePreview?: string
43
+ }
44
+
45
+ interface SnapshotLike {
46
+ byProvider?: Record<string, AgentLike[]>
47
+ icons?: Record<string, string>
48
+ }
49
+
50
+ // ponytail: solid status color underlay at fixed opacity — unlike color-mix
51
+ // it's universally supported (emulator + headless hardware screenshots).
52
+ // idle stays transparent so stale sessions read as quiet.
53
+ const TILE_COLOR: Record<string, string> = {
54
+ idle: "transparent",
55
+ running: "var(--sireno-color-primary)",
56
+ waiting: "var(--sireno-variant-amber-primary, #fbbf24)",
57
+ waiting_for_human: "var(--sireno-variant-amber-primary, #fbbf24)",
58
+ error: "var(--sireno-color-danger)",
59
+ compacting: "var(--sireno-color-primary)",
60
+ }
61
+
62
+ const DOT_COLOR: Record<string, string> = {
63
+ idle: "var(--sireno-variant-default-muted, #9ca3af)",
64
+ running: "var(--sireno-color-primary)",
65
+ waiting: "var(--sireno-variant-amber-primary, #fbbf24)",
66
+ waiting_for_human: "var(--sireno-variant-amber-primary, #fbbf24)",
67
+ error: "var(--sireno-color-danger)",
68
+ compacting: "var(--sireno-color-primary)",
69
+ }
70
+
71
+ const agentAt = (snapshot: SnapshotLike, slot: number): AgentLike | null => {
72
+ const all: AgentLike[] = []
73
+ for (const list of Object.values(snapshot.byProvider ?? {})) all.push(...list)
74
+ // keep in sync with byCreatedDesc in shared/snapshot.ts so the slot a tile
75
+ // renders matches the slot the backend resolves for gestures
76
+ all.sort(
77
+ (x, y) =>
78
+ (y.createdAt ?? 0) - (x.createdAt ?? 0) ||
79
+ (y.updatedAt ?? 0) - (x.updatedAt ?? 0),
80
+ )
81
+ return all[slot] ?? null
82
+ }
83
+
84
+ const AgentFrontend = (props: AddonFrontendButtonProps<AgentConfig>) => {
85
+ const { data } = useAddonChannel<SnapshotLike>(CHANNEL_NAME)
86
+ const snapshot = data ?? { byProvider: {} }
87
+ const slot = props.config?.slot ?? 0
88
+ const agent = agentAt(snapshot, slot)
89
+
90
+ const status = agent?.status ?? "idle"
91
+ const tileColor = TILE_COLOR[status] ?? TILE_COLOR["idle"]!
92
+ const dotColor = DOT_COLOR[status] ?? DOT_COLOR["idle"]!
93
+ const title = agent?.title ?? (slot === 0 ? "no agents" : "")
94
+ const iconSource = agent ? snapshot.icons?.[agent.providerId] : undefined
95
+
96
+ // ponytail: empty slots (no live session for this tile) read as blank —
97
+ // trailing tiles of the paginated deck stay quiet.
98
+ if (agent === null && slot !== 0) {
99
+ return <div className="h-full w-full" />
100
+ }
101
+
102
+ const pageProps = {
103
+ title,
104
+ sessionId: agent?.sessionId,
105
+ directory: agent?.directory,
106
+ lastMessagePreview: agent?.lastMessagePreview,
107
+ status,
108
+ iconSource,
109
+ cost: agent?.cost,
110
+ contextTokens: agent?.contextTokens,
111
+ contextPercent: agent?.contextPercent,
112
+ tileColor,
113
+ dotColor,
114
+ }
115
+ const pages: PaginatedPage<typeof pageProps>[] = [
116
+ { render: CurrentAgentPage, config: pageProps },
117
+ { render: AgentMetricsPage, config: pageProps },
118
+ { render: AgentContextPage, config: pageProps },
119
+ { render: AgentDetailsPage, config: pageProps },
120
+ ]
121
+ return (
122
+ <PaginatedSurface
123
+ pages={pages}
124
+ gesture={props.gesture}
125
+ autoReturnMs={AUTO_RETURN_MS}
126
+ />
127
+ )
128
+ }
129
+
130
+ export default AgentFrontend
@@ -0,0 +1,150 @@
1
+ import { Icon } from "@sirenodeck/sirenodeck"
2
+ import type { ReactElement } from "react"
3
+
4
+ import { LabelValueListSurface } from "@/ui"
5
+
6
+ interface AgentPageProps {
7
+ title: string
8
+ sessionId?: string
9
+ directory?: string
10
+ lastMessagePreview?: string
11
+ status: string
12
+ iconSource?: string
13
+ cost?: number
14
+ contextTokens?: number
15
+ contextPercent?: number
16
+ tileColor: string
17
+ dotColor: string
18
+ }
19
+
20
+ const value = (n: number | undefined, suffix = ""): string =>
21
+ typeof n === "number" && Number.isFinite(n) ? `${n}${suffix}` : "---"
22
+
23
+ // ponytail: compact token counts — k, M, kM (european milliard, 10^9),
24
+ // MM (european billion, 10^12).
25
+ const TOKEN_UNITS: ReadonlyArray<[number, string]> = [
26
+ [1_000_000_000_000, "MM"],
27
+ [1_000_000_000, "kM"],
28
+ [1_000_000, "M"],
29
+ [1_000, "k"],
30
+ ]
31
+
32
+ export const formatTokens = (tokens: number | undefined): string => {
33
+ if (typeof tokens !== "number" || !Number.isFinite(tokens)) return "---"
34
+ let n = tokens
35
+ for (const [factor, unit] of TOKEN_UNITS) {
36
+ if (Math.abs(n) < factor) continue
37
+ const scaled = n / factor
38
+ if (Math.round(scaled) >= 1000) continue
39
+ const rounded =
40
+ scaled >= 10 || scaled <= -10
41
+ ? Math.round(scaled)
42
+ : Math.round(scaled * 10) / 10
43
+ return `${rounded}${unit}`
44
+ }
45
+ return `${Math.round(n)}`
46
+ }
47
+
48
+ const formatCost = (cost: number | undefined): string =>
49
+ typeof cost === "number" && Number.isFinite(cost) && cost >= 0
50
+ ? `$${cost.toFixed(2)}`
51
+ : "---"
52
+
53
+ const shortPath = (directory: string | undefined): string => {
54
+ if (directory === undefined || directory.length === 0) return "---"
55
+ return directory.split(/[\\/]/).at(-1) || directory
56
+ }
57
+
58
+ const shortSessionId = (sessionId: string | undefined): string =>
59
+ sessionId === undefined || sessionId.length === 0
60
+ ? "---"
61
+ : sessionId.slice(-8)
62
+
63
+ export const CurrentAgentPage = ({
64
+ title,
65
+ status,
66
+ iconSource,
67
+ tileColor,
68
+ dotColor,
69
+ }: AgentPageProps): ReactElement => (
70
+ <div className="relative flex h-full w-full flex-col justify-between p-1">
71
+ {tileColor !== "transparent" && (
72
+ <div
73
+ className="pointer-events-none absolute -inset-[5px] z-0 rounded-2xl"
74
+ style={{ backgroundColor: tileColor, opacity: 0.32 }}
75
+ />
76
+ )}
77
+ <div className="relative z-10 grid min-w-0 flex-1 grid-cols-[auto_minmax(0,1fr)] grid-rows-[auto_repeat(3,minmax(0,1fr))] items-center gap-x-1 py-0">
78
+ {iconSource !== undefined ? (
79
+ <span>
80
+ <Icon source={iconSource} size={18} />
81
+ </span>
82
+ ) : (
83
+ <span
84
+ className="inline-block h-3 w-3 shrink-0 rounded-full"
85
+ style={{ backgroundColor: dotColor }}
86
+ aria-label={status}
87
+ />
88
+ )}
89
+ <span className="min-w-0 truncate text-[10px] font-medium uppercase opacity-80">
90
+ {status}
91
+ </span>
92
+ <span className="col-span-2 row-span-3 row-start-2 min-w-0 overflow-clip text-sm font-semibold leading-tight [display:-webkit-box] [-webkit-box-orient:vertical] [-webkit-line-clamp:3]">
93
+ {title || <span className="opacity-40">empty</span>}
94
+ </span>
95
+ </div>
96
+ </div>
97
+ )
98
+
99
+ export const AgentMetricsPage = ({
100
+ cost,
101
+ contextTokens,
102
+ }: AgentPageProps): ReactElement => (
103
+ <LabelValueListSurface
104
+ lines={[
105
+ { label: "cost", value: formatCost(cost), icon: "icon://coins" },
106
+ {
107
+ label: "tokens",
108
+ value: formatTokens(contextTokens),
109
+ icon: "icon://activity",
110
+ },
111
+ ]}
112
+ />
113
+ )
114
+
115
+ export const AgentContextPage = ({
116
+ contextPercent,
117
+ directory,
118
+ }: AgentPageProps): ReactElement => (
119
+ <LabelValueListSurface
120
+ lines={[
121
+ {
122
+ label: "context",
123
+ value: value(contextPercent),
124
+ units: "%",
125
+ icon: "icon://cpu",
126
+ },
127
+ { label: "project", value: shortPath(directory), icon: "icon://folder" },
128
+ ]}
129
+ />
130
+ )
131
+
132
+ export const AgentDetailsPage = ({
133
+ sessionId,
134
+ lastMessagePreview,
135
+ }: AgentPageProps): ReactElement => (
136
+ <LabelValueListSurface
137
+ lines={[
138
+ {
139
+ label: "session",
140
+ value: shortSessionId(sessionId),
141
+ icon: "icon://clock",
142
+ },
143
+ {
144
+ label: "activity",
145
+ value: lastMessagePreview ?? "---",
146
+ icon: "icon://terminal",
147
+ },
148
+ ]}
149
+ />
150
+ )
@@ -0,0 +1,48 @@
1
+ // ponytail: see packages/addons/app-shortcuts/src/index.ts for context.
2
+ import type {
3
+ AddonButtonServiceContext,
4
+ AddonButtonTypeService,
5
+ } from "../../types/types.js"
6
+
7
+ import { AGENTS_DECK_BASE } from "../../shared/live-count.js"
8
+ import { configSchema, type SummaryConfig } from "./config.js"
9
+
10
+ interface CoreMethodsShape {
11
+ navigateToDeck: (args: { id: string; addToHistory?: boolean }) => void
12
+ }
13
+
14
+ const methodsFor = (ctx: AddonButtonServiceContext<SummaryConfig>) =>
15
+ ctx.methods as Readonly<Record<string, (...args: unknown[]) => unknown>>
16
+
17
+ // ponytail: the agents deck is dynamic (pages depend on live session count),
18
+ // so the navigation target resolves from the global service at tap time —
19
+ // `-p1` when paginated, the base id when a single page exists.
20
+ const deckTargetFor = (
21
+ ctx: AddonButtonServiceContext<SummaryConfig>,
22
+ ): string => {
23
+ const get = methodsFor(ctx)["coding-agents:getDeckTarget"]
24
+ if (typeof get === "function") {
25
+ const target = get() as unknown
26
+ if (typeof target === "string" && target.length > 0) return target
27
+ }
28
+ return AGENTS_DECK_BASE
29
+ }
30
+
31
+ export default {
32
+ configSchema,
33
+ gestureHandlers: ["tap"] as const,
34
+ onMount: (_ctx: AddonButtonServiceContext<SummaryConfig>): void => {},
35
+ onTap: (ctx: AddonButtonServiceContext<SummaryConfig>): void => {
36
+ const target = deckTargetFor(ctx)
37
+ const core = ctx.coreMethods as CoreMethodsShape | undefined
38
+ if (core?.navigateToDeck) {
39
+ core.navigateToDeck({ id: target, addToHistory: true })
40
+ return
41
+ }
42
+ const fallback = methodsFor(ctx)["coding-agents:open"]
43
+ if (typeof fallback === "function") {
44
+ ;(fallback as (a: { id: string }) => void)({ id: target })
45
+ }
46
+ },
47
+ dispose: (_ctx: AddonButtonServiceContext<SummaryConfig>): void => {},
48
+ } satisfies AddonButtonTypeService<SummaryConfig>
@@ -0,0 +1,11 @@
1
+ import { z } from "zod"
2
+
3
+ export const configSchema = z
4
+ .object({
5
+ showCount: z.boolean().default(true),
6
+ attentionOnly: z.boolean().default(false),
7
+ fallingLetters: z.boolean().default(true),
8
+ })
9
+ .strict()
10
+
11
+ export type SummaryConfig = z.infer<typeof configSchema>