@withbuddi/buddi 0.1.0-pre.37 → 0.1.0-pre.39
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.
- package/extension/background.js +764 -32
- package/extension/manifest.json +2 -2
- package/node_modules/@buddi/cli/dist/.tsbuildinfo +1 -1
- package/node_modules/@buddi/cli/dist/access-cmd.d.ts +10 -0
- package/node_modules/@buddi/cli/dist/access-cmd.d.ts.map +1 -0
- package/node_modules/@buddi/cli/dist/access-cmd.js +180 -0
- package/node_modules/@buddi/cli/dist/access-cmd.js.map +1 -0
- package/node_modules/@buddi/cli/dist/args.d.ts +10 -0
- package/node_modules/@buddi/cli/dist/args.d.ts.map +1 -1
- package/node_modules/@buddi/cli/dist/args.js +34 -1
- package/node_modules/@buddi/cli/dist/args.js.map +1 -1
- package/node_modules/@buddi/cli/dist/commands.d.ts.map +1 -1
- package/node_modules/@buddi/cli/dist/commands.js +39 -2
- package/node_modules/@buddi/cli/dist/commands.js.map +1 -1
- package/node_modules/@buddi/cli/dist/doctor-probes.d.ts.map +1 -1
- package/node_modules/@buddi/cli/dist/doctor-probes.js +54 -29
- package/node_modules/@buddi/cli/dist/doctor-probes.js.map +1 -1
- package/node_modules/@buddi/cli/dist/doctor.d.ts +36 -12
- package/node_modules/@buddi/cli/dist/doctor.d.ts.map +1 -1
- package/node_modules/@buddi/cli/dist/doctor.js +32 -9
- package/node_modules/@buddi/cli/dist/doctor.js.map +1 -1
- package/node_modules/@buddi/cli/dist/main.d.ts +2 -0
- package/node_modules/@buddi/cli/dist/main.d.ts.map +1 -1
- package/node_modules/@buddi/cli/dist/main.js +20 -2
- package/node_modules/@buddi/cli/dist/main.js.map +1 -1
- package/node_modules/@buddi/core/dist/.tsbuildinfo +1 -1
- package/node_modules/@buddi/core/dist/agents/catalog.d.ts +2 -0
- package/node_modules/@buddi/core/dist/agents/catalog.d.ts.map +1 -1
- package/node_modules/@buddi/core/dist/agents/catalog.js +1 -0
- package/node_modules/@buddi/core/dist/agents/catalog.js.map +1 -1
- package/node_modules/@buddi/core/dist/agents/edit.d.ts +2 -0
- package/node_modules/@buddi/core/dist/agents/edit.d.ts.map +1 -1
- package/node_modules/@buddi/core/dist/agents/edit.js +3 -1
- package/node_modules/@buddi/core/dist/agents/edit.js.map +1 -1
- package/node_modules/@buddi/core/dist/agents/frontmatter.d.ts +11 -0
- package/node_modules/@buddi/core/dist/agents/frontmatter.d.ts.map +1 -1
- package/node_modules/@buddi/core/dist/agents/frontmatter.js +7 -0
- package/node_modules/@buddi/core/dist/agents/frontmatter.js.map +1 -1
- package/node_modules/@buddi/core/dist/index.d.ts +1 -0
- package/node_modules/@buddi/core/dist/index.d.ts.map +1 -1
- package/node_modules/@buddi/core/dist/index.js +1 -0
- package/node_modules/@buddi/core/dist/index.js.map +1 -1
- package/node_modules/@buddi/core/dist/plugin/index.d.ts +3 -0
- package/node_modules/@buddi/core/dist/plugin/index.d.ts.map +1 -1
- package/node_modules/@buddi/core/dist/plugin/index.js +3 -0
- package/node_modules/@buddi/core/dist/plugin/index.js.map +1 -1
- package/node_modules/@buddi/core/dist/plugin/version.d.ts +1 -1
- package/node_modules/@buddi/core/dist/plugin/version.js +1 -1
- package/node_modules/@buddi/core/dist/questions/store.d.ts +12 -0
- package/node_modules/@buddi/core/dist/questions/store.d.ts.map +1 -1
- package/node_modules/@buddi/core/dist/questions/store.js +11 -1
- package/node_modules/@buddi/core/dist/questions/store.js.map +1 -1
- package/node_modules/@buddi/core/dist/questions/types.d.ts +8 -0
- package/node_modules/@buddi/core/dist/questions/types.d.ts.map +1 -1
- package/node_modules/@buddi/core/dist/questions/types.js +15 -0
- package/node_modules/@buddi/core/dist/questions/types.js.map +1 -1
- package/node_modules/@buddi/core/dist/queue/jobs.d.ts +8 -0
- package/node_modules/@buddi/core/dist/queue/jobs.d.ts.map +1 -1
- package/node_modules/@buddi/core/dist/queue/jobs.js +9 -3
- package/node_modules/@buddi/core/dist/queue/jobs.js.map +1 -1
- package/node_modules/@buddi/core/dist/queue/worker.d.ts +2 -0
- package/node_modules/@buddi/core/dist/queue/worker.d.ts.map +1 -1
- package/node_modules/@buddi/core/dist/queue/worker.js +2 -1
- package/node_modules/@buddi/core/dist/queue/worker.js.map +1 -1
- package/node_modules/@buddi/core/dist/registry.d.ts +7 -1
- package/node_modules/@buddi/core/dist/registry.d.ts.map +1 -1
- package/node_modules/@buddi/core/dist/registry.js +73 -11
- package/node_modules/@buddi/core/dist/registry.js.map +1 -1
- package/node_modules/@buddi/core/dist/routes.d.ts +153 -0
- package/node_modules/@buddi/core/dist/routes.d.ts.map +1 -0
- package/node_modules/@buddi/core/dist/routes.js +47 -0
- package/node_modules/@buddi/core/dist/routes.js.map +1 -0
- package/node_modules/@buddi/core/dist/scheduler/missions.d.ts +5 -3
- package/node_modules/@buddi/core/dist/scheduler/missions.d.ts.map +1 -1
- package/node_modules/@buddi/core/dist/scheduler/missions.js +9 -7
- package/node_modules/@buddi/core/dist/scheduler/missions.js.map +1 -1
- package/node_modules/@buddi/core/dist/scheduler/types.d.ts +7 -0
- package/node_modules/@buddi/core/dist/scheduler/types.d.ts.map +1 -1
- package/node_modules/@buddi/core/dist/scheduler/types.js +1 -0
- package/node_modules/@buddi/core/dist/scheduler/types.js.map +1 -1
- package/node_modules/@buddi/core/dist/tools.d.ts +59 -2
- package/node_modules/@buddi/core/dist/tools.d.ts.map +1 -1
- package/node_modules/@buddi/core/dist/tools.js.map +1 -1
- package/node_modules/@buddi/core/migrations/075_access_providers.sql +43 -0
- package/node_modules/@buddi/core/migrations/076_mission_browser.sql +7 -0
- package/node_modules/@buddi/gateway/dist/.tsbuildinfo +1 -1
- package/node_modules/@buddi/gateway/dist/agents/canvas.d.ts +12 -12
- package/node_modules/@buddi/gateway/dist/agents/catalogue-package.d.ts +38 -20
- package/node_modules/@buddi/gateway/dist/agents/catalogue-package.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/agents/catalogue-package.js +6 -0
- package/node_modules/@buddi/gateway/dist/agents/catalogue-package.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/agents/delegation.d.ts +10 -0
- package/node_modules/@buddi/gateway/dist/agents/delegation.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/agents/delegation.js +1 -0
- package/node_modules/@buddi/gateway/dist/agents/delegation.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/agents/platform-catalogue.d.ts +7 -1
- package/node_modules/@buddi/gateway/dist/agents/platform-catalogue.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/agents/platform-catalogue.js +15 -6
- package/node_modules/@buddi/gateway/dist/agents/platform-catalogue.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/bootstrap.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/bootstrap.js +8 -0
- package/node_modules/@buddi/gateway/dist/bootstrap.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/browser-host.d.ts +30 -1
- package/node_modules/@buddi/gateway/dist/browser-host.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/browser-host.js +56 -1
- package/node_modules/@buddi/gateway/dist/browser-host.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/chat/session.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/chat/session.js +9 -5
- package/node_modules/@buddi/gateway/dist/chat/session.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/index.d.ts +2 -1
- package/node_modules/@buddi/gateway/dist/index.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/index.js +2 -1
- package/node_modules/@buddi/gateway/dist/index.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/missions/ask-cap.d.ts +26 -0
- package/node_modules/@buddi/gateway/dist/missions/ask-cap.d.ts.map +1 -0
- package/node_modules/@buddi/gateway/dist/missions/ask-cap.js +83 -0
- package/node_modules/@buddi/gateway/dist/missions/ask-cap.js.map +1 -0
- package/node_modules/@buddi/gateway/dist/missions/execute.d.ts +45 -1
- package/node_modules/@buddi/gateway/dist/missions/execute.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/missions/execute.js +156 -10
- package/node_modules/@buddi/gateway/dist/missions/execute.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/missions/parked.d.ts +139 -0
- package/node_modules/@buddi/gateway/dist/missions/parked.d.ts.map +1 -0
- package/node_modules/@buddi/gateway/dist/missions/parked.js +255 -0
- package/node_modules/@buddi/gateway/dist/missions/parked.js.map +1 -0
- package/node_modules/@buddi/gateway/dist/serve.d.ts +7 -0
- package/node_modules/@buddi/gateway/dist/serve.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/serve.js +49 -9
- package/node_modules/@buddi/gateway/dist/serve.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/surfaces/browser-cards.d.ts +18 -0
- package/node_modules/@buddi/gateway/dist/surfaces/browser-cards.d.ts.map +1 -0
- package/node_modules/@buddi/gateway/dist/surfaces/browser-cards.js +42 -0
- package/node_modules/@buddi/gateway/dist/surfaces/browser-cards.js.map +1 -0
- package/node_modules/@buddi/gateway/dist/surfaces/browser-continuation.js +1 -1
- package/node_modules/@buddi/gateway/dist/surfaces/browser-continuation.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/surfaces/owner-request.d.ts +6 -0
- package/node_modules/@buddi/gateway/dist/surfaces/owner-request.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/surfaces/owner-request.js +7 -1
- package/node_modules/@buddi/gateway/dist/surfaces/owner-request.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/system-context.d.ts +7 -0
- package/node_modules/@buddi/gateway/dist/system-context.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/system-context.js +14 -7
- package/node_modules/@buddi/gateway/dist/system-context.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/telegram/browser-view.d.ts +17 -17
- package/node_modules/@buddi/gateway/dist/telegram/browser-view.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/telegram/browser-view.js +33 -37
- package/node_modules/@buddi/gateway/dist/telegram/browser-view.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/telegram/channel.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/telegram/channel.js +17 -1
- package/node_modules/@buddi/gateway/dist/telegram/channel.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/telegram/main.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/telegram/main.js +11 -3
- package/node_modules/@buddi/gateway/dist/telegram/main.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/telegram/surface.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/telegram/surface.js +10 -0
- package/node_modules/@buddi/gateway/dist/telegram/surface.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/tips/facts.d.ts +8 -0
- package/node_modules/@buddi/gateway/dist/tips/facts.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/tips/facts.js +8 -2
- package/node_modules/@buddi/gateway/dist/tips/facts.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/tips/rules.d.ts +2 -0
- package/node_modules/@buddi/gateway/dist/tips/rules.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/tips/rules.js +15 -0
- package/node_modules/@buddi/gateway/dist/tips/rules.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/access/arrival.d.ts +24 -0
- package/node_modules/@buddi/gateway/dist/web/access/arrival.d.ts.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/arrival.js +18 -0
- package/node_modules/@buddi/gateway/dist/web/access/arrival.js.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare-api.d.ts +124 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare-api.d.ts.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare-api.js +206 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare-api.js.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare-setup.d.ts +163 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare-setup.d.ts.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare-setup.js +438 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare-setup.js.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare-token.d.ts +31 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare-token.d.ts.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare-token.js +74 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare-token.js.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare.d.ts +145 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare.d.ts.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare.js +474 -0
- package/node_modules/@buddi/gateway/dist/web/access/cloudflare.js.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/index.d.ts +9 -0
- package/node_modules/@buddi/gateway/dist/web/access/index.d.ts.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/index.js +9 -0
- package/node_modules/@buddi/gateway/dist/web/access/index.js.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/ingress.d.ts +30 -0
- package/node_modules/@buddi/gateway/dist/web/access/ingress.d.ts.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/ingress.js +76 -0
- package/node_modules/@buddi/gateway/dist/web/access/ingress.js.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/provider.d.ts +173 -0
- package/node_modules/@buddi/gateway/dist/web/access/provider.d.ts.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/provider.js +11 -0
- package/node_modules/@buddi/gateway/dist/web/access/provider.js.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/registry.d.ts +33 -0
- package/node_modules/@buddi/gateway/dist/web/access/registry.d.ts.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/access/registry.js +32 -0
- package/node_modules/@buddi/gateway/dist/web/access/registry.js.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/{tailscale.d.ts → access/tailscale.d.ts} +27 -0
- package/node_modules/@buddi/gateway/dist/web/access/tailscale.d.ts.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/{tailscale.js → access/tailscale.js} +118 -3
- package/node_modules/@buddi/gateway/dist/web/access/tailscale.js.map +1 -0
- package/node_modules/@buddi/gateway/dist/web/agents.d.ts +2 -0
- package/node_modules/@buddi/gateway/dist/web/agents.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/agents.js +19 -1
- package/node_modules/@buddi/gateway/dist/web/agents.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/api-routes.d.ts +4 -3
- package/node_modules/@buddi/gateway/dist/web/api-routes.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/api-routes.js +106 -19
- package/node_modules/@buddi/gateway/dist/web/api-routes.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/attention.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/attention.js +11 -3
- package/node_modules/@buddi/gateway/dist/web/attention.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/chat.d.ts +23 -0
- package/node_modules/@buddi/gateway/dist/web/chat.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/chat.js +62 -2
- package/node_modules/@buddi/gateway/dist/web/chat.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/client-key.d.ts +3 -0
- package/node_modules/@buddi/gateway/dist/web/client-key.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/client-key.js +8 -1
- package/node_modules/@buddi/gateway/dist/web/client-key.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/extension.d.ts +21 -1
- package/node_modules/@buddi/gateway/dist/web/extension.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/extension.js +183 -3
- package/node_modules/@buddi/gateway/dist/web/extension.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/http.d.ts +7 -0
- package/node_modules/@buddi/gateway/dist/web/http.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/http.js +15 -1
- package/node_modules/@buddi/gateway/dist/web/http.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/index.d.ts +1 -1
- package/node_modules/@buddi/gateway/dist/web/index.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/index.js +1 -1
- package/node_modules/@buddi/gateway/dist/web/index.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/onboarding.js +1 -1
- package/node_modules/@buddi/gateway/dist/web/onboarding.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/remote-hand.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/remote-hand.js +45 -2
- package/node_modules/@buddi/gateway/dist/web/remote-hand.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/secrets.d.ts +26 -1
- package/node_modules/@buddi/gateway/dist/web/secrets.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/secrets.js +66 -3
- package/node_modules/@buddi/gateway/dist/web/secrets.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/server.d.ts +38 -2
- package/node_modules/@buddi/gateway/dist/web/server.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/server.js +634 -147
- package/node_modules/@buddi/gateway/dist/web/server.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/sessions.d.ts +24 -14
- package/node_modules/@buddi/gateway/dist/web/sessions.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/sessions.js +42 -14
- package/node_modules/@buddi/gateway/dist/web/sessions.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/signed-out.d.ts +10 -0
- package/node_modules/@buddi/gateway/dist/web/signed-out.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/signed-out.js +47 -0
- package/node_modules/@buddi/gateway/dist/web/signed-out.js.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/version.d.ts +2 -0
- package/node_modules/@buddi/gateway/dist/web/version.d.ts.map +1 -1
- package/node_modules/@buddi/gateway/dist/web/version.js +5 -1
- package/node_modules/@buddi/gateway/dist/web/version.js.map +1 -1
- package/node_modules/@buddi/install/dist/.tsbuildinfo +1 -1
- package/node_modules/@buddi/install/dist/app-layout.d.ts +127 -0
- package/node_modules/@buddi/install/dist/app-layout.d.ts.map +1 -0
- package/node_modules/@buddi/install/dist/app-layout.js +323 -0
- package/node_modules/@buddi/install/dist/app-layout.js.map +1 -0
- package/node_modules/@buddi/install/dist/index.d.ts +1 -0
- package/node_modules/@buddi/install/dist/index.d.ts.map +1 -1
- package/node_modules/@buddi/install/dist/index.js +1 -0
- package/node_modules/@buddi/install/dist/index.js.map +1 -1
- package/node_modules/@buddi/install/dist/launcher.d.ts.map +1 -1
- package/node_modules/@buddi/install/dist/launcher.js +18 -4
- package/node_modules/@buddi/install/dist/launcher.js.map +1 -1
- package/node_modules/@buddi/install/dist/provenance.d.ts +34 -0
- package/node_modules/@buddi/install/dist/provenance.d.ts.map +1 -0
- package/node_modules/@buddi/install/dist/provenance.js +200 -0
- package/node_modules/@buddi/install/dist/provenance.js.map +1 -0
- package/node_modules/@buddi/install/dist/supervisor.d.ts.map +1 -1
- package/node_modules/@buddi/install/dist/supervisor.js +9 -4
- package/node_modules/@buddi/install/dist/supervisor.js.map +1 -1
- package/node_modules/@buddi/install/dist/upgrade.d.ts +77 -3
- package/node_modules/@buddi/install/dist/upgrade.d.ts.map +1 -1
- package/node_modules/@buddi/install/dist/upgrade.js +325 -43
- package/node_modules/@buddi/install/dist/upgrade.js.map +1 -1
- package/node_modules/@buddi/runtime/dist/.tsbuildinfo +1 -1
- package/node_modules/@buddi/runtime/dist/delegate.d.ts +8 -0
- package/node_modules/@buddi/runtime/dist/delegate.d.ts.map +1 -1
- package/node_modules/@buddi/runtime/dist/delegate.js +19 -1
- package/node_modules/@buddi/runtime/dist/delegate.js.map +1 -1
- package/node_modules/@buddi/runtime/dist/loop.d.ts +2 -0
- package/node_modules/@buddi/runtime/dist/loop.d.ts.map +1 -1
- package/node_modules/@buddi/runtime/dist/loop.js +18 -4
- package/node_modules/@buddi/runtime/dist/loop.js.map +1 -1
- package/node_modules/@buddi/tool-artifacts/dist/.tsbuildinfo +1 -1
- package/node_modules/@buddi/tool-browser/dist/.tsbuildinfo +1 -1
- package/node_modules/@buddi/tool-browser/dist/availability.d.ts +2 -2
- package/node_modules/@buddi/tool-browser/dist/availability.d.ts.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/availability.js +2 -2
- package/node_modules/@buddi/tool-browser/dist/availability.js.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/controller.d.ts +82 -58
- package/node_modules/@buddi/tool-browser/dist/controller.d.ts.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/controller.js +823 -217
- package/node_modules/@buddi/tool-browser/dist/controller.js.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/driver.d.ts +3 -1
- package/node_modules/@buddi/tool-browser/dist/driver.d.ts.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/driver.js +82 -1
- package/node_modules/@buddi/tool-browser/dist/driver.js.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/extension.d.ts +70 -5
- package/node_modules/@buddi/tool-browser/dist/extension.d.ts.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/extension.js +94 -9
- package/node_modules/@buddi/tool-browser/dist/extension.js.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/index.d.ts +17 -7
- package/node_modules/@buddi/tool-browser/dist/index.d.ts.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/index.js +35 -15
- package/node_modules/@buddi/tool-browser/dist/index.js.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/logins.d.ts +150 -0
- package/node_modules/@buddi/tool-browser/dist/logins.d.ts.map +1 -0
- package/node_modules/@buddi/tool-browser/dist/logins.js +476 -0
- package/node_modules/@buddi/tool-browser/dist/logins.js.map +1 -0
- package/node_modules/@buddi/tool-browser/dist/manager.d.ts +39 -21
- package/node_modules/@buddi/tool-browser/dist/manager.d.ts.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/manager.js +229 -136
- package/node_modules/@buddi/tool-browser/dist/manager.js.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/routes.d.ts +151 -0
- package/node_modules/@buddi/tool-browser/dist/routes.d.ts.map +1 -0
- package/node_modules/@buddi/tool-browser/dist/routes.js +331 -0
- package/node_modules/@buddi/tool-browser/dist/routes.js.map +1 -0
- package/node_modules/@buddi/tool-browser/dist/secrets.d.ts +3 -1
- package/node_modules/@buddi/tool-browser/dist/secrets.d.ts.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/secrets.js +10 -6
- package/node_modules/@buddi/tool-browser/dist/secrets.js.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/service.d.ts +182 -115
- package/node_modules/@buddi/tool-browser/dist/service.d.ts.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/service.js +491 -388
- package/node_modules/@buddi/tool-browser/dist/service.js.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/settings.d.ts +88 -0
- package/node_modules/@buddi/tool-browser/dist/settings.d.ts.map +1 -0
- package/node_modules/@buddi/tool-browser/dist/settings.js +86 -0
- package/node_modules/@buddi/tool-browser/dist/settings.js.map +1 -0
- package/node_modules/@buddi/tool-browser/dist/telemetry.d.ts +203 -0
- package/node_modules/@buddi/tool-browser/dist/telemetry.d.ts.map +1 -0
- package/node_modules/@buddi/tool-browser/dist/telemetry.js +142 -0
- package/node_modules/@buddi/tool-browser/dist/telemetry.js.map +1 -0
- package/node_modules/@buddi/tool-browser/dist/types.d.ts +102 -3
- package/node_modules/@buddi/tool-browser/dist/types.d.ts.map +1 -1
- package/node_modules/@buddi/tool-browser/dist/types.js +27 -9
- package/node_modules/@buddi/tool-browser/dist/types.js.map +1 -1
- package/node_modules/@buddi/tool-email/dist/.tsbuildinfo +1 -1
- package/node_modules/@buddi/tool-host/dist/.tsbuildinfo +1 -1
- package/node_modules/@buddi/tool-mcp/dist/.tsbuildinfo +1 -1
- package/node_modules/@buddi/tool-memory/dist/.tsbuildinfo +1 -1
- package/node_modules/@buddi/tool-web/dist/.tsbuildinfo +1 -1
- package/node_modules/hono/dist/cjs/hono-base.js +2 -0
- package/node_modules/hono/dist/cjs/middleware/language/language.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/mount/index.js +71 -0
- package/node_modules/hono/dist/cjs/request.js +4 -4
- package/node_modules/hono/dist/hono-base.js +2 -0
- package/node_modules/hono/dist/middleware/mount/index.js +70 -0
- package/node_modules/hono/dist/request.js +4 -4
- package/node_modules/hono/dist/types/hono-base.d.ts +2 -0
- package/node_modules/hono/dist/types/jsx/base.d.ts +1 -1
- package/node_modules/hono/dist/types/middleware/mount/index.d.ts +46 -0
- package/node_modules/hono/dist/types/request.d.ts +4 -4
- package/node_modules/hono/package.json +9 -1
- package/node_modules/imapflow/CHANGELOG.md +15 -0
- package/node_modules/imapflow/dist/cjs/package-info.d.ts +1 -1
- package/node_modules/imapflow/dist/cjs/package-info.js +1 -1
- package/node_modules/imapflow/dist/cjs/search-compiler.js +21 -31
- package/node_modules/imapflow/dist/esm/package-info.d.ts +1 -1
- package/node_modules/imapflow/dist/esm/package-info.js +1 -1
- package/node_modules/imapflow/dist/esm/search-compiler.js +21 -31
- package/node_modules/imapflow/package.json +2 -2
- package/node_modules/pino/CONTRIBUTING.md +1 -5
- package/node_modules/pino/LICENSE +1 -1
- package/node_modules/pino/README.md +1 -1
- package/node_modules/pino/SECURITY.md +2 -18
- package/node_modules/pino/browser.js +8 -68
- package/node_modules/pino/docs/api.md +4 -75
- package/node_modules/pino/docs/asynchronous.md +3 -18
- package/node_modules/pino/docs/browser.md +0 -63
- package/node_modules/pino/docs/child-loggers.md +1 -22
- package/node_modules/pino/docs/ecosystem.md +0 -1
- package/node_modules/pino/docs/help.md +3 -11
- package/node_modules/pino/docs/lts.md +0 -29
- package/node_modules/pino/docs/pretty.md +0 -3
- package/node_modules/pino/docs/redaction.md +8 -3
- package/node_modules/pino/docs/transports.md +0 -71
- package/node_modules/pino/docs/web.md +14 -11
- package/node_modules/pino/eslint.config.js +7 -15
- package/node_modules/pino/lib/caller.js +0 -10
- package/node_modules/pino/lib/meta.js +1 -1
- package/node_modules/pino/lib/multistream.js +7 -15
- package/node_modules/pino/lib/redaction.js +3 -8
- package/node_modules/pino/lib/symbols.js +1 -3
- package/node_modules/pino/lib/tools.js +2 -2
- package/node_modules/pino/lib/transport.js +1 -14
- package/node_modules/pino/lib/worker.js +1 -4
- package/node_modules/pino/package.json +22 -14
- package/node_modules/pino/pino.d.ts +736 -709
- package/node_modules/pino/pino.js +2 -27
- package/node_modules/pino/test/basic.test.js +0 -66
- package/node_modules/pino/test/escaping.test.js +0 -17
- package/node_modules/pino/test/fixtures/ts/transpile.cjs +3 -3
- package/node_modules/pino/test/helper.js +2 -6
- package/node_modules/pino/test/multistream.test.js +0 -64
- package/node_modules/pino/test/redact.test.js +0 -30
- package/node_modules/pino/test/transport/core.test.js +0 -85
- package/node_modules/pino/test/transport/sync-false.test.js +0 -12
- package/node_modules/pino/test/transport/uses-pino-config.test.js +4 -4
- package/node_modules/pino/test/types/pino-import.test-d.cts +30 -0
- package/node_modules/pino/test/types/pino-multistream.test-d.ts +28 -0
- package/node_modules/pino/test/types/pino-top-export.test-d.ts +34 -0
- package/node_modules/pino/test/types/{pino-transport.tst.ts → pino-transport.test-d.ts} +70 -85
- package/node_modules/pino/test/types/pino-type-only.test-d.ts +75 -0
- package/node_modules/pino/test/types/{pino.tst.ts → pino.test-d.ts} +137 -135
- package/node_modules/pino/test/types/pino.ts +7 -14
- package/node_modules/pino/tsconfig.json +6 -6
- package/node_modules/real-require/README.md +6 -6
- package/node_modules/real-require/package.json +21 -10
- package/node_modules/real-require/src/index.js +2 -2
- package/node_modules/thread-stream/node_modules/real-require/LICENSE.md +21 -0
- package/node_modules/thread-stream/node_modules/real-require/README.md +51 -0
- package/node_modules/thread-stream/node_modules/real-require/package.json +38 -0
- package/node_modules/thread-stream/node_modules/real-require/src/index.js +14 -0
- package/package.json +2 -2
- package/packages/cli/dist/.tsbuildinfo +1 -1
- package/packages/cli/dist/access-cmd.d.ts +10 -0
- package/packages/cli/dist/access-cmd.d.ts.map +1 -0
- package/packages/cli/dist/access-cmd.js +180 -0
- package/packages/cli/dist/access-cmd.js.map +1 -0
- package/packages/cli/dist/args.d.ts +10 -0
- package/packages/cli/dist/args.d.ts.map +1 -1
- package/packages/cli/dist/args.js +34 -1
- package/packages/cli/dist/args.js.map +1 -1
- package/packages/cli/dist/commands.d.ts.map +1 -1
- package/packages/cli/dist/commands.js +39 -2
- package/packages/cli/dist/commands.js.map +1 -1
- package/packages/cli/dist/doctor-probes.d.ts.map +1 -1
- package/packages/cli/dist/doctor-probes.js +54 -29
- package/packages/cli/dist/doctor-probes.js.map +1 -1
- package/packages/cli/dist/doctor.d.ts +36 -12
- package/packages/cli/dist/doctor.d.ts.map +1 -1
- package/packages/cli/dist/doctor.js +32 -9
- package/packages/cli/dist/doctor.js.map +1 -1
- package/packages/cli/dist/main.d.ts +2 -0
- package/packages/cli/dist/main.d.ts.map +1 -1
- package/packages/cli/dist/main.js +20 -2
- package/packages/cli/dist/main.js.map +1 -1
- package/packages/core/dist/.tsbuildinfo +1 -1
- package/packages/core/dist/agents/catalog.d.ts +2 -0
- package/packages/core/dist/agents/catalog.d.ts.map +1 -1
- package/packages/core/dist/agents/catalog.js +1 -0
- package/packages/core/dist/agents/catalog.js.map +1 -1
- package/packages/core/dist/agents/edit.d.ts +2 -0
- package/packages/core/dist/agents/edit.d.ts.map +1 -1
- package/packages/core/dist/agents/edit.js +3 -1
- package/packages/core/dist/agents/edit.js.map +1 -1
- package/packages/core/dist/agents/frontmatter.d.ts +11 -0
- package/packages/core/dist/agents/frontmatter.d.ts.map +1 -1
- package/packages/core/dist/agents/frontmatter.js +7 -0
- package/packages/core/dist/agents/frontmatter.js.map +1 -1
- package/packages/core/dist/index.d.ts +1 -0
- package/packages/core/dist/index.d.ts.map +1 -1
- package/packages/core/dist/index.js +1 -0
- package/packages/core/dist/index.js.map +1 -1
- package/packages/core/dist/plugin/index.d.ts +3 -0
- package/packages/core/dist/plugin/index.d.ts.map +1 -1
- package/packages/core/dist/plugin/index.js +3 -0
- package/packages/core/dist/plugin/index.js.map +1 -1
- package/packages/core/dist/plugin/version.d.ts +1 -1
- package/packages/core/dist/plugin/version.js +1 -1
- package/packages/core/dist/questions/store.d.ts +12 -0
- package/packages/core/dist/questions/store.d.ts.map +1 -1
- package/packages/core/dist/questions/store.js +11 -1
- package/packages/core/dist/questions/store.js.map +1 -1
- package/packages/core/dist/questions/types.d.ts +8 -0
- package/packages/core/dist/questions/types.d.ts.map +1 -1
- package/packages/core/dist/questions/types.js +15 -0
- package/packages/core/dist/questions/types.js.map +1 -1
- package/packages/core/dist/queue/jobs.d.ts +8 -0
- package/packages/core/dist/queue/jobs.d.ts.map +1 -1
- package/packages/core/dist/queue/jobs.js +9 -3
- package/packages/core/dist/queue/jobs.js.map +1 -1
- package/packages/core/dist/queue/worker.d.ts +2 -0
- package/packages/core/dist/queue/worker.d.ts.map +1 -1
- package/packages/core/dist/queue/worker.js +2 -1
- package/packages/core/dist/queue/worker.js.map +1 -1
- package/packages/core/dist/registry.d.ts +7 -1
- package/packages/core/dist/registry.d.ts.map +1 -1
- package/packages/core/dist/registry.js +73 -11
- package/packages/core/dist/registry.js.map +1 -1
- package/packages/core/dist/routes.d.ts +153 -0
- package/packages/core/dist/routes.d.ts.map +1 -0
- package/packages/core/dist/routes.js +47 -0
- package/packages/core/dist/routes.js.map +1 -0
- package/packages/core/dist/scheduler/missions.d.ts +5 -3
- package/packages/core/dist/scheduler/missions.d.ts.map +1 -1
- package/packages/core/dist/scheduler/missions.js +9 -7
- package/packages/core/dist/scheduler/missions.js.map +1 -1
- package/packages/core/dist/scheduler/types.d.ts +7 -0
- package/packages/core/dist/scheduler/types.d.ts.map +1 -1
- package/packages/core/dist/scheduler/types.js +1 -0
- package/packages/core/dist/scheduler/types.js.map +1 -1
- package/packages/core/dist/tools.d.ts +59 -2
- package/packages/core/dist/tools.d.ts.map +1 -1
- package/packages/core/dist/tools.js.map +1 -1
- package/packages/core/migrations/075_access_providers.sql +43 -0
- package/packages/core/migrations/076_mission_browser.sql +7 -0
- package/packages/gateway/dist/.tsbuildinfo +1 -1
- package/packages/gateway/dist/agents/canvas.d.ts +12 -12
- package/packages/gateway/dist/agents/catalogue-package.d.ts +38 -20
- package/packages/gateway/dist/agents/catalogue-package.d.ts.map +1 -1
- package/packages/gateway/dist/agents/catalogue-package.js +6 -0
- package/packages/gateway/dist/agents/catalogue-package.js.map +1 -1
- package/packages/gateway/dist/agents/delegation.d.ts +10 -0
- package/packages/gateway/dist/agents/delegation.d.ts.map +1 -1
- package/packages/gateway/dist/agents/delegation.js +1 -0
- package/packages/gateway/dist/agents/delegation.js.map +1 -1
- package/packages/gateway/dist/agents/platform-catalogue.d.ts +7 -1
- package/packages/gateway/dist/agents/platform-catalogue.d.ts.map +1 -1
- package/packages/gateway/dist/agents/platform-catalogue.js +15 -6
- package/packages/gateway/dist/agents/platform-catalogue.js.map +1 -1
- package/packages/gateway/dist/bootstrap.d.ts.map +1 -1
- package/packages/gateway/dist/bootstrap.js +8 -0
- package/packages/gateway/dist/bootstrap.js.map +1 -1
- package/packages/gateway/dist/browser-host.d.ts +30 -1
- package/packages/gateway/dist/browser-host.d.ts.map +1 -1
- package/packages/gateway/dist/browser-host.js +56 -1
- package/packages/gateway/dist/browser-host.js.map +1 -1
- package/packages/gateway/dist/chat/session.d.ts.map +1 -1
- package/packages/gateway/dist/chat/session.js +9 -5
- package/packages/gateway/dist/chat/session.js.map +1 -1
- package/packages/gateway/dist/index.d.ts +2 -1
- package/packages/gateway/dist/index.d.ts.map +1 -1
- package/packages/gateway/dist/index.js +2 -1
- package/packages/gateway/dist/index.js.map +1 -1
- package/packages/gateway/dist/missions/ask-cap.d.ts +26 -0
- package/packages/gateway/dist/missions/ask-cap.d.ts.map +1 -0
- package/packages/gateway/dist/missions/ask-cap.js +83 -0
- package/packages/gateway/dist/missions/ask-cap.js.map +1 -0
- package/packages/gateway/dist/missions/execute.d.ts +45 -1
- package/packages/gateway/dist/missions/execute.d.ts.map +1 -1
- package/packages/gateway/dist/missions/execute.js +156 -10
- package/packages/gateway/dist/missions/execute.js.map +1 -1
- package/packages/gateway/dist/missions/parked.d.ts +139 -0
- package/packages/gateway/dist/missions/parked.d.ts.map +1 -0
- package/packages/gateway/dist/missions/parked.js +255 -0
- package/packages/gateway/dist/missions/parked.js.map +1 -0
- package/packages/gateway/dist/serve.d.ts +7 -0
- package/packages/gateway/dist/serve.d.ts.map +1 -1
- package/packages/gateway/dist/serve.js +49 -9
- package/packages/gateway/dist/serve.js.map +1 -1
- package/packages/gateway/dist/surfaces/browser-cards.d.ts +18 -0
- package/packages/gateway/dist/surfaces/browser-cards.d.ts.map +1 -0
- package/packages/gateway/dist/surfaces/browser-cards.js +42 -0
- package/packages/gateway/dist/surfaces/browser-cards.js.map +1 -0
- package/packages/gateway/dist/surfaces/browser-continuation.js +1 -1
- package/packages/gateway/dist/surfaces/browser-continuation.js.map +1 -1
- package/packages/gateway/dist/surfaces/owner-request.d.ts +6 -0
- package/packages/gateway/dist/surfaces/owner-request.d.ts.map +1 -1
- package/packages/gateway/dist/surfaces/owner-request.js +7 -1
- package/packages/gateway/dist/surfaces/owner-request.js.map +1 -1
- package/packages/gateway/dist/system-context.d.ts +7 -0
- package/packages/gateway/dist/system-context.d.ts.map +1 -1
- package/packages/gateway/dist/system-context.js +14 -7
- package/packages/gateway/dist/system-context.js.map +1 -1
- package/packages/gateway/dist/telegram/browser-view.d.ts +17 -17
- package/packages/gateway/dist/telegram/browser-view.d.ts.map +1 -1
- package/packages/gateway/dist/telegram/browser-view.js +33 -37
- package/packages/gateway/dist/telegram/browser-view.js.map +1 -1
- package/packages/gateway/dist/telegram/channel.d.ts.map +1 -1
- package/packages/gateway/dist/telegram/channel.js +17 -1
- package/packages/gateway/dist/telegram/channel.js.map +1 -1
- package/packages/gateway/dist/telegram/main.d.ts.map +1 -1
- package/packages/gateway/dist/telegram/main.js +11 -3
- package/packages/gateway/dist/telegram/main.js.map +1 -1
- package/packages/gateway/dist/telegram/surface.d.ts.map +1 -1
- package/packages/gateway/dist/telegram/surface.js +10 -0
- package/packages/gateway/dist/telegram/surface.js.map +1 -1
- package/packages/gateway/dist/tips/facts.d.ts +8 -0
- package/packages/gateway/dist/tips/facts.d.ts.map +1 -1
- package/packages/gateway/dist/tips/facts.js +8 -2
- package/packages/gateway/dist/tips/facts.js.map +1 -1
- package/packages/gateway/dist/tips/rules.d.ts +2 -0
- package/packages/gateway/dist/tips/rules.d.ts.map +1 -1
- package/packages/gateway/dist/tips/rules.js +15 -0
- package/packages/gateway/dist/tips/rules.js.map +1 -1
- package/packages/gateway/dist/web/access/arrival.d.ts +24 -0
- package/packages/gateway/dist/web/access/arrival.d.ts.map +1 -0
- package/packages/gateway/dist/web/access/arrival.js +18 -0
- package/packages/gateway/dist/web/access/arrival.js.map +1 -0
- package/packages/gateway/dist/web/access/cloudflare-api.d.ts +124 -0
- package/packages/gateway/dist/web/access/cloudflare-api.d.ts.map +1 -0
- package/packages/gateway/dist/web/access/cloudflare-api.js +206 -0
- package/packages/gateway/dist/web/access/cloudflare-api.js.map +1 -0
- package/packages/gateway/dist/web/access/cloudflare-setup.d.ts +163 -0
- package/packages/gateway/dist/web/access/cloudflare-setup.d.ts.map +1 -0
- package/packages/gateway/dist/web/access/cloudflare-setup.js +438 -0
- package/packages/gateway/dist/web/access/cloudflare-setup.js.map +1 -0
- package/packages/gateway/dist/web/access/cloudflare-token.d.ts +31 -0
- package/packages/gateway/dist/web/access/cloudflare-token.d.ts.map +1 -0
- package/packages/gateway/dist/web/access/cloudflare-token.js +74 -0
- package/packages/gateway/dist/web/access/cloudflare-token.js.map +1 -0
- package/packages/gateway/dist/web/access/cloudflare.d.ts +145 -0
- package/packages/gateway/dist/web/access/cloudflare.d.ts.map +1 -0
- package/packages/gateway/dist/web/access/cloudflare.js +474 -0
- package/packages/gateway/dist/web/access/cloudflare.js.map +1 -0
- package/packages/gateway/dist/web/access/index.d.ts +9 -0
- package/packages/gateway/dist/web/access/index.d.ts.map +1 -0
- package/packages/gateway/dist/web/access/index.js +9 -0
- package/packages/gateway/dist/web/access/index.js.map +1 -0
- package/packages/gateway/dist/web/access/ingress.d.ts +30 -0
- package/packages/gateway/dist/web/access/ingress.d.ts.map +1 -0
- package/packages/gateway/dist/web/access/ingress.js +76 -0
- package/packages/gateway/dist/web/access/ingress.js.map +1 -0
- package/packages/gateway/dist/web/access/provider.d.ts +173 -0
- package/packages/gateway/dist/web/access/provider.d.ts.map +1 -0
- package/packages/gateway/dist/web/access/provider.js +11 -0
- package/packages/gateway/dist/web/access/provider.js.map +1 -0
- package/packages/gateway/dist/web/access/registry.d.ts +33 -0
- package/packages/gateway/dist/web/access/registry.d.ts.map +1 -0
- package/packages/gateway/dist/web/access/registry.js +32 -0
- package/packages/gateway/dist/web/access/registry.js.map +1 -0
- package/packages/gateway/dist/web/{tailscale.d.ts → access/tailscale.d.ts} +27 -0
- package/packages/gateway/dist/web/access/tailscale.d.ts.map +1 -0
- package/packages/gateway/dist/web/{tailscale.js → access/tailscale.js} +118 -3
- package/packages/gateway/dist/web/access/tailscale.js.map +1 -0
- package/packages/gateway/dist/web/agents.d.ts +2 -0
- package/packages/gateway/dist/web/agents.d.ts.map +1 -1
- package/packages/gateway/dist/web/agents.js +19 -1
- package/packages/gateway/dist/web/agents.js.map +1 -1
- package/packages/gateway/dist/web/api-routes.d.ts +4 -3
- package/packages/gateway/dist/web/api-routes.d.ts.map +1 -1
- package/packages/gateway/dist/web/api-routes.js +106 -19
- package/packages/gateway/dist/web/api-routes.js.map +1 -1
- package/packages/gateway/dist/web/attention.d.ts.map +1 -1
- package/packages/gateway/dist/web/attention.js +11 -3
- package/packages/gateway/dist/web/attention.js.map +1 -1
- package/packages/gateway/dist/web/chat.d.ts +23 -0
- package/packages/gateway/dist/web/chat.d.ts.map +1 -1
- package/packages/gateway/dist/web/chat.js +62 -2
- package/packages/gateway/dist/web/chat.js.map +1 -1
- package/packages/gateway/dist/web/client-key.d.ts +3 -0
- package/packages/gateway/dist/web/client-key.d.ts.map +1 -1
- package/packages/gateway/dist/web/client-key.js +8 -1
- package/packages/gateway/dist/web/client-key.js.map +1 -1
- package/packages/gateway/dist/web/extension.d.ts +21 -1
- package/packages/gateway/dist/web/extension.d.ts.map +1 -1
- package/packages/gateway/dist/web/extension.js +183 -3
- package/packages/gateway/dist/web/extension.js.map +1 -1
- package/packages/gateway/dist/web/http.d.ts +7 -0
- package/packages/gateway/dist/web/http.d.ts.map +1 -1
- package/packages/gateway/dist/web/http.js +15 -1
- package/packages/gateway/dist/web/http.js.map +1 -1
- package/packages/gateway/dist/web/index.d.ts +1 -1
- package/packages/gateway/dist/web/index.d.ts.map +1 -1
- package/packages/gateway/dist/web/index.js +1 -1
- package/packages/gateway/dist/web/index.js.map +1 -1
- package/packages/gateway/dist/web/onboarding.js +1 -1
- package/packages/gateway/dist/web/onboarding.js.map +1 -1
- package/packages/gateway/dist/web/remote-hand.d.ts.map +1 -1
- package/packages/gateway/dist/web/remote-hand.js +45 -2
- package/packages/gateway/dist/web/remote-hand.js.map +1 -1
- package/packages/gateway/dist/web/secrets.d.ts +26 -1
- package/packages/gateway/dist/web/secrets.d.ts.map +1 -1
- package/packages/gateway/dist/web/secrets.js +66 -3
- package/packages/gateway/dist/web/secrets.js.map +1 -1
- package/packages/gateway/dist/web/server.d.ts +38 -2
- package/packages/gateway/dist/web/server.d.ts.map +1 -1
- package/packages/gateway/dist/web/server.js +634 -147
- package/packages/gateway/dist/web/server.js.map +1 -1
- package/packages/gateway/dist/web/sessions.d.ts +24 -14
- package/packages/gateway/dist/web/sessions.d.ts.map +1 -1
- package/packages/gateway/dist/web/sessions.js +42 -14
- package/packages/gateway/dist/web/sessions.js.map +1 -1
- package/packages/gateway/dist/web/signed-out.d.ts +10 -0
- package/packages/gateway/dist/web/signed-out.d.ts.map +1 -1
- package/packages/gateway/dist/web/signed-out.js +47 -0
- package/packages/gateway/dist/web/signed-out.js.map +1 -1
- package/packages/gateway/dist/web/version.d.ts +2 -0
- package/packages/gateway/dist/web/version.d.ts.map +1 -1
- package/packages/gateway/dist/web/version.js +5 -1
- package/packages/gateway/dist/web/version.js.map +1 -1
- package/packages/install/dist/.tsbuildinfo +1 -1
- package/packages/install/dist/app-layout.d.ts +127 -0
- package/packages/install/dist/app-layout.d.ts.map +1 -0
- package/packages/install/dist/app-layout.js +323 -0
- package/packages/install/dist/app-layout.js.map +1 -0
- package/packages/install/dist/index.d.ts +1 -0
- package/packages/install/dist/index.d.ts.map +1 -1
- package/packages/install/dist/index.js +1 -0
- package/packages/install/dist/index.js.map +1 -1
- package/packages/install/dist/launcher.d.ts.map +1 -1
- package/packages/install/dist/launcher.js +18 -4
- package/packages/install/dist/launcher.js.map +1 -1
- package/packages/install/dist/provenance.d.ts +34 -0
- package/packages/install/dist/provenance.d.ts.map +1 -0
- package/packages/install/dist/provenance.js +200 -0
- package/packages/install/dist/provenance.js.map +1 -0
- package/packages/install/dist/supervisor.d.ts.map +1 -1
- package/packages/install/dist/supervisor.js +9 -4
- package/packages/install/dist/supervisor.js.map +1 -1
- package/packages/install/dist/upgrade.d.ts +77 -3
- package/packages/install/dist/upgrade.d.ts.map +1 -1
- package/packages/install/dist/upgrade.js +325 -43
- package/packages/install/dist/upgrade.js.map +1 -1
- package/packages/runtime/dist/.tsbuildinfo +1 -1
- package/packages/runtime/dist/delegate.d.ts +8 -0
- package/packages/runtime/dist/delegate.d.ts.map +1 -1
- package/packages/runtime/dist/delegate.js +19 -1
- package/packages/runtime/dist/delegate.js.map +1 -1
- package/packages/runtime/dist/loop.d.ts +2 -0
- package/packages/runtime/dist/loop.d.ts.map +1 -1
- package/packages/runtime/dist/loop.js +18 -4
- package/packages/runtime/dist/loop.js.map +1 -1
- package/packages/tools/artifacts/dist/.tsbuildinfo +1 -1
- package/packages/tools/browser/dist/.tsbuildinfo +1 -1
- package/packages/tools/browser/dist/availability.d.ts +2 -2
- package/packages/tools/browser/dist/availability.d.ts.map +1 -1
- package/packages/tools/browser/dist/availability.js +2 -2
- package/packages/tools/browser/dist/availability.js.map +1 -1
- package/packages/tools/browser/dist/controller.d.ts +82 -58
- package/packages/tools/browser/dist/controller.d.ts.map +1 -1
- package/packages/tools/browser/dist/controller.js +823 -217
- package/packages/tools/browser/dist/controller.js.map +1 -1
- package/packages/tools/browser/dist/driver.d.ts +3 -1
- package/packages/tools/browser/dist/driver.d.ts.map +1 -1
- package/packages/tools/browser/dist/driver.js +82 -1
- package/packages/tools/browser/dist/driver.js.map +1 -1
- package/packages/tools/browser/dist/extension.d.ts +70 -5
- package/packages/tools/browser/dist/extension.d.ts.map +1 -1
- package/packages/tools/browser/dist/extension.js +94 -9
- package/packages/tools/browser/dist/extension.js.map +1 -1
- package/packages/tools/browser/dist/index.d.ts +17 -7
- package/packages/tools/browser/dist/index.d.ts.map +1 -1
- package/packages/tools/browser/dist/index.js +35 -15
- package/packages/tools/browser/dist/index.js.map +1 -1
- package/packages/tools/browser/dist/logins.d.ts +150 -0
- package/packages/tools/browser/dist/logins.d.ts.map +1 -0
- package/packages/tools/browser/dist/logins.js +476 -0
- package/packages/tools/browser/dist/logins.js.map +1 -0
- package/packages/tools/browser/dist/manager.d.ts +39 -21
- package/packages/tools/browser/dist/manager.d.ts.map +1 -1
- package/packages/tools/browser/dist/manager.js +229 -136
- package/packages/tools/browser/dist/manager.js.map +1 -1
- package/packages/tools/browser/dist/routes.d.ts +151 -0
- package/packages/tools/browser/dist/routes.d.ts.map +1 -0
- package/packages/tools/browser/dist/routes.js +331 -0
- package/packages/tools/browser/dist/routes.js.map +1 -0
- package/packages/tools/browser/dist/secrets.d.ts +3 -1
- package/packages/tools/browser/dist/secrets.d.ts.map +1 -1
- package/packages/tools/browser/dist/secrets.js +10 -6
- package/packages/tools/browser/dist/secrets.js.map +1 -1
- package/packages/tools/browser/dist/service.d.ts +182 -115
- package/packages/tools/browser/dist/service.d.ts.map +1 -1
- package/packages/tools/browser/dist/service.js +491 -388
- package/packages/tools/browser/dist/service.js.map +1 -1
- package/packages/tools/browser/dist/settings.d.ts +88 -0
- package/packages/tools/browser/dist/settings.d.ts.map +1 -0
- package/packages/tools/browser/dist/settings.js +86 -0
- package/packages/tools/browser/dist/settings.js.map +1 -0
- package/packages/tools/browser/dist/telemetry.d.ts +203 -0
- package/packages/tools/browser/dist/telemetry.d.ts.map +1 -0
- package/packages/tools/browser/dist/telemetry.js +142 -0
- package/packages/tools/browser/dist/telemetry.js.map +1 -0
- package/packages/tools/browser/dist/types.d.ts +102 -3
- package/packages/tools/browser/dist/types.d.ts.map +1 -1
- package/packages/tools/browser/dist/types.js +27 -9
- package/packages/tools/browser/dist/types.js.map +1 -1
- package/packages/tools/email/dist/.tsbuildinfo +1 -1
- package/packages/tools/host/dist/.tsbuildinfo +1 -1
- package/packages/tools/mcp/dist/.tsbuildinfo +1 -1
- package/packages/tools/memory/dist/.tsbuildinfo +1 -1
- package/packages/tools/web/dist/.tsbuildinfo +1 -1
- package/packages/web/dist/assets/index-DYfeUW-0.css +1 -0
- package/packages/web/dist/assets/index-vpc904_b.js +146 -0
- package/packages/web/dist/assets/{lottie_light-CKJ88Yuk.js → lottie_light-BFAMYgLA.js} +1 -1
- package/packages/web/dist/build.json +1 -1
- package/packages/web/dist/index.html +2 -2
- package/packages/web/dist/precache.json +4 -4
- package/node_modules/@buddi/gateway/dist/web/apps.d.ts +0 -18
- package/node_modules/@buddi/gateway/dist/web/apps.d.ts.map +0 -1
- package/node_modules/@buddi/gateway/dist/web/apps.js +0 -84
- package/node_modules/@buddi/gateway/dist/web/apps.js.map +0 -1
- package/node_modules/@buddi/gateway/dist/web/tailscale.d.ts.map +0 -1
- package/node_modules/@buddi/gateway/dist/web/tailscale.js.map +0 -1
- package/node_modules/@buddi/tool-browser/dist/computer.d.ts +0 -153
- package/node_modules/@buddi/tool-browser/dist/computer.d.ts.map +0 -1
- package/node_modules/@buddi/tool-browser/dist/computer.js +0 -392
- package/node_modules/@buddi/tool-browser/dist/computer.js.map +0 -1
- package/node_modules/@buddi/tool-browser/dist/native/buddi-computer +0 -0
- package/node_modules/pino/test/browser-bindings.test.js +0 -114
- package/node_modules/pino/test/browser-redaction.test.js +0 -264
- package/node_modules/pino/test/browser-set-bindings.test.js +0 -99
- package/node_modules/pino/test/caller.test.js +0 -20
- package/node_modules/pino/test/fixtures/caller-loop.js +0 -6
- package/node_modules/pino/test/smoke.js +0 -59
- package/node_modules/pino/test/types/pino-import.tst.cts +0 -30
- package/node_modules/pino/test/types/pino-import.tst.js +0 -73
- package/node_modules/pino/test/types/pino-multistream.tst.ts +0 -49
- package/node_modules/pino/test/types/pino-top-export.tst.ts +0 -32
- package/node_modules/pino/test/types/pino-type-only.tst.ts +0 -77
- package/node_modules/pino/test/types/tsconfig.json +0 -10
- package/packages/gateway/dist/web/apps.d.ts +0 -18
- package/packages/gateway/dist/web/apps.d.ts.map +0 -1
- package/packages/gateway/dist/web/apps.js +0 -84
- package/packages/gateway/dist/web/apps.js.map +0 -1
- package/packages/gateway/dist/web/tailscale.d.ts.map +0 -1
- package/packages/gateway/dist/web/tailscale.js.map +0 -1
- package/packages/tools/browser/dist/computer.d.ts +0 -153
- package/packages/tools/browser/dist/computer.d.ts.map +0 -1
- package/packages/tools/browser/dist/computer.js +0 -392
- package/packages/tools/browser/dist/computer.js.map +0 -1
- package/packages/tools/browser/dist/native/buddi-computer +0 -0
- package/packages/web/dist/assets/index-CN1AXMI1.css +0 -1
- package/packages/web/dist/assets/index-CQRE9LJX.js +0 -145
|
@@ -56,7 +56,6 @@ import { carryOverDeps } from '../surfaces/carry-over.js';
|
|
|
56
56
|
import { deleteCarryOver } from '../surfaces/browser-handoff.js';
|
|
57
57
|
import { ProviderSettingsError } from '../providers.js';
|
|
58
58
|
import { ProviderAccountError } from '../provider-accounts.js';
|
|
59
|
-
import { listBrowserProfiles, listInstalledApps } from './apps.js';
|
|
60
59
|
import { agentSearchPath, EXAMPLES_AGENTS_DIR } from '../agents/catalog.js';
|
|
61
60
|
import { setDelegatesFromWeb } from './write.js';
|
|
62
61
|
import { mlxhBaseUrl, probeMlxh } from '../mlxh.js';
|
|
@@ -65,7 +64,7 @@ import { OnboardingRefusal, WEB_ONBOARDING_STEPS, WEB_ONBOARDING_SURFACE, claimO
|
|
|
65
64
|
import { TakeOnRefusal, readTakeOn, startTakeOn } from './take-on.js';
|
|
66
65
|
import { TelegramWebError, saveTelegramToken, telegramBot, telegramDevices, telegramPairing, telegramStatus, unpairTelegramDevice, } from './telegram.js';
|
|
67
66
|
import { PAGE_ROUTE, sendPageFile, actOnPage, actRateLimited, listPageDescriptors, runPageQuery, } from './pages.js';
|
|
68
|
-
import { listSecrets, secretUses, secretsAct } from './secrets.js';
|
|
67
|
+
import { listSecrets, ownerLoginKey, ownerLoginNames, ownerLoginStore, secretUses, secretsAct } from './secrets.js';
|
|
69
68
|
import { sayRoute, transcribeRoute } from './speech.js';
|
|
70
69
|
import { createServer } from 'node:http';
|
|
71
70
|
import { asksEachTime, getAction, idleRolloverMs, inRecovery, listMissions, listPendingActions, isJobState, parseAgentFile, setSentinelEnabled, snoozeFinding, snoozeFindings, unmuteFindings } from '@buddi/core';
|
|
@@ -77,6 +76,7 @@ import { hostService } from '@buddi/tool-host';
|
|
|
77
76
|
import { listToolPermissions, revokeToolPermission, getArtifact, readArtifactBytes, artifactBytesExist, discardUnreferencedUpload, listLibrary, getLibraryEntry, decodeCursor, filterKey, textPreviewable, readArtifactPrefix, FILE_FAMILIES, LIBRARY_PAGE_MAX, getOwnerProfile, saveOwnerProfile, isKnownTimezone, listGroups, getGroup, createGroup, updateGroup, archiveGroup, deleteGroup, restoreGroup, clearGroupHistory, groupHistorySize, GROUP_UNDO_MS, GroupRefusal, createGroupConversation, listGroupConversations, latestGroupConversation, openGroupRequest, conversationGroup } from '@buddi/core';
|
|
78
77
|
import { EXPORT_MIME, MAX_EXPORT_SOURCE_BYTES, exportFormats, exportName } from '../export/document.js';
|
|
79
78
|
import { ExportRefused, runExport } from '../export/convert.js';
|
|
79
|
+
import { createVault } from '@buddi/core';
|
|
80
80
|
import { beginOnboarding, completeOnboarding, markStepDone, setOnboardingDetails, skipOnboarding, readWebSetting, writeWebSetting, isAssetKey, readPluginAsset } from '@buddi/core';
|
|
81
81
|
import { listMemory, setPreference, forgetPreference, updateNote, forgetNote } from '@buddi/tool-memory';
|
|
82
82
|
import { purgeGroups, stopGroupWork } from './group-lifecycle.js';
|
|
@@ -94,7 +94,14 @@ import { ATTACHMENTS_UNAVAILABLE, CHAT_CONVERSATIONS_LIMIT, CHAT_UNAVAILABLE, WE
|
|
|
94
94
|
import { readAgentAttention, streamAttention } from './attention.js';
|
|
95
95
|
import { PreviewApp, PreviewTickets, parsePreviewApiPath } from './preview.js';
|
|
96
96
|
import { allowedOrigins, isLoopback, webAssetsDir, webUrl } from './config.js';
|
|
97
|
-
import { TAILSCALE_SETTING_KEY, daemonWhois,
|
|
97
|
+
import { TAILSCALE_SETTING_KEY, daemonWhois, plausibleLogin, proxiedThroughTailscale, tailscaleProvider, tailscaleSelf, tailscaleServeCommand, } from './access/tailscale.js';
|
|
98
|
+
import { CLOUDFLARE_SETTING_KEY, cloudflareProvider, createJwks, normalizeTeamDomain, plausibleTeamDomain, validateCloudflareInput, } from './access/cloudflare.js';
|
|
99
|
+
import { arrivalOf } from './access/arrival.js';
|
|
100
|
+
import { CloudflareApiError, createCloudflareApi, CLOUDFLARE_PERMISSION_LINES, CLOUDFLARE_TOKEN_URL } from './access/cloudflare-api.js';
|
|
101
|
+
import { CLOUDFLARE_SETUP_KEY, checkSetupInput, claimSetupOperation, freshProgress, setupBusySentence, setupOperation, removeCloudflareSetup, runCloudflareSetup, } from './access/cloudflare-setup.js';
|
|
102
|
+
import { ownerSecretTokenStore } from './access/cloudflare-token.js';
|
|
103
|
+
import { createAccessRegistry } from './access/registry.js';
|
|
104
|
+
import { createIngress } from './access/ingress.js';
|
|
98
105
|
import { clientKey } from './client-key.js';
|
|
99
106
|
import { retryHref, sendSignedOut, wantsSignedOutPage } from './signed-out.js';
|
|
100
107
|
import { extensionEndpoint } from './extension.js';
|
|
@@ -116,6 +123,7 @@ import { createRequirements } from '../plugins/requires.js';
|
|
|
116
123
|
import { placesList, placesRoute } from './places.js';
|
|
117
124
|
import { peopleRoute } from './people.js';
|
|
118
125
|
import { syncDateMissions } from '../missions/dates.js';
|
|
126
|
+
import { resumeParkedForPage } from '../missions/parked.js';
|
|
119
127
|
import { OWNER_DATE_FORMATS, OWNER_TIME_FORMATS, validDayMonth } from '@buddi/core';
|
|
120
128
|
import { readFacts, webSettingsStore } from '../tips/facts.js';
|
|
121
129
|
import { dismissAgentOffer, isPendingAccept, raiseAgentOffers, readAgentOffers } from './agent-offers.js';
|
|
@@ -160,6 +168,21 @@ const PREVIEW_APPS = new WeakMap();
|
|
|
160
168
|
* reloads once the next process answers `/_buddi/ready` with a new `boot`.
|
|
161
169
|
*/
|
|
162
170
|
const CLOSING_SAYS = new WeakMap();
|
|
171
|
+
/** Each server's ingress listener (web/access/ingress.ts). */
|
|
172
|
+
const INGRESS_OF = new WeakMap();
|
|
173
|
+
/** Each server's request handler, for a listener without a socket of its own (the relay, provider 3). */
|
|
174
|
+
const DISPATCH_OF = new WeakMap();
|
|
175
|
+
/** The ingress listener belonging to a server built by `createWebApp`. */
|
|
176
|
+
export function ingressOf(server) {
|
|
177
|
+
return INGRESS_OF.get(server);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The request handler of a server built by `createWebApp`: what an in-process
|
|
181
|
+
* listener hands a request to after tagging its arrival (`markRequestArrival`).
|
|
182
|
+
*/
|
|
183
|
+
export function dispatchOf(server) {
|
|
184
|
+
return DISPATCH_OF.get(server);
|
|
185
|
+
}
|
|
163
186
|
/** How long a closing frame may take to reach the pages before the sockets go. */
|
|
164
187
|
const CLOSING_FLUSH_MS = 250;
|
|
165
188
|
/** The chat surface this server is running, if any. */
|
|
@@ -270,13 +293,13 @@ export function createWebApp(deps) {
|
|
|
270
293
|
// A locked dashboard drives nothing (docs/dashboard.md, "Lock screen").
|
|
271
294
|
if (await lock.locked(session))
|
|
272
295
|
return null;
|
|
273
|
-
if (session.via === '
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
296
|
+
if (session.via === 'provider') {
|
|
297
|
+
const confirmed = await access.confirm(session, req, now);
|
|
298
|
+
// "Could not ask", or a stray request without the provider's proof:
|
|
299
|
+
// no socket now, but the session stays.
|
|
300
|
+
if (confirmed.answer === 'unanswered' || confirmed.answer === 'refuse')
|
|
301
|
+
return null;
|
|
302
|
+
if (confirmed.answer === 'end') {
|
|
280
303
|
await sessions.destroy(session.id).catch(() => { });
|
|
281
304
|
return null;
|
|
282
305
|
}
|
|
@@ -427,6 +450,7 @@ export function createWebApp(deps) {
|
|
|
427
450
|
now: deps.now,
|
|
428
451
|
get timezone() { return deps.timezone; },
|
|
429
452
|
log,
|
|
453
|
+
browser: deps.browser ?? browserHost(deps.env ?? process.env),
|
|
430
454
|
...deps.chat,
|
|
431
455
|
})
|
|
432
456
|
: undefined;
|
|
@@ -438,6 +462,27 @@ export function createWebApp(deps) {
|
|
|
438
462
|
* Failures are the recovery's own business and land in the log.
|
|
439
463
|
*/
|
|
440
464
|
void chat?.recoverPendingInput().catch((err) => log(`web chat: recovering queued input failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
465
|
+
/*
|
|
466
|
+
* Give it back: the run that waited on the page carries on. A mission parked
|
|
467
|
+
* on it wakes (missions/parked.ts); otherwise the conversation the take-over
|
|
468
|
+
* held gets its turn. Either way with no message from the owner.
|
|
469
|
+
*/
|
|
470
|
+
const offGiveBack = (deps.browser ?? browserHost(deps.env ?? process.env)).onGiveBack?.((info) => {
|
|
471
|
+
void (async () => {
|
|
472
|
+
if (await resumeParkedForPage(deps.pool, info.conversationId, deps.now()))
|
|
473
|
+
return;
|
|
474
|
+
await chat?.continueAfterGiveBack({ conversationId: info.conversationId, agentId: info.agentId });
|
|
475
|
+
})().catch((err) => log(`browser: carrying on after the page was given back failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
476
|
+
});
|
|
477
|
+
/*
|
|
478
|
+
* A sign-in the owner made on a page they held, kept when they say Save
|
|
479
|
+
* (docs/browser.md, "Saving a sign-in"): the browser host's keeper hands the
|
|
480
|
+
* pair to core's owner-secret store, as the owner, and to nothing else.
|
|
481
|
+
*/
|
|
482
|
+
{
|
|
483
|
+
const loginDeps = { pool: deps.pool, registry: deps.registry, ctx: deps.ctx, now: deps.now };
|
|
484
|
+
(deps.browser ?? browserHost(deps.env ?? process.env)).logins?.useStore(ownerLoginStore(loginDeps), ownerLoginNames(loginDeps), ownerLoginKey(deps.env ?? process.env));
|
|
485
|
+
}
|
|
441
486
|
const streams = new StreamBudget();
|
|
442
487
|
// A bundle script's run asks every time: its card offers no standing permission (`asksEachTime`).
|
|
443
488
|
const permissionScopes = (tool, envelope) => deps.registry.lookup(tool)?.reusableApproval && !asksEachTime(envelope)
|
|
@@ -483,55 +528,204 @@ export function createWebApp(deps) {
|
|
|
483
528
|
// ticket-and-session gate. The override is a test seam, nothing more.
|
|
484
529
|
const openAccess = deps.openAccess ?? (deps.env?.BUDDI_WEB_REQUIRE_AUTH !== '1' && isLoopback(deps.config.host));
|
|
485
530
|
/*
|
|
486
|
-
*
|
|
531
|
+
* Trusted access providers (web/access/): something in front of buddi
|
|
532
|
+
* proves who is knocking, buddi checks the proof itself, and its own
|
|
533
|
+
* `remote` session rules apply. Tailscale (provider 1) asks the local
|
|
534
|
+
* daemon; Cloudflare Access (provider 2) verifies the JWT Access signs.
|
|
487
535
|
*
|
|
488
536
|
* Which sessions were established that way is a field on the session itself
|
|
489
|
-
* (`via`), not a map beside it
|
|
490
|
-
*
|
|
491
|
-
* keeps a tailnet session from widening its own access (`PUT /api/tailscale`
|
|
537
|
+
* (`via: 'provider'` and `provider`), not a map beside it. It is what keeps
|
|
538
|
+
* a provider session from widening its own access (changing a provider
|
|
492
539
|
* wants a session from this machine) and what lets `/api/session` say how
|
|
493
540
|
* the browser got in.
|
|
494
541
|
*/
|
|
495
542
|
const whois = deps.tailscale?.whois ?? daemonWhois();
|
|
496
543
|
const tailscaleSelfOf = deps.tailscale?.self ?? (() => tailscaleSelf());
|
|
544
|
+
const jwks = deps.cloudflare?.jwks ?? createJwks({ log });
|
|
545
|
+
const boundPort = () => server.address()?.port ?? deps.config.port;
|
|
546
|
+
const accessCtx = {
|
|
547
|
+
dashboardPort: boundPort,
|
|
548
|
+
ingressPort: () => ingress.port(),
|
|
549
|
+
publicOrigin: () => deps.config.publicOrigin,
|
|
550
|
+
};
|
|
551
|
+
const tailscale = tailscaleProvider({ whois, self: tailscaleSelfOf, log });
|
|
552
|
+
const cloudflare = cloudflareProvider({ jwks, log, ingressProblem: () => ingress.problem() });
|
|
553
|
+
const access = createAccessRegistry({
|
|
554
|
+
providers: [tailscale, cloudflare],
|
|
555
|
+
readSetting: (key) => readWebSetting(deps.pool, key),
|
|
556
|
+
});
|
|
557
|
+
/** The command that publishes this dashboard on the tailnet, with this installation's own ports. */
|
|
558
|
+
const serveCommand = () => tailscaleServeCommand(boundPort(), deps.config.publicOrigin);
|
|
559
|
+
const readTailscaleSetting = async () => access.settingOf(tailscale);
|
|
560
|
+
/*
|
|
561
|
+
* The Cloudflare setting as last read, so the synchronous Origin check can
|
|
562
|
+
* honour the public address the panel stored. Refreshed on every read.
|
|
563
|
+
*/
|
|
564
|
+
let cloudflareSeen = null;
|
|
565
|
+
const readCloudflareSetting = async () => {
|
|
566
|
+
cloudflareSeen = await access.settingOf(cloudflare);
|
|
567
|
+
return cloudflareSeen;
|
|
568
|
+
};
|
|
569
|
+
/** Did this request come through a provider, or does it hold a session one minted? Then the block is read-only. */
|
|
570
|
+
const throughProvider = (req, session) => session.via === 'provider' || arrivalOf(req) !== 'main' || proxiedThroughTailscale(req);
|
|
571
|
+
/** The Tailscale panel's whole payload (unchanged by the move behind the provider interface). */
|
|
572
|
+
const tailscaleView = async (req, session) => {
|
|
573
|
+
const stored = await readTailscaleSetting();
|
|
574
|
+
const daemon = await tailscaleSelfOf();
|
|
575
|
+
return {
|
|
576
|
+
enabled: stored.enabled,
|
|
577
|
+
login: stored.login,
|
|
578
|
+
available: daemon.available,
|
|
579
|
+
self: daemon.self,
|
|
580
|
+
proxied: throughProvider(req, session),
|
|
581
|
+
// What the owner has to run on this machine, with this installation's own two ports in it.
|
|
582
|
+
serveCommand: serveCommand(),
|
|
583
|
+
};
|
|
584
|
+
};
|
|
585
|
+
/** The Cloudflare panel's payload: the stored fields, the status, the setup copy with the real ingress port. */
|
|
586
|
+
const cloudflareView = async (req, session) => {
|
|
587
|
+
const stored = await readCloudflareSetting();
|
|
588
|
+
const status = await cloudflare.status(stored, accessCtx);
|
|
589
|
+
const visit = cloudflare.lastVisit();
|
|
590
|
+
return {
|
|
591
|
+
enabled: stored.enabled,
|
|
592
|
+
teamDomain: stored.teamDomain,
|
|
593
|
+
aud: stored.aud,
|
|
594
|
+
email: stored.email,
|
|
595
|
+
publicOrigin: stored.publicOrigin,
|
|
596
|
+
status,
|
|
597
|
+
ingressPort: ingress.port() ?? accessCtx.dashboardPort() + 2,
|
|
598
|
+
listening: ingress.port() !== null,
|
|
599
|
+
lastVisit: visit ? { at: visit.at.toISOString(), email: visit.email } : null,
|
|
600
|
+
setup: cloudflare.setup(stored, accessCtx),
|
|
601
|
+
proxied: throughProvider(req, session),
|
|
602
|
+
};
|
|
603
|
+
};
|
|
497
604
|
/**
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
*
|
|
605
|
+
* Store a Cloudflare setting and do what a Save does: end the sessions it
|
|
606
|
+
* admitted unless the same person, team and application stay on, then bind
|
|
607
|
+
* or close the ingress listener. The panel's Save and "Set it up for me"
|
|
608
|
+
* both come through here.
|
|
501
609
|
*/
|
|
502
|
-
const
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
610
|
+
const applyCloudflareSetting = async (value) => {
|
|
611
|
+
const before = await readCloudflareSetting();
|
|
612
|
+
await writeWebSetting(deps.pool, CLOUDFLARE_SETTING_KEY, value);
|
|
613
|
+
await readCloudflareSetting();
|
|
614
|
+
const same = before.enabled && value.enabled && before.email.toLowerCase() === value.email.toLowerCase()
|
|
615
|
+
&& before.teamDomain === value.teamDomain && before.aud === value.aud;
|
|
616
|
+
if (!same) {
|
|
617
|
+
const forgotten = new Set();
|
|
618
|
+
await sessions.forget((s) => { const drop = s.via === 'provider' && s.provider === 'cloudflare-access'; if (drop && s.id)
|
|
619
|
+
forgotten.add(s.id); return drop; });
|
|
620
|
+
hand.revoke((lease) => forgotten.has(lease), 'Cloudflare access changed. Sign in again.');
|
|
509
621
|
}
|
|
510
|
-
|
|
622
|
+
await ingress.sync();
|
|
511
623
|
};
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
624
|
+
/*
|
|
625
|
+
* "Set it up for me" (access/cloudflare-setup.ts): one run at a time, in
|
|
626
|
+
* the background — the health wait lasts as long as the owner takes to run
|
|
627
|
+
* one command — and its progress kept here for the panel to poll. The token
|
|
628
|
+
* is an owner secret, read once per run.
|
|
629
|
+
*/
|
|
630
|
+
let cfTokens = deps.cloudflare?.tokens ?? null;
|
|
631
|
+
const tokenStore = () => (cfTokens ??= ownerSecretTokenStore(deps.pool, createVault({ env: deps.env ?? process.env })));
|
|
632
|
+
let setupRun = null;
|
|
633
|
+
const readSetupRecord = async () => {
|
|
634
|
+
const row = await readWebSetting(deps.pool, CLOUDFLARE_SETUP_KEY);
|
|
635
|
+
return row && typeof row === 'object' && typeof row.host === 'string' ? row : null;
|
|
636
|
+
};
|
|
637
|
+
const setupDepsFor = (token, lease, signal) => ({
|
|
638
|
+
api: createCloudflareApi({ token, transport: deps.cloudflare?.api?.transport, baseUrl: deps.cloudflare?.api?.baseUrl }),
|
|
639
|
+
ingressPort: ingress.port() ?? (askedIngressPort() || null) ?? accessCtx.dashboardPort() + 2,
|
|
640
|
+
platform: deps.cloudflare?.platform ?? process.platform,
|
|
641
|
+
readSetting: readCloudflareSetting,
|
|
642
|
+
saveSetting: applyCloudflareSetting,
|
|
643
|
+
readRecord: readSetupRecord,
|
|
644
|
+
saveRecord: (record) => writeWebSetting(deps.pool, CLOUDFLARE_SETUP_KEY, record),
|
|
645
|
+
test: (team) => jwks.refresh(team),
|
|
646
|
+
...(deps.cloudflare?.setup ?? {}),
|
|
647
|
+
signal,
|
|
648
|
+
lease,
|
|
649
|
+
});
|
|
650
|
+
const setupView = async () => {
|
|
651
|
+
const record = await readSetupRecord();
|
|
652
|
+
return {
|
|
653
|
+
progress: setupRun?.progress ?? freshProgress(record?.host ?? '', record?.email ?? ''),
|
|
654
|
+
tokenStored: await tokenStore().has().catch(() => false),
|
|
655
|
+
record: record ? { host: record.host, email: record.email, zone: record.zone.name, teamDomain: record.teamDomain } : null,
|
|
656
|
+
permissions: CLOUDFLARE_PERMISSION_LINES,
|
|
657
|
+
tokenUrl: CLOUDFLARE_TOKEN_URL,
|
|
658
|
+
ingressPort: ingress.port() ?? (askedIngressPort() || null) ?? accessCtx.dashboardPort() + 2,
|
|
659
|
+
};
|
|
660
|
+
};
|
|
661
|
+
/** Run in the background; the lease (claimed by the caller) goes when the run ends. */
|
|
662
|
+
const startSetup = (lease, job, first) => {
|
|
663
|
+
const abort = new AbortController();
|
|
664
|
+
const run = { progress: first, abort, done: Promise.resolve() };
|
|
665
|
+
setupRun = run;
|
|
666
|
+
run.done = job((p) => { run.progress = p; }, abort.signal)
|
|
667
|
+
.then((p) => { run.progress = p; })
|
|
668
|
+
.catch(() => { run.progress = { ...run.progress, state: 'failed', error: 'The setup stopped unexpectedly. Try again.' }; })
|
|
669
|
+
.finally(() => lease.release());
|
|
670
|
+
};
|
|
671
|
+
/*
|
|
672
|
+
* A setting written elsewhere (`buddi access cloudflare setup` writes the
|
|
673
|
+
* database directly) reaches the ingress listener here: bound or closed
|
|
674
|
+
* within a quarter of a minute.
|
|
517
675
|
*/
|
|
518
|
-
const
|
|
519
|
-
setting
|
|
520
|
-
|
|
676
|
+
const ingressResync = setInterval(() => {
|
|
677
|
+
void readCloudflareSetting().then((setting) => {
|
|
678
|
+
if (setting.enabled !== (ingress.port() !== null))
|
|
679
|
+
return ingress.sync();
|
|
680
|
+
return undefined;
|
|
681
|
+
}).catch(() => undefined);
|
|
682
|
+
}, 15_000);
|
|
683
|
+
ingressResync.unref?.();
|
|
684
|
+
/** Every provider's row, for "Sign in from elsewhere". */
|
|
685
|
+
const accessView = async (req, session) => ({
|
|
686
|
+
proxied: throughProvider(req, session),
|
|
687
|
+
providers: await Promise.all(access.providers.map(async (p) => {
|
|
688
|
+
const setting = await access.settingOf(p);
|
|
689
|
+
return { id: p.id, title: p.title, identity: p.identity, proxy: p.proxy, enabled: p.enabled(setting), status: await p.status(setting, accessCtx) };
|
|
690
|
+
})),
|
|
691
|
+
});
|
|
692
|
+
/*
|
|
693
|
+
* The ingress listener (specs/trusted-access.md §3.3): a second loopback
|
|
694
|
+
* listener, the dashboard's port + 2 (or `BUDDI_INGRESS_PORT`), bound only
|
|
695
|
+
* while a this-machine provider other than Tailscale is on. Every request
|
|
696
|
+
* on it is remote, whatever its headers or Host say, and it never passes a
|
|
697
|
+
* loopback-only check. cloudflared points here, never at the dashboard's
|
|
698
|
+
* own port, so a tunnel arrival can never be mistaken for this machine.
|
|
699
|
+
*/
|
|
700
|
+
/** `BUDDI_INGRESS_PORT`, when it names a port. */
|
|
701
|
+
function askedIngressPort() {
|
|
702
|
+
const raw = (deps.env ?? process.env).BUDDI_INGRESS_PORT?.trim();
|
|
703
|
+
const asked = raw ? Number(raw) : NaN;
|
|
704
|
+
return Number.isInteger(asked) && asked >= 0 && asked <= 65535 ? asked : null;
|
|
705
|
+
}
|
|
706
|
+
const ingress = createIngress({
|
|
707
|
+
onRequest: (req, res) => onRequest(req, res),
|
|
708
|
+
onUpgrade: (req, socket, head) => { server.emit('upgrade', req, socket, head); },
|
|
709
|
+
port: () => askedIngressPort() ?? (deps.config.port === 0 ? 0 : boundPort() + 2),
|
|
710
|
+
wanted: async () => (await readCloudflareSetting()).enabled,
|
|
521
711
|
log,
|
|
522
|
-
now: () => now,
|
|
523
|
-
onRefusal,
|
|
524
712
|
});
|
|
525
713
|
/**
|
|
526
714
|
* The pair a browser holds: the HttpOnly session and the readable CSRF value
|
|
527
715
|
* the page has to echo back in a header. Both carry the same `Max-Age`, which
|
|
528
716
|
* is the lifetime this session's scope earned it.
|
|
529
717
|
*/
|
|
718
|
+
/*
|
|
719
|
+
* Secure cookies for a remote session behind an HTTPS proxy: the configured
|
|
720
|
+
* public origin, or anything on the ingress listener (Cloudflare's edge
|
|
721
|
+
* always speaks HTTPS to the browser).
|
|
722
|
+
*/
|
|
723
|
+
const secureCookies = (req, scope) => scope === 'remote' && (!!deps.config.publicOrigin || arrivalOf(req) === 'ingress');
|
|
530
724
|
const sessionCookies = (req, session) => {
|
|
531
725
|
const maxAgeSeconds = SessionStore.maxAgeSeconds(session);
|
|
532
726
|
return [
|
|
533
|
-
cookieHeader(sessionCookieName(cookiePort(req)), session.id, { httpOnly: true, maxAgeSeconds, secure: session.scope
|
|
534
|
-
cookieHeader(csrfCookieName(cookiePort(req)), session.csrf, { httpOnly: false, maxAgeSeconds, secure: session.scope
|
|
727
|
+
cookieHeader(sessionCookieName(cookiePort(req)), session.id, { httpOnly: true, maxAgeSeconds, secure: secureCookies(req, session.scope) }),
|
|
728
|
+
cookieHeader(csrfCookieName(cookiePort(req)), session.csrf, { httpOnly: false, maxAgeSeconds, secure: secureCookies(req, session.scope) }),
|
|
535
729
|
];
|
|
536
730
|
};
|
|
537
731
|
/**
|
|
@@ -540,7 +734,7 @@ export function createWebApp(deps) {
|
|
|
540
734
|
* polling page then sends no credential at all, which counts as nothing.
|
|
541
735
|
*/
|
|
542
736
|
const expiredCookies = (req) => {
|
|
543
|
-
const secure = requestScope(req)
|
|
737
|
+
const secure = secureCookies(req, requestScope(req));
|
|
544
738
|
return [
|
|
545
739
|
cookieHeader(sessionCookieName(cookiePort(req)), '', { httpOnly: true, maxAgeSeconds: 0, secure }),
|
|
546
740
|
cookieHeader(csrfCookieName(cookiePort(req)), '', { httpOnly: false, maxAgeSeconds: 0, secure }),
|
|
@@ -548,9 +742,23 @@ export function createWebApp(deps) {
|
|
|
548
742
|
};
|
|
549
743
|
/**
|
|
550
744
|
* What the signed-out page should offer this request: how it arrived,
|
|
551
|
-
*
|
|
745
|
+
* which provider can sign it in, why it could not, and whether a lockout is
|
|
746
|
+
* running.
|
|
552
747
|
*/
|
|
553
|
-
const signedOutFor = async (req, pathname, search, now,
|
|
748
|
+
const signedOutFor = async (req, pathname, search, now, refused, key) => {
|
|
749
|
+
const lockedForMs = limiter.retryAfterMs(key, now);
|
|
750
|
+
const locked = lockedForMs > 0 ? { lockedForMs } : {};
|
|
751
|
+
if (arrivalOf(req) === 'ingress') {
|
|
752
|
+
return {
|
|
753
|
+
retry: retryHref(pathname, search),
|
|
754
|
+
arrived: 'remote',
|
|
755
|
+
provider: {
|
|
756
|
+
title: cloudflare.title,
|
|
757
|
+
...(refused?.provider === 'cloudflare-access' ? { refusal: refused.refusal.refusal, kind: refused.refusal.kind } : { refusal: 'no-assertion', kind: 'other' }),
|
|
758
|
+
},
|
|
759
|
+
...locked,
|
|
760
|
+
};
|
|
761
|
+
}
|
|
554
762
|
const proxied = proxiedThroughTailscale(req);
|
|
555
763
|
let publicHost;
|
|
556
764
|
try {
|
|
@@ -569,14 +777,14 @@ export function createWebApp(deps) {
|
|
|
569
777
|
const scope = requestScope(req);
|
|
570
778
|
const arrived = proxied || (publicHost !== undefined && host === publicHost) ? 'tailnet' : scope === 'local' ? 'local' : 'remote';
|
|
571
779
|
const setting = arrived === 'tailnet' ? await readTailscaleSetting() : null;
|
|
572
|
-
const
|
|
780
|
+
const tailscaleRefusal = refused?.provider === 'tailscale' ? refused.refusal : undefined;
|
|
573
781
|
return {
|
|
574
782
|
retry: retryHref(pathname, search),
|
|
575
783
|
arrived,
|
|
576
|
-
tailscaleSignIn: !!setting?.enabled &&
|
|
577
|
-
...(
|
|
578
|
-
...(
|
|
579
|
-
...
|
|
784
|
+
tailscaleSignIn: !!setting?.enabled && tailscaleRefusal?.kind !== 'login',
|
|
785
|
+
...(tailscaleRefusal?.kind === 'login' ? { tailscaleRefusal: tailscaleRefusal.sentence } : {}),
|
|
786
|
+
...(tailscaleRefusal?.kind === 'unanswered' ? { tailscaleUnanswered: true } : {}),
|
|
787
|
+
...locked,
|
|
580
788
|
};
|
|
581
789
|
};
|
|
582
790
|
/**
|
|
@@ -585,7 +793,16 @@ export function createWebApp(deps) {
|
|
|
585
793
|
* loopback page and the tailnet page each keep their own pair. Set, read and
|
|
586
794
|
* checked through this one function.
|
|
587
795
|
*/
|
|
588
|
-
const cookiePort = (req) =>
|
|
796
|
+
const cookiePort = (req) => {
|
|
797
|
+
// Cloudflare's edge serves the page on HTTPS's own port, whatever
|
|
798
|
+
// cloudflared says on the way in; the page names its cookie by it.
|
|
799
|
+
if (arrivalOf(req) === 'ingress') {
|
|
800
|
+
const host = req.headers.host ?? '';
|
|
801
|
+
if (!/:\d+$/.test(host.replace(/^\[[^\]]*\]/, '')))
|
|
802
|
+
return 443;
|
|
803
|
+
}
|
|
804
|
+
return requestPort(req, server.address()?.port ?? deps.config.port, deps.config.publicOrigin);
|
|
805
|
+
};
|
|
589
806
|
/**
|
|
590
807
|
* The origins a write may claim, resolved against the port actually bound.
|
|
591
808
|
*
|
|
@@ -596,8 +813,13 @@ export function createWebApp(deps) {
|
|
|
596
813
|
let originCache;
|
|
597
814
|
const allowed = () => {
|
|
598
815
|
const bound = server.address()?.port ?? deps.config.port;
|
|
599
|
-
|
|
600
|
-
|
|
816
|
+
// The public address the Cloudflare panel stored, while that provider is on.
|
|
817
|
+
const extra = cloudflareSeen?.enabled ? cloudflareSeen.publicOrigin : '';
|
|
818
|
+
if (originCache?.port !== bound || originCache.extra !== extra) {
|
|
819
|
+
const set = new Set(allowedOrigins({ ...deps.config, port: bound }));
|
|
820
|
+
if (extra)
|
|
821
|
+
set.add(extra);
|
|
822
|
+
originCache = { port: bound, extra, set };
|
|
601
823
|
}
|
|
602
824
|
return originCache.set;
|
|
603
825
|
};
|
|
@@ -605,7 +827,7 @@ export function createWebApp(deps) {
|
|
|
605
827
|
// caller that needs to drain it on shutdown (or in a test) can, without
|
|
606
828
|
// `createWebApp` growing a second return value every existing caller would
|
|
607
829
|
// have to unpack.
|
|
608
|
-
|
|
830
|
+
function onRequest(req, res) {
|
|
609
831
|
handle(req, res).catch((err) => {
|
|
610
832
|
// The lock could not say whether this request may pass: refused, never let in (web/lock.ts).
|
|
611
833
|
if (err instanceof LockUnavailable) {
|
|
@@ -622,9 +844,14 @@ export function createWebApp(deps) {
|
|
|
622
844
|
else
|
|
623
845
|
res.end();
|
|
624
846
|
});
|
|
625
|
-
}
|
|
847
|
+
}
|
|
848
|
+
const server = createServer(onRequest);
|
|
626
849
|
if (chat)
|
|
627
850
|
WEB_CHATS.set(server, chat);
|
|
851
|
+
INGRESS_OF.set(server, ingress);
|
|
852
|
+
// The seam the relay (provider 3) hands its in-process requests to,
|
|
853
|
+
// tagged `markRequestArrival(req, 'relay')` first. Never replayed as HTTP to 127.0.0.1.
|
|
854
|
+
DISPATCH_OF.set(server, onRequest);
|
|
628
855
|
PREVIEW_APPS.set(server, previews);
|
|
629
856
|
CLOSING_SAYS.set(server, async () => {
|
|
630
857
|
const said = frame('closing', goingAway === undefined ? {} : { for: goingAway });
|
|
@@ -655,13 +882,25 @@ export function createWebApp(deps) {
|
|
|
655
882
|
// rather than in `startWebServer` so every caller, tests included, has it.
|
|
656
883
|
extension.attach(server);
|
|
657
884
|
extension.attachPath(REMOTE_HAND_SOCKET_PATH, (req, socket, head) => hand.upgrade(req, socket, head));
|
|
658
|
-
server.once('close', () => { extension.shutdown(); hand.shutdown(); clearInterval(idleSweep); });
|
|
885
|
+
server.once('close', () => { extension.shutdown(); hand.shutdown(); clearInterval(idleSweep); clearInterval(ingressResync); setupRun?.abort.abort(); void ingress.close(); });
|
|
886
|
+
server.once('close', () => offGiveBack?.());
|
|
659
887
|
return server;
|
|
660
888
|
async function handle(req, res) {
|
|
661
889
|
const now = deps.now();
|
|
662
890
|
const method = (req.method ?? 'GET').toUpperCase();
|
|
663
891
|
const url = parseUrl(req);
|
|
664
|
-
|
|
892
|
+
/*
|
|
893
|
+
* The bucket failed sign-ins count against (client-key.ts, §7.5): by the
|
|
894
|
+
* arrival path, so a stranger behind a tunnel can never lock out this
|
|
895
|
+
* machine or the tailnet. A request on the ingress listener is asked of
|
|
896
|
+
* its provider at once — a JWT check against cached keys — because its
|
|
897
|
+
* bucket depends on it: `cf:unverified` until it verifies, then the
|
|
898
|
+
* client's own address.
|
|
899
|
+
*/
|
|
900
|
+
let key = access.bucketOf(req) ?? clientKey(req);
|
|
901
|
+
const early = arrivalOf(req) === 'main' ? null : await access.identify(req, now);
|
|
902
|
+
if (early?.result.ok && early.result.identity.bucket)
|
|
903
|
+
key = early.result.identity.bucket;
|
|
665
904
|
// No CORS, and therefore no preflight.
|
|
666
905
|
if (method === 'OPTIONS')
|
|
667
906
|
return sendEmpty(res, 405);
|
|
@@ -763,12 +1002,12 @@ export function createWebApp(deps) {
|
|
|
763
1002
|
const presentedSession = cookies[sessionCookieName(cookiePort(req))];
|
|
764
1003
|
let session = await sessions.resolve(presentedSession, scope, now);
|
|
765
1004
|
/*
|
|
766
|
-
* Why
|
|
767
|
-
* "this
|
|
768
|
-
* says; every other reason is the ordinary "you're
|
|
1005
|
+
* Why a provider gave this request no identity, when it was asked. Only
|
|
1006
|
+
* "this person is not the one allowed" and "could not ask" change what
|
|
1007
|
+
* the signed-out page says; every other reason is the ordinary "you're
|
|
1008
|
+
* signed out".
|
|
769
1009
|
*/
|
|
770
|
-
let refusal;
|
|
771
|
-
const noteRefusal = (reason) => { refusal = reason; };
|
|
1010
|
+
let refused = early && !early.result.ok ? { provider: early.provider.id, refusal: early.result } : undefined;
|
|
772
1011
|
/*
|
|
773
1012
|
* Set when the browser presented a session that is gone: the refusal then
|
|
774
1013
|
* expires its cookies, so it stops presenting them (and stops counting).
|
|
@@ -777,7 +1016,7 @@ export function createWebApp(deps) {
|
|
|
777
1016
|
/*
|
|
778
1017
|
* The answer to a refused request: the signed-out page for a person
|
|
779
1018
|
* opening a page, the empty status for everything else (docs/web.md).
|
|
780
|
-
* 401 is "signed out", 429 "too many tries from here", 503 "
|
|
1019
|
+
* 401 is "signed out", 429 "too many tries from here", 503 "the provider
|
|
781
1020
|
* could not be asked; the session is still good, try again".
|
|
782
1021
|
*/
|
|
783
1022
|
const refuse = async (status = 401) => {
|
|
@@ -788,33 +1027,40 @@ export function createWebApp(deps) {
|
|
|
788
1027
|
headers['Retry-After'] = '5';
|
|
789
1028
|
if (!wantsSignedOutPage(req, method, url.pathname))
|
|
790
1029
|
return sendEmpty(res, status, headers);
|
|
791
|
-
return sendSignedOut(res, await signedOutFor(req, url.pathname, url.search, now,
|
|
1030
|
+
return sendSignedOut(res, await signedOutFor(req, url.pathname, url.search, now, refused, key), headers, status);
|
|
792
1031
|
};
|
|
793
|
-
/* A
|
|
1032
|
+
/* A provider session this request ended: not a guess, so never a failed sign-in. */
|
|
794
1033
|
let revoked = false;
|
|
795
1034
|
/*
|
|
796
|
-
* A
|
|
1035
|
+
* A provider session is re-confirmed on every single request.
|
|
797
1036
|
*
|
|
798
|
-
* A cookie on its own would be a bearer token the
|
|
799
|
-
* loosely related to: it would outlive the setting being turned off,
|
|
800
|
-
* allowed login being changed, and the device being handed to somebody
|
|
801
|
-
* else. So the
|
|
802
|
-
* reused for a minute
|
|
803
|
-
*
|
|
804
|
-
*
|
|
805
|
-
* can only do if it is still the person the owner allowed.
|
|
1037
|
+
* A cookie on its own would be a bearer token the provider's identity is
|
|
1038
|
+
* only loosely related to: it would outlive the setting being turned off,
|
|
1039
|
+
* the allowed login being changed, and the device being handed to somebody
|
|
1040
|
+
* else. So the provider is asked again — Tailscale's daemon (a whois
|
|
1041
|
+
* answer is reused for a minute), Cloudflare's JWT on this very request —
|
|
1042
|
+
* and the person it names now must be the one this session was minted
|
|
1043
|
+
* for. Anything else and the session is gone, not merely ignored.
|
|
806
1044
|
*/
|
|
807
|
-
if (session?.via === '
|
|
808
|
-
const confirmed = await
|
|
809
|
-
if (
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
1045
|
+
if (session?.via === 'provider') {
|
|
1046
|
+
const confirmed = await access.confirm(session, req, now);
|
|
1047
|
+
if (confirmed.refusal)
|
|
1048
|
+
refused = { provider: session.provider, refusal: confirmed.refusal };
|
|
1049
|
+
/*
|
|
1050
|
+
* The provider could not be asked (a daemon busy or starting, keys that
|
|
1051
|
+
* could not be fetched). That is not an answer about anyone, so the
|
|
1052
|
+
* session is not ended for it and nothing is counted. This request waits.
|
|
1053
|
+
*/
|
|
1054
|
+
if (confirmed.answer === 'unanswered')
|
|
1055
|
+
return refuse(503);
|
|
1056
|
+
/*
|
|
1057
|
+
* This request lacks the provider's proof (Cloudflare's header), but
|
|
1058
|
+
* the session is not ended for it: a stray request without the header
|
|
1059
|
+
* must not sign the owner out. Nothing is counted either.
|
|
1060
|
+
*/
|
|
1061
|
+
if (confirmed.answer === 'refuse')
|
|
1062
|
+
return refuse(401);
|
|
1063
|
+
if (confirmed.answer === 'end') {
|
|
818
1064
|
const ended = session.id;
|
|
819
1065
|
// A hand this session was holding does not outlive the session.
|
|
820
1066
|
hand.revoke((lease) => lease === ended);
|
|
@@ -823,7 +1069,7 @@ export function createWebApp(deps) {
|
|
|
823
1069
|
revoked = true;
|
|
824
1070
|
forgetCookies = true;
|
|
825
1071
|
// The identity on this very request may still earn a new session below.
|
|
826
|
-
|
|
1072
|
+
refused = undefined;
|
|
827
1073
|
}
|
|
828
1074
|
}
|
|
829
1075
|
/*
|
|
@@ -848,26 +1094,32 @@ export function createWebApp(deps) {
|
|
|
848
1094
|
res.setHeader('Set-Cookie', sessionCookies(req, session));
|
|
849
1095
|
}
|
|
850
1096
|
/*
|
|
851
|
-
* Signed in through
|
|
1097
|
+
* Signed in through a trusted access provider.
|
|
852
1098
|
*
|
|
853
|
-
*
|
|
854
|
-
*
|
|
855
|
-
*
|
|
856
|
-
*
|
|
857
|
-
*
|
|
858
|
-
*
|
|
859
|
-
*
|
|
1099
|
+
* Tailscale: the proxy runs on this machine and the daemon confirms who
|
|
1100
|
+
* is behind the forwarded address. Cloudflare Access: the JWT Access
|
|
1101
|
+
* signed verifies against the team's keys, for this application, naming
|
|
1102
|
+
* the allowed email, and it arrived on the ingress listener. When that
|
|
1103
|
+
* agrees with the person the owner allowed, the request gets a `remote`
|
|
1104
|
+
* session exactly as the ticket exchange would have minted one — same
|
|
1105
|
+
* cookies, same 12 hours, same CSRF on every write. Anything less falls
|
|
1106
|
+
* through to the 401 below. A provider identity signs in during a lockout.
|
|
860
1107
|
*/
|
|
861
1108
|
if (!session) {
|
|
862
|
-
const
|
|
863
|
-
if (
|
|
1109
|
+
const asked = early ?? await access.identify(req, now);
|
|
1110
|
+
if (asked && !asked.result.ok)
|
|
1111
|
+
refused = { provider: asked.provider.id, refusal: asked.result };
|
|
1112
|
+
if (asked?.result.ok) {
|
|
1113
|
+
const identity = asked.result.identity;
|
|
864
1114
|
limiter.reset(key);
|
|
865
1115
|
const locked = await lock.startLocked('browser');
|
|
1116
|
+
const capMs = Math.min(asked.provider.absoluteCapMs, identity.expiresAt ? Math.max(0, identity.expiresAt.getTime() - now.getTime()) : asked.provider.absoluteCapMs);
|
|
866
1117
|
session = sessions.create('remote', now, {
|
|
867
|
-
via: '
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
1118
|
+
via: 'provider',
|
|
1119
|
+
provider: identity.provider,
|
|
1120
|
+
providerSubject: identity.subject,
|
|
1121
|
+
...(identity.detail ? { providerDetail: identity.detail } : {}),
|
|
1122
|
+
absoluteCapMs: capMs,
|
|
871
1123
|
}, locked ? { locked } : {});
|
|
872
1124
|
res.setHeader('Set-Cookie', sessionCookies(req, session));
|
|
873
1125
|
}
|
|
@@ -882,14 +1134,26 @@ export function createWebApp(deps) {
|
|
|
882
1134
|
// the same stale cookie counts once however often it is sent: all tailnet
|
|
883
1135
|
// and tunnel traffic shares 127.0.0.1, so a forgotten tab must not be
|
|
884
1136
|
// able to lock the owner out of every way in.
|
|
885
|
-
|
|
1137
|
+
//
|
|
1138
|
+
// A provider's credential that failed (a Cloudflare assertion that does
|
|
1139
|
+
// not verify) is an attempt too, cookie or not, in the bucket the
|
|
1140
|
+
// provider names (per Cf-Connecting-Ip). No assertion at all, or keys
|
|
1141
|
+
// that could not be fetched, carry no attempt.
|
|
1142
|
+
const tried = refused?.refusal.attempt;
|
|
1143
|
+
if (!presentedSession && !tried)
|
|
886
1144
|
return refuse();
|
|
887
|
-
|
|
1145
|
+
if (tried)
|
|
1146
|
+
key = tried.bucket;
|
|
1147
|
+
if (presentedSession)
|
|
1148
|
+
forgetCookies = true;
|
|
888
1149
|
if (revoked)
|
|
889
1150
|
return refuse();
|
|
890
1151
|
if (limiter.blocked(key, now))
|
|
891
1152
|
return refuse(429);
|
|
892
|
-
|
|
1153
|
+
if (tried)
|
|
1154
|
+
limiter.failCredential(key, tried.credential, now);
|
|
1155
|
+
if (presentedSession)
|
|
1156
|
+
limiter.failCredential(key, presentedSession, now);
|
|
893
1157
|
return refuse();
|
|
894
1158
|
}
|
|
895
1159
|
/*
|
|
@@ -964,7 +1228,7 @@ export function createWebApp(deps) {
|
|
|
964
1228
|
agentIds: () => deps.catalog.list().map((a) => a.id),
|
|
965
1229
|
});
|
|
966
1230
|
/** The Keys and secrets page (docs/owner-secrets.md §6): core's own queries and ownerOnly tools. */
|
|
967
|
-
const secretsDeps = () => ({ pool: deps.pool, registry: deps.registry, ctx: deps.ctx, now: deps.now });
|
|
1231
|
+
const secretsDeps = () => ({ pool: deps.pool, registry: deps.registry, ctx: deps.ctx, now: deps.now, ...(browser.logins ? { logins: browser.logins } : {}) });
|
|
968
1232
|
/** Talking to buddi on the dashboard: the speech plugin's two tools, as the owner. */
|
|
969
1233
|
const speechDeps = () => ({ pool: deps.pool, registry: deps.registry, ctx: deps.ctx, now: deps.now, agents: () => deps.catalog.list() });
|
|
970
1234
|
/** The same, for the version and upgrade routes. */
|
|
@@ -1088,6 +1352,10 @@ export function createWebApp(deps) {
|
|
|
1088
1352
|
now: deps.now,
|
|
1089
1353
|
agents: () => deps.catalog.list(),
|
|
1090
1354
|
plugins: () => deps.registry.manifests().map((m) => m.name),
|
|
1355
|
+
appsWithoutPlugin: () => {
|
|
1356
|
+
const apps = browser.status().routes?.find((route) => route.kind === 'apps');
|
|
1357
|
+
return apps !== undefined && apps.installed === false && apps.mode !== undefined && apps.mode !== 'off';
|
|
1358
|
+
},
|
|
1091
1359
|
needsSetup: async () => {
|
|
1092
1360
|
const out = [];
|
|
1093
1361
|
for (const manifest of deps.registry.manifests()) {
|
|
@@ -1387,15 +1655,16 @@ export function createWebApp(deps) {
|
|
|
1387
1655
|
const permissions = (await listToolPermissions(deps.pool, deps.ctx.ownerId)).filter(p => p.tool === 'host.exec' && (!agentId || p.agentId === agentId) && (!conversationId || !p.conversationId || p.conversationId === conversationId));
|
|
1388
1656
|
return sendJson(res, 200, { permissions, runs: host.runs(deps.ctx.ownerId, agentId, conversationId) });
|
|
1389
1657
|
}
|
|
1390
|
-
case '/api/host/apps':
|
|
1391
|
-
return sendJson(res, 200, { apps: await listInstalledApps() });
|
|
1392
|
-
case '/api/host/browser-profiles':
|
|
1393
|
-
return sendJson(res, 200, { profiles: await listBrowserProfiles(q.get('app') ?? '') });
|
|
1394
1658
|
case '/api/extension':
|
|
1395
1659
|
return sendJson(res, 200, await extension.view());
|
|
1396
1660
|
case '/api/browser':
|
|
1397
1661
|
return sendJson(res, 200, q.has('conversationId') && q.has('agentId')
|
|
1398
1662
|
? browser.status({ agentId: q.get('agentId'), conversationId: q.get('conversationId') }) : browser.status());
|
|
1663
|
+
case '/api/browser/telemetry': {
|
|
1664
|
+
// Stops by cause, cards and routes over the last week: how flakiness is seen to fall.
|
|
1665
|
+
const summary = browser.telemetrySummary?.(Number(q.get('days') ?? '7') || 7);
|
|
1666
|
+
return sendJson(res, 200, summary ?? { days: 7, tasks: 0, stops: 0, cards: 0, byCause: [], routes: {}, stopsPerTask: 0 });
|
|
1667
|
+
}
|
|
1399
1668
|
case '/api/browser/screenshot': {
|
|
1400
1669
|
const expectedSession = url.searchParams.get('sessionId');
|
|
1401
1670
|
const current = browser.status(expectedSession !== null ? { sessionId: expectedSession } : undefined);
|
|
@@ -1434,10 +1703,14 @@ export function createWebApp(deps) {
|
|
|
1434
1703
|
// from the page rather than implied by a number in a source file.
|
|
1435
1704
|
scope: session.scope,
|
|
1436
1705
|
// How this browser got in. `local` is the open loopback mint,
|
|
1437
|
-
// `ticket` the five-minute ticket exchange,
|
|
1438
|
-
//
|
|
1439
|
-
|
|
1440
|
-
|
|
1706
|
+
// `ticket` the five-minute ticket exchange, or the provider that
|
|
1707
|
+
// verified it (`tailscale`, `cloudflare-access`) — and then the
|
|
1708
|
+
// name to greet.
|
|
1709
|
+
signedInThrough: session.via === 'provider' ? session.provider : session.via,
|
|
1710
|
+
...(session.via === 'provider' ? { provider: session.provider, providerSubject: session.providerSubject } : {}),
|
|
1711
|
+
...(session.via === 'provider' && session.provider === 'tailscale'
|
|
1712
|
+
? { tailscaleName: session.providerDetail?.name ?? session.providerSubject, tailscaleLogin: session.providerSubject }
|
|
1713
|
+
: {}),
|
|
1441
1714
|
expiresAt: session.expiresAt.toISOString(),
|
|
1442
1715
|
// What this gateway is running. The page keeps it across an
|
|
1443
1716
|
// upgrade so that "it came back" can be told from "it is still
|
|
@@ -1452,20 +1725,24 @@ export function createWebApp(deps) {
|
|
|
1452
1725
|
* memory), and whether this very request came through the proxy —
|
|
1453
1726
|
* which is what disables the switch on a tailnet browser.
|
|
1454
1727
|
*/
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
return sendJson(res, 200,
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1728
|
+
// `/api/tailscale` stays one release as an alias (docs/api.md).
|
|
1729
|
+
case '/api/tailscale':
|
|
1730
|
+
case '/api/access/tailscale':
|
|
1731
|
+
return sendJson(res, 200, await tailscaleView(req, session));
|
|
1732
|
+
/*
|
|
1733
|
+
* "Sign in from elsewhere": every provider's row — its status in one
|
|
1734
|
+
* line — and whether this very request came through one, which makes
|
|
1735
|
+
* the whole block read-only.
|
|
1736
|
+
*/
|
|
1737
|
+
case '/api/access':
|
|
1738
|
+
return sendJson(res, 200, await accessView(req, session));
|
|
1739
|
+
case '/api/access/cloudflare-access':
|
|
1740
|
+
return sendJson(res, 200, await cloudflareView(req, session));
|
|
1741
|
+
case '/api/access/cloudflare-access/setup':
|
|
1742
|
+
// The install line holds the tunnel's connector token: this machine only.
|
|
1743
|
+
if (session.via !== 'local')
|
|
1744
|
+
return sendJson(res, 403, { error: 'Change this from the computer buddi runs on.' });
|
|
1745
|
+
return sendJson(res, 200, await setupView());
|
|
1469
1746
|
case '/api/overview':
|
|
1470
1747
|
return sendJson(res, 200, {
|
|
1471
1748
|
...(await readOverview({
|
|
@@ -2225,7 +2502,7 @@ export function createWebApp(deps) {
|
|
|
2225
2502
|
* everything else.
|
|
2226
2503
|
*/
|
|
2227
2504
|
if (method === 'PUT') {
|
|
2228
|
-
const puttable = ['/api/backups/schedule', '/api/backups/passphrase', '/api/version/check', '/api/tailscale', '/api/notifications/settings', '/api/notifications/focus'];
|
|
2505
|
+
const puttable = ['/api/backups/schedule', '/api/backups/passphrase', '/api/version/check', '/api/tailscale', '/api/access/tailscale', '/api/access/cloudflare-access', '/api/notifications/settings', '/api/notifications/focus'];
|
|
2229
2506
|
if (!puttable.includes(path))
|
|
2230
2507
|
return sendEmpty(res, 405);
|
|
2231
2508
|
let put;
|
|
@@ -2235,7 +2512,20 @@ export function createWebApp(deps) {
|
|
|
2235
2512
|
catch {
|
|
2236
2513
|
return sendJson(res, 400, { error: 'request body must be JSON' });
|
|
2237
2514
|
}
|
|
2238
|
-
if (path === '/api/
|
|
2515
|
+
if (path === '/api/access/cloudflare-access') {
|
|
2516
|
+
// Who may widen access: only a browser on this machine, as for Tailscale below.
|
|
2517
|
+
if (session.via !== 'local') {
|
|
2518
|
+
return sendJson(res, 403, { error: 'Change this from the computer buddi runs on.' });
|
|
2519
|
+
}
|
|
2520
|
+
const checked = validateCloudflareInput(put);
|
|
2521
|
+
if (!checked.ok)
|
|
2522
|
+
return sendJson(res, 400, { error: checked.error });
|
|
2523
|
+
await applyCloudflareSetting(checked.value);
|
|
2524
|
+
// On Save buddi fetches the team's keys once and says whether it worked.
|
|
2525
|
+
const test = checked.value.enabled ? await jwks.refresh(checked.value.teamDomain) : null;
|
|
2526
|
+
return sendJson(res, 200, { ...(await cloudflareView(req, session)), ...(test ? { test } : {}) });
|
|
2527
|
+
}
|
|
2528
|
+
if (path === '/api/tailscale' || path === '/api/access/tailscale') {
|
|
2239
2529
|
/*
|
|
2240
2530
|
* Who may widen access: only a browser that is already on this
|
|
2241
2531
|
* machine.
|
|
@@ -2272,20 +2562,11 @@ export function createWebApp(deps) {
|
|
|
2272
2562
|
* in a browser on the far side of the tailnet.
|
|
2273
2563
|
*/
|
|
2274
2564
|
const forgotten = new Set();
|
|
2275
|
-
await sessions.forget((s) => { const drop = s.via === 'tailscale'; if (drop && s.id)
|
|
2565
|
+
await sessions.forget((s) => { const drop = s.via === 'provider' && s.provider === 'tailscale'; if (drop && s.id)
|
|
2276
2566
|
forgotten.add(s.id); return drop; });
|
|
2277
2567
|
// Including whichever of them had a hand on the owner's browser.
|
|
2278
2568
|
hand.revoke((lease) => forgotten.has(lease), 'Tailscale access changed. Sign in again.');
|
|
2279
|
-
|
|
2280
|
-
const stored = toTailscaleSetting({ enabled, login });
|
|
2281
|
-
return sendJson(res, 200, {
|
|
2282
|
-
enabled: stored.enabled,
|
|
2283
|
-
login: stored.login,
|
|
2284
|
-
available: daemon.available,
|
|
2285
|
-
self: daemon.self,
|
|
2286
|
-
proxied: false,
|
|
2287
|
-
serveCommand: serveCommand(),
|
|
2288
|
-
});
|
|
2569
|
+
return sendJson(res, 200, await tailscaleView(req, session));
|
|
2289
2570
|
}
|
|
2290
2571
|
if (path === '/api/version/check')
|
|
2291
2572
|
return reply(res, await versionCheckRoute(versionDeps(), 'PUT', put));
|
|
@@ -2299,6 +2580,155 @@ export function createWebApp(deps) {
|
|
|
2299
2580
|
}
|
|
2300
2581
|
if (method !== 'POST')
|
|
2301
2582
|
return sendEmpty(res, 405);
|
|
2583
|
+
/*
|
|
2584
|
+
* "Test my setup": fetch the team's signing keys for the domain in the
|
|
2585
|
+
* form (saved or not) and say what came back. Nothing is stored. From
|
|
2586
|
+
* this machine only, like the setting itself: it makes this computer
|
|
2587
|
+
* fetch a URL the caller names part of.
|
|
2588
|
+
*/
|
|
2589
|
+
/*
|
|
2590
|
+
* "Set it up for me": start a run (202, then poll GET …/setup), stop the
|
|
2591
|
+
* health wait, remove what buddi made, or forget the kept API token. From
|
|
2592
|
+
* this machine only, like the setting it fills in.
|
|
2593
|
+
*/
|
|
2594
|
+
if (path === '/api/access/cloudflare-access/setup' || path === '/api/access/cloudflare-access/setup/stop' || path === '/api/access/cloudflare-access/setup/remove' || path === '/api/access/cloudflare-access/setup/forget-token') {
|
|
2595
|
+
if (session.via !== 'local')
|
|
2596
|
+
return sendJson(res, 403, { error: 'Change this from the computer buddi runs on.' });
|
|
2597
|
+
/*
|
|
2598
|
+
* Forget the token: drop the owner secret. Not while a run holds it.
|
|
2599
|
+
* Cloudflare still honours it until the owner revokes it there.
|
|
2600
|
+
*/
|
|
2601
|
+
if (path.endsWith('/forget-token')) {
|
|
2602
|
+
const lease = claimSetupOperation('setup');
|
|
2603
|
+
if (!lease)
|
|
2604
|
+
return sendJson(res, 409, { error: setupBusySentence(setupOperation()) });
|
|
2605
|
+
try {
|
|
2606
|
+
try {
|
|
2607
|
+
await tokenStore().remove();
|
|
2608
|
+
}
|
|
2609
|
+
catch {
|
|
2610
|
+
return sendJson(res, 409, { error: 'The token could not be forgotten. Check that the vault is unlocked, then try again.' });
|
|
2611
|
+
}
|
|
2612
|
+
return sendJson(res, 200, await setupView());
|
|
2613
|
+
}
|
|
2614
|
+
finally {
|
|
2615
|
+
lease.release();
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
/*
|
|
2619
|
+
* One operation at a time (cloudflare-setup's lock, claimed before the
|
|
2620
|
+
* first await and held to the end): a second setup or a removal while
|
|
2621
|
+
* one runs is a 409, and so is Stop while a removal runs.
|
|
2622
|
+
*/
|
|
2623
|
+
if (path.endsWith('/stop')) {
|
|
2624
|
+
const going = setupOperation();
|
|
2625
|
+
if (going === 'remove')
|
|
2626
|
+
return sendJson(res, 409, { error: setupBusySentence(going) });
|
|
2627
|
+
const lease = going === null ? claimSetupOperation('setup') : null;
|
|
2628
|
+
try {
|
|
2629
|
+
setupRun?.abort.abort();
|
|
2630
|
+
await setupRun?.done;
|
|
2631
|
+
return sendJson(res, 200, await setupView());
|
|
2632
|
+
}
|
|
2633
|
+
finally {
|
|
2634
|
+
lease?.release();
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
const lease = claimSetupOperation(path.endsWith('/remove') ? 'remove' : 'setup');
|
|
2638
|
+
if (!lease)
|
|
2639
|
+
return sendJson(res, 409, { error: setupBusySentence(setupOperation()) });
|
|
2640
|
+
let handedOff = false;
|
|
2641
|
+
try {
|
|
2642
|
+
const body = await readJsonBody(req).catch(() => ({}));
|
|
2643
|
+
const str = (v) => (typeof v === 'string' ? v.trim() : '');
|
|
2644
|
+
const tokens = tokenStore();
|
|
2645
|
+
const pasted = str(body.token);
|
|
2646
|
+
if (path.endsWith('/remove')) {
|
|
2647
|
+
const token = pasted || await tokens.use().catch(() => null);
|
|
2648
|
+
if (!token)
|
|
2649
|
+
return sendJson(res, 400, { error: 'Paste the Cloudflare API token again: buddi needs it to remove what it made.' });
|
|
2650
|
+
const run = {
|
|
2651
|
+
progress: { ...freshProgress(), state: 'removing', steps: [] }, abort: new AbortController(), done: Promise.resolve(),
|
|
2652
|
+
};
|
|
2653
|
+
setupRun = run;
|
|
2654
|
+
const progress = await removeCloudflareSetup({ ...setupDepsFor(token, lease), host: str(body.host) || undefined }, (p) => { run.progress = p; });
|
|
2655
|
+
run.progress = progress;
|
|
2656
|
+
// The token stays kept: the panel offers to forget it (…/setup/forget-token), and Set it up again reuses it.
|
|
2657
|
+
return sendJson(res, 200, await setupView());
|
|
2658
|
+
}
|
|
2659
|
+
const input = { host: str(body.host), email: str(body.email), zone: str(body.zone) || undefined, adopt: body.adopt === true };
|
|
2660
|
+
const invalid = checkSetupInput(input);
|
|
2661
|
+
if (invalid)
|
|
2662
|
+
return sendJson(res, 400, { error: invalid });
|
|
2663
|
+
if (pasted) {
|
|
2664
|
+
if (/\s/.test(pasted) || pasted.length < 20 || pasted.length > 400)
|
|
2665
|
+
return sendJson(res, 400, { error: 'That doesn’t look like a Cloudflare API token.' });
|
|
2666
|
+
try {
|
|
2667
|
+
await tokens.put(pasted);
|
|
2668
|
+
}
|
|
2669
|
+
catch {
|
|
2670
|
+
return sendJson(res, 409, { error: 'The token could not be kept in the vault. Check that the vault is unlocked, then try again.' });
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
const token = pasted || await tokens.use().catch(() => null);
|
|
2674
|
+
if (!token)
|
|
2675
|
+
return sendJson(res, 400, { error: 'Paste a Cloudflare API token.' });
|
|
2676
|
+
const first = { ...freshProgress(input.host, input.email), state: 'running' };
|
|
2677
|
+
startSetup(lease, (onProgress, signal) => runCloudflareSetup(input, setupDepsFor(token, lease, signal), onProgress), first);
|
|
2678
|
+
handedOff = true;
|
|
2679
|
+
return sendJson(res, 202, await setupView());
|
|
2680
|
+
}
|
|
2681
|
+
finally {
|
|
2682
|
+
if (!handedOff)
|
|
2683
|
+
lease.release();
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
/*
|
|
2687
|
+
* The setup form's domain choice: check the token and list the zones it
|
|
2688
|
+
* can see. The pasted token is only used for these two calls — keeping it
|
|
2689
|
+
* stays with setup — and never goes back out, not even in an error.
|
|
2690
|
+
*/
|
|
2691
|
+
if (path === '/api/access/cloudflare-access/zones') {
|
|
2692
|
+
if (session.via !== 'local')
|
|
2693
|
+
return sendJson(res, 403, { error: 'Change this from the computer buddi runs on.' });
|
|
2694
|
+
const body = await readJsonBody(req).catch(() => ({}));
|
|
2695
|
+
const pasted = typeof body?.token === 'string' ? body.token.trim() : '';
|
|
2696
|
+
if (pasted && (/\s/.test(pasted) || pasted.length < 20 || pasted.length > 400))
|
|
2697
|
+
return sendJson(res, 400, { error: 'That doesn’t look like a Cloudflare API token.' });
|
|
2698
|
+
const token = pasted || await tokenStore().use().catch(() => null);
|
|
2699
|
+
if (!token)
|
|
2700
|
+
return sendJson(res, 400, { error: 'Paste a Cloudflare API token.' });
|
|
2701
|
+
const api = createCloudflareApi({ token, transport: deps.cloudflare?.api?.transport, baseUrl: deps.cloudflare?.api?.baseUrl });
|
|
2702
|
+
try {
|
|
2703
|
+
if (await api.verifyToken() === 'inactive')
|
|
2704
|
+
return sendJson(res, 400, { error: 'Cloudflare says this API token is not active. Check that it has not expired or been turned off.' });
|
|
2705
|
+
const zones = await api.zones();
|
|
2706
|
+
return sendJson(res, 200, { zones: zones.map((z) => ({ id: z.id, name: z.name })) });
|
|
2707
|
+
}
|
|
2708
|
+
catch (error) {
|
|
2709
|
+
if (!(error instanceof CloudflareApiError))
|
|
2710
|
+
throw error;
|
|
2711
|
+
const refused = error.status === 401 || error.permission !== undefined || (error.status >= 400 && error.status < 500 && error.status !== 429);
|
|
2712
|
+
return sendJson(res, refused ? 400 : 502, { error: error.message });
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
if (path === '/api/access/cloudflare-access/test') {
|
|
2716
|
+
if (session.via !== 'local')
|
|
2717
|
+
return sendJson(res, 403, { error: 'Change this from the computer buddi runs on.' });
|
|
2718
|
+
const body = await readJsonBody(req).catch(() => ({}));
|
|
2719
|
+
const team = normalizeTeamDomain(typeof body.teamDomain === 'string' ? body.teamDomain : (await readCloudflareSetting()).teamDomain);
|
|
2720
|
+
if (!plausibleTeamDomain(team)) {
|
|
2721
|
+
return sendJson(res, 400, { error: 'That is not a Cloudflare team domain. It looks like yourteam.cloudflareaccess.com, under Zero Trust → Settings.' });
|
|
2722
|
+
}
|
|
2723
|
+
const result = await jwks.refresh(team);
|
|
2724
|
+
return sendJson(res, 200, {
|
|
2725
|
+
...result,
|
|
2726
|
+
teamDomain: team,
|
|
2727
|
+
sentence: result.ok ? `${team} answered with ${result.keys} signing key${result.keys === 1 ? '' : 's'}.` : result.error,
|
|
2728
|
+
listening: ingress.port() !== null,
|
|
2729
|
+
ingressPort: ingress.port(),
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2302
2732
|
if (path === '/api/extension/pair') {
|
|
2303
2733
|
// A six-digit code is worth guessing at scale, so a wrong one costs a
|
|
2304
2734
|
// budget — this session's pairing budget, not the shared sign-in one.
|
|
@@ -2345,24 +2775,71 @@ export function createWebApp(deps) {
|
|
|
2345
2775
|
return sendJson(res, 200, { ok: false, message: error instanceof Error ? error.message : String(error) });
|
|
2346
2776
|
}
|
|
2347
2777
|
}
|
|
2348
|
-
if (path === '/api/browser/settings'
|
|
2778
|
+
if (path === '/api/browser/settings') {
|
|
2349
2779
|
const body = await readJsonBody(req);
|
|
2350
2780
|
try {
|
|
2351
|
-
if (
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
return sendJson(res, 200, await browser.configure(body));
|
|
2355
|
-
}
|
|
2356
|
-
if (!body || typeof body !== 'object' || Array.isArray(body) || Object.keys(body).some((key) => key !== 'prompt') || ('prompt' in body && typeof body.prompt !== 'boolean'))
|
|
2357
|
-
return sendJson(res, 400, { error: 'Expected {prompt: boolean}' });
|
|
2358
|
-
if (!browser.checkPermissions)
|
|
2359
|
-
return sendJson(res, 409, { error: 'This host does not support native permission checks.' });
|
|
2360
|
-
return sendJson(res, 200, await browser.checkPermissions(body.prompt === true));
|
|
2781
|
+
if (!browser.configure)
|
|
2782
|
+
return sendJson(res, 409, { error: 'This host does not support changing control modes.' });
|
|
2783
|
+
return sendJson(res, 200, await browser.configure(body));
|
|
2361
2784
|
}
|
|
2362
2785
|
catch (error) {
|
|
2363
2786
|
return sendJson(res, 409, { error: error instanceof Error ? error.message : String(error) });
|
|
2364
2787
|
}
|
|
2365
2788
|
}
|
|
2789
|
+
/*
|
|
2790
|
+
* Pin one conversation to a route (the chip under the composer,
|
|
2791
|
+
* `/use browser:chrome`), or clear it with `auto`. A pin narrows; it never
|
|
2792
|
+
* allows a route the owner's switches forbid.
|
|
2793
|
+
*/
|
|
2794
|
+
if (path === '/api/browser/pin') {
|
|
2795
|
+
const body = await readJsonBody(req);
|
|
2796
|
+
if (typeof body?.conversationId !== 'string' || typeof body.route !== 'string')
|
|
2797
|
+
return sendJson(res, 400, { error: 'Expected {conversationId: string, route: auto|own|chrome|apps}' });
|
|
2798
|
+
if (!browser.pin)
|
|
2799
|
+
return sendJson(res, 409, { error: 'This host cannot pin a route.' });
|
|
2800
|
+
try {
|
|
2801
|
+
return sendJson(res, 200, await browser.pin(body.conversationId, body.route));
|
|
2802
|
+
}
|
|
2803
|
+
catch (error) {
|
|
2804
|
+
return sendJson(res, 400, { error: error instanceof Error ? error.message : String(error) });
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
/*
|
|
2808
|
+
* A tap on a browser card (Look, Keep going, Take over, Use my Chrome,
|
|
2809
|
+
* Resume) from a page that draws its own buttons: the same as the owner
|
|
2810
|
+
* sending the label in the chat, without a message.
|
|
2811
|
+
*/
|
|
2812
|
+
if (path === '/api/browser/card') {
|
|
2813
|
+
const body = await readJsonBody(req);
|
|
2814
|
+
if (typeof body?.conversationId !== 'string' || typeof body.answer !== 'string' || body.answer.length > 80)
|
|
2815
|
+
return sendJson(res, 400, { error: 'Expected {conversationId: string, answer: string}' });
|
|
2816
|
+
const answered = await browser.touch?.({ conversationId: body.conversationId, text: body.answer });
|
|
2817
|
+
return sendJson(res, 200, { ...(answered && 'answered' in answered ? answered : {}), status: browser.status({ conversationId: body.conversationId }) });
|
|
2818
|
+
}
|
|
2819
|
+
/*
|
|
2820
|
+
* "Save this login for amazon.com?" answered in the Page tab: Save, Not
|
|
2821
|
+
* now or Never for this site. The owner's session and CSRF, like every
|
|
2822
|
+
* write here; the body names the question, never a password — that is in
|
|
2823
|
+
* the browser host's keeper, which hands it to the owner-secret store on
|
|
2824
|
+
* Save and drops it otherwise.
|
|
2825
|
+
*/
|
|
2826
|
+
if (path === '/api/browser/login') {
|
|
2827
|
+
const body = await readJsonBody(req);
|
|
2828
|
+
const decision = body?.decision;
|
|
2829
|
+
if (typeof body?.id !== 'string' || body.id.length > 80 || (decision !== 'save' && decision !== 'later' && decision !== 'never')) {
|
|
2830
|
+
return sendJson(res, 400, { error: 'Expected {id: string, decision: save|later|never}' });
|
|
2831
|
+
}
|
|
2832
|
+
if (!browser.logins)
|
|
2833
|
+
return sendJson(res, 409, { error: 'This host keeps no sign-ins.' });
|
|
2834
|
+
try {
|
|
2835
|
+
const answer = await browser.logins.decide(body.id, decision);
|
|
2836
|
+
return sendJson(res, 200, { outcome: answer.outcome, ...(answer.saved ? { saved: { name: answer.saved.name, site: answer.saved.site, username: answer.saved.username, savedAt: answer.saved.savedAt } } : {}) });
|
|
2837
|
+
}
|
|
2838
|
+
catch {
|
|
2839
|
+
// Never the error's own words: they are the store's, and the store had the password.
|
|
2840
|
+
return sendJson(res, 409, { error: 'buddi could not keep that login. Add it in Settings → Keys and secrets.' });
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2366
2843
|
const control = /^\/api\/browser\/(stop|takeover|resume|release)$/.exec(path);
|
|
2367
2844
|
if (control) {
|
|
2368
2845
|
const body = await readJsonBody(req);
|
|
@@ -2377,10 +2854,14 @@ export function createWebApp(deps) {
|
|
|
2377
2854
|
// frozen picture.
|
|
2378
2855
|
if (action !== 'takeover')
|
|
2379
2856
|
await hand.close(action === 'stop' ? undefined : sessionId, action === 'resume' ? 'You gave the screen back.' : 'That session was released.');
|
|
2380
|
-
|
|
2857
|
+
// Stop agents' browsing expires (an hour by default); `forever` is "until I say".
|
|
2858
|
+
const status = await browser.control(action, sessionId, action === 'stop' && body?.forever === true ? { forever: true } : undefined);
|
|
2381
2859
|
// Whether this screen can be driven from here at all. The dashboard
|
|
2382
2860
|
// shows the mode's own sentence when it cannot.
|
|
2383
2861
|
const taken = sessionId ?? status.session?.id;
|
|
2862
|
+
// A page in the owner's Chrome is taken over where it is: its tab came to the front there, so no frame will come.
|
|
2863
|
+
if (action === 'takeover' && status.held)
|
|
2864
|
+
return sendJson(res, 200, { ...status, hand: false });
|
|
2384
2865
|
const offer = action === 'takeover' && taken ? browser.hand?.({ sessionId: taken }) : undefined;
|
|
2385
2866
|
/*
|
|
2386
2867
|
* "Your browser" with Chrome closed: the take-over itself succeeds (the
|
|
@@ -2388,7 +2869,7 @@ export function createWebApp(deps) {
|
|
|
2388
2869
|
* as that, with a reason the dashboard offers its ways out on, rather
|
|
2389
2870
|
* than the mode's "use this conversation's host tab".
|
|
2390
2871
|
*/
|
|
2391
|
-
if (offer && offer.supported && !offer.hand && status.mode === 'extension' && !extension.connected()) {
|
|
2872
|
+
if (offer && offer.supported && !offer.hand && (status.route === 'chrome' || status.mode === 'extension') && !extension.connected()) {
|
|
2392
2873
|
return sendJson(res, 200, { ...status, hand: false, handReason: 'browser-offline', handMessage: 'Your browser isn’t connected.' });
|
|
2393
2874
|
}
|
|
2394
2875
|
return sendJson(res, 200, offer
|
|
@@ -3724,6 +4205,10 @@ export async function startWebServer(deps) {
|
|
|
3724
4205
|
*/
|
|
3725
4206
|
if (previewPort !== null)
|
|
3726
4207
|
publishPreviewPort(deps.env ?? process.env, previewPort);
|
|
4208
|
+
// The ingress listener, after the previews have taken their port: bound
|
|
4209
|
+
// now when Cloudflare Access is on, and from then on as the setting changes.
|
|
4210
|
+
const ingress = ingressOf(server);
|
|
4211
|
+
await ingress.sync();
|
|
3727
4212
|
/*
|
|
3728
4213
|
* An approval nobody decides expires, and a delegation waiting on one is
|
|
3729
4214
|
* handed the expiry as its failure: the approval's own lifetime is the
|
|
@@ -3741,6 +4226,7 @@ export async function startWebServer(deps) {
|
|
|
3741
4226
|
server,
|
|
3742
4227
|
port,
|
|
3743
4228
|
previewPort,
|
|
4229
|
+
ingress,
|
|
3744
4230
|
url: webUrl({ host: deps.config.host, port }),
|
|
3745
4231
|
chat: webChatOf(server),
|
|
3746
4232
|
close: async () => {
|
|
@@ -3752,6 +4238,7 @@ export async function startWebServer(deps) {
|
|
|
3752
4238
|
// still held, and the next thing to want it — the next test, the
|
|
3753
4239
|
// gateway coming back up — finds it taken.
|
|
3754
4240
|
await Promise.all([
|
|
4241
|
+
ingress.close(),
|
|
3755
4242
|
new Promise((resolve) => {
|
|
3756
4243
|
if (!previews || previewPort === null)
|
|
3757
4244
|
return resolve();
|