create-factory 0.0.0-fix-thread-subscription-multi-instance-redis-streams-20260730204150

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/CHANGELOG.md ADDED
@@ -0,0 +1,437 @@
1
+ # create-factory
2
+
3
+ ## 0.0.0-fix-thread-subscription-multi-instance-redis-streams-20260730204150
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`3f8e2f2`](https://github.com/mastra-ai/mastra/commit/3f8e2f2006588171348acdb6dfcba8a37fed1106), [`1d0b2d8`](https://github.com/mastra-ai/mastra/commit/1d0b2d8712f330992bf0c8464e093d14961f83b4), [`b95e551`](https://github.com/mastra-ai/mastra/commit/b95e551e2fdb149848dcdbd5d8f46c6736d6bff4), [`01c14ae`](https://github.com/mastra-ai/mastra/commit/01c14aed071c9580678036f21cb5eb6079c0ca80), [`b35ed6f`](https://github.com/mastra-ai/mastra/commit/b35ed6f7c257583eafbad6afa3320c88b26d8112), [`37a5de6`](https://github.com/mastra-ai/mastra/commit/37a5de698003e33b33ae647533461444383c1056), [`3ea362f`](https://github.com/mastra-ai/mastra/commit/3ea362f28d36b7a6b248d0d05df3d14d83b50c39), [`a46aba2`](https://github.com/mastra-ai/mastra/commit/a46aba2249ef3cb965431eb063b9281f7e5e1eca)]:
8
+ - mastra@0.0.0-fix-thread-subscription-multi-instance-redis-streams-20260730204150
9
+
10
+ ## 0.1.5-alpha.2
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [[`3f8e2f2`](https://github.com/mastra-ai/mastra/commit/3f8e2f2006588171348acdb6dfcba8a37fed1106), [`1d0b2d8`](https://github.com/mastra-ai/mastra/commit/1d0b2d8712f330992bf0c8464e093d14961f83b4), [`01c14ae`](https://github.com/mastra-ai/mastra/commit/01c14aed071c9580678036f21cb5eb6079c0ca80)]:
15
+ - mastra@1.22.0-alpha.2
16
+
17
+ ## 0.1.5-alpha.1
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies [[`b35ed6f`](https://github.com/mastra-ai/mastra/commit/b35ed6f7c257583eafbad6afa3320c88b26d8112), [`a46aba2`](https://github.com/mastra-ai/mastra/commit/a46aba2249ef3cb965431eb063b9281f7e5e1eca)]:
22
+ - mastra@1.21.1-alpha.1
23
+
24
+ ## 0.1.5-alpha.0
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies [[`b95e551`](https://github.com/mastra-ai/mastra/commit/b95e551e2fdb149848dcdbd5d8f46c6736d6bff4), [`37a5de6`](https://github.com/mastra-ai/mastra/commit/37a5de698003e33b33ae647533461444383c1056), [`3ea362f`](https://github.com/mastra-ai/mastra/commit/3ea362f28d36b7a6b248d0d05df3d14d83b50c39)]:
29
+ - mastra@1.21.1-alpha.0
30
+
31
+ ## 0.1.4
32
+
33
+ ### Patch Changes
34
+
35
+ - Improved contributor guidance for the Factory scaffolder. ([#20327](https://github.com/mastra-ai/mastra/pull/20327))
36
+
37
+ - Updated dependencies [[`6b1e467`](https://github.com/mastra-ai/mastra/commit/6b1e46731b3062a3c26c1c5889299eb186bbf08c), [`1d3a2cf`](https://github.com/mastra-ai/mastra/commit/1d3a2cf9111d947c344c4168579fb244c52ee9f8), [`4b9dc7a`](https://github.com/mastra-ai/mastra/commit/4b9dc7a6a3e7d96831efc2a1e6a7fb0d7632ca82), [`c539a25`](https://github.com/mastra-ai/mastra/commit/c539a25b620eb27c92ac8585b2740981ba1c28bf)]:
38
+ - mastra@1.21.0
39
+
40
+ ## 0.1.4-alpha.3
41
+
42
+ ### Patch Changes
43
+
44
+ - Updated dependencies:
45
+ - mastra@1.21.0-alpha.3
46
+
47
+ ## 0.1.4-alpha.2
48
+
49
+ ### Patch Changes
50
+
51
+ - Updated dependencies [[`6b1e467`](https://github.com/mastra-ai/mastra/commit/6b1e46731b3062a3c26c1c5889299eb186bbf08c), [`4b9dc7a`](https://github.com/mastra-ai/mastra/commit/4b9dc7a6a3e7d96831efc2a1e6a7fb0d7632ca82)]:
52
+ - mastra@1.21.0-alpha.2
53
+
54
+ ## 0.1.4-alpha.1
55
+
56
+ ### Patch Changes
57
+
58
+ - Updated dependencies [[`1d3a2cf`](https://github.com/mastra-ai/mastra/commit/1d3a2cf9111d947c344c4168579fb244c52ee9f8)]:
59
+ - mastra@1.21.0-alpha.1
60
+
61
+ ## 0.1.4-alpha.0
62
+
63
+ ### Patch Changes
64
+
65
+ - Improved contributor guidance for the Factory scaffolder. ([#20327](https://github.com/mastra-ai/mastra/pull/20327))
66
+
67
+ - Updated dependencies [[`c539a25`](https://github.com/mastra-ai/mastra/commit/c539a25b620eb27c92ac8585b2740981ba1c28bf)]:
68
+ - mastra@1.21.0-alpha.0
69
+
70
+ ## 0.1.3
71
+
72
+ ### Patch Changes
73
+
74
+ - Improved Factory onboarding so any key skips optional Mastra Platform setup while Ctrl+C cancels project creation. ([#20299](https://github.com/mastra-ai/mastra/pull/20299))
75
+
76
+ - Generated Factory projects now use the Factory UI bundled with the Mastra CLI instead of including editable browser source and its build dependencies. ([#20246](https://github.com/mastra-ai/mastra/pull/20246))
77
+
78
+ - Updated dependencies [[`6ce9581`](https://github.com/mastra-ai/mastra/commit/6ce9581ba0ef671dd04e9ad6c6290f2aa7028550)]:
79
+ - mastra@1.20.3
80
+
81
+ ## 0.1.3-alpha.4
82
+
83
+ ### Patch Changes
84
+
85
+ - Updated dependencies:
86
+ - mastra@1.20.3-alpha.4
87
+
88
+ ## 0.1.3-alpha.3
89
+
90
+ ### Patch Changes
91
+
92
+ - Updated dependencies:
93
+ - mastra@1.20.3-alpha.3
94
+
95
+ ## 0.1.3-alpha.2
96
+
97
+ ### Patch Changes
98
+
99
+ - Improved Factory onboarding so any key skips optional Mastra Platform setup while Ctrl+C cancels project creation. ([#20299](https://github.com/mastra-ai/mastra/pull/20299))
100
+
101
+ - Updated dependencies [[`6ce9581`](https://github.com/mastra-ai/mastra/commit/6ce9581ba0ef671dd04e9ad6c6290f2aa7028550)]:
102
+ - mastra@1.20.3-alpha.2
103
+
104
+ ## 0.1.3-alpha.1
105
+
106
+ ### Patch Changes
107
+
108
+ - Generated Factory projects now use the Factory UI bundled with the Mastra CLI instead of including editable browser source and its build dependencies. ([#20246](https://github.com/mastra-ai/mastra/pull/20246))
109
+
110
+ - Updated dependencies:
111
+ - mastra@1.20.3-alpha.1
112
+
113
+ ## 0.1.3-alpha.0
114
+
115
+ ### Patch Changes
116
+
117
+ - Updated dependencies:
118
+ - mastra@1.20.3-alpha.0
119
+
120
+ ## 0.1.2
121
+
122
+ ### Patch Changes
123
+
124
+ - Fixed generated Factory projects to allow newly published Mastra packages when pnpm minimum release age policies are configured. ([#20104](https://github.com/mastra-ai/mastra/pull/20104))
125
+
126
+ - Fixed Factory template generation during release transitions by selecting exact published package versions that match the local source release and configuring npm for compatible prerelease installs. ([#20097](https://github.com/mastra-ai/mastra/pull/20097))
127
+
128
+ - Updated dependencies:
129
+ - mastra@1.20.2
130
+
131
+ ## 0.1.2-alpha.5
132
+
133
+ ### Patch Changes
134
+
135
+ - Updated dependencies:
136
+ - mastra@1.20.2-alpha.5
137
+
138
+ ## 0.1.2-alpha.4
139
+
140
+ ### Patch Changes
141
+
142
+ - Updated dependencies:
143
+ - mastra@1.20.2-alpha.4
144
+
145
+ ## 0.1.2-alpha.3
146
+
147
+ ### Patch Changes
148
+
149
+ - Updated dependencies:
150
+ - mastra@1.20.2-alpha.3
151
+
152
+ ## 0.1.2-alpha.2
153
+
154
+ ### Patch Changes
155
+
156
+ - Updated dependencies:
157
+ - mastra@1.20.2-alpha.2
158
+
159
+ ## 0.1.2-alpha.1
160
+
161
+ ### Patch Changes
162
+
163
+ - Fixed generated Factory projects to allow newly published Mastra packages when pnpm minimum release age policies are configured. ([#20104](https://github.com/mastra-ai/mastra/pull/20104))
164
+
165
+ - Updated dependencies:
166
+ - mastra@1.20.2-alpha.1
167
+
168
+ ## 0.1.2-alpha.0
169
+
170
+ ### Patch Changes
171
+
172
+ - Fixed Factory template generation during release transitions by selecting exact published package versions that match the local source release and configuring npm for compatible prerelease installs. ([#20097](https://github.com/mastra-ai/mastra/pull/20097))
173
+
174
+ - Updated dependencies:
175
+ - mastra@1.20.2-alpha.0
176
+
177
+ ## 0.1.1
178
+
179
+ ### Patch Changes
180
+
181
+ - Fixed generated Factory projects installing stale Mastra package versions. ([#20087](https://github.com/mastra-ai/mastra/pull/20087))
182
+
183
+ - Fixed the Factory template sync to replace linked dependencies with the exact versions currently published under npm's latest tag. ([#20093](https://github.com/mastra-ai/mastra/pull/20093))
184
+
185
+ - Updated dependencies [[`7d3a12e`](https://github.com/mastra-ai/mastra/commit/7d3a12ecfd0c4994ace2583e5497cb3a544ae350)]:
186
+ - mastra@1.20.1
187
+
188
+ ## 0.1.1-alpha.0
189
+
190
+ ### Patch Changes
191
+
192
+ - Fixed generated Factory projects installing stale Mastra package versions. ([#20087](https://github.com/mastra-ai/mastra/pull/20087))
193
+
194
+ - Updated dependencies [[`7d3a12e`](https://github.com/mastra-ai/mastra/commit/7d3a12ecfd0c4994ace2583e5497cb3a544ae350)]:
195
+ - mastra@1.20.1-alpha.0
196
+
197
+ ## 0.1.0
198
+
199
+ ### Minor Changes
200
+
201
+ - Standardized the Factory SPA directory and simplified the generated build script because `mastra build` now packages the prebuilt UI automatically. ([#19948](https://github.com/mastra-ai/mastra/pull/19948))
202
+
203
+ Before:
204
+
205
+ ```json
206
+ { "build": "npm run build:ui && mastra build --dir src/mastra" }
207
+ ```
208
+
209
+ After:
210
+
211
+ ```json
212
+ { "build": "mastra build --dir src/mastra" }
213
+ ```
214
+
215
+ Factory assets now land in `src/mastra/public/factory/` during the build and `.mastra/output/factory/` in the deployable output, replacing the previous `ui/` path.
216
+
217
+ - Added EU and US region selection when creating Factory platform projects, with a --region flag for non-interactive setup. ([#20040](https://github.com/mastra-ai/mastra/pull/20040))
218
+
219
+ ### Patch Changes
220
+
221
+ - Updated the generated project README for the single-server setup: the Factory UI and API are both served from `http://localhost:4111`, OAuth callback instructions use the server origin, and the removed `dev:prod` / `build:ui` scripts are no longer documented. ([#20036](https://github.com/mastra-ai/mastra/pull/20036))
222
+
223
+ - Apply prettier formatting to env.test.ts (post-merge follow-up) ([#19965](https://github.com/mastra-ai/mastra/pull/19965))
224
+
225
+ - Harden .env handling in create-factory: tighten file permissions and skip git init when .env cannot be gitignored ([#19945](https://github.com/mastra-ai/mastra/pull/19945))
226
+
227
+ `.env` files created during scaffolding are now written with `0600` permissions so platform secrets (`MASTRA_PLATFORM_SECRET_KEY`, `DATABASE_URL`) are only readable by the owner. If the scaffolder can't add `.env` to `.gitignore` (e.g. permission denied on `.gitignore`), it now skips `git init` entirely and warns the user, so freshly-minted secrets can't accidentally be committed to the initial commit.
228
+
229
+ - Make the Software Factory template installable and buildable against published packages so the sync-softwarefactory-template workflow can push it again. Three changes to the generation step: ([#20001](https://github.com/mastra-ai/mastra/pull/20001))
230
+
231
+ - Pin every synced Mastra dep to `"alpha"` instead of `"latest"` — the Mastra Factory sources on `main` are built against the alpha release train, and the previous `"latest"` default mixed release trains (worse, `@mastra/factory@latest` is currently an empty stub).
232
+ - Emit `.npmrc` with `legacy-peer-deps=true` so npm accepts the internally-consistent prerelease peer graph (the same relaxation pnpm applies automatically inside the monorepo).
233
+ - Downgrade `typescript` from tsgo (v7) to the classic compiler (`^5.9.2`) in the emitted template. The sources happily typecheck under tsgo, but `mastra build` transitively loads TypeScript via `typescript-paths`, which needs the classic `ts.sys` API tsgo doesn't expose. In the monorepo pnpm hoists classic TypeScript from another workspace package, hiding the problem; the standalone template has no hoist.
234
+
235
+ All three are annotated as temporary in the script and README — remove once the packages ship stable releases and the deployer supports tsgo.
236
+
237
+ - Marked projects created by create-factory as factory-enabled on the Mastra platform. ([#19973](https://github.com/mastra-ai/mastra/pull/19973))
238
+
239
+ - Changed the create-factory template sync to pin every Mastra dep to `"latest"` instead of `"alpha"`. Scaffolded projects now install the same set of Mastra packages as every other create-mastra template, and no longer ship a `.npmrc` with `legacy-peer-deps=true` (that flag only existed to accommodate the prerelease peer graph). ([#20052](https://github.com/mastra-ai/mastra/pull/20052))
240
+
241
+ - The factory template now ships a pnpm-workspace.yaml with allowBuilds, preventing pnpm v10+ from exiting with ERR_PNPM_IGNORED_BUILDS during install or build. The file mirrors the mastracode/web build-approval policy minus test-only deps stripped by the template. ([#20056](https://github.com/mastra-ai/mastra/pull/20056))
242
+
243
+ - Improved the create-factory sign-in and success experience: ([#20024](https://github.com/mastra-ai/mastra/pull/20024))
244
+
245
+ - When no Mastra platform session exists, the CLI now pauses with "Mastra account is required, press enter to continue..." before opening the browser auth flow instead of opening it unannounced.
246
+ - The success message now summarizes the infrastructure provisioned on Mastra platform (project, Postgres database, credentials in .env), notes that deployed code agent sessions run inside Mastra platform sandboxes, and links to https://projects.mastra.ai for managing the project.
247
+
248
+ - Stopped writing MASTRA_SHARED_API_URL to the scaffolded project's .env during platform provisioning. Platform consumers now use their built-in default platform URL, so scaffolded factories no longer pin the API endpoint at create time. ([#20021](https://github.com/mastra-ai/mastra/pull/20021))
249
+
250
+ - Added the `create-factory` CLI. It scaffolds a Mastra Software Factory project: enter a project name and the CLI clones the template, installs dependencies, and initializes git. Configuration (model providers, integrations, database) happens in the web UI on first load. ([#19609](https://github.com/mastra-ai/mastra/pull/19609))
251
+
252
+ ```bash
253
+ npm create factory my-factory
254
+ cd my-factory
255
+ npm run dev
256
+ ```
257
+
258
+ - Fixed generated Factory projects to serve the UI and API from a single Mastra development server. ([#20019](https://github.com/mastra-ai/mastra/pull/20019))
259
+
260
+ - Stop shipping `pnpm-workspace.yaml` and `package-lock.json` in projects scaffolded by `npm create factory`. The template generator now excludes the web project's pnpm workspace marker and lockfiles, and the sync workflow validates the template in a throwaway copy so `npm install` artifacts can no longer leak into the published template repository. ([#20041](https://github.com/mastra-ai/mastra/pull/20041))
261
+
262
+ - Simplified the create-factory success message: sandboxes now appear as a bullet in the provisioned resources list so users know code agent sessions run inside Mastra platform sandboxes. ([#20062](https://github.com/mastra-ai/mastra/pull/20062))
263
+
264
+ - Fixed generated Software Factory projects missing their required `@mastra/memory` dependency. ([#19878](https://github.com/mastra-ai/mastra/pull/19878))
265
+
266
+ - Added platform sign-in, project creation, and Neon Postgres provisioning to the `create factory` CLI. After scaffolding, the CLI: ([#19945](https://github.com/mastra-ai/mastra/pull/19945))
267
+
268
+ - Signs the user in via the existing Mastra browser-auth flow.
269
+ - Creates a Mastra platform server project in the chosen organization.
270
+ - Mints an `sk_` organization API key scoped to the new factory.
271
+ - Attaches and provisions a Neon Postgres database.
272
+ - Writes `MASTRA_SHARED_API_URL`, `MASTRA_ORGANIZATION_ID`, `MASTRA_PROJECT_ID`, `MASTRA_PLATFORM_SECRET_KEY`, and `DATABASE_URL` to the project's `.env`.
273
+
274
+ The result is a locally-runnable factory that can talk to the Mastra platform on first `npm run dev` without any manual configuration.
275
+
276
+ **New flags:**
277
+
278
+ - `--no-platform` — skip the platform round-trip; useful when iterating on the template offline.
279
+ - `--region <region>` — pass a specific Neon region id through to the platform.
280
+
281
+ - Updated dependencies [[`c03d857`](https://github.com/mastra-ai/mastra/commit/c03d85791d04177bdc6095cef924aec47a440b70), [`91930d6`](https://github.com/mastra-ai/mastra/commit/91930d69ae8146ded10c792387848970f1ca4b59), [`3b77e77`](https://github.com/mastra-ai/mastra/commit/3b77e7704936522e4769d29de1b5ea6901f302bd), [`8c88764`](https://github.com/mastra-ai/mastra/commit/8c88764162b32c8a24b3bf9d1ad2ec535aba5c9a), [`73db8db`](https://github.com/mastra-ai/mastra/commit/73db8db90d69ab6153c7942749f624db0d96952d), [`bfa8c05`](https://github.com/mastra-ai/mastra/commit/bfa8c056fc235f17805f1eb93e738ee81e8a8cb6), [`0182be3`](https://github.com/mastra-ai/mastra/commit/0182be35cd402209182097098e7ebc07a5a54a2a), [`c9de100`](https://github.com/mastra-ai/mastra/commit/c9de10008ff3f72911bf746294004c305674d855), [`0a50de7`](https://github.com/mastra-ai/mastra/commit/0a50de7024816171701e81ac3b69434cf5a302ea), [`f2e0c72`](https://github.com/mastra-ai/mastra/commit/f2e0c72fa27555bc59c0322b6398e7a6d990d679), [`471e34c`](https://github.com/mastra-ai/mastra/commit/471e34c399410c380d07c03abf7d3bbf2de736e0), [`7eb2707`](https://github.com/mastra-ai/mastra/commit/7eb2707cc388f929a38562fecb075064aed71d44), [`471e34c`](https://github.com/mastra-ai/mastra/commit/471e34c399410c380d07c03abf7d3bbf2de736e0), [`3b77e77`](https://github.com/mastra-ai/mastra/commit/3b77e7704936522e4769d29de1b5ea6901f302bd), [`40ae860`](https://github.com/mastra-ai/mastra/commit/40ae860a674e7ee383a02c1f990d1e050619d5e4), [`471e34c`](https://github.com/mastra-ai/mastra/commit/471e34c399410c380d07c03abf7d3bbf2de736e0), [`f2e0c72`](https://github.com/mastra-ai/mastra/commit/f2e0c72fa27555bc59c0322b6398e7a6d990d679), [`c328769`](https://github.com/mastra-ai/mastra/commit/c3287698ff8ef98dba86d415faa566fa3e5f4d56), [`471e34c`](https://github.com/mastra-ai/mastra/commit/471e34c399410c380d07c03abf7d3bbf2de736e0), [`471e34c`](https://github.com/mastra-ai/mastra/commit/471e34c399410c380d07c03abf7d3bbf2de736e0), [`51f62f0`](https://github.com/mastra-ai/mastra/commit/51f62f0f8ae3906a4b46ceb987f19c8245563cfa), [`20b159d`](https://github.com/mastra-ai/mastra/commit/20b159d8f5d8153493ec8d5b6cd2864af8ce2c8e)]:
282
+ - mastra@1.20.0
283
+
284
+ ## 0.1.0-alpha.12
285
+
286
+ ### Patch Changes
287
+
288
+ - Updated dependencies [[`bfa8c05`](https://github.com/mastra-ai/mastra/commit/bfa8c056fc235f17805f1eb93e738ee81e8a8cb6)]:
289
+ - mastra@1.20.0-alpha.18
290
+
291
+ ## 0.1.0-alpha.11
292
+
293
+ ### Patch Changes
294
+
295
+ - Updated dependencies [[`c9de100`](https://github.com/mastra-ai/mastra/commit/c9de10008ff3f72911bf746294004c305674d855), [`20b159d`](https://github.com/mastra-ai/mastra/commit/20b159d8f5d8153493ec8d5b6cd2864af8ce2c8e)]:
296
+ - mastra@1.20.0-alpha.17
297
+
298
+ ## 0.1.0-alpha.10
299
+
300
+ ### Patch Changes
301
+
302
+ - Changed the create-factory template sync to pin every Mastra dep to `"latest"` instead of `"alpha"`. Scaffolded projects now install the same set of Mastra packages as every other create-mastra template, and no longer ship a `.npmrc` with `legacy-peer-deps=true` (that flag only existed to accommodate the prerelease peer graph). ([#20052](https://github.com/mastra-ai/mastra/pull/20052))
303
+
304
+ - Updated dependencies [[`91930d6`](https://github.com/mastra-ai/mastra/commit/91930d69ae8146ded10c792387848970f1ca4b59)]:
305
+ - mastra@1.20.0-alpha.16
306
+
307
+ ## 0.1.0-alpha.9
308
+
309
+ ### Patch Changes
310
+
311
+ - Simplified the create-factory success message: sandboxes now appear as a bullet in the provisioned resources list so users know code agent sessions run inside Mastra platform sandboxes. ([#20062](https://github.com/mastra-ai/mastra/pull/20062))
312
+
313
+ ## 0.1.0-alpha.8
314
+
315
+ ### Patch Changes
316
+
317
+ - The factory template now ships a pnpm-workspace.yaml with allowBuilds, preventing pnpm v10+ from exiting with ERR_PNPM_IGNORED_BUILDS during install or build. The file mirrors the mastracode/web build-approval policy minus test-only deps stripped by the template. ([#20056](https://github.com/mastra-ai/mastra/pull/20056))
318
+
319
+ - Updated dependencies [[`0182be3`](https://github.com/mastra-ai/mastra/commit/0182be35cd402209182097098e7ebc07a5a54a2a)]:
320
+ - mastra@1.20.0-alpha.15
321
+
322
+ ## 0.1.0-alpha.7
323
+
324
+ ### Minor Changes
325
+
326
+ - Added EU and US region selection when creating Factory platform projects, with a --region flag for non-interactive setup. ([#20040](https://github.com/mastra-ai/mastra/pull/20040))
327
+
328
+ ### Patch Changes
329
+
330
+ - Updated the generated project README for the single-server setup: the Factory UI and API are both served from `http://localhost:4111`, OAuth callback instructions use the server origin, and the removed `dev:prod` / `build:ui` scripts are no longer documented. ([#20036](https://github.com/mastra-ai/mastra/pull/20036))
331
+
332
+ - Stop shipping `pnpm-workspace.yaml` and `package-lock.json` in projects scaffolded by `npm create factory`. The template generator now excludes the web project's pnpm workspace marker and lockfiles, and the sync workflow validates the template in a throwaway copy so `npm install` artifacts can no longer leak into the published template repository. ([#20041](https://github.com/mastra-ai/mastra/pull/20041))
333
+
334
+ ## 0.1.0-alpha.6
335
+
336
+ ### Patch Changes
337
+
338
+ - Improved the create-factory sign-in and success experience: ([#20024](https://github.com/mastra-ai/mastra/pull/20024))
339
+
340
+ - When no Mastra platform session exists, the CLI now pauses with "Mastra account is required, press enter to continue..." before opening the browser auth flow instead of opening it unannounced.
341
+ - The success message now summarizes the infrastructure provisioned on Mastra platform (project, Postgres database, credentials in .env), notes that deployed code agent sessions run inside Mastra platform sandboxes, and links to https://projects.mastra.ai for managing the project.
342
+
343
+ - Fixed generated Factory projects to serve the UI and API from a single Mastra development server. ([#20019](https://github.com/mastra-ai/mastra/pull/20019))
344
+
345
+ ## 0.1.0-alpha.5
346
+
347
+ ### Patch Changes
348
+
349
+ - Stopped writing MASTRA_SHARED_API_URL to the scaffolded project's .env during platform provisioning. Platform consumers now use their built-in default platform URL, so scaffolded factories no longer pin the API endpoint at create time. ([#20021](https://github.com/mastra-ai/mastra/pull/20021))
350
+
351
+ ## 0.1.0-alpha.4
352
+
353
+ ### Minor Changes
354
+
355
+ - Standardized the Vite SPA output directory to `src/mastra/public/factory/`. The template's `build` script delegates SPA building to `mastra build` (which calls `build:ui` automatically) instead of chaining it separately. ([#19948](https://github.com/mastra-ai/mastra/pull/19948))
356
+
357
+ ### Patch Changes
358
+
359
+ - Make the Software Factory template installable and buildable against published packages so the sync-softwarefactory-template workflow can push it again. Three changes to the generation step: ([#20001](https://github.com/mastra-ai/mastra/pull/20001))
360
+
361
+ - Pin every synced Mastra dep to `"alpha"` instead of `"latest"` — the Mastra Factory sources on `main` are built against the alpha release train, and the previous `"latest"` default mixed release trains (worse, `@mastra/factory@latest` is currently an empty stub).
362
+ - Emit `.npmrc` with `legacy-peer-deps=true` so npm accepts the internally-consistent prerelease peer graph (the same relaxation pnpm applies automatically inside the monorepo).
363
+ - Downgrade `typescript` from tsgo (v7) to the classic compiler (`^5.9.2`) in the emitted template. The sources happily typecheck under tsgo, but `mastra build` transitively loads TypeScript via `typescript-paths`, which needs the classic `ts.sys` API tsgo doesn't expose. In the monorepo pnpm hoists classic TypeScript from another workspace package, hiding the problem; the standalone template has no hoist.
364
+
365
+ All three are annotated as temporary in the script and README — remove once the packages ship stable releases and the deployer supports tsgo.
366
+
367
+ - Updated dependencies [[`0a50de7`](https://github.com/mastra-ai/mastra/commit/0a50de7024816171701e81ac3b69434cf5a302ea)]:
368
+ - mastra@1.20.0-alpha.14
369
+
370
+ ## 0.0.3-alpha.3
371
+
372
+ ### Patch Changes
373
+
374
+ - The Software Factory template now pins every Mastra dep to `"latest"` instead of a caret range anchored on the current monorepo version, matching how every other create-mastra template ships. `sync-template.mjs` no longer shells out to `npm view` and no longer needs the `--tag` flag or the `legacy-peer-deps=true` `.npmrc` (which only existed to work around prerelease pins). The sync workflow no longer breaks whenever a linked package sits mid-alpha between publishes. ([#19989](https://github.com/mastra-ai/mastra/pull/19989))
375
+
376
+ ## 0.0.3-alpha.2
377
+
378
+ ### Patch Changes
379
+
380
+ - Marked projects created by create-factory as factory-enabled on the Mastra platform. ([#19973](https://github.com/mastra-ai/mastra/pull/19973))
381
+
382
+ - Updated dependencies:
383
+ - mastra@1.20.0-alpha.13
384
+
385
+ ## 0.0.3-alpha.1
386
+
387
+ ### Patch Changes
388
+
389
+ - Apply prettier formatting to env.test.ts (post-merge follow-up) ([#19965](https://github.com/mastra-ai/mastra/pull/19965))
390
+
391
+ - Harden .env handling in create-factory: tighten file permissions and skip git init when .env cannot be gitignored ([#19945](https://github.com/mastra-ai/mastra/pull/19945))
392
+
393
+ `.env` files created during scaffolding are now written with `0600` permissions so platform secrets (`MASTRA_PLATFORM_SECRET_KEY`, `DATABASE_URL`) are only readable by the owner. If the scaffolder can't add `.env` to `.gitignore` (e.g. permission denied on `.gitignore`), it now skips `git init` entirely and warns the user, so freshly-minted secrets can't accidentally be committed to the initial commit.
394
+
395
+ - Fixed generated Software Factory projects missing their required `@mastra/memory` dependency. ([#19878](https://github.com/mastra-ai/mastra/pull/19878))
396
+
397
+ - Added platform sign-in, project creation, and Neon Postgres provisioning to the `create factory` CLI. After scaffolding, the CLI: ([#19945](https://github.com/mastra-ai/mastra/pull/19945))
398
+
399
+ - Signs the user in via the existing Mastra browser-auth flow.
400
+ - Creates a Mastra platform server project in the chosen organization.
401
+ - Mints an `sk_` organization API key scoped to the new factory.
402
+ - Attaches and provisions a Neon Postgres database.
403
+ - Writes `MASTRA_SHARED_API_URL`, `MASTRA_ORGANIZATION_ID`, `MASTRA_PROJECT_ID`, `MASTRA_PLATFORM_SECRET_KEY`, and `DATABASE_URL` to the project's `.env`.
404
+
405
+ The result is a locally-runnable factory that can talk to the Mastra platform on first `npm run dev` without any manual configuration.
406
+
407
+ **New flags:**
408
+
409
+ - `--no-platform` — skip the platform round-trip; useful when iterating on the template offline.
410
+ - `--region <region>` — pass a specific Neon region id through to the platform.
411
+
412
+ - Updated dependencies [[`8c88764`](https://github.com/mastra-ai/mastra/commit/8c88764162b32c8a24b3bf9d1ad2ec535aba5c9a), [`40ae860`](https://github.com/mastra-ai/mastra/commit/40ae860a674e7ee383a02c1f990d1e050619d5e4)]:
413
+ - mastra@1.20.0-alpha.12
414
+
415
+ ## 0.0.3-alpha.0
416
+
417
+ ### Patch Changes
418
+
419
+ - Added the `create-factory` CLI. It scaffolds a Mastra Software Factory project: enter a project name and the CLI clones the template, installs dependencies, and initializes git. Configuration (model providers, integrations, database) happens in the web UI on first load. ([#19609](https://github.com/mastra-ai/mastra/pull/19609))
420
+
421
+ ```bash
422
+ npm create factory my-factory
423
+ cd my-factory
424
+ npm run dev
425
+ ```
426
+
427
+ ## 0.0.2
428
+
429
+ ### Patch Changes
430
+
431
+ - First real scaffold release for `npm create factory`. Clones the softwarefactory template, installs dependencies, initializes git, and prints next steps. Configuration (model providers, database, integrations) happens in the web UI on first load.
432
+
433
+ ## 0.0.1
434
+
435
+ ### Patch Changes
436
+
437
+ - Initial public package name claim.
package/LICENSE.md ADDED
@@ -0,0 +1,30 @@
1
+ Portions of this software are licensed as follows:
2
+
3
+ - All content that resides under any directory named "ee/" within this
4
+ repository, including but not limited to:
5
+ - `packages/core/src/auth/ee/`
6
+ - `packages/server/src/server/auth/ee/`
7
+ is licensed under the license defined in `ee/LICENSE`.
8
+
9
+ - All third-party components incorporated into the Mastra Software are
10
+ licensed under the original license provided by the owner of the
11
+ applicable component.
12
+
13
+ - Content outside of the above-mentioned directories or restrictions is
14
+ available under the "Apache License 2.0" as defined below.
15
+
16
+ # Apache License 2.0
17
+
18
+ Copyright (c) 2025 Kepler Software, Inc.
19
+
20
+ Licensed under the Apache License, Version 2.0 (the "License");
21
+ you may not use this file except in compliance with the License.
22
+ You may obtain a copy of the License at
23
+
24
+ http://www.apache.org/licenses/LICENSE-2.0
25
+
26
+ Unless required by applicable law or agreed to in writing, software
27
+ distributed under the License is distributed on an "AS IS" BASIS,
28
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29
+ See the License for the specific language governing permissions and
30
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # `create-factory`
2
+
3
+ `create-factory` scaffolds a Mastra Factory project.
4
+
5
+ ```shell
6
+ npm create factory
7
+ ```
8
+
9
+ It clones the public [`softwarefactory-template`](https://github.com/mastra-ai/softwarefactory-template), installs dependencies, and initializes Git. Run `npm create factory -- --help` for options.
10
+
11
+ ## Development
12
+
13
+ This package owns prompts, template cloning, package-manager detection, optional Mastra platform setup, dependency installation, and Git initialization.
14
+
15
+ ```shell
16
+ pnpm --filter create-factory test
17
+ pnpm --filter create-factory check
18
+ pnpm --filter create-factory lint
19
+ pnpm --filter create-factory build
20
+ ```
21
+
22
+ Generated project behavior belongs to the separate template repository.
23
+
24
+ ## License
25
+
26
+ Apache-2.0
package/dist/index.js ADDED
@@ -0,0 +1,679 @@
1
+ #! /usr/bin/env node
2
+ import fs, { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { Command } from "commander";
6
+ import { randomUUID } from "node:crypto";
7
+ import os from "node:os";
8
+ import { PostHog } from "posthog-node";
9
+ import * as p from "@clack/prompts";
10
+ import { LoginCancelledError, MASTRA_PLATFORM_API_URL, authHeaders, extractApiErrorDetail, fetchOrgs, getToken, loadCredentials, platformFetch, resolveCurrentOrg } from "mastra/internal/auth";
11
+ import color from "picocolors";
12
+ import { x } from "tinyexec";
13
+ import fs$1 from "node:fs/promises";
14
+ //#region src/analytics.ts
15
+ /**
16
+ * Minimal PostHog analytics for create-factory, mirroring the
17
+ * create-mastra pattern (same project key, same opt-out env var, same
18
+ * ~/.mastra/analytics.json distinct-id store).
19
+ */
20
+ const ANALYTICS_CONFIG_PATH = path.join(os.homedir(), ".mastra", "analytics.json");
21
+ const POSTHOG_API_KEY = "phc_SBLpZVAB6jmHOct9CABq3PF0Yn5FU3G2FgT4xUr2XrT";
22
+ const POSTHOG_HOST = "https://us.posthog.com";
23
+ function isTelemetryEnabled() {
24
+ const value = process.env.MASTRA_TELEMETRY_DISABLED;
25
+ return !(value && value !== "0" && value.toLowerCase() !== "false");
26
+ }
27
+ function getOrCreateDistinctId() {
28
+ try {
29
+ if (existsSync(ANALYTICS_CONFIG_PATH)) {
30
+ const { distinctId } = JSON.parse(readFileSync(ANALYTICS_CONFIG_PATH, "utf-8"));
31
+ if (distinctId) return distinctId;
32
+ }
33
+ } catch {}
34
+ const distinctId = randomUUID();
35
+ try {
36
+ mkdirSync(path.dirname(ANALYTICS_CONFIG_PATH), { recursive: true });
37
+ writeFileSync(ANALYTICS_CONFIG_PATH, JSON.stringify({
38
+ distinctId,
39
+ sessionId: randomUUID()
40
+ }, null, 2));
41
+ } catch {}
42
+ return distinctId;
43
+ }
44
+ var Analytics = class {
45
+ version;
46
+ client;
47
+ distinctId = "";
48
+ constructor(version) {
49
+ this.version = version;
50
+ if (!isTelemetryEnabled()) return;
51
+ try {
52
+ this.distinctId = getOrCreateDistinctId();
53
+ this.client = new PostHog(POSTHOG_API_KEY, {
54
+ host: POSTHOG_HOST,
55
+ flushAt: 1,
56
+ flushInterval: 100
57
+ });
58
+ } catch {
59
+ this.client = void 0;
60
+ }
61
+ }
62
+ trackEvent(event, properties = {}) {
63
+ try {
64
+ this.client?.capture({
65
+ distinctId: this.distinctId,
66
+ event,
67
+ properties: {
68
+ ...properties,
69
+ cli: "create-factory",
70
+ version: this.version
71
+ }
72
+ });
73
+ } catch {}
74
+ }
75
+ async shutdown(timeoutMs = 1e3) {
76
+ if (!this.client) return;
77
+ try {
78
+ await Promise.race([this.client.shutdown(), new Promise((resolve) => setTimeout(resolve, timeoutMs))]);
79
+ } catch {}
80
+ }
81
+ };
82
+ //#endregion
83
+ //#region src/env.ts
84
+ /**
85
+ * Idempotently update a `.env` file.
86
+ *
87
+ * Replaces any existing `KEY=` line (commented or not) with `KEY=value`.
88
+ * Appends missing keys at the end of the file. Preserves other lines
89
+ * verbatim. Values are written raw — do not pass secrets that contain
90
+ * newlines or leading spaces (WorkOS `sk_` keys and Neon connection strings
91
+ * are safe).
92
+ */
93
+ function upsertEnvFile(envPath, updates) {
94
+ const existing = fs.existsSync(envPath) ? fs.readFileSync(envPath, "utf8") : "";
95
+ const lines = existing === "" ? [] : existing.split("\n");
96
+ const remaining = new Map(Object.entries(updates));
97
+ const patched = lines.map((line) => {
98
+ const match = /^(\s*#\s*)?([A-Z][A-Z0-9_]*)\s*=/.exec(line);
99
+ if (!match) return line;
100
+ const key = match[2];
101
+ if (!remaining.has(key)) return line;
102
+ const value = remaining.get(key);
103
+ remaining.delete(key);
104
+ return `${key}=${value}`;
105
+ });
106
+ if (remaining.size > 0) {
107
+ if (patched.length > 0 && patched[patched.length - 1] !== "") patched.push("");
108
+ for (const [key, value] of remaining) patched.push(`${key}=${value}`);
109
+ }
110
+ const output = patched.join("\n");
111
+ fs.writeFileSync(envPath, output, { mode: 384 });
112
+ try {
113
+ fs.chmodSync(envPath, 384);
114
+ } catch {}
115
+ }
116
+ //#endregion
117
+ //#region src/platform.ts
118
+ /**
119
+ * Common failure path: read `detail`/`message` out of the JSON body if the
120
+ * response wasn't JSON-parseable, fall back to the raw text.
121
+ */
122
+ async function extractError(res) {
123
+ const text = await res.text().catch(() => "");
124
+ if (!text) return `${res.status} ${res.statusText}`;
125
+ try {
126
+ const detail = extractApiErrorDetail(JSON.parse(text));
127
+ if (detail) return detail;
128
+ } catch {}
129
+ return text.slice(0, 500);
130
+ }
131
+ var PlatformApiError = class extends Error {
132
+ status;
133
+ constructor(status, message) {
134
+ super(message);
135
+ this.name = "PlatformApiError";
136
+ this.status = status;
137
+ }
138
+ };
139
+ /** POST /v1/server/projects — create a server project (Railway-provisioned). */
140
+ async function createServerProject({ token, orgId, name, region }) {
141
+ const res = await platformFetch(`${MASTRA_PLATFORM_API_URL}/v1/server/projects`, {
142
+ method: "POST",
143
+ headers: {
144
+ ...authHeaders(token, orgId),
145
+ "Content-Type": "application/json"
146
+ },
147
+ body: JSON.stringify({
148
+ name,
149
+ region,
150
+ factoryEnabled: true
151
+ })
152
+ });
153
+ if (!res.ok) throw new PlatformApiError(res.status, `Failed to create project — ${await extractError(res)}`);
154
+ return (await res.json()).project;
155
+ }
156
+ /**
157
+ * POST /v1/auth/tokens — mint an `sk_` WorkOS org API key.
158
+ * Returns the plaintext secret; the platform never returns it again.
159
+ */
160
+ async function mintOrgApiKey({ token, orgId, keyName }) {
161
+ const res = await platformFetch(`${MASTRA_PLATFORM_API_URL}/v1/auth/tokens`, {
162
+ method: "POST",
163
+ headers: {
164
+ ...authHeaders(token, orgId),
165
+ "Content-Type": "application/json"
166
+ },
167
+ body: JSON.stringify({ name: keyName })
168
+ });
169
+ if (!res.ok) throw new PlatformApiError(res.status, `Failed to create API key — ${await extractError(res)}`);
170
+ return (await res.json()).secret;
171
+ }
172
+ /**
173
+ * POST /v1/server/projects/:id/databases — attach a Neon Postgres database.
174
+ * Returns immediately with `status: 'provisioning'`; poll for `ready`.
175
+ *
176
+ * Note: this route requires `requireRole('admin')`. Non-admin org members
177
+ * will get a 403.
178
+ */
179
+ async function attachNeonDatabase({ token, orgId, projectId, name, regionId }) {
180
+ const res = await platformFetch(`${MASTRA_PLATFORM_API_URL}/v1/server/projects/${encodeURIComponent(projectId)}/databases`, {
181
+ method: "POST",
182
+ headers: {
183
+ ...authHeaders(token, orgId),
184
+ "Content-Type": "application/json"
185
+ },
186
+ body: JSON.stringify({
187
+ kind: "neon",
188
+ name,
189
+ regionId
190
+ })
191
+ });
192
+ if (!res.ok) {
193
+ if (res.status === 403) throw new PlatformApiError(403, `Attaching a database requires the admin role in your organization. Ask an org admin to run \`create-factory\`, or attach a database from the dashboard.`);
194
+ throw new PlatformApiError(res.status, `Failed to attach Neon database — ${await extractError(res)}`);
195
+ }
196
+ return (await res.json()).database;
197
+ }
198
+ /** GET /v1/server/projects/:id/databases/:dbId — status poll target. */
199
+ async function getDatabaseStatus({ token, orgId, projectId, databaseId, signal }) {
200
+ const res = await platformFetch(`${MASTRA_PLATFORM_API_URL}/v1/server/projects/${encodeURIComponent(projectId)}/databases/${encodeURIComponent(databaseId)}`, {
201
+ headers: authHeaders(token, orgId),
202
+ signal
203
+ });
204
+ if (!res.ok) throw new PlatformApiError(res.status, `Failed to read database status — ${await extractError(res)}`);
205
+ return (await res.json()).database;
206
+ }
207
+ /** GET /v1/server/projects/:id/databases/:dbId/connection — only 200s when `ready`. */
208
+ async function getDatabaseConnection({ token, orgId, projectId, databaseId }) {
209
+ const res = await platformFetch(`${MASTRA_PLATFORM_API_URL}/v1/server/projects/${encodeURIComponent(projectId)}/databases/${encodeURIComponent(databaseId)}/connection`, { headers: authHeaders(token, orgId) });
210
+ if (!res.ok) throw new PlatformApiError(res.status, `Failed to fetch connection string — ${await extractError(res)}`);
211
+ return await res.json();
212
+ }
213
+ /**
214
+ * Poll `getDatabaseStatus` until the database is `ready`.
215
+ *
216
+ * Each poll is bounded by the *remaining* overall budget via a per-request
217
+ * `AbortSignal`, so a hung `platformFetch` can't blow past `timeoutMs`. When
218
+ * an outer `signal` is supplied it composes with the per-request timeout —
219
+ * whichever fires first aborts the in-flight request.
220
+ *
221
+ * @param intervalMs how often to poll (default 2s)
222
+ * @param timeoutMs total budget (default 60s)
223
+ */
224
+ async function waitForDatabaseReady({ token, orgId, projectId, databaseId, intervalMs = 2e3, timeoutMs = 6e4, signal }) {
225
+ const deadline = Date.now() + timeoutMs;
226
+ let lastStatus = "provisioning";
227
+ const timeoutError = () => new PlatformApiError(504, `Neon database is still ${lastStatus} after ${Math.round(timeoutMs / 1e3)}s. Check the dashboard or retry \`mastra login\` + re-run.`);
228
+ while (true) {
229
+ signal?.throwIfAborted();
230
+ const remaining = deadline - Date.now();
231
+ if (remaining <= 0) throw timeoutError();
232
+ const perRequestSignal = composeSignals(signal, AbortSignal.timeout(remaining));
233
+ let row;
234
+ try {
235
+ row = await getDatabaseStatus({
236
+ token,
237
+ orgId,
238
+ projectId,
239
+ databaseId,
240
+ signal: perRequestSignal
241
+ });
242
+ } catch (err) {
243
+ if (err instanceof DOMException && err.name === "TimeoutError") throw timeoutError();
244
+ throw err;
245
+ }
246
+ lastStatus = row.status;
247
+ if (row.status === "ready") return row;
248
+ if (row.status === "failed") throw new PlatformApiError(500, `Neon provisioning failed${row.error ? ` — ${row.error}` : ""}`);
249
+ if (Date.now() >= deadline) throw timeoutError();
250
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
251
+ }
252
+ }
253
+ /**
254
+ * Compose an outer AbortSignal with a per-request timeout signal. Uses
255
+ * `AbortSignal.any` where available (Node ≥20.3); falls back to a manual
256
+ * proxy for older runtimes.
257
+ */
258
+ function composeSignals(outer, inner) {
259
+ if (!outer) return inner;
260
+ if (typeof AbortSignal.any === "function") return AbortSignal.any([outer, inner]);
261
+ const controller = new AbortController();
262
+ const abort = (reason) => controller.abort(reason);
263
+ if (outer.aborted) abort(outer.reason);
264
+ else outer.addEventListener("abort", () => abort(outer.reason), { once: true });
265
+ if (inner.aborted) abort(inner.reason);
266
+ else inner.addEventListener("abort", () => abort(inner.reason), { once: true });
267
+ return controller.signal;
268
+ }
269
+ //#endregion
270
+ //#region src/utils/clone.ts
271
+ /** Rewrite the scaffolded package.json name to the chosen project name. */
272
+ async function renameProject(projectPath, projectName) {
273
+ const pkgPath = path.join(projectPath, "package.json");
274
+ const pkg = JSON.parse(await fs$1.readFile(pkgPath, "utf8"));
275
+ pkg.name = toPackageName(projectName);
276
+ await fs$1.writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
277
+ }
278
+ /** Best-effort conversion of a directory name into a valid npm package name. */
279
+ function toPackageName(name) {
280
+ return name.toLowerCase().replace(/[^a-z0-9-_.~]+/g, "-").replace(/^[-_.]+|[-_.]+$/g, "") || "mastra-factory";
281
+ }
282
+ async function pathExists(filePath) {
283
+ try {
284
+ await fs$1.access(filePath);
285
+ return true;
286
+ } catch (error) {
287
+ if (error.code === "ENOENT") return false;
288
+ throw error;
289
+ }
290
+ }
291
+ async function directoryExists(dirPath) {
292
+ try {
293
+ return (await fs$1.stat(dirPath)).isDirectory();
294
+ } catch {
295
+ return false;
296
+ }
297
+ }
298
+ async function cloneTemplate(repoUrl, targetPath) {
299
+ if (await pathExists(targetPath)) throw new Error(`Directory ${path.basename(targetPath)} already exists`);
300
+ const tempRoot = await fs$1.mkdtemp(path.join(path.dirname(targetPath), `.${path.basename(targetPath)}-`));
301
+ const tempTarget = path.join(tempRoot, "template");
302
+ try {
303
+ try {
304
+ await x("npx", [
305
+ "degit",
306
+ repoUrl.replace("https://github.com/", ""),
307
+ tempTarget
308
+ ], {
309
+ throwOnError: true,
310
+ nodeOptions: { cwd: process.cwd() }
311
+ });
312
+ if ((await fs$1.readdir(tempTarget)).length === 0) throw new Error("degit completed without cloning template files");
313
+ } catch {
314
+ await fs$1.rm(tempTarget, {
315
+ recursive: true,
316
+ force: true
317
+ });
318
+ try {
319
+ await x("git", [
320
+ "clone",
321
+ repoUrl,
322
+ tempTarget
323
+ ], {
324
+ throwOnError: true,
325
+ nodeOptions: { cwd: process.cwd() }
326
+ });
327
+ const gitDir = path.join(tempTarget, ".git");
328
+ if (await directoryExists(gitDir)) await fs$1.rm(gitDir, {
329
+ recursive: true,
330
+ force: true
331
+ });
332
+ } catch (gitError) {
333
+ throw new Error(`Failed to clone repository: ${gitError instanceof Error ? gitError.message : "Unknown error"}`);
334
+ }
335
+ }
336
+ if (await pathExists(targetPath)) throw new Error(`Directory ${path.basename(targetPath)} already exists`);
337
+ await fs$1.rename(tempTarget, targetPath);
338
+ } finally {
339
+ await fs$1.rm(tempRoot, {
340
+ recursive: true,
341
+ force: true
342
+ });
343
+ }
344
+ }
345
+ //#endregion
346
+ //#region src/utils/pm.ts
347
+ function detectPackageManager() {
348
+ const userAgent = process.env.npm_config_user_agent || "";
349
+ const execPath = process.env.npm_execpath || "";
350
+ if (userAgent.includes("bun")) return "bun";
351
+ if (userAgent.includes("yarn")) return "yarn";
352
+ if (userAgent.includes("pnpm")) return "pnpm";
353
+ if (userAgent.includes("npm")) return "npm";
354
+ if (execPath.includes("bun")) return "bun";
355
+ if (execPath.includes("yarn")) return "yarn";
356
+ if (execPath.includes("pnpm")) return "pnpm";
357
+ if (execPath.includes("npm")) return "npm";
358
+ return "npm";
359
+ }
360
+ function getInstallArgs(packageManager) {
361
+ switch (packageManager) {
362
+ case "npm": return [
363
+ "install",
364
+ "--no-audit",
365
+ "--no-fund"
366
+ ];
367
+ default: return ["install"];
368
+ }
369
+ }
370
+ //#endregion
371
+ //#region src/create.ts
372
+ const PROJECT_REGION_OPTIONS = [{
373
+ value: "eu",
374
+ label: "🇪🇺 eu"
375
+ }, {
376
+ value: "us",
377
+ label: "🇺🇸 us"
378
+ }];
379
+ const NEON_REGION_BY_PROJECT_REGION = {
380
+ eu: "aws-eu-central-1",
381
+ us: "aws-us-west-2"
382
+ };
383
+ function parseProjectRegion(region) {
384
+ if (region === "eu" || region === "us") return region;
385
+ throw new Error(`Invalid --region "${region}". Expected one of: eu, us.`);
386
+ }
387
+ async function create(args) {
388
+ p.intro(color.inverse(" Mastra Factory "));
389
+ const requestedRegion = args.region ? parseProjectRegion(args.region) : void 0;
390
+ const projectName = args.projectName ?? await p.text({
391
+ message: "What do you want to name your project?",
392
+ placeholder: "my-mastra-factory",
393
+ validate: (value) => {
394
+ if (!value?.trim()) return `Project name can't be empty`;
395
+ if (fs.existsSync(path.resolve(value.trim()))) return `Directory ${value.trim()} already exists`;
396
+ }
397
+ });
398
+ if (p.isCancel(projectName)) {
399
+ p.cancel("Operation cancelled");
400
+ process.exit(0);
401
+ }
402
+ const projectPath = path.resolve(projectName);
403
+ const packageManager = detectPackageManager();
404
+ args.analytics.trackEvent("sf_create_started", {
405
+ package_manager: packageManager,
406
+ no_platform: Boolean(args.noPlatform)
407
+ });
408
+ const spinner = p.spinner();
409
+ spinner.start("Downloading the Mastra Factory template...");
410
+ try {
411
+ await cloneTemplate(args.template, projectPath);
412
+ await renameProject(projectPath, projectName);
413
+ const envPath = path.join(projectPath, ".env");
414
+ fs.copyFileSync(path.join(projectPath, ".env.example"), envPath);
415
+ try {
416
+ fs.chmodSync(envPath, 384);
417
+ } catch {}
418
+ spinner.stop("Template downloaded.");
419
+ } catch (err) {
420
+ spinner.stop("Template download failed.");
421
+ throw err;
422
+ }
423
+ const installSpinner = p.spinner();
424
+ installSpinner.start(`Installing dependencies...`);
425
+ try {
426
+ await x(packageManager, getInstallArgs(packageManager), {
427
+ throwOnError: true,
428
+ nodeOptions: { cwd: projectPath }
429
+ });
430
+ installSpinner.stop("Dependencies installed.");
431
+ } catch (err) {
432
+ installSpinner.stop("Dependency install failed.");
433
+ throw new Error(`${err instanceof Error ? err.message : String(err)}\nYou can retry manually: cd ${projectName} && ${packageManager} install`);
434
+ }
435
+ let platformResult = null;
436
+ let platformError = null;
437
+ let platformSkipped = false;
438
+ if (!args.noPlatform) try {
439
+ platformResult = await runPlatformProvisioning({
440
+ projectName,
441
+ projectPath,
442
+ region: requestedRegion,
443
+ org: args.org
444
+ });
445
+ } catch (err) {
446
+ if (err instanceof LoginCancelledError) {
447
+ platformSkipped = true;
448
+ p.log.info("Skipping Mastra platform setup.");
449
+ } else {
450
+ platformError = err instanceof Error ? err.message : String(err);
451
+ p.log.warn(`Platform provisioning failed: ${platformError}`);
452
+ }
453
+ }
454
+ let gitignoreOk = true;
455
+ try {
456
+ ensureEnvGitignored(projectPath);
457
+ } catch (err) {
458
+ gitignoreOk = false;
459
+ const detail = err instanceof Error ? err.message : String(err);
460
+ p.log.warn(`Could not update .gitignore to protect .env (${detail}). Skipping git init to avoid committing secrets. Add ".env" to .gitignore manually before running 'git init && git add -A'.`);
461
+ }
462
+ if (gitignoreOk) try {
463
+ await x("git", ["init", "-q"], {
464
+ throwOnError: true,
465
+ nodeOptions: { cwd: projectPath }
466
+ });
467
+ await x("git", ["add", "-A"], {
468
+ throwOnError: true,
469
+ nodeOptions: { cwd: projectPath }
470
+ });
471
+ await x("git", [
472
+ "commit",
473
+ "-q",
474
+ "-m",
475
+ "Initial commit from create-factory"
476
+ ], {
477
+ throwOnError: true,
478
+ nodeOptions: { cwd: projectPath }
479
+ });
480
+ } catch {
481
+ p.log.warn("git init failed — you can initialize the repository yourself later.");
482
+ }
483
+ args.analytics.trackEvent("sf_create_completed", {
484
+ package_manager: packageManager,
485
+ no_platform: Boolean(args.noPlatform),
486
+ platform_provisioned: platformResult !== null
487
+ });
488
+ const lines = [
489
+ color.green("Your Mastra Factory is ready!"),
490
+ "",
491
+ `${color.cyan("cd")} ${projectName}`,
492
+ color.cyan(`${packageManager} run dev`),
493
+ "",
494
+ `Factory UI ${color.underline("http://localhost:4111")}`,
495
+ ""
496
+ ];
497
+ if (platformResult) lines.push(`${color.green("Provisioned on Mastra platform")} in ${color.cyan(platformResult.orgName)}:`, ` - Project ${color.cyan(platformResult.project.name)}`, ` - Postgres database (credentials written to ${color.cyan(".env")})`, ` - Sandboxes (code agent sessions run here)`, "", `Manage your project at ${color.underline("https://projects.mastra.ai")}`);
498
+ else if (platformSkipped) lines.push(`${color.yellow("Skipped Mastra platform setup.")} Configure ${color.cyan(".env")} manually before running.`);
499
+ else if (args.noPlatform) lines.push(`${color.yellow("Skipped platform provisioning (--no-platform).")} Configure ${color.cyan(".env")} manually before running.`);
500
+ else if (platformError) lines.push(`${color.yellow("Platform provisioning failed:")} ${platformError}`, `Any credentials that were minted before the failure have been written to ${color.cyan(".env")}. Re-run \`npx create factory\` after fixing, or fill in the rest manually from ${color.underline("https://platform.mastra.ai")}.`);
501
+ else lines.push("Open the Factory UI to finish setup (models, integrations, database).");
502
+ p.note(lines.join("\n"), "Next steps");
503
+ }
504
+ async function runPlatformProvisioning({ projectName, projectPath, region, org }) {
505
+ const envAccumulator = {};
506
+ const envPath = path.join(projectPath, ".env");
507
+ let flushed = false;
508
+ const flush = () => {
509
+ if (flushed || Object.keys(envAccumulator).length === 0) return;
510
+ upsertEnvFile(envPath, envAccumulator);
511
+ flushed = true;
512
+ };
513
+ try {
514
+ if (!process.env.MASTRA_API_TOKEN && !await loadCredentials()) {
515
+ const proceed = await p.text({
516
+ message: "Mastra account is required, press enter to continue...",
517
+ defaultValue: ""
518
+ });
519
+ if (p.isCancel(proceed)) {
520
+ p.cancel("Operation cancelled");
521
+ process.exit(0);
522
+ }
523
+ }
524
+ p.log.info("Signing in to Mastra…");
525
+ const token = await getToken(void 0, { skipOnInput: true });
526
+ const { orgId, orgName } = org ? await resolveOrgFromFlag(token, org) : await resolveCurrentOrg(token, { forcePrompt: true });
527
+ p.log.info(`Using organization ${color.cyan(orgName)}.`);
528
+ envAccumulator.MASTRA_ORGANIZATION_ID = orgId;
529
+ const projectRegion = region ?? await p.select({
530
+ message: "Where should your Factory project run?",
531
+ options: [...PROJECT_REGION_OPTIONS]
532
+ });
533
+ if (p.isCancel(projectRegion)) {
534
+ p.cancel("Operation cancelled");
535
+ process.exit(0);
536
+ }
537
+ const projectSpinner = p.spinner();
538
+ projectSpinner.start(`Creating platform project "${projectName}"…`);
539
+ let project;
540
+ try {
541
+ project = await createServerProject({
542
+ token,
543
+ orgId,
544
+ name: projectName,
545
+ region: projectRegion
546
+ });
547
+ projectSpinner.stop(`Created platform project ${color.cyan(project.slug)}.`);
548
+ } catch (err) {
549
+ projectSpinner.stop("Project creation failed.");
550
+ throw err;
551
+ }
552
+ envAccumulator.MASTRA_PROJECT_ID = project.id;
553
+ const keySpinner = p.spinner();
554
+ keySpinner.start("Creating platform API key…");
555
+ let secretKey;
556
+ try {
557
+ secretKey = await mintOrgApiKey({
558
+ token,
559
+ orgId,
560
+ keyName: `create-factory: ${projectName}`
561
+ });
562
+ keySpinner.stop("Platform API key created.");
563
+ } catch (err) {
564
+ keySpinner.stop("API key creation failed.");
565
+ throw err;
566
+ }
567
+ envAccumulator.MASTRA_PLATFORM_SECRET_KEY = secretKey;
568
+ const neonSpinner = p.spinner();
569
+ neonSpinner.start("Provisioning Neon Postgres database…");
570
+ let databaseUrl;
571
+ try {
572
+ const attached = await attachNeonDatabase({
573
+ token,
574
+ orgId,
575
+ projectId: project.id,
576
+ name: sanitizeDatabaseName(projectName),
577
+ regionId: NEON_REGION_BY_PROJECT_REGION[projectRegion]
578
+ });
579
+ const ready = await waitForDatabaseReady({
580
+ token,
581
+ orgId,
582
+ projectId: project.id,
583
+ databaseId: attached.id
584
+ });
585
+ const dbEnv = (await getDatabaseConnection({
586
+ token,
587
+ orgId,
588
+ projectId: project.id,
589
+ databaseId: ready.id
590
+ })).envVars.find((v) => v.name === "DATABASE_URL");
591
+ if (!dbEnv?.value) throw new PlatformApiError(500, "Platform connection response missing DATABASE_URL.");
592
+ databaseUrl = dbEnv.value;
593
+ neonSpinner.stop("Neon database ready.");
594
+ } catch (err) {
595
+ neonSpinner.stop("Database provisioning failed.");
596
+ throw err;
597
+ }
598
+ envAccumulator.DATABASE_URL = databaseUrl;
599
+ flush();
600
+ return {
601
+ orgId,
602
+ orgName,
603
+ project,
604
+ secretKey,
605
+ databaseUrl
606
+ };
607
+ } finally {
608
+ flush();
609
+ }
610
+ }
611
+ /**
612
+ * `--org <value>` matches by org id or exact name. Not a substring match —
613
+ * an ambiguous or non-existent value bails with a clear message instead of
614
+ * silently picking the wrong org.
615
+ */
616
+ async function resolveOrgFromFlag(token, value) {
617
+ const orgs = await fetchOrgs(token);
618
+ const match = orgs.find((o) => o.id === value || o.name === value);
619
+ if (!match) {
620
+ const available = orgs.map((o) => `${o.name} (${o.id})`).join(", ") || "(none)";
621
+ throw new Error(`No organization matched --org "${value}". Available: ${available}.`);
622
+ }
623
+ return {
624
+ orgId: match.id,
625
+ orgName: match.name
626
+ };
627
+ }
628
+ /**
629
+ * Neon display-name charset: `[a-zA-Z0-9_-]+`, up to 64 chars. Drop anything
630
+ * outside that, and clip length.
631
+ */
632
+ function sanitizeDatabaseName(projectName) {
633
+ return (projectName.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/^-+|-+$/g, "") || "factory").slice(0, 64);
634
+ }
635
+ /**
636
+ * Append `.env` to the scaffolded project's `.gitignore` if it isn't already
637
+ * ignored. Runs before the initial `git add -A` so freshly-provisioned platform
638
+ * credentials (MASTRA_PLATFORM_SECRET_KEY, DATABASE_URL) never reach the
639
+ * initial commit.
640
+ *
641
+ * Throws if `.gitignore` cannot be written. Callers MUST treat this as fatal
642
+ * for the git-init step when `.env` may already contain platform secrets —
643
+ * silently continuing would stage secrets into the initial commit.
644
+ */
645
+ function ensureEnvGitignored(projectPath) {
646
+ const gitignorePath = path.join(projectPath, ".gitignore");
647
+ const existing = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, "utf8") : "";
648
+ if (existing.split(/\r?\n/).map((line) => line.trim()).some((line) => {
649
+ if (!line || line.startsWith("#")) return false;
650
+ return line === ".env" || line === ".env*" || line === "/.env" || line === "/.env*";
651
+ })) return;
652
+ const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
653
+ fs.writeFileSync(gitignorePath, `${existing}${prefix}\n# Added by create-factory to protect platform credentials\n.env\n`);
654
+ }
655
+ //#endregion
656
+ //#region src/index.ts
657
+ const pkg = JSON.parse(readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8"));
658
+ const analytics = new Analytics(pkg.version);
659
+ const program = new Command();
660
+ 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)", "https://github.com/mastra-ai/softwarefactory-template").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) => {
661
+ await create({
662
+ projectName: projectNameArg,
663
+ template: args.template,
664
+ noPlatform: args.platform === false,
665
+ org: args.org ? String(args.org) : void 0,
666
+ region: args.region ? String(args.region) : void 0,
667
+ analytics
668
+ });
669
+ });
670
+ try {
671
+ await program.parseAsync(process.argv);
672
+ } catch (err) {
673
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
674
+ process.exitCode = 1;
675
+ } finally {
676
+ await analytics.shutdown(1e3);
677
+ }
678
+ //#endregion
679
+ export {};
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "create-factory",
3
+ "version": "0.0.0-fix-thread-subscription-multi-instance-redis-streams-20260730204150",
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
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "bin": {
8
+ "create-factory": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "CHANGELOG.md"
13
+ ],
14
+ "engines": {
15
+ "node": ">=22.13.0"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/mastra-ai/mastra.git",
20
+ "directory": "mastracode/mastra-factory"
21
+ },
22
+ "homepage": "https://mastra.ai",
23
+ "bugs": {
24
+ "url": "https://github.com/mastra-ai/mastra/issues"
25
+ },
26
+ "keywords": [
27
+ "mastra",
28
+ "software-factory",
29
+ "create",
30
+ "scaffold",
31
+ "cli",
32
+ "agents",
33
+ "ai"
34
+ ],
35
+ "dependencies": {
36
+ "@clack/prompts": "^1.7.0",
37
+ "commander": "^14.0.3",
38
+ "picocolors": "^1.1.1",
39
+ "posthog-node": "^5.37.0",
40
+ "tinyexec": "^1.2.4",
41
+ "mastra": "0.0.0-fix-thread-subscription-multi-instance-redis-streams-20260730204150"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "22.20.1",
45
+ "eslint": "^10.4.1",
46
+ "tsdown": "0.22.9",
47
+ "typescript": "^6.0.3",
48
+ "vitest": "4.1.10",
49
+ "@internal/lint": "0.0.0-fix-thread-subscription-multi-instance-redis-streams-20260730204150"
50
+ },
51
+ "scripts": {
52
+ "build": "tsdown --silent --config tsdown.config.ts",
53
+ "check": "tsc --noEmit",
54
+ "test": "vitest run",
55
+ "lint": "oxlint . && eslint .",
56
+ "lint:fix": "oxlint --fix . && eslint --fix ."
57
+ }
58
+ }