@vellumai/assistant 0.5.11 → 0.5.13
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/Dockerfile +42 -9
- package/docs/architecture/integrations.md +34 -32
- package/node_modules/@vellumai/ces-contracts/src/__tests__/grants.test.ts +7 -7
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +5 -4
- package/node_modules/@vellumai/ces-contracts/src/index.ts +7 -0
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +5 -0
- package/node_modules/@vellumai/credential-storage/src/index.ts +1 -1
- package/openapi.yaml +87 -9
- package/package.json +1 -1
- package/src/__tests__/catalog-cache.test.ts +164 -0
- package/src/__tests__/catalog-search.test.ts +61 -0
- package/src/__tests__/cli-command-risk-guard.test.ts +181 -6
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +396 -0
- package/src/__tests__/conversation-error.test.ts +3 -2
- package/src/__tests__/credential-security-invariants.test.ts +9 -15
- package/src/__tests__/credential-vault-unit.test.ts +32 -34
- package/src/__tests__/credential-vault.test.ts +25 -33
- package/src/__tests__/credentials-cli.test.ts +3 -3
- package/src/__tests__/daemon-credential-client.test.ts +2 -2
- package/src/__tests__/first-greeting.test.ts +7 -0
- package/src/__tests__/host-bash-proxy.test.ts +79 -0
- package/src/__tests__/host-cu-proxy.test.ts +90 -0
- package/src/__tests__/host-file-proxy.test.ts +89 -0
- package/src/__tests__/integration-status.test.ts +5 -5
- package/src/__tests__/list-messages-attachments.test.ts +171 -0
- package/src/__tests__/mcp-abort-signal.test.ts +205 -0
- package/src/__tests__/messaging-send-tool.test.ts +5 -5
- package/src/__tests__/navigate-settings-tab.test.ts +6 -2
- package/src/__tests__/notification-telegram-adapter.test.ts +125 -0
- package/src/__tests__/oauth-cli.test.ts +126 -119
- package/src/__tests__/oauth-provider-profiles.test.ts +55 -20
- package/src/__tests__/oauth-scope-policy.test.ts +4 -6
- package/src/__tests__/onboarding-template-contract.test.ts +2 -2
- package/src/__tests__/platform.test.ts +3 -168
- package/src/__tests__/secret-routes-managed-proxy.test.ts +78 -0
- package/src/__tests__/secure-keys-managed-failover.test.ts +73 -0
- package/src/__tests__/skill-feature-flags.test.ts +8 -0
- package/src/__tests__/skill-secret-handling-guard.test.ts +212 -0
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/slack-messaging-token-resolution.test.ts +22 -24
- package/src/__tests__/slack-share-routes.test.ts +5 -5
- package/src/__tests__/system-prompt.test.ts +39 -0
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -1
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +5 -4
- package/src/cli/AGENTS.md +47 -7
- package/src/cli/commands/browser-relay.ts +2 -17
- package/src/cli/commands/contacts.ts +6 -4
- package/src/cli/commands/conversations.ts +13 -1
- package/src/cli/commands/credential-execution.ts +16 -1
- package/src/cli/commands/credentials.ts +2 -8
- package/src/cli/commands/oauth/__tests__/connect.test.ts +29 -108
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +13 -87
- package/src/cli/commands/oauth/__tests__/mode.test.ts +22 -69
- package/src/cli/commands/oauth/__tests__/ping.test.ts +20 -79
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +574 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +416 -0
- package/src/cli/commands/oauth/__tests__/status.test.ts +12 -40
- package/src/cli/commands/oauth/__tests__/token.test.ts +3 -50
- package/src/cli/commands/oauth/apps.ts +63 -44
- package/src/cli/commands/oauth/connect.ts +187 -155
- package/src/cli/commands/oauth/disconnect.ts +27 -75
- package/src/cli/commands/oauth/index.ts +36 -46
- package/src/cli/commands/oauth/mode.ts +22 -34
- package/src/cli/commands/oauth/ping.ts +19 -45
- package/src/cli/commands/oauth/providers.ts +569 -62
- package/src/cli/commands/oauth/request.ts +36 -48
- package/src/cli/commands/oauth/shared.ts +1 -19
- package/src/cli/commands/oauth/status.ts +14 -25
- package/src/cli/commands/oauth/token.ts +25 -34
- package/src/cli/commands/platform/__tests__/connect.test.ts +224 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +237 -0
- package/src/cli/commands/platform/__tests__/status.test.ts +246 -0
- package/src/cli/commands/platform/connect.ts +104 -0
- package/src/cli/commands/platform/disconnect.ts +118 -0
- package/src/cli/commands/{platform.ts → platform/index.ts} +108 -38
- package/src/cli/commands/sequence.ts +5 -4
- package/src/cli/commands/shotgun.ts +16 -0
- package/src/cli/commands/skills.ts +173 -41
- package/src/cli/commands/usage.ts +5 -11
- package/src/cli/lib/daemon-credential-client.ts +22 -38
- package/src/cli/program.ts +1 -1
- package/src/config/assistant-feature-flags.ts +3 -7
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
- package/src/config/bundled-skills/conversations/SKILL.md +20 -0
- package/src/config/bundled-skills/conversations/TOOLS.json +23 -0
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +66 -0
- package/src/config/bundled-skills/gmail/SKILL.md +13 -13
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +3 -3
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +2 -2
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +2 -2
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +1 -1
- package/src/config/bundled-skills/google-calendar/SKILL.md +10 -4
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +7 -7
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +5 -6
- package/src/config/bundled-skills/settings/TOOLS.json +5 -3
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +4 -2
- package/src/config/bundled-tool-registry.ts +5 -0
- package/src/config/feature-flag-registry.json +2 -2
- package/src/credential-execution/client.ts +15 -3
- package/src/daemon/conversation-agent-loop.ts +2 -0
- package/src/daemon/conversation-error.ts +36 -6
- package/src/daemon/conversation-messaging.ts +9 -0
- package/src/daemon/conversation-runtime-assembly.ts +33 -0
- package/src/daemon/conversation-surfaces.ts +120 -14
- package/src/daemon/conversation.ts +5 -0
- package/src/daemon/first-greeting.ts +6 -1
- package/src/daemon/handlers/skills.ts +148 -3
- package/src/daemon/host-bash-proxy.ts +16 -0
- package/src/daemon/host-cu-proxy.ts +16 -0
- package/src/daemon/host-file-proxy.ts +16 -0
- package/src/daemon/lifecycle.ts +56 -5
- package/src/daemon/message-types/conversations.ts +1 -0
- package/src/daemon/message-types/guardian-actions.ts +2 -0
- package/src/daemon/message-types/host-bash.ts +6 -1
- package/src/daemon/message-types/host-cu.ts +6 -1
- package/src/daemon/message-types/host-file.ts +6 -1
- package/src/daemon/message-types/integrations.ts +0 -1
- package/src/daemon/server.ts +29 -2
- package/src/hooks/cli.ts +74 -0
- package/src/inbound/platform-callback-registration.ts +7 -12
- package/src/index.ts +0 -12
- package/src/mcp/client.ts +6 -1
- package/src/mcp/manager.ts +2 -1
- package/src/memory/conversation-crud.ts +92 -3
- package/src/memory/conversation-key-store.ts +26 -0
- package/src/memory/conversation-queries.ts +6 -6
- package/src/memory/db-init.ts +16 -0
- package/src/memory/journal-memory.ts +8 -2
- package/src/memory/migrations/196-messages-conversation-created-at-index.ts +9 -0
- package/src/memory/migrations/196-strip-integration-prefix-from-provider-keys.ts +186 -0
- package/src/memory/migrations/197-oauth-providers-behavior-columns.ts +29 -0
- package/src/memory/migrations/198-drop-setup-skill-id-column.ts +11 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/oauth.ts +11 -0
- package/src/messaging/provider.ts +13 -12
- package/src/messaging/providers/gmail/adapter.ts +44 -35
- package/src/messaging/providers/slack/adapter.ts +63 -33
- package/src/messaging/providers/telegram-bot/adapter.ts +6 -8
- package/src/messaging/providers/whatsapp/adapter.ts +6 -8
- package/src/notifications/adapters/telegram.ts +78 -2
- package/src/oauth/__tests__/identity-verifier.test.ts +464 -0
- package/src/oauth/byo-connection.test.ts +22 -24
- package/src/oauth/connect-orchestrator.ts +37 -76
- package/src/oauth/connect-types.ts +7 -65
- package/src/oauth/connection-resolver.test.ts +13 -13
- package/src/oauth/connection-resolver.ts +3 -4
- package/src/oauth/identity-verifier.ts +177 -0
- package/src/oauth/oauth-store.ts +228 -3
- package/src/oauth/platform-connection.test.ts +56 -6
- package/src/oauth/platform-connection.ts +8 -1
- package/src/oauth/seed-providers.ts +247 -34
- package/src/permissions/checker.ts +127 -1
- package/src/prompts/journal-context.ts +4 -1
- package/src/prompts/system-prompt.ts +54 -9
- package/src/prompts/templates/BOOTSTRAP.md +16 -5
- package/src/providers/anthropic/client.ts +2 -33
- package/src/runtime/guardian-action-service.ts +7 -2
- package/src/runtime/http-server.ts +12 -18
- package/src/runtime/http-types.ts +8 -1
- package/src/runtime/migrations/rebind-secrets-screen.ts +2 -2
- package/src/runtime/routes/conversation-management-routes.ts +31 -0
- package/src/runtime/routes/conversation-routes.ts +79 -4
- package/src/runtime/routes/guardian-action-routes.ts +15 -2
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -8
- package/src/runtime/routes/integrations/slack/share.ts +1 -1
- package/src/runtime/routes/oauth-apps.ts +2 -1
- package/src/runtime/routes/secret-routes.ts +45 -15
- package/src/runtime/routes/settings-routes.ts +12 -19
- package/src/runtime/routes/skills-routes.ts +45 -4
- package/src/schedule/integration-status.ts +2 -2
- package/src/security/ces-rpc-credential-backend.ts +19 -16
- package/src/security/oauth-completion-page.ts +153 -0
- package/src/security/oauth2.ts +3 -17
- package/src/security/secure-keys.ts +207 -7
- package/src/security/token-manager.ts +3 -6
- package/src/signals/bash.ts +6 -1
- package/src/skills/catalog-cache.ts +44 -0
- package/src/skills/catalog-search.ts +18 -0
- package/src/tools/browser/browser-manager.ts +2 -2
- package/src/tools/credentials/post-connect-hooks.ts +1 -1
- package/src/tools/credentials/vault.ts +34 -45
- package/src/tools/host-terminal/host-shell.ts +16 -3
- package/src/tools/mcp/mcp-tool-factory.ts +2 -1
- package/src/tools/skills/sandbox-runner.ts +16 -3
- package/src/tools/terminal/shell.ts +16 -3
- package/src/util/logger.ts +11 -1
- package/src/util/platform.ts +1 -91
- package/src/util/sentry-log-stream.ts +51 -0
- package/src/watcher/providers/github.ts +2 -2
- package/src/watcher/providers/gmail.ts +1 -1
- package/src/watcher/providers/google-calendar.ts +1 -1
- package/src/watcher/providers/linear.ts +2 -2
- package/src/workspace/migrations/011-backfill-installation-id.ts +5 -3
- package/src/workspace/migrations/020-rename-oauth-skill-dirs.ts +119 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/cli/commands/oauth/connections.ts +0 -255
- package/src/oauth/provider-behaviors.ts +0 -634
|
@@ -3,11 +3,18 @@ import { type Command } from "commander";
|
|
|
3
3
|
import { loadConfig } from "../../../config/loader.js";
|
|
4
4
|
import { getOAuthCallbackUrl } from "../../../inbound/public-ingress-urls.js";
|
|
5
5
|
import {
|
|
6
|
+
deleteApp,
|
|
7
|
+
deleteConnection,
|
|
8
|
+
deleteProvider,
|
|
9
|
+
disconnectOAuthProvider,
|
|
6
10
|
getProvider,
|
|
11
|
+
listApps,
|
|
12
|
+
listConnections,
|
|
7
13
|
listProviders,
|
|
8
14
|
registerProvider,
|
|
15
|
+
updateProvider,
|
|
9
16
|
} from "../../../oauth/oauth-store.js";
|
|
10
|
-
import {
|
|
17
|
+
import { SEEDED_PROVIDER_KEYS } from "../../../oauth/seed-providers.js";
|
|
11
18
|
import { getCliLogger } from "../../logger.js";
|
|
12
19
|
import { shouldOutputJson, writeOutput } from "../../output.js";
|
|
13
20
|
|
|
@@ -17,21 +24,19 @@ const LOOPBACK_CALLBACK_PATH = "/oauth/callback";
|
|
|
17
24
|
|
|
18
25
|
/** Resolve the redirect URI for a provider based on its callback transport. */
|
|
19
26
|
function resolveRedirectUri(
|
|
20
|
-
providerKey: string,
|
|
21
27
|
callbackTransport: string | null,
|
|
28
|
+
loopbackPort: number | null,
|
|
22
29
|
): string | null {
|
|
23
30
|
const transport = callbackTransport ?? "loopback";
|
|
24
31
|
if (transport === "loopback") {
|
|
25
|
-
|
|
26
|
-
const port = behavior?.loopbackPort;
|
|
27
|
-
if (!port) {
|
|
32
|
+
if (!loopbackPort) {
|
|
28
33
|
// No fixed port — loopback still works at runtime with an OS-assigned
|
|
29
34
|
// port, but we can't predict the redirect URI ahead of time. Return
|
|
30
35
|
// a sentinel so callers know the transport is loopback-dynamic rather
|
|
31
36
|
// than unsupported.
|
|
32
37
|
return "http://localhost:<dynamic>/oauth/callback";
|
|
33
38
|
}
|
|
34
|
-
return `http://localhost:${
|
|
39
|
+
return `http://localhost:${loopbackPort}${LOOPBACK_CALLBACK_PATH}`;
|
|
35
40
|
}
|
|
36
41
|
// Gateway transport — resolve from public ingress config.
|
|
37
42
|
// Try the explicit publicBaseUrl first, then fall back to platform
|
|
@@ -55,13 +60,31 @@ function parseProviderRow(row: ReturnType<typeof getProvider>) {
|
|
|
55
60
|
description: row.description ?? null,
|
|
56
61
|
dashboardUrl: row.dashboardUrl ?? null,
|
|
57
62
|
clientIdPlaceholder: row.clientIdPlaceholder ?? null,
|
|
58
|
-
requiresClientSecret: row.requiresClientSecret ?? 1,
|
|
63
|
+
requiresClientSecret: !!(row.requiresClientSecret ?? 1),
|
|
64
|
+
supportsManagedMode: !!row.managedServiceConfigKey,
|
|
59
65
|
defaultScopes: row.defaultScopes ? JSON.parse(row.defaultScopes) : [],
|
|
60
66
|
scopePolicy: row.scopePolicy ? JSON.parse(row.scopePolicy) : {},
|
|
61
67
|
extraParams: row.extraParams ? JSON.parse(row.extraParams) : null,
|
|
62
68
|
pingHeaders: row.pingHeaders ? JSON.parse(row.pingHeaders) : null,
|
|
63
69
|
pingBody: row.pingBody ? JSON.parse(row.pingBody) : null,
|
|
64
|
-
|
|
70
|
+
loopbackPort: row.loopbackPort ?? null,
|
|
71
|
+
injectionTemplates: row.injectionTemplates
|
|
72
|
+
? JSON.parse(row.injectionTemplates)
|
|
73
|
+
: null,
|
|
74
|
+
appType: row.appType ?? null,
|
|
75
|
+
setupNotes: row.setupNotes ? JSON.parse(row.setupNotes) : null,
|
|
76
|
+
identityUrl: row.identityUrl ?? null,
|
|
77
|
+
identityMethod: row.identityMethod ?? null,
|
|
78
|
+
identityHeaders: row.identityHeaders
|
|
79
|
+
? JSON.parse(row.identityHeaders)
|
|
80
|
+
: null,
|
|
81
|
+
identityBody: row.identityBody ? JSON.parse(row.identityBody) : null,
|
|
82
|
+
identityResponsePaths: row.identityResponsePaths
|
|
83
|
+
? JSON.parse(row.identityResponsePaths)
|
|
84
|
+
: null,
|
|
85
|
+
identityFormat: row.identityFormat ?? null,
|
|
86
|
+
identityOkField: row.identityOkField ?? null,
|
|
87
|
+
redirectUri: resolveRedirectUri(row.callbackTransport, row.loopbackPort),
|
|
65
88
|
createdAt: new Date(row.createdAt).toISOString(),
|
|
66
89
|
updatedAt: new Date(row.updatedAt).toISOString(),
|
|
67
90
|
};
|
|
@@ -71,7 +94,7 @@ export function registerProviderCommands(oauth: Command): void {
|
|
|
71
94
|
const providers = oauth
|
|
72
95
|
.command("providers")
|
|
73
96
|
.description(
|
|
74
|
-
"
|
|
97
|
+
"Fetch configured OAuth providers and register custom providers of your own",
|
|
75
98
|
);
|
|
76
99
|
|
|
77
100
|
providers.addHelpText(
|
|
@@ -83,7 +106,7 @@ authorization URL, token URL, default scopes, and other endpoint details.
|
|
|
83
106
|
They are seeded on startup for built-in integrations (e.g. Google, Slack,
|
|
84
107
|
GitHub) but can also be registered dynamically via the "register" subcommand.
|
|
85
108
|
|
|
86
|
-
Each provider is identified by a provider key (e.g. "
|
|
109
|
+
Each provider is identified by a provider key (e.g. "google").`,
|
|
87
110
|
);
|
|
88
111
|
|
|
89
112
|
// ---------------------------------------------------------------------------
|
|
@@ -159,7 +182,7 @@ Examples:
|
|
|
159
182
|
"after",
|
|
160
183
|
`
|
|
161
184
|
Arguments:
|
|
162
|
-
provider-key
|
|
185
|
+
provider-key Provider key (e.g. "google").
|
|
163
186
|
Must match the key used during registration or seeding.
|
|
164
187
|
|
|
165
188
|
Returns the full provider configuration including auth URL, token URL,
|
|
@@ -167,8 +190,8 @@ default scopes, scope policy, and extra parameters. Exits with code 1
|
|
|
167
190
|
if the provider key is not found.
|
|
168
191
|
|
|
169
192
|
Examples:
|
|
170
|
-
$ assistant oauth providers get
|
|
171
|
-
$ assistant oauth providers get
|
|
193
|
+
$ assistant oauth providers get google
|
|
194
|
+
$ assistant oauth providers get twitter --json`,
|
|
172
195
|
)
|
|
173
196
|
.action((providerKey: string, _opts: unknown, cmd: Command) => {
|
|
174
197
|
try {
|
|
@@ -200,84 +223,148 @@ Examples:
|
|
|
200
223
|
.description("Register a new OAuth provider configuration")
|
|
201
224
|
.requiredOption(
|
|
202
225
|
"--provider-key <key>",
|
|
203
|
-
"Unique provider key (e.g. \"
|
|
226
|
+
"Unique provider key (e.g. \"custom-service\"). Must not collide with an existing key from 'assistant oauth providers list'.",
|
|
227
|
+
)
|
|
228
|
+
.requiredOption(
|
|
229
|
+
"--auth-url <url>",
|
|
230
|
+
"OAuth authorization endpoint URL (e.g. https://accounts.example.com/o/oauth2/auth)",
|
|
231
|
+
)
|
|
232
|
+
.requiredOption(
|
|
233
|
+
"--token-url <url>",
|
|
234
|
+
"OAuth token endpoint URL (e.g. https://oauth2.example.com/token)",
|
|
235
|
+
)
|
|
236
|
+
.option("--base-url <url>", "API base URL for the service")
|
|
237
|
+
.option("--userinfo-url <url>", "OpenID Connect userinfo endpoint URL")
|
|
238
|
+
.option(
|
|
239
|
+
"--scopes <scopes>",
|
|
240
|
+
'Comma-separated default scopes (e.g. "read,write,profile")',
|
|
241
|
+
)
|
|
242
|
+
.option(
|
|
243
|
+
"--token-auth-method <method>",
|
|
244
|
+
'How the client authenticates at the token endpoint: "client_secret_post" or "client_secret_basic"',
|
|
245
|
+
)
|
|
246
|
+
.option(
|
|
247
|
+
"--callback-transport <transport>",
|
|
248
|
+
'OAuth callback transport: "loopback" (local HTTP server, default) or "gateway" (public ingress)',
|
|
204
249
|
)
|
|
205
|
-
.requiredOption("--auth-url <url>", "Authorization endpoint URL")
|
|
206
|
-
.requiredOption("--token-url <url>", "Token endpoint URL")
|
|
207
|
-
.option("--base-url <url>", "API base URL")
|
|
208
|
-
.option("--userinfo-url <url>", "Userinfo endpoint URL")
|
|
209
|
-
.option("--scopes <scopes>", "Comma-separated default scopes")
|
|
210
|
-
.option("--token-auth-method <method>", "Token endpoint auth method")
|
|
211
|
-
.option("--callback-transport <transport>", "Callback transport")
|
|
212
250
|
.option(
|
|
213
251
|
"--ping-url <url>",
|
|
214
|
-
|
|
252
|
+
'Health-check endpoint URL for token validation (e.g. "https://api.example.com/user"). Used by "assistant oauth ping" to verify a stored token.',
|
|
215
253
|
)
|
|
216
254
|
.option(
|
|
217
255
|
"--ping-method <method>",
|
|
218
|
-
"HTTP method for the ping endpoint (default
|
|
256
|
+
"HTTP method for the ping endpoint: GET (default) or POST",
|
|
219
257
|
)
|
|
220
258
|
.option(
|
|
221
259
|
"--ping-headers <json>",
|
|
222
|
-
|
|
260
|
+
'JSON object of extra headers for the ping request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
|
|
261
|
+
)
|
|
262
|
+
.option(
|
|
263
|
+
"--ping-body <json>",
|
|
264
|
+
'JSON body to send with the ping request (e.g. \'{"query":"{ viewer { id } }"}\')',
|
|
265
|
+
)
|
|
266
|
+
.option(
|
|
267
|
+
"--display-name <name>",
|
|
268
|
+
"Human-readable display name for the provider",
|
|
269
|
+
)
|
|
270
|
+
.option("--description <text>", "Short description of the provider")
|
|
271
|
+
.option(
|
|
272
|
+
"--dashboard-url <url>",
|
|
273
|
+
"URL to the provider's developer console / dashboard",
|
|
274
|
+
)
|
|
275
|
+
.option(
|
|
276
|
+
"--client-id-placeholder <text>",
|
|
277
|
+
"Placeholder text shown in the client ID input field",
|
|
223
278
|
)
|
|
224
|
-
.option("--ping-body <json>", "JSON body to send with the ping request")
|
|
225
|
-
.option("--display-name <name>", "Display name for the provider")
|
|
226
|
-
.option("--description <text>", "Short description")
|
|
227
|
-
.option("--dashboard-url <url>", "Developer console URL")
|
|
228
|
-
.option("--client-id-placeholder <text>", "Placeholder for client ID field")
|
|
229
279
|
.option(
|
|
230
280
|
"--no-client-secret",
|
|
231
|
-
"
|
|
281
|
+
"Mark this provider as not requiring a client secret (default: required)",
|
|
282
|
+
)
|
|
283
|
+
.option(
|
|
284
|
+
"--loopback-port <port>",
|
|
285
|
+
"Fixed port for the local OAuth callback server (e.g. 17322). When set, the redirect URI is http://localhost:<port>/oauth/callback",
|
|
286
|
+
)
|
|
287
|
+
.option(
|
|
288
|
+
"--injection-templates <json>",
|
|
289
|
+
'JSON array of token injection templates — each with hostPattern, injectionType, headerName, valuePrefix (e.g. \'[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]\')',
|
|
290
|
+
)
|
|
291
|
+
.option(
|
|
292
|
+
"--app-type <type>",
|
|
293
|
+
'What the provider calls its OAuth apps (e.g. "OAuth App", "Desktop app", "Public integration")',
|
|
294
|
+
)
|
|
295
|
+
.option(
|
|
296
|
+
"--identity-url <url>",
|
|
297
|
+
"Identity verification endpoint URL — called after OAuth to identify the connected account",
|
|
298
|
+
)
|
|
299
|
+
.option(
|
|
300
|
+
"--identity-method <method>",
|
|
301
|
+
"HTTP method for the identity endpoint: GET (default) or POST",
|
|
302
|
+
)
|
|
303
|
+
.option(
|
|
304
|
+
"--identity-headers <json>",
|
|
305
|
+
'JSON object of extra headers for the identity request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
|
|
306
|
+
)
|
|
307
|
+
.option(
|
|
308
|
+
"--identity-body <body>",
|
|
309
|
+
'JSON body to send with the identity request (e.g. \'{"query":"{ viewer { email } }"}\')',
|
|
310
|
+
)
|
|
311
|
+
.option(
|
|
312
|
+
"--identity-response-paths <paths>",
|
|
313
|
+
'Comma-separated dot-notation paths to extract identity from the response (e.g. "email,name,person.email")',
|
|
314
|
+
)
|
|
315
|
+
.option(
|
|
316
|
+
"--identity-format <template>",
|
|
317
|
+
'Format template for the extracted identity using ${pathName} tokens from --identity-response-paths (e.g. "@${login}" or "@${user} (${team})")',
|
|
318
|
+
)
|
|
319
|
+
.option(
|
|
320
|
+
"--identity-ok-field <field>",
|
|
321
|
+
'Dot-notation path to a boolean field that must be truthy for the response to be considered valid (e.g. "ok")',
|
|
322
|
+
)
|
|
323
|
+
.option(
|
|
324
|
+
"--setup-notes <json>",
|
|
325
|
+
'JSON array of setup instruction notes shown during guided setup (e.g. \'["Enable the Gmail API","Add test users"]\')',
|
|
232
326
|
)
|
|
233
327
|
.addHelpText(
|
|
234
328
|
"after",
|
|
235
329
|
`
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
--base-url Optional API base URL for the service.
|
|
242
|
-
--userinfo-url Optional OpenID Connect userinfo endpoint.
|
|
243
|
-
--scopes Comma-separated list of default scopes (e.g. "read,write,profile").
|
|
244
|
-
--token-auth-method How the client authenticates at the token endpoint
|
|
245
|
-
(e.g. "client_secret_post", "client_secret_basic").
|
|
246
|
-
--callback-transport Transport method for the OAuth callback.
|
|
247
|
-
--ping-url Optional URL for a lightweight health-check endpoint.
|
|
248
|
-
Used by "assistant oauth ping" to validate that a stored token
|
|
249
|
-
is still functional (e.g. "https://api.example.com/user").
|
|
250
|
-
--ping-method HTTP method for the ping health-check (GET, POST). Defaults to GET.
|
|
251
|
-
--ping-headers JSON object of extra HTTP headers for the ping request
|
|
252
|
-
(e.g. '{"Notion-Version":"2022-06-28"}').
|
|
253
|
-
--ping-body JSON body to send with the ping request
|
|
254
|
-
(e.g. '{"query":"{ viewer { id } }"}').
|
|
255
|
-
--display-name Optional human-readable display name for the provider.
|
|
256
|
-
--description Optional short description of the provider.
|
|
257
|
-
--dashboard-url Optional URL to the provider's developer console / dashboard.
|
|
258
|
-
--client-id-placeholder Optional placeholder text for the client ID input field.
|
|
259
|
-
--no-client-secret If set, marks the provider as not requiring a client secret
|
|
260
|
-
(sets requires_client_secret to 0). Default is true (1).
|
|
261
|
-
|
|
262
|
-
Registers a new OAuth provider configuration in the local store. This is
|
|
263
|
-
used for custom integrations not covered by the built-in provider seeds.
|
|
330
|
+
Registers a new OAuth provider configuration in the local store for custom
|
|
331
|
+
integrations not covered by the built-in provider seeds. The provider key
|
|
332
|
+
must be unique — if it collides with an existing key, the command fails.
|
|
333
|
+
Run 'assistant oauth providers list' to see existing keys.
|
|
334
|
+
|
|
264
335
|
On success, returns the full provider row including generated timestamps.
|
|
336
|
+
After registering, create an OAuth app with 'assistant oauth apps create'
|
|
337
|
+
and then connect with 'assistant oauth connect <provider-key>'.
|
|
338
|
+
|
|
339
|
+
Token injection templates control how the OAuth access token is injected
|
|
340
|
+
into outgoing HTTP requests matched by host pattern. Identity config
|
|
341
|
+
defines how the assistant verifies the connected account after OAuth.
|
|
265
342
|
|
|
266
343
|
Examples:
|
|
267
344
|
$ assistant oauth providers register \\
|
|
268
|
-
--provider-key
|
|
345
|
+
--provider-key custom-api \\
|
|
269
346
|
--auth-url https://custom-api.example.com/oauth/authorize \\
|
|
270
347
|
--token-url https://custom-api.example.com/oauth/token
|
|
271
348
|
$ assistant oauth providers register \\
|
|
272
|
-
--provider-key
|
|
349
|
+
--provider-key my-service \\
|
|
273
350
|
--auth-url https://my-service.com/auth \\
|
|
274
351
|
--token-url https://my-service.com/token \\
|
|
275
352
|
--scopes read,write --json
|
|
276
353
|
$ assistant oauth providers register \\
|
|
277
|
-
--provider-key
|
|
354
|
+
--provider-key my-graphql-api \\
|
|
278
355
|
--auth-url https://example.com/auth \\
|
|
279
356
|
--token-url https://example.com/token \\
|
|
280
|
-
--ping-url https://example.com/
|
|
357
|
+
--ping-url https://example.com/graphql \\
|
|
358
|
+
--ping-method POST \\
|
|
359
|
+
--ping-body '{"query":"{ viewer { id } }"}'
|
|
360
|
+
$ assistant oauth providers register \\
|
|
361
|
+
--provider-key my-api \\
|
|
362
|
+
--auth-url https://example.com/auth \\
|
|
363
|
+
--token-url https://example.com/token \\
|
|
364
|
+
--loopback-port 17400 \\
|
|
365
|
+
--injection-templates '[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]' \\
|
|
366
|
+
--identity-url https://api.example.com/me \\
|
|
367
|
+
--identity-response-paths email,name`,
|
|
281
368
|
)
|
|
282
369
|
.action(
|
|
283
370
|
(
|
|
@@ -299,6 +386,17 @@ Examples:
|
|
|
299
386
|
dashboardUrl?: string;
|
|
300
387
|
clientIdPlaceholder?: string;
|
|
301
388
|
clientSecret: boolean;
|
|
389
|
+
loopbackPort?: string;
|
|
390
|
+
injectionTemplates?: string;
|
|
391
|
+
appType?: string;
|
|
392
|
+
identityUrl?: string;
|
|
393
|
+
identityMethod?: string;
|
|
394
|
+
identityHeaders?: string;
|
|
395
|
+
identityBody?: string;
|
|
396
|
+
identityResponsePaths?: string;
|
|
397
|
+
identityFormat?: string;
|
|
398
|
+
identityOkField?: string;
|
|
399
|
+
setupNotes?: string;
|
|
302
400
|
},
|
|
303
401
|
cmd: Command,
|
|
304
402
|
) => {
|
|
@@ -324,9 +422,418 @@ Examples:
|
|
|
324
422
|
dashboardUrl: opts.dashboardUrl,
|
|
325
423
|
clientIdPlaceholder: opts.clientIdPlaceholder,
|
|
326
424
|
requiresClientSecret: opts.clientSecret ? 1 : 0,
|
|
425
|
+
loopbackPort: opts.loopbackPort
|
|
426
|
+
? parseInt(opts.loopbackPort, 10)
|
|
427
|
+
: undefined,
|
|
428
|
+
injectionTemplates: opts.injectionTemplates
|
|
429
|
+
? JSON.parse(opts.injectionTemplates)
|
|
430
|
+
: undefined,
|
|
431
|
+
appType: opts.appType,
|
|
432
|
+
identityUrl: opts.identityUrl,
|
|
433
|
+
identityMethod: opts.identityMethod,
|
|
434
|
+
identityHeaders: opts.identityHeaders
|
|
435
|
+
? JSON.parse(opts.identityHeaders)
|
|
436
|
+
: undefined,
|
|
437
|
+
identityBody: opts.identityBody
|
|
438
|
+
? JSON.parse(opts.identityBody)
|
|
439
|
+
: undefined,
|
|
440
|
+
identityResponsePaths: opts.identityResponsePaths
|
|
441
|
+
? opts.identityResponsePaths.split(",")
|
|
442
|
+
: undefined,
|
|
443
|
+
identityFormat: opts.identityFormat,
|
|
444
|
+
identityOkField: opts.identityOkField,
|
|
445
|
+
setupNotes: opts.setupNotes
|
|
446
|
+
? JSON.parse(opts.setupNotes)
|
|
447
|
+
: undefined,
|
|
327
448
|
});
|
|
328
449
|
|
|
329
450
|
writeOutput(cmd, parseProviderRow(row));
|
|
451
|
+
} catch (err) {
|
|
452
|
+
let message = err instanceof Error ? err.message : String(err);
|
|
453
|
+
if (message.includes("already exists")) {
|
|
454
|
+
message += ` Run 'assistant oauth providers list' to see existing providers, or choose a different --provider-key.`;
|
|
455
|
+
}
|
|
456
|
+
writeOutput(cmd, { ok: false, error: message });
|
|
457
|
+
process.exitCode = 1;
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
// ---------------------------------------------------------------------------
|
|
463
|
+
// providers update <provider-key>
|
|
464
|
+
// ---------------------------------------------------------------------------
|
|
465
|
+
|
|
466
|
+
providers
|
|
467
|
+
.command("update <provider-key>")
|
|
468
|
+
.description("Update an existing custom OAuth provider configuration")
|
|
469
|
+
.option(
|
|
470
|
+
"--auth-url <url>",
|
|
471
|
+
"OAuth authorization endpoint URL (e.g. https://accounts.example.com/o/oauth2/auth)",
|
|
472
|
+
)
|
|
473
|
+
.option(
|
|
474
|
+
"--token-url <url>",
|
|
475
|
+
"OAuth token endpoint URL (e.g. https://oauth2.example.com/token)",
|
|
476
|
+
)
|
|
477
|
+
.option("--base-url <url>", "API base URL for the service")
|
|
478
|
+
.option("--userinfo-url <url>", "OpenID Connect userinfo endpoint URL")
|
|
479
|
+
.option(
|
|
480
|
+
"--scopes <scopes>",
|
|
481
|
+
'Comma-separated default scopes (e.g. "read,write,profile")',
|
|
482
|
+
)
|
|
483
|
+
.option(
|
|
484
|
+
"--token-auth-method <method>",
|
|
485
|
+
'How the client authenticates at the token endpoint: "client_secret_post" or "client_secret_basic"',
|
|
486
|
+
)
|
|
487
|
+
.option(
|
|
488
|
+
"--callback-transport <transport>",
|
|
489
|
+
'OAuth callback transport: "loopback" (local HTTP server, default) or "gateway" (public ingress)',
|
|
490
|
+
)
|
|
491
|
+
.option(
|
|
492
|
+
"--ping-url <url>",
|
|
493
|
+
'Health-check endpoint URL for token validation (e.g. "https://api.example.com/user"). Used by "assistant oauth ping" to verify a stored token.',
|
|
494
|
+
)
|
|
495
|
+
.option(
|
|
496
|
+
"--ping-method <method>",
|
|
497
|
+
"HTTP method for the ping endpoint: GET (default) or POST",
|
|
498
|
+
)
|
|
499
|
+
.option(
|
|
500
|
+
"--ping-headers <json>",
|
|
501
|
+
'JSON object of extra headers for the ping request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
|
|
502
|
+
)
|
|
503
|
+
.option(
|
|
504
|
+
"--ping-body <json>",
|
|
505
|
+
'JSON body to send with the ping request (e.g. \'{"query":"{ viewer { id } }"}\')',
|
|
506
|
+
)
|
|
507
|
+
.option(
|
|
508
|
+
"--display-name <name>",
|
|
509
|
+
"Human-readable display name for the provider",
|
|
510
|
+
)
|
|
511
|
+
.option("--description <text>", "Short description of the provider")
|
|
512
|
+
.option(
|
|
513
|
+
"--dashboard-url <url>",
|
|
514
|
+
"URL to the provider's developer console / dashboard",
|
|
515
|
+
)
|
|
516
|
+
.option(
|
|
517
|
+
"--client-id-placeholder <text>",
|
|
518
|
+
"Placeholder text shown in the client ID input field",
|
|
519
|
+
)
|
|
520
|
+
.option(
|
|
521
|
+
"--no-client-secret",
|
|
522
|
+
"Mark this provider as not requiring a client secret (default: required)",
|
|
523
|
+
)
|
|
524
|
+
.option(
|
|
525
|
+
"--loopback-port <port>",
|
|
526
|
+
"Fixed port for the local OAuth callback server (e.g. 17322). When set, the redirect URI is http://localhost:<port>/oauth/callback",
|
|
527
|
+
)
|
|
528
|
+
.option(
|
|
529
|
+
"--injection-templates <json>",
|
|
530
|
+
'JSON array of token injection templates — each with hostPattern, injectionType, headerName, valuePrefix (e.g. \'[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]\')',
|
|
531
|
+
)
|
|
532
|
+
.option(
|
|
533
|
+
"--app-type <type>",
|
|
534
|
+
'What the provider calls its OAuth apps (e.g. "OAuth App", "Desktop app", "Public integration")',
|
|
535
|
+
)
|
|
536
|
+
.option(
|
|
537
|
+
"--identity-url <url>",
|
|
538
|
+
"Identity verification endpoint URL — called after OAuth to identify the connected account",
|
|
539
|
+
)
|
|
540
|
+
.option(
|
|
541
|
+
"--identity-method <method>",
|
|
542
|
+
"HTTP method for the identity endpoint: GET (default) or POST",
|
|
543
|
+
)
|
|
544
|
+
.option(
|
|
545
|
+
"--identity-headers <json>",
|
|
546
|
+
'JSON object of extra headers for the identity request (e.g. \'{"Notion-Version":"2022-06-28"}\')',
|
|
547
|
+
)
|
|
548
|
+
.option(
|
|
549
|
+
"--identity-body <body>",
|
|
550
|
+
'JSON body to send with the identity request (e.g. \'{"query":"{ viewer { email } }"}\')',
|
|
551
|
+
)
|
|
552
|
+
.option(
|
|
553
|
+
"--identity-response-paths <paths>",
|
|
554
|
+
'Comma-separated dot-notation paths to extract identity from the response (e.g. "email,name,person.email")',
|
|
555
|
+
)
|
|
556
|
+
.option(
|
|
557
|
+
"--identity-format <template>",
|
|
558
|
+
'Format template for the extracted identity using ${pathName} tokens from --identity-response-paths (e.g. "@${login}" or "@${user} (${team})")',
|
|
559
|
+
)
|
|
560
|
+
.option(
|
|
561
|
+
"--identity-ok-field <field>",
|
|
562
|
+
'Dot-notation path to a boolean field that must be truthy for the response to be considered valid (e.g. "ok")',
|
|
563
|
+
)
|
|
564
|
+
.option(
|
|
565
|
+
"--setup-notes <json>",
|
|
566
|
+
'JSON array of setup instruction notes shown during guided setup (e.g. \'["Enable the Gmail API","Add test users"]\')',
|
|
567
|
+
)
|
|
568
|
+
.addHelpText(
|
|
569
|
+
"after",
|
|
570
|
+
`
|
|
571
|
+
Arguments:
|
|
572
|
+
provider-key Provider key to update (e.g. "custom-api").
|
|
573
|
+
Run 'assistant oauth providers list' to see all registered providers.
|
|
574
|
+
|
|
575
|
+
Only the fields you specify are updated — all other fields remain unchanged.
|
|
576
|
+
Built-in providers (e.g. "google", "slack") cannot be updated; they are
|
|
577
|
+
managed by the system and reset on startup. To create a custom provider with
|
|
578
|
+
different settings, use 'assistant oauth providers register'.
|
|
579
|
+
|
|
580
|
+
Token injection templates control how the OAuth access token is injected
|
|
581
|
+
into outgoing HTTP requests matched by host pattern. Identity config
|
|
582
|
+
defines how the assistant verifies the connected account after OAuth.
|
|
583
|
+
|
|
584
|
+
Examples:
|
|
585
|
+
$ assistant oauth providers update custom-api --display-name "My Custom API"
|
|
586
|
+
$ assistant oauth providers update custom-api --scopes read,write --auth-url https://new.example.com/auth
|
|
587
|
+
$ assistant oauth providers update custom-api --ping-url https://api.example.com/me --json
|
|
588
|
+
$ assistant oauth providers update custom-api \\
|
|
589
|
+
--identity-url https://api.example.com/me \\
|
|
590
|
+
--identity-response-paths email,name
|
|
591
|
+
$ assistant oauth providers update custom-api \\
|
|
592
|
+
--injection-templates '[{"hostPattern":"api.example.com","injectionType":"header","headerName":"Authorization","valuePrefix":"Bearer "}]'`,
|
|
593
|
+
)
|
|
594
|
+
.action(
|
|
595
|
+
(
|
|
596
|
+
providerKey: string,
|
|
597
|
+
opts: {
|
|
598
|
+
authUrl?: string;
|
|
599
|
+
tokenUrl?: string;
|
|
600
|
+
baseUrl?: string;
|
|
601
|
+
userinfoUrl?: string;
|
|
602
|
+
scopes?: string;
|
|
603
|
+
tokenAuthMethod?: string;
|
|
604
|
+
callbackTransport?: string;
|
|
605
|
+
pingUrl?: string;
|
|
606
|
+
pingMethod?: string;
|
|
607
|
+
pingHeaders?: string;
|
|
608
|
+
pingBody?: string;
|
|
609
|
+
displayName?: string;
|
|
610
|
+
description?: string;
|
|
611
|
+
dashboardUrl?: string;
|
|
612
|
+
clientIdPlaceholder?: string;
|
|
613
|
+
clientSecret: boolean;
|
|
614
|
+
loopbackPort?: string;
|
|
615
|
+
injectionTemplates?: string;
|
|
616
|
+
appType?: string;
|
|
617
|
+
identityUrl?: string;
|
|
618
|
+
identityMethod?: string;
|
|
619
|
+
identityHeaders?: string;
|
|
620
|
+
identityBody?: string;
|
|
621
|
+
identityResponsePaths?: string;
|
|
622
|
+
identityFormat?: string;
|
|
623
|
+
identityOkField?: string;
|
|
624
|
+
setupNotes?: string;
|
|
625
|
+
},
|
|
626
|
+
cmd: Command,
|
|
627
|
+
) => {
|
|
628
|
+
try {
|
|
629
|
+
// Verify provider exists
|
|
630
|
+
const existing = getProvider(providerKey);
|
|
631
|
+
if (!existing) {
|
|
632
|
+
writeOutput(cmd, {
|
|
633
|
+
ok: false,
|
|
634
|
+
error: `Provider "${providerKey}" not found. Run 'assistant oauth providers list' to see all registered providers.`,
|
|
635
|
+
});
|
|
636
|
+
process.exitCode = 1;
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Block updates to built-in providers
|
|
641
|
+
if (SEEDED_PROVIDER_KEYS.has(providerKey)) {
|
|
642
|
+
writeOutput(cmd, {
|
|
643
|
+
ok: false,
|
|
644
|
+
error: `Cannot update built-in provider "${providerKey}". Built-in providers are managed by the system and reset on startup. To create a custom provider with different settings, use 'assistant oauth providers register --provider-key <your-custom-key> ...'`,
|
|
645
|
+
});
|
|
646
|
+
process.exitCode = 1;
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Build params object from provided options, omitting undefined values
|
|
651
|
+
const params: Record<string, unknown> = {};
|
|
652
|
+
|
|
653
|
+
if (opts.authUrl !== undefined) params.authUrl = opts.authUrl;
|
|
654
|
+
if (opts.tokenUrl !== undefined) params.tokenUrl = opts.tokenUrl;
|
|
655
|
+
if (opts.baseUrl !== undefined) params.baseUrl = opts.baseUrl;
|
|
656
|
+
if (opts.userinfoUrl !== undefined)
|
|
657
|
+
params.userinfoUrl = opts.userinfoUrl;
|
|
658
|
+
if (opts.scopes !== undefined)
|
|
659
|
+
params.defaultScopes = opts.scopes.split(",");
|
|
660
|
+
if (opts.tokenAuthMethod !== undefined)
|
|
661
|
+
params.tokenEndpointAuthMethod = opts.tokenAuthMethod;
|
|
662
|
+
if (opts.callbackTransport !== undefined)
|
|
663
|
+
params.callbackTransport = opts.callbackTransport;
|
|
664
|
+
if (opts.pingUrl !== undefined) params.pingUrl = opts.pingUrl;
|
|
665
|
+
if (opts.pingMethod !== undefined)
|
|
666
|
+
params.pingMethod = opts.pingMethod;
|
|
667
|
+
if (opts.pingHeaders !== undefined)
|
|
668
|
+
params.pingHeaders = JSON.parse(opts.pingHeaders);
|
|
669
|
+
if (opts.pingBody !== undefined)
|
|
670
|
+
params.pingBody = JSON.parse(opts.pingBody);
|
|
671
|
+
if (opts.displayName !== undefined)
|
|
672
|
+
params.displayName = opts.displayName;
|
|
673
|
+
if (opts.description !== undefined)
|
|
674
|
+
params.description = opts.description;
|
|
675
|
+
if (opts.dashboardUrl !== undefined)
|
|
676
|
+
params.dashboardUrl = opts.dashboardUrl;
|
|
677
|
+
if (opts.clientIdPlaceholder !== undefined)
|
|
678
|
+
params.clientIdPlaceholder = opts.clientIdPlaceholder;
|
|
679
|
+
|
|
680
|
+
// Handle the negated --no-client-* flag: Commander defaults
|
|
681
|
+
// opts.clientSecret to true; the negated form sets it to false.
|
|
682
|
+
// Use getOptionValueSource to detect explicit user intent.
|
|
683
|
+
if (cmd.getOptionValueSource("clientSecret") === "cli") {
|
|
684
|
+
params.requiresClientSecret = opts.clientSecret ? 1 : 0;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (opts.loopbackPort !== undefined)
|
|
688
|
+
params.loopbackPort = parseInt(opts.loopbackPort, 10);
|
|
689
|
+
if (opts.injectionTemplates !== undefined)
|
|
690
|
+
params.injectionTemplates = JSON.parse(opts.injectionTemplates);
|
|
691
|
+
if (opts.appType !== undefined) params.appType = opts.appType;
|
|
692
|
+
if (opts.identityUrl !== undefined)
|
|
693
|
+
params.identityUrl = opts.identityUrl;
|
|
694
|
+
if (opts.identityMethod !== undefined)
|
|
695
|
+
params.identityMethod = opts.identityMethod;
|
|
696
|
+
if (opts.identityHeaders !== undefined)
|
|
697
|
+
params.identityHeaders = JSON.parse(opts.identityHeaders);
|
|
698
|
+
if (opts.identityBody !== undefined)
|
|
699
|
+
params.identityBody = JSON.parse(opts.identityBody);
|
|
700
|
+
if (opts.identityResponsePaths !== undefined)
|
|
701
|
+
params.identityResponsePaths =
|
|
702
|
+
opts.identityResponsePaths.split(",");
|
|
703
|
+
if (opts.identityFormat !== undefined)
|
|
704
|
+
params.identityFormat = opts.identityFormat;
|
|
705
|
+
if (opts.identityOkField !== undefined)
|
|
706
|
+
params.identityOkField = opts.identityOkField;
|
|
707
|
+
if (opts.setupNotes !== undefined)
|
|
708
|
+
params.setupNotes = JSON.parse(opts.setupNotes);
|
|
709
|
+
|
|
710
|
+
// Check if any fields were actually provided
|
|
711
|
+
if (Object.keys(params).length === 0) {
|
|
712
|
+
writeOutput(cmd, {
|
|
713
|
+
ok: false,
|
|
714
|
+
error:
|
|
715
|
+
"Nothing to update. Provide at least one option to change (e.g. --auth-url, --scopes, --display-name). Run 'assistant oauth providers update --help' for all options.",
|
|
716
|
+
});
|
|
717
|
+
process.exitCode = 1;
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const row = updateProvider(providerKey, params);
|
|
722
|
+
|
|
723
|
+
writeOutput(cmd, parseProviderRow(row));
|
|
724
|
+
} catch (err) {
|
|
725
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
726
|
+
writeOutput(cmd, { ok: false, error: message });
|
|
727
|
+
process.exitCode = 1;
|
|
728
|
+
}
|
|
729
|
+
},
|
|
730
|
+
);
|
|
731
|
+
|
|
732
|
+
// ---------------------------------------------------------------------------
|
|
733
|
+
// providers delete <provider-key>
|
|
734
|
+
// ---------------------------------------------------------------------------
|
|
735
|
+
|
|
736
|
+
providers
|
|
737
|
+
.command("delete <provider-key>")
|
|
738
|
+
.description(
|
|
739
|
+
"Delete a custom OAuth provider and optionally its associated apps and connections",
|
|
740
|
+
)
|
|
741
|
+
.option(
|
|
742
|
+
"--force",
|
|
743
|
+
"Cascade-delete all associated apps and connections before removing the provider",
|
|
744
|
+
)
|
|
745
|
+
.addHelpText(
|
|
746
|
+
"after",
|
|
747
|
+
`
|
|
748
|
+
Arguments:
|
|
749
|
+
provider-key Provider key to delete (e.g. "custom-api").
|
|
750
|
+
Run 'assistant oauth providers list' to see registered providers.
|
|
751
|
+
|
|
752
|
+
When --force is specified, all OAuth connections and apps that depend on
|
|
753
|
+
this provider are deleted before the provider itself is removed. Without
|
|
754
|
+
--force, the command refuses to delete a provider that has dependents and
|
|
755
|
+
exits with an error listing the counts.
|
|
756
|
+
|
|
757
|
+
Built-in providers (e.g. "google", "slack") can be deleted, but a warning
|
|
758
|
+
is emitted because they will be re-created automatically on the next
|
|
759
|
+
assistant startup.
|
|
760
|
+
|
|
761
|
+
Examples:
|
|
762
|
+
$ assistant oauth providers delete custom-api
|
|
763
|
+
$ assistant oauth providers delete custom-api --force
|
|
764
|
+
$ assistant oauth providers delete custom-api --force --json`,
|
|
765
|
+
)
|
|
766
|
+
.action(
|
|
767
|
+
async (providerKey: string, opts: { force?: boolean }, cmd: Command) => {
|
|
768
|
+
try {
|
|
769
|
+
const provider = getProvider(providerKey);
|
|
770
|
+
if (!provider) {
|
|
771
|
+
writeOutput(cmd, {
|
|
772
|
+
ok: false,
|
|
773
|
+
error: `Provider not found: "${providerKey}". Run 'assistant oauth providers list' to see all registered providers.`,
|
|
774
|
+
});
|
|
775
|
+
process.exitCode = 1;
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
if (SEEDED_PROVIDER_KEYS.has(providerKey) && !opts.force) {
|
|
780
|
+
log.info(
|
|
781
|
+
`Note: "${providerKey}" is a built-in provider and will be re-created on next startup.`,
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
const dependentApps = listApps().filter(
|
|
786
|
+
(a) => a.providerKey === providerKey,
|
|
787
|
+
);
|
|
788
|
+
const dependentConnections = listConnections(providerKey);
|
|
789
|
+
const appCount = dependentApps.length;
|
|
790
|
+
const connCount = dependentConnections.length;
|
|
791
|
+
|
|
792
|
+
if ((appCount > 0 || connCount > 0) && !opts.force) {
|
|
793
|
+
writeOutput(cmd, {
|
|
794
|
+
ok: false,
|
|
795
|
+
error: `Cannot delete provider "${providerKey}": ${appCount} app(s) and ${connCount} connection(s) depend on it. Use --force to cascade-delete all dependent apps and connections, or remove them manually first with 'assistant oauth apps delete' and 'assistant oauth disconnect'.`,
|
|
796
|
+
});
|
|
797
|
+
process.exitCode = 1;
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Warn about built-in providers when --force is used
|
|
802
|
+
if (SEEDED_PROVIDER_KEYS.has(providerKey) && opts.force) {
|
|
803
|
+
log.info(
|
|
804
|
+
`Note: "${providerKey}" is a built-in provider and will be re-created on next startup.`,
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// Cascade-delete connections first, then apps, then the provider.
|
|
809
|
+
// Use disconnectOAuthProvider to clean up OAuth tokens from secure
|
|
810
|
+
// storage in addition to deleting the connection DB row.
|
|
811
|
+
for (const conn of dependentConnections) {
|
|
812
|
+
const result = await disconnectOAuthProvider(
|
|
813
|
+
providerKey,
|
|
814
|
+
undefined,
|
|
815
|
+
conn.id as string,
|
|
816
|
+
);
|
|
817
|
+
if (result === "error") {
|
|
818
|
+
log.info(
|
|
819
|
+
`Warning: failed to clean up tokens for connection ${conn.id} — deleting connection row to continue cascade.`,
|
|
820
|
+
);
|
|
821
|
+
deleteConnection(conn.id);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
for (const app of dependentApps) {
|
|
825
|
+
await deleteApp(app.id);
|
|
826
|
+
}
|
|
827
|
+
deleteProvider(providerKey);
|
|
828
|
+
|
|
829
|
+
if (!shouldOutputJson(cmd)) {
|
|
830
|
+
log.info(`Deleted provider: ${providerKey}`);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
writeOutput(cmd, {
|
|
834
|
+
ok: true,
|
|
835
|
+
deleted: { provider: 1, apps: appCount, connections: connCount },
|
|
836
|
+
});
|
|
330
837
|
} catch (err) {
|
|
331
838
|
const message = err instanceof Error ? err.message : String(err);
|
|
332
839
|
writeOutput(cmd, { ok: false, error: message });
|