@withbuddi/buddi 0.1.0-pre.37 → 0.1.0-pre.38

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 (703) hide show
  1. package/extension/background.js +130 -12
  2. package/extension/manifest.json +2 -2
  3. package/node_modules/@buddi/cli/dist/.tsbuildinfo +1 -1
  4. package/node_modules/@buddi/cli/dist/access-cmd.d.ts +10 -0
  5. package/node_modules/@buddi/cli/dist/access-cmd.d.ts.map +1 -0
  6. package/node_modules/@buddi/cli/dist/access-cmd.js +160 -0
  7. package/node_modules/@buddi/cli/dist/access-cmd.js.map +1 -0
  8. package/node_modules/@buddi/cli/dist/args.d.ts +10 -0
  9. package/node_modules/@buddi/cli/dist/args.d.ts.map +1 -1
  10. package/node_modules/@buddi/cli/dist/args.js +29 -1
  11. package/node_modules/@buddi/cli/dist/args.js.map +1 -1
  12. package/node_modules/@buddi/cli/dist/commands.d.ts.map +1 -1
  13. package/node_modules/@buddi/cli/dist/commands.js +28 -2
  14. package/node_modules/@buddi/cli/dist/commands.js.map +1 -1
  15. package/node_modules/@buddi/cli/dist/doctor-probes.d.ts.map +1 -1
  16. package/node_modules/@buddi/cli/dist/doctor-probes.js +54 -29
  17. package/node_modules/@buddi/cli/dist/doctor-probes.js.map +1 -1
  18. package/node_modules/@buddi/cli/dist/doctor.d.ts +36 -12
  19. package/node_modules/@buddi/cli/dist/doctor.d.ts.map +1 -1
  20. package/node_modules/@buddi/cli/dist/doctor.js +32 -9
  21. package/node_modules/@buddi/cli/dist/doctor.js.map +1 -1
  22. package/node_modules/@buddi/cli/dist/main.d.ts +2 -0
  23. package/node_modules/@buddi/cli/dist/main.d.ts.map +1 -1
  24. package/node_modules/@buddi/cli/dist/main.js +18 -1
  25. package/node_modules/@buddi/cli/dist/main.js.map +1 -1
  26. package/node_modules/@buddi/core/dist/.tsbuildinfo +1 -1
  27. package/node_modules/@buddi/core/dist/agents/catalog.d.ts +2 -0
  28. package/node_modules/@buddi/core/dist/agents/catalog.d.ts.map +1 -1
  29. package/node_modules/@buddi/core/dist/agents/catalog.js +1 -0
  30. package/node_modules/@buddi/core/dist/agents/catalog.js.map +1 -1
  31. package/node_modules/@buddi/core/dist/agents/edit.d.ts +2 -0
  32. package/node_modules/@buddi/core/dist/agents/edit.d.ts.map +1 -1
  33. package/node_modules/@buddi/core/dist/agents/edit.js +3 -1
  34. package/node_modules/@buddi/core/dist/agents/edit.js.map +1 -1
  35. package/node_modules/@buddi/core/dist/agents/frontmatter.d.ts +11 -0
  36. package/node_modules/@buddi/core/dist/agents/frontmatter.d.ts.map +1 -1
  37. package/node_modules/@buddi/core/dist/agents/frontmatter.js +7 -0
  38. package/node_modules/@buddi/core/dist/agents/frontmatter.js.map +1 -1
  39. package/node_modules/@buddi/core/dist/index.d.ts +1 -0
  40. package/node_modules/@buddi/core/dist/index.d.ts.map +1 -1
  41. package/node_modules/@buddi/core/dist/index.js +1 -0
  42. package/node_modules/@buddi/core/dist/index.js.map +1 -1
  43. package/node_modules/@buddi/core/dist/plugin/index.d.ts +3 -0
  44. package/node_modules/@buddi/core/dist/plugin/index.d.ts.map +1 -1
  45. package/node_modules/@buddi/core/dist/plugin/index.js +3 -0
  46. package/node_modules/@buddi/core/dist/plugin/index.js.map +1 -1
  47. package/node_modules/@buddi/core/dist/plugin/version.d.ts +1 -1
  48. package/node_modules/@buddi/core/dist/plugin/version.js +1 -1
  49. package/node_modules/@buddi/core/dist/questions/store.d.ts +12 -0
  50. package/node_modules/@buddi/core/dist/questions/store.d.ts.map +1 -1
  51. package/node_modules/@buddi/core/dist/questions/store.js +11 -1
  52. package/node_modules/@buddi/core/dist/questions/store.js.map +1 -1
  53. package/node_modules/@buddi/core/dist/questions/types.d.ts +8 -0
  54. package/node_modules/@buddi/core/dist/questions/types.d.ts.map +1 -1
  55. package/node_modules/@buddi/core/dist/questions/types.js +15 -0
  56. package/node_modules/@buddi/core/dist/questions/types.js.map +1 -1
  57. package/node_modules/@buddi/core/dist/queue/jobs.d.ts +8 -0
  58. package/node_modules/@buddi/core/dist/queue/jobs.d.ts.map +1 -1
  59. package/node_modules/@buddi/core/dist/queue/jobs.js +9 -3
  60. package/node_modules/@buddi/core/dist/queue/jobs.js.map +1 -1
  61. package/node_modules/@buddi/core/dist/queue/worker.d.ts +2 -0
  62. package/node_modules/@buddi/core/dist/queue/worker.d.ts.map +1 -1
  63. package/node_modules/@buddi/core/dist/queue/worker.js +2 -1
  64. package/node_modules/@buddi/core/dist/queue/worker.js.map +1 -1
  65. package/node_modules/@buddi/core/dist/registry.d.ts +7 -1
  66. package/node_modules/@buddi/core/dist/registry.d.ts.map +1 -1
  67. package/node_modules/@buddi/core/dist/registry.js +73 -11
  68. package/node_modules/@buddi/core/dist/registry.js.map +1 -1
  69. package/node_modules/@buddi/core/dist/routes.d.ts +153 -0
  70. package/node_modules/@buddi/core/dist/routes.d.ts.map +1 -0
  71. package/node_modules/@buddi/core/dist/routes.js +47 -0
  72. package/node_modules/@buddi/core/dist/routes.js.map +1 -0
  73. package/node_modules/@buddi/core/dist/scheduler/missions.d.ts +5 -3
  74. package/node_modules/@buddi/core/dist/scheduler/missions.d.ts.map +1 -1
  75. package/node_modules/@buddi/core/dist/scheduler/missions.js +9 -7
  76. package/node_modules/@buddi/core/dist/scheduler/missions.js.map +1 -1
  77. package/node_modules/@buddi/core/dist/scheduler/types.d.ts +7 -0
  78. package/node_modules/@buddi/core/dist/scheduler/types.d.ts.map +1 -1
  79. package/node_modules/@buddi/core/dist/scheduler/types.js +1 -0
  80. package/node_modules/@buddi/core/dist/scheduler/types.js.map +1 -1
  81. package/node_modules/@buddi/core/dist/tools.d.ts +59 -2
  82. package/node_modules/@buddi/core/dist/tools.d.ts.map +1 -1
  83. package/node_modules/@buddi/core/dist/tools.js.map +1 -1
  84. package/node_modules/@buddi/core/migrations/075_access_providers.sql +43 -0
  85. package/node_modules/@buddi/core/migrations/076_mission_browser.sql +7 -0
  86. package/node_modules/@buddi/gateway/dist/.tsbuildinfo +1 -1
  87. package/node_modules/@buddi/gateway/dist/agents/canvas.d.ts +12 -12
  88. package/node_modules/@buddi/gateway/dist/agents/catalogue-package.d.ts +38 -20
  89. package/node_modules/@buddi/gateway/dist/agents/catalogue-package.d.ts.map +1 -1
  90. package/node_modules/@buddi/gateway/dist/agents/catalogue-package.js +6 -0
  91. package/node_modules/@buddi/gateway/dist/agents/catalogue-package.js.map +1 -1
  92. package/node_modules/@buddi/gateway/dist/agents/delegation.d.ts +10 -0
  93. package/node_modules/@buddi/gateway/dist/agents/delegation.d.ts.map +1 -1
  94. package/node_modules/@buddi/gateway/dist/agents/delegation.js +1 -0
  95. package/node_modules/@buddi/gateway/dist/agents/delegation.js.map +1 -1
  96. package/node_modules/@buddi/gateway/dist/agents/platform-catalogue.d.ts +7 -1
  97. package/node_modules/@buddi/gateway/dist/agents/platform-catalogue.d.ts.map +1 -1
  98. package/node_modules/@buddi/gateway/dist/agents/platform-catalogue.js +15 -6
  99. package/node_modules/@buddi/gateway/dist/agents/platform-catalogue.js.map +1 -1
  100. package/node_modules/@buddi/gateway/dist/bootstrap.d.ts.map +1 -1
  101. package/node_modules/@buddi/gateway/dist/bootstrap.js +8 -0
  102. package/node_modules/@buddi/gateway/dist/bootstrap.js.map +1 -1
  103. package/node_modules/@buddi/gateway/dist/browser-host.d.ts +30 -1
  104. package/node_modules/@buddi/gateway/dist/browser-host.d.ts.map +1 -1
  105. package/node_modules/@buddi/gateway/dist/browser-host.js +56 -1
  106. package/node_modules/@buddi/gateway/dist/browser-host.js.map +1 -1
  107. package/node_modules/@buddi/gateway/dist/chat/session.d.ts.map +1 -1
  108. package/node_modules/@buddi/gateway/dist/chat/session.js +9 -5
  109. package/node_modules/@buddi/gateway/dist/chat/session.js.map +1 -1
  110. package/node_modules/@buddi/gateway/dist/index.d.ts +2 -1
  111. package/node_modules/@buddi/gateway/dist/index.d.ts.map +1 -1
  112. package/node_modules/@buddi/gateway/dist/index.js +2 -1
  113. package/node_modules/@buddi/gateway/dist/index.js.map +1 -1
  114. package/node_modules/@buddi/gateway/dist/missions/execute.d.ts +34 -1
  115. package/node_modules/@buddi/gateway/dist/missions/execute.d.ts.map +1 -1
  116. package/node_modules/@buddi/gateway/dist/missions/execute.js +92 -8
  117. package/node_modules/@buddi/gateway/dist/missions/execute.js.map +1 -1
  118. package/node_modules/@buddi/gateway/dist/missions/parked.d.ts +133 -0
  119. package/node_modules/@buddi/gateway/dist/missions/parked.d.ts.map +1 -0
  120. package/node_modules/@buddi/gateway/dist/missions/parked.js +249 -0
  121. package/node_modules/@buddi/gateway/dist/missions/parked.js.map +1 -0
  122. package/node_modules/@buddi/gateway/dist/serve.d.ts +7 -0
  123. package/node_modules/@buddi/gateway/dist/serve.d.ts.map +1 -1
  124. package/node_modules/@buddi/gateway/dist/serve.js +49 -9
  125. package/node_modules/@buddi/gateway/dist/serve.js.map +1 -1
  126. package/node_modules/@buddi/gateway/dist/surfaces/browser-cards.d.ts +18 -0
  127. package/node_modules/@buddi/gateway/dist/surfaces/browser-cards.d.ts.map +1 -0
  128. package/node_modules/@buddi/gateway/dist/surfaces/browser-cards.js +42 -0
  129. package/node_modules/@buddi/gateway/dist/surfaces/browser-cards.js.map +1 -0
  130. package/node_modules/@buddi/gateway/dist/surfaces/browser-continuation.js +1 -1
  131. package/node_modules/@buddi/gateway/dist/surfaces/browser-continuation.js.map +1 -1
  132. package/node_modules/@buddi/gateway/dist/surfaces/owner-request.d.ts +6 -0
  133. package/node_modules/@buddi/gateway/dist/surfaces/owner-request.d.ts.map +1 -1
  134. package/node_modules/@buddi/gateway/dist/surfaces/owner-request.js +7 -1
  135. package/node_modules/@buddi/gateway/dist/surfaces/owner-request.js.map +1 -1
  136. package/node_modules/@buddi/gateway/dist/system-context.d.ts.map +1 -1
  137. package/node_modules/@buddi/gateway/dist/system-context.js +6 -7
  138. package/node_modules/@buddi/gateway/dist/system-context.js.map +1 -1
  139. package/node_modules/@buddi/gateway/dist/telegram/browser-view.d.ts +17 -17
  140. package/node_modules/@buddi/gateway/dist/telegram/browser-view.d.ts.map +1 -1
  141. package/node_modules/@buddi/gateway/dist/telegram/browser-view.js +33 -37
  142. package/node_modules/@buddi/gateway/dist/telegram/browser-view.js.map +1 -1
  143. package/node_modules/@buddi/gateway/dist/telegram/channel.d.ts.map +1 -1
  144. package/node_modules/@buddi/gateway/dist/telegram/channel.js +17 -1
  145. package/node_modules/@buddi/gateway/dist/telegram/channel.js.map +1 -1
  146. package/node_modules/@buddi/gateway/dist/telegram/main.d.ts.map +1 -1
  147. package/node_modules/@buddi/gateway/dist/telegram/main.js +11 -3
  148. package/node_modules/@buddi/gateway/dist/telegram/main.js.map +1 -1
  149. package/node_modules/@buddi/gateway/dist/telegram/surface.d.ts.map +1 -1
  150. package/node_modules/@buddi/gateway/dist/telegram/surface.js +10 -0
  151. package/node_modules/@buddi/gateway/dist/telegram/surface.js.map +1 -1
  152. package/node_modules/@buddi/gateway/dist/tips/facts.d.ts +8 -0
  153. package/node_modules/@buddi/gateway/dist/tips/facts.d.ts.map +1 -1
  154. package/node_modules/@buddi/gateway/dist/tips/facts.js +8 -2
  155. package/node_modules/@buddi/gateway/dist/tips/facts.js.map +1 -1
  156. package/node_modules/@buddi/gateway/dist/tips/rules.d.ts +2 -0
  157. package/node_modules/@buddi/gateway/dist/tips/rules.d.ts.map +1 -1
  158. package/node_modules/@buddi/gateway/dist/tips/rules.js +15 -0
  159. package/node_modules/@buddi/gateway/dist/tips/rules.js.map +1 -1
  160. package/node_modules/@buddi/gateway/dist/web/access/arrival.d.ts +24 -0
  161. package/node_modules/@buddi/gateway/dist/web/access/arrival.d.ts.map +1 -0
  162. package/node_modules/@buddi/gateway/dist/web/access/arrival.js +18 -0
  163. package/node_modules/@buddi/gateway/dist/web/access/arrival.js.map +1 -0
  164. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-api.d.ts +124 -0
  165. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-api.d.ts.map +1 -0
  166. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-api.js +206 -0
  167. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-api.js.map +1 -0
  168. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-setup.d.ts +163 -0
  169. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-setup.d.ts.map +1 -0
  170. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-setup.js +438 -0
  171. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-setup.js.map +1 -0
  172. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-token.d.ts +31 -0
  173. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-token.d.ts.map +1 -0
  174. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-token.js +74 -0
  175. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-token.js.map +1 -0
  176. package/node_modules/@buddi/gateway/dist/web/access/cloudflare.d.ts +145 -0
  177. package/node_modules/@buddi/gateway/dist/web/access/cloudflare.d.ts.map +1 -0
  178. package/node_modules/@buddi/gateway/dist/web/access/cloudflare.js +474 -0
  179. package/node_modules/@buddi/gateway/dist/web/access/cloudflare.js.map +1 -0
  180. package/node_modules/@buddi/gateway/dist/web/access/index.d.ts +9 -0
  181. package/node_modules/@buddi/gateway/dist/web/access/index.d.ts.map +1 -0
  182. package/node_modules/@buddi/gateway/dist/web/access/index.js +9 -0
  183. package/node_modules/@buddi/gateway/dist/web/access/index.js.map +1 -0
  184. package/node_modules/@buddi/gateway/dist/web/access/ingress.d.ts +30 -0
  185. package/node_modules/@buddi/gateway/dist/web/access/ingress.d.ts.map +1 -0
  186. package/node_modules/@buddi/gateway/dist/web/access/ingress.js +76 -0
  187. package/node_modules/@buddi/gateway/dist/web/access/ingress.js.map +1 -0
  188. package/node_modules/@buddi/gateway/dist/web/access/provider.d.ts +173 -0
  189. package/node_modules/@buddi/gateway/dist/web/access/provider.d.ts.map +1 -0
  190. package/node_modules/@buddi/gateway/dist/web/access/provider.js +11 -0
  191. package/node_modules/@buddi/gateway/dist/web/access/provider.js.map +1 -0
  192. package/node_modules/@buddi/gateway/dist/web/access/registry.d.ts +33 -0
  193. package/node_modules/@buddi/gateway/dist/web/access/registry.d.ts.map +1 -0
  194. package/node_modules/@buddi/gateway/dist/web/access/registry.js +32 -0
  195. package/node_modules/@buddi/gateway/dist/web/access/registry.js.map +1 -0
  196. package/node_modules/@buddi/gateway/dist/web/{tailscale.d.ts → access/tailscale.d.ts} +27 -0
  197. package/node_modules/@buddi/gateway/dist/web/access/tailscale.d.ts.map +1 -0
  198. package/node_modules/@buddi/gateway/dist/web/{tailscale.js → access/tailscale.js} +118 -3
  199. package/node_modules/@buddi/gateway/dist/web/access/tailscale.js.map +1 -0
  200. package/node_modules/@buddi/gateway/dist/web/agents.d.ts +2 -0
  201. package/node_modules/@buddi/gateway/dist/web/agents.d.ts.map +1 -1
  202. package/node_modules/@buddi/gateway/dist/web/agents.js +19 -1
  203. package/node_modules/@buddi/gateway/dist/web/agents.js.map +1 -1
  204. package/node_modules/@buddi/gateway/dist/web/api-routes.d.ts +4 -3
  205. package/node_modules/@buddi/gateway/dist/web/api-routes.d.ts.map +1 -1
  206. package/node_modules/@buddi/gateway/dist/web/api-routes.js +96 -17
  207. package/node_modules/@buddi/gateway/dist/web/api-routes.js.map +1 -1
  208. package/node_modules/@buddi/gateway/dist/web/attention.d.ts.map +1 -1
  209. package/node_modules/@buddi/gateway/dist/web/attention.js +11 -3
  210. package/node_modules/@buddi/gateway/dist/web/attention.js.map +1 -1
  211. package/node_modules/@buddi/gateway/dist/web/chat.d.ts +23 -0
  212. package/node_modules/@buddi/gateway/dist/web/chat.d.ts.map +1 -1
  213. package/node_modules/@buddi/gateway/dist/web/chat.js +62 -2
  214. package/node_modules/@buddi/gateway/dist/web/chat.js.map +1 -1
  215. package/node_modules/@buddi/gateway/dist/web/client-key.d.ts +3 -0
  216. package/node_modules/@buddi/gateway/dist/web/client-key.d.ts.map +1 -1
  217. package/node_modules/@buddi/gateway/dist/web/client-key.js +8 -1
  218. package/node_modules/@buddi/gateway/dist/web/client-key.js.map +1 -1
  219. package/node_modules/@buddi/gateway/dist/web/extension.d.ts +9 -1
  220. package/node_modules/@buddi/gateway/dist/web/extension.d.ts.map +1 -1
  221. package/node_modules/@buddi/gateway/dist/web/extension.js +61 -1
  222. package/node_modules/@buddi/gateway/dist/web/extension.js.map +1 -1
  223. package/node_modules/@buddi/gateway/dist/web/http.d.ts +7 -0
  224. package/node_modules/@buddi/gateway/dist/web/http.d.ts.map +1 -1
  225. package/node_modules/@buddi/gateway/dist/web/http.js +15 -1
  226. package/node_modules/@buddi/gateway/dist/web/http.js.map +1 -1
  227. package/node_modules/@buddi/gateway/dist/web/index.d.ts +1 -1
  228. package/node_modules/@buddi/gateway/dist/web/index.d.ts.map +1 -1
  229. package/node_modules/@buddi/gateway/dist/web/index.js +1 -1
  230. package/node_modules/@buddi/gateway/dist/web/index.js.map +1 -1
  231. package/node_modules/@buddi/gateway/dist/web/onboarding.js +1 -1
  232. package/node_modules/@buddi/gateway/dist/web/onboarding.js.map +1 -1
  233. package/node_modules/@buddi/gateway/dist/web/server.d.ts +38 -2
  234. package/node_modules/@buddi/gateway/dist/web/server.d.ts.map +1 -1
  235. package/node_modules/@buddi/gateway/dist/web/server.js +576 -145
  236. package/node_modules/@buddi/gateway/dist/web/server.js.map +1 -1
  237. package/node_modules/@buddi/gateway/dist/web/sessions.d.ts +24 -14
  238. package/node_modules/@buddi/gateway/dist/web/sessions.d.ts.map +1 -1
  239. package/node_modules/@buddi/gateway/dist/web/sessions.js +42 -14
  240. package/node_modules/@buddi/gateway/dist/web/sessions.js.map +1 -1
  241. package/node_modules/@buddi/gateway/dist/web/signed-out.d.ts +10 -0
  242. package/node_modules/@buddi/gateway/dist/web/signed-out.d.ts.map +1 -1
  243. package/node_modules/@buddi/gateway/dist/web/signed-out.js +47 -0
  244. package/node_modules/@buddi/gateway/dist/web/signed-out.js.map +1 -1
  245. package/node_modules/@buddi/install/dist/.tsbuildinfo +1 -1
  246. package/node_modules/@buddi/runtime/dist/.tsbuildinfo +1 -1
  247. package/node_modules/@buddi/runtime/dist/delegate.d.ts +8 -0
  248. package/node_modules/@buddi/runtime/dist/delegate.d.ts.map +1 -1
  249. package/node_modules/@buddi/runtime/dist/delegate.js +19 -1
  250. package/node_modules/@buddi/runtime/dist/delegate.js.map +1 -1
  251. package/node_modules/@buddi/runtime/dist/loop.d.ts +2 -0
  252. package/node_modules/@buddi/runtime/dist/loop.d.ts.map +1 -1
  253. package/node_modules/@buddi/runtime/dist/loop.js +18 -4
  254. package/node_modules/@buddi/runtime/dist/loop.js.map +1 -1
  255. package/node_modules/@buddi/tool-artifacts/dist/.tsbuildinfo +1 -1
  256. package/node_modules/@buddi/tool-browser/dist/.tsbuildinfo +1 -1
  257. package/node_modules/@buddi/tool-browser/dist/availability.d.ts +2 -2
  258. package/node_modules/@buddi/tool-browser/dist/availability.d.ts.map +1 -1
  259. package/node_modules/@buddi/tool-browser/dist/availability.js +2 -2
  260. package/node_modules/@buddi/tool-browser/dist/availability.js.map +1 -1
  261. package/node_modules/@buddi/tool-browser/dist/controller.d.ts +76 -58
  262. package/node_modules/@buddi/tool-browser/dist/controller.d.ts.map +1 -1
  263. package/node_modules/@buddi/tool-browser/dist/controller.js +751 -219
  264. package/node_modules/@buddi/tool-browser/dist/controller.js.map +1 -1
  265. package/node_modules/@buddi/tool-browser/dist/extension.d.ts +12 -0
  266. package/node_modules/@buddi/tool-browser/dist/extension.d.ts.map +1 -1
  267. package/node_modules/@buddi/tool-browser/dist/extension.js +8 -0
  268. package/node_modules/@buddi/tool-browser/dist/extension.js.map +1 -1
  269. package/node_modules/@buddi/tool-browser/dist/index.d.ts +13 -5
  270. package/node_modules/@buddi/tool-browser/dist/index.d.ts.map +1 -1
  271. package/node_modules/@buddi/tool-browser/dist/index.js +29 -12
  272. package/node_modules/@buddi/tool-browser/dist/index.js.map +1 -1
  273. package/node_modules/@buddi/tool-browser/dist/manager.d.ts +39 -21
  274. package/node_modules/@buddi/tool-browser/dist/manager.d.ts.map +1 -1
  275. package/node_modules/@buddi/tool-browser/dist/manager.js +229 -136
  276. package/node_modules/@buddi/tool-browser/dist/manager.js.map +1 -1
  277. package/node_modules/@buddi/tool-browser/dist/routes.d.ts +130 -0
  278. package/node_modules/@buddi/tool-browser/dist/routes.d.ts.map +1 -0
  279. package/node_modules/@buddi/tool-browser/dist/routes.js +283 -0
  280. package/node_modules/@buddi/tool-browser/dist/routes.js.map +1 -0
  281. package/node_modules/@buddi/tool-browser/dist/service.d.ts +157 -114
  282. package/node_modules/@buddi/tool-browser/dist/service.d.ts.map +1 -1
  283. package/node_modules/@buddi/tool-browser/dist/service.js +379 -390
  284. package/node_modules/@buddi/tool-browser/dist/service.js.map +1 -1
  285. package/node_modules/@buddi/tool-browser/dist/settings.d.ts +88 -0
  286. package/node_modules/@buddi/tool-browser/dist/settings.d.ts.map +1 -0
  287. package/node_modules/@buddi/tool-browser/dist/settings.js +86 -0
  288. package/node_modules/@buddi/tool-browser/dist/settings.js.map +1 -0
  289. package/node_modules/@buddi/tool-browser/dist/telemetry.d.ts +203 -0
  290. package/node_modules/@buddi/tool-browser/dist/telemetry.d.ts.map +1 -0
  291. package/node_modules/@buddi/tool-browser/dist/telemetry.js +142 -0
  292. package/node_modules/@buddi/tool-browser/dist/telemetry.js.map +1 -0
  293. package/node_modules/@buddi/tool-browser/dist/types.d.ts +14 -3
  294. package/node_modules/@buddi/tool-browser/dist/types.d.ts.map +1 -1
  295. package/node_modules/@buddi/tool-browser/dist/types.js +9 -9
  296. package/node_modules/@buddi/tool-browser/dist/types.js.map +1 -1
  297. package/node_modules/@buddi/tool-email/dist/.tsbuildinfo +1 -1
  298. package/node_modules/@buddi/tool-host/dist/.tsbuildinfo +1 -1
  299. package/node_modules/@buddi/tool-mcp/dist/.tsbuildinfo +1 -1
  300. package/node_modules/@buddi/tool-memory/dist/.tsbuildinfo +1 -1
  301. package/node_modules/@buddi/tool-web/dist/.tsbuildinfo +1 -1
  302. package/node_modules/imapflow/CHANGELOG.md +15 -0
  303. package/node_modules/imapflow/dist/cjs/package-info.d.ts +1 -1
  304. package/node_modules/imapflow/dist/cjs/package-info.js +1 -1
  305. package/node_modules/imapflow/dist/cjs/search-compiler.js +21 -31
  306. package/node_modules/imapflow/dist/esm/package-info.d.ts +1 -1
  307. package/node_modules/imapflow/dist/esm/package-info.js +1 -1
  308. package/node_modules/imapflow/dist/esm/search-compiler.js +21 -31
  309. package/node_modules/imapflow/package.json +2 -2
  310. package/node_modules/pino/CONTRIBUTING.md +1 -5
  311. package/node_modules/pino/LICENSE +1 -1
  312. package/node_modules/pino/README.md +1 -1
  313. package/node_modules/pino/SECURITY.md +2 -18
  314. package/node_modules/pino/browser.js +8 -68
  315. package/node_modules/pino/docs/api.md +4 -75
  316. package/node_modules/pino/docs/asynchronous.md +3 -18
  317. package/node_modules/pino/docs/browser.md +0 -63
  318. package/node_modules/pino/docs/child-loggers.md +1 -22
  319. package/node_modules/pino/docs/ecosystem.md +0 -1
  320. package/node_modules/pino/docs/help.md +3 -11
  321. package/node_modules/pino/docs/lts.md +0 -29
  322. package/node_modules/pino/docs/pretty.md +0 -3
  323. package/node_modules/pino/docs/redaction.md +8 -3
  324. package/node_modules/pino/docs/transports.md +0 -71
  325. package/node_modules/pino/docs/web.md +14 -11
  326. package/node_modules/pino/eslint.config.js +7 -15
  327. package/node_modules/pino/lib/caller.js +0 -10
  328. package/node_modules/pino/lib/meta.js +1 -1
  329. package/node_modules/pino/lib/multistream.js +7 -15
  330. package/node_modules/pino/lib/redaction.js +3 -8
  331. package/node_modules/pino/lib/symbols.js +1 -3
  332. package/node_modules/pino/lib/tools.js +2 -2
  333. package/node_modules/pino/lib/transport.js +1 -14
  334. package/node_modules/pino/lib/worker.js +1 -4
  335. package/node_modules/pino/package.json +22 -14
  336. package/node_modules/pino/pino.d.ts +736 -709
  337. package/node_modules/pino/pino.js +2 -27
  338. package/node_modules/pino/test/basic.test.js +0 -66
  339. package/node_modules/pino/test/escaping.test.js +0 -17
  340. package/node_modules/pino/test/fixtures/ts/transpile.cjs +3 -3
  341. package/node_modules/pino/test/helper.js +2 -6
  342. package/node_modules/pino/test/multistream.test.js +0 -64
  343. package/node_modules/pino/test/redact.test.js +0 -30
  344. package/node_modules/pino/test/transport/core.test.js +0 -85
  345. package/node_modules/pino/test/transport/sync-false.test.js +0 -12
  346. package/node_modules/pino/test/transport/uses-pino-config.test.js +4 -4
  347. package/node_modules/pino/test/types/pino-import.test-d.cts +30 -0
  348. package/node_modules/pino/test/types/pino-multistream.test-d.ts +28 -0
  349. package/node_modules/pino/test/types/pino-top-export.test-d.ts +34 -0
  350. package/node_modules/pino/test/types/{pino-transport.tst.ts → pino-transport.test-d.ts} +70 -85
  351. package/node_modules/pino/test/types/pino-type-only.test-d.ts +75 -0
  352. package/node_modules/pino/test/types/{pino.tst.ts → pino.test-d.ts} +137 -135
  353. package/node_modules/pino/test/types/pino.ts +7 -14
  354. package/node_modules/pino/tsconfig.json +6 -6
  355. package/node_modules/real-require/README.md +6 -6
  356. package/node_modules/real-require/package.json +21 -10
  357. package/node_modules/real-require/src/index.js +2 -2
  358. package/node_modules/thread-stream/node_modules/real-require/LICENSE.md +21 -0
  359. package/node_modules/thread-stream/node_modules/real-require/README.md +51 -0
  360. package/node_modules/thread-stream/node_modules/real-require/package.json +38 -0
  361. package/node_modules/thread-stream/node_modules/real-require/src/index.js +14 -0
  362. package/package.json +2 -2
  363. package/packages/cli/dist/.tsbuildinfo +1 -1
  364. package/packages/cli/dist/access-cmd.d.ts +10 -0
  365. package/packages/cli/dist/access-cmd.d.ts.map +1 -0
  366. package/packages/cli/dist/access-cmd.js +160 -0
  367. package/packages/cli/dist/access-cmd.js.map +1 -0
  368. package/packages/cli/dist/args.d.ts +10 -0
  369. package/packages/cli/dist/args.d.ts.map +1 -1
  370. package/packages/cli/dist/args.js +29 -1
  371. package/packages/cli/dist/args.js.map +1 -1
  372. package/packages/cli/dist/commands.d.ts.map +1 -1
  373. package/packages/cli/dist/commands.js +28 -2
  374. package/packages/cli/dist/commands.js.map +1 -1
  375. package/packages/cli/dist/doctor-probes.d.ts.map +1 -1
  376. package/packages/cli/dist/doctor-probes.js +54 -29
  377. package/packages/cli/dist/doctor-probes.js.map +1 -1
  378. package/packages/cli/dist/doctor.d.ts +36 -12
  379. package/packages/cli/dist/doctor.d.ts.map +1 -1
  380. package/packages/cli/dist/doctor.js +32 -9
  381. package/packages/cli/dist/doctor.js.map +1 -1
  382. package/packages/cli/dist/main.d.ts +2 -0
  383. package/packages/cli/dist/main.d.ts.map +1 -1
  384. package/packages/cli/dist/main.js +18 -1
  385. package/packages/cli/dist/main.js.map +1 -1
  386. package/packages/core/dist/.tsbuildinfo +1 -1
  387. package/packages/core/dist/agents/catalog.d.ts +2 -0
  388. package/packages/core/dist/agents/catalog.d.ts.map +1 -1
  389. package/packages/core/dist/agents/catalog.js +1 -0
  390. package/packages/core/dist/agents/catalog.js.map +1 -1
  391. package/packages/core/dist/agents/edit.d.ts +2 -0
  392. package/packages/core/dist/agents/edit.d.ts.map +1 -1
  393. package/packages/core/dist/agents/edit.js +3 -1
  394. package/packages/core/dist/agents/edit.js.map +1 -1
  395. package/packages/core/dist/agents/frontmatter.d.ts +11 -0
  396. package/packages/core/dist/agents/frontmatter.d.ts.map +1 -1
  397. package/packages/core/dist/agents/frontmatter.js +7 -0
  398. package/packages/core/dist/agents/frontmatter.js.map +1 -1
  399. package/packages/core/dist/index.d.ts +1 -0
  400. package/packages/core/dist/index.d.ts.map +1 -1
  401. package/packages/core/dist/index.js +1 -0
  402. package/packages/core/dist/index.js.map +1 -1
  403. package/packages/core/dist/plugin/index.d.ts +3 -0
  404. package/packages/core/dist/plugin/index.d.ts.map +1 -1
  405. package/packages/core/dist/plugin/index.js +3 -0
  406. package/packages/core/dist/plugin/index.js.map +1 -1
  407. package/packages/core/dist/plugin/version.d.ts +1 -1
  408. package/packages/core/dist/plugin/version.js +1 -1
  409. package/packages/core/dist/questions/store.d.ts +12 -0
  410. package/packages/core/dist/questions/store.d.ts.map +1 -1
  411. package/packages/core/dist/questions/store.js +11 -1
  412. package/packages/core/dist/questions/store.js.map +1 -1
  413. package/packages/core/dist/questions/types.d.ts +8 -0
  414. package/packages/core/dist/questions/types.d.ts.map +1 -1
  415. package/packages/core/dist/questions/types.js +15 -0
  416. package/packages/core/dist/questions/types.js.map +1 -1
  417. package/packages/core/dist/queue/jobs.d.ts +8 -0
  418. package/packages/core/dist/queue/jobs.d.ts.map +1 -1
  419. package/packages/core/dist/queue/jobs.js +9 -3
  420. package/packages/core/dist/queue/jobs.js.map +1 -1
  421. package/packages/core/dist/queue/worker.d.ts +2 -0
  422. package/packages/core/dist/queue/worker.d.ts.map +1 -1
  423. package/packages/core/dist/queue/worker.js +2 -1
  424. package/packages/core/dist/queue/worker.js.map +1 -1
  425. package/packages/core/dist/registry.d.ts +7 -1
  426. package/packages/core/dist/registry.d.ts.map +1 -1
  427. package/packages/core/dist/registry.js +73 -11
  428. package/packages/core/dist/registry.js.map +1 -1
  429. package/packages/core/dist/routes.d.ts +153 -0
  430. package/packages/core/dist/routes.d.ts.map +1 -0
  431. package/packages/core/dist/routes.js +47 -0
  432. package/packages/core/dist/routes.js.map +1 -0
  433. package/packages/core/dist/scheduler/missions.d.ts +5 -3
  434. package/packages/core/dist/scheduler/missions.d.ts.map +1 -1
  435. package/packages/core/dist/scheduler/missions.js +9 -7
  436. package/packages/core/dist/scheduler/missions.js.map +1 -1
  437. package/packages/core/dist/scheduler/types.d.ts +7 -0
  438. package/packages/core/dist/scheduler/types.d.ts.map +1 -1
  439. package/packages/core/dist/scheduler/types.js +1 -0
  440. package/packages/core/dist/scheduler/types.js.map +1 -1
  441. package/packages/core/dist/tools.d.ts +59 -2
  442. package/packages/core/dist/tools.d.ts.map +1 -1
  443. package/packages/core/dist/tools.js.map +1 -1
  444. package/packages/core/migrations/075_access_providers.sql +43 -0
  445. package/packages/core/migrations/076_mission_browser.sql +7 -0
  446. package/packages/gateway/dist/.tsbuildinfo +1 -1
  447. package/packages/gateway/dist/agents/canvas.d.ts +12 -12
  448. package/packages/gateway/dist/agents/catalogue-package.d.ts +38 -20
  449. package/packages/gateway/dist/agents/catalogue-package.d.ts.map +1 -1
  450. package/packages/gateway/dist/agents/catalogue-package.js +6 -0
  451. package/packages/gateway/dist/agents/catalogue-package.js.map +1 -1
  452. package/packages/gateway/dist/agents/delegation.d.ts +10 -0
  453. package/packages/gateway/dist/agents/delegation.d.ts.map +1 -1
  454. package/packages/gateway/dist/agents/delegation.js +1 -0
  455. package/packages/gateway/dist/agents/delegation.js.map +1 -1
  456. package/packages/gateway/dist/agents/platform-catalogue.d.ts +7 -1
  457. package/packages/gateway/dist/agents/platform-catalogue.d.ts.map +1 -1
  458. package/packages/gateway/dist/agents/platform-catalogue.js +15 -6
  459. package/packages/gateway/dist/agents/platform-catalogue.js.map +1 -1
  460. package/packages/gateway/dist/bootstrap.d.ts.map +1 -1
  461. package/packages/gateway/dist/bootstrap.js +8 -0
  462. package/packages/gateway/dist/bootstrap.js.map +1 -1
  463. package/packages/gateway/dist/browser-host.d.ts +30 -1
  464. package/packages/gateway/dist/browser-host.d.ts.map +1 -1
  465. package/packages/gateway/dist/browser-host.js +56 -1
  466. package/packages/gateway/dist/browser-host.js.map +1 -1
  467. package/packages/gateway/dist/chat/session.d.ts.map +1 -1
  468. package/packages/gateway/dist/chat/session.js +9 -5
  469. package/packages/gateway/dist/chat/session.js.map +1 -1
  470. package/packages/gateway/dist/index.d.ts +2 -1
  471. package/packages/gateway/dist/index.d.ts.map +1 -1
  472. package/packages/gateway/dist/index.js +2 -1
  473. package/packages/gateway/dist/index.js.map +1 -1
  474. package/packages/gateway/dist/missions/execute.d.ts +34 -1
  475. package/packages/gateway/dist/missions/execute.d.ts.map +1 -1
  476. package/packages/gateway/dist/missions/execute.js +92 -8
  477. package/packages/gateway/dist/missions/execute.js.map +1 -1
  478. package/packages/gateway/dist/missions/parked.d.ts +133 -0
  479. package/packages/gateway/dist/missions/parked.d.ts.map +1 -0
  480. package/packages/gateway/dist/missions/parked.js +249 -0
  481. package/packages/gateway/dist/missions/parked.js.map +1 -0
  482. package/packages/gateway/dist/serve.d.ts +7 -0
  483. package/packages/gateway/dist/serve.d.ts.map +1 -1
  484. package/packages/gateway/dist/serve.js +49 -9
  485. package/packages/gateway/dist/serve.js.map +1 -1
  486. package/packages/gateway/dist/surfaces/browser-cards.d.ts +18 -0
  487. package/packages/gateway/dist/surfaces/browser-cards.d.ts.map +1 -0
  488. package/packages/gateway/dist/surfaces/browser-cards.js +42 -0
  489. package/packages/gateway/dist/surfaces/browser-cards.js.map +1 -0
  490. package/packages/gateway/dist/surfaces/browser-continuation.js +1 -1
  491. package/packages/gateway/dist/surfaces/browser-continuation.js.map +1 -1
  492. package/packages/gateway/dist/surfaces/owner-request.d.ts +6 -0
  493. package/packages/gateway/dist/surfaces/owner-request.d.ts.map +1 -1
  494. package/packages/gateway/dist/surfaces/owner-request.js +7 -1
  495. package/packages/gateway/dist/surfaces/owner-request.js.map +1 -1
  496. package/packages/gateway/dist/system-context.d.ts.map +1 -1
  497. package/packages/gateway/dist/system-context.js +6 -7
  498. package/packages/gateway/dist/system-context.js.map +1 -1
  499. package/packages/gateway/dist/telegram/browser-view.d.ts +17 -17
  500. package/packages/gateway/dist/telegram/browser-view.d.ts.map +1 -1
  501. package/packages/gateway/dist/telegram/browser-view.js +33 -37
  502. package/packages/gateway/dist/telegram/browser-view.js.map +1 -1
  503. package/packages/gateway/dist/telegram/channel.d.ts.map +1 -1
  504. package/packages/gateway/dist/telegram/channel.js +17 -1
  505. package/packages/gateway/dist/telegram/channel.js.map +1 -1
  506. package/packages/gateway/dist/telegram/main.d.ts.map +1 -1
  507. package/packages/gateway/dist/telegram/main.js +11 -3
  508. package/packages/gateway/dist/telegram/main.js.map +1 -1
  509. package/packages/gateway/dist/telegram/surface.d.ts.map +1 -1
  510. package/packages/gateway/dist/telegram/surface.js +10 -0
  511. package/packages/gateway/dist/telegram/surface.js.map +1 -1
  512. package/packages/gateway/dist/tips/facts.d.ts +8 -0
  513. package/packages/gateway/dist/tips/facts.d.ts.map +1 -1
  514. package/packages/gateway/dist/tips/facts.js +8 -2
  515. package/packages/gateway/dist/tips/facts.js.map +1 -1
  516. package/packages/gateway/dist/tips/rules.d.ts +2 -0
  517. package/packages/gateway/dist/tips/rules.d.ts.map +1 -1
  518. package/packages/gateway/dist/tips/rules.js +15 -0
  519. package/packages/gateway/dist/tips/rules.js.map +1 -1
  520. package/packages/gateway/dist/web/access/arrival.d.ts +24 -0
  521. package/packages/gateway/dist/web/access/arrival.d.ts.map +1 -0
  522. package/packages/gateway/dist/web/access/arrival.js +18 -0
  523. package/packages/gateway/dist/web/access/arrival.js.map +1 -0
  524. package/packages/gateway/dist/web/access/cloudflare-api.d.ts +124 -0
  525. package/packages/gateway/dist/web/access/cloudflare-api.d.ts.map +1 -0
  526. package/packages/gateway/dist/web/access/cloudflare-api.js +206 -0
  527. package/packages/gateway/dist/web/access/cloudflare-api.js.map +1 -0
  528. package/packages/gateway/dist/web/access/cloudflare-setup.d.ts +163 -0
  529. package/packages/gateway/dist/web/access/cloudflare-setup.d.ts.map +1 -0
  530. package/packages/gateway/dist/web/access/cloudflare-setup.js +438 -0
  531. package/packages/gateway/dist/web/access/cloudflare-setup.js.map +1 -0
  532. package/packages/gateway/dist/web/access/cloudflare-token.d.ts +31 -0
  533. package/packages/gateway/dist/web/access/cloudflare-token.d.ts.map +1 -0
  534. package/packages/gateway/dist/web/access/cloudflare-token.js +74 -0
  535. package/packages/gateway/dist/web/access/cloudflare-token.js.map +1 -0
  536. package/packages/gateway/dist/web/access/cloudflare.d.ts +145 -0
  537. package/packages/gateway/dist/web/access/cloudflare.d.ts.map +1 -0
  538. package/packages/gateway/dist/web/access/cloudflare.js +474 -0
  539. package/packages/gateway/dist/web/access/cloudflare.js.map +1 -0
  540. package/packages/gateway/dist/web/access/index.d.ts +9 -0
  541. package/packages/gateway/dist/web/access/index.d.ts.map +1 -0
  542. package/packages/gateway/dist/web/access/index.js +9 -0
  543. package/packages/gateway/dist/web/access/index.js.map +1 -0
  544. package/packages/gateway/dist/web/access/ingress.d.ts +30 -0
  545. package/packages/gateway/dist/web/access/ingress.d.ts.map +1 -0
  546. package/packages/gateway/dist/web/access/ingress.js +76 -0
  547. package/packages/gateway/dist/web/access/ingress.js.map +1 -0
  548. package/packages/gateway/dist/web/access/provider.d.ts +173 -0
  549. package/packages/gateway/dist/web/access/provider.d.ts.map +1 -0
  550. package/packages/gateway/dist/web/access/provider.js +11 -0
  551. package/packages/gateway/dist/web/access/provider.js.map +1 -0
  552. package/packages/gateway/dist/web/access/registry.d.ts +33 -0
  553. package/packages/gateway/dist/web/access/registry.d.ts.map +1 -0
  554. package/packages/gateway/dist/web/access/registry.js +32 -0
  555. package/packages/gateway/dist/web/access/registry.js.map +1 -0
  556. package/packages/gateway/dist/web/{tailscale.d.ts → access/tailscale.d.ts} +27 -0
  557. package/packages/gateway/dist/web/access/tailscale.d.ts.map +1 -0
  558. package/packages/gateway/dist/web/{tailscale.js → access/tailscale.js} +118 -3
  559. package/packages/gateway/dist/web/access/tailscale.js.map +1 -0
  560. package/packages/gateway/dist/web/agents.d.ts +2 -0
  561. package/packages/gateway/dist/web/agents.d.ts.map +1 -1
  562. package/packages/gateway/dist/web/agents.js +19 -1
  563. package/packages/gateway/dist/web/agents.js.map +1 -1
  564. package/packages/gateway/dist/web/api-routes.d.ts +4 -3
  565. package/packages/gateway/dist/web/api-routes.d.ts.map +1 -1
  566. package/packages/gateway/dist/web/api-routes.js +96 -17
  567. package/packages/gateway/dist/web/api-routes.js.map +1 -1
  568. package/packages/gateway/dist/web/attention.d.ts.map +1 -1
  569. package/packages/gateway/dist/web/attention.js +11 -3
  570. package/packages/gateway/dist/web/attention.js.map +1 -1
  571. package/packages/gateway/dist/web/chat.d.ts +23 -0
  572. package/packages/gateway/dist/web/chat.d.ts.map +1 -1
  573. package/packages/gateway/dist/web/chat.js +62 -2
  574. package/packages/gateway/dist/web/chat.js.map +1 -1
  575. package/packages/gateway/dist/web/client-key.d.ts +3 -0
  576. package/packages/gateway/dist/web/client-key.d.ts.map +1 -1
  577. package/packages/gateway/dist/web/client-key.js +8 -1
  578. package/packages/gateway/dist/web/client-key.js.map +1 -1
  579. package/packages/gateway/dist/web/extension.d.ts +9 -1
  580. package/packages/gateway/dist/web/extension.d.ts.map +1 -1
  581. package/packages/gateway/dist/web/extension.js +61 -1
  582. package/packages/gateway/dist/web/extension.js.map +1 -1
  583. package/packages/gateway/dist/web/http.d.ts +7 -0
  584. package/packages/gateway/dist/web/http.d.ts.map +1 -1
  585. package/packages/gateway/dist/web/http.js +15 -1
  586. package/packages/gateway/dist/web/http.js.map +1 -1
  587. package/packages/gateway/dist/web/index.d.ts +1 -1
  588. package/packages/gateway/dist/web/index.d.ts.map +1 -1
  589. package/packages/gateway/dist/web/index.js +1 -1
  590. package/packages/gateway/dist/web/index.js.map +1 -1
  591. package/packages/gateway/dist/web/onboarding.js +1 -1
  592. package/packages/gateway/dist/web/onboarding.js.map +1 -1
  593. package/packages/gateway/dist/web/server.d.ts +38 -2
  594. package/packages/gateway/dist/web/server.d.ts.map +1 -1
  595. package/packages/gateway/dist/web/server.js +576 -145
  596. package/packages/gateway/dist/web/server.js.map +1 -1
  597. package/packages/gateway/dist/web/sessions.d.ts +24 -14
  598. package/packages/gateway/dist/web/sessions.d.ts.map +1 -1
  599. package/packages/gateway/dist/web/sessions.js +42 -14
  600. package/packages/gateway/dist/web/sessions.js.map +1 -1
  601. package/packages/gateway/dist/web/signed-out.d.ts +10 -0
  602. package/packages/gateway/dist/web/signed-out.d.ts.map +1 -1
  603. package/packages/gateway/dist/web/signed-out.js +47 -0
  604. package/packages/gateway/dist/web/signed-out.js.map +1 -1
  605. package/packages/install/dist/.tsbuildinfo +1 -1
  606. package/packages/runtime/dist/.tsbuildinfo +1 -1
  607. package/packages/runtime/dist/delegate.d.ts +8 -0
  608. package/packages/runtime/dist/delegate.d.ts.map +1 -1
  609. package/packages/runtime/dist/delegate.js +19 -1
  610. package/packages/runtime/dist/delegate.js.map +1 -1
  611. package/packages/runtime/dist/loop.d.ts +2 -0
  612. package/packages/runtime/dist/loop.d.ts.map +1 -1
  613. package/packages/runtime/dist/loop.js +18 -4
  614. package/packages/runtime/dist/loop.js.map +1 -1
  615. package/packages/tools/artifacts/dist/.tsbuildinfo +1 -1
  616. package/packages/tools/browser/dist/.tsbuildinfo +1 -1
  617. package/packages/tools/browser/dist/availability.d.ts +2 -2
  618. package/packages/tools/browser/dist/availability.d.ts.map +1 -1
  619. package/packages/tools/browser/dist/availability.js +2 -2
  620. package/packages/tools/browser/dist/availability.js.map +1 -1
  621. package/packages/tools/browser/dist/controller.d.ts +76 -58
  622. package/packages/tools/browser/dist/controller.d.ts.map +1 -1
  623. package/packages/tools/browser/dist/controller.js +751 -219
  624. package/packages/tools/browser/dist/controller.js.map +1 -1
  625. package/packages/tools/browser/dist/extension.d.ts +12 -0
  626. package/packages/tools/browser/dist/extension.d.ts.map +1 -1
  627. package/packages/tools/browser/dist/extension.js +8 -0
  628. package/packages/tools/browser/dist/extension.js.map +1 -1
  629. package/packages/tools/browser/dist/index.d.ts +13 -5
  630. package/packages/tools/browser/dist/index.d.ts.map +1 -1
  631. package/packages/tools/browser/dist/index.js +29 -12
  632. package/packages/tools/browser/dist/index.js.map +1 -1
  633. package/packages/tools/browser/dist/manager.d.ts +39 -21
  634. package/packages/tools/browser/dist/manager.d.ts.map +1 -1
  635. package/packages/tools/browser/dist/manager.js +229 -136
  636. package/packages/tools/browser/dist/manager.js.map +1 -1
  637. package/packages/tools/browser/dist/routes.d.ts +130 -0
  638. package/packages/tools/browser/dist/routes.d.ts.map +1 -0
  639. package/packages/tools/browser/dist/routes.js +283 -0
  640. package/packages/tools/browser/dist/routes.js.map +1 -0
  641. package/packages/tools/browser/dist/service.d.ts +157 -114
  642. package/packages/tools/browser/dist/service.d.ts.map +1 -1
  643. package/packages/tools/browser/dist/service.js +379 -390
  644. package/packages/tools/browser/dist/service.js.map +1 -1
  645. package/packages/tools/browser/dist/settings.d.ts +88 -0
  646. package/packages/tools/browser/dist/settings.d.ts.map +1 -0
  647. package/packages/tools/browser/dist/settings.js +86 -0
  648. package/packages/tools/browser/dist/settings.js.map +1 -0
  649. package/packages/tools/browser/dist/telemetry.d.ts +203 -0
  650. package/packages/tools/browser/dist/telemetry.d.ts.map +1 -0
  651. package/packages/tools/browser/dist/telemetry.js +142 -0
  652. package/packages/tools/browser/dist/telemetry.js.map +1 -0
  653. package/packages/tools/browser/dist/types.d.ts +14 -3
  654. package/packages/tools/browser/dist/types.d.ts.map +1 -1
  655. package/packages/tools/browser/dist/types.js +9 -9
  656. package/packages/tools/browser/dist/types.js.map +1 -1
  657. package/packages/tools/email/dist/.tsbuildinfo +1 -1
  658. package/packages/tools/host/dist/.tsbuildinfo +1 -1
  659. package/packages/tools/mcp/dist/.tsbuildinfo +1 -1
  660. package/packages/tools/memory/dist/.tsbuildinfo +1 -1
  661. package/packages/tools/web/dist/.tsbuildinfo +1 -1
  662. package/packages/web/dist/assets/index-Bac-CdD-.css +1 -0
  663. package/packages/web/dist/assets/index-DI8bp_D7.js +146 -0
  664. package/packages/web/dist/assets/{lottie_light-CKJ88Yuk.js → lottie_light-CuiSMqnl.js} +1 -1
  665. package/packages/web/dist/build.json +1 -1
  666. package/packages/web/dist/index.html +2 -2
  667. package/packages/web/dist/precache.json +4 -4
  668. package/node_modules/@buddi/gateway/dist/web/apps.d.ts +0 -18
  669. package/node_modules/@buddi/gateway/dist/web/apps.d.ts.map +0 -1
  670. package/node_modules/@buddi/gateway/dist/web/apps.js +0 -84
  671. package/node_modules/@buddi/gateway/dist/web/apps.js.map +0 -1
  672. package/node_modules/@buddi/gateway/dist/web/tailscale.d.ts.map +0 -1
  673. package/node_modules/@buddi/gateway/dist/web/tailscale.js.map +0 -1
  674. package/node_modules/@buddi/tool-browser/dist/computer.d.ts +0 -153
  675. package/node_modules/@buddi/tool-browser/dist/computer.d.ts.map +0 -1
  676. package/node_modules/@buddi/tool-browser/dist/computer.js +0 -392
  677. package/node_modules/@buddi/tool-browser/dist/computer.js.map +0 -1
  678. package/node_modules/@buddi/tool-browser/dist/native/buddi-computer +0 -0
  679. package/node_modules/pino/test/browser-bindings.test.js +0 -114
  680. package/node_modules/pino/test/browser-redaction.test.js +0 -264
  681. package/node_modules/pino/test/browser-set-bindings.test.js +0 -99
  682. package/node_modules/pino/test/caller.test.js +0 -20
  683. package/node_modules/pino/test/fixtures/caller-loop.js +0 -6
  684. package/node_modules/pino/test/smoke.js +0 -59
  685. package/node_modules/pino/test/types/pino-import.tst.cts +0 -30
  686. package/node_modules/pino/test/types/pino-import.tst.js +0 -73
  687. package/node_modules/pino/test/types/pino-multistream.tst.ts +0 -49
  688. package/node_modules/pino/test/types/pino-top-export.tst.ts +0 -32
  689. package/node_modules/pino/test/types/pino-type-only.tst.ts +0 -77
  690. package/node_modules/pino/test/types/tsconfig.json +0 -10
  691. package/packages/gateway/dist/web/apps.d.ts +0 -18
  692. package/packages/gateway/dist/web/apps.d.ts.map +0 -1
  693. package/packages/gateway/dist/web/apps.js +0 -84
  694. package/packages/gateway/dist/web/apps.js.map +0 -1
  695. package/packages/gateway/dist/web/tailscale.d.ts.map +0 -1
  696. package/packages/gateway/dist/web/tailscale.js.map +0 -1
  697. package/packages/tools/browser/dist/computer.d.ts +0 -153
  698. package/packages/tools/browser/dist/computer.d.ts.map +0 -1
  699. package/packages/tools/browser/dist/computer.js +0 -392
  700. package/packages/tools/browser/dist/computer.js.map +0 -1
  701. package/packages/tools/browser/dist/native/buddi-computer +0 -0
  702. package/packages/web/dist/assets/index-CN1AXMI1.css +0 -1
  703. package/packages/web/dist/assets/index-CQRE9LJX.js +0 -145
@@ -56,7 +56,6 @@ import { carryOverDeps } from '../surfaces/carry-over.js';
56
56
  import { deleteCarryOver } from '../surfaces/browser-handoff.js';
57
57
  import { ProviderSettingsError } from '../providers.js';
58
58
  import { ProviderAccountError } from '../provider-accounts.js';
59
- import { listBrowserProfiles, listInstalledApps } from './apps.js';
60
59
  import { agentSearchPath, EXAMPLES_AGENTS_DIR } from '../agents/catalog.js';
61
60
  import { setDelegatesFromWeb } from './write.js';
62
61
  import { mlxhBaseUrl, probeMlxh } from '../mlxh.js';
@@ -77,6 +76,7 @@ import { hostService } from '@buddi/tool-host';
77
76
  import { listToolPermissions, revokeToolPermission, getArtifact, readArtifactBytes, artifactBytesExist, discardUnreferencedUpload, listLibrary, getLibraryEntry, decodeCursor, filterKey, textPreviewable, readArtifactPrefix, FILE_FAMILIES, LIBRARY_PAGE_MAX, getOwnerProfile, saveOwnerProfile, isKnownTimezone, listGroups, getGroup, createGroup, updateGroup, archiveGroup, deleteGroup, restoreGroup, clearGroupHistory, groupHistorySize, GROUP_UNDO_MS, GroupRefusal, createGroupConversation, listGroupConversations, latestGroupConversation, openGroupRequest, conversationGroup } from '@buddi/core';
78
77
  import { EXPORT_MIME, MAX_EXPORT_SOURCE_BYTES, exportFormats, exportName } from '../export/document.js';
79
78
  import { ExportRefused, runExport } from '../export/convert.js';
79
+ import { createVault } from '@buddi/core';
80
80
  import { beginOnboarding, completeOnboarding, markStepDone, setOnboardingDetails, skipOnboarding, readWebSetting, writeWebSetting, isAssetKey, readPluginAsset } from '@buddi/core';
81
81
  import { listMemory, setPreference, forgetPreference, updateNote, forgetNote } from '@buddi/tool-memory';
82
82
  import { purgeGroups, stopGroupWork } from './group-lifecycle.js';
@@ -94,7 +94,14 @@ import { ATTACHMENTS_UNAVAILABLE, CHAT_CONVERSATIONS_LIMIT, CHAT_UNAVAILABLE, WE
94
94
  import { readAgentAttention, streamAttention } from './attention.js';
95
95
  import { PreviewApp, PreviewTickets, parsePreviewApiPath } from './preview.js';
96
96
  import { allowedOrigins, isLoopback, webAssetsDir, webUrl } from './config.js';
97
- import { TAILSCALE_SETTING_KEY, daemonWhois, isLoginRefusal, isUnansweredRefusal, plausibleLogin, proxiedThroughTailscale, sameLogin, tailscaleIdentity, tailscaleRefusalReason, tailscaleSelf, toTailscaleSetting, } from './tailscale.js';
97
+ import { TAILSCALE_SETTING_KEY, daemonWhois, plausibleLogin, proxiedThroughTailscale, tailscaleProvider, tailscaleSelf, tailscaleServeCommand, } from './access/tailscale.js';
98
+ import { CLOUDFLARE_SETTING_KEY, cloudflareProvider, createJwks, normalizeTeamDomain, plausibleTeamDomain, validateCloudflareInput, } from './access/cloudflare.js';
99
+ import { arrivalOf } from './access/arrival.js';
100
+ import { CloudflareApiError, createCloudflareApi, CLOUDFLARE_PERMISSION_LINES, CLOUDFLARE_TOKEN_URL } from './access/cloudflare-api.js';
101
+ import { CLOUDFLARE_SETUP_KEY, checkSetupInput, claimSetupOperation, freshProgress, setupBusySentence, setupOperation, removeCloudflareSetup, runCloudflareSetup, } from './access/cloudflare-setup.js';
102
+ import { ownerSecretTokenStore } from './access/cloudflare-token.js';
103
+ import { createAccessRegistry } from './access/registry.js';
104
+ import { createIngress } from './access/ingress.js';
98
105
  import { clientKey } from './client-key.js';
99
106
  import { retryHref, sendSignedOut, wantsSignedOutPage } from './signed-out.js';
100
107
  import { extensionEndpoint } from './extension.js';
@@ -116,6 +123,7 @@ import { createRequirements } from '../plugins/requires.js';
116
123
  import { placesList, placesRoute } from './places.js';
117
124
  import { peopleRoute } from './people.js';
118
125
  import { syncDateMissions } from '../missions/dates.js';
126
+ import { resumeParkedForPage } from '../missions/parked.js';
119
127
  import { OWNER_DATE_FORMATS, OWNER_TIME_FORMATS, validDayMonth } from '@buddi/core';
120
128
  import { readFacts, webSettingsStore } from '../tips/facts.js';
121
129
  import { dismissAgentOffer, isPendingAccept, raiseAgentOffers, readAgentOffers } from './agent-offers.js';
@@ -160,6 +168,21 @@ const PREVIEW_APPS = new WeakMap();
160
168
  * reloads once the next process answers `/_buddi/ready` with a new `boot`.
161
169
  */
162
170
  const CLOSING_SAYS = new WeakMap();
171
+ /** Each server's ingress listener (web/access/ingress.ts). */
172
+ const INGRESS_OF = new WeakMap();
173
+ /** Each server's request handler, for a listener without a socket of its own (the relay, provider 3). */
174
+ const DISPATCH_OF = new WeakMap();
175
+ /** The ingress listener belonging to a server built by `createWebApp`. */
176
+ export function ingressOf(server) {
177
+ return INGRESS_OF.get(server);
178
+ }
179
+ /**
180
+ * The request handler of a server built by `createWebApp`: what an in-process
181
+ * listener hands a request to after tagging its arrival (`markRequestArrival`).
182
+ */
183
+ export function dispatchOf(server) {
184
+ return DISPATCH_OF.get(server);
185
+ }
163
186
  /** How long a closing frame may take to reach the pages before the sockets go. */
164
187
  const CLOSING_FLUSH_MS = 250;
165
188
  /** The chat surface this server is running, if any. */
@@ -270,13 +293,13 @@ export function createWebApp(deps) {
270
293
  // A locked dashboard drives nothing (docs/dashboard.md, "Lock screen").
271
294
  if (await lock.locked(session))
272
295
  return null;
273
- if (session.via === 'tailscale') {
274
- let refusal;
275
- const confirmed = await identityOf(req, now, (reason) => { refusal = reason; });
276
- if (!confirmed || !sameLogin(confirmed.login, session.tailscaleLogin)) {
277
- // "Could not ask" is not an answer: no socket now, but the session stays.
278
- if (!confirmed && isUnansweredRefusal(refusal))
279
- return null;
296
+ if (session.via === 'provider') {
297
+ const confirmed = await access.confirm(session, req, now);
298
+ // "Could not ask", or a stray request without the provider's proof:
299
+ // no socket now, but the session stays.
300
+ if (confirmed.answer === 'unanswered' || confirmed.answer === 'refuse')
301
+ return null;
302
+ if (confirmed.answer === 'end') {
280
303
  await sessions.destroy(session.id).catch(() => { });
281
304
  return null;
282
305
  }
@@ -427,6 +450,7 @@ export function createWebApp(deps) {
427
450
  now: deps.now,
428
451
  get timezone() { return deps.timezone; },
429
452
  log,
453
+ browser: deps.browser ?? browserHost(deps.env ?? process.env),
430
454
  ...deps.chat,
431
455
  })
432
456
  : undefined;
@@ -438,6 +462,18 @@ export function createWebApp(deps) {
438
462
  * Failures are the recovery's own business and land in the log.
439
463
  */
440
464
  void chat?.recoverPendingInput().catch((err) => log(`web chat: recovering queued input failed: ${err instanceof Error ? err.message : String(err)}`));
465
+ /*
466
+ * Give it back: the run that waited on the page carries on. A mission parked
467
+ * on it wakes (missions/parked.ts); otherwise the conversation the take-over
468
+ * held gets its turn. Either way with no message from the owner.
469
+ */
470
+ const offGiveBack = (deps.browser ?? browserHost(deps.env ?? process.env)).onGiveBack?.((info) => {
471
+ void (async () => {
472
+ if (await resumeParkedForPage(deps.pool, info.conversationId, deps.now()))
473
+ return;
474
+ await chat?.continueAfterGiveBack({ conversationId: info.conversationId, agentId: info.agentId });
475
+ })().catch((err) => log(`browser: carrying on after the page was given back failed: ${err instanceof Error ? err.message : String(err)}`));
476
+ });
441
477
  const streams = new StreamBudget();
442
478
  // A bundle script's run asks every time: its card offers no standing permission (`asksEachTime`).
443
479
  const permissionScopes = (tool, envelope) => deps.registry.lookup(tool)?.reusableApproval && !asksEachTime(envelope)
@@ -483,55 +519,204 @@ export function createWebApp(deps) {
483
519
  // ticket-and-session gate. The override is a test seam, nothing more.
484
520
  const openAccess = deps.openAccess ?? (deps.env?.BUDDI_WEB_REQUIRE_AUTH !== '1' && isLoopback(deps.config.host));
485
521
  /*
486
- * Signing in through Tailscale: the daemon this process asks.
522
+ * Trusted access providers (web/access/): something in front of buddi
523
+ * proves who is knocking, buddi checks the proof itself, and its own
524
+ * `remote` session rules apply. Tailscale (provider 1) asks the local
525
+ * daemon; Cloudflare Access (provider 2) verifies the JWT Access signs.
487
526
  *
488
527
  * Which sessions were established that way is a field on the session itself
489
- * (`via`), not a map beside it — provenance that can drift out of step with
490
- * the session it describes is provenance that eventually lies. It is what
491
- * keeps a tailnet session from widening its own access (`PUT /api/tailscale`
528
+ * (`via: 'provider'` and `provider`), not a map beside it. It is what keeps
529
+ * a provider session from widening its own access (changing a provider
492
530
  * wants a session from this machine) and what lets `/api/session` say how
493
531
  * the browser got in.
494
532
  */
495
533
  const whois = deps.tailscale?.whois ?? daemonWhois();
496
534
  const tailscaleSelfOf = deps.tailscale?.self ?? (() => tailscaleSelf());
535
+ const jwks = deps.cloudflare?.jwks ?? createJwks({ log });
536
+ const boundPort = () => server.address()?.port ?? deps.config.port;
537
+ const accessCtx = {
538
+ dashboardPort: boundPort,
539
+ ingressPort: () => ingress.port(),
540
+ publicOrigin: () => deps.config.publicOrigin,
541
+ };
542
+ const tailscale = tailscaleProvider({ whois, self: tailscaleSelfOf, log });
543
+ const cloudflare = cloudflareProvider({ jwks, log, ingressProblem: () => ingress.problem() });
544
+ const access = createAccessRegistry({
545
+ providers: [tailscale, cloudflare],
546
+ readSetting: (key) => readWebSetting(deps.pool, key),
547
+ });
548
+ /** The command that publishes this dashboard on the tailnet, with this installation's own ports. */
549
+ const serveCommand = () => tailscaleServeCommand(boundPort(), deps.config.publicOrigin);
550
+ const readTailscaleSetting = async () => access.settingOf(tailscale);
551
+ /*
552
+ * The Cloudflare setting as last read, so the synchronous Origin check can
553
+ * honour the public address the panel stored. Refreshed on every read.
554
+ */
555
+ let cloudflareSeen = null;
556
+ const readCloudflareSetting = async () => {
557
+ cloudflareSeen = await access.settingOf(cloudflare);
558
+ return cloudflareSeen;
559
+ };
560
+ /** Did this request come through a provider, or does it hold a session one minted? Then the block is read-only. */
561
+ const throughProvider = (req, session) => session.via === 'provider' || arrivalOf(req) !== 'main' || proxiedThroughTailscale(req);
562
+ /** The Tailscale panel's whole payload (unchanged by the move behind the provider interface). */
563
+ const tailscaleView = async (req, session) => {
564
+ const stored = await readTailscaleSetting();
565
+ const daemon = await tailscaleSelfOf();
566
+ return {
567
+ enabled: stored.enabled,
568
+ login: stored.login,
569
+ available: daemon.available,
570
+ self: daemon.self,
571
+ proxied: throughProvider(req, session),
572
+ // What the owner has to run on this machine, with this installation's own two ports in it.
573
+ serveCommand: serveCommand(),
574
+ };
575
+ };
576
+ /** The Cloudflare panel's payload: the stored fields, the status, the setup copy with the real ingress port. */
577
+ const cloudflareView = async (req, session) => {
578
+ const stored = await readCloudflareSetting();
579
+ const status = await cloudflare.status(stored, accessCtx);
580
+ const visit = cloudflare.lastVisit();
581
+ return {
582
+ enabled: stored.enabled,
583
+ teamDomain: stored.teamDomain,
584
+ aud: stored.aud,
585
+ email: stored.email,
586
+ publicOrigin: stored.publicOrigin,
587
+ status,
588
+ ingressPort: ingress.port() ?? accessCtx.dashboardPort() + 2,
589
+ listening: ingress.port() !== null,
590
+ lastVisit: visit ? { at: visit.at.toISOString(), email: visit.email } : null,
591
+ setup: cloudflare.setup(stored, accessCtx),
592
+ proxied: throughProvider(req, session),
593
+ };
594
+ };
497
595
  /**
498
- * The command that publishes this dashboard on the tailnet, with this
499
- * installation's own numbers in it: the HTTPS port of the configured public
500
- * origin, and the port the gateway is actually listening on.
596
+ * Store a Cloudflare setting and do what a Save does: end the sessions it
597
+ * admitted unless the same person, team and application stay on, then bind
598
+ * or close the ingress listener. The panel's Save and "Set it up for me"
599
+ * both come through here.
501
600
  */
502
- const serveCommand = () => {
503
- const bound = server.address()?.port ?? deps.config.port;
504
- let https = 443;
505
- if (deps.config.publicOrigin) {
506
- const port = new URL(deps.config.publicOrigin).port;
507
- if (port !== '')
508
- https = Number(port);
601
+ const applyCloudflareSetting = async (value) => {
602
+ const before = await readCloudflareSetting();
603
+ await writeWebSetting(deps.pool, CLOUDFLARE_SETTING_KEY, value);
604
+ await readCloudflareSetting();
605
+ const same = before.enabled && value.enabled && before.email.toLowerCase() === value.email.toLowerCase()
606
+ && before.teamDomain === value.teamDomain && before.aud === value.aud;
607
+ if (!same) {
608
+ const forgotten = new Set();
609
+ await sessions.forget((s) => { const drop = s.via === 'provider' && s.provider === 'cloudflare-access'; if (drop && s.id)
610
+ forgotten.add(s.id); return drop; });
611
+ hand.revoke((lease) => forgotten.has(lease), 'Cloudflare access changed. Sign in again.');
509
612
  }
510
- return `tailscale serve --bg --https=${https} http://127.0.0.1:${bound}`;
613
+ await ingress.sync();
511
614
  };
512
- const readTailscaleSetting = async () => toTailscaleSetting(await readWebSetting(deps.pool, TAILSCALE_SETTING_KEY).catch(() => null));
513
- /**
514
- * Who the daemon says this request is, with the sign-in budget in front of
515
- * the daemon. Used to mint a session and, on every later request, to confirm
516
- * the one the browser is holding.
615
+ /*
616
+ * "Set it up for me" (access/cloudflare-setup.ts): one run at a time, in
617
+ * the background — the health wait lasts as long as the owner takes to run
618
+ * one command — and its progress kept here for the panel to poll. The token
619
+ * is an owner secret, read once per run.
620
+ */
621
+ let cfTokens = deps.cloudflare?.tokens ?? null;
622
+ const tokenStore = () => (cfTokens ??= ownerSecretTokenStore(deps.pool, createVault({ env: deps.env ?? process.env })));
623
+ let setupRun = null;
624
+ const readSetupRecord = async () => {
625
+ const row = await readWebSetting(deps.pool, CLOUDFLARE_SETUP_KEY);
626
+ return row && typeof row === 'object' && typeof row.host === 'string' ? row : null;
627
+ };
628
+ const setupDepsFor = (token, lease, signal) => ({
629
+ api: createCloudflareApi({ token, transport: deps.cloudflare?.api?.transport, baseUrl: deps.cloudflare?.api?.baseUrl }),
630
+ ingressPort: ingress.port() ?? (askedIngressPort() || null) ?? accessCtx.dashboardPort() + 2,
631
+ platform: deps.cloudflare?.platform ?? process.platform,
632
+ readSetting: readCloudflareSetting,
633
+ saveSetting: applyCloudflareSetting,
634
+ readRecord: readSetupRecord,
635
+ saveRecord: (record) => writeWebSetting(deps.pool, CLOUDFLARE_SETUP_KEY, record),
636
+ test: (team) => jwks.refresh(team),
637
+ ...(deps.cloudflare?.setup ?? {}),
638
+ signal,
639
+ lease,
640
+ });
641
+ const setupView = async () => {
642
+ const record = await readSetupRecord();
643
+ return {
644
+ progress: setupRun?.progress ?? freshProgress(record?.host ?? '', record?.email ?? ''),
645
+ tokenStored: await tokenStore().has().catch(() => false),
646
+ record: record ? { host: record.host, email: record.email, zone: record.zone.name, teamDomain: record.teamDomain } : null,
647
+ permissions: CLOUDFLARE_PERMISSION_LINES,
648
+ tokenUrl: CLOUDFLARE_TOKEN_URL,
649
+ ingressPort: ingress.port() ?? (askedIngressPort() || null) ?? accessCtx.dashboardPort() + 2,
650
+ };
651
+ };
652
+ /** Run in the background; the lease (claimed by the caller) goes when the run ends. */
653
+ const startSetup = (lease, job, first) => {
654
+ const abort = new AbortController();
655
+ const run = { progress: first, abort, done: Promise.resolve() };
656
+ setupRun = run;
657
+ run.done = job((p) => { run.progress = p; }, abort.signal)
658
+ .then((p) => { run.progress = p; })
659
+ .catch(() => { run.progress = { ...run.progress, state: 'failed', error: 'The setup stopped unexpectedly. Try again.' }; })
660
+ .finally(() => lease.release());
661
+ };
662
+ /*
663
+ * A setting written elsewhere (`buddi access cloudflare setup` writes the
664
+ * database directly) reaches the ingress listener here: bound or closed
665
+ * within a quarter of a minute.
666
+ */
667
+ const ingressResync = setInterval(() => {
668
+ void readCloudflareSetting().then((setting) => {
669
+ if (setting.enabled !== (ingress.port() !== null))
670
+ return ingress.sync();
671
+ return undefined;
672
+ }).catch(() => undefined);
673
+ }, 15_000);
674
+ ingressResync.unref?.();
675
+ /** Every provider's row, for "Sign in from elsewhere". */
676
+ const accessView = async (req, session) => ({
677
+ proxied: throughProvider(req, session),
678
+ providers: await Promise.all(access.providers.map(async (p) => {
679
+ const setting = await access.settingOf(p);
680
+ return { id: p.id, title: p.title, identity: p.identity, proxy: p.proxy, enabled: p.enabled(setting), status: await p.status(setting, accessCtx) };
681
+ })),
682
+ });
683
+ /*
684
+ * The ingress listener (specs/trusted-access.md §3.3): a second loopback
685
+ * listener, the dashboard's port + 2 (or `BUDDI_INGRESS_PORT`), bound only
686
+ * while a this-machine provider other than Tailscale is on. Every request
687
+ * on it is remote, whatever its headers or Host say, and it never passes a
688
+ * loopback-only check. cloudflared points here, never at the dashboard's
689
+ * own port, so a tunnel arrival can never be mistaken for this machine.
517
690
  */
518
- const identityOf = (req, now, onRefusal) => tailscaleIdentity(req, {
519
- setting: readTailscaleSetting,
520
- whois,
691
+ /** `BUDDI_INGRESS_PORT`, when it names a port. */
692
+ function askedIngressPort() {
693
+ const raw = (deps.env ?? process.env).BUDDI_INGRESS_PORT?.trim();
694
+ const asked = raw ? Number(raw) : NaN;
695
+ return Number.isInteger(asked) && asked >= 0 && asked <= 65535 ? asked : null;
696
+ }
697
+ const ingress = createIngress({
698
+ onRequest: (req, res) => onRequest(req, res),
699
+ onUpgrade: (req, socket, head) => { server.emit('upgrade', req, socket, head); },
700
+ port: () => askedIngressPort() ?? (deps.config.port === 0 ? 0 : boundPort() + 2),
701
+ wanted: async () => (await readCloudflareSetting()).enabled,
521
702
  log,
522
- now: () => now,
523
- onRefusal,
524
703
  });
525
704
  /**
526
705
  * The pair a browser holds: the HttpOnly session and the readable CSRF value
527
706
  * the page has to echo back in a header. Both carry the same `Max-Age`, which
528
707
  * is the lifetime this session's scope earned it.
529
708
  */
709
+ /*
710
+ * Secure cookies for a remote session behind an HTTPS proxy: the configured
711
+ * public origin, or anything on the ingress listener (Cloudflare's edge
712
+ * always speaks HTTPS to the browser).
713
+ */
714
+ const secureCookies = (req, scope) => scope === 'remote' && (!!deps.config.publicOrigin || arrivalOf(req) === 'ingress');
530
715
  const sessionCookies = (req, session) => {
531
716
  const maxAgeSeconds = SessionStore.maxAgeSeconds(session);
532
717
  return [
533
- cookieHeader(sessionCookieName(cookiePort(req)), session.id, { httpOnly: true, maxAgeSeconds, secure: session.scope === 'remote' && !!deps.config.publicOrigin }),
534
- cookieHeader(csrfCookieName(cookiePort(req)), session.csrf, { httpOnly: false, maxAgeSeconds, secure: session.scope === 'remote' && !!deps.config.publicOrigin }),
718
+ cookieHeader(sessionCookieName(cookiePort(req)), session.id, { httpOnly: true, maxAgeSeconds, secure: secureCookies(req, session.scope) }),
719
+ cookieHeader(csrfCookieName(cookiePort(req)), session.csrf, { httpOnly: false, maxAgeSeconds, secure: secureCookies(req, session.scope) }),
535
720
  ];
536
721
  };
537
722
  /**
@@ -540,7 +725,7 @@ export function createWebApp(deps) {
540
725
  * polling page then sends no credential at all, which counts as nothing.
541
726
  */
542
727
  const expiredCookies = (req) => {
543
- const secure = requestScope(req) === 'remote' && !!deps.config.publicOrigin;
728
+ const secure = secureCookies(req, requestScope(req));
544
729
  return [
545
730
  cookieHeader(sessionCookieName(cookiePort(req)), '', { httpOnly: true, maxAgeSeconds: 0, secure }),
546
731
  cookieHeader(csrfCookieName(cookiePort(req)), '', { httpOnly: false, maxAgeSeconds: 0, secure }),
@@ -548,9 +733,23 @@ export function createWebApp(deps) {
548
733
  };
549
734
  /**
550
735
  * What the signed-out page should offer this request: how it arrived,
551
- * whether Tailscale can sign it in, and whether a lockout is running.
736
+ * which provider can sign it in, why it could not, and whether a lockout is
737
+ * running.
552
738
  */
553
- const signedOutFor = async (req, pathname, search, now, refusal) => {
739
+ const signedOutFor = async (req, pathname, search, now, refused, key) => {
740
+ const lockedForMs = limiter.retryAfterMs(key, now);
741
+ const locked = lockedForMs > 0 ? { lockedForMs } : {};
742
+ if (arrivalOf(req) === 'ingress') {
743
+ return {
744
+ retry: retryHref(pathname, search),
745
+ arrived: 'remote',
746
+ provider: {
747
+ title: cloudflare.title,
748
+ ...(refused?.provider === 'cloudflare-access' ? { refusal: refused.refusal.refusal, kind: refused.refusal.kind } : { refusal: 'no-assertion', kind: 'other' }),
749
+ },
750
+ ...locked,
751
+ };
752
+ }
554
753
  const proxied = proxiedThroughTailscale(req);
555
754
  let publicHost;
556
755
  try {
@@ -569,14 +768,14 @@ export function createWebApp(deps) {
569
768
  const scope = requestScope(req);
570
769
  const arrived = proxied || (publicHost !== undefined && host === publicHost) ? 'tailnet' : scope === 'local' ? 'local' : 'remote';
571
770
  const setting = arrived === 'tailnet' ? await readTailscaleSetting() : null;
572
- const lockedForMs = limiter.retryAfterMs(clientKey(req), now);
771
+ const tailscaleRefusal = refused?.provider === 'tailscale' ? refused.refusal : undefined;
573
772
  return {
574
773
  retry: retryHref(pathname, search),
575
774
  arrived,
576
- tailscaleSignIn: !!setting?.enabled && !isLoginRefusal(refusal),
577
- ...(isLoginRefusal(refusal) ? { tailscaleRefusal: tailscaleRefusalReason(refusal) } : {}),
578
- ...(isUnansweredRefusal(refusal) ? { tailscaleUnanswered: true } : {}),
579
- ...(lockedForMs > 0 ? { lockedForMs } : {}),
775
+ tailscaleSignIn: !!setting?.enabled && tailscaleRefusal?.kind !== 'login',
776
+ ...(tailscaleRefusal?.kind === 'login' ? { tailscaleRefusal: tailscaleRefusal.sentence } : {}),
777
+ ...(tailscaleRefusal?.kind === 'unanswered' ? { tailscaleUnanswered: true } : {}),
778
+ ...locked,
580
779
  };
581
780
  };
582
781
  /**
@@ -585,7 +784,16 @@ export function createWebApp(deps) {
585
784
  * loopback page and the tailnet page each keep their own pair. Set, read and
586
785
  * checked through this one function.
587
786
  */
588
- const cookiePort = (req) => requestPort(req, server.address()?.port ?? deps.config.port, deps.config.publicOrigin);
787
+ const cookiePort = (req) => {
788
+ // Cloudflare's edge serves the page on HTTPS's own port, whatever
789
+ // cloudflared says on the way in; the page names its cookie by it.
790
+ if (arrivalOf(req) === 'ingress') {
791
+ const host = req.headers.host ?? '';
792
+ if (!/:\d+$/.test(host.replace(/^\[[^\]]*\]/, '')))
793
+ return 443;
794
+ }
795
+ return requestPort(req, server.address()?.port ?? deps.config.port, deps.config.publicOrigin);
796
+ };
589
797
  /**
590
798
  * The origins a write may claim, resolved against the port actually bound.
591
799
  *
@@ -596,8 +804,13 @@ export function createWebApp(deps) {
596
804
  let originCache;
597
805
  const allowed = () => {
598
806
  const bound = server.address()?.port ?? deps.config.port;
599
- if (originCache?.port !== bound) {
600
- originCache = { port: bound, set: new Set(allowedOrigins({ ...deps.config, port: bound })) };
807
+ // The public address the Cloudflare panel stored, while that provider is on.
808
+ const extra = cloudflareSeen?.enabled ? cloudflareSeen.publicOrigin : '';
809
+ if (originCache?.port !== bound || originCache.extra !== extra) {
810
+ const set = new Set(allowedOrigins({ ...deps.config, port: bound }));
811
+ if (extra)
812
+ set.add(extra);
813
+ originCache = { port: bound, extra, set };
601
814
  }
602
815
  return originCache.set;
603
816
  };
@@ -605,7 +818,7 @@ export function createWebApp(deps) {
605
818
  // caller that needs to drain it on shutdown (or in a test) can, without
606
819
  // `createWebApp` growing a second return value every existing caller would
607
820
  // have to unpack.
608
- const server = createServer((req, res) => {
821
+ function onRequest(req, res) {
609
822
  handle(req, res).catch((err) => {
610
823
  // The lock could not say whether this request may pass: refused, never let in (web/lock.ts).
611
824
  if (err instanceof LockUnavailable) {
@@ -622,9 +835,14 @@ export function createWebApp(deps) {
622
835
  else
623
836
  res.end();
624
837
  });
625
- });
838
+ }
839
+ const server = createServer(onRequest);
626
840
  if (chat)
627
841
  WEB_CHATS.set(server, chat);
842
+ INGRESS_OF.set(server, ingress);
843
+ // The seam the relay (provider 3) hands its in-process requests to,
844
+ // tagged `markRequestArrival(req, 'relay')` first. Never replayed as HTTP to 127.0.0.1.
845
+ DISPATCH_OF.set(server, onRequest);
628
846
  PREVIEW_APPS.set(server, previews);
629
847
  CLOSING_SAYS.set(server, async () => {
630
848
  const said = frame('closing', goingAway === undefined ? {} : { for: goingAway });
@@ -655,13 +873,25 @@ export function createWebApp(deps) {
655
873
  // rather than in `startWebServer` so every caller, tests included, has it.
656
874
  extension.attach(server);
657
875
  extension.attachPath(REMOTE_HAND_SOCKET_PATH, (req, socket, head) => hand.upgrade(req, socket, head));
658
- server.once('close', () => { extension.shutdown(); hand.shutdown(); clearInterval(idleSweep); });
876
+ server.once('close', () => { extension.shutdown(); hand.shutdown(); clearInterval(idleSweep); clearInterval(ingressResync); setupRun?.abort.abort(); void ingress.close(); });
877
+ server.once('close', () => offGiveBack?.());
659
878
  return server;
660
879
  async function handle(req, res) {
661
880
  const now = deps.now();
662
881
  const method = (req.method ?? 'GET').toUpperCase();
663
882
  const url = parseUrl(req);
664
- const key = clientKey(req);
883
+ /*
884
+ * The bucket failed sign-ins count against (client-key.ts, §7.5): by the
885
+ * arrival path, so a stranger behind a tunnel can never lock out this
886
+ * machine or the tailnet. A request on the ingress listener is asked of
887
+ * its provider at once — a JWT check against cached keys — because its
888
+ * bucket depends on it: `cf:unverified` until it verifies, then the
889
+ * client's own address.
890
+ */
891
+ let key = access.bucketOf(req) ?? clientKey(req);
892
+ const early = arrivalOf(req) === 'main' ? null : await access.identify(req, now);
893
+ if (early?.result.ok && early.result.identity.bucket)
894
+ key = early.result.identity.bucket;
665
895
  // No CORS, and therefore no preflight.
666
896
  if (method === 'OPTIONS')
667
897
  return sendEmpty(res, 405);
@@ -763,12 +993,12 @@ export function createWebApp(deps) {
763
993
  const presentedSession = cookies[sessionCookieName(cookiePort(req))];
764
994
  let session = await sessions.resolve(presentedSession, scope, now);
765
995
  /*
766
- * Why Tailscale gave this request no identity, when it was asked. Only
767
- * "this login is not the one allowed" changes what the signed-out page
768
- * says; every other reason is the ordinary "you're signed out".
996
+ * Why a provider gave this request no identity, when it was asked. Only
997
+ * "this person is not the one allowed" and "could not ask" change what
998
+ * the signed-out page says; every other reason is the ordinary "you're
999
+ * signed out".
769
1000
  */
770
- let refusal;
771
- const noteRefusal = (reason) => { refusal = reason; };
1001
+ let refused = early && !early.result.ok ? { provider: early.provider.id, refusal: early.result } : undefined;
772
1002
  /*
773
1003
  * Set when the browser presented a session that is gone: the refusal then
774
1004
  * expires its cookies, so it stops presenting them (and stops counting).
@@ -777,7 +1007,7 @@ export function createWebApp(deps) {
777
1007
  /*
778
1008
  * The answer to a refused request: the signed-out page for a person
779
1009
  * opening a page, the empty status for everything else (docs/web.md).
780
- * 401 is "signed out", 429 "too many tries from here", 503 "Tailscale
1010
+ * 401 is "signed out", 429 "too many tries from here", 503 "the provider
781
1011
  * could not be asked; the session is still good, try again".
782
1012
  */
783
1013
  const refuse = async (status = 401) => {
@@ -788,33 +1018,40 @@ export function createWebApp(deps) {
788
1018
  headers['Retry-After'] = '5';
789
1019
  if (!wantsSignedOutPage(req, method, url.pathname))
790
1020
  return sendEmpty(res, status, headers);
791
- return sendSignedOut(res, await signedOutFor(req, url.pathname, url.search, now, refusal), headers, status);
1021
+ return sendSignedOut(res, await signedOutFor(req, url.pathname, url.search, now, refused, key), headers, status);
792
1022
  };
793
- /* A Tailscale session this request ended: not a guess, so never a failed sign-in. */
1023
+ /* A provider session this request ended: not a guess, so never a failed sign-in. */
794
1024
  let revoked = false;
795
1025
  /*
796
- * A Tailscale session is re-confirmed on every single request.
1026
+ * A provider session is re-confirmed on every single request.
797
1027
  *
798
- * A cookie on its own would be a bearer token the tailnet identity is only
799
- * loosely related to: it would outlive the setting being turned off, the
800
- * allowed login being changed, and the device being handed to somebody
801
- * else. So the daemon is asked again — cheap, because a whois answer is
802
- * reused for a minute — and the login it names now must be the login this
803
- * session was minted for. Anything else and the session is gone, not
804
- * merely ignored: the browser is 401 and has to sign in again, which it
805
- * can only do if it is still the person the owner allowed.
1028
+ * A cookie on its own would be a bearer token the provider's identity is
1029
+ * only loosely related to: it would outlive the setting being turned off,
1030
+ * the allowed login being changed, and the device being handed to somebody
1031
+ * else. So the provider is asked again — Tailscale's daemon (a whois
1032
+ * answer is reused for a minute), Cloudflare's JWT on this very request —
1033
+ * and the person it names now must be the one this session was minted
1034
+ * for. Anything else and the session is gone, not merely ignored.
806
1035
  */
807
- if (session?.via === 'tailscale') {
808
- const confirmed = await identityOf(req, now, noteRefusal);
809
- if (!confirmed || !sameLogin(confirmed.login, session.tailscaleLogin)) {
810
- /*
811
- * The daemon could not be asked (busy, starting, timed out). That is
812
- * not an answer about anyone, so the session is not ended for it —
813
- * a restart's first burst of requests used to end every tailnet
814
- * session this way — and nothing is counted. This request waits.
815
- */
816
- if (!confirmed && isUnansweredRefusal(refusal))
817
- return refuse(503);
1036
+ if (session?.via === 'provider') {
1037
+ const confirmed = await access.confirm(session, req, now);
1038
+ if (confirmed.refusal)
1039
+ refused = { provider: session.provider, refusal: confirmed.refusal };
1040
+ /*
1041
+ * The provider could not be asked (a daemon busy or starting, keys that
1042
+ * could not be fetched). That is not an answer about anyone, so the
1043
+ * session is not ended for it and nothing is counted. This request waits.
1044
+ */
1045
+ if (confirmed.answer === 'unanswered')
1046
+ return refuse(503);
1047
+ /*
1048
+ * This request lacks the provider's proof (Cloudflare's header), but
1049
+ * the session is not ended for it: a stray request without the header
1050
+ * must not sign the owner out. Nothing is counted either.
1051
+ */
1052
+ if (confirmed.answer === 'refuse')
1053
+ return refuse(401);
1054
+ if (confirmed.answer === 'end') {
818
1055
  const ended = session.id;
819
1056
  // A hand this session was holding does not outlive the session.
820
1057
  hand.revoke((lease) => lease === ended);
@@ -823,7 +1060,7 @@ export function createWebApp(deps) {
823
1060
  revoked = true;
824
1061
  forgetCookies = true;
825
1062
  // The identity on this very request may still earn a new session below.
826
- refusal = undefined;
1063
+ refused = undefined;
827
1064
  }
828
1065
  }
829
1066
  /*
@@ -848,26 +1085,32 @@ export function createWebApp(deps) {
848
1085
  res.setHeader('Set-Cookie', sessionCookies(req, session));
849
1086
  }
850
1087
  /*
851
- * Signed in through Tailscale.
1088
+ * Signed in through a trusted access provider.
852
1089
  *
853
- * The proxy runs on this machine and the daemon on this machine confirms
854
- * who is behind the forwarded address; when that agrees with the login the
855
- * owner allowed, this is the owner's own tailnet identity arriving and the
856
- * request gets a `remote` session exactly as the ticket exchange would
857
- * have minted one — same cookies, same 12 hours, same CSRF on every write.
858
- * Anything less returns null and the request falls through to the 401
859
- * below, which is what it would have got before this existed.
1090
+ * Tailscale: the proxy runs on this machine and the daemon confirms who
1091
+ * is behind the forwarded address. Cloudflare Access: the JWT Access
1092
+ * signed verifies against the team's keys, for this application, naming
1093
+ * the allowed email, and it arrived on the ingress listener. When that
1094
+ * agrees with the person the owner allowed, the request gets a `remote`
1095
+ * session exactly as the ticket exchange would have minted one — same
1096
+ * cookies, same 12 hours, same CSRF on every write. Anything less falls
1097
+ * through to the 401 below. A provider identity signs in during a lockout.
860
1098
  */
861
1099
  if (!session) {
862
- const identity = await identityOf(req, now, noteRefusal);
863
- if (identity) {
1100
+ const asked = early ?? await access.identify(req, now);
1101
+ if (asked && !asked.result.ok)
1102
+ refused = { provider: asked.provider.id, refusal: asked.result };
1103
+ if (asked?.result.ok) {
1104
+ const identity = asked.result.identity;
864
1105
  limiter.reset(key);
865
1106
  const locked = await lock.startLocked('browser');
1107
+ const capMs = Math.min(asked.provider.absoluteCapMs, identity.expiresAt ? Math.max(0, identity.expiresAt.getTime() - now.getTime()) : asked.provider.absoluteCapMs);
866
1108
  session = sessions.create('remote', now, {
867
- via: 'tailscale',
868
- tailscaleLogin: identity.login,
869
- tailscaleAddress: identity.address,
870
- tailscaleName: identity.name,
1109
+ via: 'provider',
1110
+ provider: identity.provider,
1111
+ providerSubject: identity.subject,
1112
+ ...(identity.detail ? { providerDetail: identity.detail } : {}),
1113
+ absoluteCapMs: capMs,
871
1114
  }, locked ? { locked } : {});
872
1115
  res.setHeader('Set-Cookie', sessionCookies(req, session));
873
1116
  }
@@ -882,14 +1125,26 @@ export function createWebApp(deps) {
882
1125
  // the same stale cookie counts once however often it is sent: all tailnet
883
1126
  // and tunnel traffic shares 127.0.0.1, so a forgotten tab must not be
884
1127
  // able to lock the owner out of every way in.
885
- if (!presentedSession)
1128
+ //
1129
+ // A provider's credential that failed (a Cloudflare assertion that does
1130
+ // not verify) is an attempt too, cookie or not, in the bucket the
1131
+ // provider names (per Cf-Connecting-Ip). No assertion at all, or keys
1132
+ // that could not be fetched, carry no attempt.
1133
+ const tried = refused?.refusal.attempt;
1134
+ if (!presentedSession && !tried)
886
1135
  return refuse();
887
- forgetCookies = true;
1136
+ if (tried)
1137
+ key = tried.bucket;
1138
+ if (presentedSession)
1139
+ forgetCookies = true;
888
1140
  if (revoked)
889
1141
  return refuse();
890
1142
  if (limiter.blocked(key, now))
891
1143
  return refuse(429);
892
- limiter.failCredential(key, presentedSession, now);
1144
+ if (tried)
1145
+ limiter.failCredential(key, tried.credential, now);
1146
+ if (presentedSession)
1147
+ limiter.failCredential(key, presentedSession, now);
893
1148
  return refuse();
894
1149
  }
895
1150
  /*
@@ -1088,6 +1343,10 @@ export function createWebApp(deps) {
1088
1343
  now: deps.now,
1089
1344
  agents: () => deps.catalog.list(),
1090
1345
  plugins: () => deps.registry.manifests().map((m) => m.name),
1346
+ appsWithoutPlugin: () => {
1347
+ const apps = browser.status().routes?.find((route) => route.kind === 'apps');
1348
+ return apps !== undefined && apps.installed === false && apps.mode !== undefined && apps.mode !== 'off';
1349
+ },
1091
1350
  needsSetup: async () => {
1092
1351
  const out = [];
1093
1352
  for (const manifest of deps.registry.manifests()) {
@@ -1387,15 +1646,16 @@ export function createWebApp(deps) {
1387
1646
  const permissions = (await listToolPermissions(deps.pool, deps.ctx.ownerId)).filter(p => p.tool === 'host.exec' && (!agentId || p.agentId === agentId) && (!conversationId || !p.conversationId || p.conversationId === conversationId));
1388
1647
  return sendJson(res, 200, { permissions, runs: host.runs(deps.ctx.ownerId, agentId, conversationId) });
1389
1648
  }
1390
- case '/api/host/apps':
1391
- return sendJson(res, 200, { apps: await listInstalledApps() });
1392
- case '/api/host/browser-profiles':
1393
- return sendJson(res, 200, { profiles: await listBrowserProfiles(q.get('app') ?? '') });
1394
1649
  case '/api/extension':
1395
1650
  return sendJson(res, 200, await extension.view());
1396
1651
  case '/api/browser':
1397
1652
  return sendJson(res, 200, q.has('conversationId') && q.has('agentId')
1398
1653
  ? browser.status({ agentId: q.get('agentId'), conversationId: q.get('conversationId') }) : browser.status());
1654
+ case '/api/browser/telemetry': {
1655
+ // Stops by cause, cards and routes over the last week: how flakiness is seen to fall.
1656
+ const summary = browser.telemetrySummary?.(Number(q.get('days') ?? '7') || 7);
1657
+ return sendJson(res, 200, summary ?? { days: 7, tasks: 0, stops: 0, cards: 0, byCause: [], routes: {}, stopsPerTask: 0 });
1658
+ }
1399
1659
  case '/api/browser/screenshot': {
1400
1660
  const expectedSession = url.searchParams.get('sessionId');
1401
1661
  const current = browser.status(expectedSession !== null ? { sessionId: expectedSession } : undefined);
@@ -1434,10 +1694,14 @@ export function createWebApp(deps) {
1434
1694
  // from the page rather than implied by a number in a source file.
1435
1695
  scope: session.scope,
1436
1696
  // How this browser got in. `local` is the open loopback mint,
1437
- // `ticket` the five-minute ticket exchange, `tailscale` an identity the
1438
- // local daemon confirmed — and then the name to greet.
1439
- signedInThrough: session.via,
1440
- ...(session.via === 'tailscale' ? { tailscaleName: session.tailscaleName, tailscaleLogin: session.tailscaleLogin } : {}),
1697
+ // `ticket` the five-minute ticket exchange, or the provider that
1698
+ // verified it (`tailscale`, `cloudflare-access`) — and then the
1699
+ // name to greet.
1700
+ signedInThrough: session.via === 'provider' ? session.provider : session.via,
1701
+ ...(session.via === 'provider' ? { provider: session.provider, providerSubject: session.providerSubject } : {}),
1702
+ ...(session.via === 'provider' && session.provider === 'tailscale'
1703
+ ? { tailscaleName: session.providerDetail?.name ?? session.providerSubject, tailscaleLogin: session.providerSubject }
1704
+ : {}),
1441
1705
  expiresAt: session.expiresAt.toISOString(),
1442
1706
  // What this gateway is running. The page keeps it across an
1443
1707
  // upgrade so that "it came back" can be told from "it is still
@@ -1452,20 +1716,24 @@ export function createWebApp(deps) {
1452
1716
  * memory), and whether this very request came through the proxy —
1453
1717
  * which is what disables the switch on a tailnet browser.
1454
1718
  */
1455
- case '/api/tailscale': {
1456
- const stored = await readTailscaleSetting();
1457
- const daemon = await tailscaleSelfOf();
1458
- return sendJson(res, 200, {
1459
- enabled: stored.enabled,
1460
- login: stored.login,
1461
- available: daemon.available,
1462
- self: daemon.self,
1463
- proxied: session.via === 'tailscale' || proxiedThroughTailscale(req),
1464
- // What the owner has to run on this machine, with this
1465
- // installation's own two ports in it.
1466
- serveCommand: serveCommand(),
1467
- });
1468
- }
1719
+ // `/api/tailscale` stays one release as an alias (docs/api.md).
1720
+ case '/api/tailscale':
1721
+ case '/api/access/tailscale':
1722
+ return sendJson(res, 200, await tailscaleView(req, session));
1723
+ /*
1724
+ * "Sign in from elsewhere": every provider's row — its status in one
1725
+ * line — and whether this very request came through one, which makes
1726
+ * the whole block read-only.
1727
+ */
1728
+ case '/api/access':
1729
+ return sendJson(res, 200, await accessView(req, session));
1730
+ case '/api/access/cloudflare-access':
1731
+ return sendJson(res, 200, await cloudflareView(req, session));
1732
+ case '/api/access/cloudflare-access/setup':
1733
+ // The install line holds the tunnel's connector token: this machine only.
1734
+ if (session.via !== 'local')
1735
+ return sendJson(res, 403, { error: 'Change this from the computer buddi runs on.' });
1736
+ return sendJson(res, 200, await setupView());
1469
1737
  case '/api/overview':
1470
1738
  return sendJson(res, 200, {
1471
1739
  ...(await readOverview({
@@ -2225,7 +2493,7 @@ export function createWebApp(deps) {
2225
2493
  * everything else.
2226
2494
  */
2227
2495
  if (method === 'PUT') {
2228
- const puttable = ['/api/backups/schedule', '/api/backups/passphrase', '/api/version/check', '/api/tailscale', '/api/notifications/settings', '/api/notifications/focus'];
2496
+ const puttable = ['/api/backups/schedule', '/api/backups/passphrase', '/api/version/check', '/api/tailscale', '/api/access/tailscale', '/api/access/cloudflare-access', '/api/notifications/settings', '/api/notifications/focus'];
2229
2497
  if (!puttable.includes(path))
2230
2498
  return sendEmpty(res, 405);
2231
2499
  let put;
@@ -2235,7 +2503,20 @@ export function createWebApp(deps) {
2235
2503
  catch {
2236
2504
  return sendJson(res, 400, { error: 'request body must be JSON' });
2237
2505
  }
2238
- if (path === '/api/tailscale') {
2506
+ if (path === '/api/access/cloudflare-access') {
2507
+ // Who may widen access: only a browser on this machine, as for Tailscale below.
2508
+ if (session.via !== 'local') {
2509
+ return sendJson(res, 403, { error: 'Change this from the computer buddi runs on.' });
2510
+ }
2511
+ const checked = validateCloudflareInput(put);
2512
+ if (!checked.ok)
2513
+ return sendJson(res, 400, { error: checked.error });
2514
+ await applyCloudflareSetting(checked.value);
2515
+ // On Save buddi fetches the team's keys once and says whether it worked.
2516
+ const test = checked.value.enabled ? await jwks.refresh(checked.value.teamDomain) : null;
2517
+ return sendJson(res, 200, { ...(await cloudflareView(req, session)), ...(test ? { test } : {}) });
2518
+ }
2519
+ if (path === '/api/tailscale' || path === '/api/access/tailscale') {
2239
2520
  /*
2240
2521
  * Who may widen access: only a browser that is already on this
2241
2522
  * machine.
@@ -2272,20 +2553,11 @@ export function createWebApp(deps) {
2272
2553
  * in a browser on the far side of the tailnet.
2273
2554
  */
2274
2555
  const forgotten = new Set();
2275
- await sessions.forget((s) => { const drop = s.via === 'tailscale'; if (drop && s.id)
2556
+ await sessions.forget((s) => { const drop = s.via === 'provider' && s.provider === 'tailscale'; if (drop && s.id)
2276
2557
  forgotten.add(s.id); return drop; });
2277
2558
  // Including whichever of them had a hand on the owner's browser.
2278
2559
  hand.revoke((lease) => forgotten.has(lease), 'Tailscale access changed. Sign in again.');
2279
- const daemon = await tailscaleSelfOf();
2280
- const stored = toTailscaleSetting({ enabled, login });
2281
- return sendJson(res, 200, {
2282
- enabled: stored.enabled,
2283
- login: stored.login,
2284
- available: daemon.available,
2285
- self: daemon.self,
2286
- proxied: false,
2287
- serveCommand: serveCommand(),
2288
- });
2560
+ return sendJson(res, 200, await tailscaleView(req, session));
2289
2561
  }
2290
2562
  if (path === '/api/version/check')
2291
2563
  return reply(res, await versionCheckRoute(versionDeps(), 'PUT', put));
@@ -2299,6 +2571,135 @@ export function createWebApp(deps) {
2299
2571
  }
2300
2572
  if (method !== 'POST')
2301
2573
  return sendEmpty(res, 405);
2574
+ /*
2575
+ * "Test my setup": fetch the team's signing keys for the domain in the
2576
+ * form (saved or not) and say what came back. Nothing is stored. From
2577
+ * this machine only, like the setting itself: it makes this computer
2578
+ * fetch a URL the caller names part of.
2579
+ */
2580
+ /*
2581
+ * "Set it up for me": start a run (202, then poll GET …/setup), stop the
2582
+ * health wait, or remove what buddi made. From this machine only, like the
2583
+ * setting it fills in.
2584
+ */
2585
+ if (path === '/api/access/cloudflare-access/setup' || path === '/api/access/cloudflare-access/setup/stop' || path === '/api/access/cloudflare-access/setup/remove') {
2586
+ if (session.via !== 'local')
2587
+ return sendJson(res, 403, { error: 'Change this from the computer buddi runs on.' });
2588
+ /*
2589
+ * One operation at a time (cloudflare-setup's lock, claimed before the
2590
+ * first await and held to the end): a second setup or a removal while
2591
+ * one runs is a 409, and so is Stop while a removal runs.
2592
+ */
2593
+ if (path.endsWith('/stop')) {
2594
+ const going = setupOperation();
2595
+ if (going === 'remove')
2596
+ return sendJson(res, 409, { error: setupBusySentence(going) });
2597
+ const lease = going === null ? claimSetupOperation('setup') : null;
2598
+ try {
2599
+ setupRun?.abort.abort();
2600
+ await setupRun?.done;
2601
+ return sendJson(res, 200, await setupView());
2602
+ }
2603
+ finally {
2604
+ lease?.release();
2605
+ }
2606
+ }
2607
+ const lease = claimSetupOperation(path.endsWith('/remove') ? 'remove' : 'setup');
2608
+ if (!lease)
2609
+ return sendJson(res, 409, { error: setupBusySentence(setupOperation()) });
2610
+ let handedOff = false;
2611
+ try {
2612
+ const body = await readJsonBody(req).catch(() => ({}));
2613
+ const str = (v) => (typeof v === 'string' ? v.trim() : '');
2614
+ const tokens = tokenStore();
2615
+ const pasted = str(body.token);
2616
+ if (path.endsWith('/remove')) {
2617
+ const token = pasted || await tokens.use().catch(() => null);
2618
+ if (!token)
2619
+ return sendJson(res, 400, { error: 'Paste the Cloudflare API token again: buddi needs it to remove what it made.' });
2620
+ const run = {
2621
+ progress: { ...freshProgress(), state: 'removing', steps: [] }, abort: new AbortController(), done: Promise.resolve(),
2622
+ };
2623
+ setupRun = run;
2624
+ const progress = await removeCloudflareSetup({ ...setupDepsFor(token, lease), host: str(body.host) || undefined }, (p) => { run.progress = p; });
2625
+ run.progress = progress;
2626
+ if (!progress.error)
2627
+ await tokens.remove().catch(() => undefined);
2628
+ return sendJson(res, 200, await setupView());
2629
+ }
2630
+ const input = { host: str(body.host), email: str(body.email), zone: str(body.zone) || undefined, adopt: body.adopt === true };
2631
+ const invalid = checkSetupInput(input);
2632
+ if (invalid)
2633
+ return sendJson(res, 400, { error: invalid });
2634
+ if (pasted) {
2635
+ if (/\s/.test(pasted) || pasted.length < 20 || pasted.length > 400)
2636
+ return sendJson(res, 400, { error: 'That doesn’t look like a Cloudflare API token.' });
2637
+ try {
2638
+ await tokens.put(pasted);
2639
+ }
2640
+ catch {
2641
+ return sendJson(res, 409, { error: 'The token could not be kept in the vault. Check that the vault is unlocked, then try again.' });
2642
+ }
2643
+ }
2644
+ const token = pasted || await tokens.use().catch(() => null);
2645
+ if (!token)
2646
+ return sendJson(res, 400, { error: 'Paste a Cloudflare API token.' });
2647
+ const first = { ...freshProgress(input.host, input.email), state: 'running' };
2648
+ startSetup(lease, (onProgress, signal) => runCloudflareSetup(input, setupDepsFor(token, lease, signal), onProgress), first);
2649
+ handedOff = true;
2650
+ return sendJson(res, 202, await setupView());
2651
+ }
2652
+ finally {
2653
+ if (!handedOff)
2654
+ lease.release();
2655
+ }
2656
+ }
2657
+ /*
2658
+ * The setup form's domain choice: check the token and list the zones it
2659
+ * can see. The pasted token is only used for these two calls — keeping it
2660
+ * stays with setup — and never goes back out, not even in an error.
2661
+ */
2662
+ if (path === '/api/access/cloudflare-access/zones') {
2663
+ if (session.via !== 'local')
2664
+ return sendJson(res, 403, { error: 'Change this from the computer buddi runs on.' });
2665
+ const body = await readJsonBody(req).catch(() => ({}));
2666
+ const pasted = typeof body?.token === 'string' ? body.token.trim() : '';
2667
+ if (pasted && (/\s/.test(pasted) || pasted.length < 20 || pasted.length > 400))
2668
+ return sendJson(res, 400, { error: 'That doesn’t look like a Cloudflare API token.' });
2669
+ const token = pasted || await tokenStore().use().catch(() => null);
2670
+ if (!token)
2671
+ return sendJson(res, 400, { error: 'Paste a Cloudflare API token.' });
2672
+ const api = createCloudflareApi({ token, transport: deps.cloudflare?.api?.transport, baseUrl: deps.cloudflare?.api?.baseUrl });
2673
+ try {
2674
+ if (await api.verifyToken() === 'inactive')
2675
+ return sendJson(res, 400, { error: 'Cloudflare says this API token is not active. Check that it has not expired or been turned off.' });
2676
+ const zones = await api.zones();
2677
+ return sendJson(res, 200, { zones: zones.map((z) => ({ id: z.id, name: z.name })) });
2678
+ }
2679
+ catch (error) {
2680
+ if (!(error instanceof CloudflareApiError))
2681
+ throw error;
2682
+ const refused = error.status === 401 || error.permission !== undefined || (error.status >= 400 && error.status < 500 && error.status !== 429);
2683
+ return sendJson(res, refused ? 400 : 502, { error: error.message });
2684
+ }
2685
+ }
2686
+ if (path === '/api/access/cloudflare-access/test') {
2687
+ if (session.via !== 'local')
2688
+ return sendJson(res, 403, { error: 'Change this from the computer buddi runs on.' });
2689
+ const body = await readJsonBody(req).catch(() => ({}));
2690
+ const team = normalizeTeamDomain(typeof body.teamDomain === 'string' ? body.teamDomain : (await readCloudflareSetting()).teamDomain);
2691
+ if (!plausibleTeamDomain(team)) {
2692
+ return sendJson(res, 400, { error: 'That is not a Cloudflare team domain. It looks like yourteam.cloudflareaccess.com, under Zero Trust → Settings.' });
2693
+ }
2694
+ const result = await jwks.refresh(team);
2695
+ return sendJson(res, 200, {
2696
+ ...result,
2697
+ teamDomain: team,
2698
+ sentence: result.ok ? `${team} answered with ${result.keys} signing key${result.keys === 1 ? '' : 's'}.` : result.error,
2699
+ listening: ingress.port() !== null,
2700
+ ingressPort: ingress.port(),
2701
+ });
2702
+ }
2302
2703
  if (path === '/api/extension/pair') {
2303
2704
  // A six-digit code is worth guessing at scale, so a wrong one costs a
2304
2705
  // budget — this session's pairing budget, not the shared sign-in one.
@@ -2345,24 +2746,47 @@ export function createWebApp(deps) {
2345
2746
  return sendJson(res, 200, { ok: false, message: error instanceof Error ? error.message : String(error) });
2346
2747
  }
2347
2748
  }
2348
- if (path === '/api/browser/settings' || path === '/api/browser/permissions') {
2749
+ if (path === '/api/browser/settings') {
2349
2750
  const body = await readJsonBody(req);
2350
2751
  try {
2351
- if (path.endsWith('/settings')) {
2352
- if (!browser.configure)
2353
- return sendJson(res, 409, { error: 'This host does not support changing control modes.' });
2354
- return sendJson(res, 200, await browser.configure(body));
2355
- }
2356
- if (!body || typeof body !== 'object' || Array.isArray(body) || Object.keys(body).some((key) => key !== 'prompt') || ('prompt' in body && typeof body.prompt !== 'boolean'))
2357
- return sendJson(res, 400, { error: 'Expected {prompt: boolean}' });
2358
- if (!browser.checkPermissions)
2359
- return sendJson(res, 409, { error: 'This host does not support native permission checks.' });
2360
- return sendJson(res, 200, await browser.checkPermissions(body.prompt === true));
2752
+ if (!browser.configure)
2753
+ return sendJson(res, 409, { error: 'This host does not support changing control modes.' });
2754
+ return sendJson(res, 200, await browser.configure(body));
2361
2755
  }
2362
2756
  catch (error) {
2363
2757
  return sendJson(res, 409, { error: error instanceof Error ? error.message : String(error) });
2364
2758
  }
2365
2759
  }
2760
+ /*
2761
+ * Pin one conversation to a route (the chip under the composer,
2762
+ * `/use browser:chrome`), or clear it with `auto`. A pin narrows; it never
2763
+ * allows a route the owner's switches forbid.
2764
+ */
2765
+ if (path === '/api/browser/pin') {
2766
+ const body = await readJsonBody(req);
2767
+ if (typeof body?.conversationId !== 'string' || typeof body.route !== 'string')
2768
+ return sendJson(res, 400, { error: 'Expected {conversationId: string, route: auto|own|chrome|apps}' });
2769
+ if (!browser.pin)
2770
+ return sendJson(res, 409, { error: 'This host cannot pin a route.' });
2771
+ try {
2772
+ return sendJson(res, 200, await browser.pin(body.conversationId, body.route));
2773
+ }
2774
+ catch (error) {
2775
+ return sendJson(res, 400, { error: error instanceof Error ? error.message : String(error) });
2776
+ }
2777
+ }
2778
+ /*
2779
+ * A tap on a browser card (Look, Keep going, Take over, Use my Chrome,
2780
+ * Resume) from a page that draws its own buttons: the same as the owner
2781
+ * sending the label in the chat, without a message.
2782
+ */
2783
+ if (path === '/api/browser/card') {
2784
+ const body = await readJsonBody(req);
2785
+ if (typeof body?.conversationId !== 'string' || typeof body.answer !== 'string' || body.answer.length > 80)
2786
+ return sendJson(res, 400, { error: 'Expected {conversationId: string, answer: string}' });
2787
+ const answered = await browser.touch?.({ conversationId: body.conversationId, text: body.answer });
2788
+ return sendJson(res, 200, { ...(answered && 'answered' in answered ? answered : {}), status: browser.status({ conversationId: body.conversationId }) });
2789
+ }
2366
2790
  const control = /^\/api\/browser\/(stop|takeover|resume|release)$/.exec(path);
2367
2791
  if (control) {
2368
2792
  const body = await readJsonBody(req);
@@ -2377,7 +2801,8 @@ export function createWebApp(deps) {
2377
2801
  // frozen picture.
2378
2802
  if (action !== 'takeover')
2379
2803
  await hand.close(action === 'stop' ? undefined : sessionId, action === 'resume' ? 'You gave the screen back.' : 'That session was released.');
2380
- const status = await browser.control(action, sessionId);
2804
+ // Stop agents' browsing expires (an hour by default); `forever` is "until I say".
2805
+ const status = await browser.control(action, sessionId, action === 'stop' && body?.forever === true ? { forever: true } : undefined);
2381
2806
  // Whether this screen can be driven from here at all. The dashboard
2382
2807
  // shows the mode's own sentence when it cannot.
2383
2808
  const taken = sessionId ?? status.session?.id;
@@ -2388,7 +2813,7 @@ export function createWebApp(deps) {
2388
2813
  * as that, with a reason the dashboard offers its ways out on, rather
2389
2814
  * than the mode's "use this conversation's host tab".
2390
2815
  */
2391
- if (offer && offer.supported && !offer.hand && status.mode === 'extension' && !extension.connected()) {
2816
+ if (offer && offer.supported && !offer.hand && (status.route === 'chrome' || status.mode === 'extension') && !extension.connected()) {
2392
2817
  return sendJson(res, 200, { ...status, hand: false, handReason: 'browser-offline', handMessage: 'Your browser isn’t connected.' });
2393
2818
  }
2394
2819
  return sendJson(res, 200, offer
@@ -3724,6 +4149,10 @@ export async function startWebServer(deps) {
3724
4149
  */
3725
4150
  if (previewPort !== null)
3726
4151
  publishPreviewPort(deps.env ?? process.env, previewPort);
4152
+ // The ingress listener, after the previews have taken their port: bound
4153
+ // now when Cloudflare Access is on, and from then on as the setting changes.
4154
+ const ingress = ingressOf(server);
4155
+ await ingress.sync();
3727
4156
  /*
3728
4157
  * An approval nobody decides expires, and a delegation waiting on one is
3729
4158
  * handed the expiry as its failure: the approval's own lifetime is the
@@ -3741,6 +4170,7 @@ export async function startWebServer(deps) {
3741
4170
  server,
3742
4171
  port,
3743
4172
  previewPort,
4173
+ ingress,
3744
4174
  url: webUrl({ host: deps.config.host, port }),
3745
4175
  chat: webChatOf(server),
3746
4176
  close: async () => {
@@ -3752,6 +4182,7 @@ export async function startWebServer(deps) {
3752
4182
  // still held, and the next thing to want it — the next test, the
3753
4183
  // gateway coming back up — finds it taken.
3754
4184
  await Promise.all([
4185
+ ingress.close(),
3755
4186
  new Promise((resolve) => {
3756
4187
  if (!previews || previewPort === null)
3757
4188
  return resolve();