dsh-openwrite 0.2.21 → 0.2.22
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/cordis.patch.yml +0 -9
- package/docs/INSTALL.md +1 -1
- package/docs/RELEASE_ACCEPTANCE.md +10 -0
- package/package.json +1 -1
- package/packages/openwrite-bridge/lib/managed-runtime.d.ts +17 -0
- package/packages/openwrite-bridge/lib/managed-runtime.js +82 -10
- package/packages/studio-panel/lib/client.js +481 -481
- package/release/source.json +3 -3
- package/vendor/dsh-dog/UPSTREAM.md +2 -1
- package/vendor/dsh-dog/lib/client.js +10 -0
- package/vendor/dsh-dog/lib/client.js.map +1 -1
- package/vendor/dsh-dog/lib/index.d.ts +10 -0
- package/vendor/dsh-dog/lib/index.js +104 -2
|
@@ -7,6 +7,16 @@ import { ConnectionRpcHandler } from "@deepseek-ai/dsh-client-connection";
|
|
|
7
7
|
import { SessionId } from "@deepseek-ai/dsh-session";
|
|
8
8
|
import { JobRegistry } from "@deepseek-ai/dsh-jobs";
|
|
9
9
|
//#region src/debug-contract.d.ts
|
|
10
|
+
/**
|
|
11
|
+
* Dedicated debugger channel. It is served by this plugin directly on the Web
|
|
12
|
+
* carrier (see serveDogDebugRpc): Connection's generic channel registry mounts
|
|
13
|
+
* its HTTP route through the Connection plugin's own context, which only sees
|
|
14
|
+
* `webServer` when a bundle patch injects it into the shared connection row —
|
|
15
|
+
* and per-key patch semantics make any such patch clobber other bundles
|
|
16
|
+
* (OpenWrite issue #81), while the shared `/api` channel already belongs to
|
|
17
|
+
* the host's Typert gateway. The wire envelope mirrors Connection's, so the
|
|
18
|
+
* browser side keeps using its stock `connection.rpc.call`.
|
|
19
|
+
*/
|
|
10
20
|
declare const DOG_DEBUG_RPC_CHANNEL = "/dog-rpc";
|
|
11
21
|
declare const DOG_DEBUG_SNAPSHOT_ENDPOINT = "snapshot";
|
|
12
22
|
declare const DOG_RUNTIME_TRACE_ENDPOINT = "goal-runtime";
|
|
@@ -278,6 +278,16 @@ function defineTool(options) {
|
|
|
278
278
|
}
|
|
279
279
|
//#endregion
|
|
280
280
|
//#region src/debug-contract.ts
|
|
281
|
+
/**
|
|
282
|
+
* Dedicated debugger channel. It is served by this plugin directly on the Web
|
|
283
|
+
* carrier (see serveDogDebugRpc): Connection's generic channel registry mounts
|
|
284
|
+
* its HTTP route through the Connection plugin's own context, which only sees
|
|
285
|
+
* `webServer` when a bundle patch injects it into the shared connection row —
|
|
286
|
+
* and per-key patch semantics make any such patch clobber other bundles
|
|
287
|
+
* (OpenWrite issue #81), while the shared `/api` channel already belongs to
|
|
288
|
+
* the host's Typert gateway. The wire envelope mirrors Connection's, so the
|
|
289
|
+
* browser side keeps using its stock `connection.rpc.call`.
|
|
290
|
+
*/
|
|
281
291
|
const DOG_DEBUG_RPC_CHANNEL = "/dog-rpc";
|
|
282
292
|
const DOG_DEBUG_SNAPSHOT_ENDPOINT = "snapshot";
|
|
283
293
|
const DOG_RUNTIME_TRACE_ENDPOINT = "goal-runtime";
|
|
@@ -357,6 +367,86 @@ function createDogDebugRpcHandler(repository) {
|
|
|
357
367
|
}
|
|
358
368
|
};
|
|
359
369
|
}
|
|
370
|
+
const DOG_DEBUG_BODY_LIMIT = 8388608;
|
|
371
|
+
const ENDPOINT_SEGMENT = /^[A-Za-z0-9_$.-]+$/;
|
|
372
|
+
/**
|
|
373
|
+
* Serve the debugger RPC channel directly on the Web carrier. Connection's
|
|
374
|
+
* generic channel registry (`rpc.handle`) mounts its HTTP route through the
|
|
375
|
+
* Connection plugin's own context, which only sees `webServer` when a bundle
|
|
376
|
+
* patch injects it into the shared connection row — and per-key patch
|
|
377
|
+
* semantics make any such patch clobber other bundles (OpenWrite issue #81);
|
|
378
|
+
* the shared `/api` channel is already claimed by the host's Typert gateway.
|
|
379
|
+
* The wire envelope mirrors Connection's (`client-request`/`server-response`)
|
|
380
|
+
* so the browser side keeps using its stock `connection.rpc.call`; admission
|
|
381
|
+
* at the route reuses Connection's trusted-host fence and browser
|
|
382
|
+
* authentication, so the channel stays exactly as protected as a native one.
|
|
383
|
+
*/
|
|
384
|
+
async function serveDogDebugRpc(req, res, handler) {
|
|
385
|
+
const respond = (status, body) => {
|
|
386
|
+
res.writeHead(status, { "content-type": "application/json" });
|
|
387
|
+
res.end(typeof body === "string" ? body : JSON.stringify(body));
|
|
388
|
+
};
|
|
389
|
+
const endpoint = dogDebugEndpointOf(req.url ?? "");
|
|
390
|
+
if (endpoint === void 0 || req.method !== "POST") {
|
|
391
|
+
respond(404, "not found");
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (((req.headers["content-type"] ?? "").split(";", 1)[0] ?? "").trim().toLowerCase() !== "application/json") {
|
|
395
|
+
respond(415, "content type must be application/json");
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
let raw = "";
|
|
399
|
+
let size = 0;
|
|
400
|
+
for await (const chunk of req) {
|
|
401
|
+
size += chunk.length;
|
|
402
|
+
if (size > DOG_DEBUG_BODY_LIMIT) {
|
|
403
|
+
respond(413, "request too large");
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
raw += chunk;
|
|
407
|
+
}
|
|
408
|
+
let body;
|
|
409
|
+
try {
|
|
410
|
+
body = JSON.parse(raw);
|
|
411
|
+
} catch {
|
|
412
|
+
respond(400, "body is not JSON");
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
const rpcId = typeof body?.rpcId === "string" ? body.rpcId : "invalid-request";
|
|
416
|
+
if (body?.type !== "client-request" || body.method !== endpoint) {
|
|
417
|
+
respond(200, {
|
|
418
|
+
type: "server-response",
|
|
419
|
+
rpcId,
|
|
420
|
+
result: {
|
|
421
|
+
ok: false,
|
|
422
|
+
error: {
|
|
423
|
+
code: "gateway/bad-request",
|
|
424
|
+
message: "invalid client-request message",
|
|
425
|
+
details: {}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const controller = new AbortController();
|
|
432
|
+
req.on("close", () => controller.abort());
|
|
433
|
+
try {
|
|
434
|
+
respond(200, {
|
|
435
|
+
type: "server-response",
|
|
436
|
+
rpcId,
|
|
437
|
+
result: await handler(endpoint, body.payload, controller.signal)
|
|
438
|
+
});
|
|
439
|
+
} catch (error) {
|
|
440
|
+
respond(500, `handler failure: ${String(error)}`);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
function dogDebugEndpointOf(url) {
|
|
444
|
+
const pathname = url.split("?", 1)[0] ?? "";
|
|
445
|
+
if (!pathname.startsWith(`/dog-rpc/`)) return void 0;
|
|
446
|
+
const endpoint = pathname.slice(9);
|
|
447
|
+
if (endpoint.split("/").some((segment) => segment === "" || segment === "." || segment === ".." || !ENDPOINT_SEGMENT.test(segment))) return void 0;
|
|
448
|
+
return endpoint;
|
|
449
|
+
}
|
|
360
450
|
function compareRevisions(left, right) {
|
|
361
451
|
if (left.current !== right.current) return left.current ? -1 : 1;
|
|
362
452
|
const leftUpdated = left.runs[0]?.updatedAt ?? "";
|
|
@@ -1094,8 +1184,20 @@ async function apply(ctx, config) {
|
|
|
1094
1184
|
if (event.data.reason?.kind !== "interrupted") return;
|
|
1095
1185
|
captureInterruptedTurn(repository, session, event).catch(() => void 0);
|
|
1096
1186
|
});
|
|
1097
|
-
ctx.inject(["connection", "webServer"], (
|
|
1098
|
-
|
|
1187
|
+
ctx.inject(["connection", "webServer"], (c) => {
|
|
1188
|
+
c.effect(() => c.webServer.register({
|
|
1189
|
+
kind: "prefix",
|
|
1190
|
+
path: DOG_DEBUG_RPC_CHANNEL,
|
|
1191
|
+
handler: async (req, res) => {
|
|
1192
|
+
const rejection = c.connection.requestRejection({ headers: req.headers });
|
|
1193
|
+
if (rejection !== void 0) {
|
|
1194
|
+
res.writeHead(rejection);
|
|
1195
|
+
res.end(rejection === 401 ? "unauthorized" : "forbidden");
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
await serveDogDebugRpc(req, res, debugHandler);
|
|
1199
|
+
}
|
|
1200
|
+
}), "dog: trusted debugger RPC");
|
|
1099
1201
|
});
|
|
1100
1202
|
}
|
|
1101
1203
|
async function captureInterruptedTurn(repository, session, event) {
|