@plumpslabs/fennec-cli 1.13.6 → 1.13.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/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.6";
2094
+ var VERSION = "1.13.8";
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",
@@ -4240,36 +4241,18 @@ async function killCommand(args2) {
4240
4241
  const trackedMatch = tracked.find((t) => t.name === rawTarget);
4241
4242
  if (trackedMatch) {
4242
4243
  if (!isTrackedRunning(trackedMatch)) {
4244
+ removeTrackedByPid(trackedMatch.pid);
4243
4245
  console.error(`
4244
- ${pc8.yellow("\u26A0")} ${pc8.bold(rawTarget)} ${pc8.dim("is already stopped (no running process to kill)")}`);
4245
- console.error(` ${pc8.dim("Use")} ${pc8.cyan("fennec spawn")} ${pc8.dim("to resume")}`);
4246
- process.exit(0);
4246
+ ${pc8.green("\u2713")} ${pc8.bold(rawTarget)} ${pc8.dim("removed from tracked apps (was already stopped)")}`);
4247
+ console.error();
4248
+ return;
4247
4249
  }
4248
4250
  targetPid = trackedMatch.pid;
4249
4251
  displayName = `${trackedMatch.name} (PID ${targetPid})`;
4250
4252
  } else {
4251
- const currentPid = process.pid;
4252
- let matches = getSystemProcesses({ name: rawTarget, userOnly: true, sortBy: "cpu" });
4253
- matches = matches.filter((m) => m.pid !== currentPid);
4254
- if (matches.length === 0) {
4255
- console.error(renderError("Process not found", `No running process matching "${rawTarget}"`));
4256
- process.exit(1);
4257
- }
4258
- if (matches.length > 1) {
4259
- console.error(`
4260
- ${pc8.bold(`Multiple processes match "${rawTarget}":`)}`);
4261
- 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}%` })));
4262
- if (selected === null) {
4263
- console.error(` ${pc8.dim("Cancelled")}`);
4264
- return;
4265
- }
4266
- const idx = parseInt(selected, 10);
4267
- targetPid = matches[idx].pid;
4268
- displayName = `${matches[idx].name} (PID ${targetPid})`;
4269
- } else {
4270
- targetPid = matches[0].pid;
4271
- displayName = `${matches[0].name} (PID ${targetPid})`;
4272
- }
4253
+ console.error(renderError("Not tracked", `No tracked process named "${rawTarget}".
4254
+ Use ${pc8.cyan(`fennec kill <pid>`)} to kill a system process by its PID.`));
4255
+ process.exit(1);
4273
4256
  }
4274
4257
  }
4275
4258
  const confirmed = force || await confirmPrompt(`Kill ${pc8.bold(displayName)} with ${pc8.yellow(signal)}?`, false);
@@ -4623,35 +4606,27 @@ import { mkdirSync as mkdirSync6 } from "fs";
4623
4606
  import { resolve as resolve6 } from "path";
4624
4607
  import { homedir as homedir4 } from "os";
4625
4608
  async function restartCommand(args2) {
4626
- const name = args2[0];
4627
- if (!name) {
4628
- console.error(renderError("Missing process name/pid", "Usage: fennec restart <name|pid>"));
4609
+ const raw = args2[0];
4610
+ if (!raw) {
4611
+ console.error(renderError("Missing process name/pid", "Usage: fennec restart <name|pid> [-y]"));
4629
4612
  process.exit(1);
4630
4613
  }
4631
4614
  const tracked = readTracked();
4632
- let trackedEntry = tracked.find((t) => t.name === name);
4633
- let targetPid;
4634
- if (trackedEntry) {
4635
- targetPid = trackedEntry.pid;
4636
- } else {
4637
- const pid = parseInt(name, 10);
4638
- if (!isNaN(pid) && String(pid) === name) {
4639
- targetPid = pid;
4640
- } else {
4641
- const processes = getSystemProcesses({ name, userOnly: true, limit: 1 });
4642
- if (processes.length === 0) {
4643
- console.error(renderError("Process not found", `No process matching "${name}"`));
4644
- process.exit(1);
4645
- }
4646
- targetPid = processes[0].pid;
4647
- }
4615
+ const pidNum = parseInt(raw, 10);
4616
+ const trackedEntry = tracked.find((t) => t.name === raw) ?? (!isNaN(pidNum) && String(pidNum) === raw ? tracked.find((t) => t.pid === pidNum) : void 0);
4617
+ if (!trackedEntry) {
4618
+ console.error(renderError("Not tracked", `No tracked process named or with PID "${raw}".
4619
+ fennec restart only re-spawns Fennec-tracked apps (from their saved config).`));
4620
+ process.exit(1);
4648
4621
  }
4649
- const confirmed = await confirmPrompt(`Restart ${pc11.bold(`${name} (PID ${targetPid})`)}?`, false);
4622
+ const targetPid = trackedEntry.pid;
4623
+ const force = args2.includes("-y") || args2.includes("--yes");
4624
+ const confirmed = force || await confirmPrompt(`Restart ${pc11.bold(`${trackedEntry.name} (PID ${targetPid})`)}?`, false);
4650
4625
  if (!confirmed) {
4651
4626
  console.error(` ${pc11.dim("Cancelled")}`);
4652
4627
  return;
4653
4628
  }
4654
- const spinner = createSpinner(`Stopping PID ${targetPid}...`);
4629
+ const spinner = createSpinner(`Stopping ${trackedEntry.name} (PID ${targetPid})...`);
4655
4630
  const killed = killProcess(targetPid, "SIGTERM");
4656
4631
  if (!killed) {
4657
4632
  spinner.fail(`Failed to stop PID ${targetPid}`);
@@ -4663,35 +4638,31 @@ async function restartCommand(args2) {
4663
4638
  killProcess(targetPid, "SIGKILL");
4664
4639
  await new Promise((r) => setTimeout(r, 500));
4665
4640
  }
4666
- spinner.succeed(`${name} stopped`);
4667
- if (trackedEntry) {
4668
- const respawnSpinner = createSpinner(`Re-spawning ${trackedEntry.name}...`);
4669
- try {
4670
- const cmdParts = resolveArgs(trackedEntry);
4671
- const logDir = resolve6(homedir4(), ".fennec", "logs");
4672
- mkdirSync6(logDir, { recursive: true });
4673
- const logFilePath = resolve6(logDir, `${trackedEntry.name}.log`);
4674
- const child = spawnDaemon({ cmdParts, name: trackedEntry.name, cwd: trackedEntry.cwd, logFilePath, env: buildSpawnEnv(trackedEntry.env), logMode: trackedEntry.logMode });
4675
- removeTrackedByPid(targetPid);
4676
- addTracked({
4677
- name: trackedEntry.name,
4678
- pid: child.pid ?? 0,
4679
- command: trackedEntry.command,
4680
- args: cmdParts,
4681
- port: trackedEntry.port,
4682
- cwd: trackedEntry.cwd,
4683
- env: trackedEntry.env,
4684
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
4685
- autoRestart: trackedEntry.autoRestart,
4686
- logMode: trackedEntry.logMode
4687
- });
4688
- respawnSpinner.succeed(`${trackedEntry.name} restarted (PID: ${child.pid})`);
4689
- } catch (error) {
4690
- respawnSpinner.fail(`Failed to re-spawn ${trackedEntry.name}: ${String(error)}`);
4691
- console.error(` ${pc11.dim("Config preserved. Re-run manually:")} ${renderCommand(trackedEntry.command)}`);
4692
- }
4693
- } else {
4694
- console.error(` ${pc11.dim("No tracked config found. Re-run manually:")} ${renderCommand(name)}`);
4641
+ spinner.succeed(`${trackedEntry.name} stopped`);
4642
+ const respawnSpinner = createSpinner(`Re-spawning ${trackedEntry.name}...`);
4643
+ try {
4644
+ const cmdParts = resolveArgs(trackedEntry);
4645
+ const logDir = resolve6(homedir4(), ".fennec", "logs");
4646
+ mkdirSync6(logDir, { recursive: true });
4647
+ const logFilePath = resolve6(logDir, `${trackedEntry.name}.log`);
4648
+ const child = spawnDaemon({ cmdParts, name: trackedEntry.name, cwd: trackedEntry.cwd, logFilePath, env: buildSpawnEnv(trackedEntry.env), logMode: trackedEntry.logMode });
4649
+ removeTrackedByPid(targetPid);
4650
+ addTracked({
4651
+ name: trackedEntry.name,
4652
+ pid: child.pid ?? 0,
4653
+ command: trackedEntry.command,
4654
+ args: cmdParts,
4655
+ port: trackedEntry.port,
4656
+ cwd: trackedEntry.cwd,
4657
+ env: trackedEntry.env,
4658
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
4659
+ autoRestart: trackedEntry.autoRestart,
4660
+ logMode: trackedEntry.logMode
4661
+ });
4662
+ respawnSpinner.succeed(`${trackedEntry.name} restarted (PID: ${child.pid})`);
4663
+ } catch (error) {
4664
+ respawnSpinner.fail(`Failed to re-spawn ${trackedEntry.name}: ${String(error)}`);
4665
+ console.error(` ${pc11.dim("Config preserved. Re-run manually:")} ${renderCommand(trackedEntry.command)}`);
4695
4666
  }
4696
4667
  }
4697
4668