@agimon-ai/doompi-web 0.0.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +196 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +1 -0
  4. package/dist/bin/serve.cjs +3 -0
  5. package/dist/bin/serve.cjs.map +1 -0
  6. package/dist/bin/serve.d.cts +1 -0
  7. package/dist/bin/serve.d.mts +1 -0
  8. package/dist/bin/serve.mjs +3 -0
  9. package/dist/bin/serve.mjs.map +1 -0
  10. package/dist/bundler.cjs +1 -0
  11. package/dist/bundler.d.cts +2 -0
  12. package/dist/bundler.d.mts +2 -0
  13. package/dist/bundler.mjs +1 -0
  14. package/dist/index.cjs +1 -0
  15. package/dist/index.d.cts +7 -0
  16. package/dist/index.d.mts +7 -0
  17. package/dist/index.mjs +1 -0
  18. package/dist/src/adapters/authRoutes.cjs +2 -0
  19. package/dist/src/adapters/authRoutes.cjs.map +1 -0
  20. package/dist/src/adapters/authRoutes.mjs +2 -0
  21. package/dist/src/adapters/authRoutes.mjs.map +1 -0
  22. package/dist/src/adapters/bundledServer.cjs +2 -0
  23. package/dist/src/adapters/bundledServer.cjs.map +1 -0
  24. package/dist/src/adapters/bundledServer.mjs +2 -0
  25. package/dist/src/adapters/bundledServer.mjs.map +1 -0
  26. package/dist/src/adapters/directoryListing.cjs +2 -0
  27. package/dist/src/adapters/directoryListing.cjs.map +1 -0
  28. package/dist/src/adapters/directoryListing.mjs +2 -0
  29. package/dist/src/adapters/directoryListing.mjs.map +1 -0
  30. package/dist/src/adapters/directorySearch.cjs +3 -0
  31. package/dist/src/adapters/directorySearch.cjs.map +1 -0
  32. package/dist/src/adapters/directorySearch.mjs +3 -0
  33. package/dist/src/adapters/directorySearch.mjs.map +1 -0
  34. package/dist/src/adapters/gitStatus.cjs +2 -0
  35. package/dist/src/adapters/gitStatus.cjs.map +1 -0
  36. package/dist/src/adapters/gitStatus.mjs +2 -0
  37. package/dist/src/adapters/gitStatus.mjs.map +1 -0
  38. package/dist/src/adapters/httpServer.cjs +2 -0
  39. package/dist/src/adapters/httpServer.cjs.map +1 -0
  40. package/dist/src/adapters/httpServer.d.cts +14 -0
  41. package/dist/src/adapters/httpServer.d.cts.map +1 -0
  42. package/dist/src/adapters/httpServer.d.mts +14 -0
  43. package/dist/src/adapters/httpServer.d.mts.map +1 -0
  44. package/dist/src/adapters/httpServer.mjs +2 -0
  45. package/dist/src/adapters/httpServer.mjs.map +1 -0
  46. package/dist/src/adapters/hubProbe.cjs +2 -0
  47. package/dist/src/adapters/hubProbe.cjs.map +1 -0
  48. package/dist/src/adapters/hubProbe.mjs +2 -0
  49. package/dist/src/adapters/hubProbe.mjs.map +1 -0
  50. package/dist/src/adapters/packageApiProxy.cjs +2 -0
  51. package/dist/src/adapters/packageApiProxy.cjs.map +1 -0
  52. package/dist/src/adapters/packageApiProxy.mjs +2 -0
  53. package/dist/src/adapters/packageApiProxy.mjs.map +1 -0
  54. package/dist/src/adapters/piHubService.cjs +2 -0
  55. package/dist/src/adapters/piHubService.cjs.map +1 -0
  56. package/dist/src/adapters/piHubService.mjs +2 -0
  57. package/dist/src/adapters/piHubService.mjs.map +1 -0
  58. package/dist/src/adapters/piWebSocketListener.cjs +2 -0
  59. package/dist/src/adapters/piWebSocketListener.cjs.map +1 -0
  60. package/dist/src/adapters/piWebSocketListener.mjs +2 -0
  61. package/dist/src/adapters/piWebSocketListener.mjs.map +1 -0
  62. package/dist/src/adapters/providerAuth.cjs +2 -0
  63. package/dist/src/adapters/providerAuth.cjs.map +1 -0
  64. package/dist/src/adapters/providerAuth.mjs +2 -0
  65. package/dist/src/adapters/providerAuth.mjs.map +1 -0
  66. package/dist/src/adapters/registryWatcher.cjs +2 -0
  67. package/dist/src/adapters/registryWatcher.cjs.map +1 -0
  68. package/dist/src/adapters/registryWatcher.mjs +2 -0
  69. package/dist/src/adapters/registryWatcher.mjs.map +1 -0
  70. package/dist/src/adapters/serverSpawner.cjs +2 -0
  71. package/dist/src/adapters/serverSpawner.cjs.map +1 -0
  72. package/dist/src/adapters/serverSpawner.mjs +2 -0
  73. package/dist/src/adapters/serverSpawner.mjs.map +1 -0
  74. package/dist/src/adapters/sessionFiles.cjs +3 -0
  75. package/dist/src/adapters/sessionFiles.cjs.map +1 -0
  76. package/dist/src/adapters/sessionFiles.mjs +3 -0
  77. package/dist/src/adapters/sessionFiles.mjs.map +1 -0
  78. package/dist/src/adapters/sessionHub.cjs +2 -0
  79. package/dist/src/adapters/sessionHub.cjs.map +1 -0
  80. package/dist/src/adapters/sessionHub.mjs +2 -0
  81. package/dist/src/adapters/sessionHub.mjs.map +1 -0
  82. package/dist/src/adapters/sessionSocketClient.cjs +2 -0
  83. package/dist/src/adapters/sessionSocketClient.cjs.map +1 -0
  84. package/dist/src/adapters/sessionSocketClient.mjs +2 -0
  85. package/dist/src/adapters/sessionSocketClient.mjs.map +1 -0
  86. package/dist/src/adapters/syncGuard.cjs +2 -0
  87. package/dist/src/adapters/syncGuard.cjs.map +1 -0
  88. package/dist/src/adapters/syncGuard.mjs +2 -0
  89. package/dist/src/adapters/syncGuard.mjs.map +1 -0
  90. package/dist/src/adapters/threadJournals.cjs +2 -0
  91. package/dist/src/adapters/threadJournals.cjs.map +1 -0
  92. package/dist/src/adapters/threadJournals.mjs +2 -0
  93. package/dist/src/adapters/threadJournals.mjs.map +1 -0
  94. package/dist/src/adapters/webBundler.cjs +2 -0
  95. package/dist/src/adapters/webBundler.cjs.map +1 -0
  96. package/dist/src/adapters/webBundler.d.cts +28 -0
  97. package/dist/src/adapters/webBundler.d.cts.map +1 -0
  98. package/dist/src/adapters/webBundler.d.mts +28 -0
  99. package/dist/src/adapters/webBundler.d.mts.map +1 -0
  100. package/dist/src/adapters/webBundler.mjs +2 -0
  101. package/dist/src/adapters/webBundler.mjs.map +1 -0
  102. package/dist/src/adapters/webHubPluginLoader.cjs +2 -0
  103. package/dist/src/adapters/webHubPluginLoader.cjs.map +1 -0
  104. package/dist/src/adapters/webHubPluginLoader.mjs +2 -0
  105. package/dist/src/adapters/webHubPluginLoader.mjs.map +1 -0
  106. package/dist/src/adapters/webHubPlugins.generated.cjs +2 -0
  107. package/dist/src/adapters/webHubPlugins.generated.cjs.map +1 -0
  108. package/dist/src/adapters/webHubPlugins.generated.mjs +2 -0
  109. package/dist/src/adapters/webHubPlugins.generated.mjs.map +1 -0
  110. package/dist/src/adapters/webPluginGenerate.cjs +4 -0
  111. package/dist/src/adapters/webPluginGenerate.cjs.map +1 -0
  112. package/dist/src/adapters/webPluginGenerate.mjs +4 -0
  113. package/dist/src/adapters/webPluginGenerate.mjs.map +1 -0
  114. package/dist/src/adapters/webPluginScan.cjs +2 -0
  115. package/dist/src/adapters/webPluginScan.cjs.map +1 -0
  116. package/dist/src/adapters/webPluginScan.mjs +2 -0
  117. package/dist/src/adapters/webPluginScan.mjs.map +1 -0
  118. package/dist/src/adapters/webPluginVite.cjs +2 -0
  119. package/dist/src/adapters/webPluginVite.cjs.map +1 -0
  120. package/dist/src/adapters/webPluginVite.mjs +2 -0
  121. package/dist/src/adapters/webPluginVite.mjs.map +1 -0
  122. package/dist/src/services/directoryMatch.cjs +2 -0
  123. package/dist/src/services/directoryMatch.cjs.map +1 -0
  124. package/dist/src/services/directoryMatch.mjs +2 -0
  125. package/dist/src/services/directoryMatch.mjs.map +1 -0
  126. package/dist/src/services/fileMatch.cjs +2 -0
  127. package/dist/src/services/fileMatch.cjs.map +1 -0
  128. package/dist/src/services/fileMatch.mjs +2 -0
  129. package/dist/src/services/fileMatch.mjs.map +1 -0
  130. package/dist/src/services/fileMedia.cjs +2 -0
  131. package/dist/src/services/fileMedia.cjs.map +1 -0
  132. package/dist/src/services/fileMedia.mjs +2 -0
  133. package/dist/src/services/fileMedia.mjs.map +1 -0
  134. package/dist/src/services/frameRing.cjs +2 -0
  135. package/dist/src/services/frameRing.cjs.map +1 -0
  136. package/dist/src/services/frameRing.mjs +2 -0
  137. package/dist/src/services/frameRing.mjs.map +1 -0
  138. package/dist/src/services/journalTail.cjs +3 -0
  139. package/dist/src/services/journalTail.cjs.map +1 -0
  140. package/dist/src/services/journalTail.mjs +3 -0
  141. package/dist/src/services/journalTail.mjs.map +1 -0
  142. package/dist/src/services/loginFlow.cjs +2 -0
  143. package/dist/src/services/loginFlow.cjs.map +1 -0
  144. package/dist/src/services/loginFlow.mjs +2 -0
  145. package/dist/src/services/loginFlow.mjs.map +1 -0
  146. package/dist/src/services/registryStore.cjs +2 -0
  147. package/dist/src/services/registryStore.cjs.map +1 -0
  148. package/dist/src/services/registryStore.mjs +2 -0
  149. package/dist/src/services/registryStore.mjs.map +1 -0
  150. package/dist/src/services/retryPolicy.cjs +2 -0
  151. package/dist/src/services/retryPolicy.cjs.map +1 -0
  152. package/dist/src/services/retryPolicy.mjs +2 -0
  153. package/dist/src/services/retryPolicy.mjs.map +1 -0
  154. package/dist/src/services/serveOptions.cjs +2 -0
  155. package/dist/src/services/serveOptions.cjs.map +1 -0
  156. package/dist/src/services/serveOptions.d.cts +21 -0
  157. package/dist/src/services/serveOptions.d.cts.map +1 -0
  158. package/dist/src/services/serveOptions.d.mts +21 -0
  159. package/dist/src/services/serveOptions.d.mts.map +1 -0
  160. package/dist/src/services/serveOptions.mjs +2 -0
  161. package/dist/src/services/serveOptions.mjs.map +1 -0
  162. package/dist/src/services/sessionFraming.cjs +4 -0
  163. package/dist/src/services/sessionFraming.cjs.map +1 -0
  164. package/dist/src/services/sessionFraming.mjs +4 -0
  165. package/dist/src/services/sessionFraming.mjs.map +1 -0
  166. package/dist/src/services/sessionPresence.cjs +2 -0
  167. package/dist/src/services/sessionPresence.cjs.map +1 -0
  168. package/dist/src/services/sessionPresence.mjs +2 -0
  169. package/dist/src/services/sessionPresence.mjs.map +1 -0
  170. package/dist/src/services/staticAssets.cjs +2 -0
  171. package/dist/src/services/staticAssets.cjs.map +1 -0
  172. package/dist/src/services/staticAssets.mjs +2 -0
  173. package/dist/src/services/staticAssets.mjs.map +1 -0
  174. package/dist/src/services/webDevRoots.cjs +2 -0
  175. package/dist/src/services/webDevRoots.cjs.map +1 -0
  176. package/dist/src/services/webDevRoots.mjs +2 -0
  177. package/dist/src/services/webDevRoots.mjs.map +1 -0
  178. package/dist/src/services/webPluginManifest.cjs +2 -0
  179. package/dist/src/services/webPluginManifest.cjs.map +1 -0
  180. package/dist/src/services/webPluginManifest.mjs +2 -0
  181. package/dist/src/services/webPluginManifest.mjs.map +1 -0
  182. package/dist/src/types/auth.cjs +2 -0
  183. package/dist/src/types/auth.cjs.map +1 -0
  184. package/dist/src/types/auth.d.cts +94 -0
  185. package/dist/src/types/auth.d.cts.map +1 -0
  186. package/dist/src/types/auth.d.mts +94 -0
  187. package/dist/src/types/auth.d.mts.map +1 -0
  188. package/dist/src/types/auth.mjs +2 -0
  189. package/dist/src/types/auth.mjs.map +1 -0
  190. package/dist/src/types/bridge.d.cts +31 -0
  191. package/dist/src/types/bridge.d.cts.map +1 -0
  192. package/dist/src/types/bridge.d.mts +31 -0
  193. package/dist/src/types/bridge.d.mts.map +1 -0
  194. package/dist/src/types/hub.cjs +2 -0
  195. package/dist/src/types/hub.cjs.map +1 -0
  196. package/dist/src/types/hub.d.cts +58 -0
  197. package/dist/src/types/hub.d.cts.map +1 -0
  198. package/dist/src/types/hub.d.mts +58 -0
  199. package/dist/src/types/hub.d.mts.map +1 -0
  200. package/dist/src/types/hub.mjs +2 -0
  201. package/dist/src/types/hub.mjs.map +1 -0
  202. package/dist/src/types/media.cjs +2 -0
  203. package/dist/src/types/media.cjs.map +1 -0
  204. package/dist/src/types/media.mjs +2 -0
  205. package/dist/src/types/media.mjs.map +1 -0
  206. package/dist/src/types/registry.cjs +1 -0
  207. package/dist/src/types/registry.d.cts +2 -0
  208. package/dist/src/types/registry.d.mts +2 -0
  209. package/dist/src/types/registry.mjs +1 -0
  210. package/dist/src/types/session.cjs +2 -0
  211. package/dist/src/types/session.cjs.map +1 -0
  212. package/dist/src/types/session.d.cts +24 -0
  213. package/dist/src/types/session.d.cts.map +1 -0
  214. package/dist/src/types/session.d.mts +24 -0
  215. package/dist/src/types/session.d.mts.map +1 -0
  216. package/dist/src/types/session.mjs +2 -0
  217. package/dist/src/types/session.mjs.map +1 -0
  218. package/dist/web/apple-touch-icon.png +0 -0
  219. package/dist/web/assets/index-Cg52ngKv.css +2 -0
  220. package/dist/web/assets/index-DQO7NTIP.js +90 -0
  221. package/dist/web/assets/index-DQO7NTIP.js.map +1 -0
  222. package/dist/web/favicon-16.png +0 -0
  223. package/dist/web/favicon-32.png +0 -0
  224. package/dist/web/favicon.ico +0 -0
  225. package/dist/web/favicon.svg +16 -0
  226. package/dist/web/icon-512.png +0 -0
  227. package/dist/web/index.html +17 -0
  228. package/llms.txt +8 -0
  229. package/package.json +106 -0
  230. package/src/adapters/authRoutes.ts +95 -0
  231. package/src/adapters/bundledServer.ts +64 -0
  232. package/src/adapters/directoryListing.ts +127 -0
  233. package/src/adapters/directorySearch.ts +124 -0
  234. package/src/adapters/gitStatus.ts +27 -0
  235. package/src/adapters/httpServer.ts +556 -0
  236. package/src/adapters/hubProbe.ts +27 -0
  237. package/src/adapters/packageApiProxy.ts +60 -0
  238. package/src/adapters/piHubService.ts +129 -0
  239. package/src/adapters/piWebSocketListener.ts +98 -0
  240. package/src/adapters/providerAuth.ts +181 -0
  241. package/src/adapters/registryWatcher.ts +112 -0
  242. package/src/adapters/serverSpawner.ts +181 -0
  243. package/src/adapters/sessionFiles.ts +99 -0
  244. package/src/adapters/sessionHub.ts +567 -0
  245. package/src/adapters/sessionSocketClient.ts +105 -0
  246. package/src/adapters/syncGuard.ts +119 -0
  247. package/src/adapters/threadJournals.ts +184 -0
  248. package/src/adapters/webBundler.ts +83 -0
  249. package/src/adapters/webHubPluginLoader.ts +67 -0
  250. package/src/adapters/webHubPlugins.generated.ts +4 -0
  251. package/src/adapters/webPluginGenerate.ts +172 -0
  252. package/src/adapters/webPluginScan.ts +62 -0
  253. package/src/adapters/webPluginVite.ts +51 -0
  254. package/src/bin/serve.ts +64 -0
  255. package/src/exports/bundler.ts +1 -0
  256. package/src/exports/index.ts +9 -0
  257. package/src/services/directoryMatch.ts +71 -0
  258. package/src/services/fileMatch.ts +37 -0
  259. package/src/services/fileMedia.ts +27 -0
  260. package/src/services/frameRing.ts +33 -0
  261. package/src/services/journalTail.ts +47 -0
  262. package/src/services/loginFlow.ts +144 -0
  263. package/src/services/registryStore.ts +83 -0
  264. package/src/services/retryPolicy.ts +15 -0
  265. package/src/services/serveOptions.ts +63 -0
  266. package/src/services/sessionFraming.ts +32 -0
  267. package/src/services/sessionPresence.ts +131 -0
  268. package/src/services/staticAssets.ts +37 -0
  269. package/src/services/webDevRoots.ts +36 -0
  270. package/src/services/webPluginManifest.ts +149 -0
  271. package/src/types/auth.ts +118 -0
  272. package/src/types/bridge.ts +41 -0
  273. package/src/types/hub.ts +290 -0
  274. package/src/types/media.ts +38 -0
  275. package/src/types/registry.ts +2 -0
  276. package/src/types/session.ts +36 -0
  277. package/src/web/app/Providers.tsx +46 -0
  278. package/src/web/app/protocolRuntime.ts +106 -0
  279. package/src/web/app/sessionRuntime.ts +189 -0
  280. package/src/web/app/webPlugins.generated.ts +4 -0
  281. package/src/web/components/MascotMark.tsx +46 -0
  282. package/src/web/components/PluginSurface.tsx +10 -0
  283. package/src/web/components/ToolRendererBoundary.tsx +32 -0
  284. package/src/web/features/activity/ActivityDock.tsx +125 -0
  285. package/src/web/features/connection/RefusedCard.tsx +63 -0
  286. package/src/web/features/dialogs/DialogOverlay.tsx +176 -0
  287. package/src/web/features/leader/CommandPalette.tsx +386 -0
  288. package/src/web/features/selection/SelectionBar.tsx +529 -0
  289. package/src/web/features/session/Composer.tsx +368 -0
  290. package/src/web/features/session/MentionPreviews.tsx +58 -0
  291. package/src/web/features/session/ThreadView.tsx +35 -0
  292. package/src/web/features/session/Timeline.tsx +382 -0
  293. package/src/web/features/session/ToolCard.tsx +79 -0
  294. package/src/web/features/sessions/NewSessionDialog.tsx +222 -0
  295. package/src/web/features/sessions/SessionRail.tsx +411 -0
  296. package/src/web/features/settings/AppearanceSettings.tsx +88 -0
  297. package/src/web/features/settings/LoginFlowDialog.tsx +221 -0
  298. package/src/web/features/settings/PluginSettings.tsx +92 -0
  299. package/src/web/features/settings/ProviderSettings.tsx +238 -0
  300. package/src/web/features/settings/SettingsMenu.tsx +32 -0
  301. package/src/web/features/status/TopBar.tsx +208 -0
  302. package/src/web/index.html +16 -0
  303. package/src/web/lib/authApi.ts +90 -0
  304. package/src/web/lib/commands.ts +61 -0
  305. package/src/web/lib/composition.ts +225 -0
  306. package/src/web/lib/fileMentions.ts +32 -0
  307. package/src/web/lib/hubApi.ts +86 -0
  308. package/src/web/lib/leaderTree.ts +159 -0
  309. package/src/web/lib/piTransport.ts +61 -0
  310. package/src/web/lib/pluginRegistry.ts +488 -0
  311. package/src/web/lib/pluginSlotProps.ts +50 -0
  312. package/src/web/lib/promptFocus.ts +25 -0
  313. package/src/web/lib/protocolTimeline.ts +73 -0
  314. package/src/web/lib/sessionModel.ts +673 -0
  315. package/src/web/lib/sessionSummary.ts +58 -0
  316. package/src/web/lib/settingsSections.ts +21 -0
  317. package/src/web/lib/statusLine.ts +90 -0
  318. package/src/web/lib/threadRenderer.ts +25 -0
  319. package/src/web/lib/toolMessageProps.ts +21 -0
  320. package/src/web/lib/transport.ts +30 -0
  321. package/src/web/lib/wsClient.ts +73 -0
  322. package/src/web/main.tsx +17 -0
  323. package/src/web/public/apple-touch-icon.png +0 -0
  324. package/src/web/public/favicon-16.png +0 -0
  325. package/src/web/public/favicon-32.png +0 -0
  326. package/src/web/public/favicon.ico +0 -0
  327. package/src/web/public/favicon.svg +16 -0
  328. package/src/web/public/icon-512.png +0 -0
  329. package/src/web/routes/CockpitPage.tsx +98 -0
  330. package/src/web/routes/SettingsPage.tsx +60 -0
  331. package/src/web/routes/routeTree.tsx +49 -0
  332. package/src/web/stores/menuStore.ts +78 -0
  333. package/src/web/stores/paletteStore.ts +25 -0
  334. package/src/web/stores/sessionStore.ts +303 -0
  335. package/src/web/stores/sessionsStore.ts +155 -0
  336. package/src/web/stores/themeStore.ts +52 -0
  337. package/src/web/stores/threadStore.ts +68 -0
  338. package/src/web/stores/transientTabsStore.ts +63 -0
  339. package/src/web/stores/uiStore.ts +28 -0
  340. package/src/web/stores/useOpenTab.ts +19 -0
  341. package/src/web/stores/usePluginSlotProps.ts +22 -0
  342. package/src/web/styles/app.css +15 -0
  343. package/src/web/styles/webPluginSources.generated.css +1 -0
  344. package/src/web/vite-env.d.ts +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionPresence.mjs","names":[],"sources":["../../../src/services/sessionPresence.ts"],"sourcesContent":["import type { SessionPhase } from '../types/hub.ts';\nimport type { SessionFrame } from '../types/session.ts';\n\nconst DIALOG_METHODS = ['select', 'confirm', 'input', 'editor'];\n\n/**\n * What one session's frame stream says it is doing.\n *\n * Reduced by the hub for every session it holds, attached or not focused, so\n * the rail can describe sessions nobody is looking at. The reducer returns the\n * SAME object when a frame changes nothing it reports; callers use that\n * reference equality to skip redundant summary pushes.\n */\nexport interface SessionPresence {\n phase: SessionPhase;\n /** ISO 8601 timestamp of the last phase change. */\n phaseSince: string;\n /** ISO 8601 timestamp of the last change to any reported fact. */\n updatedAt: string;\n pendingMessageCount: number;\n everPrompted: boolean;\n awaitingInput: boolean;\n lastSettledAt?: string;\n /** Name reported by get_state, fresher than the registry record when set. */\n sessionName?: string;\n}\n\nexport function initialPresence(now: string): SessionPresence {\n return {\n phase: 'idle',\n phaseSince: now,\n updatedAt: now,\n pendingMessageCount: 0,\n everPrompted: false,\n awaitingInput: false,\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction withPhase(presence: SessionPresence, phase: SessionPhase, now: string): SessionPresence {\n if (presence.phase === phase) return presence;\n return { ...presence, phase, phaseSince: now, updatedAt: now };\n}\n\nfunction applyState(presence: SessionPresence, data: Record<string, unknown>, now: string): SessionPresence {\n let next = presence;\n const phase: SessionPhase = data.isCompacting === true ? 'compaction' : data.isStreaming === true ? 'turn' : 'idle';\n next = withPhase(next, phase, now);\n const pending = typeof data.pendingMessageCount === 'number' ? data.pendingMessageCount : next.pendingMessageCount;\n if (pending !== next.pendingMessageCount) next = { ...next, pendingMessageCount: pending, updatedAt: now };\n const name = typeof data.sessionName === 'string' && data.sessionName !== '' ? data.sessionName : undefined;\n if (name !== undefined && name !== next.sessionName) next = { ...next, sessionName: name, updatedAt: now };\n return next;\n}\n\n/**\n * Folds one agent frame into the presence.\n *\n * Pure and total like reduceSession: an unrecognised frame changes nothing.\n */\nexport function reducePresence(presence: SessionPresence, frame: SessionFrame, now: string): SessionPresence {\n switch (frame.type) {\n case 'replay':\n return isRecord(frame.frame) ? reducePresence(presence, frame.frame, now) : presence;\n\n case 'agent_start': {\n const started = withPhase(presence, 'turn', now);\n if (!started.awaitingInput) return started;\n return { ...started, awaitingInput: false, updatedAt: now };\n }\n\n case 'agent_settled': {\n const settled = withPhase(presence, 'idle', now);\n return { ...settled, awaitingInput: false, lastSettledAt: now, updatedAt: now };\n }\n\n case 'extension_ui_request': {\n if (typeof frame.method !== 'string' || !DIALOG_METHODS.includes(frame.method)) return presence;\n if (presence.awaitingInput) return presence;\n return { ...presence, awaitingInput: true, updatedAt: now };\n }\n\n case 'response':\n return frame.command === 'get_state' && isRecord(frame.data) ? applyState(presence, frame.data, now) : presence;\n\n default:\n return presence;\n }\n}\n\n/**\n * Folds one entry restored from the session's journal.\n *\n * A session outlives every hub that watches it, so \"has this ever been\n * prompted\" cannot be answered from what this process happened to see: a hub\n * that restarts, or meets a session first driven from the TUI, saw nothing. A\n * journalled user message is the proof, and without it a session carrying an\n * hour of work introduces itself in the rail as fresh.\n */\nexport function presenceAfterRestoredEntry(\n presence: SessionPresence,\n entry: Record<string, unknown>,\n now: string,\n): SessionPresence {\n if (presence.everPrompted || entry.type !== 'message') return presence;\n const message = entry.message;\n if (typeof message !== 'object' || message === null) return presence;\n if ((message as { role?: unknown }).role !== 'user') return presence;\n return { ...presence, everPrompted: true, updatedAt: now };\n}\n\n/**\n * Folds one page command heading to the agent.\n *\n * The prompt direction carries facts the event stream does not repeat: that\n * the session has ever been prompted, and that a pending dialog was answered.\n */\nexport function presenceAfterCommand(presence: SessionPresence, frame: SessionFrame, now: string): SessionPresence {\n if (frame.type === 'prompt' || frame.type === 'steer' || frame.type === 'follow_up') {\n if (presence.everPrompted) return presence;\n return { ...presence, everPrompted: true, updatedAt: now };\n }\n if (frame.type === 'extension_ui_response') {\n if (!presence.awaitingInput) return presence;\n return { ...presence, awaitingInput: false, updatedAt: now };\n }\n return presence;\n}\n"],"mappings":"AAGA,MAAM,EAAiB,CAAC,SAAU,UAAW,QAAS,QAAQ,EAwB9D,SAAgB,EAAgB,EAA8B,CAC5D,MAAO,CACL,MAAO,OACP,WAAY,EACZ,UAAW,EACX,oBAAqB,EACrB,aAAc,GACd,cAAe,EACjB,CACF,CAEA,SAAS,EAAS,EAAkD,CAClE,OAAO,OAAO,GAAU,YAAY,GAAkB,CAAC,MAAM,QAAQ,CAAK,CAC5E,CAEA,SAAS,EAAU,EAA2B,EAAqB,EAA8B,CAE/F,OADI,EAAS,QAAU,EAAc,EAC9B,CAAE,GAAG,EAAU,QAAO,WAAY,EAAK,UAAW,CAAI,CAC/D,CAEA,SAAS,EAAW,EAA2B,EAA+B,EAA8B,CAC1G,IAAI,EAAO,EACL,EAAsB,EAAK,eAAiB,GAAO,aAAe,EAAK,cAAgB,GAAO,OAAS,OAC7G,EAAO,EAAU,EAAM,EAAO,CAAG,EACjC,IAAM,EAAU,OAAO,EAAK,qBAAwB,SAAW,EAAK,oBAAsB,EAAK,oBAC3F,IAAY,EAAK,sBAAqB,EAAO,CAAE,GAAG,EAAM,oBAAqB,EAAS,UAAW,CAAI,GACzG,IAAM,EAAO,OAAO,EAAK,aAAgB,UAAY,EAAK,cAAgB,GAAK,EAAK,YAAc,IAAA,GAElG,OADI,IAAS,IAAA,IAAa,IAAS,EAAK,cAAa,EAAO,CAAE,GAAG,EAAM,YAAa,EAAM,UAAW,CAAI,GAClG,CACT,CAOA,SAAgB,EAAe,EAA2B,EAAqB,EAA8B,CAC3G,OAAQ,EAAM,KAAd,CACE,IAAK,SACH,OAAO,EAAS,EAAM,KAAK,EAAI,EAAe,EAAU,EAAM,MAAO,CAAG,EAAI,EAE9E,IAAK,cAAe,CAClB,IAAM,EAAU,EAAU,EAAU,OAAQ,CAAG,EAE/C,OADK,EAAQ,cACN,CAAE,GAAG,EAAS,cAAe,GAAO,UAAW,CAAI,EADvB,CAErC,CAEA,IAAK,gBAEH,MAAO,CAAE,GADO,EAAU,EAAU,OAAQ,CAC1B,EAAG,cAAe,GAAO,cAAe,EAAK,UAAW,CAAI,EAGhF,IAAK,uBAGH,OAFI,OAAO,EAAM,QAAW,UAAY,CAAC,EAAe,SAAS,EAAM,MAAM,GACzE,EAAS,cAAsB,EAC5B,CAAE,GAAG,EAAU,cAAe,GAAM,UAAW,CAAI,EAG5D,IAAK,WACH,OAAO,EAAM,UAAY,aAAe,EAAS,EAAM,IAAI,EAAI,EAAW,EAAU,EAAM,KAAM,CAAG,EAAI,EAEzG,QACE,OAAO,CACX,CACF,CAWA,SAAgB,EACd,EACA,EACA,EACiB,CACjB,GAAI,EAAS,cAAgB,EAAM,OAAS,UAAW,OAAO,EAC9D,IAAM,EAAU,EAAM,QAGtB,OAFI,OAAO,GAAY,WAAY,GAC9B,EAA+B,OAAS,OAAe,EACrD,CAAE,GAAG,EAAU,aAAc,GAAM,UAAW,CAAI,CAC3D,CAQA,SAAgB,EAAqB,EAA2B,EAAqB,EAA8B,CASjH,OARI,EAAM,OAAS,UAAY,EAAM,OAAS,SAAW,EAAM,OAAS,YAClE,EAAS,aAAqB,EAC3B,CAAE,GAAG,EAAU,aAAc,GAAM,UAAW,CAAI,EAEvD,EAAM,OAAS,yBACZ,EAAS,cACP,CAAE,GAAG,EAAU,cAAe,GAAO,UAAW,CAAI,EADvB,CAIxC"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);const n={".css":`text/css; charset=utf-8`,".html":`text/html; charset=utf-8`,".ico":`image/x-icon`,".js":`text/javascript; charset=utf-8`,".json":`application/json; charset=utf-8`,".map":`application/json; charset=utf-8`,".png":`image/png`,".svg":`image/svg+xml`,".woff2":`font/woff2`};function r(e){return n[t.default.extname(e).toLowerCase()]??`application/octet-stream`}function i(e,n){let r;try{r=decodeURIComponent(n)}catch{return}if(r.includes(`\0`))return;let i=t.default.resolve(e),a=t.default.resolve(i,`.${t.default.posix.normalize(r)}`);if(!(a!==i&&!a.startsWith(`${i}${t.default.sep}`)))return a}exports.contentTypeFor=r,exports.resolveAssetPath=i;
2
+ //# sourceMappingURL=staticAssets.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staticAssets.cjs","names":["path"],"sources":["../../../src/services/staticAssets.ts"],"sourcesContent":["import path from 'node:path';\n\nconst CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.css': 'text/css; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.ico': 'image/x-icon',\n '.js': 'text/javascript; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.woff2': 'font/woff2',\n};\n\nexport function contentTypeFor(filePath: string): string {\n return CONTENT_TYPES[path.extname(filePath).toLowerCase()] ?? 'application/octet-stream';\n}\n\n/**\n * Maps a request path to a file inside the asset root.\n *\n * Returns undefined when the result would escape the root, so an encoded\n * traversal cannot read files the bundle was never meant to publish.\n */\nexport function resolveAssetPath(assetsDir: string, requestPath: string): string | undefined {\n let decoded: string;\n try {\n decoded = decodeURIComponent(requestPath);\n } catch {\n return undefined;\n }\n if (decoded.includes('\\0')) return undefined;\n const root = path.resolve(assetsDir);\n const candidate = path.resolve(root, `.${path.posix.normalize(decoded)}`);\n if (candidate !== root && !candidate.startsWith(`${root}${path.sep}`)) return undefined;\n return candidate;\n}\n"],"mappings":"oGAEA,MAAM,EAAkD,CACtD,OAAQ,0BACR,QAAS,2BACT,OAAQ,eACR,MAAO,iCACP,QAAS,kCACT,OAAQ,kCACR,OAAQ,YACR,OAAQ,gBACR,SAAU,YACZ,EAEA,SAAgB,EAAe,EAA0B,CACvD,OAAO,EAAcA,EAAAA,QAAK,QAAQ,CAAQ,CAAC,CAAC,YAAY,IAAM,0BAChE,CAQA,SAAgB,EAAiB,EAAmB,EAAyC,CAC3F,IAAI,EACJ,GAAI,CACF,EAAU,mBAAmB,CAAW,CAC1C,MAAQ,CACN,MACF,CACA,GAAI,EAAQ,SAAS,IAAI,EAAG,OAC5B,IAAM,EAAOA,EAAAA,QAAK,QAAQ,CAAS,EAC7B,EAAYA,EAAAA,QAAK,QAAQ,EAAM,IAAIA,EAAAA,QAAK,MAAM,UAAU,CAAO,GAAG,EACpE,SAAc,GAAQ,CAAC,EAAU,WAAW,GAAG,IAAOA,EAAAA,QAAK,KAAK,GACpE,OAAO,CACT"}
@@ -0,0 +1,2 @@
1
+ import e from"node:path";const t={".css":`text/css; charset=utf-8`,".html":`text/html; charset=utf-8`,".ico":`image/x-icon`,".js":`text/javascript; charset=utf-8`,".json":`application/json; charset=utf-8`,".map":`application/json; charset=utf-8`,".png":`image/png`,".svg":`image/svg+xml`,".woff2":`font/woff2`};function n(n){return t[e.extname(n).toLowerCase()]??`application/octet-stream`}function r(t,n){let r;try{r=decodeURIComponent(n)}catch{return}if(r.includes(`\0`))return;let i=e.resolve(t),a=e.resolve(i,`.${e.posix.normalize(r)}`);if(!(a!==i&&!a.startsWith(`${i}${e.sep}`)))return a}export{n as contentTypeFor,r as resolveAssetPath};
2
+ //# sourceMappingURL=staticAssets.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staticAssets.mjs","names":[],"sources":["../../../src/services/staticAssets.ts"],"sourcesContent":["import path from 'node:path';\n\nconst CONTENT_TYPES: Readonly<Record<string, string>> = {\n '.css': 'text/css; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.ico': 'image/x-icon',\n '.js': 'text/javascript; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.woff2': 'font/woff2',\n};\n\nexport function contentTypeFor(filePath: string): string {\n return CONTENT_TYPES[path.extname(filePath).toLowerCase()] ?? 'application/octet-stream';\n}\n\n/**\n * Maps a request path to a file inside the asset root.\n *\n * Returns undefined when the result would escape the root, so an encoded\n * traversal cannot read files the bundle was never meant to publish.\n */\nexport function resolveAssetPath(assetsDir: string, requestPath: string): string | undefined {\n let decoded: string;\n try {\n decoded = decodeURIComponent(requestPath);\n } catch {\n return undefined;\n }\n if (decoded.includes('\\0')) return undefined;\n const root = path.resolve(assetsDir);\n const candidate = path.resolve(root, `.${path.posix.normalize(decoded)}`);\n if (candidate !== root && !candidate.startsWith(`${root}${path.sep}`)) return undefined;\n return candidate;\n}\n"],"mappings":"yBAEA,MAAM,EAAkD,CACtD,OAAQ,0BACR,QAAS,2BACT,OAAQ,eACR,MAAO,iCACP,QAAS,kCACT,OAAQ,kCACR,OAAQ,YACR,OAAQ,gBACR,SAAU,YACZ,EAEA,SAAgB,EAAe,EAA0B,CACvD,OAAO,EAAc,EAAK,QAAQ,CAAQ,CAAC,CAAC,YAAY,IAAM,0BAChE,CAQA,SAAgB,EAAiB,EAAmB,EAAyC,CAC3F,IAAI,EACJ,GAAI,CACF,EAAU,mBAAmB,CAAW,CAC1C,MAAQ,CACN,MACF,CACA,GAAI,EAAQ,SAAS,IAAI,EAAG,OAC5B,IAAM,EAAO,EAAK,QAAQ,CAAS,EAC7B,EAAY,EAAK,QAAQ,EAAM,IAAI,EAAK,MAAM,UAAU,CAAO,GAAG,EACpE,SAAc,GAAQ,CAAC,EAAU,WAAW,GAAG,IAAO,EAAK,KAAK,GACpE,OAAO,CACT"}
@@ -0,0 +1,2 @@
1
+ exports.PLUGIN_ROOTS_FILE=`pluginRoots.json`;
2
+ //# sourceMappingURL=webDevRoots.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webDevRoots.cjs","names":[],"sources":["../../../src/services/webDevRoots.ts"],"sourcesContent":["/**\n * Where the cockpit's dev server finds the plugin packages to serve with hot\n * reload. Pure: the adapter hands in the environment value and the roots file\n * the last `doompi sync` wrote next to its bundle; this only decides.\n */\n\n/** A delimiter-separated list of plugin package roots; wins over the synced roots file. */\nexport const PLUGIN_ROOTS_ENV = 'DOOMPI_WEB_PLUGIN_ROOTS';\n/** The file `doompi sync` writes beside its bundle, listing the package roots it bundled. */\nexport const PLUGIN_ROOTS_FILE = 'pluginRoots.json';\n\nexport interface DevPluginRootsInput {\n /** The raw environment value, undefined when unset. */\n envValue: string | undefined;\n /** The roots file's text, undefined when no synced bundle exists. */\n rootsFileText: string | undefined;\n /** The platform's path list delimiter (':' on POSIX, ';' on Windows). */\n delimiter: string;\n}\n\n/** The plugin roots the dev server should serve: the environment list, else the synced list, else none. */\nexport function devPluginRoots({ envValue, rootsFileText, delimiter }: DevPluginRootsInput): string[] {\n if (envValue !== undefined && envValue.trim() !== '') {\n return envValue\n .split(delimiter)\n .map((root) => root.trim())\n .filter((root) => root !== '');\n }\n if (rootsFileText === undefined) return [];\n try {\n const parsed: unknown = JSON.parse(rootsFileText);\n return Array.isArray(parsed) ? parsed.filter((root): root is string => typeof root === 'string') : [];\n } catch {\n return [];\n }\n}\n"],"mappings":""}
@@ -0,0 +1,2 @@
1
+ const e=`pluginRoots.json`;export{e as PLUGIN_ROOTS_FILE};
2
+ //# sourceMappingURL=webDevRoots.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webDevRoots.mjs","names":[],"sources":["../../../src/services/webDevRoots.ts"],"sourcesContent":["/**\n * Where the cockpit's dev server finds the plugin packages to serve with hot\n * reload. Pure: the adapter hands in the environment value and the roots file\n * the last `doompi sync` wrote next to its bundle; this only decides.\n */\n\n/** A delimiter-separated list of plugin package roots; wins over the synced roots file. */\nexport const PLUGIN_ROOTS_ENV = 'DOOMPI_WEB_PLUGIN_ROOTS';\n/** The file `doompi sync` writes beside its bundle, listing the package roots it bundled. */\nexport const PLUGIN_ROOTS_FILE = 'pluginRoots.json';\n\nexport interface DevPluginRootsInput {\n /** The raw environment value, undefined when unset. */\n envValue: string | undefined;\n /** The roots file's text, undefined when no synced bundle exists. */\n rootsFileText: string | undefined;\n /** The platform's path list delimiter (':' on POSIX, ';' on Windows). */\n delimiter: string;\n}\n\n/** The plugin roots the dev server should serve: the environment list, else the synced list, else none. */\nexport function devPluginRoots({ envValue, rootsFileText, delimiter }: DevPluginRootsInput): string[] {\n if (envValue !== undefined && envValue.trim() !== '') {\n return envValue\n .split(delimiter)\n .map((root) => root.trim())\n .filter((root) => root !== '');\n }\n if (rootsFileText === undefined) return [];\n try {\n const parsed: unknown = JSON.parse(rootsFileText);\n return Array.isArray(parsed) ? parsed.filter((root): root is string => typeof root === 'string') : [];\n } catch {\n return [];\n }\n}\n"],"mappings":"AASA,MAAa,EAAoB"}
@@ -0,0 +1,2 @@
1
+ const e=/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,t=1e3;var n=class extends Error{constructor(e,t){super(`doompiWeb manifest in ${e}: ${t}`),this.name=`WebPluginManifestError`}};function r(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function i(e,t,i,a){let o=typeof a==`string`?{entry:a}:a;if(!r(o))throw new n(e,`${i} must be a path or {entry, dist}.`);let{entry:s,dist:c}=o;if(typeof s!=`string`||!s.startsWith(`./`)||s.includes(`..`))throw new n(e,`${i}.entry must be a package-relative ./path with no '..'.`);if(c!==void 0&&(typeof c!=`string`||!c.startsWith(`./`)||c.includes(`..`)))throw new n(e,`${i}.dist must be a package-relative ./path with no '..'.`);if(i===`hub`&&!t&&typeof c!=`string`)throw new n(e,`hub.dist is required for a non-host package: the hub imports the built entry the package ships.`);return{entry:s,...typeof c==`string`?{dist:c}:{}}}function a(e){return e.doompiWeb===void 0?[]:Array.isArray(e.doompiWeb)?e.doompiWeb:[e.doompiWeb]}function o(o,s,c){let l=[];for(let u of a(s)){if(!r(u))throw new n(o,`each block must be an object.`);let{pluginId:a,registrationOrder:d=t,channels:f=[],client:p,hub:m}=u;if(typeof a!=`string`||!e.test(a))throw new n(o,`pluginId '${String(a)}' must be kebab-case.`);if(!Number.isInteger(d)||d<0)throw new n(o,`'${a}' needs a non-negative integer registrationOrder.`);if(!Array.isArray(f)||f.some(e=>typeof e!=`string`||e===``))throw new n(o,`'${a}' channels must be non-empty strings.`);l.push({pluginId:a,registrationOrder:d,channels:f,packageDir:o,packageName:typeof s.name==`string`?s.name:o,isHost:c,client:i(o,c,`client`,p),...m===void 0?{}:{hub:i(o,c,`hub`,m)}})}return l}const s=(e,t)=>e.registrationOrder-t.registrationOrder||e.pluginId.localeCompare(t.pluginId)||e.packageDir.localeCompare(t.packageDir);function c(e,t=()=>void 0){let n=[...e].sort(s),r=new Map,i=new Map,a=[];for(let e of n){let n=r.get(e.pluginId);if(n!==void 0){t(`web plugin '${e.pluginId}' from ${e.packageDir} is skipped: ${n.packageDir} already declares it.`);continue}r.set(e.pluginId,e),a.push(e);for(let n of e.channels){let r=i.get(n);if(r!==void 0){t(`web plugin '${e.pluginId}' channel '${n}' is already claimed by '${r}'; the first keeps it.`);continue}i.set(n,e.pluginId)}}return a}exports.DEFAULT_REGISTRATION_ORDER=t,exports.WebPluginManifestError=n,exports.declaredPluginsOf=o,exports.orderDeclaredPlugins=c,exports.pluginBlocksOf=a;
2
+ //# sourceMappingURL=webPluginManifest.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webPluginManifest.cjs","names":[],"sources":["../../../src/services/webPluginManifest.ts"],"sourcesContent":["/**\n * The doompiWeb manifest vocabulary: how a package declares its web plugin in\n * package.json, validated and ordered without touching the filesystem. The\n * scan adapter feeds this from real manifests; the sync bundler feeds it from\n * the installed composition's package roots.\n *\n * Plugins are independent: a manifest names no other plugin, and the order\n * plugins install in is only a tiebreak (registrationOrder, then pluginId),\n * because every relation between two plugins resolves by name once all of\n * them are installed. A collision between two packages is a notice for the\n * sync log and the first package keeps the name; only a malformed block is an\n * error, which the scanner turns into a notice for that package alone.\n */\n\nconst PLUGIN_ID_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;\n\n/** The order a plugin installs at when its manifest names none. */\nexport const DEFAULT_REGISTRATION_ORDER = 1000;\n\nexport interface WebPluginEntryDeclaration {\n /** Package-relative ./path to the source entry. */\n entry: string;\n /** Package-relative ./path to the built hub entry; required for non-host hub blocks. */\n dist?: string;\n}\n\nexport interface DeclaredWebPlugin {\n pluginId: string;\n registrationOrder: number;\n channels: string[];\n packageDir: string;\n packageName: string;\n isHost: boolean;\n client: WebPluginEntryDeclaration;\n hub?: WebPluginEntryDeclaration;\n}\n\nexport class WebPluginManifestError extends Error {\n constructor(packageDir: string, message: string) {\n super(`doompiWeb manifest in ${packageDir}: ${message}`);\n this.name = 'WebPluginManifestError';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction normalizeEntry(packageDir: string, isHost: boolean, kind: string, value: unknown): WebPluginEntryDeclaration {\n const raw = typeof value === 'string' ? { entry: value } : value;\n if (!isRecord(raw)) throw new WebPluginManifestError(packageDir, `${kind} must be a path or {entry, dist}.`);\n const { entry, dist } = raw;\n if (typeof entry !== 'string' || !entry.startsWith('./') || entry.includes('..')) {\n throw new WebPluginManifestError(packageDir, `${kind}.entry must be a package-relative ./path with no '..'.`);\n }\n if (dist !== undefined && (typeof dist !== 'string' || !dist.startsWith('./') || dist.includes('..'))) {\n throw new WebPluginManifestError(packageDir, `${kind}.dist must be a package-relative ./path with no '..'.`);\n }\n if (kind === 'hub' && !isHost && typeof dist !== 'string') {\n throw new WebPluginManifestError(\n packageDir,\n 'hub.dist is required for a non-host package: the hub imports the built entry the package ships.',\n );\n }\n return { entry, ...(typeof dist === 'string' ? { dist } : {}) };\n}\n\n/** The doompiWeb value normalized to a list: one plugin object or an array of them. */\nexport function pluginBlocksOf(manifest: Record<string, unknown>): unknown[] {\n if (manifest.doompiWeb === undefined) return [];\n return Array.isArray(manifest.doompiWeb) ? manifest.doompiWeb : [manifest.doompiWeb];\n}\n\n/** Validates one manifest's blocks into declared plugins; throws WebPluginManifestError on bad shape. */\nexport function declaredPluginsOf(\n packageDir: string,\n manifest: Record<string, unknown>,\n isHost: boolean,\n): DeclaredWebPlugin[] {\n const plugins: DeclaredWebPlugin[] = [];\n for (const block of pluginBlocksOf(manifest)) {\n if (!isRecord(block)) throw new WebPluginManifestError(packageDir, 'each block must be an object.');\n const { pluginId, registrationOrder = DEFAULT_REGISTRATION_ORDER, channels = [], client, hub } = block;\n if (typeof pluginId !== 'string' || !PLUGIN_ID_PATTERN.test(pluginId)) {\n throw new WebPluginManifestError(packageDir, `pluginId '${String(pluginId)}' must be kebab-case.`);\n }\n if (!Number.isInteger(registrationOrder) || (registrationOrder as number) < 0) {\n throw new WebPluginManifestError(packageDir, `'${pluginId}' needs a non-negative integer registrationOrder.`);\n }\n if (!Array.isArray(channels) || channels.some((channel) => typeof channel !== 'string' || channel === '')) {\n throw new WebPluginManifestError(packageDir, `'${pluginId}' channels must be non-empty strings.`);\n }\n plugins.push({\n pluginId,\n registrationOrder: registrationOrder as number,\n channels: channels as string[],\n packageDir,\n packageName: typeof manifest.name === 'string' ? manifest.name : packageDir,\n isHost,\n client: normalizeEntry(packageDir, isHost, 'client', client),\n ...(hub === undefined ? {} : { hub: normalizeEntry(packageDir, isHost, 'hub', hub) }),\n });\n }\n return plugins;\n}\n\nconst byInstallOrder = (left: DeclaredWebPlugin, right: DeclaredWebPlugin): number =>\n left.registrationOrder - right.registrationOrder ||\n left.pluginId.localeCompare(right.pluginId) ||\n left.packageDir.localeCompare(right.packageDir);\n\n/**\n * The deterministic install order, (registrationOrder, pluginId), with the\n * cross-package collisions resolved the way the client registry resolves\n * them: the first plugin keeps a shared pluginId and the later package is\n * dropped with a notice; a channel two kept plugins both declare is noticed\n * here and settled first-wins by the client and the hub loader.\n */\nexport function orderDeclaredPlugins(\n plugins: readonly DeclaredWebPlugin[],\n onNotice: (message: string) => void = () => undefined,\n): DeclaredWebPlugin[] {\n const sorted = [...plugins].sort(byInstallOrder);\n const byId = new Map<string, DeclaredWebPlugin>();\n const channelOwners = new Map<string, string>();\n const kept: DeclaredWebPlugin[] = [];\n for (const plugin of sorted) {\n const holder = byId.get(plugin.pluginId);\n if (holder !== undefined) {\n onNotice(\n `web plugin '${plugin.pluginId}' from ${plugin.packageDir} is skipped: ${holder.packageDir} already declares it.`,\n );\n continue;\n }\n byId.set(plugin.pluginId, plugin);\n kept.push(plugin);\n for (const channel of plugin.channels) {\n const owner = channelOwners.get(channel);\n if (owner !== undefined) {\n onNotice(\n `web plugin '${plugin.pluginId}' channel '${channel}' is already claimed by '${owner}'; the first keeps it.`,\n );\n continue;\n }\n channelOwners.set(channel, plugin.pluginId);\n }\n }\n return kept;\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,t=1e3;var n=class extends Error{constructor(e,t){super(`doompiWeb manifest in ${e}: ${t}`),this.name=`WebPluginManifestError`}};function r(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function i(e,t,i,a){let o=typeof a==`string`?{entry:a}:a;if(!r(o))throw new n(e,`${i} must be a path or {entry, dist}.`);let{entry:s,dist:c}=o;if(typeof s!=`string`||!s.startsWith(`./`)||s.includes(`..`))throw new n(e,`${i}.entry must be a package-relative ./path with no '..'.`);if(c!==void 0&&(typeof c!=`string`||!c.startsWith(`./`)||c.includes(`..`)))throw new n(e,`${i}.dist must be a package-relative ./path with no '..'.`);if(i===`hub`&&!t&&typeof c!=`string`)throw new n(e,`hub.dist is required for a non-host package: the hub imports the built entry the package ships.`);return{entry:s,...typeof c==`string`?{dist:c}:{}}}function a(e){return e.doompiWeb===void 0?[]:Array.isArray(e.doompiWeb)?e.doompiWeb:[e.doompiWeb]}function o(o,s,c){let l=[];for(let u of a(s)){if(!r(u))throw new n(o,`each block must be an object.`);let{pluginId:a,registrationOrder:d=t,channels:f=[],client:p,hub:m}=u;if(typeof a!=`string`||!e.test(a))throw new n(o,`pluginId '${String(a)}' must be kebab-case.`);if(!Number.isInteger(d)||d<0)throw new n(o,`'${a}' needs a non-negative integer registrationOrder.`);if(!Array.isArray(f)||f.some(e=>typeof e!=`string`||e===``))throw new n(o,`'${a}' channels must be non-empty strings.`);l.push({pluginId:a,registrationOrder:d,channels:f,packageDir:o,packageName:typeof s.name==`string`?s.name:o,isHost:c,client:i(o,c,`client`,p),...m===void 0?{}:{hub:i(o,c,`hub`,m)}})}return l}const s=(e,t)=>e.registrationOrder-t.registrationOrder||e.pluginId.localeCompare(t.pluginId)||e.packageDir.localeCompare(t.packageDir);function c(e,t=()=>void 0){let n=[...e].sort(s),r=new Map,i=new Map,a=[];for(let e of n){let n=r.get(e.pluginId);if(n!==void 0){t(`web plugin '${e.pluginId}' from ${e.packageDir} is skipped: ${n.packageDir} already declares it.`);continue}r.set(e.pluginId,e),a.push(e);for(let n of e.channels){let r=i.get(n);if(r!==void 0){t(`web plugin '${e.pluginId}' channel '${n}' is already claimed by '${r}'; the first keeps it.`);continue}i.set(n,e.pluginId)}}return a}export{t as DEFAULT_REGISTRATION_ORDER,n as WebPluginManifestError,o as declaredPluginsOf,c as orderDeclaredPlugins,a as pluginBlocksOf};
2
+ //# sourceMappingURL=webPluginManifest.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webPluginManifest.mjs","names":[],"sources":["../../../src/services/webPluginManifest.ts"],"sourcesContent":["/**\n * The doompiWeb manifest vocabulary: how a package declares its web plugin in\n * package.json, validated and ordered without touching the filesystem. The\n * scan adapter feeds this from real manifests; the sync bundler feeds it from\n * the installed composition's package roots.\n *\n * Plugins are independent: a manifest names no other plugin, and the order\n * plugins install in is only a tiebreak (registrationOrder, then pluginId),\n * because every relation between two plugins resolves by name once all of\n * them are installed. A collision between two packages is a notice for the\n * sync log and the first package keeps the name; only a malformed block is an\n * error, which the scanner turns into a notice for that package alone.\n */\n\nconst PLUGIN_ID_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;\n\n/** The order a plugin installs at when its manifest names none. */\nexport const DEFAULT_REGISTRATION_ORDER = 1000;\n\nexport interface WebPluginEntryDeclaration {\n /** Package-relative ./path to the source entry. */\n entry: string;\n /** Package-relative ./path to the built hub entry; required for non-host hub blocks. */\n dist?: string;\n}\n\nexport interface DeclaredWebPlugin {\n pluginId: string;\n registrationOrder: number;\n channels: string[];\n packageDir: string;\n packageName: string;\n isHost: boolean;\n client: WebPluginEntryDeclaration;\n hub?: WebPluginEntryDeclaration;\n}\n\nexport class WebPluginManifestError extends Error {\n constructor(packageDir: string, message: string) {\n super(`doompiWeb manifest in ${packageDir}: ${message}`);\n this.name = 'WebPluginManifestError';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction normalizeEntry(packageDir: string, isHost: boolean, kind: string, value: unknown): WebPluginEntryDeclaration {\n const raw = typeof value === 'string' ? { entry: value } : value;\n if (!isRecord(raw)) throw new WebPluginManifestError(packageDir, `${kind} must be a path or {entry, dist}.`);\n const { entry, dist } = raw;\n if (typeof entry !== 'string' || !entry.startsWith('./') || entry.includes('..')) {\n throw new WebPluginManifestError(packageDir, `${kind}.entry must be a package-relative ./path with no '..'.`);\n }\n if (dist !== undefined && (typeof dist !== 'string' || !dist.startsWith('./') || dist.includes('..'))) {\n throw new WebPluginManifestError(packageDir, `${kind}.dist must be a package-relative ./path with no '..'.`);\n }\n if (kind === 'hub' && !isHost && typeof dist !== 'string') {\n throw new WebPluginManifestError(\n packageDir,\n 'hub.dist is required for a non-host package: the hub imports the built entry the package ships.',\n );\n }\n return { entry, ...(typeof dist === 'string' ? { dist } : {}) };\n}\n\n/** The doompiWeb value normalized to a list: one plugin object or an array of them. */\nexport function pluginBlocksOf(manifest: Record<string, unknown>): unknown[] {\n if (manifest.doompiWeb === undefined) return [];\n return Array.isArray(manifest.doompiWeb) ? manifest.doompiWeb : [manifest.doompiWeb];\n}\n\n/** Validates one manifest's blocks into declared plugins; throws WebPluginManifestError on bad shape. */\nexport function declaredPluginsOf(\n packageDir: string,\n manifest: Record<string, unknown>,\n isHost: boolean,\n): DeclaredWebPlugin[] {\n const plugins: DeclaredWebPlugin[] = [];\n for (const block of pluginBlocksOf(manifest)) {\n if (!isRecord(block)) throw new WebPluginManifestError(packageDir, 'each block must be an object.');\n const { pluginId, registrationOrder = DEFAULT_REGISTRATION_ORDER, channels = [], client, hub } = block;\n if (typeof pluginId !== 'string' || !PLUGIN_ID_PATTERN.test(pluginId)) {\n throw new WebPluginManifestError(packageDir, `pluginId '${String(pluginId)}' must be kebab-case.`);\n }\n if (!Number.isInteger(registrationOrder) || (registrationOrder as number) < 0) {\n throw new WebPluginManifestError(packageDir, `'${pluginId}' needs a non-negative integer registrationOrder.`);\n }\n if (!Array.isArray(channels) || channels.some((channel) => typeof channel !== 'string' || channel === '')) {\n throw new WebPluginManifestError(packageDir, `'${pluginId}' channels must be non-empty strings.`);\n }\n plugins.push({\n pluginId,\n registrationOrder: registrationOrder as number,\n channels: channels as string[],\n packageDir,\n packageName: typeof manifest.name === 'string' ? manifest.name : packageDir,\n isHost,\n client: normalizeEntry(packageDir, isHost, 'client', client),\n ...(hub === undefined ? {} : { hub: normalizeEntry(packageDir, isHost, 'hub', hub) }),\n });\n }\n return plugins;\n}\n\nconst byInstallOrder = (left: DeclaredWebPlugin, right: DeclaredWebPlugin): number =>\n left.registrationOrder - right.registrationOrder ||\n left.pluginId.localeCompare(right.pluginId) ||\n left.packageDir.localeCompare(right.packageDir);\n\n/**\n * The deterministic install order, (registrationOrder, pluginId), with the\n * cross-package collisions resolved the way the client registry resolves\n * them: the first plugin keeps a shared pluginId and the later package is\n * dropped with a notice; a channel two kept plugins both declare is noticed\n * here and settled first-wins by the client and the hub loader.\n */\nexport function orderDeclaredPlugins(\n plugins: readonly DeclaredWebPlugin[],\n onNotice: (message: string) => void = () => undefined,\n): DeclaredWebPlugin[] {\n const sorted = [...plugins].sort(byInstallOrder);\n const byId = new Map<string, DeclaredWebPlugin>();\n const channelOwners = new Map<string, string>();\n const kept: DeclaredWebPlugin[] = [];\n for (const plugin of sorted) {\n const holder = byId.get(plugin.pluginId);\n if (holder !== undefined) {\n onNotice(\n `web plugin '${plugin.pluginId}' from ${plugin.packageDir} is skipped: ${holder.packageDir} already declares it.`,\n );\n continue;\n }\n byId.set(plugin.pluginId, plugin);\n kept.push(plugin);\n for (const channel of plugin.channels) {\n const owner = channelOwners.get(channel);\n if (owner !== undefined) {\n onNotice(\n `web plugin '${plugin.pluginId}' channel '${channel}' is already claimed by '${owner}'; the first keeps it.`,\n );\n continue;\n }\n channelOwners.set(channel, plugin.pluginId);\n }\n }\n return kept;\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ exports.AUTH_LOGINS_API_ROUTE=`/api/auth/logins`,exports.AUTH_PROVIDERS_API_ROUTE=`/api/auth/providers`;
2
+ //# sourceMappingURL=auth.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.cjs","names":[],"sources":["../../../src/types/auth.ts"],"sourcesContent":["/**\n * Wire vocabulary for provider authentication, shared by the hub routes and\n * the settings page.\n *\n * The hub keeps one Pi ModelRuntime over the same auth.json the sessions\n * read, so a login here is a login for every session on this machine. Prompt\n * and event shapes restate pi-ai's AuthPrompt and AuthEvent so neither the\n * flow service nor the page needs a pi import; the abort signal never leaves\n * the hub.\n */\n\n/** REST endpoint listing providers with their auth state; DELETE /:providerId signs out. */\nexport const AUTH_PROVIDERS_API_ROUTE = '/api/auth/providers';\n/** REST endpoint for login flows: POST starts one, GET /:flowId polls it, POST /:flowId/answer answers its prompt, DELETE /:flowId cancels. */\nexport const AUTH_LOGINS_API_ROUTE = '/api/auth/logins';\n\nexport type AuthMethodType = 'api_key' | 'oauth';\n\nexport interface ProviderAuthMethod {\n type: AuthMethodType;\n /** What the provider calls this method: \"Anthropic API key\", \"Anthropic (Claude Pro/Max)\". */\n label: string;\n}\n\nexport interface ProviderAuthSummary {\n id: string;\n name: string;\n /** Login methods the provider offers; ambient-only providers list none. */\n methods: ProviderAuthMethod[];\n /**\n * Present once the provider has working auth: how, and where the credential\n * comes from (\"stored\", or the environment variable that holds the key).\n */\n authenticated?: { type: AuthMethodType; source?: string };\n}\n\nexport interface LoginSelectOption {\n id: string;\n label: string;\n description?: string;\n}\n\n/** pi-ai's AuthPrompt, restated. `signal` cancels this one question, not the flow. */\nexport type LoginPrompt = { signal?: AbortSignal } & (\n | { type: 'text'; message: string; placeholder?: string }\n | { type: 'secret'; message: string; placeholder?: string }\n | { type: 'select'; message: string; options: readonly LoginSelectOption[] }\n | { type: 'manual_code'; message: string; placeholder?: string }\n);\n\n/** The question the page answers: a prompt with an id and without its signal. */\nexport interface LoginPromptView {\n id: string;\n type: LoginPrompt['type'];\n message: string;\n placeholder?: string;\n /** Present for select prompts. */\n options?: LoginSelectOption[];\n}\n\n/** pi-ai's AuthEvent, restated. */\nexport type LoginEvent =\n | { type: 'info'; message: string; links?: readonly { url: string; label?: string }[] }\n | { type: 'auth_url'; url: string; instructions?: string }\n | {\n type: 'device_code';\n userCode: string;\n verificationUri: string;\n intervalSeconds?: number;\n expiresInSeconds?: number;\n }\n | { type: 'progress'; message: string };\n\nexport type LoginFlowStatus = 'running' | 'succeeded' | 'failed' | 'cancelled';\n\nexport interface LoginFlowSnapshot {\n id: string;\n providerId: string;\n providerName: string;\n type: AuthMethodType;\n status: LoginFlowStatus;\n events: LoginEvent[];\n /** The question the flow is waiting on, while there is one. */\n prompt?: LoginPromptView;\n /** Why the flow failed, once it has. */\n error?: string;\n}\n\n/** pi-ai's AuthInteraction: what a provider's login needs from its host. */\nexport interface LoginInteraction {\n signal: AbortSignal;\n prompt(prompt: LoginPrompt): Promise<string>;\n notify(event: LoginEvent): void;\n}\n\nexport interface AuthRuntimeProvider {\n readonly id: string;\n readonly name: string;\n readonly auth: {\n /** Absent `login` means ambient-only (env vars, AWS profiles): nothing to sign in to. */\n apiKey?: { name: string; login?: unknown };\n oauth?: { name: string; loginLabel?: string };\n };\n}\n\n/**\n * The slice of Pi's ModelRuntime the hub relies on, so a test can stand in a\n * fake and the adapter never types against the whole agent.\n */\nexport interface AuthRuntime {\n getProviders(): readonly AuthRuntimeProvider[];\n getProviderAuthStatus(providerId: string): { configured: boolean; source?: string; label?: string };\n isUsingOAuth(providerId: string): boolean;\n /** Re-reads models.json and every provider's auth state without touching the network. */\n refresh(options: { allowNetwork: boolean }): Promise<unknown>;\n login(providerId: string, type: AuthMethodType, interaction: LoginInteraction): Promise<unknown>;\n logout(providerId: string, options?: { signal?: AbortSignal }): Promise<void>;\n}\n"],"mappings":""}
@@ -0,0 +1,94 @@
1
+ //#region src/types/auth.d.ts
2
+ type AuthMethodType = 'api_key' | 'oauth';
3
+ interface LoginSelectOption {
4
+ id: string;
5
+ label: string;
6
+ description?: string;
7
+ }
8
+ /** pi-ai's AuthPrompt, restated. `signal` cancels this one question, not the flow. */
9
+ type LoginPrompt = {
10
+ signal?: AbortSignal;
11
+ } & ({
12
+ type: 'text';
13
+ message: string;
14
+ placeholder?: string;
15
+ } | {
16
+ type: 'secret';
17
+ message: string;
18
+ placeholder?: string;
19
+ } | {
20
+ type: 'select';
21
+ message: string;
22
+ options: readonly LoginSelectOption[];
23
+ } | {
24
+ type: 'manual_code';
25
+ message: string;
26
+ placeholder?: string;
27
+ });
28
+ /** pi-ai's AuthEvent, restated. */
29
+ type LoginEvent = {
30
+ type: 'info';
31
+ message: string;
32
+ links?: readonly {
33
+ url: string;
34
+ label?: string;
35
+ }[];
36
+ } | {
37
+ type: 'auth_url';
38
+ url: string;
39
+ instructions?: string;
40
+ } | {
41
+ type: 'device_code';
42
+ userCode: string;
43
+ verificationUri: string;
44
+ intervalSeconds?: number;
45
+ expiresInSeconds?: number;
46
+ } | {
47
+ type: 'progress';
48
+ message: string;
49
+ };
50
+ /** pi-ai's AuthInteraction: what a provider's login needs from its host. */
51
+ interface LoginInteraction {
52
+ signal: AbortSignal;
53
+ prompt(prompt: LoginPrompt): Promise<string>;
54
+ notify(event: LoginEvent): void;
55
+ }
56
+ interface AuthRuntimeProvider {
57
+ readonly id: string;
58
+ readonly name: string;
59
+ readonly auth: {
60
+ /** Absent `login` means ambient-only (env vars, AWS profiles): nothing to sign in to. */
61
+ apiKey?: {
62
+ name: string;
63
+ login?: unknown;
64
+ };
65
+ oauth?: {
66
+ name: string;
67
+ loginLabel?: string;
68
+ };
69
+ };
70
+ }
71
+ /**
72
+ * The slice of Pi's ModelRuntime the hub relies on, so a test can stand in a
73
+ * fake and the adapter never types against the whole agent.
74
+ */
75
+ interface AuthRuntime {
76
+ getProviders(): readonly AuthRuntimeProvider[];
77
+ getProviderAuthStatus(providerId: string): {
78
+ configured: boolean;
79
+ source?: string;
80
+ label?: string;
81
+ };
82
+ isUsingOAuth(providerId: string): boolean;
83
+ /** Re-reads models.json and every provider's auth state without touching the network. */
84
+ refresh(options: {
85
+ allowNetwork: boolean;
86
+ }): Promise<unknown>;
87
+ login(providerId: string, type: AuthMethodType, interaction: LoginInteraction): Promise<unknown>;
88
+ logout(providerId: string, options?: {
89
+ signal?: AbortSignal;
90
+ }): Promise<void>;
91
+ }
92
+ //#endregion
93
+ export { AuthMethodType, AuthRuntime, AuthRuntimeProvider, LoginEvent, LoginInteraction, LoginPrompt, LoginSelectOption };
94
+ //# sourceMappingURL=auth.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.cts","names":[],"sources":["../../../src/types/auth.ts"],"mappings":";KAgBY;UAoBK;EACf;EACA;EACA;;;KAIU;EAAgB,SAAS;;EAC/B;EAAc;EAAiB;;EAC/B;EAAgB;EAAiB;;EACjC;EAAgB;EAAiB,kBAAkB;;EACnD;EAAqB;EAAiB;;;KAchC;EACN;EAAc;EAAiB;IAAmB;IAAa;;;EAC/D;EAAkB;EAAa;;EAE/B;EACA;EACA;EACA;EACA;;EAEA;EAAkB;;;UAkBP;EACf,QAAQ;EACR,OAAO,QAAQ,cAAc;EAC7B,OAAO,OAAO;;UAGC;WACN;WACA;WACA;;IAEP;MAAW;MAAc;;IACzB;MAAU;MAAc;;;;;;;;UAQX;EACf,yBAAyB;EACzB,sBAAsB;IAAuB;IAAqB;IAAiB;;EACnF,aAAa;;EAEb,QAAQ;IAAW;MAA0B;EAC7C,MAAM,oBAAoB,MAAM,gBAAgB,aAAa,mBAAmB;EAChF,OAAO,oBAAoB;IAAY,SAAS;MAAgB"}
@@ -0,0 +1,94 @@
1
+ //#region src/types/auth.d.ts
2
+ type AuthMethodType = 'api_key' | 'oauth';
3
+ interface LoginSelectOption {
4
+ id: string;
5
+ label: string;
6
+ description?: string;
7
+ }
8
+ /** pi-ai's AuthPrompt, restated. `signal` cancels this one question, not the flow. */
9
+ type LoginPrompt = {
10
+ signal?: AbortSignal;
11
+ } & ({
12
+ type: 'text';
13
+ message: string;
14
+ placeholder?: string;
15
+ } | {
16
+ type: 'secret';
17
+ message: string;
18
+ placeholder?: string;
19
+ } | {
20
+ type: 'select';
21
+ message: string;
22
+ options: readonly LoginSelectOption[];
23
+ } | {
24
+ type: 'manual_code';
25
+ message: string;
26
+ placeholder?: string;
27
+ });
28
+ /** pi-ai's AuthEvent, restated. */
29
+ type LoginEvent = {
30
+ type: 'info';
31
+ message: string;
32
+ links?: readonly {
33
+ url: string;
34
+ label?: string;
35
+ }[];
36
+ } | {
37
+ type: 'auth_url';
38
+ url: string;
39
+ instructions?: string;
40
+ } | {
41
+ type: 'device_code';
42
+ userCode: string;
43
+ verificationUri: string;
44
+ intervalSeconds?: number;
45
+ expiresInSeconds?: number;
46
+ } | {
47
+ type: 'progress';
48
+ message: string;
49
+ };
50
+ /** pi-ai's AuthInteraction: what a provider's login needs from its host. */
51
+ interface LoginInteraction {
52
+ signal: AbortSignal;
53
+ prompt(prompt: LoginPrompt): Promise<string>;
54
+ notify(event: LoginEvent): void;
55
+ }
56
+ interface AuthRuntimeProvider {
57
+ readonly id: string;
58
+ readonly name: string;
59
+ readonly auth: {
60
+ /** Absent `login` means ambient-only (env vars, AWS profiles): nothing to sign in to. */
61
+ apiKey?: {
62
+ name: string;
63
+ login?: unknown;
64
+ };
65
+ oauth?: {
66
+ name: string;
67
+ loginLabel?: string;
68
+ };
69
+ };
70
+ }
71
+ /**
72
+ * The slice of Pi's ModelRuntime the hub relies on, so a test can stand in a
73
+ * fake and the adapter never types against the whole agent.
74
+ */
75
+ interface AuthRuntime {
76
+ getProviders(): readonly AuthRuntimeProvider[];
77
+ getProviderAuthStatus(providerId: string): {
78
+ configured: boolean;
79
+ source?: string;
80
+ label?: string;
81
+ };
82
+ isUsingOAuth(providerId: string): boolean;
83
+ /** Re-reads models.json and every provider's auth state without touching the network. */
84
+ refresh(options: {
85
+ allowNetwork: boolean;
86
+ }): Promise<unknown>;
87
+ login(providerId: string, type: AuthMethodType, interaction: LoginInteraction): Promise<unknown>;
88
+ logout(providerId: string, options?: {
89
+ signal?: AbortSignal;
90
+ }): Promise<void>;
91
+ }
92
+ //#endregion
93
+ export { AuthMethodType, AuthRuntime, AuthRuntimeProvider, LoginEvent, LoginInteraction, LoginPrompt, LoginSelectOption };
94
+ //# sourceMappingURL=auth.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.mts","names":[],"sources":["../../../src/types/auth.ts"],"mappings":";KAgBY;UAoBK;EACf;EACA;EACA;;;KAIU;EAAgB,SAAS;;EAC/B;EAAc;EAAiB;;EAC/B;EAAgB;EAAiB;;EACjC;EAAgB;EAAiB,kBAAkB;;EACnD;EAAqB;EAAiB;;;KAchC;EACN;EAAc;EAAiB;IAAmB;IAAa;;;EAC/D;EAAkB;EAAa;;EAE/B;EACA;EACA;EACA;EACA;;EAEA;EAAkB;;;UAkBP;EACf,QAAQ;EACR,OAAO,QAAQ,cAAc;EAC7B,OAAO,OAAO;;UAGC;WACN;WACA;WACA;;IAEP;MAAW;MAAc;;IACzB;MAAU;MAAc;;;;;;;;UAQX;EACf,yBAAyB;EACzB,sBAAsB;IAAuB;IAAqB;IAAiB;;EACnF,aAAa;;EAEb,QAAQ;IAAW;MAA0B;EAC7C,MAAM,oBAAoB,MAAM,gBAAgB,aAAa,mBAAmB;EAChF,OAAO,oBAAoB;IAAY,SAAS;MAAgB"}
@@ -0,0 +1,2 @@
1
+ const e=`/api/auth/providers`,t=`/api/auth/logins`;export{t as AUTH_LOGINS_API_ROUTE,e as AUTH_PROVIDERS_API_ROUTE};
2
+ //# sourceMappingURL=auth.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.mjs","names":[],"sources":["../../../src/types/auth.ts"],"sourcesContent":["/**\n * Wire vocabulary for provider authentication, shared by the hub routes and\n * the settings page.\n *\n * The hub keeps one Pi ModelRuntime over the same auth.json the sessions\n * read, so a login here is a login for every session on this machine. Prompt\n * and event shapes restate pi-ai's AuthPrompt and AuthEvent so neither the\n * flow service nor the page needs a pi import; the abort signal never leaves\n * the hub.\n */\n\n/** REST endpoint listing providers with their auth state; DELETE /:providerId signs out. */\nexport const AUTH_PROVIDERS_API_ROUTE = '/api/auth/providers';\n/** REST endpoint for login flows: POST starts one, GET /:flowId polls it, POST /:flowId/answer answers its prompt, DELETE /:flowId cancels. */\nexport const AUTH_LOGINS_API_ROUTE = '/api/auth/logins';\n\nexport type AuthMethodType = 'api_key' | 'oauth';\n\nexport interface ProviderAuthMethod {\n type: AuthMethodType;\n /** What the provider calls this method: \"Anthropic API key\", \"Anthropic (Claude Pro/Max)\". */\n label: string;\n}\n\nexport interface ProviderAuthSummary {\n id: string;\n name: string;\n /** Login methods the provider offers; ambient-only providers list none. */\n methods: ProviderAuthMethod[];\n /**\n * Present once the provider has working auth: how, and where the credential\n * comes from (\"stored\", or the environment variable that holds the key).\n */\n authenticated?: { type: AuthMethodType; source?: string };\n}\n\nexport interface LoginSelectOption {\n id: string;\n label: string;\n description?: string;\n}\n\n/** pi-ai's AuthPrompt, restated. `signal` cancels this one question, not the flow. */\nexport type LoginPrompt = { signal?: AbortSignal } & (\n | { type: 'text'; message: string; placeholder?: string }\n | { type: 'secret'; message: string; placeholder?: string }\n | { type: 'select'; message: string; options: readonly LoginSelectOption[] }\n | { type: 'manual_code'; message: string; placeholder?: string }\n);\n\n/** The question the page answers: a prompt with an id and without its signal. */\nexport interface LoginPromptView {\n id: string;\n type: LoginPrompt['type'];\n message: string;\n placeholder?: string;\n /** Present for select prompts. */\n options?: LoginSelectOption[];\n}\n\n/** pi-ai's AuthEvent, restated. */\nexport type LoginEvent =\n | { type: 'info'; message: string; links?: readonly { url: string; label?: string }[] }\n | { type: 'auth_url'; url: string; instructions?: string }\n | {\n type: 'device_code';\n userCode: string;\n verificationUri: string;\n intervalSeconds?: number;\n expiresInSeconds?: number;\n }\n | { type: 'progress'; message: string };\n\nexport type LoginFlowStatus = 'running' | 'succeeded' | 'failed' | 'cancelled';\n\nexport interface LoginFlowSnapshot {\n id: string;\n providerId: string;\n providerName: string;\n type: AuthMethodType;\n status: LoginFlowStatus;\n events: LoginEvent[];\n /** The question the flow is waiting on, while there is one. */\n prompt?: LoginPromptView;\n /** Why the flow failed, once it has. */\n error?: string;\n}\n\n/** pi-ai's AuthInteraction: what a provider's login needs from its host. */\nexport interface LoginInteraction {\n signal: AbortSignal;\n prompt(prompt: LoginPrompt): Promise<string>;\n notify(event: LoginEvent): void;\n}\n\nexport interface AuthRuntimeProvider {\n readonly id: string;\n readonly name: string;\n readonly auth: {\n /** Absent `login` means ambient-only (env vars, AWS profiles): nothing to sign in to. */\n apiKey?: { name: string; login?: unknown };\n oauth?: { name: string; loginLabel?: string };\n };\n}\n\n/**\n * The slice of Pi's ModelRuntime the hub relies on, so a test can stand in a\n * fake and the adapter never types against the whole agent.\n */\nexport interface AuthRuntime {\n getProviders(): readonly AuthRuntimeProvider[];\n getProviderAuthStatus(providerId: string): { configured: boolean; source?: string; label?: string };\n isUsingOAuth(providerId: string): boolean;\n /** Re-reads models.json and every provider's auth state without touching the network. */\n refresh(options: { allowNetwork: boolean }): Promise<unknown>;\n login(providerId: string, type: AuthMethodType, interaction: LoginInteraction): Promise<unknown>;\n logout(providerId: string, options?: { signal?: AbortSignal }): Promise<void>;\n}\n"],"mappings":"AAYA,MAAa,EAA2B,sBAE3B,EAAwB"}
@@ -0,0 +1,31 @@
1
+ import { AuthRuntime } from "./auth.cjs";
2
+ import { SessionFrame } from "./session.cjs";
3
+ //#region src/types/bridge.d.ts
4
+ /** One live attachment to a doompi-server socket. */
5
+ interface SessionAttachment {
6
+ /** Forwards a command frame to the agent. Ignored once closed. */
7
+ send(frame: SessionFrame): void;
8
+ close(): void;
9
+ }
10
+ interface WebServerOptions {
11
+ port: number;
12
+ /** Loopback by default; the socket token is not a browser credential. */
13
+ host?: string;
14
+ /** Directory holding the built SPA. Defaults to the bundled dist/web. */
15
+ assetsDir?: string;
16
+ onNotice?: (message: string) => void;
17
+ /** Watch this registry directory for running sessions. */
18
+ registryDir: string;
19
+ /** Command launching created sessions; overridable so tests can stand in a fake. */
20
+ spawnCommand?: string;
21
+ /** Provider auth runtime; overridable so tests can stand in a fake for Pi's ModelRuntime. */
22
+ authRuntime?: () => Promise<AuthRuntime>;
23
+ }
24
+ interface WebServer {
25
+ readonly url: string;
26
+ readonly port: number;
27
+ close(): Promise<void>;
28
+ }
29
+ //#endregion
30
+ export { SessionAttachment, WebServer, WebServerOptions };
31
+ //# sourceMappingURL=bridge.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.cts","names":[],"sources":["../../../src/types/bridge.ts"],"mappings":";;;;UAIiB;;EAEf,KAAK,OAAO;EACZ;;UAce;EACf;;EAEA;;EAEA;EACA,YAAY;;EAEZ;;EAEA;;EAEA,oBAAoB,QAAQ;;UAGb;WACN;WACA;EACT,SAAS"}
@@ -0,0 +1,31 @@
1
+ import { AuthRuntime } from "./auth.mjs";
2
+ import { SessionFrame } from "./session.mjs";
3
+ //#region src/types/bridge.d.ts
4
+ /** One live attachment to a doompi-server socket. */
5
+ interface SessionAttachment {
6
+ /** Forwards a command frame to the agent. Ignored once closed. */
7
+ send(frame: SessionFrame): void;
8
+ close(): void;
9
+ }
10
+ interface WebServerOptions {
11
+ port: number;
12
+ /** Loopback by default; the socket token is not a browser credential. */
13
+ host?: string;
14
+ /** Directory holding the built SPA. Defaults to the bundled dist/web. */
15
+ assetsDir?: string;
16
+ onNotice?: (message: string) => void;
17
+ /** Watch this registry directory for running sessions. */
18
+ registryDir: string;
19
+ /** Command launching created sessions; overridable so tests can stand in a fake. */
20
+ spawnCommand?: string;
21
+ /** Provider auth runtime; overridable so tests can stand in a fake for Pi's ModelRuntime. */
22
+ authRuntime?: () => Promise<AuthRuntime>;
23
+ }
24
+ interface WebServer {
25
+ readonly url: string;
26
+ readonly port: number;
27
+ close(): Promise<void>;
28
+ }
29
+ //#endregion
30
+ export { SessionAttachment, WebServer, WebServerOptions };
31
+ //# sourceMappingURL=bridge.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.mts","names":[],"sources":["../../../src/types/bridge.ts"],"mappings":";;;;UAIiB;;EAEf,KAAK,OAAO;EACZ;;UAce;EACf;;EAEA;;EAEA;EACA,YAAY;;EAEZ;;EAEA;;EAEA,oBAAoB,QAAQ;;UAGb;WACN;WACA;EACT,SAAS"}
@@ -0,0 +1,2 @@
1
+ const e=`hub_hello`,t=`session_frame`,n=`thread_backlog`,r=`thread_frame`;function i(t){return{type:e,protocol:1,channels:t}}function a(e,n){return{type:t,sessionId:e,frame:n}}function o(e,t,r){return{type:n,sessionId:e,threadId:t,frames:r}}function s(e,t,n){return{type:r,sessionId:e,threadId:t,frame:n}}exports.API_SESSION_QUERY_PARAM=`session`,exports.DIALOG_ANSWERED_TYPE=`extension_ui_answered`,exports.DIRECTORIES_API_ROUTE=`/api/directories`,exports.HISTORY_PAGE_TYPE=`history_page`,exports.HISTORY_REQUEST_TYPE=`history_request`,exports.HUB_HELLO_TYPE=e,exports.HUB_PROTOCOL_VERSION=1,exports.HUB_RESYNCED_TYPE=`hub_resynced`,exports.HUB_ROLE=`hub`,exports.MINOR_MODE_ENTRY_TYPE=`doom-minor-modes`,exports.SESSIONS_API_ROUTE=`/api/sessions`,exports.SESSIONS_SNAPSHOT_TYPE=`sessions_snapshot`,exports.SESSION_BACKLOG_TYPE=`session_backlog`,exports.SESSION_COMMAND_TYPE=`session_command`,exports.SESSION_FRAME_TYPE=t,exports.SESSION_REMOVED_TYPE=`session_removed`,exports.SESSION_UPSERT_TYPE=`session_upsert`,exports.SUBSCRIBE_THREAD_TYPE=`subscribe_thread`,exports.SUBSCRIBE_TYPE=`subscribe`,exports.THREAD_BACKLOG_TYPE=n,exports.THREAD_FRAME_TYPE=r,exports.UNSUBSCRIBE_THREAD_TYPE=`unsubscribe_thread`,exports.UNSUBSCRIBE_TYPE=`unsubscribe`,exports.hubHello=i,exports.sessionFrameEnvelope=a,exports.threadBacklog=o,exports.threadFrameEnvelope=s;
2
+ //# sourceMappingURL=hub.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hub.cjs","names":[],"sources":["../../../src/types/hub.ts"],"sourcesContent":["import type { BridgeState, SessionFrame } from './session.ts';\n\n/**\n * Wire vocabulary between the hub and its pages.\n *\n * One WebSocket carries every session: hub-level frames describe the set of\n * sessions, and session-scoped traffic travels enveloped with the session id.\n * Field naming follows @earendil-works/pi-protocol so a future swap to the\n * upstream session server stays a transport change, not a rename.\n */\nexport const HUB_PROTOCOL_VERSION = 1;\n\n/** Role marker in the health payload; doompi-server probes for it before binding the port. */\nexport const HUB_ROLE = 'hub';\n\n/** REST endpoint for creating sessions; the page posts {cwd, name?} and receives {sessionId}. */\nexport const SESSIONS_API_ROUTE = '/api/sessions';\n\n/**\n * REST endpoint behind the new-session directory picker; the page sends the\n * typed path as ?q= and receives {directories}: the children of the parent\n * directory whose names match the trailing segment as a regular expression.\n */\nexport const DIRECTORIES_API_ROUTE = '/api/directories';\n\n/**\n * Query parameter that routes a package API request to one session's server.\n * Without it the request is answered by a hub-scoped API in this process.\n */\nexport const API_SESSION_QUERY_PARAM = 'session';\n\n/** What the agent is doing right now, derived from its frame stream. */\nexport type SessionPhase = 'idle' | 'turn' | 'compaction' | 'retry';\n\nexport interface SessionGitStatus {\n branch: string;\n /** True when the working tree has uncommitted changes; rendered as a star. */\n dirty: boolean;\n}\n\n/**\n * Everything the rail needs to render one session without subscribing to it.\n */\nexport interface SessionSummary {\n id: string;\n name: string;\n cwd: string;\n /** ISO 8601, from the session's registry record. */\n createdAt: string;\n /** ISO 8601, bumped whenever the session's frame stream moves. */\n updatedAt: string;\n phase: SessionPhase;\n /** ISO 8601 timestamp of the last phase change; drives \"running · 12m\". */\n phaseSince: string;\n /** The hub's own attachment to the session socket. */\n attach: BridgeState;\n /** Human-readable cause, present when attach is refused or closed. */\n attachReason?: string;\n pendingMessageCount: number;\n /** False until the first prompt is forwarded; \"fresh session · nothing sent yet\". */\n everPrompted: boolean;\n /** True while the agent waits on a dialog answer; \"waiting for your input\". */\n awaitingInput: boolean;\n /** ISO 8601 of the last settled run, once one finished. */\n lastSettledAt?: string;\n /** Shown in the refused overlay so the user can find the competing client. */\n socketPath: string;\n /** Where this session serves its package APIs, when it serves any; the hub proxies there. */\n apiSocketPath?: string;\n /** Omitted when the cwd is not a git repository or git is unavailable. */\n git?: SessionGitStatus;\n}\n\n/**\n * Per-plugin session data travels as ChannelFrame: the channel name is the\n * frame type and the payload shape belongs to the plugin. Re-exported so in-package code keeps importing wire shapes\n * from this one root.\n */\nexport type { ChannelFrame } from '@agimon-ai/doompi-web-contracts';\n\nexport const HUB_HELLO_TYPE = 'hub_hello';\nexport const SESSIONS_SNAPSHOT_TYPE = 'sessions_snapshot';\nexport const SESSION_UPSERT_TYPE = 'session_upsert';\nexport const SESSION_REMOVED_TYPE = 'session_removed';\nexport const SESSION_BACKLOG_TYPE = 'session_backlog';\nexport const SESSION_FRAME_TYPE = 'session_frame';\n/** Hub-synthesized close for an answered dialog; Pi itself never announces one. */\nexport const DIALOG_ANSWERED_TYPE = 'extension_ui_answered';\n/**\n * Sync rebuilt the artifacts this page is running against.\n *\n * The plugin bundle the browser loaded is one of them, and a bundle only\n * changes on disk: nothing about the running page notices. The page reloads\n * itself rather than staying on surfaces the repository no longer describes.\n */\nexport const HUB_RESYNCED_TYPE = 'hub_resynced';\n\n/**\n * The custom session entry the DoomPi runtime journals with its minor-mode\n * catalog projection; it arrives inside Pi's entry_appended frames. The shape\n * mirrors MinorModeProjection in doompi-extension-contracts.\n */\nexport const MINOR_MODE_ENTRY_TYPE = 'doom-minor-modes';\n\nexport interface MinorModeActionProjection {\n id: string;\n label: string;\n description: string;\n enabled: boolean;\n disabledReason?: string;\n needsInput: boolean;\n}\n\nexport interface MinorModeRecordProjection {\n id: string;\n label: string;\n description: string;\n order: number;\n activation: 'inactive' | 'activating' | 'active' | 'deactivating';\n condition: string;\n detail?: string;\n actions: MinorModeActionProjection[];\n}\n\nexport interface MinorModeProjection {\n version: 1;\n revision: number;\n modes: MinorModeRecordProjection[];\n}\nexport const SUBSCRIBE_TYPE = 'subscribe';\nexport const UNSUBSCRIBE_TYPE = 'unsubscribe';\nexport const SESSION_COMMAND_TYPE = 'session_command';\n/** A page follows one thread of a session: a journal a plugin's hub source names, such as a subagent run. */\nexport const SUBSCRIBE_THREAD_TYPE = 'subscribe_thread';\nexport const UNSUBSCRIBE_THREAD_TYPE = 'unsubscribe_thread';\nexport const THREAD_BACKLOG_TYPE = 'thread_backlog';\nexport const THREAD_FRAME_TYPE = 'thread_frame';\n\n/** A page asks for the transcript before what it already holds. */\nexport const HISTORY_REQUEST_TYPE = 'history_request';\n/** One older window, oldest first, answering a history_request. */\nexport const HISTORY_PAGE_TYPE = 'history_page';\n\n/**\n * How many journalled messages one page carries. Small enough that prepending\n * a window never blocks the reader's scroll, large enough that a flick through\n * a long transcript does not turn into a hundred round trips.\n */\nexport const HISTORY_PAGE_SIZE = 100;\n\nexport interface HistoryRequestFrame {\n type: typeof HISTORY_REQUEST_TYPE;\n sessionId: string;\n /** Journal id of the oldest entry the page holds; absent asks for the newest window. */\n before?: string;\n limit?: number;\n}\n\nexport interface HistoryPageFrame {\n type: typeof HISTORY_PAGE_TYPE;\n sessionId: string;\n /** Entry frames, oldest first, to prepend above what the page holds. */\n frames: Record<string, unknown>[];\n /** The cursor for the next older window; null when the transcript starts here. */\n cursor: string | null;\n /** Whether anything older than this window exists. */\n hasMore: boolean;\n /** Echoed so a page can drop an answer to a question it has since abandoned. */\n before?: string;\n}\n\n/** First frame on every page socket, before the snapshot. */\nexport interface HubHelloFrame {\n type: typeof HUB_HELLO_TYPE;\n protocol: number;\n /** Frame types of the hub's loaded data channels; lets a panel tell \"plugin server not installed\" from \"no data\". */\n channels: string[];\n}\n\nexport interface SessionsSnapshotFrame {\n type: typeof SESSIONS_SNAPSHOT_TYPE;\n sessions: SessionSummary[];\n}\n\nexport interface SessionUpsertFrame {\n type: typeof SESSION_UPSERT_TYPE;\n session: SessionSummary;\n}\n\nexport interface SessionRemovedFrame {\n type: typeof SESSION_REMOVED_TYPE;\n sessionId: string;\n}\n\n/** Reply to subscribe: recent history from the hub's ring, then live frames follow. */\nexport interface SessionBacklogFrame {\n type: typeof SESSION_BACKLOG_TYPE;\n sessionId: string;\n frames: SessionFrame[];\n /** Frames the bounded ring had to discard before this page subscribed. */\n dropped: number;\n}\n\n/** One live agent frame, addressed to subscribers of its session. */\nexport interface SessionFrameEnvelope {\n type: typeof SESSION_FRAME_TYPE;\n sessionId: string;\n frame: SessionFrame;\n}\n\nexport interface SubscribeFrame {\n type: typeof SUBSCRIBE_TYPE;\n sessionId: string;\n}\n\nexport interface UnsubscribeFrame {\n type: typeof UNSUBSCRIBE_TYPE;\n sessionId: string;\n}\n\n/** One command frame from the page, addressed to a session's agent. */\nexport interface SessionCommandFrame {\n type: typeof SESSION_COMMAND_TYPE;\n sessionId: string;\n frame: SessionFrame;\n}\n\nexport function hubHello(channels: string[]): HubHelloFrame {\n return { type: HUB_HELLO_TYPE, protocol: HUB_PROTOCOL_VERSION, channels };\n}\n\nexport function sessionFrameEnvelope(sessionId: string, frame: SessionFrame): SessionFrameEnvelope {\n return { type: SESSION_FRAME_TYPE, sessionId, frame };\n}\n\nexport function sessionCommand(sessionId: string, frame: SessionFrame): SessionCommandFrame {\n return { type: SESSION_COMMAND_TYPE, sessionId, frame };\n}\n\nexport function subscribeFrame(sessionId: string): SubscribeFrame {\n return { type: SUBSCRIBE_TYPE, sessionId };\n}\n\nexport function unsubscribeFrame(sessionId: string): UnsubscribeFrame {\n return { type: UNSUBSCRIBE_TYPE, sessionId };\n}\n\nexport interface SubscribeThreadFrame {\n type: typeof SUBSCRIBE_THREAD_TYPE;\n sessionId: string;\n threadId: string;\n}\n\nexport interface UnsubscribeThreadFrame {\n type: typeof UNSUBSCRIBE_THREAD_TYPE;\n sessionId: string;\n threadId: string;\n}\n\n/** Reply to subscribe_thread: the journal's newest entries as entry_appended frames, then live ones follow. */\nexport interface ThreadBacklogFrame {\n type: typeof THREAD_BACKLOG_TYPE;\n sessionId: string;\n threadId: string;\n frames: SessionFrame[];\n}\n\n/** One journal entry a followed thread gained, addressed to the pages following it. */\nexport interface ThreadFrameEnvelope {\n type: typeof THREAD_FRAME_TYPE;\n sessionId: string;\n threadId: string;\n frame: SessionFrame;\n}\n\nexport function subscribeThreadFrame(sessionId: string, threadId: string): SubscribeThreadFrame {\n return { type: SUBSCRIBE_THREAD_TYPE, sessionId, threadId };\n}\n\nexport function unsubscribeThreadFrame(sessionId: string, threadId: string): UnsubscribeThreadFrame {\n return { type: UNSUBSCRIBE_THREAD_TYPE, sessionId, threadId };\n}\n\nexport function threadBacklog(sessionId: string, threadId: string, frames: SessionFrame[]): ThreadBacklogFrame {\n return { type: THREAD_BACKLOG_TYPE, sessionId, threadId, frames };\n}\n\nexport function threadFrameEnvelope(sessionId: string, threadId: string, frame: SessionFrame): ThreadFrameEnvelope {\n return { type: THREAD_FRAME_TYPE, sessionId, threadId, frame };\n}\n"],"mappings":"AAUA,MAsEa,EAAiB,YAKjB,EAAqB,gBAkDrB,EAAsB,iBACtB,EAAoB,eA2FjC,SAAgB,EAAS,EAAmC,CAC1D,MAAO,CAAE,KAAM,EAAgB,SAAA,EAAgC,UAAS,CAC1E,CAEA,SAAgB,EAAqB,EAAmB,EAA2C,CACjG,MAAO,CAAE,KAAM,EAAoB,YAAW,OAAM,CACtD,CAkDA,SAAgB,EAAc,EAAmB,EAAkB,EAA4C,CAC7G,MAAO,CAAE,KAAM,EAAqB,YAAW,WAAU,QAAO,CAClE,CAEA,SAAgB,EAAoB,EAAmB,EAAkB,EAA0C,CACjH,MAAO,CAAE,KAAM,EAAmB,YAAW,WAAU,OAAM,CAC/D"}
@@ -0,0 +1,58 @@
1
+ import { BridgeState } from "./session.cjs";
2
+ import "@agimon-ai/doompi-web-contracts";
3
+ //#region src/types/hub.d.ts
4
+ /**
5
+ * Wire vocabulary between the hub and its pages.
6
+ *
7
+ * One WebSocket carries every session: hub-level frames describe the set of
8
+ * sessions, and session-scoped traffic travels enveloped with the session id.
9
+ * Field naming follows @earendil-works/pi-protocol so a future swap to the
10
+ * upstream session server stays a transport change, not a rename.
11
+ */
12
+ declare const HUB_PROTOCOL_VERSION = 1;
13
+ /** Role marker in the health payload; doompi-server probes for it before binding the port. */
14
+ declare const HUB_ROLE = "hub";
15
+ /** REST endpoint for creating sessions; the page posts {cwd, name?} and receives {sessionId}. */
16
+ declare const SESSIONS_API_ROUTE = "/api/sessions";
17
+ /** What the agent is doing right now, derived from its frame stream. */
18
+ type SessionPhase = 'idle' | 'turn' | 'compaction' | 'retry';
19
+ interface SessionGitStatus {
20
+ branch: string;
21
+ /** True when the working tree has uncommitted changes; rendered as a star. */
22
+ dirty: boolean;
23
+ }
24
+ /**
25
+ * Everything the rail needs to render one session without subscribing to it.
26
+ */
27
+ interface SessionSummary {
28
+ id: string;
29
+ name: string;
30
+ cwd: string;
31
+ /** ISO 8601, from the session's registry record. */
32
+ createdAt: string;
33
+ /** ISO 8601, bumped whenever the session's frame stream moves. */
34
+ updatedAt: string;
35
+ phase: SessionPhase;
36
+ /** ISO 8601 timestamp of the last phase change; drives "running · 12m". */
37
+ phaseSince: string;
38
+ /** The hub's own attachment to the session socket. */
39
+ attach: BridgeState;
40
+ /** Human-readable cause, present when attach is refused or closed. */
41
+ attachReason?: string;
42
+ pendingMessageCount: number;
43
+ /** False until the first prompt is forwarded; "fresh session · nothing sent yet". */
44
+ everPrompted: boolean;
45
+ /** True while the agent waits on a dialog answer; "waiting for your input". */
46
+ awaitingInput: boolean;
47
+ /** ISO 8601 of the last settled run, once one finished. */
48
+ lastSettledAt?: string;
49
+ /** Shown in the refused overlay so the user can find the competing client. */
50
+ socketPath: string;
51
+ /** Where this session serves its package APIs, when it serves any; the hub proxies there. */
52
+ apiSocketPath?: string;
53
+ /** Omitted when the cwd is not a git repository or git is unavailable. */
54
+ git?: SessionGitStatus;
55
+ }
56
+ //#endregion
57
+ export { HUB_PROTOCOL_VERSION, HUB_ROLE, SESSIONS_API_ROUTE, SessionGitStatus, SessionPhase, SessionSummary };
58
+ //# sourceMappingURL=hub.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hub.d.cts","names":[],"sources":["../../../src/types/hub.ts"],"mappings":";;;;;;;;;;;cAUa;;cAGA;;cAGA;;KAgBD;UAEK;EACf;;EAEA;;;;;UAMe;EACf;EACA;EACA;;EAEA;;EAEA;EACA,OAAO;;EAEP;;EAEA,QAAQ;;EAER;EACA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA,MAAM"}
@@ -0,0 +1,58 @@
1
+ import { BridgeState } from "./session.mjs";
2
+ import "@agimon-ai/doompi-web-contracts";
3
+ //#region src/types/hub.d.ts
4
+ /**
5
+ * Wire vocabulary between the hub and its pages.
6
+ *
7
+ * One WebSocket carries every session: hub-level frames describe the set of
8
+ * sessions, and session-scoped traffic travels enveloped with the session id.
9
+ * Field naming follows @earendil-works/pi-protocol so a future swap to the
10
+ * upstream session server stays a transport change, not a rename.
11
+ */
12
+ declare const HUB_PROTOCOL_VERSION = 1;
13
+ /** Role marker in the health payload; doompi-server probes for it before binding the port. */
14
+ declare const HUB_ROLE = "hub";
15
+ /** REST endpoint for creating sessions; the page posts {cwd, name?} and receives {sessionId}. */
16
+ declare const SESSIONS_API_ROUTE = "/api/sessions";
17
+ /** What the agent is doing right now, derived from its frame stream. */
18
+ type SessionPhase = 'idle' | 'turn' | 'compaction' | 'retry';
19
+ interface SessionGitStatus {
20
+ branch: string;
21
+ /** True when the working tree has uncommitted changes; rendered as a star. */
22
+ dirty: boolean;
23
+ }
24
+ /**
25
+ * Everything the rail needs to render one session without subscribing to it.
26
+ */
27
+ interface SessionSummary {
28
+ id: string;
29
+ name: string;
30
+ cwd: string;
31
+ /** ISO 8601, from the session's registry record. */
32
+ createdAt: string;
33
+ /** ISO 8601, bumped whenever the session's frame stream moves. */
34
+ updatedAt: string;
35
+ phase: SessionPhase;
36
+ /** ISO 8601 timestamp of the last phase change; drives "running · 12m". */
37
+ phaseSince: string;
38
+ /** The hub's own attachment to the session socket. */
39
+ attach: BridgeState;
40
+ /** Human-readable cause, present when attach is refused or closed. */
41
+ attachReason?: string;
42
+ pendingMessageCount: number;
43
+ /** False until the first prompt is forwarded; "fresh session · nothing sent yet". */
44
+ everPrompted: boolean;
45
+ /** True while the agent waits on a dialog answer; "waiting for your input". */
46
+ awaitingInput: boolean;
47
+ /** ISO 8601 of the last settled run, once one finished. */
48
+ lastSettledAt?: string;
49
+ /** Shown in the refused overlay so the user can find the competing client. */
50
+ socketPath: string;
51
+ /** Where this session serves its package APIs, when it serves any; the hub proxies there. */
52
+ apiSocketPath?: string;
53
+ /** Omitted when the cwd is not a git repository or git is unavailable. */
54
+ git?: SessionGitStatus;
55
+ }
56
+ //#endregion
57
+ export { HUB_PROTOCOL_VERSION, HUB_ROLE, SESSIONS_API_ROUTE, SessionGitStatus, SessionPhase, SessionSummary };
58
+ //# sourceMappingURL=hub.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hub.d.mts","names":[],"sources":["../../../src/types/hub.ts"],"mappings":";;;;;;;;;;;cAUa;;cAGA;;cAGA;;KAgBD;UAEK;EACf;;EAEA;;;;;UAMe;EACf;EACA;EACA;;EAEA;;EAEA;EACA,OAAO;;EAEP;;EAEA,QAAQ;;EAER;EACA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA,MAAM"}
@@ -0,0 +1,2 @@
1
+ const e=1,t=`hub`,n=`/api/sessions`,r=`/api/directories`,i=`session`,a=`hub_hello`,o=`sessions_snapshot`,s=`session_upsert`,c=`session_removed`,l=`session_backlog`,u=`session_frame`,d=`extension_ui_answered`,f=`hub_resynced`,p=`doom-minor-modes`,m=`subscribe`,h=`unsubscribe`,g=`session_command`,_=`subscribe_thread`,v=`unsubscribe_thread`,y=`thread_backlog`,b=`thread_frame`,x=`history_request`,S=`history_page`;function C(e){return{type:a,protocol:1,channels:e}}function w(e,t){return{type:u,sessionId:e,frame:t}}function T(e,t,n){return{type:y,sessionId:e,threadId:t,frames:n}}function E(e,t,n){return{type:b,sessionId:e,threadId:t,frame:n}}export{i as API_SESSION_QUERY_PARAM,d as DIALOG_ANSWERED_TYPE,r as DIRECTORIES_API_ROUTE,S as HISTORY_PAGE_TYPE,x as HISTORY_REQUEST_TYPE,a as HUB_HELLO_TYPE,e as HUB_PROTOCOL_VERSION,f as HUB_RESYNCED_TYPE,t as HUB_ROLE,p as MINOR_MODE_ENTRY_TYPE,n as SESSIONS_API_ROUTE,o as SESSIONS_SNAPSHOT_TYPE,l as SESSION_BACKLOG_TYPE,g as SESSION_COMMAND_TYPE,u as SESSION_FRAME_TYPE,c as SESSION_REMOVED_TYPE,s as SESSION_UPSERT_TYPE,_ as SUBSCRIBE_THREAD_TYPE,m as SUBSCRIBE_TYPE,y as THREAD_BACKLOG_TYPE,b as THREAD_FRAME_TYPE,v as UNSUBSCRIBE_THREAD_TYPE,h as UNSUBSCRIBE_TYPE,C as hubHello,w as sessionFrameEnvelope,T as threadBacklog,E as threadFrameEnvelope};
2
+ //# sourceMappingURL=hub.mjs.map