@ethisyscore/extension-runtime 1.136.0 → 1.138.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 +2 -2
- package/dist/MockPluginStorage-DF3DL1dI.d.ts +59 -0
- package/dist/MockPluginStorage-QKfz8CMt.d.cts +59 -0
- package/dist/{bridge-client-RJOl8WlU.d.ts → bridge-client-CkxYcZcm.d.ts} +1 -1
- package/dist/{bridge-client-D9EYFq1l.d.cts → bridge-client-DkogtCvn.d.cts} +1 -1
- package/dist/{bridge-envelopes-M5a42rAy.d.cts → bridge-envelopes-BcKu-nQm.d.cts} +72 -1
- package/dist/{bridge-envelopes-M5a42rAy.d.ts → bridge-envelopes-BcKu-nQm.d.ts} +72 -1
- package/dist/host/index.cjs +84 -2
- package/dist/host/index.cjs.map +1 -1
- package/dist/host/index.d.cts +3 -3
- package/dist/host/index.d.ts +3 -3
- package/dist/host/index.js +84 -2
- package/dist/host/index.js.map +1 -1
- package/dist/mock-host/cli.cjs +340 -7
- package/dist/mock-host/cli.cjs.map +1 -1
- package/dist/mock-host/cli.d.cts +10 -3
- package/dist/mock-host/cli.d.ts +10 -3
- package/dist/mock-host/cli.js +339 -8
- package/dist/mock-host/cli.js.map +1 -1
- package/dist/mock-host/index.cjs +276 -1
- package/dist/mock-host/index.cjs.map +1 -1
- package/dist/mock-host/index.d.cts +14 -4
- package/dist/mock-host/index.d.ts +14 -4
- package/dist/mock-host/index.js +276 -2
- package/dist/mock-host/index.js.map +1 -1
- package/dist/plugin/index.cjs +407 -100
- package/dist/plugin/index.cjs.map +1 -1
- package/dist/plugin/index.d.cts +186 -6
- package/dist/plugin/index.d.ts +186 -6
- package/dist/plugin/index.js +395 -101
- package/dist/plugin/index.js.map +1 -1
- package/dist/{transport-UaoDRdm3.d.cts → transport-D1lE-5nt.d.cts} +30 -3
- package/dist/{transport-CFzJQiFG.d.ts → transport-INVtDo2s.d.ts} +30 -3
- package/package.json +2 -2
package/dist/plugin/index.cjs
CHANGED
|
@@ -11,7 +11,376 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
11
11
|
|
|
12
12
|
var ReactReconciler__default = /*#__PURE__*/_interopDefault(ReactReconciler);
|
|
13
13
|
|
|
14
|
-
// src/
|
|
14
|
+
// src/bridge/mcp-error.ts
|
|
15
|
+
var MCP_ERROR_CODES = [
|
|
16
|
+
"unauthorized",
|
|
17
|
+
"forbidden",
|
|
18
|
+
"not_found",
|
|
19
|
+
"invalid_request",
|
|
20
|
+
"conflict",
|
|
21
|
+
"rate_limited",
|
|
22
|
+
"timeout",
|
|
23
|
+
"unavailable",
|
|
24
|
+
"internal"
|
|
25
|
+
];
|
|
26
|
+
function isMcpErrorCode(value) {
|
|
27
|
+
return typeof value === "string" && MCP_ERROR_CODES.includes(value);
|
|
28
|
+
}
|
|
29
|
+
var RETRYABLE = ["rate_limited", "timeout", "unavailable"];
|
|
30
|
+
function isRetryableMcpErrorCode(code) {
|
|
31
|
+
return RETRYABLE.includes(code);
|
|
32
|
+
}
|
|
33
|
+
var McpToolError = class extends Error {
|
|
34
|
+
/** Machine-readable classification. `internal` when the host sent none. */
|
|
35
|
+
code;
|
|
36
|
+
/**
|
|
37
|
+
* Marks the instance for {@link isMcpToolError}. A structural marker rather than a prototype
|
|
38
|
+
* check because the error can be constructed in one bundle and inspected in another, where
|
|
39
|
+
* `instanceof` compares two different class objects and answers false.
|
|
40
|
+
*/
|
|
41
|
+
isMcpToolError = true;
|
|
42
|
+
constructor(message, code = "internal") {
|
|
43
|
+
super(message);
|
|
44
|
+
this.name = "McpHostError";
|
|
45
|
+
this.code = code;
|
|
46
|
+
}
|
|
47
|
+
/** True when this failure is worth retrying unchanged. */
|
|
48
|
+
get retryable() {
|
|
49
|
+
return isRetryableMcpErrorCode(this.code);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
function isMcpToolError(value) {
|
|
53
|
+
return value instanceof McpToolError || value !== null && typeof value === "object" && value.isMcpToolError === true && isMcpErrorCode(value.code);
|
|
54
|
+
}
|
|
55
|
+
function mcpErrorCodeFromHttpStatus(status) {
|
|
56
|
+
switch (status) {
|
|
57
|
+
case 401:
|
|
58
|
+
return "unauthorized";
|
|
59
|
+
case 403:
|
|
60
|
+
return "forbidden";
|
|
61
|
+
case 404:
|
|
62
|
+
return "not_found";
|
|
63
|
+
case 400:
|
|
64
|
+
case 422:
|
|
65
|
+
return "invalid_request";
|
|
66
|
+
case 409:
|
|
67
|
+
case 412:
|
|
68
|
+
return "conflict";
|
|
69
|
+
case 429:
|
|
70
|
+
return "rate_limited";
|
|
71
|
+
case 408:
|
|
72
|
+
case 504:
|
|
73
|
+
return "timeout";
|
|
74
|
+
case 502:
|
|
75
|
+
case 503:
|
|
76
|
+
return "unavailable";
|
|
77
|
+
default:
|
|
78
|
+
return "internal";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function classifyHostResponse(response) {
|
|
82
|
+
if (response.ok) {
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
if (isMcpErrorCode(response.mcpErrorCode)) {
|
|
86
|
+
return response.mcpErrorCode;
|
|
87
|
+
}
|
|
88
|
+
return typeof response.status === "number" ? mcpErrorCodeFromHttpStatus(response.status) : "internal";
|
|
89
|
+
}
|
|
90
|
+
function classifyHostError(err) {
|
|
91
|
+
if (err === null || typeof err !== "object") {
|
|
92
|
+
return "internal";
|
|
93
|
+
}
|
|
94
|
+
const candidate = err;
|
|
95
|
+
if (isMcpErrorCode(candidate.mcpErrorCode)) {
|
|
96
|
+
return candidate.mcpErrorCode;
|
|
97
|
+
}
|
|
98
|
+
const status = typeof candidate.status === "number" ? candidate.status : typeof candidate.statusCode === "number" ? candidate.statusCode : void 0;
|
|
99
|
+
if (status !== void 0) {
|
|
100
|
+
return mcpErrorCodeFromHttpStatus(status);
|
|
101
|
+
}
|
|
102
|
+
if (candidate.name === "AbortError" || candidate.name === "TimeoutError") {
|
|
103
|
+
return "timeout";
|
|
104
|
+
}
|
|
105
|
+
return "internal";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/plugin/pluginStorageUpload.ts
|
|
109
|
+
var PLUGIN_STORAGE_UPLOAD_MAX_BYTES = 30 * 1024 * 1024;
|
|
110
|
+
var PLUGIN_STORAGE_PREFIX_MAX_LENGTH = 84;
|
|
111
|
+
var PLUGIN_STORAGE_PATH_MAX_LENGTH = 128;
|
|
112
|
+
var PluginStorageUploadError = class extends McpToolError {
|
|
113
|
+
/** What went wrong, in terms a caller branches on. */
|
|
114
|
+
reason;
|
|
115
|
+
/** HTTP status from the server, when the host surfaced one. */
|
|
116
|
+
status;
|
|
117
|
+
/**
|
|
118
|
+
* Seconds the server asked the caller to wait (`Retry-After`), on a `rate_limited` failure
|
|
119
|
+
* when the host forwarded the header. Absent otherwise.
|
|
120
|
+
*/
|
|
121
|
+
retryAfterSeconds;
|
|
122
|
+
/** Structural marker for {@link isPluginStorageUploadError}. */
|
|
123
|
+
isPluginStorageUploadError = true;
|
|
124
|
+
constructor(message, reason, options = {}) {
|
|
125
|
+
super(message, options.code ?? codeForReason(reason, options.status));
|
|
126
|
+
this.name = "PluginStorageUploadError";
|
|
127
|
+
this.reason = reason;
|
|
128
|
+
this.status = options.status;
|
|
129
|
+
if (options.retryAfterSeconds !== void 0) {
|
|
130
|
+
this.retryAfterSeconds = options.retryAfterSeconds;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
function isPluginStorageUploadError(value) {
|
|
135
|
+
return value instanceof PluginStorageUploadError || isMcpToolError(value) && value.isPluginStorageUploadError === true;
|
|
136
|
+
}
|
|
137
|
+
function pluginStorageUploadReasonFromStatus(status) {
|
|
138
|
+
switch (status) {
|
|
139
|
+
case 400:
|
|
140
|
+
return "invalid_prefix";
|
|
141
|
+
case 401:
|
|
142
|
+
return "unauthorized";
|
|
143
|
+
case 403:
|
|
144
|
+
return "forbidden";
|
|
145
|
+
case 413:
|
|
146
|
+
return "too_large";
|
|
147
|
+
case 408:
|
|
148
|
+
return "timeout";
|
|
149
|
+
case 415:
|
|
150
|
+
return "unsupported_type";
|
|
151
|
+
case 429:
|
|
152
|
+
return "rate_limited";
|
|
153
|
+
case 503:
|
|
154
|
+
return "busy";
|
|
155
|
+
case 507:
|
|
156
|
+
return "quota_exceeded";
|
|
157
|
+
case 500:
|
|
158
|
+
return "storage_failed";
|
|
159
|
+
default:
|
|
160
|
+
return "unknown";
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function parsePluginStorageRetryAfter(raw, nowMs = Date.now()) {
|
|
164
|
+
if (typeof raw === "number") {
|
|
165
|
+
return Number.isFinite(raw) && raw >= 0 ? Math.ceil(raw) : void 0;
|
|
166
|
+
}
|
|
167
|
+
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
168
|
+
return void 0;
|
|
169
|
+
}
|
|
170
|
+
const text = raw.trim();
|
|
171
|
+
if (/^\d+$/.test(text)) {
|
|
172
|
+
return Number(text);
|
|
173
|
+
}
|
|
174
|
+
const at = /[A-Za-z]/.test(text) ? Date.parse(text) : Number.NaN;
|
|
175
|
+
return Number.isNaN(at) ? void 0 : Math.max(0, Math.ceil((at - nowMs) / 1e3));
|
|
176
|
+
}
|
|
177
|
+
function toPluginStorageUploadError(err) {
|
|
178
|
+
if (isPluginStorageUploadError(err)) {
|
|
179
|
+
return err;
|
|
180
|
+
}
|
|
181
|
+
const message = err instanceof Error ? err.message : typeof err === "string" ? err : "Plugin storage upload failed";
|
|
182
|
+
const candidate = err !== null && typeof err === "object" ? err : {};
|
|
183
|
+
const status = typeof candidate.status === "number" ? candidate.status : typeof candidate.statusCode === "number" ? candidate.statusCode : void 0;
|
|
184
|
+
if (status !== void 0) {
|
|
185
|
+
return new PluginStorageUploadError(message, pluginStorageUploadReasonFromStatus(status), {
|
|
186
|
+
status,
|
|
187
|
+
retryAfterSeconds: parsePluginStorageRetryAfter(candidate.retryAfterSeconds)
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (candidate.name === "AbortError") {
|
|
191
|
+
return new PluginStorageUploadError(message, "aborted", { code: "timeout" });
|
|
192
|
+
}
|
|
193
|
+
if (isMcpToolError(err)) {
|
|
194
|
+
return new PluginStorageUploadError(message, reasonForCode(err.code), { code: err.code });
|
|
195
|
+
}
|
|
196
|
+
return new PluginStorageUploadError(message, "unknown");
|
|
197
|
+
}
|
|
198
|
+
function validatePluginStoragePrefix(raw) {
|
|
199
|
+
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
200
|
+
return { ok: false, error: "A storage path prefix is required." };
|
|
201
|
+
}
|
|
202
|
+
if (raw.length > PLUGIN_STORAGE_PREFIX_MAX_LENGTH) {
|
|
203
|
+
return {
|
|
204
|
+
ok: false,
|
|
205
|
+
error: `The storage path prefix must be at most ${PLUGIN_STORAGE_PREFIX_MAX_LENGTH} characters, so the stored path fits ${PLUGIN_STORAGE_PATH_MAX_LENGTH}.`
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
if (!/^[A-Za-z0-9_\-/]+$/.test(raw)) {
|
|
209
|
+
return {
|
|
210
|
+
ok: false,
|
|
211
|
+
error: "The storage path prefix may contain only letters, digits, '-', '_' and '/' separators."
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
if (raw.split("/").some((segment) => segment.length === 0)) {
|
|
215
|
+
return {
|
|
216
|
+
ok: false,
|
|
217
|
+
error: "The storage path prefix must not start or end with '/' or contain an empty segment."
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return { ok: true, prefix: raw };
|
|
221
|
+
}
|
|
222
|
+
function assertValidPluginStoragePrefix(raw) {
|
|
223
|
+
const result = validatePluginStoragePrefix(raw);
|
|
224
|
+
if (!result.ok) {
|
|
225
|
+
throw new PluginStorageUploadError(result.error, "invalid_prefix");
|
|
226
|
+
}
|
|
227
|
+
return result.prefix;
|
|
228
|
+
}
|
|
229
|
+
var PLUGIN_STORAGE_UPLOAD_CONTENT_TYPES = Object.freeze({
|
|
230
|
+
pdf: ["application/pdf"],
|
|
231
|
+
png: ["image/png"],
|
|
232
|
+
jpg: ["image/jpeg", "image/pjpeg", "image/jpg"],
|
|
233
|
+
jpeg: ["image/jpeg", "image/pjpeg", "image/jpg"],
|
|
234
|
+
gif: ["image/gif"],
|
|
235
|
+
webp: ["image/webp"],
|
|
236
|
+
doc: ["application/msword"],
|
|
237
|
+
docx: ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
|
|
238
|
+
xls: ["application/vnd.ms-excel"],
|
|
239
|
+
xlsx: ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],
|
|
240
|
+
ppt: ["application/vnd.ms-powerpoint"],
|
|
241
|
+
pptx: ["application/vnd.openxmlformats-officedocument.presentationml.presentation"],
|
|
242
|
+
odt: ["application/vnd.oasis.opendocument.text"],
|
|
243
|
+
ods: ["application/vnd.oasis.opendocument.spreadsheet"],
|
|
244
|
+
odp: ["application/vnd.oasis.opendocument.presentation"],
|
|
245
|
+
txt: ["text/plain"],
|
|
246
|
+
csv: ["text/csv", "application/vnd.ms-excel", "application/csv", "text/plain"],
|
|
247
|
+
zip: ["application/zip", "application/x-zip-compressed", "application/x-zip"],
|
|
248
|
+
mp3: ["audio/mpeg", "audio/mp3"],
|
|
249
|
+
m4a: ["audio/mp4", "audio/x-m4a"],
|
|
250
|
+
mp4: ["video/mp4"]
|
|
251
|
+
});
|
|
252
|
+
var OCTET_STREAM = "application/octet-stream";
|
|
253
|
+
function pluginStorageSafeExtension(fileName) {
|
|
254
|
+
if (typeof fileName !== "string" || fileName.trim().length === 0) {
|
|
255
|
+
return "";
|
|
256
|
+
}
|
|
257
|
+
const leaf = fileName.split(/[\\/]/).pop() ?? "";
|
|
258
|
+
const dot = leaf.lastIndexOf(".");
|
|
259
|
+
if (dot < 0) {
|
|
260
|
+
return "";
|
|
261
|
+
}
|
|
262
|
+
const body = leaf.slice(dot + 1);
|
|
263
|
+
return /^[A-Za-z0-9]{1,10}$/.test(body) ? `.${body.toLowerCase()}` : "";
|
|
264
|
+
}
|
|
265
|
+
function resolvePluginStorageContentType(fileName, declaredContentType) {
|
|
266
|
+
const extension = pluginStorageSafeExtension(fileName).slice(1);
|
|
267
|
+
const allowed = extension.length > 0 && Object.prototype.hasOwnProperty.call(PLUGIN_STORAGE_UPLOAD_CONTENT_TYPES, extension) ? PLUGIN_STORAGE_UPLOAD_CONTENT_TYPES[extension] : void 0;
|
|
268
|
+
if (allowed === void 0) {
|
|
269
|
+
return {
|
|
270
|
+
ok: false,
|
|
271
|
+
error: "This file type is not allowed. Allowed: " + Object.keys(PLUGIN_STORAGE_UPLOAD_CONTENT_TYPES).sort().join(", ") + "."
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
const declared = mediaTypeOf(declaredContentType);
|
|
275
|
+
if (declared === void 0 || declared === OCTET_STREAM || allowed.includes(declared)) {
|
|
276
|
+
return { ok: true, contentType: allowed[0] };
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
ok: false,
|
|
280
|
+
error: `The declared content type '${declared}' does not match the file extension '.${extension}'.`
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function mediaTypeOf(raw) {
|
|
284
|
+
if (typeof raw !== "string") {
|
|
285
|
+
return void 0;
|
|
286
|
+
}
|
|
287
|
+
const mediaType = raw.split(";")[0].trim().toLowerCase();
|
|
288
|
+
return /^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/.test(mediaType) ? mediaType : void 0;
|
|
289
|
+
}
|
|
290
|
+
async function uploadFileToPluginStorage(transport, file, options) {
|
|
291
|
+
if (typeof transport.uploadToStorage !== "function") {
|
|
292
|
+
throw new PluginStorageUploadError(
|
|
293
|
+
"The active McpTransport does not support plugin-storage uploads (no `uploadToStorage`). The host runtime must implement the uploadToStorage request kind.",
|
|
294
|
+
"unsupported"
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
const uploadToStorage = transport.uploadToStorage.bind(transport);
|
|
298
|
+
const pathPrefix = assertValidPluginStoragePrefix(options.pathPrefix);
|
|
299
|
+
const maxSizeBytes = Math.min(options.maxSizeBytes ?? PLUGIN_STORAGE_UPLOAD_MAX_BYTES, PLUGIN_STORAGE_UPLOAD_MAX_BYTES);
|
|
300
|
+
const isBuffer = file instanceof ArrayBuffer || Object.prototype.toString.call(file) === "[object ArrayBuffer]";
|
|
301
|
+
const size = isBuffer ? file.byteLength : file.size;
|
|
302
|
+
if (size > maxSizeBytes) {
|
|
303
|
+
throw new PluginStorageUploadError(
|
|
304
|
+
`File is ${size} bytes, which exceeds the ${maxSizeBytes}-byte upload limit.`,
|
|
305
|
+
"too_large"
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
const ownName = isBuffer ? void 0 : file.name;
|
|
309
|
+
const fileName = nonEmpty(options.fileName) ?? (typeof ownName === "string" ? nonEmpty(ownName) : void 0) ?? "upload.bin";
|
|
310
|
+
const declaredType = nonEmpty(options.contentType) ?? (isBuffer ? void 0 : nonEmpty(file.type));
|
|
311
|
+
const resolvedType = resolvePluginStorageContentType(fileName, declaredType);
|
|
312
|
+
if (!resolvedType.ok) {
|
|
313
|
+
throw new PluginStorageUploadError(resolvedType.error, "unsupported_type");
|
|
314
|
+
}
|
|
315
|
+
if (options.signal?.aborted === true) {
|
|
316
|
+
throw new PluginStorageUploadError("Aborted", "aborted", { code: "timeout" });
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
const buffer = isBuffer ? file : await readBlob(file);
|
|
320
|
+
const meta = {
|
|
321
|
+
pathPrefix,
|
|
322
|
+
fileName,
|
|
323
|
+
// The canonical type for the extension: always one the server accepts, and the one
|
|
324
|
+
// it stores.
|
|
325
|
+
contentType: resolvedType.contentType,
|
|
326
|
+
sizeBytes: buffer.byteLength
|
|
327
|
+
};
|
|
328
|
+
return await uploadToStorage(meta, buffer, options.signal);
|
|
329
|
+
} catch (err) {
|
|
330
|
+
throw toPluginStorageUploadError(err);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function readBlob(blob) {
|
|
334
|
+
if (typeof blob.arrayBuffer === "function") {
|
|
335
|
+
return blob.arrayBuffer();
|
|
336
|
+
}
|
|
337
|
+
return new Promise((resolve, reject) => {
|
|
338
|
+
const reader = new FileReader();
|
|
339
|
+
reader.onload = () => resolve(reader.result);
|
|
340
|
+
reader.onerror = () => reject(reader.error ?? new Error("Could not read the file."));
|
|
341
|
+
reader.readAsArrayBuffer(blob);
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function nonEmpty(value) {
|
|
345
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
346
|
+
}
|
|
347
|
+
function codeForReason(reason, status) {
|
|
348
|
+
if (status !== void 0) {
|
|
349
|
+
return mcpErrorCodeFromHttpStatus(status);
|
|
350
|
+
}
|
|
351
|
+
switch (reason) {
|
|
352
|
+
case "invalid_prefix":
|
|
353
|
+
case "too_large":
|
|
354
|
+
case "unsupported_type":
|
|
355
|
+
return "invalid_request";
|
|
356
|
+
case "timeout":
|
|
357
|
+
return "timeout";
|
|
358
|
+
case "unauthorized":
|
|
359
|
+
return "unauthorized";
|
|
360
|
+
case "forbidden":
|
|
361
|
+
return "forbidden";
|
|
362
|
+
case "busy":
|
|
363
|
+
return "unavailable";
|
|
364
|
+
case "rate_limited":
|
|
365
|
+
return "rate_limited";
|
|
366
|
+
case "aborted":
|
|
367
|
+
return "timeout";
|
|
368
|
+
default:
|
|
369
|
+
return "internal";
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function reasonForCode(code) {
|
|
373
|
+
switch (code) {
|
|
374
|
+
case "unauthorized":
|
|
375
|
+
return "unauthorized";
|
|
376
|
+
case "forbidden":
|
|
377
|
+
return "forbidden";
|
|
378
|
+
case "unavailable":
|
|
379
|
+
return "busy";
|
|
380
|
+
default:
|
|
381
|
+
return "unknown";
|
|
382
|
+
}
|
|
383
|
+
}
|
|
15
384
|
var ClientPushContext = react.createContext(null);
|
|
16
385
|
function useClientPushSubscription(options) {
|
|
17
386
|
const { groups, onEvent, onResync, enabled = true } = options;
|
|
@@ -568,100 +937,6 @@ function defaultConnectionFactory(port) {
|
|
|
568
937
|
};
|
|
569
938
|
}
|
|
570
939
|
|
|
571
|
-
// src/bridge/mcp-error.ts
|
|
572
|
-
var MCP_ERROR_CODES = [
|
|
573
|
-
"unauthorized",
|
|
574
|
-
"forbidden",
|
|
575
|
-
"not_found",
|
|
576
|
-
"invalid_request",
|
|
577
|
-
"conflict",
|
|
578
|
-
"rate_limited",
|
|
579
|
-
"timeout",
|
|
580
|
-
"unavailable",
|
|
581
|
-
"internal"
|
|
582
|
-
];
|
|
583
|
-
function isMcpErrorCode(value) {
|
|
584
|
-
return typeof value === "string" && MCP_ERROR_CODES.includes(value);
|
|
585
|
-
}
|
|
586
|
-
var RETRYABLE = ["rate_limited", "timeout", "unavailable"];
|
|
587
|
-
function isRetryableMcpErrorCode(code) {
|
|
588
|
-
return RETRYABLE.includes(code);
|
|
589
|
-
}
|
|
590
|
-
var McpToolError = class extends Error {
|
|
591
|
-
/** Machine-readable classification. `internal` when the host sent none. */
|
|
592
|
-
code;
|
|
593
|
-
/**
|
|
594
|
-
* Marks the instance for {@link isMcpToolError}. A structural marker rather than a prototype
|
|
595
|
-
* check because the error can be constructed in one bundle and inspected in another, where
|
|
596
|
-
* `instanceof` compares two different class objects and answers false.
|
|
597
|
-
*/
|
|
598
|
-
isMcpToolError = true;
|
|
599
|
-
constructor(message, code = "internal") {
|
|
600
|
-
super(message);
|
|
601
|
-
this.name = "McpHostError";
|
|
602
|
-
this.code = code;
|
|
603
|
-
}
|
|
604
|
-
/** True when this failure is worth retrying unchanged. */
|
|
605
|
-
get retryable() {
|
|
606
|
-
return isRetryableMcpErrorCode(this.code);
|
|
607
|
-
}
|
|
608
|
-
};
|
|
609
|
-
function isMcpToolError(value) {
|
|
610
|
-
return value instanceof McpToolError || value !== null && typeof value === "object" && value.isMcpToolError === true && isMcpErrorCode(value.code);
|
|
611
|
-
}
|
|
612
|
-
function mcpErrorCodeFromHttpStatus(status) {
|
|
613
|
-
switch (status) {
|
|
614
|
-
case 401:
|
|
615
|
-
return "unauthorized";
|
|
616
|
-
case 403:
|
|
617
|
-
return "forbidden";
|
|
618
|
-
case 404:
|
|
619
|
-
return "not_found";
|
|
620
|
-
case 400:
|
|
621
|
-
case 422:
|
|
622
|
-
return "invalid_request";
|
|
623
|
-
case 409:
|
|
624
|
-
case 412:
|
|
625
|
-
return "conflict";
|
|
626
|
-
case 429:
|
|
627
|
-
return "rate_limited";
|
|
628
|
-
case 408:
|
|
629
|
-
case 504:
|
|
630
|
-
return "timeout";
|
|
631
|
-
case 502:
|
|
632
|
-
case 503:
|
|
633
|
-
return "unavailable";
|
|
634
|
-
default:
|
|
635
|
-
return "internal";
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
function classifyHostResponse(response) {
|
|
639
|
-
if (response.ok) {
|
|
640
|
-
return void 0;
|
|
641
|
-
}
|
|
642
|
-
if (isMcpErrorCode(response.mcpErrorCode)) {
|
|
643
|
-
return response.mcpErrorCode;
|
|
644
|
-
}
|
|
645
|
-
return typeof response.status === "number" ? mcpErrorCodeFromHttpStatus(response.status) : "internal";
|
|
646
|
-
}
|
|
647
|
-
function classifyHostError(err) {
|
|
648
|
-
if (err === null || typeof err !== "object") {
|
|
649
|
-
return "internal";
|
|
650
|
-
}
|
|
651
|
-
const candidate = err;
|
|
652
|
-
if (isMcpErrorCode(candidate.mcpErrorCode)) {
|
|
653
|
-
return candidate.mcpErrorCode;
|
|
654
|
-
}
|
|
655
|
-
const status = typeof candidate.status === "number" ? candidate.status : typeof candidate.statusCode === "number" ? candidate.statusCode : void 0;
|
|
656
|
-
if (status !== void 0) {
|
|
657
|
-
return mcpErrorCodeFromHttpStatus(status);
|
|
658
|
-
}
|
|
659
|
-
if (candidate.name === "AbortError" || candidate.name === "TimeoutError") {
|
|
660
|
-
return "timeout";
|
|
661
|
-
}
|
|
662
|
-
return "internal";
|
|
663
|
-
}
|
|
664
|
-
|
|
665
940
|
// src/plugin/createPortMcpTransport.ts
|
|
666
941
|
var WIRE_INVOKE_TOOL_REQUEST = "ethisys:mcp:invokeTool";
|
|
667
942
|
var WIRE_INVOKE_TOOL_RESULT = "ethisys:mcp:invokeTool:result";
|
|
@@ -671,13 +946,15 @@ var WIRE_GET_RESOURCE_REQUEST = "ethisys:mcp:getResource";
|
|
|
671
946
|
var WIRE_GET_RESOURCE_RESULT = "ethisys:mcp:getResource:result";
|
|
672
947
|
var WIRE_UPLOAD_DOCUMENT_REQUEST = "ethisys:mcp:uploadDocument";
|
|
673
948
|
var WIRE_UPLOAD_DOCUMENT_RESULT = "ethisys:mcp:uploadDocument:result";
|
|
949
|
+
var WIRE_UPLOAD_TO_STORAGE_REQUEST = "ethisys:mcp:uploadToStorage";
|
|
950
|
+
var WIRE_UPLOAD_TO_STORAGE_RESULT = "ethisys:mcp:uploadToStorage:result";
|
|
674
951
|
var WIRE_ABORT = "ethisys:mcp:abort";
|
|
675
952
|
function isMcpResultEnvelope(value) {
|
|
676
953
|
if (value === null || typeof value !== "object") {
|
|
677
954
|
return false;
|
|
678
955
|
}
|
|
679
956
|
const v = value;
|
|
680
|
-
return (v.type === WIRE_INVOKE_TOOL_RESULT || v.type === WIRE_INVOKE_OPERATION_RESULT || v.type === WIRE_GET_RESOURCE_RESULT || v.type === WIRE_UPLOAD_DOCUMENT_RESULT) && typeof v.id === "string";
|
|
957
|
+
return (v.type === WIRE_INVOKE_TOOL_RESULT || v.type === WIRE_INVOKE_OPERATION_RESULT || v.type === WIRE_GET_RESOURCE_RESULT || v.type === WIRE_UPLOAD_DOCUMENT_RESULT || v.type === WIRE_UPLOAD_TO_STORAGE_RESULT) && typeof v.id === "string";
|
|
681
958
|
}
|
|
682
959
|
function createPortMcpTransport(port, options = {}) {
|
|
683
960
|
if (port === void 0 || port === null) {
|
|
@@ -701,10 +978,18 @@ function createPortMcpTransport(port, options = {}) {
|
|
|
701
978
|
if (reply.ok === true) {
|
|
702
979
|
resolver.resolve(reply.data);
|
|
703
980
|
} else {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
981
|
+
const message = typeof reply.error === "string" ? reply.error : "Unknown host error";
|
|
982
|
+
const code = isMcpErrorCode(reply.code) ? reply.code : "internal";
|
|
983
|
+
if (reply.type === WIRE_UPLOAD_TO_STORAGE_RESULT) {
|
|
984
|
+
const status = typeof reply.status === "number" && Number.isInteger(reply.status) ? reply.status : void 0;
|
|
985
|
+
resolver.reject(new PluginStorageUploadError(
|
|
986
|
+
message,
|
|
987
|
+
status !== void 0 ? pluginStorageUploadReasonFromStatus(status) : "unknown",
|
|
988
|
+
{ status, code, retryAfterSeconds: parsePluginStorageRetryAfter(reply.retryAfterSeconds) }
|
|
989
|
+
));
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
resolver.reject(new McpToolError(message, code));
|
|
708
993
|
}
|
|
709
994
|
}
|
|
710
995
|
shim.addEventListener("message", onMessage);
|
|
@@ -762,6 +1047,15 @@ function createPortMcpTransport(port, options = {}) {
|
|
|
762
1047
|
[buffer]
|
|
763
1048
|
);
|
|
764
1049
|
return data;
|
|
1050
|
+
},
|
|
1051
|
+
async uploadToStorage(meta, buffer, signal) {
|
|
1052
|
+
const data = await send(
|
|
1053
|
+
WIRE_UPLOAD_TO_STORAGE_REQUEST,
|
|
1054
|
+
{ meta, buffer },
|
|
1055
|
+
signal,
|
|
1056
|
+
[buffer]
|
|
1057
|
+
);
|
|
1058
|
+
return data;
|
|
765
1059
|
}
|
|
766
1060
|
};
|
|
767
1061
|
}
|
|
@@ -1022,7 +1316,13 @@ exports.ExtensionRuntimeProvider = ExtensionRuntimeProvider;
|
|
|
1022
1316
|
exports.HostIdentityContext = HostIdentityContext;
|
|
1023
1317
|
exports.MCP_ERROR_CODES = MCP_ERROR_CODES;
|
|
1024
1318
|
exports.McpToolError = McpToolError;
|
|
1319
|
+
exports.PLUGIN_STORAGE_PATH_MAX_LENGTH = PLUGIN_STORAGE_PATH_MAX_LENGTH;
|
|
1320
|
+
exports.PLUGIN_STORAGE_PREFIX_MAX_LENGTH = PLUGIN_STORAGE_PREFIX_MAX_LENGTH;
|
|
1321
|
+
exports.PLUGIN_STORAGE_UPLOAD_CONTENT_TYPES = PLUGIN_STORAGE_UPLOAD_CONTENT_TYPES;
|
|
1322
|
+
exports.PLUGIN_STORAGE_UPLOAD_MAX_BYTES = PLUGIN_STORAGE_UPLOAD_MAX_BYTES;
|
|
1025
1323
|
exports.PluginRealtimeContext = PluginRealtimeContext;
|
|
1324
|
+
exports.PluginStorageUploadError = PluginStorageUploadError;
|
|
1325
|
+
exports.assertValidPluginStoragePrefix = assertValidPluginStoragePrefix;
|
|
1026
1326
|
exports.classifyHostError = classifyHostError;
|
|
1027
1327
|
exports.classifyHostResponse = classifyHostResponse;
|
|
1028
1328
|
exports.createPortBridgeClient = createPortBridgeClient;
|
|
@@ -1033,9 +1333,15 @@ exports.defineDeclarativePlugin = defineDeclarativePlugin;
|
|
|
1033
1333
|
exports.defineEthisysPlugin = defineEthisysPlugin;
|
|
1034
1334
|
exports.isMcpErrorCode = isMcpErrorCode;
|
|
1035
1335
|
exports.isMcpToolError = isMcpToolError;
|
|
1336
|
+
exports.isPluginStorageUploadError = isPluginStorageUploadError;
|
|
1036
1337
|
exports.isRetryableMcpErrorCode = isRetryableMcpErrorCode;
|
|
1037
1338
|
exports.mcpErrorCodeFromHttpStatus = mcpErrorCodeFromHttpStatus;
|
|
1339
|
+
exports.pluginStorageSafeExtension = pluginStorageSafeExtension;
|
|
1340
|
+
exports.pluginStorageUploadReasonFromStatus = pluginStorageUploadReasonFromStatus;
|
|
1341
|
+
exports.resolvePluginStorageContentType = resolvePluginStorageContentType;
|
|
1342
|
+
exports.toPluginStorageUploadError = toPluginStorageUploadError;
|
|
1038
1343
|
exports.unwrapItems = unwrapItems;
|
|
1344
|
+
exports.uploadFileToPluginStorage = uploadFileToPluginStorage;
|
|
1039
1345
|
exports.useAuth = useAuth;
|
|
1040
1346
|
exports.useBridgeClient = useBridgeClient;
|
|
1041
1347
|
exports.useBridgeLocale = useBridgeLocale;
|
|
@@ -1051,5 +1357,6 @@ exports.useModuleQuery = useModuleQuery;
|
|
|
1051
1357
|
exports.useModuleTool = useModuleTool;
|
|
1052
1358
|
exports.useOptionalExtensionRuntimeTransport = useOptionalExtensionRuntimeTransport;
|
|
1053
1359
|
exports.usePluginRealtimeSource = usePluginRealtimeSource;
|
|
1360
|
+
exports.validatePluginStoragePrefix = validatePluginStoragePrefix;
|
|
1054
1361
|
//# sourceMappingURL=index.cjs.map
|
|
1055
1362
|
//# sourceMappingURL=index.cjs.map
|