@insforge/cli 0.1.80 → 0.1.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -8
- package/dist/index.js +187 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -674,17 +674,19 @@ Skill files are written to per-agent directories (e.g. `.claude/`, `.cursor/`, `
|
|
|
674
674
|
|
|
675
675
|
The CLI reports anonymous usage events to [PostHog](https://posthog.com) so we can understand which features are being used and prioritize improvements.
|
|
676
676
|
|
|
677
|
-
|
|
677
|
+
We capture only non-sensitive metadata: the command name, subcommand, outcome (`success`, `applied`, `aborted`, `dry_run`, `no_changes`, `all_skipped`, `error`), flag shape (e.g. `dry_run`, `json_mode`), section names from `insforge.toml` schema (e.g. `auth.smtp`), region, and an OSS-vs-cloud flag. We never send SQL, TOML file contents, credentials, environment variable values, or any free text you type.
|
|
678
|
+
|
|
679
|
+
If you build the CLI from source without setting `POSTHOG_API_KEY` at build time, analytics become a no-op automatically.
|
|
678
680
|
|
|
679
681
|
## Environment Variables
|
|
680
682
|
|
|
681
|
-
| Variable | Description
|
|
682
|
-
| ----------------------- |
|
|
683
|
-
| `INSFORGE_ACCESS_TOKEN` | Override the stored access token
|
|
684
|
-
| `INSFORGE_PROJECT_ID` | Override the linked project ID
|
|
685
|
-
| `INSFORGE_API_URL` | Override the Platform API URL
|
|
686
|
-
| `INSFORGE_EMAIL` | Email for non-interactive login
|
|
687
|
-
| `INSFORGE_PASSWORD` | Password for non-interactive login
|
|
683
|
+
| Variable | Description |
|
|
684
|
+
| ----------------------- | --------------------------------------------------------------- |
|
|
685
|
+
| `INSFORGE_ACCESS_TOKEN` | Override the stored access token |
|
|
686
|
+
| `INSFORGE_PROJECT_ID` | Override the linked project ID |
|
|
687
|
+
| `INSFORGE_API_URL` | Override the Platform API URL |
|
|
688
|
+
| `INSFORGE_EMAIL` | Email for non-interactive login |
|
|
689
|
+
| `INSFORGE_PASSWORD` | Password for non-interactive login |
|
|
688
690
|
|
|
689
691
|
## Non-Interactive / CI Usage
|
|
690
692
|
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { readFileSync as readFileSync12 } from "fs";
|
|
5
|
-
import { join as
|
|
5
|
+
import { join as join14, dirname as dirname2 } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
import * as clack18 from "@clack/prompts";
|
|
@@ -1215,9 +1215,24 @@ function trackPayments(subcommand, config, properties) {
|
|
|
1215
1215
|
...properties
|
|
1216
1216
|
});
|
|
1217
1217
|
}
|
|
1218
|
+
function trackConfig(subcommand, config, properties) {
|
|
1219
|
+
const distinctId = config?.project_id ?? FAKE_PROJECT_ID;
|
|
1220
|
+
captureEvent(distinctId, "cli_config_invoked", {
|
|
1221
|
+
subcommand,
|
|
1222
|
+
project_id: config?.project_id,
|
|
1223
|
+
project_name: config?.project_name,
|
|
1224
|
+
org_id: config?.org_id,
|
|
1225
|
+
region: config?.region,
|
|
1226
|
+
oss_mode: !config || config.project_id === FAKE_PROJECT_ID,
|
|
1227
|
+
...properties
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1218
1230
|
async function shutdownAnalytics() {
|
|
1231
|
+
if (!client) return;
|
|
1232
|
+
const c = client;
|
|
1233
|
+
client = null;
|
|
1219
1234
|
try {
|
|
1220
|
-
|
|
1235
|
+
await c.shutdown();
|
|
1221
1236
|
} catch {
|
|
1222
1237
|
}
|
|
1223
1238
|
}
|
|
@@ -2614,6 +2629,7 @@ var execAsync2 = promisify3(exec2);
|
|
|
2614
2629
|
var execFileAsync2 = promisify3(execFile2);
|
|
2615
2630
|
var SAFE_REPO_PATTERN2 = /^(https?:\/\/|git@)[A-Za-z0-9._:/@~+-]+(\.git)?$/;
|
|
2616
2631
|
var SAFE_BRANCH_PATTERN2 = /^[A-Za-z0-9._/-]+$/;
|
|
2632
|
+
var SAFE_MARKETPLACE_SLUG = /^[a-z0-9][a-z0-9-]{0,99}$/;
|
|
2617
2633
|
async function waitForProjectActive(projectId, apiUrl, timeoutMs = 12e4) {
|
|
2618
2634
|
const start = Date.now();
|
|
2619
2635
|
while (Date.now() - start < timeoutMs) {
|
|
@@ -2722,9 +2738,18 @@ async function copyDir(src, dest) {
|
|
|
2722
2738
|
}
|
|
2723
2739
|
}
|
|
2724
2740
|
function registerCreateCommand(program2) {
|
|
2725
|
-
program2.command("create").description("Create a new InsForge project").option("--name <name>", "Project name").option("--org-id <id>", "Organization ID").option("--region <region>", "Deployment region (us-east, us-west, eu-central, ap-southeast)").option("--template <template>", "Template to use: react, nextjs, chatbot, crm, e-commerce, todo, or empty").option("--auth <provider>", "Wire a third-party auth provider into the chosen template (currently: better-auth)").action(async (opts, cmd) => {
|
|
2741
|
+
program2.command("create").description("Create a new InsForge project").option("--name <name>", "Project name").option("--org-id <id>", "Organization ID").option("--region <region>", "Deployment region (us-east, us-west, eu-central, ap-southeast)").option("--template <template>", "Template to use: react, nextjs, chatbot, crm, e-commerce, todo, or empty").option("--marketplace <slug>", "Install a marketplace template by slug (browse: https://insforge.dev/templates)").option("--auth <provider>", "Wire a third-party auth provider into the chosen template (currently: better-auth)").action(async (opts, cmd) => {
|
|
2726
2742
|
const { json, apiUrl } = getRootOpts(cmd);
|
|
2727
2743
|
try {
|
|
2744
|
+
if (opts.marketplace && opts.template) {
|
|
2745
|
+
throw new CLIError("--marketplace and --template are mutually exclusive");
|
|
2746
|
+
}
|
|
2747
|
+
if (opts.marketplace && !SAFE_MARKETPLACE_SLUG.test(opts.marketplace)) {
|
|
2748
|
+
throw new CLIError(
|
|
2749
|
+
`Invalid --marketplace slug "${opts.marketplace}". Slugs must match ${SAFE_MARKETPLACE_SLUG}.
|
|
2750
|
+
Browse available templates: https://insforge.dev/templates`
|
|
2751
|
+
);
|
|
2752
|
+
}
|
|
2728
2753
|
await requireAuth(apiUrl, false);
|
|
2729
2754
|
if (!json) {
|
|
2730
2755
|
await animateBanner();
|
|
@@ -2781,6 +2806,9 @@ function registerCreateCommand(program2) {
|
|
|
2781
2806
|
if (template && !validTemplates.includes(template)) {
|
|
2782
2807
|
throw new CLIError(`Invalid template "${template}". Valid options: ${validTemplates.join(", ")}`);
|
|
2783
2808
|
}
|
|
2809
|
+
if (opts.marketplace) {
|
|
2810
|
+
template = opts.marketplace;
|
|
2811
|
+
}
|
|
2784
2812
|
if (!template) {
|
|
2785
2813
|
if (json) {
|
|
2786
2814
|
template = "empty";
|
|
@@ -2871,7 +2899,19 @@ function registerCreateCommand(program2) {
|
|
|
2871
2899
|
projectLinked = true;
|
|
2872
2900
|
s?.stop(`Project "${project.name}" created and linked`);
|
|
2873
2901
|
const githubTemplates = ["chatbot", "crm", "e-commerce", "nextjs", "react", "todo"];
|
|
2874
|
-
if (
|
|
2902
|
+
if (opts.marketplace) {
|
|
2903
|
+
const downloaded = await downloadGitHubTemplate(
|
|
2904
|
+
opts.marketplace,
|
|
2905
|
+
projectConfig,
|
|
2906
|
+
json
|
|
2907
|
+
);
|
|
2908
|
+
if (downloaded) {
|
|
2909
|
+
void reportMarketplaceDownload(
|
|
2910
|
+
opts.marketplace,
|
|
2911
|
+
apiUrl ?? "https://api.insforge.dev"
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2914
|
+
} else if (githubTemplates.includes(template)) {
|
|
2875
2915
|
await downloadGitHubTemplate(template, projectConfig, json);
|
|
2876
2916
|
} else if (hasTemplate) {
|
|
2877
2917
|
await downloadTemplate(template, projectConfig, projectName, json, apiUrl);
|
|
@@ -3136,6 +3176,7 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
3136
3176
|
}
|
|
3137
3177
|
}
|
|
3138
3178
|
}
|
|
3179
|
+
return true;
|
|
3139
3180
|
} catch (err) {
|
|
3140
3181
|
s?.stop(`${templateName} template download failed`);
|
|
3141
3182
|
const msg = `Failed to download ${templateName} template: ${err.message}`;
|
|
@@ -3145,11 +3186,25 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
3145
3186
|
clack12.log.warn(msg);
|
|
3146
3187
|
clack12.log.info("You can manually clone from: https://github.com/InsForge/insforge-templates");
|
|
3147
3188
|
}
|
|
3189
|
+
return false;
|
|
3148
3190
|
} finally {
|
|
3149
3191
|
await fs4.rm(tempDir, { recursive: true, force: true }).catch(() => {
|
|
3150
3192
|
});
|
|
3151
3193
|
}
|
|
3152
3194
|
}
|
|
3195
|
+
async function reportMarketplaceDownload(slug, apiUrl) {
|
|
3196
|
+
try {
|
|
3197
|
+
const res = await fetch(`${apiUrl}/templates/v1/${encodeURIComponent(slug)}/downloads`, {
|
|
3198
|
+
method: "POST",
|
|
3199
|
+
headers: { "Content-Type": "application/json" },
|
|
3200
|
+
body: "{}"
|
|
3201
|
+
});
|
|
3202
|
+
if (!res.ok) {
|
|
3203
|
+
return;
|
|
3204
|
+
}
|
|
3205
|
+
} catch {
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3153
3208
|
|
|
3154
3209
|
// src/commands/projects/link.ts
|
|
3155
3210
|
var execAsync3 = promisify4(exec3);
|
|
@@ -4539,19 +4594,21 @@ function registerFunctionsCommands(functionsCmd2) {
|
|
|
4539
4594
|
|
|
4540
4595
|
// src/commands/functions/deploy.ts
|
|
4541
4596
|
import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
|
|
4542
|
-
|
|
4597
|
+
function resolveDeployFilePath(opts) {
|
|
4598
|
+
if (!opts.file) {
|
|
4599
|
+
throw new CLIError("Missing required option: --file <path>");
|
|
4600
|
+
}
|
|
4601
|
+
if (!existsSync6(opts.file)) {
|
|
4602
|
+
throw new CLIError(`Source file not found: ${opts.file}`);
|
|
4603
|
+
}
|
|
4604
|
+
return opts.file;
|
|
4605
|
+
}
|
|
4543
4606
|
function registerFunctionsDeployCommand(functionsCmd2) {
|
|
4544
4607
|
functionsCmd2.command("deploy <slug>").description("Deploy an edge function (create or update)").option("--file <path>", "Path to the function source file").option("--name <name>", "Function display name").option("--description <desc>", "Function description").action(async (slug, opts, cmd) => {
|
|
4545
4608
|
const { json } = getRootOpts(cmd);
|
|
4546
4609
|
try {
|
|
4547
4610
|
await requireAuth();
|
|
4548
|
-
const filePath = opts
|
|
4549
|
-
if (!existsSync6(filePath)) {
|
|
4550
|
-
throw new CLIError(
|
|
4551
|
-
`Source file not found: ${filePath}
|
|
4552
|
-
Specify --file <path> or create ${join10("insforge", "functions", slug, "index.ts")}`
|
|
4553
|
-
);
|
|
4554
|
-
}
|
|
4611
|
+
const filePath = resolveDeployFilePath(opts);
|
|
4555
4612
|
const code = readFileSync5(filePath, "utf-8");
|
|
4556
4613
|
const name = opts.name ?? slug;
|
|
4557
4614
|
const description = opts.description ?? "";
|
|
@@ -4783,7 +4840,7 @@ function registerStorageUploadCommand(storageCmd2) {
|
|
|
4783
4840
|
|
|
4784
4841
|
// src/commands/storage/download.ts
|
|
4785
4842
|
import { writeFileSync as writeFileSync5 } from "fs";
|
|
4786
|
-
import { join as
|
|
4843
|
+
import { join as join10, basename as basename7 } from "path";
|
|
4787
4844
|
function registerStorageDownloadCommand(storageCmd2) {
|
|
4788
4845
|
storageCmd2.command("download <objectKey>").description("Download a file from a storage bucket").requiredOption("--bucket <name>", "Source bucket name").option("--output <path>", "Output file path (defaults to current directory)").action(async (objectKey, opts, cmd) => {
|
|
4789
4846
|
const { json } = getRootOpts(cmd);
|
|
@@ -4803,7 +4860,7 @@ function registerStorageDownloadCommand(storageCmd2) {
|
|
|
4803
4860
|
throw new CLIError(err.error ?? `Download failed: ${res.status}`);
|
|
4804
4861
|
}
|
|
4805
4862
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
4806
|
-
const outputPath = opts.output ??
|
|
4863
|
+
const outputPath = opts.output ?? join10(process.cwd(), basename7(objectKey));
|
|
4807
4864
|
writeFileSync5(outputPath, buffer);
|
|
4808
4865
|
if (json) {
|
|
4809
4866
|
outputJson({ success: true, path: outputPath, size: buffer.length });
|
|
@@ -5935,7 +5992,7 @@ function registerComputeEventsCommand(computeCmd2) {
|
|
|
5935
5992
|
|
|
5936
5993
|
// src/commands/compute/deploy.ts
|
|
5937
5994
|
import { existsSync as existsSync9 } from "fs";
|
|
5938
|
-
import { join as
|
|
5995
|
+
import { join as join12, resolve as resolve4 } from "path";
|
|
5939
5996
|
|
|
5940
5997
|
// src/lib/env-file.ts
|
|
5941
5998
|
import { readFileSync as readFileSync7 } from "fs";
|
|
@@ -5980,7 +6037,7 @@ function parseEnvFile(path6) {
|
|
|
5980
6037
|
// src/lib/flyctl.ts
|
|
5981
6038
|
import { spawn, spawnSync } from "child_process";
|
|
5982
6039
|
import { existsSync as existsSync8, writeFileSync as writeFileSync6, unlinkSync as unlinkSync3 } from "fs";
|
|
5983
|
-
import { join as
|
|
6040
|
+
import { join as join11 } from "path";
|
|
5984
6041
|
function ensureFlyctlAvailable() {
|
|
5985
6042
|
const r = spawnSync("flyctl", ["version"], {
|
|
5986
6043
|
encoding: "utf8",
|
|
@@ -5993,7 +6050,7 @@ function ensureFlyctlAvailable() {
|
|
|
5993
6050
|
}
|
|
5994
6051
|
}
|
|
5995
6052
|
function ensureFlyTomlStub(opts) {
|
|
5996
|
-
const path6 =
|
|
6053
|
+
const path6 = join11(opts.dir, "fly.toml");
|
|
5997
6054
|
if (existsSync8(path6)) {
|
|
5998
6055
|
return () => {
|
|
5999
6056
|
};
|
|
@@ -6181,7 +6238,7 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
6181
6238
|
return;
|
|
6182
6239
|
}
|
|
6183
6240
|
const absDir = resolve4(dir);
|
|
6184
|
-
const dockerfilePath =
|
|
6241
|
+
const dockerfilePath = join12(absDir, "Dockerfile");
|
|
6185
6242
|
if (!existsSync9(dockerfilePath)) {
|
|
6186
6243
|
throw new CLIError(
|
|
6187
6244
|
`No Dockerfile at ${dockerfilePath}.
|
|
@@ -6961,7 +7018,7 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
6961
7018
|
const s = !json ? clack15.spinner() : null;
|
|
6962
7019
|
s?.start("Collecting diagnostic data...");
|
|
6963
7020
|
const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
|
|
6964
|
-
const cliVersion = "0.1.
|
|
7021
|
+
const cliVersion = "0.1.82";
|
|
6965
7022
|
s?.stop("Data collected");
|
|
6966
7023
|
if (!json) {
|
|
6967
7024
|
console.log(`
|
|
@@ -8325,7 +8382,6 @@ function registerPaymentsCommands(paymentsCmd2) {
|
|
|
8325
8382
|
}
|
|
8326
8383
|
|
|
8327
8384
|
// src/commands/posthog/setup.ts
|
|
8328
|
-
import { spawnSync as spawnSync2 } from "child_process";
|
|
8329
8385
|
import * as clack16 from "@clack/prompts";
|
|
8330
8386
|
import pc3 from "picocolors";
|
|
8331
8387
|
|
|
@@ -8528,41 +8584,20 @@ async function runSetup(opts) {
|
|
|
8528
8584
|
outputSuccess(`Linked to InsForge project: ${proj.project_name} (${proj.project_id})`);
|
|
8529
8585
|
}
|
|
8530
8586
|
const dashboardConnection = await ensureDashboardConnection(proj.project_id, token, opts);
|
|
8531
|
-
if (opts.json) {
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
pc3.dim("(it will open a browser for OAuth and let you pick a PostHog project)")
|
|
8541
|
-
);
|
|
8542
|
-
const wizardResult = spawnSync2(NPX_COMMAND, ["-y", "@posthog/wizard@latest"], {
|
|
8543
|
-
stdio: "inherit",
|
|
8544
|
-
env: process.env
|
|
8545
|
-
});
|
|
8546
|
-
if (wizardResult.error) {
|
|
8547
|
-
throw new CLIError(`Failed to launch PostHog wizard: ${wizardResult.error.message}`);
|
|
8548
|
-
}
|
|
8549
|
-
const exitCode = wizardResult.status ?? 1;
|
|
8550
|
-
if (wizardResult.signal === "SIGINT" || exitCode === 130) {
|
|
8551
|
-
clack16.outro("Setup cancelled.");
|
|
8552
|
-
return {
|
|
8553
|
-
dashboardConnection,
|
|
8554
|
-
wizardSkipped: false,
|
|
8555
|
-
wizardExitCode: exitCode
|
|
8556
|
-
};
|
|
8557
|
-
}
|
|
8558
|
-
if (exitCode !== 0) {
|
|
8559
|
-
throw new CLIError(`PostHog wizard exited with code ${exitCode}.`);
|
|
8587
|
+
if (!opts.json) {
|
|
8588
|
+
clack16.note(
|
|
8589
|
+
`Run this in your terminal to wire PostHog into your app code:
|
|
8590
|
+
|
|
8591
|
+
${WIZARD_COMMAND}
|
|
8592
|
+
|
|
8593
|
+
Once it completes, open the Analytics page in your InsForge dashboard.`,
|
|
8594
|
+
"Next step"
|
|
8595
|
+
);
|
|
8560
8596
|
}
|
|
8561
|
-
clack16.outro("Done. Open the Analytics page in your InsForge dashboard to view data.");
|
|
8562
8597
|
return {
|
|
8563
8598
|
dashboardConnection,
|
|
8564
|
-
wizardSkipped:
|
|
8565
|
-
|
|
8599
|
+
wizardSkipped: true,
|
|
8600
|
+
wizardCommand: WIZARD_COMMAND
|
|
8566
8601
|
};
|
|
8567
8602
|
}
|
|
8568
8603
|
async function ensureDashboardConnection(projectId, token, opts) {
|
|
@@ -8589,10 +8624,14 @@ async function runConnectFlow(projectId, token, authorizeUrl, opts) {
|
|
|
8589
8624
|
process.stderr.write("Your browser should open automatically. If not, copy the URL above.\n");
|
|
8590
8625
|
} else {
|
|
8591
8626
|
clack16.log.info("PostHog is not yet connected to your InsForge dashboard.");
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
8627
|
+
if (opts.skipBrowser) {
|
|
8628
|
+
clack16.log.info(`Open this URL to authorize PostHog:
|
|
8629
|
+
${pc3.cyan(pc3.underline(authorizeUrl))}`);
|
|
8630
|
+
} else {
|
|
8631
|
+
clack16.log.info("Opening browser to authorize PostHog...");
|
|
8632
|
+
clack16.log.info(`If browser doesn't open, visit:
|
|
8633
|
+
${pc3.cyan(pc3.underline(authorizeUrl))}`);
|
|
8634
|
+
}
|
|
8596
8635
|
}
|
|
8597
8636
|
if (!opts.skipBrowser) {
|
|
8598
8637
|
try {
|
|
@@ -8602,7 +8641,11 @@ async function runConnectFlow(projectId, token, authorizeUrl, opts) {
|
|
|
8602
8641
|
}
|
|
8603
8642
|
}
|
|
8604
8643
|
const spinner11 = !opts.json && isInteractive ? clack16.spinner() : null;
|
|
8605
|
-
|
|
8644
|
+
if (spinner11) {
|
|
8645
|
+
spinner11.start("Waiting for InsForge dashboard connection... (timeout: 15 minutes)");
|
|
8646
|
+
} else if (!opts.json) {
|
|
8647
|
+
clack16.log.info("Waiting for InsForge dashboard connection (up to 15 minutes)...");
|
|
8648
|
+
}
|
|
8606
8649
|
try {
|
|
8607
8650
|
await pollPosthogConnection(
|
|
8608
8651
|
projectId,
|
|
@@ -8622,9 +8665,17 @@ async function runConnectFlow(projectId, token, authorizeUrl, opts) {
|
|
|
8622
8665
|
},
|
|
8623
8666
|
opts.apiUrl
|
|
8624
8667
|
);
|
|
8625
|
-
spinner11
|
|
8668
|
+
if (spinner11) {
|
|
8669
|
+
spinner11.stop("InsForge dashboard connection received.");
|
|
8670
|
+
} else if (!opts.json) {
|
|
8671
|
+
clack16.log.success("InsForge dashboard connection received.");
|
|
8672
|
+
}
|
|
8626
8673
|
} catch (err) {
|
|
8627
|
-
spinner11
|
|
8674
|
+
if (spinner11) {
|
|
8675
|
+
spinner11.stop("InsForge dashboard connection wait failed.");
|
|
8676
|
+
} else if (!opts.json) {
|
|
8677
|
+
clack16.log.error("InsForge dashboard connection wait failed.");
|
|
8678
|
+
}
|
|
8628
8679
|
throw err;
|
|
8629
8680
|
}
|
|
8630
8681
|
}
|
|
@@ -9111,7 +9162,9 @@ function configFromMetadata(raw) {
|
|
|
9111
9162
|
function registerConfigExportCommand(cfg) {
|
|
9112
9163
|
cfg.command("export").description("Pull live project config and write insforge.toml").option("--out <path>", "output path", "insforge.toml").option("--force", "overwrite without confirmation").action(async (opts, cmd) => {
|
|
9113
9164
|
const { json } = getRootOpts(cmd);
|
|
9165
|
+
let projectConfig = null;
|
|
9114
9166
|
try {
|
|
9167
|
+
projectConfig = getProjectConfig();
|
|
9115
9168
|
await requireAuth();
|
|
9116
9169
|
const target = resolve5(process.cwd(), opts.out);
|
|
9117
9170
|
if (existsSync10(target) && !opts.force) {
|
|
@@ -9128,6 +9181,12 @@ function registerConfigExportCommand(cfg) {
|
|
|
9128
9181
|
});
|
|
9129
9182
|
if (!ok || p.isCancel(ok)) {
|
|
9130
9183
|
console.log("Aborted.");
|
|
9184
|
+
await reportCliUsage("cli.config.export", true);
|
|
9185
|
+
trackConfig("export", projectConfig, {
|
|
9186
|
+
json_mode: !!json,
|
|
9187
|
+
force: !!opts.force,
|
|
9188
|
+
outcome: "aborted"
|
|
9189
|
+
});
|
|
9131
9190
|
return;
|
|
9132
9191
|
}
|
|
9133
9192
|
}
|
|
@@ -9149,9 +9208,23 @@ function registerConfigExportCommand(cfg) {
|
|
|
9149
9208
|
}
|
|
9150
9209
|
}
|
|
9151
9210
|
await reportCliUsage("cli.config.export", true);
|
|
9211
|
+
trackConfig("export", projectConfig, {
|
|
9212
|
+
json_mode: !!json,
|
|
9213
|
+
force: !!opts.force,
|
|
9214
|
+
skipped_count: skipped.length,
|
|
9215
|
+
outcome: "success"
|
|
9216
|
+
});
|
|
9152
9217
|
} catch (err) {
|
|
9153
9218
|
await reportCliUsage("cli.config.export", false);
|
|
9219
|
+
trackConfig("export", projectConfig, {
|
|
9220
|
+
json_mode: !!json,
|
|
9221
|
+
force: !!opts.force,
|
|
9222
|
+
outcome: "error"
|
|
9223
|
+
});
|
|
9224
|
+
await shutdownAnalytics();
|
|
9154
9225
|
handleError(err, json);
|
|
9226
|
+
} finally {
|
|
9227
|
+
await shutdownAnalytics();
|
|
9155
9228
|
}
|
|
9156
9229
|
});
|
|
9157
9230
|
}
|
|
@@ -9460,7 +9533,9 @@ function authPasswordWireKey(key) {
|
|
|
9460
9533
|
function registerConfigPlanCommand(cfg) {
|
|
9461
9534
|
cfg.command("plan").description("Show diff between insforge.toml and live project state").option("--file <path>", "path to insforge.toml", "insforge.toml").action(async (opts, cmd) => {
|
|
9462
9535
|
const { json } = getRootOpts(cmd);
|
|
9536
|
+
let projectConfig = null;
|
|
9463
9537
|
try {
|
|
9538
|
+
projectConfig = getProjectConfig();
|
|
9464
9539
|
await requireAuth();
|
|
9465
9540
|
const tomlPath = resolve6(process.cwd(), opts.file);
|
|
9466
9541
|
const tomlSource = readFileSync8(tomlPath, "utf8");
|
|
@@ -9483,9 +9558,25 @@ function registerConfigPlanCommand(cfg) {
|
|
|
9483
9558
|
}
|
|
9484
9559
|
}
|
|
9485
9560
|
await reportCliUsage("cli.config.plan", true);
|
|
9561
|
+
trackConfig("plan", projectConfig, {
|
|
9562
|
+
json_mode: !!json,
|
|
9563
|
+
changes_count: result.changes.length,
|
|
9564
|
+
skipped_count: skipped.length,
|
|
9565
|
+
sections_changed: Array.from(
|
|
9566
|
+
new Set(result.changes.map((c) => changePath(c)))
|
|
9567
|
+
),
|
|
9568
|
+
outcome: "success"
|
|
9569
|
+
});
|
|
9486
9570
|
} catch (err) {
|
|
9487
9571
|
await reportCliUsage("cli.config.plan", false);
|
|
9572
|
+
trackConfig("plan", projectConfig, {
|
|
9573
|
+
json_mode: !!json,
|
|
9574
|
+
outcome: "error"
|
|
9575
|
+
});
|
|
9576
|
+
await shutdownAnalytics();
|
|
9488
9577
|
handleError(err, json);
|
|
9578
|
+
} finally {
|
|
9579
|
+
await shutdownAnalytics();
|
|
9489
9580
|
}
|
|
9490
9581
|
});
|
|
9491
9582
|
}
|
|
@@ -9498,7 +9589,9 @@ import pc6 from "picocolors";
|
|
|
9498
9589
|
function registerConfigApplyCommand(cfg) {
|
|
9499
9590
|
cfg.command("apply").description("Apply insforge.toml to the live project").option("--file <path>", "path to insforge.toml", "insforge.toml").option("--dry-run", "show plan, do not apply").option("--auto-approve", "skip confirmation prompt").action(async (opts, cmd) => {
|
|
9500
9591
|
const { json, yes } = getRootOpts(cmd);
|
|
9592
|
+
let projectConfig = null;
|
|
9501
9593
|
try {
|
|
9594
|
+
projectConfig = getProjectConfig();
|
|
9502
9595
|
await requireAuth();
|
|
9503
9596
|
const tomlPath = resolve7(process.cwd(), opts.file);
|
|
9504
9597
|
const tomlSource = readFileSync9(tomlPath, "utf8");
|
|
@@ -9508,6 +9601,9 @@ function registerConfigApplyCommand(cfg) {
|
|
|
9508
9601
|
const live = liveFromMetadata(raw);
|
|
9509
9602
|
const result = diffConfig({ live, file });
|
|
9510
9603
|
const approved = opts.autoApprove || yes;
|
|
9604
|
+
const sectionsChanged = Array.from(
|
|
9605
|
+
new Set(result.changes.map((c) => changePath(c)))
|
|
9606
|
+
);
|
|
9511
9607
|
if (!json) {
|
|
9512
9608
|
console.log(formatPlan(result));
|
|
9513
9609
|
}
|
|
@@ -9518,6 +9614,13 @@ function registerConfigApplyCommand(cfg) {
|
|
|
9518
9614
|
);
|
|
9519
9615
|
}
|
|
9520
9616
|
await reportCliUsage("cli.config.apply", true);
|
|
9617
|
+
trackConfig("apply", projectConfig, {
|
|
9618
|
+
dry_run: !!opts.dryRun,
|
|
9619
|
+
json_mode: !!json,
|
|
9620
|
+
changes_count: result.changes.length,
|
|
9621
|
+
sections_changed: sectionsChanged,
|
|
9622
|
+
outcome: result.changes.length === 0 ? "no_changes" : "dry_run"
|
|
9623
|
+
});
|
|
9521
9624
|
return;
|
|
9522
9625
|
}
|
|
9523
9626
|
if (!approved) {
|
|
@@ -9535,6 +9638,12 @@ function registerConfigApplyCommand(cfg) {
|
|
|
9535
9638
|
if (!ok || p2.isCancel(ok)) {
|
|
9536
9639
|
console.log("Aborted.");
|
|
9537
9640
|
await reportCliUsage("cli.config.apply", true);
|
|
9641
|
+
trackConfig("apply", projectConfig, {
|
|
9642
|
+
json_mode: !!json,
|
|
9643
|
+
changes_count: result.changes.length,
|
|
9644
|
+
sections_changed: sectionsChanged,
|
|
9645
|
+
outcome: "aborted"
|
|
9646
|
+
});
|
|
9538
9647
|
return;
|
|
9539
9648
|
}
|
|
9540
9649
|
}
|
|
@@ -9571,9 +9680,25 @@ function registerConfigApplyCommand(cfg) {
|
|
|
9571
9680
|
}
|
|
9572
9681
|
}
|
|
9573
9682
|
await reportCliUsage("cli.config.apply", true);
|
|
9683
|
+
trackConfig("apply", projectConfig, {
|
|
9684
|
+
auto_approved: !!approved,
|
|
9685
|
+
json_mode: !!json,
|
|
9686
|
+
changes_count: result.changes.length,
|
|
9687
|
+
applied_count: applied.length,
|
|
9688
|
+
skipped_count: skipped.length,
|
|
9689
|
+
sections_changed: sectionsChanged,
|
|
9690
|
+
outcome: applied.length > 0 ? "applied" : "all_skipped"
|
|
9691
|
+
});
|
|
9574
9692
|
} catch (err) {
|
|
9575
9693
|
await reportCliUsage("cli.config.apply", false);
|
|
9694
|
+
trackConfig("apply", projectConfig, {
|
|
9695
|
+
json_mode: !!json,
|
|
9696
|
+
outcome: "error"
|
|
9697
|
+
});
|
|
9698
|
+
await shutdownAnalytics();
|
|
9576
9699
|
handleError(err, json);
|
|
9700
|
+
} finally {
|
|
9701
|
+
await shutdownAnalytics();
|
|
9577
9702
|
}
|
|
9578
9703
|
});
|
|
9579
9704
|
}
|
|
@@ -9659,7 +9784,7 @@ function registerConfigCommand(program2) {
|
|
|
9659
9784
|
|
|
9660
9785
|
// src/commands/ai/setup.ts
|
|
9661
9786
|
import { appendFileSync as appendFileSync2, existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
9662
|
-
import { isAbsolute, join as
|
|
9787
|
+
import { isAbsolute, join as join13, relative as relative3, resolve as resolve8 } from "path";
|
|
9663
9788
|
import * as clack17 from "@clack/prompts";
|
|
9664
9789
|
import pc7 from "picocolors";
|
|
9665
9790
|
|
|
@@ -9835,7 +9960,7 @@ function ensureLocalEnvIgnored(cwd, envFile) {
|
|
|
9835
9960
|
if (!relEnvPath || relEnvPath.startsWith("..") || isAbsolute(relEnvPath)) {
|
|
9836
9961
|
return false;
|
|
9837
9962
|
}
|
|
9838
|
-
const gitignorePath =
|
|
9963
|
+
const gitignorePath = join13(cwd, ".gitignore");
|
|
9839
9964
|
const existing = existsSync12(gitignorePath) ? readFileSync11(gitignorePath, "utf-8") : "";
|
|
9840
9965
|
const lines = new Set(existing.split(/\r?\n/).map((line) => line.trim()));
|
|
9841
9966
|
const envBasename = envFile.replace(/\\/g, "/").split("/").pop() ?? envFile;
|
|
@@ -9857,7 +9982,7 @@ function registerAiCommands(aiCmd2) {
|
|
|
9857
9982
|
|
|
9858
9983
|
// src/index.ts
|
|
9859
9984
|
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
9860
|
-
var pkg = JSON.parse(readFileSync12(
|
|
9985
|
+
var pkg = JSON.parse(readFileSync12(join14(__dirname, "../package.json"), "utf-8"));
|
|
9861
9986
|
var INSFORGE_LOGO = `
|
|
9862
9987
|
\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
9863
9988
|
\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
|