gitnexus 1.6.6-rc.146 → 1.6.6-rc.147
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.
|
@@ -6,10 +6,13 @@ export declare class CompatibleStdioServerTransport implements Transport {
|
|
|
6
6
|
private readonly _stdout;
|
|
7
7
|
private _readBuffer;
|
|
8
8
|
private _started;
|
|
9
|
+
private _closed;
|
|
9
10
|
private _framing;
|
|
10
11
|
onmessage?: (message: JSONRPCMessage) => void;
|
|
11
12
|
onerror?: (error: Error) => void;
|
|
12
13
|
onclose?: () => void;
|
|
14
|
+
/** Stable bound reference so `on`/`off` always use the same function. */
|
|
15
|
+
private readonly _boundClose;
|
|
13
16
|
constructor(_stdin?: NodeJS.ReadableStream, _stdout?: NodeJS.WritableStream);
|
|
14
17
|
private readonly _ondata;
|
|
15
18
|
private readonly _onerror;
|
|
@@ -35,10 +35,13 @@ export class CompatibleStdioServerTransport {
|
|
|
35
35
|
_stdout;
|
|
36
36
|
_readBuffer;
|
|
37
37
|
_started = false;
|
|
38
|
+
_closed = false;
|
|
38
39
|
_framing = null;
|
|
39
40
|
onmessage;
|
|
40
41
|
onerror;
|
|
41
42
|
onclose;
|
|
43
|
+
/** Stable bound reference so `on`/`off` always use the same function. */
|
|
44
|
+
_boundClose = this.close.bind(this);
|
|
42
45
|
constructor(_stdin = process.stdin, _stdout = process.stdout) {
|
|
43
46
|
this._stdin = _stdin;
|
|
44
47
|
this._stdout = _stdout;
|
|
@@ -59,9 +62,23 @@ export class CompatibleStdioServerTransport {
|
|
|
59
62
|
if (this._started) {
|
|
60
63
|
throw new Error('CompatibleStdioServerTransport already started!');
|
|
61
64
|
}
|
|
65
|
+
if (this._closed) {
|
|
66
|
+
throw new Error('CompatibleStdioServerTransport has been permanently closed');
|
|
67
|
+
}
|
|
68
|
+
// If stdin is already closed (parent died before we registered listeners),
|
|
69
|
+
// the 'end'/'close' event was already emitted and will never fire again.
|
|
70
|
+
// Close the transport immediately to avoid orphaned processes.
|
|
71
|
+
if (this._stdin.readableEnded ||
|
|
72
|
+
this._stdin.destroyed) {
|
|
73
|
+
this._started = true;
|
|
74
|
+
await this.close();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
62
77
|
this._started = true;
|
|
63
78
|
this._stdin.on('data', this._ondata);
|
|
64
79
|
this._stdin.on('error', this._onerror);
|
|
80
|
+
this._stdin.on('end', this._boundClose);
|
|
81
|
+
this._stdin.on('close', this._boundClose);
|
|
65
82
|
}
|
|
66
83
|
detectFraming() {
|
|
67
84
|
if (!this._readBuffer || this._readBuffer.length === 0) {
|
|
@@ -162,8 +179,13 @@ export class CompatibleStdioServerTransport {
|
|
|
162
179
|
}
|
|
163
180
|
}
|
|
164
181
|
async close() {
|
|
182
|
+
if (this._closed)
|
|
183
|
+
return;
|
|
184
|
+
this._closed = true;
|
|
165
185
|
this._stdin.off('data', this._ondata);
|
|
166
186
|
this._stdin.off('error', this._onerror);
|
|
187
|
+
this._stdin.off('end', this._boundClose);
|
|
188
|
+
this._stdin.off('close', this._boundClose);
|
|
167
189
|
const remainingDataListeners = this._stdin.listenerCount('data');
|
|
168
190
|
if (remainingDataListeners === 0) {
|
|
169
191
|
this._stdin.pause();
|
package/dist/mcp/server.js
CHANGED
|
@@ -287,7 +287,6 @@ export async function startMCPServer(backend) {
|
|
|
287
287
|
},
|
|
288
288
|
});
|
|
289
289
|
const transport = new CompatibleStdioServerTransport(process.stdin, safeStdout);
|
|
290
|
-
await server.connect(transport);
|
|
291
290
|
// Surface the redirect counter on shutdown so users see the volume of
|
|
292
291
|
// stray writes even when individual payloads were truncated/suppressed.
|
|
293
292
|
process.on('exit', () => sentinel.flushSummary());
|
|
@@ -337,9 +336,18 @@ export async function startMCPServer(backend) {
|
|
|
337
336
|
process.stderr.write(`GitNexus MCP unhandledRejection: ${reason?.stack || reason}\n`);
|
|
338
337
|
});
|
|
339
338
|
// Handle stdio errors — stdin close means the parent process is gone.
|
|
339
|
+
// Defense-in-depth: the transport also listens for stdin end/close and
|
|
340
|
+
// handles transport-level cleanup. These listeners handle process-level
|
|
341
|
+
// shutdown. Both paths are idempotent and safe to fire together.
|
|
340
342
|
// Wrap so the event payload (e.g. an Error for 'error') can never reach
|
|
341
343
|
// process.exit() as a non-numeric exit code, and void the returned promise.
|
|
342
344
|
process.stdin.on('end', () => void shutdown(0));
|
|
345
|
+
process.stdin.on('close', () => void shutdown(0));
|
|
343
346
|
process.stdin.on('error', () => void shutdown(0));
|
|
344
347
|
process.stdout.on('error', () => void shutdown(0));
|
|
348
|
+
if (process.stdin.readableEnded || process.stdin.destroyed) {
|
|
349
|
+
await shutdown(0);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
await server.connect(transport);
|
|
345
353
|
}
|
package/package.json
CHANGED