@vellumai/assistant 0.6.1 → 0.6.3
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/bun.lock +40 -40
- package/bunfig.toml +3 -0
- package/docker-entrypoint.sh +12 -2
- package/docs/architecture/memory.md +1 -1
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
- package/openapi.yaml +184 -69
- package/package.json +41 -41
- package/scripts/generate-openapi.ts +1 -2
- package/src/__tests__/acp-session.test.ts +43 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +1 -0
- package/src/__tests__/app-source-watcher.test.ts +37 -11
- package/src/__tests__/approval-routes-http.test.ts +178 -1
- package/src/__tests__/assistant-event-hub.test.ts +30 -0
- package/src/__tests__/browser-fill-credential.test.ts +229 -94
- package/src/__tests__/browser-manager.test.ts +40 -27
- package/src/__tests__/catalog-files.test.ts +862 -0
- package/src/__tests__/channel-approvals.test.ts +53 -0
- package/src/__tests__/checker.test.ts +104 -170
- package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
- package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +125 -48
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
- package/src/__tests__/context-overflow-approval.test.ts +21 -6
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +1 -1
- package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
- package/src/__tests__/conversation-attachments.test.ts +80 -4
- package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
- package/src/__tests__/conversation-directories-parse.test.ts +105 -0
- package/src/__tests__/conversation-fork-crud.test.ts +17 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
- package/src/__tests__/conversation-inject-context.test.ts +103 -0
- package/src/__tests__/conversation-queue.test.ts +45 -2
- package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
- package/src/__tests__/conversation-starter-routes.test.ts +126 -0
- package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
- package/src/__tests__/conversation-store.test.ts +195 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -3
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +4 -4
- package/src/__tests__/credential-vault.test.ts +152 -13
- package/src/__tests__/credentials-cli.test.ts +2 -2
- package/src/__tests__/date-context.test.ts +4 -4
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
- package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
- package/src/__tests__/gateway-only-guard.test.ts +3 -0
- package/src/__tests__/gemini-provider.test.ts +2 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
- package/src/__tests__/headless-browser-interactions.test.ts +707 -371
- package/src/__tests__/headless-browser-navigate.test.ts +389 -47
- package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
- package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
- package/src/__tests__/host-bash-proxy.test.ts +150 -1
- package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
- package/src/__tests__/host-browser-event-routes.test.ts +350 -0
- package/src/__tests__/host-browser-proxy.test.ts +444 -0
- package/src/__tests__/host-browser-routes.test.ts +198 -0
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
- package/src/__tests__/host-cu-proxy.test.ts +171 -1
- package/src/__tests__/host-file-proxy.test.ts +185 -1
- package/src/__tests__/host-file-read-tool.test.ts +52 -0
- package/src/__tests__/host-proxy-interface.test.ts +165 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -11
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
- package/src/__tests__/inline-command-runner.test.ts +7 -5
- package/src/__tests__/integration-status.test.ts +6 -7
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- package/src/__tests__/log-export-workspace.test.ts +190 -0
- package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
- package/src/__tests__/mcp-client-auth.test.ts +40 -4
- package/src/__tests__/mcp-health-check.test.ts +10 -3
- package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
- package/src/__tests__/migration-export-http.test.ts +61 -2
- package/src/__tests__/migration-export-streaming.test.ts +66 -0
- package/src/__tests__/migration-import-commit-http.test.ts +101 -1
- package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
- package/src/__tests__/navigate-settings-tab.test.ts +14 -1
- package/src/__tests__/notification-broadcaster.test.ts +65 -0
- package/src/__tests__/oauth-apps-routes.test.ts +17 -12
- package/src/__tests__/oauth-cli.test.ts +707 -60
- package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
- package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
- package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
- package/src/__tests__/oauth-providers-routes.test.ts +50 -14
- package/src/__tests__/oauth-store.test.ts +1386 -182
- package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
- package/src/__tests__/onboarding-template-contract.test.ts +74 -55
- package/src/__tests__/openai-provider.test.ts +2 -2
- package/src/__tests__/outlook-categories.test.ts +1 -1
- package/src/__tests__/outlook-client-automation.test.ts +1 -1
- package/src/__tests__/outlook-compose-tools.test.ts +1 -1
- package/src/__tests__/outlook-email-watcher.test.ts +1 -1
- package/src/__tests__/outlook-follow-up.test.ts +1 -1
- package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
- package/src/__tests__/outlook-trash.test.ts +1 -1
- package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
- package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
- package/src/__tests__/permission-mode.test.ts +28 -56
- package/src/__tests__/pkb-autoinject.test.ts +96 -0
- package/src/__tests__/platform-callback-registration.test.ts +19 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
- package/src/__tests__/proxy-approval-callback.test.ts +18 -0
- package/src/__tests__/require-fresh-approval.test.ts +40 -3
- package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
- package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
- package/src/__tests__/schedule-routes.test.ts +162 -0
- package/src/__tests__/secret-detection-handler.test.ts +84 -0
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/set-permission-mode.test.ts +13 -250
- package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
- package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
- package/src/__tests__/slack-channel-config.test.ts +12 -15
- package/src/__tests__/subagent-detail.test.ts +44 -2
- package/src/__tests__/subagent-disposal.test.ts +1 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
- package/src/__tests__/subagent-manager-notify.test.ts +1 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
- package/src/__tests__/subagent-tools.test.ts +1 -0
- package/src/__tests__/subagent-types.test.ts +1 -0
- package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
- package/src/__tests__/system-prompt.test.ts +72 -1
- package/src/__tests__/task-scheduler.test.ts +32 -6
- package/src/__tests__/telegram-config.test.ts +10 -13
- package/src/__tests__/terminal-sandbox.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +11 -5
- package/src/__tests__/test-preload.ts +14 -0
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
- package/src/__tests__/top-level-renderer.test.ts +73 -1
- package/src/__tests__/transport-hints-queue.test.ts +62 -0
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- package/src/__tests__/v2-consent-policy.test.ts +103 -0
- package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
- package/src/__tests__/workspace-policy.test.ts +2 -7
- package/src/acp/client-handler.ts +30 -4
- package/src/agent/loop.ts +12 -35
- package/src/approvals/guardian-request-resolvers.ts +21 -15
- package/src/browser-session/__tests__/manager.test.ts +297 -0
- package/src/browser-session/backends/cdp-inspect.ts +30 -0
- package/src/browser-session/backends/extension.ts +26 -0
- package/src/browser-session/backends/local.ts +24 -0
- package/src/browser-session/events.ts +164 -0
- package/src/browser-session/index.ts +27 -0
- package/src/browser-session/manager.ts +159 -0
- package/src/browser-session/types.ts +28 -0
- package/src/channels/__tests__/types.test.ts +134 -0
- package/src/channels/types.ts +55 -0
- package/src/cli/__tests__/run-assistant-command.ts +34 -7
- package/src/cli/__tests__/unknown-command.test.ts +33 -0
- package/src/cli/commands/browser-relay.ts +339 -409
- package/src/cli/commands/credentials.ts +3 -3
- package/src/cli/commands/default-action.ts +68 -1
- package/src/cli/commands/email.ts +18 -13
- package/src/cli/commands/mcp.ts +16 -4
- package/src/cli/commands/oauth/__tests__/connect.test.ts +68 -41
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
- package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
- package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
- package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
- package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
- package/src/cli/commands/oauth/apps.ts +7 -4
- package/src/cli/commands/oauth/connect.ts +16 -2
- package/src/cli/commands/oauth/disconnect.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +200 -36
- package/src/cli/commands/oauth/shared.ts +5 -5
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
- package/src/cli/commands/platform/index.ts +107 -10
- package/src/cli/commands/usage.ts +10 -9
- package/src/cli/lib/daemon-credential-client.ts +4 -0
- package/src/cli/program.ts +10 -3
- package/src/config/assistant-feature-flags.ts +59 -55
- package/src/config/bundled-skills/app-builder/SKILL.md +33 -173
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
- package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
- package/src/config/bundled-skills/contacts/SKILL.md +3 -0
- package/src/config/bundled-skills/document/SKILL.md +4 -0
- package/src/config/bundled-skills/gmail/SKILL.md +12 -7
- package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
- package/src/config/bundled-skills/outlook/SKILL.md +7 -0
- package/src/config/bundled-skills/settings/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
- package/src/config/bundled-skills/subagent/SKILL.md +21 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
- package/src/config/bundled-skills/tasks/SKILL.md +5 -0
- package/src/config/env-registry.ts +14 -0
- package/src/config/env.ts +21 -0
- package/src/config/feature-flag-registry.json +46 -7
- package/src/config/loader.ts +56 -1
- package/src/config/sanitize-for-transfer.ts +47 -0
- package/src/config/schema.ts +46 -5
- package/src/config/schemas/host-browser.ts +66 -0
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/schemas/memory-retrieval.ts +103 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +16 -0
- package/src/config/types.ts +0 -1
- package/src/context/post-turn-tool-result-truncation.ts +176 -0
- package/src/context/window-manager.ts +19 -1
- package/src/credential-execution/approval-bridge.ts +49 -16
- package/src/credential-execution/managed-catalog.ts +3 -7
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/config-watcher.ts +6 -2
- package/src/daemon/context-overflow-approval.ts +5 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
- package/src/daemon/conversation-agent-loop.ts +74 -19
- package/src/daemon/conversation-attachments.ts +40 -1
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +66 -3
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +159 -20
- package/src/daemon/conversation-surfaces.ts +78 -12
- package/src/daemon/conversation-tool-setup.ts +74 -11
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +227 -11
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/conversations.ts +9 -139
- package/src/daemon/handlers/shared.ts +65 -0
- package/src/daemon/handlers/skills.ts +232 -37
- package/src/daemon/host-bash-proxy.ts +48 -13
- package/src/daemon/host-browser-proxy.ts +191 -0
- package/src/daemon/host-cu-proxy.ts +36 -11
- package/src/daemon/host-file-proxy.ts +57 -9
- package/src/daemon/lifecycle.ts +86 -12
- package/src/daemon/message-protocol.ts +7 -0
- package/src/daemon/message-types/conversations.ts +59 -13
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/messages.ts +5 -6
- package/src/daemon/message-types/notifications.ts +12 -0
- package/src/daemon/message-types/settings.ts +12 -0
- package/src/daemon/message-types/skills.ts +10 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/server.ts +112 -35
- package/src/daemon/tool-side-effects.ts +6 -0
- package/src/daemon/transport-hints.ts +14 -0
- package/src/inbound/platform-callback-registration.ts +18 -17
- package/src/index.ts +1 -1
- package/src/mcp/client.ts +59 -24
- package/src/memory/app-store.ts +31 -1
- package/src/memory/conversation-crud.ts +38 -10
- package/src/memory/conversation-directories.ts +39 -0
- package/src/memory/conversation-group-migration.ts +65 -5
- package/src/memory/conversation-starters-cadence.ts +76 -0
- package/src/memory/conversation-title-service.ts +5 -2
- package/src/memory/db-init.ts +12 -0
- package/src/memory/embedding-backend.test.ts +75 -0
- package/src/memory/embedding-backend.ts +131 -5
- package/src/memory/embedding-gemini.test.ts +54 -0
- package/src/memory/embedding-gemini.ts +20 -9
- package/src/memory/embedding-local.ts +177 -18
- package/src/memory/graph/capability-seed.ts +3 -5
- package/src/memory/graph/consolidation.ts +10 -23
- package/src/memory/graph/extraction-job.ts +15 -0
- package/src/memory/graph/retriever.ts +40 -22
- package/src/memory/graph/store.test.ts +7 -3
- package/src/memory/graph/store.ts +47 -12
- package/src/memory/group-crud.ts +25 -9
- package/src/memory/llm-usage-store.ts +45 -4
- package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
- package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
- package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
- package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
- package/src/memory/migrations/217-conversation-host-access.ts +40 -0
- package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +1 -0
- package/src/memory/schema/oauth.ts +18 -13
- package/src/messaging/provider.ts +1 -1
- package/src/notifications/broadcaster.ts +6 -0
- package/src/notifications/conversation-pairing.ts +12 -4
- package/src/notifications/emit-signal.ts +14 -0
- package/src/notifications/signal.ts +11 -0
- package/src/oauth/AGENTS.md +76 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
- package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
- package/src/oauth/byo-connection.test.ts +8 -8
- package/src/oauth/byo-connection.ts +7 -7
- package/src/oauth/connect-orchestrator.ts +23 -21
- package/src/oauth/connect-types.ts +3 -3
- package/src/oauth/connection-resolver.test.ts +17 -4
- package/src/oauth/connection-resolver.ts +16 -16
- package/src/oauth/connection.ts +1 -1
- package/src/oauth/manual-token-connection.ts +13 -13
- package/src/oauth/oauth-store.ts +214 -100
- package/src/oauth/platform-connection.test.ts +5 -5
- package/src/oauth/platform-connection.ts +4 -4
- package/src/oauth/provider-serializer.ts +31 -5
- package/src/oauth/revoke.ts +76 -0
- package/src/oauth/seed-providers.ts +127 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/checker.ts +3 -3
- package/src/permissions/defaults.ts +7 -8
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -3
- package/src/permissions/v2-consent-policy.ts +87 -0
- package/src/platform/client.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -21
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -96
- package/src/prompts/templates/SOUL.md +11 -11
- package/src/providers/anthropic/client.ts +1 -0
- package/src/providers/types.ts +1 -1
- package/src/runtime/AGENTS.md +23 -0
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
- package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
- package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
- package/src/runtime/assistant-event-hub.ts +24 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
- package/src/runtime/auth/middleware.ts +98 -0
- package/src/runtime/auth/route-policy.ts +6 -7
- package/src/runtime/auth/token-service.ts +8 -0
- package/src/runtime/capability-tokens.ts +414 -0
- package/src/runtime/channel-approvals.ts +18 -5
- package/src/runtime/chrome-extension-registry.ts +332 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
- package/src/runtime/guardian-decision-types.ts +7 -0
- package/src/runtime/http-server.ts +425 -70
- package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
- package/src/runtime/migrations/migration-transport.ts +6 -0
- package/src/runtime/migrations/migration-wizard.ts +22 -2
- package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
- package/src/runtime/migrations/vbundle-builder.ts +145 -38
- package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
- package/src/runtime/migrations/vbundle-importer.ts +55 -5
- package/src/runtime/pending-interactions.ts +29 -13
- package/src/runtime/routes/approval-routes.ts +90 -16
- package/src/runtime/routes/browser-cdp-routes.ts +229 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
- package/src/runtime/routes/conversation-analysis-routes.ts +18 -5
- package/src/runtime/routes/conversation-management-routes.ts +108 -0
- package/src/runtime/routes/conversation-routes.ts +308 -28
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- package/src/runtime/routes/group-routes.ts +22 -8
- package/src/runtime/routes/guardian-action-routes.ts +24 -13
- package/src/runtime/routes/host-browser-routes.ts +279 -0
- package/src/runtime/routes/host-file-routes.ts +9 -1
- package/src/runtime/routes/identity-routes.ts +259 -16
- package/src/runtime/routes/log-export/AGENTS.md +104 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
- package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
- package/src/runtime/routes/log-export-routes.ts +60 -25
- package/src/runtime/routes/memory-item-routes.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +87 -2
- package/src/runtime/routes/oauth-apps.ts +15 -17
- package/src/runtime/routes/oauth-providers.ts +4 -0
- package/src/runtime/routes/schedule-routes.ts +24 -11
- package/src/runtime/routes/settings-routes.ts +9 -97
- package/src/runtime/routes/skills-routes.ts +52 -2
- package/src/runtime/routes/subagents-routes.ts +14 -10
- package/src/runtime/routes/usage-routes.ts +8 -7
- package/src/runtime/routes/workspace-routes.test.ts +22 -0
- package/src/runtime/routes/workspace-routes.ts +8 -1
- package/src/runtime/routes/workspace-utils.ts +2 -0
- package/src/schedule/scheduler.ts +7 -5
- package/src/security/ces-credential-client.ts +20 -0
- package/src/security/ces-rpc-credential-backend.ts +17 -0
- package/src/security/credential-backend.ts +5 -0
- package/src/security/oauth2.ts +42 -25
- package/src/security/secure-keys.ts +118 -25
- package/src/security/token-manager.ts +23 -10
- package/src/skills/catalog-files.ts +492 -0
- package/src/skills/inline-command-runner.ts +12 -14
- package/src/subagent/manager.ts +131 -26
- package/src/subagent/types.ts +19 -0
- package/src/tools/apps/executors.ts +11 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
- package/src/tools/browser/auth-detector.ts +43 -12
- package/src/tools/browser/browser-execution.ts +645 -340
- package/src/tools/browser/browser-manager.ts +36 -12
- package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
- package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
- package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
- package/src/tools/browser/cdp-client/errors.ts +34 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
- package/src/tools/browser/cdp-client/factory.ts +204 -0
- package/src/tools/browser/cdp-client/index.ts +14 -0
- package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
- package/src/tools/browser/cdp-client/types.ts +52 -0
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +1 -1
- package/src/tools/host-filesystem/read.ts +12 -15
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +21 -16
- package/src/tools/permission-checker.ts +77 -100
- package/src/tools/registry.ts +0 -2
- package/src/tools/secret-detection-handler.ts +34 -1
- package/src/tools/shared/filesystem/image-read.ts +61 -40
- package/src/tools/skills/sandbox-runner.ts +3 -6
- package/src/tools/subagent/spawn.ts +47 -3
- package/src/tools/subagent/status.ts +2 -0
- package/src/tools/system/register.ts +2 -16
- package/src/tools/terminal/safe-env.ts +7 -0
- package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
- package/src/tools/terminal/sandbox.ts +4 -1
- package/src/tools/terminal/shell.ts +24 -21
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/types.ts +2 -3
- package/src/util/platform.ts +14 -19
- package/src/watcher/provider-types.ts +1 -1
- package/src/workspace/migrations/029-seed-pkb.ts +1 -0
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/workspace/top-level-renderer.ts +19 -1
- package/src/__tests__/chrome-cdp.test.ts +0 -419
- package/src/__tests__/permission-mode-sse.test.ts +0 -418
- package/src/__tests__/permission-mode-store.test.ts +0 -277
- package/src/browser-extension-relay/protocol.ts +0 -63
- package/src/browser-extension-relay/server.ts +0 -203
- package/src/config/schemas/sandbox.ts +0 -14
- package/src/permissions/permission-mode-store.ts +0 -180
- package/src/tools/browser/chrome-cdp.ts +0 -239
- package/src/tools/system/set-permission-mode.ts +0 -103
package/src/oauth/oauth-store.ts
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Backed by Drizzle + SQLite. All JSON fields (default_scopes, scope_policy,
|
|
5
5
|
* extra_params, granted_scopes, metadata) are stored as serialized JSON strings.
|
|
6
|
+
*
|
|
7
|
+
* Note: TS field names use camelCase from the platform's naming
|
|
8
|
+
* (provider, authorizeUrl, tokenExchangeUrl, displayLabel, authorizeParams),
|
|
9
|
+
* while underlying SQL columns retain their original snake_case names
|
|
10
|
+
* (provider_key, auth_url, token_url, display_name, extra_params).
|
|
6
11
|
*/
|
|
7
12
|
|
|
8
13
|
import {
|
|
@@ -26,6 +31,7 @@ import {
|
|
|
26
31
|
setSecureKeyAsync,
|
|
27
32
|
} from "../security/secure-keys.js";
|
|
28
33
|
import { getLogger } from "../util/logger.js";
|
|
34
|
+
import { tryRevokeOAuthToken } from "./revoke.js";
|
|
29
35
|
|
|
30
36
|
const log = getLogger("oauth-store");
|
|
31
37
|
|
|
@@ -43,14 +49,16 @@ export type OAuthConnectionRow = typeof oauthConnections.$inferSelect;
|
|
|
43
49
|
|
|
44
50
|
/**
|
|
45
51
|
* Seed well-known provider profiles into the database. Uses INSERT … ON
|
|
46
|
-
* CONFLICT DO UPDATE so that implementation fields (
|
|
47
|
-
* tokenEndpointAuthMethod, userinfoUrl,
|
|
48
|
-
* pingUrl, pingMethod, pingHeaders, pingBody,
|
|
52
|
+
* CONFLICT DO UPDATE so that implementation fields (authorizeUrl, tokenExchangeUrl,
|
|
53
|
+
* refreshUrl, tokenEndpointAuthMethod, userinfoUrl, authorizeParams,
|
|
54
|
+
* pingUrl, pingMethod, pingHeaders, pingBody, revokeUrl, revokeBodyTemplate,
|
|
55
|
+
* managedServiceConfigKey,
|
|
49
56
|
* loopbackPort, injectionTemplates, appType, setupNotes,
|
|
50
57
|
* identityUrl, identityMethod, identityHeaders, identityBody,
|
|
51
|
-
* identityResponsePaths, identityFormat, identityOkField, featureFlag
|
|
52
|
-
*
|
|
53
|
-
*
|
|
58
|
+
* identityResponsePaths, identityFormat, identityOkField, featureFlag,
|
|
59
|
+
* scopeSeparator)
|
|
60
|
+
* and display metadata (displayLabel, description, dashboardUrl,
|
|
61
|
+
* clientIdPlaceholder, logoUrl, requiresClientSecret) propagate to existing
|
|
54
62
|
* installations on every startup, while user-customizable fields
|
|
55
63
|
* (defaultScopes, scopePolicy) are only written on the
|
|
56
64
|
* initial insert. baseUrl is backfilled from seed data when null
|
|
@@ -59,24 +67,29 @@ export type OAuthConnectionRow = typeof oauthConnections.$inferSelect;
|
|
|
59
67
|
*/
|
|
60
68
|
export function seedProviders(
|
|
61
69
|
profiles: Array<{
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
provider: string;
|
|
71
|
+
authorizeUrl: string;
|
|
72
|
+
tokenExchangeUrl: string;
|
|
73
|
+
refreshUrl?: string;
|
|
65
74
|
tokenEndpointAuthMethod?: string;
|
|
66
75
|
userinfoUrl?: string;
|
|
67
76
|
pingUrl?: string;
|
|
68
77
|
pingMethod?: string;
|
|
69
78
|
pingHeaders?: Record<string, string>;
|
|
70
79
|
pingBody?: unknown;
|
|
80
|
+
revokeUrl?: string;
|
|
81
|
+
revokeBodyTemplate?: Record<string, string>;
|
|
71
82
|
baseUrl?: string;
|
|
72
83
|
defaultScopes: string[];
|
|
73
84
|
scopePolicy: Record<string, unknown>;
|
|
74
|
-
|
|
85
|
+
scopeSeparator?: string;
|
|
86
|
+
authorizeParams?: Record<string, string>;
|
|
75
87
|
managedServiceConfigKey?: string;
|
|
76
|
-
|
|
88
|
+
displayLabel?: string;
|
|
77
89
|
description?: string;
|
|
78
90
|
dashboardUrl?: string | null;
|
|
79
91
|
clientIdPlaceholder?: string | null;
|
|
92
|
+
logoUrl?: string | null;
|
|
80
93
|
requiresClientSecret?: boolean;
|
|
81
94
|
loopbackPort?: number;
|
|
82
95
|
injectionTemplates?: Array<{
|
|
@@ -100,24 +113,41 @@ export function seedProviders(
|
|
|
100
113
|
const db = getDb();
|
|
101
114
|
const now = Date.now();
|
|
102
115
|
for (const p of profiles) {
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const
|
|
116
|
+
const authorizeUrl = p.authorizeUrl;
|
|
117
|
+
const tokenExchangeUrl = p.tokenExchangeUrl;
|
|
118
|
+
const refreshUrl = p.refreshUrl ?? null;
|
|
119
|
+
// Coerce undefined and empty string to the default. The schema declares
|
|
120
|
+
// this column as NOT NULL with default "client_secret_post"; passing null
|
|
121
|
+
// here would be a type error, and an empty string is never a valid OAuth
|
|
122
|
+
// token endpoint auth method.
|
|
123
|
+
const tokenEndpointAuthMethod =
|
|
124
|
+
p.tokenEndpointAuthMethod || "client_secret_post";
|
|
106
125
|
const userinfoUrl = p.userinfoUrl ?? null;
|
|
107
126
|
const pingUrl = p.pingUrl ?? null;
|
|
108
127
|
const pingMethod = p.pingMethod ?? null;
|
|
109
128
|
const pingHeaders = p.pingHeaders ? JSON.stringify(p.pingHeaders) : null;
|
|
110
129
|
const pingBody =
|
|
111
130
|
p.pingBody !== undefined ? JSON.stringify(p.pingBody) : null;
|
|
131
|
+
const revokeUrl = p.revokeUrl ?? null;
|
|
132
|
+
const revokeBodyTemplate = p.revokeBodyTemplate
|
|
133
|
+
? JSON.stringify(p.revokeBodyTemplate)
|
|
134
|
+
: null;
|
|
112
135
|
const baseUrl = p.baseUrl ?? null;
|
|
113
136
|
const defaultScopes = JSON.stringify(p.defaultScopes);
|
|
114
137
|
const scopePolicy = JSON.stringify(p.scopePolicy);
|
|
115
|
-
|
|
138
|
+
// Coerce empty string to the default space separator. An empty separator
|
|
139
|
+
// would join scopes into a single concatenated token (e.g. "readwrite"),
|
|
140
|
+
// which is never a valid OAuth authorize URL value.
|
|
141
|
+
const scopeSeparator = p.scopeSeparator || " ";
|
|
142
|
+
const authorizeParams = p.authorizeParams
|
|
143
|
+
? JSON.stringify(p.authorizeParams)
|
|
144
|
+
: null;
|
|
116
145
|
const managedServiceConfigKey = p.managedServiceConfigKey ?? null;
|
|
117
|
-
const
|
|
146
|
+
const displayLabel = p.displayLabel ?? null;
|
|
118
147
|
const description = p.description ?? null;
|
|
119
148
|
const dashboardUrl = p.dashboardUrl ?? null;
|
|
120
149
|
const clientIdPlaceholder = p.clientIdPlaceholder ?? null;
|
|
150
|
+
const logoUrl = p.logoUrl ?? null;
|
|
121
151
|
const requiresClientSecret = p.requiresClientSecret !== false ? 1 : 0;
|
|
122
152
|
const loopbackPort = p.loopbackPort ?? null;
|
|
123
153
|
const injectionTemplates = p.injectionTemplates
|
|
@@ -141,24 +171,29 @@ export function seedProviders(
|
|
|
141
171
|
|
|
142
172
|
db.insert(oauthProviders)
|
|
143
173
|
.values({
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
174
|
+
provider: p.provider,
|
|
175
|
+
authorizeUrl,
|
|
176
|
+
tokenExchangeUrl,
|
|
177
|
+
refreshUrl,
|
|
147
178
|
tokenEndpointAuthMethod,
|
|
148
179
|
userinfoUrl,
|
|
149
180
|
baseUrl,
|
|
150
181
|
defaultScopes,
|
|
151
182
|
scopePolicy,
|
|
152
|
-
|
|
183
|
+
scopeSeparator,
|
|
184
|
+
authorizeParams,
|
|
153
185
|
pingUrl,
|
|
154
186
|
pingMethod,
|
|
155
187
|
pingHeaders,
|
|
156
188
|
pingBody,
|
|
189
|
+
revokeUrl,
|
|
190
|
+
revokeBodyTemplate,
|
|
157
191
|
managedServiceConfigKey,
|
|
158
|
-
|
|
192
|
+
displayLabel,
|
|
159
193
|
description,
|
|
160
194
|
dashboardUrl,
|
|
161
195
|
clientIdPlaceholder,
|
|
196
|
+
logoUrl,
|
|
162
197
|
requiresClientSecret,
|
|
163
198
|
loopbackPort,
|
|
164
199
|
injectionTemplates,
|
|
@@ -176,23 +211,28 @@ export function seedProviders(
|
|
|
176
211
|
updatedAt: now,
|
|
177
212
|
})
|
|
178
213
|
.onConflictDoUpdate({
|
|
179
|
-
target: oauthProviders.
|
|
214
|
+
target: oauthProviders.provider,
|
|
180
215
|
set: {
|
|
181
|
-
|
|
182
|
-
|
|
216
|
+
authorizeUrl,
|
|
217
|
+
tokenExchangeUrl,
|
|
218
|
+
refreshUrl,
|
|
183
219
|
tokenEndpointAuthMethod,
|
|
184
220
|
userinfoUrl,
|
|
185
221
|
baseUrl: sql`COALESCE(${oauthProviders.baseUrl}, ${baseUrl})`,
|
|
186
|
-
|
|
222
|
+
scopeSeparator,
|
|
223
|
+
authorizeParams,
|
|
187
224
|
pingUrl,
|
|
188
225
|
pingMethod,
|
|
189
226
|
pingHeaders,
|
|
190
227
|
pingBody,
|
|
228
|
+
revokeUrl,
|
|
229
|
+
revokeBodyTemplate,
|
|
191
230
|
managedServiceConfigKey,
|
|
192
|
-
|
|
231
|
+
displayLabel,
|
|
193
232
|
description,
|
|
194
233
|
dashboardUrl,
|
|
195
234
|
clientIdPlaceholder,
|
|
235
|
+
logoUrl,
|
|
196
236
|
requiresClientSecret,
|
|
197
237
|
loopbackPort,
|
|
198
238
|
injectionTemplates,
|
|
@@ -214,12 +254,12 @@ export function seedProviders(
|
|
|
214
254
|
}
|
|
215
255
|
|
|
216
256
|
/** Look up a provider by its primary key. */
|
|
217
|
-
export function getProvider(
|
|
257
|
+
export function getProvider(provider: string): OAuthProviderRow | undefined {
|
|
218
258
|
const db = getDb();
|
|
219
259
|
return db
|
|
220
260
|
.select()
|
|
221
261
|
.from(oauthProviders)
|
|
222
|
-
.where(eq(oauthProviders.
|
|
262
|
+
.where(eq(oauthProviders.provider, provider))
|
|
223
263
|
.get();
|
|
224
264
|
}
|
|
225
265
|
|
|
@@ -234,24 +274,29 @@ export function listProviders(): OAuthProviderRow[] {
|
|
|
234
274
|
* provider_key already exists.
|
|
235
275
|
*/
|
|
236
276
|
export function registerProvider(params: {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
277
|
+
provider: string;
|
|
278
|
+
authorizeUrl: string;
|
|
279
|
+
tokenExchangeUrl: string;
|
|
280
|
+
refreshUrl?: string;
|
|
240
281
|
tokenEndpointAuthMethod?: string;
|
|
241
282
|
userinfoUrl?: string;
|
|
242
283
|
pingUrl?: string;
|
|
243
284
|
pingMethod?: string;
|
|
244
285
|
pingHeaders?: Record<string, string>;
|
|
245
286
|
pingBody?: unknown;
|
|
287
|
+
revokeUrl?: string;
|
|
288
|
+
revokeBodyTemplate?: Record<string, string>;
|
|
246
289
|
baseUrl?: string;
|
|
247
290
|
defaultScopes: string[];
|
|
248
291
|
scopePolicy: Record<string, unknown>;
|
|
249
|
-
|
|
292
|
+
scopeSeparator?: string;
|
|
293
|
+
authorizeParams?: Record<string, string>;
|
|
250
294
|
managedServiceConfigKey?: string;
|
|
251
|
-
|
|
295
|
+
displayLabel?: string;
|
|
252
296
|
description?: string;
|
|
253
297
|
dashboardUrl?: string;
|
|
254
298
|
clientIdPlaceholder?: string;
|
|
299
|
+
logoUrl?: string | null;
|
|
255
300
|
requiresClientSecret?: number;
|
|
256
301
|
loopbackPort?: number;
|
|
257
302
|
injectionTemplates?: Array<{
|
|
@@ -274,31 +319,42 @@ export function registerProvider(params: {
|
|
|
274
319
|
const db = getDb();
|
|
275
320
|
const now = Date.now();
|
|
276
321
|
|
|
277
|
-
const existing = getProvider(params.
|
|
322
|
+
const existing = getProvider(params.provider);
|
|
278
323
|
if (existing) {
|
|
279
|
-
throw new Error(`OAuth provider already exists: ${params.
|
|
324
|
+
throw new Error(`OAuth provider already exists: ${params.provider}`);
|
|
280
325
|
}
|
|
281
326
|
|
|
282
327
|
const row = {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
328
|
+
provider: params.provider,
|
|
329
|
+
authorizeUrl: params.authorizeUrl,
|
|
330
|
+
tokenExchangeUrl: params.tokenExchangeUrl,
|
|
331
|
+
refreshUrl: params.refreshUrl ?? null,
|
|
332
|
+
tokenEndpointAuthMethod:
|
|
333
|
+
params.tokenEndpointAuthMethod || "client_secret_post",
|
|
287
334
|
userinfoUrl: params.userinfoUrl ?? null,
|
|
288
335
|
baseUrl: params.baseUrl ?? null,
|
|
289
336
|
defaultScopes: JSON.stringify(params.defaultScopes),
|
|
290
337
|
scopePolicy: JSON.stringify(params.scopePolicy),
|
|
291
|
-
|
|
338
|
+
// Coerce empty string to the default space separator (see seedProviders).
|
|
339
|
+
scopeSeparator: params.scopeSeparator || " ",
|
|
340
|
+
authorizeParams: params.authorizeParams
|
|
341
|
+
? JSON.stringify(params.authorizeParams)
|
|
342
|
+
: null,
|
|
292
343
|
pingUrl: params.pingUrl ?? null,
|
|
293
344
|
pingMethod: params.pingMethod ?? null,
|
|
294
345
|
pingHeaders: params.pingHeaders ? JSON.stringify(params.pingHeaders) : null,
|
|
295
346
|
pingBody:
|
|
296
347
|
params.pingBody !== undefined ? JSON.stringify(params.pingBody) : null,
|
|
348
|
+
revokeUrl: params.revokeUrl ?? null,
|
|
349
|
+
revokeBodyTemplate: params.revokeBodyTemplate
|
|
350
|
+
? JSON.stringify(params.revokeBodyTemplate)
|
|
351
|
+
: null,
|
|
297
352
|
managedServiceConfigKey: params.managedServiceConfigKey ?? null,
|
|
298
|
-
|
|
353
|
+
displayLabel: params.displayLabel ?? null,
|
|
299
354
|
description: params.description ?? null,
|
|
300
355
|
dashboardUrl: params.dashboardUrl ?? null,
|
|
301
356
|
clientIdPlaceholder: params.clientIdPlaceholder ?? null,
|
|
357
|
+
logoUrl: params.logoUrl ?? null,
|
|
302
358
|
requiresClientSecret: params.requiresClientSecret ?? 1,
|
|
303
359
|
loopbackPort: params.loopbackPort ?? null,
|
|
304
360
|
injectionTemplates: params.injectionTemplates
|
|
@@ -333,31 +389,36 @@ export function registerProvider(params: {
|
|
|
333
389
|
/**
|
|
334
390
|
* Update mutable fields on an existing provider. Only the fields explicitly
|
|
335
391
|
* provided (not `undefined`) are written; everything else is left unchanged.
|
|
336
|
-
* JSON fields (defaultScopes, scopePolicy,
|
|
392
|
+
* JSON fields (defaultScopes, scopePolicy, authorizeParams, pingHeaders, pingBody)
|
|
337
393
|
* are serialized with JSON.stringify before storage.
|
|
338
394
|
*
|
|
339
395
|
* Returns the updated provider row, or `undefined` if no provider with the
|
|
340
396
|
* given key exists.
|
|
341
397
|
*/
|
|
342
398
|
export function updateProvider(
|
|
343
|
-
|
|
399
|
+
provider: string,
|
|
344
400
|
params: Partial<{
|
|
345
|
-
|
|
346
|
-
|
|
401
|
+
authorizeUrl: string;
|
|
402
|
+
tokenExchangeUrl: string;
|
|
403
|
+
refreshUrl: string;
|
|
347
404
|
tokenEndpointAuthMethod: string;
|
|
348
405
|
userinfoUrl: string;
|
|
349
406
|
pingUrl: string;
|
|
350
407
|
pingMethod: string;
|
|
351
408
|
pingHeaders: Record<string, string>;
|
|
352
409
|
pingBody: unknown;
|
|
410
|
+
revokeUrl: string | null;
|
|
411
|
+
revokeBodyTemplate: Record<string, string> | null;
|
|
353
412
|
baseUrl: string;
|
|
354
413
|
defaultScopes: string[];
|
|
355
414
|
scopePolicy: Record<string, unknown>;
|
|
356
|
-
|
|
357
|
-
|
|
415
|
+
scopeSeparator: string;
|
|
416
|
+
authorizeParams: Record<string, string>;
|
|
417
|
+
displayLabel: string;
|
|
358
418
|
description: string;
|
|
359
419
|
dashboardUrl: string;
|
|
360
420
|
clientIdPlaceholder: string;
|
|
421
|
+
logoUrl: string | null;
|
|
361
422
|
requiresClientSecret: boolean;
|
|
362
423
|
loopbackPort: number;
|
|
363
424
|
injectionTemplates: Array<{
|
|
@@ -378,16 +439,19 @@ export function updateProvider(
|
|
|
378
439
|
featureFlag: string;
|
|
379
440
|
}>,
|
|
380
441
|
): OAuthProviderRow | undefined {
|
|
381
|
-
const existing = getProvider(
|
|
442
|
+
const existing = getProvider(provider);
|
|
382
443
|
if (!existing) return undefined;
|
|
383
444
|
|
|
384
445
|
const db = getDb();
|
|
385
446
|
const set: Record<string, unknown> = { updatedAt: Date.now() };
|
|
386
447
|
|
|
387
|
-
if (params.
|
|
388
|
-
if (params.
|
|
448
|
+
if (params.authorizeUrl !== undefined) set.authorizeUrl = params.authorizeUrl;
|
|
449
|
+
if (params.tokenExchangeUrl !== undefined)
|
|
450
|
+
set.tokenExchangeUrl = params.tokenExchangeUrl;
|
|
451
|
+
if (params.refreshUrl !== undefined) set.refreshUrl = params.refreshUrl;
|
|
389
452
|
if (params.tokenEndpointAuthMethod !== undefined)
|
|
390
|
-
set.tokenEndpointAuthMethod =
|
|
453
|
+
set.tokenEndpointAuthMethod =
|
|
454
|
+
params.tokenEndpointAuthMethod || "client_secret_post";
|
|
391
455
|
if (params.userinfoUrl !== undefined) set.userinfoUrl = params.userinfoUrl;
|
|
392
456
|
if (params.pingUrl !== undefined) set.pingUrl = params.pingUrl;
|
|
393
457
|
if (params.pingMethod !== undefined) set.pingMethod = params.pingMethod;
|
|
@@ -395,18 +459,28 @@ export function updateProvider(
|
|
|
395
459
|
set.pingHeaders = JSON.stringify(params.pingHeaders);
|
|
396
460
|
if (params.pingBody !== undefined)
|
|
397
461
|
set.pingBody = JSON.stringify(params.pingBody);
|
|
462
|
+
if (params.revokeUrl !== undefined) set.revokeUrl = params.revokeUrl;
|
|
463
|
+
if (params.revokeBodyTemplate !== undefined)
|
|
464
|
+
set.revokeBodyTemplate =
|
|
465
|
+
params.revokeBodyTemplate === null
|
|
466
|
+
? null
|
|
467
|
+
: JSON.stringify(params.revokeBodyTemplate);
|
|
398
468
|
if (params.baseUrl !== undefined) set.baseUrl = params.baseUrl;
|
|
399
469
|
if (params.defaultScopes !== undefined)
|
|
400
470
|
set.defaultScopes = JSON.stringify(params.defaultScopes);
|
|
401
471
|
if (params.scopePolicy !== undefined)
|
|
402
472
|
set.scopePolicy = JSON.stringify(params.scopePolicy);
|
|
403
|
-
if (params.
|
|
404
|
-
|
|
405
|
-
|
|
473
|
+
if (params.scopeSeparator !== undefined)
|
|
474
|
+
// Coerce empty string to the default space separator (see seedProviders).
|
|
475
|
+
set.scopeSeparator = params.scopeSeparator || " ";
|
|
476
|
+
if (params.authorizeParams !== undefined)
|
|
477
|
+
set.authorizeParams = JSON.stringify(params.authorizeParams);
|
|
478
|
+
if (params.displayLabel !== undefined) set.displayLabel = params.displayLabel;
|
|
406
479
|
if (params.description !== undefined) set.description = params.description;
|
|
407
480
|
if (params.dashboardUrl !== undefined) set.dashboardUrl = params.dashboardUrl;
|
|
408
481
|
if (params.clientIdPlaceholder !== undefined)
|
|
409
482
|
set.clientIdPlaceholder = params.clientIdPlaceholder;
|
|
483
|
+
if (params.logoUrl !== undefined) set.logoUrl = params.logoUrl;
|
|
410
484
|
if (params.requiresClientSecret !== undefined)
|
|
411
485
|
set.requiresClientSecret = params.requiresClientSecret ? 1 : 0;
|
|
412
486
|
if (params.loopbackPort !== undefined) set.loopbackPort = params.loopbackPort;
|
|
@@ -432,10 +506,10 @@ export function updateProvider(
|
|
|
432
506
|
|
|
433
507
|
db.update(oauthProviders)
|
|
434
508
|
.set(set)
|
|
435
|
-
.where(eq(oauthProviders.
|
|
509
|
+
.where(eq(oauthProviders.provider, provider))
|
|
436
510
|
.run();
|
|
437
511
|
|
|
438
|
-
return getProvider(
|
|
512
|
+
return getProvider(provider);
|
|
439
513
|
}
|
|
440
514
|
|
|
441
515
|
/**
|
|
@@ -445,14 +519,12 @@ export function updateProvider(
|
|
|
445
519
|
* Note: SQLite enforces the foreign-key constraint from `oauth_apps.provider_key`,
|
|
446
520
|
* so deleting a provider that has existing apps will throw.
|
|
447
521
|
*/
|
|
448
|
-
export function deleteProvider(
|
|
449
|
-
const existing = getProvider(
|
|
522
|
+
export function deleteProvider(provider: string): boolean {
|
|
523
|
+
const existing = getProvider(provider);
|
|
450
524
|
if (!existing) return false;
|
|
451
525
|
|
|
452
526
|
const db = getDb();
|
|
453
|
-
db.delete(oauthProviders)
|
|
454
|
-
.where(eq(oauthProviders.providerKey, providerKey))
|
|
455
|
-
.run();
|
|
527
|
+
db.delete(oauthProviders).where(eq(oauthProviders.provider, provider)).run();
|
|
456
528
|
return rawChanges() > 0;
|
|
457
529
|
}
|
|
458
530
|
|
|
@@ -465,7 +537,7 @@ export function deleteProvider(providerKey: string): boolean {
|
|
|
465
537
|
* Generates a UUID on insert.
|
|
466
538
|
*/
|
|
467
539
|
export async function upsertApp(
|
|
468
|
-
|
|
540
|
+
provider: string,
|
|
469
541
|
clientId: string,
|
|
470
542
|
clientSecretOpts?: {
|
|
471
543
|
clientSecretValue?: string;
|
|
@@ -499,10 +571,7 @@ export async function upsertApp(
|
|
|
499
571
|
.select()
|
|
500
572
|
.from(oauthApps)
|
|
501
573
|
.where(
|
|
502
|
-
and(
|
|
503
|
-
eq(oauthApps.providerKey, providerKey),
|
|
504
|
-
eq(oauthApps.clientId, clientId),
|
|
505
|
-
),
|
|
574
|
+
and(eq(oauthApps.provider, provider), eq(oauthApps.clientId, clientId)),
|
|
506
575
|
)
|
|
507
576
|
.get();
|
|
508
577
|
|
|
@@ -549,7 +618,7 @@ export async function upsertApp(
|
|
|
549
618
|
|
|
550
619
|
const row = {
|
|
551
620
|
id,
|
|
552
|
-
|
|
621
|
+
provider,
|
|
553
622
|
clientId,
|
|
554
623
|
clientSecretCredentialPath: credPath,
|
|
555
624
|
createdAt: now,
|
|
@@ -597,7 +666,7 @@ export async function getAppClientSecret(
|
|
|
597
666
|
|
|
598
667
|
/** Look up an app by (provider_key, client_id). */
|
|
599
668
|
export function getAppByProviderAndClientId(
|
|
600
|
-
|
|
669
|
+
provider: string,
|
|
601
670
|
clientId: string,
|
|
602
671
|
): OAuthAppRow | undefined {
|
|
603
672
|
const db = getDb();
|
|
@@ -605,10 +674,7 @@ export function getAppByProviderAndClientId(
|
|
|
605
674
|
.select()
|
|
606
675
|
.from(oauthApps)
|
|
607
676
|
.where(
|
|
608
|
-
and(
|
|
609
|
-
eq(oauthApps.providerKey, providerKey),
|
|
610
|
-
eq(oauthApps.clientId, clientId),
|
|
611
|
-
),
|
|
677
|
+
and(eq(oauthApps.provider, provider), eq(oauthApps.clientId, clientId)),
|
|
612
678
|
)
|
|
613
679
|
.get();
|
|
614
680
|
}
|
|
@@ -618,13 +684,13 @@ export function getAppByProviderAndClientId(
|
|
|
618
684
|
* Returns undefined if no app exists for this provider.
|
|
619
685
|
*/
|
|
620
686
|
export function getMostRecentAppByProvider(
|
|
621
|
-
|
|
687
|
+
provider: string,
|
|
622
688
|
): OAuthAppRow | undefined {
|
|
623
689
|
const db = getDb();
|
|
624
690
|
return db
|
|
625
691
|
.select()
|
|
626
692
|
.from(oauthApps)
|
|
627
|
-
.where(eq(oauthApps.
|
|
693
|
+
.where(eq(oauthApps.provider, provider))
|
|
628
694
|
.orderBy(desc(oauthApps.createdAt))
|
|
629
695
|
.limit(1)
|
|
630
696
|
.get();
|
|
@@ -670,7 +736,7 @@ export async function deleteApp(id: string): Promise<boolean> {
|
|
|
670
736
|
*/
|
|
671
737
|
export function createConnection(params: {
|
|
672
738
|
oauthAppId: string;
|
|
673
|
-
|
|
739
|
+
provider: string;
|
|
674
740
|
accountInfo?: string;
|
|
675
741
|
grantedScopes: string[];
|
|
676
742
|
expiresAt?: number;
|
|
@@ -687,7 +753,7 @@ export function createConnection(params: {
|
|
|
687
753
|
const row = {
|
|
688
754
|
id,
|
|
689
755
|
oauthAppId: params.oauthAppId,
|
|
690
|
-
|
|
756
|
+
provider: params.provider,
|
|
691
757
|
accountInfo: params.accountInfo ?? null,
|
|
692
758
|
grantedScopes: JSON.stringify(params.grantedScopes),
|
|
693
759
|
expiresAt: params.expiresAt ?? null,
|
|
@@ -724,14 +790,14 @@ export function getConnection(id: string): OAuthConnectionRow | undefined {
|
|
|
724
790
|
* Returns `undefined` when no matching active connection exists.
|
|
725
791
|
*/
|
|
726
792
|
export function getActiveConnection(
|
|
727
|
-
|
|
793
|
+
provider: string,
|
|
728
794
|
options?: { clientId?: string; account?: string },
|
|
729
795
|
): OAuthConnectionRow | undefined {
|
|
730
796
|
const { clientId, account } = options ?? {};
|
|
731
797
|
const db = getDb();
|
|
732
798
|
|
|
733
799
|
const conditions = [
|
|
734
|
-
eq(oauthConnections.
|
|
800
|
+
eq(oauthConnections.provider, provider),
|
|
735
801
|
eq(oauthConnections.status, "active"),
|
|
736
802
|
];
|
|
737
803
|
|
|
@@ -740,7 +806,7 @@ export function getActiveConnection(
|
|
|
740
806
|
}
|
|
741
807
|
|
|
742
808
|
if (clientId) {
|
|
743
|
-
const app = getAppByProviderAndClientId(
|
|
809
|
+
const app = getAppByProviderAndClientId(provider, clientId);
|
|
744
810
|
if (!app) return undefined;
|
|
745
811
|
conditions.push(eq(oauthConnections.oauthAppId, app.id));
|
|
746
812
|
}
|
|
@@ -756,26 +822,26 @@ export function getActiveConnection(
|
|
|
756
822
|
|
|
757
823
|
/** @deprecated Use {@link getActiveConnection} instead. */
|
|
758
824
|
export function getConnectionByProvider(
|
|
759
|
-
|
|
825
|
+
provider: string,
|
|
760
826
|
clientId?: string,
|
|
761
827
|
): OAuthConnectionRow | undefined {
|
|
762
|
-
return getActiveConnection(
|
|
828
|
+
return getActiveConnection(provider, { clientId });
|
|
763
829
|
}
|
|
764
830
|
|
|
765
831
|
/** @deprecated Use {@link getActiveConnection} instead. */
|
|
766
832
|
export function getConnectionByProviderAndAccount(
|
|
767
|
-
|
|
833
|
+
provider: string,
|
|
768
834
|
accountInfo?: string,
|
|
769
835
|
clientId?: string,
|
|
770
836
|
): OAuthConnectionRow | undefined {
|
|
771
|
-
return getActiveConnection(
|
|
837
|
+
return getActiveConnection(provider, { clientId, account: accountInfo });
|
|
772
838
|
}
|
|
773
839
|
|
|
774
840
|
/**
|
|
775
841
|
* Get ALL active connections for a provider (supports multi-account).
|
|
776
842
|
*/
|
|
777
843
|
export function listActiveConnectionsByProvider(
|
|
778
|
-
|
|
844
|
+
provider: string,
|
|
779
845
|
): OAuthConnectionRow[] {
|
|
780
846
|
const db = getDb();
|
|
781
847
|
return db
|
|
@@ -783,7 +849,7 @@ export function listActiveConnectionsByProvider(
|
|
|
783
849
|
.from(oauthConnections)
|
|
784
850
|
.where(
|
|
785
851
|
and(
|
|
786
|
-
eq(oauthConnections.
|
|
852
|
+
eq(oauthConnections.provider, provider),
|
|
787
853
|
eq(oauthConnections.status, "active"),
|
|
788
854
|
),
|
|
789
855
|
)
|
|
@@ -799,10 +865,8 @@ export function listActiveConnectionsByProvider(
|
|
|
799
865
|
* but the secure-key write for the access token failed, which would make
|
|
800
866
|
* `resolveOAuthConnection()` throw at usage time.
|
|
801
867
|
*/
|
|
802
|
-
export async function isProviderConnected(
|
|
803
|
-
|
|
804
|
-
): Promise<boolean> {
|
|
805
|
-
const conn = getActiveConnection(providerKey);
|
|
868
|
+
export async function isProviderConnected(provider: string): Promise<boolean> {
|
|
869
|
+
const conn = getActiveConnection(provider);
|
|
806
870
|
if (!conn || conn.status !== "active") return false;
|
|
807
871
|
return (
|
|
808
872
|
(await getSecureKeyAsync(oauthConnectionAccessTokenPath(conn.id))) !==
|
|
@@ -853,24 +917,24 @@ export function updateConnection(
|
|
|
853
917
|
|
|
854
918
|
/** List connections, optionally filtered by provider key and/or client ID. */
|
|
855
919
|
export function listConnections(
|
|
856
|
-
|
|
920
|
+
provider?: string,
|
|
857
921
|
clientId?: string,
|
|
858
922
|
): OAuthConnectionRow[] {
|
|
859
923
|
const db = getDb();
|
|
860
924
|
|
|
861
925
|
let rows: OAuthConnectionRow[];
|
|
862
|
-
if (
|
|
926
|
+
if (provider) {
|
|
863
927
|
rows = db
|
|
864
928
|
.select()
|
|
865
929
|
.from(oauthConnections)
|
|
866
|
-
.where(eq(oauthConnections.
|
|
867
|
-
.orderBy(oauthConnections.
|
|
930
|
+
.where(eq(oauthConnections.provider, provider))
|
|
931
|
+
.orderBy(oauthConnections.provider, oauthConnections.id)
|
|
868
932
|
.all();
|
|
869
933
|
} else {
|
|
870
934
|
rows = db
|
|
871
935
|
.select()
|
|
872
936
|
.from(oauthConnections)
|
|
873
|
-
.orderBy(oauthConnections.
|
|
937
|
+
.orderBy(oauthConnections.provider, oauthConnections.id)
|
|
874
938
|
.all();
|
|
875
939
|
}
|
|
876
940
|
|
|
@@ -901,28 +965,78 @@ export function deleteConnection(id: string): boolean {
|
|
|
901
965
|
// ---------------------------------------------------------------------------
|
|
902
966
|
|
|
903
967
|
/**
|
|
904
|
-
* Fully disconnect an OAuth provider:
|
|
905
|
-
*
|
|
968
|
+
* Fully disconnect an OAuth provider:
|
|
969
|
+
* 1. Best-effort upstream token revoke when the provider has `revokeUrl`
|
|
970
|
+
* configured (mirrors platform's `try_revoke_token`).
|
|
971
|
+
* 2. Delete the new-format secure keys (access_token and refresh_token).
|
|
972
|
+
* 3. Remove the connection row from SQLite.
|
|
973
|
+
*
|
|
974
|
+
* The upstream revoke step is strictly best-effort: any failure (network
|
|
975
|
+
* error, non-2xx response, missing access token, etc.) is logged as a
|
|
976
|
+
* warning and the local cleanup proceeds anyway. The connection is always
|
|
977
|
+
* cleaned up locally regardless of whether the upstream call succeeds.
|
|
906
978
|
*
|
|
907
979
|
* When `connectionId` is provided, disconnects that specific connection
|
|
908
980
|
* (useful for multi-account providers). Otherwise falls back to the most
|
|
909
981
|
* recent active connection.
|
|
910
982
|
*
|
|
911
|
-
* Returns `"disconnected"` if a connection was found and cleaned up,
|
|
983
|
+
* Returns `"disconnected"` if a connection was found and locally cleaned up,
|
|
912
984
|
* `"not-found"` if no active connection existed for the given provider,
|
|
913
985
|
* or `"error"` if secure key deletion failed (connection row is preserved
|
|
914
986
|
* to avoid orphaning secrets).
|
|
915
987
|
*/
|
|
916
988
|
export async function disconnectOAuthProvider(
|
|
917
|
-
|
|
989
|
+
provider: string,
|
|
918
990
|
clientId?: string,
|
|
919
991
|
connectionId?: string,
|
|
920
992
|
): Promise<"disconnected" | "not-found" | "error"> {
|
|
921
993
|
const conn = connectionId
|
|
922
994
|
? getConnection(connectionId)
|
|
923
|
-
: getActiveConnection(
|
|
995
|
+
: getActiveConnection(provider, { clientId });
|
|
924
996
|
if (!conn) return "not-found";
|
|
925
997
|
|
|
998
|
+
// Best-effort upstream revoke. Mirrors platform's try_revoke_token in
|
|
999
|
+
// django/app/assistant/oauth/providers/base.py. Failures here never
|
|
1000
|
+
// block local cleanup — the connection is always cleaned up locally
|
|
1001
|
+
// regardless of whether the upstream call succeeds.
|
|
1002
|
+
try {
|
|
1003
|
+
const providerRow = getProvider(conn.provider);
|
|
1004
|
+
if (providerRow?.revokeUrl) {
|
|
1005
|
+
const app = getApp(conn.oauthAppId);
|
|
1006
|
+
const accessToken = await getSecureKeyAsync(
|
|
1007
|
+
oauthConnectionAccessTokenPath(conn.id),
|
|
1008
|
+
);
|
|
1009
|
+
if (app && accessToken) {
|
|
1010
|
+
const bodyTemplate = providerRow.revokeBodyTemplate
|
|
1011
|
+
? (JSON.parse(providerRow.revokeBodyTemplate) as Record<
|
|
1012
|
+
string,
|
|
1013
|
+
unknown
|
|
1014
|
+
>)
|
|
1015
|
+
: null;
|
|
1016
|
+
await tryRevokeOAuthToken({
|
|
1017
|
+
provider: conn.provider,
|
|
1018
|
+
revokeUrl: providerRow.revokeUrl,
|
|
1019
|
+
bodyTemplate,
|
|
1020
|
+
accessToken,
|
|
1021
|
+
clientId: app.clientId,
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
} catch (err) {
|
|
1026
|
+
// tryRevokeOAuthToken already swallows fetch errors, but the lookups
|
|
1027
|
+
// (getProvider/getApp/getSecureKeyAsync/JSON.parse) could throw too.
|
|
1028
|
+
// Defense in depth: never let the local cleanup path die because of
|
|
1029
|
+
// anything in the revoke setup.
|
|
1030
|
+
log.warn(
|
|
1031
|
+
{
|
|
1032
|
+
provider: conn.provider,
|
|
1033
|
+
connectionId: conn.id,
|
|
1034
|
+
err: err instanceof Error ? err.message : String(err),
|
|
1035
|
+
},
|
|
1036
|
+
"Error preparing upstream OAuth revoke (best-effort, continuing local cleanup)",
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
926
1040
|
// Wrap the assistant's secure-key functions into the SecureKeyBackend
|
|
927
1041
|
// interface expected by the shared deleteOAuthTokens helper.
|
|
928
1042
|
const backend: SecureKeyBackend = {
|
|
@@ -945,7 +1059,7 @@ export async function disconnectOAuthProvider(
|
|
|
945
1059
|
// way to surface the failure.
|
|
946
1060
|
log.warn(
|
|
947
1061
|
{
|
|
948
|
-
|
|
1062
|
+
provider,
|
|
949
1063
|
connectionId: conn.id,
|
|
950
1064
|
accessTokenResult,
|
|
951
1065
|
refreshTokenResult,
|