@insforge/cli 0.1.82 → 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/README.md +2 -0
- package/dist/index.js +275 -182
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
5
|
-
import { join as
|
|
4
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
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
|
|
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
|
|
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
|
|
1716
|
-
import { join as
|
|
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 =
|
|
1759
|
-
const existing =
|
|
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)
|
|
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)
|
|
1867
|
+
if (!json) clack10.log.success("InsForge agent skills installed.");
|
|
1781
1868
|
} catch (err) {
|
|
1782
1869
|
if (!json) {
|
|
1783
|
-
|
|
1784
|
-
|
|
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)
|
|
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)
|
|
1880
|
+
if (!json) clack10.log.success("find-skills installed.");
|
|
1794
1881
|
} catch (err) {
|
|
1795
1882
|
if (!json) {
|
|
1796
|
-
|
|
1797
|
-
|
|
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)
|
|
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)
|
|
1895
|
+
if (!json) clack10.log.success(`${providerEntry.label} installed.`);
|
|
1809
1896
|
} catch (err) {
|
|
1810
1897
|
if (!json) {
|
|
1811
|
-
|
|
1812
|
-
|
|
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
|
|
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 ?
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 ?
|
|
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
|
-
|
|
2692
|
+
clack12.log.success(`Live at: ${result.liveUrl}`);
|
|
2602
2693
|
}
|
|
2603
|
-
|
|
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
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
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);
|
|
@@ -2753,7 +2844,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2753
2844
|
await requireAuth(apiUrl, false);
|
|
2754
2845
|
if (!json) {
|
|
2755
2846
|
await animateBanner();
|
|
2756
|
-
|
|
2847
|
+
clack13.intro("Let's build something great");
|
|
2757
2848
|
}
|
|
2758
2849
|
let orgId = opts.orgId;
|
|
2759
2850
|
if (!orgId) {
|
|
@@ -2763,7 +2854,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2763
2854
|
}
|
|
2764
2855
|
if (orgs.length === 1) {
|
|
2765
2856
|
orgId = orgs[0].id;
|
|
2766
|
-
if (!json)
|
|
2857
|
+
if (!json) clack13.log.info(`Using organization: ${orgs[0].name}`);
|
|
2767
2858
|
} else {
|
|
2768
2859
|
if (json) {
|
|
2769
2860
|
throw new CLIError("Multiple organizations found. Specify --org-id.");
|
|
@@ -2879,7 +2970,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2879
2970
|
process.chdir(projectDir);
|
|
2880
2971
|
}
|
|
2881
2972
|
let projectLinked = false;
|
|
2882
|
-
const s = !json ?
|
|
2973
|
+
const s = !json ? clack13.spinner() : null;
|
|
2883
2974
|
try {
|
|
2884
2975
|
s?.start("Creating project...");
|
|
2885
2976
|
const project = await createProject(orgId, projectName, opts.region, apiUrl);
|
|
@@ -2906,10 +2997,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2906
2997
|
json
|
|
2907
2998
|
);
|
|
2908
2999
|
if (downloaded) {
|
|
2909
|
-
void reportMarketplaceDownload(
|
|
2910
|
-
opts.marketplace,
|
|
2911
|
-
apiUrl ?? "https://api.insforge.dev"
|
|
2912
|
-
);
|
|
3000
|
+
void reportMarketplaceDownload(opts.marketplace);
|
|
2913
3001
|
}
|
|
2914
3002
|
} else if (githubTemplates.includes(template)) {
|
|
2915
3003
|
await downloadGitHubTemplate(template, projectConfig, json);
|
|
@@ -2919,7 +3007,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2919
3007
|
try {
|
|
2920
3008
|
const anonKey = await getAnonKey();
|
|
2921
3009
|
if (!anonKey) {
|
|
2922
|
-
if (!json)
|
|
3010
|
+
if (!json) clack13.log.warn("Could not retrieve anon key. You can add it to .env.local manually.");
|
|
2923
3011
|
} else {
|
|
2924
3012
|
const envPath = path4.join(process.cwd(), ".env.local");
|
|
2925
3013
|
const envContent = [
|
|
@@ -2930,16 +3018,16 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2930
3018
|
].join("\n");
|
|
2931
3019
|
await fs4.writeFile(envPath, envContent, { flag: "wx" });
|
|
2932
3020
|
if (!json) {
|
|
2933
|
-
|
|
3021
|
+
clack13.log.success("Created .env.local with your InsForge credentials");
|
|
2934
3022
|
}
|
|
2935
3023
|
}
|
|
2936
3024
|
} catch (err) {
|
|
2937
3025
|
const error = err;
|
|
2938
3026
|
if (!json) {
|
|
2939
3027
|
if (error.code === "EEXIST") {
|
|
2940
|
-
|
|
3028
|
+
clack13.log.warn(".env.local already exists; skipping InsForge key seeding.");
|
|
2941
3029
|
} else {
|
|
2942
|
-
|
|
3030
|
+
clack13.log.warn(`Failed to create .env.local: ${error.message}`);
|
|
2943
3031
|
}
|
|
2944
3032
|
}
|
|
2945
3033
|
}
|
|
@@ -2948,12 +3036,12 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2948
3036
|
try {
|
|
2949
3037
|
const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig, json);
|
|
2950
3038
|
if (!json) {
|
|
2951
|
-
|
|
3039
|
+
clack13.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
|
|
2952
3040
|
}
|
|
2953
3041
|
} catch (err) {
|
|
2954
3042
|
const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
|
|
2955
3043
|
if (json) console.error(JSON.stringify({ warning: msg }));
|
|
2956
|
-
else
|
|
3044
|
+
else clack13.log.warn(msg);
|
|
2957
3045
|
}
|
|
2958
3046
|
}
|
|
2959
3047
|
await installSkills(json, opts.auth);
|
|
@@ -2961,7 +3049,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2961
3049
|
await reportCliUsage("cli.create", true, 6);
|
|
2962
3050
|
const templateDownloaded = hasTemplate ? await fs4.stat(path4.join(process.cwd(), "package.json")).catch(() => null) : null;
|
|
2963
3051
|
if (templateDownloaded) {
|
|
2964
|
-
const installSpinner = !json ?
|
|
3052
|
+
const installSpinner = !json ? clack13.spinner() : null;
|
|
2965
3053
|
installSpinner?.start("Installing dependencies...");
|
|
2966
3054
|
try {
|
|
2967
3055
|
await execAsync2("npm install", { cwd: process.cwd(), maxBuffer: 10 * 1024 * 1024 });
|
|
@@ -2969,8 +3057,8 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2969
3057
|
} catch (err) {
|
|
2970
3058
|
installSpinner?.stop("Failed to install dependencies");
|
|
2971
3059
|
if (!json) {
|
|
2972
|
-
|
|
2973
|
-
|
|
3060
|
+
clack13.log.warn(`npm install failed: ${err.message}`);
|
|
3061
|
+
clack13.log.info("Run `npm install` manually to install dependencies.");
|
|
2974
3062
|
}
|
|
2975
3063
|
}
|
|
2976
3064
|
}
|
|
@@ -2986,7 +3074,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2986
3074
|
if (envVars.length > 0) {
|
|
2987
3075
|
startBody.envVars = envVars;
|
|
2988
3076
|
}
|
|
2989
|
-
const deploySpinner =
|
|
3077
|
+
const deploySpinner = clack13.spinner();
|
|
2990
3078
|
const result = await deployProject({
|
|
2991
3079
|
sourceDir: process.cwd(),
|
|
2992
3080
|
startBody,
|
|
@@ -2997,12 +3085,12 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2997
3085
|
liveUrl = result.liveUrl;
|
|
2998
3086
|
} else {
|
|
2999
3087
|
deploySpinner.stop("Deployment is still building");
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
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}`);
|
|
3003
3091
|
}
|
|
3004
3092
|
} catch (err) {
|
|
3005
|
-
|
|
3093
|
+
clack13.log.warn(`Deploy failed: ${err.message}`);
|
|
3006
3094
|
}
|
|
3007
3095
|
}
|
|
3008
3096
|
}
|
|
@@ -3019,33 +3107,33 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
3019
3107
|
}
|
|
3020
3108
|
});
|
|
3021
3109
|
} else {
|
|
3022
|
-
|
|
3110
|
+
clack13.log.step(`Dashboard: ${dashboardUrl}`);
|
|
3023
3111
|
if (liveUrl) {
|
|
3024
|
-
|
|
3112
|
+
clack13.log.success(`Live site: ${liveUrl}`);
|
|
3025
3113
|
}
|
|
3026
3114
|
if (templateDownloaded) {
|
|
3027
3115
|
const steps = [
|
|
3028
3116
|
`cd ${dirName}`,
|
|
3029
3117
|
"npm run dev"
|
|
3030
3118
|
];
|
|
3031
|
-
|
|
3032
|
-
|
|
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");
|
|
3033
3121
|
} else if (hasTemplate && !templateDownloaded) {
|
|
3034
|
-
|
|
3122
|
+
clack13.log.warn("Template download failed. You can retry or set up manually.");
|
|
3035
3123
|
} else {
|
|
3036
3124
|
const prompts = [
|
|
3037
3125
|
"Build a todo app with Google OAuth sign-in",
|
|
3038
3126
|
"Build an Instagram clone where users can upload photos, like, and comment",
|
|
3039
3127
|
"Build an AI chatbot with conversation history"
|
|
3040
3128
|
];
|
|
3041
|
-
|
|
3129
|
+
clack13.note(
|
|
3042
3130
|
`Open your coding agent (Claude Code, Codex, Cursor, etc.) and try:
|
|
3043
3131
|
|
|
3044
3132
|
${prompts.map((p3) => `\u2022 "${p3}"`).join("\n")}`,
|
|
3045
3133
|
"Start building"
|
|
3046
3134
|
);
|
|
3047
3135
|
}
|
|
3048
|
-
|
|
3136
|
+
clack13.outro("Done!");
|
|
3049
3137
|
}
|
|
3050
3138
|
} catch (err) {
|
|
3051
3139
|
if (!projectLinked && hasTemplate && projectDir !== originalCwd) {
|
|
@@ -3064,7 +3152,7 @@ ${prompts.map((p3) => `\u2022 "${p3}"`).join("\n")}`,
|
|
|
3064
3152
|
});
|
|
3065
3153
|
}
|
|
3066
3154
|
async function downloadTemplate(framework, projectConfig, projectName, json, _apiUrl) {
|
|
3067
|
-
const s = !json ?
|
|
3155
|
+
const s = !json ? clack13.spinner() : null;
|
|
3068
3156
|
s?.start("Downloading template...");
|
|
3069
3157
|
try {
|
|
3070
3158
|
const anonKey = await getAnonKey();
|
|
@@ -3095,13 +3183,13 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
|
|
|
3095
3183
|
} catch (err) {
|
|
3096
3184
|
s?.stop("Template download failed");
|
|
3097
3185
|
if (!json) {
|
|
3098
|
-
|
|
3099
|
-
|
|
3186
|
+
clack13.log.warn(`Failed to download template: ${err.message}`);
|
|
3187
|
+
clack13.log.info("You can manually set up the template later.");
|
|
3100
3188
|
}
|
|
3101
3189
|
}
|
|
3102
3190
|
}
|
|
3103
3191
|
async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
3104
|
-
const s = !json ?
|
|
3192
|
+
const s = !json ? clack13.spinner() : null;
|
|
3105
3193
|
s?.start(`Downloading ${templateName} template...`);
|
|
3106
3194
|
const tempDir = path4.join(tmpdir2(), `insforge-template-${Date.now()}`);
|
|
3107
3195
|
try {
|
|
@@ -3150,7 +3238,7 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
3150
3238
|
await fs4.writeFile(envLocalPath, envFinal, { flag: "wx" });
|
|
3151
3239
|
} catch (e) {
|
|
3152
3240
|
if (e.code === "EEXIST") {
|
|
3153
|
-
if (!json)
|
|
3241
|
+
if (!json) clack13.log.warn(".env.local already exists; skipping env seeding.");
|
|
3154
3242
|
} else {
|
|
3155
3243
|
throw e;
|
|
3156
3244
|
}
|
|
@@ -3160,7 +3248,7 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
3160
3248
|
const migrationPath = path4.join(cwd, "migrations", "db_init.sql");
|
|
3161
3249
|
const migrationExists = await fs4.stat(migrationPath).catch(() => null);
|
|
3162
3250
|
if (migrationExists) {
|
|
3163
|
-
const dbSpinner = !json ?
|
|
3251
|
+
const dbSpinner = !json ? clack13.spinner() : null;
|
|
3164
3252
|
dbSpinner?.start("Running database migrations...");
|
|
3165
3253
|
try {
|
|
3166
3254
|
const sql = await fs4.readFile(migrationPath, "utf-8");
|
|
@@ -3169,8 +3257,8 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
3169
3257
|
} catch (err) {
|
|
3170
3258
|
dbSpinner?.stop("Database migration failed");
|
|
3171
3259
|
if (!json) {
|
|
3172
|
-
|
|
3173
|
-
|
|
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)"');
|
|
3174
3262
|
} else {
|
|
3175
3263
|
throw err;
|
|
3176
3264
|
}
|
|
@@ -3183,8 +3271,8 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
3183
3271
|
if (json) {
|
|
3184
3272
|
console.error(JSON.stringify({ warning: msg }));
|
|
3185
3273
|
} else {
|
|
3186
|
-
|
|
3187
|
-
|
|
3274
|
+
clack13.log.warn(msg);
|
|
3275
|
+
clack13.log.info("You can manually clone from: https://github.com/InsForge/insforge-templates");
|
|
3188
3276
|
}
|
|
3189
3277
|
return false;
|
|
3190
3278
|
} finally {
|
|
@@ -3192,12 +3280,14 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
3192
3280
|
});
|
|
3193
3281
|
}
|
|
3194
3282
|
}
|
|
3195
|
-
|
|
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) {
|
|
3196
3285
|
try {
|
|
3197
|
-
const res = await fetch(
|
|
3286
|
+
const res = await fetch(MARKETPLACE_REPORT_URL, {
|
|
3198
3287
|
method: "POST",
|
|
3199
3288
|
headers: { "Content-Type": "application/json" },
|
|
3200
|
-
body:
|
|
3289
|
+
body: JSON.stringify({ slug }),
|
|
3290
|
+
signal: AbortSignal.timeout(5e3)
|
|
3201
3291
|
});
|
|
3202
3292
|
if (!res.ok) {
|
|
3203
3293
|
return;
|
|
@@ -3209,15 +3299,15 @@ async function reportMarketplaceDownload(slug, apiUrl) {
|
|
|
3209
3299
|
// src/commands/projects/link.ts
|
|
3210
3300
|
var execAsync3 = promisify4(exec3);
|
|
3211
3301
|
async function runNpmInstall(startMessage = "Installing dependencies...") {
|
|
3212
|
-
const spinner11 =
|
|
3302
|
+
const spinner11 = clack14.spinner();
|
|
3213
3303
|
spinner11.start(startMessage);
|
|
3214
3304
|
try {
|
|
3215
3305
|
await execAsync3("npm install", { cwd: process.cwd(), maxBuffer: 10 * 1024 * 1024 });
|
|
3216
3306
|
spinner11.stop("Dependencies installed");
|
|
3217
3307
|
} catch (err) {
|
|
3218
3308
|
spinner11.stop("Failed to install dependencies");
|
|
3219
|
-
|
|
3220
|
-
|
|
3309
|
+
clack14.log.warn(`npm install failed: ${err.message}`);
|
|
3310
|
+
clack14.log.info("Run `npm install` manually to install dependencies.");
|
|
3221
3311
|
}
|
|
3222
3312
|
}
|
|
3223
3313
|
async function runNpmSetupIfPresent() {
|
|
@@ -3229,15 +3319,15 @@ async function runNpmSetupIfPresent() {
|
|
|
3229
3319
|
} catch {
|
|
3230
3320
|
}
|
|
3231
3321
|
if (!hasSetup) return;
|
|
3232
|
-
const spinner11 =
|
|
3322
|
+
const spinner11 = clack14.spinner();
|
|
3233
3323
|
spinner11.start("Running setup (schema + migrations)...");
|
|
3234
3324
|
try {
|
|
3235
3325
|
await execAsync3("npm run setup", { cwd: process.cwd(), maxBuffer: 20 * 1024 * 1024 });
|
|
3236
3326
|
spinner11.stop("Setup complete");
|
|
3237
3327
|
} catch (err) {
|
|
3238
3328
|
spinner11.stop("Setup failed");
|
|
3239
|
-
|
|
3240
|
-
|
|
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.");
|
|
3241
3331
|
}
|
|
3242
3332
|
}
|
|
3243
3333
|
function registerProjectLinkCommand(program2) {
|
|
@@ -3260,7 +3350,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
3260
3350
|
if (json) {
|
|
3261
3351
|
outputJson({ success: true, skills_only: true });
|
|
3262
3352
|
} else {
|
|
3263
|
-
|
|
3353
|
+
clack14.note(
|
|
3264
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.`,
|
|
3265
3355
|
"What's next"
|
|
3266
3356
|
);
|
|
@@ -3338,11 +3428,11 @@ function registerProjectLinkCommand(program2) {
|
|
|
3338
3428
|
if (opts.auth) {
|
|
3339
3429
|
try {
|
|
3340
3430
|
const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig2, json);
|
|
3341
|
-
if (!json)
|
|
3431
|
+
if (!json) clack14.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
|
|
3342
3432
|
} catch (err) {
|
|
3343
3433
|
const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
|
|
3344
3434
|
if (json) console.error(JSON.stringify({ warning: msg }));
|
|
3345
|
-
else
|
|
3435
|
+
else clack14.log.warn(msg);
|
|
3346
3436
|
}
|
|
3347
3437
|
}
|
|
3348
3438
|
if (templateDownloaded && !json) {
|
|
@@ -3368,9 +3458,9 @@ function registerProjectLinkCommand(program2) {
|
|
|
3368
3458
|
`${pc2.bold("1.")} ${runCommand}`,
|
|
3369
3459
|
`${pc2.bold("2.")} Open ${pc2.cyan("Claude Code")} or ${pc2.cyan("Cursor")} and prompt your agent to add more features`
|
|
3370
3460
|
];
|
|
3371
|
-
|
|
3461
|
+
clack14.note(steps.join("\n"), "What's next");
|
|
3372
3462
|
} else {
|
|
3373
|
-
|
|
3463
|
+
clack14.log.warn("Template download failed. You can retry or set up manually.");
|
|
3374
3464
|
}
|
|
3375
3465
|
}
|
|
3376
3466
|
return;
|
|
@@ -3385,7 +3475,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
3385
3475
|
try {
|
|
3386
3476
|
const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig2, json);
|
|
3387
3477
|
if (!json) {
|
|
3388
|
-
|
|
3478
|
+
clack14.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
|
|
3389
3479
|
}
|
|
3390
3480
|
if (result.packageJsonPatched && !json) {
|
|
3391
3481
|
await runNpmInstall("Installing new dependencies...");
|
|
@@ -3394,7 +3484,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
3394
3484
|
} catch (err) {
|
|
3395
3485
|
const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
|
|
3396
3486
|
if (json) console.error(JSON.stringify({ warning: msg }));
|
|
3397
|
-
else
|
|
3487
|
+
else clack14.log.warn(msg);
|
|
3398
3488
|
}
|
|
3399
3489
|
}
|
|
3400
3490
|
trackCommand("link", "oss-org", { direct: true });
|
|
@@ -3424,7 +3514,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
3424
3514
|
}
|
|
3425
3515
|
if (orgs.length === 1) {
|
|
3426
3516
|
orgId = orgs[0].id;
|
|
3427
|
-
if (!json)
|
|
3517
|
+
if (!json) clack14.log.info(`Using organization: ${orgs[0].name}`);
|
|
3428
3518
|
} else {
|
|
3429
3519
|
if (json) {
|
|
3430
3520
|
throw new CLIError("Multiple organizations found. Specify --org-id.");
|
|
@@ -3535,11 +3625,11 @@ function registerProjectLinkCommand(program2) {
|
|
|
3535
3625
|
if (opts.auth) {
|
|
3536
3626
|
try {
|
|
3537
3627
|
const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig, json);
|
|
3538
|
-
if (!json)
|
|
3628
|
+
if (!json) clack14.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
|
|
3539
3629
|
} catch (err) {
|
|
3540
3630
|
const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
|
|
3541
3631
|
if (json) console.error(JSON.stringify({ warning: msg }));
|
|
3542
|
-
else
|
|
3632
|
+
else clack14.log.warn(msg);
|
|
3543
3633
|
}
|
|
3544
3634
|
}
|
|
3545
3635
|
if (templateDownloaded && !json) {
|
|
@@ -3552,16 +3642,16 @@ function registerProjectLinkCommand(program2) {
|
|
|
3552
3642
|
await reportCliUsage("cli.link", true, 6, projectConfig);
|
|
3553
3643
|
if (!json) {
|
|
3554
3644
|
const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
|
|
3555
|
-
|
|
3645
|
+
clack14.log.step(`Dashboard: ${pc2.underline(dashboardUrl)}`);
|
|
3556
3646
|
if (templateDownloaded) {
|
|
3557
3647
|
const runCommand = `${pc2.cyan("cd")} ${pc2.green(dirName)} ${pc2.dim("&&")} ${pc2.cyan("npm run dev")}`;
|
|
3558
3648
|
const steps = [
|
|
3559
3649
|
`${pc2.bold("1.")} ${runCommand}`,
|
|
3560
3650
|
`${pc2.bold("2.")} Open ${pc2.cyan("Claude Code")} or ${pc2.cyan("Cursor")} and prompt your agent to add more features`
|
|
3561
3651
|
];
|
|
3562
|
-
|
|
3652
|
+
clack14.note(steps.join("\n"), "What's next");
|
|
3563
3653
|
} else {
|
|
3564
|
-
|
|
3654
|
+
clack14.log.warn("Template download failed. You can retry or set up manually.");
|
|
3565
3655
|
}
|
|
3566
3656
|
}
|
|
3567
3657
|
} else {
|
|
@@ -3569,7 +3659,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
3569
3659
|
try {
|
|
3570
3660
|
const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig, json);
|
|
3571
3661
|
if (!json) {
|
|
3572
|
-
|
|
3662
|
+
clack14.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
|
|
3573
3663
|
}
|
|
3574
3664
|
if (result.packageJsonPatched && !json) {
|
|
3575
3665
|
await runNpmInstall("Installing new dependencies...");
|
|
@@ -3578,20 +3668,20 @@ function registerProjectLinkCommand(program2) {
|
|
|
3578
3668
|
} catch (err) {
|
|
3579
3669
|
const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
|
|
3580
3670
|
if (json) console.error(JSON.stringify({ warning: msg }));
|
|
3581
|
-
else
|
|
3671
|
+
else clack14.log.warn(msg);
|
|
3582
3672
|
}
|
|
3583
3673
|
}
|
|
3584
3674
|
await installSkills(json, opts.auth);
|
|
3585
3675
|
await reportCliUsage("cli.link", true, 6, projectConfig);
|
|
3586
3676
|
if (!json) {
|
|
3587
3677
|
const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
|
|
3588
|
-
|
|
3678
|
+
clack14.log.step(`Dashboard: ${dashboardUrl}`);
|
|
3589
3679
|
const prompts = [
|
|
3590
3680
|
"Build a todo app with Google OAuth sign-in",
|
|
3591
3681
|
"Build an Instagram clone where users can upload photos, like, and comment",
|
|
3592
3682
|
"Build an AI chatbot with conversation history and deploy it to a live URL"
|
|
3593
3683
|
];
|
|
3594
|
-
|
|
3684
|
+
clack14.note(
|
|
3595
3685
|
`Open your coding agent (Claude Code, Codex, Cursor, etc.) and try:
|
|
3596
3686
|
|
|
3597
3687
|
${prompts.map((p3) => `\u2022 "${p3}"`).join("\n")}`,
|
|
@@ -3831,7 +3921,7 @@ function registerDbRpcCommand(dbCmd2) {
|
|
|
3831
3921
|
}
|
|
3832
3922
|
|
|
3833
3923
|
// src/commands/db/export.ts
|
|
3834
|
-
import { writeFileSync as
|
|
3924
|
+
import { writeFileSync as writeFileSync4 } from "fs";
|
|
3835
3925
|
function registerDbExportCommand(dbCmd2) {
|
|
3836
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) => {
|
|
3837
3927
|
const { json } = getRootOpts(cmd);
|
|
@@ -3871,7 +3961,7 @@ function registerDbExportCommand(dbCmd2) {
|
|
|
3871
3961
|
return;
|
|
3872
3962
|
}
|
|
3873
3963
|
if (opts.output) {
|
|
3874
|
-
|
|
3964
|
+
writeFileSync4(opts.output, content);
|
|
3875
3965
|
const tableCount = meta?.tables?.length;
|
|
3876
3966
|
const suffix = tableCount ? ` (${tableCount} tables, format: ${meta?.format ?? opts.format})` : "";
|
|
3877
3967
|
outputSuccess(`Exported to ${opts.output}${suffix}`);
|
|
@@ -3885,7 +3975,7 @@ function registerDbExportCommand(dbCmd2) {
|
|
|
3885
3975
|
}
|
|
3886
3976
|
|
|
3887
3977
|
// src/commands/db/import.ts
|
|
3888
|
-
import { readFileSync as
|
|
3978
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
3889
3979
|
import { basename as basename5 } from "path";
|
|
3890
3980
|
function registerDbImportCommand(dbCmd2) {
|
|
3891
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) => {
|
|
@@ -3894,7 +3984,7 @@ function registerDbImportCommand(dbCmd2) {
|
|
|
3894
3984
|
await requireAuth();
|
|
3895
3985
|
const config = getProjectConfig();
|
|
3896
3986
|
if (!config) throw new ProjectNotLinkedError();
|
|
3897
|
-
const fileContent =
|
|
3987
|
+
const fileContent = readFileSync4(file);
|
|
3898
3988
|
const fileName = basename5(file);
|
|
3899
3989
|
const formData = new FormData();
|
|
3900
3990
|
formData.append("file", new Blob([fileContent]), fileName);
|
|
@@ -3925,12 +4015,12 @@ function registerDbImportCommand(dbCmd2) {
|
|
|
3925
4015
|
}
|
|
3926
4016
|
|
|
3927
4017
|
// src/commands/db/migrations.ts
|
|
3928
|
-
import { existsSync as
|
|
3929
|
-
import { join as
|
|
4018
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
4019
|
+
import { join as join10 } from "path";
|
|
3930
4020
|
|
|
3931
4021
|
// src/lib/migrations.ts
|
|
3932
|
-
import { existsSync as
|
|
3933
|
-
import { join as
|
|
4022
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
4023
|
+
import { join as join9 } from "path";
|
|
3934
4024
|
var MIGRATION_VERSION_REGEX = /^\d{1,64}$/u;
|
|
3935
4025
|
var MIGRATION_FILENAME_REGEX = /^(\d{1,64})_([a-z0-9-]+)\.sql$/u;
|
|
3936
4026
|
function assertValidMigrationVersion(version) {
|
|
@@ -3994,18 +4084,18 @@ function incrementMigrationVersion(version) {
|
|
|
3994
4084
|
return formatMigrationVersion(new Date(nextTimestamp));
|
|
3995
4085
|
}
|
|
3996
4086
|
function getMigrationsDir(cwd = process.cwd()) {
|
|
3997
|
-
return
|
|
4087
|
+
return join9(cwd, "migrations");
|
|
3998
4088
|
}
|
|
3999
4089
|
function ensureMigrationsDir(cwd = process.cwd()) {
|
|
4000
4090
|
const migrationsDir = getMigrationsDir(cwd);
|
|
4001
|
-
if (!
|
|
4091
|
+
if (!existsSync5(migrationsDir)) {
|
|
4002
4092
|
mkdirSync2(migrationsDir, { recursive: true });
|
|
4003
4093
|
}
|
|
4004
4094
|
return migrationsDir;
|
|
4005
4095
|
}
|
|
4006
4096
|
function listLocalMigrationFilenames(cwd = process.cwd()) {
|
|
4007
4097
|
const migrationsDir = getMigrationsDir(cwd);
|
|
4008
|
-
if (!
|
|
4098
|
+
if (!existsSync5(migrationsDir)) {
|
|
4009
4099
|
return [];
|
|
4010
4100
|
}
|
|
4011
4101
|
return readdirSync(migrationsDir).sort((left, right) => left.localeCompare(right));
|
|
@@ -4193,12 +4283,12 @@ function registerDbMigrationsCommand(dbCmd2) {
|
|
|
4193
4283
|
migration.version,
|
|
4194
4284
|
migration.name
|
|
4195
4285
|
);
|
|
4196
|
-
const filePath =
|
|
4197
|
-
if (existingLocalVersions.has(migration.version) ||
|
|
4286
|
+
const filePath = join10(migrationsDir, filename);
|
|
4287
|
+
if (existingLocalVersions.has(migration.version) || existsSync6(filePath)) {
|
|
4198
4288
|
skippedFiles.push(filename);
|
|
4199
4289
|
continue;
|
|
4200
4290
|
}
|
|
4201
|
-
|
|
4291
|
+
writeFileSync5(filePath, formatMigrationSql(migration.statements));
|
|
4202
4292
|
createdFiles.push(filename);
|
|
4203
4293
|
existingLocalVersions.add(migration.version);
|
|
4204
4294
|
}
|
|
@@ -4236,9 +4326,9 @@ function registerDbMigrationsCommand(dbCmd2) {
|
|
|
4236
4326
|
);
|
|
4237
4327
|
const filename = buildMigrationFilename(nextVersion, migrationName);
|
|
4238
4328
|
const migrationsDir = ensureMigrationsDir();
|
|
4239
|
-
const filePath =
|
|
4329
|
+
const filePath = join10(migrationsDir, filename);
|
|
4240
4330
|
try {
|
|
4241
|
-
|
|
4331
|
+
writeFileSync5(filePath, "", { flag: "wx" });
|
|
4242
4332
|
} catch (error) {
|
|
4243
4333
|
if (error.code === "EEXIST") {
|
|
4244
4334
|
throw new CLIError(`Migration file already exists: ${filename}`);
|
|
@@ -4294,11 +4384,11 @@ function registerDbMigrationsCommand(dbCmd2) {
|
|
|
4294
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.`
|
|
4295
4385
|
);
|
|
4296
4386
|
}
|
|
4297
|
-
const filePath =
|
|
4298
|
-
if (!
|
|
4387
|
+
const filePath = join10(getMigrationsDir(), targetMigration.filename);
|
|
4388
|
+
if (!existsSync6(filePath)) {
|
|
4299
4389
|
throw new CLIError(`Local migration file not found: ${targetMigration.filename}`);
|
|
4300
4390
|
}
|
|
4301
|
-
const sql =
|
|
4391
|
+
const sql = readFileSync5(filePath, "utf-8");
|
|
4302
4392
|
if (!sql.trim()) {
|
|
4303
4393
|
throw new CLIError(`Migration file is empty: ${targetMigration.filename}`);
|
|
4304
4394
|
}
|
|
@@ -4360,11 +4450,11 @@ function registerDbMigrationsCommand(dbCmd2) {
|
|
|
4360
4450
|
}
|
|
4361
4451
|
}
|
|
4362
4452
|
for (const migration of migrationsToApply) {
|
|
4363
|
-
const filePath =
|
|
4364
|
-
if (!
|
|
4453
|
+
const filePath = join10(getMigrationsDir(), migration.filename);
|
|
4454
|
+
if (!existsSync6(filePath)) {
|
|
4365
4455
|
throw new CLIError(`Local migration file not found: ${migration.filename}`);
|
|
4366
4456
|
}
|
|
4367
|
-
const sql =
|
|
4457
|
+
const sql = readFileSync5(filePath, "utf-8");
|
|
4368
4458
|
if (!sql.trim()) {
|
|
4369
4459
|
throw new CLIError(`Migration file is empty: ${migration.filename}`);
|
|
4370
4460
|
}
|
|
@@ -4593,12 +4683,12 @@ function registerFunctionsCommands(functionsCmd2) {
|
|
|
4593
4683
|
}
|
|
4594
4684
|
|
|
4595
4685
|
// src/commands/functions/deploy.ts
|
|
4596
|
-
import { readFileSync as
|
|
4686
|
+
import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
|
|
4597
4687
|
function resolveDeployFilePath(opts) {
|
|
4598
4688
|
if (!opts.file) {
|
|
4599
4689
|
throw new CLIError("Missing required option: --file <path>");
|
|
4600
4690
|
}
|
|
4601
|
-
if (!
|
|
4691
|
+
if (!existsSync7(opts.file)) {
|
|
4602
4692
|
throw new CLIError(`Source file not found: ${opts.file}`);
|
|
4603
4693
|
}
|
|
4604
4694
|
return opts.file;
|
|
@@ -4609,7 +4699,7 @@ function registerFunctionsDeployCommand(functionsCmd2) {
|
|
|
4609
4699
|
try {
|
|
4610
4700
|
await requireAuth();
|
|
4611
4701
|
const filePath = resolveDeployFilePath(opts);
|
|
4612
|
-
const code =
|
|
4702
|
+
const code = readFileSync6(filePath, "utf-8");
|
|
4613
4703
|
const name = opts.name ?? slug;
|
|
4614
4704
|
const description = opts.description ?? "";
|
|
4615
4705
|
let exists = false;
|
|
@@ -4730,7 +4820,7 @@ function registerFunctionsCodeCommand(functionsCmd2) {
|
|
|
4730
4820
|
}
|
|
4731
4821
|
|
|
4732
4822
|
// src/commands/functions/delete.ts
|
|
4733
|
-
import * as
|
|
4823
|
+
import * as clack15 from "@clack/prompts";
|
|
4734
4824
|
function registerFunctionsDeleteCommand(functionsCmd2) {
|
|
4735
4825
|
functionsCmd2.command("delete <slug>").description("Delete an edge function").action(async (slug, _opts, cmd) => {
|
|
4736
4826
|
const { json, yes } = getRootOpts(cmd);
|
|
@@ -4741,7 +4831,7 @@ function registerFunctionsDeleteCommand(functionsCmd2) {
|
|
|
4741
4831
|
message: `Delete function "${slug}"? This cannot be undone.`
|
|
4742
4832
|
});
|
|
4743
4833
|
if (isCancel2(confirmed) || !confirmed) {
|
|
4744
|
-
|
|
4834
|
+
clack15.log.info("Cancelled.");
|
|
4745
4835
|
return;
|
|
4746
4836
|
}
|
|
4747
4837
|
}
|
|
@@ -4796,7 +4886,7 @@ function registerStorageBucketsCommand(storageCmd2) {
|
|
|
4796
4886
|
}
|
|
4797
4887
|
|
|
4798
4888
|
// src/commands/storage/upload.ts
|
|
4799
|
-
import { readFileSync as
|
|
4889
|
+
import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
|
|
4800
4890
|
import { basename as basename6 } from "path";
|
|
4801
4891
|
function registerStorageUploadCommand(storageCmd2) {
|
|
4802
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) => {
|
|
@@ -4805,10 +4895,10 @@ function registerStorageUploadCommand(storageCmd2) {
|
|
|
4805
4895
|
await requireAuth();
|
|
4806
4896
|
const config = getProjectConfig();
|
|
4807
4897
|
if (!config) throw new ProjectNotLinkedError();
|
|
4808
|
-
if (!
|
|
4898
|
+
if (!existsSync8(file)) {
|
|
4809
4899
|
throw new CLIError(`File not found: ${file}`);
|
|
4810
4900
|
}
|
|
4811
|
-
const fileContent =
|
|
4901
|
+
const fileContent = readFileSync7(file);
|
|
4812
4902
|
const objectKey = opts.key ?? basename6(file);
|
|
4813
4903
|
const bucketName = opts.bucket;
|
|
4814
4904
|
const formData = new FormData();
|
|
@@ -4839,8 +4929,8 @@ function registerStorageUploadCommand(storageCmd2) {
|
|
|
4839
4929
|
}
|
|
4840
4930
|
|
|
4841
4931
|
// src/commands/storage/download.ts
|
|
4842
|
-
import { writeFileSync as
|
|
4843
|
-
import { join as
|
|
4932
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
4933
|
+
import { join as join11, basename as basename7 } from "path";
|
|
4844
4934
|
function registerStorageDownloadCommand(storageCmd2) {
|
|
4845
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) => {
|
|
4846
4936
|
const { json } = getRootOpts(cmd);
|
|
@@ -4860,8 +4950,8 @@ function registerStorageDownloadCommand(storageCmd2) {
|
|
|
4860
4950
|
throw new CLIError(err.error ?? `Download failed: ${res.status}`);
|
|
4861
4951
|
}
|
|
4862
4952
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
4863
|
-
const outputPath = opts.output ??
|
|
4864
|
-
|
|
4953
|
+
const outputPath = opts.output ?? join11(process.cwd(), basename7(objectKey));
|
|
4954
|
+
writeFileSync6(outputPath, buffer);
|
|
4865
4955
|
if (json) {
|
|
4866
4956
|
outputJson({ success: true, path: outputPath, size: buffer.length });
|
|
4867
4957
|
} else {
|
|
@@ -5991,16 +6081,16 @@ function registerComputeEventsCommand(computeCmd2) {
|
|
|
5991
6081
|
}
|
|
5992
6082
|
|
|
5993
6083
|
// src/commands/compute/deploy.ts
|
|
5994
|
-
import { existsSync as
|
|
5995
|
-
import { join as
|
|
6084
|
+
import { existsSync as existsSync10 } from "fs";
|
|
6085
|
+
import { join as join13, resolve as resolve4 } from "path";
|
|
5996
6086
|
|
|
5997
6087
|
// src/lib/env-file.ts
|
|
5998
|
-
import { readFileSync as
|
|
6088
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
5999
6089
|
var ENV_KEY_REGEX2 = /^[A-Z_][A-Z0-9_]*$/;
|
|
6000
6090
|
function parseEnvFile(path6) {
|
|
6001
6091
|
let raw;
|
|
6002
6092
|
try {
|
|
6003
|
-
raw =
|
|
6093
|
+
raw = readFileSync8(path6, "utf-8");
|
|
6004
6094
|
} catch (err) {
|
|
6005
6095
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6006
6096
|
throw new CLIError(`Could not read --env-file at ${path6}: ${msg}`);
|
|
@@ -6036,8 +6126,8 @@ function parseEnvFile(path6) {
|
|
|
6036
6126
|
|
|
6037
6127
|
// src/lib/flyctl.ts
|
|
6038
6128
|
import { spawn, spawnSync } from "child_process";
|
|
6039
|
-
import { existsSync as
|
|
6040
|
-
import { join as
|
|
6129
|
+
import { existsSync as existsSync9, writeFileSync as writeFileSync7, unlinkSync as unlinkSync3 } from "fs";
|
|
6130
|
+
import { join as join12 } from "path";
|
|
6041
6131
|
function ensureFlyctlAvailable() {
|
|
6042
6132
|
const r = spawnSync("flyctl", ["version"], {
|
|
6043
6133
|
encoding: "utf8",
|
|
@@ -6050,8 +6140,8 @@ function ensureFlyctlAvailable() {
|
|
|
6050
6140
|
}
|
|
6051
6141
|
}
|
|
6052
6142
|
function ensureFlyTomlStub(opts) {
|
|
6053
|
-
const path6 =
|
|
6054
|
-
if (
|
|
6143
|
+
const path6 = join12(opts.dir, "fly.toml");
|
|
6144
|
+
if (existsSync9(path6)) {
|
|
6055
6145
|
return () => {
|
|
6056
6146
|
};
|
|
6057
6147
|
}
|
|
@@ -6068,7 +6158,7 @@ primary_region = "${opts.region}"
|
|
|
6068
6158
|
auto_start_machines = true
|
|
6069
6159
|
min_machines_running = 0
|
|
6070
6160
|
`;
|
|
6071
|
-
|
|
6161
|
+
writeFileSync7(path6, stub, "utf8");
|
|
6072
6162
|
return () => {
|
|
6073
6163
|
try {
|
|
6074
6164
|
unlinkSync3(path6);
|
|
@@ -6238,8 +6328,8 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
6238
6328
|
return;
|
|
6239
6329
|
}
|
|
6240
6330
|
const absDir = resolve4(dir);
|
|
6241
|
-
const dockerfilePath =
|
|
6242
|
-
if (!
|
|
6331
|
+
const dockerfilePath = join13(absDir, "Dockerfile");
|
|
6332
|
+
if (!existsSync10(dockerfilePath)) {
|
|
6243
6333
|
throw new CLIError(
|
|
6244
6334
|
`No Dockerfile at ${dockerfilePath}.
|
|
6245
6335
|
Either:
|
|
@@ -6474,7 +6564,7 @@ function formatSize2(gb) {
|
|
|
6474
6564
|
|
|
6475
6565
|
// src/commands/diagnose/index.ts
|
|
6476
6566
|
import * as os from "os";
|
|
6477
|
-
import * as
|
|
6567
|
+
import * as clack16 from "@clack/prompts";
|
|
6478
6568
|
|
|
6479
6569
|
// src/commands/diagnose/metrics.ts
|
|
6480
6570
|
var METRIC_LABELS = {
|
|
@@ -7015,10 +7105,10 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
7015
7105
|
if (question.length === 0 || question.length > 2e3) {
|
|
7016
7106
|
throw new CLIError("Question must be between 1 and 2000 characters.");
|
|
7017
7107
|
}
|
|
7018
|
-
const s = !json ?
|
|
7108
|
+
const s = !json ? clack16.spinner() : null;
|
|
7019
7109
|
s?.start("Collecting diagnostic data...");
|
|
7020
7110
|
const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
|
|
7021
|
-
const cliVersion = "0.1.
|
|
7111
|
+
const cliVersion = "0.1.84";
|
|
7022
7112
|
s?.stop("Data collected");
|
|
7023
7113
|
if (!json) {
|
|
7024
7114
|
console.log(`
|
|
@@ -7151,9 +7241,9 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
7151
7241
|
void 0,
|
|
7152
7242
|
apiUrl
|
|
7153
7243
|
);
|
|
7154
|
-
|
|
7244
|
+
clack16.log.success("Thanks for your feedback!");
|
|
7155
7245
|
} catch {
|
|
7156
|
-
|
|
7246
|
+
clack16.log.warn("Failed to submit rating.");
|
|
7157
7247
|
}
|
|
7158
7248
|
}
|
|
7159
7249
|
}
|
|
@@ -8382,7 +8472,7 @@ function registerPaymentsCommands(paymentsCmd2) {
|
|
|
8382
8472
|
}
|
|
8383
8473
|
|
|
8384
8474
|
// src/commands/posthog/setup.ts
|
|
8385
|
-
import * as
|
|
8475
|
+
import * as clack17 from "@clack/prompts";
|
|
8386
8476
|
import pc3 from "picocolors";
|
|
8387
8477
|
|
|
8388
8478
|
// src/lib/api/posthog.ts
|
|
@@ -8567,6 +8657,8 @@ function registerPosthogSetupCommand(program2) {
|
|
|
8567
8657
|
}
|
|
8568
8658
|
} catch (err) {
|
|
8569
8659
|
handleError(err, json);
|
|
8660
|
+
} finally {
|
|
8661
|
+
await shutdownAnalytics();
|
|
8570
8662
|
}
|
|
8571
8663
|
});
|
|
8572
8664
|
}
|
|
@@ -8579,13 +8671,14 @@ async function runSetup(opts) {
|
|
|
8579
8671
|
if (!token) {
|
|
8580
8672
|
throw new AuthError("Not logged in. Run `insforge login` first.");
|
|
8581
8673
|
}
|
|
8674
|
+
trackPosthog("setup", proj);
|
|
8582
8675
|
if (!opts.json) {
|
|
8583
|
-
|
|
8676
|
+
clack17.intro("PostHog setup");
|
|
8584
8677
|
outputSuccess(`Linked to InsForge project: ${proj.project_name} (${proj.project_id})`);
|
|
8585
8678
|
}
|
|
8586
8679
|
const dashboardConnection = await ensureDashboardConnection(proj.project_id, token, opts);
|
|
8587
8680
|
if (!opts.json) {
|
|
8588
|
-
|
|
8681
|
+
clack17.note(
|
|
8589
8682
|
`Run this in your terminal to wire PostHog into your app code:
|
|
8590
8683
|
|
|
8591
8684
|
${WIZARD_COMMAND}
|
|
@@ -8623,13 +8716,13 @@ async function runConnectFlow(projectId, token, authorizeUrl, opts) {
|
|
|
8623
8716
|
`);
|
|
8624
8717
|
process.stderr.write("Your browser should open automatically. If not, copy the URL above.\n");
|
|
8625
8718
|
} else {
|
|
8626
|
-
|
|
8719
|
+
clack17.log.info("PostHog is not yet connected to your InsForge dashboard.");
|
|
8627
8720
|
if (opts.skipBrowser) {
|
|
8628
|
-
|
|
8721
|
+
clack17.log.info(`Open this URL to authorize PostHog:
|
|
8629
8722
|
${pc3.cyan(pc3.underline(authorizeUrl))}`);
|
|
8630
8723
|
} else {
|
|
8631
|
-
|
|
8632
|
-
|
|
8724
|
+
clack17.log.info("Opening browser to authorize PostHog...");
|
|
8725
|
+
clack17.log.info(`If browser doesn't open, visit:
|
|
8633
8726
|
${pc3.cyan(pc3.underline(authorizeUrl))}`);
|
|
8634
8727
|
}
|
|
8635
8728
|
}
|
|
@@ -8640,11 +8733,11 @@ ${pc3.cyan(pc3.underline(authorizeUrl))}`);
|
|
|
8640
8733
|
} catch {
|
|
8641
8734
|
}
|
|
8642
8735
|
}
|
|
8643
|
-
const spinner11 = !opts.json && isInteractive ?
|
|
8736
|
+
const spinner11 = !opts.json && isInteractive ? clack17.spinner() : null;
|
|
8644
8737
|
if (spinner11) {
|
|
8645
8738
|
spinner11.start("Waiting for InsForge dashboard connection... (timeout: 15 minutes)");
|
|
8646
8739
|
} else if (!opts.json) {
|
|
8647
|
-
|
|
8740
|
+
clack17.log.info("Waiting for InsForge dashboard connection (up to 15 minutes)...");
|
|
8648
8741
|
}
|
|
8649
8742
|
try {
|
|
8650
8743
|
await pollPosthogConnection(
|
|
@@ -8668,20 +8761,20 @@ ${pc3.cyan(pc3.underline(authorizeUrl))}`);
|
|
|
8668
8761
|
if (spinner11) {
|
|
8669
8762
|
spinner11.stop("InsForge dashboard connection received.");
|
|
8670
8763
|
} else if (!opts.json) {
|
|
8671
|
-
|
|
8764
|
+
clack17.log.success("InsForge dashboard connection received.");
|
|
8672
8765
|
}
|
|
8673
8766
|
} catch (err) {
|
|
8674
8767
|
if (spinner11) {
|
|
8675
8768
|
spinner11.stop("InsForge dashboard connection wait failed.");
|
|
8676
8769
|
} else if (!opts.json) {
|
|
8677
|
-
|
|
8770
|
+
clack17.log.error("InsForge dashboard connection wait failed.");
|
|
8678
8771
|
}
|
|
8679
8772
|
throw err;
|
|
8680
8773
|
}
|
|
8681
8774
|
}
|
|
8682
8775
|
|
|
8683
8776
|
// src/commands/config/export.ts
|
|
8684
|
-
import { writeFileSync as
|
|
8777
|
+
import { writeFileSync as writeFileSync8, existsSync as existsSync11 } from "fs";
|
|
8685
8778
|
import { resolve as resolve5 } from "path";
|
|
8686
8779
|
import * as p from "@clack/prompts";
|
|
8687
8780
|
import pc4 from "picocolors";
|
|
@@ -9167,7 +9260,7 @@ function registerConfigExportCommand(cfg) {
|
|
|
9167
9260
|
projectConfig = getProjectConfig();
|
|
9168
9261
|
await requireAuth();
|
|
9169
9262
|
const target = resolve5(process.cwd(), opts.out);
|
|
9170
|
-
if (
|
|
9263
|
+
if (existsSync11(target) && !opts.force) {
|
|
9171
9264
|
if (json) {
|
|
9172
9265
|
throw new CLIError(
|
|
9173
9266
|
`${opts.out} exists. Re-run with --force to overwrite.`,
|
|
@@ -9194,7 +9287,7 @@ function registerConfigExportCommand(cfg) {
|
|
|
9194
9287
|
const raw = await res.json();
|
|
9195
9288
|
const { config, skipped } = configFromMetadata(raw);
|
|
9196
9289
|
const toml = stringifyConfigToml(config);
|
|
9197
|
-
|
|
9290
|
+
writeFileSync8(target, toml, "utf8");
|
|
9198
9291
|
if (json) {
|
|
9199
9292
|
console.log(JSON.stringify({ written: target, config, skipped }, null, 2));
|
|
9200
9293
|
} else {
|
|
@@ -9230,7 +9323,7 @@ function registerConfigExportCommand(cfg) {
|
|
|
9230
9323
|
}
|
|
9231
9324
|
|
|
9232
9325
|
// src/commands/config/plan.ts
|
|
9233
|
-
import { readFileSync as
|
|
9326
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
9234
9327
|
import { resolve as resolve6 } from "path";
|
|
9235
9328
|
import pc5 from "picocolors";
|
|
9236
9329
|
|
|
@@ -9538,7 +9631,7 @@ function registerConfigPlanCommand(cfg) {
|
|
|
9538
9631
|
projectConfig = getProjectConfig();
|
|
9539
9632
|
await requireAuth();
|
|
9540
9633
|
const tomlPath = resolve6(process.cwd(), opts.file);
|
|
9541
|
-
const tomlSource =
|
|
9634
|
+
const tomlSource = readFileSync9(tomlPath, "utf8");
|
|
9542
9635
|
const file = parseConfigToml(tomlSource);
|
|
9543
9636
|
const res = await ossFetch("/api/metadata");
|
|
9544
9637
|
const raw = await res.json();
|
|
@@ -9582,7 +9675,7 @@ function registerConfigPlanCommand(cfg) {
|
|
|
9582
9675
|
}
|
|
9583
9676
|
|
|
9584
9677
|
// src/commands/config/apply.ts
|
|
9585
|
-
import { readFileSync as
|
|
9678
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
9586
9679
|
import { resolve as resolve7 } from "path";
|
|
9587
9680
|
import * as p2 from "@clack/prompts";
|
|
9588
9681
|
import pc6 from "picocolors";
|
|
@@ -9594,7 +9687,7 @@ function registerConfigApplyCommand(cfg) {
|
|
|
9594
9687
|
projectConfig = getProjectConfig();
|
|
9595
9688
|
await requireAuth();
|
|
9596
9689
|
const tomlPath = resolve7(process.cwd(), opts.file);
|
|
9597
|
-
const tomlSource =
|
|
9690
|
+
const tomlSource = readFileSync10(tomlPath, "utf8");
|
|
9598
9691
|
const file = parseConfigToml(tomlSource);
|
|
9599
9692
|
const res = await ossFetch("/api/metadata");
|
|
9600
9693
|
const raw = await res.json();
|
|
@@ -9783,9 +9876,9 @@ function registerConfigCommand(program2) {
|
|
|
9783
9876
|
}
|
|
9784
9877
|
|
|
9785
9878
|
// src/commands/ai/setup.ts
|
|
9786
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
9787
|
-
import { isAbsolute, join as
|
|
9788
|
-
import * as
|
|
9879
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
|
|
9880
|
+
import { isAbsolute, join as join14, relative as relative3, resolve as resolve8 } from "path";
|
|
9881
|
+
import * as clack18 from "@clack/prompts";
|
|
9789
9882
|
import pc7 from "picocolors";
|
|
9790
9883
|
|
|
9791
9884
|
// src/lib/api/ai.ts
|
|
@@ -9806,7 +9899,7 @@ async function getOpenRouterApiKey() {
|
|
|
9806
9899
|
}
|
|
9807
9900
|
|
|
9808
9901
|
// src/lib/env-writer.ts
|
|
9809
|
-
import { existsSync as
|
|
9902
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
9810
9903
|
var KEY_LINE_RE = (key) => (
|
|
9811
9904
|
// Match `KEY=...` at the start of a line (allowing leading whitespace).
|
|
9812
9905
|
// Captures the value side; we only need the value portion to compare.
|
|
@@ -9821,8 +9914,8 @@ function stripQuotes(v) {
|
|
|
9821
9914
|
return hash >= 0 ? t.slice(0, hash).trimEnd() : t;
|
|
9822
9915
|
}
|
|
9823
9916
|
function upsertEnvFile(path6, entries) {
|
|
9824
|
-
const exists =
|
|
9825
|
-
let content = exists ?
|
|
9917
|
+
const exists = existsSync12(path6);
|
|
9918
|
+
let content = exists ? readFileSync11(path6, "utf-8") : "";
|
|
9826
9919
|
const result = { added: [], skipped: [], mismatched: [] };
|
|
9827
9920
|
const additions = [];
|
|
9828
9921
|
for (const [key, value] of Object.entries(entries)) {
|
|
@@ -9845,7 +9938,7 @@ function upsertEnvFile(path6, entries) {
|
|
|
9845
9938
|
content += "\n";
|
|
9846
9939
|
}
|
|
9847
9940
|
content += additions.join("\n") + "\n";
|
|
9848
|
-
|
|
9941
|
+
writeFileSync9(path6, content);
|
|
9849
9942
|
} else if (!exists) {
|
|
9850
9943
|
}
|
|
9851
9944
|
return result;
|
|
@@ -9878,10 +9971,10 @@ async function runAiSetup(opts) {
|
|
|
9878
9971
|
throw new ProjectNotLinkedError();
|
|
9879
9972
|
}
|
|
9880
9973
|
if (!opts.json) {
|
|
9881
|
-
|
|
9974
|
+
clack18.intro("AI setup");
|
|
9882
9975
|
outputSuccess(`Linked to InsForge project: ${project.project_name} (${project.project_id})`);
|
|
9883
9976
|
}
|
|
9884
|
-
const spinner11 = !opts.json && isInteractive ?
|
|
9977
|
+
const spinner11 = !opts.json && isInteractive ? clack18.spinner() : null;
|
|
9885
9978
|
spinner11?.start("Fetching OpenRouter key...");
|
|
9886
9979
|
let key;
|
|
9887
9980
|
try {
|
|
@@ -9914,7 +10007,7 @@ async function runAiSetup(opts) {
|
|
|
9914
10007
|
outputInfo(pc7.dim(`${envLabel}: ${update.skipped.join(", ")} already set (matching) - left as-is.`));
|
|
9915
10008
|
}
|
|
9916
10009
|
for (const m of update.mismatched) {
|
|
9917
|
-
|
|
10010
|
+
clack18.log.warn(
|
|
9918
10011
|
`${envLabel} already has ${m.key}; left existing value untouched. Remove it or pass --env-file to write elsewhere.`
|
|
9919
10012
|
);
|
|
9920
10013
|
}
|
|
@@ -9922,7 +10015,7 @@ async function runAiSetup(opts) {
|
|
|
9922
10015
|
outputInfo(pc7.dim("Added .env*.local to .gitignore."));
|
|
9923
10016
|
}
|
|
9924
10017
|
if (!isLocalEnvFile(envFile)) {
|
|
9925
|
-
|
|
10018
|
+
clack18.log.warn(
|
|
9926
10019
|
`${envLabel} may be committed unless it is listed in .gitignore. Keep ${OPENROUTER_ENV_KEY} server-only.`
|
|
9927
10020
|
);
|
|
9928
10021
|
}
|
|
@@ -9930,7 +10023,7 @@ async function runAiSetup(opts) {
|
|
|
9930
10023
|
outputInfo("Use this key only from server-side code as process.env.OPENROUTER_API_KEY.");
|
|
9931
10024
|
outputInfo("For deployment, add OPENROUTER_API_KEY to your hosting provider environment.");
|
|
9932
10025
|
outputInfo(`Do not rename it to ${pc7.bold("NEXT_PUBLIC_")}, ${pc7.bold("VITE_")}, or ${pc7.bold("PUBLIC_")}.`);
|
|
9933
|
-
|
|
10026
|
+
clack18.outro("Done.");
|
|
9934
10027
|
}
|
|
9935
10028
|
return {
|
|
9936
10029
|
envFile: envLabel,
|
|
@@ -9960,8 +10053,8 @@ function ensureLocalEnvIgnored(cwd, envFile) {
|
|
|
9960
10053
|
if (!relEnvPath || relEnvPath.startsWith("..") || isAbsolute(relEnvPath)) {
|
|
9961
10054
|
return false;
|
|
9962
10055
|
}
|
|
9963
|
-
const gitignorePath =
|
|
9964
|
-
const existing =
|
|
10056
|
+
const gitignorePath = join14(cwd, ".gitignore");
|
|
10057
|
+
const existing = existsSync13(gitignorePath) ? readFileSync12(gitignorePath, "utf-8") : "";
|
|
9965
10058
|
const lines = new Set(existing.split(/\r?\n/).map((line) => line.trim()));
|
|
9966
10059
|
const envBasename = envFile.replace(/\\/g, "/").split("/").pop() ?? envFile;
|
|
9967
10060
|
if (lines.has(".env*") || lines.has(".env.*") || lines.has(".env*.local") || lines.has(".env.local") && envBasename === ".env.local") {
|
|
@@ -9982,7 +10075,7 @@ function registerAiCommands(aiCmd2) {
|
|
|
9982
10075
|
|
|
9983
10076
|
// src/index.ts
|
|
9984
10077
|
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
9985
|
-
var pkg = JSON.parse(
|
|
10078
|
+
var pkg = JSON.parse(readFileSync13(join15(__dirname, "../package.json"), "utf-8"));
|
|
9986
10079
|
var INSFORGE_LOGO = `
|
|
9987
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
|
|
9988
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
|
|
@@ -10093,7 +10186,7 @@ async function showInteractiveMenu() {
|
|
|
10093
10186
|
} catch {
|
|
10094
10187
|
}
|
|
10095
10188
|
console.log(INSFORGE_LOGO);
|
|
10096
|
-
|
|
10189
|
+
clack19.intro(`InsForge CLI v${pkg.version}`);
|
|
10097
10190
|
const options = [];
|
|
10098
10191
|
if (!isLoggedIn) {
|
|
10099
10192
|
options.push({ value: "login", label: "Log in to InsForge" });
|
|
@@ -10114,7 +10207,7 @@ async function showInteractiveMenu() {
|
|
|
10114
10207
|
options
|
|
10115
10208
|
});
|
|
10116
10209
|
if (isCancel2(action)) {
|
|
10117
|
-
|
|
10210
|
+
clack19.cancel("Bye!");
|
|
10118
10211
|
process.exit(0);
|
|
10119
10212
|
}
|
|
10120
10213
|
switch (action) {
|