persistent-terminal-mcp 1.0.2

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 (87) hide show
  1. package/CHANGELOG.md +278 -0
  2. package/LICENSE +21 -0
  3. package/README.en.md +259 -0
  4. package/README.md +608 -0
  5. package/dist/examples/basic-usage.d.ts +3 -0
  6. package/dist/examples/basic-usage.d.ts.map +1 -0
  7. package/dist/examples/basic-usage.js +116 -0
  8. package/dist/examples/basic-usage.js.map +1 -0
  9. package/dist/examples/codex-bug-fix-demo.d.ts +3 -0
  10. package/dist/examples/codex-bug-fix-demo.d.ts.map +1 -0
  11. package/dist/examples/codex-bug-fix-demo.js +134 -0
  12. package/dist/examples/codex-bug-fix-demo.js.map +1 -0
  13. package/dist/examples/interactive-demo.d.ts +3 -0
  14. package/dist/examples/interactive-demo.d.ts.map +1 -0
  15. package/dist/examples/interactive-demo.js +235 -0
  16. package/dist/examples/interactive-demo.js.map +1 -0
  17. package/dist/examples/rest-api-demo.d.ts +7 -0
  18. package/dist/examples/rest-api-demo.d.ts.map +1 -0
  19. package/dist/examples/rest-api-demo.js +160 -0
  20. package/dist/examples/rest-api-demo.js.map +1 -0
  21. package/dist/examples/smart-reading-demo.d.ts +3 -0
  22. package/dist/examples/smart-reading-demo.d.ts.map +1 -0
  23. package/dist/examples/smart-reading-demo.js +153 -0
  24. package/dist/examples/smart-reading-demo.js.map +1 -0
  25. package/dist/examples/test-all-9-tools.d.ts +8 -0
  26. package/dist/examples/test-all-9-tools.d.ts.map +1 -0
  27. package/dist/examples/test-all-9-tools.js +162 -0
  28. package/dist/examples/test-all-9-tools.js.map +1 -0
  29. package/dist/examples/test-all-tools.d.ts +3 -0
  30. package/dist/examples/test-all-tools.d.ts.map +1 -0
  31. package/dist/examples/test-all-tools.js +150 -0
  32. package/dist/examples/test-all-tools.js.map +1 -0
  33. package/dist/examples/test-fixes.d.ts +9 -0
  34. package/dist/examples/test-fixes.d.ts.map +1 -0
  35. package/dist/examples/test-fixes.js +168 -0
  36. package/dist/examples/test-fixes.js.map +1 -0
  37. package/dist/examples/test-spinner-compaction.d.ts +8 -0
  38. package/dist/examples/test-spinner-compaction.d.ts.map +1 -0
  39. package/dist/examples/test-spinner-compaction.js +155 -0
  40. package/dist/examples/test-spinner-compaction.js.map +1 -0
  41. package/dist/examples/test-web-ui.d.ts +12 -0
  42. package/dist/examples/test-web-ui.d.ts.map +1 -0
  43. package/dist/examples/test-web-ui.js +90 -0
  44. package/dist/examples/test-web-ui.js.map +1 -0
  45. package/dist/index.d.ts +8 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +81 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/mcp-server.d.ts +49 -0
  50. package/dist/mcp-server.d.ts.map +1 -0
  51. package/dist/mcp-server.js +1056 -0
  52. package/dist/mcp-server.js.map +1 -0
  53. package/dist/output-buffer.d.ts +134 -0
  54. package/dist/output-buffer.d.ts.map +1 -0
  55. package/dist/output-buffer.js +553 -0
  56. package/dist/output-buffer.js.map +1 -0
  57. package/dist/rest-api.d.ts +33 -0
  58. package/dist/rest-api.d.ts.map +1 -0
  59. package/dist/rest-api.js +325 -0
  60. package/dist/rest-api.js.map +1 -0
  61. package/dist/rest-server.d.ts +3 -0
  62. package/dist/rest-server.d.ts.map +1 -0
  63. package/dist/rest-server.js +72 -0
  64. package/dist/rest-server.js.map +1 -0
  65. package/dist/terminal-manager.d.ts +93 -0
  66. package/dist/terminal-manager.d.ts.map +1 -0
  67. package/dist/terminal-manager.js +702 -0
  68. package/dist/terminal-manager.js.map +1 -0
  69. package/dist/types.d.ts +201 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +5 -0
  72. package/dist/types.js.map +1 -0
  73. package/dist/web-ui-manager.d.ts +38 -0
  74. package/dist/web-ui-manager.d.ts.map +1 -0
  75. package/dist/web-ui-manager.js +130 -0
  76. package/dist/web-ui-manager.js.map +1 -0
  77. package/dist/web-ui-server.d.ts +42 -0
  78. package/dist/web-ui-server.d.ts.map +1 -0
  79. package/dist/web-ui-server.js +324 -0
  80. package/dist/web-ui-server.js.map +1 -0
  81. package/package.json +129 -0
  82. package/public/app.js +265 -0
  83. package/public/index.html +63 -0
  84. package/public/styles.css +383 -0
  85. package/public/terminal.html +56 -0
  86. package/public/terminal.js +244 -0
  87. package/public/test.html +97 -0
@@ -0,0 +1,56 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Terminal</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css">
8
+ <link rel="stylesheet" href="/styles.css">
9
+ </head>
10
+ <body>
11
+ <div class="terminal-page">
12
+ <header class="terminal-header">
13
+ <div class="terminal-info">
14
+ <a href="/" class="back-link">← Back to List</a>
15
+ <h2 id="terminal-title">Terminal</h2>
16
+ <span id="terminal-status" class="status-badge">Loading...</span>
17
+ </div>
18
+ <div class="terminal-actions">
19
+ <button id="clear-btn" class="btn btn-secondary">Clear</button>
20
+ <button id="kill-btn" class="btn btn-danger">Kill Terminal</button>
21
+ </div>
22
+ </header>
23
+
24
+ <div class="terminal-details">
25
+ <div class="detail-item">
26
+ <span class="detail-label">PID:</span>
27
+ <span id="detail-pid">-</span>
28
+ </div>
29
+ <div class="detail-item">
30
+ <span class="detail-label">Shell:</span>
31
+ <span id="detail-shell">-</span>
32
+ </div>
33
+ <div class="detail-item">
34
+ <span class="detail-label">CWD:</span>
35
+ <span id="detail-cwd">-</span>
36
+ </div>
37
+ <div class="detail-item">
38
+ <span class="detail-label">Created:</span>
39
+ <span id="detail-created">-</span>
40
+ </div>
41
+ </div>
42
+
43
+ <div id="terminal-container"></div>
44
+
45
+ <div class="input-container">
46
+ <input type="text" id="command-input" placeholder="Type command and press Enter...">
47
+ <button id="send-btn" class="btn btn-primary">Send</button>
48
+ </div>
49
+ </div>
50
+
51
+ <script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.js"></script>
52
+ <script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js"></script>
53
+ <script src="/terminal.js?v=3"></script>
54
+ </body>
55
+ </html>
56
+
@@ -0,0 +1,244 @@
1
+ // Terminal Detail Page Logic
2
+
3
+ let term = null;
4
+ let ws = null;
5
+ let terminalId = null;
6
+ let currentCursor = 0;
7
+
8
+ // Initialize
9
+ document.addEventListener('DOMContentLoaded', () => {
10
+ // Get terminal ID from URL
11
+ const pathParts = window.location.pathname.split('/');
12
+ terminalId = pathParts[pathParts.length - 1];
13
+
14
+ if (!terminalId) {
15
+ alert('Invalid terminal ID');
16
+ window.location.href = '/';
17
+ return;
18
+ }
19
+
20
+ setupTerminal();
21
+ setupEventListeners();
22
+ connectWebSocket();
23
+ loadTerminalInfo();
24
+ loadTerminalOutput();
25
+ });
26
+
27
+ // Setup xterm.js
28
+ function setupTerminal() {
29
+ try {
30
+ term = new Terminal({
31
+ cursorBlink: true,
32
+ fontSize: 14,
33
+ fontFamily: 'Menlo, Monaco, "Courier New", monospace',
34
+ theme: {
35
+ background: '#000000',
36
+ foreground: '#ffffff',
37
+ cursor: '#ffffff',
38
+ selection: '#ffffff40'
39
+ },
40
+ convertEol: true,
41
+ rows: 24,
42
+ cols: 80
43
+ });
44
+
45
+ const container = document.getElementById('terminal-container');
46
+ term.open(container);
47
+
48
+ // Fit terminal to container (optional, fallback if FitAddon not available)
49
+ if (typeof FitAddon !== 'undefined') {
50
+ try {
51
+ const fitAddon = new FitAddon.FitAddon();
52
+ term.loadAddon(fitAddon);
53
+ fitAddon.fit();
54
+
55
+ // Resize on window resize
56
+ window.addEventListener('resize', () => {
57
+ fitAddon.fit();
58
+ });
59
+ } catch (e) {
60
+ console.warn('FitAddon not available:', e);
61
+ }
62
+ }
63
+
64
+ console.log('Terminal initialized successfully');
65
+ } catch (error) {
66
+ console.error('Failed to setup terminal:', error);
67
+ alert('Failed to initialize terminal: ' + error.message);
68
+ }
69
+ }
70
+
71
+ // Setup event listeners
72
+ function setupEventListeners() {
73
+ document.getElementById('send-btn').addEventListener('click', sendCommand);
74
+ document.getElementById('command-input').addEventListener('keypress', (e) => {
75
+ if (e.key === 'Enter') {
76
+ sendCommand();
77
+ }
78
+ });
79
+
80
+ document.getElementById('clear-btn').addEventListener('click', () => {
81
+ term.clear();
82
+ });
83
+
84
+ document.getElementById('kill-btn').addEventListener('click', killTerminal);
85
+ }
86
+
87
+ // WebSocket connection
88
+ function connectWebSocket() {
89
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
90
+ const wsUrl = `${protocol}//${window.location.host}`;
91
+
92
+ ws = new WebSocket(wsUrl);
93
+
94
+ ws.onopen = () => {
95
+ console.log('WebSocket connected');
96
+ };
97
+
98
+ ws.onmessage = (event) => {
99
+ const message = JSON.parse(event.data);
100
+ handleWebSocketMessage(message);
101
+ };
102
+
103
+ ws.onerror = (error) => {
104
+ console.error('WebSocket error:', error);
105
+ };
106
+
107
+ ws.onclose = () => {
108
+ console.log('WebSocket disconnected, reconnecting...');
109
+ setTimeout(connectWebSocket, 2000);
110
+ };
111
+ }
112
+
113
+ // Handle WebSocket messages
114
+ function handleWebSocketMessage(message) {
115
+ if (message.terminalId !== terminalId) return;
116
+
117
+ switch (message.type) {
118
+ case 'output':
119
+ term.write(message.data);
120
+ break;
121
+ case 'exit':
122
+ updateStatus('terminated');
123
+ term.write('\r\n\x1b[31m[Terminal Exited]\x1b[0m\r\n');
124
+ break;
125
+ }
126
+ }
127
+
128
+ // Load terminal info
129
+ async function loadTerminalInfo() {
130
+ try {
131
+ console.log('Loading terminal info for:', terminalId);
132
+ const response = await fetch(`/api/terminals/${terminalId}`);
133
+
134
+ console.log('Response status:', response.status);
135
+
136
+ if (!response.ok) {
137
+ const errorText = await response.text();
138
+ console.error('Error response:', errorText);
139
+ throw new Error(`Terminal not found (${response.status})`);
140
+ }
141
+
142
+ const data = await response.json();
143
+ console.log('Terminal data:', data);
144
+
145
+ document.getElementById('terminal-title').textContent = `Terminal ${data.id.substring(0, 8)}`;
146
+ document.getElementById('detail-pid').textContent = data.pid;
147
+ document.getElementById('detail-shell').textContent = data.shell;
148
+ document.getElementById('detail-cwd').textContent = data.cwd;
149
+ document.getElementById('detail-created').textContent = new Date(data.created).toLocaleString();
150
+
151
+ updateStatus(data.status);
152
+ console.log('Terminal info loaded successfully');
153
+ } catch (error) {
154
+ console.error('Failed to load terminal info:', error);
155
+ alert('Failed to load terminal: ' + error.message);
156
+ // Don't redirect immediately, let user see the error
157
+ // window.location.href = '/';
158
+ }
159
+ }
160
+
161
+ // Load terminal output
162
+ async function loadTerminalOutput() {
163
+ try {
164
+ console.log('Loading terminal output for:', terminalId);
165
+ const response = await fetch(`/api/terminals/${terminalId}/output?since=${currentCursor}`);
166
+
167
+ if (!response.ok) {
168
+ const errorText = await response.text();
169
+ console.error('Failed to load output:', errorText);
170
+ throw new Error('Failed to load output');
171
+ }
172
+
173
+ const data = await response.json();
174
+ console.log('Output data:', data);
175
+
176
+ if (data.output) {
177
+ term.write(data.output);
178
+ console.log('Wrote output to terminal');
179
+ } else {
180
+ console.log('No output to display');
181
+ }
182
+
183
+ currentCursor = data.cursor || data.since || 0;
184
+ console.log('Current cursor:', currentCursor);
185
+ } catch (error) {
186
+ console.error('Failed to load terminal output:', error);
187
+ }
188
+ }
189
+
190
+ // Send command
191
+ async function sendCommand() {
192
+ const input = document.getElementById('command-input');
193
+ const command = input.value;
194
+
195
+ if (!command.trim()) return;
196
+
197
+ try {
198
+ const response = await fetch(`/api/terminals/${terminalId}/input`, {
199
+ method: 'POST',
200
+ headers: { 'Content-Type': 'application/json' },
201
+ body: JSON.stringify({ input: command })
202
+ });
203
+
204
+ if (!response.ok) {
205
+ throw new Error('Failed to send command');
206
+ }
207
+
208
+ input.value = '';
209
+ } catch (error) {
210
+ console.error('Failed to send command:', error);
211
+ alert('Failed to send command: ' + error.message);
212
+ }
213
+ }
214
+
215
+ // Kill terminal
216
+ async function killTerminal() {
217
+ if (!confirm('Are you sure you want to kill this terminal?')) {
218
+ return;
219
+ }
220
+
221
+ try {
222
+ const response = await fetch(`/api/terminals/${terminalId}`, {
223
+ method: 'DELETE'
224
+ });
225
+
226
+ if (!response.ok) {
227
+ throw new Error('Failed to kill terminal');
228
+ }
229
+
230
+ alert('Terminal killed');
231
+ window.location.href = '/';
232
+ } catch (error) {
233
+ console.error('Failed to kill terminal:', error);
234
+ alert('Failed to kill terminal: ' + error.message);
235
+ }
236
+ }
237
+
238
+ // Update status badge
239
+ function updateStatus(status) {
240
+ const badge = document.getElementById('terminal-status');
241
+ badge.textContent = status;
242
+ badge.className = 'status-badge status-' + status;
243
+ }
244
+
@@ -0,0 +1,97 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>API Test</title>
7
+ <style>
8
+ body {
9
+ font-family: monospace;
10
+ padding: 20px;
11
+ background: #1e1e1e;
12
+ color: #d4d4d4;
13
+ }
14
+ pre {
15
+ background: #000;
16
+ padding: 10px;
17
+ border-radius: 4px;
18
+ overflow-x: auto;
19
+ }
20
+ button {
21
+ padding: 10px 20px;
22
+ margin: 5px;
23
+ background: #0e639c;
24
+ color: white;
25
+ border: none;
26
+ border-radius: 4px;
27
+ cursor: pointer;
28
+ }
29
+ button:hover {
30
+ background: #1177bb;
31
+ }
32
+ </style>
33
+ </head>
34
+ <body>
35
+ <h1>API Test Page</h1>
36
+
37
+ <div>
38
+ <button onclick="testListTerminals()">Test: List Terminals</button>
39
+ <button onclick="testGetTerminal()">Test: Get Terminal</button>
40
+ <button onclick="testGetOutput()">Test: Get Output</button>
41
+ </div>
42
+
43
+ <h2>Results:</h2>
44
+ <pre id="results">Click a button to test...</pre>
45
+
46
+ <script>
47
+ async function testListTerminals() {
48
+ try {
49
+ const response = await fetch('/api/terminals');
50
+ const data = await response.json();
51
+ document.getElementById('results').textContent = JSON.stringify(data, null, 2);
52
+ } catch (error) {
53
+ document.getElementById('results').textContent = 'Error: ' + error.message;
54
+ }
55
+ }
56
+
57
+ async function testGetTerminal() {
58
+ try {
59
+ // First get the list to find a terminal ID
60
+ const listResponse = await fetch('/api/terminals');
61
+ const listData = await listResponse.json();
62
+
63
+ if (listData.terminals && listData.terminals.length > 0) {
64
+ const terminalId = listData.terminals[0].id;
65
+ const response = await fetch(`/api/terminals/${terminalId}`);
66
+ const data = await response.json();
67
+ document.getElementById('results').textContent = JSON.stringify(data, null, 2);
68
+ } else {
69
+ document.getElementById('results').textContent = 'No terminals found';
70
+ }
71
+ } catch (error) {
72
+ document.getElementById('results').textContent = 'Error: ' + error.message;
73
+ }
74
+ }
75
+
76
+ async function testGetOutput() {
77
+ try {
78
+ // First get the list to find a terminal ID
79
+ const listResponse = await fetch('/api/terminals');
80
+ const listData = await listResponse.json();
81
+
82
+ if (listData.terminals && listData.terminals.length > 0) {
83
+ const terminalId = listData.terminals[0].id;
84
+ const response = await fetch(`/api/terminals/${terminalId}/output`);
85
+ const data = await response.json();
86
+ document.getElementById('results').textContent = JSON.stringify(data, null, 2);
87
+ } else {
88
+ document.getElementById('results').textContent = 'No terminals found';
89
+ }
90
+ } catch (error) {
91
+ document.getElementById('results').textContent = 'Error: ' + error.message;
92
+ }
93
+ }
94
+ </script>
95
+ </body>
96
+ </html>
97
+