@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/mcp/index.js DELETED
@@ -1,555 +0,0 @@
1
- #!/usr/bin/env bun
2
- "use strict";
3
- /**
4
- * Terminal MCP Server
5
- *
6
- * Exposes terminal session management via Model Context Protocol
7
- * Integrates with tmux, SSH, PTY, and file operations
8
- */
9
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
10
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
11
- return new (P || (P = Promise))(function (resolve, reject) {
12
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
13
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
14
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
15
- step((generator = generator.apply(thisArg, _arguments || [])).next());
16
- });
17
- };
18
- var __generator = (this && this.__generator) || function (thisArg, body) {
19
- 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);
20
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
21
- function verb(n) { return function (v) { return step([n, v]); }; }
22
- function step(op) {
23
- if (f) throw new TypeError("Generator is already executing.");
24
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
25
- 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;
26
- if (y = 0, t) op = [op[0] & 2, t.value];
27
- switch (op[0]) {
28
- case 0: case 1: t = op; break;
29
- case 4: _.label++; return { value: op[1], done: false };
30
- case 5: _.label++; y = op[1]; op = [0]; continue;
31
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
32
- default:
33
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
34
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
35
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
36
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
37
- if (t[2]) _.ops.pop();
38
- _.trys.pop(); continue;
39
- }
40
- op = body.call(thisArg, _);
41
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
42
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
43
- }
44
- };
45
- Object.defineProperty(exports, "__esModule", { value: true });
46
- // Re-export terminal functions
47
- var index_js_1 = require("../index.js");
48
- // ==============
49
- // MCP Tools
50
- //=============
51
- /**
52
- * List all active terminal sessions
53
- */
54
- function listSessionsTool() {
55
- return __awaiter(this, void 0, void 0, function () {
56
- var sessions, lines, _i, sessions_1, session, active;
57
- return __generator(this, function (_a) {
58
- sessions = (0, index_js_1.getAllSessionInfo)();
59
- lines = [
60
- "🖥️ Active Terminal Sessions",
61
- "=".repeat(50),
62
- "",
63
- "Total sessions: ".concat(sessions.length),
64
- "",
65
- ];
66
- for (_i = 0, sessions_1 = sessions; _i < sessions_1.length; _i++) {
67
- session = sessions_1[_i];
68
- active = session.active ? "🟢" : "⚪";
69
- lines.push("".concat(active, " ").concat(session.id));
70
- lines.push(" Host: ".concat(session.host));
71
- lines.push(" Type: ".concat(session.type));
72
- lines.push(" Created: ".concat(new Date(session.createdAt).toLocaleString()));
73
- if (session.lastActivity) {
74
- lines.push(" Last activity: ".concat(new Date(session.lastActivity).toLocaleString()));
75
- }
76
- lines.push("");
77
- }
78
- return [2 /*return*/, lines.join("\n")];
79
- });
80
- });
81
- }
82
- /**
83
- * Get detailed info about a specific session
84
- */
85
- function getSessionInfoTool(sessionId) {
86
- return __awaiter(this, void 0, void 0, function () {
87
- var info, lines, session, error_1;
88
- return __generator(this, function (_a) {
89
- switch (_a.label) {
90
- case 0:
91
- _a.trys.push([0, 2, , 3]);
92
- return [4 /*yield*/, (0, index_js_1.getSessionInfo)(sessionId)];
93
- case 1:
94
- info = _a.sent();
95
- lines = [
96
- "\uD83D\uDCCB Session: ".concat(sessionId),
97
- "=".repeat(50),
98
- "",
99
- "Host: ".concat(info.host),
100
- "Type: ".concat(info.type),
101
- "Active: ".concat(info.active ? "Yes" : "No"),
102
- "Created: ".concat(new Date(info.createdAt).toLocaleString()),
103
- ];
104
- if (info.lastActivity) {
105
- lines.push("Last activity: ".concat(new Date(info.lastActivity).toLocaleString()));
106
- }
107
- session = (0, index_js_1.getSession)(sessionId);
108
- if (session) {
109
- lines.push("");
110
- lines.push("PTY session: ".concat(session.ptyId || "N/A"));
111
- lines.push("WebSocket attached: ".concat(session.wsAttached ? "Yes" : "No"));
112
- }
113
- return [2 /*return*/, lines.join("\n")];
114
- case 2:
115
- error_1 = _a.sent();
116
- throw new Error("Failed to get session info: ".concat(error_1));
117
- case 3: return [2 /*return*/];
118
- }
119
- });
120
- });
121
- }
122
- /**
123
- * Create a new terminal session
124
- */
125
- function createSessionTool(host_1) {
126
- return __awaiter(this, arguments, void 0, function (host, type, command) {
127
- var session, error_2;
128
- if (type === void 0) { type = "ssh"; }
129
- return __generator(this, function (_a) {
130
- switch (_a.label) {
131
- case 0:
132
- _a.trys.push([0, 2, , 3]);
133
- return [4 /*yield*/, (0, index_js_1.getOrCreateSession)(host, {
134
- type: type,
135
- command: command,
136
- })];
137
- case 1:
138
- session = _a.sent();
139
- return [2 /*return*/, "\u2713 Created session ".concat(session.id, " for ").concat(host, " (").concat(type, ")")];
140
- case 2:
141
- error_2 = _a.sent();
142
- throw new Error("Failed to create session: ".concat(error_2));
143
- case 3: return [2 /*return*/];
144
- }
145
- });
146
- });
147
- }
148
- /**
149
- * Write a command to a session
150
- */
151
- function writeCommandTool(sessionId, command) {
152
- return __awaiter(this, void 0, void 0, function () {
153
- var session, error_3;
154
- return __generator(this, function (_a) {
155
- switch (_a.label) {
156
- case 0:
157
- _a.trys.push([0, 2, , 3]);
158
- session = (0, index_js_1.getSession)(sessionId);
159
- if (!session) {
160
- throw new Error("Session ".concat(sessionId, " not found"));
161
- }
162
- return [4 /*yield*/, (0, index_js_1.writeToSession)(sessionId, command)];
163
- case 1:
164
- _a.sent();
165
- return [2 /*return*/, "\u2713 Command sent to session ".concat(sessionId)];
166
- case 2:
167
- error_3 = _a.sent();
168
- throw new Error("Failed to write command: ".concat(error_3));
169
- case 3: return [2 /*return*/];
170
- }
171
- });
172
- });
173
- }
174
- /**
175
- * Resize a session
176
- */
177
- function resizeSessionTool(sessionId, rows, cols) {
178
- return __awaiter(this, void 0, void 0, function () {
179
- var error_4;
180
- return __generator(this, function (_a) {
181
- switch (_a.label) {
182
- case 0:
183
- _a.trys.push([0, 2, , 3]);
184
- return [4 /*yield*/, (0, index_js_1.resizeSession)(sessionId, rows, cols)];
185
- case 1:
186
- _a.sent();
187
- return [2 /*return*/, "\u2713 Resized session ".concat(sessionId, " to ").concat(rows, "x").concat(cols)];
188
- case 2:
189
- error_4 = _a.sent();
190
- throw new Error("Failed to resize session: ".concat(error_4));
191
- case 3: return [2 /*return*/];
192
- }
193
- });
194
- });
195
- }
196
- /**
197
- * Close a session
198
- */
199
- function closeSessionTool(sessionId) {
200
- return __awaiter(this, void 0, void 0, function () {
201
- var error_5;
202
- return __generator(this, function (_a) {
203
- switch (_a.label) {
204
- case 0:
205
- _a.trys.push([0, 2, , 3]);
206
- return [4 /*yield*/, (0, index_js_1.closeSession)(sessionId)];
207
- case 1:
208
- _a.sent();
209
- return [2 /*return*/, "\u2713 Closed session ".concat(sessionId)];
210
- case 2:
211
- error_5 = _a.sent();
212
- throw new Error("Failed to close session: ".concat(error_5));
213
- case 3: return [2 /*return*/];
214
- }
215
- });
216
- });
217
- }
218
- /**
219
- * Execute a command via SSH
220
- */
221
- function execSSHTool(host, command, options) {
222
- return __awaiter(this, void 0, void 0, function () {
223
- var result, error_6;
224
- return __generator(this, function (_a) {
225
- switch (_a.label) {
226
- case 0:
227
- _a.trys.push([0, 2, , 3]);
228
- return [4 /*yield*/, (0, index_js_1.execSSH)(host, command, options)];
229
- case 1:
230
- result = _a.sent();
231
- return [2 /*return*/, result.stdout || result.stderr || "Command executed"];
232
- case 2:
233
- error_6 = _a.sent();
234
- throw new Error("SSH command failed: ".concat(error_6));
235
- case 3: return [2 /*return*/];
236
- }
237
- });
238
- });
239
- }
240
- /**
241
- * Test SSH connection
242
- */
243
- function testConnectionTool(host) {
244
- return __awaiter(this, void 0, void 0, function () {
245
- var result, error_7;
246
- return __generator(this, function (_a) {
247
- switch (_a.label) {
248
- case 0:
249
- _a.trys.push([0, 2, , 3]);
250
- return [4 /*yield*/, (0, index_js_1.testSSHConnection)(host)];
251
- case 1:
252
- result = _a.sent();
253
- if (result.success) {
254
- return [2 /*return*/, "\u2713 Connection to ".concat(host, " successful\nLatency: ").concat(result.latency, "ms")];
255
- }
256
- else {
257
- throw new Error(result.error || "Connection failed");
258
- }
259
- return [3 /*break*/, 3];
260
- case 2:
261
- error_7 = _a.sent();
262
- throw new Error("Connection test failed: ".concat(error_7));
263
- case 3: return [2 /*return*/];
264
- }
265
- });
266
- });
267
- }
268
- /**
269
- * List tmux sessions
270
- */
271
- function listTmuxSessionsTool() {
272
- return __awaiter(this, void 0, void 0, function () {
273
- var sessions, lines, _i, sessions_2, session, error_8;
274
- return __generator(this, function (_a) {
275
- switch (_a.label) {
276
- case 0:
277
- _a.trys.push([0, 3, , 4]);
278
- return [4 /*yield*/, (0, index_js_1.ensureTmux)()];
279
- case 1:
280
- _a.sent();
281
- return [4 /*yield*/, (0, index_js_1.listTmuxSessions)()];
282
- case 2:
283
- sessions = _a.sent();
284
- lines = [
285
- "🎬 Tmux Sessions",
286
- "=".repeat(50),
287
- "",
288
- ];
289
- if (sessions.length === 0) {
290
- lines.push("No active tmux sessions");
291
- }
292
- else {
293
- for (_i = 0, sessions_2 = sessions; _i < sessions_2.length; _i++) {
294
- session = sessions_2[_i];
295
- lines.push("".concat(session.name));
296
- lines.push(" Windows: ".concat(session.windows));
297
- lines.push(" Created: ".concat(new Date(session.created).toLocaleString()));
298
- lines.push("");
299
- }
300
- }
301
- return [2 /*return*/, lines.join("\n")];
302
- case 3:
303
- error_8 = _a.sent();
304
- throw new Error("Failed to list tmux sessions: ".concat(error_8));
305
- case 4: return [2 /*return*/];
306
- }
307
- });
308
- });
309
- }
310
- /**
311
- * List files on remote host
312
- */
313
- function listFilesTool(host_1) {
314
- return __awaiter(this, arguments, void 0, function (host, path) {
315
- var files, lines, _i, files_1, file, icon, size, error_9;
316
- if (path === void 0) { path = "."; }
317
- return __generator(this, function (_a) {
318
- switch (_a.label) {
319
- case 0:
320
- _a.trys.push([0, 2, , 3]);
321
- return [4 /*yield*/, (0, index_js_1.listFiles)(host, path)];
322
- case 1:
323
- files = _a.sent();
324
- lines = [
325
- "\uD83D\uDCC1 Files on ".concat(host, ":").concat(path),
326
- "=".repeat(50),
327
- "",
328
- ];
329
- for (_i = 0, files_1 = files; _i < files_1.length; _i++) {
330
- file = files_1[_i];
331
- icon = file.type === "directory" ? "📁" : "📄";
332
- size = file.size ? " (".concat(file.size, " bytes)") : "";
333
- lines.push("".concat(icon, " ").concat(file.name).concat(size));
334
- }
335
- return [2 /*return*/, lines.join("\n")];
336
- case 2:
337
- error_9 = _a.sent();
338
- throw new Error("Failed to list files: ".concat(error_9));
339
- case 3: return [2 /*return*/];
340
- }
341
- });
342
- });
343
- }
344
- /**
345
- * Upload file via SCP
346
- */
347
- function uploadFileTool(host, localPath, remotePath) {
348
- return __awaiter(this, void 0, void 0, function () {
349
- var error_10;
350
- return __generator(this, function (_a) {
351
- switch (_a.label) {
352
- case 0:
353
- _a.trys.push([0, 2, , 3]);
354
- return [4 /*yield*/, (0, index_js_1.scpUpload)(host, { localPath: localPath, remotePath: remotePath })];
355
- case 1:
356
- _a.sent();
357
- return [2 /*return*/, "\u2713 Uploaded ".concat(localPath, " to ").concat(host, ":").concat(remotePath)];
358
- case 2:
359
- error_10 = _a.sent();
360
- throw new Error("SCP upload failed: ".concat(error_10));
361
- case 3: return [2 /*return*/];
362
- }
363
- });
364
- });
365
- }
366
- /**
367
- * Download file via SCP
368
- */
369
- function downloadFileTool(host, remotePath, localPath) {
370
- return __awaiter(this, void 0, void 0, function () {
371
- var error_11;
372
- return __generator(this, function (_a) {
373
- switch (_a.label) {
374
- case 0:
375
- _a.trys.push([0, 2, , 3]);
376
- return [4 /*yield*/, (0, index_js_1.scpDownload)(host, { remotePath: remotePath, localPath: localPath })];
377
- case 1:
378
- _a.sent();
379
- return [2 /*return*/, "\u2713 Downloaded ".concat(host, ":").concat(remotePath, " to ").concat(localPath)];
380
- case 2:
381
- error_11 = _a.sent();
382
- throw new Error("SCP download failed: ".concat(error_11));
383
- case 3: return [2 /*return*/];
384
- }
385
- });
386
- });
387
- }
388
- /**
389
- * Get active SSH connections
390
- */
391
- function getActiveConnectionsTool() {
392
- return __awaiter(this, void 0, void 0, function () {
393
- var pool, connections, lines, _i, connections_1, conn;
394
- return __generator(this, function (_a) {
395
- try {
396
- pool = (0, index_js_1.getSSHPool)();
397
- connections = (0, index_js_1.getActiveSSHConnections)();
398
- lines = [
399
- "🔗 Active SSH Connections",
400
- "=".repeat(50),
401
- "",
402
- "Total connections: ".concat(connections.length),
403
- "",
404
- ];
405
- for (_i = 0, connections_1 = connections; _i < connections_1.length; _i++) {
406
- conn = connections_1[_i];
407
- lines.push("".concat(conn.host));
408
- lines.push(" Connected: ".concat(conn.connected ? "Yes" : "No"));
409
- lines.push(" Port: ".concat(conn.port || 22));
410
- lines.push("");
411
- }
412
- return [2 /*return*/, lines.join("\n")];
413
- }
414
- catch (error) {
415
- throw new Error("Failed to get connections: ".concat(error));
416
- }
417
- return [2 /*return*/];
418
- });
419
- });
420
- }
421
- // ==============
422
- // MCP Server
423
- //=============
424
- var MCP_PORT = parseInt(process.env.MCP_PORT || "8913");
425
- Bun.serve({
426
- port: MCP_PORT,
427
- fetch: function (req) { return __awaiter(void 0, void 0, void 0, function () {
428
- var url, body, tool, args, result, _a, error_12;
429
- return __generator(this, function (_b) {
430
- switch (_b.label) {
431
- case 0:
432
- url = new URL(req.url);
433
- // Health check
434
- if (url.pathname === "/health") {
435
- return [2 /*return*/, Response.json({ status: "ok", port: MCP_PORT, service: "terminal-mcp" })];
436
- }
437
- if (!(url.pathname === "/mcp")) return [3 /*break*/, 33];
438
- if (!(req.method === "POST")) return [3 /*break*/, 32];
439
- return [4 /*yield*/, req.json()];
440
- case 1:
441
- body = _b.sent();
442
- tool = body.tool, args = body.args;
443
- _b.label = 2;
444
- case 2:
445
- _b.trys.push([2, 31, , 32]);
446
- result = void 0;
447
- _a = tool;
448
- switch (_a) {
449
- case "list_sessions": return [3 /*break*/, 3];
450
- case "get_session_info": return [3 /*break*/, 5];
451
- case "create_session": return [3 /*break*/, 7];
452
- case "write_command": return [3 /*break*/, 9];
453
- case "resize_session": return [3 /*break*/, 11];
454
- case "close_session": return [3 /*break*/, 13];
455
- case "exec_ssh": return [3 /*break*/, 15];
456
- case "test_connection": return [3 /*break*/, 17];
457
- case "list_tmux_sessions": return [3 /*break*/, 19];
458
- case "list_files": return [3 /*break*/, 21];
459
- case "upload_file": return [3 /*break*/, 23];
460
- case "download_file": return [3 /*break*/, 25];
461
- case "get_active_connections": return [3 /*break*/, 27];
462
- }
463
- return [3 /*break*/, 29];
464
- case 3: return [4 /*yield*/, listSessionsTool()];
465
- case 4:
466
- result = _b.sent();
467
- return [3 /*break*/, 30];
468
- case 5: return [4 /*yield*/, getSessionInfoTool(args === null || args === void 0 ? void 0 : args.session_id)];
469
- case 6:
470
- result = _b.sent();
471
- return [3 /*break*/, 30];
472
- case 7: return [4 /*yield*/, createSessionTool(args === null || args === void 0 ? void 0 : args.host, args === null || args === void 0 ? void 0 : args.type, args === null || args === void 0 ? void 0 : args.command)];
473
- case 8:
474
- result = _b.sent();
475
- return [3 /*break*/, 30];
476
- case 9: return [4 /*yield*/, writeCommandTool(args === null || args === void 0 ? void 0 : args.session_id, args === null || args === void 0 ? void 0 : args.command)];
477
- case 10:
478
- result = _b.sent();
479
- return [3 /*break*/, 30];
480
- case 11: return [4 /*yield*/, resizeSessionTool(args === null || args === void 0 ? void 0 : args.session_id, args === null || args === void 0 ? void 0 : args.rows, args === null || args === void 0 ? void 0 : args.cols)];
481
- case 12:
482
- result = _b.sent();
483
- return [3 /*break*/, 30];
484
- case 13: return [4 /*yield*/, closeSessionTool(args === null || args === void 0 ? void 0 : args.session_id)];
485
- case 14:
486
- result = _b.sent();
487
- return [3 /*break*/, 30];
488
- case 15: return [4 /*yield*/, execSSHTool(args === null || args === void 0 ? void 0 : args.host, args === null || args === void 0 ? void 0 : args.command, args === null || args === void 0 ? void 0 : args.options)];
489
- case 16:
490
- result = _b.sent();
491
- return [3 /*break*/, 30];
492
- case 17: return [4 /*yield*/, testConnectionTool(args === null || args === void 0 ? void 0 : args.host)];
493
- case 18:
494
- result = _b.sent();
495
- return [3 /*break*/, 30];
496
- case 19: return [4 /*yield*/, listTmuxSessionsTool()];
497
- case 20:
498
- result = _b.sent();
499
- return [3 /*break*/, 30];
500
- case 21: return [4 /*yield*/, listFilesTool(args === null || args === void 0 ? void 0 : args.host, args === null || args === void 0 ? void 0 : args.path)];
501
- case 22:
502
- result = _b.sent();
503
- return [3 /*break*/, 30];
504
- case 23: return [4 /*yield*/, uploadFileTool(args === null || args === void 0 ? void 0 : args.host, args === null || args === void 0 ? void 0 : args.local_path, args === null || args === void 0 ? void 0 : args.remote_path)];
505
- case 24:
506
- result = _b.sent();
507
- return [3 /*break*/, 30];
508
- case 25: return [4 /*yield*/, downloadFileTool(args === null || args === void 0 ? void 0 : args.host, args === null || args === void 0 ? void 0 : args.remote_path, args === null || args === void 0 ? void 0 : args.local_path)];
509
- case 26:
510
- result = _b.sent();
511
- return [3 /*break*/, 30];
512
- case 27: return [4 /*yield*/, getActiveConnectionsTool()];
513
- case 28:
514
- result = _b.sent();
515
- return [3 /*break*/, 30];
516
- case 29: return [2 /*return*/, Response.json({ error: "Unknown tool: ".concat(tool) }, { status: 400 })];
517
- case 30: return [2 /*return*/, Response.json({ result: result })];
518
- case 31:
519
- error_12 = _b.sent();
520
- return [2 /*return*/, Response.json({ error: String(error_12) }, { status: 500 })];
521
- case 32:
522
- // GET returns available tools
523
- return [2 /*return*/, Response.json({
524
- name: "terminal-mcp",
525
- version: "1.0.0",
526
- description: "Terminal session management with tmux, SSH, PTY, and file operations",
527
- tools: [
528
- // Session management
529
- { name: "list_sessions", description: "List all active terminal sessions" },
530
- { name: "get_session_info", description: "Get detailed info about a session", args: ["session_id"] },
531
- { name: "create_session", description: "Create a new terminal session", args: ["host", "type?", "command?"] },
532
- { name: "write_command", description: "Write a command to a session", args: ["session_id", "command"] },
533
- { name: "resize_session", description: "Resize a session terminal", args: ["session_id", "rows", "cols"] },
534
- { name: "close_session", description: "Close a terminal session", args: ["session_id"] },
535
- // SSH operations
536
- { name: "exec_ssh", description: "Execute command via SSH", args: ["host", "command", "options?"] },
537
- { name: "test_connection", description: "Test SSH connection", args: ["host"] },
538
- // Tmux operations
539
- { name: "list_tmux_sessions", description: "List all tmux sessions" },
540
- // File operations
541
- { name: "list_files", description: "List files on remote host", args: ["host", "path?"] },
542
- { name: "upload_file", description: "Upload file via SCP", args: ["host", "local_path", "remote_path"] },
543
- { name: "download_file", description: "Download file via SCP", args: ["host", "remote_path", "local_path"] },
544
- // Connection management
545
- { name: "get_active_connections", description: "Get active SSH connections" },
546
- ]
547
- })];
548
- case 33: return [2 /*return*/, Response.json({ error: "Not found" }, { status: 404 })];
549
- }
550
- });
551
- }); },
552
- });
553
- console.log("\uD83D\uDE80 Terminal MCP Server running on port ".concat(MCP_PORT));
554
- console.log(" Health: http://localhost:".concat(MCP_PORT, "/health"));
555
- console.log(" MCP: http://localhost:".concat(MCP_PORT, "/mcp"));