@ethisyscore/extension-runtime 1.34.0 → 1.35.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/{bridge-client-UK3qcGoi.d.ts → bridge-client-DD99netz.d.ts} +2 -33
- package/dist/{bridge-client-CIThO7jZ.d.cts → bridge-client-DzRcKIJT.d.cts} +2 -33
- package/dist/bridge-envelopes-DA6vxbyb.d.cts +151 -0
- package/dist/bridge-envelopes-DA6vxbyb.d.ts +151 -0
- package/dist/host/index.cjs +76 -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 +76 -2
- package/dist/host/index.js.map +1 -1
- package/dist/mock-host/cli.cjs +87 -2
- package/dist/mock-host/cli.cjs.map +1 -1
- package/dist/mock-host/cli.d.cts +2 -2
- package/dist/mock-host/cli.d.ts +2 -2
- package/dist/mock-host/cli.js +87 -2
- package/dist/mock-host/cli.js.map +1 -1
- package/dist/mock-host/index.cjs +41 -1
- package/dist/mock-host/index.cjs.map +1 -1
- package/dist/mock-host/index.d.cts +19 -7
- package/dist/mock-host/index.d.ts +19 -7
- package/dist/mock-host/index.js +41 -1
- package/dist/mock-host/index.js.map +1 -1
- package/dist/plugin/index.cjs +15 -3
- package/dist/plugin/index.cjs.map +1 -1
- package/dist/plugin/index.d.cts +13 -5
- package/dist/plugin/index.d.ts +13 -5
- package/dist/plugin/index.js +15 -4
- package/dist/plugin/index.js.map +1 -1
- package/dist/{transport-BN9Mzn_m.d.cts → transport-CHa8ESUx.d.ts} +39 -4
- package/dist/{transport-Jfd9KXAh.d.ts → transport-DJE6ljme.d.cts} +39 -4
- package/package.json +1 -1
- package/dist/bridge-envelopes-BRKGSiSC.d.cts +0 -63
- package/dist/bridge-envelopes-BRKGSiSC.d.ts +0 -63
|
@@ -1,35 +1,4 @@
|
|
|
1
|
-
import { d as BridgePushThemeEnvelope, c as BridgePushLocaleEnvelope, b as BridgePushDensityEnvelope, a as BridgePushA11yEnvelope, B as BridgeNavPushEnvelope, e as BridgeSessionTokenPushEnvelope } from './bridge-envelopes-
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Transport contract used by the plugin-side React hooks
|
|
5
|
-
* (`useMcpResource`, `useMcpTool`) to talk to the host.
|
|
6
|
-
*
|
|
7
|
-
* The hooks are deliberately transport-agnostic: the concrete implementation
|
|
8
|
-
* (postMessage bridge, in-process direct call, fetch-based, …) is injected
|
|
9
|
-
* via {@link ExtensionRuntimeProvider} or the per-hook `transport` option.
|
|
10
|
-
* This keeps the React surface stable across Contract A (host-rendered) and
|
|
11
|
-
* Contract B (worker remote-runtime) execution modes.
|
|
12
|
-
*
|
|
13
|
-
* Implementations must honour the supplied {@link AbortSignal} for
|
|
14
|
-
* cancellation — hooks rely on it to tear down in-flight calls on unmount
|
|
15
|
-
* or argument changes.
|
|
16
|
-
*/
|
|
17
|
-
interface McpTransport {
|
|
18
|
-
/**
|
|
19
|
-
* Fetch a resource by URI. Implementations should reject with an `Error`
|
|
20
|
-
* when the host returns a failure, and should observe `signal` to abort
|
|
21
|
-
* any in-flight work.
|
|
22
|
-
*/
|
|
23
|
-
getResource<T>(uri: string, signal?: AbortSignal): Promise<{
|
|
24
|
-
uri: string;
|
|
25
|
-
data: T;
|
|
26
|
-
}>;
|
|
27
|
-
/**
|
|
28
|
-
* Invoke a tool by name. The request object is opaque to the transport.
|
|
29
|
-
* Implementations should observe `signal` to abort in-flight work.
|
|
30
|
-
*/
|
|
31
|
-
invokeTool<TReq, TRes>(name: string, args: TReq, signal?: AbortSignal): Promise<TRes>;
|
|
32
|
-
}
|
|
1
|
+
import { d as BridgePushThemeEnvelope, c as BridgePushLocaleEnvelope, b as BridgePushDensityEnvelope, a as BridgePushA11yEnvelope, B as BridgeNavPushEnvelope, e as BridgeSessionTokenPushEnvelope } from './bridge-envelopes-DA6vxbyb.js';
|
|
33
2
|
|
|
34
3
|
/**
|
|
35
4
|
* Plugin-side bridge client for the host ↔ plugin platform contract
|
|
@@ -99,4 +68,4 @@ interface PortBridgeClient {
|
|
|
99
68
|
*/
|
|
100
69
|
declare function createPortBridgeClient(port: BridgePortShim): PortBridgeClient;
|
|
101
70
|
|
|
102
|
-
export { type A11yPayload as A, type BridgePortShim as B, type DensityPayload as D, type LocalePayload as L, type
|
|
71
|
+
export { type A11yPayload as A, type BridgePortShim as B, type DensityPayload as D, type LocalePayload as L, type NavPayload as N, type PortBridgeClient as P, type SessionTokenPayload as S, type ThemePayload as T, createPortBridgeClient as c };
|
|
@@ -1,35 +1,4 @@
|
|
|
1
|
-
import { d as BridgePushThemeEnvelope, c as BridgePushLocaleEnvelope, b as BridgePushDensityEnvelope, a as BridgePushA11yEnvelope, B as BridgeNavPushEnvelope, e as BridgeSessionTokenPushEnvelope } from './bridge-envelopes-
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Transport contract used by the plugin-side React hooks
|
|
5
|
-
* (`useMcpResource`, `useMcpTool`) to talk to the host.
|
|
6
|
-
*
|
|
7
|
-
* The hooks are deliberately transport-agnostic: the concrete implementation
|
|
8
|
-
* (postMessage bridge, in-process direct call, fetch-based, …) is injected
|
|
9
|
-
* via {@link ExtensionRuntimeProvider} or the per-hook `transport` option.
|
|
10
|
-
* This keeps the React surface stable across Contract A (host-rendered) and
|
|
11
|
-
* Contract B (worker remote-runtime) execution modes.
|
|
12
|
-
*
|
|
13
|
-
* Implementations must honour the supplied {@link AbortSignal} for
|
|
14
|
-
* cancellation — hooks rely on it to tear down in-flight calls on unmount
|
|
15
|
-
* or argument changes.
|
|
16
|
-
*/
|
|
17
|
-
interface McpTransport {
|
|
18
|
-
/**
|
|
19
|
-
* Fetch a resource by URI. Implementations should reject with an `Error`
|
|
20
|
-
* when the host returns a failure, and should observe `signal` to abort
|
|
21
|
-
* any in-flight work.
|
|
22
|
-
*/
|
|
23
|
-
getResource<T>(uri: string, signal?: AbortSignal): Promise<{
|
|
24
|
-
uri: string;
|
|
25
|
-
data: T;
|
|
26
|
-
}>;
|
|
27
|
-
/**
|
|
28
|
-
* Invoke a tool by name. The request object is opaque to the transport.
|
|
29
|
-
* Implementations should observe `signal` to abort in-flight work.
|
|
30
|
-
*/
|
|
31
|
-
invokeTool<TReq, TRes>(name: string, args: TReq, signal?: AbortSignal): Promise<TRes>;
|
|
32
|
-
}
|
|
1
|
+
import { d as BridgePushThemeEnvelope, c as BridgePushLocaleEnvelope, b as BridgePushDensityEnvelope, a as BridgePushA11yEnvelope, B as BridgeNavPushEnvelope, e as BridgeSessionTokenPushEnvelope } from './bridge-envelopes-DA6vxbyb.cjs';
|
|
33
2
|
|
|
34
3
|
/**
|
|
35
4
|
* Plugin-side bridge client for the host ↔ plugin platform contract
|
|
@@ -99,4 +68,4 @@ interface PortBridgeClient {
|
|
|
99
68
|
*/
|
|
100
69
|
declare function createPortBridgeClient(port: BridgePortShim): PortBridgeClient;
|
|
101
70
|
|
|
102
|
-
export { type A11yPayload as A, type BridgePortShim as B, type DensityPayload as D, type LocalePayload as L, type
|
|
71
|
+
export { type A11yPayload as A, type BridgePortShim as B, type DensityPayload as D, type LocalePayload as L, type NavPayload as N, type PortBridgeClient as P, type SessionTokenPayload as S, type ThemePayload as T, createPortBridgeClient as c };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport contract used by the plugin-side React hooks
|
|
3
|
+
* (`useMcpResource`, `useMcpTool`) to talk to the host.
|
|
4
|
+
*
|
|
5
|
+
* The hooks are deliberately transport-agnostic: the concrete implementation
|
|
6
|
+
* (postMessage bridge, in-process direct call, fetch-based, …) is injected
|
|
7
|
+
* via {@link ExtensionRuntimeProvider} or the per-hook `transport` option.
|
|
8
|
+
* This keeps the React surface stable across Contract A (host-rendered) and
|
|
9
|
+
* Contract B (worker remote-runtime) execution modes.
|
|
10
|
+
*
|
|
11
|
+
* Implementations must honour the supplied {@link AbortSignal} for
|
|
12
|
+
* cancellation — hooks rely on it to tear down in-flight calls on unmount
|
|
13
|
+
* or argument changes.
|
|
14
|
+
*/
|
|
15
|
+
interface McpTransport {
|
|
16
|
+
/**
|
|
17
|
+
* Fetch a resource by URI. Implementations should reject with an `Error`
|
|
18
|
+
* when the host returns a failure, and should observe `signal` to abort
|
|
19
|
+
* any in-flight work.
|
|
20
|
+
*/
|
|
21
|
+
getResource<T>(uri: string, signal?: AbortSignal): Promise<{
|
|
22
|
+
uri: string;
|
|
23
|
+
data: T;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Invoke a tool by name. The request object is opaque to the transport.
|
|
27
|
+
* Implementations should observe `signal` to abort in-flight work.
|
|
28
|
+
*/
|
|
29
|
+
invokeTool<TReq, TRes>(name: string, args: TReq, signal?: AbortSignal): Promise<TRes>;
|
|
30
|
+
/**
|
|
31
|
+
* Upload a binary document to CORE storage via the host shell and resolve
|
|
32
|
+
* the resulting {@link UploadDocumentResult} (a `StoredDocumentId` plus the
|
|
33
|
+
* server-validated file metadata).
|
|
34
|
+
*
|
|
35
|
+
* **Optional capability.** A transport that does not support binary upload
|
|
36
|
+
* simply omits this member; {@link "../../plugin-ui" useMcpUpload} throws a
|
|
37
|
+
* clear runtime error when invoked against such a transport. Keeping it
|
|
38
|
+
* optional makes adding the upload seam a **non-breaking, additive** change
|
|
39
|
+
* for existing custom transport implementations (which only provided
|
|
40
|
+
* `getResource` + `invokeTool`).
|
|
41
|
+
*
|
|
42
|
+
* The bytes are handed off as an {@link ArrayBuffer} — the transport is
|
|
43
|
+
* expected to move it as a **transferable** (zero-copy) across the
|
|
44
|
+
* worker↔host port. The plugin (worker) never sees the capability token or
|
|
45
|
+
* the kernel URL: the host shell mints the token, streams the octet-stream
|
|
46
|
+
* to the kernel document-upload route, and returns only the result.
|
|
47
|
+
*
|
|
48
|
+
* When provided, implementations MUST observe `signal` so an unmount /
|
|
49
|
+
* cancel aborts the in-flight upload rather than streaming bytes to
|
|
50
|
+
* completion.
|
|
51
|
+
*/
|
|
52
|
+
uploadDocument?(meta: UploadDocumentMeta, buffer: ArrayBuffer, signal?: AbortSignal): Promise<UploadDocumentResult>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Metadata accompanying an {@link McpTransport.uploadDocument} call. All fields
|
|
56
|
+
* are advisory hints to the host/kernel: the kernel derives the organisation
|
|
57
|
+
* from the authenticated capability token (never from this payload), binds the
|
|
58
|
+
* effective module key to the caller's plugin identity via a kernel allowlist,
|
|
59
|
+
* and re-validates / sanitises `fileName` + `contentType` server-side.
|
|
60
|
+
*/
|
|
61
|
+
interface UploadDocumentMeta {
|
|
62
|
+
/** Requested storage module key (e.g. "hr"). Validated against the caller's kernel allowlist. */
|
|
63
|
+
moduleKey: string;
|
|
64
|
+
/** Owning entity name (e.g. "Candidates", "CompanyDocuments"). */
|
|
65
|
+
entityName: string;
|
|
66
|
+
/** Optional owning entity id. */
|
|
67
|
+
entityId?: string;
|
|
68
|
+
/** Logical sub-folder / category (e.g. "cvs"). */
|
|
69
|
+
category: string;
|
|
70
|
+
/** Client-supplied file name (server sanitises + caps length). */
|
|
71
|
+
fileName: string;
|
|
72
|
+
/** Client-supplied MIME type (server re-parses; falls back to application/octet-stream). */
|
|
73
|
+
contentType: string;
|
|
74
|
+
/** Byte length of the buffer (advisory; the kernel size cap is authoritative). */
|
|
75
|
+
sizeBytes: number;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Result of a successful {@link McpTransport.uploadDocument} call — the id of
|
|
79
|
+
* the registered `StoredDocument` plus the metadata the kernel actually stored.
|
|
80
|
+
* The plugin only ever holds the id; it never receives the bytes back.
|
|
81
|
+
*/
|
|
82
|
+
interface UploadDocumentResult {
|
|
83
|
+
storedDocumentId: string;
|
|
84
|
+
fileName: string;
|
|
85
|
+
contentType: string;
|
|
86
|
+
sizeBytes: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Wire-shape constants and type guards for host→plugin bridge push messages
|
|
91
|
+
* (WI 4858 sub-plan 2). These types ride the existing MessagePort channel
|
|
92
|
+
* alongside the `ethisys:mcp:*` and `ethisys:remotedom` envelopes — the
|
|
93
|
+
* `type` discriminant keeps them fully separate at the dispatch site.
|
|
94
|
+
*
|
|
95
|
+
* The host posts bridge pushes through `WorkerRemoteDomTransport.hostPort`;
|
|
96
|
+
* the plugin side reads them in `createPortBridgeClient` (plugin-ui package).
|
|
97
|
+
* In-realm (Tier T) plugins use `InMemoryBridgeTransport` which calls the
|
|
98
|
+
* subscriber callbacks directly — no serialisation needed.
|
|
99
|
+
*/
|
|
100
|
+
declare const BRIDGE_PUSH_THEME: "ethisys:bridge:theme";
|
|
101
|
+
declare const BRIDGE_PUSH_LOCALE: "ethisys:bridge:locale";
|
|
102
|
+
declare const BRIDGE_PUSH_DENSITY: "ethisys:bridge:density";
|
|
103
|
+
declare const BRIDGE_PUSH_A11Y: "ethisys:bridge:a11y";
|
|
104
|
+
declare const BRIDGE_NAV_PUSH: "ethisys:bridge:nav";
|
|
105
|
+
declare const BRIDGE_SESSION_TOKEN_PUSH: "ethisys:bridge:token";
|
|
106
|
+
/** Theme push: host → plugin whenever the host theme changes. */
|
|
107
|
+
interface BridgePushThemeEnvelope {
|
|
108
|
+
readonly type: typeof BRIDGE_PUSH_THEME;
|
|
109
|
+
/** "light" | "dark" | "high-contrast" */
|
|
110
|
+
readonly mode: string;
|
|
111
|
+
/** Flat design-token map (CSS-variable-name → value). May be empty. */
|
|
112
|
+
readonly tokens: Readonly<Record<string, string>>;
|
|
113
|
+
}
|
|
114
|
+
/** Locale push: host → plugin whenever the active locale changes. */
|
|
115
|
+
interface BridgePushLocaleEnvelope {
|
|
116
|
+
readonly type: typeof BRIDGE_PUSH_LOCALE;
|
|
117
|
+
/** BCP 47 tag, e.g. "en-GB". */
|
|
118
|
+
readonly locale: string;
|
|
119
|
+
/** "ltr" | "rtl" */
|
|
120
|
+
readonly dir: "ltr" | "rtl";
|
|
121
|
+
}
|
|
122
|
+
/** Density push: host → plugin when the UI density preference changes. */
|
|
123
|
+
interface BridgePushDensityEnvelope {
|
|
124
|
+
readonly type: typeof BRIDGE_PUSH_DENSITY;
|
|
125
|
+
/** "comfortable" | "compact" */
|
|
126
|
+
readonly density: string;
|
|
127
|
+
}
|
|
128
|
+
/** A11y push: host → plugin when accessibility prefs change. */
|
|
129
|
+
interface BridgePushA11yEnvelope {
|
|
130
|
+
readonly type: typeof BRIDGE_PUSH_A11Y;
|
|
131
|
+
readonly reducedMotion: boolean;
|
|
132
|
+
readonly highContrast: boolean;
|
|
133
|
+
}
|
|
134
|
+
/** Nav push: host → plugin when the SPA location changes. */
|
|
135
|
+
interface BridgeNavPushEnvelope {
|
|
136
|
+
readonly type: typeof BRIDGE_NAV_PUSH;
|
|
137
|
+
/** Current SPA path (pathname + search). */
|
|
138
|
+
readonly path: string;
|
|
139
|
+
/** `window.history.length` at the time of push. */
|
|
140
|
+
readonly historyLength: number;
|
|
141
|
+
}
|
|
142
|
+
/** Host → plugin: push a short-lived frontend-session token. */
|
|
143
|
+
interface BridgeSessionTokenPushEnvelope {
|
|
144
|
+
readonly type: typeof BRIDGE_SESSION_TOKEN_PUSH;
|
|
145
|
+
/** Opaque JWT string — audience-restricted to the plugin's own backend. */
|
|
146
|
+
readonly token: string;
|
|
147
|
+
/** Absolute epoch-ms at which the token expires. Refresh fires 30 s before. */
|
|
148
|
+
readonly expiresAtMs: number;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export type { BridgeNavPushEnvelope as B, McpTransport as M, UploadDocumentMeta as U, BridgePushA11yEnvelope as a, BridgePushDensityEnvelope as b, BridgePushLocaleEnvelope as c, BridgePushThemeEnvelope as d, BridgeSessionTokenPushEnvelope as e, UploadDocumentResult as f };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport contract used by the plugin-side React hooks
|
|
3
|
+
* (`useMcpResource`, `useMcpTool`) to talk to the host.
|
|
4
|
+
*
|
|
5
|
+
* The hooks are deliberately transport-agnostic: the concrete implementation
|
|
6
|
+
* (postMessage bridge, in-process direct call, fetch-based, …) is injected
|
|
7
|
+
* via {@link ExtensionRuntimeProvider} or the per-hook `transport` option.
|
|
8
|
+
* This keeps the React surface stable across Contract A (host-rendered) and
|
|
9
|
+
* Contract B (worker remote-runtime) execution modes.
|
|
10
|
+
*
|
|
11
|
+
* Implementations must honour the supplied {@link AbortSignal} for
|
|
12
|
+
* cancellation — hooks rely on it to tear down in-flight calls on unmount
|
|
13
|
+
* or argument changes.
|
|
14
|
+
*/
|
|
15
|
+
interface McpTransport {
|
|
16
|
+
/**
|
|
17
|
+
* Fetch a resource by URI. Implementations should reject with an `Error`
|
|
18
|
+
* when the host returns a failure, and should observe `signal` to abort
|
|
19
|
+
* any in-flight work.
|
|
20
|
+
*/
|
|
21
|
+
getResource<T>(uri: string, signal?: AbortSignal): Promise<{
|
|
22
|
+
uri: string;
|
|
23
|
+
data: T;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Invoke a tool by name. The request object is opaque to the transport.
|
|
27
|
+
* Implementations should observe `signal` to abort in-flight work.
|
|
28
|
+
*/
|
|
29
|
+
invokeTool<TReq, TRes>(name: string, args: TReq, signal?: AbortSignal): Promise<TRes>;
|
|
30
|
+
/**
|
|
31
|
+
* Upload a binary document to CORE storage via the host shell and resolve
|
|
32
|
+
* the resulting {@link UploadDocumentResult} (a `StoredDocumentId` plus the
|
|
33
|
+
* server-validated file metadata).
|
|
34
|
+
*
|
|
35
|
+
* **Optional capability.** A transport that does not support binary upload
|
|
36
|
+
* simply omits this member; {@link "../../plugin-ui" useMcpUpload} throws a
|
|
37
|
+
* clear runtime error when invoked against such a transport. Keeping it
|
|
38
|
+
* optional makes adding the upload seam a **non-breaking, additive** change
|
|
39
|
+
* for existing custom transport implementations (which only provided
|
|
40
|
+
* `getResource` + `invokeTool`).
|
|
41
|
+
*
|
|
42
|
+
* The bytes are handed off as an {@link ArrayBuffer} — the transport is
|
|
43
|
+
* expected to move it as a **transferable** (zero-copy) across the
|
|
44
|
+
* worker↔host port. The plugin (worker) never sees the capability token or
|
|
45
|
+
* the kernel URL: the host shell mints the token, streams the octet-stream
|
|
46
|
+
* to the kernel document-upload route, and returns only the result.
|
|
47
|
+
*
|
|
48
|
+
* When provided, implementations MUST observe `signal` so an unmount /
|
|
49
|
+
* cancel aborts the in-flight upload rather than streaming bytes to
|
|
50
|
+
* completion.
|
|
51
|
+
*/
|
|
52
|
+
uploadDocument?(meta: UploadDocumentMeta, buffer: ArrayBuffer, signal?: AbortSignal): Promise<UploadDocumentResult>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Metadata accompanying an {@link McpTransport.uploadDocument} call. All fields
|
|
56
|
+
* are advisory hints to the host/kernel: the kernel derives the organisation
|
|
57
|
+
* from the authenticated capability token (never from this payload), binds the
|
|
58
|
+
* effective module key to the caller's plugin identity via a kernel allowlist,
|
|
59
|
+
* and re-validates / sanitises `fileName` + `contentType` server-side.
|
|
60
|
+
*/
|
|
61
|
+
interface UploadDocumentMeta {
|
|
62
|
+
/** Requested storage module key (e.g. "hr"). Validated against the caller's kernel allowlist. */
|
|
63
|
+
moduleKey: string;
|
|
64
|
+
/** Owning entity name (e.g. "Candidates", "CompanyDocuments"). */
|
|
65
|
+
entityName: string;
|
|
66
|
+
/** Optional owning entity id. */
|
|
67
|
+
entityId?: string;
|
|
68
|
+
/** Logical sub-folder / category (e.g. "cvs"). */
|
|
69
|
+
category: string;
|
|
70
|
+
/** Client-supplied file name (server sanitises + caps length). */
|
|
71
|
+
fileName: string;
|
|
72
|
+
/** Client-supplied MIME type (server re-parses; falls back to application/octet-stream). */
|
|
73
|
+
contentType: string;
|
|
74
|
+
/** Byte length of the buffer (advisory; the kernel size cap is authoritative). */
|
|
75
|
+
sizeBytes: number;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Result of a successful {@link McpTransport.uploadDocument} call — the id of
|
|
79
|
+
* the registered `StoredDocument` plus the metadata the kernel actually stored.
|
|
80
|
+
* The plugin only ever holds the id; it never receives the bytes back.
|
|
81
|
+
*/
|
|
82
|
+
interface UploadDocumentResult {
|
|
83
|
+
storedDocumentId: string;
|
|
84
|
+
fileName: string;
|
|
85
|
+
contentType: string;
|
|
86
|
+
sizeBytes: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Wire-shape constants and type guards for host→plugin bridge push messages
|
|
91
|
+
* (WI 4858 sub-plan 2). These types ride the existing MessagePort channel
|
|
92
|
+
* alongside the `ethisys:mcp:*` and `ethisys:remotedom` envelopes — the
|
|
93
|
+
* `type` discriminant keeps them fully separate at the dispatch site.
|
|
94
|
+
*
|
|
95
|
+
* The host posts bridge pushes through `WorkerRemoteDomTransport.hostPort`;
|
|
96
|
+
* the plugin side reads them in `createPortBridgeClient` (plugin-ui package).
|
|
97
|
+
* In-realm (Tier T) plugins use `InMemoryBridgeTransport` which calls the
|
|
98
|
+
* subscriber callbacks directly — no serialisation needed.
|
|
99
|
+
*/
|
|
100
|
+
declare const BRIDGE_PUSH_THEME: "ethisys:bridge:theme";
|
|
101
|
+
declare const BRIDGE_PUSH_LOCALE: "ethisys:bridge:locale";
|
|
102
|
+
declare const BRIDGE_PUSH_DENSITY: "ethisys:bridge:density";
|
|
103
|
+
declare const BRIDGE_PUSH_A11Y: "ethisys:bridge:a11y";
|
|
104
|
+
declare const BRIDGE_NAV_PUSH: "ethisys:bridge:nav";
|
|
105
|
+
declare const BRIDGE_SESSION_TOKEN_PUSH: "ethisys:bridge:token";
|
|
106
|
+
/** Theme push: host → plugin whenever the host theme changes. */
|
|
107
|
+
interface BridgePushThemeEnvelope {
|
|
108
|
+
readonly type: typeof BRIDGE_PUSH_THEME;
|
|
109
|
+
/** "light" | "dark" | "high-contrast" */
|
|
110
|
+
readonly mode: string;
|
|
111
|
+
/** Flat design-token map (CSS-variable-name → value). May be empty. */
|
|
112
|
+
readonly tokens: Readonly<Record<string, string>>;
|
|
113
|
+
}
|
|
114
|
+
/** Locale push: host → plugin whenever the active locale changes. */
|
|
115
|
+
interface BridgePushLocaleEnvelope {
|
|
116
|
+
readonly type: typeof BRIDGE_PUSH_LOCALE;
|
|
117
|
+
/** BCP 47 tag, e.g. "en-GB". */
|
|
118
|
+
readonly locale: string;
|
|
119
|
+
/** "ltr" | "rtl" */
|
|
120
|
+
readonly dir: "ltr" | "rtl";
|
|
121
|
+
}
|
|
122
|
+
/** Density push: host → plugin when the UI density preference changes. */
|
|
123
|
+
interface BridgePushDensityEnvelope {
|
|
124
|
+
readonly type: typeof BRIDGE_PUSH_DENSITY;
|
|
125
|
+
/** "comfortable" | "compact" */
|
|
126
|
+
readonly density: string;
|
|
127
|
+
}
|
|
128
|
+
/** A11y push: host → plugin when accessibility prefs change. */
|
|
129
|
+
interface BridgePushA11yEnvelope {
|
|
130
|
+
readonly type: typeof BRIDGE_PUSH_A11Y;
|
|
131
|
+
readonly reducedMotion: boolean;
|
|
132
|
+
readonly highContrast: boolean;
|
|
133
|
+
}
|
|
134
|
+
/** Nav push: host → plugin when the SPA location changes. */
|
|
135
|
+
interface BridgeNavPushEnvelope {
|
|
136
|
+
readonly type: typeof BRIDGE_NAV_PUSH;
|
|
137
|
+
/** Current SPA path (pathname + search). */
|
|
138
|
+
readonly path: string;
|
|
139
|
+
/** `window.history.length` at the time of push. */
|
|
140
|
+
readonly historyLength: number;
|
|
141
|
+
}
|
|
142
|
+
/** Host → plugin: push a short-lived frontend-session token. */
|
|
143
|
+
interface BridgeSessionTokenPushEnvelope {
|
|
144
|
+
readonly type: typeof BRIDGE_SESSION_TOKEN_PUSH;
|
|
145
|
+
/** Opaque JWT string — audience-restricted to the plugin's own backend. */
|
|
146
|
+
readonly token: string;
|
|
147
|
+
/** Absolute epoch-ms at which the token expires. Refresh fires 30 s before. */
|
|
148
|
+
readonly expiresAtMs: number;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export type { BridgeNavPushEnvelope as B, McpTransport as M, UploadDocumentMeta as U, BridgePushA11yEnvelope as a, BridgePushDensityEnvelope as b, BridgePushLocaleEnvelope as c, BridgePushThemeEnvelope as d, BridgeSessionTokenPushEnvelope as e, UploadDocumentResult as f };
|
package/dist/host/index.cjs
CHANGED
|
@@ -309,6 +309,13 @@ var WorkerRemoteDomTransport = class {
|
|
|
309
309
|
coalesceMs;
|
|
310
310
|
maxConcurrentMcpRequests;
|
|
311
311
|
abortController;
|
|
312
|
+
// Per-request AbortControllers keyed by request id. An inbound
|
|
313
|
+
// `ethisys:mcp:abort` envelope (the worker posts one when a hook's
|
|
314
|
+
// AbortSignal fires on unmount / arg change) aborts just that request;
|
|
315
|
+
// dispose() fans out to every registered controller. Without this a
|
|
316
|
+
// cancelled call — most costly for uploadDocument — keeps streaming to the
|
|
317
|
+
// kernel even though the plugin promise already rejected.
|
|
318
|
+
inFlightAborts = /* @__PURE__ */ new Map();
|
|
312
319
|
inFlightMcpRequests = 0;
|
|
313
320
|
remoteDomConsumer;
|
|
314
321
|
bridgeMessageConsumer;
|
|
@@ -496,6 +503,10 @@ var WorkerRemoteDomTransport = class {
|
|
|
496
503
|
this.disposed = true;
|
|
497
504
|
try {
|
|
498
505
|
this.abortController.abort();
|
|
506
|
+
for (const controller of this.inFlightAborts.values()) {
|
|
507
|
+
controller.abort();
|
|
508
|
+
}
|
|
509
|
+
this.inFlightAborts.clear();
|
|
499
510
|
} catch {
|
|
500
511
|
}
|
|
501
512
|
try {
|
|
@@ -534,6 +545,12 @@ var WorkerRemoteDomTransport = class {
|
|
|
534
545
|
case "ethisys:mcp:getResource":
|
|
535
546
|
this.dispatchMcp(message, "ethisys:mcp:getResource:result", (m) => this.handleGetResource(m));
|
|
536
547
|
return;
|
|
548
|
+
case "ethisys:mcp:uploadDocument":
|
|
549
|
+
this.dispatchMcp(message, "ethisys:mcp:uploadDocument:result", (m) => this.handleUploadDocument(m));
|
|
550
|
+
return;
|
|
551
|
+
case "ethisys:mcp:abort":
|
|
552
|
+
this.inFlightAborts.get(message.id ?? "")?.abort();
|
|
553
|
+
return;
|
|
537
554
|
case "ethisys:remotedom":
|
|
538
555
|
this.remoteDomConsumer?.(message.payload);
|
|
539
556
|
return;
|
|
@@ -563,10 +580,37 @@ var WorkerRemoteDomTransport = class {
|
|
|
563
580
|
return;
|
|
564
581
|
}
|
|
565
582
|
this.inFlightMcpRequests++;
|
|
583
|
+
this.registerRequestAbort(message.id);
|
|
566
584
|
handler(message).finally(() => {
|
|
585
|
+
this.releaseRequestAbort(message.id);
|
|
567
586
|
this.inFlightMcpRequests = Math.max(0, this.inFlightMcpRequests - 1);
|
|
568
587
|
});
|
|
569
588
|
}
|
|
589
|
+
/**
|
|
590
|
+
* Create a per-request AbortController and register it under the request id
|
|
591
|
+
* so an inbound `ethisys:mcp:abort` can cancel just this call. If the
|
|
592
|
+
* transport is already disposed the controller starts aborted so the
|
|
593
|
+
* handler's `mcpClient.fetch` short-circuits immediately.
|
|
594
|
+
*/
|
|
595
|
+
registerRequestAbort(id) {
|
|
596
|
+
const controller = new AbortController();
|
|
597
|
+
if (this.abortController.signal.aborted) {
|
|
598
|
+
controller.abort();
|
|
599
|
+
}
|
|
600
|
+
this.inFlightAborts.set(id, controller);
|
|
601
|
+
}
|
|
602
|
+
/** Drop a settled request's AbortController. */
|
|
603
|
+
releaseRequestAbort(id) {
|
|
604
|
+
this.inFlightAborts.delete(id);
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* The signal to hand to `mcpClient.fetch` for a given request: the
|
|
608
|
+
* per-request controller when registered (client cancel path), falling back
|
|
609
|
+
* to the dispose-level controller.
|
|
610
|
+
*/
|
|
611
|
+
requestSignal(id) {
|
|
612
|
+
return this.inFlightAborts.get(id)?.signal ?? this.abortController.signal;
|
|
613
|
+
}
|
|
570
614
|
async handleInvokeTool(message) {
|
|
571
615
|
let token;
|
|
572
616
|
try {
|
|
@@ -584,7 +628,7 @@ var WorkerRemoteDomTransport = class {
|
|
|
584
628
|
name: message.name,
|
|
585
629
|
args: message.args,
|
|
586
630
|
capabilityToken: token,
|
|
587
|
-
signal: this.
|
|
631
|
+
signal: this.requestSignal(message.id)
|
|
588
632
|
});
|
|
589
633
|
this.safePostMessage({
|
|
590
634
|
id: message.id,
|
|
@@ -613,7 +657,7 @@ var WorkerRemoteDomTransport = class {
|
|
|
613
657
|
kind: "getResource",
|
|
614
658
|
uri: message.uri,
|
|
615
659
|
capabilityToken: token,
|
|
616
|
-
signal: this.
|
|
660
|
+
signal: this.requestSignal(message.id)
|
|
617
661
|
});
|
|
618
662
|
this.safePostMessage({
|
|
619
663
|
id: message.id,
|
|
@@ -626,6 +670,36 @@ var WorkerRemoteDomTransport = class {
|
|
|
626
670
|
this.replyError(message.id, "ethisys:mcp:getResource:result", err);
|
|
627
671
|
}
|
|
628
672
|
}
|
|
673
|
+
async handleUploadDocument(message) {
|
|
674
|
+
let token;
|
|
675
|
+
try {
|
|
676
|
+
token = await this.capabilityTokenProvider();
|
|
677
|
+
} catch (err) {
|
|
678
|
+
this.replyError(message.id, "ethisys:mcp:uploadDocument:result", err);
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
if (this.disposed) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
try {
|
|
685
|
+
const result = await this.mcpClient.fetch({
|
|
686
|
+
kind: "uploadDocument",
|
|
687
|
+
meta: message.meta,
|
|
688
|
+
buffer: message.buffer,
|
|
689
|
+
capabilityToken: token,
|
|
690
|
+
signal: this.requestSignal(message.id)
|
|
691
|
+
});
|
|
692
|
+
this.safePostMessage({
|
|
693
|
+
id: message.id,
|
|
694
|
+
type: "ethisys:mcp:uploadDocument:result",
|
|
695
|
+
ok: result.ok,
|
|
696
|
+
data: result.data,
|
|
697
|
+
error: result.error
|
|
698
|
+
});
|
|
699
|
+
} catch (err) {
|
|
700
|
+
this.replyError(message.id, "ethisys:mcp:uploadDocument:result", err);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
629
703
|
replyError(id, type, err) {
|
|
630
704
|
const message = err instanceof Error ? err.message : "MCP request failed";
|
|
631
705
|
this.safePostMessage({ id, type, ok: false, error: message });
|