@gorlitzer-labs/seldon 0.1.7 → 0.1.8
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/bin/seldon.js +95 -1
- package/package.json +1 -1
package/bin/seldon.js
CHANGED
|
@@ -194,6 +194,90 @@ function doInstall(ids, { dev } = {}) {
|
|
|
194
194
|
console.log("\n" + C.bold("Done. ") + C.green(good.join(", ") || "—") + (bad.length ? " " + C.red("failed: " + bad.join(", ")) : ""));
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
// ---- uninstall --------------------------------------------------------------
|
|
198
|
+
// Remove a node CLI from the global store. `rm -g` is the same verb for pnpm,
|
|
199
|
+
// npm and bun; try the chosen PM first, then any other present (a tool may have
|
|
200
|
+
// been installed under a different one). Removing an absent package is success.
|
|
201
|
+
function uninstallNpm(m) {
|
|
202
|
+
const pm = nodePM();
|
|
203
|
+
const tried = new Set();
|
|
204
|
+
for (const cand of [pm, "pnpm", "npm", "bun"]) {
|
|
205
|
+
if (tried.has(cand) || !has(cand)) continue;
|
|
206
|
+
tried.add(cand);
|
|
207
|
+
if (run(cand, ["rm", "-g", m.pkg])) return true;
|
|
208
|
+
}
|
|
209
|
+
console.log(C.dim(` ${m.id} not found in any global store (already gone)`));
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// demerzel & co: the isolated venv lives under ~/.seldon/<id>. Just delete it.
|
|
214
|
+
function uninstallPython(m) {
|
|
215
|
+
const dest = path.join(process.env.HOME, ".seldon", m.id);
|
|
216
|
+
if (fs.existsSync(dest)) { fs.rmSync(dest, { recursive: true, force: true }); console.log(C.dim(` removed ${dest}`)); }
|
|
217
|
+
else console.log(C.dim(` ${m.id} not present`));
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// bifrost: a single script in ~/bin or ~/.local/bin, plus ~/.config/bifrost.
|
|
222
|
+
function uninstallShell(m) {
|
|
223
|
+
let removed = false;
|
|
224
|
+
for (const p of [path.join(process.env.HOME, "bin", m.bin), path.join(process.env.HOME, ".local", "bin", m.bin)]) {
|
|
225
|
+
if (fs.existsSync(p)) { fs.rmSync(p, { force: true }); console.log(C.dim(` removed ${p}`)); removed = true; }
|
|
226
|
+
}
|
|
227
|
+
const cfg = path.join(process.env.HOME, ".config", m.id);
|
|
228
|
+
if (fs.existsSync(cfg)) { fs.rmSync(cfg, { recursive: true, force: true }); console.log(C.dim(` removed ${cfg}`)); removed = true; }
|
|
229
|
+
if (!removed) console.log(C.dim(` ${m.id} not present`));
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function uninstallOne(m) {
|
|
234
|
+
console.log("\n" + C.bold(`▸ ${m.id}`) + C.dim(` — remove`));
|
|
235
|
+
const fn = { npm: uninstallNpm, shell: uninstallShell, python: uninstallPython }[m.method];
|
|
236
|
+
const ok = fn(m);
|
|
237
|
+
console.log(ok ? C.green(` ✓ ${m.id} removed`) : C.red(` ✗ ${m.id} failed`));
|
|
238
|
+
return ok;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// A single y/N prompt. Built on the same raw-keypress mechanism as the picker
|
|
242
|
+
// (proven to release stdin and let the process exit) rather than
|
|
243
|
+
// readline.createInterface, whose stdin stayed entangled and hung after abort.
|
|
244
|
+
// One keypress: 'y' = yes, anything else (incl. Ctrl-C) = no.
|
|
245
|
+
function confirm(question) {
|
|
246
|
+
return new Promise((resolve) => {
|
|
247
|
+
if (!process.stdin.isTTY) return resolve(true); // non-interactive: assume yes (paired with an explicit ids/--all)
|
|
248
|
+
process.stdout.write(question);
|
|
249
|
+
readline.emitKeypressEvents(process.stdin);
|
|
250
|
+
process.stdin.setRawMode(true);
|
|
251
|
+
process.stdin.resume();
|
|
252
|
+
const onKey = (str, key) => {
|
|
253
|
+
process.stdin.off("keypress", onKey);
|
|
254
|
+
process.stdin.setRawMode(false);
|
|
255
|
+
process.stdin.pause();
|
|
256
|
+
process.stdin.unref();
|
|
257
|
+
process.stdout.write((str || "") + "\n");
|
|
258
|
+
resolve(/^y$/i.test(str || "") && !(key && key.ctrl));
|
|
259
|
+
};
|
|
260
|
+
process.stdin.on("keypress", onKey);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function doUninstall(ids, { yes = false, all = false } = {}) {
|
|
265
|
+
const targets = all ? MODULES.map((m) => m.id) : ids;
|
|
266
|
+
if (!targets.length) { console.log(C.red("nothing to uninstall — name modules or use `seldon uninstall --all`.")); process.exitCode = 1; return; }
|
|
267
|
+
console.log(C.bold("\nUninstall: ") + targets.join(", "));
|
|
268
|
+
console.log(C.dim(" removes their global CLI / isolated venv — never your system Python, tmux, tailscale, etc."));
|
|
269
|
+
if (!yes && !(await confirm(C.gold("\nProceed? [y/N] ")))) { console.log(C.dim("aborted.")); return; }
|
|
270
|
+
const results = targets.map((id) => [id, uninstallOne(byId[id])]);
|
|
271
|
+
// A full uninstall also drops the isolated ~/.seldon (demerzel + the uv/CPython it bootstrapped).
|
|
272
|
+
if (all) {
|
|
273
|
+
const root = path.join(process.env.HOME, ".seldon");
|
|
274
|
+
if (fs.existsSync(root)) { fs.rmSync(root, { recursive: true, force: true }); console.log(C.dim(`\n removed ${root} (isolated uv/venv store)`)); }
|
|
275
|
+
}
|
|
276
|
+
const good = results.filter(([, r]) => r).map(([i]) => i);
|
|
277
|
+
const bad = results.filter(([, r]) => !r).map(([i]) => i);
|
|
278
|
+
console.log("\n" + C.bold("Done. ") + C.green("removed: " + (good.join(", ") || "—")) + (bad.length ? " " + C.red("failed: " + bad.join(", ")) : ""));
|
|
279
|
+
}
|
|
280
|
+
|
|
197
281
|
// ---- the checklist TUI ------------------------------------------------------
|
|
198
282
|
const BANNER = [
|
|
199
283
|
" ███████╗███████╗██╗ ██████╗ ██████╗ ███╗ ██╗",
|
|
@@ -266,10 +350,13 @@ ${C.bold("seldon")} — install the Seldon stack, pick what you use.
|
|
|
266
350
|
|
|
267
351
|
${C.bold("seldon")} open the checklist (space to pick, enter to install)
|
|
268
352
|
${C.bold("seldon install")} [ids…] install everything picked, or the named modules
|
|
353
|
+
${C.bold("seldon uninstall")} ids… remove the named modules (global CLI / isolated venv)
|
|
354
|
+
${C.bold("seldon uninstall --all")} remove the whole stack (also drops ~/.seldon)
|
|
269
355
|
${C.bold("seldon doctor")} [ids…] check external deps (tmux, sops, age, tailscale, python…)
|
|
270
356
|
${C.bold("seldon list")} list the modules
|
|
271
357
|
${C.bold("seldon --dev")} link node modules from this checkout instead of npm
|
|
272
358
|
${C.bold("seldon install --pm=pnpm")} force the node package manager (pnpm|bun|npm; auto-detected)
|
|
359
|
+
${C.dim("uninstall takes --yes to skip the confirm prompt.")}
|
|
273
360
|
|
|
274
361
|
${C.dim("node tools install via pnpm/bun if present (faster), else npm.")}
|
|
275
362
|
${C.dim("python (demerzel) installs into its own isolated venv — never your system Python;")}
|
|
@@ -283,7 +370,8 @@ const argv = process.argv.slice(2);
|
|
|
283
370
|
const dev = argv.includes("--dev");
|
|
284
371
|
const args = argv.filter((a) => !a.startsWith("--"));
|
|
285
372
|
const cmd = args[0];
|
|
286
|
-
const
|
|
373
|
+
const tokens = args.slice(1); // raw names after the command
|
|
374
|
+
const ids = tokens.filter((id) => byId[id]); // only the valid module ids
|
|
287
375
|
|
|
288
376
|
(async () => {
|
|
289
377
|
if (argv.includes("--help") || cmd === "help") return help();
|
|
@@ -293,6 +381,12 @@ const ids = args.slice(1).filter((id) => byId[id]);
|
|
|
293
381
|
if (ids.length) return doInstall(ids, { dev });
|
|
294
382
|
// install with no ids -> fall through to the picker
|
|
295
383
|
}
|
|
384
|
+
if (cmd === "uninstall") {
|
|
385
|
+
const all = argv.includes("--all");
|
|
386
|
+
const bad = tokens.filter((t) => !byId[t]);
|
|
387
|
+
if (bad.length) { console.log(C.red(`unknown module(s): ${bad.join(", ")}`)); console.log(C.dim(`modules: ${MODULES.map((m) => m.id).join(", ")}`)); process.exitCode = 1; return; }
|
|
388
|
+
return doUninstall(ids, { all, yes: argv.includes("--yes") });
|
|
389
|
+
}
|
|
296
390
|
if (cmd && cmd !== "install") { help(); process.exitCode = 1; return; }
|
|
297
391
|
// interactive
|
|
298
392
|
if (!process.stdin.isTTY) { console.log(C.red("no TTY — use: seldon install <ids…>")); process.exitCode = 1; return; }
|