@mastra/discord 1.1.0 → 1.2.0-alpha.0

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/dist/index.d.cts CHANGED
@@ -83,7 +83,9 @@ interface DiscordProviderConfig {
83
83
  * App credentials (bot token / public key / application id). May be omitted
84
84
  * here and provided via the `DISCORD_BOT_TOKEN` / `DISCORD_PUBLIC_KEY` /
85
85
  * `DISCORD_APPLICATION_ID` env vars, or later via {@link DiscordProvider.configure}.
86
- * Whichever source resolves first is persisted **once** to channels storage.
86
+ * A bot token alone is enough: `publicKey` and `applicationId` are resolved
87
+ * from `GET /applications/@me` when omitted. Whichever source resolves first
88
+ * is persisted **once** to channels storage.
87
89
  */
88
90
  app?: Partial<DiscordAppConfig>;
89
91
  /**
@@ -333,7 +335,10 @@ declare class DiscordProvider implements ChannelProvider {
333
335
  /**
334
336
  * Provide or clear the app credentials at runtime. An object merges/overrides
335
337
  * `botToken` / `publicKey` / `applicationId` (persisted on the next `connect`);
336
- * `null` clears the stored app config.
338
+ * `null` clears the stored app config. Supplying a **different bot token**
339
+ * replaces the app config instead of merging: the token identifies the
340
+ * Discord application, so any `publicKey`/`applicationId` from the previous
341
+ * application must not survive the switch.
337
342
  */
338
343
  configure(credentials: Partial<DiscordAppConfig> | null): Promise<void>;
339
344
  /**
@@ -444,6 +449,12 @@ interface DiscordApplication {
444
449
  id: string;
445
450
  /** The application's name. */
446
451
  name: string;
452
+ /**
453
+ * The Ed25519 public key used to verify interaction signatures. Discord
454
+ * includes it on every application object, so a bot token alone is enough to
455
+ * resolve the interaction-verification key.
456
+ */
457
+ verify_key: string;
447
458
  }
448
459
  /**
449
460
  * Call the Discord REST API with Bot-token auth. `method` is required; supplying
package/dist/index.d.ts CHANGED
@@ -83,7 +83,9 @@ interface DiscordProviderConfig {
83
83
  * App credentials (bot token / public key / application id). May be omitted
84
84
  * here and provided via the `DISCORD_BOT_TOKEN` / `DISCORD_PUBLIC_KEY` /
85
85
  * `DISCORD_APPLICATION_ID` env vars, or later via {@link DiscordProvider.configure}.
86
- * Whichever source resolves first is persisted **once** to channels storage.
86
+ * A bot token alone is enough: `publicKey` and `applicationId` are resolved
87
+ * from `GET /applications/@me` when omitted. Whichever source resolves first
88
+ * is persisted **once** to channels storage.
87
89
  */
88
90
  app?: Partial<DiscordAppConfig>;
89
91
  /**
@@ -333,7 +335,10 @@ declare class DiscordProvider implements ChannelProvider {
333
335
  /**
334
336
  * Provide or clear the app credentials at runtime. An object merges/overrides
335
337
  * `botToken` / `publicKey` / `applicationId` (persisted on the next `connect`);
336
- * `null` clears the stored app config.
338
+ * `null` clears the stored app config. Supplying a **different bot token**
339
+ * replaces the app config instead of merging: the token identifies the
340
+ * Discord application, so any `publicKey`/`applicationId` from the previous
341
+ * application must not survive the switch.
337
342
  */
338
343
  configure(credentials: Partial<DiscordAppConfig> | null): Promise<void>;
339
344
  /**
@@ -444,6 +449,12 @@ interface DiscordApplication {
444
449
  id: string;
445
450
  /** The application's name. */
446
451
  name: string;
452
+ /**
453
+ * The Ed25519 public key used to verify interaction signatures. Discord
454
+ * includes it on every application object, so a bot token alone is enough to
455
+ * resolve the interaction-verification key.
456
+ */
457
+ verify_key: string;
447
458
  }
448
459
  /**
449
460
  * Call the Discord REST API with Bot-token auth. `method` is required; supplying
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/discord-provider.ts","../src/install-store.ts","../src/discord-client.ts","../src/commands.ts","../src/crypto.ts"],"mappings":";;;;;;;;;;;;cAgBa;;cAGA;;;;;;;;cASA;WACX;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;;;;;;;cAUW;;cAWA;;;;;;UAOI;;EAEf;;EAEA;;;KAIU;EAAiC;EAAc;;;;;;;;UAQ1C;;EAEf;;EAEA;;EAEA;;;;;;;;;;UAWe;;;;;;;EAOf,MAAM,QAAQ;;;;;EAKd;;;;;;EAMA,UAAU;;;;;EAKV;;;;;;;EAOA;;;;;EAKA;;;;;;EAMA,WAAW;;;;;;;;;;EAUX;;;;;EAKA,YAAY;;;;;;;;EAQZ;;;;;EAKA;;;;;;EAMA,mBAAmB;;EAEnB,SAAS;;;;;;;;EAQT,YAAY;;;;;;EAMZ;;EASA,WAAW;;EAEX,cAAc;;EAEd,cAAc;;EAEd,QAAQ;;EAER,gBAAgB;;EAEhB,cAAc;;EAEd,oBAAoB;;EAEpB,mBAAmB;;EAEnB,OAAO;;EAEP,cAAc;;;;;;;;EAQd,cAAc;;;;;;;EAOd,QAAQ;;EAER,aAAa,cAAc,+BAA+B;;;UAI3C;;;;;;;EAOf;;EAEA;;;;;EAKA,WAAW;;EAEX;;;;;;;;;UAUe;;EAEf;;EAEA;;;;;EAKA;;;;;EAKA;;EAEA;;EAEA;;EAEA,WAAW;;;;;;EAMX,kBAAkB;;EAElB,aAAa;;;;;;;;;;;iBC9PC,4BACd,QAAQ,KAAK;EAEb,WAAW;EACX;EACA,aAAa;EACb;;;;;;;;;;;;;;;;;;;;;;;;;;;cAmCW,2BAA2B;;WAC7B;EAoCT,YAAY,SAAQ;;;;;EAWpB,SAAS,QAAQ;;;;;;;;;EAwCjB,aAAa;;EAoBb,WAAW;;;;;;EAgCL,cAAc;;;;;;EA8Bd,UAAU,aAAa,QAAQ,2BAA2B;;;;;;;;;;;;;;EAuD1D,QAAQ,iBAAiB,UAAS,wBAA6B,QAAQ;;;;;;;EA4EvE,cAAc,mBAAmB,kBAAkB,QAAQ;;;;;;;;;;EAmD3D,WAAW,kBAAkB;;EAY7B,qBAAqB,QAAQ;;EAO7B,gBAAgB,kBAAkB,QAAQ;;EAMhD;;EAKA,WAAW,yBAAyB;;;;;cCrczB;;;;;;;;;;;;;cAgCA;;mBAEQ;mBACA;EAFnB,YACmB,SAAS,iBACT;;EAMb,gBAAgB,QAAQ;;EAaxB,cAAc,KAAK,mBAAmB;;EAUtC,mBAAmB;;EAOnB,WAAW,kBAAkB,QAAQ;;EAMrC,eAAe,oBAAoB,QAAQ;;;;;;;;;;;;;;;;EAoB3C,aAAa,kBAAkB,QAAQ;;EAqBvC,KAAK,cAAc,sBAAsB;;EAKzC,QAAQ,QAAQ;;EAMhB,cAAc,kBAAkB;;;iBA6DxB,mBAAmB,SAAS,sBAAsB;;;;;;;;UC/JjD;;EAEf;;EAEA;;;;;;;;;;;;iBAmBoB,eACpB,kBACA,gBACA,cACA,qBACA,iBACC,QAAQ;;;;;;;iBA+BW,YACpB,kBACA,sBACC,QAAQ;;;;;;;;;;;;;;iBAyBW,iBACpB,kBACA,iBACA,sBACC;;;;;;;;iBA8EmB,sBACpB,kBACA,uBACA,iBACA,mBAAmB,kBACnB,sBACC;;;;;;;;iBAcmB,uBACpB,kBACA,uBACA,mBAAmB,kBACnB,sBACC;;UAOc;;EAEf;;EAEA;;EAEA;;;;;EAKA;;;;;;;;;iBAUc,eAAe,SAAS;;;;;;;;;cC3P3B,2BAA2B;;;;;;;;;;iBAaxB,kBAAkB,cAAc,oCAAoC;;;;;;;iBAyBpE,aAAa,mBAAmB;;;;iBC3BhC,YAAY;;iBAKZ,QAAQ,mBAAmB;;iBAU3B,QAAQ,eAAe"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/discord-provider.ts","../src/install-store.ts","../src/discord-client.ts","../src/commands.ts","../src/crypto.ts"],"mappings":";;;;;;;;;;;;cAgBa;;cAGA;;;;;;;;cASA;WACX;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;;;;;;;cAUW;;cAWA;;;;;;UAOI;;EAEf;;EAEA;;;KAIU;EAAiC;EAAc;;;;;;;;UAQ1C;;EAEf;;EAEA;;EAEA;;;;;;;;;;UAWe;;;;;;;;;EASf,MAAM,QAAQ;;;;;EAKd;;;;;;EAMA,UAAU;;;;;EAKV;;;;;;;EAOA;;;;;EAKA;;;;;;EAMA,WAAW;;;;;;;;;;EAUX;;;;;EAKA,YAAY;;;;;;;;EAQZ;;;;;EAKA;;;;;;EAMA,mBAAmB;;EAEnB,SAAS;;;;;;;;EAQT,YAAY;;;;;;EAMZ;;EASA,WAAW;;EAEX,cAAc;;EAEd,cAAc;;EAEd,QAAQ;;EAER,gBAAgB;;EAEhB,cAAc;;EAEd,oBAAoB;;EAEpB,mBAAmB;;EAEnB,OAAO;;EAEP,cAAc;;;;;;;;EAQd,cAAc;;;;;;;EAOd,QAAQ;;EAER,aAAa,cAAc,+BAA+B;;;UAI3C;;;;;;;EAOf;;EAEA;;;;;EAKA,WAAW;;EAEX;;;;;;;;;UAUe;;EAEf;;EAEA;;;;;EAKA;;;;;EAKA;;EAEA;;EAEA;;EAEA,WAAW;;;;;;EAMX,kBAAkB;;EAElB,aAAa;;;;;;;;;;;iBC/PC,4BACd,QAAQ,KAAK;EAEb,WAAW;EACX;EACA,aAAa;EACb;;;;;;;;;;;;;;;;;;;;;;;;;;;cAmCW,2BAA2B;;WAC7B;EAoCT,YAAY,SAAQ;;;;;EAYpB,SAAS,QAAQ;;;;;;;;;EAwCjB,aAAa;;EAoBb,WAAW;;;;;;EAgCL,cAAc;;;;;;;;;EAiCd,UAAU,aAAa,QAAQ,2BAA2B;;;;;;;;;;;;;;EAmE1D,QAAQ,iBAAiB,UAAS,wBAA6B,QAAQ;;;;;;;EA6EvE,cAAc,mBAAmB,kBAAkB,QAAQ;;;;;;;;;;EAmD3D,WAAW,kBAAkB;;EAY7B,qBAAqB,QAAQ;;EAO7B,gBAAgB,kBAAkB,QAAQ;;EAMhD;;EAKA,WAAW,yBAAyB;;;;;cCvdzB;;;;;;;;;;;;;cAgCA;;mBAEQ;mBACA;EAFnB,YACmB,SAAS,iBACT;;EAMb,gBAAgB,QAAQ;;EAaxB,cAAc,KAAK,mBAAmB;;EAUtC,mBAAmB;;EAOnB,WAAW,kBAAkB,QAAQ;;EAMrC,eAAe,oBAAoB,QAAQ;;;;;;;;;;;;;;;;EAoB3C,aAAa,kBAAkB,QAAQ;;EAqBvC,KAAK,cAAc,sBAAsB;;EAKzC,QAAQ,QAAQ;;EAMhB,cAAc,kBAAkB;;;iBA6DxB,mBAAmB,SAAS,sBAAsB;;;;;;;;UC/JjD;;EAEf;;EAEA;;;;;;EAMA;;;;;;;;;;;;iBAmBoB,eACpB,kBACA,gBACA,cACA,qBACA,iBACC,QAAQ;;;;;;;iBA+BW,YACpB,kBACA,sBACC,QAAQ;;;;;;;;;;;;;;iBAyBW,iBACpB,kBACA,iBACA,sBACC;;;;;;;;iBA8EmB,sBACpB,kBACA,uBACA,iBACA,mBAAmB,kBACnB,sBACC;;;;;;;;iBAcmB,uBACpB,kBACA,uBACA,mBAAmB,kBACnB,sBACC;;UAOc;;EAEf;;EAEA;;EAEA;;;;;EAKA;;;;;;;;;iBAUc,eAAe,SAAS;;;;;;;;;cCjQ3B,2BAA2B;;;;;;;;;;iBAaxB,kBAAkB,cAAc,oCAAoC;;;;;;;iBAyBpE,aAAa,mBAAmB;;;;iBC3BhC,YAAY;;iBAKZ,QAAQ,mBAAmB;;iBAU3B,QAAQ,eAAe"}
package/dist/index.js CHANGED
@@ -548,7 +548,7 @@ var DiscordProvider = class {
548
548
  #storeGeneration = 0;
549
549
  constructor(config = {}) {
550
550
  this.#config = config;
551
- this.#configured = this.#suppliedAppConfig() != null;
551
+ this.#configured = this.#hasSuppliedCredentials();
552
552
  }
553
553
  /**
554
554
  * Called by Mastra when this channel is registered.
@@ -565,7 +565,7 @@ var DiscordProvider = class {
565
565
  }
566
566
  if (isNewInstance) {
567
567
  this.#adapters.clear();
568
- this.#configured = this.#suppliedAppConfig() != null;
568
+ this.#configured = this.#hasSuppliedCredentials();
569
569
  }
570
570
  this.#mastra = mastra;
571
571
  }
@@ -637,7 +637,7 @@ var DiscordProvider = class {
637
637
  async #doInitialize() {
638
638
  const store = await this.#getStore();
639
639
  const active = (await store.list()).filter((i) => i.status === "active");
640
- const hasApp = await store.getAppConfig() != null || this.#suppliedAppConfig() != null;
640
+ const hasApp = await store.getAppConfig() != null || this.#hasSuppliedCredentials();
641
641
  this.#configured = hasApp || active.length > 0;
642
642
  for (const installation of active) try {
643
643
  await this.#activateInstallation(installation);
@@ -648,7 +648,10 @@ var DiscordProvider = class {
648
648
  /**
649
649
  * Provide or clear the app credentials at runtime. An object merges/overrides
650
650
  * `botToken` / `publicKey` / `applicationId` (persisted on the next `connect`);
651
- * `null` clears the stored app config.
651
+ * `null` clears the stored app config. Supplying a **different bot token**
652
+ * replaces the app config instead of merging: the token identifies the
653
+ * Discord application, so any `publicKey`/`applicationId` from the previous
654
+ * application must not survive the switch.
652
655
  */
653
656
  async configure(credentials) {
654
657
  if (credentials === null) {
@@ -670,7 +673,7 @@ var DiscordProvider = class {
670
673
  ...credentials
671
674
  }
672
675
  };
673
- this.#configured = this.#suppliedAppConfig() != null || this.#configured;
676
+ this.#configured = this.#hasSuppliedCredentials() || this.#configured;
674
677
  if (![
675
678
  "botToken",
676
679
  "publicKey",
@@ -678,7 +681,14 @@ var DiscordProvider = class {
678
681
  ].some((k) => credentials[k] !== void 0 && credentials[k] !== previous?.[k])) return;
679
682
  const store = await this.#getStore();
680
683
  const stored = await store.getAppConfig();
681
- if (stored) {
684
+ const tokenSupersedes = (base) => credentials.botToken !== void 0 && base?.botToken !== void 0 && base.botToken !== credentials.botToken;
685
+ if (tokenSupersedes(previous) || tokenSupersedes(stored)) {
686
+ this.#config = {
687
+ ...this.#config,
688
+ app: { ...credentials }
689
+ };
690
+ await store.deleteAppConfig();
691
+ } else if (stored) {
682
692
  const merged = {
683
693
  ...stored,
684
694
  ...credentials
@@ -707,8 +717,8 @@ var DiscordProvider = class {
707
717
  const store = await this.#getStore();
708
718
  const existing = await store.getByAgent(agentId);
709
719
  if (existing?.status === "active") throw new Error(`Agent "${agentId}" is already connected to Discord. Disconnect first to reconnect. To add another server, invite the bot with the existing URL — new guilds activate on first use.`);
710
- const { app, persisted } = await this.#resolveAppConfig(store);
711
- const identity = await validateApp(app.botToken, this.#apiBaseUrl());
720
+ const { app, persisted, identity: resolvedIdentity } = await this.#resolveAppConfig(store);
721
+ const identity = resolvedIdentity ?? await validateApp(app.botToken, this.#apiBaseUrl());
712
722
  if (!persisted) await store.saveAppConfig(app);
713
723
  const installationId = existing?.id ?? randomUUID();
714
724
  const webhookId = existing?.webhookId ?? randomUUID();
@@ -975,12 +985,33 @@ var DiscordProvider = class {
975
985
  return;
976
986
  }
977
987
  }
988
+ /**
989
+ * App credentials from provider config or `DISCORD_*` env.
990
+ *
991
+ * A config-supplied bot token defines the application identity, so
992
+ * `publicKey` / `applicationId` never fall back to the environment in that
993
+ * case — stale `DISCORD_PUBLIC_KEY` / `DISCORD_APPLICATION_ID` from a
994
+ * *different* application would otherwise complete the config and keep that
995
+ * application's Ed25519 key trusted for webhook verification (bypassing the
996
+ * `GET /applications/@me` backfill). Env fallback for those fields applies
997
+ * only when the bot token itself comes from the environment.
998
+ */
999
+ #suppliedPartialAppConfig() {
1000
+ const a = this.#config.app ?? {};
1001
+ if (a.botToken != null) return {
1002
+ botToken: a.botToken,
1003
+ publicKey: a.publicKey,
1004
+ applicationId: a.applicationId
1005
+ };
1006
+ return {
1007
+ botToken: process.env.DISCORD_BOT_TOKEN,
1008
+ publicKey: a.publicKey ?? process.env.DISCORD_PUBLIC_KEY,
1009
+ applicationId: a.applicationId ?? process.env.DISCORD_APPLICATION_ID
1010
+ };
1011
+ }
978
1012
  /** App credentials from provider config or `DISCORD_*` env, or `null` if incomplete. */
979
1013
  #suppliedAppConfig() {
980
- const a = this.#config.app ?? {};
981
- const botToken = a.botToken ?? process.env.DISCORD_BOT_TOKEN;
982
- const publicKey = a.publicKey ?? process.env.DISCORD_PUBLIC_KEY;
983
- const applicationId = a.applicationId ?? process.env.DISCORD_APPLICATION_ID;
1014
+ const { botToken, publicKey, applicationId } = this.#suppliedPartialAppConfig();
984
1015
  if (botToken && publicKey && applicationId) return {
985
1016
  botToken,
986
1017
  publicKey,
@@ -989,10 +1020,22 @@ var DiscordProvider = class {
989
1020
  return null;
990
1021
  }
991
1022
  /**
1023
+ * Whether credentials are resolvable without storage. A bot token alone is
1024
+ * enough: `applicationId` and `publicKey` are backfilled from
1025
+ * `GET /applications/@me` by {@link #resolveAppConfig}.
1026
+ */
1027
+ #hasSuppliedCredentials() {
1028
+ return this.#suppliedPartialAppConfig().botToken != null;
1029
+ }
1030
+ /**
992
1031
  * Resolve the app config: prefer the stored one, else the supplied credentials
993
- * (config / `DISCORD_*` env). `persisted` says whether it's already in storage,
994
- * so the caller can persist supplied credentials **once**, after validation.
995
- * Throws if neither is available.
1032
+ * (config / `DISCORD_*` env). A supplied `botToken` without `publicKey` /
1033
+ * `applicationId` is completed from `GET /applications/@me` — the application
1034
+ * object carries both the id and the Ed25519 `verify_key`. `persisted` says
1035
+ * whether it's already in storage, so the caller can persist supplied
1036
+ * credentials **once**, after validation. `identity` is set when the token was
1037
+ * already validated against `/applications/@me` here, so callers can skip a
1038
+ * second validation call. Throws if no credentials are available.
996
1039
  */
997
1040
  async #resolveAppConfig(store) {
998
1041
  const stored = await store.getAppConfig();
@@ -1005,7 +1048,22 @@ var DiscordProvider = class {
1005
1048
  app: supplied,
1006
1049
  persisted: false
1007
1050
  };
1008
- throw new Error("Discord app is not configured. Provide botToken, publicKey, and applicationId via the provider `app` option, the DISCORD_BOT_TOKEN / DISCORD_PUBLIC_KEY / DISCORD_APPLICATION_ID env vars, or configure() — Discord applications are created in the Developer Portal, not programmatically.");
1051
+ const partial = this.#suppliedPartialAppConfig();
1052
+ if (partial.botToken) {
1053
+ const identity = await validateApp(partial.botToken, this.#apiBaseUrl());
1054
+ const publicKey = partial.publicKey ?? identity.verify_key;
1055
+ if (!publicKey) throw new Error("Discord /applications/@me returned no verify_key; provide publicKey explicitly.");
1056
+ return {
1057
+ app: {
1058
+ botToken: partial.botToken,
1059
+ publicKey,
1060
+ applicationId: partial.applicationId ?? identity.id
1061
+ },
1062
+ persisted: false,
1063
+ identity
1064
+ };
1065
+ }
1066
+ throw new Error("Discord app is not configured. Provide at least a botToken via the provider `app` option, the DISCORD_BOT_TOKEN env var, or configure() — publicKey and applicationId are resolved from Discord automatically when omitted. Discord applications are created in the Developer Portal, not programmatically.");
1009
1067
  }
1010
1068
  /**
1011
1069
  * The single entry point to the store. Every caller either gets the cached