@mentiko/pty-mgr 1.4.2 → 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 +81 -0
- package/lib/pty-manager.mjs +162 -34
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,86 @@
|
|
|
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
|
+
|
|
24
|
+
## 1.4.3 - 2026-07-07
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- `p attach` now sizes the session to the attaching client's terminal instead of
|
|
29
|
+
resizing the client to the session. The old app-driven CSI-8 resize is ignored
|
|
30
|
+
inside tmux/iTerm panes (or resizes the whole window), so a session viewed in a
|
|
31
|
+
smaller pane kept its default winsize — which made a full-frame TUI (e.g. Claude
|
|
32
|
+
Code) flicker its status line and pushed its bottom row off-screen. The client
|
|
33
|
+
now sends its size in the attach request and the daemon resizes the session
|
|
34
|
+
(SIGWINCHing the child) to match.
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- Live-resize while attached: when the client's terminal changes size, the
|
|
39
|
+
session (and its child) resize to follow. The client sends an out-of-band APC
|
|
40
|
+
control frame that the daemon strips from the raw input stream, so it never
|
|
41
|
+
reaches the pty as keystrokes.
|
|
42
|
+
|
|
43
|
+
## 1.4.2 - 2026-07-07
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- Internal refactor of `lib/pty-manager.mjs`: the two near-identical socket
|
|
48
|
+
clients collapse into one `requestSocket`, and the terminal-size clamps,
|
|
49
|
+
`--log` wiring, telegram send, capture-stability check, and transcript
|
|
50
|
+
listing move to shared helpers. No behavior or public API change.
|
|
51
|
+
|
|
52
|
+
### Fixed
|
|
53
|
+
|
|
54
|
+
- Hardened error handling on three paths that could take the daemon down: the
|
|
55
|
+
socket client now turns a malformed or truncated reply into a rejection
|
|
56
|
+
instead of an uncaught throw in its data handler; the log write stream gets an
|
|
57
|
+
`error` listener (a disk-full / permission error drops logging instead of
|
|
58
|
+
crashing); and attach-mode input is guarded so a keystroke sent to a
|
|
59
|
+
just-exited session no longer throws in the socket handler.
|
|
60
|
+
|
|
61
|
+
## 1.4.1 - 2026-07-07
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- `p attach` replays the session's full terminal state via the xterm
|
|
66
|
+
serialize-addon (scrollback, colors, cursor, modes). Normal-buffer sessions
|
|
67
|
+
(a shell, Claude Code, …) are no longer forced into the alternate screen —
|
|
68
|
+
which had discarded scrollback — so history stays scrollable in the client.
|
|
69
|
+
Alt-screen TUIs still get the alt-screen switch, and the client pops back to
|
|
70
|
+
its normal screen on detach.
|
|
71
|
+
|
|
72
|
+
## 1.4.0 - 2026-07-07
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
|
|
76
|
+
- `p attach` replays full scrollback history on connect, not just the visible
|
|
77
|
+
screen.
|
|
78
|
+
- Layered flow config resolution: flows merge from the packaged defaults, the
|
|
79
|
+
XDG user config, and a project `pty-mgr.config.json` (project overrides user
|
|
80
|
+
overrides default). `p flow list` tags each flow with its source layer,
|
|
81
|
+
`p flow new [--global]` scaffolds a project (or user) flow, and `p open
|
|
82
|
+
config` opens the config directory.
|
|
83
|
+
|
|
3
84
|
## 1.3.1 - 2026-07-02
|
|
4
85
|
|
|
5
86
|
### Fixed
|
package/lib/pty-manager.mjs
CHANGED
|
@@ -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.
|
|
45
|
+
export const VERSION = "1.5.0";
|
|
46
46
|
import xterm from "@xterm/headless";
|
|
47
47
|
import { SerializeAddon } from "@xterm/addon-serialize";
|
|
48
48
|
|
|
@@ -700,6 +700,54 @@ function socketPath(name) {
|
|
|
700
700
|
const { daemon: DAEMON_NAME, args: CLI_ARGS } = splitDaemonArgs(process.argv.slice(2));
|
|
701
701
|
const SOCKET_PATH = socketPath(DAEMON_NAME);
|
|
702
702
|
|
|
703
|
+
// ── attach input: out-of-band resize control frames ──────────────────
|
|
704
|
+
// While attached, the socket carries raw keystrokes. A client signals a live
|
|
705
|
+
// terminal resize with an APC control frame the daemon pulls out of the stream
|
|
706
|
+
// instead of forwarding to the pty: ESC _ ptymgr:resize:<cols>:<rows> ESC \
|
|
707
|
+
// APC (ESC _) is ignored by terminals and is never produced by a keyboard, so
|
|
708
|
+
// it cannot collide with real input.
|
|
709
|
+
const RESIZE_PREFIX = Buffer.from("\x1b_ptymgr:resize:");
|
|
710
|
+
const RESIZE_ST = Buffer.from("\x1b\\");
|
|
711
|
+
|
|
712
|
+
// longest k (>=1) where buf's last k bytes equal prefix's first k bytes.
|
|
713
|
+
function suffixPrefixOverlap(buf, prefix) {
|
|
714
|
+
for (let k = Math.min(buf.length, prefix.length - 1); k > 0; k--) {
|
|
715
|
+
if (buf.subarray(buf.length - k).equals(prefix.subarray(0, k))) return k;
|
|
716
|
+
}
|
|
717
|
+
return 0;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// Split raw attach input into pty keystrokes and resize frames. Returns
|
|
721
|
+
// { forward: Buffer for the pty, resizes: [{cols,rows}], rest: Buffer to
|
|
722
|
+
// prepend to the next chunk }. `rest` holds an incomplete frame, or a trailing
|
|
723
|
+
// "ESC _…" that may begin one, so a frame split across reads reassembles. A
|
|
724
|
+
// lone trailing ESC is NOT held (that's the Escape key or a CSI start) so it
|
|
725
|
+
// forwards immediately.
|
|
726
|
+
export function parseAttachInput(buf) {
|
|
727
|
+
const forward = [];
|
|
728
|
+
const resizes = [];
|
|
729
|
+
let i = 0;
|
|
730
|
+
while (i < buf.length) {
|
|
731
|
+
const start = buf.indexOf(RESIZE_PREFIX, i);
|
|
732
|
+
if (start === -1) break;
|
|
733
|
+
if (start > i) forward.push(buf.subarray(i, start));
|
|
734
|
+
const st = buf.indexOf(RESIZE_ST, start + RESIZE_PREFIX.length);
|
|
735
|
+
if (st === -1) {
|
|
736
|
+
// incomplete frame: forward what precedes it, hold the rest
|
|
737
|
+
return { forward: Buffer.concat(forward), resizes, rest: buf.subarray(start) };
|
|
738
|
+
}
|
|
739
|
+
const body = buf.subarray(start + RESIZE_PREFIX.length, st).toString("latin1");
|
|
740
|
+
const m = body.match(/^(\d+):(\d+)$/);
|
|
741
|
+
if (m) resizes.push({ cols: Number(m[1]), rows: Number(m[2]) });
|
|
742
|
+
i = st + RESIZE_ST.length;
|
|
743
|
+
}
|
|
744
|
+
const tail = buf.subarray(i);
|
|
745
|
+
let keep = suffixPrefixOverlap(tail, RESIZE_PREFIX);
|
|
746
|
+
if (keep < 2) keep = 0; // never hold a lone ESC (Escape key / CSI start)
|
|
747
|
+
if (tail.length > keep) forward.push(tail.subarray(0, tail.length - keep));
|
|
748
|
+
return { forward: Buffer.concat(forward), resizes, rest: tail.subarray(tail.length - keep) };
|
|
749
|
+
}
|
|
750
|
+
|
|
703
751
|
/**
|
|
704
752
|
* daemon: long-running process that holds all sessions.
|
|
705
753
|
* clients connect via unix socket, send JSON commands, get JSON responses.
|
|
@@ -746,6 +794,7 @@ function startDaemon() {
|
|
|
746
794
|
const server = createServer((conn) => {
|
|
747
795
|
let buf = "";
|
|
748
796
|
let attached = false; // true when in attach streaming mode
|
|
797
|
+
let attachInBuf = Buffer.alloc(0); // carries a split resize frame across reads
|
|
749
798
|
|
|
750
799
|
// suppress connection errors (client disconnect, etc.)
|
|
751
800
|
conn.on("error", () => {});
|
|
@@ -756,11 +805,21 @@ function startDaemon() {
|
|
|
756
805
|
});
|
|
757
806
|
|
|
758
807
|
conn.on("data", async (data) => {
|
|
759
|
-
// in attach mode
|
|
760
|
-
//
|
|
761
|
-
//
|
|
808
|
+
// in attach mode: pull out any resize control frames (applied to the
|
|
809
|
+
// session), forward the rest as raw keystrokes to the pty. the session
|
|
810
|
+
// can exit between attach and this write (write() throws once exited);
|
|
811
|
+
// swallow so a late keystroke can't crash the daemon's data handler.
|
|
762
812
|
if (attached) {
|
|
763
|
-
|
|
813
|
+
const { forward, resizes, rest } = parseAttachInput(
|
|
814
|
+
attachInBuf.length ? Buffer.concat([attachInBuf, data]) : data
|
|
815
|
+
);
|
|
816
|
+
attachInBuf = rest;
|
|
817
|
+
for (const { cols, rows } of resizes) {
|
|
818
|
+
try { attached.resize(cols, rows); } catch {}
|
|
819
|
+
}
|
|
820
|
+
if (forward.length) {
|
|
821
|
+
try { attached.write(forward.toString()); } catch {}
|
|
822
|
+
}
|
|
764
823
|
return;
|
|
765
824
|
}
|
|
766
825
|
|
|
@@ -781,6 +840,15 @@ function startDaemon() {
|
|
|
781
840
|
// attach is special: switches to streaming mode
|
|
782
841
|
if (req.cmd === "attach") {
|
|
783
842
|
const session = mgr.get(req.name);
|
|
843
|
+
// size the session to the attaching client's terminal so the
|
|
844
|
+
// child's winsize matches what the user actually sees. A pane
|
|
845
|
+
// (tmux/iTerm split) ignores an app's own CSI-8 window resize --
|
|
846
|
+
// and honoring it would resize the user's whole window -- so we
|
|
847
|
+
// resize the session TO the client, the way tmux/ssh do. resize()
|
|
848
|
+
// SIGWINCHes the child, which repaints at the new size; a mismatch
|
|
849
|
+
// here is what makes a full-frame TUI's status line flicker and
|
|
850
|
+
// pushes its bottom row off-screen. No size sent => leave as-is.
|
|
851
|
+
if (req.cols && req.rows) session.resize(req.cols, req.rows);
|
|
784
852
|
const cols = session.terminal.cols;
|
|
785
853
|
const rows = session.terminal.rows;
|
|
786
854
|
const alt = session.terminal.buffer.active.type === "alternate";
|
|
@@ -819,6 +887,7 @@ function startDaemon() {
|
|
|
819
887
|
|
|
820
888
|
// forward client input to pty
|
|
821
889
|
attached = session;
|
|
890
|
+
attachInBuf = Buffer.alloc(0);
|
|
822
891
|
|
|
823
892
|
// cleanup on disconnect
|
|
824
893
|
conn.on("close", () => {
|
|
@@ -1410,6 +1479,32 @@ function sendCommandTo(sock, req) {
|
|
|
1410
1479
|
return requestSocket(sock, req, "daemon not running");
|
|
1411
1480
|
}
|
|
1412
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
|
+
|
|
1413
1508
|
// send to the daemon selected by @name / --daemon / $PTY_DAEMON.
|
|
1414
1509
|
function sendCommand(req) {
|
|
1415
1510
|
return requestSocket(SOCKET_PATH, req, "daemon not running. start with: pty-mgr daemon");
|
|
@@ -1437,11 +1532,19 @@ function attachToSession(name) {
|
|
|
1437
1532
|
});
|
|
1438
1533
|
|
|
1439
1534
|
conn.on("connect", () => {
|
|
1440
|
-
// send attach request
|
|
1441
|
-
|
|
1535
|
+
// send attach request with our real terminal size so the daemon can size
|
|
1536
|
+
// the session (and its child) to us. undefined when stdout isn't a TTY;
|
|
1537
|
+
// the daemon then leaves the session size unchanged.
|
|
1538
|
+
conn.write(JSON.stringify({
|
|
1539
|
+
cmd: "attach",
|
|
1540
|
+
name,
|
|
1541
|
+
cols: process.stdout.columns,
|
|
1542
|
+
rows: process.stdout.rows,
|
|
1543
|
+
}) + "\n");
|
|
1442
1544
|
|
|
1443
1545
|
let gotAck = false;
|
|
1444
1546
|
let headerBuf = "";
|
|
1547
|
+
let onStdoutResize = null;
|
|
1445
1548
|
|
|
1446
1549
|
// last-seen alt-screen state: detach pops the client back to its
|
|
1447
1550
|
// normal screen only if the session left it in the alternate buffer
|
|
@@ -1481,11 +1584,11 @@ function attachToSession(name) {
|
|
|
1481
1584
|
gotAck = true;
|
|
1482
1585
|
altActive = !!ack.alt;
|
|
1483
1586
|
|
|
1484
|
-
// resize
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1587
|
+
// NOTE: we do NOT resize our own terminal to the session. The daemon
|
|
1588
|
+
// has already sized the session to us (we sent our size in the attach
|
|
1589
|
+
// request). An app-driven CSI-8 window resize is ignored inside
|
|
1590
|
+
// tmux/iTerm panes and, where honored, resizes the user's entire
|
|
1591
|
+
// window -- which is what caused the flicker and the missing bottom row.
|
|
1489
1592
|
|
|
1490
1593
|
// put terminal in raw mode
|
|
1491
1594
|
process.stdin.setRawMode(true);
|
|
@@ -1509,6 +1612,15 @@ function attachToSession(name) {
|
|
|
1509
1612
|
conn.write(key);
|
|
1510
1613
|
});
|
|
1511
1614
|
|
|
1615
|
+
// live-resize: when our terminal changes size, tell the daemon so it
|
|
1616
|
+
// resizes the session (and its child's winsize) to match. Sent as an
|
|
1617
|
+
// out-of-band APC frame the daemon strips from the input stream.
|
|
1618
|
+
onStdoutResize = () => {
|
|
1619
|
+
const c = process.stdout.columns, r = process.stdout.rows;
|
|
1620
|
+
if (c && r) { try { conn.write(`\x1b_ptymgr:resize:${c}:${r}\x1b\\`); } catch {} }
|
|
1621
|
+
};
|
|
1622
|
+
process.stdout.on("resize", onStdoutResize);
|
|
1623
|
+
|
|
1512
1624
|
return;
|
|
1513
1625
|
}
|
|
1514
1626
|
|
|
@@ -1525,6 +1637,10 @@ function attachToSession(name) {
|
|
|
1525
1637
|
function detach() {
|
|
1526
1638
|
if (detached || failed) return;
|
|
1527
1639
|
detached = true;
|
|
1640
|
+
if (onStdoutResize) {
|
|
1641
|
+
process.stdout.removeListener("resize", onStdoutResize);
|
|
1642
|
+
onStdoutResize = null;
|
|
1643
|
+
}
|
|
1528
1644
|
if (process.stdin.isRaw) {
|
|
1529
1645
|
process.stdin.setRawMode(false);
|
|
1530
1646
|
process.stdin.pause();
|
|
@@ -1552,6 +1668,7 @@ const USAGE = `pty-mgr - PTY session manager
|
|
|
1552
1668
|
usage:
|
|
1553
1669
|
p daemon start daemon (background: &)
|
|
1554
1670
|
p daemon @myproject named daemon (isolated sessions)
|
|
1671
|
+
p daemons list all running daemons (* = current)
|
|
1555
1672
|
p status daemon info + config
|
|
1556
1673
|
p config show current config
|
|
1557
1674
|
p config screen 100x50 set default terminal size
|
|
@@ -2969,32 +3086,13 @@ async function cli() {
|
|
|
2969
3086
|
const target = args[0]; // "all" or undefined (= current daemon)
|
|
2970
3087
|
if (target === "all") {
|
|
2971
3088
|
// find all pty-manager sockets and shut them down
|
|
2972
|
-
const
|
|
2973
|
-
|
|
2974
|
-
try {
|
|
2975
|
-
socks = readdirSync(dir).filter((f) => f.endsWith(".sock"));
|
|
2976
|
-
} catch { /* dir doesn't exist */ }
|
|
2977
|
-
// also check legacy /tmp/ location for old sockets
|
|
2978
|
-
try {
|
|
2979
|
-
const tmp = tmpdir();
|
|
2980
|
-
const legacy = readdirSync(tmp).filter((f) => f.startsWith("pty-manager-") && f.endsWith(".sock"));
|
|
2981
|
-
for (const s of legacy) socks.push("__legacy__/" + s);
|
|
2982
|
-
} catch {}
|
|
2983
|
-
if (socks.length === 0) {
|
|
3089
|
+
const daemons = listDaemonSockets();
|
|
3090
|
+
if (daemons.length === 0) {
|
|
2984
3091
|
console.log("no daemons running");
|
|
2985
3092
|
return;
|
|
2986
3093
|
}
|
|
2987
3094
|
const stopped = [];
|
|
2988
|
-
for (const
|
|
2989
|
-
let sockFile, name;
|
|
2990
|
-
if (sock.startsWith("__legacy__/")) {
|
|
2991
|
-
const legacyName = sock.replace("__legacy__/", "");
|
|
2992
|
-
sockFile = join(tmpdir(), legacyName);
|
|
2993
|
-
name = legacyName.replace("pty-manager-", "").replace(".sock", "");
|
|
2994
|
-
} else {
|
|
2995
|
-
sockFile = join(dir, sock);
|
|
2996
|
-
name = sock.replace(".sock", "");
|
|
2997
|
-
}
|
|
3095
|
+
for (const { name, sockFile } of daemons) {
|
|
2998
3096
|
try {
|
|
2999
3097
|
const res = await sendCommandTo(sockFile, { cmd: "shutdown" });
|
|
3000
3098
|
if (res.ok) stopped.push(name);
|
|
@@ -3017,6 +3115,36 @@ async function cli() {
|
|
|
3017
3115
|
return;
|
|
3018
3116
|
}
|
|
3019
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
|
+
|
|
3020
3148
|
if (command === "attach") {
|
|
3021
3149
|
const name = args[0];
|
|
3022
3150
|
if (!name) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentiko/pty-mgr",
|
|
3
|
-
"version": "1.
|
|
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.
|
|
30
|
-
"@mentiko/pty-mgr-linux-arm64": "1.
|
|
31
|
-
"@mentiko/pty-mgr-darwin-x64": "1.
|
|
32
|
-
"@mentiko/pty-mgr-darwin-arm64": "1.
|
|
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"
|