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.
- package/README.md +1 -1
- package/dist/index.js +23 -19
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 ===
|
|
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
|
+
"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.
|
|
35
|
+
"@clack/prompts": "^1.8.1",
|
|
36
36
|
"commander": "^14.0.3",
|
|
37
37
|
"picocolors": "^1.1.1",
|
|
38
|
-
"tinyexec": "^1.
|
|
39
|
-
"mastra": "1.36.0-alpha.
|
|
38
|
+
"tinyexec": "^1.3.1",
|
|
39
|
+
"mastra": "1.36.0-alpha.5"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@types/node": "22.
|
|
42
|
+
"@types/node": "22.19.15",
|
|
43
43
|
"tsdown": "0.23.1",
|
|
44
44
|
"typescript": "^7.0.2",
|
|
45
45
|
"vitest": "4.1.12",
|