@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,109 @@
1
+ import { defineDmsPlugin, useDmsRouter } from '#dms-inertia/frontend-module'
2
+ import {
3
+ SIDECAR_HOST_NAME,
4
+ SIDECAR_INFO_PATH,
5
+ SIDECAR_PROBE_HOST,
6
+ SIDECAR_STATUS_CONNECTED,
7
+ SIDECAR_STATUS_REVIVING,
8
+ SIDECAR_STATUS_UNAVAILABLE,
9
+ WS_STATUS_CONNECTED,
10
+ WS_STATUS_CONNECTING,
11
+ } from '../runtime/constants'
12
+ import { installCurrentPageTracker } from '../runtime/current-page'
13
+ import { registerLauncherAction } from '../runtime/header-action'
14
+ import { createHostCommandDispatcher } from '../runtime/host-commands'
15
+ import { installNavigationCompleteEmitter } from '../runtime/navigation-complete'
16
+ import { injectOverlay } from '../runtime/overlay'
17
+ import { installToggleShortcut } from '../runtime/shortcuts'
18
+ import {
19
+ createSidecarStatusController,
20
+ type SidecarStatus,
21
+ } from '../runtime/sidecar-status'
22
+ import { createHostWsClient } from '../runtime/ws-client'
23
+
24
+ function buildIframeUrl(port: number, token: string): string {
25
+ // Seed the chatbox with the host's current light/dark mode so its very first
26
+ // paint matches; the theme bridge keeps it in sync from there on.
27
+ const mode = document.documentElement.classList.contains('dark')
28
+ ? 'dark'
29
+ : 'light'
30
+ return `${SIDECAR_PROBE_HOST}:${port}/?theme=${mode}#token=${encodeURIComponent(token)}`
31
+ }
32
+
33
+ export default defineDmsPlugin(async ({ vueApp }) => {
34
+ if (!import.meta.env.DEV) return
35
+ const { loggedIn } = useUserSession()
36
+ if (!loggedIn.value) return
37
+ const { $authFetch } = useAuthFetch()
38
+ const controller = createSidecarStatusController(() =>
39
+ $authFetch(SIDECAR_INFO_PATH),
40
+ )
41
+ // A null/disabled first probe means the assistant shouldn't appear at all —
42
+ // preserve the previous "no icon, no overlay" behavior in that case.
43
+ if (!(await controller.init())) return
44
+ const overlay = injectOverlay()
45
+ if (overlay === null) return
46
+
47
+ installToggleShortcut({ onToggle: () => overlay.toggleOpen() })
48
+ // Registered unconditionally (even while reviving) so the launcher is present
49
+ // through an outage; opening it surfaces the panel's own status placeholder.
50
+ registerLauncherAction(() => overlay.toggleFromLauncher())
51
+
52
+ const router = useDmsRouter()
53
+ // Host-side dev hot reload. A page the agent just wrote is re-registered
54
+ // asynchronously, so every overlay-driven navigation waits for the committed
55
+ // site layout to serve the route before pushing it.
56
+ const devReload = useDmsDevReload()
57
+ // Serialized so a burst of navigate commands cannot land out of order.
58
+ const dispatchHostCommand = createHostCommandDispatcher({ router, devReload })
59
+ const hostWs = createHostWsClient({
60
+ sidecarHost: SIDECAR_HOST_NAME,
61
+ getPort: () => controller.getPort(),
62
+ getClientToken: () => controller.getClientToken(),
63
+ onMessage: dispatchHostCommand,
64
+ onStatusChange: (status) => {
65
+ // A socket drop means the sidecar likely idle-exited/crashed: ask the
66
+ // controller to re-probe (relearning a possibly-new port) rather than let
67
+ // the socket keep retrying the dead one.
68
+ if (status !== WS_STATUS_CONNECTED && status !== WS_STATUS_CONNECTING) {
69
+ controller.reportWsDown()
70
+ }
71
+ },
72
+ })
73
+
74
+ let firstApply = true
75
+ const applyStatus = (status: SidecarStatus): void => {
76
+ if (status === SIDECAR_STATUS_CONNECTED) {
77
+ const port = controller.getPort()
78
+ const token = controller.getClientToken()
79
+ if (port !== null && token) overlay.repoint(buildIframeUrl(port, token))
80
+ // On the first apply the socket was just created and is connecting to the
81
+ // right port; only later connects (after a drop or port change) need a
82
+ // forced, backoff-skipping reconnect.
83
+ if (!firstApply) hostWs.reconnectNow()
84
+ } else if (status === SIDECAR_STATUS_REVIVING) {
85
+ overlay.showPlaceholder('reviving')
86
+ } else if (status === SIDECAR_STATUS_UNAVAILABLE) {
87
+ overlay.showPlaceholder('unavailable')
88
+ } else {
89
+ overlay.showPlaceholder('connecting')
90
+ }
91
+ firstApply = false
92
+ }
93
+ controller.subscribe(applyStatus)
94
+
95
+ const sendWhenConnected = (msg: unknown): void => {
96
+ if (!hostWs.isConnected()) return
97
+ hostWs.send(msg)
98
+ }
99
+ const stopTracking = installCurrentPageTracker({ send: sendWhenConnected })
100
+ const stopNavigation = installNavigationCompleteEmitter({
101
+ send: sendWhenConnected,
102
+ })
103
+ const stop = (): void => {
104
+ stopTracking()
105
+ stopNavigation()
106
+ }
107
+ vueApp.onUnmount(stop)
108
+ import.meta.hot?.dispose(stop)
109
+ })
@@ -0,0 +1,30 @@
1
+ export interface BackoffState {
2
+ index: number
3
+ }
4
+
5
+ export function createBackoff(): BackoffState {
6
+ return { index: 0 }
7
+ }
8
+
9
+ function readScheduleAt(
10
+ schedule: readonly number[],
11
+ index: number,
12
+ ): number {
13
+ const value = schedule[index]
14
+ if (value === undefined) return 0
15
+ return value
16
+ }
17
+
18
+ export function nextDelay(
19
+ state: BackoffState,
20
+ schedule: readonly number[],
21
+ ): number {
22
+ const cappedIndex = Math.min(state.index, schedule.length - 1)
23
+ const delay = readScheduleAt(schedule, cappedIndex)
24
+ state.index += 1
25
+ return delay
26
+ }
27
+
28
+ export function resetBackoff(state: BackoffState): void {
29
+ state.index = 0
30
+ }
@@ -0,0 +1,66 @@
1
+ export const OVERLAY_DEFAULT_WIDTH_PX = 460
2
+ export const OVERLAY_Z_INDEX = 999_999
3
+ export const OVERLAY_DOM_ID = 'dms-ai-overlay-root'
4
+ export const OVERLAY_IFRAME_ID = 'dms-ai-overlay-iframe'
5
+ export const SIDECAR_INFO_PATH = '/ai/sidecar-info'
6
+ export const SIDECAR_PROBE_PORT = 5010
7
+ export const SIDECAR_PROBE_HOST = 'http://localhost'
8
+ export const OVERLAY_BOX_SHADOW = '0 8px 24px rgba(0, 0, 0, 0.18)'
9
+ export const SIDECAR_HOST_NAME = 'localhost'
10
+ export const WS_HOST_PATH = '/ws/host'
11
+ export const WS_RECONNECT_DELAYS_MS = [
12
+ 1000, 2000, 4000, 8000, 15000, 30000,
13
+ ] as const
14
+ // Sidecar reachability, as seen by the host. `reviving` is the self-healing
15
+ // state (idle-exited / crashed-respawning): the probe loop is relearning the new
16
+ // random port and will repoint the frame. `unavailable` is terminal (the crash
17
+ // budget is spent) and needs a reload.
18
+ export const SIDECAR_STATUS_CONNECTING = 'connecting'
19
+ export const SIDECAR_STATUS_CONNECTED = 'connected'
20
+ export const SIDECAR_STATUS_REVIVING = 'reviving'
21
+ export const SIDECAR_STATUS_UNAVAILABLE = 'unavailable'
22
+ // Re-probe cadence while reviving. Each probe also revives the daemon backend
23
+ // side, so this doubles as the respawn heartbeat; capped so a genuinely stuck
24
+ // sidecar is polled sparingly rather than hammered.
25
+ export const SIDECAR_POLL_DELAYS_MS = [
26
+ 500, 1000, 2000, 4000, 8000,
27
+ ] as const
28
+ export const OVERLAY_PLACEHOLDER_ID = 'dms-ai-overlay-placeholder'
29
+ export const PLACEHOLDER_CONNECTING_TEXT = 'Connecting the assistant…'
30
+ export const PLACEHOLDER_REVIVING_TEXT = 'Reconnecting the assistant…'
31
+ export const PLACEHOLDER_UNAVAILABLE_TITLE = 'Assistant unavailable'
32
+ export const PLACEHOLDER_UNAVAILABLE_TEXT = 'Reload the page to try again.'
33
+
34
+ export const WS_STATUS_CONNECTING = 'connecting'
35
+ export const WS_STATUS_CONNECTED = 'connected'
36
+ export const WS_STATUS_DISCONNECTED = 'disconnected'
37
+ export const WS_STATUS_RECONNECTING = 'reconnecting'
38
+ export const WS_PROTOCOL_SECURE = 'wss:'
39
+ export const WS_PROTOCOL_INSECURE = 'ws:'
40
+ export const PAGE_PROTOCOL_SECURE = 'https:'
41
+ export const HOST_ROLE = 'host'
42
+ export const HELLO_MESSAGE_TYPE = 'hello'
43
+ export const LOG_PREFIX = '[dms-ai]'
44
+ export const HOST_STATE_UPDATE_TYPE = 'host_state_update'
45
+ export const HOST_NAVIGATION_COMPLETE_TYPE = 'host_navigation_complete'
46
+ export const HOST_COMMAND_NAVIGATE_TYPE = 'host_command_navigate'
47
+ export const OVERLAY_RESIZE_HANDLE_SIZE_PX = 6
48
+ export const OVERLAY_MIN_WIDTH_PX = 320
49
+ export const OVERLAY_PREFS_STORAGE_KEY = 'dms-ai:overlay-prefs'
50
+ export const OVERLAY_OUTSIDE_TOGGLE_SUPPRESS_MS = 300
51
+ export const OVERLAY_PREFS_DEBOUNCE_MS = 300
52
+ export const TOGGLE_SHORTCUT_KEY = 'k'
53
+ export const MODAL_OPEN_SELECTOR = '[role="dialog"][aria-modal="true"]'
54
+
55
+ // Generic header-action registry exposed by the DMS core (dms-back). Any module
56
+ // can push a button by writing to this shared Nuxt state key; the core renders
57
+ // it with no knowledge of who registered it.
58
+ export const HEADER_ACTIONS_STATE_KEY = 'dms:header-actions'
59
+ export const LAUNCHER_ACTION_ID = 'dms-ai-launcher'
60
+ export const LAUNCHER_ICON = 'i-ph-robot'
61
+ export const LAUNCHER_LABEL = 'AI assistant'
62
+ export const LAUNCHER_ORDER = 50
63
+ // Toggle shortcut, shown in the launcher tooltip. The combo is meta/ctrl + shift
64
+ // + k, so render it the way each platform expects.
65
+ export const TOGGLE_SHORTCUT_LABEL_MAC = '⌘⇧K'
66
+ export const TOGGLE_SHORTCUT_LABEL_OTHER = 'Ctrl+Shift+K'
@@ -0,0 +1,28 @@
1
+ import { router } from '@inertiajs/vue3'
2
+ import { HOST_STATE_UPDATE_TYPE } from './constants'
3
+
4
+ interface CurrentPagePayload {
5
+ path: string
6
+ }
7
+
8
+ interface HostStateUpdateMessage {
9
+ type: typeof HOST_STATE_UPDATE_TYPE
10
+ currentPage: CurrentPagePayload
11
+ }
12
+
13
+ interface InstallCurrentPageTrackerOptions {
14
+ send: (msg: HostStateUpdateMessage) => void
15
+ }
16
+
17
+ export function installCurrentPageTracker(
18
+ options: InstallCurrentPageTrackerOptions,
19
+ ): () => void {
20
+ const pushFor = (url: string): void =>
21
+ options.send({
22
+ type: HOST_STATE_UPDATE_TYPE,
23
+ currentPage: { path: new URL(url, window.location.origin).pathname },
24
+ })
25
+ const stop = router.on('navigate', (event) => pushFor(event.detail.page.url))
26
+ pushFor(window.location.href)
27
+ return stop
28
+ }
@@ -0,0 +1,64 @@
1
+ import { useDmsState as useState } from '#dms-inertia/frontend-module'
2
+ import {
3
+ HEADER_ACTIONS_STATE_KEY,
4
+ LAUNCHER_ACTION_ID,
5
+ LAUNCHER_ICON,
6
+ LAUNCHER_LABEL,
7
+ LAUNCHER_ORDER,
8
+ TOGGLE_SHORTCUT_LABEL_MAC,
9
+ TOGGLE_SHORTCUT_LABEL_OTHER,
10
+ } from './constants'
11
+
12
+ interface UserAgentData {
13
+ platform?: string
14
+ }
15
+
16
+ interface NavigatorUserAgentData {
17
+ userAgentData?: UserAgentData
18
+ }
19
+
20
+ // The shortcut combo accepts meta OR ctrl, so show the modifier each platform
21
+ // actually uses (⌘ on Apple, Ctrl elsewhere).
22
+ function isApplePlatform(): boolean {
23
+ const nav = globalThis.navigator as
24
+ | (Navigator & NavigatorUserAgentData)
25
+ | undefined
26
+ if (nav === undefined) return false
27
+ const platform = nav.userAgentData?.platform ?? nav.platform ?? ''
28
+ return /mac|iphone|ipad|ipod/i.test(platform)
29
+ }
30
+
31
+ function launcherLabel(): string {
32
+ const shortcut = isApplePlatform()
33
+ ? TOGGLE_SHORTCUT_LABEL_MAC
34
+ : TOGGLE_SHORTCUT_LABEL_OTHER
35
+ return `${LAUNCHER_LABEL} (${shortcut})`
36
+ }
37
+
38
+ // Our view of the shared header-action shape the DMS core renders. Declared
39
+ // locally so this layer has no build-time dependency on the core layer; the
40
+ // only contract is the state key and these fields.
41
+ interface HeaderAction {
42
+ id: string
43
+ icon: string
44
+ label: string
45
+ onSelect: () => void
46
+ order?: number
47
+ }
48
+
49
+ // Register the assistant launcher in the core's generic header-action registry.
50
+ // Idempotent so a re-running plugin can't add a duplicate button.
51
+ export function registerLauncherAction(onSelect: () => void): void {
52
+ const actions = useState<HeaderAction[]>(HEADER_ACTIONS_STATE_KEY, () => [])
53
+ if (actions.value.some((action) => action.id === LAUNCHER_ACTION_ID)) return
54
+ actions.value = [
55
+ ...actions.value,
56
+ {
57
+ id: LAUNCHER_ACTION_ID,
58
+ icon: LAUNCHER_ICON,
59
+ label: launcherLabel(),
60
+ order: LAUNCHER_ORDER,
61
+ onSelect,
62
+ },
63
+ ]
64
+ }
@@ -0,0 +1,127 @@
1
+ import { HOST_COMMAND_NAVIGATE_TYPE, LOG_PREFIX } from './constants'
2
+
3
+ interface RouterLike {
4
+ push: (path: string) => unknown
5
+ }
6
+
7
+ /**
8
+ * The slice of the host's `useDmsDevReload()` composable this module needs:
9
+ * resolve `true` once the committed site layout serves `path`, `false` on
10
+ * timeout.
11
+ */
12
+ export interface DevReloadWaiter {
13
+ awaitRoute: (path: string) => Promise<boolean>
14
+ }
15
+
16
+ export interface HostCommandContext {
17
+ router: RouterLike
18
+ devReload: DevReloadWaiter
19
+ }
20
+
21
+ export interface HostCommandNavigateMessage {
22
+ type: typeof HOST_COMMAND_NAVIGATE_TYPE
23
+ path: string
24
+ }
25
+
26
+ export type HostCommandMessage = HostCommandNavigateMessage
27
+
28
+ export type HostCommandDispatcher = (raw: unknown) => void
29
+
30
+ /**
31
+ * Commands run one at a time on `tail`. `nextPath` holds the newest navigation
32
+ * target still waiting for a slot, so a burst collapses into a single trip to
33
+ * the latest path instead of racing and landing on the oldest one.
34
+ */
35
+ interface DispatcherState {
36
+ ctx: HostCommandContext
37
+ tail: Promise<void>
38
+ nextPath: string | null
39
+ }
40
+
41
+ type HostCommandHandler = (
42
+ msg: HostCommandMessage,
43
+ state: DispatcherState,
44
+ ) => void
45
+
46
+ function enqueue(state: DispatcherState, job: () => Promise<void>): void {
47
+ state.tail = state.tail.then(job).catch((err: unknown) => {
48
+ console.error(`${LOG_PREFIX} host command failed`, err)
49
+ })
50
+ }
51
+
52
+ // The agent asks to navigate right after writing the page, while the host is
53
+ // still re-registering modules: the route can be momentarily unserved and
54
+ // pushing now would flash a 404. Wait for the committed layout to serve it.
55
+ // Navigate whatever the wait reports — a lapsed deadline or a broken waiter
56
+ // must not strand the user on the previous page; a genuinely missing page then
57
+ // 404s as it did before.
58
+ async function awaitRouteThenPush(
59
+ path: string,
60
+ ctx: HostCommandContext,
61
+ ): Promise<void> {
62
+ try {
63
+ await ctx.devReload.awaitRoute(path)
64
+ } catch (err: unknown) {
65
+ console.error(`${LOG_PREFIX} dev reload wait failed for ${path}`, err)
66
+ }
67
+ void ctx.router.push(path)
68
+ }
69
+
70
+ async function runNavigate(state: DispatcherState): Promise<void> {
71
+ const path = state.nextPath
72
+ // A newer navigate already claimed this slot's target: this one is superseded
73
+ // and must not drag the user back to the older path.
74
+ if (path === null) return
75
+ state.nextPath = null
76
+ await awaitRouteThenPush(path, state.ctx)
77
+ }
78
+
79
+ function handleNavigate(
80
+ msg: HostCommandMessage,
81
+ state: DispatcherState,
82
+ ): void {
83
+ if (msg.type !== HOST_COMMAND_NAVIGATE_TYPE) return
84
+ state.nextPath = msg.path
85
+ enqueue(state, () => runNavigate(state))
86
+ }
87
+
88
+ const COMMAND_HANDLERS: Record<string, HostCommandHandler> = {
89
+ [HOST_COMMAND_NAVIGATE_TYPE]: handleNavigate,
90
+ }
91
+
92
+ function isHostCommand(value: unknown): value is HostCommandMessage {
93
+ if (value === null || typeof value !== 'object') return false
94
+ const candidate = value as { type?: unknown }
95
+ return typeof candidate.type === 'string'
96
+ }
97
+
98
+ function parseRaw(raw: unknown): HostCommandMessage | null {
99
+ if (typeof raw !== 'string') return null
100
+ try {
101
+ const parsed = JSON.parse(raw) as unknown
102
+ if (!isHostCommand(parsed)) return null
103
+ return parsed
104
+ } catch {
105
+ return null
106
+ }
107
+ }
108
+
109
+ export function createHostCommandDispatcher(
110
+ ctx: HostCommandContext,
111
+ ): HostCommandDispatcher {
112
+ const state: DispatcherState = {
113
+ ctx,
114
+ tail: Promise.resolve(),
115
+ nextPath: null,
116
+ }
117
+ return (raw: unknown): void => {
118
+ const msg = parseRaw(raw)
119
+ if (msg === null) return
120
+ const handler = COMMAND_HANDLERS[msg.type]
121
+ if (handler === undefined) {
122
+ console.warn(`${LOG_PREFIX} unknown host command type=${msg.type}`)
123
+ return
124
+ }
125
+ handler(msg, state)
126
+ }
127
+ }
@@ -0,0 +1,22 @@
1
+ import { router } from '@inertiajs/vue3'
2
+ import { HOST_NAVIGATION_COMPLETE_TYPE } from './constants'
3
+
4
+ export interface HostNavigationCompleteMessage {
5
+ type: typeof HOST_NAVIGATION_COMPLETE_TYPE
6
+ path: string
7
+ }
8
+
9
+ interface InstallNavigationCompleteEmitterOptions {
10
+ send: (msg: HostNavigationCompleteMessage) => void
11
+ }
12
+
13
+ export function installNavigationCompleteEmitter(
14
+ options: InstallNavigationCompleteEmitterOptions,
15
+ ): () => void {
16
+ return router.on('navigate', (event) =>
17
+ options.send({
18
+ type: HOST_NAVIGATION_COMPLETE_TYPE,
19
+ path: new URL(event.detail.page.url, window.location.origin).pathname,
20
+ }),
21
+ )
22
+ }
@@ -0,0 +1,89 @@
1
+ import {
2
+ OVERLAY_PREFS_DEBOUNCE_MS,
3
+ OVERLAY_PREFS_STORAGE_KEY,
4
+ } from './constants'
5
+
6
+ export interface OverlayPrefs {
7
+ width: number
8
+ isOpen: boolean
9
+ }
10
+
11
+ let pendingTimer: ReturnType<typeof setTimeout> | null = null
12
+ let pendingPrefs: OverlayPrefs | null = null
13
+
14
+ function tryReadStorage(): string | null {
15
+ try {
16
+ return globalThis.localStorage.getItem(OVERLAY_PREFS_STORAGE_KEY)
17
+ } catch {
18
+ return null
19
+ }
20
+ }
21
+
22
+ function tryWriteStorage(payload: string): void {
23
+ try {
24
+ globalThis.localStorage.setItem(OVERLAY_PREFS_STORAGE_KEY, payload)
25
+ } catch {
26
+ return
27
+ }
28
+ }
29
+
30
+ function isFiniteNumber(value: unknown): value is number {
31
+ return typeof value === 'number' && Number.isFinite(value)
32
+ }
33
+
34
+ function isValidPrefs(value: unknown): value is OverlayPrefs {
35
+ if (value === null || typeof value !== 'object') return false
36
+ const candidate = value as Record<string, unknown>
37
+ if (!isFiniteNumber(candidate.width)) return false
38
+ if (typeof candidate.isOpen !== 'boolean') return false
39
+ return true
40
+ }
41
+
42
+ function parsePrefs(raw: string): OverlayPrefs | null {
43
+ try {
44
+ const parsed: unknown = JSON.parse(raw)
45
+ return isValidPrefs(parsed) ? parsed : null
46
+ } catch {
47
+ return null
48
+ }
49
+ }
50
+
51
+ export function readPrefs(): OverlayPrefs | null {
52
+ const raw = tryReadStorage()
53
+ if (raw === null) return null
54
+ return parsePrefs(raw)
55
+ }
56
+
57
+ function flushPending(): void {
58
+ if (pendingPrefs === null) return
59
+ tryWriteStorage(JSON.stringify(pendingPrefs))
60
+ pendingPrefs = null
61
+ pendingTimer = null
62
+ }
63
+
64
+ export function writePrefsDebounced(prefs: OverlayPrefs): void {
65
+ pendingPrefs = prefs
66
+ if (pendingTimer !== null) clearTimeout(pendingTimer)
67
+ pendingTimer = setTimeout(flushPending, OVERLAY_PREFS_DEBOUNCE_MS)
68
+ }
69
+
70
+ export function flushPrefsForTesting(): void {
71
+ if (pendingTimer !== null) {
72
+ clearTimeout(pendingTimer)
73
+ pendingTimer = null
74
+ }
75
+ flushPending()
76
+ }
77
+
78
+ export function resetPrefsForTesting(): void {
79
+ if (pendingTimer !== null) {
80
+ clearTimeout(pendingTimer)
81
+ pendingTimer = null
82
+ }
83
+ pendingPrefs = null
84
+ try {
85
+ globalThis.localStorage.removeItem(OVERLAY_PREFS_STORAGE_KEY)
86
+ } catch {
87
+ return
88
+ }
89
+ }