@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 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.5";
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 kill all user processes",
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 all user processes (asks for confirmation)"]
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
- const currentPid = process.pid;
4251
- let matches = getSystemProcesses({ name: rawTarget, userOnly: true, sortBy: "cpu" });
4252
- matches = matches.filter((m) => m.pid !== currentPid);
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 userProcs = getSystemProcesses({ userOnly: true, sortBy: "cpu", limit: 200 });
4309
- if (userProcs.length === 0) {
4310
- console.error(` ${pc8.dim("No user processes to kill.")}`);
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 ${userProcs.length} user processes?`)}`);
4315
- console.error(` ${pc8.dim("This will stop ALL your running processes.")}`);
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 ${userProcs.length} processes...`);
4305
+ const spinner = createSpinner(`Killing ${running.length} tracked app(s)...`);
4324
4306
  let killed = 0, failed = 0;
4325
- for (const proc of userProcs) {
4326
- if (killProcess(proc.pid, signal)) {
4307
+ for (const t of running) {
4308
+ if (killProcess(t.pid, signal)) {
4327
4309
  killed++;
4328
- removeTrackedByPid(proc.pid);
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, 500));
4335
- killed > 0 ? spinner.succeed(`${killed} process(es) killed`) : spinner.fail("Failed to kill processes");
4336
- if (failed > 0) console.error(` ${pc8.yellow(`${failed} process(es) could not be killed`)} ${pc8.dim("(try with sudo)")}`);
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 name = args2[0];
4628
- if (!name) {
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
- let trackedEntry = tracked.find((t) => t.name === name);
4634
- let targetPid;
4635
- if (trackedEntry) {
4636
- targetPid = trackedEntry.pid;
4637
- } else {
4638
- const pid = parseInt(name, 10);
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 confirmed = await confirmPrompt(`Restart ${pc11.bold(`${name} (PID ${targetPid})`)}?`, false);
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
- if (trackedEntry) {
4669
- const respawnSpinner = createSpinner(`Re-spawning ${trackedEntry.name}...`);
4670
- try {
4671
- const cmdParts = resolveArgs(trackedEntry);
4672
- const logDir = resolve6(homedir4(), ".fennec", "logs");
4673
- mkdirSync6(logDir, { recursive: true });
4674
- const logFilePath = resolve6(logDir, `${trackedEntry.name}.log`);
4675
- const child = spawnDaemon({ cmdParts, name: trackedEntry.name, cwd: trackedEntry.cwd, logFilePath, env: buildSpawnEnv(trackedEntry.env), logMode: trackedEntry.logMode });
4676
- removeTrackedByPid(targetPid);
4677
- addTracked({
4678
- name: trackedEntry.name,
4679
- pid: child.pid ?? 0,
4680
- command: trackedEntry.command,
4681
- args: cmdParts,
4682
- port: trackedEntry.port,
4683
- cwd: trackedEntry.cwd,
4684
- env: trackedEntry.env,
4685
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
4686
- autoRestart: trackedEntry.autoRestart,
4687
- logMode: trackedEntry.logMode
4688
- });
4689
- respawnSpinner.succeed(`${trackedEntry.name} restarted (PID: ${child.pid})`);
4690
- } catch (error) {
4691
- respawnSpinner.fail(`Failed to re-spawn ${trackedEntry.name}: ${String(error)}`);
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