@ethisyscore/extension-runtime 1.58.0 → 1.61.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/dist/host/index.cjs +116 -0
- package/dist/host/index.cjs.map +1 -1
- package/dist/host/index.d.cts +29 -3
- package/dist/host/index.d.ts +29 -3
- package/dist/host/index.js +112 -1
- package/dist/host/index.js.map +1 -1
- package/dist/mock-host/cli.cjs +80 -0
- package/dist/mock-host/cli.cjs.map +1 -1
- package/dist/mock-host/cli.d.cts +1 -1
- package/dist/mock-host/cli.d.ts +1 -1
- package/dist/mock-host/cli.js +80 -0
- package/dist/mock-host/cli.js.map +1 -1
- package/dist/{transport-CHa8ESUx.d.ts → transport-BPwasdNz.d.ts} +128 -6
- package/dist/{transport-DJE6ljme.d.cts → transport-BbSPAS_N.d.cts} +128 -6
- package/package.json +1 -1
package/dist/host/index.cjs
CHANGED
|
@@ -287,6 +287,50 @@ function createInputEventCoalescer(sink, options = {}) {
|
|
|
287
287
|
};
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
+
// src/host/version-skew.ts
|
|
291
|
+
var EXTENSION_VERSION_HEADER = "x-extension-version";
|
|
292
|
+
var EXTENSION_ACTIVE_VERSION_HEADER = "x-extension-active-version";
|
|
293
|
+
var CAPABILITY_REJECTION_REASON_HEADER = "x-capability-token-rejection-reason";
|
|
294
|
+
var VERSION_MISMATCH_REASON = "extensionversionmismatch";
|
|
295
|
+
function readHeader(headers, name) {
|
|
296
|
+
if (!headers) {
|
|
297
|
+
return void 0;
|
|
298
|
+
}
|
|
299
|
+
const direct = headers[name];
|
|
300
|
+
if (direct !== void 0) {
|
|
301
|
+
return direct;
|
|
302
|
+
}
|
|
303
|
+
const lowered = name.toLowerCase();
|
|
304
|
+
for (const key of Object.keys(headers)) {
|
|
305
|
+
if (key.toLowerCase() === lowered) {
|
|
306
|
+
return headers[key];
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return void 0;
|
|
310
|
+
}
|
|
311
|
+
function trimmedOrUndefined(value) {
|
|
312
|
+
const trimmed = value?.trim();
|
|
313
|
+
return trimmed !== void 0 && trimmed.length > 0 ? trimmed : void 0;
|
|
314
|
+
}
|
|
315
|
+
function detectVersionSkew(response, mountedExtensionVersion) {
|
|
316
|
+
const reason = trimmedOrUndefined(readHeader(response.headers, CAPABILITY_REJECTION_REASON_HEADER));
|
|
317
|
+
if (reason !== void 0 && reason.toLowerCase() === VERSION_MISMATCH_REASON) {
|
|
318
|
+
return {
|
|
319
|
+
activeVersion: trimmedOrUndefined(readHeader(response.headers, EXTENSION_ACTIVE_VERSION_HEADER)),
|
|
320
|
+
detectedFrom: "rejection"
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
if (!response.ok) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
const mounted = trimmedOrUndefined(mountedExtensionVersion);
|
|
327
|
+
const served = trimmedOrUndefined(readHeader(response.headers, EXTENSION_VERSION_HEADER));
|
|
328
|
+
if (mounted === void 0 || served === void 0 || served === mounted) {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
return { activeVersion: served, detectedFrom: "response-stamp" };
|
|
332
|
+
}
|
|
333
|
+
|
|
290
334
|
// src/host/worker/bridge-envelopes.ts
|
|
291
335
|
var BRIDGE_PUSH_THEME = "ethisys:bridge:theme";
|
|
292
336
|
var BRIDGE_PUSH_LOCALE = "ethisys:bridge:locale";
|
|
@@ -306,6 +350,9 @@ var WorkerRemoteDomTransport = class {
|
|
|
306
350
|
workerPort;
|
|
307
351
|
mcpClient;
|
|
308
352
|
capabilityTokenProvider;
|
|
353
|
+
mountedExtensionVersion;
|
|
354
|
+
onVersionSkew;
|
|
355
|
+
versionSkewReported = false;
|
|
309
356
|
coalesceMs;
|
|
310
357
|
maxConcurrentMcpRequests;
|
|
311
358
|
abortController;
|
|
@@ -328,6 +375,8 @@ var WorkerRemoteDomTransport = class {
|
|
|
328
375
|
}
|
|
329
376
|
this.mcpClient = options.mcpClient;
|
|
330
377
|
this.capabilityTokenProvider = options.capabilityToken;
|
|
378
|
+
this.mountedExtensionVersion = options.mountedExtensionVersion;
|
|
379
|
+
this.onVersionSkew = options.onVersionSkew;
|
|
331
380
|
this.coalesceMs = options.coalesceMs ?? 16;
|
|
332
381
|
this.maxConcurrentMcpRequests = Math.max(
|
|
333
382
|
1,
|
|
@@ -611,6 +660,34 @@ var WorkerRemoteDomTransport = class {
|
|
|
611
660
|
requestSignal(id) {
|
|
612
661
|
return this.inFlightAborts.get(id)?.signal ?? this.abortController.signal;
|
|
613
662
|
}
|
|
663
|
+
/**
|
|
664
|
+
* Raises the skew callback the first time this surface is seen to be running against a version
|
|
665
|
+
* that is no longer installed.
|
|
666
|
+
*
|
|
667
|
+
* Latched deliberately. A page typically has several MCP calls in flight, and after an upgrade
|
|
668
|
+
* every one of them reports the same skew — firing per response would ask the host to remount
|
|
669
|
+
* the same surface repeatedly, and the second remount would tear down the first. One signal per
|
|
670
|
+
* transport is all a remount needs, and the transport is discarded along with the surface.
|
|
671
|
+
*
|
|
672
|
+
* The callback is host code, so it is assumed to be able to throw. Two consequences are guarded
|
|
673
|
+
* here: the throw must not escape into the MCP handler's catch (which would reply `ok: false`
|
|
674
|
+
* for a call that actually succeeded), and it must not consume the latch (which would leave the
|
|
675
|
+
* surface with no response AND no further chance of being told to remount).
|
|
676
|
+
*/
|
|
677
|
+
reportVersionSkew(response) {
|
|
678
|
+
if (this.versionSkewReported || this.onVersionSkew === void 0) {
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
const skew = detectVersionSkew(response, this.mountedExtensionVersion);
|
|
682
|
+
if (skew === null) {
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
try {
|
|
686
|
+
this.onVersionSkew(skew);
|
|
687
|
+
this.versionSkewReported = true;
|
|
688
|
+
} catch {
|
|
689
|
+
}
|
|
690
|
+
}
|
|
614
691
|
async handleInvokeTool(message) {
|
|
615
692
|
let token;
|
|
616
693
|
try {
|
|
@@ -630,6 +707,7 @@ var WorkerRemoteDomTransport = class {
|
|
|
630
707
|
capabilityToken: token,
|
|
631
708
|
signal: this.requestSignal(message.id)
|
|
632
709
|
});
|
|
710
|
+
this.reportVersionSkew(result);
|
|
633
711
|
this.safePostMessage({
|
|
634
712
|
id: message.id,
|
|
635
713
|
type: "ethisys:mcp:invokeTool:result",
|
|
@@ -659,6 +737,7 @@ var WorkerRemoteDomTransport = class {
|
|
|
659
737
|
capabilityToken: token,
|
|
660
738
|
signal: this.requestSignal(message.id)
|
|
661
739
|
});
|
|
740
|
+
this.reportVersionSkew(result);
|
|
662
741
|
this.safePostMessage({
|
|
663
742
|
id: message.id,
|
|
664
743
|
type: "ethisys:mcp:getResource:result",
|
|
@@ -689,6 +768,7 @@ var WorkerRemoteDomTransport = class {
|
|
|
689
768
|
capabilityToken: token,
|
|
690
769
|
signal: this.requestSignal(message.id)
|
|
691
770
|
});
|
|
771
|
+
this.reportVersionSkew(result);
|
|
692
772
|
this.safePostMessage({
|
|
693
773
|
id: message.id,
|
|
694
774
|
type: "ethisys:mcp:uploadDocument:result",
|
|
@@ -731,6 +811,9 @@ var IframeBridgeTransport = class {
|
|
|
731
811
|
maxConcurrentMcpRequests;
|
|
732
812
|
maxMessagesPerSecond;
|
|
733
813
|
onSecurityEvent;
|
|
814
|
+
mountedExtensionVersion;
|
|
815
|
+
onVersionSkew;
|
|
816
|
+
versionSkewReported = false;
|
|
734
817
|
abortController = new AbortController();
|
|
735
818
|
hostPort;
|
|
736
819
|
framePort;
|
|
@@ -745,6 +828,8 @@ var IframeBridgeTransport = class {
|
|
|
745
828
|
this.targetOrigin = options.targetOrigin;
|
|
746
829
|
this.capabilityTokenProvider = options.capabilityToken;
|
|
747
830
|
this.mcpClient = options.mcpClient;
|
|
831
|
+
this.mountedExtensionVersion = options.mountedExtensionVersion;
|
|
832
|
+
this.onVersionSkew = options.onVersionSkew;
|
|
748
833
|
this.maxConcurrentMcpRequests = Math.max(1, options.maxConcurrentMcpRequests ?? DEFAULT_MAX_CONCURRENT_MCP_REQUESTS);
|
|
749
834
|
this.maxMessagesPerSecond = Math.max(1, options.maxMessagesPerSecond ?? 64);
|
|
750
835
|
this.onSecurityEvent = options.onSecurityEvent;
|
|
@@ -897,6 +982,7 @@ var IframeBridgeTransport = class {
|
|
|
897
982
|
capabilityToken: token,
|
|
898
983
|
signal: this.abortController.signal
|
|
899
984
|
});
|
|
985
|
+
this.reportVersionSkew(result);
|
|
900
986
|
this.safePostMessage({
|
|
901
987
|
id: message.id,
|
|
902
988
|
type: "ethisys:mcp:invokeTool:result",
|
|
@@ -908,6 +994,30 @@ var IframeBridgeTransport = class {
|
|
|
908
994
|
this.replyError(message.id, "ethisys:mcp:invokeTool:result", err);
|
|
909
995
|
}
|
|
910
996
|
}
|
|
997
|
+
/**
|
|
998
|
+
* Raises the skew callback the first time this surface is seen to be running against a version
|
|
999
|
+
* that is no longer installed. Latched: a page usually has several MCP calls in flight and after
|
|
1000
|
+
* an upgrade every one of them reports the same skew, so firing per response would ask the host
|
|
1001
|
+
* to remount the same surface repeatedly and each remount would tear down the last.
|
|
1002
|
+
*
|
|
1003
|
+
* The callback is host code and may throw. That must neither escape into the MCP handler's catch
|
|
1004
|
+
* (replying `ok: false` for a call that succeeded) nor consume the latch (leaving the surface with
|
|
1005
|
+
* no response and no further chance of being told to remount).
|
|
1006
|
+
*/
|
|
1007
|
+
reportVersionSkew(response) {
|
|
1008
|
+
if (this.versionSkewReported || this.onVersionSkew === void 0) {
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
const skew = detectVersionSkew(response, this.mountedExtensionVersion);
|
|
1012
|
+
if (skew === null) {
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
try {
|
|
1016
|
+
this.onVersionSkew(skew);
|
|
1017
|
+
this.versionSkewReported = true;
|
|
1018
|
+
} catch {
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
911
1021
|
async handleGetResource(message) {
|
|
912
1022
|
let token;
|
|
913
1023
|
try {
|
|
@@ -926,6 +1036,7 @@ var IframeBridgeTransport = class {
|
|
|
926
1036
|
capabilityToken: token,
|
|
927
1037
|
signal: this.abortController.signal
|
|
928
1038
|
});
|
|
1039
|
+
this.reportVersionSkew(result);
|
|
929
1040
|
this.safePostMessage({
|
|
930
1041
|
id: message.id,
|
|
931
1042
|
type: "ethisys:mcp:getResource:result",
|
|
@@ -965,16 +1076,21 @@ Object.defineProperty(exports, "createRemoteComponentRenderer", {
|
|
|
965
1076
|
enumerable: true,
|
|
966
1077
|
get: function () { return host.createRemoteComponentRenderer; }
|
|
967
1078
|
});
|
|
1079
|
+
exports.CAPABILITY_REJECTION_REASON_HEADER = CAPABILITY_REJECTION_REASON_HEADER;
|
|
968
1080
|
exports.CONTRACT_B_PRIMITIVES = CONTRACT_B_PRIMITIVES;
|
|
969
1081
|
exports.DEFAULT_MAX_CONCURRENT_MCP_REQUESTS = DEFAULT_MAX_CONCURRENT_MCP_REQUESTS;
|
|
970
1082
|
exports.DEFAULT_OFFSCREEN_COALESCE_MS = DEFAULT_OFFSCREEN_COALESCE_MS;
|
|
1083
|
+
exports.EXTENSION_ACTIVE_VERSION_HEADER = EXTENSION_ACTIVE_VERSION_HEADER;
|
|
1084
|
+
exports.EXTENSION_VERSION_HEADER = EXTENSION_VERSION_HEADER;
|
|
971
1085
|
exports.IFRAME_BRIDGE_PROTOCOL = IFRAME_BRIDGE_PROTOCOL;
|
|
972
1086
|
exports.IframeBridgeTransport = IframeBridgeTransport;
|
|
973
1087
|
exports.SemanticComponentRegistry = SemanticComponentRegistry;
|
|
1088
|
+
exports.VERSION_MISMATCH_REASON = VERSION_MISMATCH_REASON;
|
|
974
1089
|
exports.WORKER_TRANSPORT_PROTOCOL = WORKER_TRANSPORT_PROTOCOL;
|
|
975
1090
|
exports.WorkerRemoteDomTransport = WorkerRemoteDomTransport;
|
|
976
1091
|
exports.createInputEventCoalescer = createInputEventCoalescer;
|
|
977
1092
|
exports.createOffscreenCanvasTransfer = createOffscreenCanvasTransfer;
|
|
1093
|
+
exports.detectVersionSkew = detectVersionSkew;
|
|
978
1094
|
exports.evaluate = evaluate;
|
|
979
1095
|
exports.interpret = interpret;
|
|
980
1096
|
//# sourceMappingURL=index.cjs.map
|