@insforge/cli 0.1.21 → 0.1.24

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 readFileSync6 } from "fs";
5
- import { join as join6, dirname } from "path";
5
+ import { join as join7, dirname } from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  import { Command } from "commander";
8
8
  import * as clack14 from "@clack/prompts";
@@ -360,7 +360,7 @@ async function refreshAccessToken(apiUrl) {
360
360
  }
361
361
 
362
362
  // src/lib/api/platform.ts
363
- async function platformFetch(path3, options = {}, apiUrl) {
363
+ async function platformFetch(path4, options = {}, apiUrl) {
364
364
  const baseUrl = getPlatformApiUrl(apiUrl);
365
365
  const token = getAccessToken();
366
366
  if (!token) {
@@ -371,11 +371,11 @@ async function platformFetch(path3, options = {}, apiUrl) {
371
371
  Authorization: `Bearer ${token}`,
372
372
  ...options.headers ?? {}
373
373
  };
374
- const res = await fetch(`${baseUrl}${path3}`, { ...options, headers });
374
+ const res = await fetch(`${baseUrl}${path4}`, { ...options, headers });
375
375
  if (res.status === 401) {
376
376
  const newToken = await refreshAccessToken(apiUrl);
377
377
  headers.Authorization = `Bearer ${newToken}`;
378
- const retryRes = await fetch(`${baseUrl}${path3}`, { ...options, headers });
378
+ const retryRes = await fetch(`${baseUrl}${path4}`, { ...options, headers });
379
379
  if (!retryRes.ok) {
380
380
  const err = await retryRes.json().catch(() => ({}));
381
381
  throw new CLIError(err.error ?? `Request failed: ${retryRes.status}`, retryRes.status === 403 ? 5 : 1);
@@ -866,14 +866,14 @@ async function getAnonKey() {
866
866
  const data = await res.json();
867
867
  return data.accessToken;
868
868
  }
869
- async function ossFetch(path3, options = {}) {
869
+ async function ossFetch(path4, options = {}) {
870
870
  const config = requireProjectConfig();
871
871
  const headers = {
872
872
  "Content-Type": "application/json",
873
873
  Authorization: `Bearer ${config.api_key}`,
874
874
  ...options.headers ?? {}
875
875
  };
876
- const res = await fetch(`${config.oss_host}${path3}`, { ...options, headers });
876
+ const res = await fetch(`${config.oss_host}${path4}`, { ...options, headers });
877
877
  if (!res.ok) {
878
878
  const err = await res.json().catch(() => ({}));
879
879
  throw new CLIError(err.error ?? `OSS request failed: ${res.status}`);
@@ -1215,8 +1215,8 @@ function registerRecordsCommands(recordsCmd2) {
1215
1215
  if (opts.limit) params.set("limit", String(opts.limit));
1216
1216
  if (opts.offset) params.set("offset", String(opts.offset));
1217
1217
  const query = params.toString();
1218
- const path3 = `/api/database/records/${encodeURIComponent(table)}${query ? `?${query}` : ""}`;
1219
- const res = await ossFetch(path3);
1218
+ const path4 = `/api/database/records/${encodeURIComponent(table)}${query ? `?${query}` : ""}`;
1219
+ const res = await ossFetch(path4);
1220
1220
  const data = await res.json();
1221
1221
  const records = data.data ?? [];
1222
1222
  if (json) {
@@ -1779,13 +1779,38 @@ function registerStorageListObjectsCommand(storageCmd2) {
1779
1779
  import { exec as exec2 } from "child_process";
1780
1780
  import { tmpdir } from "os";
1781
1781
  import { promisify as promisify2 } from "util";
1782
- import * as fs2 from "fs/promises";
1783
- import * as path2 from "path";
1782
+ import * as fs3 from "fs/promises";
1783
+ import * as path3 from "path";
1784
1784
  import * as clack10 from "@clack/prompts";
1785
1785
 
1786
- // src/commands/deployments/deploy.ts
1787
- import * as path from "path";
1786
+ // src/lib/env.ts
1788
1787
  import * as fs from "fs/promises";
1788
+ import * as path from "path";
1789
+ async function readEnvFile(cwd) {
1790
+ const candidates = [".env.local", ".env.production", ".env"];
1791
+ for (const name of candidates) {
1792
+ const filePath = path.join(cwd, name);
1793
+ const exists = await fs.stat(filePath).catch(() => null);
1794
+ if (!exists) continue;
1795
+ const content = await fs.readFile(filePath, "utf-8");
1796
+ const vars = [];
1797
+ for (const line of content.split("\n")) {
1798
+ const trimmed = line.trim();
1799
+ if (!trimmed || trimmed.startsWith("#")) continue;
1800
+ const eqIndex = trimmed.indexOf("=");
1801
+ if (eqIndex === -1) continue;
1802
+ const key = trimmed.slice(0, eqIndex).trim();
1803
+ const value = trimmed.slice(eqIndex + 1).trim();
1804
+ if (key) vars.push({ key, value });
1805
+ }
1806
+ return vars;
1807
+ }
1808
+ return [];
1809
+ }
1810
+
1811
+ // src/commands/deployments/deploy.ts
1812
+ import * as path2 from "path";
1813
+ import * as fs2 from "fs/promises";
1789
1814
  import * as clack9 from "@clack/prompts";
1790
1815
  import archiver from "archiver";
1791
1816
  var POLL_INTERVAL_MS = 5e3;
@@ -1885,8 +1910,8 @@ function registerDeploymentsDeployCommand(deploymentsCmd2) {
1885
1910
  await requireAuth();
1886
1911
  const config = getProjectConfig();
1887
1912
  if (!config) throw new ProjectNotLinkedError();
1888
- const sourceDir = path.resolve(directory ?? ".");
1889
- const stats = await fs.stat(sourceDir).catch(() => null);
1913
+ const sourceDir = path2.resolve(directory ?? ".");
1914
+ const stats = await fs2.stat(sourceDir).catch(() => null);
1890
1915
  if (!stats?.isDirectory()) {
1891
1916
  throw new CLIError(`"${sourceDir}" is not a valid directory.`);
1892
1917
  }
@@ -1955,20 +1980,20 @@ async function waitForProjectActive(projectId, apiUrl, timeoutMs = 12e4) {
1955
1980
  throw new CLIError("Project creation timed out. Check the dashboard for status.");
1956
1981
  }
1957
1982
  async function copyDir(src, dest) {
1958
- const entries = await fs2.readdir(src, { withFileTypes: true });
1983
+ const entries = await fs3.readdir(src, { withFileTypes: true });
1959
1984
  for (const entry of entries) {
1960
- const srcPath = path2.join(src, entry.name);
1961
- const destPath = path2.join(dest, entry.name);
1985
+ const srcPath = path3.join(src, entry.name);
1986
+ const destPath = path3.join(dest, entry.name);
1962
1987
  if (entry.isDirectory()) {
1963
- await fs2.mkdir(destPath, { recursive: true });
1988
+ await fs3.mkdir(destPath, { recursive: true });
1964
1989
  await copyDir(srcPath, destPath);
1965
1990
  } else {
1966
- await fs2.copyFile(srcPath, destPath);
1991
+ await fs3.copyFile(srcPath, destPath);
1967
1992
  }
1968
1993
  }
1969
1994
  }
1970
1995
  function registerCreateCommand(program2) {
1971
- 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, or empty").action(async (opts, cmd) => {
1996
+ 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, or empty").action(async (opts, cmd) => {
1972
1997
  const { json, apiUrl } = getRootOpts(cmd);
1973
1998
  try {
1974
1999
  await requireAuth(apiUrl);
@@ -2017,6 +2042,7 @@ function registerCreateCommand(program2) {
2017
2042
  options: [
2018
2043
  { value: "react", label: "Web app template with React" },
2019
2044
  { value: "nextjs", label: "Web app template with Next.js" },
2045
+ { value: "chatbot", label: "AI Chatbot with Next.js" },
2020
2046
  { value: "empty", label: "Empty project" }
2021
2047
  ]
2022
2048
  });
@@ -2042,7 +2068,9 @@ function registerCreateCommand(program2) {
2042
2068
  saveProjectConfig(projectConfig);
2043
2069
  s?.stop(`Project "${project.name}" created and linked`);
2044
2070
  const hasTemplate = template !== "empty";
2045
- if (hasTemplate) {
2071
+ if (template === "chatbot") {
2072
+ await downloadGitHubTemplate("chatbot", projectConfig, json);
2073
+ } else if (hasTemplate) {
2046
2074
  await downloadTemplate(template, projectConfig, projectName, json, apiUrl);
2047
2075
  }
2048
2076
  await installCliGlobally(json);
@@ -2069,9 +2097,15 @@ function registerCreateCommand(program2) {
2069
2097
  });
2070
2098
  if (!clack10.isCancel(shouldDeploy) && shouldDeploy) {
2071
2099
  try {
2100
+ const envVars = await readEnvFile(process.cwd());
2101
+ const startBody = {};
2102
+ if (envVars.length > 0) {
2103
+ startBody.envVars = envVars;
2104
+ }
2072
2105
  const deploySpinner = clack10.spinner();
2073
2106
  const result = await deployProject({
2074
2107
  sourceDir: process.cwd(),
2108
+ startBody,
2075
2109
  spinner: deploySpinner
2076
2110
  });
2077
2111
  if (result.isReady) {
@@ -2121,9 +2155,9 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
2121
2155
  }
2122
2156
  const tempDir = tmpdir();
2123
2157
  const targetDir = projectName;
2124
- const templatePath = path2.join(tempDir, targetDir);
2158
+ const templatePath = path3.join(tempDir, targetDir);
2125
2159
  try {
2126
- await fs2.rm(templatePath, { recursive: true, force: true });
2160
+ await fs3.rm(templatePath, { recursive: true, force: true });
2127
2161
  } catch {
2128
2162
  }
2129
2163
  const frame = framework === "nextjs" ? "nextjs" : "react";
@@ -2137,7 +2171,7 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
2137
2171
  s?.message("Copying template files...");
2138
2172
  const cwd = process.cwd();
2139
2173
  await copyDir(templatePath, cwd);
2140
- await fs2.rm(templatePath, { recursive: true, force: true }).catch(() => {
2174
+ await fs3.rm(templatePath, { recursive: true, force: true }).catch(() => {
2141
2175
  });
2142
2176
  s?.stop("Template files downloaded");
2143
2177
  } catch (err) {
@@ -2148,6 +2182,72 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
2148
2182
  }
2149
2183
  }
2150
2184
  }
2185
+ async function downloadGitHubTemplate(templateName, projectConfig, json) {
2186
+ const s = !json ? clack10.spinner() : null;
2187
+ s?.start(`Downloading ${templateName} template...`);
2188
+ const tempDir = path3.join(tmpdir(), `insforge-template-${Date.now()}`);
2189
+ try {
2190
+ await fs3.mkdir(tempDir, { recursive: true });
2191
+ await execAsync2(
2192
+ "git clone --depth 1 https://github.com/InsForge/insforge-templates.git .",
2193
+ { cwd: tempDir, maxBuffer: 10 * 1024 * 1024, timeout: 6e4 }
2194
+ );
2195
+ const templateDir = path3.join(tempDir, templateName);
2196
+ const stat4 = await fs3.stat(templateDir).catch(() => null);
2197
+ if (!stat4?.isDirectory()) {
2198
+ throw new Error(`Template "${templateName}" not found in repository`);
2199
+ }
2200
+ s?.message("Copying template files...");
2201
+ const cwd = process.cwd();
2202
+ await copyDir(templateDir, cwd);
2203
+ const envExamplePath = path3.join(cwd, ".env.example");
2204
+ const envExampleExists = await fs3.stat(envExamplePath).catch(() => null);
2205
+ if (envExampleExists) {
2206
+ const anonKey = await getAnonKey();
2207
+ const envExample = await fs3.readFile(envExamplePath, "utf-8");
2208
+ const envContent = envExample.replace(
2209
+ /^([A-Z_]+=)(.*)$/gm,
2210
+ (_, prefix, _value) => {
2211
+ const key = prefix.slice(0, -1);
2212
+ if (/INSFORGE.*(URL|BASE_URL)$/.test(key)) return `${prefix}${projectConfig.oss_host}`;
2213
+ if (/INSFORGE.*ANON_KEY$/.test(key)) return `${prefix}${anonKey}`;
2214
+ return `${prefix}${_value}`;
2215
+ }
2216
+ );
2217
+ await fs3.writeFile(path3.join(cwd, ".env.local"), envContent);
2218
+ }
2219
+ s?.stop(`${templateName} template downloaded`);
2220
+ const migrationPath = path3.join(cwd, "migrations", "db_int.sql");
2221
+ const migrationExists = await fs3.stat(migrationPath).catch(() => null);
2222
+ if (migrationExists) {
2223
+ const dbSpinner = !json ? clack10.spinner() : null;
2224
+ dbSpinner?.start("Running database migrations...");
2225
+ try {
2226
+ const sql = await fs3.readFile(migrationPath, "utf-8");
2227
+ await ossFetch("/api/database/advance/rawsql/unrestricted", {
2228
+ method: "POST",
2229
+ body: JSON.stringify({ query: sql })
2230
+ });
2231
+ dbSpinner?.stop("Database migrations applied");
2232
+ } catch (err) {
2233
+ dbSpinner?.stop("Database migration failed");
2234
+ if (!json) {
2235
+ clack10.log.warn(`Migration failed: ${err.message}`);
2236
+ clack10.log.info('You can run the migration manually: insforge db query --unrestricted "$(cat migrations/db_int.sql)"');
2237
+ }
2238
+ }
2239
+ }
2240
+ } catch (err) {
2241
+ s?.stop(`${templateName} template download failed`);
2242
+ if (!json) {
2243
+ clack10.log.warn(`Failed to download ${templateName} template: ${err.message}`);
2244
+ clack10.log.info("You can manually clone from: https://github.com/InsForge/insforge-templates");
2245
+ }
2246
+ } finally {
2247
+ await fs3.rm(tempDir, { recursive: true, force: true }).catch(() => {
2248
+ });
2249
+ }
2250
+ }
2151
2251
 
2152
2252
  // src/commands/info.ts
2153
2253
  function registerContextCommand(program2) {
@@ -2406,8 +2506,8 @@ async function listDocs(json) {
2406
2506
  );
2407
2507
  }
2408
2508
  }
2409
- async function fetchDoc(path3, label, json) {
2410
- const res = await ossFetch(path3);
2509
+ async function fetchDoc(path4, label, json) {
2510
+ const res = await ossFetch(path4);
2411
2511
  const data = await res.json();
2412
2512
  const doc = data.data ?? data;
2413
2513
  if (json) {
@@ -2921,7 +3021,7 @@ function formatSize2(gb) {
2921
3021
 
2922
3022
  // src/index.ts
2923
3023
  var __dirname = dirname(fileURLToPath(import.meta.url));
2924
- var pkg = JSON.parse(readFileSync6(join6(__dirname, "../package.json"), "utf-8"));
3024
+ var pkg = JSON.parse(readFileSync6(join7(__dirname, "../package.json"), "utf-8"));
2925
3025
  var INSFORGE_LOGO = `
2926
3026
  \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
2927
3027
  \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