@vellumai/assistant 0.4.35 → 0.4.37
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 +1 -1
- package/ARCHITECTURE.md +44 -49
- package/README.md +32 -20
- package/docs/architecture/keychain-broker.md +186 -0
- package/docs/architecture/security.md +110 -116
- package/docs/runbook-trusted-contacts.md +2 -2
- package/docs/skills.md +25 -25
- package/package.json +5 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +11 -2
- package/src/__tests__/actor-token-service.test.ts +1 -0
- package/src/__tests__/amazon-cdp-integration.test.ts +74 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +38 -9
- package/src/__tests__/assistant-id-boundary-guard.test.ts +29 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/bundle-scanner.test.ts +1 -1
- package/src/__tests__/channel-guardian.test.ts +102 -102
- package/src/__tests__/channel-invite-transport.test.ts +155 -256
- package/src/__tests__/channel-readiness-routes.test.ts +336 -0
- package/src/__tests__/checker.test.ts +6 -6
- package/src/__tests__/chrome-cdp.test.ts +350 -0
- package/src/__tests__/computer-use-session-lifecycle.test.ts +3 -3
- package/src/__tests__/computer-use-session-working-dir.test.ts +86 -52
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +1 -1
- package/src/__tests__/config-loader-migration.test.ts +85 -0
- package/src/__tests__/conversation-pairing.test.ts +370 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +1 -10
- package/src/__tests__/credential-broker-server-use.test.ts +1 -10
- package/src/__tests__/credential-security-e2e.test.ts +7 -1
- package/src/__tests__/credential-security-invariants.test.ts +14 -20
- package/src/__tests__/credential-vault-unit.test.ts +1 -11
- package/src/__tests__/credential-vault.test.ts +5 -19
- package/src/__tests__/credentials-cli.test.ts +814 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +23 -4
- package/src/__tests__/email-invite-adapter.test.ts +78 -0
- package/src/__tests__/email-service-config-fallback.test.ts +102 -0
- package/src/__tests__/encrypted-store.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +5 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +70 -12
- package/src/__tests__/guardian-outbound-http.test.ts +53 -47
- package/src/__tests__/handle-user-message-secret-resume.test.ts +23 -0
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +32 -23
- package/src/__tests__/handlers-telegram-config.test.ts +8 -2
- package/src/__tests__/handlers-twitter-config.test.ts +2 -2
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +108 -7
- package/src/__tests__/ingress-reconcile.test.ts +6 -0
- package/src/__tests__/intent-routing.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -0
- package/src/__tests__/ipc-snapshot.test.ts +8 -2
- package/src/__tests__/keychain-broker-client.test.ts +543 -0
- package/src/__tests__/llm-usage-store.test.ts +344 -0
- package/src/__tests__/mcp-client-auth.test.ts +2 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/migration-transport.test.ts +49 -0
- package/src/__tests__/notification-broadcaster.test.ts +205 -5
- package/src/__tests__/notification-deep-link.test.ts +365 -1
- package/src/__tests__/oauth-connect-handler.test.ts +2 -2
- package/src/__tests__/onboarding-starter-tasks.test.ts +17 -4
- package/src/__tests__/proxy-approval-callback.test.ts +1 -1
- package/src/__tests__/recording-handler.test.ts +1 -1
- package/src/__tests__/recording-intent-handler.test.ts +6 -1
- package/src/__tests__/recording-state-machine.test.ts +1 -1
- package/src/__tests__/relay-server.test.ts +9 -1
- package/src/__tests__/ride-shotgun-handler.test.ts +499 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +160 -1
- package/src/__tests__/script-proxy-injection-runtime.test.ts +299 -2
- package/src/__tests__/script-proxy-profile-template-fallback.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +8 -2
- package/src/__tests__/secure-keys.test.ts +175 -216
- package/src/__tests__/session-confirmation-signals.test.ts +1 -1
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -1
- package/src/__tests__/session-queue.test.ts +2 -1
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +2 -2
- package/src/__tests__/skill-feature-flags-integration.test.ts +29 -4
- package/src/__tests__/skill-feature-flags.test.ts +12 -9
- package/src/__tests__/skill-load-feature-flag.test.ts +26 -5
- package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
- package/src/__tests__/skills.test.ts +34 -4
- package/src/__tests__/slack-channel-config.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +26 -4
- package/src/__tests__/telegram-bot-username-resolution.test.ts +212 -0
- package/src/__tests__/telegram-invite-adapter.test.ts +164 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-permission-simulate-handler.test.ts +8 -2
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +9 -1
- package/src/__tests__/twitter-auth-handler.test.ts +2 -2
- package/src/__tests__/twitter-oauth-client.test.ts +1 -1
- package/src/__tests__/usage-routes.test.ts +339 -0
- package/src/__tests__/whatsapp-invite-adapter.test.ts +94 -0
- package/src/agent/loop.ts +3 -0
- package/src/amazon/checkout.ts +0 -1
- package/src/approvals/guardian-request-resolvers.ts +9 -1
- package/src/bundler/app-bundler.ts +28 -12
- package/src/bundler/bundle-scanner.ts +1 -1
- package/src/bundler/bundle-signer.ts +3 -3
- package/src/bundler/manifest.ts +1 -1
- package/src/bundler/signature-verifier.ts +3 -3
- package/src/channels/config.ts +1 -1
- package/src/cli/AGENTS.md +63 -0
- package/src/cli/__tests__/notifications.test.ts +470 -0
- package/src/cli/amazon.ts +344 -167
- package/src/cli/audit.ts +85 -0
- package/src/cli/autonomy.ts +369 -0
- package/src/cli/channels.ts +51 -0
- package/src/cli/completions.ts +208 -0
- package/src/cli/config.ts +220 -0
- package/src/cli/contacts.ts +471 -0
- package/src/cli/credentials.ts +564 -0
- package/src/cli/default-action.ts +14 -0
- package/src/cli/dev.ts +131 -0
- package/src/cli/doctor.ts +398 -0
- package/src/cli/email.ts +494 -0
- package/src/cli/influencer.ts +72 -0
- package/src/cli/integrations.ts +248 -57
- package/src/cli/keys.ts +114 -0
- package/src/cli/map.ts +46 -54
- package/src/cli/mcp.ts +111 -3
- package/src/cli/{config-commands.ts → memory.ts} +134 -245
- package/src/cli/notifications.ts +407 -0
- package/src/cli/program.ts +65 -0
- package/src/cli/reference.ts +48 -0
- package/src/cli/sequence.ts +154 -0
- package/src/cli/sessions.ts +262 -0
- package/src/cli/trust.ts +175 -0
- package/src/cli/twitter.ts +323 -106
- package/src/config/__tests__/build-cli-reference-section.test.ts +49 -0
- package/src/config/bundled-skills/amazon/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/TOOLS.json +26 -0
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +13 -0
- package/src/config/bundled-skills/contacts/SKILL.md +178 -10
- package/src/config/bundled-skills/doordash/doordash-cli.ts +23 -168
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +135 -34
- package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
- package/src/config/bundled-skills/twilio-setup/SKILL.md +70 -17
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/core-schema.ts +7 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +26 -0
- package/src/config/schema.ts +4 -0
- package/src/config/skill-state.ts +0 -13
- package/src/config/system-prompt.ts +27 -0
- package/src/contacts/contact-store.ts +25 -0
- package/src/daemon/computer-use-session.ts +1 -1
- package/src/daemon/handlers/apps.ts +1 -0
- package/src/daemon/handlers/config-channels.ts +3 -3
- package/src/daemon/handlers/config-dispatch.ts +29 -0
- package/src/daemon/handlers/config-inbox.ts +4 -3
- package/src/daemon/handlers/config.ts +3 -43
- package/src/daemon/handlers/contacts.ts +34 -0
- package/src/daemon/handlers/index.ts +17 -3
- package/src/daemon/handlers/session-user-message.ts +7 -0
- package/src/daemon/handlers/sessions.ts +21 -2
- package/src/daemon/handlers/shared.ts +17 -0
- package/src/daemon/ipc-contract/apps.ts +2 -0
- package/src/daemon/ipc-contract/computer-use.ts +9 -0
- package/src/daemon/ipc-contract/contacts.ts +3 -3
- package/src/daemon/ipc-contract/inbox.ts +2 -0
- package/src/daemon/ipc-contract/messages.ts +4 -0
- package/src/daemon/ipc-contract/sessions.ts +8 -0
- package/src/daemon/ipc-contract-inventory.json +1 -0
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/ride-shotgun-handler.ts +139 -25
- package/src/daemon/session-agent-loop-handlers.ts +100 -0
- package/src/daemon/session-agent-loop.ts +72 -0
- package/src/daemon/session-tool-setup.ts +7 -0
- package/src/daemon/session.ts +23 -1
- package/src/daemon/tool-side-effects.ts +39 -1
- package/src/email/service.ts +59 -2
- package/src/index.ts +2 -60
- package/src/mcp/mcp-oauth-provider.ts +90 -8
- package/src/media/app-icon-generator.ts +86 -0
- package/src/memory/db-init.ts +11 -0
- package/src/memory/llm-usage-store.ts +186 -0
- package/src/memory/migrations/137-usage-dashboard-indexes.ts +26 -0
- package/src/memory/migrations/139-drop-usage-composite-indexes.ts +30 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema-migration.ts +1 -0
- package/src/memory/shared-app-links-store.ts +1 -1
- package/src/messaging/registry.ts +27 -0
- package/src/notifications/README.md +79 -70
- package/src/notifications/broadcaster.ts +2 -1
- package/src/notifications/conversation-pairing.ts +147 -13
- package/src/notifications/copy-composer.ts +7 -3
- package/src/notifications/destination-resolver.ts +14 -1
- package/src/notifications/emit-signal.ts +3 -2
- package/src/notifications/signal.ts +105 -1
- package/src/notifications/types.ts +16 -0
- package/src/permissions/checker.ts +29 -3
- package/src/permissions/prompter.ts +11 -3
- package/src/runtime/access-request-helper.ts +2 -1
- package/src/runtime/auth/route-policy.ts +7 -1
- package/src/runtime/channel-invite-transport.ts +40 -63
- package/src/runtime/channel-invite-transports/email.ts +13 -39
- package/src/runtime/channel-invite-transports/slack.ts +5 -34
- package/src/runtime/channel-invite-transports/sms.ts +8 -29
- package/src/runtime/channel-invite-transports/telegram.ts +69 -28
- package/src/runtime/channel-invite-transports/voice.ts +0 -7
- package/src/runtime/channel-invite-transports/whatsapp.ts +43 -0
- package/src/runtime/channel-readiness-service.ts +202 -45
- package/src/runtime/confirmation-request-guardian-bridge.ts +2 -1
- package/src/runtime/guardian-outbound-actions.ts +8 -5
- package/src/runtime/http-server.ts +2 -0
- package/src/runtime/invite-instruction-generator.ts +178 -0
- package/src/runtime/invite-service.ts +22 -25
- package/src/runtime/migrations/migration-transport.ts +13 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +8 -7
- package/src/runtime/routes/channel-readiness-routes.ts +30 -11
- package/src/runtime/routes/contact-routes.ts +54 -26
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +2 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +2 -1
- package/src/runtime/routes/integration-routes.ts +1 -1
- package/src/runtime/routes/invite-routes.ts +1 -1
- package/src/runtime/routes/secret-routes.ts +31 -7
- package/src/runtime/routes/twilio-routes.ts +32 -1
- package/src/runtime/routes/usage-routes.ts +114 -0
- package/src/runtime/tool-grant-request-helper.ts +2 -1
- package/src/security/encrypted-store.ts +9 -5
- package/src/security/keychain-broker-client.ts +393 -0
- package/src/security/secure-keys.ts +106 -321
- package/src/tools/apps/executors.ts +73 -0
- package/src/tools/browser/auto-navigate.ts +15 -6
- package/src/tools/browser/chrome-cdp.ts +211 -0
- package/src/tools/browser/network-recorder.test.ts +83 -0
- package/src/tools/browser/network-recorder.ts +8 -7
- package/src/tools/browser/x-auto-navigate.ts +12 -6
- package/src/tools/credentials/policy-types.ts +24 -0
- package/src/tools/credentials/vault.ts +22 -27
- package/src/tools/network/script-proxy/session-manager.ts +47 -3
- package/src/tools/permission-checker.ts +1 -0
- package/src/tools/types.ts +2 -0
- package/src/tools/ui-surface/definitions.ts +1 -2
- package/src/tools/watch/watch-state.ts +2 -0
- package/src/__tests__/key-migration.test.ts +0 -240
- package/src/__tests__/keychain.test.ts +0 -286
- package/src/cli/core-commands.ts +0 -899
- package/src/security/keychain-to-encrypted-migration.ts +0 -66
- package/src/security/keychain.ts +0 -490
|
@@ -1,291 +1,190 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return undefined;
|
|
12
|
-
},
|
|
13
|
-
upsertCredentialMetadata: () => {},
|
|
14
|
-
deleteCredentialMetadata: () => {},
|
|
15
|
-
listCredentialMetadata: () => [],
|
|
16
|
-
}));
|
|
1
|
+
/**
|
|
2
|
+
* Tests for channel invite adapter resolution and registry wiring.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that `resolveAdapterHandle()` correctly prefers the async
|
|
5
|
+
* path when available and falls back to the sync path for adapters
|
|
6
|
+
* that only implement `resolveChannelHandle`. Also covers the
|
|
7
|
+
* canonical registry APIs (`createInviteAdapterRegistry`,
|
|
8
|
+
* `getInviteAdapterRegistry`) that replaced the deprecated shims.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, expect, test } from "bun:test";
|
|
17
11
|
|
|
18
12
|
import {
|
|
19
|
-
_resetRegistry,
|
|
20
13
|
type ChannelInviteAdapter,
|
|
14
|
+
createInviteAdapterRegistry,
|
|
21
15
|
getInviteAdapterRegistry,
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
InviteAdapterRegistry,
|
|
17
|
+
resolveAdapterHandle,
|
|
24
18
|
} from "../runtime/channel-invite-transport.js";
|
|
25
|
-
import { telegramInviteAdapter } from "../runtime/channel-invite-transports/telegram.js";
|
|
26
|
-
|
|
27
|
-
describe("channel-invite-transport", () => {
|
|
28
|
-
beforeEach(() => {
|
|
29
|
-
_resetRegistry();
|
|
30
|
-
mockBotUsername = "test_invite_bot";
|
|
31
|
-
// Re-register after reset so Telegram tests work
|
|
32
|
-
registerTransport(telegramInviteAdapter);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// =========================================================================
|
|
36
|
-
// Registry
|
|
37
|
-
// =========================================================================
|
|
38
|
-
|
|
39
|
-
describe("registry", () => {
|
|
40
|
-
test("returns the Telegram adapter for telegram channel", () => {
|
|
41
|
-
const adapter = getTransport("telegram");
|
|
42
|
-
expect(adapter).toBeDefined();
|
|
43
|
-
expect(adapter!.channel).toBe("telegram");
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test("returns undefined for an unregistered channel", () => {
|
|
47
|
-
const adapter = getTransport("sms");
|
|
48
|
-
expect(adapter).toBeUndefined();
|
|
49
|
-
});
|
|
50
19
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
registerTransport(custom);
|
|
58
|
-
const adapter = getTransport("telegram");
|
|
59
|
-
expect(
|
|
60
|
-
adapter!.buildShareLink!({
|
|
61
|
-
rawToken: "x",
|
|
62
|
-
sourceChannel: "telegram",
|
|
63
|
-
}).url,
|
|
64
|
-
).toBe("custom");
|
|
65
|
-
});
|
|
20
|
+
describe("resolveAdapterHandle", () => {
|
|
21
|
+
test("returns sync handle when only resolveChannelHandle is defined", async () => {
|
|
22
|
+
const adapter: ChannelInviteAdapter = {
|
|
23
|
+
channel: "telegram",
|
|
24
|
+
resolveChannelHandle: () => "@mybot",
|
|
25
|
+
};
|
|
66
26
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
});
|
|
27
|
+
const handle = await resolveAdapterHandle(adapter);
|
|
28
|
+
expect(handle).toBe("@mybot");
|
|
29
|
+
});
|
|
71
30
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
31
|
+
test("returns undefined when sync resolveChannelHandle returns undefined", async () => {
|
|
32
|
+
const adapter: ChannelInviteAdapter = {
|
|
33
|
+
channel: "email",
|
|
34
|
+
resolveChannelHandle: () => undefined,
|
|
35
|
+
};
|
|
76
36
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const all = registry.getAll();
|
|
80
|
-
expect(all.length).toBeGreaterThanOrEqual(1);
|
|
81
|
-
expect(all.some((a) => a.channel === "telegram")).toBe(true);
|
|
82
|
-
});
|
|
37
|
+
const handle = await resolveAdapterHandle(adapter);
|
|
38
|
+
expect(handle).toBeUndefined();
|
|
83
39
|
});
|
|
84
40
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
describe("telegram buildShareLink", () => {
|
|
90
|
-
test("produces a valid Telegram deep link", () => {
|
|
91
|
-
const result = telegramInviteAdapter.buildShareLink!({
|
|
92
|
-
rawToken: "abc123_test-token",
|
|
93
|
-
sourceChannel: "telegram",
|
|
94
|
-
});
|
|
41
|
+
test("returns undefined when adapter has no handle resolution methods", async () => {
|
|
42
|
+
const adapter: ChannelInviteAdapter = {
|
|
43
|
+
channel: "slack",
|
|
44
|
+
};
|
|
95
45
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
expect(result.displayText).toContain(
|
|
100
|
-
"https://t.me/test_invite_bot?start=iv_abc123_test-token",
|
|
101
|
-
);
|
|
102
|
-
});
|
|
46
|
+
const handle = await resolveAdapterHandle(adapter);
|
|
47
|
+
expect(handle).toBeUndefined();
|
|
48
|
+
});
|
|
103
49
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const b = telegramInviteAdapter.buildShareLink!({
|
|
110
|
-
rawToken: "tok1",
|
|
111
|
-
sourceChannel: "telegram",
|
|
112
|
-
});
|
|
113
|
-
expect(a.url).toBe(b.url);
|
|
114
|
-
expect(a.displayText).toBe(b.displayText);
|
|
115
|
-
});
|
|
50
|
+
test("returns async handle when only resolveChannelHandleAsync is defined", async () => {
|
|
51
|
+
const adapter: ChannelInviteAdapter = {
|
|
52
|
+
channel: "email",
|
|
53
|
+
resolveChannelHandleAsync: async () => "hello@assistant.agentmail.to",
|
|
54
|
+
};
|
|
116
55
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
rawToken: "token",
|
|
121
|
-
sourceChannel: "telegram",
|
|
122
|
-
});
|
|
123
|
-
expect(result.url).toBe("https://t.me/my_custom_bot?start=iv_token");
|
|
124
|
-
});
|
|
56
|
+
const handle = await resolveAdapterHandle(adapter);
|
|
57
|
+
expect(handle).toBe("hello@assistant.agentmail.to");
|
|
58
|
+
});
|
|
125
59
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
expect(() =>
|
|
133
|
-
telegramInviteAdapter.buildShareLink!({
|
|
134
|
-
rawToken: "token",
|
|
135
|
-
sourceChannel: "telegram",
|
|
136
|
-
}),
|
|
137
|
-
).toThrow("bot username is not configured");
|
|
138
|
-
} finally {
|
|
139
|
-
if (prev !== undefined) process.env.TELEGRAM_BOT_USERNAME = prev;
|
|
140
|
-
}
|
|
141
|
-
});
|
|
60
|
+
test("prefers async over sync when both are defined", async () => {
|
|
61
|
+
const adapter: ChannelInviteAdapter = {
|
|
62
|
+
channel: "email",
|
|
63
|
+
resolveChannelHandle: () => "sync-handle",
|
|
64
|
+
resolveChannelHandleAsync: async () => "async-handle",
|
|
65
|
+
};
|
|
142
66
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const prev = process.env.TELEGRAM_BOT_USERNAME;
|
|
146
|
-
process.env.TELEGRAM_BOT_USERNAME = "env_bot";
|
|
147
|
-
try {
|
|
148
|
-
const result = telegramInviteAdapter.buildShareLink!({
|
|
149
|
-
rawToken: "token",
|
|
150
|
-
sourceChannel: "telegram",
|
|
151
|
-
});
|
|
152
|
-
expect(result.url).toBe("https://t.me/env_bot?start=iv_token");
|
|
153
|
-
} finally {
|
|
154
|
-
if (prev !== undefined) {
|
|
155
|
-
process.env.TELEGRAM_BOT_USERNAME = prev;
|
|
156
|
-
} else {
|
|
157
|
-
delete process.env.TELEGRAM_BOT_USERNAME;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
});
|
|
67
|
+
const handle = await resolveAdapterHandle(adapter);
|
|
68
|
+
expect(handle).toBe("async-handle");
|
|
161
69
|
});
|
|
162
70
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
71
|
+
test("returns undefined when async resolveChannelHandleAsync returns undefined", async () => {
|
|
72
|
+
const adapter: ChannelInviteAdapter = {
|
|
73
|
+
channel: "whatsapp",
|
|
74
|
+
resolveChannelHandleAsync: async () => undefined,
|
|
75
|
+
};
|
|
166
76
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
commandIntent: { type: "start", payload: "iv_abc123" },
|
|
171
|
-
content: "/start iv_abc123",
|
|
172
|
-
});
|
|
173
|
-
expect(token).toBe("abc123");
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
test("extracts base64url token from commandIntent", () => {
|
|
177
|
-
const token = telegramInviteAdapter.extractInboundToken!({
|
|
178
|
-
commandIntent: { type: "start", payload: "iv_YWJjMTIz-_test" },
|
|
179
|
-
content: "/start iv_YWJjMTIz-_test",
|
|
180
|
-
});
|
|
181
|
-
expect(token).toBe("YWJjMTIz-_test");
|
|
182
|
-
});
|
|
77
|
+
const handle = await resolveAdapterHandle(adapter);
|
|
78
|
+
expect(handle).toBeUndefined();
|
|
79
|
+
});
|
|
183
80
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
81
|
+
test("returns undefined when async resolveChannelHandleAsync rejects", async () => {
|
|
82
|
+
const adapter: ChannelInviteAdapter = {
|
|
83
|
+
channel: "email",
|
|
84
|
+
resolveChannelHandleAsync: async () => {
|
|
85
|
+
throw new Error("transient API failure");
|
|
86
|
+
},
|
|
87
|
+
};
|
|
191
88
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
content: "/start gv_abc123",
|
|
196
|
-
});
|
|
197
|
-
expect(token).toBeUndefined();
|
|
198
|
-
});
|
|
89
|
+
const handle = await resolveAdapterHandle(adapter);
|
|
90
|
+
expect(handle).toBeUndefined();
|
|
91
|
+
});
|
|
199
92
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
93
|
+
test("returns undefined when sync resolveChannelHandle throws", async () => {
|
|
94
|
+
const adapter: ChannelInviteAdapter = {
|
|
95
|
+
channel: "telegram",
|
|
96
|
+
resolveChannelHandle: () => {
|
|
97
|
+
throw new Error("credential lookup failed");
|
|
98
|
+
},
|
|
99
|
+
};
|
|
207
100
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
});
|
|
213
|
-
expect(token).toBeUndefined();
|
|
214
|
-
});
|
|
101
|
+
const handle = await resolveAdapterHandle(adapter);
|
|
102
|
+
expect(handle).toBeUndefined();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
215
105
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// Registry APIs
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
describe("createInviteAdapterRegistry", () => {
|
|
111
|
+
const builtInChannels = [
|
|
112
|
+
"email",
|
|
113
|
+
"slack",
|
|
114
|
+
"sms",
|
|
115
|
+
"telegram",
|
|
116
|
+
"voice",
|
|
117
|
+
"whatsapp",
|
|
118
|
+
] as const;
|
|
119
|
+
|
|
120
|
+
test("returns a registry with all built-in adapters registered", () => {
|
|
121
|
+
const registry = createInviteAdapterRegistry();
|
|
122
|
+
|
|
123
|
+
for (const channel of builtInChannels) {
|
|
124
|
+
const adapter = registry.get(channel);
|
|
125
|
+
expect(adapter).toBeDefined();
|
|
126
|
+
expect(adapter!.channel).toBe(channel);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
223
129
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
content: "/start iv_ ",
|
|
228
|
-
});
|
|
229
|
-
expect(token).toBeUndefined();
|
|
230
|
-
});
|
|
130
|
+
test("getAll returns exactly the built-in adapters", () => {
|
|
131
|
+
const registry = createInviteAdapterRegistry();
|
|
132
|
+
const all = registry.getAll();
|
|
231
133
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
134
|
+
expect(all).toHaveLength(builtInChannels.length);
|
|
135
|
+
const channels = new Set(all.map((a) => a.channel));
|
|
136
|
+
for (const ch of builtInChannels) {
|
|
137
|
+
expect(channels.has(ch)).toBe(true);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
238
140
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
141
|
+
test("returns a new registry instance on each call", () => {
|
|
142
|
+
const a = createInviteAdapterRegistry();
|
|
143
|
+
const b = createInviteAdapterRegistry();
|
|
144
|
+
expect(a).not.toBe(b);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
245
147
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
});
|
|
148
|
+
describe("getInviteAdapterRegistry", () => {
|
|
149
|
+
test("returns the singleton registry", () => {
|
|
150
|
+
const registry = getInviteAdapterRegistry();
|
|
151
|
+
expect(registry).toBeInstanceOf(InviteAdapterRegistry);
|
|
152
|
+
});
|
|
252
153
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
154
|
+
test("returns the same instance on repeated calls", () => {
|
|
155
|
+
const first = getInviteAdapterRegistry();
|
|
156
|
+
const second = getInviteAdapterRegistry();
|
|
157
|
+
expect(first).toBe(second);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
259
160
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
161
|
+
describe("InviteAdapterRegistry register / get", () => {
|
|
162
|
+
test("register stores and get retrieves a custom adapter", () => {
|
|
163
|
+
const registry = new InviteAdapterRegistry();
|
|
164
|
+
const custom: ChannelInviteAdapter = {
|
|
165
|
+
channel: "telegram",
|
|
166
|
+
resolveChannelHandle: () => "@custom",
|
|
167
|
+
};
|
|
266
168
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
});
|
|
271
|
-
expect(token).toBeUndefined();
|
|
272
|
-
});
|
|
169
|
+
registry.register(custom);
|
|
170
|
+
expect(registry.get("telegram")).toBe(custom);
|
|
171
|
+
});
|
|
273
172
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
173
|
+
test("register overwrites a previously registered adapter", () => {
|
|
174
|
+
const registry = new InviteAdapterRegistry();
|
|
175
|
+
const first: ChannelInviteAdapter = { channel: "email" };
|
|
176
|
+
const second: ChannelInviteAdapter = {
|
|
177
|
+
channel: "email",
|
|
178
|
+
resolveChannelHandle: () => "new@example.com",
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
registry.register(first);
|
|
182
|
+
registry.register(second);
|
|
183
|
+
expect(registry.get("email")).toBe(second);
|
|
184
|
+
});
|
|
281
185
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
commandIntent: { type: "start", payload: "gv_abc123" },
|
|
286
|
-
content: "/start iv_valid_token",
|
|
287
|
-
});
|
|
288
|
-
expect(token).toBeUndefined();
|
|
289
|
-
});
|
|
186
|
+
test("get returns undefined for an unregistered channel", () => {
|
|
187
|
+
const registry = new InviteAdapterRegistry();
|
|
188
|
+
expect(registry.get("slack")).toBeUndefined();
|
|
290
189
|
});
|
|
291
190
|
});
|