@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 +127 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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(
|
|
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}${
|
|
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}${
|
|
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(
|
|
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}${
|
|
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
|
|
1219
|
-
const res = await ossFetch(
|
|
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
|
|
1783
|
-
import * as
|
|
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/
|
|
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 =
|
|
1889
|
-
const stats = await
|
|
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
|
|
1983
|
+
const entries = await fs3.readdir(src, { withFileTypes: true });
|
|
1959
1984
|
for (const entry of entries) {
|
|
1960
|
-
const srcPath =
|
|
1961
|
-
const destPath =
|
|
1985
|
+
const srcPath = path3.join(src, entry.name);
|
|
1986
|
+
const destPath = path3.join(dest, entry.name);
|
|
1962
1987
|
if (entry.isDirectory()) {
|
|
1963
|
-
await
|
|
1988
|
+
await fs3.mkdir(destPath, { recursive: true });
|
|
1964
1989
|
await copyDir(srcPath, destPath);
|
|
1965
1990
|
} else {
|
|
1966
|
-
await
|
|
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 (
|
|
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 =
|
|
2158
|
+
const templatePath = path3.join(tempDir, targetDir);
|
|
2125
2159
|
try {
|
|
2126
|
-
await
|
|
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
|
|
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(
|
|
2410
|
-
const res = await ossFetch(
|
|
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(
|
|
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
|