返回 CodeWhale
rpc.mjs
根目录 / crates / tui / extension-host / dist / rpc.mjs
1 // generated by crates/tui/extension-host/build.mjs from src/rpc.ts; do not edit.
2
3 // src/protocol.generated.ts
4 var MAGIC_ASCII = "CWX1";
5 var MAX_INFLIGHT = 256;
6 var ErrorCode = {
7 ParseError: -32700,
8 InvalidRequest: -32600,
9 MethodNotFound: -32601,
10 InvalidParams: -32602,
11 Internal: -32603,
12 ExecutionFailed: -32e3,
13 NotAvailable: -32001,
14 Refused: -32002,
15 Denied: -32003,
16 Cancelled: -32800
17 };
18 var METHODS = [
19 { name: "host/initialize", direction: "core_to_host", request: true, params: "InitializeParams", tiers: ["plugin", "builtin"] },
20 { name: "host/ping", direction: "core_to_host", request: true, params: "EmptyParams", tiers: ["plugin", "builtin"] },
21 { name: "host/shutdown", direction: "core_to_host", request: true, params: "EmptyParams", tiers: ["plugin", "builtin"] },
22 { name: "ext/activate", direction: "core_to_host", request: true, params: "ActivateParams", tiers: ["plugin", "builtin"] },
23 { name: "ext/deactivate", direction: "core_to_host", request: true, params: "DeactivateParams", tiers: ["plugin", "builtin"] },
24 { name: "tool/call", direction: "core_to_host", request: true, params: "ToolCallParams", tiers: ["plugin", "builtin"] },
25 { name: "command/run", direction: "core_to_host", request: true, params: "CommandRunParams", tiers: ["plugin", "builtin"] },
26 { name: "hook/evaluate", direction: "core_to_host", request: true, params: "HookEvaluateParams", tiers: ["plugin", "builtin"] },
27 { name: "mcp/open", direction: "core_to_host", request: true, params: "McpOpenParams", tiers: ["builtin"] },
28 { name: "mcp/request", direction: "core_to_host", request: true, params: "McpRequestParams", tiers: ["builtin"] },
29 { name: "mcp/close", direction: "core_to_host", request: true, params: "McpCloseParams", tiers: ["builtin"] },
30 { name: "harness/run", direction: "core_to_host", request: true, params: "HarnessRunParams", tiers: ["builtin"] },
31 { name: "$/cancel", direction: "core_to_host", request: false, params: "CancelParams", tiers: ["plugin", "builtin"] },
32 { name: "host/hello", direction: "host_to_core", request: false, params: "HelloParams", tiers: ["plugin", "builtin"] },
33 { name: "host/ready", direction: "host_to_core", request: false, params: "EmptyParams", tiers: ["plugin", "builtin"] },
34 { name: "registry/register", direction: "host_to_core", request: true, params: "RegisterParams", tiers: ["plugin", "builtin"] },
35 { name: "registry/unregister", direction: "host_to_core", request: true, params: "UnregisterParams", tiers: ["plugin", "builtin"] },
36 { name: "core/call", direction: "host_to_core", request: true, params: "CoreCallParams", tiers: ["plugin", "builtin"] },
37 { name: "proc/launch", direction: "host_to_core", request: true, params: "ProcLaunchParams", tiers: ["builtin"] },
38 { name: "proc/read", direction: "host_to_core", request: true, params: "ProcSessionParams", tiers: ["builtin"] },
39 { name: "proc/write", direction: "host_to_core", request: true, params: "ProcWriteParams", tiers: ["builtin"] },
40 { name: "proc/close", direction: "host_to_core", request: true, params: "ProcSessionParams", tiers: ["builtin"] },
41 { name: "net/start", direction: "host_to_core", request: true, params: "ProcLaunchParams", tiers: ["builtin"] },
42 { name: "net/fetch", direction: "host_to_core", request: true, params: "NetFetchParams", tiers: ["builtin"] },
43 { name: "net/read", direction: "host_to_core", request: true, params: "NetReadParams", tiers: ["builtin"] },
44 { name: "net/release", direction: "host_to_core", request: true, params: "NetReadParams", tiers: ["builtin"] },
45 { name: "net/close", direction: "host_to_core", request: true, params: "ProcSessionParams", tiers: ["builtin"] },
46 { name: "exec/redeem", direction: "host_to_core", request: true, params: "ExecutionRedeemParams", tiers: ["builtin"] },
47 { name: "ext/faulted", direction: "host_to_core", request: false, params: "FaultedParams", tiers: ["plugin", "builtin"] },
48 { name: "log", direction: "host_to_core", request: false, params: "LogParams", tiers: ["plugin", "builtin"] },
49 { name: "$/cancel", direction: "host_to_core", request: false, params: "CancelParams", tiers: ["plugin", "builtin"] }
50 ];
51 var SHAPES = {
52 ActivateParams: {
53 strict: false,
54 required: { owner: { ref: "OwnerRef" }, plugin_name: "string", entry: { ref: "EntryRef" } },
55 optional: { scope: { ref: "EntryRef" }, config: "json", data_dir: "string" }
56 },
57 CancelParams: {
58 strict: true,
59 required: { id: "uint" },
60 optional: {}
61 },
62 CommandRunParams: {
63 strict: false,
64 required: { handle: "uint", command_id: "string", raw_input: "string", deadline_ms: "uint" },
65 optional: { workspace: "string", session_id: "string", agent_id: "string", origin_turn_id: "string" }
66 },
67 CoreCallParams: {
68 strict: true,
69 required: { owner: { ref: "OwnerRef" }, ticket: "string", name: "string", input: "json" },
70 optional: {}
71 },
72 DeactivateParams: {
73 strict: false,
74 required: { owner: { ref: "OwnerRef" } },
75 optional: { entry: { ref: "EntryRef" } }
76 },
77 EmptyParams: {
78 strict: true,
79 required: {},
80 optional: {}
81 },
82 EntryRef: {
83 strict: false,
84 required: { path: "string", sha256: "string" },
85 optional: {}
86 },
87 ExecutionRedeemParams: {
88 strict: true,
89 required: { owner: { ref: "OwnerRef" }, execution_id: "string", ticket: "string" },
90 optional: {}
91 },
92 FaultedParams: {
93 strict: true,
94 required: { owner: { ref: "OwnerRef" }, error: "string" },
95 optional: {}
96 },
97 HarnessRunParams: {
98 strict: true,
99 required: { owner: { ref: "OwnerRef" }, execution_id: "string", ticket: "string", deadline_ms: "uint" },
100 optional: { hook: { ref: "HookDispatchWire" } }
101 },
102 HelloParams: {
103 strict: true,
104 required: { protocol: { ref: "ProtocolRange" }, host_version: "string", bundle_sha256: "string", runtime: { ref: "HelloRuntime" }, tier: { enum: ["builtin", "plugin"] }, builtin_modules: { items: { ref: "ModuleDigestWire" } } },
105 optional: { memory_limit_mib: "uint" }
106 },
107 HelloRuntime: {
108 strict: true,
109 required: { name: "string", version: "string" },
110 optional: {}
111 },
112 HookCallPayload: {
113 strict: true,
114 required: { name: "string", call_id: "string", input: "json", mode: "string", workspace: "string", model: "string" },
115 optional: {}
116 },
117 HookDispatchWire: {
118 strict: true,
119 required: { event: "string", dialect: "string", point: "string", query: "string" },
120 optional: { matcher: "string" }
121 },
122 HookEvaluateParams: {
123 strict: true,
124 required: { handle: "uint", event: "string", payload: { ref: "HookCallPayload" }, deadline_ms: "uint" },
125 optional: {}
126 },
127 HostLimits: {
128 strict: false,
129 required: { max_frame: "uint", max_inflight: "uint", dispose_deadline_ms: "uint", activate_deadline_ms: "uint" },
130 optional: {}
131 },
132 InitializeParams: {
133 strict: false,
134 required: { protocol: "uint", limits: { ref: "HostLimits" } },
135 optional: {}
136 },
137 LogParams: {
138 strict: true,
139 required: { level: "string", msg: "string" },
140 optional: { plugin_id: "string" }
141 },
142 McpCloseParams: {
143 strict: true,
144 required: { owner: { ref: "OwnerRef" }, session_id: "string", deadline_ms: "uint" },
145 optional: {}
146 },
147 McpHttpHeaders: {
148 strict: true,
149 required: {},
150 optional: { accept: "string", content_type: "string", mcp_session_id: "string", mcp_protocol_version: "string" }
151 },
152 McpOpenParams: {
153 strict: true,
154 required: { owner: { ref: "OwnerRef" }, session_id: "string", launch_ticket: "string", transport: "string", initialize_grant: { ref: "McpOperationGrant" }, initialized_grant: { ref: "McpOperationGrant" }, client_version: "string", deadline_ms: "uint" },
155 optional: {}
156 },
157 McpOperationGrant: {
158 strict: true,
159 required: { ticket: "string", operation_id: "string", method: "string", params: "json" },
160 optional: { wire_id: "string" }
161 },
162 McpRequestParams: {
163 strict: true,
164 required: { owner: { ref: "OwnerRef" }, session_id: "string", grant: { ref: "McpOperationGrant" }, deadline_ms: "uint" },
165 optional: {}
166 },
167 ModuleDigestWire: {
168 strict: true,
169 required: { id: "string", sha256: "string" },
170 optional: {}
171 },
172 NetFetchParams: {
173 strict: true,
174 required: { owner: { ref: "OwnerRef" }, session_id: "string", url: "string", method: "string", headers: { ref: "McpHttpHeaders" } },
175 optional: { frame: "json", ticket: "string", operation_id: "string" }
176 },
177 NetReadParams: {
178 strict: true,
179 required: { owner: { ref: "OwnerRef" }, session_id: "string", response_id: "string" },
180 optional: {}
181 },
182 OwnerRef: {
183 strict: true,
184 required: { plugin_id: "string", generation: "uint", owner_token: "string" },
185 optional: {}
186 },
187 ProcLaunchParams: {
188 strict: true,
189 required: { owner: { ref: "OwnerRef" }, session_id: "string", ticket: "string" },
190 optional: {}
191 },
192 ProcSessionParams: {
193 strict: true,
194 required: { owner: { ref: "OwnerRef" }, session_id: "string" },
195 optional: {}
196 },
197 ProcWriteParams: {
198 strict: true,
199 required: { owner: { ref: "OwnerRef" }, session_id: "string", frame: "json" },
200 optional: { ticket: "string", operation_id: "string" }
201 },
202 ProtocolRange: {
203 strict: true,
204 required: { min: "uint", max: "uint" },
205 optional: {}
206 },
207 RegisterParams: {
208 strict: true,
209 required: { owner: { ref: "OwnerRef" }, kind: { enum: ["tool", "command", "hook", "prompt_section", "prompt_template", "skill_root", "shell_hook", "mcp_server"] }, spec: { ref: "RegisterSpecWire" } },
210 optional: { scope: { ref: "EntryRef" } }
211 },
212 RegisterSpecWire: {
213 strict: true,
214 required: { name: "string", description: "string" },
215 optional: { input_schema: "object", argument_hint: "string" }
216 },
217 RpcErrorWire: {
218 strict: true,
219 required: { code: "integer", message: "string" },
220 optional: { data: "json" }
221 },
222 ToolCallParams: {
223 strict: false,
224 required: { handle: "uint", call_id: "string", input: "json", deadline_ms: "uint" },
225 optional: { workspace: "string", ticket: "string", session_id: "string", agent_id: "string", origin_turn_id: "string" }
226 },
227 UnregisterParams: {
228 strict: true,
229 required: { owner: { ref: "OwnerRef" }, handle: "uint" },
230 optional: {}
231 }
232 };
233
234 // src/protocol.ts
235 var MAGIC = Buffer.from(MAGIC_ASCII, "ascii");
236 var ProtocolError = class extends Error {
237 constructor(message) {
238 super(message);
239 this.name = "ProtocolError";
240 }
241 };
242 function isObject(value) {
243 return typeof value === "object" && value !== null && !Array.isArray(value);
244 }
245 function checkShape(where, value, shape) {
246 if (!isObject(value)) throw new ProtocolError(`${where}: expected an object`);
247 for (const [key, kind] of Object.entries(shape.required)) {
248 if (!(key in value)) throw new ProtocolError(`${where}: missing field \`${key}\``);
249 checkKind(`${where}.${key}`, value[key], kind);
250 }
251 for (const [key, kind] of Object.entries(shape.optional)) {
252 if (value[key] !== void 0 && value[key] !== null) checkKind(`${where}.${key}`, value[key], kind);
253 }
254 if (shape.strict) {
255 for (const key of Object.keys(value)) {
256 if (!(key in shape.required) && !(key in shape.optional)) {
257 throw new ProtocolError(`${where}: unknown field \`${key}\``);
258 }
259 }
260 }
261 }
262 function checkKind(where, value, kind) {
263 if (typeof kind === "object") {
264 if ("ref" in kind) return checkShape(where, value, SHAPES[kind.ref]);
265 if ("enum" in kind) {
266 if (typeof value !== "string" || !kind.enum.includes(value)) {
267 throw new ProtocolError(`${where}: expected one of ${kind.enum.map((v) => `\`${v}\``).join(", ")}`);
268 }
269 return;
270 }
271 if (!Array.isArray(value)) throw new ProtocolError(`${where}: expected an array`);
272 value.forEach((item, index) => checkKind(`${where}[${index}]`, item, kind.items));
273 return;
274 }
275 switch (kind) {
276 case "string":
277 if (typeof value !== "string") throw new ProtocolError(`${where}: expected a string`);
278 return;
279 case "uint":
280 if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
281 throw new ProtocolError(`${where}: expected an unsigned integer`);
282 }
283 return;
284 case "integer":
285 if (typeof value !== "number" || !Number.isSafeInteger(value)) throw new ProtocolError(`${where}: expected an integer`);
286 return;
287 case "boolean":
288 if (typeof value !== "boolean") throw new ProtocolError(`${where}: expected a boolean`);
289 return;
290 case "object":
291 if (!isObject(value)) throw new ProtocolError(`${where}: expected an object`);
292 return;
293 case "json":
294 return;
295 }
296 }
297 function validateMessage(value, direction, tier, methods = METHODS) {
298 const strict = direction === "host_to_core";
299 if (!isObject(value)) throw new ProtocolError("message: expected an object");
300 if (value.jsonrpc !== "2.0") throw new ProtocolError('message: jsonrpc must be "2.0"');
301 const hasId = "id" in value;
302 if (hasId) checkKind("message.id", value.id, "uint");
303 if ("method" in value) {
304 checkShape("message", value, { strict, required: { jsonrpc: "string", method: "string" }, optional: { id: "uint", params: "json" } });
305 const method = value.method;
306 const spec = methods.find((entry) => entry.name === method && entry.direction === direction);
307 if (!spec) throw new ProtocolError(`unknown ${direction} method \`${method}\``);
308 if (!spec.tiers.includes(tier)) throw new ProtocolError(`\`${method}\` is not allowed on the ${tier} tier`);
309 if (spec.request !== hasId) {
310 throw new ProtocolError(`\`${method}\` must be ${spec.request ? "a request (with id)" : "a notification (no id)"}`);
311 }
312 const params = "params" in value ? value.params : {};
313 checkShape(method, params, SHAPES[spec.params]);
314 if (method === "host/hello" && params.runtime.name !== "bun" && params.runtime.name !== "node") {
315 throw new ProtocolError(`host/hello.runtime.name: unknown runtime \`${params.runtime.name}\``);
316 }
317 if (method === "registry/register") {
318 const { kind, spec: spec2 } = params;
319 const reason = kind === "tool" && spec2.input_schema == null ? "a tool registration needs `spec.input_schema`" : kind === "tool" && spec2.argument_hint != null ? "a tool registration has no `spec.argument_hint`" : kind === "command" && spec2.input_schema != null ? "a command registration has no `spec.input_schema`" : (kind === "hook" || kind === "prompt_section" || kind === "prompt_template" || kind === "skill_root" || kind === "shell_hook" || kind === "mcp_server") && (spec2.input_schema != null || spec2.argument_hint != null) ? "a hook, prompt or skill root registration has no input schema or argument hint" : void 0;
320 if (reason !== void 0) throw new ProtocolError(`${method}: ${reason}`);
321 }
322 return value;
323 }
324 if (!hasId) throw new ProtocolError("response: missing id");
325 if ("error" in value) {
326 checkShape("message", value, { strict, required: { jsonrpc: "string", id: "uint", error: { ref: "RpcErrorWire" } }, optional: {} });
327 return value;
328 }
329 if (!("result" in value)) throw new ProtocolError("response: needs result or error");
330 checkShape("message", value, { strict, required: { jsonrpc: "string", id: "uint", result: "json" }, optional: {} });
331 return value;
332 }
333
334 // src/rpc.ts
335 var RpcError = class extends Error {
336 constructor(code, message, data) {
337 super(message);
338 this.code = code;
339 this.data = data;
340 this.name = "RpcError";
341 }
342 code;
343 data;
344 toWire() {
345 return this.data === void 0 ? { code: this.code, message: this.message } : { code: this.code, message: this.message, data: this.data };
346 }
347 };
348 var RpcPeer = class {
349 constructor(send, tier) {
350 this.send = send;
351 this.tier = tier;
352 }
353 send;
354 tier;
355 nextId = 1;
356 pending = /* @__PURE__ */ new Map();
357 inbound = /* @__PURE__ */ new Map();
358 requestHandlers = /* @__PURE__ */ new Map();
359 notificationHandlers = /* @__PURE__ */ new Map();
360 closed = false;
361 onRequest(method, handler) {
362 this.requestHandlers.set(method, handler);
363 }
364 onNotification(method, handler) {
365 this.notificationHandlers.set(method, handler);
366 }
367 /**
368 * Send a host→core request. Outbound messages are validated strictly first.
369 * When `signal` aborts before the answer, the core is sent `$/cancel` for it
370 * and the promise rejects as cancelled at once; an answer that still arrives
371 * is dropped (the core drops its own after a cancel as well).
372 */
373 request(method, params, signal) {
374 if (this.closed) return Promise.reject(new RpcError(ErrorCode.NotAvailable, "channel closed"));
375 if (signal?.aborted) return Promise.reject(new RpcError(ErrorCode.Cancelled, "cancelled"));
376 if (this.pending.size >= MAX_INFLIGHT) {
377 return Promise.reject(new RpcError(ErrorCode.Internal, `more than ${MAX_INFLIGHT} requests in flight`));
378 }
379 const id = this.nextId++;
380 const message = { jsonrpc: "2.0", id, method, params };
381 validateMessage(message, "host_to_core", this.tier);
382 return new Promise((resolve, reject) => {
383 const onAbort = () => {
384 if (!this.pending.delete(id)) return;
385 this.notify("$/cancel", { id });
386 reject(new RpcError(ErrorCode.Cancelled, "cancelled"));
387 };
388 this.pending.set(id, {
389 resolve: (value) => {
390 signal?.removeEventListener("abort", onAbort);
391 resolve(value);
392 },
393 reject: (error) => {
394 signal?.removeEventListener("abort", onAbort);
395 reject(error);
396 }
397 });
398 signal?.addEventListener("abort", onAbort, { once: true });
399 this.send(message);
400 });
401 }
402 notify(method, params) {
403 if (this.closed) return;
404 const message = { jsonrpc: "2.0", method, params };
405 validateMessage(message, "host_to_core", this.tier);
406 this.send(message);
407 }
408 /** Dispatch one decoded core→host message. Throws `ProtocolError` for malformed input. */
409 handle(raw) {
410 const message = validateMessage(raw, "core_to_host", this.tier);
411 if ("method" in message) {
412 if (message.method === "$/cancel") {
413 this.inbound.get(message.params.id)?.abort();
414 return;
415 }
416 if ("id" in message) {
417 void this.dispatchRequest(message.id, message.method, message.params ?? {});
418 return;
419 }
420 this.notificationHandlers.get(message.method)?.(message.params ?? {});
421 return;
422 }
423 const waiter = this.pending.get(message.id);
424 if (!waiter) return;
425 this.pending.delete(message.id);
426 if ("error" in message) {
427 waiter.reject(new RpcError(message.error.code, message.error.message, message.error.data));
428 } else {
429 waiter.resolve(message.result);
430 }
431 }
432 /** Fail every outbound request; used when the channel closes. */
433 close(reason) {
434 this.closed = true;
435 for (const [, waiter] of this.pending) waiter.reject(new RpcError(ErrorCode.NotAvailable, reason));
436 this.pending.clear();
437 for (const [, controller] of this.inbound) controller.abort();
438 }
439 async dispatchRequest(id, method, params) {
440 const handler = this.requestHandlers.get(method);
441 if (!handler) {
442 this.reply(id, void 0, new RpcError(ErrorCode.MethodNotFound, `no handler for \`${method}\``));
443 return;
444 }
445 const controller = new AbortController();
446 this.inbound.set(id, controller);
447 try {
448 const result = await handler(params, { id, signal: controller.signal });
449 this.reply(id, result ?? {});
450 } catch (error) {
451 this.reply(id, void 0, toRpcError(error, controller.signal));
452 } finally {
453 this.inbound.delete(id);
454 }
455 }
456 reply(id, result, error) {
457 if (this.closed) return;
458 this.send(error ? { jsonrpc: "2.0", id, error: error.toWire() } : { jsonrpc: "2.0", id, result });
459 }
460 };
461 function toRpcError(error, signal) {
462 if (error instanceof RpcError) return error;
463 if (signal?.aborted) return new RpcError(ErrorCode.Cancelled, "cancelled");
464 const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
465 return new RpcError(ErrorCode.ExecutionFailed, message.slice(0, 4096));
466 }
467 export {
468 RpcError,
469 RpcPeer,
470 toRpcError
471 };
472
472 lines Plain Text