staff-mcp 1.2.0 → 2.0.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/CHANGELOG.md +29 -0
- package/README.md +178 -49
- package/README_zh.md +176 -47
- package/dist/src/data/HttpDataSession.d.ts +22 -0
- package/dist/src/data/HttpDataSession.js +925 -0
- package/dist/src/data/HttpDataSession.js.map +1 -0
- package/dist/src/data/node.d.ts +4 -0
- package/dist/src/data/node.js +93 -0
- package/dist/src/data/node.js.map +1 -0
- package/dist/src/data/types.d.ts +54 -0
- package/dist/src/data/types.js +2 -0
- package/dist/src/data/types.js.map +1 -0
- package/dist/src/data-stdio-docker.d.ts +10 -0
- package/dist/src/data-stdio-docker.js +140 -0
- package/dist/src/data-stdio-docker.js.map +1 -0
- package/dist/src/docker-proxy.d.ts +6 -1
- package/dist/src/docker-proxy.js +324 -147
- package/dist/src/docker-proxy.js.map +1 -1
- package/dist/src/extensions/apps.d.ts +12 -0
- package/dist/src/extensions/apps.js +85 -0
- package/dist/src/extensions/apps.js.map +1 -0
- package/dist/src/extensions/config.d.ts +27 -0
- package/dist/src/extensions/config.js +91 -0
- package/dist/src/extensions/config.js.map +1 -0
- package/dist/src/extensions/protocol.d.ts +35 -0
- package/dist/src/extensions/protocol.js +47 -0
- package/dist/src/extensions/protocol.js.map +1 -0
- package/dist/src/extensions/runtime.d.ts +16 -0
- package/dist/src/extensions/runtime.js +266 -0
- package/dist/src/extensions/runtime.js.map +1 -0
- package/dist/src/file-manager/extension.d.ts +21 -0
- package/dist/src/file-manager/extension.js +514 -0
- package/dist/src/file-manager/extension.js.map +1 -0
- package/dist/src/file-manager/file-core.d.ts +128 -0
- package/dist/src/file-manager/file-core.js +750 -0
- package/dist/src/file-manager/file-core.js.map +1 -0
- package/dist/src/file-manager/transfers.d.ts +423 -0
- package/dist/src/file-manager/transfers.js +540 -0
- package/dist/src/file-manager/transfers.js.map +1 -0
- package/dist/src/file-manager/ui/index.html +81 -0
- package/dist/src/index.js +289 -128
- package/dist/src/index.js.map +1 -1
- package/dist/src/lsp/manager.d.ts +2 -0
- package/dist/src/lsp/manager.js +22 -10
- package/dist/src/lsp/manager.js.map +1 -1
- package/dist/src/reverse/SseTransport.d.ts +34 -0
- package/dist/src/reverse/SseTransport.js +215 -0
- package/dist/src/reverse/SseTransport.js.map +1 -0
- package/dist/src/reverse/http.d.ts +7 -0
- package/dist/src/reverse/http.js +85 -0
- package/dist/src/reverse/http.js.map +1 -0
- package/dist/src/server.d.ts +4 -2
- package/dist/src/server.js +80 -18
- package/dist/src/server.js.map +1 -1
- package/dist/src/terminal/extension.d.ts +4 -0
- package/dist/src/terminal/extension.js +108 -0
- package/dist/src/terminal/extension.js.map +1 -0
- package/dist/src/terminal/pty.d.ts +14 -0
- package/dist/src/terminal/pty.js +121 -0
- package/dist/src/terminal/pty.js.map +1 -0
- package/dist/src/terminal/session.d.ts +136 -0
- package/dist/src/terminal/session.js +422 -0
- package/dist/src/terminal/session.js.map +1 -0
- package/dist/src/terminal/ui/index.html +183 -0
- package/dist/src/tools/file-tools.d.ts +2 -1
- package/dist/src/tools/file-tools.js +19 -7
- package/dist/src/tools/file-tools.js.map +1 -1
- package/dist/src/tools/mcp-client-tools.js +244 -104
- package/dist/src/tools/mcp-client-tools.js.map +1 -1
- package/dist/src/tools/shell-tools.js +1 -1
- package/dist/src/tools/shell-tools.js.map +1 -1
- package/dist/src/tools/skills.js +26 -1
- package/dist/src/tools/skills.js.map +1 -1
- package/dist/src/tools/system-tools.d.ts +9 -3
- package/dist/src/tools/system-tools.js +31 -48
- package/dist/src/tools/system-tools.js.map +1 -1
- package/dist/src/transports/http.d.ts +16 -2
- package/dist/src/transports/http.js +261 -59
- package/dist/src/transports/http.js.map +1 -1
- package/dist/src/transports/reverse.d.ts +23 -19
- package/dist/src/transports/reverse.js +130 -74
- package/dist/src/transports/reverse.js.map +1 -1
- package/dist/src/transports/stdio.d.ts +6 -2
- package/dist/src/transports/stdio.js +131 -19
- package/dist/src/transports/stdio.js.map +1 -1
- package/dist/src/utils/proxy.d.ts +3 -3
- package/dist/src/utils/proxy.js +84 -62
- package/dist/src/utils/proxy.js.map +1 -1
- package/dist/src/utils/server-activity.d.ts +6 -0
- package/dist/src/utils/server-activity.js +46 -0
- package/dist/src/utils/server-activity.js.map +1 -0
- package/dist/src/utils/server-lifecycle.d.ts +5 -0
- package/dist/src/utils/server-lifecycle.js +51 -0
- package/dist/src/utils/server-lifecycle.js.map +1 -0
- package/docs/file-manager.md +179 -0
- package/docs/terminal.md +66 -0
- package/package.json +40 -19
- package/dist/src/transports/fte-lifecycle.d.ts +0 -13
- package/dist/src/transports/fte-lifecycle.js +0 -76
- package/dist/src/transports/fte-lifecycle.js.map +0 -1
- package/dist/tests/docker-proxy.test.d.ts +0 -1
- package/dist/tests/docker-proxy.test.js +0 -395
- package/dist/tests/docker-proxy.test.js.map +0 -1
- package/dist/tests/file-write-queue.test.d.ts +0 -1
- package/dist/tests/file-write-queue.test.js +0 -105
- package/dist/tests/file-write-queue.test.js.map +0 -1
- package/dist/tests/fte-lifecycle.test.d.ts +0 -1
- package/dist/tests/fte-lifecycle.test.js +0 -118
- package/dist/tests/fte-lifecycle.test.js.map +0 -1
- package/dist/tests/lsp-client.test.d.ts +0 -1
- package/dist/tests/lsp-client.test.js +0 -52
- package/dist/tests/lsp-client.test.js.map +0 -1
- package/dist/tests/package-info.test.d.ts +0 -1
- package/dist/tests/package-info.test.js +0 -54
- package/dist/tests/package-info.test.js.map +0 -1
- package/dist/tests/proxy-no-proxy.test.d.ts +0 -1
- package/dist/tests/proxy-no-proxy.test.js +0 -156
- package/dist/tests/proxy-no-proxy.test.js.map +0 -1
- package/dist/tests/proxy.test.d.ts +0 -1
- package/dist/tests/proxy.test.js +0 -89
- package/dist/tests/proxy.test.js.map +0 -1
- package/dist/tests/reverse-reconnect.test.d.ts +0 -1
- package/dist/tests/reverse-reconnect.test.js +0 -89
- package/dist/tests/reverse-reconnect.test.js.map +0 -1
- package/dist/tests/security.test.d.ts +0 -1
- package/dist/tests/security.test.js +0 -21
- package/dist/tests/security.test.js.map +0 -1
- package/dist/tests/shell-tools.test.d.ts +0 -1
- package/dist/tests/shell-tools.test.js +0 -113
- package/dist/tests/shell-tools.test.js.map +0 -1
- package/dist/tests/test_files/test.d.ts +0 -1
- package/dist/tests/test_files/test.js +0 -4
- package/dist/tests/test_files/test.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,395 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { EventEmitter } from "node:events";
|
|
3
|
-
import * as fs from "node:fs";
|
|
4
|
-
import * as os from "node:os";
|
|
5
|
-
import * as path from "node:path";
|
|
6
|
-
import { PassThrough } from "node:stream";
|
|
7
|
-
import { test } from "node:test";
|
|
8
|
-
import { buildDockerNameHint, runDockerProxy, sanitizeNameSegment, validateAdditionalDockerArgs } from "../src/docker-proxy.js";
|
|
9
|
-
const FIXTURE_CONTAINER_ID = "a".repeat(64);
|
|
10
|
-
const FIXTURE_SOURCE = String.raw `
|
|
11
|
-
import * as fs from "node:fs";
|
|
12
|
-
|
|
13
|
-
const [mode, eventsPath, capturePath, command, ...args] = process.argv.slice(2);
|
|
14
|
-
const appendEvent = (event) => fs.appendFileSync(eventsPath, event + "\n");
|
|
15
|
-
|
|
16
|
-
if (command === "rm") {
|
|
17
|
-
const containerId = args.at(-1);
|
|
18
|
-
appendEvent("rm:" + containerId);
|
|
19
|
-
if (mode === "cleanup-missing") {
|
|
20
|
-
console.error("Error: No such container: " + containerId);
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
if (mode === "cleanup-fail") {
|
|
24
|
-
console.error("permission denied while removing " + containerId);
|
|
25
|
-
process.exit(2);
|
|
26
|
-
}
|
|
27
|
-
if (mode === "cleanup-hang") {
|
|
28
|
-
process.on("SIGTERM", () => appendEvent("rm-sigterm"));
|
|
29
|
-
setInterval(() => {}, 1_000);
|
|
30
|
-
await new Promise(() => {});
|
|
31
|
-
}
|
|
32
|
-
process.exit(0);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (command !== "run") {
|
|
36
|
-
throw new Error("Unexpected fake Docker command: " + command);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const cidFileIndex = args.findIndex((arg) => arg === "--cidfile");
|
|
40
|
-
const nameIndex = args.findIndex((arg) => arg === "--name");
|
|
41
|
-
if (cidFileIndex < 0 || !args[cidFileIndex + 1]) {
|
|
42
|
-
throw new Error("The Docker proxy did not provide --cidfile");
|
|
43
|
-
}
|
|
44
|
-
if (nameIndex < 0 || !args[nameIndex + 1]) {
|
|
45
|
-
throw new Error("The Docker proxy did not provide --name");
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const containerName = args[nameIndex + 1];
|
|
49
|
-
if (mode === "crash-before-cidfile") {
|
|
50
|
-
appendEvent("created:" + containerName);
|
|
51
|
-
setTimeout(() => process.kill(process.pid, "SIGKILL"), 50);
|
|
52
|
-
await new Promise(() => {});
|
|
53
|
-
}
|
|
54
|
-
if (mode === "delay-before-cidfile") {
|
|
55
|
-
appendEvent("created:" + containerName);
|
|
56
|
-
process.stdin.resume();
|
|
57
|
-
process.on("SIGTERM", () => {
|
|
58
|
-
appendEvent("sigterm");
|
|
59
|
-
setTimeout(() => process.exit(143), 20);
|
|
60
|
-
});
|
|
61
|
-
setInterval(() => {}, 1_000);
|
|
62
|
-
await new Promise(() => {});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
fs.writeFileSync(args[cidFileIndex + 1], "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n");
|
|
66
|
-
appendEvent("start");
|
|
67
|
-
|
|
68
|
-
if (mode === "capture") {
|
|
69
|
-
const chunks = [];
|
|
70
|
-
process.stdin.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
71
|
-
process.stdin.on("end", () => {
|
|
72
|
-
fs.writeFileSync(capturePath, Buffer.concat(chunks));
|
|
73
|
-
appendEvent("stdin-end");
|
|
74
|
-
});
|
|
75
|
-
process.stdin.resume();
|
|
76
|
-
} else if (
|
|
77
|
-
mode === "ignore-shutdown" ||
|
|
78
|
-
mode === "cleanup-missing" ||
|
|
79
|
-
mode === "cleanup-fail" ||
|
|
80
|
-
mode === "cleanup-hang"
|
|
81
|
-
) {
|
|
82
|
-
process.stdin.on("end", () => appendEvent("stdin-end"));
|
|
83
|
-
process.stdin.resume();
|
|
84
|
-
process.on("SIGTERM", () => appendEvent("sigterm"));
|
|
85
|
-
setInterval(() => {}, 1_000);
|
|
86
|
-
} else if (mode === "crash") {
|
|
87
|
-
setTimeout(() => process.kill(process.pid, "SIGKILL"), 50);
|
|
88
|
-
setInterval(() => {}, 1_000);
|
|
89
|
-
} else if (mode === "exit-nonzero") {
|
|
90
|
-
setTimeout(() => process.exit(125), 50);
|
|
91
|
-
setInterval(() => {}, 1_000);
|
|
92
|
-
} else {
|
|
93
|
-
throw new Error("Unknown fixture mode: " + mode);
|
|
94
|
-
}
|
|
95
|
-
`;
|
|
96
|
-
function delay(milliseconds) {
|
|
97
|
-
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
98
|
-
}
|
|
99
|
-
async function waitForEvent(eventsPath, event, timeoutMs = 3_000) {
|
|
100
|
-
const deadline = Date.now() + timeoutMs;
|
|
101
|
-
while (Date.now() < deadline) {
|
|
102
|
-
const events = fs.existsSync(eventsPath) ? fs.readFileSync(eventsPath, "utf8").split("\n") : [];
|
|
103
|
-
if (events.includes(event)) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
await delay(10);
|
|
107
|
-
}
|
|
108
|
-
throw new Error(`Timed out waiting for fixture event '${event}'`);
|
|
109
|
-
}
|
|
110
|
-
function readEvents(eventsPath) {
|
|
111
|
-
if (!fs.existsSync(eventsPath)) {
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
|
-
return fs.readFileSync(eventsPath, "utf8").trim().split("\n").filter(Boolean);
|
|
115
|
-
}
|
|
116
|
-
function createFixture(t) {
|
|
117
|
-
const fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), "staff-mcp-docker-proxy-test-"));
|
|
118
|
-
const fixturePath = path.join(fixtureDir, "fake-docker.mjs");
|
|
119
|
-
const eventsPath = path.join(fixtureDir, "events.log");
|
|
120
|
-
const capturePath = path.join(fixtureDir, "stdin.bin");
|
|
121
|
-
fs.writeFileSync(fixturePath, FIXTURE_SOURCE);
|
|
122
|
-
t.after(() => fs.rmSync(fixtureDir, { recursive: true, force: true }));
|
|
123
|
-
return { fixtureDir, fixturePath, eventsPath, capturePath };
|
|
124
|
-
}
|
|
125
|
-
function runFixture(fixturePath, eventsPath, capturePath, mode, transport, input, signals, timeoutOverrides = {}, proxyOptions = {}) {
|
|
126
|
-
return runDockerProxy(["run", "fake-image"], transport, {
|
|
127
|
-
command: process.execPath,
|
|
128
|
-
commandPrefixArgs: [fixturePath, mode, eventsPath, capturePath],
|
|
129
|
-
input,
|
|
130
|
-
signalSource: signals,
|
|
131
|
-
gracefulShutdownTimeoutMs: 500,
|
|
132
|
-
containerIdWaitTimeoutMs: 200,
|
|
133
|
-
containerCleanupTimeoutMs: 500,
|
|
134
|
-
forceKillTimeoutMs: 200,
|
|
135
|
-
...timeoutOverrides,
|
|
136
|
-
...proxyOptions,
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
test("reverse Docker proxy ignores closed host stdin and shuts down through its private lifecycle pipe", async (t) => {
|
|
140
|
-
const { fixturePath, eventsPath, capturePath } = createFixture(t);
|
|
141
|
-
const hostInput = new PassThrough();
|
|
142
|
-
const signals = new EventEmitter();
|
|
143
|
-
hostInput.end(); // Simulate PM2/systemd providing /dev/null or an already-closed fd 0.
|
|
144
|
-
let settled = false;
|
|
145
|
-
const resultPromise = runFixture(fixturePath, eventsPath, capturePath, "capture", "reverse", hostInput, signals);
|
|
146
|
-
void resultPromise.then(() => {
|
|
147
|
-
settled = true;
|
|
148
|
-
});
|
|
149
|
-
await waitForEvent(eventsPath, "start");
|
|
150
|
-
await delay(100);
|
|
151
|
-
assert.equal(settled, false, "closed host stdin must not stop reverse/http transports");
|
|
152
|
-
assert.deepEqual(readEvents(eventsPath), ["start"]);
|
|
153
|
-
signals.emit("SIGTERM");
|
|
154
|
-
assert.equal(await resultPromise, 0);
|
|
155
|
-
assert.equal(fs.readFileSync(capturePath).length, 0);
|
|
156
|
-
assert.deepEqual(readEvents(eventsPath), ["start", "stdin-end"]);
|
|
157
|
-
assert.equal(signals.listenerCount("SIGTERM"), 0);
|
|
158
|
-
});
|
|
159
|
-
test("HTTP Docker proxy also ignores closed host stdin", async (t) => {
|
|
160
|
-
const { fixturePath, eventsPath, capturePath } = createFixture(t);
|
|
161
|
-
const hostInput = new PassThrough();
|
|
162
|
-
const signals = new EventEmitter();
|
|
163
|
-
hostInput.end();
|
|
164
|
-
let settled = false;
|
|
165
|
-
const resultPromise = runFixture(fixturePath, eventsPath, capturePath, "capture", "http", hostInput, signals);
|
|
166
|
-
void resultPromise.then(() => {
|
|
167
|
-
settled = true;
|
|
168
|
-
});
|
|
169
|
-
await waitForEvent(eventsPath, "start");
|
|
170
|
-
await delay(100);
|
|
171
|
-
assert.equal(settled, false);
|
|
172
|
-
signals.emit("SIGHUP");
|
|
173
|
-
assert.equal(await resultPromise, 0);
|
|
174
|
-
assert.deepEqual(readEvents(eventsPath), ["start", "stdin-end"]);
|
|
175
|
-
});
|
|
176
|
-
test("stdio Docker proxy still forwards MCP input unchanged", async (t) => {
|
|
177
|
-
const { fixturePath, eventsPath, capturePath } = createFixture(t);
|
|
178
|
-
const hostInput = new PassThrough();
|
|
179
|
-
const signals = new EventEmitter();
|
|
180
|
-
const payload = Buffer.from('{"jsonrpc":"2.0","method":"ping"}\nsecond-frame\n');
|
|
181
|
-
const resultPromise = runFixture(fixturePath, eventsPath, capturePath, "capture", "stdio", hostInput, signals);
|
|
182
|
-
await waitForEvent(eventsPath, "start");
|
|
183
|
-
hostInput.end(payload);
|
|
184
|
-
assert.equal(await resultPromise, 0);
|
|
185
|
-
assert.deepEqual(fs.readFileSync(capturePath), payload);
|
|
186
|
-
assert.deepEqual(readEvents(eventsPath), ["start", "stdin-end"]);
|
|
187
|
-
});
|
|
188
|
-
test("stuck Docker shutdown removes the tracked container before killing the CLI", async (t) => {
|
|
189
|
-
const { fixturePath, eventsPath, capturePath } = createFixture(t);
|
|
190
|
-
const hostInput = new PassThrough();
|
|
191
|
-
const signals = new EventEmitter();
|
|
192
|
-
const resultPromise = runFixture(fixturePath, eventsPath, capturePath, "ignore-shutdown", "reverse", hostInput, signals, {
|
|
193
|
-
gracefulShutdownTimeoutMs: 50,
|
|
194
|
-
forceKillTimeoutMs: 50,
|
|
195
|
-
});
|
|
196
|
-
await waitForEvent(eventsPath, "start");
|
|
197
|
-
signals.emit("SIGTERM");
|
|
198
|
-
assert.equal(await resultPromise, 137);
|
|
199
|
-
const events = readEvents(eventsPath);
|
|
200
|
-
assert.ok(events.includes("stdin-end"), "shutdown should first close the lifecycle pipe");
|
|
201
|
-
const cleanupEvent = `rm:${FIXTURE_CONTAINER_ID}`;
|
|
202
|
-
assert.ok(events.includes(cleanupEvent), "the exact cidfile container should be removed");
|
|
203
|
-
assert.ok(events.includes("sigterm"), "the Docker CLI should receive SIGTERM only after cleanup");
|
|
204
|
-
assert.ok(events.indexOf(cleanupEvent) < events.indexOf("sigterm"), "container cleanup must precede terminating the stuck Docker CLI");
|
|
205
|
-
});
|
|
206
|
-
test("a Docker CLI killed externally triggers exact-ID fallback cleanup", async (t) => {
|
|
207
|
-
const { fixturePath, eventsPath, capturePath } = createFixture(t);
|
|
208
|
-
const hostInput = new PassThrough();
|
|
209
|
-
const signals = new EventEmitter();
|
|
210
|
-
const resultPromise = runFixture(fixturePath, eventsPath, capturePath, "crash", "reverse", hostInput, signals);
|
|
211
|
-
assert.equal(await resultPromise, 137);
|
|
212
|
-
assert.ok(readEvents(eventsPath).includes(`rm:${FIXTURE_CONTAINER_ID}`));
|
|
213
|
-
});
|
|
214
|
-
test("a Docker CLI killed before writing its cidfile falls back to the unique generated name", async (t) => {
|
|
215
|
-
const { fixturePath, eventsPath, capturePath } = createFixture(t);
|
|
216
|
-
const hostInput = new PassThrough();
|
|
217
|
-
const signals = new EventEmitter();
|
|
218
|
-
const resultPromise = runFixture(fixturePath, eventsPath, capturePath, "crash-before-cidfile", "reverse", hostInput, signals);
|
|
219
|
-
const deadline = Date.now() + 3_000;
|
|
220
|
-
let createdEvent;
|
|
221
|
-
while (!createdEvent && Date.now() < deadline) {
|
|
222
|
-
createdEvent = readEvents(eventsPath).find((event) => event.startsWith("created:"));
|
|
223
|
-
if (!createdEvent) {
|
|
224
|
-
await delay(10);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
assert.ok(createdEvent, "fixture should expose the generated container name");
|
|
228
|
-
assert.equal(await resultPromise, 137);
|
|
229
|
-
const containerName = createdEvent.slice("created:".length);
|
|
230
|
-
assert.match(containerName, /^staff-mcp-session-[0-9a-f]{6}$/);
|
|
231
|
-
assert.ok(readEvents(eventsPath).includes(`rm:${containerName}`));
|
|
232
|
-
});
|
|
233
|
-
test("shutdown before cidfile creation stops the CLI before cleaning up by name", async (t) => {
|
|
234
|
-
const { fixturePath, eventsPath, capturePath } = createFixture(t);
|
|
235
|
-
const hostInput = new PassThrough();
|
|
236
|
-
const signals = new EventEmitter();
|
|
237
|
-
const resultPromise = runFixture(fixturePath, eventsPath, capturePath, "delay-before-cidfile", "reverse", hostInput, signals, {
|
|
238
|
-
gracefulShutdownTimeoutMs: 30,
|
|
239
|
-
containerIdWaitTimeoutMs: 50,
|
|
240
|
-
forceKillTimeoutMs: 100,
|
|
241
|
-
});
|
|
242
|
-
let createdEvent;
|
|
243
|
-
const deadline = Date.now() + 3_000;
|
|
244
|
-
while (!createdEvent && Date.now() < deadline) {
|
|
245
|
-
createdEvent = readEvents(eventsPath).find((event) => event.startsWith("created:"));
|
|
246
|
-
if (!createdEvent) {
|
|
247
|
-
await delay(10);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
assert.ok(createdEvent);
|
|
251
|
-
signals.emit("SIGTERM");
|
|
252
|
-
assert.equal(await resultPromise, 143);
|
|
253
|
-
const containerName = createdEvent.slice("created:".length);
|
|
254
|
-
const events = readEvents(eventsPath);
|
|
255
|
-
assert.ok(events.includes("sigterm"));
|
|
256
|
-
assert.ok(events.includes(`rm:${containerName}`));
|
|
257
|
-
assert.ok(events.indexOf("sigterm") < events.indexOf(`rm:${containerName}`), "name cleanup must happen only after the creating CLI has been stopped");
|
|
258
|
-
});
|
|
259
|
-
test("a normal nonzero Docker CLI exit also triggers exact-ID fallback cleanup", async (t) => {
|
|
260
|
-
const { fixturePath, eventsPath, capturePath } = createFixture(t);
|
|
261
|
-
const hostInput = new PassThrough();
|
|
262
|
-
const signals = new EventEmitter();
|
|
263
|
-
const resultPromise = runFixture(fixturePath, eventsPath, capturePath, "exit-nonzero", "reverse", hostInput, signals);
|
|
264
|
-
assert.equal(await resultPromise, 125);
|
|
265
|
-
assert.ok(readEvents(eventsPath).includes(`rm:${FIXTURE_CONTAINER_ID}`));
|
|
266
|
-
});
|
|
267
|
-
test("a missing container during fallback cleanup is treated as already removed", async (t) => {
|
|
268
|
-
const { fixturePath, eventsPath, capturePath } = createFixture(t);
|
|
269
|
-
const hostInput = new PassThrough();
|
|
270
|
-
const signals = new EventEmitter();
|
|
271
|
-
const resultPromise = runFixture(fixturePath, eventsPath, capturePath, "cleanup-missing", "reverse", hostInput, signals, {
|
|
272
|
-
gracefulShutdownTimeoutMs: 50,
|
|
273
|
-
forceKillTimeoutMs: 50,
|
|
274
|
-
});
|
|
275
|
-
await waitForEvent(eventsPath, "start");
|
|
276
|
-
signals.emit("SIGTERM");
|
|
277
|
-
assert.equal(await resultPromise, 137);
|
|
278
|
-
assert.ok(readEvents(eventsPath).includes(`rm:${FIXTURE_CONTAINER_ID}`));
|
|
279
|
-
});
|
|
280
|
-
test("failed fallback cleanup reaches a bounded error result", async (t) => {
|
|
281
|
-
const { fixturePath, eventsPath, capturePath } = createFixture(t);
|
|
282
|
-
const hostInput = new PassThrough();
|
|
283
|
-
const signals = new EventEmitter();
|
|
284
|
-
const resultPromise = runFixture(fixturePath, eventsPath, capturePath, "cleanup-fail", "reverse", hostInput, signals, {
|
|
285
|
-
gracefulShutdownTimeoutMs: 50,
|
|
286
|
-
containerCleanupTimeoutMs: 100,
|
|
287
|
-
forceKillTimeoutMs: 50,
|
|
288
|
-
});
|
|
289
|
-
await waitForEvent(eventsPath, "start");
|
|
290
|
-
signals.emit("SIGINT");
|
|
291
|
-
assert.equal(await resultPromise, 1);
|
|
292
|
-
const cleanupEvents = readEvents(eventsPath).filter((event) => event === `rm:${FIXTURE_CONTAINER_ID}`);
|
|
293
|
-
assert.equal(cleanupEvents.length, 1, "a failed cleanup must not overlap with an automatic retry");
|
|
294
|
-
});
|
|
295
|
-
test("a hung fallback cleanup is escalated through SIGTERM and SIGKILL", async (t) => {
|
|
296
|
-
const { fixturePath, eventsPath, capturePath } = createFixture(t);
|
|
297
|
-
const hostInput = new PassThrough();
|
|
298
|
-
const signals = new EventEmitter();
|
|
299
|
-
const resultPromise = runFixture(fixturePath, eventsPath, capturePath, "cleanup-hang", "reverse", hostInput, signals, {
|
|
300
|
-
gracefulShutdownTimeoutMs: 30,
|
|
301
|
-
containerCleanupTimeoutMs: 50,
|
|
302
|
-
forceKillTimeoutMs: 50,
|
|
303
|
-
});
|
|
304
|
-
await waitForEvent(eventsPath, "start");
|
|
305
|
-
signals.emit("SIGTERM");
|
|
306
|
-
assert.equal(await resultPromise, 1);
|
|
307
|
-
const events = readEvents(eventsPath);
|
|
308
|
-
assert.equal(events.filter((event) => event === `rm:${FIXTURE_CONTAINER_ID}`).length, 1);
|
|
309
|
-
assert.ok(events.includes("rm-sigterm"), "hung cleanup should receive SIGTERM before SIGKILL");
|
|
310
|
-
});
|
|
311
|
-
test("unsafe additional Docker arguments are rejected", () => {
|
|
312
|
-
assert.doesNotThrow(() => validateAdditionalDockerArgs(["--detach=false", "--rm", "--rm=t", "-i", "--interactive=true"]));
|
|
313
|
-
for (const args of [
|
|
314
|
-
["-d"],
|
|
315
|
-
["-itd"],
|
|
316
|
-
["--detach"],
|
|
317
|
-
["--detach=true"],
|
|
318
|
-
["--rm=false"],
|
|
319
|
-
["--rm=f"],
|
|
320
|
-
["--rm=0"],
|
|
321
|
-
["--interactive=false"],
|
|
322
|
-
["--interactive=f"],
|
|
323
|
-
["--interactive=0"],
|
|
324
|
-
["-i=false"],
|
|
325
|
-
["-it=false"],
|
|
326
|
-
["--cidfile=/tmp/unsafe.cid"],
|
|
327
|
-
["--name=staff-mcp-test"],
|
|
328
|
-
]) {
|
|
329
|
-
assert.throws(() => validateAdditionalDockerArgs(args));
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
test("Docker proxy reports a spawn failure without hanging", async () => {
|
|
333
|
-
const signals = new EventEmitter();
|
|
334
|
-
const code = await runDockerProxy(["run", "fake-image"], "reverse", {
|
|
335
|
-
command: path.join(os.tmpdir(), `missing-staff-mcp-runtime-${process.pid}`),
|
|
336
|
-
signalSource: signals,
|
|
337
|
-
gracefulShutdownTimeoutMs: 100,
|
|
338
|
-
containerIdWaitTimeoutMs: 100,
|
|
339
|
-
containerCleanupTimeoutMs: 100,
|
|
340
|
-
forceKillTimeoutMs: 100,
|
|
341
|
-
});
|
|
342
|
-
assert.equal(code, 1);
|
|
343
|
-
assert.equal(signals.listenerCount("SIGTERM"), 0);
|
|
344
|
-
});
|
|
345
|
-
test("Docker proxy refuses a pre-existing user cidfile to avoid cleaning a stale container ID", async (t) => {
|
|
346
|
-
const { fixtureDir } = createFixture(t);
|
|
347
|
-
const cidFilePath = path.join(fixtureDir, "existing.cid");
|
|
348
|
-
fs.writeFileSync(cidFilePath, "unrelated-container\n");
|
|
349
|
-
const signals = new EventEmitter();
|
|
350
|
-
const code = await runDockerProxy(["run", "--cidfile", cidFilePath, "fake-image"], "reverse", {
|
|
351
|
-
command: process.execPath,
|
|
352
|
-
signalSource: signals,
|
|
353
|
-
});
|
|
354
|
-
assert.equal(code, 1);
|
|
355
|
-
assert.equal(fs.readFileSync(cidFilePath, "utf8"), "unrelated-container\n");
|
|
356
|
-
assert.equal(signals.listenerCount("SIGTERM"), 0);
|
|
357
|
-
});
|
|
358
|
-
test("sanitizeNameSegment folds Docker-invalid characters and trims decorations", () => {
|
|
359
|
-
assert.equal(sanitizeNameSegment("Chat Poc!"), "chat-poc");
|
|
360
|
-
assert.equal(sanitizeNameSegment("--Weird__Name--"), "weird__name");
|
|
361
|
-
assert.equal(sanitizeNameSegment("my_app.v2"), "my_app.v2");
|
|
362
|
-
assert.equal(sanitizeNameSegment("工作 目录"), "");
|
|
363
|
-
assert.equal(sanitizeNameSegment(""), "");
|
|
364
|
-
assert.equal(sanitizeNameSegment("abc-def", 4), "abc");
|
|
365
|
-
assert.equal(sanitizeNameSegment("a".repeat(50)), "a".repeat(24));
|
|
366
|
-
});
|
|
367
|
-
test("buildDockerNameHint derives transport-aware hints from launch context", () => {
|
|
368
|
-
assert.equal(buildDockerNameHint({ workspaceDir: "/data/projs/chat-poc", transport: "stdio" }), "chat-poc");
|
|
369
|
-
assert.equal(buildDockerNameHint({ workspaceDir: "/data/projs/test", transport: "http", port: "18000" }), "test-18000");
|
|
370
|
-
assert.equal(buildDockerNameHint({ workspaceDir: "/data/projs/app", transport: "http" }), "app");
|
|
371
|
-
// reverseName identical to the workspace is redundant and therefore omitted
|
|
372
|
-
assert.equal(buildDockerNameHint({ workspaceDir: "/data/projs/chat-poc", transport: "reverse", reverseName: "chat-poc" }), "chat-poc");
|
|
373
|
-
assert.equal(buildDockerNameHint({ workspaceDir: "/data/projs/myapp", transport: "reverse", reverseName: "android-dev" }), "myapp-android-dev");
|
|
374
|
-
});
|
|
375
|
-
test("container name embeds a sanitized hint and a short random suffix", async (t) => {
|
|
376
|
-
const { fixturePath, eventsPath, capturePath } = createFixture(t);
|
|
377
|
-
const hostInput = new PassThrough();
|
|
378
|
-
const signals = new EventEmitter();
|
|
379
|
-
const resultPromise = runFixture(fixturePath, eventsPath, capturePath, "crash-before-cidfile", "reverse", hostInput, signals, {}, { nameHint: "My Proj_18000" });
|
|
380
|
-
const deadline = Date.now() + 3_000;
|
|
381
|
-
let createdEvent;
|
|
382
|
-
while (!createdEvent && Date.now() < deadline) {
|
|
383
|
-
createdEvent = readEvents(eventsPath).find((event) => event.startsWith("created:"));
|
|
384
|
-
if (!createdEvent) {
|
|
385
|
-
await delay(10);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
assert.ok(createdEvent, "fixture should expose the generated container name");
|
|
389
|
-
assert.equal(await resultPromise, 137);
|
|
390
|
-
const containerName = createdEvent.slice("created:".length);
|
|
391
|
-
assert.match(containerName, /^staff-mcp-my-proj_18000-[0-9a-f]{6}$/);
|
|
392
|
-
assert.ok(containerName.length <= 63, "container name should stay within 63 characters");
|
|
393
|
-
assert.ok(readEvents(eventsPath).includes(`rm:${containerName}`));
|
|
394
|
-
});
|
|
395
|
-
//# sourceMappingURL=docker-proxy.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"docker-proxy.test.js","sourceRoot":"","sources":["../../tests/docker-proxy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { test, before, after } from "node:test";
|
|
3
|
-
import * as fs from "fs/promises";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
import * as os from "os";
|
|
6
|
-
import { enqueueFileWrite, atomicWriteFile } from "../src/utils/file-write-queue.js";
|
|
7
|
-
let tmpDir;
|
|
8
|
-
before(async () => {
|
|
9
|
-
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "fwq-test-"));
|
|
10
|
-
});
|
|
11
|
-
after(async () => {
|
|
12
|
-
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
13
|
-
});
|
|
14
|
-
test("concurrent read-modify-write cycles lose no updates", async () => {
|
|
15
|
-
const file = path.join(tmpDir, "concurrent.txt");
|
|
16
|
-
await fs.writeFile(file, "", "utf-8");
|
|
17
|
-
// The regression this guards against: N concurrent read-modify-write
|
|
18
|
-
// cycles (the edit_file_by_replace pattern) must all survive.
|
|
19
|
-
const N = 50;
|
|
20
|
-
await Promise.all(Array.from({ length: N }, (_, i) => enqueueFileWrite(file, async () => {
|
|
21
|
-
const content = await fs.readFile(file, "utf-8");
|
|
22
|
-
await atomicWriteFile(file, content + `line-${i}\n`);
|
|
23
|
-
})));
|
|
24
|
-
const final = await fs.readFile(file, "utf-8");
|
|
25
|
-
const lines = final.trim().split("\n");
|
|
26
|
-
assert.equal(lines.length, N, "all appends must survive");
|
|
27
|
-
for (let i = 0; i < N; i++) {
|
|
28
|
-
assert.ok(lines.includes(`line-${i}`), `missing line-${i}`);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
test("atomic write never produces torn content under concurrency", async () => {
|
|
32
|
-
const file = path.join(tmpDir, "atomic.txt");
|
|
33
|
-
const bigA = "A".repeat(200_000);
|
|
34
|
-
const bigB = "B".repeat(200_000);
|
|
35
|
-
await Promise.all([
|
|
36
|
-
enqueueFileWrite(file, () => atomicWriteFile(file, bigA)),
|
|
37
|
-
enqueueFileWrite(file, () => atomicWriteFile(file, bigB)),
|
|
38
|
-
enqueueFileWrite(file, () => atomicWriteFile(file, bigA)),
|
|
39
|
-
enqueueFileWrite(file, () => atomicWriteFile(file, bigB)),
|
|
40
|
-
]);
|
|
41
|
-
const final = await fs.readFile(file, "utf-8");
|
|
42
|
-
assert.ok(final === bigA || final === bigB, "content must be one complete version, never mixed");
|
|
43
|
-
});
|
|
44
|
-
test("writes to different paths still run in parallel", async () => {
|
|
45
|
-
const fileA = path.join(tmpDir, "a.txt");
|
|
46
|
-
const fileB = path.join(tmpDir, "b.txt");
|
|
47
|
-
let aRunning = false;
|
|
48
|
-
let overlapObserved = false;
|
|
49
|
-
await Promise.all([
|
|
50
|
-
enqueueFileWrite(fileA, async () => {
|
|
51
|
-
aRunning = true;
|
|
52
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
53
|
-
aRunning = false;
|
|
54
|
-
await atomicWriteFile(fileA, "a");
|
|
55
|
-
}),
|
|
56
|
-
enqueueFileWrite(fileB, async () => {
|
|
57
|
-
if (aRunning)
|
|
58
|
-
overlapObserved = true;
|
|
59
|
-
await atomicWriteFile(fileB, "b");
|
|
60
|
-
}),
|
|
61
|
-
]);
|
|
62
|
-
assert.equal(overlapObserved, true, "different paths must not block each other");
|
|
63
|
-
assert.equal(await fs.readFile(fileA, "utf-8"), "a");
|
|
64
|
-
assert.equal(await fs.readFile(fileB, "utf-8"), "b");
|
|
65
|
-
});
|
|
66
|
-
test("a failing task does not poison the queue", async () => {
|
|
67
|
-
const file = path.join(tmpDir, "poison.txt");
|
|
68
|
-
await assert.rejects(enqueueFileWrite(file, async () => {
|
|
69
|
-
throw new Error("boom");
|
|
70
|
-
}), /boom/);
|
|
71
|
-
// Subsequent task on the same path must still run.
|
|
72
|
-
const result = await enqueueFileWrite(file, async () => {
|
|
73
|
-
await atomicWriteFile(file, "alive");
|
|
74
|
-
return "ok";
|
|
75
|
-
});
|
|
76
|
-
assert.equal(result, "ok");
|
|
77
|
-
assert.equal(await fs.readFile(file, "utf-8"), "alive");
|
|
78
|
-
});
|
|
79
|
-
test("atomicWriteFile leaves no temp file behind", async () => {
|
|
80
|
-
const file = path.join(tmpDir, "clean.txt");
|
|
81
|
-
await atomicWriteFile(file, "content-中文-🎉");
|
|
82
|
-
assert.equal(await fs.readFile(file, "utf-8"), "content-中文-🎉");
|
|
83
|
-
const siblings = await fs.readdir(tmpDir);
|
|
84
|
-
assert.ok(!siblings.some((name) => name.endsWith(".tmp")), `temp file leaked: ${siblings.join(", ")}`);
|
|
85
|
-
});
|
|
86
|
-
test("atomicWriteFile cleans up temp file when write fails", async () => {
|
|
87
|
-
const dirFile = path.join(tmpDir, "is-a-dir");
|
|
88
|
-
await fs.mkdir(dirFile); // rename onto an existing non-empty path is fine, but writing INTO a dir path fails below
|
|
89
|
-
const blocked = path.join(dirFile, "sub.txt");
|
|
90
|
-
await fs.writeFile(blocked, "x");
|
|
91
|
-
// rename(file, existing-empty-dir) is OK on POSIX, so force failure differently:
|
|
92
|
-
// make the directory read-only so the temp file cannot be created.
|
|
93
|
-
const roDir = path.join(tmpDir, "read-only");
|
|
94
|
-
await fs.mkdir(roDir);
|
|
95
|
-
await fs.chmod(roDir, 0o500);
|
|
96
|
-
try {
|
|
97
|
-
await assert.rejects(atomicWriteFile(path.join(roDir, "x.txt"), "data"));
|
|
98
|
-
const leftovers = await fs.readdir(roDir);
|
|
99
|
-
assert.deepEqual(leftovers, [], "temp file must be cleaned up on failure");
|
|
100
|
-
}
|
|
101
|
-
finally {
|
|
102
|
-
await fs.chmod(roDir, 0o700);
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
//# sourceMappingURL=file-write-queue.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"file-write-queue.test.js","sourceRoot":"","sources":["../../tests/file-write-queue.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|