@huoye/app-ship 0.2.0 → 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/README.md +172 -139
- package/browser-extension/capture-fields.js +195 -0
- package/browser-extension/manifest.json +8 -0
- package/browser-extension/popup.html +152 -0
- package/browser-extension/popup.js +312 -0
- package/dist/browser-native.js +2031 -0
- package/dist/chunk-PUSXETML.js +21041 -0
- package/dist/cli.js +4936 -1632
- package/dist/index.cjs +7457 -4068
- package/dist/index.d.cts +1309 -531
- package/dist/index.d.ts +1309 -531
- package/dist/index.js +7397 -4060
- package/dist/mcp-tools-PTJDFF2T.js +8703 -0
- package/dist/mcp.js +15 -0
- package/dist/stdio-BXBWA2I3.js +514 -0
- package/docs/browser-credentials.md +178 -0
- package/package.json +26 -8
- package/scripts/register-browser-host.mjs +110 -0
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/mcp.ts
|
|
4
|
+
var nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
5
|
+
if (nodeMajor < 20) {
|
|
6
|
+
console.error("app-ship-mcp \u9700\u8981 Node.js 20 \u6216\u66F4\u9AD8\u7248\u672C");
|
|
7
|
+
process.exitCode = 1;
|
|
8
|
+
} else {
|
|
9
|
+
const [{ serveStdio }, { createMcpServer }] = await Promise.all([
|
|
10
|
+
import("./stdio-BXBWA2I3.js"),
|
|
11
|
+
import("./mcp-tools-PTJDFF2T.js")
|
|
12
|
+
]);
|
|
13
|
+
serveStdio(() => createMcpServer("0.3.0"));
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=mcp.js.map
|
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_MAX_SUBSCRIPTIONS,
|
|
4
|
+
McpServer,
|
|
5
|
+
ProtocolErrorCode,
|
|
6
|
+
ReadBuffer,
|
|
7
|
+
SUPPORTED_MODERN_PROTOCOL_VERSIONS,
|
|
8
|
+
StdioListenRouter,
|
|
9
|
+
UnsupportedProtocolVersionError,
|
|
10
|
+
carriesValidModernEnvelopeClaim,
|
|
11
|
+
envelopeClaimVersion,
|
|
12
|
+
hasEnvelopeClaim,
|
|
13
|
+
installModernOnlyHandlers,
|
|
14
|
+
isJSONRPCErrorResponse,
|
|
15
|
+
isJSONRPCNotification,
|
|
16
|
+
isJSONRPCRequest,
|
|
17
|
+
isJSONRPCResultResponse,
|
|
18
|
+
modernOnlyStrictRejection,
|
|
19
|
+
process,
|
|
20
|
+
requestMetaOf,
|
|
21
|
+
serializeMessage,
|
|
22
|
+
serverIdentityOf,
|
|
23
|
+
setNegotiatedProtocolVersion,
|
|
24
|
+
validateEnvelopeMeta
|
|
25
|
+
} from "./chunk-PUSXETML.js";
|
|
26
|
+
|
|
27
|
+
// node_modules/@modelcontextprotocol/server/dist/stdio.mjs
|
|
28
|
+
var StdioServerTransport = class {
|
|
29
|
+
_readBuffer;
|
|
30
|
+
_started = false;
|
|
31
|
+
_closed = false;
|
|
32
|
+
constructor(_stdin = process.stdin, _stdout = process.stdout, options) {
|
|
33
|
+
this._stdin = _stdin;
|
|
34
|
+
this._stdout = _stdout;
|
|
35
|
+
this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
|
|
36
|
+
}
|
|
37
|
+
onclose;
|
|
38
|
+
onerror;
|
|
39
|
+
onmessage;
|
|
40
|
+
_ondata = (chunk) => {
|
|
41
|
+
try {
|
|
42
|
+
this._readBuffer.append(chunk);
|
|
43
|
+
this.processReadBuffer();
|
|
44
|
+
} catch (error) {
|
|
45
|
+
this.onerror?.(error);
|
|
46
|
+
this.close().catch(() => {
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
_onerror = (error) => {
|
|
51
|
+
this.onerror?.(error);
|
|
52
|
+
};
|
|
53
|
+
_onstdouterror = (error) => {
|
|
54
|
+
this.onerror?.(error);
|
|
55
|
+
this.close().catch(() => {
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Starts listening for messages on `stdin`.
|
|
60
|
+
*/
|
|
61
|
+
async start() {
|
|
62
|
+
if (this._started) throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
|
|
63
|
+
this._started = true;
|
|
64
|
+
this._stdin.on("data", this._ondata);
|
|
65
|
+
this._stdin.on("error", this._onerror);
|
|
66
|
+
this._stdout.on("error", this._onstdouterror);
|
|
67
|
+
}
|
|
68
|
+
processReadBuffer() {
|
|
69
|
+
while (true) try {
|
|
70
|
+
const message = this._readBuffer.readMessage();
|
|
71
|
+
if (message === null) break;
|
|
72
|
+
this.onmessage?.(message);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
this.onerror?.(error);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async close() {
|
|
78
|
+
if (this._closed) return;
|
|
79
|
+
this._closed = true;
|
|
80
|
+
this._stdin.off("data", this._ondata);
|
|
81
|
+
this._stdin.off("error", this._onerror);
|
|
82
|
+
this._stdout.off("error", this._onstdouterror);
|
|
83
|
+
if (this._stdin.listenerCount("data") === 0) this._stdin.pause();
|
|
84
|
+
this._readBuffer.clear();
|
|
85
|
+
this.onclose?.();
|
|
86
|
+
}
|
|
87
|
+
send(message) {
|
|
88
|
+
if (this._closed) return Promise.reject(/* @__PURE__ */ new Error("StdioServerTransport is closed"));
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
const json = serializeMessage(message);
|
|
91
|
+
let settled = false;
|
|
92
|
+
const onError = (error) => {
|
|
93
|
+
if (settled) return;
|
|
94
|
+
settled = true;
|
|
95
|
+
this._stdout.off("error", onError);
|
|
96
|
+
this._stdout.off("drain", onDrain);
|
|
97
|
+
reject(error);
|
|
98
|
+
};
|
|
99
|
+
const onDrain = () => {
|
|
100
|
+
if (settled) return;
|
|
101
|
+
settled = true;
|
|
102
|
+
this._stdout.off("error", onError);
|
|
103
|
+
this._stdout.off("drain", onDrain);
|
|
104
|
+
resolve();
|
|
105
|
+
};
|
|
106
|
+
this._stdout.once("error", onError);
|
|
107
|
+
if (this._stdout.write(json)) {
|
|
108
|
+
if (settled) return;
|
|
109
|
+
settled = true;
|
|
110
|
+
this._stdout.off("error", onError);
|
|
111
|
+
resolve();
|
|
112
|
+
} else if (!settled) this._stdout.once("drain", onDrain);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
var DISCARD_ANSWER_TIMEOUT_MS = 3e3;
|
|
117
|
+
var StdioConnectionChannel = class {
|
|
118
|
+
onclose;
|
|
119
|
+
onerror;
|
|
120
|
+
onmessage;
|
|
121
|
+
_closed = false;
|
|
122
|
+
/** Request ids the entry delivered to the instance that the instance has not yet answered. */
|
|
123
|
+
_pendingRequests = /* @__PURE__ */ new Set();
|
|
124
|
+
_drainWaiters = [];
|
|
125
|
+
constructor(_wire, _onInstanceClose, _outboundIntercept) {
|
|
126
|
+
this._wire = _wire;
|
|
127
|
+
this._onInstanceClose = _onInstanceClose;
|
|
128
|
+
this._outboundIntercept = _outboundIntercept;
|
|
129
|
+
}
|
|
130
|
+
async start() {
|
|
131
|
+
}
|
|
132
|
+
async send(message, options) {
|
|
133
|
+
if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {
|
|
134
|
+
const { id } = message;
|
|
135
|
+
if (id !== void 0) this._settle(id);
|
|
136
|
+
}
|
|
137
|
+
if (this._closed) return;
|
|
138
|
+
if (this._outboundIntercept?.(message) === "handled") return;
|
|
139
|
+
return this._wire.send(message, options);
|
|
140
|
+
}
|
|
141
|
+
setProtocolVersion = (version) => {
|
|
142
|
+
this._wire.setProtocolVersion?.(version);
|
|
143
|
+
};
|
|
144
|
+
/** Forwards one inbound message to the connected instance. */
|
|
145
|
+
deliver(message, extra) {
|
|
146
|
+
if (this._closed) return;
|
|
147
|
+
if (isJSONRPCRequest(message)) this._pendingRequests.add(message.id);
|
|
148
|
+
else if (isJSONRPCNotification(message) && message.method === "notifications/cancelled") {
|
|
149
|
+
const cancelledId = message.params?.requestId;
|
|
150
|
+
if (cancelledId !== void 0) this._settle(cancelledId);
|
|
151
|
+
}
|
|
152
|
+
this.onmessage?.(message, extra);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Resolves once every request delivered to the instance has been answered
|
|
156
|
+
* through {@linkcode send}, settled by a delivered cancellation, or the
|
|
157
|
+
* channel has been closed and nothing further can be answered. The wait is
|
|
158
|
+
* bounded by `timeoutMs` as a backstop so no edge can hold the caller
|
|
159
|
+
* indefinitely; resolves `false` only when the bound elapsed with requests
|
|
160
|
+
* still unanswered. Used by the probe-discard path so a probe request the
|
|
161
|
+
* entry accepted is never silently dropped.
|
|
162
|
+
*/
|
|
163
|
+
async whenRequestsAnswered(timeoutMs) {
|
|
164
|
+
if (this._closed || this._pendingRequests.size === 0) return true;
|
|
165
|
+
return await new Promise((resolve) => {
|
|
166
|
+
const waiter = () => {
|
|
167
|
+
clearTimeout(timer);
|
|
168
|
+
resolve(true);
|
|
169
|
+
};
|
|
170
|
+
const timer = setTimeout(() => {
|
|
171
|
+
this._drainWaiters = this._drainWaiters.filter((pending) => pending !== waiter);
|
|
172
|
+
resolve(false);
|
|
173
|
+
}, timeoutMs);
|
|
174
|
+
this._drainWaiters.push(waiter);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
async close() {
|
|
178
|
+
if (this._closed) return;
|
|
179
|
+
this._closed = true;
|
|
180
|
+
this._pendingRequests.clear();
|
|
181
|
+
this._releaseDrainWaiters();
|
|
182
|
+
try {
|
|
183
|
+
this._onInstanceClose();
|
|
184
|
+
} finally {
|
|
185
|
+
this.onclose?.();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
_settle(id) {
|
|
189
|
+
this._pendingRequests.delete(id);
|
|
190
|
+
if (this._pendingRequests.size === 0) this._releaseDrainWaiters();
|
|
191
|
+
}
|
|
192
|
+
_releaseDrainWaiters() {
|
|
193
|
+
const waiters = this._drainWaiters;
|
|
194
|
+
this._drainWaiters = [];
|
|
195
|
+
for (const waiter of waiters) waiter();
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
function classifyOpeningMessage(message) {
|
|
199
|
+
const params = message.params;
|
|
200
|
+
if (message.method === "initialize" && !carriesValidModernEnvelopeClaim(params)) {
|
|
201
|
+
const requestedVersion = params !== null && typeof params === "object" && typeof params.protocolVersion === "string" ? params.protocolVersion : void 0;
|
|
202
|
+
return {
|
|
203
|
+
kind: "legacy",
|
|
204
|
+
reason: "initialize",
|
|
205
|
+
...requestedVersion !== void 0 && { requestedVersion }
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
if (!hasEnvelopeClaim(params)) return {
|
|
209
|
+
kind: "legacy",
|
|
210
|
+
reason: "no-claim"
|
|
211
|
+
};
|
|
212
|
+
const meta = requestMetaOf(params);
|
|
213
|
+
const firstIssue = (meta === void 0 ? [] : validateEnvelopeMeta(meta))[0];
|
|
214
|
+
if (firstIssue !== void 0) return {
|
|
215
|
+
kind: "invalid-envelope",
|
|
216
|
+
issue: firstIssue
|
|
217
|
+
};
|
|
218
|
+
const claimedVersion = envelopeClaimVersion(params);
|
|
219
|
+
if (claimedVersion === void 0 || !SUPPORTED_MODERN_PROTOCOL_VERSIONS.includes(claimedVersion)) return {
|
|
220
|
+
kind: "unsupported-revision",
|
|
221
|
+
requested: claimedVersion ?? "unknown"
|
|
222
|
+
};
|
|
223
|
+
return {
|
|
224
|
+
kind: "modern",
|
|
225
|
+
revision: claimedVersion,
|
|
226
|
+
classification: {
|
|
227
|
+
era: "modern",
|
|
228
|
+
revision: claimedVersion
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function serveStdio(factory, options = {}) {
|
|
233
|
+
const legacyMode = options.legacy ?? "serve";
|
|
234
|
+
const wire = options.transport ?? new StdioServerTransport();
|
|
235
|
+
let state = { phase: "opening" };
|
|
236
|
+
let discarding;
|
|
237
|
+
let closing = false;
|
|
238
|
+
const isTornDown = () => closing || state.phase === "closed";
|
|
239
|
+
const reportError = (error) => {
|
|
240
|
+
try {
|
|
241
|
+
options.onerror?.(error);
|
|
242
|
+
} catch {
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
const writeErrorResponse = (id, code, message, data) => wire.send({
|
|
246
|
+
jsonrpc: "2.0",
|
|
247
|
+
id,
|
|
248
|
+
error: {
|
|
249
|
+
code,
|
|
250
|
+
message,
|
|
251
|
+
...data !== void 0 && { data }
|
|
252
|
+
}
|
|
253
|
+
}).catch((error) => reportError(toError(error)));
|
|
254
|
+
const listenRouter = new StdioListenRouter(options.maxSubscriptions ?? DEFAULT_MAX_SUBSCRIPTIONS);
|
|
255
|
+
const modernOutboundIntercept = (message) => {
|
|
256
|
+
if (!isJSONRPCNotification(message)) return void 0;
|
|
257
|
+
const routed = listenRouter.routeOutbound(message);
|
|
258
|
+
if (routed === "passthrough") return void 0;
|
|
259
|
+
for (const stamped of routed) wire.send({
|
|
260
|
+
jsonrpc: "2.0",
|
|
261
|
+
...stamped
|
|
262
|
+
}).catch((error) => reportError(toError(error)));
|
|
263
|
+
return "handled";
|
|
264
|
+
};
|
|
265
|
+
const tryServeListen = async (message) => {
|
|
266
|
+
if (isJSONRPCRequest(message) && message.method === "subscriptions/listen") {
|
|
267
|
+
const meta = requestMetaOf(message.params);
|
|
268
|
+
const issue = hasEnvelopeClaim(message.params) ? (meta === void 0 ? [] : validateEnvelopeMeta(meta))[0] : {
|
|
269
|
+
key: "_meta",
|
|
270
|
+
problem: "the per-request envelope is required on protocol revision 2026-07-28"
|
|
271
|
+
};
|
|
272
|
+
const claimedVersion = envelopeClaimVersion(message.params);
|
|
273
|
+
let reply;
|
|
274
|
+
if (issue !== void 0) reply = {
|
|
275
|
+
jsonrpc: "2.0",
|
|
276
|
+
id: message.id,
|
|
277
|
+
error: {
|
|
278
|
+
code: -32602,
|
|
279
|
+
message: `Invalid _meta envelope: ${issue.key}: ${issue.problem}`
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
else if (claimedVersion === void 0 || !SUPPORTED_MODERN_PROTOCOL_VERSIONS.includes(claimedVersion)) {
|
|
283
|
+
const error = new UnsupportedProtocolVersionError({
|
|
284
|
+
supported: [...SUPPORTED_MODERN_PROTOCOL_VERSIONS],
|
|
285
|
+
requested: claimedVersion ?? "unknown"
|
|
286
|
+
});
|
|
287
|
+
reply = {
|
|
288
|
+
jsonrpc: "2.0",
|
|
289
|
+
id: message.id,
|
|
290
|
+
error: {
|
|
291
|
+
code: error.code,
|
|
292
|
+
message: error.message,
|
|
293
|
+
data: error.data
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
} else reply = listenRouter.serve(message);
|
|
297
|
+
await wire.send("error" in reply ? reply : {
|
|
298
|
+
jsonrpc: "2.0",
|
|
299
|
+
method: reply.method,
|
|
300
|
+
params: reply.params
|
|
301
|
+
}).catch((error) => reportError(toError(error)));
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
if (isJSONRPCNotification(message) && message.method === "notifications/cancelled") {
|
|
305
|
+
const cancelledId = message.params?.requestId;
|
|
306
|
+
if (cancelledId !== void 0 && listenRouter.cancel(cancelledId)) return true;
|
|
307
|
+
}
|
|
308
|
+
return false;
|
|
309
|
+
};
|
|
310
|
+
const answerLegacyRejection = (request, reason, requestedVersion) => {
|
|
311
|
+
const rejection = modernOnlyStrictRejection({
|
|
312
|
+
kind: "legacy",
|
|
313
|
+
reason,
|
|
314
|
+
...requestedVersion !== void 0 && { requestedVersion }
|
|
315
|
+
}, SUPPORTED_MODERN_PROTOCOL_VERSIONS);
|
|
316
|
+
if (rejection === void 0) return Promise.resolve();
|
|
317
|
+
reportError(/* @__PURE__ */ new Error(`Rejected 2025-era request on a modern-only stdio connection (${rejection.cell}): ${rejection.message}`));
|
|
318
|
+
return writeErrorResponse(request.id, rejection.code, rejection.message, rejection.data);
|
|
319
|
+
};
|
|
320
|
+
const onInstanceClosed = (channel) => {
|
|
321
|
+
if (closing || channel === discarding) return;
|
|
322
|
+
closeAll();
|
|
323
|
+
};
|
|
324
|
+
const connectInstance = async (era, revision) => {
|
|
325
|
+
const product = await factory({ era });
|
|
326
|
+
const server = product instanceof McpServer ? product.server : product;
|
|
327
|
+
if (era === "modern") {
|
|
328
|
+
setNegotiatedProtocolVersion(server, revision);
|
|
329
|
+
installModernOnlyHandlers(server, SUPPORTED_MODERN_PROTOCOL_VERSIONS);
|
|
330
|
+
listenRouter.setServerCapabilities(server.getCapabilities(), serverIdentityOf(server));
|
|
331
|
+
}
|
|
332
|
+
const channel = new StdioConnectionChannel(wire, () => onInstanceClosed(channel), era === "modern" ? modernOutboundIntercept : void 0);
|
|
333
|
+
await product.connect(channel);
|
|
334
|
+
return {
|
|
335
|
+
product,
|
|
336
|
+
channel
|
|
337
|
+
};
|
|
338
|
+
};
|
|
339
|
+
const disposeLateInstance = (instance) => instance.product.close().catch((error) => reportError(toError(error)));
|
|
340
|
+
const discardProbeInstance = async (instance) => {
|
|
341
|
+
discarding = instance.channel;
|
|
342
|
+
try {
|
|
343
|
+
if (!await instance.channel.whenRequestsAnswered(DISCARD_ANSWER_TIMEOUT_MS)) reportError(/* @__PURE__ */ new Error(`Discarded the probe instance with requests still unanswered after ${DISCARD_ANSWER_TIMEOUT_MS}ms; continuing with the fallback`));
|
|
344
|
+
await instance.product.close();
|
|
345
|
+
} catch (error) {
|
|
346
|
+
reportError(toError(error));
|
|
347
|
+
} finally {
|
|
348
|
+
discarding = void 0;
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
const processMessage = async (message) => {
|
|
352
|
+
if (state.phase === "closed") return;
|
|
353
|
+
if (state.phase === "pinned") {
|
|
354
|
+
if (state.era === "modern" && isJSONRPCRequest(message) && message.method === "initialize" && !carriesValidModernEnvelopeClaim(message.params)) {
|
|
355
|
+
await answerLegacyRejection(message, "initialize", message.params !== null && typeof message.params === "object" && typeof message.params.protocolVersion === "string" ? message.params.protocolVersion : void 0);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (state.era === "modern" && await tryServeListen(message)) return;
|
|
359
|
+
state.instance.channel.deliver(message);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (!isJSONRPCRequest(message) && !isJSONRPCNotification(message)) {
|
|
363
|
+
reportError(/* @__PURE__ */ new Error("Discarded a JSON-RPC response received before the connection negotiated an era"));
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
const opening = classifyOpeningMessage(message);
|
|
367
|
+
switch (opening.kind) {
|
|
368
|
+
case "invalid-envelope": {
|
|
369
|
+
const detail = `Invalid _meta envelope for protocol revision 2026-07-28: ${opening.issue.key}: ${opening.issue.problem}`;
|
|
370
|
+
if (isJSONRPCRequest(message)) await writeErrorResponse(message.id, ProtocolErrorCode.InvalidParams, detail, { envelope: opening.issue });
|
|
371
|
+
else reportError(/* @__PURE__ */ new Error(`Discarded a notification with a malformed envelope: ${detail}`));
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
case "unsupported-revision":
|
|
375
|
+
if (isJSONRPCRequest(message)) {
|
|
376
|
+
const error = new UnsupportedProtocolVersionError({
|
|
377
|
+
supported: [...SUPPORTED_MODERN_PROTOCOL_VERSIONS],
|
|
378
|
+
requested: opening.requested
|
|
379
|
+
});
|
|
380
|
+
reportError(error);
|
|
381
|
+
await writeErrorResponse(message.id, error.code, error.message, error.data);
|
|
382
|
+
} else reportError(/* @__PURE__ */ new Error(`Discarded a notification claiming unsupported protocol revision ${opening.requested}`));
|
|
383
|
+
return;
|
|
384
|
+
case "modern":
|
|
385
|
+
if (isJSONRPCRequest(message) && message.method === "server/discover") {
|
|
386
|
+
if (state.phase === "probe") {
|
|
387
|
+
state.instance.channel.deliver(message, { classification: opening.classification });
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const instance = await connectInstance("modern", opening.revision);
|
|
391
|
+
if (isTornDown()) {
|
|
392
|
+
await disposeLateInstance(instance);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
state = {
|
|
396
|
+
phase: "probe",
|
|
397
|
+
instance
|
|
398
|
+
};
|
|
399
|
+
instance.channel.deliver(message, { classification: opening.classification });
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (state.phase === "probe") {
|
|
403
|
+
if (isJSONRPCNotification(message)) {
|
|
404
|
+
state.instance.channel.deliver(message, { classification: opening.classification });
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
state = {
|
|
408
|
+
phase: "pinned",
|
|
409
|
+
era: "modern",
|
|
410
|
+
instance: state.instance
|
|
411
|
+
};
|
|
412
|
+
} else {
|
|
413
|
+
const instance = await connectInstance("modern", opening.revision);
|
|
414
|
+
if (isTornDown()) {
|
|
415
|
+
await disposeLateInstance(instance);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
state = {
|
|
419
|
+
phase: "pinned",
|
|
420
|
+
era: "modern",
|
|
421
|
+
instance
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
if (await tryServeListen(message)) return;
|
|
425
|
+
state.instance.channel.deliver(message, { classification: opening.classification });
|
|
426
|
+
return;
|
|
427
|
+
case "legacy": {
|
|
428
|
+
if (legacyMode === "reject") {
|
|
429
|
+
if (isJSONRPCRequest(message)) await answerLegacyRejection(message, opening.reason, opening.requestedVersion);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
if (state.phase === "probe") {
|
|
433
|
+
await discardProbeInstance(state.instance);
|
|
434
|
+
if (isTornDown()) return;
|
|
435
|
+
state = { phase: "opening" };
|
|
436
|
+
}
|
|
437
|
+
const instance = await connectInstance("legacy");
|
|
438
|
+
if (isTornDown()) {
|
|
439
|
+
await disposeLateInstance(instance);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
state = {
|
|
443
|
+
phase: "pinned",
|
|
444
|
+
era: "legacy",
|
|
445
|
+
instance
|
|
446
|
+
};
|
|
447
|
+
state.instance.channel.deliver(message);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
const queue = [];
|
|
453
|
+
let pumping = false;
|
|
454
|
+
const pump = async () => {
|
|
455
|
+
if (pumping) return;
|
|
456
|
+
pumping = true;
|
|
457
|
+
try {
|
|
458
|
+
while (queue.length > 0) {
|
|
459
|
+
const message = queue.shift();
|
|
460
|
+
try {
|
|
461
|
+
await processMessage(message);
|
|
462
|
+
} catch (error) {
|
|
463
|
+
if (isJSONRPCRequest(message)) await writeErrorResponse(message.id, ProtocolErrorCode.InternalError, "Internal server error");
|
|
464
|
+
reportError(toError(error));
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
} finally {
|
|
468
|
+
pumping = false;
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
const closeAll = async () => {
|
|
472
|
+
if (closing || state.phase === "closed") return;
|
|
473
|
+
closing = true;
|
|
474
|
+
const current = state;
|
|
475
|
+
state = { phase: "closed" };
|
|
476
|
+
for (const result of listenRouter.teardownAll()) await wire.send(result).catch((error) => reportError(toError(error)));
|
|
477
|
+
if (current.phase === "probe" || current.phase === "pinned") await current.instance.product.close().catch((error) => reportError(toError(error)));
|
|
478
|
+
await wire.close().catch((error) => reportError(toError(error)));
|
|
479
|
+
};
|
|
480
|
+
wire.onmessage = (message) => {
|
|
481
|
+
queue.push(message);
|
|
482
|
+
pump();
|
|
483
|
+
};
|
|
484
|
+
wire.onerror = (error) => {
|
|
485
|
+
reportError(error);
|
|
486
|
+
if (state.phase === "probe" || state.phase === "pinned") state.instance.channel.onerror?.(error);
|
|
487
|
+
};
|
|
488
|
+
wire.onclose = () => {
|
|
489
|
+
if (closing || state.phase === "closed") return;
|
|
490
|
+
closing = true;
|
|
491
|
+
const current = state;
|
|
492
|
+
state = { phase: "closed" };
|
|
493
|
+
if (current.phase === "probe" || current.phase === "pinned") current.instance.product.close().catch((error) => reportError(toError(error)));
|
|
494
|
+
};
|
|
495
|
+
const started = wire.start().catch((error) => {
|
|
496
|
+
reportError(toError(error));
|
|
497
|
+
throw error;
|
|
498
|
+
});
|
|
499
|
+
started.catch(() => {
|
|
500
|
+
});
|
|
501
|
+
return { close: async () => {
|
|
502
|
+
await started.catch(() => {
|
|
503
|
+
});
|
|
504
|
+
await closeAll();
|
|
505
|
+
} };
|
|
506
|
+
}
|
|
507
|
+
function toError(value) {
|
|
508
|
+
return value instanceof Error ? value : new Error(String(value));
|
|
509
|
+
}
|
|
510
|
+
export {
|
|
511
|
+
StdioServerTransport,
|
|
512
|
+
serveStdio
|
|
513
|
+
};
|
|
514
|
+
//# sourceMappingURL=stdio-BXBWA2I3.js.map
|