create-op-node 0.7.0 → 0.8.0
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 +5 -0
- package/dist/cli.js +43 -7
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -202,6 +202,11 @@ npx create-op-node reset --region us-ca --dry-run
|
|
|
202
202
|
|
|
203
203
|
# Nuke from orbit: containers + volumes + LaunchAgent + ghcr credentials.
|
|
204
204
|
npx create-op-node reset --region us-ca --wipe-data
|
|
205
|
+
|
|
206
|
+
# Even more: ALSO drop all docker images (forces re-pull on next bootstrap).
|
|
207
|
+
# Use this for the "wipe everything and start over" iteration loop while
|
|
208
|
+
# debugging a fresh bootstrap. Implies --wipe-data.
|
|
209
|
+
npx create-op-node reset --region us-ca --wipe-images
|
|
205
210
|
```
|
|
206
211
|
|
|
207
212
|
## Verifying a live node
|
package/dist/cli.js
CHANGED
|
@@ -1151,6 +1151,13 @@ async function enableAutoRestartOnPowerFailure() {
|
|
|
1151
1151
|
async function disableDiskSleep() {
|
|
1152
1152
|
return runSudoPmset(["disksleep", "0"]);
|
|
1153
1153
|
}
|
|
1154
|
+
async function detectUnifiedMemoryGB() {
|
|
1155
|
+
const res = await safeExeca("sysctl", ["-n", "hw.memsize"]);
|
|
1156
|
+
if (res === null || res.exitCode !== 0) return null;
|
|
1157
|
+
const bytes = Number.parseInt(res.stdout.trim(), 10);
|
|
1158
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return null;
|
|
1159
|
+
return Math.round(bytes / 2 ** 30);
|
|
1160
|
+
}
|
|
1154
1161
|
async function runSudoPmset(args) {
|
|
1155
1162
|
const res = await safeExeca("sudo", ["pmset", "-a", ...args]);
|
|
1156
1163
|
if (res === null) {
|
|
@@ -1372,8 +1379,14 @@ async function composeDown(opts) {
|
|
|
1372
1379
|
const flags = ["down"];
|
|
1373
1380
|
if (opts.wipeVolumes) flags.push("-v");
|
|
1374
1381
|
if (opts.removeOrphans) flags.push("--remove-orphans");
|
|
1382
|
+
if (opts.removeImages) flags.push("--rmi", opts.removeImages);
|
|
1383
|
+
const label = [
|
|
1384
|
+
"compose down",
|
|
1385
|
+
opts.wipeVolumes ? "-v" : "",
|
|
1386
|
+
opts.removeImages ? `--rmi ${opts.removeImages}` : ""
|
|
1387
|
+
].filter(Boolean).join(" ");
|
|
1375
1388
|
const res = await safeExeca("docker", composeArgs(opts, flags));
|
|
1376
|
-
return result(res,
|
|
1389
|
+
return result(res, label);
|
|
1377
1390
|
}
|
|
1378
1391
|
async function dockerLogout(registry = GHCR_REGISTRY) {
|
|
1379
1392
|
const res = await safeExeca("docker", ["logout", registry]);
|
|
@@ -2023,12 +2036,22 @@ var LLM_MODEL_CHOICES = [
|
|
|
2023
2036
|
}
|
|
2024
2037
|
];
|
|
2025
2038
|
var OTHER_SENTINEL = "__OTHER__";
|
|
2039
|
+
function recommendLlmModel(ramGB) {
|
|
2040
|
+
if (ramGB === null) return null;
|
|
2041
|
+
if (ramGB >= 96) return "qwen2.5:72b";
|
|
2042
|
+
if (ramGB >= 48) return "qwen2.5:32b";
|
|
2043
|
+
return "qwen3.5:9b";
|
|
2044
|
+
}
|
|
2026
2045
|
async function selectLlmModel(opts) {
|
|
2027
2046
|
if (opts.yes) return DEFAULT_LLM_MODEL;
|
|
2047
|
+
const ramGB = await detectUnifiedMemoryGB();
|
|
2048
|
+
const recommended = recommendLlmModel(ramGB) ?? "qwen2.5:72b";
|
|
2049
|
+
const ramNote = ramGB !== null ? `Detected ${ramGB} GB unified memory \u2014 pre-selecting ${recommended}.` : `Couldn't detect Studio memory \u2014 defaulting to ${recommended} (override if your config differs).`;
|
|
2050
|
+
p3.note(ramNote, "Hardware");
|
|
2028
2051
|
const choice = unwrap(
|
|
2029
2052
|
await p3.select({
|
|
2030
2053
|
message: "Choose the LLM model to pull + run",
|
|
2031
|
-
initialValue:
|
|
2054
|
+
initialValue: recommended,
|
|
2032
2055
|
options: [
|
|
2033
2056
|
...LLM_MODEL_CHOICES.map((c) => ({ value: c.value, label: c.label, hint: c.hint })),
|
|
2034
2057
|
{
|
|
@@ -2220,7 +2243,7 @@ async function runReset(input, deps = DEFAULT_DEPS) {
|
|
|
2220
2243
|
push({
|
|
2221
2244
|
name: RESET_PHASES.STOP_STACK,
|
|
2222
2245
|
status: "dry-run",
|
|
2223
|
-
detail: `would run: docker compose -f ${input.stack.composeFiles.join(" -f ")} down${input.stack.wipeVolumes ? " -v" : ""}${input.stack.removeOrphans ? " --remove-orphans" : ""}`
|
|
2246
|
+
detail: `would run: docker compose -f ${input.stack.composeFiles.join(" -f ")} down${input.stack.wipeVolumes ? " -v" : ""}${input.stack.removeOrphans ? " --remove-orphans" : ""}${input.stack.wipeImages ? " --rmi all" : ""}`
|
|
2224
2247
|
});
|
|
2225
2248
|
} else {
|
|
2226
2249
|
const result2 = await deps.composeDown({
|
|
@@ -2228,13 +2251,18 @@ async function runReset(input, deps = DEFAULT_DEPS) {
|
|
|
2228
2251
|
cwd: input.stack.repoPath,
|
|
2229
2252
|
...input.stack.envFile ? { envFile: input.stack.envFile } : {},
|
|
2230
2253
|
wipeVolumes: input.stack.wipeVolumes,
|
|
2231
|
-
removeOrphans: input.stack.removeOrphans
|
|
2254
|
+
removeOrphans: input.stack.removeOrphans,
|
|
2255
|
+
...input.stack.wipeImages ? { removeImages: "all" } : {}
|
|
2232
2256
|
});
|
|
2233
2257
|
if (result2.ok) {
|
|
2258
|
+
const bits = [
|
|
2259
|
+
input.stack.wipeVolumes ? "volumes destroyed" : "containers stopped, volumes preserved",
|
|
2260
|
+
input.stack.wipeImages ? "images removed (next bootstrap will re-pull)" : null
|
|
2261
|
+
].filter(Boolean);
|
|
2234
2262
|
push({
|
|
2235
2263
|
name: RESET_PHASES.STOP_STACK,
|
|
2236
2264
|
status: "ok",
|
|
2237
|
-
detail:
|
|
2265
|
+
detail: bits.join("; ")
|
|
2238
2266
|
});
|
|
2239
2267
|
} else {
|
|
2240
2268
|
push({ name: RESET_PHASES.STOP_STACK, status: "fail", detail: result2.reason ?? "unknown failure" });
|
|
@@ -2321,6 +2349,11 @@ var resetCommand = new Command("reset").description(
|
|
|
2321
2349
|
"--wipe-data",
|
|
2322
2350
|
"DESTROYS docker volumes (adds -v to `compose down`). Requires retyping the region label as confirmation. Default off \u2014 volumes preserved."
|
|
2323
2351
|
).default(false)
|
|
2352
|
+
).addOption(
|
|
2353
|
+
new Option(
|
|
2354
|
+
"--wipe-images",
|
|
2355
|
+
'ALSO remove all docker images referenced by the compose file (adds --rmi all). Forces a fresh pull on next bootstrap. Implies --wipe-data semantics for the iteration loop ("wipe everything and start over"). Default off.'
|
|
2356
|
+
).default(false)
|
|
2324
2357
|
).addOption(
|
|
2325
2358
|
new Option(
|
|
2326
2359
|
"--no-remove-orphans",
|
|
@@ -2333,7 +2366,8 @@ var resetCommand = new Command("reset").description(
|
|
|
2333
2366
|
).default(false)
|
|
2334
2367
|
).addOption(new Option("--skip-docker-logout", "Don't run `docker logout`").default(false)).addOption(new Option("--registry <registry>", "Registry to log out of").default(GHCR_REGISTRY)).addOption(new Option("--dry-run", "Show what would happen without acting").default(false)).addOption(new Option("-y, --yes", "Skip non-destructive confirmations (wipe still confirms)").default(false)).action(async (opts) => {
|
|
2335
2368
|
p3.intro(pc2.bgCyan(pc2.black(" create-op-node reset ")));
|
|
2336
|
-
const
|
|
2369
|
+
const wipeImages = opts.wipeImages ?? false;
|
|
2370
|
+
const wipeData = (opts.wipeData ?? false) || wipeImages;
|
|
2337
2371
|
const removeOrphans = opts.removeOrphans ?? true;
|
|
2338
2372
|
const region = opts.region ? opts.region : unwrap(
|
|
2339
2373
|
await p3.text({
|
|
@@ -2399,6 +2433,7 @@ var resetCommand = new Command("reset").description(
|
|
|
2399
2433
|
`pgsodium key file: ${keyFileExists ? pc2.cyan(launchAgentPaths.keyFile) : pc2.dim("not present")}`,
|
|
2400
2434
|
`Registry to log out: ${pc2.cyan(opts.registry ?? GHCR_REGISTRY)}`,
|
|
2401
2435
|
`Volume policy: ${wipeData ? pc2.red("WIPE") : pc2.green("preserve (default)")}`,
|
|
2436
|
+
`Image policy: ${wipeImages ? pc2.red("WIPE (re-pull on next bootstrap)") : pc2.green("keep (default)")}`,
|
|
2402
2437
|
`Dry run: ${opts.dryRun ? pc2.yellow("yes") : pc2.dim("no")}`
|
|
2403
2438
|
].join("\n"),
|
|
2404
2439
|
"Snapshot"
|
|
@@ -2426,6 +2461,7 @@ var resetCommand = new Command("reset").description(
|
|
|
2426
2461
|
repoPath,
|
|
2427
2462
|
composeFiles: resolveComposeFiles(repoPath, opts.composeFile),
|
|
2428
2463
|
wipeVolumes: wipeData,
|
|
2464
|
+
wipeImages,
|
|
2429
2465
|
removeOrphans,
|
|
2430
2466
|
...opts.envFile ? { envFile: opts.envFile } : {}
|
|
2431
2467
|
} : void 0;
|
|
@@ -3900,7 +3936,7 @@ async function looksLikeRegionsRepo(dir) {
|
|
|
3900
3936
|
}
|
|
3901
3937
|
|
|
3902
3938
|
// src/cli.ts
|
|
3903
|
-
var VERSION = "0.
|
|
3939
|
+
var VERSION = "0.8.0";
|
|
3904
3940
|
var program = new Command();
|
|
3905
3941
|
program.name("create-op-node").description(
|
|
3906
3942
|
"Interactive bootstrap for an Opus Populi federation node.\nCloudflare infrastructure \u2192 Mac Studio \u2192 live public API."
|