@insforge/cli 0.1.81 → 0.1.82

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { readFileSync as readFileSync12 } from "fs";
5
- import { join as join15, dirname as dirname2 } from "path";
5
+ import { join as join14, dirname as dirname2 } from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  import { Command } from "commander";
8
8
  import * as clack18 from "@clack/prompts";
@@ -2629,6 +2629,7 @@ var execAsync2 = promisify3(exec2);
2629
2629
  var execFileAsync2 = promisify3(execFile2);
2630
2630
  var SAFE_REPO_PATTERN2 = /^(https?:\/\/|git@)[A-Za-z0-9._:/@~+-]+(\.git)?$/;
2631
2631
  var SAFE_BRANCH_PATTERN2 = /^[A-Za-z0-9._/-]+$/;
2632
+ var SAFE_MARKETPLACE_SLUG = /^[a-z0-9][a-z0-9-]{0,99}$/;
2632
2633
  async function waitForProjectActive(projectId, apiUrl, timeoutMs = 12e4) {
2633
2634
  const start = Date.now();
2634
2635
  while (Date.now() - start < timeoutMs) {
@@ -2737,9 +2738,18 @@ async function copyDir(src, dest) {
2737
2738
  }
2738
2739
  }
2739
2740
  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) => {
2741
+ 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
2742
  const { json, apiUrl } = getRootOpts(cmd);
2742
2743
  try {
2744
+ if (opts.marketplace && opts.template) {
2745
+ throw new CLIError("--marketplace and --template are mutually exclusive");
2746
+ }
2747
+ if (opts.marketplace && !SAFE_MARKETPLACE_SLUG.test(opts.marketplace)) {
2748
+ throw new CLIError(
2749
+ `Invalid --marketplace slug "${opts.marketplace}". Slugs must match ${SAFE_MARKETPLACE_SLUG}.
2750
+ Browse available templates: https://insforge.dev/templates`
2751
+ );
2752
+ }
2743
2753
  await requireAuth(apiUrl, false);
2744
2754
  if (!json) {
2745
2755
  await animateBanner();
@@ -2796,6 +2806,9 @@ function registerCreateCommand(program2) {
2796
2806
  if (template && !validTemplates.includes(template)) {
2797
2807
  throw new CLIError(`Invalid template "${template}". Valid options: ${validTemplates.join(", ")}`);
2798
2808
  }
2809
+ if (opts.marketplace) {
2810
+ template = opts.marketplace;
2811
+ }
2799
2812
  if (!template) {
2800
2813
  if (json) {
2801
2814
  template = "empty";
@@ -2886,7 +2899,19 @@ function registerCreateCommand(program2) {
2886
2899
  projectLinked = true;
2887
2900
  s?.stop(`Project "${project.name}" created and linked`);
2888
2901
  const githubTemplates = ["chatbot", "crm", "e-commerce", "nextjs", "react", "todo"];
2889
- if (githubTemplates.includes(template)) {
2902
+ if (opts.marketplace) {
2903
+ const downloaded = await downloadGitHubTemplate(
2904
+ opts.marketplace,
2905
+ projectConfig,
2906
+ json
2907
+ );
2908
+ if (downloaded) {
2909
+ void reportMarketplaceDownload(
2910
+ opts.marketplace,
2911
+ apiUrl ?? "https://api.insforge.dev"
2912
+ );
2913
+ }
2914
+ } else if (githubTemplates.includes(template)) {
2890
2915
  await downloadGitHubTemplate(template, projectConfig, json);
2891
2916
  } else if (hasTemplate) {
2892
2917
  await downloadTemplate(template, projectConfig, projectName, json, apiUrl);
@@ -3151,6 +3176,7 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
3151
3176
  }
3152
3177
  }
3153
3178
  }
3179
+ return true;
3154
3180
  } catch (err) {
3155
3181
  s?.stop(`${templateName} template download failed`);
3156
3182
  const msg = `Failed to download ${templateName} template: ${err.message}`;
@@ -3160,11 +3186,25 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
3160
3186
  clack12.log.warn(msg);
3161
3187
  clack12.log.info("You can manually clone from: https://github.com/InsForge/insforge-templates");
3162
3188
  }
3189
+ return false;
3163
3190
  } finally {
3164
3191
  await fs4.rm(tempDir, { recursive: true, force: true }).catch(() => {
3165
3192
  });
3166
3193
  }
3167
3194
  }
3195
+ async function reportMarketplaceDownload(slug, apiUrl) {
3196
+ try {
3197
+ const res = await fetch(`${apiUrl}/templates/v1/${encodeURIComponent(slug)}/downloads`, {
3198
+ method: "POST",
3199
+ headers: { "Content-Type": "application/json" },
3200
+ body: "{}"
3201
+ });
3202
+ if (!res.ok) {
3203
+ return;
3204
+ }
3205
+ } catch {
3206
+ }
3207
+ }
3168
3208
 
3169
3209
  // src/commands/projects/link.ts
3170
3210
  var execAsync3 = promisify4(exec3);
@@ -4554,19 +4594,21 @@ function registerFunctionsCommands(functionsCmd2) {
4554
4594
 
4555
4595
  // src/commands/functions/deploy.ts
4556
4596
  import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
4557
- import { join as join10 } from "path";
4597
+ function resolveDeployFilePath(opts) {
4598
+ if (!opts.file) {
4599
+ throw new CLIError("Missing required option: --file <path>");
4600
+ }
4601
+ if (!existsSync6(opts.file)) {
4602
+ throw new CLIError(`Source file not found: ${opts.file}`);
4603
+ }
4604
+ return opts.file;
4605
+ }
4558
4606
  function registerFunctionsDeployCommand(functionsCmd2) {
4559
4607
  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
4608
  const { json } = getRootOpts(cmd);
4561
4609
  try {
4562
4610
  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
- }
4611
+ const filePath = resolveDeployFilePath(opts);
4570
4612
  const code = readFileSync5(filePath, "utf-8");
4571
4613
  const name = opts.name ?? slug;
4572
4614
  const description = opts.description ?? "";
@@ -4798,7 +4840,7 @@ function registerStorageUploadCommand(storageCmd2) {
4798
4840
 
4799
4841
  // src/commands/storage/download.ts
4800
4842
  import { writeFileSync as writeFileSync5 } from "fs";
4801
- import { join as join11, basename as basename7 } from "path";
4843
+ import { join as join10, basename as basename7 } from "path";
4802
4844
  function registerStorageDownloadCommand(storageCmd2) {
4803
4845
  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) => {
4804
4846
  const { json } = getRootOpts(cmd);
@@ -4818,7 +4860,7 @@ function registerStorageDownloadCommand(storageCmd2) {
4818
4860
  throw new CLIError(err.error ?? `Download failed: ${res.status}`);
4819
4861
  }
4820
4862
  const buffer = Buffer.from(await res.arrayBuffer());
4821
- const outputPath = opts.output ?? join11(process.cwd(), basename7(objectKey));
4863
+ const outputPath = opts.output ?? join10(process.cwd(), basename7(objectKey));
4822
4864
  writeFileSync5(outputPath, buffer);
4823
4865
  if (json) {
4824
4866
  outputJson({ success: true, path: outputPath, size: buffer.length });
@@ -5950,7 +5992,7 @@ function registerComputeEventsCommand(computeCmd2) {
5950
5992
 
5951
5993
  // src/commands/compute/deploy.ts
5952
5994
  import { existsSync as existsSync9 } from "fs";
5953
- import { join as join13, resolve as resolve4 } from "path";
5995
+ import { join as join12, resolve as resolve4 } from "path";
5954
5996
 
5955
5997
  // src/lib/env-file.ts
5956
5998
  import { readFileSync as readFileSync7 } from "fs";
@@ -5995,7 +6037,7 @@ function parseEnvFile(path6) {
5995
6037
  // src/lib/flyctl.ts
5996
6038
  import { spawn, spawnSync } from "child_process";
5997
6039
  import { existsSync as existsSync8, writeFileSync as writeFileSync6, unlinkSync as unlinkSync3 } from "fs";
5998
- import { join as join12 } from "path";
6040
+ import { join as join11 } from "path";
5999
6041
  function ensureFlyctlAvailable() {
6000
6042
  const r = spawnSync("flyctl", ["version"], {
6001
6043
  encoding: "utf8",
@@ -6008,7 +6050,7 @@ function ensureFlyctlAvailable() {
6008
6050
  }
6009
6051
  }
6010
6052
  function ensureFlyTomlStub(opts) {
6011
- const path6 = join12(opts.dir, "fly.toml");
6053
+ const path6 = join11(opts.dir, "fly.toml");
6012
6054
  if (existsSync8(path6)) {
6013
6055
  return () => {
6014
6056
  };
@@ -6196,7 +6238,7 @@ function registerComputeDeployCommand(computeCmd2) {
6196
6238
  return;
6197
6239
  }
6198
6240
  const absDir = resolve4(dir);
6199
- const dockerfilePath = join13(absDir, "Dockerfile");
6241
+ const dockerfilePath = join12(absDir, "Dockerfile");
6200
6242
  if (!existsSync9(dockerfilePath)) {
6201
6243
  throw new CLIError(
6202
6244
  `No Dockerfile at ${dockerfilePath}.
@@ -6976,7 +7018,7 @@ function registerDiagnoseCommands(diagnoseCmd2) {
6976
7018
  const s = !json ? clack15.spinner() : null;
6977
7019
  s?.start("Collecting diagnostic data...");
6978
7020
  const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
6979
- const cliVersion = "0.1.81";
7021
+ const cliVersion = "0.1.82";
6980
7022
  s?.stop("Data collected");
6981
7023
  if (!json) {
6982
7024
  console.log(`
@@ -9742,7 +9784,7 @@ function registerConfigCommand(program2) {
9742
9784
 
9743
9785
  // src/commands/ai/setup.ts
9744
9786
  import { appendFileSync as appendFileSync2, existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
9745
- import { isAbsolute, join as join14, relative as relative3, resolve as resolve8 } from "path";
9787
+ import { isAbsolute, join as join13, relative as relative3, resolve as resolve8 } from "path";
9746
9788
  import * as clack17 from "@clack/prompts";
9747
9789
  import pc7 from "picocolors";
9748
9790
 
@@ -9918,7 +9960,7 @@ function ensureLocalEnvIgnored(cwd, envFile) {
9918
9960
  if (!relEnvPath || relEnvPath.startsWith("..") || isAbsolute(relEnvPath)) {
9919
9961
  return false;
9920
9962
  }
9921
- const gitignorePath = join14(cwd, ".gitignore");
9963
+ const gitignorePath = join13(cwd, ".gitignore");
9922
9964
  const existing = existsSync12(gitignorePath) ? readFileSync11(gitignorePath, "utf-8") : "";
9923
9965
  const lines = new Set(existing.split(/\r?\n/).map((line) => line.trim()));
9924
9966
  const envBasename = envFile.replace(/\\/g, "/").split("/").pop() ?? envFile;
@@ -9940,7 +9982,7 @@ function registerAiCommands(aiCmd2) {
9940
9982
 
9941
9983
  // src/index.ts
9942
9984
  var __dirname = dirname2(fileURLToPath(import.meta.url));
9943
- var pkg = JSON.parse(readFileSync12(join15(__dirname, "../package.json"), "utf-8"));
9985
+ var pkg = JSON.parse(readFileSync12(join14(__dirname, "../package.json"), "utf-8"));
9944
9986
  var INSFORGE_LOGO = `
9945
9987
  \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
9988
  \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