@cjhyy/code-shell-server 0.9.42 → 0.9.44
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.
|
@@ -80,7 +80,7 @@ export declare const REMOTE_LINK_ADAPTERS: ({
|
|
|
80
80
|
id: "figma";
|
|
81
81
|
name: "Figma";
|
|
82
82
|
group: "files";
|
|
83
|
-
actions: readonly ["get_file", "get_comments"];
|
|
83
|
+
actions: readonly ["get_file", "get_comments", "get_nodes", "get_images", "get_image_fills"];
|
|
84
84
|
})[];
|
|
85
85
|
export type RemoteProviderId = RemoteLinkProviderId;
|
|
86
86
|
export interface RemoteProviderCapabilities {
|
package/dist/links/service.js
CHANGED
|
@@ -1042,7 +1042,6 @@ export function createLinkService(options = {}) {
|
|
|
1042
1042
|
!REMOTE_LINK_ADAPTERS.some((adapter) => adapter.id === input.providerId) ||
|
|
1043
1043
|
(input.resourceUrl !== undefined &&
|
|
1044
1044
|
(input.providerId !== "figma" ||
|
|
1045
|
-
!input.connectionId ||
|
|
1046
1045
|
typeof input.resourceUrl !== "string" ||
|
|
1047
1046
|
!input.resourceUrl)))
|
|
1048
1047
|
throw new LinkServiceError(400, "invalid_request");
|
|
@@ -1070,13 +1069,12 @@ export function createLinkService(options = {}) {
|
|
|
1070
1069
|
throw new LinkServiceError(409, "conflict");
|
|
1071
1070
|
}
|
|
1072
1071
|
const prepared = { id, expected, input: { ...input, label: string(input.label) } };
|
|
1073
|
-
if (input.resourceUrl !== undefined && !expected)
|
|
1074
|
-
throw new LinkServiceError(400, "invalid_request");
|
|
1075
1072
|
let attempt;
|
|
1076
1073
|
try {
|
|
1077
1074
|
let resourceFiles;
|
|
1078
1075
|
if (input.providerId === "figma") {
|
|
1079
|
-
//
|
|
1076
|
+
// A first connection may select the pasted file in the same OAuth consent.
|
|
1077
|
+
// Existing files still come exclusively from Host custody; input suggests one new file.
|
|
1080
1078
|
const groups = expected?.meta?.linkResourceGroups;
|
|
1081
1079
|
if (expected && (!groups || groups.length !== 1 || groups[0]?.id !== "files"))
|
|
1082
1080
|
throw new Error("Invalid saved Figma resources");
|
|
@@ -1179,6 +1177,21 @@ export function createLinkService(options = {}) {
|
|
|
1179
1177
|
throw new LinkServiceError(409, "conflict");
|
|
1180
1178
|
if (matches.length === 1) {
|
|
1181
1179
|
const expected = matches[0];
|
|
1180
|
+
if (credential.meta?.linkProvider === "figma") {
|
|
1181
|
+
const oldGroups = expected.meta?.linkResourceGroups;
|
|
1182
|
+
const newGroups = credential.meta?.linkResourceGroups;
|
|
1183
|
+
// A concurrent first connection never reviewed this saved grant.
|
|
1184
|
+
// Reuse only when the newly minted authority already covers it;
|
|
1185
|
+
// a local union cannot expand the remote token's file whitelist.
|
|
1186
|
+
if (expected.meta?.linkAccountId !== credential.meta?.linkAccountId ||
|
|
1187
|
+
oldGroups?.length !== 1 ||
|
|
1188
|
+
oldGroups[0]?.id !== "files" ||
|
|
1189
|
+
newGroups?.length !== 1 ||
|
|
1190
|
+
newGroups[0]?.id !== "files" ||
|
|
1191
|
+
oldGroups[0].items.some((item) => !newGroups[0].items.some((next) => next.id === item.id)) ||
|
|
1192
|
+
expected.meta?.linkCapabilityIds?.some((id) => !credential.meta?.linkCapabilityIds?.includes(id)))
|
|
1193
|
+
throw new LinkServiceError(409, "conflict");
|
|
1194
|
+
}
|
|
1182
1195
|
prepared = { ...prepared, id: expected.id, expected };
|
|
1183
1196
|
credential = { ...credential, id: expected.id, label: expected.label };
|
|
1184
1197
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { HostRuntime, interceptHostRequest, unsupportedAskUserDecision, } from "@cjhyy/code-shell-host";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { createRequire } from "node:module";
|
|
3
4
|
import { isAbsolute } from "node:path";
|
|
@@ -59,6 +60,10 @@ function progressFromEvent(event) {
|
|
|
59
60
|
}
|
|
60
61
|
/** Real independent Core workers shared by Desktop Web and standalone Hub. */
|
|
61
62
|
export function createPanelAgentTaskHost(options = {}) {
|
|
63
|
+
const hostRuntime = new HostRuntime({ kind: "headless", label: "Panel agent task" }, [
|
|
64
|
+
{ id: "panels", capabilities: options.onPanelAction ? ["panel"] : [] },
|
|
65
|
+
]);
|
|
66
|
+
hostRuntime.start();
|
|
62
67
|
const instances = new Map();
|
|
63
68
|
let closed = false;
|
|
64
69
|
const models = options.models ??
|
|
@@ -144,30 +149,36 @@ export function createPanelAgentTaskHost(options = {}) {
|
|
|
144
149
|
...(typeof params.connectionId === "string" ? { connectionId: params.connectionId } : {}),
|
|
145
150
|
...(Number.isInteger(params.generation) ? { generation: params.generation } : {}),
|
|
146
151
|
};
|
|
147
|
-
if (request.toolName
|
|
152
|
+
if (request.toolName === "__ask_user__") {
|
|
153
|
+
// The Panel runtime only has a yes/no confirmation UI. It cannot answer
|
|
154
|
+
// a question; Core retires async questions without injecting a user answer.
|
|
155
|
+
outcomeResult(await rpc(running, "agent/approve", {
|
|
156
|
+
sessionId: pending.sessionId,
|
|
157
|
+
requestId: pending.id,
|
|
158
|
+
connectionId: pending.connectionId,
|
|
159
|
+
generation: pending.generation,
|
|
160
|
+
decision: unsupportedAskUserDecision(hostRuntime.adapter.label),
|
|
161
|
+
}));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (request.toolName === "__panel_action__" && options.onPanelAction) {
|
|
148
165
|
let decision;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
decision = { approved: true, answer };
|
|
157
|
-
}
|
|
158
|
-
catch (error) {
|
|
159
|
-
decision = {
|
|
160
|
-
approved: false,
|
|
161
|
-
failure: "unavailable",
|
|
162
|
-
reason: String(error).slice(0, 500),
|
|
163
|
-
};
|
|
164
|
-
}
|
|
166
|
+
try {
|
|
167
|
+
const result = await options.onPanelAction(instance.scope, request.args ?? {});
|
|
168
|
+
await assertAuthorized(instance);
|
|
169
|
+
const answer = JSON.stringify(result);
|
|
170
|
+
if (!answer || Buffer.byteLength(answer) > 512 * 1024)
|
|
171
|
+
throw new Error("Panel callback returned an invalid or oversized result");
|
|
172
|
+
decision = { approved: true, answer };
|
|
165
173
|
}
|
|
166
|
-
|
|
174
|
+
catch (error) {
|
|
167
175
|
decision = {
|
|
168
|
-
approved:
|
|
169
|
-
|
|
170
|
-
|
|
176
|
+
approved: true,
|
|
177
|
+
answer: JSON.stringify({
|
|
178
|
+
ok: false,
|
|
179
|
+
failure: "unavailable",
|
|
180
|
+
detail: String(error).slice(0, 500),
|
|
181
|
+
}),
|
|
171
182
|
};
|
|
172
183
|
}
|
|
173
184
|
await assertAuthorized(instance);
|
|
@@ -258,15 +269,19 @@ export function createPanelAgentTaskHost(options = {}) {
|
|
|
258
269
|
entryPath,
|
|
259
270
|
execPath: options.execPath,
|
|
260
271
|
fallbackCwd: () => scope.cwd,
|
|
261
|
-
buildEnv:
|
|
262
|
-
(()
|
|
272
|
+
buildEnv: () => ({
|
|
273
|
+
...(options.buildEnv?.() ?? {
|
|
263
274
|
...process.env,
|
|
264
|
-
// These switch on Desktop-main-only IPC clients in the worker.
|
|
265
|
-
CODE_SHELL_OPERATION_BATCH_ACCESS: undefined,
|
|
266
|
-
CODE_SHELL_MODEL_REQUEST_SIGNING: undefined,
|
|
267
|
-
CODE_SHELL_CREDENTIAL_ACCESS: "local",
|
|
268
275
|
CODE_SHELL_CAPABILITY_MODULES: `${import.meta.resolve("@cjhyy/code-shell-capability-coding")}#createCodingModule`,
|
|
269
|
-
})
|
|
276
|
+
}),
|
|
277
|
+
// A task worker has no Desktop main bridge, even inside Desktop Web.
|
|
278
|
+
CODE_SHELL_REMOTE_LINK_CLIENT_SECRET: undefined,
|
|
279
|
+
CODE_SHELL_OPERATION_BATCH_ACCESS: undefined,
|
|
280
|
+
CODE_SHELL_MODEL_REQUEST_SIGNING: undefined,
|
|
281
|
+
CODE_SHELL_WORKSPACE_ACCESS: undefined,
|
|
282
|
+
CODE_SHELL_HOST_MCP_SERVERS: undefined,
|
|
283
|
+
CODE_SHELL_CREDENTIAL_ACCESS: "local",
|
|
284
|
+
}),
|
|
270
285
|
onWorkerStarted: ({ pid }) => {
|
|
271
286
|
running.pid = pid;
|
|
272
287
|
},
|
|
@@ -281,13 +296,27 @@ export function createPanelAgentTaskHost(options = {}) {
|
|
|
281
296
|
catch {
|
|
282
297
|
return;
|
|
283
298
|
}
|
|
299
|
+
if (interceptHostRequest(message, line, {
|
|
300
|
+
runtime: hostRuntime,
|
|
301
|
+
sendLine: (reply) => running.bridge.sendLine(reply),
|
|
302
|
+
approve: (params) => {
|
|
303
|
+
if (params.sessionId !== running.sessionId)
|
|
304
|
+
return;
|
|
305
|
+
void assertAuthorized(instance)
|
|
306
|
+
.then(() => rpc(running, "agent/approve", params))
|
|
307
|
+
.then(outcomeResult)
|
|
308
|
+
.catch(() => stopWorker(running));
|
|
309
|
+
},
|
|
310
|
+
}))
|
|
311
|
+
return;
|
|
284
312
|
if (message.params?.sessionId !== input.sessionId)
|
|
285
313
|
return;
|
|
286
314
|
if (message.method === "agent/approvalRequest") {
|
|
287
315
|
void handleWorkerApproval(instance, running, message.params).catch(() => stopWorker(running));
|
|
288
316
|
}
|
|
289
317
|
else if (message.method === "agent/approvalResolved") {
|
|
290
|
-
instance.approvals.delete(message.params.requestId)
|
|
318
|
+
if (!instance.approvals.delete(message.params.requestId))
|
|
319
|
+
return;
|
|
291
320
|
void deliver(instance, "agent.task.approvalResolved", {
|
|
292
321
|
taskId: input.taskId,
|
|
293
322
|
requestId: message.params.requestId,
|
|
@@ -435,7 +464,10 @@ export function createPanelAgentTaskHost(options = {}) {
|
|
|
435
464
|
connectionId: pending.connectionId,
|
|
436
465
|
generation: pending.generation,
|
|
437
466
|
decision: input.approved
|
|
438
|
-
? {
|
|
467
|
+
? {
|
|
468
|
+
approved: true,
|
|
469
|
+
scope: "once",
|
|
470
|
+
}
|
|
439
471
|
: { approved: false, reason: "Declined by user" },
|
|
440
472
|
}));
|
|
441
473
|
instance.approvals.delete(pending.id);
|
|
@@ -454,6 +486,7 @@ export function createPanelAgentTaskHost(options = {}) {
|
|
|
454
486
|
for (const id of instances.keys())
|
|
455
487
|
revokeInstance(id);
|
|
456
488
|
closed = true;
|
|
489
|
+
hostRuntime.stop();
|
|
457
490
|
},
|
|
458
491
|
activeTaskCount,
|
|
459
492
|
};
|
|
@@ -42,7 +42,7 @@ import { createHubSessions, hubSessionPreview, readHubSessionState, readHubTrans
|
|
|
42
42
|
import { createHubFiles } from "../hub/files.js";
|
|
43
43
|
import { createSessionHttpResponses } from "../session-http-responses.js";
|
|
44
44
|
import { WorkerBridgeCore, previewLine } from "../worker-bridge-core.js";
|
|
45
|
-
import {
|
|
45
|
+
import { HostRuntime, interceptHostRequest } from "@cjhyy/code-shell-host";
|
|
46
46
|
export async function startHeadlessServer(opts) {
|
|
47
47
|
const remoteLink = remoteLinkHostConfiguration(opts.remoteLink, opts.publicOrigin);
|
|
48
48
|
if (remoteLink && opts.authMode !== "hub")
|
|
@@ -227,48 +227,26 @@ export async function startHeadlessServer(opts) {
|
|
|
227
227
|
}
|
|
228
228
|
}, pendingResponseReaperMs);
|
|
229
229
|
pendingResponseReaper.unref?.();
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
log("worker.loopback_unsupported", { capability });
|
|
251
|
-
void bridge
|
|
252
|
-
.request("agent/approve", {
|
|
253
|
-
sessionId: approval.sessionId,
|
|
254
|
-
requestId: approval.requestId,
|
|
255
|
-
connectionId: approval.connectionId,
|
|
256
|
-
generation: approval.generation,
|
|
257
|
-
decision: { approved: true, answer: unsupportedLoopbackAnswer(capability) },
|
|
258
|
-
}, {
|
|
259
|
-
id: `unsupported-reply-${randomUUID()}`,
|
|
260
|
-
consume: true,
|
|
261
|
-
settleOnExit: true,
|
|
262
|
-
failFast: true,
|
|
263
|
-
timeoutMs: 5000,
|
|
264
|
-
meta: { origin: "host", producer: "serve-unsupported-loopback" },
|
|
265
|
-
})
|
|
266
|
-
.catch((e) => log("worker.loopback_unsupported_failed", {
|
|
267
|
-
capability,
|
|
268
|
-
reason: e instanceof Error ? e.message : "error",
|
|
269
|
-
}));
|
|
270
|
-
return true;
|
|
271
|
-
};
|
|
230
|
+
const hostRuntime = new HostRuntime({ kind: "headless", label: "headless serve" }, [
|
|
231
|
+
{ id: "panels", capabilities: hubAuth ? ["panel"] : [] },
|
|
232
|
+
]);
|
|
233
|
+
hostRuntime.start();
|
|
234
|
+
const interceptWorkerRequest = (message, line) => interceptHostRequest(message, line, {
|
|
235
|
+
runtime: hostRuntime,
|
|
236
|
+
sendLine: (reply) => bridge.sendLine(reply),
|
|
237
|
+
approve: (params) => {
|
|
238
|
+
void bridge
|
|
239
|
+
.request("agent/approve", params, {
|
|
240
|
+
id: `unsupported-reply-${randomUUID()}`,
|
|
241
|
+
consume: true,
|
|
242
|
+
settleOnExit: true,
|
|
243
|
+
failFast: true,
|
|
244
|
+
timeoutMs: 5000,
|
|
245
|
+
meta: { origin: "host", producer: "serve-unsupported-loopback" },
|
|
246
|
+
})
|
|
247
|
+
.catch((error) => log("worker.loopback_unsupported_failed", { reason: String(error) }));
|
|
248
|
+
},
|
|
249
|
+
});
|
|
272
250
|
// Notifications describe shared agent state and are broadcast. Correlated
|
|
273
251
|
// JSON-RPC responses must return only to the tab that issued the request;
|
|
274
252
|
// browser tabs all start their local counters at `web-1`, so broadcasting a
|
|
@@ -282,16 +260,9 @@ export async function startHeadlessServer(opts) {
|
|
|
282
260
|
log("worker.frame_dropped", { reason: "not json", raw: previewLine(line) });
|
|
283
261
|
return;
|
|
284
262
|
}
|
|
263
|
+
if (interceptWorkerRequest(message, line))
|
|
264
|
+
return;
|
|
285
265
|
if (!message || message.id === undefined || message.method !== undefined) {
|
|
286
|
-
if (message && isHostAuthorityRequest(message)) {
|
|
287
|
-
log("worker.host_request_unsupported", { method: message.method });
|
|
288
|
-
bridge.sendLine(unsupportedHostRequestResponse(message.id, message.method));
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
if (!hubAuth &&
|
|
292
|
-
message?.method === "agent/approvalRequest" &&
|
|
293
|
-
replyUnsupportedLoopback(message.params))
|
|
294
|
-
return;
|
|
295
266
|
if (hubAuth && message?.method === "agent/streamEvent") {
|
|
296
267
|
const params = message.params;
|
|
297
268
|
if (typeof params?.sessionId === "string" && params.event !== undefined) {
|
|
@@ -340,8 +311,6 @@ export async function startHeadlessServer(opts) {
|
|
|
340
311
|
typeof approval.sessionId === "string") {
|
|
341
312
|
if (automationWorker?.handleApproval(approval))
|
|
342
313
|
return;
|
|
343
|
-
if (replyUnsupportedLoopback(approval))
|
|
344
|
-
return;
|
|
345
314
|
const request = approval.request;
|
|
346
315
|
if (request?.toolName === "__panel_action__") {
|
|
347
316
|
const ownerId = runOwners.get(approval.sessionId);
|
|
@@ -448,6 +417,8 @@ export async function startHeadlessServer(opts) {
|
|
|
448
417
|
// These switch on Desktop-main-only IPC clients in the worker.
|
|
449
418
|
CODE_SHELL_OPERATION_BATCH_ACCESS: undefined,
|
|
450
419
|
CODE_SHELL_MODEL_REQUEST_SIGNING: undefined,
|
|
420
|
+
CODE_SHELL_HOST_MCP_SERVERS: undefined,
|
|
421
|
+
CODE_SHELL_WORKSPACE_ACCESS: undefined,
|
|
451
422
|
CODE_SHELL_DATA_ROOT: workerDataRoot,
|
|
452
423
|
CODE_SHELL_CREDENTIAL_ACCESS: "local",
|
|
453
424
|
...(opts.workerCapabilityModules
|
|
@@ -562,6 +533,8 @@ export async function startHeadlessServer(opts) {
|
|
|
562
533
|
// These switch on Desktop-main-only IPC clients in the worker.
|
|
563
534
|
CODE_SHELL_OPERATION_BATCH_ACCESS: undefined,
|
|
564
535
|
CODE_SHELL_MODEL_REQUEST_SIGNING: undefined,
|
|
536
|
+
CODE_SHELL_HOST_MCP_SERVERS: undefined,
|
|
537
|
+
CODE_SHELL_WORKSPACE_ACCESS: undefined,
|
|
565
538
|
CODE_SHELL_DATA_ROOT: workerDataRoot,
|
|
566
539
|
CODE_SHELL_CREDENTIAL_ACCESS: "local",
|
|
567
540
|
...(opts.workerCapabilityModules
|
|
@@ -1164,6 +1137,7 @@ export async function startHeadlessServer(opts) {
|
|
|
1164
1137
|
responses.clear();
|
|
1165
1138
|
await panelAutomations?.close();
|
|
1166
1139
|
clearInterval(pendingResponseReaper);
|
|
1140
|
+
hostRuntime.stop();
|
|
1167
1141
|
if (authReaper)
|
|
1168
1142
|
clearInterval(authReaper);
|
|
1169
1143
|
await uploads?.close();
|
|
@@ -1,29 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Desktop main answers the browser, credential-cookie and workspace-switch
|
|
5
|
-
* loopbacks plus the `desktop/*` authority requests. serve used to broadcast
|
|
6
|
-
* them to Web tabs as ordinary approvals, so the worker waited for its timeout
|
|
7
|
-
* (5 minutes for loopbacks, 30 seconds for automation). Answering at once with
|
|
8
|
-
* a typed `unsupported` result lets the tool fail fast with a reason.
|
|
9
|
-
*
|
|
10
|
-
* Pure and host-neutral on purpose: it moves into packages/host unchanged
|
|
11
|
-
* (architecture ch.16 §3.2 step 1.1). The wire shapes are documented in
|
|
12
|
-
* docs/todo/platform-s0-protocol-inventory.md ("S0 应答格式").
|
|
13
|
-
*/
|
|
14
|
-
/** What this host can serve itself; Panel actions are only served with a Panel runtime. */
|
|
15
|
-
export interface ServeHostCapabilities {
|
|
16
|
-
panels: boolean;
|
|
17
|
-
}
|
|
18
|
-
export declare function unsupportedLoopbackCapability(toolName: unknown, host: ServeHostCapabilities): string | undefined;
|
|
19
|
-
/** The `answer` string for an approved loopback decision. */
|
|
20
|
-
export declare function unsupportedLoopbackAnswer(capability: string): string;
|
|
21
|
-
export declare function isHostAuthorityRequest(message: {
|
|
22
|
-
id?: unknown;
|
|
23
|
-
method?: unknown;
|
|
24
|
-
}): message is {
|
|
25
|
-
id: string | number;
|
|
26
|
-
method: string;
|
|
27
|
-
};
|
|
28
|
-
/** One JSON-RPC response line to write back to the worker. */
|
|
29
|
-
export declare function unsupportedHostRequestResponse(id: string | number, method: string): string;
|
|
1
|
+
/** @deprecated Import the shared host dispatcher from @cjhyy/code-shell-host. */
|
|
2
|
+
export { isHostAuthorityRequest, unsupportedHostRequestResponse, unsupportedLoopbackAnswer, unsupportedLoopbackCapability, type ServeHostCapabilities, } from "@cjhyy/code-shell-host";
|
|
@@ -1,62 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Desktop main answers the browser, credential-cookie and workspace-switch
|
|
5
|
-
* loopbacks plus the `desktop/*` authority requests. serve used to broadcast
|
|
6
|
-
* them to Web tabs as ordinary approvals, so the worker waited for its timeout
|
|
7
|
-
* (5 minutes for loopbacks, 30 seconds for automation). Answering at once with
|
|
8
|
-
* a typed `unsupported` result lets the tool fail fast with a reason.
|
|
9
|
-
*
|
|
10
|
-
* Pure and host-neutral on purpose: it moves into packages/host unchanged
|
|
11
|
-
* (architecture ch.16 §3.2 step 1.1). The wire shapes are documented in
|
|
12
|
-
* docs/todo/platform-s0-protocol-inventory.md ("S0 应答格式").
|
|
13
|
-
*/
|
|
14
|
-
const DESKTOP_ONLY_LOOPBACKS = Object.freeze({
|
|
15
|
-
__browser_action__: "browser",
|
|
16
|
-
__credential_action__: "credential.browserCookie",
|
|
17
|
-
__workspace_action__: "workspace.switch",
|
|
18
|
-
});
|
|
19
|
-
const PANEL_LOOPBACK = "__panel_action__";
|
|
20
|
-
const PANEL_CAPABILITY = "panel";
|
|
21
|
-
const HOST_AUTHORITY_METHOD_PREFIX = "desktop/";
|
|
22
|
-
const METHOD_NOT_FOUND = -32601;
|
|
23
|
-
function unsupportedText(capability) {
|
|
24
|
-
return `${capability} is not supported by this host (headless serve)`;
|
|
25
|
-
}
|
|
26
|
-
export function unsupportedLoopbackCapability(toolName, host) {
|
|
27
|
-
if (typeof toolName !== "string")
|
|
28
|
-
return undefined;
|
|
29
|
-
if (toolName === PANEL_LOOPBACK)
|
|
30
|
-
return host.panels ? undefined : PANEL_CAPABILITY;
|
|
31
|
-
return Object.hasOwn(DESKTOP_ONLY_LOOPBACKS, toolName)
|
|
32
|
-
? DESKTOP_ONLY_LOOPBACKS[toolName]
|
|
33
|
-
: undefined;
|
|
34
|
-
}
|
|
35
|
-
/** The `answer` string for an approved loopback decision. */
|
|
36
|
-
export function unsupportedLoopbackAnswer(capability) {
|
|
37
|
-
const text = unsupportedText(capability);
|
|
38
|
-
return JSON.stringify({
|
|
39
|
-
ok: false,
|
|
40
|
-
failure: "unsupported",
|
|
41
|
-
capability,
|
|
42
|
-
error: text,
|
|
43
|
-
detail: text,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
export function isHostAuthorityRequest(message) {
|
|
47
|
-
return ((typeof message.id === "string" || typeof message.id === "number") &&
|
|
48
|
-
typeof message.method === "string" &&
|
|
49
|
-
message.method.startsWith(HOST_AUTHORITY_METHOD_PREFIX));
|
|
50
|
-
}
|
|
51
|
-
/** One JSON-RPC response line to write back to the worker. */
|
|
52
|
-
export function unsupportedHostRequestResponse(id, method) {
|
|
53
|
-
return JSON.stringify({
|
|
54
|
-
jsonrpc: "2.0",
|
|
55
|
-
id,
|
|
56
|
-
error: {
|
|
57
|
-
code: METHOD_NOT_FOUND,
|
|
58
|
-
message: unsupportedText(method),
|
|
59
|
-
data: { failure: "unsupported", capability: method },
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
}
|
|
1
|
+
/** @deprecated Import the shared host dispatcher from @cjhyy/code-shell-host. */
|
|
2
|
+
export { isHostAuthorityRequest, unsupportedHostRequestResponse, unsupportedLoopbackAnswer, unsupportedLoopbackCapability, } from "@cjhyy/code-shell-host";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cjhyy/code-shell-server",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.44",
|
|
4
4
|
"description": "CodeShell server transport layer — mobile remote HTTP/WS host, pairing, passcode gate, tunnel, rooms, uploads, and session/attachment services. Electron-free; precursor of the auth gateway.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -61,13 +61,14 @@
|
|
|
61
61
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\""
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@cjhyy/code-shell-capability-coding": "0.9.
|
|
65
|
-
"@cjhyy/code-shell-core": "0.9.
|
|
66
|
-
"@cjhyy/code-shell-web": "0.9.
|
|
64
|
+
"@cjhyy/code-shell-capability-coding": "0.9.44",
|
|
65
|
+
"@cjhyy/code-shell-core": "0.9.44",
|
|
66
|
+
"@cjhyy/code-shell-web": "0.9.44",
|
|
67
67
|
"@modelcontextprotocol/sdk": "^1.31.0",
|
|
68
68
|
"@types/ws": "^8.18.1",
|
|
69
69
|
"ws": "^8.21.0",
|
|
70
|
-
"@cjhyy/code-shell-link": "0.9.
|
|
70
|
+
"@cjhyy/code-shell-link": "0.9.44",
|
|
71
|
+
"@cjhyy/code-shell-host": "0.9.44"
|
|
71
72
|
},
|
|
72
73
|
"engines": {
|
|
73
74
|
"node": ">=20.10"
|