@insforge/cli 0.1.80 → 0.1.81

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 CHANGED
@@ -674,17 +674,19 @@ Skill files are written to per-agent directories (e.g. `.claude/`, `.cursor/`, `
674
674
 
675
675
  The CLI reports anonymous usage events to [PostHog](https://posthog.com) so we can understand which features are being used and prioritize improvements.
676
676
 
677
- Analytics are enabled by default in the published npm package. If you build the CLI from source without setting `POSTHOG_API_KEY` at build time, analytics become a no-op automatically.
677
+ We capture only non-sensitive metadata: the command name, subcommand, outcome (`success`, `applied`, `aborted`, `dry_run`, `no_changes`, `all_skipped`, `error`), flag shape (e.g. `dry_run`, `json_mode`), section names from `insforge.toml` schema (e.g. `auth.smtp`), region, and an OSS-vs-cloud flag. We never send SQL, TOML file contents, credentials, environment variable values, or any free text you type.
678
+
679
+ If you build the CLI from source without setting `POSTHOG_API_KEY` at build time, analytics become a no-op automatically.
678
680
 
679
681
  ## Environment Variables
680
682
 
681
- | Variable | Description |
682
- | ----------------------- | ---------------------------------- |
683
- | `INSFORGE_ACCESS_TOKEN` | Override the stored access token |
684
- | `INSFORGE_PROJECT_ID` | Override the linked project ID |
685
- | `INSFORGE_API_URL` | Override the Platform API URL |
686
- | `INSFORGE_EMAIL` | Email for non-interactive login |
687
- | `INSFORGE_PASSWORD` | Password for non-interactive login |
683
+ | Variable | Description |
684
+ | ----------------------- | --------------------------------------------------------------- |
685
+ | `INSFORGE_ACCESS_TOKEN` | Override the stored access token |
686
+ | `INSFORGE_PROJECT_ID` | Override the linked project ID |
687
+ | `INSFORGE_API_URL` | Override the Platform API URL |
688
+ | `INSFORGE_EMAIL` | Email for non-interactive login |
689
+ | `INSFORGE_PASSWORD` | Password for non-interactive login |
688
690
 
689
691
  ## Non-Interactive / CI Usage
690
692
 
package/dist/index.js CHANGED
@@ -1215,9 +1215,24 @@ function trackPayments(subcommand, config, properties) {
1215
1215
  ...properties
1216
1216
  });
1217
1217
  }
1218
+ function trackConfig(subcommand, config, properties) {
1219
+ const distinctId = config?.project_id ?? FAKE_PROJECT_ID;
1220
+ captureEvent(distinctId, "cli_config_invoked", {
1221
+ subcommand,
1222
+ project_id: config?.project_id,
1223
+ project_name: config?.project_name,
1224
+ org_id: config?.org_id,
1225
+ region: config?.region,
1226
+ oss_mode: !config || config.project_id === FAKE_PROJECT_ID,
1227
+ ...properties
1228
+ });
1229
+ }
1218
1230
  async function shutdownAnalytics() {
1231
+ if (!client) return;
1232
+ const c = client;
1233
+ client = null;
1219
1234
  try {
1220
- if (client) await client.shutdown();
1235
+ await c.shutdown();
1221
1236
  } catch {
1222
1237
  }
1223
1238
  }
@@ -6961,7 +6976,7 @@ function registerDiagnoseCommands(diagnoseCmd2) {
6961
6976
  const s = !json ? clack15.spinner() : null;
6962
6977
  s?.start("Collecting diagnostic data...");
6963
6978
  const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
6964
- const cliVersion = "0.1.80";
6979
+ const cliVersion = "0.1.81";
6965
6980
  s?.stop("Data collected");
6966
6981
  if (!json) {
6967
6982
  console.log(`
@@ -8325,7 +8340,6 @@ function registerPaymentsCommands(paymentsCmd2) {
8325
8340
  }
8326
8341
 
8327
8342
  // src/commands/posthog/setup.ts
8328
- import { spawnSync as spawnSync2 } from "child_process";
8329
8343
  import * as clack16 from "@clack/prompts";
8330
8344
  import pc3 from "picocolors";
8331
8345
 
@@ -8528,41 +8542,20 @@ async function runSetup(opts) {
8528
8542
  outputSuccess(`Linked to InsForge project: ${proj.project_name} (${proj.project_id})`);
8529
8543
  }
8530
8544
  const dashboardConnection = await ensureDashboardConnection(proj.project_id, token, opts);
8531
- if (opts.json) {
8532
- return {
8533
- dashboardConnection,
8534
- wizardSkipped: true,
8535
- wizardCommand: WIZARD_COMMAND
8536
- };
8537
- }
8538
- outputInfo("Running the official PostHog setup wizard to wire PostHog into your app...");
8539
- outputInfo(
8540
- pc3.dim("(it will open a browser for OAuth and let you pick a PostHog project)")
8541
- );
8542
- const wizardResult = spawnSync2(NPX_COMMAND, ["-y", "@posthog/wizard@latest"], {
8543
- stdio: "inherit",
8544
- env: process.env
8545
- });
8546
- if (wizardResult.error) {
8547
- throw new CLIError(`Failed to launch PostHog wizard: ${wizardResult.error.message}`);
8548
- }
8549
- const exitCode = wizardResult.status ?? 1;
8550
- if (wizardResult.signal === "SIGINT" || exitCode === 130) {
8551
- clack16.outro("Setup cancelled.");
8552
- return {
8553
- dashboardConnection,
8554
- wizardSkipped: false,
8555
- wizardExitCode: exitCode
8556
- };
8557
- }
8558
- if (exitCode !== 0) {
8559
- throw new CLIError(`PostHog wizard exited with code ${exitCode}.`);
8545
+ if (!opts.json) {
8546
+ clack16.note(
8547
+ `Run this in your terminal to wire PostHog into your app code:
8548
+
8549
+ ${WIZARD_COMMAND}
8550
+
8551
+ Once it completes, open the Analytics page in your InsForge dashboard.`,
8552
+ "Next step"
8553
+ );
8560
8554
  }
8561
- clack16.outro("Done. Open the Analytics page in your InsForge dashboard to view data.");
8562
8555
  return {
8563
8556
  dashboardConnection,
8564
- wizardSkipped: false,
8565
- wizardExitCode: exitCode
8557
+ wizardSkipped: true,
8558
+ wizardCommand: WIZARD_COMMAND
8566
8559
  };
8567
8560
  }
8568
8561
  async function ensureDashboardConnection(projectId, token, opts) {
@@ -8589,10 +8582,14 @@ async function runConnectFlow(projectId, token, authorizeUrl, opts) {
8589
8582
  process.stderr.write("Your browser should open automatically. If not, copy the URL above.\n");
8590
8583
  } else {
8591
8584
  clack16.log.info("PostHog is not yet connected to your InsForge dashboard.");
8592
- outputInfo("");
8593
- outputInfo(`Open this URL to authorize PostHog:
8594
- ${pc3.cyan(pc3.underline(authorizeUrl))}`);
8595
- outputInfo("");
8585
+ if (opts.skipBrowser) {
8586
+ clack16.log.info(`Open this URL to authorize PostHog:
8587
+ ${pc3.cyan(pc3.underline(authorizeUrl))}`);
8588
+ } else {
8589
+ clack16.log.info("Opening browser to authorize PostHog...");
8590
+ clack16.log.info(`If browser doesn't open, visit:
8591
+ ${pc3.cyan(pc3.underline(authorizeUrl))}`);
8592
+ }
8596
8593
  }
8597
8594
  if (!opts.skipBrowser) {
8598
8595
  try {
@@ -8602,7 +8599,11 @@ async function runConnectFlow(projectId, token, authorizeUrl, opts) {
8602
8599
  }
8603
8600
  }
8604
8601
  const spinner11 = !opts.json && isInteractive ? clack16.spinner() : null;
8605
- spinner11?.start("Waiting for InsForge dashboard connection... (timeout: 15 minutes)");
8602
+ if (spinner11) {
8603
+ spinner11.start("Waiting for InsForge dashboard connection... (timeout: 15 minutes)");
8604
+ } else if (!opts.json) {
8605
+ clack16.log.info("Waiting for InsForge dashboard connection (up to 15 minutes)...");
8606
+ }
8606
8607
  try {
8607
8608
  await pollPosthogConnection(
8608
8609
  projectId,
@@ -8622,9 +8623,17 @@ async function runConnectFlow(projectId, token, authorizeUrl, opts) {
8622
8623
  },
8623
8624
  opts.apiUrl
8624
8625
  );
8625
- spinner11?.stop("InsForge dashboard connection received.");
8626
+ if (spinner11) {
8627
+ spinner11.stop("InsForge dashboard connection received.");
8628
+ } else if (!opts.json) {
8629
+ clack16.log.success("InsForge dashboard connection received.");
8630
+ }
8626
8631
  } catch (err) {
8627
- spinner11?.stop("InsForge dashboard connection wait failed.");
8632
+ if (spinner11) {
8633
+ spinner11.stop("InsForge dashboard connection wait failed.");
8634
+ } else if (!opts.json) {
8635
+ clack16.log.error("InsForge dashboard connection wait failed.");
8636
+ }
8628
8637
  throw err;
8629
8638
  }
8630
8639
  }
@@ -9111,7 +9120,9 @@ function configFromMetadata(raw) {
9111
9120
  function registerConfigExportCommand(cfg) {
9112
9121
  cfg.command("export").description("Pull live project config and write insforge.toml").option("--out <path>", "output path", "insforge.toml").option("--force", "overwrite without confirmation").action(async (opts, cmd) => {
9113
9122
  const { json } = getRootOpts(cmd);
9123
+ let projectConfig = null;
9114
9124
  try {
9125
+ projectConfig = getProjectConfig();
9115
9126
  await requireAuth();
9116
9127
  const target = resolve5(process.cwd(), opts.out);
9117
9128
  if (existsSync10(target) && !opts.force) {
@@ -9128,6 +9139,12 @@ function registerConfigExportCommand(cfg) {
9128
9139
  });
9129
9140
  if (!ok || p.isCancel(ok)) {
9130
9141
  console.log("Aborted.");
9142
+ await reportCliUsage("cli.config.export", true);
9143
+ trackConfig("export", projectConfig, {
9144
+ json_mode: !!json,
9145
+ force: !!opts.force,
9146
+ outcome: "aborted"
9147
+ });
9131
9148
  return;
9132
9149
  }
9133
9150
  }
@@ -9149,9 +9166,23 @@ function registerConfigExportCommand(cfg) {
9149
9166
  }
9150
9167
  }
9151
9168
  await reportCliUsage("cli.config.export", true);
9169
+ trackConfig("export", projectConfig, {
9170
+ json_mode: !!json,
9171
+ force: !!opts.force,
9172
+ skipped_count: skipped.length,
9173
+ outcome: "success"
9174
+ });
9152
9175
  } catch (err) {
9153
9176
  await reportCliUsage("cli.config.export", false);
9177
+ trackConfig("export", projectConfig, {
9178
+ json_mode: !!json,
9179
+ force: !!opts.force,
9180
+ outcome: "error"
9181
+ });
9182
+ await shutdownAnalytics();
9154
9183
  handleError(err, json);
9184
+ } finally {
9185
+ await shutdownAnalytics();
9155
9186
  }
9156
9187
  });
9157
9188
  }
@@ -9460,7 +9491,9 @@ function authPasswordWireKey(key) {
9460
9491
  function registerConfigPlanCommand(cfg) {
9461
9492
  cfg.command("plan").description("Show diff between insforge.toml and live project state").option("--file <path>", "path to insforge.toml", "insforge.toml").action(async (opts, cmd) => {
9462
9493
  const { json } = getRootOpts(cmd);
9494
+ let projectConfig = null;
9463
9495
  try {
9496
+ projectConfig = getProjectConfig();
9464
9497
  await requireAuth();
9465
9498
  const tomlPath = resolve6(process.cwd(), opts.file);
9466
9499
  const tomlSource = readFileSync8(tomlPath, "utf8");
@@ -9483,9 +9516,25 @@ function registerConfigPlanCommand(cfg) {
9483
9516
  }
9484
9517
  }
9485
9518
  await reportCliUsage("cli.config.plan", true);
9519
+ trackConfig("plan", projectConfig, {
9520
+ json_mode: !!json,
9521
+ changes_count: result.changes.length,
9522
+ skipped_count: skipped.length,
9523
+ sections_changed: Array.from(
9524
+ new Set(result.changes.map((c) => changePath(c)))
9525
+ ),
9526
+ outcome: "success"
9527
+ });
9486
9528
  } catch (err) {
9487
9529
  await reportCliUsage("cli.config.plan", false);
9530
+ trackConfig("plan", projectConfig, {
9531
+ json_mode: !!json,
9532
+ outcome: "error"
9533
+ });
9534
+ await shutdownAnalytics();
9488
9535
  handleError(err, json);
9536
+ } finally {
9537
+ await shutdownAnalytics();
9489
9538
  }
9490
9539
  });
9491
9540
  }
@@ -9498,7 +9547,9 @@ import pc6 from "picocolors";
9498
9547
  function registerConfigApplyCommand(cfg) {
9499
9548
  cfg.command("apply").description("Apply insforge.toml to the live project").option("--file <path>", "path to insforge.toml", "insforge.toml").option("--dry-run", "show plan, do not apply").option("--auto-approve", "skip confirmation prompt").action(async (opts, cmd) => {
9500
9549
  const { json, yes } = getRootOpts(cmd);
9550
+ let projectConfig = null;
9501
9551
  try {
9552
+ projectConfig = getProjectConfig();
9502
9553
  await requireAuth();
9503
9554
  const tomlPath = resolve7(process.cwd(), opts.file);
9504
9555
  const tomlSource = readFileSync9(tomlPath, "utf8");
@@ -9508,6 +9559,9 @@ function registerConfigApplyCommand(cfg) {
9508
9559
  const live = liveFromMetadata(raw);
9509
9560
  const result = diffConfig({ live, file });
9510
9561
  const approved = opts.autoApprove || yes;
9562
+ const sectionsChanged = Array.from(
9563
+ new Set(result.changes.map((c) => changePath(c)))
9564
+ );
9511
9565
  if (!json) {
9512
9566
  console.log(formatPlan(result));
9513
9567
  }
@@ -9518,6 +9572,13 @@ function registerConfigApplyCommand(cfg) {
9518
9572
  );
9519
9573
  }
9520
9574
  await reportCliUsage("cli.config.apply", true);
9575
+ trackConfig("apply", projectConfig, {
9576
+ dry_run: !!opts.dryRun,
9577
+ json_mode: !!json,
9578
+ changes_count: result.changes.length,
9579
+ sections_changed: sectionsChanged,
9580
+ outcome: result.changes.length === 0 ? "no_changes" : "dry_run"
9581
+ });
9521
9582
  return;
9522
9583
  }
9523
9584
  if (!approved) {
@@ -9535,6 +9596,12 @@ function registerConfigApplyCommand(cfg) {
9535
9596
  if (!ok || p2.isCancel(ok)) {
9536
9597
  console.log("Aborted.");
9537
9598
  await reportCliUsage("cli.config.apply", true);
9599
+ trackConfig("apply", projectConfig, {
9600
+ json_mode: !!json,
9601
+ changes_count: result.changes.length,
9602
+ sections_changed: sectionsChanged,
9603
+ outcome: "aborted"
9604
+ });
9538
9605
  return;
9539
9606
  }
9540
9607
  }
@@ -9571,9 +9638,25 @@ function registerConfigApplyCommand(cfg) {
9571
9638
  }
9572
9639
  }
9573
9640
  await reportCliUsage("cli.config.apply", true);
9641
+ trackConfig("apply", projectConfig, {
9642
+ auto_approved: !!approved,
9643
+ json_mode: !!json,
9644
+ changes_count: result.changes.length,
9645
+ applied_count: applied.length,
9646
+ skipped_count: skipped.length,
9647
+ sections_changed: sectionsChanged,
9648
+ outcome: applied.length > 0 ? "applied" : "all_skipped"
9649
+ });
9574
9650
  } catch (err) {
9575
9651
  await reportCliUsage("cli.config.apply", false);
9652
+ trackConfig("apply", projectConfig, {
9653
+ json_mode: !!json,
9654
+ outcome: "error"
9655
+ });
9656
+ await shutdownAnalytics();
9576
9657
  handleError(err, json);
9658
+ } finally {
9659
+ await shutdownAnalytics();
9577
9660
  }
9578
9661
  });
9579
9662
  }