@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
|
@@ -37,7 +37,7 @@ let mockUpsertAppCalls: Array<{
|
|
|
37
37
|
}> = [];
|
|
38
38
|
let mockUpsertAppResult: Record<string, unknown> = {
|
|
39
39
|
id: "app-upsert-1",
|
|
40
|
-
providerKey: "
|
|
40
|
+
providerKey: "test",
|
|
41
41
|
clientId: "test-client-id",
|
|
42
42
|
createdAt: 1700000000000,
|
|
43
43
|
updatedAt: 1700000000000,
|
|
@@ -53,7 +53,7 @@ let mockUpsertAppImpl:
|
|
|
53
53
|
) => Promise<Record<string, unknown>>)
|
|
54
54
|
| undefined;
|
|
55
55
|
|
|
56
|
-
// Transitive mock state (connect-orchestrator,
|
|
56
|
+
// Transitive mock state (connect-orchestrator, etc.)
|
|
57
57
|
let mockOrchestrateOAuthConnect: (
|
|
58
58
|
opts: Record<string, unknown>,
|
|
59
59
|
) => Promise<Record<string, unknown>>;
|
|
@@ -67,9 +67,6 @@ let mockGetMostRecentAppByProvider: (
|
|
|
67
67
|
let mockGetProvider: (
|
|
68
68
|
providerKey: string,
|
|
69
69
|
) => Record<string, unknown> | undefined = () => undefined;
|
|
70
|
-
let mockGetProviderBehavior: (
|
|
71
|
-
providerKey: string,
|
|
72
|
-
) => Record<string, unknown> | undefined = () => undefined;
|
|
73
70
|
let mockGetSecureKey: (account: string) => string | undefined = () => undefined;
|
|
74
71
|
let mockResolveOAuthConnection: (
|
|
75
72
|
providerKey: string,
|
|
@@ -153,6 +150,8 @@ mock.module("../oauth/oauth-store.js", () => ({
|
|
|
153
150
|
getProvider: (providerKey: string) => mockGetProvider(providerKey),
|
|
154
151
|
listProviders: () => mockListProviders(),
|
|
155
152
|
registerProvider: () => ({}),
|
|
153
|
+
updateProvider: () => undefined,
|
|
154
|
+
deleteProvider: () => false,
|
|
156
155
|
seedProviders: () => {},
|
|
157
156
|
getActiveConnection: () => undefined,
|
|
158
157
|
listActiveConnectionsByProvider: () => [],
|
|
@@ -208,14 +207,16 @@ mock.module("../oauth/connect-orchestrator.js", () => ({
|
|
|
208
207
|
mockOrchestrateOAuthConnect(opts),
|
|
209
208
|
}));
|
|
210
209
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
210
|
+
mock.module("../oauth/seed-providers.js", () => ({
|
|
211
|
+
SEEDED_PROVIDER_KEYS: new Set([
|
|
212
|
+
"google",
|
|
213
|
+
"slack",
|
|
214
|
+
"github",
|
|
215
|
+
"notion",
|
|
216
|
+
"twitter",
|
|
217
|
+
"linear",
|
|
218
|
+
]),
|
|
219
|
+
seedOAuthProviders: () => {},
|
|
219
220
|
}));
|
|
220
221
|
|
|
221
222
|
// ---------------------------------------------------------------------------
|
|
@@ -315,17 +316,13 @@ describe("assistant oauth token <provider-key>", () => {
|
|
|
315
316
|
});
|
|
316
317
|
|
|
317
318
|
test("prints bare token in human mode", async () => {
|
|
318
|
-
const { exitCode, stdout } = await runCli(["token", "
|
|
319
|
+
const { exitCode, stdout } = await runCli(["token", "twitter"]);
|
|
319
320
|
expect(exitCode).toBe(0);
|
|
320
321
|
expect(stdout).toBe("mock-access-token-xyz\n");
|
|
321
322
|
});
|
|
322
323
|
|
|
323
324
|
test("prints JSON in --json mode", async () => {
|
|
324
|
-
const { exitCode, stdout } = await runCli([
|
|
325
|
-
"token",
|
|
326
|
-
"integration:twitter",
|
|
327
|
-
"--json",
|
|
328
|
-
]);
|
|
325
|
+
const { exitCode, stdout } = await runCli(["token", "twitter", "--json"]);
|
|
329
326
|
expect(exitCode).toBe(0);
|
|
330
327
|
const parsed = JSON.parse(stdout);
|
|
331
328
|
expect(parsed).toEqual({ ok: true, token: "mock-access-token-xyz" });
|
|
@@ -338,8 +335,8 @@ describe("assistant oauth token <provider-key>", () => {
|
|
|
338
335
|
return cb("tok");
|
|
339
336
|
};
|
|
340
337
|
|
|
341
|
-
await runCli(["token", "
|
|
342
|
-
expect(capturedService).toBe("
|
|
338
|
+
await runCli(["token", "twitter"]);
|
|
339
|
+
expect(capturedService).toBe("twitter");
|
|
343
340
|
});
|
|
344
341
|
|
|
345
342
|
test("works with other provider keys", async () => {
|
|
@@ -349,24 +346,20 @@ describe("assistant oauth token <provider-key>", () => {
|
|
|
349
346
|
return cb("gmail-token");
|
|
350
347
|
};
|
|
351
348
|
|
|
352
|
-
const { exitCode, stdout } = await runCli(["token", "
|
|
349
|
+
const { exitCode, stdout } = await runCli(["token", "google"]);
|
|
353
350
|
expect(exitCode).toBe(0);
|
|
354
351
|
expect(stdout).toBe("gmail-token\n");
|
|
355
|
-
expect(capturedService).toBe("
|
|
352
|
+
expect(capturedService).toBe("google");
|
|
356
353
|
});
|
|
357
354
|
|
|
358
355
|
test("exits 1 when no token exists", async () => {
|
|
359
356
|
mockWithValidToken = async () => {
|
|
360
357
|
throw new Error(
|
|
361
|
-
'No access token found for "
|
|
358
|
+
'No access token found for "twitter". Authorization required.',
|
|
362
359
|
);
|
|
363
360
|
};
|
|
364
361
|
|
|
365
|
-
const { exitCode, stdout } = await runCli([
|
|
366
|
-
"token",
|
|
367
|
-
"integration:twitter",
|
|
368
|
-
"--json",
|
|
369
|
-
]);
|
|
362
|
+
const { exitCode, stdout } = await runCli(["token", "twitter", "--json"]);
|
|
370
363
|
expect(exitCode).toBe(1);
|
|
371
364
|
const parsed = JSON.parse(stdout);
|
|
372
365
|
expect(parsed.ok).toBe(false);
|
|
@@ -375,16 +368,10 @@ describe("assistant oauth token <provider-key>", () => {
|
|
|
375
368
|
|
|
376
369
|
test("exits 1 when refresh fails", async () => {
|
|
377
370
|
mockWithValidToken = async () => {
|
|
378
|
-
throw new Error(
|
|
379
|
-
'Token refresh failed for "integration:twitter": invalid_grant.',
|
|
380
|
-
);
|
|
371
|
+
throw new Error('Token refresh failed for "twitter": invalid_grant.');
|
|
381
372
|
};
|
|
382
373
|
|
|
383
|
-
const { exitCode, stdout } = await runCli([
|
|
384
|
-
"token",
|
|
385
|
-
"integration:twitter",
|
|
386
|
-
"--json",
|
|
387
|
-
]);
|
|
374
|
+
const { exitCode, stdout } = await runCli(["token", "twitter", "--json"]);
|
|
388
375
|
expect(exitCode).toBe(1);
|
|
389
376
|
const parsed = JSON.parse(stdout);
|
|
390
377
|
expect(parsed.ok).toBe(false);
|
|
@@ -395,7 +382,7 @@ describe("assistant oauth token <provider-key>", () => {
|
|
|
395
382
|
// Simulate withValidToken refreshing and returning a new token
|
|
396
383
|
mockWithValidToken = async (_service, cb) => cb("refreshed-new-token");
|
|
397
384
|
|
|
398
|
-
const { exitCode, stdout } = await runCli(["token", "
|
|
385
|
+
const { exitCode, stdout } = await runCli(["token", "twitter"]);
|
|
399
386
|
expect(exitCode).toBe(0);
|
|
400
387
|
expect(stdout).toBe("refreshed-new-token\n");
|
|
401
388
|
});
|
|
@@ -413,7 +400,7 @@ describe("assistant oauth token <provider-key>", () => {
|
|
|
413
400
|
describe("assistant oauth providers list", () => {
|
|
414
401
|
const fakeProviders = [
|
|
415
402
|
{
|
|
416
|
-
providerKey: "
|
|
403
|
+
providerKey: "google",
|
|
417
404
|
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
418
405
|
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
419
406
|
defaultScopes: "[]",
|
|
@@ -423,7 +410,7 @@ describe("assistant oauth providers list", () => {
|
|
|
423
410
|
updatedAt: "2025-01-01T00:00:00.000Z",
|
|
424
411
|
},
|
|
425
412
|
{
|
|
426
|
-
providerKey: "
|
|
413
|
+
providerKey: "google-calendar",
|
|
427
414
|
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
428
415
|
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
429
416
|
defaultScopes: "[]",
|
|
@@ -433,7 +420,7 @@ describe("assistant oauth providers list", () => {
|
|
|
433
420
|
updatedAt: "2025-01-01T00:00:00.000Z",
|
|
434
421
|
},
|
|
435
422
|
{
|
|
436
|
-
providerKey: "
|
|
423
|
+
providerKey: "slack",
|
|
437
424
|
authUrl: "https://slack.com/oauth/v2/authorize",
|
|
438
425
|
tokenUrl: "https://slack.com/api/oauth.v2.access",
|
|
439
426
|
defaultScopes: "[]",
|
|
@@ -443,7 +430,7 @@ describe("assistant oauth providers list", () => {
|
|
|
443
430
|
updatedAt: "2025-01-01T00:00:00.000Z",
|
|
444
431
|
},
|
|
445
432
|
{
|
|
446
|
-
providerKey: "
|
|
433
|
+
providerKey: "twitter",
|
|
447
434
|
authUrl: "https://twitter.com/i/oauth2/authorize",
|
|
448
435
|
tokenUrl: "https://api.twitter.com/2/oauth2/token",
|
|
449
436
|
defaultScopes: "[]",
|
|
@@ -465,10 +452,10 @@ describe("assistant oauth providers list", () => {
|
|
|
465
452
|
const parsed = JSON.parse(stdout);
|
|
466
453
|
expect(parsed).toHaveLength(4);
|
|
467
454
|
const keys = parsed.map((p: { providerKey: string }) => p.providerKey);
|
|
468
|
-
expect(keys).toContain("
|
|
469
|
-
expect(keys).toContain("
|
|
470
|
-
expect(keys).toContain("
|
|
471
|
-
expect(keys).toContain("
|
|
455
|
+
expect(keys).toContain("google");
|
|
456
|
+
expect(keys).toContain("google-calendar");
|
|
457
|
+
expect(keys).toContain("slack");
|
|
458
|
+
expect(keys).toContain("twitter");
|
|
472
459
|
});
|
|
473
460
|
|
|
474
461
|
test("filters by single --provider-key value", async () => {
|
|
@@ -482,7 +469,7 @@ describe("assistant oauth providers list", () => {
|
|
|
482
469
|
expect(exitCode).toBe(0);
|
|
483
470
|
const parsed = JSON.parse(stdout);
|
|
484
471
|
expect(parsed).toHaveLength(1);
|
|
485
|
-
expect(parsed[0].providerKey).toBe("
|
|
472
|
+
expect(parsed[0].providerKey).toBe("slack");
|
|
486
473
|
});
|
|
487
474
|
|
|
488
475
|
test("filters by comma-separated OR values", async () => {
|
|
@@ -497,9 +484,9 @@ describe("assistant oauth providers list", () => {
|
|
|
497
484
|
const parsed = JSON.parse(stdout);
|
|
498
485
|
expect(parsed).toHaveLength(3);
|
|
499
486
|
const keys = parsed.map((p: { providerKey: string }) => p.providerKey);
|
|
500
|
-
expect(keys).toContain("
|
|
501
|
-
expect(keys).toContain("
|
|
502
|
-
expect(keys).toContain("
|
|
487
|
+
expect(keys).toContain("google");
|
|
488
|
+
expect(keys).toContain("google-calendar");
|
|
489
|
+
expect(keys).toContain("slack");
|
|
503
490
|
});
|
|
504
491
|
|
|
505
492
|
test("returns empty array when comma-separated filter has no matches", async () => {
|
|
@@ -527,9 +514,9 @@ describe("assistant oauth providers list", () => {
|
|
|
527
514
|
const parsed = JSON.parse(stdout);
|
|
528
515
|
expect(parsed).toHaveLength(3);
|
|
529
516
|
const keys = parsed.map((p: { providerKey: string }) => p.providerKey);
|
|
530
|
-
expect(keys).toContain("
|
|
531
|
-
expect(keys).toContain("
|
|
532
|
-
expect(keys).toContain("
|
|
517
|
+
expect(keys).toContain("google");
|
|
518
|
+
expect(keys).toContain("google-calendar");
|
|
519
|
+
expect(keys).toContain("slack");
|
|
533
520
|
});
|
|
534
521
|
|
|
535
522
|
test("ignores empty segments from extra commas in --provider-key", async () => {
|
|
@@ -544,9 +531,9 @@ describe("assistant oauth providers list", () => {
|
|
|
544
531
|
const parsed = JSON.parse(stdout);
|
|
545
532
|
expect(parsed).toHaveLength(3);
|
|
546
533
|
const keys = parsed.map((p: { providerKey: string }) => p.providerKey);
|
|
547
|
-
expect(keys).toContain("
|
|
548
|
-
expect(keys).toContain("
|
|
549
|
-
expect(keys).toContain("
|
|
534
|
+
expect(keys).toContain("google");
|
|
535
|
+
expect(keys).toContain("google-calendar");
|
|
536
|
+
expect(keys).toContain("slack");
|
|
550
537
|
});
|
|
551
538
|
});
|
|
552
539
|
|
|
@@ -560,7 +547,7 @@ describe("assistant oauth apps upsert --client-secret-credential-path", () => {
|
|
|
560
547
|
mockUpsertAppCalls = [];
|
|
561
548
|
mockUpsertAppResult = {
|
|
562
549
|
id: "app-upsert-1",
|
|
563
|
-
providerKey: "
|
|
550
|
+
providerKey: "google",
|
|
564
551
|
clientId: "abc123",
|
|
565
552
|
createdAt: 1700000000000,
|
|
566
553
|
updatedAt: 1700000000000,
|
|
@@ -573,18 +560,20 @@ describe("assistant oauth apps upsert --client-secret-credential-path", () => {
|
|
|
573
560
|
mockGetAppByProviderAndClientId = () => undefined;
|
|
574
561
|
mockGetMostRecentAppByProvider = () => undefined;
|
|
575
562
|
mockGetProvider = () => undefined;
|
|
576
|
-
mockGetProviderBehavior = () => undefined;
|
|
577
563
|
mockGetSecureKey = () => undefined;
|
|
578
564
|
mockGetCredentialMetadata = () => undefined;
|
|
579
565
|
mockUpsertAppImpl = undefined;
|
|
580
566
|
});
|
|
581
567
|
|
|
582
568
|
test("upsert with --client-secret-credential-path passes path to upsertApp", async () => {
|
|
569
|
+
// "custom/path" has no colon and no credential/ or oauth_app/ prefix.
|
|
570
|
+
// resolveCredentialPath passes it through unchanged since it doesn't
|
|
571
|
+
// match the service:field shorthand pattern.
|
|
583
572
|
const { exitCode, stdout } = await runCli([
|
|
584
573
|
"apps",
|
|
585
574
|
"upsert",
|
|
586
575
|
"--provider",
|
|
587
|
-
"
|
|
576
|
+
"google",
|
|
588
577
|
"--client-id",
|
|
589
578
|
"abc123",
|
|
590
579
|
"--client-secret-credential-path",
|
|
@@ -594,7 +583,7 @@ describe("assistant oauth apps upsert --client-secret-credential-path", () => {
|
|
|
594
583
|
expect(exitCode).toBe(0);
|
|
595
584
|
expect(mockUpsertAppCalls).toHaveLength(1);
|
|
596
585
|
expect(mockUpsertAppCalls[0]).toEqual({
|
|
597
|
-
provider: "
|
|
586
|
+
provider: "google",
|
|
598
587
|
clientId: "abc123",
|
|
599
588
|
clientSecretOpts: { clientSecretCredentialPath: "custom/path" },
|
|
600
589
|
});
|
|
@@ -607,7 +596,7 @@ describe("assistant oauth apps upsert --client-secret-credential-path", () => {
|
|
|
607
596
|
"apps",
|
|
608
597
|
"upsert",
|
|
609
598
|
"--provider",
|
|
610
|
-
"
|
|
599
|
+
"google",
|
|
611
600
|
"--client-id",
|
|
612
601
|
"abc123",
|
|
613
602
|
"--client-secret",
|
|
@@ -631,7 +620,7 @@ describe("assistant oauth apps upsert --client-secret-credential-path", () => {
|
|
|
631
620
|
"apps",
|
|
632
621
|
"upsert",
|
|
633
622
|
"--provider",
|
|
634
|
-
"
|
|
623
|
+
"google",
|
|
635
624
|
"--client-id",
|
|
636
625
|
"abc123",
|
|
637
626
|
"--client-secret",
|
|
@@ -641,7 +630,7 @@ describe("assistant oauth apps upsert --client-secret-credential-path", () => {
|
|
|
641
630
|
expect(exitCode).toBe(0);
|
|
642
631
|
expect(mockUpsertAppCalls).toHaveLength(1);
|
|
643
632
|
expect(mockUpsertAppCalls[0]).toEqual({
|
|
644
|
-
provider: "
|
|
633
|
+
provider: "google",
|
|
645
634
|
clientId: "abc123",
|
|
646
635
|
clientSecretOpts: { clientSecretValue: "s3cret" },
|
|
647
636
|
});
|
|
@@ -652,7 +641,7 @@ describe("assistant oauth apps upsert --client-secret-credential-path", () => {
|
|
|
652
641
|
"apps",
|
|
653
642
|
"upsert",
|
|
654
643
|
"--provider",
|
|
655
|
-
"
|
|
644
|
+
"google",
|
|
656
645
|
"--client-id",
|
|
657
646
|
"abc123",
|
|
658
647
|
"--json",
|
|
@@ -660,48 +649,57 @@ describe("assistant oauth apps upsert --client-secret-credential-path", () => {
|
|
|
660
649
|
expect(exitCode).toBe(0);
|
|
661
650
|
expect(mockUpsertAppCalls).toHaveLength(1);
|
|
662
651
|
expect(mockUpsertAppCalls[0]).toEqual({
|
|
663
|
-
provider: "
|
|
652
|
+
provider: "google",
|
|
664
653
|
clientId: "abc123",
|
|
665
654
|
clientSecretOpts: undefined,
|
|
666
655
|
});
|
|
667
656
|
});
|
|
668
657
|
|
|
669
|
-
test("upsert resolves
|
|
670
|
-
// The
|
|
671
|
-
// "integration:google:client_secret" → service="integration:google", field="client_secret"
|
|
672
|
-
mockGetCredentialMetadata = (service, field) =>
|
|
673
|
-
service === "integration:google" && field === "client_secret"
|
|
674
|
-
? {
|
|
675
|
-
credentialId: "cred-1",
|
|
676
|
-
service: "integration:google",
|
|
677
|
-
field: "client_secret",
|
|
678
|
-
allowedTools: [],
|
|
679
|
-
allowedDomains: [],
|
|
680
|
-
createdAt: Date.now(),
|
|
681
|
-
updatedAt: Date.now(),
|
|
682
|
-
}
|
|
683
|
-
: undefined;
|
|
684
|
-
|
|
658
|
+
test("upsert resolves service:field shorthand to full credential path", async () => {
|
|
659
|
+
// The service:field shorthand is resolved to credential/{service}/{field}
|
|
685
660
|
const { exitCode, stdout } = await runCli([
|
|
686
661
|
"apps",
|
|
687
662
|
"upsert",
|
|
688
663
|
"--provider",
|
|
689
|
-
"
|
|
664
|
+
"google",
|
|
690
665
|
"--client-id",
|
|
691
666
|
"abc",
|
|
692
667
|
"--client-secret-credential-path",
|
|
693
|
-
"
|
|
668
|
+
"google:client_secret",
|
|
694
669
|
"--json",
|
|
695
670
|
]);
|
|
696
671
|
expect(exitCode).toBe(0);
|
|
697
672
|
expect(mockUpsertAppCalls).toHaveLength(1);
|
|
698
|
-
// The non-prefixed path should have been resolved to the full credential key
|
|
699
673
|
expect(mockUpsertAppCalls[0]).toEqual({
|
|
700
|
-
provider: "
|
|
674
|
+
provider: "google",
|
|
701
675
|
clientId: "abc",
|
|
702
676
|
clientSecretOpts: {
|
|
703
|
-
clientSecretCredentialPath:
|
|
704
|
-
|
|
677
|
+
clientSecretCredentialPath: "credential/google/client_secret",
|
|
678
|
+
},
|
|
679
|
+
});
|
|
680
|
+
const parsed = JSON.parse(stdout);
|
|
681
|
+
expect(parsed.id).toBe("app-upsert-1");
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
test("upsert resolves slack:client_secret shorthand to full credential path", async () => {
|
|
685
|
+
const { exitCode, stdout } = await runCli([
|
|
686
|
+
"apps",
|
|
687
|
+
"upsert",
|
|
688
|
+
"--provider",
|
|
689
|
+
"slack",
|
|
690
|
+
"--client-id",
|
|
691
|
+
"slack-abc",
|
|
692
|
+
"--client-secret-credential-path",
|
|
693
|
+
"slack:client_secret",
|
|
694
|
+
"--json",
|
|
695
|
+
]);
|
|
696
|
+
expect(exitCode).toBe(0);
|
|
697
|
+
expect(mockUpsertAppCalls).toHaveLength(1);
|
|
698
|
+
expect(mockUpsertAppCalls[0]).toEqual({
|
|
699
|
+
provider: "slack",
|
|
700
|
+
clientId: "slack-abc",
|
|
701
|
+
clientSecretOpts: {
|
|
702
|
+
clientSecretCredentialPath: "credential/slack/client_secret",
|
|
705
703
|
},
|
|
706
704
|
});
|
|
707
705
|
const parsed = JSON.parse(stdout);
|
|
@@ -713,22 +711,47 @@ describe("assistant oauth apps upsert --client-secret-credential-path", () => {
|
|
|
713
711
|
"apps",
|
|
714
712
|
"upsert",
|
|
715
713
|
"--provider",
|
|
716
|
-
"
|
|
714
|
+
"google",
|
|
717
715
|
"--client-id",
|
|
718
716
|
"abc",
|
|
719
717
|
"--client-secret-credential-path",
|
|
720
|
-
"credential/
|
|
718
|
+
"credential/google/client_secret",
|
|
721
719
|
"--json",
|
|
722
720
|
]);
|
|
723
721
|
expect(exitCode).toBe(0);
|
|
724
722
|
expect(mockUpsertAppCalls).toHaveLength(1);
|
|
725
723
|
// Already-prefixed path should be passed through as-is
|
|
726
724
|
expect(mockUpsertAppCalls[0]).toEqual({
|
|
727
|
-
provider: "
|
|
725
|
+
provider: "google",
|
|
726
|
+
clientId: "abc",
|
|
727
|
+
clientSecretOpts: {
|
|
728
|
+
clientSecretCredentialPath: "credential/google/client_secret",
|
|
729
|
+
},
|
|
730
|
+
});
|
|
731
|
+
const parsed = JSON.parse(stdout);
|
|
732
|
+
expect(parsed.id).toBe("app-upsert-1");
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
test("upsert passes oauth_app/ prefixed credential path through unchanged", async () => {
|
|
736
|
+
const { exitCode, stdout } = await runCli([
|
|
737
|
+
"apps",
|
|
738
|
+
"upsert",
|
|
739
|
+
"--provider",
|
|
740
|
+
"google",
|
|
741
|
+
"--client-id",
|
|
742
|
+
"abc",
|
|
743
|
+
"--client-secret-credential-path",
|
|
744
|
+
"oauth_app/some-id/client_secret",
|
|
745
|
+
"--json",
|
|
746
|
+
]);
|
|
747
|
+
expect(exitCode).toBe(0);
|
|
748
|
+
expect(mockUpsertAppCalls).toHaveLength(1);
|
|
749
|
+
// oauth_app/ prefixed path should be passed through as-is
|
|
750
|
+
expect(mockUpsertAppCalls[0]).toEqual({
|
|
751
|
+
provider: "google",
|
|
728
752
|
clientId: "abc",
|
|
729
753
|
clientSecretOpts: {
|
|
730
|
-
clientSecretCredentialPath:
|
|
731
|
-
"credential/integration:google/client_secret",
|
|
754
|
+
clientSecretCredentialPath: "oauth_app/some-id/client_secret",
|
|
732
755
|
},
|
|
733
756
|
});
|
|
734
757
|
const parsed = JSON.parse(stdout);
|
|
@@ -747,7 +770,7 @@ describe("assistant oauth apps upsert --client-secret-credential-path", () => {
|
|
|
747
770
|
"apps",
|
|
748
771
|
"upsert",
|
|
749
772
|
"--provider",
|
|
750
|
-
"
|
|
773
|
+
"google",
|
|
751
774
|
"--client-id",
|
|
752
775
|
"abc",
|
|
753
776
|
"--client-secret-credential-path",
|
|
@@ -776,7 +799,7 @@ describe("assistant oauth ping <provider-key>", () => {
|
|
|
776
799
|
|
|
777
800
|
test("returns ok when ping endpoint returns 200", async () => {
|
|
778
801
|
mockGetProvider = () => ({
|
|
779
|
-
providerKey: "
|
|
802
|
+
providerKey: "google",
|
|
780
803
|
pingUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
|
|
781
804
|
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
782
805
|
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
@@ -788,16 +811,12 @@ describe("assistant oauth ping <provider-key>", () => {
|
|
|
788
811
|
});
|
|
789
812
|
mockResolveOAuthConnection = async () => ({
|
|
790
813
|
id: "conn-1",
|
|
791
|
-
providerKey: "
|
|
814
|
+
providerKey: "google",
|
|
792
815
|
accountInfo: null,
|
|
793
816
|
request: async () => ({ status: 200, headers: {}, body: {} }),
|
|
794
817
|
withToken: async (fn) => fn("mock-access-token-xyz"),
|
|
795
818
|
});
|
|
796
|
-
const { exitCode, stdout } = await runCli([
|
|
797
|
-
"ping",
|
|
798
|
-
"integration:google",
|
|
799
|
-
"--json",
|
|
800
|
-
]);
|
|
819
|
+
const { exitCode, stdout } = await runCli(["ping", "google", "--json"]);
|
|
801
820
|
expect(exitCode).toBe(0);
|
|
802
821
|
const parsed = JSON.parse(stdout);
|
|
803
822
|
expect(parsed.ok).toBe(true);
|
|
@@ -806,11 +825,7 @@ describe("assistant oauth ping <provider-key>", () => {
|
|
|
806
825
|
|
|
807
826
|
test("exits 1 when provider not found", async () => {
|
|
808
827
|
mockGetProvider = () => undefined;
|
|
809
|
-
const { exitCode, stdout } = await runCli([
|
|
810
|
-
"ping",
|
|
811
|
-
"integration:unknown",
|
|
812
|
-
"--json",
|
|
813
|
-
]);
|
|
828
|
+
const { exitCode, stdout } = await runCli(["ping", "unknown", "--json"]);
|
|
814
829
|
expect(exitCode).toBe(1);
|
|
815
830
|
const parsed = JSON.parse(stdout);
|
|
816
831
|
expect(parsed.ok).toBe(false);
|
|
@@ -838,7 +853,7 @@ describe("assistant oauth ping <provider-key>", () => {
|
|
|
838
853
|
|
|
839
854
|
test("exits 1 when ping endpoint returns non-2xx", async () => {
|
|
840
855
|
mockGetProvider = () => ({
|
|
841
|
-
providerKey: "
|
|
856
|
+
providerKey: "google",
|
|
842
857
|
pingUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
|
|
843
858
|
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
844
859
|
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
@@ -850,16 +865,12 @@ describe("assistant oauth ping <provider-key>", () => {
|
|
|
850
865
|
});
|
|
851
866
|
mockResolveOAuthConnection = async () => ({
|
|
852
867
|
id: "conn-1",
|
|
853
|
-
providerKey: "
|
|
868
|
+
providerKey: "google",
|
|
854
869
|
accountInfo: null,
|
|
855
870
|
request: async () => ({ status: 403, headers: {}, body: "Forbidden" }),
|
|
856
871
|
withToken: async (fn) => fn("mock-access-token-xyz"),
|
|
857
872
|
});
|
|
858
|
-
const { exitCode, stdout } = await runCli([
|
|
859
|
-
"ping",
|
|
860
|
-
"integration:google",
|
|
861
|
-
"--json",
|
|
862
|
-
]);
|
|
873
|
+
const { exitCode, stdout } = await runCli(["ping", "google", "--json"]);
|
|
863
874
|
expect(exitCode).toBe(1);
|
|
864
875
|
const parsed = JSON.parse(stdout);
|
|
865
876
|
expect(parsed.ok).toBe(false);
|
|
@@ -868,7 +879,7 @@ describe("assistant oauth ping <provider-key>", () => {
|
|
|
868
879
|
|
|
869
880
|
test("exits 1 when no connection can be resolved", async () => {
|
|
870
881
|
mockGetProvider = () => ({
|
|
871
|
-
providerKey: "
|
|
882
|
+
providerKey: "google",
|
|
872
883
|
pingUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
|
|
873
884
|
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
874
885
|
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
@@ -879,13 +890,9 @@ describe("assistant oauth ping <provider-key>", () => {
|
|
|
879
890
|
updatedAt: Date.now(),
|
|
880
891
|
});
|
|
881
892
|
mockResolveOAuthConnection = async () => {
|
|
882
|
-
throw new Error('No access token found for "
|
|
893
|
+
throw new Error('No access token found for "google".');
|
|
883
894
|
};
|
|
884
|
-
const { exitCode, stdout } = await runCli([
|
|
885
|
-
"ping",
|
|
886
|
-
"integration:google",
|
|
887
|
-
"--json",
|
|
888
|
-
]);
|
|
895
|
+
const { exitCode, stdout } = await runCli(["ping", "google", "--json"]);
|
|
889
896
|
expect(exitCode).toBe(1);
|
|
890
897
|
const parsed = JSON.parse(stdout);
|
|
891
898
|
expect(parsed.ok).toBe(false);
|
|
@@ -1,23 +1,58 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
import { mkdtempSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
5
|
+
|
|
6
|
+
const testDir = mkdtempSync(join(tmpdir(), "oauth-provider-profiles-test-"));
|
|
7
|
+
|
|
8
|
+
mock.module("../util/platform.js", () => ({
|
|
9
|
+
getDataDir: () => testDir,
|
|
10
|
+
isMacOS: () => process.platform === "darwin",
|
|
11
|
+
isLinux: () => process.platform === "linux",
|
|
12
|
+
isWindows: () => process.platform === "win32",
|
|
13
|
+
getPidPath: () => join(testDir, "test.pid"),
|
|
14
|
+
getDbPath: () => ":memory:",
|
|
15
|
+
getLogPath: () => join(testDir, "test.log"),
|
|
16
|
+
ensureDataDir: () => {},
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
mock.module("../util/logger.js", () => ({
|
|
20
|
+
getLogger: () =>
|
|
21
|
+
new Proxy({} as Record<string, unknown>, {
|
|
22
|
+
get: () => () => {},
|
|
23
|
+
}),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
mock.module("../security/secure-keys.js", () => ({
|
|
27
|
+
deleteSecureKeyAsync: async () => "deleted" as const,
|
|
28
|
+
setSecureKeyAsync: async () => true,
|
|
29
|
+
getSecureKeyAsync: async () => undefined,
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
import { initializeDb } from "../memory/db.js";
|
|
33
|
+
import { getProvider } from "../oauth/oauth-store.js";
|
|
34
|
+
import { seedOAuthProviders } from "../oauth/seed-providers.js";
|
|
35
|
+
|
|
36
|
+
initializeDb();
|
|
37
|
+
seedOAuthProviders();
|
|
38
|
+
|
|
39
|
+
describe("oauth provider profiles (DB-seeded)", () => {
|
|
40
|
+
test("google provider row contains bearer injection templates for 3 Google API hosts", () => {
|
|
41
|
+
const provider = getProvider("google");
|
|
42
|
+
|
|
43
|
+
expect(provider).toBeDefined();
|
|
44
|
+
expect(provider?.injectionTemplates).toBeDefined();
|
|
45
|
+
|
|
46
|
+
const templates = JSON.parse(provider!.injectionTemplates!) as Array<{
|
|
47
|
+
hostPattern: string;
|
|
48
|
+
injectionType: string;
|
|
49
|
+
headerName: string;
|
|
50
|
+
valuePrefix: string;
|
|
51
|
+
}>;
|
|
52
|
+
|
|
53
|
+
expect(templates).toHaveLength(3);
|
|
54
|
+
|
|
55
|
+
const byHost = new Map(templates.map((t) => [t.hostPattern, t]));
|
|
21
56
|
|
|
22
57
|
for (const host of [
|
|
23
58
|
"gmail.googleapis.com",
|
|
@@ -12,7 +12,7 @@ function makeProfile(
|
|
|
12
12
|
overrides: Partial<ScopeResolverInput> = {},
|
|
13
13
|
): ScopeResolverInput {
|
|
14
14
|
return {
|
|
15
|
-
service: "
|
|
15
|
+
service: "test-service",
|
|
16
16
|
defaultScopes: ["read", "write"],
|
|
17
17
|
scopePolicy: {
|
|
18
18
|
allowAdditionalScopes: false,
|
|
@@ -57,9 +57,7 @@ describe("resolveScopes", () => {
|
|
|
57
57
|
const result = resolveScopes(profile, ["delete"]);
|
|
58
58
|
expect(result.ok).toBe(false);
|
|
59
59
|
if (!result.ok) {
|
|
60
|
-
expect(result.error).toBe(
|
|
61
|
-
"Scope 'delete' is forbidden for integration:test-service",
|
|
62
|
-
);
|
|
60
|
+
expect(result.error).toBe("Scope 'delete' is forbidden for test-service");
|
|
63
61
|
}
|
|
64
62
|
});
|
|
65
63
|
|
|
@@ -76,7 +74,7 @@ describe("resolveScopes", () => {
|
|
|
76
74
|
expect(result.ok).toBe(false);
|
|
77
75
|
if (!result.ok) {
|
|
78
76
|
expect(result.error).toContain(
|
|
79
|
-
"Additional scopes are not allowed for
|
|
77
|
+
"Additional scopes are not allowed for test-service",
|
|
80
78
|
);
|
|
81
79
|
expect(result.allowedScopes).toEqual(["read", "write"]);
|
|
82
80
|
}
|
|
@@ -95,7 +93,7 @@ describe("resolveScopes", () => {
|
|
|
95
93
|
expect(result.ok).toBe(false);
|
|
96
94
|
if (!result.ok) {
|
|
97
95
|
expect(result.error).toBe(
|
|
98
|
-
"Scope 'admin' is not in the allowed optional scopes for
|
|
96
|
+
"Scope 'admin' is not in the allowed optional scopes for test-service",
|
|
99
97
|
);
|
|
100
98
|
expect(result.allowedScopes).toEqual(["read", "write"]);
|
|
101
99
|
}
|