@mentiko/pty-mgr 1.4.3 → 1.5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.5.0 - 2026-07-18
4
+
5
+ ### Added
6
+
7
+ - `p daemons` lists every running daemon on the machine — one line per daemon
8
+ with pid, uptime, session counts, and cwd, in the same style as `p list`. The
9
+ currently-selected daemon (`@name` / `$PTY_DAEMON`) is marked with a leading
10
+ `*`; a socket that no longer answers is shown as `(stale)` and one with an
11
+ empty name as `(unnamed)`. It is read-only — unlike `p stop all` it never
12
+ removes stale sockets. Both commands now share one socket-enumeration helper.
13
+
14
+ ### Fixed
15
+
16
+ - The installer no longer reports success while an older `pty-mgr` shadows the
17
+ freshly installed one. `install.sh` used to print `PATH is set.` once
18
+ `~/.pty-mgr/bin` was on `PATH`, which said nothing about *which* copy answers:
19
+ a stale binary earlier in `PATH` (e.g. a hand-placed `/usr/local/bin/pty-mgr`,
20
+ which sits on line 1 of macOS `/etc/paths`) would win silently. The installer
21
+ now scans every `PATH` entry after installing and prints any other `pty-mgr`
22
+ or `p` it finds, with an exact `rm` line to remove them.
23
+
3
24
  ## 1.4.3 - 2026-07-07
4
25
 
5
26
  ### Fixed
@@ -42,7 +42,7 @@ import { fileURLToPath } from "node:url";
42
42
  import { spawn as spawnChild } from "node:child_process";
43
43
 
44
44
  // kept in sync by scripts/version-sync.cjs (rewrites this literal on release)
45
- export const VERSION = "1.4.3";
45
+ export const VERSION = "1.5.0";
46
46
  import xterm from "@xterm/headless";
47
47
  import { SerializeAddon } from "@xterm/addon-serialize";
48
48
 
@@ -1479,6 +1479,32 @@ function sendCommandTo(sock, req) {
1479
1479
  return requestSocket(sock, req, "daemon not running");
1480
1480
  }
1481
1481
 
1482
+ // enumerate every daemon socket: ~/.pty-manager/*.sock plus legacy
1483
+ // /tmp/pty-manager-*.sock. returns [{ name, sockFile }]. shared by `stop all`
1484
+ // and `daemons` so both see the same set. presence of a .sock file does not
1485
+ // prove the daemon is alive -- the caller probes it.
1486
+ function listDaemonSockets() {
1487
+ const found = [];
1488
+ const dir = join(homedir(), ".pty-manager");
1489
+ try {
1490
+ for (const f of readdirSync(dir)) {
1491
+ if (f.endsWith(".sock")) {
1492
+ found.push({ name: f.replace(/\.sock$/, ""), sockFile: join(dir, f) });
1493
+ }
1494
+ }
1495
+ } catch { /* dir doesn't exist yet */ }
1496
+ try {
1497
+ const tmp = tmpdir();
1498
+ for (const f of readdirSync(tmp)) {
1499
+ if (f.startsWith("pty-manager-") && f.endsWith(".sock")) {
1500
+ const name = f.replace(/^pty-manager-/, "").replace(/\.sock$/, "");
1501
+ found.push({ name, sockFile: join(tmp, f) });
1502
+ }
1503
+ }
1504
+ } catch { /* tmp unreadable */ }
1505
+ return found;
1506
+ }
1507
+
1482
1508
  // send to the daemon selected by @name / --daemon / $PTY_DAEMON.
1483
1509
  function sendCommand(req) {
1484
1510
  return requestSocket(SOCKET_PATH, req, "daemon not running. start with: pty-mgr daemon");
@@ -1642,6 +1668,7 @@ const USAGE = `pty-mgr - PTY session manager
1642
1668
  usage:
1643
1669
  p daemon start daemon (background: &)
1644
1670
  p daemon @myproject named daemon (isolated sessions)
1671
+ p daemons list all running daemons (* = current)
1645
1672
  p status daemon info + config
1646
1673
  p config show current config
1647
1674
  p config screen 100x50 set default terminal size
@@ -3059,32 +3086,13 @@ async function cli() {
3059
3086
  const target = args[0]; // "all" or undefined (= current daemon)
3060
3087
  if (target === "all") {
3061
3088
  // find all pty-manager sockets and shut them down
3062
- const dir = join(homedir(), ".pty-manager");
3063
- let socks = [];
3064
- try {
3065
- socks = readdirSync(dir).filter((f) => f.endsWith(".sock"));
3066
- } catch { /* dir doesn't exist */ }
3067
- // also check legacy /tmp/ location for old sockets
3068
- try {
3069
- const tmp = tmpdir();
3070
- const legacy = readdirSync(tmp).filter((f) => f.startsWith("pty-manager-") && f.endsWith(".sock"));
3071
- for (const s of legacy) socks.push("__legacy__/" + s);
3072
- } catch {}
3073
- if (socks.length === 0) {
3089
+ const daemons = listDaemonSockets();
3090
+ if (daemons.length === 0) {
3074
3091
  console.log("no daemons running");
3075
3092
  return;
3076
3093
  }
3077
3094
  const stopped = [];
3078
- for (const sock of socks) {
3079
- let sockFile, name;
3080
- if (sock.startsWith("__legacy__/")) {
3081
- const legacyName = sock.replace("__legacy__/", "");
3082
- sockFile = join(tmpdir(), legacyName);
3083
- name = legacyName.replace("pty-manager-", "").replace(".sock", "");
3084
- } else {
3085
- sockFile = join(dir, sock);
3086
- name = sock.replace(".sock", "");
3087
- }
3095
+ for (const { name, sockFile } of daemons) {
3088
3096
  try {
3089
3097
  const res = await sendCommandTo(sockFile, { cmd: "shutdown" });
3090
3098
  if (res.ok) stopped.push(name);
@@ -3107,6 +3115,36 @@ async function cli() {
3107
3115
  return;
3108
3116
  }
3109
3117
 
3118
+ if (command === "daemons") {
3119
+ // read-only listing of every daemon socket. probes each for live status;
3120
+ // unlike `stop all` it never removes stale sockets. one line per daemon,
3121
+ // matching the `list` house style. current daemon (@name/$PTY_DAEMON) is
3122
+ // marked with a leading '*'.
3123
+ const daemons = listDaemonSockets();
3124
+ if (daemons.length === 0) {
3125
+ console.log("no daemons running");
3126
+ return;
3127
+ }
3128
+ for (const { name, sockFile } of daemons) {
3129
+ const marker = sockFile === SOCKET_PATH ? "*" : " ";
3130
+ let st = null;
3131
+ try {
3132
+ const res = await sendCommandTo(sockFile, { cmd: "status" });
3133
+ if (res.ok) st = res.status;
3134
+ } catch { /* stale: file exists but nothing is listening */ }
3135
+ const label = name || "(unnamed)";
3136
+ if (st) {
3137
+ const s = st.sessions;
3138
+ let line = `${marker} ${label} pid=${st.pid} up=${st.uptime} ${s.alive}/${s.total} sessions`;
3139
+ if (st.cwd) line += ` ${st.cwd}`; // older daemons may not report cwd
3140
+ console.log(line);
3141
+ } else {
3142
+ console.log(`${marker} ${label} (stale)`);
3143
+ }
3144
+ }
3145
+ return;
3146
+ }
3147
+
3110
3148
  if (command === "attach") {
3111
3149
  const name = args[0];
3112
3150
  if (!name) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentiko/pty-mgr",
3
- "version": "1.4.3",
3
+ "version": "1.5.0",
4
4
  "description": "PTY session manager with terminal emulation for programmatic session control.",
5
5
  "type": "module",
6
6
  "main": "lib/pty-manager.mjs",
@@ -26,10 +26,10 @@
26
26
  "@xterm/headless": "^6.0.0"
27
27
  },
28
28
  "optionalDependencies": {
29
- "@mentiko/pty-mgr-linux-x64": "1.4.3",
30
- "@mentiko/pty-mgr-linux-arm64": "1.4.3",
31
- "@mentiko/pty-mgr-darwin-x64": "1.4.3",
32
- "@mentiko/pty-mgr-darwin-arm64": "1.4.3"
29
+ "@mentiko/pty-mgr-linux-x64": "1.5.0",
30
+ "@mentiko/pty-mgr-linux-arm64": "1.5.0",
31
+ "@mentiko/pty-mgr-darwin-x64": "1.5.0",
32
+ "@mentiko/pty-mgr-darwin-arm64": "1.5.0"
33
33
  },
34
34
  "engines": {
35
35
  "bun": ">=1.0"