@mario.andreschak/mcp-bash 3.46.0 → 3.46.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.
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/resources.js +242 -242
- package/dist/shared.d.ts +14 -0
- package/dist/shared.js +54 -2
- package/dist/shared.js.map +1 -1
- package/dist/tools.d.ts +5 -2
- package/dist/tools.js +48 -26
- package/dist/tools.js.map +1 -1
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -88,7 +88,7 @@ async function shutdown(reason) {
|
|
|
88
88
|
// Stop accepting new work BEFORE tearing anything down, so a request that
|
|
89
89
|
// arrives mid-shutdown cannot start a process nothing will ever kill.
|
|
90
90
|
configureRootsProvider(undefined);
|
|
91
|
-
shutdownBashSessions();
|
|
91
|
+
await shutdownBashSessions();
|
|
92
92
|
await server.close().catch(() => undefined);
|
|
93
93
|
}
|
|
94
94
|
process.once('SIGINT', () => void shutdown('SIGINT'));
|
|
@@ -102,8 +102,8 @@ process.stdin.once('close', () => void shutdown('stdin close'));
|
|
|
102
102
|
// installs its own transport.onclose, so assigning that directly would be
|
|
103
103
|
// silently overwritten by server.connect() below.
|
|
104
104
|
server.onclose = () => void shutdown('transport close');
|
|
105
|
-
server.connect(transport).catch((error) => {
|
|
106
|
-
shutdownBashSessions();
|
|
105
|
+
server.connect(transport).catch(async (error) => {
|
|
106
|
+
await shutdownBashSessions();
|
|
107
107
|
process.stderr.write(`@mario.andreschak/mcp-bash failed: ${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
|
|
108
108
|
process.exitCode = 1;
|
|
109
109
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/resources.js
CHANGED
|
@@ -52,247 +52,247 @@ export function bashReadResource(uri) {
|
|
|
52
52
|
},
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
-
const BASH_TERMINAL_APP_HTML = `<!doctype html>
|
|
56
|
-
<html lang="en">
|
|
57
|
-
<head>
|
|
58
|
-
<meta charset="utf-8" />
|
|
59
|
-
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
60
|
-
<style>${XTERM_CSS}</style>
|
|
61
|
-
<style>
|
|
62
|
-
:root { color-scheme:dark; --bar:#111827; --border:#334155; --fg:#e5e7eb; --muted:#94a3b8; --accent:#60a5fa; --danger:#f87171; }
|
|
63
|
-
* { box-sizing:border-box; }
|
|
64
|
-
html,body { width:100%; height:100%; margin:0; overflow:hidden; background:#050a07; color:var(--fg); font:12px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; }
|
|
65
|
-
body { display:flex; flex-direction:column; }
|
|
66
|
-
#toolbar { display:flex; align-items:center; gap:7px; min-height:40px; padding:5px 8px; background:var(--bar); border-bottom:1px solid var(--border); }
|
|
67
|
-
#terminal { flex:1 1 auto; min-height:180px; padding:5px; }
|
|
68
|
-
#status { flex:1 1 auto; min-width:0; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
69
|
-
.live { color:#86efac !important; }
|
|
70
|
-
.error { color:var(--danger) !important; }
|
|
71
|
-
button,select,input { min-height:28px; border:1px solid var(--border); border-radius:5px; background:#0f172a; color:var(--fg); font:inherit; padding:3px 7px; }
|
|
72
|
-
button { cursor:pointer; }
|
|
73
|
-
button:disabled { opacity:.5; cursor:not-allowed; }
|
|
74
|
-
#cwd { width:min(28vw,260px); }
|
|
75
|
-
#shell { max-width:125px; }
|
|
76
|
-
.xterm { height:100%; }
|
|
77
|
-
.xterm-viewport { scrollbar-color:#475569 #07120d; }
|
|
78
|
-
</style>
|
|
79
|
-
<script>${XTERM_JS}</script>
|
|
80
|
-
<script>${XTERM_FIT_JS}</script>
|
|
81
|
-
</head>
|
|
82
|
-
<body>
|
|
83
|
-
<div id="toolbar">
|
|
84
|
-
<select id="shell" aria-label="Shell"><option value="default">Automatic</option><option value="pwsh">PowerShell 7</option><option value="powershell">Windows PowerShell 5.1</option><option value="bash">Bash</option><option value="cmd">cmd</option></select>
|
|
85
|
-
<input id="cwd" aria-label="Working directory" value="." title="Working directory" />
|
|
86
|
-
<button id="new" type="button">New</button>
|
|
87
|
-
<button id="copy" type="button" title="Copy selection">Copy</button>
|
|
88
|
-
<button id="clear" type="button">Clear</button>
|
|
89
|
-
<button id="close" type="button">Close</button>
|
|
90
|
-
<span id="status">Initializing terminal…</span>
|
|
91
|
-
</div>
|
|
92
|
-
<div id="terminal" aria-label="Interactive terminal"></div>
|
|
93
|
-
<script>
|
|
94
|
-
(function () {
|
|
95
|
-
"use strict";
|
|
96
|
-
var nextId = 1;
|
|
97
|
-
var pending = {};
|
|
98
|
-
var sessionId = "";
|
|
99
|
-
var cursor = 0;
|
|
100
|
-
var running = false;
|
|
101
|
-
var pollTimer = null;
|
|
102
|
-
var pollBusy = false;
|
|
103
|
-
var opening = false;
|
|
104
|
-
var inputQueue = "";
|
|
105
|
-
var inputBusy = false;
|
|
106
|
-
var resizeTimer = null;
|
|
107
|
-
var lastToolInput = {};
|
|
108
|
-
|
|
109
|
-
var statusEl = document.getElementById("status");
|
|
110
|
-
var shellEl = document.getElementById("shell");
|
|
111
|
-
var cwdEl = document.getElementById("cwd");
|
|
112
|
-
var terminal = new Terminal({
|
|
113
|
-
cursorBlink:true,
|
|
114
|
-
convertEol:false,
|
|
115
|
-
scrollback:10000,
|
|
116
|
-
fontFamily:'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace',
|
|
117
|
-
fontSize:13,
|
|
118
|
-
theme:{ background:'#050a07', foreground:'#d1fae5', cursor:'#86efac', selectionBackground:'#2563eb88' }
|
|
119
|
-
});
|
|
120
|
-
var fitAddon = new FitAddon.FitAddon();
|
|
121
|
-
terminal.loadAddon(fitAddon);
|
|
122
|
-
terminal.open(document.getElementById("terminal"));
|
|
123
|
-
fitAddon.fit();
|
|
124
|
-
|
|
125
|
-
function post(message) { window.parent.postMessage(message, "*"); }
|
|
126
|
-
function rpc(method, params) {
|
|
127
|
-
return new Promise(function (resolve, reject) {
|
|
128
|
-
var id = nextId++;
|
|
129
|
-
pending[id] = { resolve:resolve, reject:reject };
|
|
130
|
-
post({ jsonrpc:"2.0", id:id, method:method, params:params || {} });
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
function notify(method, params) { post({ jsonrpc:"2.0", method:method, params:params || {} }); }
|
|
134
|
-
function payloadOf(result) {
|
|
135
|
-
if (!result) return null;
|
|
136
|
-
if (result.structuredContent) return result.structuredContent;
|
|
137
|
-
try {
|
|
138
|
-
var text = result.content && result.content[0] && result.content[0].text;
|
|
139
|
-
return text ? JSON.parse(text) : null;
|
|
140
|
-
} catch (e) { return null; }
|
|
141
|
-
}
|
|
142
|
-
async function callTool(name, args) {
|
|
143
|
-
var result = await rpc("tools/call", { name:name, arguments:args || {} });
|
|
144
|
-
var data = payloadOf(result) || {};
|
|
145
|
-
if (result && result.isError) throw new Error(data.error || name + " failed");
|
|
146
|
-
return data;
|
|
147
|
-
}
|
|
148
|
-
function setStatus(message, kind) {
|
|
149
|
-
statusEl.textContent = message;
|
|
150
|
-
statusEl.className = kind || "";
|
|
151
|
-
}
|
|
152
|
-
function schedulePoll(delay) {
|
|
153
|
-
if (pollTimer) clearTimeout(pollTimer);
|
|
154
|
-
pollTimer = sessionId ? setTimeout(poll, delay == null ? (running ? 60 : 500) : delay) : null;
|
|
155
|
-
}
|
|
156
|
-
function acceptSession(data) {
|
|
157
|
-
if (!data || !data.sessionId) return false;
|
|
158
|
-
sessionId = String(data.sessionId);
|
|
159
|
-
cursor = Number(data.cursor || data.nextCursor || 0);
|
|
160
|
-
running = data.running !== false;
|
|
161
|
-
setStatus((running ? "● " : "○ ") + (data.shell || "terminal") + " — " + (data.cwd || sessionId), running ? "live" : "");
|
|
162
|
-
schedulePoll(0);
|
|
163
|
-
terminal.focus();
|
|
164
|
-
return true;
|
|
165
|
-
}
|
|
166
|
-
async function openTerminal() {
|
|
167
|
-
if (opening) return;
|
|
168
|
-
opening = true;
|
|
169
|
-
document.getElementById("new").disabled = true;
|
|
170
|
-
try {
|
|
171
|
-
if (sessionId && running) await callTool("terminal_close", { sessionId:sessionId }).catch(function () {});
|
|
172
|
-
terminal.reset();
|
|
173
|
-
terminal.clear();
|
|
174
|
-
var data = await callTool("open_terminal", {
|
|
175
|
-
shell:shellEl.value,
|
|
176
|
-
cwd:cwdEl.value.trim() || ".",
|
|
177
|
-
cols:terminal.cols,
|
|
178
|
-
rows:terminal.rows
|
|
179
|
-
});
|
|
180
|
-
acceptSession(data);
|
|
181
|
-
} catch (error) {
|
|
182
|
-
setStatus(error && error.message ? error.message : String(error), "error");
|
|
183
|
-
terminal.writeln("\\r\\n\\x1b[31m" + (error && error.message ? error.message : String(error)) + "\\x1b[0m");
|
|
184
|
-
} finally {
|
|
185
|
-
opening = false;
|
|
186
|
-
document.getElementById("new").disabled = false;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
async function poll() {
|
|
190
|
-
if (!sessionId || pollBusy) return;
|
|
191
|
-
pollBusy = true;
|
|
192
|
-
try {
|
|
193
|
-
var data = await callTool("terminal_read", { sessionId:sessionId, cursor:cursor });
|
|
194
|
-
if (data.reset) { terminal.reset(); terminal.clear(); }
|
|
195
|
-
if (typeof data.chunk === "string" && data.chunk) terminal.write(data.chunk);
|
|
196
|
-
cursor = Number(data.nextCursor == null ? cursor : data.nextCursor);
|
|
197
|
-
running = data.running !== false;
|
|
198
|
-
setStatus((running ? "● " : "○ ") + (data.shell || "terminal") + " — " + (data.cwd || sessionId) + (running ? "" : " — exited " + String(data.exitCode)), running ? "live" : "");
|
|
199
|
-
} catch (error) {
|
|
200
|
-
running = false;
|
|
201
|
-
setStatus(error && error.message ? error.message : String(error), "error");
|
|
202
|
-
} finally {
|
|
203
|
-
pollBusy = false;
|
|
204
|
-
schedulePoll();
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
async function flushInput() {
|
|
208
|
-
if (inputBusy || !inputQueue || !sessionId || !running) return;
|
|
209
|
-
inputBusy = true;
|
|
210
|
-
var data = inputQueue;
|
|
211
|
-
inputQueue = "";
|
|
212
|
-
try { await callTool("terminal_write", { sessionId:sessionId, data:data }); }
|
|
213
|
-
catch (error) { setStatus(error && error.message ? error.message : String(error), "error"); }
|
|
214
|
-
finally { inputBusy = false; if (inputQueue) setTimeout(flushInput, 0); }
|
|
215
|
-
}
|
|
216
|
-
function sendResize() {
|
|
217
|
-
if (!sessionId || !running) return;
|
|
218
|
-
callTool("terminal_resize", { sessionId:sessionId, cols:terminal.cols, rows:terminal.rows }).catch(function () {});
|
|
219
|
-
}
|
|
220
|
-
function acceptToolResult(result) {
|
|
221
|
-
var data = payloadOf(result) || {};
|
|
222
|
-
acceptSession(data);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
terminal.onData(function (data) {
|
|
226
|
-
inputQueue += data;
|
|
227
|
-
setTimeout(flushInput, 0);
|
|
228
|
-
});
|
|
229
|
-
terminal.onResize(function () {
|
|
230
|
-
if (resizeTimer) clearTimeout(resizeTimer);
|
|
231
|
-
resizeTimer = setTimeout(sendResize, 100);
|
|
232
|
-
});
|
|
233
|
-
window.addEventListener("resize", function () { try { fitAddon.fit(); } catch (e) {} });
|
|
234
|
-
document.getElementById("new").onclick = openTerminal;
|
|
235
|
-
document.getElementById("clear").onclick = function () { terminal.clear(); };
|
|
236
|
-
document.getElementById("copy").onclick = function () {
|
|
237
|
-
var text = terminal.getSelection();
|
|
238
|
-
if (!text) { setStatus("Select terminal text first.", ""); return; }
|
|
239
|
-
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
240
|
-
navigator.clipboard.writeText(text).then(function () { setStatus("Selection copied.", ""); }).catch(function () { setStatus("Copy was denied by the host.", "error"); });
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
document.getElementById("close").onclick = async function () {
|
|
244
|
-
if (!sessionId) return;
|
|
245
|
-
await callTool("terminal_close", { sessionId:sessionId }).catch(function () {});
|
|
246
|
-
running = false;
|
|
247
|
-
setStatus("Terminal closed.", "");
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
window.addEventListener("message", function (event) {
|
|
251
|
-
var message = event.data;
|
|
252
|
-
if (!message || message.jsonrpc !== "2.0") return;
|
|
253
|
-
if (message.id !== undefined && (message.result !== undefined || message.error !== undefined)) {
|
|
254
|
-
var waiting = pending[message.id];
|
|
255
|
-
if (!waiting) return;
|
|
256
|
-
delete pending[message.id];
|
|
257
|
-
if (message.error) waiting.reject(new Error(message.error.message || "RPC error"));
|
|
258
|
-
else waiting.resolve(message.result);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
if (message.method === "ui/notifications/tool-input") {
|
|
262
|
-
lastToolInput = message.params && message.params.arguments || {};
|
|
263
|
-
if (typeof lastToolInput.cwd === "string") cwdEl.value = lastToolInput.cwd;
|
|
264
|
-
if (typeof lastToolInput.shell === "string") shellEl.value = lastToolInput.shell;
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
if (message.method === "ui/notifications/tool-result") { acceptToolResult(message.params && message.params.result ? message.params.result : message.params); return; }
|
|
268
|
-
if (message.method === "ping" && message.id !== undefined) { post({ jsonrpc:"2.0", id:message.id, result:{} }); return; }
|
|
269
|
-
if (message.method === "ui/resource-teardown" && message.id !== undefined) {
|
|
270
|
-
// Dock/fullscreen transitions tear down Views; the owner-scoped PTY stays
|
|
271
|
-
// alive so the replacement View can reattach through terminal_list.
|
|
272
|
-
post({ jsonrpc:"2.0", id:message.id, result:{} });
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
rpc("ui/initialize", {
|
|
277
|
-
appInfo:{ name:"bash-terminal", version:"2.0.0" },
|
|
278
|
-
appCapabilities:{ availableDisplayModes:["inline","fullscreen","pip"] },
|
|
279
|
-
protocolVersion:"${MCP_APPS_PROTOCOL_VERSION}"
|
|
280
|
-
}).then(async function () {
|
|
281
|
-
notify("ui/notifications/initialized", {});
|
|
282
|
-
try { fitAddon.fit(); } catch (e) {}
|
|
283
|
-
setTimeout(async function () {
|
|
284
|
-
if (sessionId) return;
|
|
285
|
-
try {
|
|
286
|
-
var listed = await callTool("terminal_list", {});
|
|
287
|
-
var sessions = Array.isArray(listed.sessions) ? listed.sessions : [];
|
|
288
|
-
var active = sessions.filter(function (item) { return item.running; }).pop();
|
|
289
|
-
if (!acceptSession(active)) await openTerminal();
|
|
290
|
-
} catch (e) { await openTerminal(); }
|
|
291
|
-
}, 350);
|
|
292
|
-
rpc("ui/request-display-mode", { mode:"pip" }).catch(function () {});
|
|
293
|
-
}).catch(function (error) { setStatus("MCP App initialization failed: " + (error && error.message ? error.message : error), "error"); });
|
|
294
|
-
})();
|
|
295
|
-
</script>
|
|
296
|
-
</body>
|
|
55
|
+
const BASH_TERMINAL_APP_HTML = `<!doctype html>
|
|
56
|
+
<html lang="en">
|
|
57
|
+
<head>
|
|
58
|
+
<meta charset="utf-8" />
|
|
59
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
60
|
+
<style>${XTERM_CSS}</style>
|
|
61
|
+
<style>
|
|
62
|
+
:root { color-scheme:dark; --bar:#111827; --border:#334155; --fg:#e5e7eb; --muted:#94a3b8; --accent:#60a5fa; --danger:#f87171; }
|
|
63
|
+
* { box-sizing:border-box; }
|
|
64
|
+
html,body { width:100%; height:100%; margin:0; overflow:hidden; background:#050a07; color:var(--fg); font:12px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; }
|
|
65
|
+
body { display:flex; flex-direction:column; }
|
|
66
|
+
#toolbar { display:flex; align-items:center; gap:7px; min-height:40px; padding:5px 8px; background:var(--bar); border-bottom:1px solid var(--border); }
|
|
67
|
+
#terminal { flex:1 1 auto; min-height:180px; padding:5px; }
|
|
68
|
+
#status { flex:1 1 auto; min-width:0; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
69
|
+
.live { color:#86efac !important; }
|
|
70
|
+
.error { color:var(--danger) !important; }
|
|
71
|
+
button,select,input { min-height:28px; border:1px solid var(--border); border-radius:5px; background:#0f172a; color:var(--fg); font:inherit; padding:3px 7px; }
|
|
72
|
+
button { cursor:pointer; }
|
|
73
|
+
button:disabled { opacity:.5; cursor:not-allowed; }
|
|
74
|
+
#cwd { width:min(28vw,260px); }
|
|
75
|
+
#shell { max-width:125px; }
|
|
76
|
+
.xterm { height:100%; }
|
|
77
|
+
.xterm-viewport { scrollbar-color:#475569 #07120d; }
|
|
78
|
+
</style>
|
|
79
|
+
<script>${XTERM_JS}</script>
|
|
80
|
+
<script>${XTERM_FIT_JS}</script>
|
|
81
|
+
</head>
|
|
82
|
+
<body>
|
|
83
|
+
<div id="toolbar">
|
|
84
|
+
<select id="shell" aria-label="Shell"><option value="default">Automatic</option><option value="pwsh">PowerShell 7</option><option value="powershell">Windows PowerShell 5.1</option><option value="bash">Bash</option><option value="cmd">cmd</option></select>
|
|
85
|
+
<input id="cwd" aria-label="Working directory" value="." title="Working directory" />
|
|
86
|
+
<button id="new" type="button">New</button>
|
|
87
|
+
<button id="copy" type="button" title="Copy selection">Copy</button>
|
|
88
|
+
<button id="clear" type="button">Clear</button>
|
|
89
|
+
<button id="close" type="button">Close</button>
|
|
90
|
+
<span id="status">Initializing terminal…</span>
|
|
91
|
+
</div>
|
|
92
|
+
<div id="terminal" aria-label="Interactive terminal"></div>
|
|
93
|
+
<script>
|
|
94
|
+
(function () {
|
|
95
|
+
"use strict";
|
|
96
|
+
var nextId = 1;
|
|
97
|
+
var pending = {};
|
|
98
|
+
var sessionId = "";
|
|
99
|
+
var cursor = 0;
|
|
100
|
+
var running = false;
|
|
101
|
+
var pollTimer = null;
|
|
102
|
+
var pollBusy = false;
|
|
103
|
+
var opening = false;
|
|
104
|
+
var inputQueue = "";
|
|
105
|
+
var inputBusy = false;
|
|
106
|
+
var resizeTimer = null;
|
|
107
|
+
var lastToolInput = {};
|
|
108
|
+
|
|
109
|
+
var statusEl = document.getElementById("status");
|
|
110
|
+
var shellEl = document.getElementById("shell");
|
|
111
|
+
var cwdEl = document.getElementById("cwd");
|
|
112
|
+
var terminal = new Terminal({
|
|
113
|
+
cursorBlink:true,
|
|
114
|
+
convertEol:false,
|
|
115
|
+
scrollback:10000,
|
|
116
|
+
fontFamily:'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace',
|
|
117
|
+
fontSize:13,
|
|
118
|
+
theme:{ background:'#050a07', foreground:'#d1fae5', cursor:'#86efac', selectionBackground:'#2563eb88' }
|
|
119
|
+
});
|
|
120
|
+
var fitAddon = new FitAddon.FitAddon();
|
|
121
|
+
terminal.loadAddon(fitAddon);
|
|
122
|
+
terminal.open(document.getElementById("terminal"));
|
|
123
|
+
fitAddon.fit();
|
|
124
|
+
|
|
125
|
+
function post(message) { window.parent.postMessage(message, "*"); }
|
|
126
|
+
function rpc(method, params) {
|
|
127
|
+
return new Promise(function (resolve, reject) {
|
|
128
|
+
var id = nextId++;
|
|
129
|
+
pending[id] = { resolve:resolve, reject:reject };
|
|
130
|
+
post({ jsonrpc:"2.0", id:id, method:method, params:params || {} });
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function notify(method, params) { post({ jsonrpc:"2.0", method:method, params:params || {} }); }
|
|
134
|
+
function payloadOf(result) {
|
|
135
|
+
if (!result) return null;
|
|
136
|
+
if (result.structuredContent) return result.structuredContent;
|
|
137
|
+
try {
|
|
138
|
+
var text = result.content && result.content[0] && result.content[0].text;
|
|
139
|
+
return text ? JSON.parse(text) : null;
|
|
140
|
+
} catch (e) { return null; }
|
|
141
|
+
}
|
|
142
|
+
async function callTool(name, args) {
|
|
143
|
+
var result = await rpc("tools/call", { name:name, arguments:args || {} });
|
|
144
|
+
var data = payloadOf(result) || {};
|
|
145
|
+
if (result && result.isError) throw new Error(data.error || name + " failed");
|
|
146
|
+
return data;
|
|
147
|
+
}
|
|
148
|
+
function setStatus(message, kind) {
|
|
149
|
+
statusEl.textContent = message;
|
|
150
|
+
statusEl.className = kind || "";
|
|
151
|
+
}
|
|
152
|
+
function schedulePoll(delay) {
|
|
153
|
+
if (pollTimer) clearTimeout(pollTimer);
|
|
154
|
+
pollTimer = sessionId ? setTimeout(poll, delay == null ? (running ? 60 : 500) : delay) : null;
|
|
155
|
+
}
|
|
156
|
+
function acceptSession(data) {
|
|
157
|
+
if (!data || !data.sessionId) return false;
|
|
158
|
+
sessionId = String(data.sessionId);
|
|
159
|
+
cursor = Number(data.cursor || data.nextCursor || 0);
|
|
160
|
+
running = data.running !== false;
|
|
161
|
+
setStatus((running ? "● " : "○ ") + (data.shell || "terminal") + " — " + (data.cwd || sessionId), running ? "live" : "");
|
|
162
|
+
schedulePoll(0);
|
|
163
|
+
terminal.focus();
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
async function openTerminal() {
|
|
167
|
+
if (opening) return;
|
|
168
|
+
opening = true;
|
|
169
|
+
document.getElementById("new").disabled = true;
|
|
170
|
+
try {
|
|
171
|
+
if (sessionId && running) await callTool("terminal_close", { sessionId:sessionId }).catch(function () {});
|
|
172
|
+
terminal.reset();
|
|
173
|
+
terminal.clear();
|
|
174
|
+
var data = await callTool("open_terminal", {
|
|
175
|
+
shell:shellEl.value,
|
|
176
|
+
cwd:cwdEl.value.trim() || ".",
|
|
177
|
+
cols:terminal.cols,
|
|
178
|
+
rows:terminal.rows
|
|
179
|
+
});
|
|
180
|
+
acceptSession(data);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
setStatus(error && error.message ? error.message : String(error), "error");
|
|
183
|
+
terminal.writeln("\\r\\n\\x1b[31m" + (error && error.message ? error.message : String(error)) + "\\x1b[0m");
|
|
184
|
+
} finally {
|
|
185
|
+
opening = false;
|
|
186
|
+
document.getElementById("new").disabled = false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async function poll() {
|
|
190
|
+
if (!sessionId || pollBusy) return;
|
|
191
|
+
pollBusy = true;
|
|
192
|
+
try {
|
|
193
|
+
var data = await callTool("terminal_read", { sessionId:sessionId, cursor:cursor });
|
|
194
|
+
if (data.reset) { terminal.reset(); terminal.clear(); }
|
|
195
|
+
if (typeof data.chunk === "string" && data.chunk) terminal.write(data.chunk);
|
|
196
|
+
cursor = Number(data.nextCursor == null ? cursor : data.nextCursor);
|
|
197
|
+
running = data.running !== false;
|
|
198
|
+
setStatus((running ? "● " : "○ ") + (data.shell || "terminal") + " — " + (data.cwd || sessionId) + (running ? "" : " — exited " + String(data.exitCode)), running ? "live" : "");
|
|
199
|
+
} catch (error) {
|
|
200
|
+
running = false;
|
|
201
|
+
setStatus(error && error.message ? error.message : String(error), "error");
|
|
202
|
+
} finally {
|
|
203
|
+
pollBusy = false;
|
|
204
|
+
schedulePoll();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async function flushInput() {
|
|
208
|
+
if (inputBusy || !inputQueue || !sessionId || !running) return;
|
|
209
|
+
inputBusy = true;
|
|
210
|
+
var data = inputQueue;
|
|
211
|
+
inputQueue = "";
|
|
212
|
+
try { await callTool("terminal_write", { sessionId:sessionId, data:data }); }
|
|
213
|
+
catch (error) { setStatus(error && error.message ? error.message : String(error), "error"); }
|
|
214
|
+
finally { inputBusy = false; if (inputQueue) setTimeout(flushInput, 0); }
|
|
215
|
+
}
|
|
216
|
+
function sendResize() {
|
|
217
|
+
if (!sessionId || !running) return;
|
|
218
|
+
callTool("terminal_resize", { sessionId:sessionId, cols:terminal.cols, rows:terminal.rows }).catch(function () {});
|
|
219
|
+
}
|
|
220
|
+
function acceptToolResult(result) {
|
|
221
|
+
var data = payloadOf(result) || {};
|
|
222
|
+
acceptSession(data);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
terminal.onData(function (data) {
|
|
226
|
+
inputQueue += data;
|
|
227
|
+
setTimeout(flushInput, 0);
|
|
228
|
+
});
|
|
229
|
+
terminal.onResize(function () {
|
|
230
|
+
if (resizeTimer) clearTimeout(resizeTimer);
|
|
231
|
+
resizeTimer = setTimeout(sendResize, 100);
|
|
232
|
+
});
|
|
233
|
+
window.addEventListener("resize", function () { try { fitAddon.fit(); } catch (e) {} });
|
|
234
|
+
document.getElementById("new").onclick = openTerminal;
|
|
235
|
+
document.getElementById("clear").onclick = function () { terminal.clear(); };
|
|
236
|
+
document.getElementById("copy").onclick = function () {
|
|
237
|
+
var text = terminal.getSelection();
|
|
238
|
+
if (!text) { setStatus("Select terminal text first.", ""); return; }
|
|
239
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
240
|
+
navigator.clipboard.writeText(text).then(function () { setStatus("Selection copied.", ""); }).catch(function () { setStatus("Copy was denied by the host.", "error"); });
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
document.getElementById("close").onclick = async function () {
|
|
244
|
+
if (!sessionId) return;
|
|
245
|
+
await callTool("terminal_close", { sessionId:sessionId }).catch(function () {});
|
|
246
|
+
running = false;
|
|
247
|
+
setStatus("Terminal closed.", "");
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
window.addEventListener("message", function (event) {
|
|
251
|
+
var message = event.data;
|
|
252
|
+
if (!message || message.jsonrpc !== "2.0") return;
|
|
253
|
+
if (message.id !== undefined && (message.result !== undefined || message.error !== undefined)) {
|
|
254
|
+
var waiting = pending[message.id];
|
|
255
|
+
if (!waiting) return;
|
|
256
|
+
delete pending[message.id];
|
|
257
|
+
if (message.error) waiting.reject(new Error(message.error.message || "RPC error"));
|
|
258
|
+
else waiting.resolve(message.result);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (message.method === "ui/notifications/tool-input") {
|
|
262
|
+
lastToolInput = message.params && message.params.arguments || {};
|
|
263
|
+
if (typeof lastToolInput.cwd === "string") cwdEl.value = lastToolInput.cwd;
|
|
264
|
+
if (typeof lastToolInput.shell === "string") shellEl.value = lastToolInput.shell;
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (message.method === "ui/notifications/tool-result") { acceptToolResult(message.params && message.params.result ? message.params.result : message.params); return; }
|
|
268
|
+
if (message.method === "ping" && message.id !== undefined) { post({ jsonrpc:"2.0", id:message.id, result:{} }); return; }
|
|
269
|
+
if (message.method === "ui/resource-teardown" && message.id !== undefined) {
|
|
270
|
+
// Dock/fullscreen transitions tear down Views; the owner-scoped PTY stays
|
|
271
|
+
// alive so the replacement View can reattach through terminal_list.
|
|
272
|
+
post({ jsonrpc:"2.0", id:message.id, result:{} });
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
rpc("ui/initialize", {
|
|
277
|
+
appInfo:{ name:"bash-terminal", version:"2.0.0" },
|
|
278
|
+
appCapabilities:{ availableDisplayModes:["inline","fullscreen","pip"] },
|
|
279
|
+
protocolVersion:"${MCP_APPS_PROTOCOL_VERSION}"
|
|
280
|
+
}).then(async function () {
|
|
281
|
+
notify("ui/notifications/initialized", {});
|
|
282
|
+
try { fitAddon.fit(); } catch (e) {}
|
|
283
|
+
setTimeout(async function () {
|
|
284
|
+
if (sessionId) return;
|
|
285
|
+
try {
|
|
286
|
+
var listed = await callTool("terminal_list", {});
|
|
287
|
+
var sessions = Array.isArray(listed.sessions) ? listed.sessions : [];
|
|
288
|
+
var active = sessions.filter(function (item) { return item.running; }).pop();
|
|
289
|
+
if (!acceptSession(active)) await openTerminal();
|
|
290
|
+
} catch (e) { await openTerminal(); }
|
|
291
|
+
}, 350);
|
|
292
|
+
rpc("ui/request-display-mode", { mode:"pip" }).catch(function () {});
|
|
293
|
+
}).catch(function (error) { setStatus("MCP App initialization failed: " + (error && error.message ? error.message : error), "error"); });
|
|
294
|
+
})();
|
|
295
|
+
</script>
|
|
296
|
+
</body>
|
|
297
297
|
</html>`;
|
|
298
298
|
//# sourceMappingURL=resources.js.map
|
package/dist/shared.d.ts
CHANGED
|
@@ -15,3 +15,17 @@ export type RootsProvider = () => Promise<Array<{
|
|
|
15
15
|
export declare function configureRootsProvider(provider: RootsProvider | undefined): void;
|
|
16
16
|
export declare function loadEffectiveRoots(_serverName: string, envVarNames: string | string[], _callerNodeId?: string): Promise<string[]>;
|
|
17
17
|
export declare function killProcessTree(child: ChildProcess, graceMs?: number): () => void;
|
|
18
|
+
export interface ProcessTreeTerminationResult {
|
|
19
|
+
pid?: number;
|
|
20
|
+
/** True only after the ChildProcess close event has been observed. */
|
|
21
|
+
exited: boolean;
|
|
22
|
+
durationMs: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Terminate a process tree and wait for Node to observe the tracked child as
|
|
26
|
+
* closed. On Windows, synchronous taskkill completion is not sufficient: the
|
|
27
|
+
* child close event (and inherited stdio handle release) can arrive later under
|
|
28
|
+
* load. Subscribe before requesting termination so that transition cannot be
|
|
29
|
+
* missed, then bound the final observation wait.
|
|
30
|
+
*/
|
|
31
|
+
export declare function killProcessTreeAndWait(child: ChildProcess, graceMs?: number, finalWaitMs?: number): Promise<ProcessTreeTerminationResult>;
|
package/dist/shared.js
CHANGED
|
@@ -93,8 +93,9 @@ export function killProcessTree(child, graceMs = 2000) {
|
|
|
93
93
|
return () => undefined;
|
|
94
94
|
if (process.platform === 'win32') {
|
|
95
95
|
try {
|
|
96
|
-
// taskkill
|
|
97
|
-
//
|
|
96
|
+
// Wait for taskkill itself so the tree-termination request is complete.
|
|
97
|
+
// Node may publish the child's close event later; callers that must
|
|
98
|
+
// synchronize observable liveness use killProcessTreeAndWait below.
|
|
98
99
|
spawnSync('taskkill', ['/pid', String(pid), '/T', '/F'], {
|
|
99
100
|
stdio: 'ignore',
|
|
100
101
|
timeout: Math.max(graceMs, 5_000),
|
|
@@ -123,4 +124,55 @@ export function killProcessTree(child, graceMs = 2000) {
|
|
|
123
124
|
escalation.unref?.();
|
|
124
125
|
return () => clearTimeout(escalation);
|
|
125
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Terminate a process tree and wait for Node to observe the tracked child as
|
|
129
|
+
* closed. On Windows, synchronous taskkill completion is not sufficient: the
|
|
130
|
+
* child close event (and inherited stdio handle release) can arrive later under
|
|
131
|
+
* load. Subscribe before requesting termination so that transition cannot be
|
|
132
|
+
* missed, then bound the final observation wait.
|
|
133
|
+
*/
|
|
134
|
+
export async function killProcessTreeAndWait(child, graceMs = 2_000, finalWaitMs = 5_000) {
|
|
135
|
+
const startedAt = Date.now();
|
|
136
|
+
const pid = child.pid;
|
|
137
|
+
if (pid === undefined) {
|
|
138
|
+
return { pid, exited: false, durationMs: Date.now() - startedAt };
|
|
139
|
+
}
|
|
140
|
+
let closeObserved = false;
|
|
141
|
+
let resolveClose;
|
|
142
|
+
const closed = new Promise((resolve) => {
|
|
143
|
+
resolveClose = resolve;
|
|
144
|
+
});
|
|
145
|
+
const onClose = () => {
|
|
146
|
+
closeObserved = true;
|
|
147
|
+
resolveClose?.();
|
|
148
|
+
};
|
|
149
|
+
child.once('close', onClose);
|
|
150
|
+
let cancelEscalation = () => undefined;
|
|
151
|
+
try {
|
|
152
|
+
cancelEscalation = killProcessTree(child, graceMs);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
child.removeListener('close', onClose);
|
|
156
|
+
return { pid, exited: false, durationMs: Date.now() - startedAt };
|
|
157
|
+
}
|
|
158
|
+
// Windows taskkill already force-terminates the tree. POSIX may need the
|
|
159
|
+
// grace interval before killProcessTree escalates from SIGTERM to SIGKILL.
|
|
160
|
+
const observationMs = Math.max(0, finalWaitMs)
|
|
161
|
+
+ (process.platform === 'win32' ? 0 : Math.max(0, graceMs));
|
|
162
|
+
let timeout;
|
|
163
|
+
if (!closeObserved) {
|
|
164
|
+
await Promise.race([
|
|
165
|
+
closed,
|
|
166
|
+
new Promise((resolve) => {
|
|
167
|
+
timeout = setTimeout(resolve, observationMs);
|
|
168
|
+
}),
|
|
169
|
+
]);
|
|
170
|
+
}
|
|
171
|
+
if (timeout)
|
|
172
|
+
clearTimeout(timeout);
|
|
173
|
+
if (!closeObserved)
|
|
174
|
+
child.removeListener('close', onClose);
|
|
175
|
+
cancelEscalation();
|
|
176
|
+
return { pid, exited: closeObserved, durationMs: Date.now() - startedAt };
|
|
177
|
+
}
|
|
126
178
|
//# sourceMappingURL=shared.js.map
|