@antelopejs/dms-ai 0.0.1

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 (416) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +46 -0
  3. package/dist/builder/presence.d.ts +1 -0
  4. package/dist/builder/presence.js +22 -0
  5. package/dist/builder/presence.js.map +1 -0
  6. package/dist/constants/builder.d.ts +7 -0
  7. package/dist/constants/builder.js +11 -0
  8. package/dist/constants/builder.js.map +1 -0
  9. package/dist/constants/frontend-module.d.ts +3 -0
  10. package/dist/constants/frontend-module.js +7 -0
  11. package/dist/constants/frontend-module.js.map +1 -0
  12. package/dist/constants/logs.d.ts +3 -0
  13. package/dist/constants/logs.js +7 -0
  14. package/dist/constants/logs.js.map +1 -0
  15. package/dist/constants/module.d.ts +3 -0
  16. package/dist/constants/module.js +7 -0
  17. package/dist/constants/module.js.map +1 -0
  18. package/dist/constants/pages-registry.d.ts +2 -0
  19. package/dist/constants/pages-registry.js +6 -0
  20. package/dist/constants/pages-registry.js.map +1 -0
  21. package/dist/constants/sidecar.d.ts +24 -0
  22. package/dist/constants/sidecar.js +32 -0
  23. package/dist/constants/sidecar.js.map +1 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.js +41 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/lifecycle/module-roots.d.ts +1 -0
  28. package/dist/lifecycle/module-roots.js +52 -0
  29. package/dist/lifecycle/module-roots.js.map +1 -0
  30. package/dist/lifecycle/respawn-tracker.d.ts +5 -0
  31. package/dist/lifecycle/respawn-tracker.js +20 -0
  32. package/dist/lifecycle/respawn-tracker.js.map +1 -0
  33. package/dist/lifecycle/skill-sources.d.ts +5 -0
  34. package/dist/lifecycle/skill-sources.js +181 -0
  35. package/dist/lifecycle/skill-sources.js.map +1 -0
  36. package/dist/lifecycle/spawn-sidecar.d.ts +17 -0
  37. package/dist/lifecycle/spawn-sidecar.js +299 -0
  38. package/dist/lifecycle/spawn-sidecar.js.map +1 -0
  39. package/dist/logging/log-buffer.d.ts +9 -0
  40. package/dist/logging/log-buffer.js +76 -0
  41. package/dist/logging/log-buffer.js.map +1 -0
  42. package/dist/pages/activity/page.d.ts +10 -0
  43. package/dist/pages/activity/page.js +62 -0
  44. package/dist/pages/activity/page.js.map +1 -0
  45. package/dist/pages/all.d.ts +4 -0
  46. package/dist/pages/all.js +21 -0
  47. package/dist/pages/all.js.map +1 -0
  48. package/dist/pages/index.d.ts +4 -0
  49. package/dist/pages/index.js +21 -0
  50. package/dist/pages/index.js.map +1 -0
  51. package/dist/pages/module.d.ts +1 -0
  52. package/dist/pages/module.js +11 -0
  53. package/dist/pages/module.js.map +1 -0
  54. package/dist/pages/overview/page.d.ts +11 -0
  55. package/dist/pages/overview/page.js +82 -0
  56. package/dist/pages/overview/page.js.map +1 -0
  57. package/dist/pages/settings/page.d.ts +9 -0
  58. package/dist/pages/settings/page.js +29 -0
  59. package/dist/pages/settings/page.js.map +1 -0
  60. package/dist/pages/skills/page.d.ts +9 -0
  61. package/dist/pages/skills/page.js +29 -0
  62. package/dist/pages/skills/page.js.map +1 -0
  63. package/dist/pages-registry.d.ts +2 -0
  64. package/dist/pages-registry.js +41 -0
  65. package/dist/pages-registry.js.map +1 -0
  66. package/dist/routes/builder.d.ts +11 -0
  67. package/dist/routes/builder.js +135 -0
  68. package/dist/routes/builder.js.map +1 -0
  69. package/dist/routes/health.d.ts +8 -0
  70. package/dist/routes/health.js +27 -0
  71. package/dist/routes/health.js.map +1 -0
  72. package/dist/routes/index.d.ts +8 -0
  73. package/dist/routes/index.js +25 -0
  74. package/dist/routes/index.js.map +1 -0
  75. package/dist/routes/logs.d.ts +7 -0
  76. package/dist/routes/logs.js +51 -0
  77. package/dist/routes/logs.js.map +1 -0
  78. package/dist/routes/metrics.d.ts +54 -0
  79. package/dist/routes/metrics.js +104 -0
  80. package/dist/routes/metrics.js.map +1 -0
  81. package/dist/routes/pages-registry.d.ts +7 -0
  82. package/dist/routes/pages-registry.js +86 -0
  83. package/dist/routes/pages-registry.js.map +1 -0
  84. package/dist/routes/proxy.d.ts +4 -0
  85. package/dist/routes/proxy.js +55 -0
  86. package/dist/routes/proxy.js.map +1 -0
  87. package/dist/routes/settings.d.ts +13 -0
  88. package/dist/routes/settings.js +52 -0
  89. package/dist/routes/settings.js.map +1 -0
  90. package/dist/routes/sidecar-auth.d.ts +3 -0
  91. package/dist/routes/sidecar-auth.js +27 -0
  92. package/dist/routes/sidecar-auth.js.map +1 -0
  93. package/dist/routes/sidecar-info.d.ts +6 -0
  94. package/dist/routes/sidecar-info.js +39 -0
  95. package/dist/routes/sidecar-info.js.map +1 -0
  96. package/dist/routes/skills.d.ts +18 -0
  97. package/dist/routes/skills.js +33 -0
  98. package/dist/routes/skills.js.map +1 -0
  99. package/dist/types/index.d.ts +2 -0
  100. package/dist/types/index.js +19 -0
  101. package/dist/types/index.js.map +1 -0
  102. package/dist/types/logs.d.ts +12 -0
  103. package/dist/types/logs.js +3 -0
  104. package/dist/types/logs.js.map +1 -0
  105. package/dist/types/pages-registry.d.ts +6 -0
  106. package/dist/types/pages-registry.js +3 -0
  107. package/dist/types/pages-registry.js.map +1 -0
  108. package/frontend-vue/.editorconfig +12 -0
  109. package/frontend-vue/app/components/ActivityView.vue +94 -0
  110. package/frontend-vue/app/components/SettingsView.vue +300 -0
  111. package/frontend-vue/app/components/SkillsView.vue +228 -0
  112. package/frontend-vue/app/plugins/ai.client.ts +109 -0
  113. package/frontend-vue/app/runtime/backoff.ts +30 -0
  114. package/frontend-vue/app/runtime/constants.ts +66 -0
  115. package/frontend-vue/app/runtime/current-page.ts +28 -0
  116. package/frontend-vue/app/runtime/header-action.ts +64 -0
  117. package/frontend-vue/app/runtime/host-commands.ts +127 -0
  118. package/frontend-vue/app/runtime/navigation-complete.ts +22 -0
  119. package/frontend-vue/app/runtime/overlay-prefs.ts +89 -0
  120. package/frontend-vue/app/runtime/overlay.ts +383 -0
  121. package/frontend-vue/app/runtime/shortcuts.ts +28 -0
  122. package/frontend-vue/app/runtime/sidecar-status.ts +163 -0
  123. package/frontend-vue/app/runtime/theme-bridge.ts +177 -0
  124. package/frontend-vue/app/runtime/ws-client.ts +196 -0
  125. package/frontend-vue/dms.frontend.ts +26 -0
  126. package/frontend-vue/eslint.config.mjs +24 -0
  127. package/frontend-vue/package.json +33 -0
  128. package/frontend-vue/pnpm-lock.yaml +2723 -0
  129. package/frontend-vue/tests/backoff.test.ts +44 -0
  130. package/frontend-vue/tests/host-commands.test.ts +142 -0
  131. package/frontend-vue/tests/navigation.test.ts +45 -0
  132. package/frontend-vue/tests/overlay-prefs.test.ts +91 -0
  133. package/frontend-vue/tests/sidecar-auth.test.ts +87 -0
  134. package/frontend-vue/tests/sidecar-guest.test.ts +45 -0
  135. package/frontend-vue/vitest.config.ts +8 -0
  136. package/package.json +79 -0
  137. package/sidecar/chatbox/dist/assets/index-Cy2BwTG0.js +50 -0
  138. package/sidecar/chatbox/dist/assets/index-pP_KOHIn.css +1 -0
  139. package/sidecar/chatbox/dist/index.html +13 -0
  140. package/sidecar/dist/agent/attachments.d.ts +8 -0
  141. package/sidecar/dist/agent/attachments.js +85 -0
  142. package/sidecar/dist/agent/attachments.js.map +1 -0
  143. package/sidecar/dist/agent/claude-runner.d.ts +33 -0
  144. package/sidecar/dist/agent/claude-runner.js +264 -0
  145. package/sidecar/dist/agent/claude-runner.js.map +1 -0
  146. package/sidecar/dist/agent/claude-session.d.ts +18 -0
  147. package/sidecar/dist/agent/claude-session.js +155 -0
  148. package/sidecar/dist/agent/claude-session.js.map +1 -0
  149. package/sidecar/dist/agent/edit-tracker.d.ts +9 -0
  150. package/sidecar/dist/agent/edit-tracker.js +34 -0
  151. package/sidecar/dist/agent/edit-tracker.js.map +1 -0
  152. package/sidecar/dist/agent/host-context.d.ts +4 -0
  153. package/sidecar/dist/agent/host-context.js +16 -0
  154. package/sidecar/dist/agent/host-context.js.map +1 -0
  155. package/sidecar/dist/agent/input-queue.d.ts +7 -0
  156. package/sidecar/dist/agent/input-queue.js +39 -0
  157. package/sidecar/dist/agent/input-queue.js.map +1 -0
  158. package/sidecar/dist/agent/permission-bus.d.ts +26 -0
  159. package/sidecar/dist/agent/permission-bus.js +138 -0
  160. package/sidecar/dist/agent/permission-bus.js.map +1 -0
  161. package/sidecar/dist/agent/question-bus.d.ts +22 -0
  162. package/sidecar/dist/agent/question-bus.js +91 -0
  163. package/sidecar/dist/agent/question-bus.js.map +1 -0
  164. package/sidecar/dist/agent/read-access.d.ts +2 -0
  165. package/sidecar/dist/agent/read-access.js +33 -0
  166. package/sidecar/dist/agent/read-access.js.map +1 -0
  167. package/sidecar/dist/agent/resolve-claude-binary.d.ts +1 -0
  168. package/sidecar/dist/agent/resolve-claude-binary.js +43 -0
  169. package/sidecar/dist/agent/resolve-claude-binary.js.map +1 -0
  170. package/sidecar/dist/agent/runner-events.d.ts +28 -0
  171. package/sidecar/dist/agent/runner-events.js +2 -0
  172. package/sidecar/dist/agent/runner-events.js.map +1 -0
  173. package/sidecar/dist/agent/sdk-adapter.d.ts +3 -0
  174. package/sidecar/dist/agent/sdk-adapter.js +106 -0
  175. package/sidecar/dist/agent/sdk-adapter.js.map +1 -0
  176. package/sidecar/dist/agent/sdk-loader.d.ts +5 -0
  177. package/sidecar/dist/agent/sdk-loader.js +14 -0
  178. package/sidecar/dist/agent/sdk-loader.js.map +1 -0
  179. package/sidecar/dist/agent/system-prompt.d.ts +6 -0
  180. package/sidecar/dist/agent/system-prompt.js +29 -0
  181. package/sidecar/dist/agent/system-prompt.js.map +1 -0
  182. package/sidecar/dist/agent/tool-summary.d.ts +1 -0
  183. package/sidecar/dist/agent/tool-summary.js +46 -0
  184. package/sidecar/dist/agent/tool-summary.js.map +1 -0
  185. package/sidecar/dist/agent/typecheck.d.ts +36 -0
  186. package/sidecar/dist/agent/typecheck.js +104 -0
  187. package/sidecar/dist/agent/typecheck.js.map +1 -0
  188. package/sidecar/dist/builder/builder-client.d.ts +10 -0
  189. package/sidecar/dist/builder/builder-client.js +52 -0
  190. package/sidecar/dist/builder/builder-client.js.map +1 -0
  191. package/sidecar/dist/builder/capability.d.ts +4 -0
  192. package/sidecar/dist/builder/capability.js +11 -0
  193. package/sidecar/dist/builder/capability.js.map +1 -0
  194. package/sidecar/dist/constants/agent.d.ts +25 -0
  195. package/sidecar/dist/constants/agent.js +25 -0
  196. package/sidecar/dist/constants/agent.js.map +1 -0
  197. package/sidecar/dist/constants/attachments.d.ts +7 -0
  198. package/sidecar/dist/constants/attachments.js +12 -0
  199. package/sidecar/dist/constants/attachments.js.map +1 -0
  200. package/sidecar/dist/constants/builder.d.ts +5 -0
  201. package/sidecar/dist/constants/builder.js +32 -0
  202. package/sidecar/dist/constants/builder.js.map +1 -0
  203. package/sidecar/dist/constants/daemon.d.ts +7 -0
  204. package/sidecar/dist/constants/daemon.js +8 -0
  205. package/sidecar/dist/constants/daemon.js.map +1 -0
  206. package/sidecar/dist/constants/env.d.ts +3 -0
  207. package/sidecar/dist/constants/env.js +4 -0
  208. package/sidecar/dist/constants/env.js.map +1 -0
  209. package/sidecar/dist/constants/host-state.d.ts +1 -0
  210. package/sidecar/dist/constants/host-state.js +2 -0
  211. package/sidecar/dist/constants/host-state.js.map +1 -0
  212. package/sidecar/dist/constants/http.d.ts +20 -0
  213. package/sidecar/dist/constants/http.js +31 -0
  214. package/sidecar/dist/constants/http.js.map +1 -0
  215. package/sidecar/dist/constants/logs.d.ts +11 -0
  216. package/sidecar/dist/constants/logs.js +16 -0
  217. package/sidecar/dist/constants/logs.js.map +1 -0
  218. package/sidecar/dist/constants/mcp.d.ts +24 -0
  219. package/sidecar/dist/constants/mcp.js +38 -0
  220. package/sidecar/dist/constants/mcp.js.map +1 -0
  221. package/sidecar/dist/constants/native-binary.d.ts +4 -0
  222. package/sidecar/dist/constants/native-binary.js +5 -0
  223. package/sidecar/dist/constants/native-binary.js.map +1 -0
  224. package/sidecar/dist/constants/pages.d.ts +16 -0
  225. package/sidecar/dist/constants/pages.js +24 -0
  226. package/sidecar/dist/constants/pages.js.map +1 -0
  227. package/sidecar/dist/constants/paths.d.ts +3 -0
  228. package/sidecar/dist/constants/paths.js +7 -0
  229. package/sidecar/dist/constants/paths.js.map +1 -0
  230. package/sidecar/dist/constants/permissions.d.ts +15 -0
  231. package/sidecar/dist/constants/permissions.js +15 -0
  232. package/sidecar/dist/constants/permissions.js.map +1 -0
  233. package/sidecar/dist/constants/ports.d.ts +3 -0
  234. package/sidecar/dist/constants/ports.js +4 -0
  235. package/sidecar/dist/constants/ports.js.map +1 -0
  236. package/sidecar/dist/constants/project.d.ts +8 -0
  237. package/sidecar/dist/constants/project.js +12 -0
  238. package/sidecar/dist/constants/project.js.map +1 -0
  239. package/sidecar/dist/constants/questions.d.ts +4 -0
  240. package/sidecar/dist/constants/questions.js +5 -0
  241. package/sidecar/dist/constants/questions.js.map +1 -0
  242. package/sidecar/dist/constants/read-access.d.ts +1 -0
  243. package/sidecar/dist/constants/read-access.js +8 -0
  244. package/sidecar/dist/constants/read-access.js.map +1 -0
  245. package/sidecar/dist/constants/safety-net.d.ts +5 -0
  246. package/sidecar/dist/constants/safety-net.js +6 -0
  247. package/sidecar/dist/constants/safety-net.js.map +1 -0
  248. package/sidecar/dist/constants/settings.d.ts +9 -0
  249. package/sidecar/dist/constants/settings.js +29 -0
  250. package/sidecar/dist/constants/settings.js.map +1 -0
  251. package/sidecar/dist/constants/state.d.ts +7 -0
  252. package/sidecar/dist/constants/state.js +7 -0
  253. package/sidecar/dist/constants/state.js.map +1 -0
  254. package/sidecar/dist/constants/timing.d.ts +1 -0
  255. package/sidecar/dist/constants/timing.js +2 -0
  256. package/sidecar/dist/constants/timing.js.map +1 -0
  257. package/sidecar/dist/constants/tool-summary.d.ts +20 -0
  258. package/sidecar/dist/constants/tool-summary.js +21 -0
  259. package/sidecar/dist/constants/tool-summary.js.map +1 -0
  260. package/sidecar/dist/constants/typecheck.d.ts +16 -0
  261. package/sidecar/dist/constants/typecheck.js +23 -0
  262. package/sidecar/dist/constants/typecheck.js.map +1 -0
  263. package/sidecar/dist/constants/ws.d.ts +5 -0
  264. package/sidecar/dist/constants/ws.js +6 -0
  265. package/sidecar/dist/constants/ws.js.map +1 -0
  266. package/sidecar/dist/host/project-info.d.ts +6 -0
  267. package/sidecar/dist/host/project-info.js +51 -0
  268. package/sidecar/dist/host/project-info.js.map +1 -0
  269. package/sidecar/dist/index.d.ts +3 -0
  270. package/sidecar/dist/index.js +280 -0
  271. package/sidecar/dist/index.js.map +1 -0
  272. package/sidecar/dist/logs/logs-client.d.ts +11 -0
  273. package/sidecar/dist/logs/logs-client.js +52 -0
  274. package/sidecar/dist/logs/logs-client.js.map +1 -0
  275. package/sidecar/dist/logs/types.d.ts +12 -0
  276. package/sidecar/dist/logs/types.js +2 -0
  277. package/sidecar/dist/logs/types.js.map +1 -0
  278. package/sidecar/dist/mcp/server.d.ts +2 -0
  279. package/sidecar/dist/mcp/server.js +50 -0
  280. package/sidecar/dist/mcp/server.js.map +1 -0
  281. package/sidecar/dist/mcp/tools/ask-user.d.ts +41 -0
  282. package/sidecar/dist/mcp/tools/ask-user.js +29 -0
  283. package/sidecar/dist/mcp/tools/ask-user.js.map +1 -0
  284. package/sidecar/dist/mcp/tools/builder.d.ts +259 -0
  285. package/sidecar/dist/mcp/tools/builder.js +216 -0
  286. package/sidecar/dist/mcp/tools/builder.js.map +1 -0
  287. package/sidecar/dist/mcp/tools/find-pages-using.d.ts +15 -0
  288. package/sidecar/dist/mcp/tools/find-pages-using.js +108 -0
  289. package/sidecar/dist/mcp/tools/find-pages-using.js.map +1 -0
  290. package/sidecar/dist/mcp/tools/get-current-page.d.ts +4 -0
  291. package/sidecar/dist/mcp/tools/get-current-page.js +12 -0
  292. package/sidecar/dist/mcp/tools/get-current-page.js.map +1 -0
  293. package/sidecar/dist/mcp/tools/list-pages.d.ts +8 -0
  294. package/sidecar/dist/mcp/tools/list-pages.js +29 -0
  295. package/sidecar/dist/mcp/tools/list-pages.js.map +1 -0
  296. package/sidecar/dist/mcp/tools/navigate-to-page.d.ts +14 -0
  297. package/sidecar/dist/mcp/tools/navigate-to-page.js +66 -0
  298. package/sidecar/dist/mcp/tools/navigate-to-page.js.map +1 -0
  299. package/sidecar/dist/mcp/tools/query-logs.d.ts +11 -0
  300. package/sidecar/dist/mcp/tools/query-logs.js +32 -0
  301. package/sidecar/dist/mcp/tools/query-logs.js.map +1 -0
  302. package/sidecar/dist/mcp/tools/typecheck.d.ts +9 -0
  303. package/sidecar/dist/mcp/tools/typecheck.js +20 -0
  304. package/sidecar/dist/mcp/tools/typecheck.js.map +1 -0
  305. package/sidecar/dist/mcp/types.d.ts +27 -0
  306. package/sidecar/dist/mcp/types.js +2 -0
  307. package/sidecar/dist/mcp/types.js.map +1 -0
  308. package/sidecar/dist/metrics/aggregate.d.ts +11 -0
  309. package/sidecar/dist/metrics/aggregate.js +189 -0
  310. package/sidecar/dist/metrics/aggregate.js.map +1 -0
  311. package/sidecar/dist/metrics/types.d.ts +53 -0
  312. package/sidecar/dist/metrics/types.js +8 -0
  313. package/sidecar/dist/metrics/types.js.map +1 -0
  314. package/sidecar/dist/pages/imports-scanner.d.ts +9 -0
  315. package/sidecar/dist/pages/imports-scanner.js +143 -0
  316. package/sidecar/dist/pages/imports-scanner.js.map +1 -0
  317. package/sidecar/dist/pages/registry-client.d.ts +15 -0
  318. package/sidecar/dist/pages/registry-client.js +83 -0
  319. package/sidecar/dist/pages/registry-client.js.map +1 -0
  320. package/sidecar/dist/pages/route-match.d.ts +2 -0
  321. package/sidecar/dist/pages/route-match.js +10 -0
  322. package/sidecar/dist/pages/route-match.js.map +1 -0
  323. package/sidecar/dist/pages/types.d.ts +11 -0
  324. package/sidecar/dist/pages/types.js +2 -0
  325. package/sidecar/dist/pages/types.js.map +1 -0
  326. package/sidecar/dist/prompts/agent.d.ts +3 -0
  327. package/sidecar/dist/prompts/agent.js +40 -0
  328. package/sidecar/dist/prompts/agent.js.map +1 -0
  329. package/sidecar/dist/prompts/builder.d.ts +3 -0
  330. package/sidecar/dist/prompts/builder.js +4 -0
  331. package/sidecar/dist/prompts/builder.js.map +1 -0
  332. package/sidecar/dist/protocol/events.d.ts +1050 -0
  333. package/sidecar/dist/protocol/events.js +154 -0
  334. package/sidecar/dist/protocol/events.js.map +1 -0
  335. package/sidecar/dist/protocol/messages.d.ts +644 -0
  336. package/sidecar/dist/protocol/messages.js +139 -0
  337. package/sidecar/dist/protocol/messages.js.map +1 -0
  338. package/sidecar/dist/server/client-auth.d.ts +2 -0
  339. package/sidecar/dist/server/client-auth.js +25 -0
  340. package/sidecar/dist/server/client-auth.js.map +1 -0
  341. package/sidecar/dist/server/host-socket-registry.d.ts +9 -0
  342. package/sidecar/dist/server/host-socket-registry.js +42 -0
  343. package/sidecar/dist/server/host-socket-registry.js.map +1 -0
  344. package/sidecar/dist/server/http.d.ts +25 -0
  345. package/sidecar/dist/server/http.js +263 -0
  346. package/sidecar/dist/server/http.js.map +1 -0
  347. package/sidecar/dist/server/idle-shutdown.d.ts +10 -0
  348. package/sidecar/dist/server/idle-shutdown.js +35 -0
  349. package/sidecar/dist/server/idle-shutdown.js.map +1 -0
  350. package/sidecar/dist/server/iframe-socket-registry.d.ts +10 -0
  351. package/sidecar/dist/server/iframe-socket-registry.js +57 -0
  352. package/sidecar/dist/server/iframe-socket-registry.js.map +1 -0
  353. package/sidecar/dist/server/live-turns.d.ts +8 -0
  354. package/sidecar/dist/server/live-turns.js +16 -0
  355. package/sidecar/dist/server/live-turns.js.map +1 -0
  356. package/sidecar/dist/server/navigation-completer.d.ts +5 -0
  357. package/sidecar/dist/server/navigation-completer.js +49 -0
  358. package/sidecar/dist/server/navigation-completer.js.map +1 -0
  359. package/sidecar/dist/server/pending-queue.d.ts +11 -0
  360. package/sidecar/dist/server/pending-queue.js +54 -0
  361. package/sidecar/dist/server/pending-queue.js.map +1 -0
  362. package/sidecar/dist/server/raw-data.d.ts +2 -0
  363. package/sidecar/dist/server/raw-data.js +13 -0
  364. package/sidecar/dist/server/raw-data.js.map +1 -0
  365. package/sidecar/dist/server/routing.d.ts +64 -0
  366. package/sidecar/dist/server/routing.js +571 -0
  367. package/sidecar/dist/server/routing.js.map +1 -0
  368. package/sidecar/dist/server/safety-net.d.ts +16 -0
  369. package/sidecar/dist/server/safety-net.js +76 -0
  370. package/sidecar/dist/server/safety-net.js.map +1 -0
  371. package/sidecar/dist/server/ws.d.ts +33 -0
  372. package/sidecar/dist/server/ws.js +182 -0
  373. package/sidecar/dist/server/ws.js.map +1 -0
  374. package/sidecar/dist/skills/build-catalog.d.ts +15 -0
  375. package/sidecar/dist/skills/build-catalog.js +39 -0
  376. package/sidecar/dist/skills/build-catalog.js.map +1 -0
  377. package/sidecar/dist/skills/frontmatter.d.ts +9 -0
  378. package/sidecar/dist/skills/frontmatter.js +50 -0
  379. package/sidecar/dist/skills/frontmatter.js.map +1 -0
  380. package/sidecar/dist/skills/plugin-wrapper.d.ts +6 -0
  381. package/sidecar/dist/skills/plugin-wrapper.js +37 -0
  382. package/sidecar/dist/skills/plugin-wrapper.js.map +1 -0
  383. package/sidecar/dist/skills/resolve-sources.d.ts +3 -0
  384. package/sidecar/dist/skills/resolve-sources.js +14 -0
  385. package/sidecar/dist/skills/resolve-sources.js.map +1 -0
  386. package/sidecar/dist/skills/types.d.ts +4 -0
  387. package/sidecar/dist/skills/types.js +2 -0
  388. package/sidecar/dist/skills/types.js.map +1 -0
  389. package/sidecar/dist/state/conversations.d.ts +20 -0
  390. package/sidecar/dist/state/conversations.js +93 -0
  391. package/sidecar/dist/state/conversations.js.map +1 -0
  392. package/sidecar/dist/state/host-state.d.ts +10 -0
  393. package/sidecar/dist/state/host-state.js +14 -0
  394. package/sidecar/dist/state/host-state.js.map +1 -0
  395. package/sidecar/dist/state/lock.d.ts +12 -0
  396. package/sidecar/dist/state/lock.js +43 -0
  397. package/sidecar/dist/state/lock.js.map +1 -0
  398. package/sidecar/dist/state/settings-store.d.ts +12 -0
  399. package/sidecar/dist/state/settings-store.js +82 -0
  400. package/sidecar/dist/state/settings-store.js.map +1 -0
  401. package/sidecar/dist/state/settings-types.d.ts +12 -0
  402. package/sidecar/dist/state/settings-types.js +4 -0
  403. package/sidecar/dist/state/settings-types.js.map +1 -0
  404. package/sidecar/dist/state/sidecar-version.d.ts +1 -0
  405. package/sidecar/dist/state/sidecar-version.js +14 -0
  406. package/sidecar/dist/state/sidecar-version.js.map +1 -0
  407. package/sidecar/dist/state/store.d.ts +11 -0
  408. package/sidecar/dist/state/store.js +89 -0
  409. package/sidecar/dist/state/store.js.map +1 -0
  410. package/sidecar/dist/state/types.d.ts +36 -0
  411. package/sidecar/dist/state/types.js +10 -0
  412. package/sidecar/dist/state/types.js.map +1 -0
  413. package/sidecar/package.json +42 -0
  414. package/skills/dms-builder-page/SKILL.md +80 -0
  415. package/skills/dms-builder-safe/REFERENCE.md +111 -0
  416. package/skills/dms-builder-safe/SKILL.md +118 -0
@@ -0,0 +1,383 @@
1
+ import {
2
+ OVERLAY_BOX_SHADOW,
3
+ OVERLAY_DEFAULT_WIDTH_PX,
4
+ OVERLAY_DOM_ID,
5
+ OVERLAY_IFRAME_ID,
6
+ OVERLAY_MIN_WIDTH_PX,
7
+ OVERLAY_OUTSIDE_TOGGLE_SUPPRESS_MS,
8
+ OVERLAY_PLACEHOLDER_ID,
9
+ OVERLAY_RESIZE_HANDLE_SIZE_PX,
10
+ OVERLAY_Z_INDEX,
11
+ PLACEHOLDER_CONNECTING_TEXT,
12
+ PLACEHOLDER_REVIVING_TEXT,
13
+ PLACEHOLDER_UNAVAILABLE_TEXT,
14
+ PLACEHOLDER_UNAVAILABLE_TITLE,
15
+ } from './constants'
16
+ import {
17
+ type OverlayPrefs,
18
+ readPrefs,
19
+ writePrefsDebounced,
20
+ } from './overlay-prefs'
21
+ import {
22
+ type HostTheme,
23
+ installThemeBridge,
24
+ readHostMode,
25
+ } from './theme-bridge'
26
+
27
+ // What the host-origin placeholder shows while the sidecar frame can't be shown.
28
+ // `connecting`/`reviving` are transient (spinner); `unavailable` is terminal.
29
+ export type PlaceholderKind = 'connecting' | 'reviving' | 'unavailable'
30
+
31
+ export interface OverlayHandle {
32
+ // Toggle the docked panel's slide-in open/closed state.
33
+ toggleOpen: () => void
34
+ // Launcher-specific toggle: ignores an open request that immediately follows
35
+ // an outside-click close, since the launcher's `click` fires after the same
36
+ // `pointerdown` that already closed the panel. The keyboard shortcut must
37
+ // use `toggleOpen` so it is never suppressed.
38
+ toggleFromLauncher: () => void
39
+ isOpen: () => boolean
40
+ // Point the frame at a (possibly new) sidecar URL. A no-op when the URL is
41
+ // unchanged and already loaded, so a redundant `connected` notify won't reload
42
+ // the chatbox; otherwise it reloads and shows a connecting placeholder until
43
+ // the frame paints.
44
+ repoint: (url: string) => void
45
+ // Cover the frame with the host-origin status panel (never the browser's raw
46
+ // error page) while the sidecar is unreachable.
47
+ showPlaceholder: (kind: PlaceholderKind) => void
48
+ }
49
+
50
+ interface OverlayElements {
51
+ container: HTMLDivElement
52
+ iframe: HTMLIFrameElement
53
+ resizeHandle: HTMLDivElement
54
+ placeholder: HTMLDivElement
55
+ }
56
+
57
+ interface OverlayPrefsRef {
58
+ value: OverlayPrefs
59
+ }
60
+
61
+ interface MessageData {
62
+ type?: unknown
63
+ }
64
+
65
+ const MAX_WIDTH_VW_RATIO = 0.94
66
+ // Message types accepted from the chatbox iframe to close the panel.
67
+ const CLOSE_MESSAGE_TYPES = new Set(['dms-ai:close'])
68
+
69
+ // Fallback backdrop shown behind the iframe while it slides in, before the
70
+ // bridge reports the host's real `--ui-bg`. Only briefly visible.
71
+ const BACKDROP_DARK = '#0a0b12'
72
+ const BACKDROP_LIGHT = '#ffffff'
73
+
74
+ function backdropFor(mode: 'dark' | 'light'): string {
75
+ return mode === 'dark' ? BACKDROP_DARK : BACKDROP_LIGHT
76
+ }
77
+
78
+ function maxWidth(): number {
79
+ const vw = globalThis.innerWidth ?? OVERLAY_DEFAULT_WIDTH_PX * 3
80
+ return Math.max(OVERLAY_MIN_WIDTH_PX, Math.round(vw * MAX_WIDTH_VW_RATIO))
81
+ }
82
+
83
+ function clampWidth(width: number): number {
84
+ return Math.min(Math.max(width, OVERLAY_MIN_WIDTH_PX), maxWidth())
85
+ }
86
+
87
+ function buildContainer(): HTMLDivElement {
88
+ const container = document.createElement('div')
89
+ container.id = OVERLAY_DOM_ID
90
+ const style = container.style
91
+ style.position = 'fixed'
92
+ style.top = '0'
93
+ style.right = '0'
94
+ style.height = '100vh'
95
+ style.zIndex = String(OVERLAY_Z_INDEX)
96
+ style.boxShadow = OVERLAY_BOX_SHADOW
97
+ // Backdrop behind the iframe while it slides in — follow the host mode so a
98
+ // light-mode host doesn't flash a dark sheet. The iframe fills the container
99
+ // once painted, so this is only ever briefly visible.
100
+ style.background = backdropFor(readHostMode())
101
+ style.borderLeft = '1px solid rgba(140,160,220,0.20)'
102
+ style.overflow = 'hidden'
103
+ style.display = 'flex'
104
+ style.transition = 'transform 220ms cubic-bezier(0.16, 1, 0.3, 1)'
105
+ style.willChange = 'transform'
106
+ return container
107
+ }
108
+
109
+ // Built with no `src`: the frame is only pointed at the sidecar via `repoint`
110
+ // once the status controller confirms a reachable port, so the browser never
111
+ // renders its native "can't reach" page for a dead port.
112
+ function buildIframe(): HTMLIFrameElement {
113
+ const iframe = document.createElement('iframe')
114
+ iframe.id = OVERLAY_IFRAME_ID
115
+ iframe.style.flex = '1 1 auto'
116
+ iframe.style.width = '100%'
117
+ iframe.style.height = '100%'
118
+ iframe.style.border = '0'
119
+ iframe.style.colorScheme = readHostMode()
120
+ return iframe
121
+ }
122
+
123
+ const SPINNER_STYLE_ID = 'dms-ai-overlay-spinner-style'
124
+
125
+ function ensureSpinnerKeyframes(): void {
126
+ if (document.getElementById(SPINNER_STYLE_ID) !== null) return
127
+ const style = document.createElement('style')
128
+ style.id = SPINNER_STYLE_ID
129
+ style.textContent =
130
+ '@keyframes dms-ai-spin{to{transform:rotate(360deg)}}'
131
+ document.head.appendChild(style)
132
+ }
133
+
134
+ function buildPlaceholder(): HTMLDivElement {
135
+ const placeholder = document.createElement('div')
136
+ placeholder.id = OVERLAY_PLACEHOLDER_ID
137
+ const style = placeholder.style
138
+ style.position = 'absolute'
139
+ style.inset = '0'
140
+ // Above the iframe, but transparent to pointers so the resize handle and the
141
+ // (harmless) dead frame beneath stay reachable.
142
+ style.zIndex = '2'
143
+ style.pointerEvents = 'none'
144
+ style.display = 'none'
145
+ style.flexDirection = 'column'
146
+ style.alignItems = 'center'
147
+ style.justifyContent = 'center'
148
+ style.gap = '14px'
149
+ style.padding = '24px'
150
+ style.textAlign = 'center'
151
+ style.font =
152
+ '500 13px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif'
153
+ style.background = backdropFor(readHostMode())
154
+ return placeholder
155
+ }
156
+
157
+ function spinnerHtml(color: string): string {
158
+ return `<span style="width:22px;height:22px;border:2.5px solid ${color}33;border-top-color:${color};border-radius:50%;animation:dms-ai-spin 0.7s linear infinite;"></span>`
159
+ }
160
+
161
+ function placeholderContent(kind: PlaceholderKind): string {
162
+ if (kind === 'unavailable') {
163
+ return `<div style="font-size:15px;font-weight:600;">${PLACEHOLDER_UNAVAILABLE_TITLE}</div><div style="opacity:0.7;">${PLACEHOLDER_UNAVAILABLE_TEXT}</div>`
164
+ }
165
+ const text =
166
+ kind === 'reviving'
167
+ ? PLACEHOLDER_REVIVING_TEXT
168
+ : PLACEHOLDER_CONNECTING_TEXT
169
+ return `${spinnerHtml('currentColor')}<div style="opacity:0.75;">${text}</div>`
170
+ }
171
+
172
+ function buildResizeHandle(): HTMLDivElement {
173
+ const handle = document.createElement('div')
174
+ const style = handle.style
175
+ style.position = 'absolute'
176
+ style.left = '0'
177
+ style.top = '0'
178
+ style.width = `${OVERLAY_RESIZE_HANDLE_SIZE_PX}px`
179
+ style.height = '100%'
180
+ style.cursor = 'ew-resize'
181
+ style.touchAction = 'none'
182
+ style.zIndex = '1'
183
+ return handle
184
+ }
185
+
186
+ function applyPrefs(elements: OverlayElements, prefs: OverlayPrefs): void {
187
+ const width = clampWidth(prefs.width)
188
+ elements.container.style.width = `${width}px`
189
+ elements.container.style.transform = prefs.isOpen
190
+ ? 'translateX(0)'
191
+ : 'translateX(100%)'
192
+ }
193
+
194
+ function resolveInitialPrefs(): OverlayPrefs {
195
+ const stored = readPrefs()
196
+ if (stored !== null)
197
+ return { width: clampWidth(stored.width), isOpen: stored.isOpen }
198
+ return { width: OVERLAY_DEFAULT_WIDTH_PX, isOpen: false }
199
+ }
200
+
201
+ interface InteractionContext {
202
+ elements: OverlayElements
203
+ stateRef: OverlayPrefsRef
204
+ }
205
+
206
+ function installWidthResize(ctx: InteractionContext): void {
207
+ const { resizeHandle, container, iframe } = ctx.elements
208
+ let startX = 0
209
+ let startWidth = 0
210
+
211
+ const onMove = (event: PointerEvent): void => {
212
+ // Panel is pinned right, so dragging left (smaller clientX) grows it.
213
+ const next = clampWidth(startWidth + (startX - event.clientX))
214
+ ctx.stateRef.value = { ...ctx.stateRef.value, width: next }
215
+ container.style.width = `${next}px`
216
+ }
217
+
218
+ const onUp = (): void => {
219
+ globalThis.removeEventListener('pointermove', onMove)
220
+ globalThis.removeEventListener('pointerup', onUp)
221
+ // Restore the iframe's pointer capture now the drag is over.
222
+ iframe.style.pointerEvents = ''
223
+ writePrefsDebounced(ctx.stateRef.value)
224
+ }
225
+
226
+ resizeHandle.addEventListener('pointerdown', (event: PointerEvent) => {
227
+ event.preventDefault()
228
+ startX = event.clientX
229
+ startWidth = ctx.stateRef.value.width
230
+ // While dragging, dragging over the iframe would let it swallow the
231
+ // pointer stream, so the parent never sees pointermove/up and the drag
232
+ // sticks. Make the iframe transparent to pointers for the drag's duration.
233
+ iframe.style.pointerEvents = 'none'
234
+ globalThis.addEventListener('pointermove', onMove)
235
+ globalThis.addEventListener('pointerup', onUp)
236
+ })
237
+ }
238
+
239
+ // Close the panel when the user clicks anywhere in the host page outside it.
240
+ // Clicks inside the iframe never reach the host document, so they can't close
241
+ // the panel. Returns a predicate telling whether an outside click just closed
242
+ // the panel: the header launcher toggles on `click`, which follows the same
243
+ // `pointerdown` that closed the panel — without this guard, clicking the
244
+ // launcher while open would close then immediately re-open it.
245
+ function installOutsideClickClose(ctx: InteractionContext): () => boolean {
246
+ let lastCloseAt = Number.NEGATIVE_INFINITY
247
+ document.addEventListener(
248
+ 'pointerdown',
249
+ (event: PointerEvent) => {
250
+ if (!ctx.stateRef.value.isOpen) return
251
+ if (event.composedPath().includes(ctx.elements.container)) return
252
+ lastCloseAt = event.timeStamp
253
+ setOpen(ctx.elements, ctx.stateRef, false)
254
+ },
255
+ { capture: true },
256
+ )
257
+ return (): boolean =>
258
+ performance.now() - lastCloseAt < OVERLAY_OUTSIDE_TOGGLE_SUPPRESS_MS
259
+ }
260
+
261
+ function appendElements(elements: OverlayElements): void {
262
+ elements.container.appendChild(elements.iframe)
263
+ elements.container.appendChild(elements.placeholder)
264
+ elements.container.appendChild(elements.resizeHandle)
265
+ document.body.appendChild(elements.container)
266
+ }
267
+
268
+ interface PlaceholderController {
269
+ show: (kind: PlaceholderKind) => void
270
+ hide: () => void
271
+ recolor: (theme: HostTheme) => void
272
+ }
273
+
274
+ function createPlaceholderController(
275
+ placeholder: HTMLDivElement,
276
+ ): PlaceholderController {
277
+ return {
278
+ show: (kind) => {
279
+ placeholder.innerHTML = placeholderContent(kind)
280
+ placeholder.style.display = 'flex'
281
+ },
282
+ hide: () => {
283
+ placeholder.style.display = 'none'
284
+ },
285
+ recolor: (theme) => {
286
+ placeholder.style.background =
287
+ theme.tokens['--ui-bg'] || backdropFor(theme.mode)
288
+ placeholder.style.color = theme.tokens['--ui-text'] || ''
289
+ },
290
+ }
291
+ }
292
+
293
+ // Repoint the frame at `url`, deferring the placeholder-hide until it paints so a
294
+ // port change reloads the chatbox without flashing the previous dead frame. A
295
+ // no-op reload when the URL is already loaded.
296
+ function createRepointer(
297
+ iframe: HTMLIFrameElement,
298
+ placeholder: PlaceholderController,
299
+ ): (url: string) => void {
300
+ let currentUrl = ''
301
+ let loaded = false
302
+ iframe.addEventListener('load', () => {
303
+ if (iframe.src === '') return
304
+ loaded = true
305
+ placeholder.hide()
306
+ })
307
+ return (url: string): void => {
308
+ if (url === currentUrl && loaded) {
309
+ placeholder.hide()
310
+ return
311
+ }
312
+ currentUrl = url
313
+ loaded = false
314
+ placeholder.show('connecting')
315
+ iframe.src = url
316
+ }
317
+ }
318
+
319
+ function setOpen(
320
+ elements: OverlayElements,
321
+ stateRef: OverlayPrefsRef,
322
+ isOpen: boolean,
323
+ ): void {
324
+ stateRef.value = { ...stateRef.value, isOpen }
325
+ applyPrefs(elements, stateRef.value)
326
+ writePrefsDebounced(stateRef.value)
327
+ }
328
+
329
+ export function injectOverlay(): OverlayHandle | null {
330
+ const isAlreadyInjected = document.getElementById(OVERLAY_DOM_ID) !== null
331
+ if (isAlreadyInjected) return null
332
+ ensureSpinnerKeyframes()
333
+ const elements: OverlayElements = {
334
+ container: buildContainer(),
335
+ iframe: buildIframe(),
336
+ resizeHandle: buildResizeHandle(),
337
+ placeholder: buildPlaceholder(),
338
+ }
339
+ appendElements(elements)
340
+ const placeholder = createPlaceholderController(elements.placeholder)
341
+ const repoint = createRepointer(elements.iframe, placeholder)
342
+ const stateRef = { value: resolveInitialPrefs() }
343
+ applyPrefs(elements, stateRef.value)
344
+ installWidthResize({ elements, stateRef })
345
+ const wasJustClosedByOutsideClick = installOutsideClickClose({
346
+ elements,
347
+ stateRef,
348
+ })
349
+ // Mirror the host's primary palette + light/dark mode into the iframe, and
350
+ // keep the panel backdrop / iframe color-scheme (and the status placeholder)
351
+ // following live mode changes (e.g. an OS-driven dark switch) so no light edge
352
+ // lingers until a reload.
353
+ installThemeBridge(elements.iframe, (theme) => {
354
+ elements.container.style.background =
355
+ theme.tokens['--ui-bg'] || backdropFor(theme.mode)
356
+ elements.iframe.style.colorScheme = theme.mode
357
+ placeholder.recolor(theme)
358
+ })
359
+
360
+ // The chatbox header's close button lives inside the iframe; it asks the
361
+ // host to slide the panel away via postMessage.
362
+ globalThis.addEventListener('message', (event: MessageEvent) => {
363
+ const type = (event.data as MessageData)?.type
364
+ if (typeof type === 'string' && CLOSE_MESSAGE_TYPES.has(type)) {
365
+ setOpen(elements, stateRef, false)
366
+ }
367
+ })
368
+
369
+ const toggle = (): void =>
370
+ setOpen(elements, stateRef, !stateRef.value.isOpen)
371
+
372
+ return {
373
+ toggleOpen: toggle,
374
+ toggleFromLauncher: (): void => {
375
+ const wouldOpen = !stateRef.value.isOpen
376
+ if (wouldOpen && wasJustClosedByOutsideClick()) return
377
+ toggle()
378
+ },
379
+ isOpen: (): boolean => stateRef.value.isOpen,
380
+ repoint,
381
+ showPlaceholder: (kind) => placeholder.show(kind),
382
+ }
383
+ }
@@ -0,0 +1,28 @@
1
+ import { MODAL_OPEN_SELECTOR, TOGGLE_SHORTCUT_KEY } from './constants'
2
+
3
+ export interface InstallToggleShortcutOptions {
4
+ onToggle: () => void
5
+ }
6
+
7
+ function isModalOpen(): boolean {
8
+ return document.querySelector(MODAL_OPEN_SELECTOR) !== null
9
+ }
10
+
11
+ function isToggleCombo(event: KeyboardEvent): boolean {
12
+ const isModifierPressed = event.metaKey || event.ctrlKey
13
+ const isTargetKey = event.key.toLowerCase() === TOGGLE_SHORTCUT_KEY
14
+ // Require shift too — plain ctrl/cmd + k is the DMS search palette.
15
+ return isModifierPressed && event.shiftKey && isTargetKey
16
+ }
17
+
18
+ export function installToggleShortcut(
19
+ options: InstallToggleShortcutOptions,
20
+ ): void {
21
+ const handler = (event: KeyboardEvent): void => {
22
+ if (!isToggleCombo(event)) return
23
+ if (isModalOpen()) return
24
+ event.preventDefault()
25
+ options.onToggle()
26
+ }
27
+ document.addEventListener('keydown', handler)
28
+ }
@@ -0,0 +1,163 @@
1
+ import { createBackoff, nextDelay, resetBackoff } from './backoff'
2
+ import {
3
+ SIDECAR_POLL_DELAYS_MS,
4
+ SIDECAR_STATUS_CONNECTED,
5
+ SIDECAR_STATUS_CONNECTING,
6
+ SIDECAR_STATUS_REVIVING,
7
+ SIDECAR_STATUS_UNAVAILABLE,
8
+ } from './constants'
9
+
10
+ export type SidecarStatus =
11
+ | typeof SIDECAR_STATUS_CONNECTING
12
+ | typeof SIDECAR_STATUS_CONNECTED
13
+ | typeof SIDECAR_STATUS_REVIVING
14
+ | typeof SIDECAR_STATUS_UNAVAILABLE
15
+
16
+ interface SidecarInfo {
17
+ port: number | null
18
+ clientToken: string | null
19
+ isRunning: boolean
20
+ hasGivenUp: boolean
21
+ disabled: boolean
22
+ }
23
+
24
+ export interface SidecarStatusController {
25
+ // Runs the first probe. Returns false when the assistant should not be shown
26
+ // at all — the dms-ai backend route is absent (probe unreachable) or spawning
27
+ // is disabled for this process — so the caller skips injection entirely.
28
+ init: () => Promise<boolean>
29
+ getStatus: () => SidecarStatus
30
+ getPort: () => number | null
31
+ getClientToken: () => string | null
32
+ // Subscribe to status changes; fires immediately with the current status so a
33
+ // late subscriber (overlay/icon injected after init) reflects it right away.
34
+ subscribe: (cb: (status: SidecarStatus) => void) => () => void
35
+ // The host WebSocket dropped: re-probe now to relearn a possibly-new port
36
+ // rather than let the socket keep retrying the dead one.
37
+ reportWsDown: () => void
38
+ dispose: () => void
39
+ }
40
+
41
+ export type FetchSidecarInfo = () => Promise<Partial<SidecarInfo>>
42
+
43
+ async function fetchInfo(
44
+ fetchDetails: FetchSidecarInfo,
45
+ ): Promise<SidecarInfo | null> {
46
+ try {
47
+ const body = await fetchDetails()
48
+ return {
49
+ port: typeof body.port === 'number' ? body.port : null,
50
+ clientToken:
51
+ typeof body.clientToken === 'string' ? body.clientToken : null,
52
+ isRunning: body.isRunning === true,
53
+ hasGivenUp: body.hasGivenUp === true,
54
+ disabled: body.disabled === true,
55
+ }
56
+ } catch {
57
+ return null
58
+ }
59
+ }
60
+
61
+ export function createSidecarStatusController(
62
+ fetchDetails: FetchSidecarInfo,
63
+ ): SidecarStatusController {
64
+ const backoff = createBackoff()
65
+ const subscribers = new Set<(status: SidecarStatus) => void>()
66
+ let status: SidecarStatus = SIDECAR_STATUS_CONNECTING
67
+ let port: number | null = null
68
+ let clientToken: string | null = null
69
+ let pollTimer: ReturnType<typeof setTimeout> | null = null
70
+ let disposed = false
71
+
72
+ function notify(): void {
73
+ for (const cb of subscribers) cb(status)
74
+ }
75
+
76
+ function setStatus(next: SidecarStatus): void {
77
+ if (status === next) return
78
+ status = next
79
+ notify()
80
+ }
81
+
82
+ function schedulePoll(): void {
83
+ if (disposed || pollTimer !== null) return
84
+ const delay = nextDelay(backoff, SIDECAR_POLL_DELAYS_MS)
85
+ pollTimer = setTimeout(() => {
86
+ pollTimer = null
87
+ void poll()
88
+ }, delay)
89
+ }
90
+
91
+ async function poll(): Promise<void> {
92
+ if (disposed) return
93
+ handleInfo(await fetchInfo(fetchDetails))
94
+ }
95
+
96
+ function handleInfo(info: SidecarInfo | null): void {
97
+ if (disposed) return
98
+ // Probe itself failed (backend momentarily unreachable): keep waiting.
99
+ if (info === null) {
100
+ setStatus(SIDECAR_STATUS_REVIVING)
101
+ schedulePoll()
102
+ return
103
+ }
104
+ if (info.hasGivenUp) {
105
+ setStatus(SIDECAR_STATUS_UNAVAILABLE)
106
+ return
107
+ }
108
+ if (info.isRunning && info.port !== null && info.clientToken) {
109
+ port = info.port
110
+ clientToken = info.clientToken
111
+ resetBackoff(backoff)
112
+ // A reviving→connected transition notifies via setStatus; a port change
113
+ // while already connected leaves the status word unchanged, so force a
114
+ // notify there too to drive the overlay/WS onto the new port.
115
+ if (status === SIDECAR_STATUS_CONNECTED) notify()
116
+ else setStatus(SIDECAR_STATUS_CONNECTED)
117
+ return
118
+ }
119
+ // Down but not terminal — the probe already asked the backend to respawn;
120
+ // keep polling until it reports a port.
121
+ setStatus(SIDECAR_STATUS_REVIVING)
122
+ schedulePoll()
123
+ }
124
+
125
+ return {
126
+ init: async (): Promise<boolean> => {
127
+ const info = await fetchInfo(fetchDetails)
128
+ if (info === null) return false
129
+ if (info.disabled) return false
130
+ handleInfo(info)
131
+ return true
132
+ },
133
+ getStatus: () => status,
134
+ getPort: () => port,
135
+ getClientToken: () => clientToken,
136
+ subscribe: (cb) => {
137
+ subscribers.add(cb)
138
+ cb(status)
139
+ return () => {
140
+ subscribers.delete(cb)
141
+ }
142
+ },
143
+ reportWsDown: () => {
144
+ if (disposed) return
145
+ if (status !== SIDECAR_STATUS_CONNECTED) return
146
+ // Surface the drop right away (amber dot + spinner) instead of waiting on
147
+ // the re-probe: that probe awaits a respawn and can take a second or two,
148
+ // during which the icon would otherwise look healthy. poll() flips back
149
+ // to connected once the new port is live.
150
+ resetBackoff(backoff)
151
+ setStatus(SIDECAR_STATUS_REVIVING)
152
+ void poll()
153
+ },
154
+ dispose: () => {
155
+ disposed = true
156
+ if (pollTimer !== null) {
157
+ clearTimeout(pollTimer)
158
+ pollTimer = null
159
+ }
160
+ subscribers.clear()
161
+ },
162
+ }
163
+ }