@insforge/cli 0.1.81 → 0.1.84

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/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { readFileSync as readFileSync12 } from "fs";
4
+ import { readFileSync as readFileSync13 } from "fs";
5
5
  import { join as join15, dirname as dirname2 } from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  import { Command } from "commander";
8
- import * as clack18 from "@clack/prompts";
8
+ import * as clack19 from "@clack/prompts";
9
9
 
10
10
  // src/lib/prompts.ts
11
11
  import * as readline from "readline";
@@ -1215,6 +1215,17 @@ function trackPayments(subcommand, config, properties) {
1215
1215
  ...properties
1216
1216
  });
1217
1217
  }
1218
+ function trackPosthog(subcommand, config, properties) {
1219
+ captureEvent(config.project_id, "cli_posthog_invoked", {
1220
+ subcommand,
1221
+ project_id: config.project_id,
1222
+ project_name: config.project_name,
1223
+ org_id: config.org_id,
1224
+ region: config.region,
1225
+ oss_mode: config.project_id === FAKE_PROJECT_ID,
1226
+ ...properties
1227
+ });
1228
+ }
1218
1229
  function trackConfig(subcommand, config, properties) {
1219
1230
  const distinctId = config?.project_id ?? FAKE_PROJECT_ID;
1220
1231
  captureEvent(distinctId, "cli_config_invoked", {
@@ -1707,15 +1718,91 @@ import { exec as exec3 } from "child_process";
1707
1718
  import { promisify as promisify4 } from "util";
1708
1719
  import * as fs5 from "fs/promises";
1709
1720
  import * as path5 from "path";
1710
- import * as clack13 from "@clack/prompts";
1721
+ import * as clack14 from "@clack/prompts";
1711
1722
  import pc2 from "picocolors";
1712
1723
 
1713
1724
  // src/lib/skills.ts
1714
1725
  import { exec } from "child_process";
1715
- import { existsSync as existsSync3, readFileSync as readFileSync2, appendFileSync } from "fs";
1716
- import { join as join2 } from "path";
1726
+ import { existsSync as existsSync4, readFileSync as readFileSync3, appendFileSync } from "fs";
1727
+ import { join as join3 } from "path";
1717
1728
  import { promisify } from "util";
1729
+ import * as clack10 from "@clack/prompts";
1730
+
1731
+ // src/lib/agents-md.ts
1732
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
1733
+ import { join as join2 } from "path";
1718
1734
  import * as clack9 from "@clack/prompts";
1735
+ var AGENTS_MD_START = "<!-- INSFORGE:START -->";
1736
+ var AGENTS_MD_END = "<!-- INSFORGE:END -->";
1737
+ function buildInsforgeBlock(config) {
1738
+ const lines = [
1739
+ AGENTS_MD_START,
1740
+ "## InsForge backend",
1741
+ "",
1742
+ "This project uses [InsForge](https://insforge.dev): an all-in-one, open-source Postgres-based backend (BaaS) that gives this app a database, authentication, file storage, edge functions, realtime, an AI model gateway, and payments through one platform.",
1743
+ ""
1744
+ ];
1745
+ if (config?.project_name || config?.oss_host) {
1746
+ const name = config.project_name ? `**${config.project_name}**` : "This project";
1747
+ const host = config.oss_host ? ` (API base \`${config.oss_host}\`)` : "";
1748
+ lines.push(`- **Project:** ${name}${host}`);
1749
+ }
1750
+ lines.push(
1751
+ "- **Skills:** these InsForge skills are installed for supported coding agents. Reach for them before implementing any InsForge feature instead of guessing the API:",
1752
+ " - `insforge`: app code with the `@insforge/sdk` client (database CRUD, auth, storage, edge functions, realtime, AI, email, and Stripe payments).",
1753
+ " - `insforge-cli`: backend and infrastructure via the `insforge` CLI (projects, SQL, migrations, RLS policies, storage buckets, functions, secrets, payment setup, schedules, deploys).",
1754
+ " - `insforge-debug`: diagnosing failures (SDK/HTTP errors, RLS denials, auth and OAuth issues) and running security or performance audits.",
1755
+ " - `insforge-integrations`: wiring external auth providers (Clerk, Auth0, WorkOS, Better Auth, etc.) for JWT-based RLS, or the OKX x402 payment facilitator.",
1756
+ " - `find-skills`: discovering additional skills on demand.",
1757
+ "- **Credentials:** app code reads keys from `.env.local`; the CLI reads `.insforge/project.json`. Never hardcode or commit keys.",
1758
+ "",
1759
+ "Key patterns:",
1760
+ "",
1761
+ "- Database inserts take an array: `insert([{ ... }])`.",
1762
+ "- Reference users with `auth.users(id)`; use `auth.uid()` in RLS policies.",
1763
+ "- For storage uploads, persist both the returned `url` and `key`.",
1764
+ AGENTS_MD_END
1765
+ );
1766
+ return lines.join("\n");
1767
+ }
1768
+ function mergeAgentsMd(existing, config) {
1769
+ const block = buildInsforgeBlock(config);
1770
+ if (existing === null || existing.trim() === "") {
1771
+ return `# AGENTS.md
1772
+
1773
+ ${block}
1774
+ `;
1775
+ }
1776
+ const startIdx = existing.indexOf(AGENTS_MD_START);
1777
+ if (startIdx !== -1) {
1778
+ const endMarkerIdx = existing.indexOf(AGENTS_MD_END, startIdx + AGENTS_MD_START.length);
1779
+ let before = existing.slice(0, startIdx);
1780
+ if (before.length > 0 && !before.endsWith("\n")) before += "\n";
1781
+ const after = endMarkerIdx === -1 ? "\n" : existing.slice(endMarkerIdx + AGENTS_MD_END.length);
1782
+ return `${before}${block}${after}`;
1783
+ }
1784
+ return `${existing.replace(/\s+$/, "")}
1785
+
1786
+ ${block}
1787
+ `;
1788
+ }
1789
+ function writeLocalAgentsMd(json, opts) {
1790
+ const cwd = opts?.cwd ?? process.cwd();
1791
+ const config = opts?.config !== void 0 ? opts.config : getProjectConfig();
1792
+ const path6 = join2(cwd, "AGENTS.md");
1793
+ const existed = existsSync3(path6);
1794
+ const existing = existed ? readFileSync2(path6, "utf-8") : null;
1795
+ const next = mergeAgentsMd(existing, config);
1796
+ if (existing === next) return;
1797
+ writeFileSync3(path6, next);
1798
+ if (!json) {
1799
+ clack9.log.success(
1800
+ existed ? "Updated AGENTS.md with InsForge guidance." : "Created AGENTS.md with InsForge guidance."
1801
+ );
1802
+ }
1803
+ }
1804
+
1805
+ // src/lib/skills.ts
1719
1806
  var execAsync = promisify(exec);
1720
1807
  var SKILL_INSTALL_TIMEOUT_MS = 6e4;
1721
1808
  function describeExecError(err) {
@@ -1755,8 +1842,8 @@ var GITIGNORE_ENTRIES = [
1755
1842
  ".windsurf"
1756
1843
  ];
1757
1844
  function updateGitignore() {
1758
- const gitignorePath = join2(process.cwd(), ".gitignore");
1759
- const existing = existsSync3(gitignorePath) ? readFileSync2(gitignorePath, "utf-8") : "";
1845
+ const gitignorePath = join3(process.cwd(), ".gitignore");
1846
+ const existing = existsSync4(gitignorePath) ? readFileSync3(gitignorePath, "utf-8") : "";
1760
1847
  const lines = new Set(existing.split("\n").map((l) => l.trim()));
1761
1848
  const missing = GITIGNORE_ENTRIES.filter((entry) => !lines.has(entry));
1762
1849
  if (!missing.length) return;
@@ -1772,44 +1859,44 @@ var PROVIDER_SKILLS = {
1772
1859
  };
1773
1860
  async function installSkills(json, authProvider) {
1774
1861
  try {
1775
- if (!json) clack9.log.info("Installing InsForge agent skills (global)...");
1862
+ if (!json) clack10.log.info("Installing InsForge agent skills (global)...");
1776
1863
  await execAsync(`npx skills add insforge/agent-skills -g -y ${AGENT_FLAGS}`, {
1777
1864
  cwd: process.cwd(),
1778
1865
  timeout: SKILL_INSTALL_TIMEOUT_MS
1779
1866
  });
1780
- if (!json) clack9.log.success("InsForge agent skills installed.");
1867
+ if (!json) clack10.log.success("InsForge agent skills installed.");
1781
1868
  } catch (err) {
1782
1869
  if (!json) {
1783
- clack9.log.warn(`Could not install agent skills: ${describeExecError(err)}`);
1784
- clack9.log.info("Run `npx skills add insforge/agent-skills` once resolved to see the full output.");
1870
+ clack10.log.warn(`Could not install agent skills: ${describeExecError(err)}`);
1871
+ clack10.log.info("Run `npx skills add insforge/agent-skills` once resolved to see the full output.");
1785
1872
  }
1786
1873
  }
1787
1874
  try {
1788
- if (!json) clack9.log.info("Installing find-skills (global)...");
1875
+ if (!json) clack10.log.info("Installing find-skills (global)...");
1789
1876
  await execAsync("npx skills add https://github.com/vercel-labs/skills --skill find-skills -g -y", {
1790
1877
  cwd: process.cwd(),
1791
1878
  timeout: SKILL_INSTALL_TIMEOUT_MS
1792
1879
  });
1793
- if (!json) clack9.log.success("find-skills installed.");
1880
+ if (!json) clack10.log.success("find-skills installed.");
1794
1881
  } catch (err) {
1795
1882
  if (!json) {
1796
- clack9.log.warn(`Could not install find-skills: ${describeExecError(err)}`);
1797
- clack9.log.info("Run `npx skills add https://github.com/vercel-labs/skills --skill find-skills` once resolved.");
1883
+ clack10.log.warn(`Could not install find-skills: ${describeExecError(err)}`);
1884
+ clack10.log.info("Run `npx skills add https://github.com/vercel-labs/skills --skill find-skills` once resolved.");
1798
1885
  }
1799
1886
  }
1800
1887
  const providerEntry = authProvider ? PROVIDER_SKILLS[authProvider] : void 0;
1801
1888
  if (providerEntry) {
1802
1889
  try {
1803
- if (!json) clack9.log.info(`Installing ${providerEntry.label} (global)...`);
1890
+ if (!json) clack10.log.info(`Installing ${providerEntry.label} (global)...`);
1804
1891
  await execAsync(`npx skills add ${providerEntry.repo} -g -y ${AGENT_FLAGS}`, {
1805
1892
  cwd: process.cwd(),
1806
1893
  timeout: SKILL_INSTALL_TIMEOUT_MS
1807
1894
  });
1808
- if (!json) clack9.log.success(`${providerEntry.label} installed.`);
1895
+ if (!json) clack10.log.success(`${providerEntry.label} installed.`);
1809
1896
  } catch (err) {
1810
1897
  if (!json) {
1811
- clack9.log.warn(`Could not install ${providerEntry.label}: ${describeExecError(err)}`);
1812
- clack9.log.info(`Run \`npx skills add ${providerEntry.repo}\` once resolved to see the full output.`);
1898
+ clack10.log.warn(`Could not install ${providerEntry.label}: ${describeExecError(err)}`);
1899
+ clack10.log.info(`Run \`npx skills add ${providerEntry.repo}\` once resolved to see the full output.`);
1813
1900
  }
1814
1901
  }
1815
1902
  }
@@ -1817,6 +1904,10 @@ async function installSkills(json, authProvider) {
1817
1904
  updateGitignore();
1818
1905
  } catch {
1819
1906
  }
1907
+ try {
1908
+ writeLocalAgentsMd(json);
1909
+ } catch {
1910
+ }
1820
1911
  }
1821
1912
  async function reportCliUsage(toolName, success, maxRetries = 1, explicitConfig) {
1822
1913
  let config = explicitConfig;
@@ -1866,7 +1957,7 @@ import { tmpdir } from "os";
1866
1957
  import { execFile } from "child_process";
1867
1958
  import { promisify as promisify2 } from "util";
1868
1959
  import { randomBytes as randomBytes2 } from "crypto";
1869
- import * as clack10 from "@clack/prompts";
1960
+ import * as clack11 from "@clack/prompts";
1870
1961
 
1871
1962
  // src/lib/api/oss.ts
1872
1963
  function requireProjectConfig() {
@@ -2107,7 +2198,7 @@ async function applyAuthProvider(provider, cwd, projectConfig, json) {
2107
2198
  if (!VALID_AUTH_PROVIDERS.includes(provider)) {
2108
2199
  throw new Error(`Unknown auth provider: ${provider}`);
2109
2200
  }
2110
- const fetchSpinner = !json ? clack10.spinner() : null;
2201
+ const fetchSpinner = !json ? clack11.spinner() : null;
2111
2202
  fetchSpinner?.start(`Fetching ${provider} scaffold from templates repo...`);
2112
2203
  const { dir: providerDir, cleanup } = await fetchProviderTree(provider);
2113
2204
  fetchSpinner?.stop(`${provider} scaffold ready`);
@@ -2206,15 +2297,15 @@ async function applyAuthProvider(provider, cwd, projectConfig, json) {
2206
2297
  result.envKeysRefreshed = Array.from(/* @__PURE__ */ new Set([...result.envKeysRefreshed, ...refreshed]));
2207
2298
  }
2208
2299
  if (!jwtSecret && !json) {
2209
- clack10.log.warn("Could not auto-fill JWT_SECRET \u2014 run `npx @insforge/cli secrets get JWT_SECRET` and paste it into .env.local.");
2300
+ clack11.log.warn("Could not auto-fill JWT_SECRET \u2014 run `npx @insforge/cli secrets get JWT_SECRET` and paste it into .env.local.");
2210
2301
  }
2211
2302
  if (result.envKeysSkipped.length > 0 && !json) {
2212
- clack10.log.warn(
2303
+ clack11.log.warn(
2213
2304
  `Kept your existing values for: ${result.envKeysSkipped.join(", ")}. If any of these need the auth-provider's defaults, see .env.example for reference.`
2214
2305
  );
2215
2306
  }
2216
2307
  if (result.envKeysRefreshed.length > 0 && !json) {
2217
- clack10.log.info(
2308
+ clack11.log.info(
2218
2309
  `Refreshed stale platform defaults: ${result.envKeysRefreshed.join(", ")}. Your value matched the manifest's default, so we replaced it with the live one.`
2219
2310
  );
2220
2311
  }
@@ -2230,7 +2321,7 @@ import { tmpdir as tmpdir2 } from "os";
2230
2321
  import { promisify as promisify3 } from "util";
2231
2322
  import * as fs4 from "fs/promises";
2232
2323
  import * as path4 from "path";
2233
- import * as clack12 from "@clack/prompts";
2324
+ import * as clack13 from "@clack/prompts";
2234
2325
 
2235
2326
  // src/lib/env.ts
2236
2327
  import * as fs2 from "fs/promises";
@@ -2265,7 +2356,7 @@ import * as path3 from "path";
2265
2356
  import * as fs3 from "fs/promises";
2266
2357
  import { createReadStream } from "fs";
2267
2358
  import { createHash as createHash2 } from "crypto";
2268
- import * as clack11 from "@clack/prompts";
2359
+ import * as clack12 from "@clack/prompts";
2269
2360
  import archiver from "archiver";
2270
2361
  var POLL_INTERVAL_MS3 = 5e3;
2271
2362
  var POLL_TIMEOUT_MS3 = 3e5;
@@ -2565,7 +2656,7 @@ function registerDeploymentsDeployCommand(deploymentsCmd2) {
2565
2656
  `"${dirName}" is an excluded directory and cannot be used as a deploy source. Please specify your project root or output directory instead.`
2566
2657
  );
2567
2658
  }
2568
- const spinner11 = !json ? clack11.spinner() : null;
2659
+ const spinner11 = !json ? clack12.spinner() : null;
2569
2660
  const startBody = {};
2570
2661
  if (opts.env) {
2571
2662
  try {
@@ -2598,9 +2689,9 @@ function registerDeploymentsDeployCommand(deploymentsCmd2) {
2598
2689
  outputJson(result.deployment);
2599
2690
  } else {
2600
2691
  if (result.liveUrl) {
2601
- clack11.log.success(`Live at: ${result.liveUrl}`);
2692
+ clack12.log.success(`Live at: ${result.liveUrl}`);
2602
2693
  }
2603
- clack11.log.info(`Deployment ID: ${result.deploymentId}`);
2694
+ clack12.log.info(`Deployment ID: ${result.deploymentId}`);
2604
2695
  }
2605
2696
  } else {
2606
2697
  spinner11?.stop("Deployment is still building");
@@ -2611,9 +2702,9 @@ function registerDeploymentsDeployCommand(deploymentsCmd2) {
2611
2702
  timedOut: true
2612
2703
  });
2613
2704
  } else {
2614
- clack11.log.info(`Deployment ID: ${result.deploymentId}`);
2615
- clack11.log.warn("Deployment did not finish within 5 minutes.");
2616
- clack11.log.info(`Check status with: npx @insforge/cli deployments status ${result.deploymentId}`);
2705
+ clack12.log.info(`Deployment ID: ${result.deploymentId}`);
2706
+ clack12.log.warn("Deployment did not finish within 5 minutes.");
2707
+ clack12.log.info(`Check status with: npx @insforge/cli deployments status ${result.deploymentId}`);
2617
2708
  }
2618
2709
  }
2619
2710
  await reportCliUsage("cli.deployments.deploy", true);
@@ -2629,6 +2720,7 @@ var execAsync2 = promisify3(exec2);
2629
2720
  var execFileAsync2 = promisify3(execFile2);
2630
2721
  var SAFE_REPO_PATTERN2 = /^(https?:\/\/|git@)[A-Za-z0-9._:/@~+-]+(\.git)?$/;
2631
2722
  var SAFE_BRANCH_PATTERN2 = /^[A-Za-z0-9._/-]+$/;
2723
+ var SAFE_MARKETPLACE_SLUG = /^[a-z0-9][a-z0-9-]{0,99}$/;
2632
2724
  async function waitForProjectActive(projectId, apiUrl, timeoutMs = 12e4) {
2633
2725
  const start = Date.now();
2634
2726
  while (Date.now() - start < timeoutMs) {
@@ -2737,13 +2829,22 @@ async function copyDir(src, dest) {
2737
2829
  }
2738
2830
  }
2739
2831
  function registerCreateCommand(program2) {
2740
- program2.command("create").description("Create a new InsForge project").option("--name <name>", "Project name").option("--org-id <id>", "Organization ID").option("--region <region>", "Deployment region (us-east, us-west, eu-central, ap-southeast)").option("--template <template>", "Template to use: react, nextjs, chatbot, crm, e-commerce, todo, or empty").option("--auth <provider>", "Wire a third-party auth provider into the chosen template (currently: better-auth)").action(async (opts, cmd) => {
2832
+ program2.command("create").description("Create a new InsForge project").option("--name <name>", "Project name").option("--org-id <id>", "Organization ID").option("--region <region>", "Deployment region (us-east, us-west, eu-central, ap-southeast)").option("--template <template>", "Template to use: react, nextjs, chatbot, crm, e-commerce, todo, or empty").option("--marketplace <slug>", "Install a marketplace template by slug (browse: https://insforge.dev/templates)").option("--auth <provider>", "Wire a third-party auth provider into the chosen template (currently: better-auth)").action(async (opts, cmd) => {
2741
2833
  const { json, apiUrl } = getRootOpts(cmd);
2742
2834
  try {
2835
+ if (opts.marketplace && opts.template) {
2836
+ throw new CLIError("--marketplace and --template are mutually exclusive");
2837
+ }
2838
+ if (opts.marketplace && !SAFE_MARKETPLACE_SLUG.test(opts.marketplace)) {
2839
+ throw new CLIError(
2840
+ `Invalid --marketplace slug "${opts.marketplace}". Slugs must match ${SAFE_MARKETPLACE_SLUG}.
2841
+ Browse available templates: https://insforge.dev/templates`
2842
+ );
2843
+ }
2743
2844
  await requireAuth(apiUrl, false);
2744
2845
  if (!json) {
2745
2846
  await animateBanner();
2746
- clack12.intro("Let's build something great");
2847
+ clack13.intro("Let's build something great");
2747
2848
  }
2748
2849
  let orgId = opts.orgId;
2749
2850
  if (!orgId) {
@@ -2753,7 +2854,7 @@ function registerCreateCommand(program2) {
2753
2854
  }
2754
2855
  if (orgs.length === 1) {
2755
2856
  orgId = orgs[0].id;
2756
- if (!json) clack12.log.info(`Using organization: ${orgs[0].name}`);
2857
+ if (!json) clack13.log.info(`Using organization: ${orgs[0].name}`);
2757
2858
  } else {
2758
2859
  if (json) {
2759
2860
  throw new CLIError("Multiple organizations found. Specify --org-id.");
@@ -2796,6 +2897,9 @@ function registerCreateCommand(program2) {
2796
2897
  if (template && !validTemplates.includes(template)) {
2797
2898
  throw new CLIError(`Invalid template "${template}". Valid options: ${validTemplates.join(", ")}`);
2798
2899
  }
2900
+ if (opts.marketplace) {
2901
+ template = opts.marketplace;
2902
+ }
2799
2903
  if (!template) {
2800
2904
  if (json) {
2801
2905
  template = "empty";
@@ -2866,7 +2970,7 @@ function registerCreateCommand(program2) {
2866
2970
  process.chdir(projectDir);
2867
2971
  }
2868
2972
  let projectLinked = false;
2869
- const s = !json ? clack12.spinner() : null;
2973
+ const s = !json ? clack13.spinner() : null;
2870
2974
  try {
2871
2975
  s?.start("Creating project...");
2872
2976
  const project = await createProject(orgId, projectName, opts.region, apiUrl);
@@ -2886,7 +2990,16 @@ function registerCreateCommand(program2) {
2886
2990
  projectLinked = true;
2887
2991
  s?.stop(`Project "${project.name}" created and linked`);
2888
2992
  const githubTemplates = ["chatbot", "crm", "e-commerce", "nextjs", "react", "todo"];
2889
- if (githubTemplates.includes(template)) {
2993
+ if (opts.marketplace) {
2994
+ const downloaded = await downloadGitHubTemplate(
2995
+ opts.marketplace,
2996
+ projectConfig,
2997
+ json
2998
+ );
2999
+ if (downloaded) {
3000
+ void reportMarketplaceDownload(opts.marketplace);
3001
+ }
3002
+ } else if (githubTemplates.includes(template)) {
2890
3003
  await downloadGitHubTemplate(template, projectConfig, json);
2891
3004
  } else if (hasTemplate) {
2892
3005
  await downloadTemplate(template, projectConfig, projectName, json, apiUrl);
@@ -2894,7 +3007,7 @@ function registerCreateCommand(program2) {
2894
3007
  try {
2895
3008
  const anonKey = await getAnonKey();
2896
3009
  if (!anonKey) {
2897
- if (!json) clack12.log.warn("Could not retrieve anon key. You can add it to .env.local manually.");
3010
+ if (!json) clack13.log.warn("Could not retrieve anon key. You can add it to .env.local manually.");
2898
3011
  } else {
2899
3012
  const envPath = path4.join(process.cwd(), ".env.local");
2900
3013
  const envContent = [
@@ -2905,16 +3018,16 @@ function registerCreateCommand(program2) {
2905
3018
  ].join("\n");
2906
3019
  await fs4.writeFile(envPath, envContent, { flag: "wx" });
2907
3020
  if (!json) {
2908
- clack12.log.success("Created .env.local with your InsForge credentials");
3021
+ clack13.log.success("Created .env.local with your InsForge credentials");
2909
3022
  }
2910
3023
  }
2911
3024
  } catch (err) {
2912
3025
  const error = err;
2913
3026
  if (!json) {
2914
3027
  if (error.code === "EEXIST") {
2915
- clack12.log.warn(".env.local already exists; skipping InsForge key seeding.");
3028
+ clack13.log.warn(".env.local already exists; skipping InsForge key seeding.");
2916
3029
  } else {
2917
- clack12.log.warn(`Failed to create .env.local: ${error.message}`);
3030
+ clack13.log.warn(`Failed to create .env.local: ${error.message}`);
2918
3031
  }
2919
3032
  }
2920
3033
  }
@@ -2923,12 +3036,12 @@ function registerCreateCommand(program2) {
2923
3036
  try {
2924
3037
  const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig, json);
2925
3038
  if (!json) {
2926
- clack12.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
3039
+ clack13.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
2927
3040
  }
2928
3041
  } catch (err) {
2929
3042
  const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
2930
3043
  if (json) console.error(JSON.stringify({ warning: msg }));
2931
- else clack12.log.warn(msg);
3044
+ else clack13.log.warn(msg);
2932
3045
  }
2933
3046
  }
2934
3047
  await installSkills(json, opts.auth);
@@ -2936,7 +3049,7 @@ function registerCreateCommand(program2) {
2936
3049
  await reportCliUsage("cli.create", true, 6);
2937
3050
  const templateDownloaded = hasTemplate ? await fs4.stat(path4.join(process.cwd(), "package.json")).catch(() => null) : null;
2938
3051
  if (templateDownloaded) {
2939
- const installSpinner = !json ? clack12.spinner() : null;
3052
+ const installSpinner = !json ? clack13.spinner() : null;
2940
3053
  installSpinner?.start("Installing dependencies...");
2941
3054
  try {
2942
3055
  await execAsync2("npm install", { cwd: process.cwd(), maxBuffer: 10 * 1024 * 1024 });
@@ -2944,8 +3057,8 @@ function registerCreateCommand(program2) {
2944
3057
  } catch (err) {
2945
3058
  installSpinner?.stop("Failed to install dependencies");
2946
3059
  if (!json) {
2947
- clack12.log.warn(`npm install failed: ${err.message}`);
2948
- clack12.log.info("Run `npm install` manually to install dependencies.");
3060
+ clack13.log.warn(`npm install failed: ${err.message}`);
3061
+ clack13.log.info("Run `npm install` manually to install dependencies.");
2949
3062
  }
2950
3063
  }
2951
3064
  }
@@ -2961,7 +3074,7 @@ function registerCreateCommand(program2) {
2961
3074
  if (envVars.length > 0) {
2962
3075
  startBody.envVars = envVars;
2963
3076
  }
2964
- const deploySpinner = clack12.spinner();
3077
+ const deploySpinner = clack13.spinner();
2965
3078
  const result = await deployProject({
2966
3079
  sourceDir: process.cwd(),
2967
3080
  startBody,
@@ -2972,12 +3085,12 @@ function registerCreateCommand(program2) {
2972
3085
  liveUrl = result.liveUrl;
2973
3086
  } else {
2974
3087
  deploySpinner.stop("Deployment is still building");
2975
- clack12.log.info(`Deployment ID: ${result.deploymentId}`);
2976
- clack12.log.warn("Deployment did not finish within 2 minutes.");
2977
- clack12.log.info(`Check status with: npx @insforge/cli deployments status ${result.deploymentId}`);
3088
+ clack13.log.info(`Deployment ID: ${result.deploymentId}`);
3089
+ clack13.log.warn("Deployment did not finish within 2 minutes.");
3090
+ clack13.log.info(`Check status with: npx @insforge/cli deployments status ${result.deploymentId}`);
2978
3091
  }
2979
3092
  } catch (err) {
2980
- clack12.log.warn(`Deploy failed: ${err.message}`);
3093
+ clack13.log.warn(`Deploy failed: ${err.message}`);
2981
3094
  }
2982
3095
  }
2983
3096
  }
@@ -2994,33 +3107,33 @@ function registerCreateCommand(program2) {
2994
3107
  }
2995
3108
  });
2996
3109
  } else {
2997
- clack12.log.step(`Dashboard: ${dashboardUrl}`);
3110
+ clack13.log.step(`Dashboard: ${dashboardUrl}`);
2998
3111
  if (liveUrl) {
2999
- clack12.log.success(`Live site: ${liveUrl}`);
3112
+ clack13.log.success(`Live site: ${liveUrl}`);
3000
3113
  }
3001
3114
  if (templateDownloaded) {
3002
3115
  const steps = [
3003
3116
  `cd ${dirName}`,
3004
3117
  "npm run dev"
3005
3118
  ];
3006
- clack12.note(steps.join("\n"), "Next steps");
3007
- clack12.note("Open your coding agent (Claude Code, Codex, Cursor, etc.) to add new features.", "Keep building");
3119
+ clack13.note(steps.join("\n"), "Next steps");
3120
+ clack13.note("Open your coding agent (Claude Code, Codex, Cursor, etc.) to add new features.", "Keep building");
3008
3121
  } else if (hasTemplate && !templateDownloaded) {
3009
- clack12.log.warn("Template download failed. You can retry or set up manually.");
3122
+ clack13.log.warn("Template download failed. You can retry or set up manually.");
3010
3123
  } else {
3011
3124
  const prompts = [
3012
3125
  "Build a todo app with Google OAuth sign-in",
3013
3126
  "Build an Instagram clone where users can upload photos, like, and comment",
3014
3127
  "Build an AI chatbot with conversation history"
3015
3128
  ];
3016
- clack12.note(
3129
+ clack13.note(
3017
3130
  `Open your coding agent (Claude Code, Codex, Cursor, etc.) and try:
3018
3131
 
3019
3132
  ${prompts.map((p3) => `\u2022 "${p3}"`).join("\n")}`,
3020
3133
  "Start building"
3021
3134
  );
3022
3135
  }
3023
- clack12.outro("Done!");
3136
+ clack13.outro("Done!");
3024
3137
  }
3025
3138
  } catch (err) {
3026
3139
  if (!projectLinked && hasTemplate && projectDir !== originalCwd) {
@@ -3039,7 +3152,7 @@ ${prompts.map((p3) => `\u2022 "${p3}"`).join("\n")}`,
3039
3152
  });
3040
3153
  }
3041
3154
  async function downloadTemplate(framework, projectConfig, projectName, json, _apiUrl) {
3042
- const s = !json ? clack12.spinner() : null;
3155
+ const s = !json ? clack13.spinner() : null;
3043
3156
  s?.start("Downloading template...");
3044
3157
  try {
3045
3158
  const anonKey = await getAnonKey();
@@ -3070,13 +3183,13 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
3070
3183
  } catch (err) {
3071
3184
  s?.stop("Template download failed");
3072
3185
  if (!json) {
3073
- clack12.log.warn(`Failed to download template: ${err.message}`);
3074
- clack12.log.info("You can manually set up the template later.");
3186
+ clack13.log.warn(`Failed to download template: ${err.message}`);
3187
+ clack13.log.info("You can manually set up the template later.");
3075
3188
  }
3076
3189
  }
3077
3190
  }
3078
3191
  async function downloadGitHubTemplate(templateName, projectConfig, json) {
3079
- const s = !json ? clack12.spinner() : null;
3192
+ const s = !json ? clack13.spinner() : null;
3080
3193
  s?.start(`Downloading ${templateName} template...`);
3081
3194
  const tempDir = path4.join(tmpdir2(), `insforge-template-${Date.now()}`);
3082
3195
  try {
@@ -3125,7 +3238,7 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
3125
3238
  await fs4.writeFile(envLocalPath, envFinal, { flag: "wx" });
3126
3239
  } catch (e) {
3127
3240
  if (e.code === "EEXIST") {
3128
- if (!json) clack12.log.warn(".env.local already exists; skipping env seeding.");
3241
+ if (!json) clack13.log.warn(".env.local already exists; skipping env seeding.");
3129
3242
  } else {
3130
3243
  throw e;
3131
3244
  }
@@ -3135,7 +3248,7 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
3135
3248
  const migrationPath = path4.join(cwd, "migrations", "db_init.sql");
3136
3249
  const migrationExists = await fs4.stat(migrationPath).catch(() => null);
3137
3250
  if (migrationExists) {
3138
- const dbSpinner = !json ? clack12.spinner() : null;
3251
+ const dbSpinner = !json ? clack13.spinner() : null;
3139
3252
  dbSpinner?.start("Running database migrations...");
3140
3253
  try {
3141
3254
  const sql = await fs4.readFile(migrationPath, "utf-8");
@@ -3144,40 +3257,57 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
3144
3257
  } catch (err) {
3145
3258
  dbSpinner?.stop("Database migration failed");
3146
3259
  if (!json) {
3147
- clack12.log.warn(`Migration failed: ${err.message}`);
3148
- clack12.log.info('You can run the migration manually: npx @insforge/cli db query --unrestricted "$(cat migrations/db_init.sql)"');
3260
+ clack13.log.warn(`Migration failed: ${err.message}`);
3261
+ clack13.log.info('You can run the migration manually: npx @insforge/cli db query --unrestricted "$(cat migrations/db_init.sql)"');
3149
3262
  } else {
3150
3263
  throw err;
3151
3264
  }
3152
3265
  }
3153
3266
  }
3267
+ return true;
3154
3268
  } catch (err) {
3155
3269
  s?.stop(`${templateName} template download failed`);
3156
3270
  const msg = `Failed to download ${templateName} template: ${err.message}`;
3157
3271
  if (json) {
3158
3272
  console.error(JSON.stringify({ warning: msg }));
3159
3273
  } else {
3160
- clack12.log.warn(msg);
3161
- clack12.log.info("You can manually clone from: https://github.com/InsForge/insforge-templates");
3274
+ clack13.log.warn(msg);
3275
+ clack13.log.info("You can manually clone from: https://github.com/InsForge/insforge-templates");
3162
3276
  }
3277
+ return false;
3163
3278
  } finally {
3164
3279
  await fs4.rm(tempDir, { recursive: true, force: true }).catch(() => {
3165
3280
  });
3166
3281
  }
3167
3282
  }
3283
+ var MARKETPLACE_REPORT_URL = process.env.INSFORGE_MARKETPLACE_REPORT_URL ?? "https://p8n7m7ci.us-east.insforge.app/functions/report-download";
3284
+ async function reportMarketplaceDownload(slug) {
3285
+ try {
3286
+ const res = await fetch(MARKETPLACE_REPORT_URL, {
3287
+ method: "POST",
3288
+ headers: { "Content-Type": "application/json" },
3289
+ body: JSON.stringify({ slug }),
3290
+ signal: AbortSignal.timeout(5e3)
3291
+ });
3292
+ if (!res.ok) {
3293
+ return;
3294
+ }
3295
+ } catch {
3296
+ }
3297
+ }
3168
3298
 
3169
3299
  // src/commands/projects/link.ts
3170
3300
  var execAsync3 = promisify4(exec3);
3171
3301
  async function runNpmInstall(startMessage = "Installing dependencies...") {
3172
- const spinner11 = clack13.spinner();
3302
+ const spinner11 = clack14.spinner();
3173
3303
  spinner11.start(startMessage);
3174
3304
  try {
3175
3305
  await execAsync3("npm install", { cwd: process.cwd(), maxBuffer: 10 * 1024 * 1024 });
3176
3306
  spinner11.stop("Dependencies installed");
3177
3307
  } catch (err) {
3178
3308
  spinner11.stop("Failed to install dependencies");
3179
- clack13.log.warn(`npm install failed: ${err.message}`);
3180
- clack13.log.info("Run `npm install` manually to install dependencies.");
3309
+ clack14.log.warn(`npm install failed: ${err.message}`);
3310
+ clack14.log.info("Run `npm install` manually to install dependencies.");
3181
3311
  }
3182
3312
  }
3183
3313
  async function runNpmSetupIfPresent() {
@@ -3189,15 +3319,15 @@ async function runNpmSetupIfPresent() {
3189
3319
  } catch {
3190
3320
  }
3191
3321
  if (!hasSetup) return;
3192
- const spinner11 = clack13.spinner();
3322
+ const spinner11 = clack14.spinner();
3193
3323
  spinner11.start("Running setup (schema + migrations)...");
3194
3324
  try {
3195
3325
  await execAsync3("npm run setup", { cwd: process.cwd(), maxBuffer: 20 * 1024 * 1024 });
3196
3326
  spinner11.stop("Setup complete");
3197
3327
  } catch (err) {
3198
3328
  spinner11.stop("Setup failed");
3199
- clack13.log.warn(`npm run setup failed: ${err.message.split("\n")[0]}`);
3200
- clack13.log.info("Inspect the error, fix DATABASE_URL or network access, then run `npm run setup` manually.");
3329
+ clack14.log.warn(`npm run setup failed: ${err.message.split("\n")[0]}`);
3330
+ clack14.log.info("Inspect the error, fix DATABASE_URL or network access, then run `npm run setup` manually.");
3201
3331
  }
3202
3332
  }
3203
3333
  function registerProjectLinkCommand(program2) {
@@ -3220,7 +3350,7 @@ function registerProjectLinkCommand(program2) {
3220
3350
  if (json) {
3221
3351
  outputJson({ success: true, skills_only: true });
3222
3352
  } else {
3223
- clack13.note(
3353
+ clack14.note(
3224
3354
  `Open your coding agent (Claude Code, Codex, Cursor, etc.) and ask it to build something. It will walk you through provisioning an InsForge project when needed. If you're not signed in yet, your browser will open for sign-in at that point.`,
3225
3355
  "What's next"
3226
3356
  );
@@ -3298,11 +3428,11 @@ function registerProjectLinkCommand(program2) {
3298
3428
  if (opts.auth) {
3299
3429
  try {
3300
3430
  const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig2, json);
3301
- if (!json) clack13.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
3431
+ if (!json) clack14.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
3302
3432
  } catch (err) {
3303
3433
  const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
3304
3434
  if (json) console.error(JSON.stringify({ warning: msg }));
3305
- else clack13.log.warn(msg);
3435
+ else clack14.log.warn(msg);
3306
3436
  }
3307
3437
  }
3308
3438
  if (templateDownloaded && !json) {
@@ -3328,9 +3458,9 @@ function registerProjectLinkCommand(program2) {
3328
3458
  `${pc2.bold("1.")} ${runCommand}`,
3329
3459
  `${pc2.bold("2.")} Open ${pc2.cyan("Claude Code")} or ${pc2.cyan("Cursor")} and prompt your agent to add more features`
3330
3460
  ];
3331
- clack13.note(steps.join("\n"), "What's next");
3461
+ clack14.note(steps.join("\n"), "What's next");
3332
3462
  } else {
3333
- clack13.log.warn("Template download failed. You can retry or set up manually.");
3463
+ clack14.log.warn("Template download failed. You can retry or set up manually.");
3334
3464
  }
3335
3465
  }
3336
3466
  return;
@@ -3345,7 +3475,7 @@ function registerProjectLinkCommand(program2) {
3345
3475
  try {
3346
3476
  const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig2, json);
3347
3477
  if (!json) {
3348
- clack13.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
3478
+ clack14.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
3349
3479
  }
3350
3480
  if (result.packageJsonPatched && !json) {
3351
3481
  await runNpmInstall("Installing new dependencies...");
@@ -3354,7 +3484,7 @@ function registerProjectLinkCommand(program2) {
3354
3484
  } catch (err) {
3355
3485
  const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
3356
3486
  if (json) console.error(JSON.stringify({ warning: msg }));
3357
- else clack13.log.warn(msg);
3487
+ else clack14.log.warn(msg);
3358
3488
  }
3359
3489
  }
3360
3490
  trackCommand("link", "oss-org", { direct: true });
@@ -3384,7 +3514,7 @@ function registerProjectLinkCommand(program2) {
3384
3514
  }
3385
3515
  if (orgs.length === 1) {
3386
3516
  orgId = orgs[0].id;
3387
- if (!json) clack13.log.info(`Using organization: ${orgs[0].name}`);
3517
+ if (!json) clack14.log.info(`Using organization: ${orgs[0].name}`);
3388
3518
  } else {
3389
3519
  if (json) {
3390
3520
  throw new CLIError("Multiple organizations found. Specify --org-id.");
@@ -3495,11 +3625,11 @@ function registerProjectLinkCommand(program2) {
3495
3625
  if (opts.auth) {
3496
3626
  try {
3497
3627
  const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig, json);
3498
- if (!json) clack13.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
3628
+ if (!json) clack14.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
3499
3629
  } catch (err) {
3500
3630
  const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
3501
3631
  if (json) console.error(JSON.stringify({ warning: msg }));
3502
- else clack13.log.warn(msg);
3632
+ else clack14.log.warn(msg);
3503
3633
  }
3504
3634
  }
3505
3635
  if (templateDownloaded && !json) {
@@ -3512,16 +3642,16 @@ function registerProjectLinkCommand(program2) {
3512
3642
  await reportCliUsage("cli.link", true, 6, projectConfig);
3513
3643
  if (!json) {
3514
3644
  const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
3515
- clack13.log.step(`Dashboard: ${pc2.underline(dashboardUrl)}`);
3645
+ clack14.log.step(`Dashboard: ${pc2.underline(dashboardUrl)}`);
3516
3646
  if (templateDownloaded) {
3517
3647
  const runCommand = `${pc2.cyan("cd")} ${pc2.green(dirName)} ${pc2.dim("&&")} ${pc2.cyan("npm run dev")}`;
3518
3648
  const steps = [
3519
3649
  `${pc2.bold("1.")} ${runCommand}`,
3520
3650
  `${pc2.bold("2.")} Open ${pc2.cyan("Claude Code")} or ${pc2.cyan("Cursor")} and prompt your agent to add more features`
3521
3651
  ];
3522
- clack13.note(steps.join("\n"), "What's next");
3652
+ clack14.note(steps.join("\n"), "What's next");
3523
3653
  } else {
3524
- clack13.log.warn("Template download failed. You can retry or set up manually.");
3654
+ clack14.log.warn("Template download failed. You can retry or set up manually.");
3525
3655
  }
3526
3656
  }
3527
3657
  } else {
@@ -3529,7 +3659,7 @@ function registerProjectLinkCommand(program2) {
3529
3659
  try {
3530
3660
  const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig, json);
3531
3661
  if (!json) {
3532
- clack13.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
3662
+ clack14.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
3533
3663
  }
3534
3664
  if (result.packageJsonPatched && !json) {
3535
3665
  await runNpmInstall("Installing new dependencies...");
@@ -3538,20 +3668,20 @@ function registerProjectLinkCommand(program2) {
3538
3668
  } catch (err) {
3539
3669
  const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
3540
3670
  if (json) console.error(JSON.stringify({ warning: msg }));
3541
- else clack13.log.warn(msg);
3671
+ else clack14.log.warn(msg);
3542
3672
  }
3543
3673
  }
3544
3674
  await installSkills(json, opts.auth);
3545
3675
  await reportCliUsage("cli.link", true, 6, projectConfig);
3546
3676
  if (!json) {
3547
3677
  const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
3548
- clack13.log.step(`Dashboard: ${dashboardUrl}`);
3678
+ clack14.log.step(`Dashboard: ${dashboardUrl}`);
3549
3679
  const prompts = [
3550
3680
  "Build a todo app with Google OAuth sign-in",
3551
3681
  "Build an Instagram clone where users can upload photos, like, and comment",
3552
3682
  "Build an AI chatbot with conversation history and deploy it to a live URL"
3553
3683
  ];
3554
- clack13.note(
3684
+ clack14.note(
3555
3685
  `Open your coding agent (Claude Code, Codex, Cursor, etc.) and try:
3556
3686
 
3557
3687
  ${prompts.map((p3) => `\u2022 "${p3}"`).join("\n")}`,
@@ -3791,7 +3921,7 @@ function registerDbRpcCommand(dbCmd2) {
3791
3921
  }
3792
3922
 
3793
3923
  // src/commands/db/export.ts
3794
- import { writeFileSync as writeFileSync3 } from "fs";
3924
+ import { writeFileSync as writeFileSync4 } from "fs";
3795
3925
  function registerDbExportCommand(dbCmd2) {
3796
3926
  dbCmd2.command("export").description("Export database schema and/or data").option("--format <format>", "Export format: sql or json", "sql").option("--tables <tables>", "Comma-separated list of tables to export (default: all)").option("--no-data", "Exclude table data (schema only)").option("--include-functions", "Include database functions").option("--include-sequences", "Include sequences").option("--include-views", "Include views").option("--row-limit <n>", "Maximum rows per table").option("-o, --output <file>", "Output file path (default: stdout)").action(async (opts, cmd) => {
3797
3927
  const { json } = getRootOpts(cmd);
@@ -3831,7 +3961,7 @@ function registerDbExportCommand(dbCmd2) {
3831
3961
  return;
3832
3962
  }
3833
3963
  if (opts.output) {
3834
- writeFileSync3(opts.output, content);
3964
+ writeFileSync4(opts.output, content);
3835
3965
  const tableCount = meta?.tables?.length;
3836
3966
  const suffix = tableCount ? ` (${tableCount} tables, format: ${meta?.format ?? opts.format})` : "";
3837
3967
  outputSuccess(`Exported to ${opts.output}${suffix}`);
@@ -3845,7 +3975,7 @@ function registerDbExportCommand(dbCmd2) {
3845
3975
  }
3846
3976
 
3847
3977
  // src/commands/db/import.ts
3848
- import { readFileSync as readFileSync3 } from "fs";
3978
+ import { readFileSync as readFileSync4 } from "fs";
3849
3979
  import { basename as basename5 } from "path";
3850
3980
  function registerDbImportCommand(dbCmd2) {
3851
3981
  dbCmd2.command("import <file>").description("Import database from a local SQL file").option("--truncate", "Truncate existing tables before import").action(async (file, opts, cmd) => {
@@ -3854,7 +3984,7 @@ function registerDbImportCommand(dbCmd2) {
3854
3984
  await requireAuth();
3855
3985
  const config = getProjectConfig();
3856
3986
  if (!config) throw new ProjectNotLinkedError();
3857
- const fileContent = readFileSync3(file);
3987
+ const fileContent = readFileSync4(file);
3858
3988
  const fileName = basename5(file);
3859
3989
  const formData = new FormData();
3860
3990
  formData.append("file", new Blob([fileContent]), fileName);
@@ -3885,12 +4015,12 @@ function registerDbImportCommand(dbCmd2) {
3885
4015
  }
3886
4016
 
3887
4017
  // src/commands/db/migrations.ts
3888
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
3889
- import { join as join9 } from "path";
4018
+ import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
4019
+ import { join as join10 } from "path";
3890
4020
 
3891
4021
  // src/lib/migrations.ts
3892
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync } from "fs";
3893
- import { join as join8 } from "path";
4022
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
4023
+ import { join as join9 } from "path";
3894
4024
  var MIGRATION_VERSION_REGEX = /^\d{1,64}$/u;
3895
4025
  var MIGRATION_FILENAME_REGEX = /^(\d{1,64})_([a-z0-9-]+)\.sql$/u;
3896
4026
  function assertValidMigrationVersion(version) {
@@ -3954,18 +4084,18 @@ function incrementMigrationVersion(version) {
3954
4084
  return formatMigrationVersion(new Date(nextTimestamp));
3955
4085
  }
3956
4086
  function getMigrationsDir(cwd = process.cwd()) {
3957
- return join8(cwd, "migrations");
4087
+ return join9(cwd, "migrations");
3958
4088
  }
3959
4089
  function ensureMigrationsDir(cwd = process.cwd()) {
3960
4090
  const migrationsDir = getMigrationsDir(cwd);
3961
- if (!existsSync4(migrationsDir)) {
4091
+ if (!existsSync5(migrationsDir)) {
3962
4092
  mkdirSync2(migrationsDir, { recursive: true });
3963
4093
  }
3964
4094
  return migrationsDir;
3965
4095
  }
3966
4096
  function listLocalMigrationFilenames(cwd = process.cwd()) {
3967
4097
  const migrationsDir = getMigrationsDir(cwd);
3968
- if (!existsSync4(migrationsDir)) {
4098
+ if (!existsSync5(migrationsDir)) {
3969
4099
  return [];
3970
4100
  }
3971
4101
  return readdirSync(migrationsDir).sort((left, right) => left.localeCompare(right));
@@ -4153,12 +4283,12 @@ function registerDbMigrationsCommand(dbCmd2) {
4153
4283
  migration.version,
4154
4284
  migration.name
4155
4285
  );
4156
- const filePath = join9(migrationsDir, filename);
4157
- if (existingLocalVersions.has(migration.version) || existsSync5(filePath)) {
4286
+ const filePath = join10(migrationsDir, filename);
4287
+ if (existingLocalVersions.has(migration.version) || existsSync6(filePath)) {
4158
4288
  skippedFiles.push(filename);
4159
4289
  continue;
4160
4290
  }
4161
- writeFileSync4(filePath, formatMigrationSql(migration.statements));
4291
+ writeFileSync5(filePath, formatMigrationSql(migration.statements));
4162
4292
  createdFiles.push(filename);
4163
4293
  existingLocalVersions.add(migration.version);
4164
4294
  }
@@ -4196,9 +4326,9 @@ function registerDbMigrationsCommand(dbCmd2) {
4196
4326
  );
4197
4327
  const filename = buildMigrationFilename(nextVersion, migrationName);
4198
4328
  const migrationsDir = ensureMigrationsDir();
4199
- const filePath = join9(migrationsDir, filename);
4329
+ const filePath = join10(migrationsDir, filename);
4200
4330
  try {
4201
- writeFileSync4(filePath, "", { flag: "wx" });
4331
+ writeFileSync5(filePath, "", { flag: "wx" });
4202
4332
  } catch (error) {
4203
4333
  if (error.code === "EEXIST") {
4204
4334
  throw new CLIError(`Migration file already exists: ${filename}`);
@@ -4254,11 +4384,11 @@ function registerDbMigrationsCommand(dbCmd2) {
4254
4384
  `Migration ${targetMigration.filename} is not the next pending local migration. Apply ${earlierPendingMigration.filename} first, or fix/delete it locally if it is invalid or no longer needed.`
4255
4385
  );
4256
4386
  }
4257
- const filePath = join9(getMigrationsDir(), targetMigration.filename);
4258
- if (!existsSync5(filePath)) {
4387
+ const filePath = join10(getMigrationsDir(), targetMigration.filename);
4388
+ if (!existsSync6(filePath)) {
4259
4389
  throw new CLIError(`Local migration file not found: ${targetMigration.filename}`);
4260
4390
  }
4261
- const sql = readFileSync4(filePath, "utf-8");
4391
+ const sql = readFileSync5(filePath, "utf-8");
4262
4392
  if (!sql.trim()) {
4263
4393
  throw new CLIError(`Migration file is empty: ${targetMigration.filename}`);
4264
4394
  }
@@ -4320,11 +4450,11 @@ function registerDbMigrationsCommand(dbCmd2) {
4320
4450
  }
4321
4451
  }
4322
4452
  for (const migration of migrationsToApply) {
4323
- const filePath = join9(getMigrationsDir(), migration.filename);
4324
- if (!existsSync5(filePath)) {
4453
+ const filePath = join10(getMigrationsDir(), migration.filename);
4454
+ if (!existsSync6(filePath)) {
4325
4455
  throw new CLIError(`Local migration file not found: ${migration.filename}`);
4326
4456
  }
4327
- const sql = readFileSync4(filePath, "utf-8");
4457
+ const sql = readFileSync5(filePath, "utf-8");
4328
4458
  if (!sql.trim()) {
4329
4459
  throw new CLIError(`Migration file is empty: ${migration.filename}`);
4330
4460
  }
@@ -4553,21 +4683,23 @@ function registerFunctionsCommands(functionsCmd2) {
4553
4683
  }
4554
4684
 
4555
4685
  // src/commands/functions/deploy.ts
4556
- import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
4557
- import { join as join10 } from "path";
4686
+ import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
4687
+ function resolveDeployFilePath(opts) {
4688
+ if (!opts.file) {
4689
+ throw new CLIError("Missing required option: --file <path>");
4690
+ }
4691
+ if (!existsSync7(opts.file)) {
4692
+ throw new CLIError(`Source file not found: ${opts.file}`);
4693
+ }
4694
+ return opts.file;
4695
+ }
4558
4696
  function registerFunctionsDeployCommand(functionsCmd2) {
4559
4697
  functionsCmd2.command("deploy <slug>").description("Deploy an edge function (create or update)").option("--file <path>", "Path to the function source file").option("--name <name>", "Function display name").option("--description <desc>", "Function description").action(async (slug, opts, cmd) => {
4560
4698
  const { json } = getRootOpts(cmd);
4561
4699
  try {
4562
4700
  await requireAuth();
4563
- const filePath = opts.file ?? join10(process.cwd(), "insforge", "functions", slug, "index.ts");
4564
- if (!existsSync6(filePath)) {
4565
- throw new CLIError(
4566
- `Source file not found: ${filePath}
4567
- Specify --file <path> or create ${join10("insforge", "functions", slug, "index.ts")}`
4568
- );
4569
- }
4570
- const code = readFileSync5(filePath, "utf-8");
4701
+ const filePath = resolveDeployFilePath(opts);
4702
+ const code = readFileSync6(filePath, "utf-8");
4571
4703
  const name = opts.name ?? slug;
4572
4704
  const description = opts.description ?? "";
4573
4705
  let exists = false;
@@ -4688,7 +4820,7 @@ function registerFunctionsCodeCommand(functionsCmd2) {
4688
4820
  }
4689
4821
 
4690
4822
  // src/commands/functions/delete.ts
4691
- import * as clack14 from "@clack/prompts";
4823
+ import * as clack15 from "@clack/prompts";
4692
4824
  function registerFunctionsDeleteCommand(functionsCmd2) {
4693
4825
  functionsCmd2.command("delete <slug>").description("Delete an edge function").action(async (slug, _opts, cmd) => {
4694
4826
  const { json, yes } = getRootOpts(cmd);
@@ -4699,7 +4831,7 @@ function registerFunctionsDeleteCommand(functionsCmd2) {
4699
4831
  message: `Delete function "${slug}"? This cannot be undone.`
4700
4832
  });
4701
4833
  if (isCancel2(confirmed) || !confirmed) {
4702
- clack14.log.info("Cancelled.");
4834
+ clack15.log.info("Cancelled.");
4703
4835
  return;
4704
4836
  }
4705
4837
  }
@@ -4754,7 +4886,7 @@ function registerStorageBucketsCommand(storageCmd2) {
4754
4886
  }
4755
4887
 
4756
4888
  // src/commands/storage/upload.ts
4757
- import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
4889
+ import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
4758
4890
  import { basename as basename6 } from "path";
4759
4891
  function registerStorageUploadCommand(storageCmd2) {
4760
4892
  storageCmd2.command("upload <file>").description("Upload a file to a storage bucket").requiredOption("--bucket <name>", "Target bucket name").option("--key <objectKey>", "Object key (defaults to filename)").action(async (file, opts, cmd) => {
@@ -4763,10 +4895,10 @@ function registerStorageUploadCommand(storageCmd2) {
4763
4895
  await requireAuth();
4764
4896
  const config = getProjectConfig();
4765
4897
  if (!config) throw new ProjectNotLinkedError();
4766
- if (!existsSync7(file)) {
4898
+ if (!existsSync8(file)) {
4767
4899
  throw new CLIError(`File not found: ${file}`);
4768
4900
  }
4769
- const fileContent = readFileSync6(file);
4901
+ const fileContent = readFileSync7(file);
4770
4902
  const objectKey = opts.key ?? basename6(file);
4771
4903
  const bucketName = opts.bucket;
4772
4904
  const formData = new FormData();
@@ -4797,7 +4929,7 @@ function registerStorageUploadCommand(storageCmd2) {
4797
4929
  }
4798
4930
 
4799
4931
  // src/commands/storage/download.ts
4800
- import { writeFileSync as writeFileSync5 } from "fs";
4932
+ import { writeFileSync as writeFileSync6 } from "fs";
4801
4933
  import { join as join11, basename as basename7 } from "path";
4802
4934
  function registerStorageDownloadCommand(storageCmd2) {
4803
4935
  storageCmd2.command("download <objectKey>").description("Download a file from a storage bucket").requiredOption("--bucket <name>", "Source bucket name").option("--output <path>", "Output file path (defaults to current directory)").action(async (objectKey, opts, cmd) => {
@@ -4819,7 +4951,7 @@ function registerStorageDownloadCommand(storageCmd2) {
4819
4951
  }
4820
4952
  const buffer = Buffer.from(await res.arrayBuffer());
4821
4953
  const outputPath = opts.output ?? join11(process.cwd(), basename7(objectKey));
4822
- writeFileSync5(outputPath, buffer);
4954
+ writeFileSync6(outputPath, buffer);
4823
4955
  if (json) {
4824
4956
  outputJson({ success: true, path: outputPath, size: buffer.length });
4825
4957
  } else {
@@ -5949,16 +6081,16 @@ function registerComputeEventsCommand(computeCmd2) {
5949
6081
  }
5950
6082
 
5951
6083
  // src/commands/compute/deploy.ts
5952
- import { existsSync as existsSync9 } from "fs";
6084
+ import { existsSync as existsSync10 } from "fs";
5953
6085
  import { join as join13, resolve as resolve4 } from "path";
5954
6086
 
5955
6087
  // src/lib/env-file.ts
5956
- import { readFileSync as readFileSync7 } from "fs";
6088
+ import { readFileSync as readFileSync8 } from "fs";
5957
6089
  var ENV_KEY_REGEX2 = /^[A-Z_][A-Z0-9_]*$/;
5958
6090
  function parseEnvFile(path6) {
5959
6091
  let raw;
5960
6092
  try {
5961
- raw = readFileSync7(path6, "utf-8");
6093
+ raw = readFileSync8(path6, "utf-8");
5962
6094
  } catch (err) {
5963
6095
  const msg = err instanceof Error ? err.message : String(err);
5964
6096
  throw new CLIError(`Could not read --env-file at ${path6}: ${msg}`);
@@ -5994,7 +6126,7 @@ function parseEnvFile(path6) {
5994
6126
 
5995
6127
  // src/lib/flyctl.ts
5996
6128
  import { spawn, spawnSync } from "child_process";
5997
- import { existsSync as existsSync8, writeFileSync as writeFileSync6, unlinkSync as unlinkSync3 } from "fs";
6129
+ import { existsSync as existsSync9, writeFileSync as writeFileSync7, unlinkSync as unlinkSync3 } from "fs";
5998
6130
  import { join as join12 } from "path";
5999
6131
  function ensureFlyctlAvailable() {
6000
6132
  const r = spawnSync("flyctl", ["version"], {
@@ -6009,7 +6141,7 @@ function ensureFlyctlAvailable() {
6009
6141
  }
6010
6142
  function ensureFlyTomlStub(opts) {
6011
6143
  const path6 = join12(opts.dir, "fly.toml");
6012
- if (existsSync8(path6)) {
6144
+ if (existsSync9(path6)) {
6013
6145
  return () => {
6014
6146
  };
6015
6147
  }
@@ -6026,7 +6158,7 @@ primary_region = "${opts.region}"
6026
6158
  auto_start_machines = true
6027
6159
  min_machines_running = 0
6028
6160
  `;
6029
- writeFileSync6(path6, stub, "utf8");
6161
+ writeFileSync7(path6, stub, "utf8");
6030
6162
  return () => {
6031
6163
  try {
6032
6164
  unlinkSync3(path6);
@@ -6197,7 +6329,7 @@ function registerComputeDeployCommand(computeCmd2) {
6197
6329
  }
6198
6330
  const absDir = resolve4(dir);
6199
6331
  const dockerfilePath = join13(absDir, "Dockerfile");
6200
- if (!existsSync9(dockerfilePath)) {
6332
+ if (!existsSync10(dockerfilePath)) {
6201
6333
  throw new CLIError(
6202
6334
  `No Dockerfile at ${dockerfilePath}.
6203
6335
  Either:
@@ -6432,7 +6564,7 @@ function formatSize2(gb) {
6432
6564
 
6433
6565
  // src/commands/diagnose/index.ts
6434
6566
  import * as os from "os";
6435
- import * as clack15 from "@clack/prompts";
6567
+ import * as clack16 from "@clack/prompts";
6436
6568
 
6437
6569
  // src/commands/diagnose/metrics.ts
6438
6570
  var METRIC_LABELS = {
@@ -6973,10 +7105,10 @@ function registerDiagnoseCommands(diagnoseCmd2) {
6973
7105
  if (question.length === 0 || question.length > 2e3) {
6974
7106
  throw new CLIError("Question must be between 1 and 2000 characters.");
6975
7107
  }
6976
- const s = !json ? clack15.spinner() : null;
7108
+ const s = !json ? clack16.spinner() : null;
6977
7109
  s?.start("Collecting diagnostic data...");
6978
7110
  const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
6979
- const cliVersion = "0.1.81";
7111
+ const cliVersion = "0.1.84";
6980
7112
  s?.stop("Data collected");
6981
7113
  if (!json) {
6982
7114
  console.log(`
@@ -7109,9 +7241,9 @@ function registerDiagnoseCommands(diagnoseCmd2) {
7109
7241
  void 0,
7110
7242
  apiUrl
7111
7243
  );
7112
- clack15.log.success("Thanks for your feedback!");
7244
+ clack16.log.success("Thanks for your feedback!");
7113
7245
  } catch {
7114
- clack15.log.warn("Failed to submit rating.");
7246
+ clack16.log.warn("Failed to submit rating.");
7115
7247
  }
7116
7248
  }
7117
7249
  }
@@ -8340,7 +8472,7 @@ function registerPaymentsCommands(paymentsCmd2) {
8340
8472
  }
8341
8473
 
8342
8474
  // src/commands/posthog/setup.ts
8343
- import * as clack16 from "@clack/prompts";
8475
+ import * as clack17 from "@clack/prompts";
8344
8476
  import pc3 from "picocolors";
8345
8477
 
8346
8478
  // src/lib/api/posthog.ts
@@ -8525,6 +8657,8 @@ function registerPosthogSetupCommand(program2) {
8525
8657
  }
8526
8658
  } catch (err) {
8527
8659
  handleError(err, json);
8660
+ } finally {
8661
+ await shutdownAnalytics();
8528
8662
  }
8529
8663
  });
8530
8664
  }
@@ -8537,13 +8671,14 @@ async function runSetup(opts) {
8537
8671
  if (!token) {
8538
8672
  throw new AuthError("Not logged in. Run `insforge login` first.");
8539
8673
  }
8674
+ trackPosthog("setup", proj);
8540
8675
  if (!opts.json) {
8541
- clack16.intro("PostHog setup");
8676
+ clack17.intro("PostHog setup");
8542
8677
  outputSuccess(`Linked to InsForge project: ${proj.project_name} (${proj.project_id})`);
8543
8678
  }
8544
8679
  const dashboardConnection = await ensureDashboardConnection(proj.project_id, token, opts);
8545
8680
  if (!opts.json) {
8546
- clack16.note(
8681
+ clack17.note(
8547
8682
  `Run this in your terminal to wire PostHog into your app code:
8548
8683
 
8549
8684
  ${WIZARD_COMMAND}
@@ -8581,13 +8716,13 @@ async function runConnectFlow(projectId, token, authorizeUrl, opts) {
8581
8716
  `);
8582
8717
  process.stderr.write("Your browser should open automatically. If not, copy the URL above.\n");
8583
8718
  } else {
8584
- clack16.log.info("PostHog is not yet connected to your InsForge dashboard.");
8719
+ clack17.log.info("PostHog is not yet connected to your InsForge dashboard.");
8585
8720
  if (opts.skipBrowser) {
8586
- clack16.log.info(`Open this URL to authorize PostHog:
8721
+ clack17.log.info(`Open this URL to authorize PostHog:
8587
8722
  ${pc3.cyan(pc3.underline(authorizeUrl))}`);
8588
8723
  } else {
8589
- clack16.log.info("Opening browser to authorize PostHog...");
8590
- clack16.log.info(`If browser doesn't open, visit:
8724
+ clack17.log.info("Opening browser to authorize PostHog...");
8725
+ clack17.log.info(`If browser doesn't open, visit:
8591
8726
  ${pc3.cyan(pc3.underline(authorizeUrl))}`);
8592
8727
  }
8593
8728
  }
@@ -8598,11 +8733,11 @@ ${pc3.cyan(pc3.underline(authorizeUrl))}`);
8598
8733
  } catch {
8599
8734
  }
8600
8735
  }
8601
- const spinner11 = !opts.json && isInteractive ? clack16.spinner() : null;
8736
+ const spinner11 = !opts.json && isInteractive ? clack17.spinner() : null;
8602
8737
  if (spinner11) {
8603
8738
  spinner11.start("Waiting for InsForge dashboard connection... (timeout: 15 minutes)");
8604
8739
  } else if (!opts.json) {
8605
- clack16.log.info("Waiting for InsForge dashboard connection (up to 15 minutes)...");
8740
+ clack17.log.info("Waiting for InsForge dashboard connection (up to 15 minutes)...");
8606
8741
  }
8607
8742
  try {
8608
8743
  await pollPosthogConnection(
@@ -8626,20 +8761,20 @@ ${pc3.cyan(pc3.underline(authorizeUrl))}`);
8626
8761
  if (spinner11) {
8627
8762
  spinner11.stop("InsForge dashboard connection received.");
8628
8763
  } else if (!opts.json) {
8629
- clack16.log.success("InsForge dashboard connection received.");
8764
+ clack17.log.success("InsForge dashboard connection received.");
8630
8765
  }
8631
8766
  } catch (err) {
8632
8767
  if (spinner11) {
8633
8768
  spinner11.stop("InsForge dashboard connection wait failed.");
8634
8769
  } else if (!opts.json) {
8635
- clack16.log.error("InsForge dashboard connection wait failed.");
8770
+ clack17.log.error("InsForge dashboard connection wait failed.");
8636
8771
  }
8637
8772
  throw err;
8638
8773
  }
8639
8774
  }
8640
8775
 
8641
8776
  // src/commands/config/export.ts
8642
- import { writeFileSync as writeFileSync7, existsSync as existsSync10 } from "fs";
8777
+ import { writeFileSync as writeFileSync8, existsSync as existsSync11 } from "fs";
8643
8778
  import { resolve as resolve5 } from "path";
8644
8779
  import * as p from "@clack/prompts";
8645
8780
  import pc4 from "picocolors";
@@ -9125,7 +9260,7 @@ function registerConfigExportCommand(cfg) {
9125
9260
  projectConfig = getProjectConfig();
9126
9261
  await requireAuth();
9127
9262
  const target = resolve5(process.cwd(), opts.out);
9128
- if (existsSync10(target) && !opts.force) {
9263
+ if (existsSync11(target) && !opts.force) {
9129
9264
  if (json) {
9130
9265
  throw new CLIError(
9131
9266
  `${opts.out} exists. Re-run with --force to overwrite.`,
@@ -9152,7 +9287,7 @@ function registerConfigExportCommand(cfg) {
9152
9287
  const raw = await res.json();
9153
9288
  const { config, skipped } = configFromMetadata(raw);
9154
9289
  const toml = stringifyConfigToml(config);
9155
- writeFileSync7(target, toml, "utf8");
9290
+ writeFileSync8(target, toml, "utf8");
9156
9291
  if (json) {
9157
9292
  console.log(JSON.stringify({ written: target, config, skipped }, null, 2));
9158
9293
  } else {
@@ -9188,7 +9323,7 @@ function registerConfigExportCommand(cfg) {
9188
9323
  }
9189
9324
 
9190
9325
  // src/commands/config/plan.ts
9191
- import { readFileSync as readFileSync8 } from "fs";
9326
+ import { readFileSync as readFileSync9 } from "fs";
9192
9327
  import { resolve as resolve6 } from "path";
9193
9328
  import pc5 from "picocolors";
9194
9329
 
@@ -9496,7 +9631,7 @@ function registerConfigPlanCommand(cfg) {
9496
9631
  projectConfig = getProjectConfig();
9497
9632
  await requireAuth();
9498
9633
  const tomlPath = resolve6(process.cwd(), opts.file);
9499
- const tomlSource = readFileSync8(tomlPath, "utf8");
9634
+ const tomlSource = readFileSync9(tomlPath, "utf8");
9500
9635
  const file = parseConfigToml(tomlSource);
9501
9636
  const res = await ossFetch("/api/metadata");
9502
9637
  const raw = await res.json();
@@ -9540,7 +9675,7 @@ function registerConfigPlanCommand(cfg) {
9540
9675
  }
9541
9676
 
9542
9677
  // src/commands/config/apply.ts
9543
- import { readFileSync as readFileSync9 } from "fs";
9678
+ import { readFileSync as readFileSync10 } from "fs";
9544
9679
  import { resolve as resolve7 } from "path";
9545
9680
  import * as p2 from "@clack/prompts";
9546
9681
  import pc6 from "picocolors";
@@ -9552,7 +9687,7 @@ function registerConfigApplyCommand(cfg) {
9552
9687
  projectConfig = getProjectConfig();
9553
9688
  await requireAuth();
9554
9689
  const tomlPath = resolve7(process.cwd(), opts.file);
9555
- const tomlSource = readFileSync9(tomlPath, "utf8");
9690
+ const tomlSource = readFileSync10(tomlPath, "utf8");
9556
9691
  const file = parseConfigToml(tomlSource);
9557
9692
  const res = await ossFetch("/api/metadata");
9558
9693
  const raw = await res.json();
@@ -9741,9 +9876,9 @@ function registerConfigCommand(program2) {
9741
9876
  }
9742
9877
 
9743
9878
  // src/commands/ai/setup.ts
9744
- import { appendFileSync as appendFileSync2, existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
9879
+ import { appendFileSync as appendFileSync2, existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
9745
9880
  import { isAbsolute, join as join14, relative as relative3, resolve as resolve8 } from "path";
9746
- import * as clack17 from "@clack/prompts";
9881
+ import * as clack18 from "@clack/prompts";
9747
9882
  import pc7 from "picocolors";
9748
9883
 
9749
9884
  // src/lib/api/ai.ts
@@ -9764,7 +9899,7 @@ async function getOpenRouterApiKey() {
9764
9899
  }
9765
9900
 
9766
9901
  // src/lib/env-writer.ts
9767
- import { existsSync as existsSync11, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
9902
+ import { existsSync as existsSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
9768
9903
  var KEY_LINE_RE = (key) => (
9769
9904
  // Match `KEY=...` at the start of a line (allowing leading whitespace).
9770
9905
  // Captures the value side; we only need the value portion to compare.
@@ -9779,8 +9914,8 @@ function stripQuotes(v) {
9779
9914
  return hash >= 0 ? t.slice(0, hash).trimEnd() : t;
9780
9915
  }
9781
9916
  function upsertEnvFile(path6, entries) {
9782
- const exists = existsSync11(path6);
9783
- let content = exists ? readFileSync10(path6, "utf-8") : "";
9917
+ const exists = existsSync12(path6);
9918
+ let content = exists ? readFileSync11(path6, "utf-8") : "";
9784
9919
  const result = { added: [], skipped: [], mismatched: [] };
9785
9920
  const additions = [];
9786
9921
  for (const [key, value] of Object.entries(entries)) {
@@ -9803,7 +9938,7 @@ function upsertEnvFile(path6, entries) {
9803
9938
  content += "\n";
9804
9939
  }
9805
9940
  content += additions.join("\n") + "\n";
9806
- writeFileSync8(path6, content);
9941
+ writeFileSync9(path6, content);
9807
9942
  } else if (!exists) {
9808
9943
  }
9809
9944
  return result;
@@ -9836,10 +9971,10 @@ async function runAiSetup(opts) {
9836
9971
  throw new ProjectNotLinkedError();
9837
9972
  }
9838
9973
  if (!opts.json) {
9839
- clack17.intro("AI setup");
9974
+ clack18.intro("AI setup");
9840
9975
  outputSuccess(`Linked to InsForge project: ${project.project_name} (${project.project_id})`);
9841
9976
  }
9842
- const spinner11 = !opts.json && isInteractive ? clack17.spinner() : null;
9977
+ const spinner11 = !opts.json && isInteractive ? clack18.spinner() : null;
9843
9978
  spinner11?.start("Fetching OpenRouter key...");
9844
9979
  let key;
9845
9980
  try {
@@ -9872,7 +10007,7 @@ async function runAiSetup(opts) {
9872
10007
  outputInfo(pc7.dim(`${envLabel}: ${update.skipped.join(", ")} already set (matching) - left as-is.`));
9873
10008
  }
9874
10009
  for (const m of update.mismatched) {
9875
- clack17.log.warn(
10010
+ clack18.log.warn(
9876
10011
  `${envLabel} already has ${m.key}; left existing value untouched. Remove it or pass --env-file to write elsewhere.`
9877
10012
  );
9878
10013
  }
@@ -9880,7 +10015,7 @@ async function runAiSetup(opts) {
9880
10015
  outputInfo(pc7.dim("Added .env*.local to .gitignore."));
9881
10016
  }
9882
10017
  if (!isLocalEnvFile(envFile)) {
9883
- clack17.log.warn(
10018
+ clack18.log.warn(
9884
10019
  `${envLabel} may be committed unless it is listed in .gitignore. Keep ${OPENROUTER_ENV_KEY} server-only.`
9885
10020
  );
9886
10021
  }
@@ -9888,7 +10023,7 @@ async function runAiSetup(opts) {
9888
10023
  outputInfo("Use this key only from server-side code as process.env.OPENROUTER_API_KEY.");
9889
10024
  outputInfo("For deployment, add OPENROUTER_API_KEY to your hosting provider environment.");
9890
10025
  outputInfo(`Do not rename it to ${pc7.bold("NEXT_PUBLIC_")}, ${pc7.bold("VITE_")}, or ${pc7.bold("PUBLIC_")}.`);
9891
- clack17.outro("Done.");
10026
+ clack18.outro("Done.");
9892
10027
  }
9893
10028
  return {
9894
10029
  envFile: envLabel,
@@ -9919,7 +10054,7 @@ function ensureLocalEnvIgnored(cwd, envFile) {
9919
10054
  return false;
9920
10055
  }
9921
10056
  const gitignorePath = join14(cwd, ".gitignore");
9922
- const existing = existsSync12(gitignorePath) ? readFileSync11(gitignorePath, "utf-8") : "";
10057
+ const existing = existsSync13(gitignorePath) ? readFileSync12(gitignorePath, "utf-8") : "";
9923
10058
  const lines = new Set(existing.split(/\r?\n/).map((line) => line.trim()));
9924
10059
  const envBasename = envFile.replace(/\\/g, "/").split("/").pop() ?? envFile;
9925
10060
  if (lines.has(".env*") || lines.has(".env.*") || lines.has(".env*.local") || lines.has(".env.local") && envBasename === ".env.local") {
@@ -9940,7 +10075,7 @@ function registerAiCommands(aiCmd2) {
9940
10075
 
9941
10076
  // src/index.ts
9942
10077
  var __dirname = dirname2(fileURLToPath(import.meta.url));
9943
- var pkg = JSON.parse(readFileSync12(join15(__dirname, "../package.json"), "utf-8"));
10078
+ var pkg = JSON.parse(readFileSync13(join15(__dirname, "../package.json"), "utf-8"));
9944
10079
  var INSFORGE_LOGO = `
9945
10080
  \u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
9946
10081
  \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
@@ -10051,7 +10186,7 @@ async function showInteractiveMenu() {
10051
10186
  } catch {
10052
10187
  }
10053
10188
  console.log(INSFORGE_LOGO);
10054
- clack18.intro(`InsForge CLI v${pkg.version}`);
10189
+ clack19.intro(`InsForge CLI v${pkg.version}`);
10055
10190
  const options = [];
10056
10191
  if (!isLoggedIn) {
10057
10192
  options.push({ value: "login", label: "Log in to InsForge" });
@@ -10072,7 +10207,7 @@ async function showInteractiveMenu() {
10072
10207
  options
10073
10208
  });
10074
10209
  if (isCancel2(action)) {
10075
- clack18.cancel("Bye!");
10210
+ clack19.cancel("Bye!");
10076
10211
  process.exit(0);
10077
10212
  }
10078
10213
  switch (action) {