create-factory 0.3.1-alpha.3 → 0.4.0-alpha.5

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +23 -19
  3. package/package.json +5 -5
package/README.md CHANGED
@@ -33,7 +33,7 @@ pnpm create factory@latest
33
33
 
34
34
  The interactive setup asks where to create the project and helps configure your Mastra Factory project.
35
35
 
36
- By default, the setup wizard provisions Mastra platform resources during setup, enabling you to fully run Mastra Factory on platform or locally with cloud-backed capabilities. If you prefer to [self-host](https://factory.mastra.ai/deployment#self-host) Mastra Factory, run the setup with the `--no-platform` flag.
36
+ By default, the setup wizard scaffolds the [platform template](https://github.com/mastra-ai/template-factory) and provisions Mastra platform resources during setup, enabling you to fully run Mastra Factory on platform or locally with cloud-backed capabilities. If you prefer to [self-host](https://factory.mastra.ai/deployment#self-host) Mastra Factory, run the setup with the `--no-platform` flag. It scaffolds the [self-hosted template](https://github.com/mastra-ai/template-factory-self-hosted) instead, which uses PostgreSQL, WorkOS, a GitHub App, and local sandboxes. Either way, the setup writes a freshly generated `FACTORY_CREDENTIAL_ENCRYPTION_KEY` to `.env` when the template uses one, so stored credentials are encrypted from the first start.
37
37
 
38
38
  ```bash
39
39
  npx create-factory@latest -- --no-platform
package/dist/index.js CHANGED
@@ -4,12 +4,13 @@ import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { Command } from "commander";
6
6
  import { PosthogAnalytics, setAnalytics } from "mastra/dist/analytics/index.js";
7
+ import { randomBytes } from "node:crypto";
7
8
  import * as p from "@clack/prompts";
8
9
  import { LoginCancelledError, MASTRA_PLATFORM_API_URL, authHeaders, extractApiErrorDetail, fetchOrgs, getToken, loadCredentials, platformFetch, resolveCurrentOrg } from "mastra/internal/auth";
9
10
  import color from "picocolors";
10
11
  import { x } from "tinyexec";
11
12
  import fs$1 from "node:fs/promises";
12
- //#region src/env.ts
13
+ // src/env.ts
13
14
  /**
14
15
  * Idempotently update a `.env` file.
15
16
  *
@@ -42,8 +43,7 @@ function upsertEnvFile(envPath, updates) {
42
43
  fs.chmodSync(envPath, 384);
43
44
  } catch {}
44
45
  }
45
- //#endregion
46
- //#region src/platform.ts
46
+ // src/platform.ts
47
47
  /**
48
48
  * Common failure path: read `detail`/`message` out of the JSON body if the
49
49
  * response wasn't JSON-parseable, fall back to the raw text.
@@ -218,8 +218,7 @@ function composeSignals(outer, inner) {
218
218
  else inner.addEventListener("abort", () => abort(inner.reason), { once: true });
219
219
  return controller.signal;
220
220
  }
221
- //#endregion
222
- //#region src/utils/clone.ts
221
+ // src/utils/clone.ts
223
222
  /** Rewrite the scaffolded package.json name to the chosen project name. */
224
223
  async function renameProject(projectPath, projectName) {
225
224
  const pkgPath = path.join(projectPath, "package.json");
@@ -295,8 +294,7 @@ async function cloneTemplate(repoUrl, targetPath) {
295
294
  });
296
295
  }
297
296
  }
298
- //#endregion
299
- //#region src/utils/pm.ts
297
+ // src/utils/pm.ts
300
298
  function detectPackageManager() {
301
299
  const userAgent = process.env.npm_config_user_agent || "";
302
300
  const execPath = process.env.npm_execpath || "";
@@ -320,8 +318,6 @@ function getInstallArgs(packageManager) {
320
318
  default: return ["install"];
321
319
  }
322
320
  }
323
- //#endregion
324
- //#region src/create.ts
325
321
  const PROJECT_REGION_OPTIONS = [{
326
322
  value: "eu",
327
323
  label: "🇪🇺 eu"
@@ -361,13 +357,14 @@ async function create(args) {
361
357
  const spinner = p.spinner();
362
358
  spinner.start("Downloading the Mastra Factory template...");
363
359
  try {
364
- await cloneTemplate(args.template, projectPath);
360
+ await cloneTemplate(args.template ?? (args.noPlatform ? "https://github.com/mastra-ai/template-factory-self-hosted" : "https://github.com/mastra-ai/template-factory"), projectPath);
365
361
  await renameProject(projectPath, projectName);
366
362
  const envPath = path.join(projectPath, ".env");
367
363
  fs.copyFileSync(path.join(projectPath, ".env.example"), envPath);
368
364
  try {
369
365
  fs.chmodSync(envPath, 384);
370
366
  } catch {}
367
+ seedCredentialEncryptionKey(envPath);
371
368
  spinner.stop("Template downloaded.");
372
369
  } catch (err) {
373
370
  spinner.stop("Template download failed.");
@@ -603,6 +600,17 @@ function sanitizeDatabaseName(projectName) {
603
600
  return (projectName.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/^-+|-+$/g, "") || "factory").slice(0, 64);
604
601
  }
605
602
  /**
603
+ * Fill the template's empty `FACTORY_CREDENTIAL_ENCRYPTION_KEY` placeholder
604
+ * with a fresh AES-256 key so stored credentials are encrypted from the first
605
+ * boot. Templates that don't declare the key, or that already set one, are
606
+ * left alone.
607
+ */
608
+ function seedCredentialEncryptionKey(envPath) {
609
+ const env = fs.readFileSync(envPath, "utf8");
610
+ if (!/^\s*(#\s*)?FACTORY_CREDENTIAL_ENCRYPTION_KEY\s*=\s*$/m.test(env)) return;
611
+ upsertEnvFile(envPath, { FACTORY_CREDENTIAL_ENCRYPTION_KEY: randomBytes(32).toString("base64") });
612
+ }
613
+ /**
606
614
  * Append `.env` to the scaffolded project's `.gitignore` if it isn't already
607
615
  * ignored. Runs before the initial `git add -A` so freshly-provisioned platform
608
616
  * credentials (MASTRA_PLATFORM_SECRET_KEY, DATABASE_URL) never reach the
@@ -622,8 +630,7 @@ function ensureEnvGitignored(projectPath) {
622
630
  const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
623
631
  fs.writeFileSync(gitignorePath, `${existing}${prefix}\n# Added by create-factory to protect platform credentials\n.env\n`);
624
632
  }
625
- //#endregion
626
- //#region src/utils/redact.ts
633
+ // src/utils/redact.ts
627
634
  /**
628
635
  * Sanitizes error messages before they are sent to analytics.
629
636
  *
@@ -669,8 +676,7 @@ function redactError(error, sensitiveValues = []) {
669
676
  const message = error instanceof Error ? error.message : String(error);
670
677
  return new Error(redactErrorMessage(message, sensitiveValues));
671
678
  }
672
- //#endregion
673
- //#region src/index.ts
679
+ // src/index.ts
674
680
  const pkg = JSON.parse(readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8"));
675
681
  const analytics = new PosthogAnalytics({
676
682
  apiKey: "phc_SBLpZVAB6jmHOct9CABq3PF0Yn5FU3G2FgT4xUr2XrT",
@@ -679,12 +685,11 @@ const analytics = new PosthogAnalytics({
679
685
  });
680
686
  setAnalytics(analytics);
681
687
  const program = new Command();
682
- const DEFAULT_TEMPLATE_REPO = "https://github.com/mastra-ai/softwarefactory-template";
683
- program.name("create-factory").description("Create a new Mastra Factory project").argument("[project-name]", "Directory name of the project").option("--template <template-name>", "Create a project from a template (public GitHub URL)", DEFAULT_TEMPLATE_REPO).option("--no-platform", "Skip Mastra platform sign-in, project, and Neon provisioning").option("--org <org>", "Mastra organization id or name — skips the interactive org picker").option("--region <region>", "Platform project region (eu or us); prompts when omitted").version(pkg.version, "-v, --version").action(async (projectNameArg, args) => {
688
+ program.name("create-factory").description("Create a new Mastra Factory project").argument("[project-name]", "Directory name of the project").option("--template <template-name>", "Create a project from a template (public GitHub URL). Defaults to the platform template, or the self-hosted template with --no-platform").option("--no-platform", "Skip Mastra platform provisioning and scaffold the self-hosted template").option("--org <org>", "Mastra organization id or name — skips the interactive org picker").option("--region <region>", "Platform project region (eu or us); prompts when omitted").version(pkg.version, "-v, --version").action(async (projectNameArg, args) => {
684
689
  const region = args.region ? String(args.region) : void 0;
685
690
  const validRegion = region === "eu" || region === "us" ? region : void 0;
686
691
  const sensitiveValues = [
687
- args.template === DEFAULT_TEMPLATE_REPO ? void 0 : args.template,
692
+ args.template,
688
693
  args.org ? String(args.org) : void 0,
689
694
  validRegion ? void 0 : region,
690
695
  projectNameArg
@@ -694,7 +699,7 @@ program.name("create-factory").description("Create a new Mastra Factory project"
694
699
  await analytics.trackCommandExecution({
695
700
  command: "create-factory",
696
701
  args: {
697
- default_template: args.template === DEFAULT_TEMPLATE_REPO,
702
+ default_template: args.template === void 0,
698
703
  no_platform: args.platform === false,
699
704
  has_org: Boolean(args.org),
700
705
  region: validRegion ?? (region ? "invalid" : void 0)
@@ -727,5 +732,4 @@ try {
727
732
  } finally {
728
733
  await analytics.shutdown(1e3);
729
734
  }
730
- //#endregion
731
735
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-factory",
3
- "version": "0.3.1-alpha.3",
3
+ "version": "0.4.0-alpha.5",
4
4
  "description": "Create a Mastra Factory project: an agent-powered software delivery environment built on Mastra. Run `npm create factory` to scaffold and get started.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -32,14 +32,14 @@
32
32
  "ai"
33
33
  ],
34
34
  "dependencies": {
35
- "@clack/prompts": "^1.7.0",
35
+ "@clack/prompts": "^1.8.1",
36
36
  "commander": "^14.0.3",
37
37
  "picocolors": "^1.1.1",
38
- "tinyexec": "^1.2.4",
39
- "mastra": "1.36.0-alpha.3"
38
+ "tinyexec": "^1.3.1",
39
+ "mastra": "1.36.0-alpha.5"
40
40
  },
41
41
  "devDependencies": {
42
- "@types/node": "22.20.5",
42
+ "@types/node": "22.19.15",
43
43
  "tsdown": "0.23.1",
44
44
  "typescript": "^7.0.2",
45
45
  "vitest": "4.1.12",