@plumpslabs/fennec-cli 1.13.5 → 1.13.7
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 +64 -95
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2091,7 +2091,7 @@ function hexColor(color) {
|
|
|
2091
2091
|
}
|
|
2092
2092
|
var fennecOrange2 = hexColor("#FF6432");
|
|
2093
2093
|
var fennecGold = hexColor("#FFB347");
|
|
2094
|
-
var VERSION = "1.13.
|
|
2094
|
+
var VERSION = "1.13.7";
|
|
2095
2095
|
var cachedBanner = null;
|
|
2096
2096
|
var cachedCompact = null;
|
|
2097
2097
|
function generateBanner() {
|
|
@@ -2242,13 +2242,14 @@ var COMMANDS = {
|
|
|
2242
2242
|
kill: {
|
|
2243
2243
|
name: "kill",
|
|
2244
2244
|
usage: "kill <pid|name|all>",
|
|
2245
|
-
summary: "Kill a process by PID, name, or
|
|
2246
|
-
description: "Permanently removes a tracked app (unlike `stop`, which keeps it). Prompts before killing.",
|
|
2245
|
+
summary: "Kill a Fennec-tracked process (by PID, tracked name, or all tracked)",
|
|
2246
|
+
description: "Permanently removes a tracked app (unlike `stop`, which keeps it). By NAME it only matches Fennec-tracked apps \u2014 use an explicit PID to kill a system process. Prompts before killing.",
|
|
2247
2247
|
options: [
|
|
2248
2248
|
["--signal <sig>", "Signal to send (default: SIGTERM). e.g. SIGKILL, SIGINT"],
|
|
2249
|
-
["--all, -a", "Kill
|
|
2249
|
+
["--all, -a", "Kill ALL tracked apps (asks for confirmation)"],
|
|
2250
|
+
["-y, --yes", "Skip the confirmation prompt"]
|
|
2250
2251
|
],
|
|
2251
|
-
examples: ["kill web", "kill 12345 --signal SIGKILL", "kill all"]
|
|
2252
|
+
examples: ["kill web", "kill 12345 --signal SIGKILL", "kill all -y"]
|
|
2252
2253
|
},
|
|
2253
2254
|
supervisor: {
|
|
2254
2255
|
name: "supervisor",
|
|
@@ -4216,8 +4217,9 @@ async function killCommand(args2) {
|
|
|
4216
4217
|
const signalIndex = args2.indexOf("--signal");
|
|
4217
4218
|
const signalRaw = signalIndex !== -1 ? args2[signalIndex + 1] : "SIGTERM";
|
|
4218
4219
|
const signal = signalRaw ?? "SIGTERM";
|
|
4220
|
+
const force = args2.includes("-y") || args2.includes("--yes");
|
|
4219
4221
|
if (rawTarget === "all" || args2.includes("--all") || args2.includes("-a")) {
|
|
4220
|
-
await killAll(signal);
|
|
4222
|
+
await killAll(signal, force);
|
|
4221
4223
|
return;
|
|
4222
4224
|
}
|
|
4223
4225
|
if (!rawTarget) {
|
|
@@ -4247,31 +4249,11 @@ async function killCommand(args2) {
|
|
|
4247
4249
|
targetPid = trackedMatch.pid;
|
|
4248
4250
|
displayName = `${trackedMatch.name} (PID ${targetPid})`;
|
|
4249
4251
|
} else {
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
if (matches.length === 0) {
|
|
4254
|
-
console.error(renderError("Process not found", `No running process matching "${rawTarget}"`));
|
|
4255
|
-
process.exit(1);
|
|
4256
|
-
}
|
|
4257
|
-
if (matches.length > 1) {
|
|
4258
|
-
console.error(`
|
|
4259
|
-
${pc8.bold(`Multiple processes match "${rawTarget}":`)}`);
|
|
4260
|
-
const selected = await selectPrompt("Select which to kill:", matches.map((p, i) => ({ value: String(i), label: `${p.name} (PID ${p.pid})`, description: `${p.command.slice(0, 80)} \u2014 CPU: ${p.cpuPercent}% MEM: ${p.memPercent}%` })));
|
|
4261
|
-
if (selected === null) {
|
|
4262
|
-
console.error(` ${pc8.dim("Cancelled")}`);
|
|
4263
|
-
return;
|
|
4264
|
-
}
|
|
4265
|
-
const idx = parseInt(selected, 10);
|
|
4266
|
-
targetPid = matches[idx].pid;
|
|
4267
|
-
displayName = `${matches[idx].name} (PID ${targetPid})`;
|
|
4268
|
-
} else {
|
|
4269
|
-
targetPid = matches[0].pid;
|
|
4270
|
-
displayName = `${matches[0].name} (PID ${targetPid})`;
|
|
4271
|
-
}
|
|
4252
|
+
console.error(renderError("Not tracked", `No tracked process named "${rawTarget}".
|
|
4253
|
+
Use ${pc8.cyan(`fennec kill <pid>`)} to kill a system process by its PID.`));
|
|
4254
|
+
process.exit(1);
|
|
4272
4255
|
}
|
|
4273
4256
|
}
|
|
4274
|
-
const force = args2.includes("-y") || args2.includes("--yes");
|
|
4275
4257
|
const confirmed = force || await confirmPrompt(`Kill ${pc8.bold(displayName)} with ${pc8.yellow(signal)}?`, false);
|
|
4276
4258
|
if (!confirmed) {
|
|
4277
4259
|
console.error(` ${pc8.dim("Cancelled")}`);
|
|
@@ -4304,36 +4286,35 @@ async function killCommand(args2) {
|
|
|
4304
4286
|
console.error(renderError("Permission denied", "Try running with sudo or use a different signal."));
|
|
4305
4287
|
}
|
|
4306
4288
|
}
|
|
4307
|
-
async function killAll(signal) {
|
|
4308
|
-
const
|
|
4309
|
-
|
|
4310
|
-
|
|
4289
|
+
async function killAll(signal, force) {
|
|
4290
|
+
const tracked = readTracked();
|
|
4291
|
+
const running = tracked.filter((t) => isTrackedRunning(t));
|
|
4292
|
+
if (running.length === 0) {
|
|
4293
|
+
console.error(` ${pc8.dim("No running tracked apps to kill.")}`);
|
|
4311
4294
|
return;
|
|
4312
4295
|
}
|
|
4313
4296
|
console.error(`
|
|
4314
|
-
${pc8.bold(`Kill ${
|
|
4315
|
-
console.error(` ${pc8.dim("This
|
|
4316
|
-
console.error(` ${pc8.yellow("\u26A0 System processes will not be affected.")}
|
|
4297
|
+
${pc8.bold(`Kill ${running.length} tracked app(s)?`)}`);
|
|
4298
|
+
console.error(` ${pc8.dim("This stops every Fennec-tracked running process only \u2014 other processes are untouched.")}
|
|
4317
4299
|
`);
|
|
4318
|
-
const confirmed = await confirmPrompt(`${pc8.red("Are you sure?")} ${pc8.dim("This cannot be undone")}`, false);
|
|
4300
|
+
const confirmed = force || await confirmPrompt(`${pc8.red("Are you sure?")} ${pc8.dim("This cannot be undone")}`, false);
|
|
4319
4301
|
if (!confirmed) {
|
|
4320
4302
|
console.error(` ${pc8.dim("Cancelled.")}`);
|
|
4321
4303
|
return;
|
|
4322
4304
|
}
|
|
4323
|
-
const spinner = createSpinner(`Killing ${
|
|
4305
|
+
const spinner = createSpinner(`Killing ${running.length} tracked app(s)...`);
|
|
4324
4306
|
let killed = 0, failed = 0;
|
|
4325
|
-
for (const
|
|
4326
|
-
if (killProcess(
|
|
4307
|
+
for (const t of running) {
|
|
4308
|
+
if (killProcess(t.pid, signal)) {
|
|
4327
4309
|
killed++;
|
|
4328
|
-
removeTrackedByPid(
|
|
4310
|
+
removeTrackedByPid(t.pid);
|
|
4329
4311
|
} else {
|
|
4330
4312
|
failed++;
|
|
4331
4313
|
}
|
|
4332
|
-
if (killed % 10 === 0) await new Promise((r) => setTimeout(r, 50));
|
|
4333
4314
|
}
|
|
4334
|
-
await new Promise((r) => setTimeout(r,
|
|
4335
|
-
killed > 0 ? spinner.succeed(`${killed}
|
|
4336
|
-
if (failed > 0) console.error(` ${pc8.yellow(`${failed}
|
|
4315
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
4316
|
+
killed > 0 ? spinner.succeed(`${killed} tracked app(s) killed`) : spinner.fail("Failed to kill apps");
|
|
4317
|
+
if (failed > 0) console.error(` ${pc8.yellow(`${failed} app(s) could not be killed`)}`);
|
|
4337
4318
|
}
|
|
4338
4319
|
|
|
4339
4320
|
// src/commands/stop.ts
|
|
@@ -4624,35 +4605,27 @@ import { mkdirSync as mkdirSync6 } from "fs";
|
|
|
4624
4605
|
import { resolve as resolve6 } from "path";
|
|
4625
4606
|
import { homedir as homedir4 } from "os";
|
|
4626
4607
|
async function restartCommand(args2) {
|
|
4627
|
-
const
|
|
4628
|
-
if (!
|
|
4629
|
-
console.error(renderError("Missing process name/pid", "Usage: fennec restart <name|pid>"));
|
|
4608
|
+
const raw = args2[0];
|
|
4609
|
+
if (!raw) {
|
|
4610
|
+
console.error(renderError("Missing process name/pid", "Usage: fennec restart <name|pid> [-y]"));
|
|
4630
4611
|
process.exit(1);
|
|
4631
4612
|
}
|
|
4632
4613
|
const tracked = readTracked();
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
if (trackedEntry) {
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
if (!isNaN(pid) && String(pid) === name) {
|
|
4640
|
-
targetPid = pid;
|
|
4641
|
-
} else {
|
|
4642
|
-
const processes = getSystemProcesses({ name, userOnly: true, limit: 1 });
|
|
4643
|
-
if (processes.length === 0) {
|
|
4644
|
-
console.error(renderError("Process not found", `No process matching "${name}"`));
|
|
4645
|
-
process.exit(1);
|
|
4646
|
-
}
|
|
4647
|
-
targetPid = processes[0].pid;
|
|
4648
|
-
}
|
|
4614
|
+
const pidNum = parseInt(raw, 10);
|
|
4615
|
+
const trackedEntry = tracked.find((t) => t.name === raw) ?? (!isNaN(pidNum) && String(pidNum) === raw ? tracked.find((t) => t.pid === pidNum) : void 0);
|
|
4616
|
+
if (!trackedEntry) {
|
|
4617
|
+
console.error(renderError("Not tracked", `No tracked process named or with PID "${raw}".
|
|
4618
|
+
fennec restart only re-spawns Fennec-tracked apps (from their saved config).`));
|
|
4619
|
+
process.exit(1);
|
|
4649
4620
|
}
|
|
4650
|
-
const
|
|
4621
|
+
const targetPid = trackedEntry.pid;
|
|
4622
|
+
const force = args2.includes("-y") || args2.includes("--yes");
|
|
4623
|
+
const confirmed = force || await confirmPrompt(`Restart ${pc11.bold(`${trackedEntry.name} (PID ${targetPid})`)}?`, false);
|
|
4651
4624
|
if (!confirmed) {
|
|
4652
4625
|
console.error(` ${pc11.dim("Cancelled")}`);
|
|
4653
4626
|
return;
|
|
4654
4627
|
}
|
|
4655
|
-
const spinner = createSpinner(`Stopping PID ${targetPid}...`);
|
|
4628
|
+
const spinner = createSpinner(`Stopping ${trackedEntry.name} (PID ${targetPid})...`);
|
|
4656
4629
|
const killed = killProcess(targetPid, "SIGTERM");
|
|
4657
4630
|
if (!killed) {
|
|
4658
4631
|
spinner.fail(`Failed to stop PID ${targetPid}`);
|
|
@@ -4664,35 +4637,31 @@ async function restartCommand(args2) {
|
|
|
4664
4637
|
killProcess(targetPid, "SIGKILL");
|
|
4665
4638
|
await new Promise((r) => setTimeout(r, 500));
|
|
4666
4639
|
}
|
|
4667
|
-
spinner.succeed(`${name} stopped`);
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
}
|
|
4691
|
-
|
|
4692
|
-
console.error(` ${pc11.dim("Config preserved. Re-run manually:")} ${renderCommand(trackedEntry.command)}`);
|
|
4693
|
-
}
|
|
4694
|
-
} else {
|
|
4695
|
-
console.error(` ${pc11.dim("No tracked config found. Re-run manually:")} ${renderCommand(name)}`);
|
|
4640
|
+
spinner.succeed(`${trackedEntry.name} stopped`);
|
|
4641
|
+
const respawnSpinner = createSpinner(`Re-spawning ${trackedEntry.name}...`);
|
|
4642
|
+
try {
|
|
4643
|
+
const cmdParts = resolveArgs(trackedEntry);
|
|
4644
|
+
const logDir = resolve6(homedir4(), ".fennec", "logs");
|
|
4645
|
+
mkdirSync6(logDir, { recursive: true });
|
|
4646
|
+
const logFilePath = resolve6(logDir, `${trackedEntry.name}.log`);
|
|
4647
|
+
const child = spawnDaemon({ cmdParts, name: trackedEntry.name, cwd: trackedEntry.cwd, logFilePath, env: buildSpawnEnv(trackedEntry.env), logMode: trackedEntry.logMode });
|
|
4648
|
+
removeTrackedByPid(targetPid);
|
|
4649
|
+
addTracked({
|
|
4650
|
+
name: trackedEntry.name,
|
|
4651
|
+
pid: child.pid ?? 0,
|
|
4652
|
+
command: trackedEntry.command,
|
|
4653
|
+
args: cmdParts,
|
|
4654
|
+
port: trackedEntry.port,
|
|
4655
|
+
cwd: trackedEntry.cwd,
|
|
4656
|
+
env: trackedEntry.env,
|
|
4657
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4658
|
+
autoRestart: trackedEntry.autoRestart,
|
|
4659
|
+
logMode: trackedEntry.logMode
|
|
4660
|
+
});
|
|
4661
|
+
respawnSpinner.succeed(`${trackedEntry.name} restarted (PID: ${child.pid})`);
|
|
4662
|
+
} catch (error) {
|
|
4663
|
+
respawnSpinner.fail(`Failed to re-spawn ${trackedEntry.name}: ${String(error)}`);
|
|
4664
|
+
console.error(` ${pc11.dim("Config preserved. Re-run manually:")} ${renderCommand(trackedEntry.command)}`);
|
|
4696
4665
|
}
|
|
4697
4666
|
}
|
|
4698
4667
|
|