@vellumai/assistant 0.11.2-dev.202608070534.d55eaf1 → 0.11.2-dev.202608070630.7754d48
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/package.json
CHANGED
|
@@ -139,6 +139,51 @@ describe("syncManualTokenConnection", () => {
|
|
|
139
139
|
warnings.length = 0;
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
+
// ---- Discord server bot: one required field ----
|
|
143
|
+
|
|
144
|
+
test("connects discord_channel once the bot token is stored", async () => {
|
|
145
|
+
// Seeding the provider without this reconciliation leaves it listed and
|
|
146
|
+
// permanently disconnected, which is worse than not listing it: the
|
|
147
|
+
// setup skill stores the token and nothing ever reflects that.
|
|
148
|
+
setCredentialResult("discord_channel", "bot_token", {
|
|
149
|
+
value: "bot-token-value",
|
|
150
|
+
unreachable: false,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await syncManualTokenConnection("discord_channel");
|
|
154
|
+
|
|
155
|
+
expect(createdConnections.map((c) => c.provider)).toContain(
|
|
156
|
+
"discord_channel",
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("removes the discord_channel connection when the bot token is gone", async () => {
|
|
161
|
+
seedConnection("discord_channel");
|
|
162
|
+
setCredentialResult("discord_channel", "bot_token", {
|
|
163
|
+
value: undefined,
|
|
164
|
+
unreachable: false,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
await syncManualTokenConnection("discord_channel");
|
|
168
|
+
|
|
169
|
+
expect(deletedConnectionIds).toContain("conn-discord_channel");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("leaves the discord_channel row alone when the backend is unreachable", async () => {
|
|
173
|
+
// An unreachable credential store is not evidence the token is gone, so
|
|
174
|
+
// reconciliation must not read it as a disconnection.
|
|
175
|
+
seedConnection("discord_channel");
|
|
176
|
+
setCredentialResult("discord_channel", "bot_token", {
|
|
177
|
+
value: undefined,
|
|
178
|
+
unreachable: true,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
await syncManualTokenConnection("discord_channel");
|
|
182
|
+
|
|
183
|
+
expect(deletedConnectionIds).not.toContain("conn-discord_channel");
|
|
184
|
+
expect(connectionStore["discord_channel"]).toBeDefined();
|
|
185
|
+
});
|
|
186
|
+
|
|
142
187
|
// ---- Slack: reachable backend, missing tokens -> removes row ----
|
|
143
188
|
|
|
144
189
|
test("removes slack_channel connection when bot token is missing and backend is reachable", async () => {
|
|
@@ -47,6 +47,11 @@ export class BYOOAuthConnection implements OAuthConnection {
|
|
|
47
47
|
async (token) => {
|
|
48
48
|
const effectiveBaseUrl = req.baseUrl ?? this.baseUrl;
|
|
49
49
|
const isTelegram = this.provider === "telegram";
|
|
50
|
+
// Discord bot tokens authenticate with the `Bot ` scheme, not
|
|
51
|
+
// `Bearer`. Sending Bearer here reaches Discord as an unusable
|
|
52
|
+
// credential and comes back 401, which reads as a revoked token.
|
|
53
|
+
const authScheme =
|
|
54
|
+
this.provider === "discord_channel" ? "Bot" : "Bearer";
|
|
50
55
|
const requestPath = isTelegram
|
|
51
56
|
? buildTelegramBotApiPath(req.path, token)
|
|
52
57
|
: req.path;
|
|
@@ -87,7 +92,7 @@ export class BYOOAuthConnection implements OAuthConnection {
|
|
|
87
92
|
}
|
|
88
93
|
}
|
|
89
94
|
if (!isTelegram) {
|
|
90
|
-
headers.set("Authorization",
|
|
95
|
+
headers.set("Authorization", `${authScheme} ${token}`);
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
const resp = await fetch(fullUrl, {
|
|
@@ -49,6 +49,8 @@ function manualTokenAccessCredentialKey(provider: string): string | null {
|
|
|
49
49
|
return credentialKey("slack_channel", "bot_token");
|
|
50
50
|
case "telegram":
|
|
51
51
|
return credentialKey("telegram", "bot_token");
|
|
52
|
+
case "discord_channel":
|
|
53
|
+
return credentialKey("discord_channel", "bot_token");
|
|
52
54
|
case "sanity":
|
|
53
55
|
return credentialKey("sanity", "api_token");
|
|
54
56
|
default:
|
|
@@ -178,6 +178,24 @@ export async function syncManualTokenConnection(
|
|
|
178
178
|
return;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
case "discord_channel": {
|
|
182
|
+
const botTokenResult = await getSecureKeyResultAsync(
|
|
183
|
+
credentialKey("discord_channel", "bot_token"),
|
|
184
|
+
);
|
|
185
|
+
if (botTokenResult.unreachable) {
|
|
186
|
+
log.warn(
|
|
187
|
+
"Skipping discord_channel manual-token reconciliation — credential backend unreachable",
|
|
188
|
+
);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (botTokenResult.value) {
|
|
192
|
+
await ensureManualTokenConnection(provider, accountInfoToStore);
|
|
193
|
+
} else {
|
|
194
|
+
removeManualTokenConnection(provider);
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
181
199
|
case "sanity": {
|
|
182
200
|
const tokenResult = await getSecureKeyResultAsync(
|
|
183
201
|
credentialKey("sanity", "api_token"),
|
|
@@ -216,5 +234,6 @@ export async function syncManualTokenConnection(
|
|
|
216
234
|
export async function backfillManualTokenConnections(): Promise<void> {
|
|
217
235
|
await syncManualTokenConnection("telegram");
|
|
218
236
|
await syncManualTokenConnection("slack_channel");
|
|
237
|
+
await syncManualTokenConnection("discord_channel");
|
|
219
238
|
await syncManualTokenConnection("sanity");
|
|
220
239
|
}
|
|
@@ -454,16 +454,15 @@ export const PROVIDER_SEED_DATA: Record<
|
|
|
454
454
|
pingUrl: "https://discord.com/api/v10/users/@me",
|
|
455
455
|
baseUrl: "https://discord.com/api/v10",
|
|
456
456
|
displayLabel: "Discord",
|
|
457
|
-
|
|
457
|
+
// Your servers, not their contents. These scopes reach the guild list and
|
|
458
|
+
// your membership in one; reading messages is not among them. Discord
|
|
459
|
+
// documents `messages.read` as local RPC only, so requesting it would put
|
|
460
|
+
// a permission on the consent screen that grants nothing here.
|
|
461
|
+
description: "Your servers and profile",
|
|
458
462
|
dashboardUrl: "https://discord.com/developers/applications",
|
|
459
463
|
clientIdPlaceholder: null,
|
|
460
464
|
logoUrl: "https://cdn.simpleicons.org/discord",
|
|
461
|
-
defaultScopes: [
|
|
462
|
-
"identify",
|
|
463
|
-
"guilds",
|
|
464
|
-
"guilds.members.read",
|
|
465
|
-
"messages.read",
|
|
466
|
-
],
|
|
465
|
+
defaultScopes: ["identify", "guilds", "guilds.members.read"],
|
|
467
466
|
availableScopes:
|
|
468
467
|
"https://discord.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes",
|
|
469
468
|
loopbackPort: 17326,
|
|
@@ -777,6 +776,35 @@ export const PROVIDER_SEED_DATA: Record<
|
|
|
777
776
|
],
|
|
778
777
|
},
|
|
779
778
|
|
|
779
|
+
// The bot that sits in a server and talks to people there, which is a
|
|
780
|
+
// different thing from the `discord` integration above and starts at the
|
|
781
|
+
// same Discord authorize URL. Both are reasonably called "connecting
|
|
782
|
+
// Discord", so each has to be nameable on its own. The bot token is already
|
|
783
|
+
// stored under this key by `skills/discord-app-setup`; seeding the provider
|
|
784
|
+
// is what lets the product tell the two apart.
|
|
785
|
+
discord_channel: {
|
|
786
|
+
provider: "discord_channel",
|
|
787
|
+
authorizeUrl: "urn:manual-token",
|
|
788
|
+
tokenExchangeUrl: "urn:manual-token",
|
|
789
|
+
pingUrl: "https://discord.com/api/v10/users/@me",
|
|
790
|
+
baseUrl: "https://discord.com/api/v10",
|
|
791
|
+
displayLabel: "Discord Server",
|
|
792
|
+
description: "A bot in your server",
|
|
793
|
+
dashboardUrl: "https://discord.com/developers/applications",
|
|
794
|
+
clientIdPlaceholder: null,
|
|
795
|
+
requiresClientSecret: false,
|
|
796
|
+
logoUrl: "https://cdn.simpleicons.org/discord",
|
|
797
|
+
defaultScopes: [],
|
|
798
|
+
injectionTemplates: [
|
|
799
|
+
{
|
|
800
|
+
hostPattern: "discord.com",
|
|
801
|
+
injectionType: "header",
|
|
802
|
+
headerName: "Authorization",
|
|
803
|
+
valuePrefix: "Bot ",
|
|
804
|
+
},
|
|
805
|
+
],
|
|
806
|
+
},
|
|
807
|
+
|
|
780
808
|
telegram: {
|
|
781
809
|
provider: "telegram",
|
|
782
810
|
authorizeUrl: "urn:manual-token",
|