create-factory 0.1.6 → 0.1.7-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # create-factory
2
2
 
3
+ ## 0.1.7-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Use the same PostHog analytics as create-mastra in the create-factory CLI, including the MASTRA_TELEMETRY_DISABLED opt-out and shared anonymous distinct id. ([#20073](https://github.com/mastra-ai/mastra/pull/20073))
8
+
9
+ - Updated dependencies [[`ed5d606`](https://github.com/mastra-ai/mastra/commit/ed5d606739c5e3fbdfa9f272df7809aa5ab43b1d)]:
10
+ - mastra@1.23.1-alpha.0
11
+
3
12
  ## 0.1.6
4
13
 
5
14
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1,85 +1,14 @@
1
1
  #! /usr/bin/env node
2
- import fs, { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import fs, { readFileSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { Command } from "commander";
6
- import { randomUUID } from "node:crypto";
7
- import os from "node:os";
8
- import { PostHog } from "posthog-node";
6
+ import { PosthogAnalytics, setAnalytics } from "mastra/dist/analytics/index.js";
9
7
  import * as p from "@clack/prompts";
10
8
  import { LoginCancelledError, MASTRA_PLATFORM_API_URL, authHeaders, extractApiErrorDetail, fetchOrgs, getToken, loadCredentials, platformFetch, resolveCurrentOrg } from "mastra/internal/auth";
11
9
  import color from "picocolors";
12
10
  import { x } from "tinyexec";
13
11
  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
12
  //#region src/env.ts
84
13
  /**
85
14
  * Idempotently update a `.env` file.
@@ -653,19 +582,101 @@ function ensureEnvGitignored(projectPath) {
653
582
  fs.writeFileSync(gitignorePath, `${existing}${prefix}\n# Added by create-factory to protect platform credentials\n.env\n`);
654
583
  }
655
584
  //#endregion
585
+ //#region src/utils/redact.ts
586
+ /**
587
+ * Sanitizes error messages before they are sent to analytics.
588
+ *
589
+ * Failure telemetry reports `error.message`, and several failure paths embed
590
+ * user-supplied input in their messages: a clone failure includes the full
591
+ * `git clone <url>` command (a custom template URL can identify a private repo
592
+ * and may carry embedded credentials), and `--region`/`--org` validation
593
+ * errors echo the raw flag value back. The user still sees the original
594
+ * message on stderr — only the analytics copy is redacted.
595
+ */
596
+ const REDACTED = "[redacted]";
597
+ /** Strips `user:password@` credentials from any URL in the message. */
598
+ function stripUrlCredentials(message) {
599
+ return message.replace(/\/\/[^\s/@]+@/g, `//${REDACTED}@`);
600
+ }
601
+ /**
602
+ * A value can surface in messages in derived forms: without embedded
603
+ * credentials (git may omit them when echoing the URL) or with the
604
+ * `https://github.com/` prefix stripped (degit's repo shorthand).
605
+ */
606
+ function variantsOf(value) {
607
+ const variants = /* @__PURE__ */ new Set([value, value.replace(/\/\/[^\s/@]+@/g, "//")]);
608
+ for (const variant of [...variants]) if (variant.startsWith("https://github.com/")) variants.add(variant.slice(19));
609
+ return [...variants];
610
+ }
611
+ /**
612
+ * Redacts every literal occurrence (and derived variant) of the provided
613
+ * user-supplied values, any URL credentials, and the org list echoed by
614
+ * `--org` mismatch errors.
615
+ */
616
+ function redactErrorMessage(message, sensitiveValues = []) {
617
+ let redacted = message;
618
+ for (const value of sensitiveValues) {
619
+ if (!value) continue;
620
+ for (const variant of variantsOf(value)) redacted = redacted.split(variant).join(REDACTED);
621
+ }
622
+ redacted = stripUrlCredentials(redacted);
623
+ redacted = redacted.replace(/Available: .*$/s, `Available: ${REDACTED}.`);
624
+ return redacted;
625
+ }
626
+ /** Returns a copy of `error` safe to report to analytics. */
627
+ function redactError(error, sensitiveValues = []) {
628
+ const message = error instanceof Error ? error.message : String(error);
629
+ return new Error(redactErrorMessage(message, sensitiveValues));
630
+ }
631
+ //#endregion
656
632
  //#region src/index.ts
657
633
  const pkg = JSON.parse(readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8"));
658
- const analytics = new Analytics(pkg.version);
634
+ const analytics = new PosthogAnalytics({
635
+ apiKey: "phc_SBLpZVAB6jmHOct9CABq3PF0Yn5FU3G2FgT4xUr2XrT",
636
+ host: "https://us.posthog.com",
637
+ version: pkg.version
638
+ });
639
+ setAnalytics(analytics);
659
640
  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
- });
641
+ const DEFAULT_TEMPLATE_REPO = "https://github.com/mastra-ai/softwarefactory-template";
642
+ 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) => {
643
+ const region = args.region ? String(args.region) : void 0;
644
+ const validRegion = region === "eu" || region === "us" ? region : void 0;
645
+ const sensitiveValues = [
646
+ args.template === DEFAULT_TEMPLATE_REPO ? void 0 : args.template,
647
+ args.org ? String(args.org) : void 0,
648
+ validRegion ? void 0 : region,
649
+ projectNameArg
650
+ ];
651
+ let rawError;
652
+ try {
653
+ await analytics.trackCommandExecution({
654
+ command: "create-factory",
655
+ args: {
656
+ default_template: args.template === DEFAULT_TEMPLATE_REPO,
657
+ no_platform: args.platform === false,
658
+ has_org: Boolean(args.org),
659
+ region: validRegion ?? (region ? "invalid" : void 0)
660
+ },
661
+ execution: async () => {
662
+ try {
663
+ await create({
664
+ projectName: projectNameArg,
665
+ template: args.template,
666
+ noPlatform: args.platform === false,
667
+ org: args.org ? String(args.org) : void 0,
668
+ region,
669
+ analytics
670
+ });
671
+ } catch (err) {
672
+ rawError = err;
673
+ throw redactError(err, sensitiveValues);
674
+ }
675
+ }
676
+ });
677
+ } catch (err) {
678
+ throw rawError ?? err;
679
+ }
669
680
  });
670
681
  try {
671
682
  await program.parseAsync(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-factory",
3
- "version": "0.1.6",
3
+ "version": "0.1.7-alpha.0",
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",
@@ -36,9 +36,8 @@
36
36
  "@clack/prompts": "^1.7.0",
37
37
  "commander": "^14.0.3",
38
38
  "picocolors": "^1.1.1",
39
- "posthog-node": "^5.37.0",
40
39
  "tinyexec": "^1.2.4",
41
- "mastra": "1.23.0"
40
+ "mastra": "1.23.1-alpha.0"
42
41
  },
43
42
  "devDependencies": {
44
43
  "@types/node": "22.20.1",