create-op-node 0.10.3 → 0.10.5
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/cli.js +59 -19
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -28,7 +28,10 @@ async function get(token, path, fetchImpl = fetch) {
|
|
|
28
28
|
function shortError(body) {
|
|
29
29
|
if (typeof body === "object" && body !== null && "errors" in body && Array.isArray(body.errors)) {
|
|
30
30
|
const errs = body.errors;
|
|
31
|
-
return errs.map((e) =>
|
|
31
|
+
return errs.map((e) => {
|
|
32
|
+
const prefix = e.code ? `CF ${e.code}: ` : "";
|
|
33
|
+
return `${prefix}${e.message ?? "unknown"}`;
|
|
34
|
+
}).join("; ");
|
|
32
35
|
}
|
|
33
36
|
return "no error message returned";
|
|
34
37
|
}
|
|
@@ -484,7 +487,7 @@ function generatePgsodiumRootKey() {
|
|
|
484
487
|
}
|
|
485
488
|
function base64url(input) {
|
|
486
489
|
const buf = typeof input === "string" ? Buffer.from(input) : input;
|
|
487
|
-
return buf.toString("base64").replace(
|
|
490
|
+
return buf.toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
488
491
|
}
|
|
489
492
|
function generatePostgresPassword() {
|
|
490
493
|
return base64url(randomBytes(32));
|
|
@@ -710,12 +713,13 @@ var initCommand = new Command("init").description(
|
|
|
710
713
|
const kcNote = keychain.available ? `\u2713 macOS Keychain available \u2014 pgsodium key + Tunnel token will be saved to your login keychain.
|
|
711
714
|
` + pc2.dim("Note: items don't sync to iCloud Keychain via the security CLI.\n") + pc2.dim("On the Studio, bootstrap will read locally and prompt you to paste if missing.") : `\u26A0 ${keychain.reason ?? "Keychain unavailable"}. Secrets will be printed for manual stash.`;
|
|
712
715
|
p3.note(kcNote, "Secret store");
|
|
716
|
+
const orgWriteAccess = pc2.dim(`required for ${owner}`);
|
|
713
717
|
p3.note(
|
|
714
718
|
opts.localOnly ? [
|
|
715
719
|
`Region label: ${pc2.cyan(region)}`,
|
|
716
720
|
`New node repo: ${pc2.cyan(newRepoFull)}`,
|
|
717
721
|
`Template: ${pc2.dim(opts.template ?? "OpusPopuli/opuspopuli-node")}`,
|
|
718
|
-
`GH org write access: ${
|
|
722
|
+
`GH org write access: ${orgWriteAccess}`,
|
|
719
723
|
``,
|
|
720
724
|
`Will create the repo from template (private, no Cloudflare),`,
|
|
721
725
|
`and store a fresh pgsodium key in Keychain. That's it \u2014 no PR,`,
|
|
@@ -727,7 +731,7 @@ var initCommand = new Command("init").description(
|
|
|
727
731
|
`New node repo: ${pc2.cyan(newRepoFull)}`,
|
|
728
732
|
`Template: ${pc2.dim(opts.template ?? "OpusPopuli/opuspopuli-node")}`,
|
|
729
733
|
`TFC organization: ${pc2.cyan(publicConfig?.tfOrg ?? "")}`,
|
|
730
|
-
`GH org write access: ${
|
|
734
|
+
`GH org write access: ${orgWriteAccess}`,
|
|
731
735
|
``,
|
|
732
736
|
`Will create the repo, seed 5 secrets, write prod.tfvars on a branch,`,
|
|
733
737
|
`open a PR, generate a fresh pgsodium key, and (after you merge) wait`,
|
|
@@ -1010,7 +1014,10 @@ async function collectPublicConfig(opts) {
|
|
|
1010
1014
|
await p3.text({
|
|
1011
1015
|
message: "Domain registered in Cloudflare?",
|
|
1012
1016
|
placeholder: "example.org",
|
|
1013
|
-
validate: (v) =>
|
|
1017
|
+
validate: (v) => {
|
|
1018
|
+
if (!v) return "Required";
|
|
1019
|
+
return v.includes(".") ? void 0 : "Missing a TLD?";
|
|
1020
|
+
}
|
|
1014
1021
|
})
|
|
1015
1022
|
);
|
|
1016
1023
|
const cfToken = await collectSecret(
|
|
@@ -1054,9 +1061,10 @@ async function collectPublicConfig(opts) {
|
|
|
1054
1061
|
p3.cancel("Fix the token / org, then re-run.");
|
|
1055
1062
|
process.exit(1);
|
|
1056
1063
|
}
|
|
1064
|
+
const asUser = tfProbe.userName ? ` (as ${tfProbe.userName})` : "";
|
|
1057
1065
|
tfSpin.stop(
|
|
1058
1066
|
pc2.green(
|
|
1059
|
-
`\u2713 TFC token valid${
|
|
1067
|
+
`\u2713 TFC token valid${asUser}, org "${tfOrg}" reachable.`
|
|
1060
1068
|
)
|
|
1061
1069
|
);
|
|
1062
1070
|
return { domain, cfToken, cfAccount, cfZone, tfToken, tfOrg };
|
|
@@ -2297,7 +2305,10 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2297
2305
|
validate: (v) => URL_SAFE_PASSWORD_RE.test(v) ? void 0 : "must be base64url chars only (no + / =)"
|
|
2298
2306
|
});
|
|
2299
2307
|
}
|
|
2300
|
-
const promptServiceUrl = nodeType === "region-with-prompts" ?
|
|
2308
|
+
const promptServiceUrl = nodeType === "region-with-prompts" ? (
|
|
2309
|
+
// eslint-disable-next-line sonarjs/no-clear-text-protocols -- in-network (docker) prompt-service URL is legitimately plaintext http; no TLS on the internal bridge
|
|
2310
|
+
"http://opuspopuli-prompts:3210"
|
|
2311
|
+
) : opts.promptServiceUrl ?? "https://prompts.opuspopuli.org";
|
|
2301
2312
|
if (!SAFE_URL_RE.test(promptServiceUrl)) {
|
|
2302
2313
|
p3.cancel(
|
|
2303
2314
|
`--prompt-service-url ${JSON.stringify(promptServiceUrl)} contains characters outside the allowed URL set`
|
|
@@ -2314,7 +2325,10 @@ var bootstrapCommand = new Command("bootstrap").description(
|
|
|
2314
2325
|
await p3.text({
|
|
2315
2326
|
message: "Public-facing Supabase URL (what browsers + microservices use to reach kong)?",
|
|
2316
2327
|
placeholder: "https://supabase.civicfeed.tx",
|
|
2317
|
-
validate: (v) =>
|
|
2328
|
+
validate: (v) => {
|
|
2329
|
+
if (!v) return "required";
|
|
2330
|
+
return SAFE_URL_RE.test(v) ? void 0 : "contains characters outside the allowed URL set";
|
|
2331
|
+
}
|
|
2318
2332
|
})
|
|
2319
2333
|
);
|
|
2320
2334
|
}
|
|
@@ -2587,8 +2601,8 @@ async function selectLlmModel(opts) {
|
|
|
2587
2601
|
);
|
|
2588
2602
|
}
|
|
2589
2603
|
function estimatedPullTime(model) {
|
|
2590
|
-
if (/\d
|
|
2591
|
-
const match = /(\d
|
|
2604
|
+
if (/\d{1,3}x\d{1,3}b/i.test(model)) return "~60+ min for frontier MoE";
|
|
2605
|
+
const match = /(\d{1,4})b\b/i.exec(model);
|
|
2592
2606
|
if (!match) return "time depends on model size";
|
|
2593
2607
|
const size = Number.parseInt(match[1], 10);
|
|
2594
2608
|
if (size <= 13) return "~3\u20135 min for \u2264 13B-class";
|
|
@@ -2940,11 +2954,19 @@ var resetCommand = new Command("reset").description(
|
|
|
2940
2954
|
...opts.envFile ? { envFile: opts.envFile } : {}
|
|
2941
2955
|
});
|
|
2942
2956
|
}
|
|
2957
|
+
let runningContainersLabel;
|
|
2958
|
+
if (runningContainers === null) {
|
|
2959
|
+
runningContainersLabel = pc2.dim("unknown");
|
|
2960
|
+
} else if (runningContainers.length === 0) {
|
|
2961
|
+
runningContainersLabel = pc2.dim("none");
|
|
2962
|
+
} else {
|
|
2963
|
+
runningContainersLabel = pc2.cyan(`${runningContainers.length} listed by compose ps`);
|
|
2964
|
+
}
|
|
2943
2965
|
p3.note(
|
|
2944
2966
|
[
|
|
2945
2967
|
`Region: ${pc2.cyan(region)}`,
|
|
2946
2968
|
`Repo path: ${repoPath ? pc2.cyan(repoPath) : pc2.dim(`not used (${stackSkipReason ?? "no repo path resolved"})`)}`,
|
|
2947
|
-
`Running containers: ${
|
|
2969
|
+
`Running containers: ${runningContainersLabel}`,
|
|
2948
2970
|
`LaunchAgent plist: ${plistExists ? pc2.cyan(launchAgentPaths.plistFile) : pc2.dim("not present")}`,
|
|
2949
2971
|
`pgsodium key file: ${keyFileExists ? pc2.cyan(launchAgentPaths.keyFile) : pc2.dim("not present")}`,
|
|
2950
2972
|
`Registry to log out: ${pc2.cyan(opts.registry ?? GHCR_REGISTRY)}`,
|
|
@@ -3400,7 +3422,7 @@ var verifyCommand = new Command("verify").description(
|
|
|
3400
3422
|
);
|
|
3401
3423
|
activeSpin?.stop("Done.");
|
|
3402
3424
|
const visible = opts.showSkipped ?? false ? report.phases : report.phases.filter((ph) => ph.status !== "skipped");
|
|
3403
|
-
const lines = visible.map((ph
|
|
3425
|
+
const lines = visible.map((ph) => {
|
|
3404
3426
|
const idx = report.phases.indexOf(ph) + 1;
|
|
3405
3427
|
return `${phaseIcon2(ph)} [${idx}/${totalPhases}] ${ph.name}: ${pc2.dim(ph.detail)}`;
|
|
3406
3428
|
});
|
|
@@ -3411,9 +3433,11 @@ var verifyCommand = new Command("verify").description(
|
|
|
3411
3433
|
p3.note(lines.join("\n"), "Summary");
|
|
3412
3434
|
const summary = summarize(report);
|
|
3413
3435
|
if (summary.ok) {
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3436
|
+
if (summary.warned === 0) {
|
|
3437
|
+
p3.outro(pc2.green("All checks passed."));
|
|
3438
|
+
} else {
|
|
3439
|
+
p3.outro(pc2.yellow(`Passed with ${summary.warned} warning${summary.warned === 1 ? "" : "s"}.`));
|
|
3440
|
+
}
|
|
3417
3441
|
} else {
|
|
3418
3442
|
p3.outro(pc2.red(`${summary.failed} check${summary.failed === 1 ? "" : "s"} failed.`));
|
|
3419
3443
|
process.exit(1);
|
|
@@ -3431,9 +3455,21 @@ function phaseIcon2(ph) {
|
|
|
3431
3455
|
return pc2.red("\u2717");
|
|
3432
3456
|
}
|
|
3433
3457
|
}
|
|
3458
|
+
function phaseColor(status) {
|
|
3459
|
+
switch (status) {
|
|
3460
|
+
case "ok":
|
|
3461
|
+
return pc2.green;
|
|
3462
|
+
case "warn":
|
|
3463
|
+
return pc2.yellow;
|
|
3464
|
+
case "skipped":
|
|
3465
|
+
return pc2.dim;
|
|
3466
|
+
case "fail":
|
|
3467
|
+
return pc2.red;
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3434
3470
|
function formatPhaseLine(prefix, ph) {
|
|
3435
3471
|
const icon = phaseIcon2(ph);
|
|
3436
|
-
const colorize = ph.status
|
|
3472
|
+
const colorize = phaseColor(ph.status);
|
|
3437
3473
|
return colorize(`${icon} ${prefix}: ${ph.detail}`);
|
|
3438
3474
|
}
|
|
3439
3475
|
|
|
@@ -4096,7 +4132,7 @@ var SOURCE_TYPES = [
|
|
|
4096
4132
|
var SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
4097
4133
|
var STATE_CODE_RE = /^[A-Z]{2}$/;
|
|
4098
4134
|
function slugify(input) {
|
|
4099
|
-
return input.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(
|
|
4135
|
+
return input.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
4100
4136
|
}
|
|
4101
4137
|
function isValidSlug(v) {
|
|
4102
4138
|
return SLUG_RE.test(v);
|
|
@@ -4318,7 +4354,11 @@ var regionCommand = new Command("region").description(
|
|
|
4318
4354
|
await p3.text({
|
|
4319
4355
|
message: level === "county" ? "County FIPS (5 digits)?" : "State FIPS (2 digits)?",
|
|
4320
4356
|
placeholder: level === "county" ? "06001" : "06",
|
|
4321
|
-
validate: (v) =>
|
|
4357
|
+
validate: (v) => {
|
|
4358
|
+
if (isValidFips(v ?? "", level)) return void 0;
|
|
4359
|
+
const expectedDigits = level === "county" ? "5" : "2";
|
|
4360
|
+
return `Expected ${expectedDigits} digits`;
|
|
4361
|
+
}
|
|
4322
4362
|
})
|
|
4323
4363
|
);
|
|
4324
4364
|
if (!isValidFips(fipsCode, level)) {
|
|
@@ -4452,7 +4492,7 @@ async function looksLikeRegionsRepo(dir) {
|
|
|
4452
4492
|
}
|
|
4453
4493
|
|
|
4454
4494
|
// src/cli.ts
|
|
4455
|
-
var VERSION = "0.10.
|
|
4495
|
+
var VERSION = "0.10.5";
|
|
4456
4496
|
var program = new Command();
|
|
4457
4497
|
program.name("create-op-node").description(
|
|
4458
4498
|
"Interactive bootstrap for an Opus Populi federation node.\nCloudflare infrastructure \u2192 Mac Studio \u2192 live public API."
|