create-mastra 1.37.0-alpha.2 → 1.37.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.
@@ -1,4 +1,4 @@
1
- import { a as throwApiError, r as createApiClient } from "./client-JZw8MfpM-zeD5gOhK.js";
1
+ import { i as createApiClient, s as throwApiError } from "./client-JZw8MfpM-BPeyQaUL.js";
2
2
  // ../cli/dist/api-CV5-CXEm.js
3
3
  async function fetchOrgs(token) {
4
4
  const { data, error, response } = await createApiClient(token).GET("/v1/auth/orgs");
@@ -0,0 +1 @@
1
+ export { t as fetchOrgs } from "./api-CV5-CXEm-CueiBLg3.js";
@@ -388,7 +388,7 @@ function removeTrailingSlash(url) {
388
388
  }
389
389
  // ../cli/dist/client-JZw8MfpM.js
390
390
  const MASTRA_PLATFORM_API_URL = process.env.MASTRA_PLATFORM_API_URL || "https://platform.mastra.ai";
391
- process.env.MASTRA_PROJECTS_URL;
391
+ const MASTRA_PROJECTS_URL = process.env.MASTRA_PROJECTS_URL || "https://projects.mastra.ai";
392
392
  /**
393
393
  * Derive the gateway URL from the platform URL when not explicitly set.
394
394
  * If the platform points at staging, the gateway defaults to staging too.
@@ -420,6 +420,22 @@ function throwApiError(message, status, detail) {
420
420
  if (detail) throw new Error(detail);
421
421
  throw new Error(`${message}: ${status}`);
422
422
  }
423
+ /** Best-effort message from platform JSON error bodies (RFC 7807 `detail`, etc.). */
424
+ function extractApiErrorDetail(error) {
425
+ if (!error || typeof error !== "object") return void 0;
426
+ const o = error;
427
+ let detail;
428
+ if (typeof o.detail === "string" && o.detail.trim()) detail = o.detail;
429
+ else if (typeof o.message === "string" && o.message.trim()) detail = o.message;
430
+ else if (typeof o.error === "string" && o.error.trim()) detail = o.error;
431
+ const fieldErrors = Array.isArray(o.errors) ? o.errors.map((e) => {
432
+ if (!e || typeof e !== "object") return "";
433
+ const fe = e;
434
+ return [typeof fe.field === "string" ? fe.field : "", typeof fe.message === "string" ? fe.message : ""].filter(Boolean).join(" — ");
435
+ }).filter(Boolean).join("; ") : "";
436
+ if (detail && fieldErrors) return `${detail}: ${fieldErrors}`;
437
+ return detail ?? (fieldErrors || void 0);
438
+ }
423
439
  let _currentToken = null;
424
440
  let _refreshInFlight = null;
425
441
  /**
@@ -444,7 +460,7 @@ async function authenticatedFetch(input, init) {
444
460
  if (response.status !== 401 || !_currentToken) return response;
445
461
  if (!_refreshInFlight) _refreshInFlight = (async () => {
446
462
  try {
447
- const { tryRefreshToken, loadCredentials } = await import("./credentials-CdkRA-3T--5O17XQq.js");
463
+ const { tryRefreshToken, loadCredentials } = await import("./credentials-CdkRA-3T-Cagpr0K5.js");
448
464
  const creds = await loadCredentials();
449
465
  if (!creds) throw new Error("No credentials");
450
466
  const newToken = await tryRefreshToken(creds);
@@ -502,4 +518,4 @@ function authHeaders(token, orgId) {
502
518
  function platformFetch(input, init) {
503
519
  return authenticatedFetch(input, init);
504
520
  }
505
- export { throwApiError as a, platformFetch as i, authHeaders as n, createApiClient as r, MASTRA_PLATFORM_API_URL as t };
521
+ export { extractApiErrorDetail as a, createApiClient as i, MASTRA_PROJECTS_URL as n, platformFetch as o, authHeaders as r, throwApiError as s, MASTRA_PLATFORM_API_URL as t };
@@ -0,0 +1 @@
1
+ export { i as loadCredentials, s as tryRefreshToken } from "./credentials-HeXP-kY2-89ktsX3S.js";
@@ -1,4 +1,4 @@
1
- import { t as MASTRA_PLATFORM_API_URL } from "./client-JZw8MfpM-zeD5gOhK.js";
1
+ import { t as MASTRA_PLATFORM_API_URL } from "./client-JZw8MfpM-BPeyQaUL.js";
2
2
  import { createRequire } from "node:module";
3
3
  import "node:path";
4
4
  import process$1, { stdin, stdout } from "node:process";
@@ -1448,4 +1448,4 @@ async function getToken(signal, options = {}) {
1448
1448
  if (options.allowLogin === false || !isInteractive()) throw new Error("Session expired. Run `mastra auth login` interactively or set MASTRA_API_TOKEN.");
1449
1449
  return (await login(signal, options)).token;
1450
1450
  }
1451
- export { __require as _, setCurrentOrgId as a, log as c, password as d, select as f, __commonJSMin as g, isCancel as h, loadCredentials as i, note as l, text as m, getCurrentOrgId as n, tryRefreshToken as o, spinner as p, getToken as r, cancel as s, LoginCancelledError as t, outro as u, __toESM as v };
1451
+ export { __commonJSMin as _, openBrowser as a, cancel as c, outro as d, password as f, isCancel as g, text as h, loadCredentials as i, log as l, spinner as m, getCurrentOrgId as n, setCurrentOrgId as o, select as p, getToken as r, tryRefreshToken as s, LoginCancelledError as t, note as u, __require as v, __toESM as y };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #! /usr/bin/env node
2
- import { _ as __require, a as setCurrentOrgId, c as log, d as password, f as select, g as __commonJSMin, h as isCancel, l as note, m as text, n as getCurrentOrgId, p as spinner, r as getToken, s as cancel, t as LoginCancelledError, u as outro, v as __toESM } from "./credentials-HeXP-kY2-jky92lr5.js";
3
- import { i as platformFetch, n as authHeaders, t as MASTRA_PLATFORM_API_URL } from "./client-JZw8MfpM-zeD5gOhK.js";
4
- import { t as fetchOrgs } from "./api-CV5-CXEm-Cpd49bi2.js";
2
+ import { _ as __commonJSMin, a as openBrowser, c as cancel, d as outro, f as password, g as isCancel, h as text, l as log, m as spinner, n as getCurrentOrgId, o as setCurrentOrgId, p as select, r as getToken, t as LoginCancelledError, u as note, v as __require, y as __toESM } from "./credentials-HeXP-kY2-89ktsX3S.js";
3
+ import { a as extractApiErrorDetail, n as MASTRA_PROJECTS_URL, o as platformFetch, r as authHeaders, t as MASTRA_PLATFORM_API_URL } from "./client-JZw8MfpM-BPeyQaUL.js";
4
+ import { t as fetchOrgs } from "./api-CV5-CXEm-CueiBLg3.js";
5
5
  import { EventEmitter, addAbortListener, on, once, setMaxListeners } from "node:events";
6
6
  import { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
7
7
  import path, { basename, delimiter, dirname, isAbsolute, normalize, resolve } from "node:path";
@@ -17628,8 +17628,13 @@ var require_yocto_spinner = /* @__PURE__ */ __commonJSMin(((exports, module) =>
17628
17628
  return new YoctoSpinner(options);
17629
17629
  };
17630
17630
  }));
17631
- // ../cli/dist/analytics-BN1VDbPp.js
17631
+ // ../cli/dist/analytics-BJXFfGkK.js
17632
17632
  const CREATE_LLM_PROVIDERS = [
17633
+ {
17634
+ value: "mastra",
17635
+ label: "Mastra Gateway",
17636
+ hint: "every model with one key, sign in to get it"
17637
+ },
17633
17638
  {
17634
17639
  value: "openai",
17635
17640
  label: "OpenAI"
@@ -19354,7 +19359,7 @@ var require_semver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
19354
19359
  rcompareIdentifiers: identifiers.rcompareIdentifiers
19355
19360
  };
19356
19361
  }));
19357
- // ../cli/dist/create-BGnzr-Pz.js
19362
+ // ../cli/dist/create-CLVyvlKU.js
19358
19363
  var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
19359
19364
  var import_yocto_spinner = /* @__PURE__ */ __toESM(require_yocto_spinner(), 1);
19360
19365
  var import_semver = /* @__PURE__ */ __toESM(require_semver(), 1);
@@ -19512,6 +19517,117 @@ function findTemplateByName(templates, templateName) {
19512
19517
  if (template) return template;
19513
19518
  return null;
19514
19519
  }
19520
+ const GATEWAY_API_KEY_ENV = "MASTRA_GATEWAY_API_KEY";
19521
+ const STARTER_CREDIT_POOL = "gateway_credits";
19522
+ var GatewayApiError = class extends Error {
19523
+ status;
19524
+ constructor(message, status) {
19525
+ super(message);
19526
+ this.status = status;
19527
+ this.name = "GatewayApiError";
19528
+ }
19529
+ };
19530
+ async function request(token, orgId, path, init = {}, signal) {
19531
+ const res = await platformFetch(`${MASTRA_PLATFORM_API_URL}${path}`, {
19532
+ ...init,
19533
+ signal,
19534
+ headers: {
19535
+ ...authHeaders(token, orgId),
19536
+ ...init.body ? { "Content-Type": "application/json" } : {}
19537
+ }
19538
+ });
19539
+ if (!res.ok) {
19540
+ const body = await res.json().catch(() => void 0);
19541
+ throw new GatewayApiError(extractApiErrorDetail(body) ?? `Request failed (${res.status})`, res.status);
19542
+ }
19543
+ return await res.json();
19544
+ }
19545
+ /**
19546
+ * Create a Gateway API key in the organization. Uses the organization key service and falls back
19547
+ * to provisioning a Gateway project with a key only where that service doesn't exist (404). A 503
19548
+ * means the service failed, so it surfaces instead of leaving a stray project behind.
19549
+ */
19550
+ async function createGatewayApiKey(token, orgId, label, signal) {
19551
+ try {
19552
+ const result = await request(token, orgId, "/v1/gateway/organization/v2/keys", {
19553
+ method: "POST",
19554
+ body: JSON.stringify({ label })
19555
+ }, signal);
19556
+ return {
19557
+ apiKey: result.apiKey,
19558
+ keyId: result.keyId
19559
+ };
19560
+ } catch (error) {
19561
+ if (!(error instanceof GatewayApiError) || error.status !== 404) throw error;
19562
+ }
19563
+ return { apiKey: (await request(token, orgId, "/v1/gateway/projects/provision", {
19564
+ method: "POST",
19565
+ body: JSON.stringify({
19566
+ name: label,
19567
+ keyLabel: label
19568
+ })
19569
+ }, signal)).apiKey };
19570
+ }
19571
+ /** Revoke an organization key, e.g. one created for a project that was never written. */
19572
+ async function revokeGatewayApiKey(token, orgId, keyId) {
19573
+ await request(token, orgId, `/v1/gateway/organization/v2/keys/${encodeURIComponent(keyId)}`, { method: "DELETE" });
19574
+ }
19575
+ async function getGatewayCredits(token, orgId, signal) {
19576
+ const { customer } = await request(token, orgId, "/v1/billing/customer", {}, signal);
19577
+ const featureId = customer?.creditPool?.featureId ?? STARTER_CREDIT_POOL;
19578
+ const balance = findBalance(customer?.balances, featureId);
19579
+ return {
19580
+ remaining: remainingCredits(balance),
19581
+ unlimited: balance?.unlimited === true
19582
+ };
19583
+ }
19584
+ function findBalance(balances, featureId) {
19585
+ if (!balances) return void 0;
19586
+ return Object.values(balances).find((entry) => entry?.featureId === featureId) ?? balances[featureId];
19587
+ }
19588
+ function remainingCredits(balance) {
19589
+ if (balance?.remaining != null) return Math.max(0, balance.remaining);
19590
+ if (balance?.granted != null) return Math.max(0, balance.granted - (balance.usage ?? 0));
19591
+ return 0;
19592
+ }
19593
+ /** The dashboard's Add credits page for the organization, where credits and auto top-up are bought. */
19594
+ function creditsPageUrl(orgId) {
19595
+ return `${MASTRA_PROJECTS_URL}/add-credits/${orgId}`;
19596
+ }
19597
+ function formatCredits(amount) {
19598
+ return `$${amount.toFixed(2)}`;
19599
+ }
19600
+ /**
19601
+ * Offer to add credits when the organization has none. Credits (and auto top-up) are bought in the
19602
+ * dashboard, which opens in the browser; setup carries on without waiting for the purchase.
19603
+ * Resolves to whether the dashboard was opened.
19604
+ */
19605
+ async function offerGatewayCredits({ orgId, credits, signal }) {
19606
+ if (credits.unlimited || credits.remaining > 0) return false;
19607
+ const choice = await select({
19608
+ message: `Your Gateway balance is ${formatCredits(credits.remaining)}. Model calls are paid per token from credits.`,
19609
+ options: [{
19610
+ value: "buy",
19611
+ label: "Add credits",
19612
+ hint: "opens the Mastra dashboard"
19613
+ }, {
19614
+ value: "skip",
19615
+ label: "Skip for now"
19616
+ }],
19617
+ initialValue: "buy",
19618
+ showInstructions: false,
19619
+ signal
19620
+ });
19621
+ if (isCancel(choice) || choice === "skip") return false;
19622
+ const url = creditsPageUrl(orgId);
19623
+ try {
19624
+ openBrowser(url);
19625
+ log.info(`Opened the Mastra dashboard. Add credits there. If it didn't open, go to:\n${import_picocolors.default.cyan(url)}`);
19626
+ } catch {
19627
+ log.info(`Add credits in the Mastra dashboard:\n${import_picocolors.default.cyan(url)}`);
19628
+ }
19629
+ return true;
19630
+ }
19515
19631
  const DEFAULT_PLATFORM_API_URL = "https://platform.mastra.ai";
19516
19632
  /**
19517
19633
  * Walk the user through enabling Mastra Observability for a freshly-scaffolded
@@ -19860,6 +19976,10 @@ async function writeEmptyScaffold({ projectPath, projectName, versionTag, packag
19860
19976
  await Promise.all(writes);
19861
19977
  }
19862
19978
  const MANAGED_PROVIDER_CONFIGS = {
19979
+ mastra: {
19980
+ displayName: "Mastra Gateway",
19981
+ apiKeyEnv: "MASTRA_GATEWAY_API_KEY"
19982
+ },
19863
19983
  openai: {
19864
19984
  displayName: "OpenAI",
19865
19985
  apiKeyEnv: "OPENAI_API_KEY"
@@ -19954,6 +20074,21 @@ function adaptAgentSource(source, provider, config) {
19954
20074
  applied: true,
19955
20075
  content: source
19956
20076
  };
20077
+ if (provider === "mastra") {
20078
+ if (findMatches(source, PRIMARY_OPENAI_MODEL).length !== 1) return {
20079
+ applied: false,
20080
+ content: source
20081
+ };
20082
+ if (findMatches(source, OBSERVATIONAL_OPENAI_MODEL).length !== 1) return {
20083
+ applied: false,
20084
+ content: source
20085
+ };
20086
+ const toGateway = (match) => match.replace(/(['"])openai\//, "$1mastra/openai/");
20087
+ return {
20088
+ applied: true,
20089
+ content: source.replace(PRIMARY_OPENAI_MODEL, toGateway).replace(OBSERVATIONAL_OPENAI_MODEL, toGateway)
20090
+ };
20091
+ }
19957
20092
  if (!config.primaryModel || !config.observationalModel) return {
19958
20093
  applied: false,
19959
20094
  content: source
@@ -20005,6 +20140,22 @@ async function writeSecureEnv(envPath, content) {
20005
20140
  return false;
20006
20141
  }
20007
20142
  }
20143
+ /**
20144
+ * Write an API key that arrived after the template was adapted (the Gateway key is created
20145
+ * while the template clones). Starts from `.env`, or `.env.example` when there is no `.env` yet.
20146
+ */
20147
+ async function writeManagedApiKey({ projectPath, apiKeyEnv, apiKey }) {
20148
+ const envPath = path$1.join(projectPath, ".env");
20149
+ let source = "";
20150
+ for (const candidate of [envPath, path$1.join(projectPath, ".env.example")]) try {
20151
+ source = await fsPromises.readFile(candidate, "utf8");
20152
+ break;
20153
+ } catch (error) {
20154
+ if (error.code !== "ENOENT") return false;
20155
+ }
20156
+ const result = setEnvValue(source, apiKeyEnv, apiKey);
20157
+ return writeSecureEnv(envPath, result.applied ? result.content : `${source}${source && !source.endsWith("\n") ? "\n" : ""}${apiKeyEnv}=${apiKey}\n`);
20158
+ }
20008
20159
  async function adaptDefaultTemplate({ projectPath, projectName, packageManager, provider, apiKey, versionTag }) {
20009
20160
  const config = MANAGED_PROVIDER_CONFIGS[provider];
20010
20161
  let adaptationFailed = false;
@@ -20232,6 +20383,7 @@ const create = async (args) => {
20232
20383
  let llmApiKey = options.llmApiKey;
20233
20384
  let providerSelectionMethod;
20234
20385
  let observabilityEnabled = false;
20386
+ let needsGatewayKey = false;
20235
20387
  let platformSetupController;
20236
20388
  let platformSetupPromise;
20237
20389
  if (mode === "managed") {
@@ -20241,7 +20393,8 @@ const create = async (args) => {
20241
20393
  llmProvider = await promptForProvider();
20242
20394
  providerSelectionMethod = "interactive";
20243
20395
  }
20244
- if (llmApiKey === void 0 && !providerProvidedByCli) llmApiKey = await promptForApiKey(llmProvider);
20396
+ if (llmApiKey === void 0 && !providerProvidedByCli && llmProvider !== "mastra") llmApiKey = await promptForApiKey(llmProvider);
20397
+ needsGatewayKey = llmProvider === "mastra" && llmApiKey === void 0;
20245
20398
  if (options.projectName === void 0 || options.llmProvider === void 0) {
20246
20399
  observabilityEnabled = await promptForObservabilityOptIn();
20247
20400
  analytics?.trackEvent("cli_observability_selected", {
@@ -20250,32 +20403,40 @@ const create = async (args) => {
20250
20403
  answer: observabilityEnabled ? "yes" : "no",
20251
20404
  selection_method: "interactive"
20252
20405
  });
20253
- if (observabilityEnabled) {
20254
- platformSetupController = new AbortController();
20255
- platformSetupPromise = (async () => {
20256
- try {
20257
- const token = await getToken(platformSetupController.signal, {
20258
- skipOnInput: true,
20259
- source: args.loginSource
20260
- });
20261
- return {
20262
- status: "ready",
20406
+ }
20407
+ if (observabilityEnabled || needsGatewayKey) {
20408
+ platformSetupController = new AbortController();
20409
+ const signal = platformSetupController.signal;
20410
+ platformSetupPromise = (async () => {
20411
+ try {
20412
+ const token = await getToken(signal, {
20413
+ skipOnInput: true,
20414
+ source: args.loginSource
20415
+ });
20416
+ const org = await resolveCurrentOrg(token, {
20417
+ forcePrompt: true,
20418
+ exitOnCancel: false,
20419
+ signal
20420
+ });
20421
+ return {
20422
+ status: "ready",
20423
+ token,
20424
+ org,
20425
+ gateway: needsGatewayKey ? await setUpGateway({
20263
20426
  token,
20264
- org: await resolveCurrentOrg(token, {
20265
- forcePrompt: true,
20266
- exitOnCancel: false,
20267
- signal: platformSetupController.signal
20268
- })
20269
- };
20270
- } catch (error) {
20271
- if (error instanceof LoginCancelledError || error instanceof OrgSelectionCancelledError) return { status: "cancelled" };
20272
- return {
20273
- status: "failed",
20274
- error
20275
- };
20276
- }
20277
- })();
20278
- }
20427
+ org,
20428
+ projectName,
20429
+ signal
20430
+ }) : void 0
20431
+ };
20432
+ } catch (error) {
20433
+ if (error instanceof LoginCancelledError || error instanceof OrgSelectionCancelledError) return { status: "cancelled" };
20434
+ return {
20435
+ status: "failed",
20436
+ error
20437
+ };
20438
+ }
20439
+ })();
20279
20440
  }
20280
20441
  }
20281
20442
  const selectedTemplate = mode === "empty" ? void 0 : await resolveTemplate(mode, options.template);
@@ -20327,7 +20488,7 @@ const create = async (args) => {
20327
20488
  targetDir: staging.rootPath,
20328
20489
  branch,
20329
20490
  signal: materializationController.signal,
20330
- ...observabilityEnabled ? { silent: true } : {}
20491
+ ...platformSetupPromise ? { silent: true } : {}
20331
20492
  });
20332
20493
  if (isManaged) {
20333
20494
  const providerConfig = await adaptDefaultTemplate({
@@ -20345,7 +20506,7 @@ const create = async (args) => {
20345
20506
  }
20346
20507
  }
20347
20508
  if (options.install) {
20348
- if (observabilityEnabled) await installDependencies(staging.projectPath, packageManager, options.timeout, materializationController.signal, true);
20509
+ if (platformSetupPromise) await installDependencies(staging.projectPath, packageManager, options.timeout, materializationController.signal, true);
20349
20510
  else await installDependencies(staging.projectPath, packageManager, options.timeout, materializationController.signal);
20350
20511
  }
20351
20512
  materializationController.signal.throwIfAborted();
@@ -20370,7 +20531,7 @@ const create = async (args) => {
20370
20531
  if (platformSetupPromise) {
20371
20532
  platformSetup = await platformSetupPromise;
20372
20533
  if (platformSetup.status === "cancelled" && !interruptionSignal) {
20373
- analytics?.trackEvent("cli_observability_outcome", {
20534
+ if (observabilityEnabled) analytics?.trackEvent("cli_observability_outcome", {
20374
20535
  command: "create",
20375
20536
  outcome: "skipped"
20376
20537
  });
@@ -20384,6 +20545,7 @@ const create = async (args) => {
20384
20545
  } else await materializationPromise;
20385
20546
  process.removeListener("SIGINT", handleSigint);
20386
20547
  process.removeListener("SIGTERM", handleSigterm);
20548
+ if (interruptionSignal || materializationError) await discardUnusedGatewayKey(platformSetup);
20387
20549
  if (interruptionSignal === "SIGINT") {
20388
20550
  if (observabilityEnabled) analytics?.trackEvent("cli_observability_outcome", {
20389
20551
  command: "create",
@@ -20407,10 +20569,16 @@ const create = async (args) => {
20407
20569
  const setupSummary = formatPostCreateSetup(postSetup);
20408
20570
  if (postSetup.hasFailure) log.warn(setupSummary);
20409
20571
  else log.success(setupSummary);
20572
+ let gatewaySummary;
20573
+ if (needsGatewayKey) gatewaySummary = await finishGatewaySetup({
20574
+ projectPath: targetPath,
20575
+ platformSetup,
20576
+ analytics
20577
+ });
20410
20578
  let observabilitySummary;
20411
20579
  let platformEnvWritten = false;
20412
- let platformError = platformSetup?.status === "failed" ? platformSetup.error : void 0;
20413
- if (platformSetup?.status === "ready") try {
20580
+ let platformError = observabilityEnabled && platformSetup?.status === "failed" ? platformSetup.error : void 0;
20581
+ if (observabilityEnabled && platformSetup?.status === "ready") try {
20414
20582
  const result = await provisionObservabilityProject({
20415
20583
  defaultProjectName: projectName,
20416
20584
  mode: "create",
@@ -20446,7 +20614,7 @@ const create = async (args) => {
20446
20614
  observabilitySummary = `${import_picocolors.default.yellow("Could not connect this project to Mastra platform automatically:")} ${message}\n\n${placeholderSummary}1. Visit ${import_picocolors.default.cyan("https://projects.mastra.ai")} to create a project and an access token.\n2. Paste the token into ${import_picocolors.default.cyan("MASTRA_PLATFORM_ACCESS_TOKEN")} and the project id into ${import_picocolors.default.cyan("MASTRA_PROJECT_ID")}.`;
20447
20615
  }
20448
20616
  if (mode === "managed") {
20449
- const apiKeySummary = llmApiKey ? selectedApiKeyWritten ? `Your ${selectedApiKeyEnv} value was written to ${import_picocolors.default.cyan(".env")}.` : `Set ${selectedApiKeyEnv} in ${import_picocolors.default.cyan(".env")} before starting.` : platformEnvWritten ? `Set ${selectedApiKeyEnv} in ${import_picocolors.default.cyan(".env")} before starting.` : `Copy ${import_picocolors.default.cyan(".env.example")} to ${import_picocolors.default.cyan(".env")} and set ${selectedApiKeyEnv} before starting.`;
20617
+ const apiKeySummary = gatewaySummary ? gatewaySummary : llmApiKey ? selectedApiKeyWritten ? `Your ${selectedApiKeyEnv} value was written to ${import_picocolors.default.cyan(".env")}.` : `Set ${selectedApiKeyEnv} in ${import_picocolors.default.cyan(".env")} before starting.` : platformEnvWritten ? `Set ${selectedApiKeyEnv} in ${import_picocolors.default.cyan(".env")} before starting.` : `Copy ${import_picocolors.default.cyan(".env.example")} to ${import_picocolors.default.cyan(".env")} and set ${selectedApiKeyEnv} before starting.`;
20450
20618
  note(`${import_picocolors.default.green("Success!")}\n\n${apiKeySummary}${observabilitySummary ? `\n\n${observabilitySummary}` : ""}`);
20451
20619
  } else if (mode === "template") note(`${import_picocolors.default.green("Success!")}\n\nCreate a ${import_picocolors.default.cyan(".env")} file if the template requires environment variables.`);
20452
20620
  postCreate({
@@ -20454,6 +20622,109 @@ const create = async (args) => {
20454
20622
  packageManager
20455
20623
  });
20456
20624
  };
20625
+ async function setUpGateway({ token, org, projectName, signal }) {
20626
+ const spinner$2 = spinner();
20627
+ spinner$2.start("Setting up your Gateway key...");
20628
+ let spinning = true;
20629
+ let key;
20630
+ try {
20631
+ key = await createGatewayApiKey(token, org.orgId, projectName, signal);
20632
+ let credits;
20633
+ try {
20634
+ credits = await getGatewayCredits(token, org.orgId, signal);
20635
+ } catch (error) {
20636
+ if (signal.aborted) throw error;
20637
+ }
20638
+ spinner$2.stop(`Created a Gateway key in ${org.orgName}.`);
20639
+ spinning = false;
20640
+ let openedDashboard = false;
20641
+ if (credits) try {
20642
+ openedDashboard = await offerGatewayCredits({
20643
+ orgId: org.orgId,
20644
+ credits,
20645
+ signal
20646
+ });
20647
+ } catch (error) {
20648
+ if (signal.aborted) throw error;
20649
+ }
20650
+ return {
20651
+ status: "ready",
20652
+ apiKey: key.apiKey,
20653
+ keyId: key.keyId,
20654
+ orgName: org.orgName,
20655
+ creditsUrl: creditsPageUrl(org.orgId),
20656
+ remaining: credits?.remaining,
20657
+ unlimited: credits?.unlimited,
20658
+ openedDashboard
20659
+ };
20660
+ } catch (error) {
20661
+ if (spinning) spinner$2.stop("Could not set up a Gateway key.");
20662
+ if (signal.aborted) {
20663
+ await discardGatewayKey({
20664
+ token,
20665
+ orgId: org.orgId,
20666
+ keyId: key?.keyId
20667
+ });
20668
+ throw error;
20669
+ }
20670
+ return {
20671
+ status: "failed",
20672
+ error
20673
+ };
20674
+ }
20675
+ }
20676
+ /** Revoke a key no project ended up with. Best effort: a failure here shouldn't hide the real one. */
20677
+ async function discardGatewayKey({ token, orgId, keyId }) {
20678
+ if (!keyId) return;
20679
+ try {
20680
+ await revokeGatewayApiKey(token, orgId, keyId);
20681
+ } catch {}
20682
+ }
20683
+ /** The Gateway key that was created but won't be written to a project. */
20684
+ async function discardUnusedGatewayKey(platformSetup) {
20685
+ if (platformSetup?.status !== "ready" || platformSetup.gateway?.status !== "ready") return;
20686
+ await discardGatewayKey({
20687
+ token: platformSetup.token,
20688
+ orgId: platformSetup.org.orgId,
20689
+ keyId: platformSetup.gateway.keyId
20690
+ });
20691
+ }
20692
+ async function finishGatewaySetup({ projectPath, platformSetup, analytics }) {
20693
+ const envName = import_picocolors.default.cyan(GATEWAY_API_KEY_ENV);
20694
+ const manualSteps = `Get a key at ${import_picocolors.default.cyan("https://gateway.mastra.ai")} and set ${envName} in ${import_picocolors.default.cyan(".env")}.`;
20695
+ const gateway = platformSetup?.status === "ready" ? platformSetup.gateway : void 0;
20696
+ if (gateway?.status !== "ready") {
20697
+ const error = gateway?.status === "failed" ? gateway.error : platformSetup?.status === "failed" ? platformSetup.error : void 0;
20698
+ analytics?.trackEvent("cli_gateway_outcome", {
20699
+ command: "create",
20700
+ outcome: error ? "failed" : "skipped"
20701
+ });
20702
+ return `${error instanceof Error ? `${import_picocolors.default.yellow("Could not create a Gateway key:")} ${error.message}\n\n` : ""}${manualSteps}`;
20703
+ }
20704
+ if (!await writeManagedApiKey({
20705
+ projectPath,
20706
+ apiKeyEnv: "MASTRA_GATEWAY_API_KEY",
20707
+ apiKey: gateway.apiKey
20708
+ })) {
20709
+ await discardUnusedGatewayKey(platformSetup);
20710
+ analytics?.trackEvent("cli_gateway_outcome", {
20711
+ command: "create",
20712
+ outcome: "failed"
20713
+ });
20714
+ return `${import_picocolors.default.yellow("Could not write the Gateway key to .env.")}\n\n${manualSteps}`;
20715
+ }
20716
+ analytics?.trackEvent("cli_gateway_outcome", {
20717
+ command: "create",
20718
+ outcome: "completed",
20719
+ opened_dashboard: gateway.openedDashboard ?? false
20720
+ });
20721
+ const written = `Your Gateway key was written to ${import_picocolors.default.cyan(".env")} as ${envName}.`;
20722
+ const creditsUrl = import_picocolors.default.cyan(gateway.creditsUrl);
20723
+ if (gateway.unlimited) return `${written}\n${gateway.orgName} has unlimited Gateway credits.`;
20724
+ if (gateway.remaining === void 0) return `${written}\nModel calls are paid per token from Gateway credits. Check your balance in the Mastra dashboard: ${creditsUrl}`;
20725
+ if (gateway.remaining === 0) return `${written}\n${import_picocolors.default.yellow("Model calls fail until you add Gateway credits:")} ${creditsUrl}`;
20726
+ return `${written}\nBalance: ${formatCredits(gateway.remaining)} in ${gateway.orgName}. Model calls are paid per token from these credits.`;
20727
+ }
20457
20728
  async function runPostCreateSetup({ projectPath, installSkills, initializeGit, invocationIsGitWorktree }) {
20458
20729
  let skillsAgents = [];
20459
20730
  let skillsOutcome = "opted_out";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-mastra",
3
- "version": "1.37.0-alpha.2",
3
+ "version": "1.37.0",
4
4
  "description": "Create Mastra apps with one command",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -36,8 +36,8 @@
36
36
  "tsdown": "0.23.1",
37
37
  "typescript": "^7.0.2",
38
38
  "vitest": "4.1.12",
39
- "@internal/lint": "0.0.146",
40
- "mastra": "^1.37.0-alpha.2"
39
+ "@internal/lint": "0.0.147",
40
+ "mastra": "^1.37.0"
41
41
  },
42
42
  "engines": {
43
43
  "node": ">=22.13.0"
@@ -1 +0,0 @@
1
- export { t as fetchOrgs } from "./api-CV5-CXEm-Cpd49bi2.js";
@@ -1 +0,0 @@
1
- export { i as loadCredentials, o as tryRefreshToken } from "./credentials-HeXP-kY2-jky92lr5.js";