@ebowwa/terminal 0.2.1 → 0.3.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/src/tmux-local.js DELETED
@@ -1,937 +0,0 @@
1
- "use strict";
2
- /**
3
- * tmux-based Local Terminal Sessions
4
- * Provides persistent terminal sessions using local tmux for SSH connections
5
- * SSH connections stay active within tmux sessions on the local machine
6
- */
7
- var __assign = (this && this.__assign) || function () {
8
- __assign = Object.assign || function(t) {
9
- for (var s, i = 1, n = arguments.length; i < n; i++) {
10
- s = arguments[i];
11
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
12
- t[p] = s[p];
13
- }
14
- return t;
15
- };
16
- return __assign.apply(this, arguments);
17
- };
18
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
19
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
20
- return new (P || (P = Promise))(function (resolve, reject) {
21
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
22
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
23
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
24
- step((generator = generator.apply(thisArg, _arguments || [])).next());
25
- });
26
- };
27
- var __generator = (this && this.__generator) || function (thisArg, body) {
28
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
29
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
30
- function verb(n) { return function (v) { return step([n, v]); }; }
31
- function step(op) {
32
- if (f) throw new TypeError("Generator is already executing.");
33
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
34
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
35
- if (y = 0, t) op = [op[0] & 2, t.value];
36
- switch (op[0]) {
37
- case 0: case 1: t = op; break;
38
- case 4: _.label++; return { value: op[1], done: false };
39
- case 5: _.label++; y = op[1]; op = [0]; continue;
40
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
41
- default:
42
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
43
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
44
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
45
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
46
- if (t[2]) _.ops.pop();
47
- _.trys.pop(); continue;
48
- }
49
- op = body.call(thisArg, _);
50
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
51
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
52
- }
53
- };
54
- Object.defineProperty(exports, "__esModule", { value: true });
55
- exports.isLocalTmuxInstalled = isLocalTmuxInstalled;
56
- exports.generateLocalSessionName = generateLocalSessionName;
57
- exports.listLocalSessions = listLocalSessions;
58
- exports.hasLocalSession = hasLocalSession;
59
- exports.createLocalTmuxSSHSession = createLocalTmuxSSHSession;
60
- exports.sendCommandToLocalSession = sendCommandToLocalSession;
61
- exports.captureLocalPane = captureLocalPane;
62
- exports.getLocalPaneHistory = getLocalPaneHistory;
63
- exports.killLocalSession = killLocalSession;
64
- exports.getLocalSessionInfo = getLocalSessionInfo;
65
- exports.listLocalSessionWindows = listLocalSessionWindows;
66
- exports.listLocalWindowPanes = listLocalWindowPanes;
67
- exports.splitLocalPane = splitLocalPane;
68
- exports.cleanupOldLocalSessions = cleanupOldLocalSessions;
69
- exports.getLocalTmuxResourceUsage = getLocalTmuxResourceUsage;
70
- exports.waitForTextInPane = waitForTextInPane;
71
- exports.switchLocalWindow = switchLocalWindow;
72
- exports.switchLocalPane = switchLocalPane;
73
- exports.renameLocalWindow = renameLocalWindow;
74
- exports.killLocalPane = killLocalPane;
75
- exports.getDetailedLocalSessionInfo = getDetailedLocalSessionInfo;
76
- var node_child_process_1 = require("node:child_process");
77
- var node_util_1 = require("node:util");
78
- var execAsync = (0, node_util_1.promisify)(node_child_process_1.exec);
79
- var DEFAULT_CONFIG = {
80
- sessionPrefix: "mcp-ssh",
81
- defaultShell: "/bin/bash",
82
- term: "xterm-256color",
83
- timeout: 30,
84
- historyLimit: 10000, // 10k lines ~ 1-2MB per session
85
- sessionAgeLimit: 30 * 24 * 60 * 60 * 1000, // 30 days
86
- };
87
- /**
88
- * Check if tmux is installed locally
89
- */
90
- function isLocalTmuxInstalled() {
91
- return __awaiter(this, void 0, void 0, function () {
92
- var stdout, _a;
93
- return __generator(this, function (_b) {
94
- switch (_b.label) {
95
- case 0:
96
- _b.trys.push([0, 2, , 3]);
97
- return [4 /*yield*/, execAsync("type tmux", { timeout: 5000 })];
98
- case 1:
99
- stdout = (_b.sent()).stdout;
100
- return [2 /*return*/, stdout.trim() !== "tmux not found" && stdout.trim() !== ""];
101
- case 2:
102
- _a = _b.sent();
103
- return [2 /*return*/, false];
104
- case 3: return [2 /*return*/];
105
- }
106
- });
107
- });
108
- }
109
- /**
110
- * Generate a local tmux session name for a host
111
- * @param host - Remote host IP or hostname
112
- * @param user - SSH user (default: "root")
113
- * @returns Session name (e.g., "mcp-ssh-192-168-1-1")
114
- */
115
- function generateLocalSessionName(host, user) {
116
- if (user === void 0) { user = "root"; }
117
- var sanitizedHost = host.replace(/[.]/g, "-");
118
- return "".concat(DEFAULT_CONFIG.sessionPrefix, "-").concat(sanitizedHost);
119
- }
120
- /**
121
- * List all local tmux sessions
122
- * @returns Array of session names
123
- */
124
- function listLocalSessions() {
125
- return __awaiter(this, void 0, void 0, function () {
126
- var stdout, _a;
127
- return __generator(this, function (_b) {
128
- switch (_b.label) {
129
- case 0:
130
- _b.trys.push([0, 2, , 3]);
131
- return [4 /*yield*/, execAsync('tmux list-sessions -F "#{session_name}" 2>/dev/null || echo ""', { timeout: 5000 })];
132
- case 1:
133
- stdout = (_b.sent()).stdout;
134
- if (!stdout || stdout.trim() === "") {
135
- return [2 /*return*/, []];
136
- }
137
- return [2 /*return*/, stdout.trim().split("\n")];
138
- case 2:
139
- _a = _b.sent();
140
- return [2 /*return*/, []];
141
- case 3: return [2 /*return*/];
142
- }
143
- });
144
- });
145
- }
146
- /**
147
- * Check if a specific local tmux session exists
148
- * @param sessionName - Session name to check
149
- * @returns True if session exists
150
- */
151
- function hasLocalSession(sessionName) {
152
- return __awaiter(this, void 0, void 0, function () {
153
- var sessions;
154
- return __generator(this, function (_a) {
155
- switch (_a.label) {
156
- case 0: return [4 /*yield*/, listLocalSessions()];
157
- case 1:
158
- sessions = _a.sent();
159
- return [2 /*return*/, sessions.includes(sessionName)];
160
- }
161
- });
162
- });
163
- }
164
- /**
165
- * Create a local tmux session with an active SSH connection
166
- *
167
- * This function creates a tmux session on the local machine that maintains
168
- * an active SSH connection to the remote host. The connection stays alive
169
- * within the tmux session, allowing for persistent interactions.
170
- *
171
- * @param host - Remote host IP or hostname
172
- * @param user - SSH user (default: "root")
173
- * @param keyPath - Path to SSH private key (for key-based auth)
174
- * @param password - SSH password (for password-based auth)
175
- * @param options - Additional options for session creation
176
- * @returns Session creation result
177
- *
178
- * @example
179
- * ```ts
180
- * // Key-based authentication
181
- * const result = await createLocalTmuxSSHSession(
182
- * "192.168.1.100",
183
- * "root",
184
- * "/path/to/key"
185
- * );
186
- *
187
- * // Password-based authentication (requires sshpass)
188
- * const result = await createLocalTmuxSSHSession(
189
- * "192.168.1.100",
190
- * "root",
191
- * undefined,
192
- * "mypassword"
193
- * );
194
- * ```
195
- */
196
- function createLocalTmuxSSHSession(host_1) {
197
- return __awaiter(this, arguments, void 0, function (host, user, keyPath, password, options) {
198
- var tmuxInstalled, sessionName, windowName, sessionExists, sshCommand, _a, createCmd, _b, error_1;
199
- if (user === void 0) { user = "root"; }
200
- if (options === void 0) { options = {}; }
201
- return __generator(this, function (_c) {
202
- switch (_c.label) {
203
- case 0: return [4 /*yield*/, isLocalTmuxInstalled()];
204
- case 1:
205
- tmuxInstalled = _c.sent();
206
- if (!tmuxInstalled) {
207
- throw new Error("tmux is not installed on the local machine. Please install it using: brew install tmux (macOS) or apt install tmux (Linux)");
208
- }
209
- sessionName = options.sessionName || generateLocalSessionName(host, user);
210
- windowName = options.windowName || "ssh";
211
- return [4 /*yield*/, hasLocalSession(sessionName)];
212
- case 2:
213
- sessionExists = _c.sent();
214
- if (sessionExists) {
215
- // Session already exists, return info
216
- return [2 /*return*/, {
217
- sessionName: sessionName,
218
- newlyCreated: false,
219
- command: "tmux attach-session -t ".concat(sessionName),
220
- }];
221
- }
222
- if (!keyPath) return [3 /*break*/, 3];
223
- // Key-based authentication
224
- sshCommand = "ssh -i ".concat(keyPath, " -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ").concat(user, "@").concat(host);
225
- return [3 /*break*/, 9];
226
- case 3:
227
- if (!password) return [3 /*break*/, 8];
228
- _c.label = 4;
229
- case 4:
230
- _c.trys.push([4, 6, , 7]);
231
- return [4 /*yield*/, execAsync("type sshpass", { timeout: 5000 })];
232
- case 5:
233
- _c.sent();
234
- sshCommand = "sshpass -p '".concat(password, "' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ").concat(user, "@").concat(host);
235
- return [3 /*break*/, 7];
236
- case 6:
237
- _a = _c.sent();
238
- throw new Error("Password authentication requires 'sshpass' to be installed. Please install it using: brew install sshpass (macOS) or apt install sshpass (Linux). Alternatively, use key-based authentication.");
239
- case 7: return [3 /*break*/, 9];
240
- case 8:
241
- // No auth method provided, try default SSH agent
242
- sshCommand = "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ".concat(user, "@").concat(host);
243
- _c.label = 9;
244
- case 9:
245
- createCmd = [
246
- "tmux",
247
- "new-session",
248
- "-d",
249
- "-s", sessionName,
250
- "-n", windowName,
251
- sshCommand,
252
- ].join(" ");
253
- _c.label = 10;
254
- case 10:
255
- _c.trys.push([10, 19, , 20]);
256
- // Create the session
257
- return [4 /*yield*/, execAsync(createCmd, { timeout: DEFAULT_CONFIG.timeout * 1000 })];
258
- case 11:
259
- // Create the session
260
- _c.sent();
261
- _c.label = 12;
262
- case 12:
263
- _c.trys.push([12, 14, , 15]);
264
- return [4 /*yield*/, execAsync("tmux set-option -t \"".concat(sessionName, "\" history-limit ").concat(DEFAULT_CONFIG.historyLimit), { timeout: 5000 })];
265
- case 13:
266
- _c.sent();
267
- return [3 /*break*/, 15];
268
- case 14:
269
- _b = _c.sent();
270
- return [3 /*break*/, 15];
271
- case 15:
272
- if (!options.initialCommand) return [3 /*break*/, 18];
273
- // Wait a moment for SSH to connect
274
- return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 2000); })];
275
- case 16:
276
- // Wait a moment for SSH to connect
277
- _c.sent();
278
- return [4 /*yield*/, sendCommandToLocalSession(sessionName, options.initialCommand)];
279
- case 17:
280
- _c.sent();
281
- _c.label = 18;
282
- case 18: return [2 /*return*/, {
283
- sessionName: sessionName,
284
- newlyCreated: true,
285
- command: createCmd,
286
- }];
287
- case 19:
288
- error_1 = _c.sent();
289
- throw new Error("Failed to create local tmux session: ".concat(error_1 instanceof Error ? error_1.message : String(error_1)));
290
- case 20: return [2 /*return*/];
291
- }
292
- });
293
- });
294
- }
295
- /**
296
- * Send a command to a local tmux pane (already SSH'd into the remote host)
297
- *
298
- * This function sends a command to the tmux pane, which will be executed
299
- * on the remote host since the SSH connection is already active.
300
- *
301
- * @param sessionName - Local tmux session name
302
- * @param command - Command to send to the remote host
303
- * @param paneIndex - Pane index (default: "0")
304
- * @param windowName - Window name (default: auto-detects first window)
305
- * @returns True if command was sent successfully
306
- */
307
- function sendCommandToLocalSession(sessionName_1, command_1) {
308
- return __awaiter(this, arguments, void 0, function (sessionName, command, paneIndex, windowName) {
309
- var escapedCmd, targetWindow, windows, _a, target, sendCmd, error_2;
310
- if (paneIndex === void 0) { paneIndex = "0"; }
311
- return __generator(this, function (_b) {
312
- switch (_b.label) {
313
- case 0:
314
- _b.trys.push([0, 6, , 7]);
315
- escapedCmd = command.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
316
- targetWindow = windowName;
317
- if (!!targetWindow) return [3 /*break*/, 4];
318
- _b.label = 1;
319
- case 1:
320
- _b.trys.push([1, 3, , 4]);
321
- return [4 /*yield*/, listLocalSessionWindows(sessionName)];
322
- case 2:
323
- windows = _b.sent();
324
- if (windows.length > 0) {
325
- targetWindow = windows[0].name;
326
- }
327
- return [3 /*break*/, 4];
328
- case 3:
329
- _a = _b.sent();
330
- // Fallback to default
331
- targetWindow = "ssh";
332
- return [3 /*break*/, 4];
333
- case 4:
334
- target = paneIndex === "0"
335
- ? "".concat(sessionName, ":").concat(targetWindow)
336
- : "".concat(sessionName, ":").concat(targetWindow, ".").concat(parseInt(paneIndex, 10));
337
- sendCmd = "tmux send-keys -t \"".concat(target, "\" \"").concat(escapedCmd, "\" Enter");
338
- return [4 /*yield*/, execAsync(sendCmd, { timeout: 5000 })];
339
- case 5:
340
- _b.sent();
341
- return [2 /*return*/, true];
342
- case 6:
343
- error_2 = _b.sent();
344
- console.error("[LocalTmux] Failed to send command to ".concat(sessionName, ":").concat(paneIndex, ":"), error_2);
345
- return [2 /*return*/, false];
346
- case 7: return [2 /*return*/];
347
- }
348
- });
349
- });
350
- }
351
- /**
352
- * Capture output from a local tmux pane
353
- *
354
- * This captures the current visible content of the pane, including
355
- * the scrollback history.
356
- *
357
- * @param sessionName - Local tmux session name
358
- * @param paneIndex - Pane index (default: "0")
359
- * @param windowName - Window name (default: auto-detects first window)
360
- * @returns Captured output or null if failed
361
- */
362
- function captureLocalPane(sessionName_1) {
363
- return __awaiter(this, arguments, void 0, function (sessionName, paneIndex, windowName) {
364
- var targetWindow, windows, _a, target, stdout, error_3;
365
- if (paneIndex === void 0) { paneIndex = "0"; }
366
- return __generator(this, function (_b) {
367
- switch (_b.label) {
368
- case 0:
369
- _b.trys.push([0, 6, , 7]);
370
- targetWindow = windowName;
371
- if (!!targetWindow) return [3 /*break*/, 4];
372
- _b.label = 1;
373
- case 1:
374
- _b.trys.push([1, 3, , 4]);
375
- return [4 /*yield*/, listLocalSessionWindows(sessionName)];
376
- case 2:
377
- windows = _b.sent();
378
- if (windows.length > 0) {
379
- targetWindow = windows[0].name;
380
- }
381
- return [3 /*break*/, 4];
382
- case 3:
383
- _a = _b.sent();
384
- targetWindow = "ssh";
385
- return [3 /*break*/, 4];
386
- case 4:
387
- target = paneIndex === "0"
388
- ? "".concat(sessionName, ":").concat(targetWindow)
389
- : "".concat(sessionName, ":").concat(targetWindow, ".").concat(parseInt(paneIndex, 10));
390
- return [4 /*yield*/, execAsync("tmux capture-pane -t \"".concat(target, "\" -p"), { timeout: 5000 })];
391
- case 5:
392
- stdout = (_b.sent()).stdout;
393
- return [2 /*return*/, stdout || null];
394
- case 6:
395
- error_3 = _b.sent();
396
- console.error("[LocalTmux] Failed to capture pane ".concat(sessionName, ":").concat(paneIndex, ":"), error_3);
397
- return [2 /*return*/, null];
398
- case 7: return [2 /*return*/];
399
- }
400
- });
401
- });
402
- }
403
- /**
404
- * Get scrollback/history from a local tmux pane
405
- *
406
- * @param sessionName - Local tmux session name
407
- * @param paneIndex - Pane index (default: "0")
408
- * @param lines - Number of lines to retrieve (default: -1 for all)
409
- * @param windowName - Window name (default: auto-detects first window)
410
- * @returns History content or null if failed
411
- */
412
- function getLocalPaneHistory(sessionName_1) {
413
- return __awaiter(this, arguments, void 0, function (sessionName, paneIndex, lines, windowName) {
414
- var targetWindow, windows, _a, target, linesArg, stdout, error_4;
415
- if (paneIndex === void 0) { paneIndex = "0"; }
416
- if (lines === void 0) { lines = -1; }
417
- return __generator(this, function (_b) {
418
- switch (_b.label) {
419
- case 0:
420
- _b.trys.push([0, 6, , 7]);
421
- targetWindow = windowName;
422
- if (!!targetWindow) return [3 /*break*/, 4];
423
- _b.label = 1;
424
- case 1:
425
- _b.trys.push([1, 3, , 4]);
426
- return [4 /*yield*/, listLocalSessionWindows(sessionName)];
427
- case 2:
428
- windows = _b.sent();
429
- if (windows.length > 0) {
430
- targetWindow = windows[0].name;
431
- }
432
- return [3 /*break*/, 4];
433
- case 3:
434
- _a = _b.sent();
435
- targetWindow = "ssh";
436
- return [3 /*break*/, 4];
437
- case 4:
438
- target = paneIndex === "0"
439
- ? "".concat(sessionName, ":").concat(targetWindow)
440
- : "".concat(sessionName, ":").concat(targetWindow, ".").concat(parseInt(paneIndex, 10));
441
- linesArg = lines > 0 ? "-S -".concat(lines) : "-S -";
442
- return [4 /*yield*/, execAsync("tmux capture-pane ".concat(linesArg, " -t \"").concat(target, "\" -p"), { timeout: 10000 })];
443
- case 5:
444
- stdout = (_b.sent()).stdout;
445
- return [2 /*return*/, stdout || null];
446
- case 6:
447
- error_4 = _b.sent();
448
- console.error("[LocalTmux] Failed to get history for ".concat(sessionName, ":").concat(paneIndex, ":"), error_4);
449
- return [2 /*return*/, null];
450
- case 7: return [2 /*return*/];
451
- }
452
- });
453
- });
454
- }
455
- /**
456
- * Kill a local tmux session
457
- *
458
- * @param sessionName - Session name to kill
459
- * @returns True if session was killed successfully
460
- */
461
- function killLocalSession(sessionName) {
462
- return __awaiter(this, void 0, void 0, function () {
463
- var _a;
464
- return __generator(this, function (_b) {
465
- switch (_b.label) {
466
- case 0:
467
- _b.trys.push([0, 2, , 3]);
468
- return [4 /*yield*/, execAsync("tmux kill-session -t \"".concat(sessionName, "\" 2>/dev/null"), {
469
- timeout: 5000,
470
- })];
471
- case 1:
472
- _b.sent();
473
- return [2 /*return*/, true];
474
- case 2:
475
- _a = _b.sent();
476
- return [2 /*return*/, false];
477
- case 3: return [2 /*return*/];
478
- }
479
- });
480
- });
481
- }
482
- /**
483
- * Get information about a local tmux session
484
- *
485
- * @param sessionName - Session name to query
486
- * @returns Session information or null if session doesn't exist
487
- */
488
- function getLocalSessionInfo(sessionName) {
489
- return __awaiter(this, void 0, void 0, function () {
490
- var stdout, _a, windows, panes, _b;
491
- return __generator(this, function (_c) {
492
- switch (_c.label) {
493
- case 0:
494
- _c.trys.push([0, 2, , 3]);
495
- return [4 /*yield*/, execAsync("tmux display-message -t \"".concat(sessionName, "\" -p '#{session_windows} #{window_panes}' 2>/dev/null || echo \"\""), { timeout: 5000 })];
496
- case 1:
497
- stdout = (_c.sent()).stdout;
498
- if (!stdout || stdout.trim() === "") {
499
- return [2 /*return*/, { exists: false }];
500
- }
501
- _a = stdout.trim().split(" ").map(Number), windows = _a[0], panes = _a[1];
502
- return [2 /*return*/, { exists: true, windows: windows, panes: panes }];
503
- case 2:
504
- _b = _c.sent();
505
- return [2 /*return*/, { exists: false }];
506
- case 3: return [2 /*return*/];
507
- }
508
- });
509
- });
510
- }
511
- /**
512
- * List all windows in a local tmux session
513
- *
514
- * @param sessionName - Local tmux session name
515
- * @returns Array of window information
516
- */
517
- function listLocalSessionWindows(sessionName) {
518
- return __awaiter(this, void 0, void 0, function () {
519
- var stdout, _a;
520
- return __generator(this, function (_b) {
521
- switch (_b.label) {
522
- case 0:
523
- _b.trys.push([0, 2, , 3]);
524
- return [4 /*yield*/, execAsync("tmux list-windows -t \"".concat(sessionName, "\" -F '#{window_index} #{window_name} #{window_active}' 2>/dev/null || echo ''"), { timeout: 5000 })];
525
- case 1:
526
- stdout = (_b.sent()).stdout;
527
- if (!stdout || stdout.trim() === "") {
528
- return [2 /*return*/, []];
529
- }
530
- return [2 /*return*/, stdout.trim().split("\n").map(function (line) {
531
- var _a = line.split(" "), index = _a[0], name = _a[1], active = _a[2];
532
- return { index: index, name: name, active: active === "1" };
533
- })];
534
- case 2:
535
- _a = _b.sent();
536
- return [2 /*return*/, []];
537
- case 3: return [2 /*return*/];
538
- }
539
- });
540
- });
541
- }
542
- /**
543
- * List all panes in a local tmux session window
544
- *
545
- * @param sessionName - Local tmux session name
546
- * @param windowIndex - Window index (default: "0")
547
- * @returns Array of pane information
548
- */
549
- function listLocalWindowPanes(sessionName_1) {
550
- return __awaiter(this, arguments, void 0, function (sessionName, windowIndex) {
551
- var stdout, _a;
552
- if (windowIndex === void 0) { windowIndex = "0"; }
553
- return __generator(this, function (_b) {
554
- switch (_b.label) {
555
- case 0:
556
- _b.trys.push([0, 2, , 3]);
557
- return [4 /*yield*/, execAsync("tmux list-panes -t \"".concat(sessionName, ":").concat(windowIndex, "\" -F '#{pane_index} #{pane_current_path} #{pane_pid} #{pane_active}' 2>/dev/null || echo ''"), { timeout: 5000 })];
558
- case 1:
559
- stdout = (_b.sent()).stdout;
560
- if (!stdout || stdout.trim() === "") {
561
- return [2 /*return*/, []];
562
- }
563
- return [2 /*return*/, stdout.trim().split("\n").map(function (line) {
564
- var _a = line.split(" "), index = _a[0], currentPath = _a[1], pid = _a[2], active = _a[3];
565
- return { index: index, currentPath: currentPath, pid: pid, active: active === "1" };
566
- })];
567
- case 2:
568
- _a = _b.sent();
569
- return [2 /*return*/, []];
570
- case 3: return [2 /*return*/];
571
- }
572
- });
573
- });
574
- }
575
- /**
576
- * Split a pane horizontally or vertically in a local tmux session
577
- *
578
- * @param sessionName - Local tmux session name
579
- * @param direction - Split direction: "h" (horizontal) or "v" (vertical)
580
- * @param command - Optional command to run in the new pane
581
- * @param windowName - Window name (default: auto-detects first window)
582
- * @returns The new pane index or null if failed
583
- */
584
- function splitLocalPane(sessionName_1) {
585
- return __awaiter(this, arguments, void 0, function (sessionName, direction, command, windowName) {
586
- var targetWindow, windows, _a, target, splitCmd, stdout, error_5;
587
- if (direction === void 0) { direction = "v"; }
588
- return __generator(this, function (_b) {
589
- switch (_b.label) {
590
- case 0:
591
- _b.trys.push([0, 6, , 7]);
592
- targetWindow = windowName;
593
- if (!!targetWindow) return [3 /*break*/, 4];
594
- _b.label = 1;
595
- case 1:
596
- _b.trys.push([1, 3, , 4]);
597
- return [4 /*yield*/, listLocalSessionWindows(sessionName)];
598
- case 2:
599
- windows = _b.sent();
600
- if (windows.length > 0) {
601
- targetWindow = windows[0].name;
602
- }
603
- return [3 /*break*/, 4];
604
- case 3:
605
- _a = _b.sent();
606
- targetWindow = "ssh";
607
- return [3 /*break*/, 4];
608
- case 4:
609
- target = "".concat(sessionName, ":").concat(targetWindow);
610
- splitCmd = command
611
- ? "tmux split-window -".concat(direction, " -t \"").concat(target, "\" -c \"#{pane_current_path}\" \"").concat(command, "\"")
612
- : "tmux split-window -".concat(direction, " -t \"").concat(target, "\"");
613
- return [4 /*yield*/, execAsync(splitCmd, { timeout: 10000 })];
614
- case 5:
615
- stdout = (_b.sent()).stdout;
616
- return [2 /*return*/, (stdout === null || stdout === void 0 ? void 0 : stdout.trim()) || null];
617
- case 6:
618
- error_5 = _b.sent();
619
- console.error("[LocalTmux] Failed to split pane in ".concat(sessionName, ":"), error_5);
620
- return [2 /*return*/, null];
621
- case 7: return [2 /*return*/];
622
- }
623
- });
624
- });
625
- }
626
- /**
627
- * Cleanup old local tmux sessions
628
- *
629
- * @param config - Optional configuration (uses default age limit if not provided)
630
- * @returns Object with cleaned count and errors
631
- */
632
- function cleanupOldLocalSessions() {
633
- return __awaiter(this, arguments, void 0, function (config) {
634
- var fullConfig, errors, cleaned, sessions, mcpSessions, _i, mcpSessions_1, sessionName, ageCheckCmd, stdout, ageMs, err_1, err_2;
635
- if (config === void 0) { config = {}; }
636
- return __generator(this, function (_a) {
637
- switch (_a.label) {
638
- case 0:
639
- fullConfig = __assign(__assign({}, DEFAULT_CONFIG), config);
640
- errors = [];
641
- cleaned = 0;
642
- _a.label = 1;
643
- case 1:
644
- _a.trys.push([1, 11, , 12]);
645
- return [4 /*yield*/, listLocalSessions()];
646
- case 2:
647
- sessions = _a.sent();
648
- mcpSessions = sessions.filter(function (s) { return s.startsWith(fullConfig.sessionPrefix); });
649
- _i = 0, mcpSessions_1 = mcpSessions;
650
- _a.label = 3;
651
- case 3:
652
- if (!(_i < mcpSessions_1.length)) return [3 /*break*/, 10];
653
- sessionName = mcpSessions_1[_i];
654
- _a.label = 4;
655
- case 4:
656
- _a.trys.push([4, 8, , 9]);
657
- ageCheckCmd = "\n find /tmp -type s -name \"*".concat(sessionName, "*\" 2>/dev/null | head -1 | while read socket; do\n if [ -n \"$socket\" ]; then\n mtime=$(stat -f %m \"$socket\" 2>/dev/null || stat -c %Y \"$socket\" 2>/dev/null)\n if [ -n \"$mtime\" ]; then\n now=$(date +%s)\n age=$((now - mtime))\n age_ms=$((age * 1000))\n echo \"$age_ms\"\n fi\n fi\n done\n ");
658
- return [4 /*yield*/, execAsync(ageCheckCmd, { timeout: 10000 })];
659
- case 5:
660
- stdout = (_a.sent()).stdout;
661
- ageMs = parseInt(stdout.trim());
662
- if (!(!isNaN(ageMs) && ageMs > fullConfig.sessionAgeLimit)) return [3 /*break*/, 7];
663
- console.log("[LocalTmux] Cleaning up old session \"".concat(sessionName, "\" (age: ").concat(Math.round(ageMs / 86400000), " days)"));
664
- return [4 /*yield*/, killLocalSession(sessionName)];
665
- case 6:
666
- _a.sent();
667
- cleaned++;
668
- _a.label = 7;
669
- case 7: return [3 /*break*/, 9];
670
- case 8:
671
- err_1 = _a.sent();
672
- errors.push("".concat(sessionName, ": ").concat(err_1 instanceof Error ? err_1.message : String(err_1)));
673
- return [3 /*break*/, 9];
674
- case 9:
675
- _i++;
676
- return [3 /*break*/, 3];
677
- case 10: return [2 /*return*/, { cleaned: cleaned, errors: errors }];
678
- case 11:
679
- err_2 = _a.sent();
680
- errors.push("Cleanup failed: ".concat(err_2 instanceof Error ? err_2.message : String(err_2)));
681
- return [2 /*return*/, { cleaned: cleaned, errors: errors }];
682
- case 12: return [2 /*return*/];
683
- }
684
- });
685
- });
686
- }
687
- /**
688
- * Get resource usage information for local tmux sessions
689
- *
690
- * @returns Resource usage summary
691
- */
692
- function getLocalTmuxResourceUsage() {
693
- return __awaiter(this, void 0, void 0, function () {
694
- var sessions, mcpSessions, estimatedMemoryMB, _a;
695
- return __generator(this, function (_b) {
696
- switch (_b.label) {
697
- case 0:
698
- _b.trys.push([0, 2, , 3]);
699
- return [4 /*yield*/, listLocalSessions()];
700
- case 1:
701
- sessions = _b.sent();
702
- mcpSessions = sessions.filter(function (s) {
703
- return s.startsWith(DEFAULT_CONFIG.sessionPrefix);
704
- });
705
- estimatedMemoryMB = mcpSessions.length * 11;
706
- return [2 /*return*/, {
707
- totalSessions: sessions.length,
708
- mcpSessions: mcpSessions.length,
709
- estimatedMemoryMB: estimatedMemoryMB,
710
- }];
711
- case 2:
712
- _a = _b.sent();
713
- return [2 /*return*/, null];
714
- case 3: return [2 /*return*/];
715
- }
716
- });
717
- });
718
- }
719
- /**
720
- * Wait for a specific text to appear in the pane output
721
- *
722
- * @param sessionName - Local tmux session name
723
- * @param text - Text to wait for
724
- * @param timeoutMs - Maximum time to wait in milliseconds (default: 30000)
725
- * @param paneIndex - Pane index (default: "0")
726
- * @param windowName - Window name (default: auto-detects first window)
727
- * @returns True if text appeared, false if timeout
728
- */
729
- function waitForTextInPane(sessionName_1, text_1) {
730
- return __awaiter(this, arguments, void 0, function (sessionName, text, timeoutMs, paneIndex, windowName) {
731
- var startTime, checkInterval, output;
732
- if (timeoutMs === void 0) { timeoutMs = 30000; }
733
- if (paneIndex === void 0) { paneIndex = "0"; }
734
- return __generator(this, function (_a) {
735
- switch (_a.label) {
736
- case 0:
737
- startTime = Date.now();
738
- checkInterval = 500;
739
- _a.label = 1;
740
- case 1:
741
- if (!(Date.now() - startTime < timeoutMs)) return [3 /*break*/, 4];
742
- return [4 /*yield*/, captureLocalPane(sessionName, paneIndex, windowName)];
743
- case 2:
744
- output = _a.sent();
745
- if (output && output.includes(text)) {
746
- return [2 /*return*/, true];
747
- }
748
- return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, checkInterval); })];
749
- case 3:
750
- _a.sent();
751
- return [3 /*break*/, 1];
752
- case 4: return [2 /*return*/, false];
753
- }
754
- });
755
- });
756
- }
757
- /**
758
- * Switch to a specific window in a local tmux session
759
- *
760
- * @param sessionName - Local tmux session name
761
- * @param windowIndex - Target window index
762
- * @returns True if successful
763
- */
764
- function switchLocalWindow(sessionName, windowIndex) {
765
- return __awaiter(this, void 0, void 0, function () {
766
- var _a;
767
- return __generator(this, function (_b) {
768
- switch (_b.label) {
769
- case 0:
770
- _b.trys.push([0, 2, , 3]);
771
- return [4 /*yield*/, execAsync("tmux select-window -t \"".concat(sessionName, ":").concat(windowIndex, "\""), {
772
- timeout: 5000,
773
- })];
774
- case 1:
775
- _b.sent();
776
- return [2 /*return*/, true];
777
- case 2:
778
- _a = _b.sent();
779
- return [2 /*return*/, false];
780
- case 3: return [2 /*return*/];
781
- }
782
- });
783
- });
784
- }
785
- /**
786
- * Switch to a specific pane in a local tmux session window
787
- *
788
- * @param sessionName - Local tmux session name
789
- * @param paneIndex - Target pane index (e.g., "0", "1", "0.1" for window.pane)
790
- * @returns True if successful
791
- */
792
- function switchLocalPane(sessionName, paneIndex) {
793
- return __awaiter(this, void 0, void 0, function () {
794
- var _a;
795
- return __generator(this, function (_b) {
796
- switch (_b.label) {
797
- case 0:
798
- _b.trys.push([0, 2, , 3]);
799
- return [4 /*yield*/, execAsync("tmux select-pane -t \"".concat(sessionName, ":").concat(paneIndex, "\""), {
800
- timeout: 5000,
801
- })];
802
- case 1:
803
- _b.sent();
804
- return [2 /*return*/, true];
805
- case 2:
806
- _a = _b.sent();
807
- return [2 /*return*/, false];
808
- case 3: return [2 /*return*/];
809
- }
810
- });
811
- });
812
- }
813
- /**
814
- * Rename a window in a local tmux session
815
- *
816
- * @param sessionName - Local tmux session name
817
- * @param windowIndex - Window index (default: "0")
818
- * @param newName - New window name
819
- * @returns True if successful
820
- */
821
- function renameLocalWindow(sessionName, windowIndex, newName) {
822
- return __awaiter(this, void 0, void 0, function () {
823
- var _a;
824
- return __generator(this, function (_b) {
825
- switch (_b.label) {
826
- case 0:
827
- _b.trys.push([0, 2, , 3]);
828
- return [4 /*yield*/, execAsync("tmux rename-window -t \"".concat(sessionName, ":").concat(windowIndex, "\" \"").concat(newName, "\""), { timeout: 5000 })];
829
- case 1:
830
- _b.sent();
831
- return [2 /*return*/, true];
832
- case 2:
833
- _a = _b.sent();
834
- return [2 /*return*/, false];
835
- case 3: return [2 /*return*/];
836
- }
837
- });
838
- });
839
- }
840
- /**
841
- * Kill a specific pane in a local tmux session
842
- *
843
- * @param sessionName - Local tmux session name
844
- * @param paneIndex - Pane index to kill
845
- * @param windowName - Window name (default: auto-detects first window)
846
- * @returns True if successful
847
- */
848
- function killLocalPane(sessionName, paneIndex, windowName) {
849
- return __awaiter(this, void 0, void 0, function () {
850
- var targetWindow, windows, _a, target, _b;
851
- return __generator(this, function (_c) {
852
- switch (_c.label) {
853
- case 0:
854
- _c.trys.push([0, 6, , 7]);
855
- targetWindow = windowName;
856
- if (!!targetWindow) return [3 /*break*/, 4];
857
- _c.label = 1;
858
- case 1:
859
- _c.trys.push([1, 3, , 4]);
860
- return [4 /*yield*/, listLocalSessionWindows(sessionName)];
861
- case 2:
862
- windows = _c.sent();
863
- if (windows.length > 0) {
864
- targetWindow = windows[0].name;
865
- }
866
- return [3 /*break*/, 4];
867
- case 3:
868
- _a = _c.sent();
869
- targetWindow = "ssh";
870
- return [3 /*break*/, 4];
871
- case 4:
872
- target = paneIndex === "0"
873
- ? "".concat(sessionName, ":").concat(targetWindow)
874
- : "".concat(sessionName, ":").concat(targetWindow, ".").concat(parseInt(paneIndex, 10));
875
- return [4 /*yield*/, execAsync("tmux kill-pane -t \"".concat(target, "\""), {
876
- timeout: 5000,
877
- })];
878
- case 5:
879
- _c.sent();
880
- return [2 /*return*/, true];
881
- case 6:
882
- _b = _c.sent();
883
- return [2 /*return*/, false];
884
- case 7: return [2 /*return*/];
885
- }
886
- });
887
- });
888
- }
889
- /**
890
- * Get detailed information about all panes in a local session
891
- *
892
- * @param sessionName - Local tmux session name
893
- * @returns Detailed session information or null
894
- */
895
- function getDetailedLocalSessionInfo(sessionName) {
896
- return __awaiter(this, void 0, void 0, function () {
897
- var exists, windows, windowsWithPanes, error_6;
898
- var _this = this;
899
- return __generator(this, function (_a) {
900
- switch (_a.label) {
901
- case 0:
902
- _a.trys.push([0, 4, , 5]);
903
- return [4 /*yield*/, hasLocalSession(sessionName)];
904
- case 1:
905
- exists = _a.sent();
906
- if (!exists) {
907
- return [2 /*return*/, { exists: false, windows: [] }];
908
- }
909
- return [4 /*yield*/, listLocalSessionWindows(sessionName)];
910
- case 2:
911
- windows = _a.sent();
912
- return [4 /*yield*/, Promise.all(windows.map(function (window) { return __awaiter(_this, void 0, void 0, function () {
913
- var panes;
914
- return __generator(this, function (_a) {
915
- switch (_a.label) {
916
- case 0: return [4 /*yield*/, listLocalWindowPanes(sessionName, window.index)];
917
- case 1:
918
- panes = _a.sent();
919
- return [2 /*return*/, __assign(__assign({}, window), { panes: panes })];
920
- }
921
- });
922
- }); }))];
923
- case 3:
924
- windowsWithPanes = _a.sent();
925
- return [2 /*return*/, {
926
- exists: true,
927
- windows: windowsWithPanes,
928
- }];
929
- case 4:
930
- error_6 = _a.sent();
931
- console.error("[LocalTmux] Failed to get detailed info for ".concat(sessionName, ":"), error_6);
932
- return [2 /*return*/, null];
933
- case 5: return [2 /*return*/];
934
- }
935
- });
936
- });
937
- }