aimux-cli 0.1.16 → 0.1.19

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 (357) hide show
  1. package/README.md +184 -67
  2. package/bin/aimux-dev +10 -0
  3. package/dist/agent-events.js +0 -1
  4. package/dist/agent-output-parser.js +0 -1
  5. package/dist/agent-prompt-delivery.js +0 -1
  6. package/dist/agent-tracker.js +0 -1
  7. package/dist/agent-watcher.js +0 -1
  8. package/dist/alert-display.d.ts +21 -0
  9. package/dist/alert-display.js +85 -0
  10. package/dist/atomic-write.js +0 -1
  11. package/dist/attachment-store.d.ts +0 -7
  12. package/dist/attachment-store.js +2 -87
  13. package/dist/builtin-metadata-watchers.js +4 -5
  14. package/dist/claude-hooks.d.ts +1 -0
  15. package/dist/claude-hooks.js +25 -1
  16. package/dist/config.d.ts +19 -13
  17. package/dist/config.js +28 -15
  18. package/dist/connection-targets.d.ts +8 -0
  19. package/dist/connection-targets.js +27 -0
  20. package/dist/context/compactor.js +0 -1
  21. package/dist/context/context-bridge.js +0 -1
  22. package/dist/context/context-file.js +0 -1
  23. package/dist/context/history.js +0 -1
  24. package/dist/credentials.d.ts +12 -0
  25. package/dist/credentials.js +48 -0
  26. package/dist/daemon.d.ts +23 -0
  27. package/dist/daemon.js +391 -67
  28. package/dist/dashboard/command-spec.js +0 -1
  29. package/dist/dashboard/feedback.js +0 -1
  30. package/dist/dashboard/index.d.ts +13 -10
  31. package/dist/dashboard/index.js +3 -27
  32. package/dist/dashboard/operation-failures.js +0 -1
  33. package/dist/dashboard/order.d.ts +22 -0
  34. package/dist/dashboard/order.js +54 -0
  35. package/dist/dashboard/pending-actions.d.ts +39 -10
  36. package/dist/dashboard/pending-actions.js +166 -37
  37. package/dist/dashboard/quick-jump.d.ts +2 -1
  38. package/dist/dashboard/quick-jump.js +7 -5
  39. package/dist/dashboard/runtime-evidence.js +0 -1
  40. package/dist/dashboard/session-actions.d.ts +4 -4
  41. package/dist/dashboard/session-actions.js +1 -2
  42. package/dist/dashboard/session-registry.d.ts +4 -3
  43. package/dist/dashboard/session-registry.js +16 -51
  44. package/dist/dashboard/sort.js +0 -1
  45. package/dist/dashboard/state.d.ts +1 -1
  46. package/dist/dashboard/state.js +0 -1
  47. package/dist/dashboard/targets.js +0 -1
  48. package/dist/dashboard/ui-state-store.d.ts +16 -1
  49. package/dist/dashboard/ui-state-store.js +73 -3
  50. package/dist/debug-state.d.ts +97 -0
  51. package/dist/debug-state.js +540 -0
  52. package/dist/debug.d.ts +38 -0
  53. package/dist/debug.js +219 -16
  54. package/dist/default-plugins/gh-pr-context.d.ts +2 -1
  55. package/dist/default-plugins/gh-pr-context.js +17 -12
  56. package/dist/default-plugins/transcript-length.js +15 -3
  57. package/dist/fast-control.js +37 -20
  58. package/dist/hotkeys.js +0 -1
  59. package/dist/http-client.js +31 -3
  60. package/dist/key-parser.js +0 -1
  61. package/dist/last-used.js +0 -1
  62. package/dist/local-ui-server.d.ts +22 -0
  63. package/dist/local-ui-server.js +185 -0
  64. package/dist/login-flow.d.ts +7 -0
  65. package/dist/login-flow.js +119 -0
  66. package/dist/main.js +821 -152
  67. package/dist/managed-launch-env.js +14 -1
  68. package/dist/metadata-server.d.ts +36 -36
  69. package/dist/metadata-server.js +638 -138
  70. package/dist/metadata-store.d.ts +4 -1
  71. package/dist/metadata-store.js +30 -3
  72. package/dist/multiplexer/agent-io-methods.d.ts +2 -10
  73. package/dist/multiplexer/agent-io-methods.js +12 -44
  74. package/dist/multiplexer/archives.js +8 -10
  75. package/dist/multiplexer/dashboard-actions-methods.js +0 -1
  76. package/dist/multiplexer/dashboard-control.js +45 -14
  77. package/dist/multiplexer/dashboard-interaction.d.ts +8 -2
  78. package/dist/multiplexer/dashboard-interaction.js +187 -29
  79. package/dist/multiplexer/dashboard-model.d.ts +10 -3
  80. package/dist/multiplexer/dashboard-model.js +417 -36
  81. package/dist/multiplexer/dashboard-ops.d.ts +9 -7
  82. package/dist/multiplexer/dashboard-ops.js +178 -69
  83. package/dist/multiplexer/dashboard-state-methods.d.ts +2 -1
  84. package/dist/multiplexer/dashboard-state-methods.js +3 -3
  85. package/dist/multiplexer/dashboard-tail-methods.d.ts +22 -10
  86. package/dist/multiplexer/dashboard-tail-methods.js +164 -48
  87. package/dist/multiplexer/dashboard-view-methods.d.ts +1 -1
  88. package/dist/multiplexer/dashboard-view-methods.js +23 -9
  89. package/dist/multiplexer/graveyard-view-model.d.ts +9 -1
  90. package/dist/multiplexer/graveyard-view-model.js +39 -1
  91. package/dist/multiplexer/index.d.ts +15 -12
  92. package/dist/multiplexer/index.js +64 -44
  93. package/dist/multiplexer/navigation.js +0 -1
  94. package/dist/multiplexer/notifications.js +107 -25
  95. package/dist/multiplexer/persistence-methods.d.ts +31 -4
  96. package/dist/multiplexer/persistence-methods.js +304 -309
  97. package/dist/multiplexer/runtime-lifecycle-methods.d.ts +8 -10
  98. package/dist/multiplexer/runtime-lifecycle-methods.js +104 -87
  99. package/dist/multiplexer/runtime-state.d.ts +8 -10
  100. package/dist/multiplexer/runtime-state.js +82 -146
  101. package/dist/multiplexer/runtime-sync.d.ts +2 -10
  102. package/dist/multiplexer/runtime-sync.js +3 -19
  103. package/dist/multiplexer/service-state-snapshot.d.ts +2 -4
  104. package/dist/multiplexer/service-state-snapshot.js +4 -52
  105. package/dist/multiplexer/services.d.ts +1 -0
  106. package/dist/multiplexer/services.js +55 -6
  107. package/dist/multiplexer/session-capture.d.ts +1 -0
  108. package/dist/multiplexer/session-capture.js +23 -0
  109. package/dist/multiplexer/session-launch.d.ts +4 -1
  110. package/dist/multiplexer/session-launch.js +152 -64
  111. package/dist/multiplexer/session-runtime-core.d.ts +8 -20
  112. package/dist/multiplexer/session-runtime-core.js +40 -136
  113. package/dist/multiplexer/subscreens.js +10 -4
  114. package/dist/multiplexer/tool-picker.js +0 -1
  115. package/dist/multiplexer/worktree-graveyard.d.ts +0 -1
  116. package/dist/multiplexer/worktree-graveyard.js +15 -17
  117. package/dist/multiplexer/worktrees.js +96 -41
  118. package/dist/notification-context.js +8 -5
  119. package/dist/notifications.js +163 -102
  120. package/dist/notify.d.ts +4 -0
  121. package/dist/notify.js +14 -1
  122. package/dist/orchestration-actions.js +0 -1
  123. package/dist/orchestration-routing.js +0 -1
  124. package/dist/orchestration.js +0 -1
  125. package/dist/osc-notifications.js +0 -1
  126. package/dist/paths.d.ts +32 -7
  127. package/dist/paths.js +82 -59
  128. package/dist/pending-actions.d.ts +5 -0
  129. package/dist/pending-actions.js +13 -0
  130. package/dist/plugin-runtime.js +9 -3
  131. package/dist/project-events.d.ts +1 -10
  132. package/dist/project-events.js +0 -11
  133. package/dist/project-scanner.d.ts +2 -3
  134. package/dist/project-scanner.js +58 -130
  135. package/dist/project-service-manifest.d.ts +1 -3
  136. package/dist/project-service-manifest.js +1 -4
  137. package/dist/recency.js +0 -1
  138. package/dist/recorder.js +0 -1
  139. package/dist/relay-client.d.ts +30 -0
  140. package/dist/relay-client.js +190 -0
  141. package/dist/remote-access.d.ts +16 -0
  142. package/dist/remote-access.js +90 -0
  143. package/dist/runtime-core/exchange-derived.d.ts +2 -0
  144. package/dist/runtime-core/exchange-derived.js +153 -0
  145. package/dist/runtime-core/exchange-import.d.ts +24 -0
  146. package/dist/runtime-core/exchange-import.js +317 -0
  147. package/dist/runtime-core/exchange-store.d.ts +157 -0
  148. package/dist/runtime-core/exchange-store.js +452 -0
  149. package/dist/runtime-core/topology-services.d.ts +38 -0
  150. package/dist/runtime-core/topology-services.js +170 -0
  151. package/dist/runtime-core/topology-sessions.d.ts +52 -0
  152. package/dist/runtime-core/topology-sessions.js +238 -0
  153. package/dist/runtime-core/topology-store.d.ts +171 -0
  154. package/dist/runtime-core/topology-store.js +419 -0
  155. package/dist/runtime-core/topology-worktrees.d.ts +60 -0
  156. package/dist/runtime-core/topology-worktrees.js +199 -0
  157. package/dist/runtime-migration.d.ts +69 -0
  158. package/dist/runtime-migration.js +398 -0
  159. package/dist/session-bootstrap.d.ts +8 -6
  160. package/dist/session-bootstrap.js +51 -159
  161. package/dist/session-runtime.d.ts +2 -0
  162. package/dist/session-runtime.js +1 -1
  163. package/dist/session-semantics.d.ts +12 -4
  164. package/dist/session-semantics.js +14 -1
  165. package/dist/shell-args.js +0 -1
  166. package/dist/shell-hooks.js +32 -11
  167. package/dist/shell-state.d.ts +2 -0
  168. package/dist/shell-state.js +26 -2
  169. package/dist/status-detector.js +0 -1
  170. package/dist/statusline-model.d.ts +10 -2
  171. package/dist/statusline-model.js +106 -31
  172. package/dist/task-workflow.d.ts +6 -9
  173. package/dist/task-workflow.js +37 -85
  174. package/dist/tasks.d.ts +6 -33
  175. package/dist/tasks.js +46 -89
  176. package/dist/team.d.ts +29 -0
  177. package/dist/team.js +40 -1
  178. package/dist/terminal-host.js +0 -1
  179. package/dist/threads.d.ts +6 -35
  180. package/dist/threads.js +89 -99
  181. package/dist/tmux/doctor.js +0 -1
  182. package/dist/tmux/inbox-popup.js +37 -16
  183. package/dist/tmux/runtime-manager.d.ts +3 -0
  184. package/dist/tmux/runtime-manager.js +21 -5
  185. package/dist/tmux/session-transport.js +0 -1
  186. package/dist/tmux/statusline-cache.js +0 -1
  187. package/dist/tmux/statusline.js +49 -10
  188. package/dist/tmux/switcher.js +0 -1
  189. package/dist/tmux/window-open.js +1 -3
  190. package/dist/tool-output-watchers.d.ts +0 -18
  191. package/dist/tool-output-watchers.js +0 -323
  192. package/dist/tui/render/box.js +0 -1
  193. package/dist/tui/render/text.js +0 -1
  194. package/dist/tui/screens/dashboard-renderers.js +37 -26
  195. package/dist/tui/screens/overlay-renderers.d.ts +2 -0
  196. package/dist/tui/screens/overlay-renderers.js +37 -2
  197. package/dist/tui/screens/subscreen-renderers.js +7 -1
  198. package/dist/workflow.js +0 -1
  199. package/dist/worktree.js +17 -1
  200. package/dist-ui/_expo/static/css/web-30453ede1678c16acb08b97e83e8646d.css +1 -0
  201. package/dist-ui/_expo/static/js/web/entry-477c745b2adc79367a4380ecf07d9ff6.js +14620 -0
  202. package/dist-ui/assets/assets/images/icon.a5413dcd2e811c9f2317d01a28118d8a.png +0 -0
  203. package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/back-icon-mask.0a328cd9c1afd0afe8e3b1ec5165b1b4.png +0 -0
  204. package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/back-icon.35ba0eaec5a4f5ed12ca16fabeae451d.png +0 -0
  205. package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55.png +0 -0
  206. package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@2x.png +0 -0
  207. package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@3x.png +0 -0
  208. package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@4x.png +0 -0
  209. package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7.png +0 -0
  210. package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@2x.png +0 -0
  211. package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@3x.png +0 -0
  212. package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@4x.png +0 -0
  213. package/dist-ui/assets/node_modules/@react-navigation/elements/lib/module/assets/search-icon.286d67d3f74808a60a78d3ebf1a5fb57.png +0 -0
  214. package/dist-ui/assets/node_modules/expo-router/assets/arrow_down.017bc6ba3fc25503e5eb5e53826d48a8.png +0 -0
  215. package/dist-ui/assets/node_modules/expo-router/assets/error.d1ea1496f9057eb392d5bbf3732a61b7.png +0 -0
  216. package/dist-ui/assets/node_modules/expo-router/assets/file.19eeb73b9593a38f8e9f418337fc7d10.png +0 -0
  217. package/dist-ui/assets/node_modules/expo-router/assets/forward.d8b800c443b8972542883e0b9de2bdc6.png +0 -0
  218. package/dist-ui/assets/node_modules/expo-router/assets/pkg.ab19f4cbc543357183a20571f68380a3.png +0 -0
  219. package/dist-ui/assets/node_modules/expo-router/assets/sitemap.412dd9275b6b48ad28f5e3d81bb1f626.png +0 -0
  220. package/dist-ui/assets/node_modules/expo-router/assets/unmatched.20e71bdf79e3a97bf55fd9e164041578.png +0 -0
  221. package/dist-ui/favicon.ico +0 -0
  222. package/dist-ui/index.html +38 -0
  223. package/dist-ui/metadata.json +1 -0
  224. package/package.json +29 -12
  225. package/dist/agent-events.js.map +0 -1
  226. package/dist/agent-message-parts.d.ts +0 -17
  227. package/dist/agent-message-parts.js +0 -31
  228. package/dist/agent-message-parts.js.map +0 -1
  229. package/dist/agent-output-parser.js.map +0 -1
  230. package/dist/agent-prompt-delivery.js.map +0 -1
  231. package/dist/agent-tracker.js.map +0 -1
  232. package/dist/agent-watcher.js.map +0 -1
  233. package/dist/atomic-write.js.map +0 -1
  234. package/dist/attachment-store.js.map +0 -1
  235. package/dist/builtin-metadata-watchers.js.map +0 -1
  236. package/dist/claude-hooks.js.map +0 -1
  237. package/dist/config.js.map +0 -1
  238. package/dist/context/compactor.js.map +0 -1
  239. package/dist/context/context-bridge.js.map +0 -1
  240. package/dist/context/context-file.js.map +0 -1
  241. package/dist/context/history.js.map +0 -1
  242. package/dist/daemon.js.map +0 -1
  243. package/dist/dashboard/command-spec.js.map +0 -1
  244. package/dist/dashboard/feedback.js.map +0 -1
  245. package/dist/dashboard/index.js.map +0 -1
  246. package/dist/dashboard/operation-failures.js.map +0 -1
  247. package/dist/dashboard/pending-actions.js.map +0 -1
  248. package/dist/dashboard/quick-jump.js.map +0 -1
  249. package/dist/dashboard/runtime-evidence.js.map +0 -1
  250. package/dist/dashboard/session-actions.js.map +0 -1
  251. package/dist/dashboard/session-registry.js.map +0 -1
  252. package/dist/dashboard/sort.js.map +0 -1
  253. package/dist/dashboard/state.js.map +0 -1
  254. package/dist/dashboard/targets.js.map +0 -1
  255. package/dist/dashboard/ui-state-store.js.map +0 -1
  256. package/dist/debug.js.map +0 -1
  257. package/dist/default-plugins/gh-pr-context.js.map +0 -1
  258. package/dist/default-plugins/transcript-length.js.map +0 -1
  259. package/dist/fast-control.js.map +0 -1
  260. package/dist/hotkeys.js.map +0 -1
  261. package/dist/http-client.js.map +0 -1
  262. package/dist/instance-directory.d.ts +0 -32
  263. package/dist/instance-directory.js +0 -82
  264. package/dist/instance-directory.js.map +0 -1
  265. package/dist/instance-registry.d.ts +0 -39
  266. package/dist/instance-registry.js +0 -208
  267. package/dist/instance-registry.js.map +0 -1
  268. package/dist/key-parser.js.map +0 -1
  269. package/dist/last-used.js.map +0 -1
  270. package/dist/main.js.map +0 -1
  271. package/dist/managed-launch-env.js.map +0 -1
  272. package/dist/metadata-server.js.map +0 -1
  273. package/dist/metadata-store.js.map +0 -1
  274. package/dist/multiplexer/agent-io-methods.js.map +0 -1
  275. package/dist/multiplexer/archives.js.map +0 -1
  276. package/dist/multiplexer/dashboard-actions-methods.js.map +0 -1
  277. package/dist/multiplexer/dashboard-control.js.map +0 -1
  278. package/dist/multiplexer/dashboard-interaction.js.map +0 -1
  279. package/dist/multiplexer/dashboard-model.js.map +0 -1
  280. package/dist/multiplexer/dashboard-ops.js.map +0 -1
  281. package/dist/multiplexer/dashboard-state-methods.js.map +0 -1
  282. package/dist/multiplexer/dashboard-tail-methods.js.map +0 -1
  283. package/dist/multiplexer/dashboard-view-methods.js.map +0 -1
  284. package/dist/multiplexer/graveyard-view-model.js.map +0 -1
  285. package/dist/multiplexer/index.js.map +0 -1
  286. package/dist/multiplexer/navigation.js.map +0 -1
  287. package/dist/multiplexer/notifications.js.map +0 -1
  288. package/dist/multiplexer/persistence-methods.js.map +0 -1
  289. package/dist/multiplexer/runtime-lifecycle-methods.js.map +0 -1
  290. package/dist/multiplexer/runtime-state.js.map +0 -1
  291. package/dist/multiplexer/runtime-sync.js.map +0 -1
  292. package/dist/multiplexer/service-state-snapshot.js.map +0 -1
  293. package/dist/multiplexer/services.js.map +0 -1
  294. package/dist/multiplexer/session-actions.d.ts +0 -40
  295. package/dist/multiplexer/session-actions.js +0 -110
  296. package/dist/multiplexer/session-actions.js.map +0 -1
  297. package/dist/multiplexer/session-launch.js.map +0 -1
  298. package/dist/multiplexer/session-runtime-core.js.map +0 -1
  299. package/dist/multiplexer/subscreens.js.map +0 -1
  300. package/dist/multiplexer/tool-picker.js.map +0 -1
  301. package/dist/multiplexer/worktree-graveyard.js.map +0 -1
  302. package/dist/multiplexer/worktrees.js.map +0 -1
  303. package/dist/notification-context.js.map +0 -1
  304. package/dist/notifications.js.map +0 -1
  305. package/dist/notify.js.map +0 -1
  306. package/dist/orchestration-actions.js.map +0 -1
  307. package/dist/orchestration-dispatcher.d.ts +0 -25
  308. package/dist/orchestration-dispatcher.js +0 -59
  309. package/dist/orchestration-dispatcher.js.map +0 -1
  310. package/dist/orchestration-routing.js.map +0 -1
  311. package/dist/orchestration.js.map +0 -1
  312. package/dist/osc-notifications.js.map +0 -1
  313. package/dist/paths.js.map +0 -1
  314. package/dist/plugin-runtime.js.map +0 -1
  315. package/dist/project-events.js.map +0 -1
  316. package/dist/project-scanner.js.map +0 -1
  317. package/dist/project-service-manifest.js.map +0 -1
  318. package/dist/recency.js.map +0 -1
  319. package/dist/recorder.js.map +0 -1
  320. package/dist/session-bootstrap.js.map +0 -1
  321. package/dist/session-input-operations.d.ts +0 -19
  322. package/dist/session-input-operations.js +0 -46
  323. package/dist/session-input-operations.js.map +0 -1
  324. package/dist/session-message-history.d.ts +0 -27
  325. package/dist/session-message-history.js +0 -105
  326. package/dist/session-message-history.js.map +0 -1
  327. package/dist/session-runtime.js.map +0 -1
  328. package/dist/session-semantics.js.map +0 -1
  329. package/dist/shell-args.js.map +0 -1
  330. package/dist/shell-hooks.js.map +0 -1
  331. package/dist/shell-state.js.map +0 -1
  332. package/dist/status-detector.js.map +0 -1
  333. package/dist/statusline-model.js.map +0 -1
  334. package/dist/task-dispatcher.d.ts +0 -64
  335. package/dist/task-dispatcher.js +0 -213
  336. package/dist/task-dispatcher.js.map +0 -1
  337. package/dist/task-workflow.js.map +0 -1
  338. package/dist/tasks.js.map +0 -1
  339. package/dist/team.js.map +0 -1
  340. package/dist/terminal-host.js.map +0 -1
  341. package/dist/threads.js.map +0 -1
  342. package/dist/tmux/doctor.js.map +0 -1
  343. package/dist/tmux/inbox-popup.js.map +0 -1
  344. package/dist/tmux/runtime-manager.js.map +0 -1
  345. package/dist/tmux/session-transport.js.map +0 -1
  346. package/dist/tmux/statusline-cache.js.map +0 -1
  347. package/dist/tmux/statusline.js.map +0 -1
  348. package/dist/tmux/switcher.js.map +0 -1
  349. package/dist/tmux/window-open.js.map +0 -1
  350. package/dist/tool-output-watchers.js.map +0 -1
  351. package/dist/tui/render/box.js.map +0 -1
  352. package/dist/tui/render/text.js.map +0 -1
  353. package/dist/tui/screens/dashboard-renderers.js.map +0 -1
  354. package/dist/tui/screens/overlay-renderers.js.map +0 -1
  355. package/dist/tui/screens/subscreen-renderers.js.map +0 -1
  356. package/dist/workflow.js.map +0 -1
  357. package/dist/worktree.js.map +0 -1
@@ -0,0 +1,13 @@
1
+ const BLOCKING_PENDING_DASHBOARD_ACTIONS = new Set([
2
+ "creating",
3
+ "forking",
4
+ "migrating",
5
+ "starting",
6
+ "stopping",
7
+ "graveyarding",
8
+ "renaming",
9
+ "removing",
10
+ ]);
11
+ export function isBlockingPendingDashboardActionKind(kind) {
12
+ return Boolean(kind && BLOCKING_PENDING_DASHBOARD_ACTIONS.has(kind));
13
+ }
@@ -6,6 +6,8 @@ import { updateSessionMetadata, clearSessionLogs, loadMetadataState, } from "./m
6
6
  import { debug } from "./debug.js";
7
7
  import { createBuiltinMetadataWatchers } from "./builtin-metadata-watchers.js";
8
8
  import { AgentTracker } from "./agent-tracker.js";
9
+ import { listTopologySessionStates } from "./runtime-core/topology-sessions.js";
10
+ import { contextualizeAlertInput, metadataDisplayContext } from "./alert-display.js";
9
11
  function listPluginFiles(dir) {
10
12
  if (!existsSync(dir))
11
13
  return [];
@@ -122,7 +124,12 @@ export class PluginRuntime {
122
124
  const alert = deriveAlertFromAgentEvent(sessionId, event);
123
125
  if (!alert)
124
126
  return;
125
- this.eventBus.publishAlert(alert);
127
+ let context;
128
+ try {
129
+ context = metadataDisplayContext(loadMetadataState().sessions[sessionId]);
130
+ }
131
+ catch { }
132
+ this.eventBus.publishAlert(contextualizeAlertInput(alert, context));
126
133
  }
127
134
  async start() {
128
135
  const tracker = new AgentTracker();
@@ -226,7 +233,7 @@ export class PluginRuntime {
226
233
  },
227
234
  },
228
235
  sessions: {
229
- list: () => Object.keys(loadMetadataState().sessions).map((id) => ({ id })),
236
+ list: () => listTopologySessionStates().map((session) => ({ id: session.id })),
230
237
  },
231
238
  };
232
239
  for (const watcher of createBuiltinMetadataWatchers(api)) {
@@ -316,4 +323,3 @@ export function deriveAlertFromAgentEvent(sessionId, event) {
316
323
  cooldownMs: kind === "task_done" ? 10_000 : 15_000,
317
324
  };
318
325
  }
319
- //# sourceMappingURL=plugin-runtime.js.map
@@ -13,22 +13,13 @@ export interface AlertEvent {
13
13
  dedupeKey?: string;
14
14
  forceNotify?: boolean;
15
15
  }
16
- export interface HistoryUpdateEvent {
17
- type: "history_update";
18
- projectId: string;
19
- sessionId: string;
20
- ts: string;
21
- messages: unknown[];
22
- lastN?: number;
23
- }
24
- export type ProjectStreamEvent = AlertEvent | HistoryUpdateEvent;
16
+ export type ProjectStreamEvent = AlertEvent;
25
17
  type ProjectEventListener = (event: ProjectStreamEvent) => void;
26
18
  export declare class ProjectEventBus {
27
19
  private listeners;
28
20
  private alertCooldowns;
29
21
  subscribe(listener: ProjectEventListener): () => void;
30
22
  publish(event: ProjectStreamEvent): void;
31
- publishHistoryUpdate(input: Omit<HistoryUpdateEvent, "type" | "projectId" | "ts">): void;
32
23
  publishAlert(alert: Omit<AlertEvent, "type" | "projectId" | "ts"> & {
33
24
  dedupeKey?: string;
34
25
  cooldownMs?: number;
@@ -15,16 +15,6 @@ export class ProjectEventBus {
15
15
  listener(event);
16
16
  }
17
17
  }
18
- publishHistoryUpdate(input) {
19
- this.publish({
20
- type: "history_update",
21
- projectId: getProjectId(),
22
- ts: new Date().toISOString(),
23
- sessionId: input.sessionId,
24
- messages: input.messages,
25
- lastN: input.lastN,
26
- });
27
- }
28
18
  publishAlert(alert) {
29
19
  const cooldownMs = alert.cooldownMs ?? 15_000;
30
20
  const dedupeKey = alert.dedupeKey?.trim() || undefined;
@@ -63,4 +53,3 @@ export class ProjectEventBus {
63
53
  return true;
64
54
  }
65
55
  }
66
- //# sourceMappingURL=project-events.js.map
@@ -7,7 +7,6 @@ export interface GlobalSession {
7
7
  headline?: string;
8
8
  role?: string;
9
9
  worktreePath?: string;
10
- ownerPid?: number;
11
10
  }
12
11
  export interface ProjectInfo {
13
12
  name: string;
@@ -20,11 +19,11 @@ export interface DesktopProjectInfo {
20
19
  path: string;
21
20
  lastSeen?: string;
22
21
  dashboardSessionName: string;
23
- sessions: GlobalSession[];
24
22
  }
25
23
  /**
26
24
  * Discover local projects with aimux state.
27
- * Uses the global projects registry plus filesystem scanning as fallback.
25
+ * Uses the global projects registry. Runtime topology lives in the
26
+ * registry-owned project state directory, not in legacy presence files.
28
27
  */
29
28
  export declare function discoverProjects(): string[];
30
29
  /**
@@ -1,11 +1,19 @@
1
- import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
1
+ import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
2
2
  import { join, basename } from "node:path";
3
- import { homedir, tmpdir } from "node:os";
3
+ import { tmpdir } from "node:os";
4
4
  import { getAimuxDirFor, getProjectStateDirById, listProjects } from "./paths.js";
5
5
  import { TmuxRuntimeManager } from "./tmux/runtime-manager.js";
6
+ import { RuntimeTopologyStore } from "./runtime-core/topology-store.js";
7
+ import { topologySessionToSessionState } from "./runtime-core/topology-sessions.js";
8
+ function topologyStatusToGlobalStatus(status) {
9
+ if (status === "running" || status === "idle" || status === "waiting" || status === "offline")
10
+ return status;
11
+ return "offline";
12
+ }
6
13
  /**
7
14
  * Discover local projects with aimux state.
8
- * Uses the global projects registry plus filesystem scanning as fallback.
15
+ * Uses the global projects registry. Runtime topology lives in the
16
+ * registry-owned project state directory, not in legacy presence files.
9
17
  */
10
18
  export function discoverProjects() {
11
19
  const found = new Set();
@@ -15,34 +23,6 @@ export function discoverProjects() {
15
23
  found.add(entry.repoRoot);
16
24
  }
17
25
  }
18
- // Fallback: scan common dev directories for old-style in-repo .aimux/
19
- const home = homedir();
20
- const scanDirs = [
21
- join(home, "cs"),
22
- join(home, "projects"),
23
- join(home, "dev"),
24
- join(home, "src"),
25
- join(home, "code"),
26
- join(home, "work"),
27
- ];
28
- for (const scanDir of scanDirs) {
29
- try {
30
- const entries = readdirSync(scanDir);
31
- for (const entry of entries) {
32
- const projectPath = join(scanDir, entry);
33
- try {
34
- if (!statSync(projectPath).isDirectory())
35
- continue;
36
- const aimuxDir = join(projectPath, ".aimux");
37
- if (existsSync(join(aimuxDir, "instances.json")) || existsSync(join(aimuxDir, "state.json"))) {
38
- found.add(projectPath);
39
- }
40
- }
41
- catch { }
42
- }
43
- }
44
- catch { }
45
- }
46
26
  return [...found];
47
27
  }
48
28
  /**
@@ -53,133 +33,85 @@ export function scanProject(projectPath) {
53
33
  const seenIds = new Set();
54
34
  const registryEntry = listProjects().find((entry) => entry.repoRoot === projectPath);
55
35
  const sessionById = new Map();
56
- // Check both global state dir and in-repo .aimux/ for instances
57
- const instancesPaths = [join(getAimuxDirFor(projectPath), "instances.json")];
58
36
  const statusDirs = [join(getAimuxDirFor(projectPath), "status")];
59
37
  // Also check global project state dirs for registered projects
60
38
  if (registryEntry) {
61
39
  const projectStateDir = getProjectStateDirById(registryEntry.id);
62
- const globalInstances = join(projectStateDir, "instances.json");
63
- if (!instancesPaths.includes(globalInstances)) {
64
- instancesPaths.unshift(globalInstances);
65
- }
66
40
  statusDirs.unshift(join(projectStateDir, "status"));
67
41
  }
68
- for (const instancesPath of instancesPaths) {
69
- if (!existsSync(instancesPath))
70
- continue;
71
- try {
72
- const instances = JSON.parse(readFileSync(instancesPath, "utf-8"));
73
- for (const inst of instances) {
74
- // Check PID alive
75
- try {
76
- process.kill(inst.pid, 0);
77
- }
78
- catch {
79
- continue;
80
- }
81
- for (const s of inst.sessions) {
82
- if (seenIds.has(s.id))
83
- continue;
84
- seenIds.add(s.id);
85
- let headline;
86
- for (const statusDir of statusDirs) {
87
- try {
88
- const statusPath = join(statusDir, `${s.id}.md`);
89
- if (existsSync(statusPath)) {
90
- const content = readFileSync(statusPath, "utf-8").trim();
91
- if (content) {
92
- headline = content.split("\n")[0].slice(0, 80);
93
- break;
94
- }
95
- }
96
- }
97
- catch {
98
- // Try the next candidate directory.
99
- }
42
+ function readStatusHeadline(sessionId) {
43
+ for (const statusDir of statusDirs) {
44
+ try {
45
+ const statusPath = join(statusDir, `${sessionId}.md`);
46
+ if (existsSync(statusPath)) {
47
+ const content = readFileSync(statusPath, "utf-8").trim();
48
+ if (content) {
49
+ return content.split("\n")[0].slice(0, 80);
100
50
  }
101
- sessions.push({
102
- id: s.id,
103
- tool: s.tool,
104
- status: "running",
105
- headline,
106
- worktreePath: s.worktreePath,
107
- ownerPid: inst.pid,
108
- });
109
- sessionById.set(s.id, sessions[sessions.length - 1]);
110
51
  }
111
52
  }
53
+ catch {
54
+ // Try the next candidate directory.
55
+ }
112
56
  }
113
- catch { }
57
+ return undefined;
114
58
  }
115
- // Enrich live sessions with statusline.json when available.
116
- const statuslinePaths = [join(getAimuxDirFor(projectPath), "statusline.json")];
59
+ const topologyPaths = [];
117
60
  if (registryEntry) {
118
- const globalStatusline = join(getProjectStateDirById(registryEntry.id), "statusline.json");
119
- if (!statuslinePaths.includes(globalStatusline)) {
120
- statuslinePaths.unshift(globalStatusline);
121
- }
61
+ topologyPaths.push(join(getProjectStateDirById(registryEntry.id), "runtime-topology.yaml"));
122
62
  }
123
- for (const statuslinePath of statuslinePaths) {
124
- if (!existsSync(statuslinePath))
63
+ for (const topologyPath of topologyPaths) {
64
+ if (!existsSync(topologyPath))
125
65
  continue;
126
66
  try {
127
- const stat = statSync(statuslinePath);
128
- if (Date.now() - stat.mtimeMs > 10_000)
129
- continue;
130
- const statusline = JSON.parse(readFileSync(statuslinePath, "utf-8"));
131
- for (const s of statusline.sessions ?? []) {
132
- const existing = sessionById.get(s.id);
133
- if (existing) {
134
- existing.tool = s.tool ?? existing.tool;
135
- existing.label = s.label ?? existing.label;
136
- existing.headline = s.headline ?? existing.headline;
137
- existing.status = s.status ?? existing.status;
138
- existing.role = s.role ?? existing.role;
67
+ const topology = new RuntimeTopologyStore(topologyPath).read();
68
+ for (const topologySession of topology.sessions.filter((session) => session.status !== "graveyard")) {
69
+ const s = topologySessionToSessionState(topologySession, topology);
70
+ if (seenIds.has(s.id)) {
71
+ const existing = sessionById.get(s.id);
72
+ if (existing) {
73
+ existing.status = topologyStatusToGlobalStatus(topologySession.status);
74
+ existing.label = s.label ?? existing.label;
75
+ existing.headline = s.headline ?? existing.headline ?? readStatusHeadline(s.id);
76
+ existing.worktreePath = s.worktreePath ?? existing.worktreePath;
77
+ }
139
78
  continue;
140
79
  }
141
80
  const session = {
142
81
  id: s.id,
143
- tool: s.tool ?? "unknown",
82
+ tool: s.command ?? s.tool ?? "unknown",
83
+ status: topologyStatusToGlobalStatus(topologySession.status),
144
84
  label: s.label,
145
- headline: s.headline,
146
- status: s.status ?? "idle",
147
- role: s.role,
85
+ headline: s.headline ?? readStatusHeadline(s.id),
86
+ worktreePath: s.worktreePath,
148
87
  };
149
88
  sessions.push(session);
150
89
  sessionById.set(s.id, session);
151
90
  seenIds.add(s.id);
152
91
  }
153
- break;
154
92
  }
155
93
  catch { }
156
94
  }
157
- // Offline sessions check both global state and in-repo
158
- const statePaths = [join(getAimuxDirFor(projectPath), "state.json")];
159
- if (registryEntry) {
160
- const globalState = join(getProjectStateDirById(registryEntry.id), "state.json");
161
- if (!statePaths.includes(globalState)) {
162
- statePaths.unshift(globalState);
163
- }
164
- }
165
- for (const statePath of statePaths) {
166
- if (!existsSync(statePath))
95
+ // Enrich known sessions with the global statusline projection when available.
96
+ const statuslinePaths = registryEntry ? [join(getProjectStateDirById(registryEntry.id), "statusline.json")] : [];
97
+ for (const statuslinePath of statuslinePaths) {
98
+ if (!existsSync(statuslinePath))
167
99
  continue;
168
100
  try {
169
- const state = JSON.parse(readFileSync(statePath, "utf-8"));
170
- for (const s of state.sessions) {
171
- if (seenIds.has(s.id))
101
+ const stat = statSync(statuslinePath);
102
+ if (Date.now() - stat.mtimeMs > 10_000)
103
+ continue;
104
+ const statusline = JSON.parse(readFileSync(statuslinePath, "utf-8"));
105
+ for (const s of statusline.sessions ?? []) {
106
+ const existing = sessionById.get(s.id);
107
+ if (!existing)
172
108
  continue;
173
- seenIds.add(s.id);
174
- sessions.push({
175
- id: s.id,
176
- tool: s.command ?? s.tool ?? "unknown",
177
- status: "offline",
178
- label: s.label,
179
- headline: s.headline,
180
- worktreePath: s.worktreePath,
181
- });
109
+ existing.tool = s.tool ?? existing.tool;
110
+ existing.label = s.label ?? existing.label;
111
+ existing.headline = s.headline ?? existing.headline;
112
+ existing.role = s.role ?? existing.role;
182
113
  }
114
+ break;
183
115
  }
184
116
  catch { }
185
117
  }
@@ -224,7 +156,6 @@ export function listDesktopProjects(tmux = new TmuxRuntimeManager()) {
224
156
  for (const entry of listProjects()) {
225
157
  if (shouldHideDesktopProject(entry.repoRoot))
226
158
  continue;
227
- const scanned = scannedByPath.get(entry.repoRoot);
228
159
  const tmuxSession = tmux.getProjectSession(entry.repoRoot);
229
160
  projects.set(entry.repoRoot, {
230
161
  id: entry.id,
@@ -232,7 +163,6 @@ export function listDesktopProjects(tmux = new TmuxRuntimeManager()) {
232
163
  path: entry.repoRoot,
233
164
  lastSeen: entry.lastSeen,
234
165
  dashboardSessionName: tmuxSession.sessionName,
235
- sessions: scanned?.sessions ?? [],
236
166
  });
237
167
  }
238
168
  for (const scanned of scannedByPath.values()) {
@@ -246,9 +176,7 @@ export function listDesktopProjects(tmux = new TmuxRuntimeManager()) {
246
176
  name: scanned.name,
247
177
  path: scanned.path,
248
178
  dashboardSessionName: tmuxSession.sessionName,
249
- sessions: scanned.sessions,
250
179
  });
251
180
  }
252
181
  return [...projects.values()].sort((a, b) => a.name.localeCompare(b.name) || a.path.localeCompare(b.path));
253
182
  }
254
- //# sourceMappingURL=project-scanner.js.map
@@ -1,9 +1,7 @@
1
1
  export declare const PROJECT_SERVICE_API_VERSION = 4;
2
2
  export declare const PROJECT_SERVICE_CAPABILITIES: {
3
- readonly structuredAgentInput: true;
4
3
  readonly parsedAgentOutput: true;
5
- readonly attachments: true;
6
- readonly agentHistory: true;
4
+ readonly attachmentRead: true;
7
5
  readonly chatEventStream: true;
8
6
  };
9
7
  export interface ProjectServiceManifest {
@@ -3,10 +3,8 @@ import { existsSync, readFileSync, statSync } from "node:fs";
3
3
  import { fileURLToPath } from "node:url";
4
4
  export const PROJECT_SERVICE_API_VERSION = 4;
5
5
  export const PROJECT_SERVICE_CAPABILITIES = {
6
- structuredAgentInput: true,
7
6
  parsedAgentOutput: true,
8
- attachments: true,
9
- agentHistory: true,
7
+ attachmentRead: true,
10
8
  chatEventStream: true,
11
9
  };
12
10
  function computeBuildStamp() {
@@ -53,4 +51,3 @@ export function manifestsMatch(expected, actual) {
53
51
  const actualCapabilities = actual.capabilities || {};
54
52
  return Object.entries(expected.capabilities).every(([key, value]) => actualCapabilities[key] === value);
55
53
  }
56
- //# sourceMappingURL=project-service-manifest.js.map
package/dist/recency.js CHANGED
@@ -31,4 +31,3 @@ export function formatRelativeRecency(value, now = Date.now()) {
31
31
  const years = Math.floor(days / 365);
32
32
  return `${years}y ago`;
33
33
  }
34
- //# sourceMappingURL=recency.js.map
package/dist/recorder.js CHANGED
@@ -127,4 +127,3 @@ export class Recorder {
127
127
  this.txtStream.end();
128
128
  }
129
129
  }
130
- //# sourceMappingURL=recorder.js.map
@@ -0,0 +1,30 @@
1
+ import type { AimuxDaemon } from "./daemon.js";
2
+ export type RelayConnectionStatus = "connected" | "connecting" | "reconnecting" | "disconnected" | "auth_failed";
3
+ export interface RelayStatusSnapshot {
4
+ status: RelayConnectionStatus;
5
+ relayUrl: string;
6
+ lastConnectedAt: string | null;
7
+ lastError: string | null;
8
+ }
9
+ export declare class RelayClient {
10
+ private readonly token;
11
+ private readonly daemon;
12
+ private ws;
13
+ private retryMs;
14
+ private stopped;
15
+ private retryTimer;
16
+ private status;
17
+ private lastConnectedAt;
18
+ private lastError;
19
+ private readonly relayUrl;
20
+ private handshakeFailures;
21
+ private readonly recentRemoteClientNotifications;
22
+ constructor(relayUrl: string, token: string, daemon: AimuxDaemon);
23
+ getStatus(): RelayStatusSnapshot;
24
+ connect(): void;
25
+ disconnect(): void;
26
+ private handleMessage;
27
+ private sendRaw;
28
+ private shouldNotifyRemoteClientConnected;
29
+ private scheduleRetry;
30
+ }
@@ -0,0 +1,190 @@
1
+ import { notifyRemoteClientConnected } from "./notify.js";
2
+ const INITIAL_RETRY_MS = 1_000;
3
+ const MAX_RETRY_MS = 30_000;
4
+ const MAX_HANDSHAKE_FAILURES = 5;
5
+ const REMOTE_CLIENT_NOTIFICATION_DEDUPE_MS = 5 * 60 * 1000;
6
+ const TOKEN_PROTOCOL_PREFIX = "aimux-token.";
7
+ export class RelayClient {
8
+ token;
9
+ daemon;
10
+ ws = null;
11
+ retryMs = INITIAL_RETRY_MS;
12
+ stopped = false;
13
+ retryTimer = null;
14
+ status = "disconnected";
15
+ lastConnectedAt = null;
16
+ lastError = null;
17
+ relayUrl;
18
+ handshakeFailures = 0;
19
+ recentRemoteClientNotifications = new Map();
20
+ constructor(relayUrl, token, daemon) {
21
+ this.token = token;
22
+ this.daemon = daemon;
23
+ this.relayUrl = relayUrl.replace(/\/+$/, "");
24
+ }
25
+ getStatus() {
26
+ return {
27
+ status: this.status,
28
+ relayUrl: this.relayUrl,
29
+ lastConnectedAt: this.lastConnectedAt,
30
+ lastError: this.lastError,
31
+ };
32
+ }
33
+ connect() {
34
+ if (this.stopped)
35
+ return;
36
+ // Aimux targets Node 24+; fail fast when an older runtime lacks WebSocket.
37
+ // clear error so the user doesn't see an endless reconnect loop full of
38
+ // ReferenceErrors when running on an older runtime.
39
+ if (typeof globalThis.WebSocket !== "function") {
40
+ this.status = "disconnected";
41
+ this.lastError = "Node runtime is missing globalThis.WebSocket — upgrade to Node 24+ to use the aimux relay";
42
+ this.stopped = true;
43
+ return;
44
+ }
45
+ this.status = this.lastConnectedAt ? "reconnecting" : "connecting";
46
+ const url = `${this.relayUrl}/daemon/connect`;
47
+ try {
48
+ this.ws = new WebSocket(url, ["aimux", `${TOKEN_PROTOCOL_PREFIX}${this.token}`]);
49
+ }
50
+ catch (err) {
51
+ this.lastError = err instanceof Error ? err.message : String(err);
52
+ this.scheduleRetry();
53
+ return;
54
+ }
55
+ this.ws.addEventListener("open", () => {
56
+ this.retryMs = INITIAL_RETRY_MS;
57
+ this.handshakeFailures = 0;
58
+ this.status = "connected";
59
+ this.lastConnectedAt = new Date().toISOString();
60
+ this.lastError = null;
61
+ console.log("[relay] Connected to relay server");
62
+ });
63
+ this.ws.addEventListener("message", (event) => {
64
+ void this.handleMessage(String(event.data));
65
+ });
66
+ this.ws.addEventListener("close", (event) => {
67
+ this.ws = null;
68
+ const code = event.code;
69
+ // 1008/4001 = auth rejected by relay; don't hammer with retries.
70
+ if (code === 1008 || code === 4001) {
71
+ this.status = "auth_failed";
72
+ this.lastError = "Relay rejected credentials — run `aimux login` again";
73
+ console.warn(`[relay] Auth failed (code ${code}). Stopping reconnect.`);
74
+ return;
75
+ }
76
+ // 1006 = abnormal close, often a failed HTTP upgrade (e.g. 401).
77
+ // Stop after repeated handshake failures to avoid infinite retry spam.
78
+ if (code === 1006) {
79
+ this.handshakeFailures++;
80
+ if (this.handshakeFailures >= MAX_HANDSHAKE_FAILURES) {
81
+ this.status = "auth_failed";
82
+ this.lastError = "Too many handshake failures — token may be expired, run `aimux login` again";
83
+ console.warn(`[relay] ${MAX_HANDSHAKE_FAILURES} consecutive handshake failures. Stopping reconnect.`);
84
+ return;
85
+ }
86
+ }
87
+ if (!this.stopped) {
88
+ this.status = "reconnecting";
89
+ console.log(`[relay] Disconnected, retrying in ${this.retryMs}ms`);
90
+ this.scheduleRetry();
91
+ }
92
+ else {
93
+ this.status = "disconnected";
94
+ }
95
+ });
96
+ this.ws.addEventListener("error", () => {
97
+ try {
98
+ this.ws?.close();
99
+ }
100
+ catch { }
101
+ });
102
+ }
103
+ disconnect() {
104
+ this.stopped = true;
105
+ this.status = "disconnected";
106
+ if (this.retryTimer)
107
+ clearTimeout(this.retryTimer);
108
+ if (this.ws) {
109
+ try {
110
+ this.ws.close(1000, "Daemon shutting down");
111
+ }
112
+ catch { }
113
+ this.ws = null;
114
+ }
115
+ }
116
+ async handleMessage(data) {
117
+ let msg;
118
+ try {
119
+ msg = JSON.parse(data);
120
+ }
121
+ catch {
122
+ return;
123
+ }
124
+ if (msg.type === "ping") {
125
+ this.sendRaw(JSON.stringify({ type: "pong" }));
126
+ return;
127
+ }
128
+ if (msg.type === "connected") {
129
+ console.log("[relay] Registered as daemon");
130
+ return;
131
+ }
132
+ if (msg.type === "error") {
133
+ console.warn("[relay] Server error:", msg.message);
134
+ return;
135
+ }
136
+ if (msg.type === "security_event") {
137
+ if (msg.event?.kind === "client_connected") {
138
+ const dedupeKey = msg.event.deviceId ?? `${msg.event.title}:${msg.event.body}`;
139
+ if (this.shouldNotifyRemoteClientConnected(dedupeKey)) {
140
+ notifyRemoteClientConnected({
141
+ title: msg.event.title,
142
+ body: msg.event.body,
143
+ });
144
+ }
145
+ }
146
+ return;
147
+ }
148
+ if (msg.type === "request") {
149
+ try {
150
+ const result = await this.daemon.routeRequest(msg.method, msg.path, msg.body, msg.headers);
151
+ this.sendRaw(JSON.stringify({ id: msg.id, type: "response", status: result.status, body: result.body }));
152
+ }
153
+ catch (err) {
154
+ this.sendRaw(JSON.stringify({
155
+ id: msg.id,
156
+ type: "response",
157
+ status: 500,
158
+ body: { ok: false, error: err instanceof Error ? err.message : "Internal error" },
159
+ }));
160
+ }
161
+ }
162
+ }
163
+ sendRaw(data) {
164
+ if (this.ws?.readyState === WebSocket.OPEN) {
165
+ this.ws.send(data);
166
+ }
167
+ }
168
+ shouldNotifyRemoteClientConnected(key) {
169
+ const now = Date.now();
170
+ for (const [existingKey, lastNotifiedAt] of this.recentRemoteClientNotifications) {
171
+ if (now - lastNotifiedAt > REMOTE_CLIENT_NOTIFICATION_DEDUPE_MS) {
172
+ this.recentRemoteClientNotifications.delete(existingKey);
173
+ }
174
+ }
175
+ const previous = this.recentRemoteClientNotifications.get(key);
176
+ if (previous && now - previous <= REMOTE_CLIENT_NOTIFICATION_DEDUPE_MS)
177
+ return false;
178
+ this.recentRemoteClientNotifications.set(key, now);
179
+ return true;
180
+ }
181
+ scheduleRetry() {
182
+ if (this.stopped)
183
+ return;
184
+ this.retryTimer = setTimeout(() => {
185
+ this.retryTimer = null;
186
+ this.connect();
187
+ }, this.retryMs);
188
+ this.retryMs = Math.min(this.retryMs * 2, MAX_RETRY_MS);
189
+ }
190
+ }
@@ -0,0 +1,16 @@
1
+ export type RemoteActorRole = "owner" | "guest";
2
+ export interface RemoteActor {
3
+ role: RemoteActorRole;
4
+ userId?: string;
5
+ displayName?: string;
6
+ email?: string;
7
+ shareId?: string;
8
+ shareSessionId?: string;
9
+ }
10
+ export interface RemoteAccessDecision {
11
+ ok: boolean;
12
+ status?: number;
13
+ error?: string;
14
+ }
15
+ export declare function parseRemoteActor(headers: Record<string, string> | undefined): RemoteActor | null;
16
+ export declare function assertRemoteAccessAllowed(actor: RemoteActor | null, method: string, pathname: string, searchParams: URLSearchParams): RemoteAccessDecision;