@vellumai/assistant 0.5.10 → 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 +8 -0
- package/ARCHITECTURE.md +43 -43
- package/Dockerfile +2 -0
- package/docs/architecture/integrations.md +3 -10
- package/docs/architecture/memory.md +7 -12
- 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__/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-018-rekey-compound-credential-keys.test.ts +181 -0
- 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/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 +26 -8
- 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 +26 -2
- 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/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 +90 -0
- 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/docs/architecture/keychain-broker.md +0 -68
- 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
|
@@ -5,51 +5,14 @@ import {
|
|
|
5
5
|
fetchManagedCatalog,
|
|
6
6
|
type ManagedCredentialDescriptor,
|
|
7
7
|
} from "../../../credential-execution/managed-catalog.js";
|
|
8
|
-
import { orchestrateOAuthConnect } from "../../../oauth/connect-orchestrator.js";
|
|
9
8
|
import {
|
|
10
|
-
disconnectOAuthProvider,
|
|
11
|
-
getAppByProviderAndClientId,
|
|
12
9
|
getConnection,
|
|
13
10
|
getConnectionByProvider,
|
|
14
|
-
getMostRecentAppByProvider,
|
|
15
|
-
getProvider,
|
|
16
11
|
listConnections,
|
|
17
12
|
} from "../../../oauth/oauth-store.js";
|
|
18
|
-
import {
|
|
19
|
-
getProviderBehavior,
|
|
20
|
-
resolveService,
|
|
21
|
-
} from "../../../oauth/provider-behaviors.js";
|
|
22
|
-
import { withValidToken } from "../../../security/token-manager.js";
|
|
23
|
-
import {
|
|
24
|
-
assertMetadataWritable,
|
|
25
|
-
deleteCredentialMetadata,
|
|
26
|
-
} from "../../../tools/credentials/metadata-store.js";
|
|
27
|
-
import { openInBrowser } from "../../../util/browser.js";
|
|
28
|
-
import {
|
|
29
|
-
deleteSecureKeyViaDaemon,
|
|
30
|
-
getSecureKeyViaDaemon,
|
|
31
|
-
} from "../../lib/daemon-credential-client.js";
|
|
32
13
|
import { getCliLogger } from "../../logger.js";
|
|
33
14
|
import { shouldOutputJson, writeOutput } from "../../output.js";
|
|
34
15
|
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
// CES shell lockdown guard
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Returns true when the current process is running inside an untrusted shell
|
|
41
|
-
* (CES shell lockdown active). CLI commands that reveal raw tokens must
|
|
42
|
-
* check this and fail deterministically.
|
|
43
|
-
*/
|
|
44
|
-
function isUntrustedShell(): boolean {
|
|
45
|
-
return process.env.VELLUM_UNTRUSTED_SHELL === "1";
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Error message for commands blocked by CES shell lockdown. */
|
|
49
|
-
const UNTRUSTED_SHELL_ERROR =
|
|
50
|
-
"This command is not available in untrusted shell mode. " +
|
|
51
|
-
"Raw token access is restricted when running under CES shell lockdown.";
|
|
52
|
-
|
|
53
16
|
const log = getCliLogger("cli");
|
|
54
17
|
|
|
55
18
|
/**
|
|
@@ -137,12 +100,7 @@ Examples:
|
|
|
137
100
|
$ assistant oauth connections list --client-id abc123
|
|
138
101
|
$ assistant oauth connections get --id <uuid>
|
|
139
102
|
$ assistant oauth connections get --provider integration:google
|
|
140
|
-
$ assistant oauth connections get --provider integration:google --client-id abc123
|
|
141
|
-
$ assistant oauth connections token integration:twitter
|
|
142
|
-
$ assistant oauth connections ping integration:google
|
|
143
|
-
$ assistant oauth connections connect integration:google
|
|
144
|
-
$ assistant oauth connections connect integration:google --open-browser
|
|
145
|
-
$ assistant oauth connections disconnect integration:google`,
|
|
103
|
+
$ assistant oauth connections get --provider integration:google --client-id abc123`,
|
|
146
104
|
);
|
|
147
105
|
|
|
148
106
|
// ---------------------------------------------------------------------------
|
|
@@ -226,14 +184,17 @@ Examples:
|
|
|
226
184
|
connections
|
|
227
185
|
.command("get")
|
|
228
186
|
.description("Look up an OAuth connection by ID or provider")
|
|
229
|
-
.option(
|
|
187
|
+
.option(
|
|
188
|
+
"--id <id>",
|
|
189
|
+
"Connection ID (UUID) from 'assistant oauth connections list' or 'assistant oauth status <provider>'",
|
|
190
|
+
)
|
|
230
191
|
.option(
|
|
231
192
|
"--provider <key>",
|
|
232
|
-
"Provider key (
|
|
193
|
+
"Provider key (e.g. integration:google) from 'assistant oauth providers list'. Returns most recent active connection.",
|
|
233
194
|
)
|
|
234
195
|
.option(
|
|
235
196
|
"--client-id <id>",
|
|
236
|
-
"Filter by OAuth client ID (used with --provider)",
|
|
197
|
+
"Filter by OAuth client ID (used with --provider). Find IDs via 'assistant oauth apps list'.",
|
|
237
198
|
)
|
|
238
199
|
.addHelpText(
|
|
239
200
|
"after",
|
|
@@ -264,466 +225,26 @@ At least --id or --provider must be specified.`,
|
|
|
264
225
|
} else {
|
|
265
226
|
writeOutput(cmd, {
|
|
266
227
|
ok: false,
|
|
267
|
-
error:
|
|
228
|
+
error:
|
|
229
|
+
"Provide --id or --provider. Run 'assistant oauth connections list' to see all connections, or 'assistant oauth status <provider>' to find connection IDs for a specific provider.",
|
|
268
230
|
});
|
|
269
231
|
process.exitCode = 1;
|
|
270
232
|
return;
|
|
271
233
|
}
|
|
272
234
|
|
|
273
235
|
if (!row) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
writeOutput(cmd, formatConnectionRow(row));
|
|
280
|
-
} catch (err) {
|
|
281
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
282
|
-
writeOutput(cmd, { ok: false, error: message });
|
|
283
|
-
process.exitCode = 1;
|
|
284
|
-
}
|
|
285
|
-
},
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
// ---------------------------------------------------------------------------
|
|
289
|
-
// connections token <provider-key>
|
|
290
|
-
// ---------------------------------------------------------------------------
|
|
291
|
-
|
|
292
|
-
connections
|
|
293
|
-
.command("token <provider-key>")
|
|
294
|
-
.description(
|
|
295
|
-
"Print a valid OAuth access token for a provider, refreshing if expired",
|
|
296
|
-
)
|
|
297
|
-
.option(
|
|
298
|
-
"--client-id <id>",
|
|
299
|
-
"Filter by OAuth client ID when multiple apps exist for the provider",
|
|
300
|
-
)
|
|
301
|
-
.addHelpText(
|
|
302
|
-
"after",
|
|
303
|
-
`
|
|
304
|
-
Arguments:
|
|
305
|
-
provider-key Provider key (e.g. integration:google, integration:twitter)
|
|
306
|
-
|
|
307
|
-
Returns a valid OAuth access token for the given provider. If the stored token
|
|
308
|
-
is expired or near-expiry, it is refreshed automatically before being returned.
|
|
309
|
-
|
|
310
|
-
In human mode, prints the bare token to stdout (suitable for shell substitution).
|
|
311
|
-
In JSON mode (--json), prints {"ok": true, "token": "..."}.
|
|
312
|
-
|
|
313
|
-
Exits with code 1 if no access token exists or refresh fails.
|
|
314
|
-
|
|
315
|
-
Examples:
|
|
316
|
-
$ assistant oauth connections token integration:twitter
|
|
317
|
-
$ assistant oauth connections token integration:google --json
|
|
318
|
-
$ assistant oauth connections token integration:google --client-id abc123`,
|
|
319
|
-
)
|
|
320
|
-
.action(
|
|
321
|
-
async (
|
|
322
|
-
providerKey: string,
|
|
323
|
-
opts: { clientId?: string },
|
|
324
|
-
cmd: Command,
|
|
325
|
-
) => {
|
|
326
|
-
try {
|
|
327
|
-
// CES shell lockdown: deny raw token reveal in untrusted shells.
|
|
328
|
-
if (isUntrustedShell()) {
|
|
329
|
-
writeOutput(cmd, { ok: false, error: UNTRUSTED_SHELL_ERROR });
|
|
330
|
-
process.exitCode = 1;
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const token = await withValidToken(
|
|
335
|
-
providerKey,
|
|
336
|
-
async (t) => t,
|
|
337
|
-
opts.clientId,
|
|
338
|
-
);
|
|
339
|
-
if (shouldOutputJson(cmd)) {
|
|
340
|
-
writeOutput(cmd, { ok: true, token });
|
|
341
|
-
} else {
|
|
342
|
-
process.stdout.write(token + "\n");
|
|
343
|
-
}
|
|
344
|
-
} catch (err) {
|
|
345
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
346
|
-
writeOutput(cmd, { ok: false, error: message });
|
|
347
|
-
process.exitCode = 1;
|
|
348
|
-
}
|
|
349
|
-
},
|
|
350
|
-
);
|
|
351
|
-
|
|
352
|
-
// ---------------------------------------------------------------------------
|
|
353
|
-
// connections ping <provider-key>
|
|
354
|
-
// ---------------------------------------------------------------------------
|
|
355
|
-
|
|
356
|
-
connections
|
|
357
|
-
.command("ping <provider-key>")
|
|
358
|
-
.description(
|
|
359
|
-
"Verify that a stored OAuth token is still valid by hitting the provider's health-check endpoint",
|
|
360
|
-
)
|
|
361
|
-
.option(
|
|
362
|
-
"--client-id <id>",
|
|
363
|
-
"Filter by OAuth client ID when multiple apps exist for the provider",
|
|
364
|
-
)
|
|
365
|
-
.addHelpText(
|
|
366
|
-
"after",
|
|
367
|
-
`
|
|
368
|
-
Arguments:
|
|
369
|
-
provider-key Provider key (e.g. integration:google, integration:twitter)
|
|
370
|
-
|
|
371
|
-
Fetches a valid access token (refreshing if needed) and sends a GET request
|
|
372
|
-
to the provider's configured ping URL. Reports success (HTTP 2xx) or failure.
|
|
373
|
-
|
|
374
|
-
The ping URL is set per-provider in seed data or via "providers register --ping-url".
|
|
375
|
-
If no ping URL is configured for the provider, exits with an error.
|
|
376
|
-
|
|
377
|
-
Examples:
|
|
378
|
-
$ assistant oauth connections ping integration:google
|
|
379
|
-
$ assistant oauth connections ping integration:twitter --json
|
|
380
|
-
$ assistant oauth connections ping integration:google --client-id abc123`,
|
|
381
|
-
)
|
|
382
|
-
.action(
|
|
383
|
-
async (
|
|
384
|
-
providerKey: string,
|
|
385
|
-
opts: { clientId?: string },
|
|
386
|
-
cmd: Command,
|
|
387
|
-
) => {
|
|
388
|
-
try {
|
|
389
|
-
const provider = getProvider(providerKey);
|
|
390
|
-
if (!provider) {
|
|
236
|
+
const source = opts.id
|
|
237
|
+
? `--id ${opts.id}`
|
|
238
|
+
: `--provider ${opts.provider}`;
|
|
391
239
|
writeOutput(cmd, {
|
|
392
240
|
ok: false,
|
|
393
|
-
error: `
|
|
241
|
+
error: `No connection found for ${source}. Run 'assistant oauth connections list' to see all connections, or 'assistant oauth connect <provider>' to create a new connection.`,
|
|
394
242
|
});
|
|
395
243
|
process.exitCode = 1;
|
|
396
244
|
return;
|
|
397
245
|
}
|
|
398
246
|
|
|
399
|
-
|
|
400
|
-
writeOutput(cmd, {
|
|
401
|
-
ok: false,
|
|
402
|
-
error: `No ping URL configured for "${providerKey}"`,
|
|
403
|
-
});
|
|
404
|
-
process.exitCode = 1;
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
const pingUrl = provider.pingUrl;
|
|
409
|
-
|
|
410
|
-
const PING_TIMEOUT_MS = 15_000;
|
|
411
|
-
|
|
412
|
-
const result = await withValidToken(
|
|
413
|
-
providerKey,
|
|
414
|
-
async (token) => {
|
|
415
|
-
const controller = new AbortController();
|
|
416
|
-
const timer = setTimeout(
|
|
417
|
-
() => controller.abort(),
|
|
418
|
-
PING_TIMEOUT_MS,
|
|
419
|
-
);
|
|
420
|
-
try {
|
|
421
|
-
const res = await fetch(pingUrl, {
|
|
422
|
-
method: "GET",
|
|
423
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
424
|
-
signal: controller.signal,
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
if (res.status === 401) {
|
|
428
|
-
const err = new Error(
|
|
429
|
-
`Ping returned HTTP 401 from ${pingUrl}`,
|
|
430
|
-
);
|
|
431
|
-
(err as unknown as { status: number }).status = 401;
|
|
432
|
-
throw err;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
return { status: res.status, ok: res.ok };
|
|
436
|
-
} finally {
|
|
437
|
-
clearTimeout(timer);
|
|
438
|
-
}
|
|
439
|
-
},
|
|
440
|
-
opts.clientId,
|
|
441
|
-
);
|
|
442
|
-
|
|
443
|
-
if (result.ok) {
|
|
444
|
-
if (shouldOutputJson(cmd)) {
|
|
445
|
-
writeOutput(cmd, {
|
|
446
|
-
ok: true,
|
|
447
|
-
provider: providerKey,
|
|
448
|
-
status: result.status,
|
|
449
|
-
});
|
|
450
|
-
} else {
|
|
451
|
-
log.info(`${providerKey}: OK (HTTP ${result.status})`);
|
|
452
|
-
writeOutput(cmd, {
|
|
453
|
-
ok: true,
|
|
454
|
-
provider: providerKey,
|
|
455
|
-
status: result.status,
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
} else {
|
|
459
|
-
writeOutput(cmd, {
|
|
460
|
-
ok: false,
|
|
461
|
-
provider: providerKey,
|
|
462
|
-
status: result.status,
|
|
463
|
-
error: `Ping failed with HTTP ${result.status}`,
|
|
464
|
-
});
|
|
465
|
-
process.exitCode = 1;
|
|
466
|
-
}
|
|
467
|
-
} catch (err) {
|
|
468
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
469
|
-
writeOutput(cmd, { ok: false, error: message });
|
|
470
|
-
process.exitCode = 1;
|
|
471
|
-
}
|
|
472
|
-
},
|
|
473
|
-
);
|
|
474
|
-
|
|
475
|
-
// ---------------------------------------------------------------------------
|
|
476
|
-
// connections disconnect <provider-key>
|
|
477
|
-
// ---------------------------------------------------------------------------
|
|
478
|
-
|
|
479
|
-
connections
|
|
480
|
-
.command("disconnect <provider-key>")
|
|
481
|
-
.description(
|
|
482
|
-
"Disconnect an OAuth integration and remove all associated credentials",
|
|
483
|
-
)
|
|
484
|
-
.option(
|
|
485
|
-
"--client-id <id>",
|
|
486
|
-
"Filter by OAuth client ID when multiple apps exist for the provider",
|
|
487
|
-
)
|
|
488
|
-
.addHelpText(
|
|
489
|
-
"after",
|
|
490
|
-
`
|
|
491
|
-
Arguments:
|
|
492
|
-
provider-key The full provider key (e.g. integration:google, integration:slack)
|
|
493
|
-
|
|
494
|
-
Removes the OAuth connection, tokens, and any legacy credential metadata for
|
|
495
|
-
the provider. The <provider-key> argument is the full provider key as-is — it
|
|
496
|
-
is not parsed through service:field splitting.
|
|
497
|
-
|
|
498
|
-
Legacy credential keys for common fields (access_token, refresh_token,
|
|
499
|
-
client_id, client_secret) are also cleaned up if present.
|
|
500
|
-
|
|
501
|
-
Examples:
|
|
502
|
-
$ assistant oauth connections disconnect integration:google
|
|
503
|
-
$ assistant oauth connections disconnect integration:slack
|
|
504
|
-
$ assistant oauth connections disconnect integration:google --client-id abc123`,
|
|
505
|
-
)
|
|
506
|
-
.action(
|
|
507
|
-
async (
|
|
508
|
-
providerKey: string,
|
|
509
|
-
opts: { clientId?: string },
|
|
510
|
-
cmd: Command,
|
|
511
|
-
) => {
|
|
512
|
-
try {
|
|
513
|
-
assertMetadataWritable();
|
|
514
|
-
|
|
515
|
-
let cleanedUp = false;
|
|
516
|
-
|
|
517
|
-
// 1. Disconnect the OAuth connection (new-format keys + connection row)
|
|
518
|
-
const oauthResult = await disconnectOAuthProvider(
|
|
519
|
-
providerKey,
|
|
520
|
-
opts.clientId,
|
|
521
|
-
);
|
|
522
|
-
if (oauthResult === "error") {
|
|
523
|
-
writeOutput(cmd, {
|
|
524
|
-
ok: false,
|
|
525
|
-
error: `Failed to disconnect OAuth provider "${providerKey}" — please try again`,
|
|
526
|
-
});
|
|
527
|
-
process.exitCode = 1;
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
if (oauthResult === "disconnected") cleanedUp = true;
|
|
531
|
-
|
|
532
|
-
// 2. Clean up legacy credential keys for common fields
|
|
533
|
-
const legacyFields = [
|
|
534
|
-
"access_token",
|
|
535
|
-
"refresh_token",
|
|
536
|
-
"client_id",
|
|
537
|
-
"client_secret",
|
|
538
|
-
];
|
|
539
|
-
for (const field of legacyFields) {
|
|
540
|
-
const result = await deleteSecureKeyViaDaemon(
|
|
541
|
-
"credential",
|
|
542
|
-
`${providerKey}:${field}`,
|
|
543
|
-
);
|
|
544
|
-
if (result === "deleted") cleanedUp = true;
|
|
545
|
-
|
|
546
|
-
const metaDeleted = deleteCredentialMetadata(providerKey, field);
|
|
547
|
-
if (metaDeleted) cleanedUp = true;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
if (!cleanedUp) {
|
|
551
|
-
writeOutput(cmd, {
|
|
552
|
-
ok: false,
|
|
553
|
-
error: `No OAuth connection or credentials found for "${providerKey}"`,
|
|
554
|
-
});
|
|
555
|
-
process.exitCode = 1;
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
writeOutput(cmd, { ok: true, service: providerKey });
|
|
560
|
-
|
|
561
|
-
if (!shouldOutputJson(cmd)) {
|
|
562
|
-
log.info(`Disconnected ${providerKey}`);
|
|
563
|
-
}
|
|
564
|
-
} catch (err) {
|
|
565
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
566
|
-
writeOutput(cmd, { ok: false, error: message });
|
|
567
|
-
process.exitCode = 1;
|
|
568
|
-
}
|
|
569
|
-
},
|
|
570
|
-
);
|
|
571
|
-
|
|
572
|
-
// ---------------------------------------------------------------------------
|
|
573
|
-
// connections connect <provider-key>
|
|
574
|
-
// ---------------------------------------------------------------------------
|
|
575
|
-
|
|
576
|
-
connections
|
|
577
|
-
.command("connect <provider-key>")
|
|
578
|
-
.description("Initiate an OAuth2 authorization flow for a provider")
|
|
579
|
-
.option(
|
|
580
|
-
"--client-id <id>",
|
|
581
|
-
"Filter by OAuth client ID when multiple apps exist for the provider",
|
|
582
|
-
)
|
|
583
|
-
.option(
|
|
584
|
-
"--scopes <scopes...>",
|
|
585
|
-
"Additional scopes beyond the provider's defaults",
|
|
586
|
-
)
|
|
587
|
-
.option(
|
|
588
|
-
"--open-browser",
|
|
589
|
-
"Open the auth URL in the browser and wait for completion",
|
|
590
|
-
)
|
|
591
|
-
.addHelpText(
|
|
592
|
-
"after",
|
|
593
|
-
`
|
|
594
|
-
Arguments:
|
|
595
|
-
provider-key Provider key (e.g. integration:google) or alias (e.g. gmail)
|
|
596
|
-
|
|
597
|
-
Initiates an OAuth2 authorization flow for the given provider. By default,
|
|
598
|
-
prints the authorization URL to stdout — useful for headless/remote sessions.
|
|
599
|
-
The token exchange completes in the background when the user authorizes.
|
|
600
|
-
|
|
601
|
-
With --open-browser, opens the authorization URL in your browser and waits
|
|
602
|
-
for completion.
|
|
603
|
-
|
|
604
|
-
Client credentials are resolved from the OAuth app store. Use --client-id
|
|
605
|
-
to select a specific app when multiple apps exist for the same provider.
|
|
606
|
-
|
|
607
|
-
Examples:
|
|
608
|
-
$ assistant oauth connections connect integration:google
|
|
609
|
-
$ assistant oauth connections connect gmail --open-browser
|
|
610
|
-
$ assistant oauth connections connect integration:slack --client-id abc123
|
|
611
|
-
$ assistant oauth connections connect integration:google --scopes calendar.readonly --json`,
|
|
612
|
-
)
|
|
613
|
-
.action(
|
|
614
|
-
async (
|
|
615
|
-
providerKey: string,
|
|
616
|
-
opts: {
|
|
617
|
-
clientId?: string;
|
|
618
|
-
scopes?: string[];
|
|
619
|
-
openBrowser?: boolean;
|
|
620
|
-
},
|
|
621
|
-
cmd: Command,
|
|
622
|
-
) => {
|
|
623
|
-
try {
|
|
624
|
-
// a. Resolve service alias
|
|
625
|
-
const resolvedServiceKey = resolveService(providerKey);
|
|
626
|
-
|
|
627
|
-
// b. Resolve client credentials from the DB
|
|
628
|
-
const dbApp = opts.clientId
|
|
629
|
-
? getAppByProviderAndClientId(resolvedServiceKey, opts.clientId)
|
|
630
|
-
: getMostRecentAppByProvider(resolvedServiceKey);
|
|
631
|
-
|
|
632
|
-
let clientId = opts.clientId;
|
|
633
|
-
let clientSecret: string | undefined;
|
|
634
|
-
|
|
635
|
-
if (dbApp) {
|
|
636
|
-
if (!clientId) clientId = dbApp.clientId;
|
|
637
|
-
const storedSecret = await getSecureKeyViaDaemon(
|
|
638
|
-
dbApp.clientSecretCredentialPath,
|
|
639
|
-
);
|
|
640
|
-
if (storedSecret) clientSecret = storedSecret;
|
|
641
|
-
} else if (opts.clientId) {
|
|
642
|
-
// --client-id was explicitly provided but no matching app exists
|
|
643
|
-
writeOutput(cmd, {
|
|
644
|
-
ok: false,
|
|
645
|
-
error: `No registered app found for "${resolvedServiceKey}" with client ID "${opts.clientId}". Register it first with 'assistant oauth apps upsert --provider ${resolvedServiceKey} --client-id ${opts.clientId}'.`,
|
|
646
|
-
});
|
|
647
|
-
process.exitCode = 1;
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// c. Validate client_id
|
|
652
|
-
if (!clientId) {
|
|
653
|
-
writeOutput(cmd, {
|
|
654
|
-
ok: false,
|
|
655
|
-
error:
|
|
656
|
-
"No client_id found. Provide --client-id or register an app first with 'assistant oauth apps upsert'.",
|
|
657
|
-
});
|
|
658
|
-
process.exitCode = 1;
|
|
659
|
-
return;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
// d. Check if client_secret is required but missing
|
|
663
|
-
if (clientSecret === undefined) {
|
|
664
|
-
const providerRow = getProvider(resolvedServiceKey);
|
|
665
|
-
const behavior = getProviderBehavior(resolvedServiceKey);
|
|
666
|
-
|
|
667
|
-
const requiresSecret =
|
|
668
|
-
behavior?.setup?.requiresClientSecret ??
|
|
669
|
-
!!(
|
|
670
|
-
providerRow?.tokenEndpointAuthMethod || providerRow?.extraParams
|
|
671
|
-
);
|
|
672
|
-
|
|
673
|
-
if (requiresSecret) {
|
|
674
|
-
writeOutput(cmd, {
|
|
675
|
-
ok: false,
|
|
676
|
-
error: `client_secret is required for ${resolvedServiceKey} but not found. Store it first with 'assistant oauth apps upsert --client-secret'.`,
|
|
677
|
-
});
|
|
678
|
-
process.exitCode = 1;
|
|
679
|
-
return;
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
// e. Call the orchestrator
|
|
684
|
-
const result = await orchestrateOAuthConnect({
|
|
685
|
-
service: providerKey,
|
|
686
|
-
clientId,
|
|
687
|
-
clientSecret,
|
|
688
|
-
isInteractive: !!opts.openBrowser,
|
|
689
|
-
openUrl: opts.openBrowser ? openInBrowser : undefined,
|
|
690
|
-
...(opts.scopes ? { requestedScopes: opts.scopes } : {}),
|
|
691
|
-
});
|
|
692
|
-
|
|
693
|
-
// f. Handle results
|
|
694
|
-
if (!result.success) {
|
|
695
|
-
writeOutput(cmd, { ok: false, error: result.error });
|
|
696
|
-
process.exitCode = 1;
|
|
697
|
-
return;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
if (result.deferred) {
|
|
701
|
-
if (shouldOutputJson(cmd)) {
|
|
702
|
-
writeOutput(cmd, {
|
|
703
|
-
ok: true,
|
|
704
|
-
deferred: true,
|
|
705
|
-
authUrl: result.authUrl,
|
|
706
|
-
service: result.service,
|
|
707
|
-
});
|
|
708
|
-
} else {
|
|
709
|
-
process.stdout.write(
|
|
710
|
-
`Open this URL to authorize:\n\n${result.authUrl}\n\nThe connection will complete automatically once you authorize.\n`,
|
|
711
|
-
);
|
|
712
|
-
}
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
// Interactive mode completed
|
|
717
|
-
if (shouldOutputJson(cmd)) {
|
|
718
|
-
writeOutput(cmd, {
|
|
719
|
-
ok: true,
|
|
720
|
-
grantedScopes: result.grantedScopes,
|
|
721
|
-
accountInfo: result.accountInfo,
|
|
722
|
-
});
|
|
723
|
-
} else {
|
|
724
|
-
const msg = `Connected to ${resolvedServiceKey}${result.accountInfo ? ` as ${result.accountInfo}` : ""}`;
|
|
725
|
-
process.stdout.write(msg + "\n");
|
|
726
|
-
}
|
|
247
|
+
writeOutput(cmd, formatConnectionRow(row));
|
|
727
248
|
} catch (err) {
|
|
728
249
|
const message = err instanceof Error ? err.message : String(err);
|
|
729
250
|
writeOutput(cmd, { ok: false, error: message });
|