@ethisyscore/extension-runtime 1.135.0 → 1.137.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-BM3bq99B.d.cts +35 -0
- package/dist/MockPluginStorage-BndTX2RR.d.ts +35 -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 +57 -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 +57 -2
- package/dist/host/index.js.map +1 -1
- package/dist/mock-host/cli.cjs +273 -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 +272 -8
- package/dist/mock-host/cli.js.map +1 -1
- package/dist/mock-host/index.cjs +236 -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 +236 -2
- package/dist/mock-host/index.js.map +1 -1
- package/dist/plugin/index.cjs +378 -100
- package/dist/plugin/index.cjs.map +1 -1
- package/dist/plugin/index.d.cts +171 -6
- package/dist/plugin/index.d.ts +171 -6
- package/dist/plugin/index.js +366 -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 +1 -1
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ Built on top of `@ethisyscore/protocol` (canonical bridge / manifest / SDUI / ca
|
|
|
13
13
|
| ----- | --- | ------- |
|
|
14
14
|
| `@ethisyscore/extension-runtime` | Root re-exports | Stable consumer-facing types and helpers. |
|
|
15
15
|
| `@ethisyscore/extension-runtime/host` | Host integration | `WorkerRemoteDomTransport`, declarative interpreter, semantic component registry, offscreen-canvas helpers. |
|
|
16
|
-
| `@ethisyscore/extension-runtime/plugin` | Plugin side | `ExtensionRuntimeProvider`, `useMcpResource`, `useMcpTool`, transport abstraction. |
|
|
17
|
-
| `@ethisyscore/extension-runtime/mock-host` | Local dev | `DeclarativeMockHost` and the `mock-host` CLI entry. |
|
|
16
|
+
| `@ethisyscore/extension-runtime/plugin` | Plugin side | `ExtensionRuntimeProvider`, `useMcpResource`, `useMcpTool`, transport abstraction, `uploadFileToPluginStorage` (browser file → the app's own plugin storage via the `uploadToStorage` request kind). |
|
|
17
|
+
| `@ethisyscore/extension-runtime/mock-host` | Local dev | `DeclarativeMockHost`, `InMemoryMcpTransport` (its `uploadToStorage` writes to an in-memory `MockPluginStorage`) and the `mock-host` CLI entry. |
|
|
18
18
|
|
|
19
19
|
The `mock-host` bin (`npx mock-host <dir>`) is shipped — boots a Vite dev page that mounts the declarative interpreter against a directory of SDUI JSON resources. Run `--render-mode remote-runtime <bundle>` for the Contract B variant.
|
|
20
20
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { g as UploadToStorageMeta, h as UploadToStorageResult } from './bridge-envelopes-BcKu-nQm.cjs';
|
|
2
|
+
|
|
3
|
+
/** A file the mock host stored for an `uploadToStorage` call. */
|
|
4
|
+
interface MockStoredFile {
|
|
5
|
+
readonly path: string;
|
|
6
|
+
readonly fileName: string;
|
|
7
|
+
readonly contentType: string;
|
|
8
|
+
readonly bytes: Uint8Array;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* In-memory stand-in for an app's plugin storage, answering the `uploadToStorage` request kind for
|
|
12
|
+
* `dev:mock` and tests.
|
|
13
|
+
*
|
|
14
|
+
* It behaves like the kernel route where a plugin can observe the difference:
|
|
15
|
+
* - the prefix is validated with the same rules (an invalid one rejects with status 400);
|
|
16
|
+
* - the file type is checked against the same allow-list (415), and the CANONICAL type is stored;
|
|
17
|
+
* - a body over 30 MB rejects with 413;
|
|
18
|
+
* - the path is server-chosen, `{prefix}/{32 hex}{ext}` with the kernel's extension rule, so two
|
|
19
|
+
* uploads never share a path.
|
|
20
|
+
*
|
|
21
|
+
* It does not model quota (507), the storage lock or upload slots (503), or the deadline (408).
|
|
22
|
+
*/
|
|
23
|
+
declare class MockPluginStorage {
|
|
24
|
+
private readonly files;
|
|
25
|
+
/** Store `buffer` under a new path below `meta.pathPrefix`. */
|
|
26
|
+
store(meta: UploadToStorageMeta, buffer: ArrayBuffer): UploadToStorageResult;
|
|
27
|
+
/** The stored file at `path`, or `undefined`. */
|
|
28
|
+
get(path: string): MockStoredFile | undefined;
|
|
29
|
+
/** Every stored file, in upload order. */
|
|
30
|
+
list(): readonly MockStoredFile[];
|
|
31
|
+
/** Forget every stored file. */
|
|
32
|
+
clear(): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { MockPluginStorage as M, type MockStoredFile as a };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { g as UploadToStorageMeta, h as UploadToStorageResult } from './bridge-envelopes-BcKu-nQm.js';
|
|
2
|
+
|
|
3
|
+
/** A file the mock host stored for an `uploadToStorage` call. */
|
|
4
|
+
interface MockStoredFile {
|
|
5
|
+
readonly path: string;
|
|
6
|
+
readonly fileName: string;
|
|
7
|
+
readonly contentType: string;
|
|
8
|
+
readonly bytes: Uint8Array;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* In-memory stand-in for an app's plugin storage, answering the `uploadToStorage` request kind for
|
|
12
|
+
* `dev:mock` and tests.
|
|
13
|
+
*
|
|
14
|
+
* It behaves like the kernel route where a plugin can observe the difference:
|
|
15
|
+
* - the prefix is validated with the same rules (an invalid one rejects with status 400);
|
|
16
|
+
* - the file type is checked against the same allow-list (415), and the CANONICAL type is stored;
|
|
17
|
+
* - a body over 30 MB rejects with 413;
|
|
18
|
+
* - the path is server-chosen, `{prefix}/{32 hex}{ext}` with the kernel's extension rule, so two
|
|
19
|
+
* uploads never share a path.
|
|
20
|
+
*
|
|
21
|
+
* It does not model quota (507), the storage lock or upload slots (503), or the deadline (408).
|
|
22
|
+
*/
|
|
23
|
+
declare class MockPluginStorage {
|
|
24
|
+
private readonly files;
|
|
25
|
+
/** Store `buffer` under a new path below `meta.pathPrefix`. */
|
|
26
|
+
store(meta: UploadToStorageMeta, buffer: ArrayBuffer): UploadToStorageResult;
|
|
27
|
+
/** The stored file at `path`, or `undefined`. */
|
|
28
|
+
get(path: string): MockStoredFile | undefined;
|
|
29
|
+
/** Every stored file, in upload order. */
|
|
30
|
+
list(): readonly MockStoredFile[];
|
|
31
|
+
/** Forget every stored file. */
|
|
32
|
+
clear(): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { MockPluginStorage as M, type MockStoredFile as a };
|
|
@@ -1,4 +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-
|
|
1
|
+
import { d as BridgePushThemeEnvelope, c as BridgePushLocaleEnvelope, b as BridgePushDensityEnvelope, a as BridgePushA11yEnvelope, B as BridgeNavPushEnvelope, e as BridgeSessionTokenPushEnvelope } from './bridge-envelopes-BcKu-nQm.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Plugin-side bridge client for the host ↔ plugin platform contract
|
|
@@ -1,4 +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-
|
|
1
|
+
import { d as BridgePushThemeEnvelope, c as BridgePushLocaleEnvelope, b as BridgePushDensityEnvelope, a as BridgePushA11yEnvelope, B as BridgeNavPushEnvelope, e as BridgeSessionTokenPushEnvelope } from './bridge-envelopes-BcKu-nQm.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Plugin-side bridge client for the host ↔ plugin platform contract
|
|
@@ -107,6 +107,77 @@ interface McpTransport {
|
|
|
107
107
|
* completion.
|
|
108
108
|
*/
|
|
109
109
|
uploadDocument?(meta: UploadDocumentMeta, buffer: ArrayBuffer, signal?: AbortSignal): Promise<UploadDocumentResult>;
|
|
110
|
+
/**
|
|
111
|
+
* Stream a browser file into the calling app's OWN plugin storage via the host shell, and
|
|
112
|
+
* resolve the {@link UploadToStorageResult}: the plugin-relative `path` the server chose, which
|
|
113
|
+
* is exactly the string the plugin's backend `IPluginStorage` reads, deletes or links.
|
|
114
|
+
*
|
|
115
|
+
* This is the route for every browser upload that a plugin's backend will later read. Never
|
|
116
|
+
* base64 a file through a tool call: the bytes would cross the MCP JSON pipe, the tool's
|
|
117
|
+
* argument limits and the audit payload. Unlike {@link uploadDocument} (legacy CORE storage,
|
|
118
|
+
* `StoredDocument` ids), the destination is the app's own storage namespace, which the kernel
|
|
119
|
+
* derives from the capability token alone.
|
|
120
|
+
*
|
|
121
|
+
* **Optional capability**, for the same reason as {@link uploadDocument}: a transport that
|
|
122
|
+
* predates it simply omits the member, and `uploadFileToPluginStorage` fails with a typed
|
|
123
|
+
* `unsupported` error rather than a `not a function` TypeError.
|
|
124
|
+
*
|
|
125
|
+
* The bytes are handed off as an {@link ArrayBuffer} the transport moves as a transferable
|
|
126
|
+
* (zero-copy) across the worker↔host port, so the caller's buffer is detached afterwards. The
|
|
127
|
+
* worker never sees the capability token or the kernel URL: the host mints the token, POSTs the
|
|
128
|
+
* octet-stream to `extensions/storage/upload-stream` with `x-cc-storage-prefix`,
|
|
129
|
+
* `x-cc-file-name` and `x-cc-content-type`, and returns only the result.
|
|
130
|
+
*
|
|
131
|
+
* When provided, implementations MUST observe `signal`. A host failure SHOULD reject with an
|
|
132
|
+
* error carrying the HTTP `status` (see `PluginStorageUploadError`), so a plugin can tell an
|
|
133
|
+
* over-size file (413) from an exhausted quota (507).
|
|
134
|
+
*/
|
|
135
|
+
uploadToStorage?(meta: UploadToStorageMeta, buffer: ArrayBuffer, signal?: AbortSignal): Promise<UploadToStorageResult>;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Metadata accompanying an {@link McpTransport.uploadToStorage} call. There is no module key and
|
|
139
|
+
* no organisation: the kernel derives the storage namespace from the capability token, so there is
|
|
140
|
+
* nothing for the client to claim.
|
|
141
|
+
*/
|
|
142
|
+
interface UploadToStorageMeta {
|
|
143
|
+
/**
|
|
144
|
+
* Folder inside the app's own storage namespace: one or more `/`-separated segments of
|
|
145
|
+
* `[A-Za-z0-9_-]` (HR: `cvs`; Legal: `governance/{documentId}/{versionId}`), at most 84
|
|
146
|
+
* characters, with no leading or trailing `/`. A malformed prefix is refused (400), never
|
|
147
|
+
* repaired. Sent as `x-cc-storage-prefix`. The server re-validates it and is authoritative.
|
|
148
|
+
*/
|
|
149
|
+
pathPrefix: string;
|
|
150
|
+
/**
|
|
151
|
+
* Client-supplied file name: display metadata, and its extension decides the stored type. It
|
|
152
|
+
* must end in an allow-listed extension (`PLUGIN_STORAGE_UPLOAD_CONTENT_TYPES`) or the server
|
|
153
|
+
* answers 415. Sent as `x-cc-file-name`.
|
|
154
|
+
*/
|
|
155
|
+
fileName: string;
|
|
156
|
+
/**
|
|
157
|
+
* Declared MIME type. It must be the extension's canonical type or a listed alias (415
|
|
158
|
+
* otherwise); empty or `application/octet-stream` means "use the extension's type". Sent as
|
|
159
|
+
* `x-cc-content-type`.
|
|
160
|
+
*/
|
|
161
|
+
contentType: string;
|
|
162
|
+
/** Byte length of the buffer (advisory; the server reports what actually reached storage). */
|
|
163
|
+
sizeBytes: number;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Result of a successful {@link McpTransport.uploadToStorage} call.
|
|
167
|
+
*/
|
|
168
|
+
interface UploadToStorageResult {
|
|
169
|
+
/**
|
|
170
|
+
* Plugin-relative storage path, `{pathPrefix}/{guid}{ext}`, chosen by the server: exactly the
|
|
171
|
+
* string the plugin backend's `IPluginStorage` accepts. Not a URI. Record it with the app's own
|
|
172
|
+
* command.
|
|
173
|
+
*/
|
|
174
|
+
path: string;
|
|
175
|
+
/** The sanitised file name the server recorded. */
|
|
176
|
+
fileName: string;
|
|
177
|
+
/** The content type the server recorded. */
|
|
178
|
+
contentType: string;
|
|
179
|
+
/** Bytes that actually reached storage, counted by the server. */
|
|
180
|
+
sizeBytes: number;
|
|
110
181
|
}
|
|
111
182
|
/**
|
|
112
183
|
* Metadata accompanying an {@link McpTransport.uploadDocument} call. All fields
|
|
@@ -205,4 +276,4 @@ interface BridgeSessionTokenPushEnvelope {
|
|
|
205
276
|
readonly expiresAtMs: number;
|
|
206
277
|
}
|
|
207
278
|
|
|
208
|
-
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 };
|
|
279
|
+
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, UploadToStorageMeta as g, UploadToStorageResult as h };
|
|
@@ -107,6 +107,77 @@ interface McpTransport {
|
|
|
107
107
|
* completion.
|
|
108
108
|
*/
|
|
109
109
|
uploadDocument?(meta: UploadDocumentMeta, buffer: ArrayBuffer, signal?: AbortSignal): Promise<UploadDocumentResult>;
|
|
110
|
+
/**
|
|
111
|
+
* Stream a browser file into the calling app's OWN plugin storage via the host shell, and
|
|
112
|
+
* resolve the {@link UploadToStorageResult}: the plugin-relative `path` the server chose, which
|
|
113
|
+
* is exactly the string the plugin's backend `IPluginStorage` reads, deletes or links.
|
|
114
|
+
*
|
|
115
|
+
* This is the route for every browser upload that a plugin's backend will later read. Never
|
|
116
|
+
* base64 a file through a tool call: the bytes would cross the MCP JSON pipe, the tool's
|
|
117
|
+
* argument limits and the audit payload. Unlike {@link uploadDocument} (legacy CORE storage,
|
|
118
|
+
* `StoredDocument` ids), the destination is the app's own storage namespace, which the kernel
|
|
119
|
+
* derives from the capability token alone.
|
|
120
|
+
*
|
|
121
|
+
* **Optional capability**, for the same reason as {@link uploadDocument}: a transport that
|
|
122
|
+
* predates it simply omits the member, and `uploadFileToPluginStorage` fails with a typed
|
|
123
|
+
* `unsupported` error rather than a `not a function` TypeError.
|
|
124
|
+
*
|
|
125
|
+
* The bytes are handed off as an {@link ArrayBuffer} the transport moves as a transferable
|
|
126
|
+
* (zero-copy) across the worker↔host port, so the caller's buffer is detached afterwards. The
|
|
127
|
+
* worker never sees the capability token or the kernel URL: the host mints the token, POSTs the
|
|
128
|
+
* octet-stream to `extensions/storage/upload-stream` with `x-cc-storage-prefix`,
|
|
129
|
+
* `x-cc-file-name` and `x-cc-content-type`, and returns only the result.
|
|
130
|
+
*
|
|
131
|
+
* When provided, implementations MUST observe `signal`. A host failure SHOULD reject with an
|
|
132
|
+
* error carrying the HTTP `status` (see `PluginStorageUploadError`), so a plugin can tell an
|
|
133
|
+
* over-size file (413) from an exhausted quota (507).
|
|
134
|
+
*/
|
|
135
|
+
uploadToStorage?(meta: UploadToStorageMeta, buffer: ArrayBuffer, signal?: AbortSignal): Promise<UploadToStorageResult>;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Metadata accompanying an {@link McpTransport.uploadToStorage} call. There is no module key and
|
|
139
|
+
* no organisation: the kernel derives the storage namespace from the capability token, so there is
|
|
140
|
+
* nothing for the client to claim.
|
|
141
|
+
*/
|
|
142
|
+
interface UploadToStorageMeta {
|
|
143
|
+
/**
|
|
144
|
+
* Folder inside the app's own storage namespace: one or more `/`-separated segments of
|
|
145
|
+
* `[A-Za-z0-9_-]` (HR: `cvs`; Legal: `governance/{documentId}/{versionId}`), at most 84
|
|
146
|
+
* characters, with no leading or trailing `/`. A malformed prefix is refused (400), never
|
|
147
|
+
* repaired. Sent as `x-cc-storage-prefix`. The server re-validates it and is authoritative.
|
|
148
|
+
*/
|
|
149
|
+
pathPrefix: string;
|
|
150
|
+
/**
|
|
151
|
+
* Client-supplied file name: display metadata, and its extension decides the stored type. It
|
|
152
|
+
* must end in an allow-listed extension (`PLUGIN_STORAGE_UPLOAD_CONTENT_TYPES`) or the server
|
|
153
|
+
* answers 415. Sent as `x-cc-file-name`.
|
|
154
|
+
*/
|
|
155
|
+
fileName: string;
|
|
156
|
+
/**
|
|
157
|
+
* Declared MIME type. It must be the extension's canonical type or a listed alias (415
|
|
158
|
+
* otherwise); empty or `application/octet-stream` means "use the extension's type". Sent as
|
|
159
|
+
* `x-cc-content-type`.
|
|
160
|
+
*/
|
|
161
|
+
contentType: string;
|
|
162
|
+
/** Byte length of the buffer (advisory; the server reports what actually reached storage). */
|
|
163
|
+
sizeBytes: number;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Result of a successful {@link McpTransport.uploadToStorage} call.
|
|
167
|
+
*/
|
|
168
|
+
interface UploadToStorageResult {
|
|
169
|
+
/**
|
|
170
|
+
* Plugin-relative storage path, `{pathPrefix}/{guid}{ext}`, chosen by the server: exactly the
|
|
171
|
+
* string the plugin backend's `IPluginStorage` accepts. Not a URI. Record it with the app's own
|
|
172
|
+
* command.
|
|
173
|
+
*/
|
|
174
|
+
path: string;
|
|
175
|
+
/** The sanitised file name the server recorded. */
|
|
176
|
+
fileName: string;
|
|
177
|
+
/** The content type the server recorded. */
|
|
178
|
+
contentType: string;
|
|
179
|
+
/** Bytes that actually reached storage, counted by the server. */
|
|
180
|
+
sizeBytes: number;
|
|
110
181
|
}
|
|
111
182
|
/**
|
|
112
183
|
* Metadata accompanying an {@link McpTransport.uploadDocument} call. All fields
|
|
@@ -205,4 +276,4 @@ interface BridgeSessionTokenPushEnvelope {
|
|
|
205
276
|
readonly expiresAtMs: number;
|
|
206
277
|
}
|
|
207
278
|
|
|
208
|
-
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 };
|
|
279
|
+
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, UploadToStorageMeta as g, UploadToStorageResult as h };
|
package/dist/host/index.cjs
CHANGED
|
@@ -694,6 +694,9 @@ var WorkerRemoteDomTransport = class {
|
|
|
694
694
|
case "ethisys:mcp:uploadDocument":
|
|
695
695
|
this.dispatchMcp(message, "ethisys:mcp:uploadDocument:result", (m) => this.handleUploadDocument(m));
|
|
696
696
|
return;
|
|
697
|
+
case "ethisys:mcp:uploadToStorage":
|
|
698
|
+
this.dispatchMcp(message, "ethisys:mcp:uploadToStorage:result", (m) => this.handleUploadToStorage(m));
|
|
699
|
+
return;
|
|
697
700
|
case "ethisys:mcp:abort":
|
|
698
701
|
this.inFlightAborts.get(message.id ?? "")?.abort();
|
|
699
702
|
return;
|
|
@@ -931,11 +934,63 @@ var WorkerRemoteDomTransport = class {
|
|
|
931
934
|
this.replyError(message.id, "ethisys:mcp:uploadDocument:result", err);
|
|
932
935
|
}
|
|
933
936
|
}
|
|
934
|
-
|
|
937
|
+
async handleUploadToStorage(message) {
|
|
938
|
+
const resultType = "ethisys:mcp:uploadToStorage:result";
|
|
939
|
+
let token;
|
|
940
|
+
try {
|
|
941
|
+
token = await this.capabilityTokenProvider();
|
|
942
|
+
} catch (err) {
|
|
943
|
+
this.replyError(message.id, resultType, err, true);
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
if (this.disposed) {
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
try {
|
|
950
|
+
const result = await this.mcpClient.fetch({
|
|
951
|
+
kind: "uploadToStorage",
|
|
952
|
+
meta: message.meta,
|
|
953
|
+
buffer: message.buffer,
|
|
954
|
+
capabilityToken: token,
|
|
955
|
+
signal: this.requestSignal(message.id)
|
|
956
|
+
});
|
|
957
|
+
this.reportVersionSkew(result);
|
|
958
|
+
this.safePostMessage({
|
|
959
|
+
id: message.id,
|
|
960
|
+
type: resultType,
|
|
961
|
+
ok: result.ok,
|
|
962
|
+
data: result.data,
|
|
963
|
+
error: result.error,
|
|
964
|
+
code: classifyHostResponse(result),
|
|
965
|
+
// The HTTP status, on failure only. A bare number carries no host internals, and it
|
|
966
|
+
// is the only way the plugin can tell 413 (file too large) from 507 (quota) from 503
|
|
967
|
+
// (storage lock busy): the closed MCP code folds all three into `internal`/`unavailable`.
|
|
968
|
+
...!result.ok && typeof result.status === "number" ? { status: result.status } : {}
|
|
969
|
+
});
|
|
970
|
+
} catch (err) {
|
|
971
|
+
this.replyError(message.id, resultType, err, true);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
replyError(id, type, err, includeStatus = false) {
|
|
935
975
|
const message = err instanceof Error ? err.message : "MCP request failed";
|
|
936
|
-
|
|
976
|
+
const status = includeStatus ? statusOf(err) : void 0;
|
|
977
|
+
this.safePostMessage({
|
|
978
|
+
id,
|
|
979
|
+
type,
|
|
980
|
+
ok: false,
|
|
981
|
+
error: message,
|
|
982
|
+
code: classifyHostError(err),
|
|
983
|
+
...status !== void 0 ? { status } : {}
|
|
984
|
+
});
|
|
937
985
|
}
|
|
938
986
|
};
|
|
987
|
+
function statusOf(err) {
|
|
988
|
+
if (err === null || typeof err !== "object") {
|
|
989
|
+
return void 0;
|
|
990
|
+
}
|
|
991
|
+
const candidate = err;
|
|
992
|
+
return typeof candidate.status === "number" ? candidate.status : typeof candidate.statusCode === "number" ? candidate.statusCode : void 0;
|
|
993
|
+
}
|
|
939
994
|
var IFRAME_BRIDGE_PROTOCOL = "ethisys.iframe.bridge.v1";
|
|
940
995
|
var InboundEnvelope = zod.z.discriminatedUnion("type", [
|
|
941
996
|
zod.z.object({ type: zod.z.literal("ethisys:mcp:invokeTool"), id: zod.z.string().min(1), name: zod.z.string().min(1), args: zod.z.unknown(), nonce: zod.z.string() }),
|