@vellumai/assistant 0.5.9 → 0.5.11
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/AGENTS.md +9 -1
- package/ARCHITECTURE.md +48 -48
- package/Dockerfile +2 -0
- package/README.md +1 -1
- package/docs/architecture/integrations.md +6 -13
- package/docs/architecture/memory.md +7 -12
- package/docs/architecture/security.md +5 -5
- package/docs/credential-execution-service.md +9 -9
- package/docs/skills.md +1 -1
- package/node_modules/@vellumai/credential-storage/src/index.ts +2 -2
- package/node_modules/@vellumai/credential-storage/src/static-credentials.ts +1 -1
- package/openapi.yaml +7130 -0
- package/package.json +2 -1
- package/scripts/generate-openapi.ts +562 -0
- package/src/__tests__/acp-session.test.ts +239 -44
- package/src/__tests__/assistant-feature-flag-guard.test.ts +8 -8
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +5 -86
- package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -14
- package/src/__tests__/browser-skill-endstate.test.ts +1 -1
- package/src/__tests__/btw-routes.test.ts +8 -0
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +10 -10
- package/src/__tests__/channel-approvals.test.ts +7 -7
- package/src/__tests__/channel-readiness-service.test.ts +41 -0
- package/src/__tests__/config-schema.test.ts +10 -2
- package/src/__tests__/context-memory-e2e.test.ts +2 -6
- package/src/__tests__/conversation-skill-tools.test.ts +1 -3
- package/src/__tests__/conversation-title-service.test.ts +2 -15
- package/src/__tests__/credential-execution-feature-gates.test.ts +4 -8
- package/src/__tests__/credential-execution-managed-contract.test.ts +8 -8
- package/src/__tests__/credential-security-e2e.test.ts +4 -4
- package/src/__tests__/credential-security-invariants.test.ts +3 -3
- package/src/__tests__/credentials-cli.test.ts +3 -3
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -0
- package/src/__tests__/heartbeat-service.test.ts +35 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +3 -3
- package/src/__tests__/llm-request-log-turn-query.test.ts +64 -0
- package/src/__tests__/log-export-workspace.test.ts +1 -1
- package/src/__tests__/mcp-client-auth.test.ts +1 -1
- package/src/__tests__/memory-lifecycle-e2e.test.ts +2 -2
- package/src/__tests__/memory-recall-log-store.test.ts +182 -0
- package/src/__tests__/memory-recall-quality.test.ts +6 -8
- package/src/__tests__/memory-regressions.test.ts +53 -42
- package/src/__tests__/memory-retrieval.benchmark.test.ts +5 -9
- package/src/__tests__/messaging-skill-split.test.ts +2 -17
- package/src/__tests__/oauth-cli.test.ts +98 -551
- package/src/__tests__/platform-callback-registration.test.ts +119 -0
- package/src/__tests__/secret-ingress-channel.test.ts +261 -0
- package/src/__tests__/secret-ingress-cli.test.ts +201 -0
- package/src/__tests__/secret-ingress-http.test.ts +312 -0
- package/src/__tests__/secret-ingress.test.ts +283 -0
- package/src/__tests__/secret-onetime-send.test.ts +4 -4
- package/src/__tests__/skill-feature-flags-integration.test.ts +4 -4
- package/src/__tests__/skill-feature-flags.test.ts +11 -19
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -1
- package/src/__tests__/skill-load-inline-command.test.ts +3 -3
- package/src/__tests__/skill-load-inline-includes.test.ts +2 -2
- package/src/__tests__/skill-memory.test.ts +2 -4
- package/src/__tests__/skill-projection-feature-flag.test.ts +2 -4
- package/src/__tests__/skill-projection.benchmark.test.ts +1 -3
- package/src/__tests__/skills.test.ts +16 -2
- package/src/__tests__/slack-channel-config.test.ts +1 -1
- package/src/__tests__/slack-skill.test.ts +5 -69
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -1
- package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +5 -238
- package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +5 -206
- package/src/__tests__/workspace-migration-018-rekey-compound-credential-keys.test.ts +181 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +15 -7
- package/src/acp/client-handler.ts +113 -31
- package/src/acp/session-manager.ts +29 -27
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/cli/AGENTS.md +73 -0
- package/src/cli/commands/autonomy.ts +3 -5
- package/src/cli/commands/credential-execution.ts +1 -2
- package/src/cli/commands/credentials.ts +4 -4
- package/src/cli/commands/memory.ts +2 -3
- package/src/cli/commands/oauth/__tests__/connect.test.ts +785 -0
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +760 -0
- package/src/cli/commands/oauth/__tests__/mode.test.ts +672 -0
- package/src/cli/commands/oauth/__tests__/ping.test.ts +690 -0
- package/src/cli/commands/oauth/__tests__/status.test.ts +579 -0
- package/src/cli/commands/oauth/__tests__/token.test.ts +467 -0
- package/src/cli/commands/oauth/apps.ts +29 -11
- package/src/cli/commands/oauth/connect.ts +373 -0
- package/src/cli/commands/oauth/connections.ts +14 -493
- package/src/cli/commands/oauth/disconnect.ts +333 -0
- package/src/cli/commands/oauth/index.ts +62 -10
- package/src/cli/commands/oauth/mode.ts +263 -0
- package/src/cli/commands/oauth/ping.ts +222 -0
- package/src/cli/commands/oauth/providers.ts +30 -3
- package/src/cli/commands/oauth/request.ts +576 -0
- package/src/cli/commands/oauth/shared.ts +132 -0
- package/src/cli/commands/oauth/status.ts +202 -0
- package/src/cli/commands/oauth/token.ts +159 -0
- package/src/cli/commands/platform.ts +20 -14
- package/src/cli.ts +82 -17
- package/src/config/assistant-feature-flags.ts +74 -11
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +1 -1
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +13 -36
- package/src/config/bundled-skills/messaging/TOOLS.json +9 -9
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +1 -1
- package/src/config/bundled-skills/notifications/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +2 -2
- package/src/config/bundled-skills/settings/SKILL.md +5 -3
- package/src/config/bundled-skills/settings/TOOLS.json +17 -0
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +50 -0
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +7 -0
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +6 -1
- package/src/config/bundled-skills/settings/tools/identity-avatar.ts +55 -0
- package/src/config/bundled-skills/skills-catalog/SKILL.md +3 -3
- package/src/config/bundled-skills/slack/SKILL.md +58 -44
- package/src/config/bundled-tool-registry.ts +2 -19
- package/src/config/env.ts +5 -1
- package/src/config/feature-flag-registry.json +57 -41
- package/src/config/loader.ts +4 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/security.ts +9 -1
- package/src/config/schemas/services.ts +1 -1
- package/src/config/skill-state.ts +1 -3
- package/src/config/skills.ts +2 -4
- package/src/credential-execution/feature-gates.ts +9 -16
- package/src/credential-execution/process-manager.ts +12 -0
- package/src/daemon/config-watcher.ts +4 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +10 -0
- package/src/daemon/conversation-agent-loop.ts +49 -2
- package/src/daemon/conversation-memory.ts +0 -1
- package/src/daemon/handlers/config-slack-channel.ts +43 -1
- package/src/daemon/handlers/conversations.ts +41 -33
- package/src/daemon/lifecycle.ts +28 -5
- package/src/daemon/message-types/acp.ts +0 -15
- package/src/daemon/message-types/memory.ts +0 -1
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/schedules.ts +9 -0
- package/src/daemon/server.ts +19 -7
- package/src/email/feature-gate.ts +3 -3
- package/src/heartbeat/heartbeat-service.ts +48 -0
- package/src/inbound/platform-callback-registration.ts +61 -7
- package/src/mcp/mcp-oauth-provider.ts +3 -3
- package/src/memory/app-store.ts +3 -3
- package/src/memory/conversation-crud.ts +124 -0
- package/src/memory/conversation-title-service.ts +7 -17
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-local.ts +47 -2
- package/src/memory/indexer.ts +13 -10
- package/src/memory/items-extractor.ts +12 -4
- package/src/memory/job-utils.ts +5 -0
- package/src/memory/jobs-store.ts +10 -2
- package/src/memory/journal-memory.ts +6 -2
- package/src/memory/llm-request-log-store.ts +88 -21
- package/src/memory/memory-recall-log-store.ts +128 -0
- package/src/memory/migrations/194-memory-recall-logs.ts +50 -0
- package/src/memory/migrations/195-oauth-providers-ping-config.ts +23 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/migrations/validate-migration-state.ts +14 -1
- package/src/memory/retriever.test.ts +4 -5
- package/src/memory/schema/infrastructure.ts +31 -0
- package/src/memory/schema/oauth.ts +3 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -1
- package/src/oauth/connect-orchestrator.ts +54 -0
- package/src/oauth/manual-token-connection.ts +5 -5
- package/src/oauth/oauth-store.ts +26 -5
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -2
- package/src/permissions/trust-client.ts +2 -2
- package/src/platform/client.ts +2 -2
- package/src/prompts/journal-context.ts +6 -1
- package/src/providers/anthropic/client.ts +143 -1
- package/src/runtime/auth/__tests__/middleware.test.ts +19 -0
- package/src/runtime/auth/route-policy.ts +0 -1
- package/src/runtime/btw-sidechain.ts +7 -1
- package/src/runtime/channel-approvals.ts +2 -2
- package/src/runtime/channel-readiness-service.ts +30 -7
- package/src/runtime/http-router.ts +31 -0
- package/src/runtime/http-server.ts +21 -4
- package/src/runtime/http-types.ts +2 -0
- package/src/runtime/pending-interactions.ts +21 -3
- package/src/runtime/routes/acp-routes.ts +46 -28
- package/src/runtime/routes/app-management-routes.ts +123 -0
- package/src/runtime/routes/app-routes.ts +31 -0
- package/src/runtime/routes/approval-routes.ts +108 -3
- package/src/runtime/routes/attachment-routes.ts +45 -0
- package/src/runtime/routes/avatar-routes.ts +16 -0
- package/src/runtime/routes/brain-graph-routes.ts +18 -0
- package/src/runtime/routes/btw-routes.ts +20 -0
- package/src/runtime/routes/call-routes.ts +81 -0
- package/src/runtime/routes/channel-readiness-routes.ts +48 -7
- package/src/runtime/routes/channel-routes.ts +18 -0
- package/src/runtime/routes/channel-verification-routes.ts +49 -1
- package/src/runtime/routes/contact-routes.ts +77 -0
- package/src/runtime/routes/conversation-attention-routes.ts +37 -0
- package/src/runtime/routes/conversation-management-routes.ts +94 -0
- package/src/runtime/routes/conversation-query-routes.ts +78 -0
- package/src/runtime/routes/conversation-routes.ts +115 -38
- package/src/runtime/routes/conversation-starter-routes.ts +29 -0
- package/src/runtime/routes/debug-routes.ts +23 -0
- package/src/runtime/routes/diagnostics-routes.ts +30 -0
- package/src/runtime/routes/documents-routes.ts +42 -0
- package/src/runtime/routes/events-routes.ts +10 -0
- package/src/runtime/routes/global-search-routes.ts +35 -0
- package/src/runtime/routes/guardian-action-routes.ts +47 -2
- package/src/runtime/routes/guardian-approval-prompt.ts +77 -2
- package/src/runtime/routes/heartbeat-routes.ts +278 -0
- package/src/runtime/routes/host-bash-routes.ts +16 -1
- package/src/runtime/routes/host-cu-routes.ts +23 -1
- package/src/runtime/routes/host-file-routes.ts +18 -1
- package/src/runtime/routes/identity-routes.ts +35 -0
- package/src/runtime/routes/inbound-message-handler.ts +46 -25
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +30 -2
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +1 -2
- package/src/runtime/routes/integrations/twilio.ts +32 -22
- package/src/runtime/routes/invite-routes.ts +83 -0
- package/src/runtime/routes/log-export-routes.ts +14 -0
- package/src/runtime/routes/memory-item-routes.ts +99 -1
- package/src/runtime/routes/migration-rollback-routes.ts +25 -0
- package/src/runtime/routes/migration-routes.ts +40 -0
- package/src/runtime/routes/notification-routes.ts +20 -0
- package/src/runtime/routes/oauth-apps.ts +11 -3
- package/src/runtime/routes/pairing-routes.ts +15 -0
- package/src/runtime/routes/recording-routes.ts +72 -0
- package/src/runtime/routes/schedule-routes.ts +77 -5
- package/src/runtime/routes/secret-routes.ts +63 -1
- package/src/runtime/routes/settings-routes.ts +91 -1
- package/src/runtime/routes/skills-routes.ts +98 -16
- package/src/runtime/routes/subagents-routes.ts +38 -3
- package/src/runtime/routes/surface-action-routes.ts +66 -24
- package/src/runtime/routes/surface-content-routes.ts +20 -0
- package/src/runtime/routes/telemetry-routes.ts +12 -0
- package/src/runtime/routes/trace-event-routes.ts +25 -0
- package/src/runtime/routes/trust-rules-routes.ts +46 -0
- package/src/runtime/routes/tts-routes.ts +15 -4
- package/src/runtime/routes/upgrade-broadcast-routes.ts +38 -0
- package/src/runtime/routes/usage-routes.ts +59 -0
- package/src/runtime/routes/watch-routes.ts +28 -0
- package/src/runtime/routes/work-items-routes.ts +59 -0
- package/src/runtime/routes/workspace-commit-routes.ts +12 -0
- package/src/runtime/routes/workspace-routes.ts +102 -0
- package/src/schedule/scheduler.ts +7 -1
- package/src/security/AGENTS.md +7 -0
- package/src/security/credential-backend.ts +1 -1
- package/src/security/encrypted-store.ts +3 -3
- package/src/security/oauth2.ts +55 -0
- package/src/security/secret-ingress.ts +174 -0
- package/src/security/secret-patterns.ts +133 -0
- package/src/security/secret-scanner.ts +28 -117
- package/src/signals/confirm.ts +12 -8
- package/src/signals/user-message.ts +18 -3
- package/src/skills/skill-memory.ts +1 -2
- package/src/tasks/task-runner.ts +7 -1
- package/src/tools/credentials/broker.ts +1 -1
- package/src/tools/credentials/metadata-store.ts +1 -1
- package/src/tools/credentials/vault.ts +2 -3
- package/src/tools/memory/definitions.ts +1 -1
- package/src/tools/memory/handlers.test.ts +2 -4
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/terminal/safe-env.ts +7 -0
- package/src/tools/tool-manifest.ts +1 -1
- package/src/util/log-redact.ts +9 -34
- package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +13 -148
- package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +7 -145
- package/src/workspace/migrations/AGENTS.md +11 -0
- package/src/workspace/migrations/runner.ts +16 -6
- package/src/workspace/migrations/types.ts +7 -0
- package/docs/architecture/keychain-broker.md +0 -69
- package/src/__tests__/keychain-broker-client.test.ts +0 -800
- package/src/cli/commands/oauth/platform.ts +0 -525
- package/src/config/bundled-skills/slack/TOOLS.json +0 -272
- package/src/config/bundled-skills/slack/tools/shared.ts +0 -34
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +0 -27
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +0 -38
- package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +0 -146
- package/src/config/bundled-skills/slack/tools/slack-configure-channels.ts +0 -105
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +0 -26
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +0 -27
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +0 -25
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +0 -372
- package/src/security/keychain-broker-client.ts +0 -446
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
|
|
3
|
+
import { orchestrateOAuthConnect } from "../../../oauth/connect-orchestrator.js";
|
|
4
|
+
import {
|
|
5
|
+
getAppByProviderAndClientId,
|
|
6
|
+
getMostRecentAppByProvider,
|
|
7
|
+
getProvider,
|
|
8
|
+
} from "../../../oauth/oauth-store.js";
|
|
9
|
+
import { getProviderBehavior } from "../../../oauth/provider-behaviors.js";
|
|
10
|
+
import { openInBrowser } from "../../../util/browser.js";
|
|
11
|
+
import { getSecureKeyViaDaemon } from "../../lib/daemon-credential-client.js";
|
|
12
|
+
import { getCliLogger } from "../../logger.js";
|
|
13
|
+
import { shouldOutputJson, writeOutput } from "../../output.js";
|
|
14
|
+
import {
|
|
15
|
+
fetchActiveConnections,
|
|
16
|
+
isManagedMode,
|
|
17
|
+
requirePlatformClient,
|
|
18
|
+
resolveService,
|
|
19
|
+
toBareProvider,
|
|
20
|
+
} from "./shared.js";
|
|
21
|
+
|
|
22
|
+
const log = getCliLogger("cli");
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Command registration
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
export function registerConnectCommand(oauth: Command): void {
|
|
29
|
+
oauth
|
|
30
|
+
.command("connect <provider>")
|
|
31
|
+
.description(
|
|
32
|
+
"Initiate an OAuth authorization flow for a provider (auto-detects managed vs BYO mode)",
|
|
33
|
+
)
|
|
34
|
+
.option("--scopes <scopes...>", "Scopes to request for the authorization")
|
|
35
|
+
.option(
|
|
36
|
+
"--open-browser",
|
|
37
|
+
"Open the auth URL in the browser and wait for completion",
|
|
38
|
+
)
|
|
39
|
+
.option("--client-id <id>", "BYO app client ID disambiguation")
|
|
40
|
+
.addHelpText(
|
|
41
|
+
"after",
|
|
42
|
+
`
|
|
43
|
+
Arguments:
|
|
44
|
+
provider Provider key, alias, or ID from 'assistant oauth providers list'.
|
|
45
|
+
Accepts canonical keys (e.g. integration:google), aliases (e.g.
|
|
46
|
+
gmail), or bare provider names (e.g. google).
|
|
47
|
+
|
|
48
|
+
Options:
|
|
49
|
+
--scopes <scopes...> Scopes to request for the authorization. In managed
|
|
50
|
+
mode, each scope must be in the provider's allowed set
|
|
51
|
+
(use full scope URLs where required). In BYO mode,
|
|
52
|
+
scopes are appended to the provider's defaults.
|
|
53
|
+
--open-browser Open the authorization URL in your browser and wait
|
|
54
|
+
for completion. In managed mode, polls for a new
|
|
55
|
+
platform connection. In BYO mode, starts a local
|
|
56
|
+
callback server and blocks until the OAuth redirect.
|
|
57
|
+
--client-id <id> BYO-only: select a specific OAuth app when multiple
|
|
58
|
+
apps exist for the same provider. Ignored for
|
|
59
|
+
platform-managed providers.
|
|
60
|
+
|
|
61
|
+
Mode detection:
|
|
62
|
+
The command checks the services config to determine whether the provider
|
|
63
|
+
runs in platform-managed or BYO (bring-your-own credentials) mode.
|
|
64
|
+
|
|
65
|
+
Managed mode: Calls the platform /start/ endpoint, returns a connect URL.
|
|
66
|
+
With --open-browser, opens the URL and polls for a new connection.
|
|
67
|
+
BYO mode: Resolves local client credentials from the OAuth app store and
|
|
68
|
+
runs the OAuth2 authorization code flow via the local orchestrator.
|
|
69
|
+
|
|
70
|
+
Examples:
|
|
71
|
+
$ assistant oauth connect google
|
|
72
|
+
$ assistant oauth connect gmail --open-browser
|
|
73
|
+
$ assistant oauth connect integration:google --scopes https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.events
|
|
74
|
+
$ assistant oauth connect google --client-id abc123 --open-browser`,
|
|
75
|
+
)
|
|
76
|
+
.action(
|
|
77
|
+
async (
|
|
78
|
+
provider: string,
|
|
79
|
+
opts: {
|
|
80
|
+
scopes?: string[];
|
|
81
|
+
openBrowser?: boolean;
|
|
82
|
+
clientId?: string;
|
|
83
|
+
},
|
|
84
|
+
cmd: Command,
|
|
85
|
+
) => {
|
|
86
|
+
const jsonMode = shouldOutputJson(cmd);
|
|
87
|
+
|
|
88
|
+
// Helper: write an error and set exit code
|
|
89
|
+
const writeError = (error: string): void => {
|
|
90
|
+
writeOutput(cmd, { ok: false, error });
|
|
91
|
+
process.exitCode = 1;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
// ---------------------------------------------------------------
|
|
96
|
+
// 1. Resolve provider key
|
|
97
|
+
// ---------------------------------------------------------------
|
|
98
|
+
const providerKey = resolveService(provider);
|
|
99
|
+
|
|
100
|
+
// ---------------------------------------------------------------
|
|
101
|
+
// 2. Validate provider exists
|
|
102
|
+
// ---------------------------------------------------------------
|
|
103
|
+
const providerRow = getProvider(providerKey);
|
|
104
|
+
if (!providerRow) {
|
|
105
|
+
writeError(
|
|
106
|
+
`Unknown provider "${providerKey}". ` +
|
|
107
|
+
`Run 'assistant oauth providers list' to see available providers.`,
|
|
108
|
+
);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------
|
|
113
|
+
// 3. Detect mode
|
|
114
|
+
// ---------------------------------------------------------------
|
|
115
|
+
const managed = isManagedMode(providerKey);
|
|
116
|
+
|
|
117
|
+
if (managed) {
|
|
118
|
+
// =============================================================
|
|
119
|
+
// MANAGED PATH
|
|
120
|
+
// =============================================================
|
|
121
|
+
|
|
122
|
+
// Warn about --client-id being ignored in managed mode
|
|
123
|
+
if (opts.clientId) {
|
|
124
|
+
log.info(
|
|
125
|
+
`Warning: --client-id is ignored for platform-managed providers. The platform manages OAuth apps for "${providerKey}".`,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const client = await requirePlatformClient(cmd);
|
|
130
|
+
if (!client) return;
|
|
131
|
+
|
|
132
|
+
// Call the platform's OAuth start endpoint
|
|
133
|
+
const startPath = `/v1/assistants/${encodeURIComponent(client.platformAssistantId)}/oauth/${encodeURIComponent(toBareProvider(providerKey))}/start/`;
|
|
134
|
+
|
|
135
|
+
const body: Record<string, unknown> = {};
|
|
136
|
+
if (opts.scopes && opts.scopes.length > 0) {
|
|
137
|
+
body.requested_scopes = opts.scopes;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const response = await client.fetch(startPath, {
|
|
141
|
+
method: "POST",
|
|
142
|
+
headers: { "Content-Type": "application/json" },
|
|
143
|
+
body: JSON.stringify(body),
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
const errorText = await response.text().catch(() => "");
|
|
148
|
+
writeError(
|
|
149
|
+
`Platform returned HTTP ${response.status}${errorText ? `: ${errorText}` : ""}`,
|
|
150
|
+
);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const result = (await response.json()) as {
|
|
155
|
+
connect_url?: string;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
if (!result.connect_url) {
|
|
159
|
+
writeError(
|
|
160
|
+
"Platform did not return a connect URL — the OAuth flow could not be started",
|
|
161
|
+
);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (opts.openBrowser) {
|
|
166
|
+
// Snapshot existing connection IDs before opening browser
|
|
167
|
+
const snapshotEntries = await fetchActiveConnections(
|
|
168
|
+
client,
|
|
169
|
+
providerKey,
|
|
170
|
+
cmd,
|
|
171
|
+
);
|
|
172
|
+
if (!snapshotEntries) {
|
|
173
|
+
// fetchActiveConnections already wrote the error output
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const snapshotIds = new Set(snapshotEntries.map((e) => e.id));
|
|
177
|
+
|
|
178
|
+
openInBrowser(result.connect_url);
|
|
179
|
+
|
|
180
|
+
if (!jsonMode) {
|
|
181
|
+
log.info(
|
|
182
|
+
`Opening browser to connect ${providerKey}. Waiting for authorization...`,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Poll for a new connection every 2s for up to 5 minutes
|
|
187
|
+
const pollIntervalMs = 2000;
|
|
188
|
+
const timeoutMs = 5 * 60 * 1000;
|
|
189
|
+
const deadline = Date.now() + timeoutMs;
|
|
190
|
+
let newConnection: {
|
|
191
|
+
id: string;
|
|
192
|
+
account_label?: string;
|
|
193
|
+
scopes_granted?: string[];
|
|
194
|
+
} | null = null;
|
|
195
|
+
|
|
196
|
+
while (Date.now() < deadline) {
|
|
197
|
+
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
198
|
+
|
|
199
|
+
const currentEntries = await fetchActiveConnections(
|
|
200
|
+
client,
|
|
201
|
+
providerKey,
|
|
202
|
+
cmd,
|
|
203
|
+
{ silent: true },
|
|
204
|
+
);
|
|
205
|
+
if (!currentEntries) continue;
|
|
206
|
+
|
|
207
|
+
const found = currentEntries.find(
|
|
208
|
+
(e) => !snapshotIds.has(e.id),
|
|
209
|
+
);
|
|
210
|
+
if (found) {
|
|
211
|
+
newConnection = found;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (newConnection) {
|
|
217
|
+
// Success — new connection found
|
|
218
|
+
if (jsonMode) {
|
|
219
|
+
writeOutput(cmd, {
|
|
220
|
+
ok: true,
|
|
221
|
+
provider: providerKey,
|
|
222
|
+
connectionId: newConnection.id,
|
|
223
|
+
accountLabel: newConnection.account_label ?? null,
|
|
224
|
+
scopesGranted: newConnection.scopes_granted ?? [],
|
|
225
|
+
});
|
|
226
|
+
} else {
|
|
227
|
+
const label = newConnection.account_label
|
|
228
|
+
? ` as ${newConnection.account_label}`
|
|
229
|
+
: "";
|
|
230
|
+
process.stdout.write(`Connected to ${providerKey}${label}\n`);
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
// Timeout — authorization may still be in progress
|
|
234
|
+
if (jsonMode) {
|
|
235
|
+
writeOutput(cmd, {
|
|
236
|
+
ok: true,
|
|
237
|
+
deferred: true,
|
|
238
|
+
provider: providerKey,
|
|
239
|
+
connectUrl: result.connect_url,
|
|
240
|
+
message:
|
|
241
|
+
"Authorization may still be in progress. Check with 'assistant oauth status <provider>'.",
|
|
242
|
+
});
|
|
243
|
+
} else {
|
|
244
|
+
process.stdout.write(
|
|
245
|
+
`Timed out waiting for authorization. It may still be in progress.\n` +
|
|
246
|
+
`Check with: assistant oauth status ${providerKey}\n`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
// No --open-browser: output the connect URL
|
|
252
|
+
if (jsonMode) {
|
|
253
|
+
writeOutput(cmd, {
|
|
254
|
+
ok: true,
|
|
255
|
+
deferred: true,
|
|
256
|
+
connectUrl: result.connect_url,
|
|
257
|
+
provider: providerKey,
|
|
258
|
+
});
|
|
259
|
+
} else {
|
|
260
|
+
process.stdout.write(result.connect_url + "\n");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
// =============================================================
|
|
265
|
+
// BYO PATH
|
|
266
|
+
// =============================================================
|
|
267
|
+
|
|
268
|
+
// a. Resolve service alias (already done above via resolveService)
|
|
269
|
+
const resolvedServiceKey = providerKey;
|
|
270
|
+
|
|
271
|
+
// b. Resolve client credentials from the DB
|
|
272
|
+
const dbApp = opts.clientId
|
|
273
|
+
? getAppByProviderAndClientId(resolvedServiceKey, opts.clientId)
|
|
274
|
+
: getMostRecentAppByProvider(resolvedServiceKey);
|
|
275
|
+
|
|
276
|
+
let clientId = opts.clientId;
|
|
277
|
+
let clientSecret: string | undefined;
|
|
278
|
+
|
|
279
|
+
if (dbApp) {
|
|
280
|
+
if (!clientId) clientId = dbApp.clientId;
|
|
281
|
+
const storedSecret = await getSecureKeyViaDaemon(
|
|
282
|
+
dbApp.clientSecretCredentialPath,
|
|
283
|
+
);
|
|
284
|
+
if (storedSecret) clientSecret = storedSecret;
|
|
285
|
+
} else if (opts.clientId) {
|
|
286
|
+
// --client-id was explicitly provided but no matching app exists
|
|
287
|
+
writeError(
|
|
288
|
+
`No registered app found for "${resolvedServiceKey}" with client ID "${opts.clientId}". ` +
|
|
289
|
+
`Register one with 'assistant oauth apps upsert'.`,
|
|
290
|
+
);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// c. Validate client_id exists
|
|
295
|
+
if (!clientId) {
|
|
296
|
+
writeError(
|
|
297
|
+
`No client_id found for "${resolvedServiceKey}". ` +
|
|
298
|
+
`Register one with 'assistant oauth apps upsert'.`,
|
|
299
|
+
);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// d. Check if client_secret is required but missing
|
|
304
|
+
if (clientSecret === undefined) {
|
|
305
|
+
const behavior = getProviderBehavior(resolvedServiceKey);
|
|
306
|
+
|
|
307
|
+
const requiresSecret =
|
|
308
|
+
behavior?.setup?.requiresClientSecret ??
|
|
309
|
+
!!(
|
|
310
|
+
providerRow?.tokenEndpointAuthMethod ||
|
|
311
|
+
providerRow?.extraParams
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
if (requiresSecret) {
|
|
315
|
+
writeError(
|
|
316
|
+
`client_secret is required for ${resolvedServiceKey} but not found. ` +
|
|
317
|
+
`Store it with 'assistant oauth apps upsert --client-secret'.`,
|
|
318
|
+
);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// e. Call the orchestrator
|
|
324
|
+
const result = await orchestrateOAuthConnect({
|
|
325
|
+
service: provider,
|
|
326
|
+
clientId,
|
|
327
|
+
clientSecret,
|
|
328
|
+
isInteractive: !!opts.openBrowser,
|
|
329
|
+
openUrl: opts.openBrowser ? openInBrowser : undefined,
|
|
330
|
+
...(opts.scopes ? { requestedScopes: opts.scopes } : {}),
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// f. Handle results
|
|
334
|
+
if (!result.success) {
|
|
335
|
+
writeError(result.error ?? "OAuth connect failed");
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (result.deferred) {
|
|
340
|
+
if (jsonMode) {
|
|
341
|
+
writeOutput(cmd, {
|
|
342
|
+
ok: true,
|
|
343
|
+
deferred: true,
|
|
344
|
+
authUrl: result.authUrl,
|
|
345
|
+
service: result.service,
|
|
346
|
+
});
|
|
347
|
+
} else {
|
|
348
|
+
process.stdout.write(
|
|
349
|
+
`\nAuthorize with ${resolvedServiceKey}:\n\n${result.authUrl}\n\nThe connection will complete automatically once you authorize.\n`,
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Interactive mode completed
|
|
356
|
+
if (jsonMode) {
|
|
357
|
+
writeOutput(cmd, {
|
|
358
|
+
ok: true,
|
|
359
|
+
grantedScopes: result.grantedScopes,
|
|
360
|
+
accountInfo: result.accountInfo,
|
|
361
|
+
});
|
|
362
|
+
} else {
|
|
363
|
+
const msg = `Connected to ${resolvedServiceKey}${result.accountInfo ? ` as ${result.accountInfo}` : ""}`;
|
|
364
|
+
process.stdout.write(msg + "\n");
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
} catch (err) {
|
|
368
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
369
|
+
writeError(message);
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
);
|
|
373
|
+
}
|