tmuxes 0.1.9 → 0.1.11

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.
Files changed (142) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +283 -296
  3. package/{server/bin → bin}/tmuxes.js +36 -36
  4. package/dist/agentHooks.js +91 -0
  5. package/dist/agentHooks.js.map +1 -0
  6. package/dist/agentOutput.js +30 -0
  7. package/dist/agentOutput.js.map +1 -0
  8. package/dist/agentState.js +45 -0
  9. package/dist/agentState.js.map +1 -0
  10. package/dist/config.js +32 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/exe.js +37 -0
  13. package/dist/exe.js.map +1 -0
  14. package/dist/exec.js +43 -0
  15. package/dist/exec.js.map +1 -0
  16. package/dist/files.js +308 -0
  17. package/dist/files.js.map +1 -0
  18. package/dist/foldersStore.js +103 -0
  19. package/dist/foldersStore.js.map +1 -0
  20. package/dist/index.js +117 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/logger.js +16 -0
  23. package/dist/logger.js.map +1 -0
  24. package/{server/src/monitor.ts → dist/monitor.js} +9 -10
  25. package/dist/monitor.js.map +1 -0
  26. package/dist/openBrowser.js +31 -0
  27. package/dist/openBrowser.js.map +1 -0
  28. package/{server/src/platform.ts → dist/platform.js} +5 -4
  29. package/dist/platform.js.map +1 -0
  30. package/dist/rest/router.js +198 -0
  31. package/dist/rest/router.js.map +1 -0
  32. package/dist/targetCommand.js +60 -0
  33. package/dist/targetCommand.js.map +1 -0
  34. package/dist/targets.js +131 -0
  35. package/dist/targets.js.map +1 -0
  36. package/dist/tmux/builder.js +174 -0
  37. package/dist/tmux/builder.js.map +1 -0
  38. package/dist/tmux/formats.js +61 -0
  39. package/dist/tmux/formats.js.map +1 -0
  40. package/dist/tmux/sessions.js +157 -0
  41. package/dist/tmux/sessions.js.map +1 -0
  42. package/dist/validate.js +65 -0
  43. package/dist/validate.js.map +1 -0
  44. package/dist/windowsSsh.js +209 -0
  45. package/dist/windowsSsh.js.map +1 -0
  46. package/dist/winshell/manager.js +267 -0
  47. package/dist/winshell/manager.js.map +1 -0
  48. package/dist/ws/protocol.js +4 -0
  49. package/dist/ws/protocol.js.map +1 -0
  50. package/dist/ws/sshState.js +35 -0
  51. package/dist/ws/sshState.js.map +1 -0
  52. package/dist/ws/terminalSession.js +204 -0
  53. package/dist/ws/terminalSession.js.map +1 -0
  54. package/dist/ws/wsServer.js +151 -0
  55. package/dist/ws/wsServer.js.map +1 -0
  56. package/dist/wsl.js +35 -0
  57. package/dist/wsl.js.map +1 -0
  58. package/package.json +61 -28
  59. package/public/assets/index-D_X5SnGx.css +1 -0
  60. package/public/assets/index-Dl69CPyt.js +44 -0
  61. package/{client → public}/index.html +13 -12
  62. package/.node-version +0 -1
  63. package/.nvmrc +0 -1
  64. package/.tmp-npm-cache/_cacache/content-v2/sha512/43/27/5e000b8b9c56a6ccc66f709485499f4304e2cb1982582ba571321c07b3ef56fcabd2c671898cc8003365a0485b6fd8e73e7b17b073cec0f7d1628c1a99df +0 -0
  65. package/.tmp-npm-cache/_cacache/content-v2/sha512/51/cf/4301295d74559ed494bae160d54d8741077f89faebb311882ac065019246951e7b53f3dcb913793c42b331e14c7070c4810c3cdc27a427d103a7db4614e0 +0 -0
  66. package/.tmp-npm-cache/_cacache/content-v2/sha512/c3/4d/d68a454a916e74c2617f586fbf770981b33811d667c2547eb0e9fc21938f4ee7e98f1ceee4bde8ad8815b5f6efe21b60eee798837d68f51a3340d7e5bb7a +0 -0
  67. package/.tmp-npm-cache/_cacache/content-v2/sha512/fe/40/2abfbefc96299e8bf714aa91d62607190ae299e102cf5933db2e2904640d65d25d67dbbb6fa2ddc92a17f00b9dbfdf2e37487f67d96ec36c64a285b59a7d +0 -0
  68. package/.tmp-npm-cache/_cacache/index-v5/27/fe/81a3de6ce7ae3d1e41a3421de20c5629998c4ee5d0ffe2037630f03b03b2 +0 -4
  69. package/.tmp-npm-cache/_cacache/index-v5/65/22/dd66711f62681fce09aabb2357a2907b4a0c778ac5227c4baf9603fd86e8 +0 -4
  70. package/.tmp-npm-cache/_update-notifier-last-checked +0 -0
  71. package/AGENTS.md +0 -15
  72. package/CLAUDE.md +0 -3
  73. package/README.en.md +0 -304
  74. package/SECURITY.md +0 -31
  75. package/client/package.json +0 -29
  76. package/client/src/App.tsx +0 -123
  77. package/client/src/activity.ts +0 -5
  78. package/client/src/api.ts +0 -130
  79. package/client/src/attention.tsx +0 -157
  80. package/client/src/components/FileExplorer.tsx +0 -156
  81. package/client/src/components/FileViewer.tsx +0 -194
  82. package/client/src/components/SessionRow.tsx +0 -108
  83. package/client/src/components/SessionTree.tsx +0 -197
  84. package/client/src/components/SettingsButton.tsx +0 -122
  85. package/client/src/components/Sidebar.tsx +0 -96
  86. package/client/src/components/StatusBanner.tsx +0 -31
  87. package/client/src/components/TargetGroup.tsx +0 -275
  88. package/client/src/components/TerminalPanel.tsx +0 -192
  89. package/client/src/folders.ts +0 -245
  90. package/client/src/hooks/useTerminal.ts +0 -67
  91. package/client/src/hooks/useTmuxSocket.ts +0 -65
  92. package/client/src/i18n.ts +0 -213
  93. package/client/src/main.tsx +0 -17
  94. package/client/src/settings.tsx +0 -87
  95. package/client/src/styles.css +0 -723
  96. package/client/src/types.ts +0 -93
  97. package/client/src/util.ts +0 -65
  98. package/client/tsconfig.json +0 -13
  99. package/client/vite.config.ts +0 -15
  100. package/fig/fig1.png +0 -0
  101. package/scripts/prepack.mjs +0 -35
  102. package/server/package.json +0 -61
  103. package/server/src/agentHooks.ts +0 -120
  104. package/server/src/agentOutput.ts +0 -36
  105. package/server/src/agentState.ts +0 -70
  106. package/server/src/config.ts +0 -31
  107. package/server/src/exe.ts +0 -34
  108. package/server/src/exec.ts +0 -61
  109. package/server/src/files.ts +0 -330
  110. package/server/src/foldersStore.ts +0 -114
  111. package/server/src/index.ts +0 -114
  112. package/server/src/logger.ts +0 -16
  113. package/server/src/openBrowser.ts +0 -28
  114. package/server/src/rest/router.ts +0 -290
  115. package/server/src/targetCommand.ts +0 -79
  116. package/server/src/targets.ts +0 -152
  117. package/server/src/tmux/builder.ts +0 -198
  118. package/server/src/tmux/formats.ts +0 -95
  119. package/server/src/tmux/sessions.ts +0 -204
  120. package/server/src/validate.ts +0 -79
  121. package/server/src/windowsSsh.ts +0 -239
  122. package/server/src/winshell/manager.ts +0 -296
  123. package/server/src/ws/protocol.ts +0 -15
  124. package/server/src/ws/sshState.ts +0 -36
  125. package/server/src/ws/terminalSession.ts +0 -207
  126. package/server/src/ws/wsServer.ts +0 -153
  127. package/server/src/wsl.ts +0 -38
  128. package/server/test/agentHooks.test.ts +0 -66
  129. package/server/test/agentOutput.test.ts +0 -26
  130. package/server/test/agentState.test.ts +0 -24
  131. package/server/test/builder.test.ts +0 -162
  132. package/server/test/files.test.ts +0 -81
  133. package/server/test/formats.test.ts +0 -123
  134. package/server/test/monitor.test.ts +0 -25
  135. package/server/test/validate.test.ts +0 -71
  136. package/server/test/wsl.test.ts +0 -18
  137. package/server/tsconfig.json +0 -9
  138. package/server/vitest.config.ts +0 -12
  139. package/start.cmd +0 -30
  140. package/start.command +0 -20
  141. package/start.sh +0 -20
  142. package/tsconfig.base.json +0 -19
@@ -0,0 +1,267 @@
1
+ import * as pty from 'node-pty';
2
+ import { existsSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { isWindows } from '../platform.js';
6
+ import { log } from '../logger.js';
7
+ const SCROLLBACK_CAP = 256 * 1024; // bytes of history replayed to a new client
8
+ function nowEpoch() {
9
+ return Math.floor(Date.now() / 1000);
10
+ }
11
+ /** One persistent native shell (its own pty), shared by 0..N attached clients.
12
+ * Survives client disconnects (the pty lives until killed or it exits), which
13
+ * is what gives "refresh / reconnect / multi-tab" persistence. */
14
+ export class WinShellSession {
15
+ name;
16
+ shell;
17
+ ptyProc;
18
+ onClosed;
19
+ created = nowEpoch();
20
+ clients = new Set();
21
+ scroll = [];
22
+ scrollBytes = 0;
23
+ disposed = false;
24
+ /** epoch seconds of last pty output — drives idle/attention detection */
25
+ lastActivity = nowEpoch();
26
+ constructor(name, shell, ptyProc, onClosed) {
27
+ this.name = name;
28
+ this.shell = shell;
29
+ this.ptyProc = ptyProc;
30
+ this.onClosed = onClosed;
31
+ this.ptyProc.onData((d) => this.onData(d));
32
+ this.ptyProc.onExit(({ exitCode }) => this.onExit(exitCode));
33
+ }
34
+ get attached() {
35
+ return this.clients.size > 0;
36
+ }
37
+ onData(data) {
38
+ this.lastActivity = nowEpoch();
39
+ const buf = Buffer.from(data, 'utf8');
40
+ this.scroll.push(buf);
41
+ this.scrollBytes += buf.length;
42
+ while (this.scrollBytes > SCROLLBACK_CAP && this.scroll.length > 1) {
43
+ this.scrollBytes -= this.scroll.shift().length;
44
+ }
45
+ for (const c of this.clients)
46
+ if (c.isOpen())
47
+ c.sendBinary(buf);
48
+ }
49
+ onExit(code) {
50
+ if (this.disposed)
51
+ return;
52
+ this.disposed = true;
53
+ for (const c of this.clients) {
54
+ c.sendControl({ type: 'exit', code });
55
+ c.close(1000, 'shell exited');
56
+ }
57
+ this.clients.clear();
58
+ this.onClosed(this.name);
59
+ }
60
+ /** Add a client: replay history, then it's live. Caller sends `ready` after. */
61
+ attach(client) {
62
+ if (this.scroll.length)
63
+ client.sendBinary(Buffer.concat(this.scroll));
64
+ this.clients.add(client);
65
+ }
66
+ detach(client) {
67
+ this.clients.delete(client); // pty stays alive — persistence across reconnects
68
+ }
69
+ write(data) {
70
+ if (!this.disposed)
71
+ this.ptyProc.write(data);
72
+ }
73
+ resize(cols, rows) {
74
+ try {
75
+ this.ptyProc.resize(cols, rows);
76
+ }
77
+ catch {
78
+ /* pty may have exited */
79
+ }
80
+ }
81
+ info() {
82
+ return {
83
+ name: this.name,
84
+ windows: 1,
85
+ attached: this.attached,
86
+ created: this.created,
87
+ lastActivity: this.lastActivity,
88
+ };
89
+ }
90
+ dispose() {
91
+ if (this.disposed) {
92
+ this.onClosed(this.name);
93
+ return;
94
+ }
95
+ this.disposed = true;
96
+ try {
97
+ this.ptyProc.kill();
98
+ }
99
+ catch {
100
+ /* already gone */
101
+ }
102
+ for (const c of this.clients)
103
+ c.close(1000, 'killed');
104
+ this.clients.clear();
105
+ this.onClosed(this.name);
106
+ }
107
+ }
108
+ const NAME_RE = /^[A-Za-z0-9_-]{1,64}$/;
109
+ /** Owns all native shell sessions for the local Windows machine. */
110
+ export class WinShellManager {
111
+ sessions = new Map();
112
+ shells;
113
+ constructor(shells) {
114
+ this.shells = shells ?? detectShells();
115
+ }
116
+ listShells() {
117
+ return this.shells.map((s) => ({ id: s.id, label: s.label }));
118
+ }
119
+ resolveShell(shellId) {
120
+ if (shellId) {
121
+ const found = this.shells.find((s) => s.id === shellId);
122
+ if (found)
123
+ return found;
124
+ }
125
+ if (this.shells.length === 0)
126
+ throw new Error('no shell available');
127
+ return this.shells[0];
128
+ }
129
+ list() {
130
+ return [...this.sessions.values()]
131
+ .map((s) => s.info())
132
+ .sort((a, b) => a.created - b.created);
133
+ }
134
+ windows(name) {
135
+ const s = this.sessions.get(name);
136
+ if (!s)
137
+ return [];
138
+ return [{ index: 0, name: s.shell.label, panes: 1, active: true }];
139
+ }
140
+ has(name) {
141
+ return this.sessions.has(name);
142
+ }
143
+ autoName(shell) {
144
+ for (let i = 1; i < 10000; i++) {
145
+ const candidate = `${shell.id}-${i}`;
146
+ if (!this.sessions.has(candidate))
147
+ return candidate;
148
+ }
149
+ return `${shell.id}-${nowEpoch()}`;
150
+ }
151
+ /** Create a new shell session. Returns the (possibly auto-assigned) name. */
152
+ create(opts) {
153
+ const shell = this.resolveShell(opts.shellId);
154
+ const name = opts.name ?? this.autoName(shell);
155
+ if (!NAME_RE.test(name))
156
+ throw new ManagerError(400, 'invalid session name');
157
+ if (this.sessions.has(name))
158
+ throw new ManagerError(409, `session "${name}" already exists`);
159
+ const cols = opts.cols ?? 80;
160
+ const rows = opts.rows ?? 24;
161
+ let proc;
162
+ try {
163
+ proc = pty.spawn(shell.file, shell.args, {
164
+ name: 'xterm-256color',
165
+ cols,
166
+ rows,
167
+ cwd: homedir(),
168
+ env: { ...process.env, TERM: 'xterm-256color' },
169
+ });
170
+ }
171
+ catch (e) {
172
+ throw new ManagerError(502, `failed to start ${shell.label}: ${e.message}`);
173
+ }
174
+ const session = new WinShellSession(name, shell, proc, (n) => this.sessions.delete(n));
175
+ this.sessions.set(name, session);
176
+ if (opts.command && opts.command.length > 0)
177
+ session.write(`${opts.command}\r`);
178
+ log.info(`winshell created ${name} (${shell.label})`);
179
+ return { name };
180
+ }
181
+ rename(oldName, newName) {
182
+ if (!NAME_RE.test(newName))
183
+ throw new ManagerError(400, 'invalid new session name');
184
+ const s = this.sessions.get(oldName);
185
+ if (!s)
186
+ throw new ManagerError(404, `session "${oldName}" not found`);
187
+ if (this.sessions.has(newName))
188
+ throw new ManagerError(409, `session "${newName}" already exists`);
189
+ // Re-key the same live instance (its pty handlers read `this.name`).
190
+ this.sessions.delete(oldName);
191
+ s.name = newName;
192
+ this.sessions.set(newName, s);
193
+ }
194
+ kill(name) {
195
+ const s = this.sessions.get(name);
196
+ if (!s)
197
+ throw new ManagerError(404, `session "${name}" not found`);
198
+ s.dispose();
199
+ }
200
+ /** Attach a client to an existing session, creating it (default shell) if missing. */
201
+ attachOrCreate(name, cols, rows, client) {
202
+ let s = this.sessions.get(name);
203
+ if (!s) {
204
+ this.create({ name, cols, rows });
205
+ s = this.sessions.get(name);
206
+ }
207
+ else {
208
+ s.resize(cols, rows);
209
+ }
210
+ s.attach(client);
211
+ return s;
212
+ }
213
+ disposeAll() {
214
+ for (const s of [...this.sessions.values()])
215
+ s.dispose();
216
+ }
217
+ }
218
+ /** Manager error carrying an HTTP status for the router. */
219
+ export class ManagerError extends Error {
220
+ status;
221
+ constructor(status, message) {
222
+ super(message);
223
+ this.status = status;
224
+ this.name = 'ManagerError';
225
+ }
226
+ }
227
+ /** Detect launchable shells for this machine. Windows: PowerShell 7 (if on
228
+ * PATH) → Windows PowerShell → cmd → Git Bash. Otherwise (test/fake): the
229
+ * user's shell, so the whole path can be exercised on Linux. */
230
+ export function detectShells() {
231
+ if (!isWindows) {
232
+ const file = process.env.SHELL || '/bin/bash';
233
+ return [{ id: 'shell', label: file.split('/').pop() || 'shell', file, args: ['-i'] }];
234
+ }
235
+ const shells = [];
236
+ const sysRoot = process.env.SystemRoot || process.env.windir || 'C:\\Windows';
237
+ const pwsh = findOnPath('pwsh.exe');
238
+ if (pwsh)
239
+ shells.push({ id: 'pwsh', label: 'PowerShell 7', file: pwsh, args: ['-NoLogo'] });
240
+ const winPs = join(sysRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
241
+ if (existsSync(winPs))
242
+ shells.push({ id: 'powershell', label: 'Windows PowerShell', file: winPs, args: ['-NoLogo'] });
243
+ const cmd = join(sysRoot, 'System32', 'cmd.exe');
244
+ if (existsSync(cmd))
245
+ shells.push({ id: 'cmd', label: 'Command Prompt', file: cmd, args: [] });
246
+ const gitBash = 'C:\\Program Files\\Git\\bin\\bash.exe';
247
+ if (existsSync(gitBash))
248
+ shells.push({ id: 'gitbash', label: 'Git Bash', file: gitBash, args: ['-i', '-l'] });
249
+ // Last-resort fallback so the target is never shell-less.
250
+ if (shells.length === 0)
251
+ shells.push({ id: 'cmd', label: 'Command Prompt', file: 'cmd.exe', args: [] });
252
+ return shells;
253
+ }
254
+ function findOnPath(exe) {
255
+ const dirs = (process.env.PATH || '').split(isWindows ? ';' : ':');
256
+ for (const dir of dirs) {
257
+ if (!dir)
258
+ continue;
259
+ const candidate = join(dir, exe);
260
+ if (existsSync(candidate))
261
+ return candidate;
262
+ }
263
+ return null;
264
+ }
265
+ /** Singleton used by the REST and WS layers. */
266
+ export const winShell = new WinShellManager();
267
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/winshell/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** Shared WebSocket control-message types. Terminal bytes travel as BINARY
2
+ * frames; everything below travels as TEXT (JSON) frames. */
3
+ export {};
4
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/ws/protocol.ts"],"names":[],"mappings":"AAAA;8DAC8D"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Classify ssh failure/prompt states from PTY output so the UI can show
3
+ * something better than a black screen. Best-effort string matching on the
4
+ * raw ssh client output.
5
+ */
6
+ const MATCHERS = [
7
+ {
8
+ state: 'hostkey',
9
+ re: /authenticity of host|fingerprint|known_hosts|REMOTE HOST IDENTIFICATION HAS CHANGED/i,
10
+ message: 'Unknown or changed host key — verify the host in a regular terminal first.',
11
+ },
12
+ {
13
+ state: 'authfail',
14
+ re: /permission denied|too many authentication failures|no such identity|authentication failed/i,
15
+ message: 'SSH authentication failed — check your keys / ssh-agent.',
16
+ },
17
+ {
18
+ state: 'refused',
19
+ re: /connection refused|could not resolve hostname|name or service not known|no route to host/i,
20
+ message: 'Could not connect to the host (refused / unresolved).',
21
+ },
22
+ {
23
+ state: 'timeout',
24
+ re: /connection timed out|operation timed out|timed out waiting/i,
25
+ message: 'Connection timed out.',
26
+ },
27
+ ];
28
+ export function classifySsh(text) {
29
+ for (const m of MATCHERS) {
30
+ if (m.re.test(text))
31
+ return { state: m.state, message: m.message };
32
+ }
33
+ return null;
34
+ }
35
+ //# sourceMappingURL=sshState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sshState.js","sourceRoot":"","sources":["../../src/ws/sshState.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,QAAQ,GAAuD;IACnE;QACE,KAAK,EAAE,SAAS;QAChB,EAAE,EAAE,sFAAsF;QAC1F,OAAO,EAAE,4EAA4E;KACtF;IACD;QACE,KAAK,EAAE,UAAU;QACjB,EAAE,EAAE,4FAA4F;QAChG,OAAO,EAAE,0DAA0D;KACpE;IACD;QACE,KAAK,EAAE,SAAS;QAChB,EAAE,EAAE,2FAA2F;QAC/F,OAAO,EAAE,uDAAuD;KACjE;IACD;QACE,KAAK,EAAE,SAAS;QAChB,EAAE,EAAE,6DAA6D;QACjE,OAAO,EAAE,uBAAuB;KACjC;CACF,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IACrE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,204 @@
1
+ import * as pty from 'node-pty';
2
+ import { homedir } from 'node:os';
3
+ import { attachArgv } from '../tmux/builder.js';
4
+ import { resolveExecutable } from '../exe.js';
5
+ import { classifySsh } from './sshState.js';
6
+ import { log } from '../logger.js';
7
+ const HEARTBEAT_MS = 30_000;
8
+ const HIGH_WATER = 1 << 20; // 1 MiB buffered → pause the PTY
9
+ const LOW_WATER = 1 << 18; // 256 KiB → resume
10
+ const KILL_GRACE_MS = 2_000;
11
+ /** Owns exactly one PTY for one WebSocket. dispose() is idempotent. */
12
+ export class TerminalSession {
13
+ ws;
14
+ target;
15
+ session;
16
+ ptyProc;
17
+ disposed = false;
18
+ alive = true;
19
+ paused = false;
20
+ heartbeat;
21
+ drainTimer;
22
+ killTimer;
23
+ /** Scan ssh output for failure/prompt states until the link looks healthy. */
24
+ sshScanBudget;
25
+ constructor(ws, target, session, cols, rows) {
26
+ this.ws = ws;
27
+ this.target = target;
28
+ this.session = session;
29
+ this.sshScanBudget = target.kind === 'ssh' ? 8192 : 0;
30
+ const { file, args } = attachArgv(target, session);
31
+ // node-pty on Windows needs a full exe path (no PATH/.exe resolution).
32
+ this.ptyProc = pty.spawn(resolveExecutable(file), args, {
33
+ name: 'xterm-256color',
34
+ cols,
35
+ rows,
36
+ cwd: homedir(),
37
+ env: { ...process.env, TERM: 'xterm-256color' },
38
+ });
39
+ this.ptyProc.onData((data) => this.onPtyData(data));
40
+ this.ptyProc.onExit(({ exitCode }) => this.onPtyExit(exitCode));
41
+ this.ws.on('message', (data, isBinary) => this.onClientMessage(data, isBinary));
42
+ this.ws.on('close', () => this.dispose());
43
+ this.ws.on('error', () => this.dispose());
44
+ this.ws.on('pong', () => {
45
+ this.alive = true;
46
+ });
47
+ this.heartbeat = setInterval(() => this.tick(), HEARTBEAT_MS);
48
+ this.sendControl({ type: 'ready', target: target.id, session });
49
+ }
50
+ onPtyData(data) {
51
+ if (this.sshScanBudget > 0) {
52
+ this.sshScanBudget -= data.length;
53
+ const ssh = classifySsh(data);
54
+ if (ssh)
55
+ this.sendControl({ type: 'ssh', state: ssh.state, message: ssh.message });
56
+ }
57
+ this.sendBinary(Buffer.from(data, 'utf8'));
58
+ }
59
+ onPtyExit(exitCode) {
60
+ this.sendControl({ type: 'exit', code: exitCode });
61
+ this.closeWs(1000, 'pty exited');
62
+ this.dispose();
63
+ }
64
+ onClientMessage(data, isBinary) {
65
+ if (this.disposed)
66
+ return;
67
+ if (isBinary) {
68
+ // Raw keystrokes → straight into the PTY.
69
+ this.ptyProc.write(toBufferString(data));
70
+ return;
71
+ }
72
+ let msg;
73
+ try {
74
+ msg = JSON.parse(toBufferString(data));
75
+ }
76
+ catch {
77
+ return; // ignore malformed control frames
78
+ }
79
+ if (msg.type === 'resize') {
80
+ const cols = clampDim(msg.cols);
81
+ const rows = clampDim(msg.rows);
82
+ if (cols && rows) {
83
+ try {
84
+ this.ptyProc.resize(cols, rows);
85
+ }
86
+ catch {
87
+ /* pty may have exited */
88
+ }
89
+ }
90
+ }
91
+ else if (msg.type === 'ping') {
92
+ this.sendControl({ type: 'pong' });
93
+ }
94
+ }
95
+ sendBinary(buf) {
96
+ if (this.disposed || this.ws.readyState !== this.ws.OPEN)
97
+ return;
98
+ this.ws.send(buf, { binary: true });
99
+ if (!this.paused && this.ws.bufferedAmount > HIGH_WATER) {
100
+ this.paused = true;
101
+ this.ptyProc.pause();
102
+ this.drainTimer = setInterval(() => this.checkDrain(), 50);
103
+ }
104
+ }
105
+ checkDrain() {
106
+ if (this.disposed)
107
+ return;
108
+ if (this.ws.bufferedAmount < LOW_WATER) {
109
+ this.paused = false;
110
+ if (this.drainTimer)
111
+ clearInterval(this.drainTimer);
112
+ this.drainTimer = undefined;
113
+ this.ptyProc.resume();
114
+ }
115
+ }
116
+ sendControl(msg) {
117
+ if (this.disposed || this.ws.readyState !== this.ws.OPEN)
118
+ return;
119
+ this.ws.send(JSON.stringify(msg), { binary: false });
120
+ }
121
+ tick() {
122
+ if (!this.alive) {
123
+ log.warn(`heartbeat lost for ${this.target.id}/${this.session}, terminating`);
124
+ try {
125
+ this.ws.terminate();
126
+ }
127
+ catch {
128
+ /* ignore */
129
+ }
130
+ this.dispose();
131
+ return;
132
+ }
133
+ this.alive = false;
134
+ try {
135
+ this.ws.ping();
136
+ }
137
+ catch {
138
+ /* ignore */
139
+ }
140
+ }
141
+ closeWs(code, reason) {
142
+ try {
143
+ if (this.ws.readyState === this.ws.OPEN)
144
+ this.ws.close(code, reason);
145
+ }
146
+ catch {
147
+ /* ignore */
148
+ }
149
+ }
150
+ /** Idempotent teardown — called from pty exit, ws close, and shutdown. */
151
+ dispose() {
152
+ if (this.disposed)
153
+ return;
154
+ this.disposed = true;
155
+ if (this.heartbeat)
156
+ clearInterval(this.heartbeat);
157
+ if (this.drainTimer)
158
+ clearInterval(this.drainTimer);
159
+ try {
160
+ this.ptyProc.kill(); // SIGHUP → tmux client detaches; session keeps running
161
+ }
162
+ catch {
163
+ /* already gone */
164
+ }
165
+ // Force-kill if it lingers.
166
+ this.killTimer = setTimeout(() => {
167
+ try {
168
+ this.ptyProc.kill('SIGKILL');
169
+ }
170
+ catch {
171
+ /* already gone */
172
+ }
173
+ }, KILL_GRACE_MS);
174
+ this.killTimer.unref?.();
175
+ this.closeWs(1000, 'disposed');
176
+ registry.delete(this);
177
+ }
178
+ }
179
+ function toBufferString(data) {
180
+ if (typeof data === 'string')
181
+ return data;
182
+ if (Buffer.isBuffer(data))
183
+ return data.toString('utf8');
184
+ if (Array.isArray(data))
185
+ return Buffer.concat(data).toString('utf8');
186
+ if (data instanceof ArrayBuffer)
187
+ return Buffer.from(data).toString('utf8');
188
+ return String(data);
189
+ }
190
+ function clampDim(n) {
191
+ if (typeof n !== 'number' || !Number.isInteger(n) || n < 1 || n > 1000)
192
+ return null;
193
+ return n;
194
+ }
195
+ /** All live sessions, so the process can tear them down on shutdown. */
196
+ export const registry = new Set();
197
+ export function track(s) {
198
+ registry.add(s);
199
+ }
200
+ export function disposeAll() {
201
+ for (const s of [...registry])
202
+ s.dispose();
203
+ }
204
+ //# sourceMappingURL=terminalSession.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminalSession.js","sourceRoot":"","sources":["../../src/ws/terminalSession.ts"],"names":[],"mappings":"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"}