@insforge/cli 0.1.82 → 0.1.85
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 +397 -229
- 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";
|
|
@@ -1172,7 +1172,7 @@ import * as clack5 from "@clack/prompts";
|
|
|
1172
1172
|
|
|
1173
1173
|
// src/lib/analytics.ts
|
|
1174
1174
|
import { PostHog } from "posthog-node";
|
|
1175
|
-
var POSTHOG_API_KEY = "
|
|
1175
|
+
var POSTHOG_API_KEY = "";
|
|
1176
1176
|
var POSTHOG_HOST = process.env.POSTHOG_HOST || "https://us.i.posthog.com";
|
|
1177
1177
|
var client = null;
|
|
1178
1178
|
function getClient() {
|
|
@@ -1215,6 +1215,28 @@ function trackPayments(subcommand, config, properties) {
|
|
|
1215
1215
|
...properties
|
|
1216
1216
|
});
|
|
1217
1217
|
}
|
|
1218
|
+
function trackDeployments(subcommand, config, properties) {
|
|
1219
|
+
captureEvent(config.project_id, "cli_deployments_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
|
+
}
|
|
1229
|
+
function trackPosthog(subcommand, config, properties) {
|
|
1230
|
+
captureEvent(config.project_id, "cli_posthog_invoked", {
|
|
1231
|
+
subcommand,
|
|
1232
|
+
project_id: config.project_id,
|
|
1233
|
+
project_name: config.project_name,
|
|
1234
|
+
org_id: config.org_id,
|
|
1235
|
+
region: config.region,
|
|
1236
|
+
oss_mode: config.project_id === FAKE_PROJECT_ID,
|
|
1237
|
+
...properties
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1218
1240
|
function trackConfig(subcommand, config, properties) {
|
|
1219
1241
|
const distinctId = config?.project_id ?? FAKE_PROJECT_ID;
|
|
1220
1242
|
captureEvent(distinctId, "cli_config_invoked", {
|
|
@@ -1707,15 +1729,91 @@ import { exec as exec3 } from "child_process";
|
|
|
1707
1729
|
import { promisify as promisify4 } from "util";
|
|
1708
1730
|
import * as fs5 from "fs/promises";
|
|
1709
1731
|
import * as path5 from "path";
|
|
1710
|
-
import * as
|
|
1732
|
+
import * as clack14 from "@clack/prompts";
|
|
1711
1733
|
import pc2 from "picocolors";
|
|
1712
1734
|
|
|
1713
1735
|
// src/lib/skills.ts
|
|
1714
1736
|
import { exec } from "child_process";
|
|
1715
|
-
import { existsSync as
|
|
1716
|
-
import { join as
|
|
1737
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, appendFileSync } from "fs";
|
|
1738
|
+
import { join as join3 } from "path";
|
|
1717
1739
|
import { promisify } from "util";
|
|
1740
|
+
import * as clack10 from "@clack/prompts";
|
|
1741
|
+
|
|
1742
|
+
// src/lib/agents-md.ts
|
|
1743
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
1744
|
+
import { join as join2 } from "path";
|
|
1718
1745
|
import * as clack9 from "@clack/prompts";
|
|
1746
|
+
var AGENTS_MD_START = "<!-- INSFORGE:START -->";
|
|
1747
|
+
var AGENTS_MD_END = "<!-- INSFORGE:END -->";
|
|
1748
|
+
function buildInsforgeBlock(config) {
|
|
1749
|
+
const lines = [
|
|
1750
|
+
AGENTS_MD_START,
|
|
1751
|
+
"## InsForge backend",
|
|
1752
|
+
"",
|
|
1753
|
+
"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.",
|
|
1754
|
+
""
|
|
1755
|
+
];
|
|
1756
|
+
if (config?.project_name || config?.oss_host) {
|
|
1757
|
+
const name = config.project_name ? `**${config.project_name}**` : "This project";
|
|
1758
|
+
const host = config.oss_host ? ` (API base \`${config.oss_host}\`)` : "";
|
|
1759
|
+
lines.push(`- **Project:** ${name}${host}`);
|
|
1760
|
+
}
|
|
1761
|
+
lines.push(
|
|
1762
|
+
"- **Skills:** these InsForge skills are installed for supported coding agents. Reach for them before implementing any InsForge feature instead of guessing the API:",
|
|
1763
|
+
" - `insforge`: app code with the `@insforge/sdk` client (database CRUD, auth, storage, edge functions, realtime, AI, email, and Stripe payments).",
|
|
1764
|
+
" - `insforge-cli`: backend and infrastructure via the `insforge` CLI (projects, SQL, migrations, RLS policies, storage buckets, functions, secrets, payment setup, schedules, deploys).",
|
|
1765
|
+
" - `insforge-debug`: diagnosing failures (SDK/HTTP errors, RLS denials, auth and OAuth issues) and running security or performance audits.",
|
|
1766
|
+
" - `insforge-integrations`: wiring external auth providers (Clerk, Auth0, WorkOS, Better Auth, etc.) for JWT-based RLS, or the OKX x402 payment facilitator.",
|
|
1767
|
+
" - `find-skills`: discovering additional skills on demand.",
|
|
1768
|
+
"- **Credentials:** app code reads keys from `.env.local`; the CLI reads `.insforge/project.json`. Never hardcode or commit keys.",
|
|
1769
|
+
"",
|
|
1770
|
+
"Key patterns:",
|
|
1771
|
+
"",
|
|
1772
|
+
"- Database inserts take an array: `insert([{ ... }])`.",
|
|
1773
|
+
"- Reference users with `auth.users(id)`; use `auth.uid()` in RLS policies.",
|
|
1774
|
+
"- For storage uploads, persist both the returned `url` and `key`.",
|
|
1775
|
+
AGENTS_MD_END
|
|
1776
|
+
);
|
|
1777
|
+
return lines.join("\n");
|
|
1778
|
+
}
|
|
1779
|
+
function mergeAgentsMd(existing, config) {
|
|
1780
|
+
const block = buildInsforgeBlock(config);
|
|
1781
|
+
if (existing === null || existing.trim() === "") {
|
|
1782
|
+
return `# AGENTS.md
|
|
1783
|
+
|
|
1784
|
+
${block}
|
|
1785
|
+
`;
|
|
1786
|
+
}
|
|
1787
|
+
const startIdx = existing.indexOf(AGENTS_MD_START);
|
|
1788
|
+
if (startIdx !== -1) {
|
|
1789
|
+
const endMarkerIdx = existing.indexOf(AGENTS_MD_END, startIdx + AGENTS_MD_START.length);
|
|
1790
|
+
let before = existing.slice(0, startIdx);
|
|
1791
|
+
if (before.length > 0 && !before.endsWith("\n")) before += "\n";
|
|
1792
|
+
const after = endMarkerIdx === -1 ? "\n" : existing.slice(endMarkerIdx + AGENTS_MD_END.length);
|
|
1793
|
+
return `${before}${block}${after}`;
|
|
1794
|
+
}
|
|
1795
|
+
return `${existing.replace(/\s+$/, "")}
|
|
1796
|
+
|
|
1797
|
+
${block}
|
|
1798
|
+
`;
|
|
1799
|
+
}
|
|
1800
|
+
function writeLocalAgentsMd(json, opts) {
|
|
1801
|
+
const cwd = opts?.cwd ?? process.cwd();
|
|
1802
|
+
const config = opts?.config !== void 0 ? opts.config : getProjectConfig();
|
|
1803
|
+
const path6 = join2(cwd, "AGENTS.md");
|
|
1804
|
+
const existed = existsSync3(path6);
|
|
1805
|
+
const existing = existed ? readFileSync2(path6, "utf-8") : null;
|
|
1806
|
+
const next = mergeAgentsMd(existing, config);
|
|
1807
|
+
if (existing === next) return;
|
|
1808
|
+
writeFileSync3(path6, next);
|
|
1809
|
+
if (!json) {
|
|
1810
|
+
clack9.log.success(
|
|
1811
|
+
existed ? "Updated AGENTS.md with InsForge guidance." : "Created AGENTS.md with InsForge guidance."
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// src/lib/skills.ts
|
|
1719
1817
|
var execAsync = promisify(exec);
|
|
1720
1818
|
var SKILL_INSTALL_TIMEOUT_MS = 6e4;
|
|
1721
1819
|
function describeExecError(err) {
|
|
@@ -1755,8 +1853,8 @@ var GITIGNORE_ENTRIES = [
|
|
|
1755
1853
|
".windsurf"
|
|
1756
1854
|
];
|
|
1757
1855
|
function updateGitignore() {
|
|
1758
|
-
const gitignorePath =
|
|
1759
|
-
const existing =
|
|
1856
|
+
const gitignorePath = join3(process.cwd(), ".gitignore");
|
|
1857
|
+
const existing = existsSync4(gitignorePath) ? readFileSync3(gitignorePath, "utf-8") : "";
|
|
1760
1858
|
const lines = new Set(existing.split("\n").map((l) => l.trim()));
|
|
1761
1859
|
const missing = GITIGNORE_ENTRIES.filter((entry) => !lines.has(entry));
|
|
1762
1860
|
if (!missing.length) return;
|
|
@@ -1772,44 +1870,44 @@ var PROVIDER_SKILLS = {
|
|
|
1772
1870
|
};
|
|
1773
1871
|
async function installSkills(json, authProvider) {
|
|
1774
1872
|
try {
|
|
1775
|
-
if (!json)
|
|
1873
|
+
if (!json) clack10.log.info("Installing InsForge agent skills (global)...");
|
|
1776
1874
|
await execAsync(`npx skills add insforge/agent-skills -g -y ${AGENT_FLAGS}`, {
|
|
1777
1875
|
cwd: process.cwd(),
|
|
1778
1876
|
timeout: SKILL_INSTALL_TIMEOUT_MS
|
|
1779
1877
|
});
|
|
1780
|
-
if (!json)
|
|
1878
|
+
if (!json) clack10.log.success("InsForge agent skills installed.");
|
|
1781
1879
|
} catch (err) {
|
|
1782
1880
|
if (!json) {
|
|
1783
|
-
|
|
1784
|
-
|
|
1881
|
+
clack10.log.warn(`Could not install agent skills: ${describeExecError(err)}`);
|
|
1882
|
+
clack10.log.info("Run `npx skills add insforge/agent-skills` once resolved to see the full output.");
|
|
1785
1883
|
}
|
|
1786
1884
|
}
|
|
1787
1885
|
try {
|
|
1788
|
-
if (!json)
|
|
1886
|
+
if (!json) clack10.log.info("Installing find-skills (global)...");
|
|
1789
1887
|
await execAsync("npx skills add https://github.com/vercel-labs/skills --skill find-skills -g -y", {
|
|
1790
1888
|
cwd: process.cwd(),
|
|
1791
1889
|
timeout: SKILL_INSTALL_TIMEOUT_MS
|
|
1792
1890
|
});
|
|
1793
|
-
if (!json)
|
|
1891
|
+
if (!json) clack10.log.success("find-skills installed.");
|
|
1794
1892
|
} catch (err) {
|
|
1795
1893
|
if (!json) {
|
|
1796
|
-
|
|
1797
|
-
|
|
1894
|
+
clack10.log.warn(`Could not install find-skills: ${describeExecError(err)}`);
|
|
1895
|
+
clack10.log.info("Run `npx skills add https://github.com/vercel-labs/skills --skill find-skills` once resolved.");
|
|
1798
1896
|
}
|
|
1799
1897
|
}
|
|
1800
1898
|
const providerEntry = authProvider ? PROVIDER_SKILLS[authProvider] : void 0;
|
|
1801
1899
|
if (providerEntry) {
|
|
1802
1900
|
try {
|
|
1803
|
-
if (!json)
|
|
1901
|
+
if (!json) clack10.log.info(`Installing ${providerEntry.label} (global)...`);
|
|
1804
1902
|
await execAsync(`npx skills add ${providerEntry.repo} -g -y ${AGENT_FLAGS}`, {
|
|
1805
1903
|
cwd: process.cwd(),
|
|
1806
1904
|
timeout: SKILL_INSTALL_TIMEOUT_MS
|
|
1807
1905
|
});
|
|
1808
|
-
if (!json)
|
|
1906
|
+
if (!json) clack10.log.success(`${providerEntry.label} installed.`);
|
|
1809
1907
|
} catch (err) {
|
|
1810
1908
|
if (!json) {
|
|
1811
|
-
|
|
1812
|
-
|
|
1909
|
+
clack10.log.warn(`Could not install ${providerEntry.label}: ${describeExecError(err)}`);
|
|
1910
|
+
clack10.log.info(`Run \`npx skills add ${providerEntry.repo}\` once resolved to see the full output.`);
|
|
1813
1911
|
}
|
|
1814
1912
|
}
|
|
1815
1913
|
}
|
|
@@ -1817,6 +1915,10 @@ async function installSkills(json, authProvider) {
|
|
|
1817
1915
|
updateGitignore();
|
|
1818
1916
|
} catch {
|
|
1819
1917
|
}
|
|
1918
|
+
try {
|
|
1919
|
+
writeLocalAgentsMd(json);
|
|
1920
|
+
} catch {
|
|
1921
|
+
}
|
|
1820
1922
|
}
|
|
1821
1923
|
async function reportCliUsage(toolName, success, maxRetries = 1, explicitConfig) {
|
|
1822
1924
|
let config = explicitConfig;
|
|
@@ -1866,7 +1968,7 @@ import { tmpdir } from "os";
|
|
|
1866
1968
|
import { execFile } from "child_process";
|
|
1867
1969
|
import { promisify as promisify2 } from "util";
|
|
1868
1970
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
1869
|
-
import * as
|
|
1971
|
+
import * as clack11 from "@clack/prompts";
|
|
1870
1972
|
|
|
1871
1973
|
// src/lib/api/oss.ts
|
|
1872
1974
|
function requireProjectConfig() {
|
|
@@ -2107,7 +2209,7 @@ async function applyAuthProvider(provider, cwd, projectConfig, json) {
|
|
|
2107
2209
|
if (!VALID_AUTH_PROVIDERS.includes(provider)) {
|
|
2108
2210
|
throw new Error(`Unknown auth provider: ${provider}`);
|
|
2109
2211
|
}
|
|
2110
|
-
const fetchSpinner = !json ?
|
|
2212
|
+
const fetchSpinner = !json ? clack11.spinner() : null;
|
|
2111
2213
|
fetchSpinner?.start(`Fetching ${provider} scaffold from templates repo...`);
|
|
2112
2214
|
const { dir: providerDir, cleanup } = await fetchProviderTree(provider);
|
|
2113
2215
|
fetchSpinner?.stop(`${provider} scaffold ready`);
|
|
@@ -2206,15 +2308,15 @@ async function applyAuthProvider(provider, cwd, projectConfig, json) {
|
|
|
2206
2308
|
result.envKeysRefreshed = Array.from(/* @__PURE__ */ new Set([...result.envKeysRefreshed, ...refreshed]));
|
|
2207
2309
|
}
|
|
2208
2310
|
if (!jwtSecret && !json) {
|
|
2209
|
-
|
|
2311
|
+
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
2312
|
}
|
|
2211
2313
|
if (result.envKeysSkipped.length > 0 && !json) {
|
|
2212
|
-
|
|
2314
|
+
clack11.log.warn(
|
|
2213
2315
|
`Kept your existing values for: ${result.envKeysSkipped.join(", ")}. If any of these need the auth-provider's defaults, see .env.example for reference.`
|
|
2214
2316
|
);
|
|
2215
2317
|
}
|
|
2216
2318
|
if (result.envKeysRefreshed.length > 0 && !json) {
|
|
2217
|
-
|
|
2319
|
+
clack11.log.info(
|
|
2218
2320
|
`Refreshed stale platform defaults: ${result.envKeysRefreshed.join(", ")}. Your value matched the manifest's default, so we replaced it with the live one.`
|
|
2219
2321
|
);
|
|
2220
2322
|
}
|
|
@@ -2230,7 +2332,7 @@ import { tmpdir as tmpdir2 } from "os";
|
|
|
2230
2332
|
import { promisify as promisify3 } from "util";
|
|
2231
2333
|
import * as fs4 from "fs/promises";
|
|
2232
2334
|
import * as path4 from "path";
|
|
2233
|
-
import * as
|
|
2335
|
+
import * as clack13 from "@clack/prompts";
|
|
2234
2336
|
|
|
2235
2337
|
// src/lib/env.ts
|
|
2236
2338
|
import * as fs2 from "fs/promises";
|
|
@@ -2265,8 +2367,26 @@ import * as path3 from "path";
|
|
|
2265
2367
|
import * as fs3 from "fs/promises";
|
|
2266
2368
|
import { createReadStream } from "fs";
|
|
2267
2369
|
import { createHash as createHash2 } from "crypto";
|
|
2268
|
-
import * as
|
|
2370
|
+
import * as clack12 from "@clack/prompts";
|
|
2269
2371
|
import archiver from "archiver";
|
|
2372
|
+
|
|
2373
|
+
// src/commands/deployments/utils.ts
|
|
2374
|
+
async function trackDeploymentUsage(subcommand, success, properties = {}) {
|
|
2375
|
+
try {
|
|
2376
|
+
const config = getProjectConfig();
|
|
2377
|
+
if (config) {
|
|
2378
|
+
trackDeployments(subcommand, config, {
|
|
2379
|
+
success,
|
|
2380
|
+
...properties
|
|
2381
|
+
});
|
|
2382
|
+
}
|
|
2383
|
+
} catch {
|
|
2384
|
+
} finally {
|
|
2385
|
+
await shutdownAnalytics();
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
// src/commands/deployments/deploy.ts
|
|
2270
2390
|
var POLL_INTERVAL_MS3 = 5e3;
|
|
2271
2391
|
var POLL_TIMEOUT_MS3 = 3e5;
|
|
2272
2392
|
var DIRECT_UPLOAD_CONCURRENCY = 8;
|
|
@@ -2565,7 +2685,7 @@ function registerDeploymentsDeployCommand(deploymentsCmd2) {
|
|
|
2565
2685
|
`"${dirName}" is an excluded directory and cannot be used as a deploy source. Please specify your project root or output directory instead.`
|
|
2566
2686
|
);
|
|
2567
2687
|
}
|
|
2568
|
-
const spinner11 = !json ?
|
|
2688
|
+
const spinner11 = !json ? clack12.spinner() : null;
|
|
2569
2689
|
const startBody = {};
|
|
2570
2690
|
if (opts.env) {
|
|
2571
2691
|
try {
|
|
@@ -2598,9 +2718,9 @@ function registerDeploymentsDeployCommand(deploymentsCmd2) {
|
|
|
2598
2718
|
outputJson(result.deployment);
|
|
2599
2719
|
} else {
|
|
2600
2720
|
if (result.liveUrl) {
|
|
2601
|
-
|
|
2721
|
+
clack12.log.success(`Live at: ${result.liveUrl}`);
|
|
2602
2722
|
}
|
|
2603
|
-
|
|
2723
|
+
clack12.log.info(`Deployment ID: ${result.deploymentId}`);
|
|
2604
2724
|
}
|
|
2605
2725
|
} else {
|
|
2606
2726
|
spinner11?.stop("Deployment is still building");
|
|
@@ -2611,14 +2731,21 @@ function registerDeploymentsDeployCommand(deploymentsCmd2) {
|
|
|
2611
2731
|
timedOut: true
|
|
2612
2732
|
});
|
|
2613
2733
|
} else {
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2734
|
+
clack12.log.info(`Deployment ID: ${result.deploymentId}`);
|
|
2735
|
+
clack12.log.warn("Deployment did not finish within 5 minutes.");
|
|
2736
|
+
clack12.log.info(`Check status with: npx @insforge/cli deployments status ${result.deploymentId}`);
|
|
2617
2737
|
}
|
|
2618
2738
|
}
|
|
2619
|
-
await
|
|
2739
|
+
await trackDeploymentUsage("deploy", true, {
|
|
2740
|
+
has_env: opts.env !== void 0,
|
|
2741
|
+
has_meta: opts.meta !== void 0,
|
|
2742
|
+
ready: result.isReady
|
|
2743
|
+
});
|
|
2620
2744
|
} catch (err) {
|
|
2621
|
-
await
|
|
2745
|
+
await trackDeploymentUsage("deploy", false, {
|
|
2746
|
+
has_env: opts.env !== void 0,
|
|
2747
|
+
has_meta: opts.meta !== void 0
|
|
2748
|
+
});
|
|
2622
2749
|
handleError(err, json);
|
|
2623
2750
|
}
|
|
2624
2751
|
});
|
|
@@ -2753,7 +2880,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2753
2880
|
await requireAuth(apiUrl, false);
|
|
2754
2881
|
if (!json) {
|
|
2755
2882
|
await animateBanner();
|
|
2756
|
-
|
|
2883
|
+
clack13.intro("Let's build something great");
|
|
2757
2884
|
}
|
|
2758
2885
|
let orgId = opts.orgId;
|
|
2759
2886
|
if (!orgId) {
|
|
@@ -2763,7 +2890,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2763
2890
|
}
|
|
2764
2891
|
if (orgs.length === 1) {
|
|
2765
2892
|
orgId = orgs[0].id;
|
|
2766
|
-
if (!json)
|
|
2893
|
+
if (!json) clack13.log.info(`Using organization: ${orgs[0].name}`);
|
|
2767
2894
|
} else {
|
|
2768
2895
|
if (json) {
|
|
2769
2896
|
throw new CLIError("Multiple organizations found. Specify --org-id.");
|
|
@@ -2879,7 +3006,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2879
3006
|
process.chdir(projectDir);
|
|
2880
3007
|
}
|
|
2881
3008
|
let projectLinked = false;
|
|
2882
|
-
const s = !json ?
|
|
3009
|
+
const s = !json ? clack13.spinner() : null;
|
|
2883
3010
|
try {
|
|
2884
3011
|
s?.start("Creating project...");
|
|
2885
3012
|
const project = await createProject(orgId, projectName, opts.region, apiUrl);
|
|
@@ -2906,10 +3033,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2906
3033
|
json
|
|
2907
3034
|
);
|
|
2908
3035
|
if (downloaded) {
|
|
2909
|
-
void reportMarketplaceDownload(
|
|
2910
|
-
opts.marketplace,
|
|
2911
|
-
apiUrl ?? "https://api.insforge.dev"
|
|
2912
|
-
);
|
|
3036
|
+
void reportMarketplaceDownload(opts.marketplace);
|
|
2913
3037
|
}
|
|
2914
3038
|
} else if (githubTemplates.includes(template)) {
|
|
2915
3039
|
await downloadGitHubTemplate(template, projectConfig, json);
|
|
@@ -2919,7 +3043,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2919
3043
|
try {
|
|
2920
3044
|
const anonKey = await getAnonKey();
|
|
2921
3045
|
if (!anonKey) {
|
|
2922
|
-
if (!json)
|
|
3046
|
+
if (!json) clack13.log.warn("Could not retrieve anon key. You can add it to .env.local manually.");
|
|
2923
3047
|
} else {
|
|
2924
3048
|
const envPath = path4.join(process.cwd(), ".env.local");
|
|
2925
3049
|
const envContent = [
|
|
@@ -2930,16 +3054,16 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2930
3054
|
].join("\n");
|
|
2931
3055
|
await fs4.writeFile(envPath, envContent, { flag: "wx" });
|
|
2932
3056
|
if (!json) {
|
|
2933
|
-
|
|
3057
|
+
clack13.log.success("Created .env.local with your InsForge credentials");
|
|
2934
3058
|
}
|
|
2935
3059
|
}
|
|
2936
3060
|
} catch (err) {
|
|
2937
3061
|
const error = err;
|
|
2938
3062
|
if (!json) {
|
|
2939
3063
|
if (error.code === "EEXIST") {
|
|
2940
|
-
|
|
3064
|
+
clack13.log.warn(".env.local already exists; skipping InsForge key seeding.");
|
|
2941
3065
|
} else {
|
|
2942
|
-
|
|
3066
|
+
clack13.log.warn(`Failed to create .env.local: ${error.message}`);
|
|
2943
3067
|
}
|
|
2944
3068
|
}
|
|
2945
3069
|
}
|
|
@@ -2948,12 +3072,12 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2948
3072
|
try {
|
|
2949
3073
|
const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig, json);
|
|
2950
3074
|
if (!json) {
|
|
2951
|
-
|
|
3075
|
+
clack13.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
|
|
2952
3076
|
}
|
|
2953
3077
|
} catch (err) {
|
|
2954
3078
|
const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
|
|
2955
3079
|
if (json) console.error(JSON.stringify({ warning: msg }));
|
|
2956
|
-
else
|
|
3080
|
+
else clack13.log.warn(msg);
|
|
2957
3081
|
}
|
|
2958
3082
|
}
|
|
2959
3083
|
await installSkills(json, opts.auth);
|
|
@@ -2961,7 +3085,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2961
3085
|
await reportCliUsage("cli.create", true, 6);
|
|
2962
3086
|
const templateDownloaded = hasTemplate ? await fs4.stat(path4.join(process.cwd(), "package.json")).catch(() => null) : null;
|
|
2963
3087
|
if (templateDownloaded) {
|
|
2964
|
-
const installSpinner = !json ?
|
|
3088
|
+
const installSpinner = !json ? clack13.spinner() : null;
|
|
2965
3089
|
installSpinner?.start("Installing dependencies...");
|
|
2966
3090
|
try {
|
|
2967
3091
|
await execAsync2("npm install", { cwd: process.cwd(), maxBuffer: 10 * 1024 * 1024 });
|
|
@@ -2969,8 +3093,8 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2969
3093
|
} catch (err) {
|
|
2970
3094
|
installSpinner?.stop("Failed to install dependencies");
|
|
2971
3095
|
if (!json) {
|
|
2972
|
-
|
|
2973
|
-
|
|
3096
|
+
clack13.log.warn(`npm install failed: ${err.message}`);
|
|
3097
|
+
clack13.log.info("Run `npm install` manually to install dependencies.");
|
|
2974
3098
|
}
|
|
2975
3099
|
}
|
|
2976
3100
|
}
|
|
@@ -2986,7 +3110,7 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2986
3110
|
if (envVars.length > 0) {
|
|
2987
3111
|
startBody.envVars = envVars;
|
|
2988
3112
|
}
|
|
2989
|
-
const deploySpinner =
|
|
3113
|
+
const deploySpinner = clack13.spinner();
|
|
2990
3114
|
const result = await deployProject({
|
|
2991
3115
|
sourceDir: process.cwd(),
|
|
2992
3116
|
startBody,
|
|
@@ -2997,12 +3121,12 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
2997
3121
|
liveUrl = result.liveUrl;
|
|
2998
3122
|
} else {
|
|
2999
3123
|
deploySpinner.stop("Deployment is still building");
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3124
|
+
clack13.log.info(`Deployment ID: ${result.deploymentId}`);
|
|
3125
|
+
clack13.log.warn("Deployment did not finish within 2 minutes.");
|
|
3126
|
+
clack13.log.info(`Check status with: npx @insforge/cli deployments status ${result.deploymentId}`);
|
|
3003
3127
|
}
|
|
3004
3128
|
} catch (err) {
|
|
3005
|
-
|
|
3129
|
+
clack13.log.warn(`Deploy failed: ${err.message}`);
|
|
3006
3130
|
}
|
|
3007
3131
|
}
|
|
3008
3132
|
}
|
|
@@ -3019,33 +3143,33 @@ Browse available templates: https://insforge.dev/templates`
|
|
|
3019
3143
|
}
|
|
3020
3144
|
});
|
|
3021
3145
|
} else {
|
|
3022
|
-
|
|
3146
|
+
clack13.log.step(`Dashboard: ${dashboardUrl}`);
|
|
3023
3147
|
if (liveUrl) {
|
|
3024
|
-
|
|
3148
|
+
clack13.log.success(`Live site: ${liveUrl}`);
|
|
3025
3149
|
}
|
|
3026
3150
|
if (templateDownloaded) {
|
|
3027
3151
|
const steps = [
|
|
3028
3152
|
`cd ${dirName}`,
|
|
3029
3153
|
"npm run dev"
|
|
3030
3154
|
];
|
|
3031
|
-
|
|
3032
|
-
|
|
3155
|
+
clack13.note(steps.join("\n"), "Next steps");
|
|
3156
|
+
clack13.note("Open your coding agent (Claude Code, Codex, Cursor, etc.) to add new features.", "Keep building");
|
|
3033
3157
|
} else if (hasTemplate && !templateDownloaded) {
|
|
3034
|
-
|
|
3158
|
+
clack13.log.warn("Template download failed. You can retry or set up manually.");
|
|
3035
3159
|
} else {
|
|
3036
3160
|
const prompts = [
|
|
3037
3161
|
"Build a todo app with Google OAuth sign-in",
|
|
3038
3162
|
"Build an Instagram clone where users can upload photos, like, and comment",
|
|
3039
3163
|
"Build an AI chatbot with conversation history"
|
|
3040
3164
|
];
|
|
3041
|
-
|
|
3165
|
+
clack13.note(
|
|
3042
3166
|
`Open your coding agent (Claude Code, Codex, Cursor, etc.) and try:
|
|
3043
3167
|
|
|
3044
3168
|
${prompts.map((p3) => `\u2022 "${p3}"`).join("\n")}`,
|
|
3045
3169
|
"Start building"
|
|
3046
3170
|
);
|
|
3047
3171
|
}
|
|
3048
|
-
|
|
3172
|
+
clack13.outro("Done!");
|
|
3049
3173
|
}
|
|
3050
3174
|
} catch (err) {
|
|
3051
3175
|
if (!projectLinked && hasTemplate && projectDir !== originalCwd) {
|
|
@@ -3064,7 +3188,7 @@ ${prompts.map((p3) => `\u2022 "${p3}"`).join("\n")}`,
|
|
|
3064
3188
|
});
|
|
3065
3189
|
}
|
|
3066
3190
|
async function downloadTemplate(framework, projectConfig, projectName, json, _apiUrl) {
|
|
3067
|
-
const s = !json ?
|
|
3191
|
+
const s = !json ? clack13.spinner() : null;
|
|
3068
3192
|
s?.start("Downloading template...");
|
|
3069
3193
|
try {
|
|
3070
3194
|
const anonKey = await getAnonKey();
|
|
@@ -3095,13 +3219,13 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
|
|
|
3095
3219
|
} catch (err) {
|
|
3096
3220
|
s?.stop("Template download failed");
|
|
3097
3221
|
if (!json) {
|
|
3098
|
-
|
|
3099
|
-
|
|
3222
|
+
clack13.log.warn(`Failed to download template: ${err.message}`);
|
|
3223
|
+
clack13.log.info("You can manually set up the template later.");
|
|
3100
3224
|
}
|
|
3101
3225
|
}
|
|
3102
3226
|
}
|
|
3103
3227
|
async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
3104
|
-
const s = !json ?
|
|
3228
|
+
const s = !json ? clack13.spinner() : null;
|
|
3105
3229
|
s?.start(`Downloading ${templateName} template...`);
|
|
3106
3230
|
const tempDir = path4.join(tmpdir2(), `insforge-template-${Date.now()}`);
|
|
3107
3231
|
try {
|
|
@@ -3150,7 +3274,7 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
3150
3274
|
await fs4.writeFile(envLocalPath, envFinal, { flag: "wx" });
|
|
3151
3275
|
} catch (e) {
|
|
3152
3276
|
if (e.code === "EEXIST") {
|
|
3153
|
-
if (!json)
|
|
3277
|
+
if (!json) clack13.log.warn(".env.local already exists; skipping env seeding.");
|
|
3154
3278
|
} else {
|
|
3155
3279
|
throw e;
|
|
3156
3280
|
}
|
|
@@ -3160,7 +3284,7 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
3160
3284
|
const migrationPath = path4.join(cwd, "migrations", "db_init.sql");
|
|
3161
3285
|
const migrationExists = await fs4.stat(migrationPath).catch(() => null);
|
|
3162
3286
|
if (migrationExists) {
|
|
3163
|
-
const dbSpinner = !json ?
|
|
3287
|
+
const dbSpinner = !json ? clack13.spinner() : null;
|
|
3164
3288
|
dbSpinner?.start("Running database migrations...");
|
|
3165
3289
|
try {
|
|
3166
3290
|
const sql = await fs4.readFile(migrationPath, "utf-8");
|
|
@@ -3169,8 +3293,8 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
3169
3293
|
} catch (err) {
|
|
3170
3294
|
dbSpinner?.stop("Database migration failed");
|
|
3171
3295
|
if (!json) {
|
|
3172
|
-
|
|
3173
|
-
|
|
3296
|
+
clack13.log.warn(`Migration failed: ${err.message}`);
|
|
3297
|
+
clack13.log.info('You can run the migration manually: npx @insforge/cli db query --unrestricted "$(cat migrations/db_init.sql)"');
|
|
3174
3298
|
} else {
|
|
3175
3299
|
throw err;
|
|
3176
3300
|
}
|
|
@@ -3183,8 +3307,8 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
3183
3307
|
if (json) {
|
|
3184
3308
|
console.error(JSON.stringify({ warning: msg }));
|
|
3185
3309
|
} else {
|
|
3186
|
-
|
|
3187
|
-
|
|
3310
|
+
clack13.log.warn(msg);
|
|
3311
|
+
clack13.log.info("You can manually clone from: https://github.com/InsForge/insforge-templates");
|
|
3188
3312
|
}
|
|
3189
3313
|
return false;
|
|
3190
3314
|
} finally {
|
|
@@ -3192,12 +3316,14 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
3192
3316
|
});
|
|
3193
3317
|
}
|
|
3194
3318
|
}
|
|
3195
|
-
|
|
3319
|
+
var MARKETPLACE_REPORT_URL = process.env.INSFORGE_MARKETPLACE_REPORT_URL ?? "https://p8n7m7ci.us-east.insforge.app/functions/report-download";
|
|
3320
|
+
async function reportMarketplaceDownload(slug) {
|
|
3196
3321
|
try {
|
|
3197
|
-
const res = await fetch(
|
|
3322
|
+
const res = await fetch(MARKETPLACE_REPORT_URL, {
|
|
3198
3323
|
method: "POST",
|
|
3199
3324
|
headers: { "Content-Type": "application/json" },
|
|
3200
|
-
body:
|
|
3325
|
+
body: JSON.stringify({ slug }),
|
|
3326
|
+
signal: AbortSignal.timeout(5e3)
|
|
3201
3327
|
});
|
|
3202
3328
|
if (!res.ok) {
|
|
3203
3329
|
return;
|
|
@@ -3209,15 +3335,15 @@ async function reportMarketplaceDownload(slug, apiUrl) {
|
|
|
3209
3335
|
// src/commands/projects/link.ts
|
|
3210
3336
|
var execAsync3 = promisify4(exec3);
|
|
3211
3337
|
async function runNpmInstall(startMessage = "Installing dependencies...") {
|
|
3212
|
-
const spinner11 =
|
|
3338
|
+
const spinner11 = clack14.spinner();
|
|
3213
3339
|
spinner11.start(startMessage);
|
|
3214
3340
|
try {
|
|
3215
3341
|
await execAsync3("npm install", { cwd: process.cwd(), maxBuffer: 10 * 1024 * 1024 });
|
|
3216
3342
|
spinner11.stop("Dependencies installed");
|
|
3217
3343
|
} catch (err) {
|
|
3218
3344
|
spinner11.stop("Failed to install dependencies");
|
|
3219
|
-
|
|
3220
|
-
|
|
3345
|
+
clack14.log.warn(`npm install failed: ${err.message}`);
|
|
3346
|
+
clack14.log.info("Run `npm install` manually to install dependencies.");
|
|
3221
3347
|
}
|
|
3222
3348
|
}
|
|
3223
3349
|
async function runNpmSetupIfPresent() {
|
|
@@ -3229,15 +3355,15 @@ async function runNpmSetupIfPresent() {
|
|
|
3229
3355
|
} catch {
|
|
3230
3356
|
}
|
|
3231
3357
|
if (!hasSetup) return;
|
|
3232
|
-
const spinner11 =
|
|
3358
|
+
const spinner11 = clack14.spinner();
|
|
3233
3359
|
spinner11.start("Running setup (schema + migrations)...");
|
|
3234
3360
|
try {
|
|
3235
3361
|
await execAsync3("npm run setup", { cwd: process.cwd(), maxBuffer: 20 * 1024 * 1024 });
|
|
3236
3362
|
spinner11.stop("Setup complete");
|
|
3237
3363
|
} catch (err) {
|
|
3238
3364
|
spinner11.stop("Setup failed");
|
|
3239
|
-
|
|
3240
|
-
|
|
3365
|
+
clack14.log.warn(`npm run setup failed: ${err.message.split("\n")[0]}`);
|
|
3366
|
+
clack14.log.info("Inspect the error, fix DATABASE_URL or network access, then run `npm run setup` manually.");
|
|
3241
3367
|
}
|
|
3242
3368
|
}
|
|
3243
3369
|
function registerProjectLinkCommand(program2) {
|
|
@@ -3260,7 +3386,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
3260
3386
|
if (json) {
|
|
3261
3387
|
outputJson({ success: true, skills_only: true });
|
|
3262
3388
|
} else {
|
|
3263
|
-
|
|
3389
|
+
clack14.note(
|
|
3264
3390
|
`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
3391
|
"What's next"
|
|
3266
3392
|
);
|
|
@@ -3338,11 +3464,11 @@ function registerProjectLinkCommand(program2) {
|
|
|
3338
3464
|
if (opts.auth) {
|
|
3339
3465
|
try {
|
|
3340
3466
|
const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig2, json);
|
|
3341
|
-
if (!json)
|
|
3467
|
+
if (!json) clack14.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
|
|
3342
3468
|
} catch (err) {
|
|
3343
3469
|
const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
|
|
3344
3470
|
if (json) console.error(JSON.stringify({ warning: msg }));
|
|
3345
|
-
else
|
|
3471
|
+
else clack14.log.warn(msg);
|
|
3346
3472
|
}
|
|
3347
3473
|
}
|
|
3348
3474
|
if (templateDownloaded && !json) {
|
|
@@ -3368,9 +3494,9 @@ function registerProjectLinkCommand(program2) {
|
|
|
3368
3494
|
`${pc2.bold("1.")} ${runCommand}`,
|
|
3369
3495
|
`${pc2.bold("2.")} Open ${pc2.cyan("Claude Code")} or ${pc2.cyan("Cursor")} and prompt your agent to add more features`
|
|
3370
3496
|
];
|
|
3371
|
-
|
|
3497
|
+
clack14.note(steps.join("\n"), "What's next");
|
|
3372
3498
|
} else {
|
|
3373
|
-
|
|
3499
|
+
clack14.log.warn("Template download failed. You can retry or set up manually.");
|
|
3374
3500
|
}
|
|
3375
3501
|
}
|
|
3376
3502
|
return;
|
|
@@ -3385,7 +3511,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
3385
3511
|
try {
|
|
3386
3512
|
const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig2, json);
|
|
3387
3513
|
if (!json) {
|
|
3388
|
-
|
|
3514
|
+
clack14.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
|
|
3389
3515
|
}
|
|
3390
3516
|
if (result.packageJsonPatched && !json) {
|
|
3391
3517
|
await runNpmInstall("Installing new dependencies...");
|
|
@@ -3394,7 +3520,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
3394
3520
|
} catch (err) {
|
|
3395
3521
|
const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
|
|
3396
3522
|
if (json) console.error(JSON.stringify({ warning: msg }));
|
|
3397
|
-
else
|
|
3523
|
+
else clack14.log.warn(msg);
|
|
3398
3524
|
}
|
|
3399
3525
|
}
|
|
3400
3526
|
trackCommand("link", "oss-org", { direct: true });
|
|
@@ -3424,7 +3550,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
3424
3550
|
}
|
|
3425
3551
|
if (orgs.length === 1) {
|
|
3426
3552
|
orgId = orgs[0].id;
|
|
3427
|
-
if (!json)
|
|
3553
|
+
if (!json) clack14.log.info(`Using organization: ${orgs[0].name}`);
|
|
3428
3554
|
} else {
|
|
3429
3555
|
if (json) {
|
|
3430
3556
|
throw new CLIError("Multiple organizations found. Specify --org-id.");
|
|
@@ -3535,11 +3661,11 @@ function registerProjectLinkCommand(program2) {
|
|
|
3535
3661
|
if (opts.auth) {
|
|
3536
3662
|
try {
|
|
3537
3663
|
const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig, json);
|
|
3538
|
-
if (!json)
|
|
3664
|
+
if (!json) clack14.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
|
|
3539
3665
|
} catch (err) {
|
|
3540
3666
|
const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
|
|
3541
3667
|
if (json) console.error(JSON.stringify({ warning: msg }));
|
|
3542
|
-
else
|
|
3668
|
+
else clack14.log.warn(msg);
|
|
3543
3669
|
}
|
|
3544
3670
|
}
|
|
3545
3671
|
if (templateDownloaded && !json) {
|
|
@@ -3552,16 +3678,16 @@ function registerProjectLinkCommand(program2) {
|
|
|
3552
3678
|
await reportCliUsage("cli.link", true, 6, projectConfig);
|
|
3553
3679
|
if (!json) {
|
|
3554
3680
|
const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
|
|
3555
|
-
|
|
3681
|
+
clack14.log.step(`Dashboard: ${pc2.underline(dashboardUrl)}`);
|
|
3556
3682
|
if (templateDownloaded) {
|
|
3557
3683
|
const runCommand = `${pc2.cyan("cd")} ${pc2.green(dirName)} ${pc2.dim("&&")} ${pc2.cyan("npm run dev")}`;
|
|
3558
3684
|
const steps = [
|
|
3559
3685
|
`${pc2.bold("1.")} ${runCommand}`,
|
|
3560
3686
|
`${pc2.bold("2.")} Open ${pc2.cyan("Claude Code")} or ${pc2.cyan("Cursor")} and prompt your agent to add more features`
|
|
3561
3687
|
];
|
|
3562
|
-
|
|
3688
|
+
clack14.note(steps.join("\n"), "What's next");
|
|
3563
3689
|
} else {
|
|
3564
|
-
|
|
3690
|
+
clack14.log.warn("Template download failed. You can retry or set up manually.");
|
|
3565
3691
|
}
|
|
3566
3692
|
}
|
|
3567
3693
|
} else {
|
|
@@ -3569,7 +3695,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
3569
3695
|
try {
|
|
3570
3696
|
const result = await applyAuthProvider(opts.auth, process.cwd(), projectConfig, json);
|
|
3571
3697
|
if (!json) {
|
|
3572
|
-
|
|
3698
|
+
clack14.log.success(`Wired in ${opts.auth}: ${result.written.length} new, ${result.overwritten.length} replaced`);
|
|
3573
3699
|
}
|
|
3574
3700
|
if (result.packageJsonPatched && !json) {
|
|
3575
3701
|
await runNpmInstall("Installing new dependencies...");
|
|
@@ -3578,20 +3704,20 @@ function registerProjectLinkCommand(program2) {
|
|
|
3578
3704
|
} catch (err) {
|
|
3579
3705
|
const msg = `Failed to apply --auth ${opts.auth}: ${err.message}`;
|
|
3580
3706
|
if (json) console.error(JSON.stringify({ warning: msg }));
|
|
3581
|
-
else
|
|
3707
|
+
else clack14.log.warn(msg);
|
|
3582
3708
|
}
|
|
3583
3709
|
}
|
|
3584
3710
|
await installSkills(json, opts.auth);
|
|
3585
3711
|
await reportCliUsage("cli.link", true, 6, projectConfig);
|
|
3586
3712
|
if (!json) {
|
|
3587
3713
|
const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
|
|
3588
|
-
|
|
3714
|
+
clack14.log.step(`Dashboard: ${dashboardUrl}`);
|
|
3589
3715
|
const prompts = [
|
|
3590
3716
|
"Build a todo app with Google OAuth sign-in",
|
|
3591
3717
|
"Build an Instagram clone where users can upload photos, like, and comment",
|
|
3592
3718
|
"Build an AI chatbot with conversation history and deploy it to a live URL"
|
|
3593
3719
|
];
|
|
3594
|
-
|
|
3720
|
+
clack14.note(
|
|
3595
3721
|
`Open your coding agent (Claude Code, Codex, Cursor, etc.) and try:
|
|
3596
3722
|
|
|
3597
3723
|
${prompts.map((p3) => `\u2022 "${p3}"`).join("\n")}`,
|
|
@@ -3831,7 +3957,7 @@ function registerDbRpcCommand(dbCmd2) {
|
|
|
3831
3957
|
}
|
|
3832
3958
|
|
|
3833
3959
|
// src/commands/db/export.ts
|
|
3834
|
-
import { writeFileSync as
|
|
3960
|
+
import { writeFileSync as writeFileSync4 } from "fs";
|
|
3835
3961
|
function registerDbExportCommand(dbCmd2) {
|
|
3836
3962
|
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
3963
|
const { json } = getRootOpts(cmd);
|
|
@@ -3871,7 +3997,7 @@ function registerDbExportCommand(dbCmd2) {
|
|
|
3871
3997
|
return;
|
|
3872
3998
|
}
|
|
3873
3999
|
if (opts.output) {
|
|
3874
|
-
|
|
4000
|
+
writeFileSync4(opts.output, content);
|
|
3875
4001
|
const tableCount = meta?.tables?.length;
|
|
3876
4002
|
const suffix = tableCount ? ` (${tableCount} tables, format: ${meta?.format ?? opts.format})` : "";
|
|
3877
4003
|
outputSuccess(`Exported to ${opts.output}${suffix}`);
|
|
@@ -3885,7 +4011,7 @@ function registerDbExportCommand(dbCmd2) {
|
|
|
3885
4011
|
}
|
|
3886
4012
|
|
|
3887
4013
|
// src/commands/db/import.ts
|
|
3888
|
-
import { readFileSync as
|
|
4014
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
3889
4015
|
import { basename as basename5 } from "path";
|
|
3890
4016
|
function registerDbImportCommand(dbCmd2) {
|
|
3891
4017
|
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 +4020,7 @@ function registerDbImportCommand(dbCmd2) {
|
|
|
3894
4020
|
await requireAuth();
|
|
3895
4021
|
const config = getProjectConfig();
|
|
3896
4022
|
if (!config) throw new ProjectNotLinkedError();
|
|
3897
|
-
const fileContent =
|
|
4023
|
+
const fileContent = readFileSync4(file);
|
|
3898
4024
|
const fileName = basename5(file);
|
|
3899
4025
|
const formData = new FormData();
|
|
3900
4026
|
formData.append("file", new Blob([fileContent]), fileName);
|
|
@@ -3925,12 +4051,12 @@ function registerDbImportCommand(dbCmd2) {
|
|
|
3925
4051
|
}
|
|
3926
4052
|
|
|
3927
4053
|
// src/commands/db/migrations.ts
|
|
3928
|
-
import { existsSync as
|
|
3929
|
-
import { join as
|
|
4054
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
4055
|
+
import { join as join10 } from "path";
|
|
3930
4056
|
|
|
3931
4057
|
// src/lib/migrations.ts
|
|
3932
|
-
import { existsSync as
|
|
3933
|
-
import { join as
|
|
4058
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync } from "fs";
|
|
4059
|
+
import { join as join9 } from "path";
|
|
3934
4060
|
var MIGRATION_VERSION_REGEX = /^\d{1,64}$/u;
|
|
3935
4061
|
var MIGRATION_FILENAME_REGEX = /^(\d{1,64})_([a-z0-9-]+)\.sql$/u;
|
|
3936
4062
|
function assertValidMigrationVersion(version) {
|
|
@@ -3994,18 +4120,18 @@ function incrementMigrationVersion(version) {
|
|
|
3994
4120
|
return formatMigrationVersion(new Date(nextTimestamp));
|
|
3995
4121
|
}
|
|
3996
4122
|
function getMigrationsDir(cwd = process.cwd()) {
|
|
3997
|
-
return
|
|
4123
|
+
return join9(cwd, "migrations");
|
|
3998
4124
|
}
|
|
3999
4125
|
function ensureMigrationsDir(cwd = process.cwd()) {
|
|
4000
4126
|
const migrationsDir = getMigrationsDir(cwd);
|
|
4001
|
-
if (!
|
|
4127
|
+
if (!existsSync5(migrationsDir)) {
|
|
4002
4128
|
mkdirSync2(migrationsDir, { recursive: true });
|
|
4003
4129
|
}
|
|
4004
4130
|
return migrationsDir;
|
|
4005
4131
|
}
|
|
4006
4132
|
function listLocalMigrationFilenames(cwd = process.cwd()) {
|
|
4007
4133
|
const migrationsDir = getMigrationsDir(cwd);
|
|
4008
|
-
if (!
|
|
4134
|
+
if (!existsSync5(migrationsDir)) {
|
|
4009
4135
|
return [];
|
|
4010
4136
|
}
|
|
4011
4137
|
return readdirSync(migrationsDir).sort((left, right) => left.localeCompare(right));
|
|
@@ -4193,12 +4319,12 @@ function registerDbMigrationsCommand(dbCmd2) {
|
|
|
4193
4319
|
migration.version,
|
|
4194
4320
|
migration.name
|
|
4195
4321
|
);
|
|
4196
|
-
const filePath =
|
|
4197
|
-
if (existingLocalVersions.has(migration.version) ||
|
|
4322
|
+
const filePath = join10(migrationsDir, filename);
|
|
4323
|
+
if (existingLocalVersions.has(migration.version) || existsSync6(filePath)) {
|
|
4198
4324
|
skippedFiles.push(filename);
|
|
4199
4325
|
continue;
|
|
4200
4326
|
}
|
|
4201
|
-
|
|
4327
|
+
writeFileSync5(filePath, formatMigrationSql(migration.statements));
|
|
4202
4328
|
createdFiles.push(filename);
|
|
4203
4329
|
existingLocalVersions.add(migration.version);
|
|
4204
4330
|
}
|
|
@@ -4236,9 +4362,9 @@ function registerDbMigrationsCommand(dbCmd2) {
|
|
|
4236
4362
|
);
|
|
4237
4363
|
const filename = buildMigrationFilename(nextVersion, migrationName);
|
|
4238
4364
|
const migrationsDir = ensureMigrationsDir();
|
|
4239
|
-
const filePath =
|
|
4365
|
+
const filePath = join10(migrationsDir, filename);
|
|
4240
4366
|
try {
|
|
4241
|
-
|
|
4367
|
+
writeFileSync5(filePath, "", { flag: "wx" });
|
|
4242
4368
|
} catch (error) {
|
|
4243
4369
|
if (error.code === "EEXIST") {
|
|
4244
4370
|
throw new CLIError(`Migration file already exists: ${filename}`);
|
|
@@ -4294,11 +4420,11 @@ function registerDbMigrationsCommand(dbCmd2) {
|
|
|
4294
4420
|
`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
4421
|
);
|
|
4296
4422
|
}
|
|
4297
|
-
const filePath =
|
|
4298
|
-
if (!
|
|
4423
|
+
const filePath = join10(getMigrationsDir(), targetMigration.filename);
|
|
4424
|
+
if (!existsSync6(filePath)) {
|
|
4299
4425
|
throw new CLIError(`Local migration file not found: ${targetMigration.filename}`);
|
|
4300
4426
|
}
|
|
4301
|
-
const sql =
|
|
4427
|
+
const sql = readFileSync5(filePath, "utf-8");
|
|
4302
4428
|
if (!sql.trim()) {
|
|
4303
4429
|
throw new CLIError(`Migration file is empty: ${targetMigration.filename}`);
|
|
4304
4430
|
}
|
|
@@ -4360,11 +4486,11 @@ function registerDbMigrationsCommand(dbCmd2) {
|
|
|
4360
4486
|
}
|
|
4361
4487
|
}
|
|
4362
4488
|
for (const migration of migrationsToApply) {
|
|
4363
|
-
const filePath =
|
|
4364
|
-
if (!
|
|
4489
|
+
const filePath = join10(getMigrationsDir(), migration.filename);
|
|
4490
|
+
if (!existsSync6(filePath)) {
|
|
4365
4491
|
throw new CLIError(`Local migration file not found: ${migration.filename}`);
|
|
4366
4492
|
}
|
|
4367
|
-
const sql =
|
|
4493
|
+
const sql = readFileSync5(filePath, "utf-8");
|
|
4368
4494
|
if (!sql.trim()) {
|
|
4369
4495
|
throw new CLIError(`Migration file is empty: ${migration.filename}`);
|
|
4370
4496
|
}
|
|
@@ -4593,12 +4719,12 @@ function registerFunctionsCommands(functionsCmd2) {
|
|
|
4593
4719
|
}
|
|
4594
4720
|
|
|
4595
4721
|
// src/commands/functions/deploy.ts
|
|
4596
|
-
import { readFileSync as
|
|
4722
|
+
import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
|
|
4597
4723
|
function resolveDeployFilePath(opts) {
|
|
4598
4724
|
if (!opts.file) {
|
|
4599
4725
|
throw new CLIError("Missing required option: --file <path>");
|
|
4600
4726
|
}
|
|
4601
|
-
if (!
|
|
4727
|
+
if (!existsSync7(opts.file)) {
|
|
4602
4728
|
throw new CLIError(`Source file not found: ${opts.file}`);
|
|
4603
4729
|
}
|
|
4604
4730
|
return opts.file;
|
|
@@ -4609,7 +4735,7 @@ function registerFunctionsDeployCommand(functionsCmd2) {
|
|
|
4609
4735
|
try {
|
|
4610
4736
|
await requireAuth();
|
|
4611
4737
|
const filePath = resolveDeployFilePath(opts);
|
|
4612
|
-
const code =
|
|
4738
|
+
const code = readFileSync6(filePath, "utf-8");
|
|
4613
4739
|
const name = opts.name ?? slug;
|
|
4614
4740
|
const description = opts.description ?? "";
|
|
4615
4741
|
let exists = false;
|
|
@@ -4730,7 +4856,7 @@ function registerFunctionsCodeCommand(functionsCmd2) {
|
|
|
4730
4856
|
}
|
|
4731
4857
|
|
|
4732
4858
|
// src/commands/functions/delete.ts
|
|
4733
|
-
import * as
|
|
4859
|
+
import * as clack15 from "@clack/prompts";
|
|
4734
4860
|
function registerFunctionsDeleteCommand(functionsCmd2) {
|
|
4735
4861
|
functionsCmd2.command("delete <slug>").description("Delete an edge function").action(async (slug, _opts, cmd) => {
|
|
4736
4862
|
const { json, yes } = getRootOpts(cmd);
|
|
@@ -4741,7 +4867,7 @@ function registerFunctionsDeleteCommand(functionsCmd2) {
|
|
|
4741
4867
|
message: `Delete function "${slug}"? This cannot be undone.`
|
|
4742
4868
|
});
|
|
4743
4869
|
if (isCancel2(confirmed) || !confirmed) {
|
|
4744
|
-
|
|
4870
|
+
clack15.log.info("Cancelled.");
|
|
4745
4871
|
return;
|
|
4746
4872
|
}
|
|
4747
4873
|
}
|
|
@@ -4796,7 +4922,7 @@ function registerStorageBucketsCommand(storageCmd2) {
|
|
|
4796
4922
|
}
|
|
4797
4923
|
|
|
4798
4924
|
// src/commands/storage/upload.ts
|
|
4799
|
-
import { readFileSync as
|
|
4925
|
+
import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
|
|
4800
4926
|
import { basename as basename6 } from "path";
|
|
4801
4927
|
function registerStorageUploadCommand(storageCmd2) {
|
|
4802
4928
|
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 +4931,10 @@ function registerStorageUploadCommand(storageCmd2) {
|
|
|
4805
4931
|
await requireAuth();
|
|
4806
4932
|
const config = getProjectConfig();
|
|
4807
4933
|
if (!config) throw new ProjectNotLinkedError();
|
|
4808
|
-
if (!
|
|
4934
|
+
if (!existsSync8(file)) {
|
|
4809
4935
|
throw new CLIError(`File not found: ${file}`);
|
|
4810
4936
|
}
|
|
4811
|
-
const fileContent =
|
|
4937
|
+
const fileContent = readFileSync7(file);
|
|
4812
4938
|
const objectKey = opts.key ?? basename6(file);
|
|
4813
4939
|
const bucketName = opts.bucket;
|
|
4814
4940
|
const formData = new FormData();
|
|
@@ -4839,8 +4965,8 @@ function registerStorageUploadCommand(storageCmd2) {
|
|
|
4839
4965
|
}
|
|
4840
4966
|
|
|
4841
4967
|
// src/commands/storage/download.ts
|
|
4842
|
-
import { writeFileSync as
|
|
4843
|
-
import { join as
|
|
4968
|
+
import { writeFileSync as writeFileSync6 } from "fs";
|
|
4969
|
+
import { join as join11, basename as basename7 } from "path";
|
|
4844
4970
|
function registerStorageDownloadCommand(storageCmd2) {
|
|
4845
4971
|
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
4972
|
const { json } = getRootOpts(cmd);
|
|
@@ -4860,8 +4986,8 @@ function registerStorageDownloadCommand(storageCmd2) {
|
|
|
4860
4986
|
throw new CLIError(err.error ?? `Download failed: ${res.status}`);
|
|
4861
4987
|
}
|
|
4862
4988
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
4863
|
-
const outputPath = opts.output ??
|
|
4864
|
-
|
|
4989
|
+
const outputPath = opts.output ?? join11(process.cwd(), basename7(objectKey));
|
|
4990
|
+
writeFileSync6(outputPath, buffer);
|
|
4865
4991
|
if (json) {
|
|
4866
4992
|
outputJson({ success: true, path: outputPath, size: buffer.length });
|
|
4867
4993
|
} else {
|
|
@@ -5102,11 +5228,9 @@ function registerDeploymentsListCommand(deploymentsCmd2) {
|
|
|
5102
5228
|
const deployments = Array.isArray(raw) ? raw : raw && typeof raw === "object" && "data" in raw ? raw.data ?? [] : [];
|
|
5103
5229
|
if (json) {
|
|
5104
5230
|
outputJson(raw);
|
|
5231
|
+
} else if (!deployments.length) {
|
|
5232
|
+
console.log("No deployments found.");
|
|
5105
5233
|
} else {
|
|
5106
|
-
if (!deployments.length) {
|
|
5107
|
-
console.log("No deployments found.");
|
|
5108
|
-
return;
|
|
5109
|
-
}
|
|
5110
5234
|
outputTable(
|
|
5111
5235
|
["ID", "Status", "Provider", "URL", "Created"],
|
|
5112
5236
|
deployments.map((d) => [
|
|
@@ -5118,9 +5242,9 @@ function registerDeploymentsListCommand(deploymentsCmd2) {
|
|
|
5118
5242
|
])
|
|
5119
5243
|
);
|
|
5120
5244
|
}
|
|
5121
|
-
await
|
|
5245
|
+
await trackDeploymentUsage("list", true);
|
|
5122
5246
|
} catch (err) {
|
|
5123
|
-
await
|
|
5247
|
+
await trackDeploymentUsage("list", false);
|
|
5124
5248
|
handleError(err, json);
|
|
5125
5249
|
}
|
|
5126
5250
|
});
|
|
@@ -5156,7 +5280,9 @@ function registerDeploymentsStatusCommand(deploymentsCmd2) {
|
|
|
5156
5280
|
]
|
|
5157
5281
|
);
|
|
5158
5282
|
}
|
|
5283
|
+
await trackDeploymentUsage("status", true, { sync: Boolean(opts.sync) });
|
|
5159
5284
|
} catch (err) {
|
|
5285
|
+
await trackDeploymentUsage("status", false, { sync: Boolean(opts.sync) });
|
|
5160
5286
|
handleError(err, json);
|
|
5161
5287
|
}
|
|
5162
5288
|
});
|
|
@@ -5182,7 +5308,9 @@ function registerDeploymentsCancelCommand(deploymentsCmd2) {
|
|
|
5182
5308
|
} else {
|
|
5183
5309
|
outputSuccess(`Deployment ${id} cancelled.`);
|
|
5184
5310
|
}
|
|
5311
|
+
await trackDeploymentUsage("cancel", true);
|
|
5185
5312
|
} catch (err) {
|
|
5313
|
+
await trackDeploymentUsage("cancel", false);
|
|
5186
5314
|
handleError(err, json);
|
|
5187
5315
|
}
|
|
5188
5316
|
});
|
|
@@ -5201,11 +5329,9 @@ function registerDeploymentsEnvVarsCommand(deploymentsCmd2) {
|
|
|
5201
5329
|
const envVars = data.envVars ?? [];
|
|
5202
5330
|
if (json) {
|
|
5203
5331
|
outputJson(data);
|
|
5332
|
+
} else if (!envVars.length) {
|
|
5333
|
+
console.log("No environment variables found.");
|
|
5204
5334
|
} else {
|
|
5205
|
-
if (!envVars.length) {
|
|
5206
|
-
console.log("No environment variables found.");
|
|
5207
|
-
return;
|
|
5208
|
-
}
|
|
5209
5335
|
outputTable(
|
|
5210
5336
|
["ID", "Key", "Type", "Updated At"],
|
|
5211
5337
|
envVars.map((v) => [
|
|
@@ -5216,9 +5342,9 @@ function registerDeploymentsEnvVarsCommand(deploymentsCmd2) {
|
|
|
5216
5342
|
])
|
|
5217
5343
|
);
|
|
5218
5344
|
}
|
|
5219
|
-
await
|
|
5345
|
+
await trackDeploymentUsage("env.list", true);
|
|
5220
5346
|
} catch (err) {
|
|
5221
|
-
await
|
|
5347
|
+
await trackDeploymentUsage("env.list", false);
|
|
5222
5348
|
handleError(err, json);
|
|
5223
5349
|
}
|
|
5224
5350
|
});
|
|
@@ -5237,9 +5363,9 @@ function registerDeploymentsEnvVarsCommand(deploymentsCmd2) {
|
|
|
5237
5363
|
} else {
|
|
5238
5364
|
outputSuccess(data.message);
|
|
5239
5365
|
}
|
|
5240
|
-
await
|
|
5366
|
+
await trackDeploymentUsage("env.set", true);
|
|
5241
5367
|
} catch (err) {
|
|
5242
|
-
await
|
|
5368
|
+
await trackDeploymentUsage("env.set", false);
|
|
5243
5369
|
handleError(err, json);
|
|
5244
5370
|
}
|
|
5245
5371
|
});
|
|
@@ -5257,9 +5383,9 @@ function registerDeploymentsEnvVarsCommand(deploymentsCmd2) {
|
|
|
5257
5383
|
} else {
|
|
5258
5384
|
outputSuccess(data.message);
|
|
5259
5385
|
}
|
|
5260
|
-
await
|
|
5386
|
+
await trackDeploymentUsage("env.delete", true);
|
|
5261
5387
|
} catch (err) {
|
|
5262
|
-
await
|
|
5388
|
+
await trackDeploymentUsage("env.delete", false);
|
|
5263
5389
|
handleError(err, json);
|
|
5264
5390
|
}
|
|
5265
5391
|
});
|
|
@@ -5729,14 +5855,17 @@ function registerComputeListCommand(computeCmd2) {
|
|
|
5729
5855
|
}
|
|
5730
5856
|
outputTable(
|
|
5731
5857
|
["Name", "Status", "Image", "CPU", "Memory", "Endpoint"],
|
|
5732
|
-
services.map((s) =>
|
|
5733
|
-
String(s.
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5858
|
+
services.map((s) => {
|
|
5859
|
+
const endpoint = s.protocol === "tcp" && s.endpointUrl && s.port ? `${String(s.endpointUrl).replace(/^https?:\/\//, "")}:${s.port}` : String(s.endpointUrl ?? "-");
|
|
5860
|
+
return [
|
|
5861
|
+
String(s.name ?? "-"),
|
|
5862
|
+
String(s.status ?? "-"),
|
|
5863
|
+
String(s.imageUrl ?? "-"),
|
|
5864
|
+
String(s.cpu ?? "-"),
|
|
5865
|
+
s.memory ? `${s.memory}MB` : "-",
|
|
5866
|
+
endpoint
|
|
5867
|
+
];
|
|
5868
|
+
})
|
|
5740
5869
|
);
|
|
5741
5870
|
}
|
|
5742
5871
|
await reportCliUsage("cli.compute.list", true);
|
|
@@ -5758,14 +5887,16 @@ function registerComputeGetCommand(computeCmd2) {
|
|
|
5758
5887
|
if (json) {
|
|
5759
5888
|
outputJson(service);
|
|
5760
5889
|
} else {
|
|
5890
|
+
const endpoint = service.protocol === "tcp" && service.endpointUrl && service.port ? `${String(service.endpointUrl).replace(/^https?:\/\//, "")}:${service.port}` : service.endpointUrl ?? "n/a";
|
|
5761
5891
|
outputInfo(`Name: ${service.name}`);
|
|
5762
5892
|
outputInfo(`ID: ${service.id}`);
|
|
5763
5893
|
outputInfo(`Status: ${service.status}`);
|
|
5764
5894
|
outputInfo(`Image: ${service.imageUrl}`);
|
|
5895
|
+
outputInfo(`Protocol: ${service.protocol ?? "http"}`);
|
|
5765
5896
|
outputInfo(`CPU: ${service.cpu}`);
|
|
5766
5897
|
outputInfo(`Memory: ${service.memory}MB`);
|
|
5767
5898
|
outputInfo(`Region: ${service.region}`);
|
|
5768
|
-
outputInfo(`Endpoint: ${
|
|
5899
|
+
outputInfo(`Endpoint: ${endpoint}`);
|
|
5769
5900
|
outputInfo(`Created: ${service.createdAt}`);
|
|
5770
5901
|
}
|
|
5771
5902
|
await reportCliUsage("cli.compute.get", true);
|
|
@@ -5991,16 +6122,16 @@ function registerComputeEventsCommand(computeCmd2) {
|
|
|
5991
6122
|
}
|
|
5992
6123
|
|
|
5993
6124
|
// src/commands/compute/deploy.ts
|
|
5994
|
-
import { existsSync as
|
|
5995
|
-
import { join as
|
|
6125
|
+
import { existsSync as existsSync10 } from "fs";
|
|
6126
|
+
import { join as join13, resolve as resolve4 } from "path";
|
|
5996
6127
|
|
|
5997
6128
|
// src/lib/env-file.ts
|
|
5998
|
-
import { readFileSync as
|
|
6129
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
5999
6130
|
var ENV_KEY_REGEX2 = /^[A-Z_][A-Z0-9_]*$/;
|
|
6000
6131
|
function parseEnvFile(path6) {
|
|
6001
6132
|
let raw;
|
|
6002
6133
|
try {
|
|
6003
|
-
raw =
|
|
6134
|
+
raw = readFileSync8(path6, "utf-8");
|
|
6004
6135
|
} catch (err) {
|
|
6005
6136
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6006
6137
|
throw new CLIError(`Could not read --env-file at ${path6}: ${msg}`);
|
|
@@ -6036,8 +6167,8 @@ function parseEnvFile(path6) {
|
|
|
6036
6167
|
|
|
6037
6168
|
// src/lib/flyctl.ts
|
|
6038
6169
|
import { spawn, spawnSync } from "child_process";
|
|
6039
|
-
import { existsSync as
|
|
6040
|
-
import { join as
|
|
6170
|
+
import { existsSync as existsSync9, writeFileSync as writeFileSync7, unlinkSync as unlinkSync3 } from "fs";
|
|
6171
|
+
import { join as join12 } from "path";
|
|
6041
6172
|
function ensureFlyctlAvailable() {
|
|
6042
6173
|
const r = spawnSync("flyctl", ["version"], {
|
|
6043
6174
|
encoding: "utf8",
|
|
@@ -6050,25 +6181,35 @@ function ensureFlyctlAvailable() {
|
|
|
6050
6181
|
}
|
|
6051
6182
|
}
|
|
6052
6183
|
function ensureFlyTomlStub(opts) {
|
|
6053
|
-
const path6 =
|
|
6054
|
-
if (
|
|
6184
|
+
const path6 = join12(opts.dir, "fly.toml");
|
|
6185
|
+
if (existsSync9(path6)) {
|
|
6055
6186
|
return () => {
|
|
6056
6187
|
};
|
|
6057
6188
|
}
|
|
6058
|
-
const
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6189
|
+
const serviceBlock = opts.protocol === "tcp" ? `[[services]]
|
|
6190
|
+
internal_port = ${opts.port}
|
|
6191
|
+
protocol = "tcp"
|
|
6192
|
+
auto_stop_machines = false
|
|
6193
|
+
auto_start_machines = true
|
|
6194
|
+
min_machines_running = 0
|
|
6063
6195
|
|
|
6064
|
-
[
|
|
6196
|
+
[[services.ports]]
|
|
6197
|
+
port = ${opts.port}
|
|
6198
|
+
` : `[http_service]
|
|
6065
6199
|
internal_port = ${opts.port}
|
|
6066
6200
|
force_https = true
|
|
6067
6201
|
auto_stop_machines = false
|
|
6068
6202
|
auto_start_machines = true
|
|
6069
6203
|
min_machines_running = 0
|
|
6070
6204
|
`;
|
|
6071
|
-
|
|
6205
|
+
const stub = `# Auto-generated by @insforge/cli for compute deploy. Safe to delete.
|
|
6206
|
+
app = "${opts.appId}"
|
|
6207
|
+
primary_region = "${opts.region}"
|
|
6208
|
+
|
|
6209
|
+
[build]
|
|
6210
|
+
|
|
6211
|
+
` + serviceBlock;
|
|
6212
|
+
writeFileSync7(path6, stub, "utf8");
|
|
6072
6213
|
return () => {
|
|
6073
6214
|
try {
|
|
6074
6215
|
unlinkSync3(path6);
|
|
@@ -6082,7 +6223,8 @@ function flyctlBuildAndPush(opts) {
|
|
|
6082
6223
|
dir: opts.dir,
|
|
6083
6224
|
appId: opts.appId,
|
|
6084
6225
|
region: opts.region,
|
|
6085
|
-
port: opts.port
|
|
6226
|
+
port: opts.port,
|
|
6227
|
+
protocol: opts.protocol
|
|
6086
6228
|
});
|
|
6087
6229
|
const child = spawn(
|
|
6088
6230
|
"flyctl",
|
|
@@ -6149,6 +6291,10 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
6149
6291
|
).option("--memory <mb>", "Memory in MB", "512").option("--region <region>", "Fly.io region", "iad").option("--env <json>", "Env vars as JSON object").option(
|
|
6150
6292
|
"--env-file <path>",
|
|
6151
6293
|
"Path to a .env file (KEY=VALUE per line, #-comments + blank lines ok). Mutually exclusive with --env."
|
|
6294
|
+
).option(
|
|
6295
|
+
"--protocol <protocol>",
|
|
6296
|
+
'Edge protocol: "http" (default) or "tcp" (raw pass-through for Redis, etc.)',
|
|
6297
|
+
"http"
|
|
6152
6298
|
).action(async (dir, opts, cmd) => {
|
|
6153
6299
|
const { json } = getRootOpts(cmd);
|
|
6154
6300
|
try {
|
|
@@ -6165,6 +6311,9 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
6165
6311
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
6166
6312
|
throw new CLIError(`Invalid --port: ${opts.port}`);
|
|
6167
6313
|
}
|
|
6314
|
+
if (opts.protocol !== "http" && opts.protocol !== "tcp") {
|
|
6315
|
+
throw new CLIError(`Invalid --protocol: ${opts.protocol} (expected "http" or "tcp")`);
|
|
6316
|
+
}
|
|
6168
6317
|
const memory = Number(opts.memory);
|
|
6169
6318
|
if (!Number.isInteger(memory) || memory <= 0) {
|
|
6170
6319
|
throw new CLIError(`Invalid --memory: ${opts.memory}`);
|
|
@@ -6204,6 +6353,7 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
6204
6353
|
region: opts.region
|
|
6205
6354
|
};
|
|
6206
6355
|
if (envVars) baseBody.envVars = envVars;
|
|
6356
|
+
if (opts.protocol === "tcp") baseBody.protocol = "tcp";
|
|
6207
6357
|
if (!dir) {
|
|
6208
6358
|
const body = { ...baseBody, imageUrl: opts.image };
|
|
6209
6359
|
const listRes2 = await ossFetch("/api/compute/services");
|
|
@@ -6215,6 +6365,7 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
6215
6365
|
if (!json) outputInfo(`Found existing service "${opts.name}", updating...`);
|
|
6216
6366
|
const updateBody2 = { ...body };
|
|
6217
6367
|
delete updateBody2.name;
|
|
6368
|
+
if (opts.protocol === "tcp") updateBody2.protocol = "tcp";
|
|
6218
6369
|
res = await ossFetch(`/api/compute/services/${encodeURIComponent(existing2.id)}`, {
|
|
6219
6370
|
method: "PATCH",
|
|
6220
6371
|
body: JSON.stringify(updateBody2)
|
|
@@ -6231,15 +6382,22 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
6231
6382
|
} else {
|
|
6232
6383
|
const verb = existing2 ? "updated" : "deployed";
|
|
6233
6384
|
outputSuccess(`Service "${service2.name}" ${verb} [${service2.status}]`);
|
|
6234
|
-
if (service2.endpointUrl
|
|
6385
|
+
if (service2.endpointUrl && opts.protocol === "tcp") {
|
|
6386
|
+
const host = String(service2.endpointUrl).replace(/^https?:\/\//, "");
|
|
6387
|
+
console.log(` Endpoint: ${host}:${service2.port} (connect with <scheme>://${host}:${service2.port})`);
|
|
6388
|
+
console.log(` Note: TCP services are reachable from the public internet.`);
|
|
6389
|
+
console.log(` Configure auth on your container (e.g. redis --requirepass <secret>).`);
|
|
6390
|
+
} else if (service2.endpointUrl) {
|
|
6391
|
+
console.log(` Endpoint: ${service2.endpointUrl}`);
|
|
6392
|
+
}
|
|
6235
6393
|
if (service2.port !== void 0) console.log(` Port: ${service2.port} (container must listen on this port)`);
|
|
6236
6394
|
}
|
|
6237
6395
|
await reportCliUsage("cli.compute.deploy", true);
|
|
6238
6396
|
return;
|
|
6239
6397
|
}
|
|
6240
6398
|
const absDir = resolve4(dir);
|
|
6241
|
-
const dockerfilePath =
|
|
6242
|
-
if (!
|
|
6399
|
+
const dockerfilePath = join13(absDir, "Dockerfile");
|
|
6400
|
+
if (!existsSync10(dockerfilePath)) {
|
|
6243
6401
|
throw new CLIError(
|
|
6244
6402
|
`No Dockerfile at ${dockerfilePath}.
|
|
6245
6403
|
Either:
|
|
@@ -6289,7 +6447,8 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
6289
6447
|
imageLabel,
|
|
6290
6448
|
token: tokenJson.token,
|
|
6291
6449
|
region: opts.region,
|
|
6292
|
-
port
|
|
6450
|
+
port,
|
|
6451
|
+
protocol: opts.protocol === "tcp" ? "tcp" : "http"
|
|
6293
6452
|
}));
|
|
6294
6453
|
} catch (buildErr) {
|
|
6295
6454
|
if (!existing) {
|
|
@@ -6317,6 +6476,7 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
6317
6476
|
region: opts.region
|
|
6318
6477
|
};
|
|
6319
6478
|
if (envVars) updateBody.envVars = envVars;
|
|
6479
|
+
if (opts.protocol === "tcp") updateBody.protocol = "tcp";
|
|
6320
6480
|
const finalRes = await ossFetch(
|
|
6321
6481
|
`/api/compute/services/${encodeURIComponent(serviceId)}`,
|
|
6322
6482
|
{ method: "PATCH", body: JSON.stringify(updateBody) }
|
|
@@ -6327,7 +6487,14 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
6327
6487
|
} else {
|
|
6328
6488
|
const verb = existing ? "updated" : "deployed";
|
|
6329
6489
|
outputSuccess(`Service "${service.name}" ${verb} [${service.status}]`);
|
|
6330
|
-
if (service.endpointUrl
|
|
6490
|
+
if (service.endpointUrl && opts.protocol === "tcp") {
|
|
6491
|
+
const host = String(service.endpointUrl).replace(/^https?:\/\//, "");
|
|
6492
|
+
console.log(` Endpoint: ${host}:${service.port} (connect with <scheme>://${host}:${service.port})`);
|
|
6493
|
+
console.log(` Note: TCP services are reachable from the public internet.`);
|
|
6494
|
+
console.log(` Configure auth on your container (e.g. redis --requirepass <secret>).`);
|
|
6495
|
+
} else if (service.endpointUrl) {
|
|
6496
|
+
console.log(` Endpoint: ${service.endpointUrl}`);
|
|
6497
|
+
}
|
|
6331
6498
|
if (service.port !== void 0) console.log(` Port: ${service.port} (container must listen on this port)`);
|
|
6332
6499
|
console.log(` Image: ${imageRef} (built remotely; no local image to clean up)`);
|
|
6333
6500
|
}
|
|
@@ -6474,7 +6641,7 @@ function formatSize2(gb) {
|
|
|
6474
6641
|
|
|
6475
6642
|
// src/commands/diagnose/index.ts
|
|
6476
6643
|
import * as os from "os";
|
|
6477
|
-
import * as
|
|
6644
|
+
import * as clack16 from "@clack/prompts";
|
|
6478
6645
|
|
|
6479
6646
|
// src/commands/diagnose/metrics.ts
|
|
6480
6647
|
var METRIC_LABELS = {
|
|
@@ -7015,10 +7182,10 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
7015
7182
|
if (question.length === 0 || question.length > 2e3) {
|
|
7016
7183
|
throw new CLIError("Question must be between 1 and 2000 characters.");
|
|
7017
7184
|
}
|
|
7018
|
-
const s = !json ?
|
|
7185
|
+
const s = !json ? clack16.spinner() : null;
|
|
7019
7186
|
s?.start("Collecting diagnostic data...");
|
|
7020
7187
|
const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
|
|
7021
|
-
const cliVersion = "0.1.
|
|
7188
|
+
const cliVersion = "0.1.85";
|
|
7022
7189
|
s?.stop("Data collected");
|
|
7023
7190
|
if (!json) {
|
|
7024
7191
|
console.log(`
|
|
@@ -7151,9 +7318,9 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
7151
7318
|
void 0,
|
|
7152
7319
|
apiUrl
|
|
7153
7320
|
);
|
|
7154
|
-
|
|
7321
|
+
clack16.log.success("Thanks for your feedback!");
|
|
7155
7322
|
} catch {
|
|
7156
|
-
|
|
7323
|
+
clack16.log.warn("Failed to submit rating.");
|
|
7157
7324
|
}
|
|
7158
7325
|
}
|
|
7159
7326
|
}
|
|
@@ -7499,16 +7666,14 @@ function formatRecurring(interval, intervalCount) {
|
|
|
7499
7666
|
}
|
|
7500
7667
|
async function trackPaymentUsage(subcommand, success, properties = {}) {
|
|
7501
7668
|
try {
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
});
|
|
7509
|
-
}
|
|
7510
|
-
} catch {
|
|
7669
|
+
const config = getProjectConfig();
|
|
7670
|
+
if (config) {
|
|
7671
|
+
trackPayments(subcommand, config, {
|
|
7672
|
+
success,
|
|
7673
|
+
...properties
|
|
7674
|
+
});
|
|
7511
7675
|
}
|
|
7676
|
+
} catch {
|
|
7512
7677
|
} finally {
|
|
7513
7678
|
await shutdownAnalytics();
|
|
7514
7679
|
}
|
|
@@ -8382,7 +8547,7 @@ function registerPaymentsCommands(paymentsCmd2) {
|
|
|
8382
8547
|
}
|
|
8383
8548
|
|
|
8384
8549
|
// src/commands/posthog/setup.ts
|
|
8385
|
-
import * as
|
|
8550
|
+
import * as clack17 from "@clack/prompts";
|
|
8386
8551
|
import pc3 from "picocolors";
|
|
8387
8552
|
|
|
8388
8553
|
// src/lib/api/posthog.ts
|
|
@@ -8567,6 +8732,8 @@ function registerPosthogSetupCommand(program2) {
|
|
|
8567
8732
|
}
|
|
8568
8733
|
} catch (err) {
|
|
8569
8734
|
handleError(err, json);
|
|
8735
|
+
} finally {
|
|
8736
|
+
await shutdownAnalytics();
|
|
8570
8737
|
}
|
|
8571
8738
|
});
|
|
8572
8739
|
}
|
|
@@ -8579,13 +8746,14 @@ async function runSetup(opts) {
|
|
|
8579
8746
|
if (!token) {
|
|
8580
8747
|
throw new AuthError("Not logged in. Run `insforge login` first.");
|
|
8581
8748
|
}
|
|
8749
|
+
trackPosthog("setup", proj);
|
|
8582
8750
|
if (!opts.json) {
|
|
8583
|
-
|
|
8751
|
+
clack17.intro("PostHog setup");
|
|
8584
8752
|
outputSuccess(`Linked to InsForge project: ${proj.project_name} (${proj.project_id})`);
|
|
8585
8753
|
}
|
|
8586
8754
|
const dashboardConnection = await ensureDashboardConnection(proj.project_id, token, opts);
|
|
8587
8755
|
if (!opts.json) {
|
|
8588
|
-
|
|
8756
|
+
clack17.note(
|
|
8589
8757
|
`Run this in your terminal to wire PostHog into your app code:
|
|
8590
8758
|
|
|
8591
8759
|
${WIZARD_COMMAND}
|
|
@@ -8623,13 +8791,13 @@ async function runConnectFlow(projectId, token, authorizeUrl, opts) {
|
|
|
8623
8791
|
`);
|
|
8624
8792
|
process.stderr.write("Your browser should open automatically. If not, copy the URL above.\n");
|
|
8625
8793
|
} else {
|
|
8626
|
-
|
|
8794
|
+
clack17.log.info("PostHog is not yet connected to your InsForge dashboard.");
|
|
8627
8795
|
if (opts.skipBrowser) {
|
|
8628
|
-
|
|
8796
|
+
clack17.log.info(`Open this URL to authorize PostHog:
|
|
8629
8797
|
${pc3.cyan(pc3.underline(authorizeUrl))}`);
|
|
8630
8798
|
} else {
|
|
8631
|
-
|
|
8632
|
-
|
|
8799
|
+
clack17.log.info("Opening browser to authorize PostHog...");
|
|
8800
|
+
clack17.log.info(`If browser doesn't open, visit:
|
|
8633
8801
|
${pc3.cyan(pc3.underline(authorizeUrl))}`);
|
|
8634
8802
|
}
|
|
8635
8803
|
}
|
|
@@ -8640,11 +8808,11 @@ ${pc3.cyan(pc3.underline(authorizeUrl))}`);
|
|
|
8640
8808
|
} catch {
|
|
8641
8809
|
}
|
|
8642
8810
|
}
|
|
8643
|
-
const spinner11 = !opts.json && isInteractive ?
|
|
8811
|
+
const spinner11 = !opts.json && isInteractive ? clack17.spinner() : null;
|
|
8644
8812
|
if (spinner11) {
|
|
8645
8813
|
spinner11.start("Waiting for InsForge dashboard connection... (timeout: 15 minutes)");
|
|
8646
8814
|
} else if (!opts.json) {
|
|
8647
|
-
|
|
8815
|
+
clack17.log.info("Waiting for InsForge dashboard connection (up to 15 minutes)...");
|
|
8648
8816
|
}
|
|
8649
8817
|
try {
|
|
8650
8818
|
await pollPosthogConnection(
|
|
@@ -8668,20 +8836,20 @@ ${pc3.cyan(pc3.underline(authorizeUrl))}`);
|
|
|
8668
8836
|
if (spinner11) {
|
|
8669
8837
|
spinner11.stop("InsForge dashboard connection received.");
|
|
8670
8838
|
} else if (!opts.json) {
|
|
8671
|
-
|
|
8839
|
+
clack17.log.success("InsForge dashboard connection received.");
|
|
8672
8840
|
}
|
|
8673
8841
|
} catch (err) {
|
|
8674
8842
|
if (spinner11) {
|
|
8675
8843
|
spinner11.stop("InsForge dashboard connection wait failed.");
|
|
8676
8844
|
} else if (!opts.json) {
|
|
8677
|
-
|
|
8845
|
+
clack17.log.error("InsForge dashboard connection wait failed.");
|
|
8678
8846
|
}
|
|
8679
8847
|
throw err;
|
|
8680
8848
|
}
|
|
8681
8849
|
}
|
|
8682
8850
|
|
|
8683
8851
|
// src/commands/config/export.ts
|
|
8684
|
-
import { writeFileSync as
|
|
8852
|
+
import { writeFileSync as writeFileSync8, existsSync as existsSync11 } from "fs";
|
|
8685
8853
|
import { resolve as resolve5 } from "path";
|
|
8686
8854
|
import * as p from "@clack/prompts";
|
|
8687
8855
|
import pc4 from "picocolors";
|
|
@@ -9167,7 +9335,7 @@ function registerConfigExportCommand(cfg) {
|
|
|
9167
9335
|
projectConfig = getProjectConfig();
|
|
9168
9336
|
await requireAuth();
|
|
9169
9337
|
const target = resolve5(process.cwd(), opts.out);
|
|
9170
|
-
if (
|
|
9338
|
+
if (existsSync11(target) && !opts.force) {
|
|
9171
9339
|
if (json) {
|
|
9172
9340
|
throw new CLIError(
|
|
9173
9341
|
`${opts.out} exists. Re-run with --force to overwrite.`,
|
|
@@ -9194,7 +9362,7 @@ function registerConfigExportCommand(cfg) {
|
|
|
9194
9362
|
const raw = await res.json();
|
|
9195
9363
|
const { config, skipped } = configFromMetadata(raw);
|
|
9196
9364
|
const toml = stringifyConfigToml(config);
|
|
9197
|
-
|
|
9365
|
+
writeFileSync8(target, toml, "utf8");
|
|
9198
9366
|
if (json) {
|
|
9199
9367
|
console.log(JSON.stringify({ written: target, config, skipped }, null, 2));
|
|
9200
9368
|
} else {
|
|
@@ -9230,7 +9398,7 @@ function registerConfigExportCommand(cfg) {
|
|
|
9230
9398
|
}
|
|
9231
9399
|
|
|
9232
9400
|
// src/commands/config/plan.ts
|
|
9233
|
-
import { readFileSync as
|
|
9401
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
9234
9402
|
import { resolve as resolve6 } from "path";
|
|
9235
9403
|
import pc5 from "picocolors";
|
|
9236
9404
|
|
|
@@ -9538,7 +9706,7 @@ function registerConfigPlanCommand(cfg) {
|
|
|
9538
9706
|
projectConfig = getProjectConfig();
|
|
9539
9707
|
await requireAuth();
|
|
9540
9708
|
const tomlPath = resolve6(process.cwd(), opts.file);
|
|
9541
|
-
const tomlSource =
|
|
9709
|
+
const tomlSource = readFileSync9(tomlPath, "utf8");
|
|
9542
9710
|
const file = parseConfigToml(tomlSource);
|
|
9543
9711
|
const res = await ossFetch("/api/metadata");
|
|
9544
9712
|
const raw = await res.json();
|
|
@@ -9582,7 +9750,7 @@ function registerConfigPlanCommand(cfg) {
|
|
|
9582
9750
|
}
|
|
9583
9751
|
|
|
9584
9752
|
// src/commands/config/apply.ts
|
|
9585
|
-
import { readFileSync as
|
|
9753
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
9586
9754
|
import { resolve as resolve7 } from "path";
|
|
9587
9755
|
import * as p2 from "@clack/prompts";
|
|
9588
9756
|
import pc6 from "picocolors";
|
|
@@ -9594,7 +9762,7 @@ function registerConfigApplyCommand(cfg) {
|
|
|
9594
9762
|
projectConfig = getProjectConfig();
|
|
9595
9763
|
await requireAuth();
|
|
9596
9764
|
const tomlPath = resolve7(process.cwd(), opts.file);
|
|
9597
|
-
const tomlSource =
|
|
9765
|
+
const tomlSource = readFileSync10(tomlPath, "utf8");
|
|
9598
9766
|
const file = parseConfigToml(tomlSource);
|
|
9599
9767
|
const res = await ossFetch("/api/metadata");
|
|
9600
9768
|
const raw = await res.json();
|
|
@@ -9783,9 +9951,9 @@ function registerConfigCommand(program2) {
|
|
|
9783
9951
|
}
|
|
9784
9952
|
|
|
9785
9953
|
// src/commands/ai/setup.ts
|
|
9786
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
9787
|
-
import { isAbsolute, join as
|
|
9788
|
-
import * as
|
|
9954
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
|
|
9955
|
+
import { isAbsolute, join as join14, relative as relative3, resolve as resolve8 } from "path";
|
|
9956
|
+
import * as clack18 from "@clack/prompts";
|
|
9789
9957
|
import pc7 from "picocolors";
|
|
9790
9958
|
|
|
9791
9959
|
// src/lib/api/ai.ts
|
|
@@ -9806,7 +9974,7 @@ async function getOpenRouterApiKey() {
|
|
|
9806
9974
|
}
|
|
9807
9975
|
|
|
9808
9976
|
// src/lib/env-writer.ts
|
|
9809
|
-
import { existsSync as
|
|
9977
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
9810
9978
|
var KEY_LINE_RE = (key) => (
|
|
9811
9979
|
// Match `KEY=...` at the start of a line (allowing leading whitespace).
|
|
9812
9980
|
// Captures the value side; we only need the value portion to compare.
|
|
@@ -9821,8 +9989,8 @@ function stripQuotes(v) {
|
|
|
9821
9989
|
return hash >= 0 ? t.slice(0, hash).trimEnd() : t;
|
|
9822
9990
|
}
|
|
9823
9991
|
function upsertEnvFile(path6, entries) {
|
|
9824
|
-
const exists =
|
|
9825
|
-
let content = exists ?
|
|
9992
|
+
const exists = existsSync12(path6);
|
|
9993
|
+
let content = exists ? readFileSync11(path6, "utf-8") : "";
|
|
9826
9994
|
const result = { added: [], skipped: [], mismatched: [] };
|
|
9827
9995
|
const additions = [];
|
|
9828
9996
|
for (const [key, value] of Object.entries(entries)) {
|
|
@@ -9845,7 +10013,7 @@ function upsertEnvFile(path6, entries) {
|
|
|
9845
10013
|
content += "\n";
|
|
9846
10014
|
}
|
|
9847
10015
|
content += additions.join("\n") + "\n";
|
|
9848
|
-
|
|
10016
|
+
writeFileSync9(path6, content);
|
|
9849
10017
|
} else if (!exists) {
|
|
9850
10018
|
}
|
|
9851
10019
|
return result;
|
|
@@ -9878,10 +10046,10 @@ async function runAiSetup(opts) {
|
|
|
9878
10046
|
throw new ProjectNotLinkedError();
|
|
9879
10047
|
}
|
|
9880
10048
|
if (!opts.json) {
|
|
9881
|
-
|
|
10049
|
+
clack18.intro("AI setup");
|
|
9882
10050
|
outputSuccess(`Linked to InsForge project: ${project.project_name} (${project.project_id})`);
|
|
9883
10051
|
}
|
|
9884
|
-
const spinner11 = !opts.json && isInteractive ?
|
|
10052
|
+
const spinner11 = !opts.json && isInteractive ? clack18.spinner() : null;
|
|
9885
10053
|
spinner11?.start("Fetching OpenRouter key...");
|
|
9886
10054
|
let key;
|
|
9887
10055
|
try {
|
|
@@ -9914,7 +10082,7 @@ async function runAiSetup(opts) {
|
|
|
9914
10082
|
outputInfo(pc7.dim(`${envLabel}: ${update.skipped.join(", ")} already set (matching) - left as-is.`));
|
|
9915
10083
|
}
|
|
9916
10084
|
for (const m of update.mismatched) {
|
|
9917
|
-
|
|
10085
|
+
clack18.log.warn(
|
|
9918
10086
|
`${envLabel} already has ${m.key}; left existing value untouched. Remove it or pass --env-file to write elsewhere.`
|
|
9919
10087
|
);
|
|
9920
10088
|
}
|
|
@@ -9922,7 +10090,7 @@ async function runAiSetup(opts) {
|
|
|
9922
10090
|
outputInfo(pc7.dim("Added .env*.local to .gitignore."));
|
|
9923
10091
|
}
|
|
9924
10092
|
if (!isLocalEnvFile(envFile)) {
|
|
9925
|
-
|
|
10093
|
+
clack18.log.warn(
|
|
9926
10094
|
`${envLabel} may be committed unless it is listed in .gitignore. Keep ${OPENROUTER_ENV_KEY} server-only.`
|
|
9927
10095
|
);
|
|
9928
10096
|
}
|
|
@@ -9930,7 +10098,7 @@ async function runAiSetup(opts) {
|
|
|
9930
10098
|
outputInfo("Use this key only from server-side code as process.env.OPENROUTER_API_KEY.");
|
|
9931
10099
|
outputInfo("For deployment, add OPENROUTER_API_KEY to your hosting provider environment.");
|
|
9932
10100
|
outputInfo(`Do not rename it to ${pc7.bold("NEXT_PUBLIC_")}, ${pc7.bold("VITE_")}, or ${pc7.bold("PUBLIC_")}.`);
|
|
9933
|
-
|
|
10101
|
+
clack18.outro("Done.");
|
|
9934
10102
|
}
|
|
9935
10103
|
return {
|
|
9936
10104
|
envFile: envLabel,
|
|
@@ -9960,8 +10128,8 @@ function ensureLocalEnvIgnored(cwd, envFile) {
|
|
|
9960
10128
|
if (!relEnvPath || relEnvPath.startsWith("..") || isAbsolute(relEnvPath)) {
|
|
9961
10129
|
return false;
|
|
9962
10130
|
}
|
|
9963
|
-
const gitignorePath =
|
|
9964
|
-
const existing =
|
|
10131
|
+
const gitignorePath = join14(cwd, ".gitignore");
|
|
10132
|
+
const existing = existsSync13(gitignorePath) ? readFileSync12(gitignorePath, "utf-8") : "";
|
|
9965
10133
|
const lines = new Set(existing.split(/\r?\n/).map((line) => line.trim()));
|
|
9966
10134
|
const envBasename = envFile.replace(/\\/g, "/").split("/").pop() ?? envFile;
|
|
9967
10135
|
if (lines.has(".env*") || lines.has(".env.*") || lines.has(".env*.local") || lines.has(".env.local") && envBasename === ".env.local") {
|
|
@@ -9982,7 +10150,7 @@ function registerAiCommands(aiCmd2) {
|
|
|
9982
10150
|
|
|
9983
10151
|
// src/index.ts
|
|
9984
10152
|
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
9985
|
-
var pkg = JSON.parse(
|
|
10153
|
+
var pkg = JSON.parse(readFileSync13(join15(__dirname, "../package.json"), "utf-8"));
|
|
9986
10154
|
var INSFORGE_LOGO = `
|
|
9987
10155
|
\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
10156
|
\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 +10261,7 @@ async function showInteractiveMenu() {
|
|
|
10093
10261
|
} catch {
|
|
10094
10262
|
}
|
|
10095
10263
|
console.log(INSFORGE_LOGO);
|
|
10096
|
-
|
|
10264
|
+
clack19.intro(`InsForge CLI v${pkg.version}`);
|
|
10097
10265
|
const options = [];
|
|
10098
10266
|
if (!isLoggedIn) {
|
|
10099
10267
|
options.push({ value: "login", label: "Log in to InsForge" });
|
|
@@ -10114,7 +10282,7 @@ async function showInteractiveMenu() {
|
|
|
10114
10282
|
options
|
|
10115
10283
|
});
|
|
10116
10284
|
if (isCancel2(action)) {
|
|
10117
|
-
|
|
10285
|
+
clack19.cancel("Bye!");
|
|
10118
10286
|
process.exit(0);
|
|
10119
10287
|
}
|
|
10120
10288
|
switch (action) {
|