@insforge/cli 0.1.74 → 0.1.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +440 -59
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { readFileSync as
|
|
4
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
5
5
|
import { join as join17, dirname as dirname3 } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -41,8 +41,8 @@ var LineReader = class {
|
|
|
41
41
|
this.output.write(prompt);
|
|
42
42
|
if (this.queue.length > 0) return this.queue.shift();
|
|
43
43
|
if (this.closed) return null;
|
|
44
|
-
return new Promise((
|
|
45
|
-
this.waiter =
|
|
44
|
+
return new Promise((resolve8) => {
|
|
45
|
+
this.waiter = resolve8;
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
close() {
|
|
@@ -424,8 +424,8 @@ function startCallbackServer() {
|
|
|
424
424
|
return new Promise((resolveServer) => {
|
|
425
425
|
let resolveResult;
|
|
426
426
|
let rejectResult;
|
|
427
|
-
const resultPromise = new Promise((
|
|
428
|
-
resolveResult =
|
|
427
|
+
const resultPromise = new Promise((resolve8, reject) => {
|
|
428
|
+
resolveResult = resolve8;
|
|
429
429
|
rejectResult = reject;
|
|
430
430
|
});
|
|
431
431
|
const server = createServer((req, res) => {
|
|
@@ -1158,7 +1158,7 @@ function registerProjectsCommands(projectsCmd2) {
|
|
|
1158
1158
|
}
|
|
1159
1159
|
outputTable(
|
|
1160
1160
|
["ID", "Name", "Region", "Status", "AppKey"],
|
|
1161
|
-
projects.map((
|
|
1161
|
+
projects.map((p3) => [p3.id, p3.name, p3.region, p3.status, p3.appkey])
|
|
1162
1162
|
);
|
|
1163
1163
|
}
|
|
1164
1164
|
} catch (err) {
|
|
@@ -1888,18 +1888,34 @@ async function getJwtSecret() {
|
|
|
1888
1888
|
function spliceDatabasePassword(maskedUrl, password3) {
|
|
1889
1889
|
return maskedUrl.replace(/^(postgresql:\/\/[^:]+:)[^@]+(@)/, `$1${password3}$2`);
|
|
1890
1890
|
}
|
|
1891
|
+
function isMaskedDatabasePassword(value) {
|
|
1892
|
+
return /^\*+$/.test(value);
|
|
1893
|
+
}
|
|
1894
|
+
async function fetchDatabasePasswordOnce() {
|
|
1895
|
+
try {
|
|
1896
|
+
const res = await ossFetch("/api/metadata/database-password");
|
|
1897
|
+
const body = await res.json();
|
|
1898
|
+
const pw = body.databasePassword;
|
|
1899
|
+
if (typeof pw !== "string" || !pw || isMaskedDatabasePassword(pw)) return null;
|
|
1900
|
+
return pw;
|
|
1901
|
+
} catch {
|
|
1902
|
+
return null;
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1891
1905
|
async function getDatabaseConnectionString() {
|
|
1892
1906
|
try {
|
|
1893
|
-
const
|
|
1894
|
-
ossFetch("/api/metadata/database-connection-string"),
|
|
1895
|
-
ossFetch("/api/metadata/database-password")
|
|
1896
|
-
]);
|
|
1907
|
+
const urlRes = await ossFetch("/api/metadata/database-connection-string");
|
|
1897
1908
|
const urlBody = await urlRes.json();
|
|
1898
|
-
const pwBody = await pwRes.json();
|
|
1899
1909
|
const masked = urlBody.connectionURL;
|
|
1900
|
-
const password3 = pwBody.databasePassword;
|
|
1901
1910
|
if (typeof masked !== "string" || !masked) return null;
|
|
1902
|
-
|
|
1911
|
+
let password3 = await fetchDatabasePasswordOnce();
|
|
1912
|
+
const POLL_ATTEMPTS = 9;
|
|
1913
|
+
const POLL_DELAY_MS = 2e3;
|
|
1914
|
+
for (let attempt = 0; password3 === null && attempt < POLL_ATTEMPTS; attempt++) {
|
|
1915
|
+
await new Promise((r) => setTimeout(r, POLL_DELAY_MS));
|
|
1916
|
+
password3 = await fetchDatabasePasswordOnce();
|
|
1917
|
+
}
|
|
1918
|
+
if (password3 === null) return null;
|
|
1903
1919
|
return spliceDatabasePassword(masked, password3);
|
|
1904
1920
|
} catch {
|
|
1905
1921
|
return null;
|
|
@@ -1938,8 +1954,8 @@ ${err.nextActions}`;
|
|
|
1938
1954
|
// src/auth-providers/apply.ts
|
|
1939
1955
|
var execFileAsync = promisify2(execFile);
|
|
1940
1956
|
var VALID_AUTH_PROVIDERS = ["better-auth"];
|
|
1941
|
-
function pathExists(
|
|
1942
|
-
return fs.stat(
|
|
1957
|
+
function pathExists(p3) {
|
|
1958
|
+
return fs.stat(p3).then(() => true, () => false);
|
|
1943
1959
|
}
|
|
1944
1960
|
function deepMergeKeepBase(base, patch) {
|
|
1945
1961
|
const out = { ...base };
|
|
@@ -2330,11 +2346,11 @@ async function collectDeploymentFiles(sourceDir) {
|
|
|
2330
2346
|
return files;
|
|
2331
2347
|
}
|
|
2332
2348
|
async function createZipBuffer(sourceDir) {
|
|
2333
|
-
return new Promise((
|
|
2349
|
+
return new Promise((resolve8, reject) => {
|
|
2334
2350
|
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
2335
2351
|
const chunks = [];
|
|
2336
2352
|
archive.on("data", (chunk) => chunks.push(chunk));
|
|
2337
|
-
archive.on("end", () =>
|
|
2353
|
+
archive.on("end", () => resolve8(Buffer.concat(chunks)));
|
|
2338
2354
|
archive.on("error", (err) => reject(err));
|
|
2339
2355
|
archive.directory(sourceDir, false, (entry) => {
|
|
2340
2356
|
if (shouldExclude(entry.name)) return false;
|
|
@@ -2416,7 +2432,7 @@ async function pollDeployment(deploymentId, spinner10, syncBeforeRead) {
|
|
|
2416
2432
|
const startTime = Date.now();
|
|
2417
2433
|
let deployment = null;
|
|
2418
2434
|
while (Date.now() - startTime < POLL_TIMEOUT_MS3) {
|
|
2419
|
-
await new Promise((
|
|
2435
|
+
await new Promise((resolve8) => setTimeout(resolve8, POLL_INTERVAL_MS3));
|
|
2420
2436
|
try {
|
|
2421
2437
|
if (syncBeforeRead) {
|
|
2422
2438
|
await ossFetch(`/api/deployments/${deploymentId}/sync`, { method: "POST" });
|
|
@@ -2982,7 +2998,7 @@ function registerCreateCommand(program2) {
|
|
|
2982
2998
|
clack12.note(
|
|
2983
2999
|
`Open your coding agent (Claude Code, Codex, Cursor, etc.) and try:
|
|
2984
3000
|
|
|
2985
|
-
${prompts.map((
|
|
3001
|
+
${prompts.map((p3) => `\u2022 "${p3}"`).join("\n")}`,
|
|
2986
3002
|
"Start building"
|
|
2987
3003
|
);
|
|
2988
3004
|
}
|
|
@@ -3379,9 +3395,9 @@ function registerProjectLinkCommand(program2) {
|
|
|
3379
3395
|
}
|
|
3380
3396
|
const selected = await select2({
|
|
3381
3397
|
message: "Select a project to link:",
|
|
3382
|
-
options: projects.map((
|
|
3383
|
-
value:
|
|
3384
|
-
label: `${
|
|
3398
|
+
options: projects.map((p3) => ({
|
|
3399
|
+
value: p3.id,
|
|
3400
|
+
label: `${p3.name} (${p3.region}, ${p3.status})`
|
|
3385
3401
|
}))
|
|
3386
3402
|
});
|
|
3387
3403
|
if (isCancel2(selected)) process.exit(0);
|
|
@@ -3520,7 +3536,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
3520
3536
|
clack13.note(
|
|
3521
3537
|
`Open your coding agent (Claude Code, Codex, Cursor, etc.) and try:
|
|
3522
3538
|
|
|
3523
|
-
${prompts.map((
|
|
3539
|
+
${prompts.map((p3) => `\u2022 "${p3}"`).join("\n")}`,
|
|
3524
3540
|
"Start building"
|
|
3525
3541
|
);
|
|
3526
3542
|
}
|
|
@@ -3677,13 +3693,13 @@ function registerDbPoliciesCommand(dbCmd2) {
|
|
|
3677
3693
|
}
|
|
3678
3694
|
outputTable(
|
|
3679
3695
|
["Table", "Policy Name", "Command", "Roles", "Qual", "With Check"],
|
|
3680
|
-
policies.map((
|
|
3681
|
-
String(
|
|
3682
|
-
String(
|
|
3683
|
-
String(
|
|
3684
|
-
Array.isArray(
|
|
3685
|
-
String(
|
|
3686
|
-
String(
|
|
3696
|
+
policies.map((p3) => [
|
|
3697
|
+
String(p3.tableName ?? "-"),
|
|
3698
|
+
String(p3.policyName ?? "-"),
|
|
3699
|
+
String(p3.cmd ?? "-"),
|
|
3700
|
+
Array.isArray(p3.roles) ? p3.roles.join(", ") : String(p3.roles ?? "-"),
|
|
3701
|
+
String(p3.qual ?? "-"),
|
|
3702
|
+
String(p3.withCheck ?? "-")
|
|
3687
3703
|
])
|
|
3688
3704
|
);
|
|
3689
3705
|
}
|
|
@@ -4829,10 +4845,10 @@ function registerStorageDeleteBucketCommand(storageCmd2) {
|
|
|
4829
4845
|
try {
|
|
4830
4846
|
await requireAuth();
|
|
4831
4847
|
if (!yes && !json) {
|
|
4832
|
-
const
|
|
4848
|
+
const confirm8 = await confirm2({
|
|
4833
4849
|
message: `Delete bucket "${name}" and all its objects? This cannot be undone.`
|
|
4834
4850
|
});
|
|
4835
|
-
if (isCancel2(
|
|
4851
|
+
if (isCancel2(confirm8) || !confirm8) {
|
|
4836
4852
|
process.exit(0);
|
|
4837
4853
|
}
|
|
4838
4854
|
}
|
|
@@ -4979,12 +4995,12 @@ function registerListCommand(program2) {
|
|
|
4979
4995
|
id: org.id,
|
|
4980
4996
|
name: org.name,
|
|
4981
4997
|
type: org.type ?? null,
|
|
4982
|
-
projects: projects.map((
|
|
4983
|
-
id:
|
|
4984
|
-
name:
|
|
4985
|
-
region:
|
|
4986
|
-
status:
|
|
4987
|
-
appkey:
|
|
4998
|
+
projects: projects.map((p3) => ({
|
|
4999
|
+
id: p3.id,
|
|
5000
|
+
name: p3.name,
|
|
5001
|
+
region: p3.region,
|
|
5002
|
+
status: p3.status,
|
|
5003
|
+
appkey: p3.appkey
|
|
4988
5004
|
}))
|
|
4989
5005
|
}))
|
|
4990
5006
|
);
|
|
@@ -4996,13 +5012,13 @@ function registerListCommand(program2) {
|
|
|
4996
5012
|
rows.push([org.name, "-", "-", "-", "-"]);
|
|
4997
5013
|
} else {
|
|
4998
5014
|
for (let i = 0; i < projects.length; i++) {
|
|
4999
|
-
const
|
|
5015
|
+
const p3 = projects[i];
|
|
5000
5016
|
rows.push([
|
|
5001
5017
|
i === 0 ? org.name : "",
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5018
|
+
p3.name,
|
|
5019
|
+
p3.region,
|
|
5020
|
+
p3.status,
|
|
5021
|
+
p3.appkey
|
|
5006
5022
|
]);
|
|
5007
5023
|
}
|
|
5008
5024
|
}
|
|
@@ -5383,10 +5399,10 @@ function registerSecretsDeleteCommand(secretsCmd2) {
|
|
|
5383
5399
|
try {
|
|
5384
5400
|
await requireAuth();
|
|
5385
5401
|
if (!yes && !json) {
|
|
5386
|
-
const
|
|
5402
|
+
const confirm8 = await confirm2({
|
|
5387
5403
|
message: `Delete secret "${key}"? This cannot be undone.`
|
|
5388
5404
|
});
|
|
5389
|
-
if (isCancel2(
|
|
5405
|
+
if (isCancel2(confirm8) || !confirm8) {
|
|
5390
5406
|
process.exit(0);
|
|
5391
5407
|
}
|
|
5392
5408
|
}
|
|
@@ -5574,10 +5590,10 @@ function registerSchedulesDeleteCommand(schedulesCmd2) {
|
|
|
5574
5590
|
try {
|
|
5575
5591
|
await requireAuth();
|
|
5576
5592
|
if (!yes && !json) {
|
|
5577
|
-
const
|
|
5593
|
+
const confirm8 = await confirm2({
|
|
5578
5594
|
message: `Delete schedule "${id}"? This cannot be undone.`
|
|
5579
5595
|
});
|
|
5580
|
-
if (isCancel2(
|
|
5596
|
+
if (isCancel2(confirm8) || !confirm8) {
|
|
5581
5597
|
process.exit(0);
|
|
5582
5598
|
}
|
|
5583
5599
|
}
|
|
@@ -5799,6 +5815,8 @@ function registerComputeUpdateCommand(computeCmd2) {
|
|
|
5799
5815
|
outputJson(service);
|
|
5800
5816
|
} else {
|
|
5801
5817
|
outputSuccess(`Service "${service.name}" updated [${service.status}]`);
|
|
5818
|
+
if (service.endpointUrl) console.log(` Endpoint: ${service.endpointUrl}`);
|
|
5819
|
+
if (service.port !== void 0) console.log(` Port: ${service.port} (container must listen on this port)`);
|
|
5802
5820
|
}
|
|
5803
5821
|
await reportCliUsage("cli.compute.update", true);
|
|
5804
5822
|
} catch (err) {
|
|
@@ -5999,7 +6017,7 @@ primary_region = "${opts.region}"
|
|
|
5999
6017
|
};
|
|
6000
6018
|
}
|
|
6001
6019
|
function flyctlBuildAndPush(opts) {
|
|
6002
|
-
return new Promise((
|
|
6020
|
+
return new Promise((resolve8, reject) => {
|
|
6003
6021
|
const cleanupStub = ensureFlyTomlStub({
|
|
6004
6022
|
dir: opts.dir,
|
|
6005
6023
|
appId: opts.appId,
|
|
@@ -6055,7 +6073,7 @@ function flyctlBuildAndPush(opts) {
|
|
|
6055
6073
|
)
|
|
6056
6074
|
);
|
|
6057
6075
|
}
|
|
6058
|
-
|
|
6076
|
+
resolve8({ imageRef: `registry.fly.io/${opts.appId}@${m[1]}` });
|
|
6059
6077
|
});
|
|
6060
6078
|
});
|
|
6061
6079
|
}
|
|
@@ -6154,6 +6172,7 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
6154
6172
|
const verb = existing2 ? "updated" : "deployed";
|
|
6155
6173
|
outputSuccess(`Service "${service2.name}" ${verb} [${service2.status}]`);
|
|
6156
6174
|
if (service2.endpointUrl) console.log(` Endpoint: ${service2.endpointUrl}`);
|
|
6175
|
+
if (service2.port !== void 0) console.log(` Port: ${service2.port} (container must listen on this port)`);
|
|
6157
6176
|
}
|
|
6158
6177
|
await reportCliUsage("cli.compute.deploy", true);
|
|
6159
6178
|
return;
|
|
@@ -6249,6 +6268,7 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
6249
6268
|
const verb = existing ? "updated" : "deployed";
|
|
6250
6269
|
outputSuccess(`Service "${service.name}" ${verb} [${service.status}]`);
|
|
6251
6270
|
if (service.endpointUrl) console.log(` Endpoint: ${service.endpointUrl}`);
|
|
6271
|
+
if (service.port !== void 0) console.log(` Port: ${service.port} (container must listen on this port)`);
|
|
6252
6272
|
console.log(` Image: ${imageRef} (built remotely; no local image to clean up)`);
|
|
6253
6273
|
}
|
|
6254
6274
|
await reportCliUsage("cli.compute.deploy", true);
|
|
@@ -6938,7 +6958,7 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
6938
6958
|
const s = !json ? clack15.spinner() : null;
|
|
6939
6959
|
s?.start("Collecting diagnostic data...");
|
|
6940
6960
|
const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
|
|
6941
|
-
const cliVersion = "0.1.
|
|
6961
|
+
const cliVersion = "0.1.76";
|
|
6942
6962
|
s?.stop("Data collected");
|
|
6943
6963
|
if (!json) {
|
|
6944
6964
|
console.log(`
|
|
@@ -7572,10 +7592,10 @@ function registerPaymentsConfigCommand(paymentsCmd2) {
|
|
|
7572
7592
|
throw new CLIError("Use --yes with --json to remove a Stripe key non-interactively.");
|
|
7573
7593
|
}
|
|
7574
7594
|
if (!yes) {
|
|
7575
|
-
const
|
|
7595
|
+
const confirm8 = await confirm2({
|
|
7576
7596
|
message: `Remove Stripe ${environment} key? Payment sync and mutations for this environment will stop.`
|
|
7577
7597
|
});
|
|
7578
|
-
if (isCancel2(
|
|
7598
|
+
if (isCancel2(confirm8) || !confirm8) process.exit(0);
|
|
7579
7599
|
}
|
|
7580
7600
|
const data = await removeStripeSecretKey(environment);
|
|
7581
7601
|
if (json) {
|
|
@@ -8098,10 +8118,10 @@ function registerPaymentsProductsCommand(paymentsCmd2) {
|
|
|
8098
8118
|
);
|
|
8099
8119
|
}
|
|
8100
8120
|
if (!yes) {
|
|
8101
|
-
const
|
|
8121
|
+
const confirm8 = await confirm2({
|
|
8102
8122
|
message: `Delete Stripe ${environment} product "${productId}"?`
|
|
8103
8123
|
});
|
|
8104
|
-
if (isCancel2(
|
|
8124
|
+
if (isCancel2(confirm8) || !confirm8) process.exit(0);
|
|
8105
8125
|
}
|
|
8106
8126
|
const data = await deletePaymentProduct(environment, productId);
|
|
8107
8127
|
if (json) {
|
|
@@ -8452,14 +8472,14 @@ async function startPosthogCliFlow(projectId, jwt, apiUrl) {
|
|
|
8452
8472
|
throw new CLIError("PostHog cli-start returned an unexpected response shape.");
|
|
8453
8473
|
}
|
|
8454
8474
|
function sleep(ms, signal) {
|
|
8455
|
-
return new Promise((
|
|
8475
|
+
return new Promise((resolve8, reject) => {
|
|
8456
8476
|
if (signal?.aborted) {
|
|
8457
8477
|
reject(new CLIError("Connection wait cancelled."));
|
|
8458
8478
|
return;
|
|
8459
8479
|
}
|
|
8460
8480
|
const timer = setTimeout(() => {
|
|
8461
8481
|
signal?.removeEventListener("abort", onAbort);
|
|
8462
|
-
|
|
8482
|
+
resolve8();
|
|
8463
8483
|
}, ms);
|
|
8464
8484
|
const onAbort = () => {
|
|
8465
8485
|
clearTimeout(timer);
|
|
@@ -9027,13 +9047,373 @@ function frameworkLabel(framework) {
|
|
|
9027
9047
|
return "Astro";
|
|
9028
9048
|
}
|
|
9029
9049
|
}
|
|
9030
|
-
function relative3(
|
|
9031
|
-
return
|
|
9050
|
+
function relative3(p3) {
|
|
9051
|
+
return p3.replace(process.cwd() + "/", "");
|
|
9052
|
+
}
|
|
9053
|
+
|
|
9054
|
+
// src/commands/config/export.ts
|
|
9055
|
+
import { writeFileSync as writeFileSync9, existsSync as existsSync14 } from "fs";
|
|
9056
|
+
import { resolve as resolve5 } from "path";
|
|
9057
|
+
import * as p from "@clack/prompts";
|
|
9058
|
+
import pc4 from "picocolors";
|
|
9059
|
+
|
|
9060
|
+
// src/lib/config-toml.ts
|
|
9061
|
+
import * as smolToml from "smol-toml";
|
|
9062
|
+
|
|
9063
|
+
// src/lib/config-schema.ts
|
|
9064
|
+
var ConfigValidationError = class extends Error {
|
|
9065
|
+
constructor(path6, message) {
|
|
9066
|
+
super(`config.${path6}: ${message}`);
|
|
9067
|
+
this.path = path6;
|
|
9068
|
+
this.name = "ConfigValidationError";
|
|
9069
|
+
}
|
|
9070
|
+
};
|
|
9071
|
+
function validateConfig(input) {
|
|
9072
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
9073
|
+
throw new ConfigValidationError("", "must be an object");
|
|
9074
|
+
}
|
|
9075
|
+
const obj = input;
|
|
9076
|
+
const out = {};
|
|
9077
|
+
if ("project_id" in obj) {
|
|
9078
|
+
if (typeof obj.project_id !== "string") {
|
|
9079
|
+
throw new ConfigValidationError("project_id", "must be a string");
|
|
9080
|
+
}
|
|
9081
|
+
out.project_id = obj.project_id;
|
|
9082
|
+
}
|
|
9083
|
+
if ("auth" in obj) out.auth = validateAuth(obj.auth);
|
|
9084
|
+
return out;
|
|
9085
|
+
}
|
|
9086
|
+
function validateAuth(input) {
|
|
9087
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
9088
|
+
throw new ConfigValidationError("auth", "must be an object");
|
|
9089
|
+
}
|
|
9090
|
+
const obj = input;
|
|
9091
|
+
const out = {};
|
|
9092
|
+
if ("allowed_redirect_urls" in obj) {
|
|
9093
|
+
const v = obj.allowed_redirect_urls;
|
|
9094
|
+
if (!Array.isArray(v) || !v.every((u) => typeof u === "string")) {
|
|
9095
|
+
throw new ConfigValidationError(
|
|
9096
|
+
"auth.allowed_redirect_urls",
|
|
9097
|
+
"must be an array of strings"
|
|
9098
|
+
);
|
|
9099
|
+
}
|
|
9100
|
+
out.allowed_redirect_urls = v;
|
|
9101
|
+
}
|
|
9102
|
+
return out;
|
|
9103
|
+
}
|
|
9104
|
+
|
|
9105
|
+
// src/lib/config-toml.ts
|
|
9106
|
+
function parseConfigToml(input) {
|
|
9107
|
+
let parsed;
|
|
9108
|
+
try {
|
|
9109
|
+
parsed = smolToml.parse(input);
|
|
9110
|
+
} catch (err) {
|
|
9111
|
+
throw new Error(`TOML parse error: ${err.message}`, { cause: err });
|
|
9112
|
+
}
|
|
9113
|
+
return validateConfig(parsed);
|
|
9114
|
+
}
|
|
9115
|
+
function stringifyConfigToml(config) {
|
|
9116
|
+
const lines = [];
|
|
9117
|
+
if (config.project_id !== void 0) {
|
|
9118
|
+
lines.push(`project_id = ${JSON.stringify(config.project_id)}`);
|
|
9119
|
+
lines.push("");
|
|
9120
|
+
}
|
|
9121
|
+
if (config.auth) {
|
|
9122
|
+
lines.push("[auth]");
|
|
9123
|
+
if (config.auth.allowed_redirect_urls !== void 0) {
|
|
9124
|
+
const urls = config.auth.allowed_redirect_urls.map((u) => JSON.stringify(u)).join(", ");
|
|
9125
|
+
lines.push(`allowed_redirect_urls = [${urls}]`);
|
|
9126
|
+
}
|
|
9127
|
+
lines.push("");
|
|
9128
|
+
}
|
|
9129
|
+
return lines.join("\n").replace(/\n+$/, "\n");
|
|
9130
|
+
}
|
|
9131
|
+
|
|
9132
|
+
// src/commands/config/export.ts
|
|
9133
|
+
function registerConfigExportCommand(cfg) {
|
|
9134
|
+
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) => {
|
|
9135
|
+
const { json } = getRootOpts(cmd);
|
|
9136
|
+
try {
|
|
9137
|
+
await requireAuth();
|
|
9138
|
+
const target = resolve5(process.cwd(), opts.out);
|
|
9139
|
+
if (existsSync14(target) && !opts.force) {
|
|
9140
|
+
if (json) {
|
|
9141
|
+
throw new CLIError(
|
|
9142
|
+
`${opts.out} exists. Re-run with --force to overwrite.`,
|
|
9143
|
+
1,
|
|
9144
|
+
"OUTPUT_EXISTS"
|
|
9145
|
+
);
|
|
9146
|
+
}
|
|
9147
|
+
const ok = await p.confirm({
|
|
9148
|
+
message: `${opts.out} exists. Overwrite?`,
|
|
9149
|
+
initialValue: false
|
|
9150
|
+
});
|
|
9151
|
+
if (!ok || p.isCancel(ok)) {
|
|
9152
|
+
console.log("Aborted.");
|
|
9153
|
+
return;
|
|
9154
|
+
}
|
|
9155
|
+
}
|
|
9156
|
+
const res = await ossFetch("/api/metadata");
|
|
9157
|
+
const raw = await res.json();
|
|
9158
|
+
const config = {};
|
|
9159
|
+
const skipped = [];
|
|
9160
|
+
const authSlice = raw?.auth;
|
|
9161
|
+
if (authSlice && typeof authSlice === "object" && "allowedRedirectUrls" in authSlice) {
|
|
9162
|
+
config.auth = {
|
|
9163
|
+
allowed_redirect_urls: authSlice.allowedRedirectUrls ?? []
|
|
9164
|
+
};
|
|
9165
|
+
} else {
|
|
9166
|
+
skipped.push("auth.allowed_redirect_urls");
|
|
9167
|
+
}
|
|
9168
|
+
const toml = stringifyConfigToml(config);
|
|
9169
|
+
writeFileSync9(target, toml, "utf8");
|
|
9170
|
+
if (json) {
|
|
9171
|
+
console.log(JSON.stringify({ written: target, config, skipped }, null, 2));
|
|
9172
|
+
} else {
|
|
9173
|
+
console.log(`${pc4.green("\u2713")} Wrote ${target}`);
|
|
9174
|
+
if (skipped.length) {
|
|
9175
|
+
console.warn(
|
|
9176
|
+
pc4.yellow(
|
|
9177
|
+
`\u26A0 Skipped ${skipped.length} section(s) not supported by this backend:`
|
|
9178
|
+
) + "\n" + skipped.map((k) => ` - ${k}`).join("\n")
|
|
9179
|
+
);
|
|
9180
|
+
}
|
|
9181
|
+
}
|
|
9182
|
+
await reportCliUsage("cli.config.export", true);
|
|
9183
|
+
} catch (err) {
|
|
9184
|
+
await reportCliUsage("cli.config.export", false);
|
|
9185
|
+
handleError(err, json);
|
|
9186
|
+
}
|
|
9187
|
+
});
|
|
9188
|
+
}
|
|
9189
|
+
|
|
9190
|
+
// src/commands/config/plan.ts
|
|
9191
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
9192
|
+
import { resolve as resolve6 } from "path";
|
|
9193
|
+
import pc5 from "picocolors";
|
|
9194
|
+
|
|
9195
|
+
// src/lib/config-diff.ts
|
|
9196
|
+
function diffConfig({ live, file }) {
|
|
9197
|
+
const changes = [];
|
|
9198
|
+
const fileAuth = file.auth;
|
|
9199
|
+
const liveAuth = live.auth ?? {};
|
|
9200
|
+
if (fileAuth && "allowed_redirect_urls" in fileAuth) {
|
|
9201
|
+
const fromV = normalizeUrlList(liveAuth.allowed_redirect_urls);
|
|
9202
|
+
const toV = normalizeUrlList(fileAuth.allowed_redirect_urls);
|
|
9203
|
+
if (!arrayEquals(fromV, toV)) {
|
|
9204
|
+
changes.push({
|
|
9205
|
+
section: "auth",
|
|
9206
|
+
op: "modify",
|
|
9207
|
+
key: "allowed_redirect_urls",
|
|
9208
|
+
from: fromV,
|
|
9209
|
+
to: toV
|
|
9210
|
+
});
|
|
9211
|
+
}
|
|
9212
|
+
}
|
|
9213
|
+
return { changes, summary: summarize(changes) };
|
|
9214
|
+
}
|
|
9215
|
+
function summarize(changes) {
|
|
9216
|
+
const s = { add: 0, modify: 0, remove: 0, kept: 0 };
|
|
9217
|
+
for (const c of changes) {
|
|
9218
|
+
if (c.op === "modify") s.modify++;
|
|
9219
|
+
}
|
|
9220
|
+
return s;
|
|
9221
|
+
}
|
|
9222
|
+
function normalizeUrlList(input) {
|
|
9223
|
+
return Array.from(new Set(input ?? [])).sort();
|
|
9224
|
+
}
|
|
9225
|
+
function arrayEquals(a, b) {
|
|
9226
|
+
if (a.length !== b.length) return false;
|
|
9227
|
+
return a.every((v, i) => v === b[i]);
|
|
9228
|
+
}
|
|
9229
|
+
|
|
9230
|
+
// src/lib/config-format.ts
|
|
9231
|
+
function formatPlan(result) {
|
|
9232
|
+
if (result.changes.length === 0) {
|
|
9233
|
+
return "No changes. Live state matches insforge.toml.";
|
|
9234
|
+
}
|
|
9235
|
+
const bySection = /* @__PURE__ */ new Map();
|
|
9236
|
+
for (const c of result.changes) {
|
|
9237
|
+
const arr = bySection.get(c.section) ?? [];
|
|
9238
|
+
arr.push(c);
|
|
9239
|
+
bySection.set(c.section, arr);
|
|
9240
|
+
}
|
|
9241
|
+
const lines = [];
|
|
9242
|
+
for (const [section, changes] of bySection) {
|
|
9243
|
+
lines.push(` ${section}:`);
|
|
9244
|
+
for (const c of changes) {
|
|
9245
|
+
lines.push(` ${formatChange(c)}`);
|
|
9246
|
+
}
|
|
9247
|
+
lines.push("");
|
|
9248
|
+
}
|
|
9249
|
+
const s = result.summary;
|
|
9250
|
+
lines.push(
|
|
9251
|
+
`${s.add} add, ${s.modify} modify, ${s.remove} remove, ${s.kept} untracked kept.`
|
|
9252
|
+
);
|
|
9253
|
+
return lines.join("\n");
|
|
9254
|
+
}
|
|
9255
|
+
function formatChange(c) {
|
|
9256
|
+
return `~ ${c.key}: ${JSON.stringify(c.from)} \u2192 ${JSON.stringify(c.to)}`;
|
|
9257
|
+
}
|
|
9258
|
+
|
|
9259
|
+
// src/lib/config-capabilities.ts
|
|
9260
|
+
function metadataSupports(raw, change) {
|
|
9261
|
+
if (change.section === "auth" && change.key === "allowed_redirect_urls") {
|
|
9262
|
+
return raw?.auth !== void 0 && raw.auth !== null && typeof raw.auth === "object" && "allowedRedirectUrls" in raw.auth;
|
|
9263
|
+
}
|
|
9264
|
+
return false;
|
|
9265
|
+
}
|
|
9266
|
+
function changePath(change) {
|
|
9267
|
+
return `${change.section}.${change.key}`;
|
|
9268
|
+
}
|
|
9269
|
+
|
|
9270
|
+
// src/commands/config/plan.ts
|
|
9271
|
+
function registerConfigPlanCommand(cfg) {
|
|
9272
|
+
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) => {
|
|
9273
|
+
const { json } = getRootOpts(cmd);
|
|
9274
|
+
try {
|
|
9275
|
+
await requireAuth();
|
|
9276
|
+
const tomlPath = resolve6(process.cwd(), opts.file);
|
|
9277
|
+
const tomlSource = readFileSync11(tomlPath, "utf8");
|
|
9278
|
+
const file = parseConfigToml(tomlSource);
|
|
9279
|
+
const res = await ossFetch("/api/metadata");
|
|
9280
|
+
const raw = await res.json();
|
|
9281
|
+
const live = {
|
|
9282
|
+
auth: { allowed_redirect_urls: raw.auth?.allowedRedirectUrls ?? [] }
|
|
9283
|
+
};
|
|
9284
|
+
const result = diffConfig({ live, file });
|
|
9285
|
+
const skipped = result.changes.filter((c) => !metadataSupports(raw, c)).map((c) => changePath(c));
|
|
9286
|
+
if (json) {
|
|
9287
|
+
console.log(JSON.stringify({ ...result, skipped }, null, 2));
|
|
9288
|
+
} else {
|
|
9289
|
+
console.log(`Plan for insforge.toml (file: ${opts.file}):
|
|
9290
|
+
`);
|
|
9291
|
+
console.log(formatPlan(result));
|
|
9292
|
+
if (skipped.length) {
|
|
9293
|
+
console.warn(
|
|
9294
|
+
"\n" + pc5.yellow(`\u26A0 Apply will skip ${skipped.length} section(s) \u2014 backend doesn't support them yet:`) + "\n" + skipped.map((k) => ` - ${k}`).join("\n")
|
|
9295
|
+
);
|
|
9296
|
+
}
|
|
9297
|
+
}
|
|
9298
|
+
await reportCliUsage("cli.config.plan", true);
|
|
9299
|
+
} catch (err) {
|
|
9300
|
+
await reportCliUsage("cli.config.plan", false);
|
|
9301
|
+
handleError(err, json);
|
|
9302
|
+
}
|
|
9303
|
+
});
|
|
9304
|
+
}
|
|
9305
|
+
|
|
9306
|
+
// src/commands/config/apply.ts
|
|
9307
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
9308
|
+
import { resolve as resolve7 } from "path";
|
|
9309
|
+
import * as p2 from "@clack/prompts";
|
|
9310
|
+
import pc6 from "picocolors";
|
|
9311
|
+
function registerConfigApplyCommand(cfg) {
|
|
9312
|
+
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) => {
|
|
9313
|
+
const { json, yes } = getRootOpts(cmd);
|
|
9314
|
+
try {
|
|
9315
|
+
await requireAuth();
|
|
9316
|
+
const tomlPath = resolve7(process.cwd(), opts.file);
|
|
9317
|
+
const tomlSource = readFileSync12(tomlPath, "utf8");
|
|
9318
|
+
const file = parseConfigToml(tomlSource);
|
|
9319
|
+
const res = await ossFetch("/api/metadata");
|
|
9320
|
+
const raw = await res.json();
|
|
9321
|
+
const live = {
|
|
9322
|
+
auth: { allowed_redirect_urls: raw.auth?.allowedRedirectUrls ?? [] }
|
|
9323
|
+
};
|
|
9324
|
+
const result = diffConfig({ live, file });
|
|
9325
|
+
const approved = opts.autoApprove || yes;
|
|
9326
|
+
if (!json) {
|
|
9327
|
+
console.log(formatPlan(result));
|
|
9328
|
+
}
|
|
9329
|
+
if (result.changes.length === 0 || opts.dryRun) {
|
|
9330
|
+
if (json) {
|
|
9331
|
+
console.log(
|
|
9332
|
+
JSON.stringify({ plan: result, applied: false, dryRun: !!opts.dryRun }, null, 2)
|
|
9333
|
+
);
|
|
9334
|
+
}
|
|
9335
|
+
await reportCliUsage("cli.config.apply", true);
|
|
9336
|
+
return;
|
|
9337
|
+
}
|
|
9338
|
+
if (!approved) {
|
|
9339
|
+
if (json) {
|
|
9340
|
+
throw new CLIError(
|
|
9341
|
+
"Refusing to apply in --json mode without --auto-approve or --yes.",
|
|
9342
|
+
1,
|
|
9343
|
+
"CONFIRMATION_REQUIRED"
|
|
9344
|
+
);
|
|
9345
|
+
}
|
|
9346
|
+
const ok = await p2.confirm({
|
|
9347
|
+
message: "Apply these changes?",
|
|
9348
|
+
initialValue: false
|
|
9349
|
+
});
|
|
9350
|
+
if (!ok || p2.isCancel(ok)) {
|
|
9351
|
+
console.log("Aborted.");
|
|
9352
|
+
await reportCliUsage("cli.config.apply", true);
|
|
9353
|
+
return;
|
|
9354
|
+
}
|
|
9355
|
+
}
|
|
9356
|
+
const applied = [];
|
|
9357
|
+
const skipped = [];
|
|
9358
|
+
for (const change of result.changes) {
|
|
9359
|
+
const path6 = changePath(change);
|
|
9360
|
+
if (!metadataSupports(raw, change)) {
|
|
9361
|
+
skipped.push({
|
|
9362
|
+
key: path6,
|
|
9363
|
+
reason: `your backend doesn't expose ${path6} \u2014 upgrade the project to apply this section`
|
|
9364
|
+
});
|
|
9365
|
+
continue;
|
|
9366
|
+
}
|
|
9367
|
+
await applyChange(change);
|
|
9368
|
+
applied.push(change);
|
|
9369
|
+
}
|
|
9370
|
+
if (json) {
|
|
9371
|
+
console.log(
|
|
9372
|
+
JSON.stringify({ plan: result, applied, skipped }, null, 2)
|
|
9373
|
+
);
|
|
9374
|
+
} else {
|
|
9375
|
+
if (skipped.length) {
|
|
9376
|
+
console.warn(
|
|
9377
|
+
pc6.yellow(`\u26A0 Skipped ${skipped.length} section(s):`) + "\n" + skipped.map((s) => ` - ${s.key}: ${s.reason}`).join("\n")
|
|
9378
|
+
);
|
|
9379
|
+
}
|
|
9380
|
+
if (applied.length) {
|
|
9381
|
+
console.log(
|
|
9382
|
+
`${pc6.green("\u2713")} Applied ${applied.length} of ${result.changes.length} change(s).`
|
|
9383
|
+
);
|
|
9384
|
+
} else {
|
|
9385
|
+
console.log("Nothing applied.");
|
|
9386
|
+
}
|
|
9387
|
+
}
|
|
9388
|
+
await reportCliUsage("cli.config.apply", true);
|
|
9389
|
+
} catch (err) {
|
|
9390
|
+
await reportCliUsage("cli.config.apply", false);
|
|
9391
|
+
handleError(err, json);
|
|
9392
|
+
}
|
|
9393
|
+
});
|
|
9394
|
+
}
|
|
9395
|
+
async function applyChange(change) {
|
|
9396
|
+
if (change.section === "auth" && change.key === "allowed_redirect_urls") {
|
|
9397
|
+
await ossFetch("/api/auth/config", {
|
|
9398
|
+
method: "PUT",
|
|
9399
|
+
body: JSON.stringify({ allowedRedirectUrls: change.to })
|
|
9400
|
+
});
|
|
9401
|
+
return;
|
|
9402
|
+
}
|
|
9403
|
+
throw new Error(`Unsupported change type: ${change.section}.${change.key}`);
|
|
9404
|
+
}
|
|
9405
|
+
|
|
9406
|
+
// src/commands/config/index.ts
|
|
9407
|
+
function registerConfigCommand(program2) {
|
|
9408
|
+
const cfg = program2.command("config").description("Manage insforge.toml (declarative project configuration)");
|
|
9409
|
+
registerConfigExportCommand(cfg);
|
|
9410
|
+
registerConfigPlanCommand(cfg);
|
|
9411
|
+
registerConfigApplyCommand(cfg);
|
|
9032
9412
|
}
|
|
9033
9413
|
|
|
9034
9414
|
// src/index.ts
|
|
9035
9415
|
var __dirname = dirname3(fileURLToPath(import.meta.url));
|
|
9036
|
-
var pkg = JSON.parse(
|
|
9416
|
+
var pkg = JSON.parse(readFileSync13(join17(__dirname, "../package.json"), "utf-8"));
|
|
9037
9417
|
var INSFORGE_LOGO = `
|
|
9038
9418
|
\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
|
|
9039
9419
|
\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
|
|
@@ -9124,6 +9504,7 @@ registerSchedulesCreateCommand(schedulesCmd);
|
|
|
9124
9504
|
registerSchedulesUpdateCommand(schedulesCmd);
|
|
9125
9505
|
registerSchedulesDeleteCommand(schedulesCmd);
|
|
9126
9506
|
registerSchedulesLogsCommand(schedulesCmd);
|
|
9507
|
+
registerConfigCommand(program);
|
|
9127
9508
|
if (process.argv.length <= 2 && process.stdout.isTTY) {
|
|
9128
9509
|
await showInteractiveMenu();
|
|
9129
9510
|
} else {
|