@withbuddi/buddi 0.1.0-pre.36 → 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 (975) hide show
  1. package/examples/agents/concierge/agent.md +5 -0
  2. package/examples/skills/letterhead/SKILL.md +21 -0
  3. package/examples/skills/letterhead/assets/letterhead.md +16 -0
  4. package/examples/skills/letterhead/assets/logo.svg +6 -0
  5. package/extension/background.js +130 -12
  6. package/extension/manifest.json +2 -2
  7. package/node_modules/@buddi/cli/dist/.tsbuildinfo +1 -1
  8. package/node_modules/@buddi/cli/dist/access-cmd.d.ts +10 -0
  9. package/node_modules/@buddi/cli/dist/access-cmd.d.ts.map +1 -0
  10. package/node_modules/@buddi/cli/dist/access-cmd.js +160 -0
  11. package/node_modules/@buddi/cli/dist/access-cmd.js.map +1 -0
  12. package/node_modules/@buddi/cli/dist/args.d.ts +10 -0
  13. package/node_modules/@buddi/cli/dist/args.d.ts.map +1 -1
  14. package/node_modules/@buddi/cli/dist/args.js +29 -1
  15. package/node_modules/@buddi/cli/dist/args.js.map +1 -1
  16. package/node_modules/@buddi/cli/dist/commands.d.ts.map +1 -1
  17. package/node_modules/@buddi/cli/dist/commands.js +28 -2
  18. package/node_modules/@buddi/cli/dist/commands.js.map +1 -1
  19. package/node_modules/@buddi/cli/dist/doctor-probes.d.ts.map +1 -1
  20. package/node_modules/@buddi/cli/dist/doctor-probes.js +90 -30
  21. package/node_modules/@buddi/cli/dist/doctor-probes.js.map +1 -1
  22. package/node_modules/@buddi/cli/dist/doctor.d.ts +61 -12
  23. package/node_modules/@buddi/cli/dist/doctor.d.ts.map +1 -1
  24. package/node_modules/@buddi/cli/dist/doctor.js +60 -9
  25. package/node_modules/@buddi/cli/dist/doctor.js.map +1 -1
  26. package/node_modules/@buddi/cli/dist/main.d.ts +2 -0
  27. package/node_modules/@buddi/cli/dist/main.d.ts.map +1 -1
  28. package/node_modules/@buddi/cli/dist/main.js +18 -1
  29. package/node_modules/@buddi/cli/dist/main.js.map +1 -1
  30. package/node_modules/@buddi/core/dist/.tsbuildinfo +1 -1
  31. package/node_modules/@buddi/core/dist/actions/approvals.d.ts +7 -0
  32. package/node_modules/@buddi/core/dist/actions/approvals.d.ts.map +1 -1
  33. package/node_modules/@buddi/core/dist/actions/approvals.js +11 -1
  34. package/node_modules/@buddi/core/dist/actions/approvals.js.map +1 -1
  35. package/node_modules/@buddi/core/dist/agents/catalog.d.ts +2 -0
  36. package/node_modules/@buddi/core/dist/agents/catalog.d.ts.map +1 -1
  37. package/node_modules/@buddi/core/dist/agents/catalog.js +3 -2
  38. package/node_modules/@buddi/core/dist/agents/catalog.js.map +1 -1
  39. package/node_modules/@buddi/core/dist/agents/edit.d.ts +2 -0
  40. package/node_modules/@buddi/core/dist/agents/edit.d.ts.map +1 -1
  41. package/node_modules/@buddi/core/dist/agents/edit.js +3 -1
  42. package/node_modules/@buddi/core/dist/agents/edit.js.map +1 -1
  43. package/node_modules/@buddi/core/dist/agents/frontmatter.d.ts +11 -0
  44. package/node_modules/@buddi/core/dist/agents/frontmatter.d.ts.map +1 -1
  45. package/node_modules/@buddi/core/dist/agents/frontmatter.js +7 -0
  46. package/node_modules/@buddi/core/dist/agents/frontmatter.js.map +1 -1
  47. package/node_modules/@buddi/core/dist/agents/skills.d.ts +60 -1
  48. package/node_modules/@buddi/core/dist/agents/skills.d.ts.map +1 -1
  49. package/node_modules/@buddi/core/dist/agents/skills.js +157 -17
  50. package/node_modules/@buddi/core/dist/agents/skills.js.map +1 -1
  51. package/node_modules/@buddi/core/dist/day-month.d.ts +52 -0
  52. package/node_modules/@buddi/core/dist/day-month.d.ts.map +1 -0
  53. package/node_modules/@buddi/core/dist/day-month.js +101 -0
  54. package/node_modules/@buddi/core/dist/day-month.js.map +1 -0
  55. package/node_modules/@buddi/core/dist/host/build.d.ts +11 -2
  56. package/node_modules/@buddi/core/dist/host/build.d.ts.map +1 -1
  57. package/node_modules/@buddi/core/dist/host/build.js +115 -4
  58. package/node_modules/@buddi/core/dist/host/build.js.map +1 -1
  59. package/node_modules/@buddi/core/dist/host/http.d.ts +35 -0
  60. package/node_modules/@buddi/core/dist/host/http.d.ts.map +1 -1
  61. package/node_modules/@buddi/core/dist/host/http.js +133 -29
  62. package/node_modules/@buddi/core/dist/host/http.js.map +1 -1
  63. package/node_modules/@buddi/core/dist/host/types.d.ts +72 -1
  64. package/node_modules/@buddi/core/dist/host/types.d.ts.map +1 -1
  65. package/node_modules/@buddi/core/dist/index.d.ts +3 -0
  66. package/node_modules/@buddi/core/dist/index.d.ts.map +1 -1
  67. package/node_modules/@buddi/core/dist/index.js +3 -0
  68. package/node_modules/@buddi/core/dist/index.js.map +1 -1
  69. package/node_modules/@buddi/core/dist/onboarding/store.d.ts.map +1 -1
  70. package/node_modules/@buddi/core/dist/onboarding/store.js +28 -5
  71. package/node_modules/@buddi/core/dist/onboarding/store.js.map +1 -1
  72. package/node_modules/@buddi/core/dist/onboarding/types.d.ts +10 -0
  73. package/node_modules/@buddi/core/dist/onboarding/types.d.ts.map +1 -1
  74. package/node_modules/@buddi/core/dist/onboarding/types.js.map +1 -1
  75. package/node_modules/@buddi/core/dist/pages.d.ts +145 -11
  76. package/node_modules/@buddi/core/dist/pages.d.ts.map +1 -1
  77. package/node_modules/@buddi/core/dist/pages.js +113 -5
  78. package/node_modules/@buddi/core/dist/pages.js.map +1 -1
  79. package/node_modules/@buddi/core/dist/plugin/index.d.ts +7 -0
  80. package/node_modules/@buddi/core/dist/plugin/index.d.ts.map +1 -1
  81. package/node_modules/@buddi/core/dist/plugin/index.js +5 -0
  82. package/node_modules/@buddi/core/dist/plugin/index.js.map +1 -1
  83. package/node_modules/@buddi/core/dist/plugin/sign-in.d.ts +78 -0
  84. package/node_modules/@buddi/core/dist/plugin/sign-in.d.ts.map +1 -0
  85. package/node_modules/@buddi/core/dist/plugin/sign-in.js +54 -0
  86. package/node_modules/@buddi/core/dist/plugin/sign-in.js.map +1 -0
  87. package/node_modules/@buddi/core/dist/plugin/version.d.ts +1 -1
  88. package/node_modules/@buddi/core/dist/plugin/version.js +1 -1
  89. package/node_modules/@buddi/core/dist/provider-accounts.d.ts +7 -0
  90. package/node_modules/@buddi/core/dist/provider-accounts.d.ts.map +1 -1
  91. package/node_modules/@buddi/core/dist/provider-accounts.js +15 -0
  92. package/node_modules/@buddi/core/dist/provider-accounts.js.map +1 -1
  93. package/node_modules/@buddi/core/dist/questions/store.d.ts +12 -0
  94. package/node_modules/@buddi/core/dist/questions/store.d.ts.map +1 -1
  95. package/node_modules/@buddi/core/dist/questions/store.js +11 -1
  96. package/node_modules/@buddi/core/dist/questions/store.js.map +1 -1
  97. package/node_modules/@buddi/core/dist/questions/types.d.ts +9 -1
  98. package/node_modules/@buddi/core/dist/questions/types.d.ts.map +1 -1
  99. package/node_modules/@buddi/core/dist/questions/types.js +16 -1
  100. package/node_modules/@buddi/core/dist/questions/types.js.map +1 -1
  101. package/node_modules/@buddi/core/dist/queue/jobs.d.ts +65 -2
  102. package/node_modules/@buddi/core/dist/queue/jobs.d.ts.map +1 -1
  103. package/node_modules/@buddi/core/dist/queue/jobs.js +123 -3
  104. package/node_modules/@buddi/core/dist/queue/jobs.js.map +1 -1
  105. package/node_modules/@buddi/core/dist/queue/worker.d.ts +26 -2
  106. package/node_modules/@buddi/core/dist/queue/worker.d.ts.map +1 -1
  107. package/node_modules/@buddi/core/dist/queue/worker.js +68 -12
  108. package/node_modules/@buddi/core/dist/queue/worker.js.map +1 -1
  109. package/node_modules/@buddi/core/dist/registry.d.ts +15 -1
  110. package/node_modules/@buddi/core/dist/registry.d.ts.map +1 -1
  111. package/node_modules/@buddi/core/dist/registry.js +88 -13
  112. package/node_modules/@buddi/core/dist/registry.js.map +1 -1
  113. package/node_modules/@buddi/core/dist/routes.d.ts +153 -0
  114. package/node_modules/@buddi/core/dist/routes.d.ts.map +1 -0
  115. package/node_modules/@buddi/core/dist/routes.js +47 -0
  116. package/node_modules/@buddi/core/dist/routes.js.map +1 -0
  117. package/node_modules/@buddi/core/dist/scheduler/missions.d.ts +5 -3
  118. package/node_modules/@buddi/core/dist/scheduler/missions.d.ts.map +1 -1
  119. package/node_modules/@buddi/core/dist/scheduler/missions.js +9 -7
  120. package/node_modules/@buddi/core/dist/scheduler/missions.js.map +1 -1
  121. package/node_modules/@buddi/core/dist/scheduler/types.d.ts +7 -0
  122. package/node_modules/@buddi/core/dist/scheduler/types.d.ts.map +1 -1
  123. package/node_modules/@buddi/core/dist/scheduler/types.js +1 -0
  124. package/node_modules/@buddi/core/dist/scheduler/types.js.map +1 -1
  125. package/node_modules/@buddi/core/dist/secrets/scrub.d.ts.map +1 -1
  126. package/node_modules/@buddi/core/dist/secrets/scrub.js +15 -0
  127. package/node_modules/@buddi/core/dist/secrets/scrub.js.map +1 -1
  128. package/node_modules/@buddi/core/dist/tools.d.ts +88 -2
  129. package/node_modules/@buddi/core/dist/tools.d.ts.map +1 -1
  130. package/node_modules/@buddi/core/dist/tools.js.map +1 -1
  131. package/node_modules/@buddi/core/dist/views.d.ts +12 -12
  132. package/node_modules/@buddi/core/dist/widgets.d.ts +14 -1
  133. package/node_modules/@buddi/core/dist/widgets.d.ts.map +1 -1
  134. package/node_modules/@buddi/core/dist/widgets.js +30 -0
  135. package/node_modules/@buddi/core/dist/widgets.js.map +1 -1
  136. package/node_modules/@buddi/core/migrations/074_owner_identity.sql +14 -0
  137. package/node_modules/@buddi/core/migrations/075_access_providers.sql +43 -0
  138. package/node_modules/@buddi/core/migrations/076_mission_browser.sql +7 -0
  139. package/node_modules/@buddi/gateway/dist/.tsbuildinfo +1 -1
  140. package/node_modules/@buddi/gateway/dist/agents/canvas.d.ts +12 -12
  141. package/node_modules/@buddi/gateway/dist/agents/catalog.d.ts.map +1 -1
  142. package/node_modules/@buddi/gateway/dist/agents/catalog.js +3 -3
  143. package/node_modules/@buddi/gateway/dist/agents/catalog.js.map +1 -1
  144. package/node_modules/@buddi/gateway/dist/agents/catalogue-package.d.ts +46 -20
  145. package/node_modules/@buddi/gateway/dist/agents/catalogue-package.d.ts.map +1 -1
  146. package/node_modules/@buddi/gateway/dist/agents/catalogue-package.js +17 -4
  147. package/node_modules/@buddi/gateway/dist/agents/catalogue-package.js.map +1 -1
  148. package/node_modules/@buddi/gateway/dist/agents/delegation.d.ts +10 -0
  149. package/node_modules/@buddi/gateway/dist/agents/delegation.d.ts.map +1 -1
  150. package/node_modules/@buddi/gateway/dist/agents/delegation.js +1 -0
  151. package/node_modules/@buddi/gateway/dist/agents/delegation.js.map +1 -1
  152. package/node_modules/@buddi/gateway/dist/agents/owner-tools.d.ts.map +1 -1
  153. package/node_modules/@buddi/gateway/dist/agents/owner-tools.js +36 -2
  154. package/node_modules/@buddi/gateway/dist/agents/owner-tools.js.map +1 -1
  155. package/node_modules/@buddi/gateway/dist/agents/platform-catalogue.d.ts +26 -1
  156. package/node_modules/@buddi/gateway/dist/agents/platform-catalogue.d.ts.map +1 -1
  157. package/node_modules/@buddi/gateway/dist/agents/platform-catalogue.js +123 -6
  158. package/node_modules/@buddi/gateway/dist/agents/platform-catalogue.js.map +1 -1
  159. package/node_modules/@buddi/gateway/dist/agents/platform.d.ts +4 -4
  160. package/node_modules/@buddi/gateway/dist/agents/platform.d.ts.map +1 -1
  161. package/node_modules/@buddi/gateway/dist/agents/platform.js +11 -0
  162. package/node_modules/@buddi/gateway/dist/agents/platform.js.map +1 -1
  163. package/node_modules/@buddi/gateway/dist/bootstrap.d.ts.map +1 -1
  164. package/node_modules/@buddi/gateway/dist/bootstrap.js +12 -1
  165. package/node_modules/@buddi/gateway/dist/bootstrap.js.map +1 -1
  166. package/node_modules/@buddi/gateway/dist/browser-host.d.ts +30 -1
  167. package/node_modules/@buddi/gateway/dist/browser-host.d.ts.map +1 -1
  168. package/node_modules/@buddi/gateway/dist/browser-host.js +56 -1
  169. package/node_modules/@buddi/gateway/dist/browser-host.js.map +1 -1
  170. package/node_modules/@buddi/gateway/dist/chat/session.d.ts.map +1 -1
  171. package/node_modules/@buddi/gateway/dist/chat/session.js +9 -5
  172. package/node_modules/@buddi/gateway/dist/chat/session.js.map +1 -1
  173. package/node_modules/@buddi/gateway/dist/evals/golden.d.ts +33 -1
  174. package/node_modules/@buddi/gateway/dist/evals/golden.d.ts.map +1 -1
  175. package/node_modules/@buddi/gateway/dist/evals/golden.js +168 -3
  176. package/node_modules/@buddi/gateway/dist/evals/golden.js.map +1 -1
  177. package/node_modules/@buddi/gateway/dist/index.d.ts +3 -1
  178. package/node_modules/@buddi/gateway/dist/index.d.ts.map +1 -1
  179. package/node_modules/@buddi/gateway/dist/index.js +3 -1
  180. package/node_modules/@buddi/gateway/dist/index.js.map +1 -1
  181. package/node_modules/@buddi/gateway/dist/missions/dates.d.ts +98 -0
  182. package/node_modules/@buddi/gateway/dist/missions/dates.d.ts.map +1 -0
  183. package/node_modules/@buddi/gateway/dist/missions/dates.js +252 -0
  184. package/node_modules/@buddi/gateway/dist/missions/dates.js.map +1 -0
  185. package/node_modules/@buddi/gateway/dist/missions/execute.d.ts +42 -1
  186. package/node_modules/@buddi/gateway/dist/missions/execute.d.ts.map +1 -1
  187. package/node_modules/@buddi/gateway/dist/missions/execute.js +107 -8
  188. package/node_modules/@buddi/gateway/dist/missions/execute.js.map +1 -1
  189. package/node_modules/@buddi/gateway/dist/missions/parked.d.ts +133 -0
  190. package/node_modules/@buddi/gateway/dist/missions/parked.d.ts.map +1 -0
  191. package/node_modules/@buddi/gateway/dist/missions/parked.js +249 -0
  192. package/node_modules/@buddi/gateway/dist/missions/parked.js.map +1 -0
  193. package/node_modules/@buddi/gateway/dist/missions/report.d.ts.map +1 -1
  194. package/node_modules/@buddi/gateway/dist/missions/report.js +1 -0
  195. package/node_modules/@buddi/gateway/dist/missions/report.js.map +1 -1
  196. package/node_modules/@buddi/gateway/dist/ollama-local.d.ts +90 -0
  197. package/node_modules/@buddi/gateway/dist/ollama-local.d.ts.map +1 -0
  198. package/node_modules/@buddi/gateway/dist/ollama-local.js +201 -0
  199. package/node_modules/@buddi/gateway/dist/ollama-local.js.map +1 -0
  200. package/node_modules/@buddi/gateway/dist/plugins-cli.d.ts +8 -0
  201. package/node_modules/@buddi/gateway/dist/plugins-cli.d.ts.map +1 -1
  202. package/node_modules/@buddi/gateway/dist/plugins-cli.js +22 -1
  203. package/node_modules/@buddi/gateway/dist/plugins-cli.js.map +1 -1
  204. package/node_modules/@buddi/gateway/dist/serve.d.ts +7 -0
  205. package/node_modules/@buddi/gateway/dist/serve.d.ts.map +1 -1
  206. package/node_modules/@buddi/gateway/dist/serve.js +99 -14
  207. package/node_modules/@buddi/gateway/dist/serve.js.map +1 -1
  208. package/node_modules/@buddi/gateway/dist/surfaces/browser-cards.d.ts +18 -0
  209. package/node_modules/@buddi/gateway/dist/surfaces/browser-cards.d.ts.map +1 -0
  210. package/node_modules/@buddi/gateway/dist/surfaces/browser-cards.js +42 -0
  211. package/node_modules/@buddi/gateway/dist/surfaces/browser-cards.js.map +1 -0
  212. package/node_modules/@buddi/gateway/dist/surfaces/browser-continuation.js +1 -1
  213. package/node_modules/@buddi/gateway/dist/surfaces/browser-continuation.js.map +1 -1
  214. package/node_modules/@buddi/gateway/dist/surfaces/owner-request.d.ts +6 -0
  215. package/node_modules/@buddi/gateway/dist/surfaces/owner-request.d.ts.map +1 -1
  216. package/node_modules/@buddi/gateway/dist/surfaces/owner-request.js +7 -1
  217. package/node_modules/@buddi/gateway/dist/surfaces/owner-request.js.map +1 -1
  218. package/node_modules/@buddi/gateway/dist/surfaces/pending-question.js +2 -2
  219. package/node_modules/@buddi/gateway/dist/system-context.d.ts +21 -1
  220. package/node_modules/@buddi/gateway/dist/system-context.d.ts.map +1 -1
  221. package/node_modules/@buddi/gateway/dist/system-context.js +75 -2
  222. package/node_modules/@buddi/gateway/dist/system-context.js.map +1 -1
  223. package/node_modules/@buddi/gateway/dist/telegram/approvals.d.ts.map +1 -1
  224. package/node_modules/@buddi/gateway/dist/telegram/approvals.js +4 -2
  225. package/node_modules/@buddi/gateway/dist/telegram/approvals.js.map +1 -1
  226. package/node_modules/@buddi/gateway/dist/telegram/browser-view.d.ts +17 -17
  227. package/node_modules/@buddi/gateway/dist/telegram/browser-view.d.ts.map +1 -1
  228. package/node_modules/@buddi/gateway/dist/telegram/browser-view.js +33 -37
  229. package/node_modules/@buddi/gateway/dist/telegram/browser-view.js.map +1 -1
  230. package/node_modules/@buddi/gateway/dist/telegram/channel.d.ts.map +1 -1
  231. package/node_modules/@buddi/gateway/dist/telegram/channel.js +23 -2
  232. package/node_modules/@buddi/gateway/dist/telegram/channel.js.map +1 -1
  233. package/node_modules/@buddi/gateway/dist/telegram/html.d.ts +34 -0
  234. package/node_modules/@buddi/gateway/dist/telegram/html.d.ts.map +1 -0
  235. package/node_modules/@buddi/gateway/dist/telegram/html.js +263 -0
  236. package/node_modules/@buddi/gateway/dist/telegram/html.js.map +1 -0
  237. package/node_modules/@buddi/gateway/dist/telegram/main.d.ts.map +1 -1
  238. package/node_modules/@buddi/gateway/dist/telegram/main.js +11 -3
  239. package/node_modules/@buddi/gateway/dist/telegram/main.js.map +1 -1
  240. package/node_modules/@buddi/gateway/dist/telegram/notify.d.ts +6 -0
  241. package/node_modules/@buddi/gateway/dist/telegram/notify.d.ts.map +1 -1
  242. package/node_modules/@buddi/gateway/dist/telegram/notify.js +25 -3
  243. package/node_modules/@buddi/gateway/dist/telegram/notify.js.map +1 -1
  244. package/node_modules/@buddi/gateway/dist/telegram/surface.d.ts.map +1 -1
  245. package/node_modules/@buddi/gateway/dist/telegram/surface.js +10 -0
  246. package/node_modules/@buddi/gateway/dist/telegram/surface.js.map +1 -1
  247. package/node_modules/@buddi/gateway/dist/tips/facts.d.ts +10 -0
  248. package/node_modules/@buddi/gateway/dist/tips/facts.d.ts.map +1 -1
  249. package/node_modules/@buddi/gateway/dist/tips/facts.js +12 -3
  250. package/node_modules/@buddi/gateway/dist/tips/facts.js.map +1 -1
  251. package/node_modules/@buddi/gateway/dist/tips/rules.d.ts +2 -0
  252. package/node_modules/@buddi/gateway/dist/tips/rules.d.ts.map +1 -1
  253. package/node_modules/@buddi/gateway/dist/tips/rules.js +25 -0
  254. package/node_modules/@buddi/gateway/dist/tips/rules.js.map +1 -1
  255. package/node_modules/@buddi/gateway/dist/web/access/arrival.d.ts +24 -0
  256. package/node_modules/@buddi/gateway/dist/web/access/arrival.d.ts.map +1 -0
  257. package/node_modules/@buddi/gateway/dist/web/access/arrival.js +18 -0
  258. package/node_modules/@buddi/gateway/dist/web/access/arrival.js.map +1 -0
  259. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-api.d.ts +124 -0
  260. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-api.d.ts.map +1 -0
  261. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-api.js +206 -0
  262. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-api.js.map +1 -0
  263. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-setup.d.ts +163 -0
  264. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-setup.d.ts.map +1 -0
  265. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-setup.js +438 -0
  266. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-setup.js.map +1 -0
  267. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-token.d.ts +31 -0
  268. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-token.d.ts.map +1 -0
  269. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-token.js +74 -0
  270. package/node_modules/@buddi/gateway/dist/web/access/cloudflare-token.js.map +1 -0
  271. package/node_modules/@buddi/gateway/dist/web/access/cloudflare.d.ts +145 -0
  272. package/node_modules/@buddi/gateway/dist/web/access/cloudflare.d.ts.map +1 -0
  273. package/node_modules/@buddi/gateway/dist/web/access/cloudflare.js +474 -0
  274. package/node_modules/@buddi/gateway/dist/web/access/cloudflare.js.map +1 -0
  275. package/node_modules/@buddi/gateway/dist/web/access/index.d.ts +9 -0
  276. package/node_modules/@buddi/gateway/dist/web/access/index.d.ts.map +1 -0
  277. package/node_modules/@buddi/gateway/dist/web/access/index.js +9 -0
  278. package/node_modules/@buddi/gateway/dist/web/access/index.js.map +1 -0
  279. package/node_modules/@buddi/gateway/dist/web/access/ingress.d.ts +30 -0
  280. package/node_modules/@buddi/gateway/dist/web/access/ingress.d.ts.map +1 -0
  281. package/node_modules/@buddi/gateway/dist/web/access/ingress.js +76 -0
  282. package/node_modules/@buddi/gateway/dist/web/access/ingress.js.map +1 -0
  283. package/node_modules/@buddi/gateway/dist/web/access/provider.d.ts +173 -0
  284. package/node_modules/@buddi/gateway/dist/web/access/provider.d.ts.map +1 -0
  285. package/node_modules/@buddi/gateway/dist/web/access/provider.js +11 -0
  286. package/node_modules/@buddi/gateway/dist/web/access/provider.js.map +1 -0
  287. package/node_modules/@buddi/gateway/dist/web/access/registry.d.ts +33 -0
  288. package/node_modules/@buddi/gateway/dist/web/access/registry.d.ts.map +1 -0
  289. package/node_modules/@buddi/gateway/dist/web/access/registry.js +32 -0
  290. package/node_modules/@buddi/gateway/dist/web/access/registry.js.map +1 -0
  291. package/node_modules/@buddi/gateway/dist/web/{tailscale.d.ts → access/tailscale.d.ts} +27 -0
  292. package/node_modules/@buddi/gateway/dist/web/access/tailscale.d.ts.map +1 -0
  293. package/node_modules/@buddi/gateway/dist/web/{tailscale.js → access/tailscale.js} +118 -3
  294. package/node_modules/@buddi/gateway/dist/web/access/tailscale.js.map +1 -0
  295. package/node_modules/@buddi/gateway/dist/web/agents.d.ts +2 -0
  296. package/node_modules/@buddi/gateway/dist/web/agents.d.ts.map +1 -1
  297. package/node_modules/@buddi/gateway/dist/web/agents.js +19 -1
  298. package/node_modules/@buddi/gateway/dist/web/agents.js.map +1 -1
  299. package/node_modules/@buddi/gateway/dist/web/api-routes.d.ts +4 -3
  300. package/node_modules/@buddi/gateway/dist/web/api-routes.d.ts.map +1 -1
  301. package/node_modules/@buddi/gateway/dist/web/api-routes.js +168 -26
  302. package/node_modules/@buddi/gateway/dist/web/api-routes.js.map +1 -1
  303. package/node_modules/@buddi/gateway/dist/web/api-scan.d.ts +1 -1
  304. package/node_modules/@buddi/gateway/dist/web/api-scan.d.ts.map +1 -1
  305. package/node_modules/@buddi/gateway/dist/web/api-scan.js +1 -0
  306. package/node_modules/@buddi/gateway/dist/web/api-scan.js.map +1 -1
  307. package/node_modules/@buddi/gateway/dist/web/attention.d.ts.map +1 -1
  308. package/node_modules/@buddi/gateway/dist/web/attention.js +11 -3
  309. package/node_modules/@buddi/gateway/dist/web/attention.js.map +1 -1
  310. package/node_modules/@buddi/gateway/dist/web/chat.d.ts +23 -0
  311. package/node_modules/@buddi/gateway/dist/web/chat.d.ts.map +1 -1
  312. package/node_modules/@buddi/gateway/dist/web/chat.js +71 -2
  313. package/node_modules/@buddi/gateway/dist/web/chat.js.map +1 -1
  314. package/node_modules/@buddi/gateway/dist/web/client-key.d.ts +3 -0
  315. package/node_modules/@buddi/gateway/dist/web/client-key.d.ts.map +1 -1
  316. package/node_modules/@buddi/gateway/dist/web/client-key.js +8 -1
  317. package/node_modules/@buddi/gateway/dist/web/client-key.js.map +1 -1
  318. package/node_modules/@buddi/gateway/dist/web/composer.d.ts +45 -0
  319. package/node_modules/@buddi/gateway/dist/web/composer.d.ts.map +1 -0
  320. package/node_modules/@buddi/gateway/dist/web/composer.js +85 -0
  321. package/node_modules/@buddi/gateway/dist/web/composer.js.map +1 -0
  322. package/node_modules/@buddi/gateway/dist/web/extension.d.ts +9 -1
  323. package/node_modules/@buddi/gateway/dist/web/extension.d.ts.map +1 -1
  324. package/node_modules/@buddi/gateway/dist/web/extension.js +61 -1
  325. package/node_modules/@buddi/gateway/dist/web/extension.js.map +1 -1
  326. package/node_modules/@buddi/gateway/dist/web/http.d.ts +7 -0
  327. package/node_modules/@buddi/gateway/dist/web/http.d.ts.map +1 -1
  328. package/node_modules/@buddi/gateway/dist/web/http.js +15 -1
  329. package/node_modules/@buddi/gateway/dist/web/http.js.map +1 -1
  330. package/node_modules/@buddi/gateway/dist/web/index.d.ts +1 -1
  331. package/node_modules/@buddi/gateway/dist/web/index.d.ts.map +1 -1
  332. package/node_modules/@buddi/gateway/dist/web/index.js +1 -1
  333. package/node_modules/@buddi/gateway/dist/web/index.js.map +1 -1
  334. package/node_modules/@buddi/gateway/dist/web/market.d.ts +20 -1
  335. package/node_modules/@buddi/gateway/dist/web/market.d.ts.map +1 -1
  336. package/node_modules/@buddi/gateway/dist/web/market.js +41 -1
  337. package/node_modules/@buddi/gateway/dist/web/market.js.map +1 -1
  338. package/node_modules/@buddi/gateway/dist/web/onboarding.js +1 -1
  339. package/node_modules/@buddi/gateway/dist/web/onboarding.js.map +1 -1
  340. package/node_modules/@buddi/gateway/dist/web/people.d.ts +44 -0
  341. package/node_modules/@buddi/gateway/dist/web/people.d.ts.map +1 -0
  342. package/node_modules/@buddi/gateway/dist/web/people.js +114 -0
  343. package/node_modules/@buddi/gateway/dist/web/people.js.map +1 -0
  344. package/node_modules/@buddi/gateway/dist/web/server.d.ts +42 -2
  345. package/node_modules/@buddi/gateway/dist/web/server.d.ts.map +1 -1
  346. package/node_modules/@buddi/gateway/dist/web/server.js +713 -156
  347. package/node_modules/@buddi/gateway/dist/web/server.js.map +1 -1
  348. package/node_modules/@buddi/gateway/dist/web/sessions.d.ts +24 -14
  349. package/node_modules/@buddi/gateway/dist/web/sessions.d.ts.map +1 -1
  350. package/node_modules/@buddi/gateway/dist/web/sessions.js +42 -14
  351. package/node_modules/@buddi/gateway/dist/web/sessions.js.map +1 -1
  352. package/node_modules/@buddi/gateway/dist/web/signed-out.d.ts +10 -0
  353. package/node_modules/@buddi/gateway/dist/web/signed-out.d.ts.map +1 -1
  354. package/node_modules/@buddi/gateway/dist/web/signed-out.js +47 -0
  355. package/node_modules/@buddi/gateway/dist/web/signed-out.js.map +1 -1
  356. package/node_modules/@buddi/gateway/dist/web/skill-bundles.d.ts +101 -0
  357. package/node_modules/@buddi/gateway/dist/web/skill-bundles.d.ts.map +1 -0
  358. package/node_modules/@buddi/gateway/dist/web/skill-bundles.js +439 -0
  359. package/node_modules/@buddi/gateway/dist/web/skill-bundles.js.map +1 -0
  360. package/node_modules/@buddi/gateway/dist/web/skills.d.ts +57 -1
  361. package/node_modules/@buddi/gateway/dist/web/skills.d.ts.map +1 -1
  362. package/node_modules/@buddi/gateway/dist/web/skills.js +171 -9
  363. package/node_modules/@buddi/gateway/dist/web/skills.js.map +1 -1
  364. package/node_modules/@buddi/gateway/dist/web/zip-read.d.ts +31 -0
  365. package/node_modules/@buddi/gateway/dist/web/zip-read.d.ts.map +1 -0
  366. package/node_modules/@buddi/gateway/dist/web/zip-read.js +125 -0
  367. package/node_modules/@buddi/gateway/dist/web/zip-read.js.map +1 -0
  368. package/node_modules/@buddi/install/dist/.tsbuildinfo +1 -1
  369. package/node_modules/@buddi/runtime/dist/.tsbuildinfo +1 -1
  370. package/node_modules/@buddi/runtime/dist/delegate.d.ts +8 -0
  371. package/node_modules/@buddi/runtime/dist/delegate.d.ts.map +1 -1
  372. package/node_modules/@buddi/runtime/dist/delegate.js +19 -1
  373. package/node_modules/@buddi/runtime/dist/delegate.js.map +1 -1
  374. package/node_modules/@buddi/runtime/dist/index.d.ts +1 -0
  375. package/node_modules/@buddi/runtime/dist/index.d.ts.map +1 -1
  376. package/node_modules/@buddi/runtime/dist/index.js +1 -0
  377. package/node_modules/@buddi/runtime/dist/index.js.map +1 -1
  378. package/node_modules/@buddi/runtime/dist/loop.d.ts +2 -0
  379. package/node_modules/@buddi/runtime/dist/loop.d.ts.map +1 -1
  380. package/node_modules/@buddi/runtime/dist/loop.js +29 -5
  381. package/node_modules/@buddi/runtime/dist/loop.js.map +1 -1
  382. package/node_modules/@buddi/runtime/dist/plugin-sign-in.d.ts +12 -0
  383. package/node_modules/@buddi/runtime/dist/plugin-sign-in.d.ts.map +1 -0
  384. package/node_modules/@buddi/runtime/dist/plugin-sign-in.js +397 -0
  385. package/node_modules/@buddi/runtime/dist/plugin-sign-in.js.map +1 -0
  386. package/node_modules/@buddi/tool-artifacts/dist/.tsbuildinfo +1 -1
  387. package/node_modules/@buddi/tool-artifacts/dist/tools/write.d.ts.map +1 -1
  388. package/node_modules/@buddi/tool-artifacts/dist/tools/write.js +1 -0
  389. package/node_modules/@buddi/tool-artifacts/dist/tools/write.js.map +1 -1
  390. package/node_modules/@buddi/tool-browser/dist/.tsbuildinfo +1 -1
  391. package/node_modules/@buddi/tool-browser/dist/availability.d.ts +2 -2
  392. package/node_modules/@buddi/tool-browser/dist/availability.d.ts.map +1 -1
  393. package/node_modules/@buddi/tool-browser/dist/availability.js +2 -2
  394. package/node_modules/@buddi/tool-browser/dist/availability.js.map +1 -1
  395. package/node_modules/@buddi/tool-browser/dist/controller.d.ts +76 -58
  396. package/node_modules/@buddi/tool-browser/dist/controller.d.ts.map +1 -1
  397. package/node_modules/@buddi/tool-browser/dist/controller.js +751 -219
  398. package/node_modules/@buddi/tool-browser/dist/controller.js.map +1 -1
  399. package/node_modules/@buddi/tool-browser/dist/extension.d.ts +12 -0
  400. package/node_modules/@buddi/tool-browser/dist/extension.d.ts.map +1 -1
  401. package/node_modules/@buddi/tool-browser/dist/extension.js +8 -0
  402. package/node_modules/@buddi/tool-browser/dist/extension.js.map +1 -1
  403. package/node_modules/@buddi/tool-browser/dist/index.d.ts +13 -5
  404. package/node_modules/@buddi/tool-browser/dist/index.d.ts.map +1 -1
  405. package/node_modules/@buddi/tool-browser/dist/index.js +29 -12
  406. package/node_modules/@buddi/tool-browser/dist/index.js.map +1 -1
  407. package/node_modules/@buddi/tool-browser/dist/manager.d.ts +39 -21
  408. package/node_modules/@buddi/tool-browser/dist/manager.d.ts.map +1 -1
  409. package/node_modules/@buddi/tool-browser/dist/manager.js +229 -136
  410. package/node_modules/@buddi/tool-browser/dist/manager.js.map +1 -1
  411. package/node_modules/@buddi/tool-browser/dist/routes.d.ts +130 -0
  412. package/node_modules/@buddi/tool-browser/dist/routes.d.ts.map +1 -0
  413. package/node_modules/@buddi/tool-browser/dist/routes.js +283 -0
  414. package/node_modules/@buddi/tool-browser/dist/routes.js.map +1 -0
  415. package/node_modules/@buddi/tool-browser/dist/service.d.ts +157 -114
  416. package/node_modules/@buddi/tool-browser/dist/service.d.ts.map +1 -1
  417. package/node_modules/@buddi/tool-browser/dist/service.js +379 -390
  418. package/node_modules/@buddi/tool-browser/dist/service.js.map +1 -1
  419. package/node_modules/@buddi/tool-browser/dist/settings.d.ts +88 -0
  420. package/node_modules/@buddi/tool-browser/dist/settings.d.ts.map +1 -0
  421. package/node_modules/@buddi/tool-browser/dist/settings.js +86 -0
  422. package/node_modules/@buddi/tool-browser/dist/settings.js.map +1 -0
  423. package/node_modules/@buddi/tool-browser/dist/telemetry.d.ts +203 -0
  424. package/node_modules/@buddi/tool-browser/dist/telemetry.d.ts.map +1 -0
  425. package/node_modules/@buddi/tool-browser/dist/telemetry.js +142 -0
  426. package/node_modules/@buddi/tool-browser/dist/telemetry.js.map +1 -0
  427. package/node_modules/@buddi/tool-browser/dist/types.d.ts +14 -3
  428. package/node_modules/@buddi/tool-browser/dist/types.d.ts.map +1 -1
  429. package/node_modules/@buddi/tool-browser/dist/types.js +9 -9
  430. package/node_modules/@buddi/tool-browser/dist/types.js.map +1 -1
  431. package/node_modules/@buddi/tool-email/dist/.tsbuildinfo +1 -1
  432. package/node_modules/@buddi/tool-email/dist/widgets.d.ts.map +1 -1
  433. package/node_modules/@buddi/tool-email/dist/widgets.js +2 -1
  434. package/node_modules/@buddi/tool-email/dist/widgets.js.map +1 -1
  435. package/node_modules/@buddi/tool-host/dist/.tsbuildinfo +1 -1
  436. package/node_modules/@buddi/tool-host/dist/index.d.ts +2 -1
  437. package/node_modules/@buddi/tool-host/dist/index.d.ts.map +1 -1
  438. package/node_modules/@buddi/tool-host/dist/index.js +3 -2
  439. package/node_modules/@buddi/tool-host/dist/index.js.map +1 -1
  440. package/node_modules/@buddi/tool-host/dist/process.d.ts +4 -0
  441. package/node_modules/@buddi/tool-host/dist/process.d.ts.map +1 -1
  442. package/node_modules/@buddi/tool-host/dist/process.js +4 -2
  443. package/node_modules/@buddi/tool-host/dist/process.js.map +1 -1
  444. package/node_modules/@buddi/tool-host/dist/service.d.ts +66 -17
  445. package/node_modules/@buddi/tool-host/dist/service.d.ts.map +1 -1
  446. package/node_modules/@buddi/tool-host/dist/service.js +172 -9
  447. package/node_modules/@buddi/tool-host/dist/service.js.map +1 -1
  448. package/node_modules/@buddi/tool-host/dist/skill-run.d.ts +55 -0
  449. package/node_modules/@buddi/tool-host/dist/skill-run.d.ts.map +1 -0
  450. package/node_modules/@buddi/tool-host/dist/skill-run.js +132 -0
  451. package/node_modules/@buddi/tool-host/dist/skill-run.js.map +1 -0
  452. package/node_modules/@buddi/tool-mcp/dist/.tsbuildinfo +1 -1
  453. package/node_modules/@buddi/tool-memory/dist/.tsbuildinfo +1 -1
  454. package/node_modules/@buddi/tool-memory/dist/index.d.ts +1 -0
  455. package/node_modules/@buddi/tool-memory/dist/index.d.ts.map +1 -1
  456. package/node_modules/@buddi/tool-memory/dist/index.js +7 -2
  457. package/node_modules/@buddi/tool-memory/dist/index.js.map +1 -1
  458. package/node_modules/@buddi/tool-memory/dist/people.d.ts +218 -0
  459. package/node_modules/@buddi/tool-memory/dist/people.d.ts.map +1 -0
  460. package/node_modules/@buddi/tool-memory/dist/people.js +582 -0
  461. package/node_modules/@buddi/tool-memory/dist/people.js.map +1 -0
  462. package/node_modules/@buddi/tool-memory/dist/preamble.d.ts +4 -1
  463. package/node_modules/@buddi/tool-memory/dist/preamble.d.ts.map +1 -1
  464. package/node_modules/@buddi/tool-memory/dist/preamble.js +25 -7
  465. package/node_modules/@buddi/tool-memory/dist/preamble.js.map +1 -1
  466. package/node_modules/@buddi/tool-memory/migrations/002_people.sql +40 -0
  467. package/node_modules/@buddi/tool-web/dist/.tsbuildinfo +1 -1
  468. package/node_modules/imapflow/CHANGELOG.md +23 -0
  469. package/node_modules/imapflow/dist/cjs/commands/append.js +27 -3
  470. package/node_modules/imapflow/dist/cjs/commands/store.js +31 -6
  471. package/node_modules/imapflow/dist/cjs/imap-flow.d.ts +1 -1
  472. package/node_modules/imapflow/dist/cjs/imap-flow.js +1 -1
  473. package/node_modules/imapflow/dist/cjs/package-info.d.ts +1 -1
  474. package/node_modules/imapflow/dist/cjs/package-info.js +1 -1
  475. package/node_modules/imapflow/dist/cjs/search-compiler.js +21 -31
  476. package/node_modules/imapflow/dist/esm/commands/append.js +27 -3
  477. package/node_modules/imapflow/dist/esm/commands/store.js +31 -6
  478. package/node_modules/imapflow/dist/esm/imap-flow.d.ts +1 -1
  479. package/node_modules/imapflow/dist/esm/imap-flow.js +1 -1
  480. package/node_modules/imapflow/dist/esm/package-info.d.ts +1 -1
  481. package/node_modules/imapflow/dist/esm/package-info.js +1 -1
  482. package/node_modules/imapflow/dist/esm/search-compiler.js +21 -31
  483. package/node_modules/imapflow/package.json +3 -3
  484. package/package.json +2 -2
  485. package/packages/cli/dist/.tsbuildinfo +1 -1
  486. package/packages/cli/dist/access-cmd.d.ts +10 -0
  487. package/packages/cli/dist/access-cmd.d.ts.map +1 -0
  488. package/packages/cli/dist/access-cmd.js +160 -0
  489. package/packages/cli/dist/access-cmd.js.map +1 -0
  490. package/packages/cli/dist/args.d.ts +10 -0
  491. package/packages/cli/dist/args.d.ts.map +1 -1
  492. package/packages/cli/dist/args.js +29 -1
  493. package/packages/cli/dist/args.js.map +1 -1
  494. package/packages/cli/dist/commands.d.ts.map +1 -1
  495. package/packages/cli/dist/commands.js +28 -2
  496. package/packages/cli/dist/commands.js.map +1 -1
  497. package/packages/cli/dist/doctor-probes.d.ts.map +1 -1
  498. package/packages/cli/dist/doctor-probes.js +90 -30
  499. package/packages/cli/dist/doctor-probes.js.map +1 -1
  500. package/packages/cli/dist/doctor.d.ts +61 -12
  501. package/packages/cli/dist/doctor.d.ts.map +1 -1
  502. package/packages/cli/dist/doctor.js +60 -9
  503. package/packages/cli/dist/doctor.js.map +1 -1
  504. package/packages/cli/dist/main.d.ts +2 -0
  505. package/packages/cli/dist/main.d.ts.map +1 -1
  506. package/packages/cli/dist/main.js +18 -1
  507. package/packages/cli/dist/main.js.map +1 -1
  508. package/packages/core/dist/.tsbuildinfo +1 -1
  509. package/packages/core/dist/actions/approvals.d.ts +7 -0
  510. package/packages/core/dist/actions/approvals.d.ts.map +1 -1
  511. package/packages/core/dist/actions/approvals.js +11 -1
  512. package/packages/core/dist/actions/approvals.js.map +1 -1
  513. package/packages/core/dist/agents/catalog.d.ts +2 -0
  514. package/packages/core/dist/agents/catalog.d.ts.map +1 -1
  515. package/packages/core/dist/agents/catalog.js +3 -2
  516. package/packages/core/dist/agents/catalog.js.map +1 -1
  517. package/packages/core/dist/agents/edit.d.ts +2 -0
  518. package/packages/core/dist/agents/edit.d.ts.map +1 -1
  519. package/packages/core/dist/agents/edit.js +3 -1
  520. package/packages/core/dist/agents/edit.js.map +1 -1
  521. package/packages/core/dist/agents/frontmatter.d.ts +11 -0
  522. package/packages/core/dist/agents/frontmatter.d.ts.map +1 -1
  523. package/packages/core/dist/agents/frontmatter.js +7 -0
  524. package/packages/core/dist/agents/frontmatter.js.map +1 -1
  525. package/packages/core/dist/agents/skills.d.ts +60 -1
  526. package/packages/core/dist/agents/skills.d.ts.map +1 -1
  527. package/packages/core/dist/agents/skills.js +157 -17
  528. package/packages/core/dist/agents/skills.js.map +1 -1
  529. package/packages/core/dist/day-month.d.ts +52 -0
  530. package/packages/core/dist/day-month.d.ts.map +1 -0
  531. package/packages/core/dist/day-month.js +101 -0
  532. package/packages/core/dist/day-month.js.map +1 -0
  533. package/packages/core/dist/host/build.d.ts +11 -2
  534. package/packages/core/dist/host/build.d.ts.map +1 -1
  535. package/packages/core/dist/host/build.js +115 -4
  536. package/packages/core/dist/host/build.js.map +1 -1
  537. package/packages/core/dist/host/http.d.ts +35 -0
  538. package/packages/core/dist/host/http.d.ts.map +1 -1
  539. package/packages/core/dist/host/http.js +133 -29
  540. package/packages/core/dist/host/http.js.map +1 -1
  541. package/packages/core/dist/host/types.d.ts +72 -1
  542. package/packages/core/dist/host/types.d.ts.map +1 -1
  543. package/packages/core/dist/index.d.ts +3 -0
  544. package/packages/core/dist/index.d.ts.map +1 -1
  545. package/packages/core/dist/index.js +3 -0
  546. package/packages/core/dist/index.js.map +1 -1
  547. package/packages/core/dist/onboarding/store.d.ts.map +1 -1
  548. package/packages/core/dist/onboarding/store.js +28 -5
  549. package/packages/core/dist/onboarding/store.js.map +1 -1
  550. package/packages/core/dist/onboarding/types.d.ts +10 -0
  551. package/packages/core/dist/onboarding/types.d.ts.map +1 -1
  552. package/packages/core/dist/onboarding/types.js.map +1 -1
  553. package/packages/core/dist/pages.d.ts +145 -11
  554. package/packages/core/dist/pages.d.ts.map +1 -1
  555. package/packages/core/dist/pages.js +113 -5
  556. package/packages/core/dist/pages.js.map +1 -1
  557. package/packages/core/dist/plugin/index.d.ts +7 -0
  558. package/packages/core/dist/plugin/index.d.ts.map +1 -1
  559. package/packages/core/dist/plugin/index.js +5 -0
  560. package/packages/core/dist/plugin/index.js.map +1 -1
  561. package/packages/core/dist/plugin/sign-in.d.ts +78 -0
  562. package/packages/core/dist/plugin/sign-in.d.ts.map +1 -0
  563. package/packages/core/dist/plugin/sign-in.js +54 -0
  564. package/packages/core/dist/plugin/sign-in.js.map +1 -0
  565. package/packages/core/dist/plugin/version.d.ts +1 -1
  566. package/packages/core/dist/plugin/version.js +1 -1
  567. package/packages/core/dist/provider-accounts.d.ts +7 -0
  568. package/packages/core/dist/provider-accounts.d.ts.map +1 -1
  569. package/packages/core/dist/provider-accounts.js +15 -0
  570. package/packages/core/dist/provider-accounts.js.map +1 -1
  571. package/packages/core/dist/questions/store.d.ts +12 -0
  572. package/packages/core/dist/questions/store.d.ts.map +1 -1
  573. package/packages/core/dist/questions/store.js +11 -1
  574. package/packages/core/dist/questions/store.js.map +1 -1
  575. package/packages/core/dist/questions/types.d.ts +9 -1
  576. package/packages/core/dist/questions/types.d.ts.map +1 -1
  577. package/packages/core/dist/questions/types.js +16 -1
  578. package/packages/core/dist/questions/types.js.map +1 -1
  579. package/packages/core/dist/queue/jobs.d.ts +65 -2
  580. package/packages/core/dist/queue/jobs.d.ts.map +1 -1
  581. package/packages/core/dist/queue/jobs.js +123 -3
  582. package/packages/core/dist/queue/jobs.js.map +1 -1
  583. package/packages/core/dist/queue/worker.d.ts +26 -2
  584. package/packages/core/dist/queue/worker.d.ts.map +1 -1
  585. package/packages/core/dist/queue/worker.js +68 -12
  586. package/packages/core/dist/queue/worker.js.map +1 -1
  587. package/packages/core/dist/registry.d.ts +15 -1
  588. package/packages/core/dist/registry.d.ts.map +1 -1
  589. package/packages/core/dist/registry.js +88 -13
  590. package/packages/core/dist/registry.js.map +1 -1
  591. package/packages/core/dist/routes.d.ts +153 -0
  592. package/packages/core/dist/routes.d.ts.map +1 -0
  593. package/packages/core/dist/routes.js +47 -0
  594. package/packages/core/dist/routes.js.map +1 -0
  595. package/packages/core/dist/scheduler/missions.d.ts +5 -3
  596. package/packages/core/dist/scheduler/missions.d.ts.map +1 -1
  597. package/packages/core/dist/scheduler/missions.js +9 -7
  598. package/packages/core/dist/scheduler/missions.js.map +1 -1
  599. package/packages/core/dist/scheduler/types.d.ts +7 -0
  600. package/packages/core/dist/scheduler/types.d.ts.map +1 -1
  601. package/packages/core/dist/scheduler/types.js +1 -0
  602. package/packages/core/dist/scheduler/types.js.map +1 -1
  603. package/packages/core/dist/secrets/scrub.d.ts.map +1 -1
  604. package/packages/core/dist/secrets/scrub.js +15 -0
  605. package/packages/core/dist/secrets/scrub.js.map +1 -1
  606. package/packages/core/dist/tools.d.ts +88 -2
  607. package/packages/core/dist/tools.d.ts.map +1 -1
  608. package/packages/core/dist/tools.js.map +1 -1
  609. package/packages/core/dist/views.d.ts +12 -12
  610. package/packages/core/dist/widgets.d.ts +14 -1
  611. package/packages/core/dist/widgets.d.ts.map +1 -1
  612. package/packages/core/dist/widgets.js +30 -0
  613. package/packages/core/dist/widgets.js.map +1 -1
  614. package/packages/core/migrations/074_owner_identity.sql +14 -0
  615. package/packages/core/migrations/075_access_providers.sql +43 -0
  616. package/packages/core/migrations/076_mission_browser.sql +7 -0
  617. package/packages/gateway/dist/.tsbuildinfo +1 -1
  618. package/packages/gateway/dist/agents/canvas.d.ts +12 -12
  619. package/packages/gateway/dist/agents/catalog.d.ts.map +1 -1
  620. package/packages/gateway/dist/agents/catalog.js +3 -3
  621. package/packages/gateway/dist/agents/catalog.js.map +1 -1
  622. package/packages/gateway/dist/agents/catalogue-package.d.ts +46 -20
  623. package/packages/gateway/dist/agents/catalogue-package.d.ts.map +1 -1
  624. package/packages/gateway/dist/agents/catalogue-package.js +17 -4
  625. package/packages/gateway/dist/agents/catalogue-package.js.map +1 -1
  626. package/packages/gateway/dist/agents/delegation.d.ts +10 -0
  627. package/packages/gateway/dist/agents/delegation.d.ts.map +1 -1
  628. package/packages/gateway/dist/agents/delegation.js +1 -0
  629. package/packages/gateway/dist/agents/delegation.js.map +1 -1
  630. package/packages/gateway/dist/agents/owner-tools.d.ts.map +1 -1
  631. package/packages/gateway/dist/agents/owner-tools.js +36 -2
  632. package/packages/gateway/dist/agents/owner-tools.js.map +1 -1
  633. package/packages/gateway/dist/agents/platform-catalogue.d.ts +26 -1
  634. package/packages/gateway/dist/agents/platform-catalogue.d.ts.map +1 -1
  635. package/packages/gateway/dist/agents/platform-catalogue.js +123 -6
  636. package/packages/gateway/dist/agents/platform-catalogue.js.map +1 -1
  637. package/packages/gateway/dist/agents/platform.d.ts +4 -4
  638. package/packages/gateway/dist/agents/platform.d.ts.map +1 -1
  639. package/packages/gateway/dist/agents/platform.js +11 -0
  640. package/packages/gateway/dist/agents/platform.js.map +1 -1
  641. package/packages/gateway/dist/bootstrap.d.ts.map +1 -1
  642. package/packages/gateway/dist/bootstrap.js +12 -1
  643. package/packages/gateway/dist/bootstrap.js.map +1 -1
  644. package/packages/gateway/dist/browser-host.d.ts +30 -1
  645. package/packages/gateway/dist/browser-host.d.ts.map +1 -1
  646. package/packages/gateway/dist/browser-host.js +56 -1
  647. package/packages/gateway/dist/browser-host.js.map +1 -1
  648. package/packages/gateway/dist/chat/session.d.ts.map +1 -1
  649. package/packages/gateway/dist/chat/session.js +9 -5
  650. package/packages/gateway/dist/chat/session.js.map +1 -1
  651. package/packages/gateway/dist/evals/golden.d.ts +33 -1
  652. package/packages/gateway/dist/evals/golden.d.ts.map +1 -1
  653. package/packages/gateway/dist/evals/golden.js +168 -3
  654. package/packages/gateway/dist/evals/golden.js.map +1 -1
  655. package/packages/gateway/dist/index.d.ts +3 -1
  656. package/packages/gateway/dist/index.d.ts.map +1 -1
  657. package/packages/gateway/dist/index.js +3 -1
  658. package/packages/gateway/dist/index.js.map +1 -1
  659. package/packages/gateway/dist/missions/dates.d.ts +98 -0
  660. package/packages/gateway/dist/missions/dates.d.ts.map +1 -0
  661. package/packages/gateway/dist/missions/dates.js +252 -0
  662. package/packages/gateway/dist/missions/dates.js.map +1 -0
  663. package/packages/gateway/dist/missions/execute.d.ts +42 -1
  664. package/packages/gateway/dist/missions/execute.d.ts.map +1 -1
  665. package/packages/gateway/dist/missions/execute.js +107 -8
  666. package/packages/gateway/dist/missions/execute.js.map +1 -1
  667. package/packages/gateway/dist/missions/parked.d.ts +133 -0
  668. package/packages/gateway/dist/missions/parked.d.ts.map +1 -0
  669. package/packages/gateway/dist/missions/parked.js +249 -0
  670. package/packages/gateway/dist/missions/parked.js.map +1 -0
  671. package/packages/gateway/dist/missions/report.d.ts.map +1 -1
  672. package/packages/gateway/dist/missions/report.js +1 -0
  673. package/packages/gateway/dist/missions/report.js.map +1 -1
  674. package/packages/gateway/dist/ollama-local.d.ts +90 -0
  675. package/packages/gateway/dist/ollama-local.d.ts.map +1 -0
  676. package/packages/gateway/dist/ollama-local.js +201 -0
  677. package/packages/gateway/dist/ollama-local.js.map +1 -0
  678. package/packages/gateway/dist/plugins-cli.d.ts +8 -0
  679. package/packages/gateway/dist/plugins-cli.d.ts.map +1 -1
  680. package/packages/gateway/dist/plugins-cli.js +22 -1
  681. package/packages/gateway/dist/plugins-cli.js.map +1 -1
  682. package/packages/gateway/dist/serve.d.ts +7 -0
  683. package/packages/gateway/dist/serve.d.ts.map +1 -1
  684. package/packages/gateway/dist/serve.js +99 -14
  685. package/packages/gateway/dist/serve.js.map +1 -1
  686. package/packages/gateway/dist/surfaces/browser-cards.d.ts +18 -0
  687. package/packages/gateway/dist/surfaces/browser-cards.d.ts.map +1 -0
  688. package/packages/gateway/dist/surfaces/browser-cards.js +42 -0
  689. package/packages/gateway/dist/surfaces/browser-cards.js.map +1 -0
  690. package/packages/gateway/dist/surfaces/browser-continuation.js +1 -1
  691. package/packages/gateway/dist/surfaces/browser-continuation.js.map +1 -1
  692. package/packages/gateway/dist/surfaces/owner-request.d.ts +6 -0
  693. package/packages/gateway/dist/surfaces/owner-request.d.ts.map +1 -1
  694. package/packages/gateway/dist/surfaces/owner-request.js +7 -1
  695. package/packages/gateway/dist/surfaces/owner-request.js.map +1 -1
  696. package/packages/gateway/dist/surfaces/pending-question.js +2 -2
  697. package/packages/gateway/dist/system-context.d.ts +21 -1
  698. package/packages/gateway/dist/system-context.d.ts.map +1 -1
  699. package/packages/gateway/dist/system-context.js +75 -2
  700. package/packages/gateway/dist/system-context.js.map +1 -1
  701. package/packages/gateway/dist/telegram/approvals.d.ts.map +1 -1
  702. package/packages/gateway/dist/telegram/approvals.js +4 -2
  703. package/packages/gateway/dist/telegram/approvals.js.map +1 -1
  704. package/packages/gateway/dist/telegram/browser-view.d.ts +17 -17
  705. package/packages/gateway/dist/telegram/browser-view.d.ts.map +1 -1
  706. package/packages/gateway/dist/telegram/browser-view.js +33 -37
  707. package/packages/gateway/dist/telegram/browser-view.js.map +1 -1
  708. package/packages/gateway/dist/telegram/channel.d.ts.map +1 -1
  709. package/packages/gateway/dist/telegram/channel.js +23 -2
  710. package/packages/gateway/dist/telegram/channel.js.map +1 -1
  711. package/packages/gateway/dist/telegram/html.d.ts +34 -0
  712. package/packages/gateway/dist/telegram/html.d.ts.map +1 -0
  713. package/packages/gateway/dist/telegram/html.js +263 -0
  714. package/packages/gateway/dist/telegram/html.js.map +1 -0
  715. package/packages/gateway/dist/telegram/main.d.ts.map +1 -1
  716. package/packages/gateway/dist/telegram/main.js +11 -3
  717. package/packages/gateway/dist/telegram/main.js.map +1 -1
  718. package/packages/gateway/dist/telegram/notify.d.ts +6 -0
  719. package/packages/gateway/dist/telegram/notify.d.ts.map +1 -1
  720. package/packages/gateway/dist/telegram/notify.js +25 -3
  721. package/packages/gateway/dist/telegram/notify.js.map +1 -1
  722. package/packages/gateway/dist/telegram/surface.d.ts.map +1 -1
  723. package/packages/gateway/dist/telegram/surface.js +10 -0
  724. package/packages/gateway/dist/telegram/surface.js.map +1 -1
  725. package/packages/gateway/dist/tips/facts.d.ts +10 -0
  726. package/packages/gateway/dist/tips/facts.d.ts.map +1 -1
  727. package/packages/gateway/dist/tips/facts.js +12 -3
  728. package/packages/gateway/dist/tips/facts.js.map +1 -1
  729. package/packages/gateway/dist/tips/rules.d.ts +2 -0
  730. package/packages/gateway/dist/tips/rules.d.ts.map +1 -1
  731. package/packages/gateway/dist/tips/rules.js +25 -0
  732. package/packages/gateway/dist/tips/rules.js.map +1 -1
  733. package/packages/gateway/dist/web/access/arrival.d.ts +24 -0
  734. package/packages/gateway/dist/web/access/arrival.d.ts.map +1 -0
  735. package/packages/gateway/dist/web/access/arrival.js +18 -0
  736. package/packages/gateway/dist/web/access/arrival.js.map +1 -0
  737. package/packages/gateway/dist/web/access/cloudflare-api.d.ts +124 -0
  738. package/packages/gateway/dist/web/access/cloudflare-api.d.ts.map +1 -0
  739. package/packages/gateway/dist/web/access/cloudflare-api.js +206 -0
  740. package/packages/gateway/dist/web/access/cloudflare-api.js.map +1 -0
  741. package/packages/gateway/dist/web/access/cloudflare-setup.d.ts +163 -0
  742. package/packages/gateway/dist/web/access/cloudflare-setup.d.ts.map +1 -0
  743. package/packages/gateway/dist/web/access/cloudflare-setup.js +438 -0
  744. package/packages/gateway/dist/web/access/cloudflare-setup.js.map +1 -0
  745. package/packages/gateway/dist/web/access/cloudflare-token.d.ts +31 -0
  746. package/packages/gateway/dist/web/access/cloudflare-token.d.ts.map +1 -0
  747. package/packages/gateway/dist/web/access/cloudflare-token.js +74 -0
  748. package/packages/gateway/dist/web/access/cloudflare-token.js.map +1 -0
  749. package/packages/gateway/dist/web/access/cloudflare.d.ts +145 -0
  750. package/packages/gateway/dist/web/access/cloudflare.d.ts.map +1 -0
  751. package/packages/gateway/dist/web/access/cloudflare.js +474 -0
  752. package/packages/gateway/dist/web/access/cloudflare.js.map +1 -0
  753. package/packages/gateway/dist/web/access/index.d.ts +9 -0
  754. package/packages/gateway/dist/web/access/index.d.ts.map +1 -0
  755. package/packages/gateway/dist/web/access/index.js +9 -0
  756. package/packages/gateway/dist/web/access/index.js.map +1 -0
  757. package/packages/gateway/dist/web/access/ingress.d.ts +30 -0
  758. package/packages/gateway/dist/web/access/ingress.d.ts.map +1 -0
  759. package/packages/gateway/dist/web/access/ingress.js +76 -0
  760. package/packages/gateway/dist/web/access/ingress.js.map +1 -0
  761. package/packages/gateway/dist/web/access/provider.d.ts +173 -0
  762. package/packages/gateway/dist/web/access/provider.d.ts.map +1 -0
  763. package/packages/gateway/dist/web/access/provider.js +11 -0
  764. package/packages/gateway/dist/web/access/provider.js.map +1 -0
  765. package/packages/gateway/dist/web/access/registry.d.ts +33 -0
  766. package/packages/gateway/dist/web/access/registry.d.ts.map +1 -0
  767. package/packages/gateway/dist/web/access/registry.js +32 -0
  768. package/packages/gateway/dist/web/access/registry.js.map +1 -0
  769. package/packages/gateway/dist/web/{tailscale.d.ts → access/tailscale.d.ts} +27 -0
  770. package/packages/gateway/dist/web/access/tailscale.d.ts.map +1 -0
  771. package/packages/gateway/dist/web/{tailscale.js → access/tailscale.js} +118 -3
  772. package/packages/gateway/dist/web/access/tailscale.js.map +1 -0
  773. package/packages/gateway/dist/web/agents.d.ts +2 -0
  774. package/packages/gateway/dist/web/agents.d.ts.map +1 -1
  775. package/packages/gateway/dist/web/agents.js +19 -1
  776. package/packages/gateway/dist/web/agents.js.map +1 -1
  777. package/packages/gateway/dist/web/api-routes.d.ts +4 -3
  778. package/packages/gateway/dist/web/api-routes.d.ts.map +1 -1
  779. package/packages/gateway/dist/web/api-routes.js +168 -26
  780. package/packages/gateway/dist/web/api-routes.js.map +1 -1
  781. package/packages/gateway/dist/web/api-scan.d.ts +1 -1
  782. package/packages/gateway/dist/web/api-scan.d.ts.map +1 -1
  783. package/packages/gateway/dist/web/api-scan.js +1 -0
  784. package/packages/gateway/dist/web/api-scan.js.map +1 -1
  785. package/packages/gateway/dist/web/attention.d.ts.map +1 -1
  786. package/packages/gateway/dist/web/attention.js +11 -3
  787. package/packages/gateway/dist/web/attention.js.map +1 -1
  788. package/packages/gateway/dist/web/chat.d.ts +23 -0
  789. package/packages/gateway/dist/web/chat.d.ts.map +1 -1
  790. package/packages/gateway/dist/web/chat.js +71 -2
  791. package/packages/gateway/dist/web/chat.js.map +1 -1
  792. package/packages/gateway/dist/web/client-key.d.ts +3 -0
  793. package/packages/gateway/dist/web/client-key.d.ts.map +1 -1
  794. package/packages/gateway/dist/web/client-key.js +8 -1
  795. package/packages/gateway/dist/web/client-key.js.map +1 -1
  796. package/packages/gateway/dist/web/composer.d.ts +45 -0
  797. package/packages/gateway/dist/web/composer.d.ts.map +1 -0
  798. package/packages/gateway/dist/web/composer.js +85 -0
  799. package/packages/gateway/dist/web/composer.js.map +1 -0
  800. package/packages/gateway/dist/web/extension.d.ts +9 -1
  801. package/packages/gateway/dist/web/extension.d.ts.map +1 -1
  802. package/packages/gateway/dist/web/extension.js +61 -1
  803. package/packages/gateway/dist/web/extension.js.map +1 -1
  804. package/packages/gateway/dist/web/http.d.ts +7 -0
  805. package/packages/gateway/dist/web/http.d.ts.map +1 -1
  806. package/packages/gateway/dist/web/http.js +15 -1
  807. package/packages/gateway/dist/web/http.js.map +1 -1
  808. package/packages/gateway/dist/web/index.d.ts +1 -1
  809. package/packages/gateway/dist/web/index.d.ts.map +1 -1
  810. package/packages/gateway/dist/web/index.js +1 -1
  811. package/packages/gateway/dist/web/index.js.map +1 -1
  812. package/packages/gateway/dist/web/market.d.ts +20 -1
  813. package/packages/gateway/dist/web/market.d.ts.map +1 -1
  814. package/packages/gateway/dist/web/market.js +41 -1
  815. package/packages/gateway/dist/web/market.js.map +1 -1
  816. package/packages/gateway/dist/web/onboarding.js +1 -1
  817. package/packages/gateway/dist/web/onboarding.js.map +1 -1
  818. package/packages/gateway/dist/web/people.d.ts +44 -0
  819. package/packages/gateway/dist/web/people.d.ts.map +1 -0
  820. package/packages/gateway/dist/web/people.js +114 -0
  821. package/packages/gateway/dist/web/people.js.map +1 -0
  822. package/packages/gateway/dist/web/server.d.ts +42 -2
  823. package/packages/gateway/dist/web/server.d.ts.map +1 -1
  824. package/packages/gateway/dist/web/server.js +713 -156
  825. package/packages/gateway/dist/web/server.js.map +1 -1
  826. package/packages/gateway/dist/web/sessions.d.ts +24 -14
  827. package/packages/gateway/dist/web/sessions.d.ts.map +1 -1
  828. package/packages/gateway/dist/web/sessions.js +42 -14
  829. package/packages/gateway/dist/web/sessions.js.map +1 -1
  830. package/packages/gateway/dist/web/signed-out.d.ts +10 -0
  831. package/packages/gateway/dist/web/signed-out.d.ts.map +1 -1
  832. package/packages/gateway/dist/web/signed-out.js +47 -0
  833. package/packages/gateway/dist/web/signed-out.js.map +1 -1
  834. package/packages/gateway/dist/web/skill-bundles.d.ts +101 -0
  835. package/packages/gateway/dist/web/skill-bundles.d.ts.map +1 -0
  836. package/packages/gateway/dist/web/skill-bundles.js +439 -0
  837. package/packages/gateway/dist/web/skill-bundles.js.map +1 -0
  838. package/packages/gateway/dist/web/skills.d.ts +57 -1
  839. package/packages/gateway/dist/web/skills.d.ts.map +1 -1
  840. package/packages/gateway/dist/web/skills.js +171 -9
  841. package/packages/gateway/dist/web/skills.js.map +1 -1
  842. package/packages/gateway/dist/web/zip-read.d.ts +31 -0
  843. package/packages/gateway/dist/web/zip-read.d.ts.map +1 -0
  844. package/packages/gateway/dist/web/zip-read.js +125 -0
  845. package/packages/gateway/dist/web/zip-read.js.map +1 -0
  846. package/packages/install/dist/.tsbuildinfo +1 -1
  847. package/packages/runtime/dist/.tsbuildinfo +1 -1
  848. package/packages/runtime/dist/delegate.d.ts +8 -0
  849. package/packages/runtime/dist/delegate.d.ts.map +1 -1
  850. package/packages/runtime/dist/delegate.js +19 -1
  851. package/packages/runtime/dist/delegate.js.map +1 -1
  852. package/packages/runtime/dist/index.d.ts +1 -0
  853. package/packages/runtime/dist/index.d.ts.map +1 -1
  854. package/packages/runtime/dist/index.js +1 -0
  855. package/packages/runtime/dist/index.js.map +1 -1
  856. package/packages/runtime/dist/loop.d.ts +2 -0
  857. package/packages/runtime/dist/loop.d.ts.map +1 -1
  858. package/packages/runtime/dist/loop.js +29 -5
  859. package/packages/runtime/dist/loop.js.map +1 -1
  860. package/packages/runtime/dist/plugin-sign-in.d.ts +12 -0
  861. package/packages/runtime/dist/plugin-sign-in.d.ts.map +1 -0
  862. package/packages/runtime/dist/plugin-sign-in.js +397 -0
  863. package/packages/runtime/dist/plugin-sign-in.js.map +1 -0
  864. package/packages/tools/artifacts/dist/.tsbuildinfo +1 -1
  865. package/packages/tools/artifacts/dist/tools/write.d.ts.map +1 -1
  866. package/packages/tools/artifacts/dist/tools/write.js +1 -0
  867. package/packages/tools/artifacts/dist/tools/write.js.map +1 -1
  868. package/packages/tools/browser/dist/.tsbuildinfo +1 -1
  869. package/packages/tools/browser/dist/availability.d.ts +2 -2
  870. package/packages/tools/browser/dist/availability.d.ts.map +1 -1
  871. package/packages/tools/browser/dist/availability.js +2 -2
  872. package/packages/tools/browser/dist/availability.js.map +1 -1
  873. package/packages/tools/browser/dist/controller.d.ts +76 -58
  874. package/packages/tools/browser/dist/controller.d.ts.map +1 -1
  875. package/packages/tools/browser/dist/controller.js +751 -219
  876. package/packages/tools/browser/dist/controller.js.map +1 -1
  877. package/packages/tools/browser/dist/extension.d.ts +12 -0
  878. package/packages/tools/browser/dist/extension.d.ts.map +1 -1
  879. package/packages/tools/browser/dist/extension.js +8 -0
  880. package/packages/tools/browser/dist/extension.js.map +1 -1
  881. package/packages/tools/browser/dist/index.d.ts +13 -5
  882. package/packages/tools/browser/dist/index.d.ts.map +1 -1
  883. package/packages/tools/browser/dist/index.js +29 -12
  884. package/packages/tools/browser/dist/index.js.map +1 -1
  885. package/packages/tools/browser/dist/manager.d.ts +39 -21
  886. package/packages/tools/browser/dist/manager.d.ts.map +1 -1
  887. package/packages/tools/browser/dist/manager.js +229 -136
  888. package/packages/tools/browser/dist/manager.js.map +1 -1
  889. package/packages/tools/browser/dist/routes.d.ts +130 -0
  890. package/packages/tools/browser/dist/routes.d.ts.map +1 -0
  891. package/packages/tools/browser/dist/routes.js +283 -0
  892. package/packages/tools/browser/dist/routes.js.map +1 -0
  893. package/packages/tools/browser/dist/service.d.ts +157 -114
  894. package/packages/tools/browser/dist/service.d.ts.map +1 -1
  895. package/packages/tools/browser/dist/service.js +379 -390
  896. package/packages/tools/browser/dist/service.js.map +1 -1
  897. package/packages/tools/browser/dist/settings.d.ts +88 -0
  898. package/packages/tools/browser/dist/settings.d.ts.map +1 -0
  899. package/packages/tools/browser/dist/settings.js +86 -0
  900. package/packages/tools/browser/dist/settings.js.map +1 -0
  901. package/packages/tools/browser/dist/telemetry.d.ts +203 -0
  902. package/packages/tools/browser/dist/telemetry.d.ts.map +1 -0
  903. package/packages/tools/browser/dist/telemetry.js +142 -0
  904. package/packages/tools/browser/dist/telemetry.js.map +1 -0
  905. package/packages/tools/browser/dist/types.d.ts +14 -3
  906. package/packages/tools/browser/dist/types.d.ts.map +1 -1
  907. package/packages/tools/browser/dist/types.js +9 -9
  908. package/packages/tools/browser/dist/types.js.map +1 -1
  909. package/packages/tools/email/dist/.tsbuildinfo +1 -1
  910. package/packages/tools/email/dist/widgets.d.ts.map +1 -1
  911. package/packages/tools/email/dist/widgets.js +2 -1
  912. package/packages/tools/email/dist/widgets.js.map +1 -1
  913. package/packages/tools/host/dist/.tsbuildinfo +1 -1
  914. package/packages/tools/host/dist/index.d.ts +2 -1
  915. package/packages/tools/host/dist/index.d.ts.map +1 -1
  916. package/packages/tools/host/dist/index.js +3 -2
  917. package/packages/tools/host/dist/index.js.map +1 -1
  918. package/packages/tools/host/dist/process.d.ts +4 -0
  919. package/packages/tools/host/dist/process.d.ts.map +1 -1
  920. package/packages/tools/host/dist/process.js +4 -2
  921. package/packages/tools/host/dist/process.js.map +1 -1
  922. package/packages/tools/host/dist/service.d.ts +66 -17
  923. package/packages/tools/host/dist/service.d.ts.map +1 -1
  924. package/packages/tools/host/dist/service.js +172 -9
  925. package/packages/tools/host/dist/service.js.map +1 -1
  926. package/packages/tools/host/dist/skill-run.d.ts +55 -0
  927. package/packages/tools/host/dist/skill-run.d.ts.map +1 -0
  928. package/packages/tools/host/dist/skill-run.js +132 -0
  929. package/packages/tools/host/dist/skill-run.js.map +1 -0
  930. package/packages/tools/mcp/dist/.tsbuildinfo +1 -1
  931. package/packages/tools/memory/dist/.tsbuildinfo +1 -1
  932. package/packages/tools/memory/dist/index.d.ts +1 -0
  933. package/packages/tools/memory/dist/index.d.ts.map +1 -1
  934. package/packages/tools/memory/dist/index.js +7 -2
  935. package/packages/tools/memory/dist/index.js.map +1 -1
  936. package/packages/tools/memory/dist/people.d.ts +218 -0
  937. package/packages/tools/memory/dist/people.d.ts.map +1 -0
  938. package/packages/tools/memory/dist/people.js +582 -0
  939. package/packages/tools/memory/dist/people.js.map +1 -0
  940. package/packages/tools/memory/dist/preamble.d.ts +4 -1
  941. package/packages/tools/memory/dist/preamble.d.ts.map +1 -1
  942. package/packages/tools/memory/dist/preamble.js +25 -7
  943. package/packages/tools/memory/dist/preamble.js.map +1 -1
  944. package/packages/tools/memory/migrations/002_people.sql +40 -0
  945. package/packages/tools/web/dist/.tsbuildinfo +1 -1
  946. package/packages/web/dist/assets/index-Bac-CdD-.css +1 -0
  947. package/packages/web/dist/assets/index-DI8bp_D7.js +146 -0
  948. package/packages/web/dist/assets/{lottie_light-Dj5SSwxs.js → lottie_light-CuiSMqnl.js} +1 -1
  949. package/packages/web/dist/build.json +1 -1
  950. package/packages/web/dist/index.html +2 -2
  951. package/packages/web/dist/precache.json +4 -4
  952. package/node_modules/@buddi/gateway/dist/web/apps.d.ts +0 -18
  953. package/node_modules/@buddi/gateway/dist/web/apps.d.ts.map +0 -1
  954. package/node_modules/@buddi/gateway/dist/web/apps.js +0 -84
  955. package/node_modules/@buddi/gateway/dist/web/apps.js.map +0 -1
  956. package/node_modules/@buddi/gateway/dist/web/tailscale.d.ts.map +0 -1
  957. package/node_modules/@buddi/gateway/dist/web/tailscale.js.map +0 -1
  958. package/node_modules/@buddi/tool-browser/dist/computer.d.ts +0 -153
  959. package/node_modules/@buddi/tool-browser/dist/computer.d.ts.map +0 -1
  960. package/node_modules/@buddi/tool-browser/dist/computer.js +0 -392
  961. package/node_modules/@buddi/tool-browser/dist/computer.js.map +0 -1
  962. package/node_modules/@buddi/tool-browser/dist/native/buddi-computer +0 -0
  963. package/packages/gateway/dist/web/apps.d.ts +0 -18
  964. package/packages/gateway/dist/web/apps.d.ts.map +0 -1
  965. package/packages/gateway/dist/web/apps.js +0 -84
  966. package/packages/gateway/dist/web/apps.js.map +0 -1
  967. package/packages/gateway/dist/web/tailscale.d.ts.map +0 -1
  968. package/packages/gateway/dist/web/tailscale.js.map +0 -1
  969. package/packages/tools/browser/dist/computer.d.ts +0 -153
  970. package/packages/tools/browser/dist/computer.d.ts.map +0 -1
  971. package/packages/tools/browser/dist/computer.js +0 -392
  972. package/packages/tools/browser/dist/computer.js.map +0 -1
  973. package/packages/tools/browser/dist/native/buddi-computer +0 -0
  974. package/packages/web/dist/assets/index-B_P_ux2I.css +0 -1
  975. package/packages/web/dist/assets/index-CjOzIGL7.js +0 -134
@@ -46,7 +46,8 @@ import { agentMuteRoute, focusRoute, listNotificationsRoute, markSeenRoute, noti
46
46
  import { catalogSkillLookup, discardProposalFromWeb, keepAllProposalsFromWeb, keepProposalFromWeb, readProposals, registryChangeLookup } from './proposals.js';
47
47
  import { latestDigest, readDigestSchedule, setDigestSchedule } from '../agents/learning-digest.js';
48
48
  import { readAgentSkills, removeLearnedSkillFromWeb } from '../agents/learned-skills.js';
49
- import { createSkillRoute, deleteSkillRoute, editSkillRoute, grantSkillRoute, listSkillsRoute, skillDetailRoute, skillDownload, trustSkillRoute, } from './skills.js';
49
+ import { createSkillRoute, deleteSkillRoute, editSkillRoute, grantSkillRoute, listSkillsRoute, skillDetailRoute, skillDownload, skillFileRoute, skillImage, stagedFileRoute, stagedImage, acceptBundleRoute, trustSkillRoute, } from './skills.js';
50
+ import { discardStaged, incomingDirFor, receiveBundleUpload, stageBundle, uploadLabel } from './skill-bundles.js';
50
51
  import { bindMcpRequests, requestThroughMcp } from '../mcp/requests.js';
51
52
  import { randomUUID, createHmac, createHash } from 'node:crypto';
52
53
  import { StringDecoder } from 'node:string_decoder';
@@ -55,18 +56,18 @@ import { carryOverDeps } from '../surfaces/carry-over.js';
55
56
  import { deleteCarryOver } from '../surfaces/browser-handoff.js';
56
57
  import { ProviderSettingsError } from '../providers.js';
57
58
  import { ProviderAccountError } from '../provider-accounts.js';
58
- import { listBrowserProfiles, listInstalledApps } from './apps.js';
59
59
  import { agentSearchPath, EXAMPLES_AGENTS_DIR } from '../agents/catalog.js';
60
60
  import { setDelegatesFromWeb } from './write.js';
61
61
  import { mlxhBaseUrl, probeMlxh } from '../mlxh.js';
62
- import { OnboardingRefusal, WEB_ONBOARDING_STEPS, WEB_ONBOARDING_SURFACE, claimOpeningTurn, createFirstAgent, probeOllama, readOnboarding, rebindBrain, updateFirstAgent, withFirstRunFacts, readFirstAgentPersona } from './onboarding.js';
62
+ import { PullRefusal, createOllamaPulls, ollamaMachine } from '../ollama-local.js';
63
+ import { OnboardingRefusal, WEB_ONBOARDING_STEPS, WEB_ONBOARDING_SURFACE, claimOpeningTurn, createFirstAgent, OLLAMA_BASE_URL, probeOllama, readOnboarding, rebindBrain, updateFirstAgent, withFirstRunFacts, readFirstAgentPersona } from './onboarding.js';
63
64
  import { TakeOnRefusal, readTakeOn, startTakeOn } from './take-on.js';
64
65
  import { TelegramWebError, saveTelegramToken, telegramBot, telegramDevices, telegramPairing, telegramStatus, unpairTelegramDevice, } from './telegram.js';
65
66
  import { PAGE_ROUTE, sendPageFile, actOnPage, actRateLimited, listPageDescriptors, runPageQuery, } from './pages.js';
66
67
  import { listSecrets, secretUses, secretsAct } from './secrets.js';
67
68
  import { sayRoute, transcribeRoute } from './speech.js';
68
69
  import { createServer } from 'node:http';
69
- import { getAction, idleRolloverMs, inRecovery, listMissions, listPendingActions, isJobState, parseAgentFile, setSentinelEnabled, snoozeFinding, snoozeFindings, unmuteFindings } from '@buddi/core';
70
+ import { asksEachTime, getAction, idleRolloverMs, inRecovery, listMissions, listPendingActions, isJobState, parseAgentFile, setSentinelEnabled, snoozeFinding, snoozeFindings, unmuteFindings } from '@buddi/core';
70
71
  import { actOnAlerts, askPrompt, findingsForAsk, muteAlert } from './alerts.js';
71
72
  import { connectionsOf } from '@buddi/tool-mcp';
72
73
  import { CONNECTIONS_CALLBACK_PATH, connectionsRoute } from './connections.js';
@@ -75,6 +76,7 @@ import { hostService } from '@buddi/tool-host';
75
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';
76
77
  import { EXPORT_MIME, MAX_EXPORT_SOURCE_BYTES, exportFormats, exportName } from '../export/document.js';
77
78
  import { ExportRefused, runExport } from '../export/convert.js';
79
+ import { createVault } from '@buddi/core';
78
80
  import { beginOnboarding, completeOnboarding, markStepDone, setOnboardingDetails, skipOnboarding, readWebSetting, writeWebSetting, isAssetKey, readPluginAsset } from '@buddi/core';
79
81
  import { listMemory, setPreference, forgetPreference, updateNote, forgetNote } from '@buddi/tool-memory';
80
82
  import { purgeGroups, stopGroupWork } from './group-lifecycle.js';
@@ -92,7 +94,14 @@ import { ATTACHMENTS_UNAVAILABLE, CHAT_CONVERSATIONS_LIMIT, CHAT_UNAVAILABLE, WE
92
94
  import { readAgentAttention, streamAttention } from './attention.js';
93
95
  import { PreviewApp, PreviewTickets, parsePreviewApiPath } from './preview.js';
94
96
  import { allowedOrigins, isLoopback, webAssetsDir, webUrl } from './config.js';
95
- 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';
96
105
  import { clientKey } from './client-key.js';
97
106
  import { retryHref, sendSignedOut, wantsSignedOutPage } from './signed-out.js';
98
107
  import { extensionEndpoint } from './extension.js';
@@ -112,7 +121,10 @@ import { tipsRoute } from '../tips/route.js';
112
121
  import { createWidgets, widgetsRoute } from './widgets.js';
113
122
  import { createRequirements } from '../plugins/requires.js';
114
123
  import { placesList, placesRoute } from './places.js';
115
- import { OWNER_DATE_FORMATS, OWNER_TIME_FORMATS } from '@buddi/core';
124
+ import { peopleRoute } from './people.js';
125
+ import { syncDateMissions } from '../missions/dates.js';
126
+ import { resumeParkedForPage } from '../missions/parked.js';
127
+ import { OWNER_DATE_FORMATS, OWNER_TIME_FORMATS, validDayMonth } from '@buddi/core';
116
128
  import { readFacts, webSettingsStore } from '../tips/facts.js';
117
129
  import { dismissAgentOffer, isPendingAccept, raiseAgentOffers, readAgentOffers } from './agent-offers.js';
118
130
  import { currentVersion, upgradeJobRoute, upgradeRoute, versionCheckRoute, versionRoute, } from './version.js';
@@ -125,6 +137,8 @@ import { ensureWebToken, verifyTicket } from './token.js';
125
137
  import { MAX_UPLOAD_BYTES, readUpload } from './upload.js';
126
138
  import { LOCKED_BODY, LockUnavailable, allowedWhileLocked, clientOf, createLock } from './lock.js';
127
139
  import { matchApiRoute, TOKEN_REFUSALS } from './api-routes.js';
140
+ import { QUIET_UNAVAILABLE_TEXT, pluginCommands } from './composer.js';
141
+ import { createEngagementHooks } from '../missions/engagement.js';
128
142
  import { apiTokensRoute, bearerOf, verifyApiToken } from './api-tokens.js';
129
143
  import { LockImageRefusal, MAX_LOCK_IMAGE_BYTES, normaliseLockImage } from './lock-image.js';
130
144
  import { cancelJobFromWeb, cancelReminderFromWeb, decideApprovalFromWeb, retryJobFromWeb, retryJobsFromWeb, dismissJobsFromWeb, undismissJobsFromWeb, setMissionEnabledFromWeb, keepMissionFromWeb, answerStillUsefulFromWeb, setPausedFromWeb, setScheduleFromWeb, dismissOfferFromWeb, dismissOffersFromWeb, takeOfferFromWeb, } from './write.js';
@@ -154,6 +168,21 @@ const PREVIEW_APPS = new WeakMap();
154
168
  * reloads once the next process answers `/_buddi/ready` with a new `boot`.
155
169
  */
156
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
+ }
157
186
  /** How long a closing frame may take to reach the pages before the sockets go. */
158
187
  const CLOSING_FLUSH_MS = 250;
159
188
  /** The chat surface this server is running, if any. */
@@ -264,13 +293,13 @@ export function createWebApp(deps) {
264
293
  // A locked dashboard drives nothing (docs/dashboard.md, "Lock screen").
265
294
  if (await lock.locked(session))
266
295
  return null;
267
- if (session.via === 'tailscale') {
268
- let refusal;
269
- const confirmed = await identityOf(req, now, (reason) => { refusal = reason; });
270
- if (!confirmed || !sameLogin(confirmed.login, session.tailscaleLogin)) {
271
- // "Could not ask" is not an answer: no socket now, but the session stays.
272
- if (!confirmed && isUnansweredRefusal(refusal))
273
- 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') {
274
303
  await sessions.destroy(session.id).catch(() => { });
275
304
  return null;
276
305
  }
@@ -312,6 +341,9 @@ export function createWebApp(deps) {
312
341
  // Plugin readiness and requirements: serve's, or this server's own.
313
342
  const requirements = deps.requirements ??
314
343
  createRequirements({ registry: deps.registry, ctx: deps.ctx, env: deps.env ?? process.env, pool: deps.pool, now: deps.now, log });
344
+ // Ollama on this computer: the first run's pull of a small model, one at a time.
345
+ const ollamaPulls = deps.ollamaPulls ?? createOllamaPulls({ baseUrl: OLLAMA_BASE_URL });
346
+ const machine = deps.ollamaMachine ?? (() => ollamaMachine());
315
347
  // Home's widgets: one cache per server (web/widgets.ts).
316
348
  const widgets = createWidgets({ pool: deps.pool, registry: deps.registry, ctx: deps.ctx, now: deps.now });
317
349
  /*
@@ -418,6 +450,7 @@ export function createWebApp(deps) {
418
450
  now: deps.now,
419
451
  get timezone() { return deps.timezone; },
420
452
  log,
453
+ browser: deps.browser ?? browserHost(deps.env ?? process.env),
421
454
  ...deps.chat,
422
455
  })
423
456
  : undefined;
@@ -429,8 +462,21 @@ export function createWebApp(deps) {
429
462
  * Failures are the recovery's own business and land in the log.
430
463
  */
431
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
+ });
432
477
  const streams = new StreamBudget();
433
- const permissionScopes = (tool) => deps.registry.lookup(tool)?.reusableApproval
478
+ // A bundle script's run asks every time: its card offers no standing permission (`asksEachTime`).
479
+ const permissionScopes = (tool, envelope) => deps.registry.lookup(tool)?.reusableApproval && !asksEachTime(envelope)
434
480
  ? { permissionScopes: ['conversation', 'always'] } : {};
435
481
  writeDeps.resumeInteractive = (action, outcome) => chat?.resumeHost(action, outcome);
436
482
  /*
@@ -473,55 +519,204 @@ export function createWebApp(deps) {
473
519
  // ticket-and-session gate. The override is a test seam, nothing more.
474
520
  const openAccess = deps.openAccess ?? (deps.env?.BUDDI_WEB_REQUIRE_AUTH !== '1' && isLoopback(deps.config.host));
475
521
  /*
476
- * 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.
477
526
  *
478
527
  * Which sessions were established that way is a field on the session itself
479
- * (`via`), not a map beside it — provenance that can drift out of step with
480
- * the session it describes is provenance that eventually lies. It is what
481
- * 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
482
530
  * wants a session from this machine) and what lets `/api/session` say how
483
531
  * the browser got in.
484
532
  */
485
533
  const whois = deps.tailscale?.whois ?? daemonWhois();
486
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
+ };
487
595
  /**
488
- * The command that publishes this dashboard on the tailnet, with this
489
- * installation's own numbers in it: the HTTPS port of the configured public
490
- * 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.
491
600
  */
492
- const serveCommand = () => {
493
- const bound = server.address()?.port ?? deps.config.port;
494
- let https = 443;
495
- if (deps.config.publicOrigin) {
496
- const port = new URL(deps.config.publicOrigin).port;
497
- if (port !== '')
498
- 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.');
499
612
  }
500
- return `tailscale serve --bg --https=${https} http://127.0.0.1:${bound}`;
613
+ await ingress.sync();
501
614
  };
502
- const readTailscaleSetting = async () => toTailscaleSetting(await readWebSetting(deps.pool, TAILSCALE_SETTING_KEY).catch(() => null));
503
- /**
504
- * Who the daemon says this request is, with the sign-in budget in front of
505
- * the daemon. Used to mint a session and, on every later request, to confirm
506
- * 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.
507
620
  */
508
- const identityOf = (req, now, onRefusal) => tailscaleIdentity(req, {
509
- setting: readTailscaleSetting,
510
- whois,
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.
690
+ */
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,
511
702
  log,
512
- now: () => now,
513
- onRefusal,
514
703
  });
515
704
  /**
516
705
  * The pair a browser holds: the HttpOnly session and the readable CSRF value
517
706
  * the page has to echo back in a header. Both carry the same `Max-Age`, which
518
707
  * is the lifetime this session's scope earned it.
519
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');
520
715
  const sessionCookies = (req, session) => {
521
716
  const maxAgeSeconds = SessionStore.maxAgeSeconds(session);
522
717
  return [
523
- cookieHeader(sessionCookieName(cookiePort(req)), session.id, { httpOnly: true, maxAgeSeconds, secure: session.scope === 'remote' && !!deps.config.publicOrigin }),
524
- 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) }),
525
720
  ];
526
721
  };
527
722
  /**
@@ -530,7 +725,7 @@ export function createWebApp(deps) {
530
725
  * polling page then sends no credential at all, which counts as nothing.
531
726
  */
532
727
  const expiredCookies = (req) => {
533
- const secure = requestScope(req) === 'remote' && !!deps.config.publicOrigin;
728
+ const secure = secureCookies(req, requestScope(req));
534
729
  return [
535
730
  cookieHeader(sessionCookieName(cookiePort(req)), '', { httpOnly: true, maxAgeSeconds: 0, secure }),
536
731
  cookieHeader(csrfCookieName(cookiePort(req)), '', { httpOnly: false, maxAgeSeconds: 0, secure }),
@@ -538,9 +733,23 @@ export function createWebApp(deps) {
538
733
  };
539
734
  /**
540
735
  * What the signed-out page should offer this request: how it arrived,
541
- * 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.
542
738
  */
543
- 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
+ }
544
753
  const proxied = proxiedThroughTailscale(req);
545
754
  let publicHost;
546
755
  try {
@@ -559,14 +768,14 @@ export function createWebApp(deps) {
559
768
  const scope = requestScope(req);
560
769
  const arrived = proxied || (publicHost !== undefined && host === publicHost) ? 'tailnet' : scope === 'local' ? 'local' : 'remote';
561
770
  const setting = arrived === 'tailnet' ? await readTailscaleSetting() : null;
562
- const lockedForMs = limiter.retryAfterMs(clientKey(req), now);
771
+ const tailscaleRefusal = refused?.provider === 'tailscale' ? refused.refusal : undefined;
563
772
  return {
564
773
  retry: retryHref(pathname, search),
565
774
  arrived,
566
- tailscaleSignIn: !!setting?.enabled && !isLoginRefusal(refusal),
567
- ...(isLoginRefusal(refusal) ? { tailscaleRefusal: tailscaleRefusalReason(refusal) } : {}),
568
- ...(isUnansweredRefusal(refusal) ? { tailscaleUnanswered: true } : {}),
569
- ...(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,
570
779
  };
571
780
  };
572
781
  /**
@@ -575,7 +784,16 @@ export function createWebApp(deps) {
575
784
  * loopback page and the tailnet page each keep their own pair. Set, read and
576
785
  * checked through this one function.
577
786
  */
578
- 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
+ };
579
797
  /**
580
798
  * The origins a write may claim, resolved against the port actually bound.
581
799
  *
@@ -586,8 +804,13 @@ export function createWebApp(deps) {
586
804
  let originCache;
587
805
  const allowed = () => {
588
806
  const bound = server.address()?.port ?? deps.config.port;
589
- if (originCache?.port !== bound) {
590
- 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 };
591
814
  }
592
815
  return originCache.set;
593
816
  };
@@ -595,7 +818,7 @@ export function createWebApp(deps) {
595
818
  // caller that needs to drain it on shutdown (or in a test) can, without
596
819
  // `createWebApp` growing a second return value every existing caller would
597
820
  // have to unpack.
598
- const server = createServer((req, res) => {
821
+ function onRequest(req, res) {
599
822
  handle(req, res).catch((err) => {
600
823
  // The lock could not say whether this request may pass: refused, never let in (web/lock.ts).
601
824
  if (err instanceof LockUnavailable) {
@@ -612,9 +835,14 @@ export function createWebApp(deps) {
612
835
  else
613
836
  res.end();
614
837
  });
615
- });
838
+ }
839
+ const server = createServer(onRequest);
616
840
  if (chat)
617
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);
618
846
  PREVIEW_APPS.set(server, previews);
619
847
  CLOSING_SAYS.set(server, async () => {
620
848
  const said = frame('closing', goingAway === undefined ? {} : { for: goingAway });
@@ -645,13 +873,25 @@ export function createWebApp(deps) {
645
873
  // rather than in `startWebServer` so every caller, tests included, has it.
646
874
  extension.attach(server);
647
875
  extension.attachPath(REMOTE_HAND_SOCKET_PATH, (req, socket, head) => hand.upgrade(req, socket, head));
648
- 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?.());
649
878
  return server;
650
879
  async function handle(req, res) {
651
880
  const now = deps.now();
652
881
  const method = (req.method ?? 'GET').toUpperCase();
653
882
  const url = parseUrl(req);
654
- 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;
655
895
  // No CORS, and therefore no preflight.
656
896
  if (method === 'OPTIONS')
657
897
  return sendEmpty(res, 405);
@@ -753,12 +993,12 @@ export function createWebApp(deps) {
753
993
  const presentedSession = cookies[sessionCookieName(cookiePort(req))];
754
994
  let session = await sessions.resolve(presentedSession, scope, now);
755
995
  /*
756
- * Why Tailscale gave this request no identity, when it was asked. Only
757
- * "this login is not the one allowed" changes what the signed-out page
758
- * 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".
759
1000
  */
760
- let refusal;
761
- const noteRefusal = (reason) => { refusal = reason; };
1001
+ let refused = early && !early.result.ok ? { provider: early.provider.id, refusal: early.result } : undefined;
762
1002
  /*
763
1003
  * Set when the browser presented a session that is gone: the refusal then
764
1004
  * expires its cookies, so it stops presenting them (and stops counting).
@@ -767,7 +1007,7 @@ export function createWebApp(deps) {
767
1007
  /*
768
1008
  * The answer to a refused request: the signed-out page for a person
769
1009
  * opening a page, the empty status for everything else (docs/web.md).
770
- * 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
771
1011
  * could not be asked; the session is still good, try again".
772
1012
  */
773
1013
  const refuse = async (status = 401) => {
@@ -778,33 +1018,40 @@ export function createWebApp(deps) {
778
1018
  headers['Retry-After'] = '5';
779
1019
  if (!wantsSignedOutPage(req, method, url.pathname))
780
1020
  return sendEmpty(res, status, headers);
781
- 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);
782
1022
  };
783
- /* 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. */
784
1024
  let revoked = false;
785
1025
  /*
786
- * A Tailscale session is re-confirmed on every single request.
1026
+ * A provider session is re-confirmed on every single request.
787
1027
  *
788
- * A cookie on its own would be a bearer token the tailnet identity is only
789
- * loosely related to: it would outlive the setting being turned off, the
790
- * allowed login being changed, and the device being handed to somebody
791
- * else. So the daemon is asked again — cheap, because a whois answer is
792
- * reused for a minute — and the login it names now must be the login this
793
- * session was minted for. Anything else and the session is gone, not
794
- * merely ignored: the browser is 401 and has to sign in again, which it
795
- * 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.
796
1035
  */
797
- if (session?.via === 'tailscale') {
798
- const confirmed = await identityOf(req, now, noteRefusal);
799
- if (!confirmed || !sameLogin(confirmed.login, session.tailscaleLogin)) {
800
- /*
801
- * The daemon could not be asked (busy, starting, timed out). That is
802
- * not an answer about anyone, so the session is not ended for it —
803
- * a restart's first burst of requests used to end every tailnet
804
- * session this way — and nothing is counted. This request waits.
805
- */
806
- if (!confirmed && isUnansweredRefusal(refusal))
807
- 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') {
808
1055
  const ended = session.id;
809
1056
  // A hand this session was holding does not outlive the session.
810
1057
  hand.revoke((lease) => lease === ended);
@@ -813,7 +1060,7 @@ export function createWebApp(deps) {
813
1060
  revoked = true;
814
1061
  forgetCookies = true;
815
1062
  // The identity on this very request may still earn a new session below.
816
- refusal = undefined;
1063
+ refused = undefined;
817
1064
  }
818
1065
  }
819
1066
  /*
@@ -838,26 +1085,32 @@ export function createWebApp(deps) {
838
1085
  res.setHeader('Set-Cookie', sessionCookies(req, session));
839
1086
  }
840
1087
  /*
841
- * Signed in through Tailscale.
1088
+ * Signed in through a trusted access provider.
842
1089
  *
843
- * The proxy runs on this machine and the daemon on this machine confirms
844
- * who is behind the forwarded address; when that agrees with the login the
845
- * owner allowed, this is the owner's own tailnet identity arriving and the
846
- * request gets a `remote` session exactly as the ticket exchange would
847
- * have minted one — same cookies, same 12 hours, same CSRF on every write.
848
- * Anything less returns null and the request falls through to the 401
849
- * 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.
850
1098
  */
851
1099
  if (!session) {
852
- const identity = await identityOf(req, now, noteRefusal);
853
- 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;
854
1105
  limiter.reset(key);
855
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);
856
1108
  session = sessions.create('remote', now, {
857
- via: 'tailscale',
858
- tailscaleLogin: identity.login,
859
- tailscaleAddress: identity.address,
860
- tailscaleName: identity.name,
1109
+ via: 'provider',
1110
+ provider: identity.provider,
1111
+ providerSubject: identity.subject,
1112
+ ...(identity.detail ? { providerDetail: identity.detail } : {}),
1113
+ absoluteCapMs: capMs,
861
1114
  }, locked ? { locked } : {});
862
1115
  res.setHeader('Set-Cookie', sessionCookies(req, session));
863
1116
  }
@@ -872,14 +1125,26 @@ export function createWebApp(deps) {
872
1125
  // the same stale cookie counts once however often it is sent: all tailnet
873
1126
  // and tunnel traffic shares 127.0.0.1, so a forgotten tab must not be
874
1127
  // able to lock the owner out of every way in.
875
- 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)
876
1135
  return refuse();
877
- forgetCookies = true;
1136
+ if (tried)
1137
+ key = tried.bucket;
1138
+ if (presentedSession)
1139
+ forgetCookies = true;
878
1140
  if (revoked)
879
1141
  return refuse();
880
1142
  if (limiter.blocked(key, now))
881
1143
  return refuse(429);
882
- limiter.failCredential(key, presentedSession, now);
1144
+ if (tried)
1145
+ limiter.failCredential(key, tried.credential, now);
1146
+ if (presentedSession)
1147
+ limiter.failCredential(key, presentedSession, now);
883
1148
  return refuse();
884
1149
  }
885
1150
  /*
@@ -1078,6 +1343,10 @@ export function createWebApp(deps) {
1078
1343
  now: deps.now,
1079
1344
  agents: () => deps.catalog.list(),
1080
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
+ },
1081
1350
  needsSetup: async () => {
1082
1351
  const out = [];
1083
1352
  for (const manifest of deps.registry.manifests()) {
@@ -1158,6 +1427,20 @@ export function createWebApp(deps) {
1158
1427
  return sendEmpty(res, 204);
1159
1428
  return sendJson(res, answer.status, answer.body);
1160
1429
  }
1430
+ // `/quiet` typed into the composer: the verb Telegram and the terminal answer, in their words (missions/engagement.ts).
1431
+ if (path === '/api/quiet' && method === 'POST') {
1432
+ let body;
1433
+ try {
1434
+ body = await readJsonBody(req);
1435
+ }
1436
+ catch {
1437
+ return sendJson(res, 400, { error: 'request body must be JSON' });
1438
+ }
1439
+ const raw = body?.arg;
1440
+ const arg = typeof raw === 'string' ? raw.trim().slice(0, 40) : '';
1441
+ const text = await createEngagementHooks({ pool: deps.pool, now: deps.now, timezone: deps.timezone, unavailableText: QUIET_UNAVAILABLE_TEXT }).quiet(arg);
1442
+ return sendJson(res, 200, { text });
1443
+ }
1161
1444
  // Home's widgets: the gallery, the owner's layout, each placed one's body (web/widgets.ts).
1162
1445
  if (path === '/api/widgets' || path.startsWith('/api/widgets/')) {
1163
1446
  let body = {};
@@ -1363,15 +1646,16 @@ export function createWebApp(deps) {
1363
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));
1364
1647
  return sendJson(res, 200, { permissions, runs: host.runs(deps.ctx.ownerId, agentId, conversationId) });
1365
1648
  }
1366
- case '/api/host/apps':
1367
- return sendJson(res, 200, { apps: await listInstalledApps() });
1368
- case '/api/host/browser-profiles':
1369
- return sendJson(res, 200, { profiles: await listBrowserProfiles(q.get('app') ?? '') });
1370
1649
  case '/api/extension':
1371
1650
  return sendJson(res, 200, await extension.view());
1372
1651
  case '/api/browser':
1373
1652
  return sendJson(res, 200, q.has('conversationId') && q.has('agentId')
1374
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
+ }
1375
1659
  case '/api/browser/screenshot': {
1376
1660
  const expectedSession = url.searchParams.get('sessionId');
1377
1661
  const current = browser.status(expectedSession !== null ? { sessionId: expectedSession } : undefined);
@@ -1410,10 +1694,14 @@ export function createWebApp(deps) {
1410
1694
  // from the page rather than implied by a number in a source file.
1411
1695
  scope: session.scope,
1412
1696
  // How this browser got in. `local` is the open loopback mint,
1413
- // `ticket` the five-minute ticket exchange, `tailscale` an identity the
1414
- // local daemon confirmed — and then the name to greet.
1415
- signedInThrough: session.via,
1416
- ...(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
+ : {}),
1417
1705
  expiresAt: session.expiresAt.toISOString(),
1418
1706
  // What this gateway is running. The page keeps it across an
1419
1707
  // upgrade so that "it came back" can be told from "it is still
@@ -1428,20 +1716,24 @@ export function createWebApp(deps) {
1428
1716
  * memory), and whether this very request came through the proxy —
1429
1717
  * which is what disables the switch on a tailnet browser.
1430
1718
  */
1431
- case '/api/tailscale': {
1432
- const stored = await readTailscaleSetting();
1433
- const daemon = await tailscaleSelfOf();
1434
- return sendJson(res, 200, {
1435
- enabled: stored.enabled,
1436
- login: stored.login,
1437
- available: daemon.available,
1438
- self: daemon.self,
1439
- proxied: session.via === 'tailscale' || proxiedThroughTailscale(req),
1440
- // What the owner has to run on this machine, with this
1441
- // installation's own two ports in it.
1442
- serveCommand: serveCommand(),
1443
- });
1444
- }
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());
1445
1737
  case '/api/overview':
1446
1738
  return sendJson(res, 200, {
1447
1739
  ...(await readOverview({
@@ -1493,8 +1785,8 @@ export function createWebApp(deps) {
1493
1785
  }
1494
1786
  case '/api/approvals': {
1495
1787
  const approvals = await readApprovals(deps.pool, now, boundedLimit(q.get('limit'), 50));
1496
- return sendJson(res, 200, { pending: approvals.pending.map(a => ({ ...a, ...permissionScopes(a.tool) })),
1497
- recent: approvals.recent.map(a => ({ ...a, ...permissionScopes(a.tool) })) });
1788
+ return sendJson(res, 200, { pending: approvals.pending.map(a => ({ ...a, ...permissionScopes(a.tool, a.envelope) })),
1789
+ recent: approvals.recent.map(a => ({ ...a, ...permissionScopes(a.tool, a.envelope) })) });
1498
1790
  }
1499
1791
  case '/api/offers':
1500
1792
  // The roster goes in so the read can lapse an offer whose agent is
@@ -1524,7 +1816,8 @@ export function createWebApp(deps) {
1524
1816
  case '/api/sentinels':
1525
1817
  return sendJson(res, 200, await readSentinels(deps.pool, deps.registry, deps.now()));
1526
1818
  case '/api/chat/agents':
1527
- return sendJson(res, 200, readChatAgents(deps.catalog, await avatarVersions(deps.pool)));
1819
+ // With the commands the plugins add to the composer's `/` menu (web/composer.ts).
1820
+ return sendJson(res, 200, { ...readChatAgents(deps.catalog, await avatarVersions(deps.pool)), commands: pluginCommands(deps.registry) });
1528
1821
  // Which agents are waiting on the owner. One small query, and the only
1529
1822
  // definition of "waiting" in the installation — see attention.ts.
1530
1823
  case '/api/chat/attention':
@@ -1681,8 +1974,12 @@ export function createWebApp(deps) {
1681
1974
  const persona = readFirstAgentPersona(onboardingDeps());
1682
1975
  return persona ? sendJson(res, 200, persona) : sendJson(res, 404, { error: 'There is no assistant of your own yet.' });
1683
1976
  }
1977
+ // With what the machine says: installed or not, how to install it, the
1978
+ // model the first run would fetch here, and a fetch already going.
1684
1979
  case '/api/onboarding/ollama':
1685
- return sendJson(res, 200, await probeOllama());
1980
+ return sendJson(res, 200, { ...(await probeOllama()), machine: machine(), pull: ollamaPulls.read() });
1981
+ case '/api/onboarding/ollama/pull':
1982
+ return sendJson(res, 200, { pull: ollamaPulls.read() });
1686
1983
  // Is mlxh running here? The same question, asked the same way, of port 1060.
1687
1984
  case '/api/onboarding/mlxh':
1688
1985
  return sendJson(res, 200, await probeMlxh({ baseUrl: mlxhBaseUrl(deps.env ?? process.env) }));
@@ -1700,6 +1997,12 @@ export function createWebApp(deps) {
1700
1997
  const profile = await getOwnerProfile(deps.pool);
1701
1998
  return sendJson(res, 200, { ...profile, places: await placesList(deps.pool), detectedTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone, zones: knownTimezones() });
1702
1999
  }
2000
+ // Settings → Memory → People, and Home's birthday card (web/people.ts).
2001
+ case '/api/memory/people':
2002
+ case '/api/owner/birthday': {
2003
+ const answered = await peopleRoute({ pool: deps.pool, catalog: deps.catalog, now: deps.now, log }, 'GET', path, {});
2004
+ return sendJson(res, answered.status, answered.body);
2005
+ }
1703
2006
  case '/api/memory': {
1704
2007
  try {
1705
2008
  // `?agent=` narrows it to what that agent sees: the agent sheet's tab.
@@ -1859,6 +2162,30 @@ export function createWebApp(deps) {
1859
2162
  // The Skills page: every skill, grouped, who holds each; one whole; one as its file.
1860
2163
  if (path === '/api/skills')
1861
2164
  return reply(res, listSkillsRoute(skillsDeps()));
2165
+ /*
2166
+ * A bundle's files, for the viewer: one file's text (or its size when
2167
+ * it is not text), and a picture's bytes for an <img>. The same for an
2168
+ * upload still waiting in the preview. A picture is served with a CSP
2169
+ * that runs nothing, so an SVG opened on its own cannot script the page.
2170
+ */
2171
+ const bundleRead = /^\/api\/skills\/(bundles\/)?([^/]+)\/(file|image)$/.exec(path);
2172
+ if (bundleRead) {
2173
+ const id = decodeURIComponent(bundleRead[2]);
2174
+ const rel = url.searchParams.get('path') ?? '';
2175
+ if (bundleRead[3] === 'file') {
2176
+ return reply(res, bundleRead[1] ? stagedFileRoute(skillsDeps(), id, rel) : skillFileRoute(skillsDeps(), id, rel));
2177
+ }
2178
+ const picture = bundleRead[1] ? stagedImage(skillsDeps(), id, rel) : skillImage(skillsDeps(), id, rel);
2179
+ if (!picture)
2180
+ return sendJson(res, 404, { error: `There is no picture "${rel}" in this bundle.` });
2181
+ res.statusCode = 200;
2182
+ res.setHeader('Content-Type', picture.type);
2183
+ res.setHeader('Content-Security-Policy', "default-src 'none'; style-src 'unsafe-inline'; sandbox");
2184
+ res.setHeader('X-Content-Type-Options', 'nosniff');
2185
+ res.setHeader('Cache-Control', 'no-store');
2186
+ res.end(method === 'HEAD' ? undefined : picture.bytes);
2187
+ return;
2188
+ }
1862
2189
  const skillRead = /^\/api\/skills\/([^/]+)(\/download)?$/.exec(path);
1863
2190
  if (skillRead) {
1864
2191
  const id = decodeURIComponent(skillRead[1]);
@@ -1867,6 +2194,14 @@ export function createWebApp(deps) {
1867
2194
  const file = skillDownload(skillsDeps(), id);
1868
2195
  if (!file)
1869
2196
  return sendJson(res, 404, { error: `There is no skill "${id}".` });
2197
+ if ('zip' in file) {
2198
+ res.statusCode = 200;
2199
+ res.setHeader('Content-Type', 'application/zip');
2200
+ res.setHeader('Content-Disposition', `attachment; filename="${file.filename}"`);
2201
+ res.setHeader('Cache-Control', 'no-store');
2202
+ res.end(method === 'HEAD' ? undefined : Buffer.from(file.zip));
2203
+ return;
2204
+ }
1870
2205
  res.statusCode = 200;
1871
2206
  res.setHeader('Content-Type', 'text/markdown; charset=utf-8');
1872
2207
  res.setHeader('Content-Disposition', `attachment; filename="${file.filename}"`);
@@ -1914,7 +2249,7 @@ export function createWebApp(deps) {
1914
2249
  const action = await getAction(deps.pool, decodeURIComponent(approval[1]));
1915
2250
  if (!action)
1916
2251
  return sendJson(res, 404, { error: 'no such action' });
1917
- return sendJson(res, 200, { action: { ...toApprovalView(action), ...permissionScopes(action.tool) } });
2252
+ return sendJson(res, 200, { action: { ...toApprovalView(action), ...permissionScopes(action.tool, action.envelope) } });
1918
2253
  }
1919
2254
  /* ---------------- chat ---------------- */
1920
2255
  const groupConversations = /^\/api\/groups\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\/conversations$/i.exec(path);
@@ -2078,6 +2413,9 @@ export function createWebApp(deps) {
2078
2413
  return sendJson(res, forgotten.status, forgotten.body);
2079
2414
  }
2080
2415
  // Delete a skill: the agents that asked for it stop, the file goes to the trash.
2416
+ const stagedGone = /^\/api\/skills\/bundles\/([^/]+)$/.exec(path);
2417
+ if (stagedGone)
2418
+ return reply(res, discardStaged(incomingDirFor(skillsDeps().skillsDir), decodeURIComponent(stagedGone[1])));
2081
2419
  const skillGone = /^\/api\/skills\/([^/]+)$/.exec(path);
2082
2420
  if (skillGone)
2083
2421
  return reply(res, await deleteSkillRoute(skillsDeps(), decodeURIComponent(skillGone[1])));
@@ -2155,7 +2493,7 @@ export function createWebApp(deps) {
2155
2493
  * everything else.
2156
2494
  */
2157
2495
  if (method === 'PUT') {
2158
- 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'];
2159
2497
  if (!puttable.includes(path))
2160
2498
  return sendEmpty(res, 405);
2161
2499
  let put;
@@ -2165,7 +2503,20 @@ export function createWebApp(deps) {
2165
2503
  catch {
2166
2504
  return sendJson(res, 400, { error: 'request body must be JSON' });
2167
2505
  }
2168
- 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') {
2169
2520
  /*
2170
2521
  * Who may widen access: only a browser that is already on this
2171
2522
  * machine.
@@ -2202,20 +2553,11 @@ export function createWebApp(deps) {
2202
2553
  * in a browser on the far side of the tailnet.
2203
2554
  */
2204
2555
  const forgotten = new Set();
2205
- 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)
2206
2557
  forgotten.add(s.id); return drop; });
2207
2558
  // Including whichever of them had a hand on the owner's browser.
2208
2559
  hand.revoke((lease) => forgotten.has(lease), 'Tailscale access changed. Sign in again.');
2209
- const daemon = await tailscaleSelfOf();
2210
- const stored = toTailscaleSetting({ enabled, login });
2211
- return sendJson(res, 200, {
2212
- enabled: stored.enabled,
2213
- login: stored.login,
2214
- available: daemon.available,
2215
- self: daemon.self,
2216
- proxied: false,
2217
- serveCommand: serveCommand(),
2218
- });
2560
+ return sendJson(res, 200, await tailscaleView(req, session));
2219
2561
  }
2220
2562
  if (path === '/api/version/check')
2221
2563
  return reply(res, await versionCheckRoute(versionDeps(), 'PUT', put));
@@ -2229,6 +2571,135 @@ export function createWebApp(deps) {
2229
2571
  }
2230
2572
  if (method !== 'POST')
2231
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
+ }
2232
2703
  if (path === '/api/extension/pair') {
2233
2704
  // A six-digit code is worth guessing at scale, so a wrong one costs a
2234
2705
  // budget — this session's pairing budget, not the shared sign-in one.
@@ -2275,24 +2746,47 @@ export function createWebApp(deps) {
2275
2746
  return sendJson(res, 200, { ok: false, message: error instanceof Error ? error.message : String(error) });
2276
2747
  }
2277
2748
  }
2278
- if (path === '/api/browser/settings' || path === '/api/browser/permissions') {
2749
+ if (path === '/api/browser/settings') {
2279
2750
  const body = await readJsonBody(req);
2280
2751
  try {
2281
- if (path.endsWith('/settings')) {
2282
- if (!browser.configure)
2283
- return sendJson(res, 409, { error: 'This host does not support changing control modes.' });
2284
- return sendJson(res, 200, await browser.configure(body));
2285
- }
2286
- if (!body || typeof body !== 'object' || Array.isArray(body) || Object.keys(body).some((key) => key !== 'prompt') || ('prompt' in body && typeof body.prompt !== 'boolean'))
2287
- return sendJson(res, 400, { error: 'Expected {prompt: boolean}' });
2288
- if (!browser.checkPermissions)
2289
- return sendJson(res, 409, { error: 'This host does not support native permission checks.' });
2290
- 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));
2291
2755
  }
2292
2756
  catch (error) {
2293
2757
  return sendJson(res, 409, { error: error instanceof Error ? error.message : String(error) });
2294
2758
  }
2295
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
+ }
2296
2790
  const control = /^\/api\/browser\/(stop|takeover|resume|release)$/.exec(path);
2297
2791
  if (control) {
2298
2792
  const body = await readJsonBody(req);
@@ -2307,7 +2801,8 @@ export function createWebApp(deps) {
2307
2801
  // frozen picture.
2308
2802
  if (action !== 'takeover')
2309
2803
  await hand.close(action === 'stop' ? undefined : sessionId, action === 'resume' ? 'You gave the screen back.' : 'That session was released.');
2310
- 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);
2311
2806
  // Whether this screen can be driven from here at all. The dashboard
2312
2807
  // shows the mode's own sentence when it cannot.
2313
2808
  const taken = sessionId ?? status.session?.id;
@@ -2318,7 +2813,7 @@ export function createWebApp(deps) {
2318
2813
  * as that, with a reason the dashboard offers its ways out on, rather
2319
2814
  * than the mode's "use this conversation's host tab".
2320
2815
  */
2321
- 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()) {
2322
2817
  return sendJson(res, 200, { ...status, hand: false, handReason: 'browser-offline', handMessage: 'Your browser isn’t connected.' });
2323
2818
  }
2324
2819
  return sendJson(res, 200, offer
@@ -2369,6 +2864,20 @@ export function createWebApp(deps) {
2369
2864
  * staged exactly like a `.tgz` path they could have typed, and the upload
2370
2865
  * is deleted once staging has copied it.
2371
2866
  */
2867
+ /*
2868
+ * A skill bundle (.zip), handed over raw like a plugin tarball: streamed
2869
+ * to a temporary file under a cap, checked, unpacked into staging, and
2870
+ * answered with what is inside. Nothing in it runs, and nothing is kept
2871
+ * until the owner accepts it (`POST /api/skills/bundles/:staged`).
2872
+ */
2873
+ if (path === '/api/skills/bundles') {
2874
+ const incoming = incomingDirFor(skillsDeps().skillsDir);
2875
+ const filename = uploadLabel(first(req.headers['x-filename']));
2876
+ const received = await receiveBundleUpload(incoming, req, filename);
2877
+ if ('status' in received)
2878
+ return reply(res, received);
2879
+ return reply(res, stageBundle(incoming, received.path, filename, deps.now()));
2880
+ }
2372
2881
  if (path === '/api/plugins/upload') {
2373
2882
  const received = await receivePluginUpload(pluginDeps(), req, first(req.headers['x-filename']));
2374
2883
  if ('status' in received)
@@ -3014,6 +3523,20 @@ export function createWebApp(deps) {
3014
3523
  * Chapter 3: what buddi takes on. Records the tiles and starts the By-buddi
3015
3524
  * installs in the background; answers at once with a job per plugin.
3016
3525
  */
3526
+ /*
3527
+ * Fetch a model into the local Ollama, for the first run's "on this
3528
+ * computer". Answers at once; GET the same path for the progress.
3529
+ */
3530
+ if (path === '/api/onboarding/ollama/pull') {
3531
+ try {
3532
+ return sendJson(res, 202, { pull: ollamaPulls.start(typeof body.model === 'string' ? body.model : '') });
3533
+ }
3534
+ catch (error) {
3535
+ if (error instanceof PullRefusal)
3536
+ return sendJson(res, error.status, { error: error.message });
3537
+ throw error;
3538
+ }
3539
+ }
3017
3540
  if (path === '/api/onboarding/take-on') {
3018
3541
  try {
3019
3542
  return sendJson(res, 202, await startTakeOn(takeOnDeps(), body.tiles));
@@ -3193,7 +3716,7 @@ export function createWebApp(deps) {
3193
3716
  */
3194
3717
  if (path === '/api/owner') {
3195
3718
  const patch = {};
3196
- for (const key of ['preferredName', 'timezone', 'language', 'about']) {
3719
+ for (const key of ['preferredName', 'timezone', 'language', 'about', 'fullName', 'pronouns']) {
3197
3720
  const given = body[key];
3198
3721
  if (given === undefined)
3199
3722
  continue;
@@ -3218,8 +3741,27 @@ export function createWebApp(deps) {
3218
3741
  return sendJson(res, 400, { error: 'The name is too long (80 characters at most).' });
3219
3742
  if (patch.about && patch.about.length > 1000)
3220
3743
  return sendJson(res, 400, { error: 'Keep the line about you under 1,000 characters.' });
3744
+ if (patch.fullName && patch.fullName.length > 120)
3745
+ return sendJson(res, 400, { error: 'The full name is too long (120 characters at most).' });
3746
+ if (patch.pronouns && patch.pronouns.length > 40)
3747
+ return sendJson(res, 400, { error: 'Keep the pronouns under 40 characters.' });
3748
+ // The birthday: day and month (the year optional), or null to clear it.
3749
+ if (body.birthday !== undefined) {
3750
+ if (body.birthday === null)
3751
+ patch.birthday = null;
3752
+ else {
3753
+ const birthday = validDayMonth(body.birthday);
3754
+ if (!birthday)
3755
+ return sendJson(res, 400, { error: 'The birthday needs a real day and month; the year is optional.' });
3756
+ patch.birthday = birthday;
3757
+ }
3758
+ }
3221
3759
  // The zone applies at once; schedules kept in the old one move with it.
3222
3760
  const { profile, zoneChange } = await saveOwnerProfile(deps.pool, patch, deps.env ?? process.env);
3761
+ // The birthday greeting follows the new day at once (missions/dates.ts).
3762
+ if (patch.birthday !== undefined) {
3763
+ await syncDateMissions({ pool: deps.pool, catalog: deps.catalog, now: deps.now, log }).catch((err) => log(`dates: ${err instanceof Error ? err.message : String(err)}`));
3764
+ }
3223
3765
  if (zoneChange)
3224
3766
  log(`owner: timezone ${zoneChange.from} → ${zoneChange.to}${zoneChange.missions.length > 0 ? `; moved ${zoneChange.missions.join(', ')}` : ''}`);
3225
3767
  return sendJson(res, 200, { ...profile, places: await placesList(deps.pool), detectedTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone, zones: knownTimezones() });
@@ -3233,6 +3775,12 @@ export function createWebApp(deps) {
3233
3775
  return sendJson(res, answered.status, answered.body);
3234
3776
  }
3235
3777
  }
3778
+ /* People, the owner's side: add, change, forget, bring back (web/people.ts). */
3779
+ if (path.startsWith('/api/memory/people')) {
3780
+ const answered = await peopleRoute({ pool: deps.pool, catalog: deps.catalog, now: deps.now, log }, 'POST', path, body);
3781
+ if (answered)
3782
+ return sendJson(res, answered.status, answered.body);
3783
+ }
3236
3784
  /* Memory, the owner's side: correct a preference, retire one, edit or forget a note. */
3237
3785
  if (path === '/api/memory/preferences') {
3238
3786
  const key = typeof body.key === 'string' ? body.key.trim() : '';
@@ -3424,6 +3972,9 @@ export function createWebApp(deps) {
3424
3972
  /* The Skills page's writes (skills.ts): each checked by reloading the catalog. */
3425
3973
  if (path === '/api/skills')
3426
3974
  return reply(res, createSkillRoute(skillsDeps(), body));
3975
+ const bundleAccept = /^\/api\/skills\/bundles\/([^/]+)$/.exec(path);
3976
+ if (bundleAccept)
3977
+ return reply(res, acceptBundleRoute(skillsDeps(), decodeURIComponent(bundleAccept[1]), body));
3427
3978
  const skillWrite = /^\/api\/skills\/([^/]+)\/(text|grants|trust)$/.exec(path);
3428
3979
  if (skillWrite) {
3429
3980
  const id = decodeURIComponent(skillWrite[1]);
@@ -3598,6 +4149,10 @@ export async function startWebServer(deps) {
3598
4149
  */
3599
4150
  if (previewPort !== null)
3600
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();
3601
4156
  /*
3602
4157
  * An approval nobody decides expires, and a delegation waiting on one is
3603
4158
  * handed the expiry as its failure: the approval's own lifetime is the
@@ -3615,6 +4170,7 @@ export async function startWebServer(deps) {
3615
4170
  server,
3616
4171
  port,
3617
4172
  previewPort,
4173
+ ingress,
3618
4174
  url: webUrl({ host: deps.config.host, port }),
3619
4175
  chat: webChatOf(server),
3620
4176
  close: async () => {
@@ -3626,6 +4182,7 @@ export async function startWebServer(deps) {
3626
4182
  // still held, and the next thing to want it — the next test, the
3627
4183
  // gateway coming back up — finds it taken.
3628
4184
  await Promise.all([
4185
+ ingress.close(),
3629
4186
  new Promise((resolve) => {
3630
4187
  if (!previews || previewPort === null)
3631
4188
  return resolve();