@mastra/connect 0.4.0-alpha.0 → 0.5.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.ts CHANGED
@@ -4,7 +4,7 @@ export { connect } from './connect.js';
4
4
  export type { ConnectOptions, ConnectIntegrationOptions, ConnectTools } from './connect.js';
5
5
  export { channels } from './channels.js';
6
6
  export type { ChannelsOptions, ChannelsIntegrationOptions, ChannelsResolver, ChannelsResolverContext, ResolvedChannels, } from './channels.js';
7
- export type { ChannelProviderRegistration, ChannelBuildContext } from './providers/channel-provider.js';
7
+ export type { ChannelProviderRegistration, ChannelInstance, ChannelRuntime } from './providers/channel-provider.js';
8
8
  export { credential } from './credential.js';
9
9
  export { environment } from './environment.js';
10
10
  export type { ConnectEnvironment, EnvironmentIntegrationOptions, EnvironmentOptions } from './environment.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,YAAY,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,cAAc,EAAE,yBAAyB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EACV,eAAe,EACf,0BAA0B,EAC1B,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACxG,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,kBAAkB,EAAE,6BAA6B,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC9G,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjG,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACtG,YAAY,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,YAAY,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,cAAc,EAAE,yBAAyB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EACV,eAAe,EACf,0BAA0B,EAC1B,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,2BAA2B,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACpH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,kBAAkB,EAAE,6BAA6B,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC9G,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjG,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACtG,YAAY,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -346,14 +346,12 @@ async function proxyRequestWithResponse(client, connectionId, options) {
346
346
  function credentialToken$1(credential) {
347
347
  return credential.type === "oauth2" ? credential.accessToken : credential.apiKey;
348
348
  }
349
- function missingPeerError(integrationId, packageName, error) {
350
- return new MastraConnectError("invalid_options", `channels() cannot build '${integrationId}' provider: install '${packageName}' as a dependency of your app (${error instanceof Error ? error.message : String(error)}).`);
351
- }
352
349
  const RESERVED_OPTION_KEYS = {
353
350
  slack: [
354
351
  "baseUrl",
355
352
  "refreshToken",
356
353
  "token",
354
+ "tokenResolver",
357
355
  "encryptionKey"
358
356
  ],
359
357
  telegram: [
@@ -383,61 +381,53 @@ function stripReservedOptions(integrationId, options) {
383
381
  const CHANNELS$1 = [
384
382
  {
385
383
  integrationId: "slack",
386
- async build(credential, options) {
387
- const refreshToken = credentialToken$1(credential);
388
- let mod;
389
- try {
390
- mod = await import("@mastra/slack");
391
- } catch (error) {
392
- throw missingPeerError("slack", "@mastra/slack", error);
393
- }
384
+ async create(options, runtime) {
385
+ const mod = await import("@mastra/slack");
394
386
  const safeOptions = stripReservedOptions("slack", options);
395
- return new mod.SlackProvider({
396
- refreshToken,
387
+ const tokenResolver = async () => {
388
+ return credentialToken$1(await runtime.getCredential());
389
+ };
390
+ return { provider: new mod.SlackProvider({
391
+ tokenResolver,
397
392
  ...safeOptions ?? {}
398
- });
393
+ }) };
399
394
  }
400
395
  },
401
396
  {
402
397
  integrationId: "telegram",
403
- async build(credential, options) {
404
- const botToken = credentialToken$1(credential);
405
- let mod;
406
- try {
407
- mod = await import("@mastra/telegram");
408
- } catch (error) {
409
- throw missingPeerError("telegram", "@mastra/telegram", error);
410
- }
398
+ async create(options, runtime) {
399
+ const mod = await import("@mastra/telegram");
411
400
  const safeOptions = stripReservedOptions("telegram", options);
412
- return new mod.TelegramProvider({
413
- botToken,
414
- ...safeOptions ?? {}
415
- });
401
+ const provider = new mod.TelegramProvider({ ...safeOptions ?? {} });
402
+ return {
403
+ provider,
404
+ async sync() {
405
+ const botToken = credentialToken$1(await runtime.getCredential());
406
+ await provider.configure?.({ botToken });
407
+ }
408
+ };
416
409
  }
417
410
  },
418
411
  {
419
412
  integrationId: "discord",
420
- async build(credential, options, context) {
421
- const botToken = credentialToken$1(credential);
422
- const metadata = context?.context?.metadata ?? {};
423
- const applicationId = options?.applicationId ?? (typeof metadata.applicationId === "string" ? metadata.applicationId : void 0) ?? (typeof metadata.application_id === "string" ? metadata.application_id : void 0);
424
- const publicKey = options?.publicKey ?? (typeof metadata.publicKey === "string" ? metadata.publicKey : void 0) ?? (typeof metadata.public_key === "string" ? metadata.public_key : void 0);
425
- let mod;
426
- try {
427
- mod = await import("@mastra/discord");
428
- } catch (error) {
429
- throw missingPeerError("discord", "@mastra/discord", error);
430
- }
431
- if (!applicationId || !publicKey) console.warn(`[@mastra/connect] discord channel: missing ${[!applicationId && "applicationId", !publicKey && "publicKey"].filter(Boolean).join(" + ")} on the connection. Store them on the connection's metadata or pass them via integrations.discord.providerOptions.`);
432
- const { applicationId: _optAppId, publicKey: _optPubKey, ...rest } = options ?? {};
433
- return new mod.DiscordProvider({
434
- app: {
435
- botToken,
436
- applicationId,
437
- publicKey
438
- },
439
- ...rest
440
- });
413
+ async create(options, runtime) {
414
+ const mod = await import("@mastra/discord");
415
+ const { applicationId: optionsAppId, publicKey: optionsPublicKey, ...rest } = options ?? {};
416
+ const safeOptions = stripReservedOptions("discord", rest);
417
+ const provider = new mod.DiscordProvider({ ...safeOptions ?? {} });
418
+ return {
419
+ provider,
420
+ async sync() {
421
+ const botToken = credentialToken$1(await runtime.getCredential());
422
+ const metadata = (await runtime.getConnectionContext())?.metadata ?? {};
423
+ const applicationId = optionsAppId ?? (typeof metadata.applicationId === "string" ? metadata.applicationId : void 0) ?? (typeof metadata.application_id === "string" ? metadata.application_id : void 0);
424
+ const publicKey = optionsPublicKey ?? (typeof metadata.publicKey === "string" ? metadata.publicKey : void 0) ?? (typeof metadata.public_key === "string" ? metadata.public_key : void 0);
425
+ const credentials = { botToken };
426
+ if (applicationId) credentials.applicationId = applicationId;
427
+ if (publicKey) credentials.publicKey = publicKey;
428
+ await provider.configure?.(credentials);
429
+ }
430
+ };
441
431
  }
442
432
  }
443
433
  ];
@@ -66153,16 +66143,22 @@ const DEFAULT_TTL_MS = 3e4;
66153
66143
  /** Minimum wait after a failed platform fetch before another background revalidation. */
66154
66144
  const FAILURE_COOLDOWN_MS = 3e4;
66155
66145
  /**
66156
- * Returns a live channels resolver over the project's Platform connections.
66157
- * Channel-capable integrations (Slack, Telegram, Discord) with an active
66158
- * project connection are materialized into `ChannelProvider` instances ready
66159
- * to hand to `new Mastra({ channels: await channels({...}) })`.
66146
+ * Returns a live channels resolver over the project's Platform connections,
66147
+ * ready to hand to `new Mastra({ channels: await channels({...}) })`.
66148
+ *
66149
+ * Provider instances for every channel-capable integration (Slack, Telegram,
66150
+ * Discord) are constructed once, up front and credential-less, so their
66151
+ * webhook/OAuth routes can mount at Mastra construction. Each resolution
66152
+ * fetches the project's connections and late-binds credentials into the live
66153
+ * instances: integrations with an active connection appear in the resolved
66154
+ * map, others don't — so connecting a new channel on the platform takes
66155
+ * effect without redeploying the app.
66160
66156
  *
66161
66157
  * Configuration errors (missing project id, bad ttlMs, malformed integration
66162
- * id) throw at call time so they surface at startup. Actionable per-integration
66163
- * problems during resolution (needs re-auth, ambiguity, missing peer package)
66164
- * are downgraded to warn-and-skip so one bad integration never takes down the
66165
- * whole map.
66158
+ * id) reject at call time so they surface at startup. Actionable
66159
+ * per-integration problems (provider construction failure, needs re-auth,
66160
+ * ambiguity, credential sync failure) are downgraded to warn-and-skip so one
66161
+ * bad integration never takes down the whole map.
66166
66162
  *
66167
66163
  * @example
66168
66164
  * ```ts
@@ -66175,13 +66171,48 @@ const FAILURE_COOLDOWN_MS = 3e4;
66175
66171
  * });
66176
66172
  * ```
66177
66173
  */
66178
- function channels(options = {}) {
66174
+ async function channels(options = {}) {
66179
66175
  const projectId = options.projectId?.trim() || process.env.MASTRA_PROJECT_ID?.trim();
66180
66176
  if (!projectId) throw new MastraConnectError("missing_project_id", "Missing project id: set MASTRA_PROJECT_ID or pass projectId.");
66181
66177
  if (options.ttlMs !== void 0 && (!Number.isFinite(options.ttlMs) || options.ttlMs < 0)) throw new MastraConnectError("invalid_options", `Invalid ttlMs (${options.ttlMs}): expected a finite number of milliseconds >= 0.`);
66182
66178
  validateIntegrationOverrides(options.integrations);
66183
66179
  const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
66184
66180
  const client = resolveClient(options.client);
66181
+ const states = [];
66182
+ for (const registration of CHANNELS) {
66183
+ const overrides = options.integrations?.[registration.integrationId] ?? {};
66184
+ if (overrides.disabled) continue;
66185
+ const state = {
66186
+ registration,
66187
+ instance: void 0,
66188
+ connectionId: void 0
66189
+ };
66190
+ const runtime = {
66191
+ client,
66192
+ getConnectionId: () => state.connectionId,
66193
+ getCredential: async () => {
66194
+ const connectionId = state.connectionId;
66195
+ if (!connectionId) throw new MastraConnectError("no_active_connection", `No active ${registration.integrationId} connection for project ${projectId}. Connect one on the platform, then retry.`);
66196
+ return getCredential(client, connectionId);
66197
+ },
66198
+ getConnectionContext: async () => {
66199
+ const connectionId = state.connectionId;
66200
+ if (!connectionId) return void 0;
66201
+ try {
66202
+ return await getConnectionContext(client, connectionId);
66203
+ } catch {
66204
+ return;
66205
+ }
66206
+ }
66207
+ };
66208
+ try {
66209
+ state.instance = await registration.create(overrides.providerOptions ?? {}, runtime);
66210
+ } catch (error) {
66211
+ console.warn(`[@mastra/connect] Skipping ${registration.integrationId} channel: ${error instanceof Error ? error.message : String(error)}`);
66212
+ continue;
66213
+ }
66214
+ states.push(state);
66215
+ }
66185
66216
  let cache;
66186
66217
  let inflight;
66187
66218
  let lastFailureAt;
@@ -66195,17 +66226,20 @@ function channels(options = {}) {
66195
66226
  }
66196
66227
  const byIntegrationId = groupByIntegrationId(connections);
66197
66228
  const providers = {};
66198
- for (const registration of CHANNELS) {
66199
- const integrationId = registration.integrationId;
66229
+ for (const state of states) {
66230
+ const integrationId = state.registration.integrationId;
66200
66231
  const overrides = options.integrations?.[integrationId] ?? {};
66201
- if (overrides.disabled) continue;
66202
66232
  const candidates = byIntegrationId.get(integrationId) ?? [];
66203
- if (candidates.length === 0) continue;
66204
- const connectionId = selectChannelConnection(integrationId, overrides.connectionId, candidates);
66233
+ const connectionId = candidates.length === 0 ? void 0 : selectChannelConnection(integrationId, overrides.connectionId, candidates);
66234
+ state.connectionId = connectionId;
66205
66235
  if (!connectionId) continue;
66206
- const provider = await buildProvider(registration, connectionId, overrides, client);
66207
- if (!provider) continue;
66208
- providers[integrationId] = provider;
66236
+ if (state.instance.sync) try {
66237
+ await state.instance.sync();
66238
+ } catch (error) {
66239
+ console.warn(`[@mastra/connect] Skipping ${integrationId} channel: ${error instanceof Error ? error.message : String(error)}`);
66240
+ continue;
66241
+ }
66242
+ providers[integrationId] = state.instance.provider;
66209
66243
  }
66210
66244
  lastFailureAt = void 0;
66211
66245
  return providers;
@@ -66240,6 +66274,7 @@ function channels(options = {}) {
66240
66274
  return refresh();
66241
66275
  };
66242
66276
  const invocable = ((_context) => resolve());
66277
+ invocable.getRoutes = () => states.flatMap((state) => state.instance.provider.getRoutes());
66243
66278
  invocable.invalidate = () => {
66244
66279
  cache = void 0;
66245
66280
  };
@@ -66247,35 +66282,8 @@ function channels(options = {}) {
66247
66282
  invocable.disconnect = async () => {
66248
66283
  cache = void 0;
66249
66284
  };
66250
- invocable.then = ((onfulfilled, onrejected) => resolve().then(onfulfilled, onrejected));
66251
66285
  return invocable;
66252
66286
  }
66253
- async function buildProvider(registration, connectionId, overrides, client) {
66254
- let credential;
66255
- try {
66256
- credential = await getCredential(client, connectionId);
66257
- } catch (error) {
66258
- console.warn(`[@mastra/connect] Skipping ${registration.integrationId} channel: ${error instanceof Error ? error.message : String(error)}`);
66259
- return;
66260
- }
66261
- let context = {
66262
- connectionId,
66263
- client
66264
- };
66265
- try {
66266
- context = {
66267
- connectionId,
66268
- client,
66269
- context: await getConnectionContext(client, connectionId)
66270
- };
66271
- } catch {}
66272
- try {
66273
- return await registration.build(credential, overrides.providerOptions ?? {}, context);
66274
- } catch (error) {
66275
- console.warn(`[@mastra/connect] Skipping ${registration.integrationId} channel: ${error instanceof Error ? error.message : String(error)}`);
66276
- return;
66277
- }
66278
- }
66279
66287
  /**
66280
66288
  * Selects the connection `channels()` should use for one provider.
66281
66289
  *