@agentdeck/bridge 0.2.2 → 0.2.3

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 (383) hide show
  1. package/assets/fonts/IBMPlexSans-Bold.ttf +0 -0
  2. package/assets/fonts/IBMPlexSans-Regular.ttf +0 -0
  3. package/assets/fonts/JetBrainsMono-Bold.ttf +0 -0
  4. package/assets/fonts/JetBrainsMono-Regular.ttf +0 -0
  5. package/assets/fonts/LICENSES.md +22 -0
  6. package/dist/adapters/claude-code.d.ts +6 -0
  7. package/dist/adapters/claude-code.d.ts.map +1 -1
  8. package/dist/adapters/claude-code.js +8 -0
  9. package/dist/adapters/claude-code.js.map +1 -1
  10. package/dist/adapters/openclaw.d.ts +24 -0
  11. package/dist/adapters/openclaw.d.ts.map +1 -1
  12. package/dist/adapters/openclaw.js +291 -35
  13. package/dist/adapters/openclaw.js.map +1 -1
  14. package/dist/adapters/opencode-adapter.d.ts +59 -0
  15. package/dist/adapters/opencode-adapter.d.ts.map +1 -1
  16. package/dist/adapters/opencode-adapter.js +297 -35
  17. package/dist/adapters/opencode-adapter.js.map +1 -1
  18. package/dist/adb-reverse.d.ts +1 -1
  19. package/dist/adb-reverse.js +1 -1
  20. package/dist/antigravity-local.d.ts +1 -0
  21. package/dist/antigravity-local.d.ts.map +1 -1
  22. package/dist/antigravity-local.js +33 -0
  23. package/dist/antigravity-local.js.map +1 -1
  24. package/dist/apme/adapters/claude-hook.d.ts.map +1 -1
  25. package/dist/apme/adapters/claude-hook.js +5 -0
  26. package/dist/apme/adapters/claude-hook.js.map +1 -1
  27. package/dist/apme/adapters/codex-hook.d.ts +17 -0
  28. package/dist/apme/adapters/codex-hook.d.ts.map +1 -0
  29. package/dist/apme/adapters/codex-hook.js +95 -0
  30. package/dist/apme/adapters/codex-hook.js.map +1 -0
  31. package/dist/apme/adapters/codex-turn-manager.d.ts +86 -0
  32. package/dist/apme/adapters/codex-turn-manager.d.ts.map +1 -0
  33. package/dist/apme/adapters/codex-turn-manager.js +496 -0
  34. package/dist/apme/adapters/codex-turn-manager.js.map +1 -0
  35. package/dist/apme/adapters/openclaw-hook.d.ts +99 -0
  36. package/dist/apme/adapters/openclaw-hook.d.ts.map +1 -0
  37. package/dist/apme/adapters/openclaw-hook.js +234 -0
  38. package/dist/apme/adapters/openclaw-hook.js.map +1 -0
  39. package/dist/apme/adapters/opencode-hook.d.ts +56 -0
  40. package/dist/apme/adapters/opencode-hook.d.ts.map +1 -0
  41. package/dist/apme/adapters/opencode-hook.js +182 -0
  42. package/dist/apme/adapters/opencode-hook.js.map +1 -0
  43. package/dist/apme/adapters/timeline.d.ts.map +1 -1
  44. package/dist/apme/adapters/timeline.js +6 -1
  45. package/dist/apme/adapters/timeline.js.map +1 -1
  46. package/dist/apme/antigravity-transcript.d.ts +18 -0
  47. package/dist/apme/antigravity-transcript.d.ts.map +1 -0
  48. package/dist/apme/antigravity-transcript.js +94 -0
  49. package/dist/apme/antigravity-transcript.js.map +1 -0
  50. package/dist/apme/classify-turn.d.ts +13 -0
  51. package/dist/apme/classify-turn.d.ts.map +1 -0
  52. package/dist/apme/classify-turn.js +49 -0
  53. package/dist/apme/classify-turn.js.map +1 -0
  54. package/dist/apme/claude-transcript-reader.d.ts +10 -0
  55. package/dist/apme/claude-transcript-reader.d.ts.map +1 -1
  56. package/dist/apme/claude-transcript-reader.js +35 -0
  57. package/dist/apme/claude-transcript-reader.js.map +1 -1
  58. package/dist/apme/collector.d.ts +129 -6
  59. package/dist/apme/collector.d.ts.map +1 -1
  60. package/dist/apme/collector.js +470 -23
  61. package/dist/apme/collector.js.map +1 -1
  62. package/dist/apme/dashboard-html.d.ts.map +1 -1
  63. package/dist/apme/dashboard-html.js +87 -11
  64. package/dist/apme/dashboard-html.js.map +1 -1
  65. package/dist/apme/http.d.ts.map +1 -1
  66. package/dist/apme/http.js +43 -6
  67. package/dist/apme/http.js.map +1 -1
  68. package/dist/apme/index.d.ts +26 -5
  69. package/dist/apme/index.d.ts.map +1 -1
  70. package/dist/apme/index.js +135 -9
  71. package/dist/apme/index.js.map +1 -1
  72. package/dist/apme/pareto.d.ts +39 -0
  73. package/dist/apme/pareto.d.ts.map +1 -0
  74. package/dist/apme/pareto.js +59 -0
  75. package/dist/apme/pareto.js.map +1 -0
  76. package/dist/apme/recommend.d.ts +3 -0
  77. package/dist/apme/recommend.d.ts.map +1 -1
  78. package/dist/apme/recommend.js +43 -1
  79. package/dist/apme/recommend.js.map +1 -1
  80. package/dist/apme/runner.d.ts +92 -4
  81. package/dist/apme/runner.d.ts.map +1 -1
  82. package/dist/apme/runner.js +586 -70
  83. package/dist/apme/runner.js.map +1 -1
  84. package/dist/apme/sample-to-timeline.d.ts +26 -0
  85. package/dist/apme/sample-to-timeline.d.ts.map +1 -0
  86. package/dist/apme/sample-to-timeline.js +95 -0
  87. package/dist/apme/sample-to-timeline.js.map +1 -0
  88. package/dist/apme/scorers/index.d.ts +47 -0
  89. package/dist/apme/scorers/index.d.ts.map +1 -0
  90. package/dist/apme/scorers/index.js +96 -0
  91. package/dist/apme/scorers/index.js.map +1 -0
  92. package/dist/apme/settings.d.ts +14 -10
  93. package/dist/apme/settings.d.ts.map +1 -1
  94. package/dist/apme/settings.js +34 -8
  95. package/dist/apme/settings.js.map +1 -1
  96. package/dist/apme/store.d.ts +33 -1
  97. package/dist/apme/store.d.ts.map +1 -1
  98. package/dist/apme/store.js +334 -25
  99. package/dist/apme/store.js.map +1 -1
  100. package/dist/awaiting-overlay.d.ts +99 -0
  101. package/dist/awaiting-overlay.d.ts.map +1 -0
  102. package/dist/awaiting-overlay.js +148 -0
  103. package/dist/awaiting-overlay.js.map +1 -0
  104. package/dist/ble-sync-spawn.d.ts +63 -0
  105. package/dist/ble-sync-spawn.d.ts.map +1 -0
  106. package/dist/ble-sync-spawn.js +196 -0
  107. package/dist/ble-sync-spawn.js.map +1 -0
  108. package/dist/bridge-core.d.ts +24 -1
  109. package/dist/bridge-core.d.ts.map +1 -1
  110. package/dist/bridge-core.js +157 -24
  111. package/dist/bridge-core.js.map +1 -1
  112. package/dist/broadcast.d.ts +3 -0
  113. package/dist/broadcast.d.ts.map +1 -0
  114. package/dist/broadcast.js +122 -0
  115. package/dist/broadcast.js.map +1 -0
  116. package/dist/check-deps.d.ts.map +1 -1
  117. package/dist/check-deps.js +25 -9
  118. package/dist/check-deps.js.map +1 -1
  119. package/dist/cli.d.ts +15 -1
  120. package/dist/cli.d.ts.map +1 -1
  121. package/dist/cli.js +1021 -93
  122. package/dist/cli.js.map +1 -1
  123. package/dist/codex-auth.d.ts +21 -0
  124. package/dist/codex-auth.d.ts.map +1 -1
  125. package/dist/codex-auth.js +52 -1
  126. package/dist/codex-auth.js.map +1 -1
  127. package/dist/codex-output-parser.d.ts +8 -0
  128. package/dist/codex-output-parser.d.ts.map +1 -1
  129. package/dist/codex-output-parser.js +110 -25
  130. package/dist/codex-output-parser.js.map +1 -1
  131. package/dist/codex-rate-limits.d.ts +16 -0
  132. package/dist/codex-rate-limits.d.ts.map +1 -0
  133. package/dist/codex-rate-limits.js +197 -0
  134. package/dist/codex-rate-limits.js.map +1 -0
  135. package/dist/codex-rollout-response.d.ts +31 -0
  136. package/dist/codex-rollout-response.d.ts.map +1 -0
  137. package/dist/codex-rollout-response.js +135 -0
  138. package/dist/codex-rollout-response.js.map +1 -0
  139. package/dist/daemon-server.d.ts +85 -0
  140. package/dist/daemon-server.d.ts.map +1 -1
  141. package/dist/daemon-server.js +1627 -159
  142. package/dist/daemon-server.js.map +1 -1
  143. package/dist/daemon.js +15 -6
  144. package/dist/daemon.js.map +1 -1
  145. package/dist/display-dim.d.ts +9 -0
  146. package/dist/display-dim.d.ts.map +1 -0
  147. package/dist/display-dim.js +53 -0
  148. package/dist/display-dim.js.map +1 -0
  149. package/dist/display-monitor.d.ts +15 -2
  150. package/dist/display-monitor.d.ts.map +1 -1
  151. package/dist/display-monitor.js +104 -26
  152. package/dist/display-monitor.js.map +1 -1
  153. package/dist/esp32-serial.d.ts +160 -1
  154. package/dist/esp32-serial.d.ts.map +1 -1
  155. package/dist/esp32-serial.js +950 -153
  156. package/dist/esp32-serial.js.map +1 -1
  157. package/dist/fallback-task-timeline.d.ts +42 -0
  158. package/dist/fallback-task-timeline.d.ts.map +1 -0
  159. package/dist/fallback-task-timeline.js +112 -0
  160. package/dist/fallback-task-timeline.js.map +1 -0
  161. package/dist/foundation-models-helper.d.ts +11 -0
  162. package/dist/foundation-models-helper.d.ts.map +1 -0
  163. package/dist/foundation-models-helper.js +241 -0
  164. package/dist/foundation-models-helper.js.map +1 -0
  165. package/dist/hook-migration.js +1 -1
  166. package/dist/hook-migration.js.map +1 -1
  167. package/dist/hook-server.d.ts +2 -3
  168. package/dist/hook-server.d.ts.map +1 -1
  169. package/dist/hook-server.js +22 -10
  170. package/dist/hook-server.js.map +1 -1
  171. package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts +33 -0
  172. package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts.map +1 -0
  173. package/dist/idotmatrix/idotmatrix-daemon-sync.js +164 -0
  174. package/dist/idotmatrix/idotmatrix-daemon-sync.js.map +1 -0
  175. package/dist/idotmatrix/idotmatrix-discover.d.ts +18 -0
  176. package/dist/idotmatrix/idotmatrix-discover.d.ts.map +1 -0
  177. package/dist/idotmatrix/idotmatrix-discover.js +85 -0
  178. package/dist/idotmatrix/idotmatrix-discover.js.map +1 -0
  179. package/dist/idotmatrix/idotmatrix-settings.d.ts +20 -0
  180. package/dist/idotmatrix/idotmatrix-settings.d.ts.map +1 -0
  181. package/dist/idotmatrix/idotmatrix-settings.js +57 -0
  182. package/dist/idotmatrix/idotmatrix-settings.js.map +1 -0
  183. package/dist/index.d.ts.map +1 -1
  184. package/dist/index.js +215 -179
  185. package/dist/index.js.map +1 -1
  186. package/dist/log-stream.d.ts +15 -16
  187. package/dist/log-stream.d.ts.map +1 -1
  188. package/dist/log-stream.js +18 -96
  189. package/dist/log-stream.js.map +1 -1
  190. package/dist/logger.d.ts.map +1 -1
  191. package/dist/logger.js +12 -3
  192. package/dist/logger.js.map +1 -1
  193. package/dist/mdns.d.ts +17 -0
  194. package/dist/mdns.d.ts.map +1 -1
  195. package/dist/mdns.js +52 -18
  196. package/dist/mdns.js.map +1 -1
  197. package/dist/modules/adb-module.d.ts +1 -1
  198. package/dist/modules/adb-module.d.ts.map +1 -1
  199. package/dist/modules/adb-module.js +4 -3
  200. package/dist/modules/adb-module.js.map +1 -1
  201. package/dist/modules/broadcast-module.d.ts +17 -0
  202. package/dist/modules/broadcast-module.d.ts.map +1 -0
  203. package/dist/modules/broadcast-module.js +26 -0
  204. package/dist/modules/broadcast-module.js.map +1 -0
  205. package/dist/modules/idotmatrix-module.d.ts +9 -0
  206. package/dist/modules/idotmatrix-module.d.ts.map +1 -0
  207. package/dist/modules/idotmatrix-module.js +46 -0
  208. package/dist/modules/idotmatrix-module.js.map +1 -0
  209. package/dist/modules/index.d.ts +3 -1
  210. package/dist/modules/index.d.ts.map +1 -1
  211. package/dist/modules/index.js +9 -3
  212. package/dist/modules/index.js.map +1 -1
  213. package/dist/modules/pixoo-module.d.ts.map +1 -1
  214. package/dist/modules/pixoo-module.js +14 -4
  215. package/dist/modules/pixoo-module.js.map +1 -1
  216. package/dist/modules/serial-module.d.ts +4 -0
  217. package/dist/modules/serial-module.d.ts.map +1 -1
  218. package/dist/modules/serial-module.js +14 -12
  219. package/dist/modules/serial-module.js.map +1 -1
  220. package/dist/modules/timebox-module.d.ts +9 -0
  221. package/dist/modules/timebox-module.d.ts.map +1 -0
  222. package/dist/modules/timebox-module.js +46 -0
  223. package/dist/modules/timebox-module.js.map +1 -0
  224. package/dist/modules/types.d.ts +4 -2
  225. package/dist/modules/types.d.ts.map +1 -1
  226. package/dist/openclaw-session.d.ts +18 -0
  227. package/dist/openclaw-session.d.ts.map +1 -0
  228. package/dist/openclaw-session.js +34 -0
  229. package/dist/openclaw-session.js.map +1 -0
  230. package/dist/output-parser.d.ts +30 -0
  231. package/dist/output-parser.d.ts.map +1 -1
  232. package/dist/output-parser.js +125 -9
  233. package/dist/output-parser.js.map +1 -1
  234. package/dist/passive-observer.d.ts +26 -0
  235. package/dist/passive-observer.d.ts.map +1 -1
  236. package/dist/passive-observer.js +258 -49
  237. package/dist/passive-observer.js.map +1 -1
  238. package/dist/permission-resolver.d.ts +47 -0
  239. package/dist/permission-resolver.d.ts.map +1 -0
  240. package/dist/permission-resolver.js +103 -0
  241. package/dist/permission-resolver.js.map +1 -0
  242. package/dist/pixoo/micro-glyphs.d.ts +49 -0
  243. package/dist/pixoo/micro-glyphs.d.ts.map +1 -0
  244. package/dist/pixoo/micro-glyphs.js +276 -0
  245. package/dist/pixoo/micro-glyphs.js.map +1 -0
  246. package/dist/pixoo/pixoo-bridge.d.ts +2 -2
  247. package/dist/pixoo/pixoo-bridge.d.ts.map +1 -1
  248. package/dist/pixoo/pixoo-bridge.js +93 -25
  249. package/dist/pixoo/pixoo-bridge.js.map +1 -1
  250. package/dist/pixoo/pixoo-camera.d.ts +19 -3
  251. package/dist/pixoo/pixoo-camera.d.ts.map +1 -1
  252. package/dist/pixoo/pixoo-camera.js +62 -27
  253. package/dist/pixoo/pixoo-camera.js.map +1 -1
  254. package/dist/pixoo/pixoo-client.d.ts +13 -3
  255. package/dist/pixoo/pixoo-client.d.ts.map +1 -1
  256. package/dist/pixoo/pixoo-client.js +51 -22
  257. package/dist/pixoo/pixoo-client.js.map +1 -1
  258. package/dist/pixoo/pixoo-discover.d.ts +37 -0
  259. package/dist/pixoo/pixoo-discover.d.ts.map +1 -0
  260. package/dist/pixoo/pixoo-discover.js +105 -0
  261. package/dist/pixoo/pixoo-discover.js.map +1 -0
  262. package/dist/pixoo/pixoo-renderer.d.ts +8 -3
  263. package/dist/pixoo/pixoo-renderer.d.ts.map +1 -1
  264. package/dist/pixoo/pixoo-renderer.js +189 -60
  265. package/dist/pixoo/pixoo-renderer.js.map +1 -1
  266. package/dist/pixoo/pixoo-settings.d.ts +6 -0
  267. package/dist/pixoo/pixoo-settings.d.ts.map +1 -1
  268. package/dist/pixoo/pixoo-settings.js +9 -0
  269. package/dist/pixoo/pixoo-settings.js.map +1 -1
  270. package/dist/pixoo/pixoo-sprites.d.ts +60 -11
  271. package/dist/pixoo/pixoo-sprites.d.ts.map +1 -1
  272. package/dist/pixoo/pixoo-sprites.js +512 -209
  273. package/dist/pixoo/pixoo-sprites.js.map +1 -1
  274. package/dist/pty-manager.d.ts.map +1 -1
  275. package/dist/pty-manager.js +8 -2
  276. package/dist/pty-manager.js.map +1 -1
  277. package/dist/session-activity.d.ts +14 -0
  278. package/dist/session-activity.d.ts.map +1 -0
  279. package/dist/session-activity.js +160 -0
  280. package/dist/session-activity.js.map +1 -0
  281. package/dist/session-aggregator.d.ts +17 -0
  282. package/dist/session-aggregator.d.ts.map +1 -1
  283. package/dist/session-aggregator.js +48 -9
  284. package/dist/session-aggregator.js.map +1 -1
  285. package/dist/session-registry.d.ts +71 -3
  286. package/dist/session-registry.d.ts.map +1 -1
  287. package/dist/session-registry.js +137 -8
  288. package/dist/session-registry.js.map +1 -1
  289. package/dist/session-timeline-relay.d.ts.map +1 -1
  290. package/dist/session-timeline-relay.js +13 -4
  291. package/dist/session-timeline-relay.js.map +1 -1
  292. package/dist/session-transcript-timeline.d.ts +49 -0
  293. package/dist/session-transcript-timeline.d.ts.map +1 -0
  294. package/dist/session-transcript-timeline.js +276 -0
  295. package/dist/session-transcript-timeline.js.map +1 -0
  296. package/dist/state-machine.d.ts +6 -1
  297. package/dist/state-machine.d.ts.map +1 -1
  298. package/dist/state-machine.js +86 -28
  299. package/dist/state-machine.js.map +1 -1
  300. package/dist/timebox/timebox-daemon-sync.d.ts +19 -0
  301. package/dist/timebox/timebox-daemon-sync.d.ts.map +1 -0
  302. package/dist/timebox/timebox-daemon-sync.js +135 -0
  303. package/dist/timebox/timebox-daemon-sync.js.map +1 -0
  304. package/dist/timebox/timebox-discover.d.ts +19 -0
  305. package/dist/timebox/timebox-discover.d.ts.map +1 -0
  306. package/dist/timebox/timebox-discover.js +87 -0
  307. package/dist/timebox/timebox-discover.js.map +1 -0
  308. package/dist/timebox/timebox-settings.d.ts +32 -0
  309. package/dist/timebox/timebox-settings.d.ts.map +1 -0
  310. package/dist/timebox/timebox-settings.js +80 -0
  311. package/dist/timebox/timebox-settings.js.map +1 -0
  312. package/dist/timeline-store.d.ts +63 -3
  313. package/dist/timeline-store.d.ts.map +1 -1
  314. package/dist/timeline-store.js +292 -14
  315. package/dist/timeline-store.js.map +1 -1
  316. package/dist/timeline-summarizer.d.ts.map +1 -1
  317. package/dist/timeline-summarizer.js +29 -5
  318. package/dist/timeline-summarizer.js.map +1 -1
  319. package/dist/tui/ansi.d.ts +0 -4
  320. package/dist/tui/ansi.d.ts.map +1 -1
  321. package/dist/tui/ansi.js +14 -7
  322. package/dist/tui/ansi.js.map +1 -1
  323. package/dist/tui/dashboard.d.ts +1 -0
  324. package/dist/tui/dashboard.d.ts.map +1 -1
  325. package/dist/tui/dashboard.js +24 -7
  326. package/dist/tui/dashboard.js.map +1 -1
  327. package/dist/tui/renderer.d.ts +36 -0
  328. package/dist/tui/renderer.d.ts.map +1 -1
  329. package/dist/tui/renderer.js +303 -119
  330. package/dist/tui/renderer.js.map +1 -1
  331. package/dist/tui/terrarium.d.ts.map +1 -1
  332. package/dist/tui/terrarium.js +37 -49
  333. package/dist/tui/terrarium.js.map +1 -1
  334. package/dist/types.d.ts +2 -2
  335. package/dist/types.d.ts.map +1 -1
  336. package/dist/types.js +2 -2
  337. package/dist/types.js.map +1 -1
  338. package/dist/usage-api.d.ts.map +1 -1
  339. package/dist/usage-api.js +7 -1
  340. package/dist/usage-api.js.map +1 -1
  341. package/dist/usage-event.d.ts +10 -3
  342. package/dist/usage-event.d.ts.map +1 -1
  343. package/dist/usage-event.js +74 -12
  344. package/dist/usage-event.js.map +1 -1
  345. package/dist/utility-proxy.d.ts.map +1 -1
  346. package/dist/utility-proxy.js +8 -0
  347. package/dist/utility-proxy.js.map +1 -1
  348. package/dist/utils/project-name.d.ts +3 -0
  349. package/dist/utils/project-name.d.ts.map +1 -1
  350. package/dist/utils/project-name.js +44 -2
  351. package/dist/utils/project-name.js.map +1 -1
  352. package/dist/windows-service.d.ts +26 -0
  353. package/dist/windows-service.d.ts.map +1 -0
  354. package/dist/windows-service.js +145 -0
  355. package/dist/windows-service.js.map +1 -0
  356. package/dist/ws-server.d.ts +22 -1
  357. package/dist/ws-server.d.ts.map +1 -1
  358. package/dist/ws-server.js +89 -4
  359. package/dist/ws-server.js.map +1 -1
  360. package/fm-helper/AgentDeckFMHelper.swift +112 -0
  361. package/package.json +9 -3
  362. package/scripts/build-fm-helper.mjs +26 -0
  363. package/LICENSE +0 -21
  364. package/dist/apme/tuner.d.ts +0 -64
  365. package/dist/apme/tuner.d.ts.map +0 -1
  366. package/dist/apme/tuner.js +0 -297
  367. package/dist/apme/tuner.js.map +0 -1
  368. package/dist/d200h/hid-protocol.d.ts +0 -62
  369. package/dist/d200h/hid-protocol.d.ts.map +0 -1
  370. package/dist/d200h/hid-protocol.js +0 -124
  371. package/dist/d200h/hid-protocol.js.map +0 -1
  372. package/dist/d200h/image-renderer.d.ts +0 -36
  373. package/dist/d200h/image-renderer.d.ts.map +0 -1
  374. package/dist/d200h/image-renderer.js +0 -487
  375. package/dist/d200h/image-renderer.js.map +0 -1
  376. package/dist/modules/d200h-module.d.ts +0 -43
  377. package/dist/modules/d200h-module.d.ts.map +0 -1
  378. package/dist/modules/d200h-module.js +0 -360
  379. package/dist/modules/d200h-module.js.map +0 -1
  380. package/dist/modules/d200h-renderer.d.ts +0 -7
  381. package/dist/modules/d200h-renderer.d.ts.map +0 -1
  382. package/dist/modules/d200h-renderer.js +0 -321
  383. package/dist/modules/d200h-renderer.js.map +0 -1
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Windows daemon autostart via a per-user Scheduled Task (logon trigger).
3
+ *
4
+ * The macOS LaunchAgent analog on Windows is a per-user Scheduled Task with a
5
+ * logon trigger — NOT a real Windows Service. A service runs in session 0 with
6
+ * no desktop and restricted device access, which breaks the daemon's USB-HID
7
+ * (D200H), audio (wake-word), mDNS, and Stream Deck app integration. A logon
8
+ * task runs in the interactive user session, needs no admin elevation, and uses
9
+ * only the built-in schtasks.exe (no npm dependency).
10
+ *
11
+ * The pure XML/args builder is unit-tested (cross-platform); the schtasks
12
+ * /Create|/Run|/Query|/Delete calls are integration-only (real Windows host +
13
+ * side effects) and are exercised manually per docs/daemon.md.
14
+ */
15
+ import { writeFileSync, unlinkSync } from 'fs';
16
+ import { homedir, tmpdir, userInfo } from 'os';
17
+ import { dirname, join } from 'path';
18
+ import { execSync } from 'child_process';
19
+ import { fileURLToPath } from 'url';
20
+ export const TASK_NAME = 'AgentDeckDaemon';
21
+ export function xmlEscape(s) {
22
+ return s
23
+ .replace(/&/g, '&')
24
+ .replace(/</g, '&lt;')
25
+ .replace(/>/g, '&gt;')
26
+ .replace(/"/g, '&quot;');
27
+ }
28
+ // Resolve node.exe + cli.js directly rather than the `agentdeck` npm shim: the
29
+ // global bin on Windows is an `agentdeck.cmd` wrapper whose quoting breaks the
30
+ // Task Scheduler <Command>/<Arguments> split. process.execPath is the running
31
+ // node; cli.js sits next to this module in the dist dir.
32
+ export function getDaemonNodeTarget() {
33
+ const distDir = dirname(fileURLToPath(import.meta.url));
34
+ return { node: process.execPath, cliJs: join(distDir, 'cli.js') };
35
+ }
36
+ export function getCurrentTaskUser() {
37
+ const domain = process.env.USERDOMAIN;
38
+ const user = process.env.USERNAME || userInfo().username;
39
+ return domain ? `${domain}\\${user}` : user;
40
+ }
41
+ export function buildScheduledTaskXml(opts) {
42
+ const { node, cliJs } = { ...getDaemonNodeTarget(), ...opts };
43
+ const user = opts?.user ?? getCurrentTaskUser();
44
+ const workingDir = join(homedir(), '.agentdeck');
45
+ const command = xmlEscape(node);
46
+ // cli.js wrapped in quotes to survive paths with spaces; --foreground so the
47
+ // task process IS the daemon (lets RestartOnFailure track the real process).
48
+ const args = xmlEscape(`"${cliJs}" daemon start --foreground`);
49
+ const userEsc = xmlEscape(user);
50
+ // schtasks /XML requires UTF-16 with a BOM (see installWindowsTask); declaring
51
+ // UTF-8 triggers "unable to switch the encoding". The bytes are written as
52
+ // UTF-16LE+BOM at install time to match this declaration.
53
+ return `<?xml version="1.0" encoding="UTF-16"?>
54
+ <Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
55
+ <RegistrationInfo>
56
+ <Description>AgentDeck monitoring daemon (auto-start on logon)</Description>
57
+ <URI>\\${TASK_NAME}</URI>
58
+ </RegistrationInfo>
59
+ <Triggers>
60
+ <LogonTrigger>
61
+ <Enabled>true</Enabled>
62
+ <UserId>${userEsc}</UserId>
63
+ </LogonTrigger>
64
+ </Triggers>
65
+ <Principals>
66
+ <Principal id="Author">
67
+ <UserId>${userEsc}</UserId>
68
+ <LogonType>InteractiveToken</LogonType>
69
+ <RunLevel>LeastPrivilege</RunLevel>
70
+ </Principal>
71
+ </Principals>
72
+ <Settings>
73
+ <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
74
+ <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
75
+ <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
76
+ <AllowHardTerminate>true</AllowHardTerminate>
77
+ <StartWhenAvailable>true</StartWhenAvailable>
78
+ <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
79
+ <IdleSettings>
80
+ <StopOnIdleEnd>false</StopOnIdleEnd>
81
+ <RestartOnIdle>false</RestartOnIdle>
82
+ </IdleSettings>
83
+ <AllowStartOnDemand>true</AllowStartOnDemand>
84
+ <Enabled>true</Enabled>
85
+ <Hidden>false</Hidden>
86
+ <RunOnlyIfIdle>false</RunOnlyIfIdle>
87
+ <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
88
+ <Priority>7</Priority>
89
+ <RestartOnFailure>
90
+ <Interval>PT1M</Interval>
91
+ <Count>3</Count>
92
+ </RestartOnFailure>
93
+ </Settings>
94
+ <Actions Context="Author">
95
+ <Exec>
96
+ <Command>${command}</Command>
97
+ <Arguments>${args}</Arguments>
98
+ <WorkingDirectory>${xmlEscape(workingDir)}</WorkingDirectory>
99
+ </Exec>
100
+ </Actions>
101
+ </Task>`;
102
+ }
103
+ /**
104
+ * Register (or overwrite) the AgentDeckDaemon scheduled task.
105
+ * Throws if schtasks /Create fails (e.g. group policy disabling user task creation).
106
+ */
107
+ export function installWindowsTask() {
108
+ const xml = buildScheduledTaskXml();
109
+ const tmpFile = join(tmpdir(), `agentdeck-task-${process.pid}-${Date.now()}.xml`);
110
+ // Write UTF-16LE with a BOM (U+FEFF) — schtasks /XML rejects UTF-8 here.
111
+ writeFileSync(tmpFile, '' + xml, 'utf16le');
112
+ try {
113
+ // /F overwrites an existing task (idempotent; mirrors macOS unload-before-load).
114
+ execSync(`schtasks /Create /TN "${TASK_NAME}" /XML "${tmpFile}" /F`, { stdio: 'pipe' });
115
+ }
116
+ finally {
117
+ try {
118
+ unlinkSync(tmpFile);
119
+ }
120
+ catch { /* temp cleanup best-effort */ }
121
+ }
122
+ }
123
+ /** True if the AgentDeckDaemon scheduled task is registered. */
124
+ export function taskExists() {
125
+ try {
126
+ execSync(`schtasks /Query /TN "${TASK_NAME}"`, { stdio: 'pipe' });
127
+ return true;
128
+ }
129
+ catch {
130
+ return false;
131
+ }
132
+ }
133
+ /** Start the registered task immediately (so no logout is required). */
134
+ export function runWindowsTask() {
135
+ execSync(`schtasks /Run /TN "${TASK_NAME}"`, { stdio: 'pipe' });
136
+ }
137
+ /** Stop a running task instance (best-effort; ignores "not running"). */
138
+ export function endWindowsTask() {
139
+ execSync(`schtasks /End /TN "${TASK_NAME}"`, { stdio: 'pipe' });
140
+ }
141
+ /** Delete the registered task (/F suppresses the confirm prompt). */
142
+ export function deleteWindowsTask() {
143
+ execSync(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: 'pipe' });
144
+ }
145
+ //# sourceMappingURL=windows-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"windows-service.js","sourceRoot":"","sources":["../src/windows-service.ts"],"names":[],"mappings":"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"}
@@ -8,13 +8,26 @@ export declare class WsServer {
8
8
  private onConnectCallback;
9
9
  private onDisconnectCallback;
10
10
  private clientAlive;
11
+ private esp32Clients;
12
+ private eventTransformer;
13
+ private ulanziClients;
14
+ private ulanziPresenceCallback;
15
+ private tuiClients;
11
16
  private pingTimer;
12
17
  constructor(server: Server);
13
18
  private broadcastHooks;
14
19
  /** Register a hook that gets called on every broadcast (e.g., ESP32 serial relay). */
15
20
  onBroadcast(hook: (event: BridgeEvent) => void): void;
21
+ setEventTransformer(transformer: ((event: BridgeEvent, client: WebSocket) => BridgeEvent | null) | null): void;
22
+ isEsp32Client(ws: WebSocket): boolean;
23
+ markEsp32Client(ws: WebSocket): void;
24
+ private payloadFor;
16
25
  broadcast(event: BridgeEvent): void;
17
26
  onCommand(callback: (cmd: PluginCommand) => void): void;
27
+ /** Register a callback invoked when the Ulanzi-plugin presence flips (true =
28
+ * at least one connected, false = none). Fires once immediately with the
29
+ * current state so the consumer can sync on startup. */
30
+ onUlanziPluginPresence(callback: (present: boolean) => void): void;
18
31
  /** Inject a command from a non-WS source (e.g., D200H agent via stdout/stdin pipe). */
19
32
  dispatchCommand(cmd: PluginCommand): void;
20
33
  /** Register a callback for raw messages before PluginCommand dispatch. Return true to consume. */
@@ -22,9 +35,17 @@ export declare class WsServer {
22
35
  /** Broadcast to all clients except the sender */
23
36
  broadcastExcept(event: BridgeEvent, except: WebSocket): void;
24
37
  onClientConnect(callback: (ws: WebSocket) => void): void;
25
- onClientDisconnect(callback: () => void): void;
38
+ onClientDisconnect(callback: (ws: WebSocket) => void): void;
26
39
  sendTo(ws: WebSocket, event: BridgeEvent): void;
27
40
  getClientCount(): number;
41
+ getUlanziClientCount(): number;
42
+ /** Registered TUI dashboards (`client_register {clientType:"tui"}`), deduped
43
+ * by client id so a TUI that reconnects (new WS, same host+pid) yields one
44
+ * entry while both sockets briefly overlap. */
45
+ getTuiClients(): Array<{
46
+ id: string;
47
+ name: string;
48
+ }>;
28
49
  close(): void;
29
50
  }
30
51
  //# sourceMappingURL=ws-server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ws-server.d.ts","sourceRoot":"","sources":["../src/ws-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,SAAS,EAAE,MAAM,IAAI,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAmB,MAAM,MAAM,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAK7D,qBAAa,QAAQ;IACnB,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,eAAe,CAA+C;IACtE,OAAO,CAAC,kBAAkB,CAA+E;IACzG,OAAO,CAAC,iBAAiB,CAA0C;IACnE,OAAO,CAAC,oBAAoB,CAA6B;IACzD,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,SAAS,CAAiC;gBAEtC,MAAM,EAAE,MAAM;IAqF1B,OAAO,CAAC,cAAc,CAA2C;IAEjE,sFAAsF;IACtF,WAAW,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;IAIrD,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAenC,SAAS,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI;IAIvD,uFAAuF;IACvF,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IAIzC,kGAAkG;IAClG,YAAY,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,GAAG,IAAI;IAI1F,iDAAiD;IACjD,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI;IAS5D,eAAe,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,GAAG,IAAI;IAIxD,kBAAkB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAI9C,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI;IAM/C,cAAc,IAAI,MAAM;IAIxB,KAAK,IAAI,IAAI;CASd"}
1
+ {"version":3,"file":"ws-server.d.ts","sourceRoot":"","sources":["../src/ws-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,SAAS,EAAE,MAAM,IAAI,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAmB,MAAM,MAAM,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAK7D,qBAAa,QAAQ;IACnB,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,eAAe,CAA+C;IACtE,OAAO,CAAC,kBAAkB,CAA+E;IACzG,OAAO,CAAC,iBAAiB,CAA0C;IACnE,OAAO,CAAC,oBAAoB,CAA0C;IACtE,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,gBAAgB,CAAgF;IAIxG,OAAO,CAAC,aAAa,CAAwB;IAC7C,OAAO,CAAC,sBAAsB,CAA6C;IAK3E,OAAO,CAAC,UAAU,CAAsD;IACxE,OAAO,CAAC,SAAS,CAAiC;gBAEtC,MAAM,EAAE,MAAM;IAmH1B,OAAO,CAAC,cAAc,CAA2C;IAEjE,sFAAsF;IACtF,WAAW,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;IAIrD,mBAAmB,CAAC,WAAW,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,KAAK,WAAW,GAAG,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI;IAI9G,aAAa,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO;IAIrC,eAAe,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;IAIpC,OAAO,CAAC,UAAU;IAOlB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAiBnC,SAAS,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI;IAIvD;;6DAEyD;IACzD,sBAAsB,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAKlE,uFAAuF;IACvF,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IAIzC,kGAAkG;IAClG,YAAY,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,GAAG,IAAI;IAI1F,iDAAiD;IACjD,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI;IAW5D,eAAe,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,GAAG,IAAI;IAIxD,kBAAkB,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,GAAG,IAAI;IAI3D,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI;IAQ/C,cAAc,IAAI,MAAM;IAIxB,oBAAoB,IAAI,MAAM;IAI9B;;oDAEgD;IAChD,aAAa,IAAI,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAMpD,KAAK,IAAI,IAAI;CASd"}
package/dist/ws-server.js CHANGED
@@ -9,6 +9,18 @@ export class WsServer {
9
9
  onConnectCallback = null;
10
10
  onDisconnectCallback = null;
11
11
  clientAlive = new Map();
12
+ esp32Clients = new Set();
13
+ eventTransformer = null;
14
+ // Clients that registered as the Ulanzi Studio plugin. While any are present,
15
+ // the daemon's direct-HID D200H module stands down so the two don't fight over
16
+ // the device (Ulanzi Studio drives it through the official plugin instead).
17
+ ulanziClients = new Set();
18
+ ulanziPresenceCallback = null;
19
+ // TUI dashboards (`agentdeck dashboard`) that registered via
20
+ // `client_register {clientType:"tui"}`. Volunteer-roster model like the
21
+ // Stream Deck plugin — presence only lives as long as the WS does, so the
22
+ // topology row disappears the moment the TUI exits.
23
+ tuiClients = new Map();
12
24
  pingTimer;
13
25
  constructor(server) {
14
26
  this.wss = new WebSocketServer({ server });
@@ -49,6 +61,11 @@ export class WsServer {
49
61
  }
50
62
  debug('WS', `Remote client authenticated from ${remoteIp}`);
51
63
  }
64
+ const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
65
+ if (url.searchParams.get('clientType') === 'esp32' || url.searchParams.get('esp32') === '1') {
66
+ this.esp32Clients.add(ws);
67
+ debug('WS', 'ESP32 WiFi client tagged from query');
68
+ }
52
69
  debug('WS', 'Plugin connected');
53
70
  this.clientAlive.set(ws, true);
54
71
  // Send current state to newly connected client
@@ -62,6 +79,23 @@ export class WsServer {
62
79
  try {
63
80
  const msg = JSON.parse(data.toString());
64
81
  debug('WS', `recv cmd: ${msg.type}`);
82
+ // Track Ulanzi plugin presence (device-ownership arbitration).
83
+ if (msg.type === 'client_register' && msg.clientType === 'ulanzi-plugin') {
84
+ const was = this.ulanziClients.size > 0;
85
+ this.ulanziClients.add(ws);
86
+ if (!was) {
87
+ debug('WS', 'Ulanzi plugin registered — direct-HID D200H stands down');
88
+ this.ulanziPresenceCallback?.(true);
89
+ }
90
+ }
91
+ // Track TUI dashboard presence (topology row on all dashboards).
92
+ if (msg.type === 'client_register' && msg.clientType === 'tui') {
93
+ const dev = (Array.isArray(msg.devices) ? msg.devices[0] : null);
94
+ const name = typeof dev?.name === 'string' && dev.name ? dev.name : 'terminal';
95
+ const id = typeof dev?.id === 'string' && dev.id ? dev.id : name;
96
+ this.tuiClients.set(ws, { id, name });
97
+ debug('WS', `TUI dashboard registered: ${id}`);
98
+ }
65
99
  // Allow raw message callback to intercept relay events (e.g. deck_slot_map)
66
100
  if (this.rawMessageCallback && this.rawMessageCallback(msg, ws)) {
67
101
  return; // handled
@@ -77,8 +111,14 @@ export class WsServer {
77
111
  ws.on('close', () => {
78
112
  debug('WS', 'Plugin disconnected');
79
113
  this.clientAlive.delete(ws);
114
+ this.esp32Clients.delete(ws);
115
+ this.tuiClients.delete(ws);
116
+ if (this.ulanziClients.delete(ws) && this.ulanziClients.size === 0) {
117
+ debug('WS', 'Ulanzi plugin gone — direct-HID D200H may resume');
118
+ this.ulanziPresenceCallback?.(false);
119
+ }
80
120
  if (this.onDisconnectCallback) {
81
- this.onDisconnectCallback();
121
+ this.onDisconnectCallback(ws);
82
122
  }
83
123
  });
84
124
  ws.on('error', (err) => {
@@ -91,14 +131,34 @@ export class WsServer {
91
131
  onBroadcast(hook) {
92
132
  this.broadcastHooks.push(hook);
93
133
  }
134
+ setEventTransformer(transformer) {
135
+ this.eventTransformer = transformer;
136
+ }
137
+ isEsp32Client(ws) {
138
+ return this.esp32Clients.has(ws);
139
+ }
140
+ markEsp32Client(ws) {
141
+ this.esp32Clients.add(ws);
142
+ }
143
+ payloadFor(event, client, cachedPayload) {
144
+ if (!this.eventTransformer)
145
+ return cachedPayload ?? JSON.stringify(event);
146
+ const transformed = this.eventTransformer(event, client);
147
+ if (!transformed)
148
+ return null;
149
+ return transformed === event ? (cachedPayload ?? JSON.stringify(event)) : JSON.stringify(transformed);
150
+ }
94
151
  broadcast(event) {
95
152
  const payload = JSON.stringify(event);
96
153
  const clientCount = this.wss.clients.size;
97
154
  debug('WS', `broadcast(${event.type}) to ${clientCount} clients`);
98
155
  for (const client of this.wss.clients) {
99
156
  if (client.readyState === WebSocket.OPEN) {
157
+ const clientPayload = this.payloadFor(event, client, payload);
158
+ if (!clientPayload)
159
+ continue;
100
160
  try {
101
- client.send(payload);
161
+ client.send(clientPayload);
102
162
  }
103
163
  catch { /* client disconnecting */ }
104
164
  }
@@ -114,6 +174,13 @@ export class WsServer {
114
174
  onCommand(callback) {
115
175
  this.commandCallback = callback;
116
176
  }
177
+ /** Register a callback invoked when the Ulanzi-plugin presence flips (true =
178
+ * at least one connected, false = none). Fires once immediately with the
179
+ * current state so the consumer can sync on startup. */
180
+ onUlanziPluginPresence(callback) {
181
+ this.ulanziPresenceCallback = callback;
182
+ callback(this.ulanziClients.size > 0);
183
+ }
117
184
  /** Inject a command from a non-WS source (e.g., D200H agent via stdout/stdin pipe). */
118
185
  dispatchCommand(cmd) {
119
186
  this.commandCallback?.(cmd);
@@ -127,8 +194,11 @@ export class WsServer {
127
194
  const payload = JSON.stringify(event);
128
195
  for (const client of this.wss.clients) {
129
196
  if (client !== except && client.readyState === WebSocket.OPEN) {
197
+ const clientPayload = this.payloadFor(event, client, payload);
198
+ if (!clientPayload)
199
+ continue;
130
200
  try {
131
- client.send(payload);
201
+ client.send(clientPayload);
132
202
  }
133
203
  catch { /* client disconnecting */ }
134
204
  }
@@ -142,8 +212,11 @@ export class WsServer {
142
212
  }
143
213
  sendTo(ws, event) {
144
214
  if (ws.readyState === WebSocket.OPEN) {
215
+ const payload = this.payloadFor(event, ws);
216
+ if (!payload)
217
+ return;
145
218
  try {
146
- ws.send(JSON.stringify(event));
219
+ ws.send(payload);
147
220
  }
148
221
  catch { /* client disconnecting */ }
149
222
  }
@@ -151,6 +224,18 @@ export class WsServer {
151
224
  getClientCount() {
152
225
  return this.wss.clients.size;
153
226
  }
227
+ getUlanziClientCount() {
228
+ return this.ulanziClients.size;
229
+ }
230
+ /** Registered TUI dashboards (`client_register {clientType:"tui"}`), deduped
231
+ * by client id so a TUI that reconnects (new WS, same host+pid) yields one
232
+ * entry while both sockets briefly overlap. */
233
+ getTuiClients() {
234
+ const byId = new Map();
235
+ for (const info of this.tuiClients.values())
236
+ byId.set(info.id, info);
237
+ return [...byId.values()];
238
+ }
154
239
  close() {
155
240
  clearInterval(this.pingTimer);
156
241
  this.clientAlive.clear();
@@ -1 +1 @@
1
- {"version":3,"file":"ws-server.js","sourceRoot":"","sources":["../src/ws-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ws-server.js","sourceRoot":"","sources":["../src/ws-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAGhD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,OAAO,QAAQ;IACX,GAAG,CAAkB;IACrB,eAAe,GAA0C,IAAI,CAAC;IAC9D,kBAAkB,GAA0E,IAAI,CAAC;IACjG,iBAAiB,GAAqC,IAAI,CAAC;IAC3D,oBAAoB,GAAqC,IAAI,CAAC;IAC9D,WAAW,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC5C,YAAY,GAAG,IAAI,GAAG,EAAa,CAAC;IACpC,gBAAgB,GAA2E,IAAI,CAAC;IACxG,8EAA8E;IAC9E,+EAA+E;IAC/E,4EAA4E;IACpE,aAAa,GAAG,IAAI,GAAG,EAAa,CAAC;IACrC,sBAAsB,GAAwC,IAAI,CAAC;IAC3E,6DAA6D;IAC7D,wEAAwE;IACxE,0EAA0E;IAC1E,oDAAoD;IAC5C,UAAU,GAAG,IAAI,GAAG,EAA2C,CAAC;IAChE,SAAS,CAAiC;IAElD,YAAY,MAAc;QACxB,IAAI,CAAC,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QAE3C,yEAAyE;QACzE,yEAAyE;QACzE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YAC3B,KAAK,CAAC,IAAI,EAAE,0BAA0B,GAAG,EAAE,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,qDAAqD;QACrD,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;YAChC,MAAM,IAAI,GAAgB,EAAE,CAAC;YAC7B,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;gBAClC,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC;oBACvC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACd,SAAS;gBACX,CAAC;gBACD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;gBAChC,EAAE,CAAC,IAAI,EAAE,CAAC;YACZ,CAAC;YACD,qEAAqE;YACrE,qEAAqE;YACrE,KAAK,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;gBACtB,KAAK,CAAC,IAAI,EAAE,iCAAiC,CAAC,CAAC;gBAC/C,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC5B,EAAE,CAAC,SAAS,EAAE,CAAC;YACjB,CAAC;QACH,CAAC,EAAE,mBAAmB,CAAC,CAAC;QAExB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,GAAoB,EAAE,EAAE;YACrD,oCAAoC;YACpC,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,EAAE,CAAC;YAChD,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACjC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,UAAU,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW,EAAE,CAAC,CAAC;gBACjF,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;gBAClD,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC1B,KAAK,CAAC,IAAI,EAAE,mCAAmC,QAAQ,kBAAkB,CAAC,CAAC;oBAC3E,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;oBAC/B,OAAO;gBACT,CAAC;gBACD,KAAK,CAAC,IAAI,EAAE,oCAAoC,QAAQ,EAAE,CAAC,CAAC;YAC9D,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,UAAU,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW,EAAE,CAAC,CAAC;YACjF,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,OAAO,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC5F,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC1B,KAAK,CAAC,IAAI,EAAE,qCAAqC,CAAC,CAAC;YACrD,CAAC;YAED,KAAK,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;YAChC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAE/B,+CAA+C;YAC/C,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAC3B,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;YAC7B,CAAC;YAED,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;gBACjB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YACjC,CAAC,CAAC,CAAC;YAEH,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;gBACxB,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAA4B,CAAC;oBACnE,KAAK,CAAC,IAAI,EAAE,aAAa,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;oBACrC,+DAA+D;oBAC/D,IAAI,GAAG,CAAC,IAAI,KAAK,iBAAiB,IAAI,GAAG,CAAC,UAAU,KAAK,eAAe,EAAE,CAAC;wBACzE,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,CAAC;wBACxC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;wBAC3B,IAAI,CAAC,GAAG,EAAE,CAAC;4BACT,KAAK,CAAC,IAAI,EAAE,yDAAyD,CAAC,CAAC;4BACvE,IAAI,CAAC,sBAAsB,EAAE,CAAC,IAAI,CAAC,CAAC;wBACtC,CAAC;oBACH,CAAC;oBACD,iEAAiE;oBACjE,IAAI,GAAG,CAAC,IAAI,KAAK,iBAAiB,IAAI,GAAG,CAAC,UAAU,KAAK,KAAK,EAAE,CAAC;wBAC/D,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAEvD,CAAC;wBACT,MAAM,IAAI,GAAG,OAAO,GAAG,EAAE,IAAI,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC;wBAC/E,MAAM,EAAE,GAAG,OAAO,GAAG,EAAE,EAAE,KAAK,QAAQ,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;wBACjE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;wBACtC,KAAK,CAAC,IAAI,EAAE,6BAA6B,EAAE,EAAE,CAAC,CAAC;oBACjD,CAAC;oBACD,4EAA4E;oBAC5E,IAAI,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC;wBAChE,OAAO,CAAC,UAAU;oBACpB,CAAC;oBACD,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;wBACzB,IAAI,CAAC,eAAe,CAAC,GAA+B,CAAC,CAAC;oBACxD,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,KAAK,CAAC,IAAI,EAAE,4BAA4B,GAAG,EAAE,CAAC,CAAC;gBACjD,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBAClB,KAAK,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAC;gBACnC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC5B,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC7B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC3B,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;oBACnE,KAAK,CAAC,IAAI,EAAE,kDAAkD,CAAC,CAAC;oBAChE,IAAI,CAAC,sBAAsB,EAAE,CAAC,KAAK,CAAC,CAAC;gBACvC,CAAC;gBACD,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;oBAC9B,IAAI,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACrB,KAAK,CAAC,IAAI,EAAE,oBAAoB,GAAG,EAAE,CAAC,CAAC;YACzC,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,cAAc,GAAwC,EAAE,CAAC;IAEjE,sFAAsF;IACtF,WAAW,CAAC,IAAkC;QAC5C,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,mBAAmB,CAAC,WAAmF;QACrG,IAAI,CAAC,gBAAgB,GAAG,WAAW,CAAC;IACtC,CAAC;IAED,aAAa,CAAC,EAAa;QACzB,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,eAAe,CAAC,EAAa;QAC3B,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAEO,UAAU,CAAC,KAAkB,EAAE,MAAiB,EAAE,aAAsB;QAC9E,IAAI,CAAC,IAAI,CAAC,gBAAgB;YAAE,OAAO,aAAa,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC1E,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACzD,IAAI,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC;QAC9B,OAAO,WAAW,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACxG,CAAC;IAED,SAAS,CAAC,KAAkB;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;QAC1C,KAAK,CAAC,IAAI,EAAE,aAAa,KAAK,CAAC,IAAI,QAAQ,WAAW,UAAU,CAAC,CAAC;QAClE,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;YACtC,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;gBACzC,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;gBAC9D,IAAI,CAAC,aAAa;oBAAE,SAAS;gBAC7B,IAAI,CAAC;oBAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,0BAA0B,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;QACD,iDAAiD;QACjD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACvC,IAAI,CAAC;gBAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IAED,SAAS,CAAC,QAAsC;QAC9C,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;IAClC,CAAC;IAED;;6DAEyD;IACzD,sBAAsB,CAAC,QAAoC;QACzD,IAAI,CAAC,sBAAsB,GAAG,QAAQ,CAAC;QACvC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,uFAAuF;IACvF,eAAe,CAAC,GAAkB;QAChC,IAAI,CAAC,eAAe,EAAE,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,kGAAkG;IAClG,YAAY,CAAC,QAAsE;QACjF,IAAI,CAAC,kBAAkB,GAAG,QAAQ,CAAC;IACrC,CAAC;IAED,iDAAiD;IACjD,eAAe,CAAC,KAAkB,EAAE,MAAiB;QACnD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACtC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;YACtC,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;gBAC9D,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;gBAC9D,IAAI,CAAC,aAAa;oBAAE,SAAS;gBAC7B,IAAI,CAAC;oBAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,0BAA0B,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;IACH,CAAC;IAED,eAAe,CAAC,QAAiC;QAC/C,IAAI,CAAC,iBAAiB,GAAG,QAAQ,CAAC;IACpC,CAAC;IAED,kBAAkB,CAAC,QAAiC;QAClD,IAAI,CAAC,oBAAoB,GAAG,QAAQ,CAAC;IACvC,CAAC;IAED,MAAM,CAAC,EAAa,EAAE,KAAkB;QACtC,IAAI,EAAE,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;YACrC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC3C,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,IAAI,CAAC;gBAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,0BAA0B,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;IAC/B,CAAC;IAED,oBAAoB;QAClB,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;IACjC,CAAC;IAED;;oDAEgD;IAChD,aAAa;QACX,MAAM,IAAI,GAAG,IAAI,GAAG,EAAwC,CAAC;QAC7D,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE;YAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACrE,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK;QACH,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9B,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QACzB,4DAA4D;QAC5D,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3C,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,112 @@
1
+ import Foundation
2
+ #if canImport(FoundationModels)
3
+ import FoundationModels
4
+ #endif
5
+
6
+ struct HelperRequest: Decodable {
7
+ let id: Int
8
+ let type: String?
9
+ let prompt: String?
10
+ let instructions: String?
11
+ let temperature: Double?
12
+ }
13
+
14
+ @main
15
+ struct AgentDeckFMHelper {
16
+ static func main() async {
17
+ do {
18
+ for try await line in FileHandle.standardInput.bytes.lines {
19
+ await handle(line)
20
+ }
21
+ } catch {
22
+ write(["id": -1, "error": "stdin_error", "reason": String(describing: error)])
23
+ }
24
+ }
25
+
26
+ private static func handle(_ line: String) async {
27
+ guard let data = line.data(using: .utf8),
28
+ let request = try? JSONDecoder().decode(HelperRequest.self, from: data) else {
29
+ write(["id": -1, "error": "bad_request", "reason": "invalid JSON line"])
30
+ return
31
+ }
32
+
33
+ if request.type == "health" {
34
+ write(healthResponse(id: request.id))
35
+ return
36
+ }
37
+
38
+ guard let prompt = request.prompt, !prompt.isEmpty else {
39
+ write(["id": request.id, "error": "bad_request", "reason": "missing prompt"])
40
+ return
41
+ }
42
+
43
+ #if canImport(FoundationModels)
44
+ if #available(macOS 26.0, *) {
45
+ guard case .available = SystemLanguageModel.default.availability else {
46
+ write([
47
+ "id": request.id,
48
+ "error": "unavailable",
49
+ "reason": unavailableReason(),
50
+ ])
51
+ return
52
+ }
53
+
54
+ do {
55
+ let session = LanguageModelSession(
56
+ instructions: request.instructions ?? "You are an exacting code evaluator. Reply with strict JSON only."
57
+ )
58
+ let options = GenerationOptions(temperature: request.temperature ?? 0)
59
+ let response = try await session.respond(to: prompt, options: options)
60
+ write(["id": request.id, "text": response.content])
61
+ } catch {
62
+ write(["id": request.id, "error": "session_error", "reason": String(describing: error)])
63
+ }
64
+ } else {
65
+ write(["id": request.id, "error": "unavailable", "reason": "macOS 26 or later required"])
66
+ }
67
+ #else
68
+ write(["id": request.id, "error": "unavailable", "reason": "FoundationModels framework not present"])
69
+ #endif
70
+ }
71
+
72
+ private static func healthResponse(id: Int) -> [String: Any] {
73
+ #if canImport(FoundationModels)
74
+ if #available(macOS 26.0, *) {
75
+ if case .available = SystemLanguageModel.default.availability {
76
+ return ["id": id, "status": "ready"]
77
+ }
78
+ return ["id": id, "status": "unavailable", "reason": unavailableReason()]
79
+ }
80
+ return ["id": id, "status": "unavailable", "reason": "macOS 26 or later required"]
81
+ #else
82
+ return ["id": id, "status": "unavailable", "reason": "FoundationModels framework not present"]
83
+ #endif
84
+ }
85
+
86
+ private static func unavailableReason() -> String {
87
+ #if canImport(FoundationModels)
88
+ if #available(macOS 26.0, *) {
89
+ switch SystemLanguageModel.default.availability {
90
+ case .available:
91
+ return "available"
92
+ case .unavailable(let reason):
93
+ return "unavailable: \(reason)"
94
+ @unknown default:
95
+ return "unavailable: unknown state"
96
+ }
97
+ }
98
+ return "macOS 26 or later required"
99
+ #else
100
+ return "FoundationModels framework not present"
101
+ #endif
102
+ }
103
+
104
+ private static func write(_ object: [String: Any]) {
105
+ guard JSONSerialization.isValidJSONObject(object),
106
+ let data = try? JSONSerialization.data(withJSONObject: object),
107
+ let text = String(data: data, encoding: .utf8) else {
108
+ return
109
+ }
110
+ FileHandle.standardOutput.write(Data((text + "\n").utf8))
111
+ }
112
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentdeck/bridge",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -10,7 +10,11 @@
10
10
  },
11
11
  "files": [
12
12
  "dist",
13
- "!dist/__tests__"
13
+ "!dist/__tests__",
14
+ "assets/fonts",
15
+ "assets/fm-helper",
16
+ "fm-helper",
17
+ "scripts/build-fm-helper.mjs"
14
18
  ],
15
19
  "main": "./dist/index.js",
16
20
  "bin": {
@@ -23,7 +27,8 @@
23
27
  "qrcode": "^1.5.4",
24
28
  "strip-ansi": "^7.2.0",
25
29
  "ws": "^8.20.0",
26
- "@agentdeck/shared": "0.2.0"
30
+ "@agentdeck/hooks": "0.2.3",
31
+ "@agentdeck/shared": "0.2.3"
27
32
  },
28
33
  "optionalDependencies": {
29
34
  "@picovoice/porcupine-node": "^4.0.2",
@@ -43,6 +48,7 @@
43
48
  },
44
49
  "scripts": {
45
50
  "build": "tsc",
51
+ "build:fm-helper": "node scripts/build-fm-helper.mjs",
46
52
  "dev": "tsc --watch",
47
53
  "clean": "rm -rf dist",
48
54
  "typecheck": "tsc --noEmit",
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ import { execFileSync } from 'node:child_process';
3
+ import { chmodSync, mkdirSync } from 'node:fs';
4
+ import { dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const root = dirname(dirname(fileURLToPath(import.meta.url)));
8
+ const source = join(root, 'fm-helper', 'AgentDeckFMHelper.swift');
9
+ const out = join(root, 'assets', 'fm-helper', 'agentdeck-fm-helper');
10
+ const arch = process.arch === 'arm64' ? 'arm64' : 'x86_64';
11
+ const target = `${arch}-apple-macos26.0`;
12
+
13
+ if (process.platform !== 'darwin') {
14
+ console.log('[fm-helper] skipped: darwin only');
15
+ process.exit(0);
16
+ }
17
+
18
+ mkdirSync(dirname(out), { recursive: true });
19
+ const swiftc = execFileSync('/usr/bin/xcrun', ['--find', 'swiftc'], { encoding: 'utf8' }).trim();
20
+ // swiftc is run directly (not via `xcrun swiftc`), so SDKROOT isn't set and the
21
+ // stdlib can't be found ("unable to load standard library for target ..."). Pass
22
+ // the SDK path explicitly so the build is reproducible from the npm lifecycle.
23
+ const sdk = execFileSync('/usr/bin/xcrun', ['--sdk', 'macosx', '--show-sdk-path'], { encoding: 'utf8' }).trim();
24
+ execFileSync(swiftc, ['-parse-as-library', '-sdk', sdk, '-target', target, source, '-o', out], { stdio: 'inherit' });
25
+ chmodSync(out, 0o755);
26
+ console.log(`[fm-helper] built ${out}`);
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 SerendipityBound
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.