dsh-interconnect 0.11.3 → 0.11.4
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/dsh.plugin.json +1 -1
- package/lib/index.js +47 -21
- package/lib/interconnect/index.js +47 -21
- package/lib/interconnect/index.js.map +2 -2
- package/lib/tool-interconnect/index.js +4 -3
- package/lib/tool-interconnect/index.js.map +2 -2
- package/lib/types/interconnect/index.d.ts +9 -0
- package/lib/types/interconnect/index.d.ts.map +1 -1
- package/lib/types/interconnect/types.d.ts +12 -7
- package/lib/types/interconnect/types.d.ts.map +1 -1
- package/lib/types/tool-interconnect/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dsh.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-interconnect",
|
|
3
3
|
"description": "Cross-instance message/event handoff for DeepSeek Harness (DSH): WebSocket link, model-facing tools, and companion skill.",
|
|
4
|
-
"version": "0.11.
|
|
4
|
+
"version": "0.11.4",
|
|
5
5
|
"entry": {
|
|
6
6
|
"name": "dsh-interconnect/interconnect",
|
|
7
7
|
"inject": ["agents", "credentials"]
|
package/lib/index.js
CHANGED
|
@@ -859,14 +859,31 @@ var withoutNullFields = (value) => {
|
|
|
859
859
|
return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== null).map(([key, field]) => [key, withoutNullFields(field)]));
|
|
860
860
|
};
|
|
861
861
|
var projectMsgResult = (result) => result.delivered ? { delivered: true, instance: result.instance, ...result.delivery === void 0 ? {} : { delivery: result.delivery } } : { delivered: false, instance: result.instance, reason: result.reason };
|
|
862
|
-
var
|
|
862
|
+
var parseQueryResult = (schema, result) => {
|
|
863
863
|
try {
|
|
864
864
|
const [parsed] = Schema.resolve(result, schema, {});
|
|
865
|
-
return parsed
|
|
865
|
+
return parsed;
|
|
866
866
|
} catch {
|
|
867
|
-
return
|
|
867
|
+
return void 0;
|
|
868
868
|
}
|
|
869
869
|
};
|
|
870
|
+
var projectPingResult = (result) => {
|
|
871
|
+
const parsed = parseQueryResult(pingResultSchema, result);
|
|
872
|
+
return parsed === void 0 ? void 0 : { pong: true, instance: parsed.instance };
|
|
873
|
+
};
|
|
874
|
+
var projectListResult = (result) => {
|
|
875
|
+
const parsed = parseQueryResult(listResultSchema, result);
|
|
876
|
+
if (parsed === void 0) return void 0;
|
|
877
|
+
const rows = parsed.sessions;
|
|
878
|
+
return {
|
|
879
|
+
instance: parsed.instance,
|
|
880
|
+
sessions: rows.map((row) => ({
|
|
881
|
+
sessionId: row.sessionId,
|
|
882
|
+
...row.title === void 0 || row.title === null ? {} : { title: row.title },
|
|
883
|
+
...row.status === void 0 || row.status === null ? {} : { status: row.status }
|
|
884
|
+
}))
|
|
885
|
+
};
|
|
886
|
+
};
|
|
870
887
|
var queryResultSchema = Schema.union([
|
|
871
888
|
pingResultSchema,
|
|
872
889
|
listResultSchema,
|
|
@@ -1125,7 +1142,7 @@ var InterconnectService = class extends Service {
|
|
|
1125
1142
|
const state = this.linkStates.get(instanceId);
|
|
1126
1143
|
if (state === void 0 || !state.writable()) return void 0;
|
|
1127
1144
|
const reqId = `q${++this.reqIdCounter}-${randomUUID()}`;
|
|
1128
|
-
const accept = query.kind === "ping" ?
|
|
1145
|
+
const accept = query.kind === "ping" ? projectPingResult : projectListResult;
|
|
1129
1146
|
try {
|
|
1130
1147
|
return await this.waitForResult(reqId, "query", (failure) => {
|
|
1131
1148
|
const wrote = state.sendFrame({ type: "query", reqId, query });
|
|
@@ -1142,16 +1159,12 @@ var InterconnectService = class extends Service {
|
|
|
1142
1159
|
this.pendingMessages.delete(reqId);
|
|
1143
1160
|
reject(new Error(`interconnect: no result for ${reqId} within ${this.requestTimeoutMs}ms`));
|
|
1144
1161
|
}, this.requestTimeoutMs);
|
|
1145
|
-
|
|
1146
|
-
timer
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
this.pendingMessages.delete(reqId);
|
|
1152
|
-
resolve2(result);
|
|
1153
|
-
}
|
|
1154
|
-
});
|
|
1162
|
+
const settle = (result) => {
|
|
1163
|
+
clearTimeout(timer);
|
|
1164
|
+
this.pendingMessages.delete(reqId);
|
|
1165
|
+
resolve2(result);
|
|
1166
|
+
};
|
|
1167
|
+
this.pendingMessages.set(reqId, kind === "query" ? { timer, kind, accept, resolve: settle } : { timer, kind, resolve: settle });
|
|
1155
1168
|
send((error) => {
|
|
1156
1169
|
clearTimeout(timer);
|
|
1157
1170
|
this.pendingMessages.delete(reqId);
|
|
@@ -1395,6 +1408,19 @@ var InterconnectService = class extends Service {
|
|
|
1395
1408
|
const credential = await this.ctx.credentials.resolve(credentialRef(INTERCONNECT_TOKEN_REF));
|
|
1396
1409
|
return credential === void 0 || credential.value.length === 0 ? void 0 : credential.value;
|
|
1397
1410
|
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Answer one refused upgrade and close its socket. `socket.end()` alone only
|
|
1413
|
+
* half-closes the connection: a client that keeps its own writing side open
|
|
1414
|
+
* (a socket created with `allowHalfOpen`) leaves the connection live, and a
|
|
1415
|
+
* refused socket never enters the link pool, so nothing else would reap it.
|
|
1416
|
+
* @param socket - the refused upgrade socket.
|
|
1417
|
+
* @param response - the complete HTTP response sent to the client.
|
|
1418
|
+
*/
|
|
1419
|
+
refuseUpgrade(socket, response) {
|
|
1420
|
+
socket.end(response, () => {
|
|
1421
|
+
socket.destroy();
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1398
1424
|
/** Inbound WebSocket upgrade: authenticate the bearer header, then accept. */
|
|
1399
1425
|
async handleUpgrade(req, socket, head) {
|
|
1400
1426
|
let token;
|
|
@@ -1402,21 +1428,21 @@ var InterconnectService = class extends Service {
|
|
|
1402
1428
|
token = await this.resolveToken();
|
|
1403
1429
|
} catch (error) {
|
|
1404
1430
|
this.ctx.logger.warn(`interconnect: upgrade token read failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1405
|
-
|
|
1431
|
+
this.refuseUpgrade(socket, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
|
|
1406
1432
|
return;
|
|
1407
1433
|
}
|
|
1408
1434
|
if (token === void 0) {
|
|
1409
|
-
|
|
1435
|
+
this.refuseUpgrade(socket, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
|
|
1410
1436
|
return;
|
|
1411
1437
|
}
|
|
1412
1438
|
if (this.disposed) {
|
|
1413
|
-
|
|
1439
|
+
this.refuseUpgrade(socket, "HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\nContent-Length: 11\r\n\r\nunavailable");
|
|
1414
1440
|
return;
|
|
1415
1441
|
}
|
|
1416
1442
|
const expected = `Bearer ${token}`;
|
|
1417
1443
|
const header = req.headers.authorization;
|
|
1418
1444
|
if (header === void 0 || !timingSafeEqual(header, expected)) {
|
|
1419
|
-
|
|
1445
|
+
this.refuseUpgrade(socket, "HTTP/1.1 401 Unauthorized\r\nConnection: close\r\nContent-Length: 12\r\n\r\nunauthorized");
|
|
1420
1446
|
return;
|
|
1421
1447
|
}
|
|
1422
1448
|
this.server.handleUpgrade(req, socket, head, (websocket) => {
|
|
@@ -1497,9 +1523,9 @@ var InterconnectService = class extends Service {
|
|
|
1497
1523
|
}
|
|
1498
1524
|
if (frame.type === "query-result") {
|
|
1499
1525
|
const pending = this.pendingMessages.get(frame.reqId);
|
|
1500
|
-
if (pending !== void 0 && pending.kind === "query"
|
|
1501
|
-
;
|
|
1502
|
-
pending.resolve?.(
|
|
1526
|
+
if (pending !== void 0 && pending.kind === "query") {
|
|
1527
|
+
const answer = pending.accept(frame.result);
|
|
1528
|
+
if (answer !== void 0) pending.resolve?.(answer);
|
|
1503
1529
|
}
|
|
1504
1530
|
return;
|
|
1505
1531
|
}
|
|
@@ -859,14 +859,31 @@ var withoutNullFields = (value) => {
|
|
|
859
859
|
return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== null).map(([key, field]) => [key, withoutNullFields(field)]));
|
|
860
860
|
};
|
|
861
861
|
var projectMsgResult = (result) => result.delivered ? { delivered: true, instance: result.instance, ...result.delivery === void 0 ? {} : { delivery: result.delivery } } : { delivered: false, instance: result.instance, reason: result.reason };
|
|
862
|
-
var
|
|
862
|
+
var parseQueryResult = (schema, result) => {
|
|
863
863
|
try {
|
|
864
864
|
const [parsed] = Schema.resolve(result, schema, {});
|
|
865
|
-
return parsed
|
|
865
|
+
return parsed;
|
|
866
866
|
} catch {
|
|
867
|
-
return
|
|
867
|
+
return void 0;
|
|
868
868
|
}
|
|
869
869
|
};
|
|
870
|
+
var projectPingResult = (result) => {
|
|
871
|
+
const parsed = parseQueryResult(pingResultSchema, result);
|
|
872
|
+
return parsed === void 0 ? void 0 : { pong: true, instance: parsed.instance };
|
|
873
|
+
};
|
|
874
|
+
var projectListResult = (result) => {
|
|
875
|
+
const parsed = parseQueryResult(listResultSchema, result);
|
|
876
|
+
if (parsed === void 0) return void 0;
|
|
877
|
+
const rows = parsed.sessions;
|
|
878
|
+
return {
|
|
879
|
+
instance: parsed.instance,
|
|
880
|
+
sessions: rows.map((row) => ({
|
|
881
|
+
sessionId: row.sessionId,
|
|
882
|
+
...row.title === void 0 || row.title === null ? {} : { title: row.title },
|
|
883
|
+
...row.status === void 0 || row.status === null ? {} : { status: row.status }
|
|
884
|
+
}))
|
|
885
|
+
};
|
|
886
|
+
};
|
|
870
887
|
var queryResultSchema = Schema.union([
|
|
871
888
|
pingResultSchema,
|
|
872
889
|
listResultSchema,
|
|
@@ -1125,7 +1142,7 @@ var InterconnectService = class extends Service {
|
|
|
1125
1142
|
const state = this.linkStates.get(instanceId);
|
|
1126
1143
|
if (state === void 0 || !state.writable()) return void 0;
|
|
1127
1144
|
const reqId = `q${++this.reqIdCounter}-${randomUUID()}`;
|
|
1128
|
-
const accept = query.kind === "ping" ?
|
|
1145
|
+
const accept = query.kind === "ping" ? projectPingResult : projectListResult;
|
|
1129
1146
|
try {
|
|
1130
1147
|
return await this.waitForResult(reqId, "query", (failure) => {
|
|
1131
1148
|
const wrote = state.sendFrame({ type: "query", reqId, query });
|
|
@@ -1142,16 +1159,12 @@ var InterconnectService = class extends Service {
|
|
|
1142
1159
|
this.pendingMessages.delete(reqId);
|
|
1143
1160
|
reject(new Error(`interconnect: no result for ${reqId} within ${this.requestTimeoutMs}ms`));
|
|
1144
1161
|
}, this.requestTimeoutMs);
|
|
1145
|
-
|
|
1146
|
-
timer
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
this.pendingMessages.delete(reqId);
|
|
1152
|
-
resolve2(result);
|
|
1153
|
-
}
|
|
1154
|
-
});
|
|
1162
|
+
const settle = (result) => {
|
|
1163
|
+
clearTimeout(timer);
|
|
1164
|
+
this.pendingMessages.delete(reqId);
|
|
1165
|
+
resolve2(result);
|
|
1166
|
+
};
|
|
1167
|
+
this.pendingMessages.set(reqId, kind === "query" ? { timer, kind, accept, resolve: settle } : { timer, kind, resolve: settle });
|
|
1155
1168
|
send((error) => {
|
|
1156
1169
|
clearTimeout(timer);
|
|
1157
1170
|
this.pendingMessages.delete(reqId);
|
|
@@ -1395,6 +1408,19 @@ var InterconnectService = class extends Service {
|
|
|
1395
1408
|
const credential = await this.ctx.credentials.resolve(credentialRef(INTERCONNECT_TOKEN_REF));
|
|
1396
1409
|
return credential === void 0 || credential.value.length === 0 ? void 0 : credential.value;
|
|
1397
1410
|
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Answer one refused upgrade and close its socket. `socket.end()` alone only
|
|
1413
|
+
* half-closes the connection: a client that keeps its own writing side open
|
|
1414
|
+
* (a socket created with `allowHalfOpen`) leaves the connection live, and a
|
|
1415
|
+
* refused socket never enters the link pool, so nothing else would reap it.
|
|
1416
|
+
* @param socket - the refused upgrade socket.
|
|
1417
|
+
* @param response - the complete HTTP response sent to the client.
|
|
1418
|
+
*/
|
|
1419
|
+
refuseUpgrade(socket, response) {
|
|
1420
|
+
socket.end(response, () => {
|
|
1421
|
+
socket.destroy();
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1398
1424
|
/** Inbound WebSocket upgrade: authenticate the bearer header, then accept. */
|
|
1399
1425
|
async handleUpgrade(req, socket, head) {
|
|
1400
1426
|
let token;
|
|
@@ -1402,21 +1428,21 @@ var InterconnectService = class extends Service {
|
|
|
1402
1428
|
token = await this.resolveToken();
|
|
1403
1429
|
} catch (error) {
|
|
1404
1430
|
this.ctx.logger.warn(`interconnect: upgrade token read failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1405
|
-
|
|
1431
|
+
this.refuseUpgrade(socket, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
|
|
1406
1432
|
return;
|
|
1407
1433
|
}
|
|
1408
1434
|
if (token === void 0) {
|
|
1409
|
-
|
|
1435
|
+
this.refuseUpgrade(socket, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
|
|
1410
1436
|
return;
|
|
1411
1437
|
}
|
|
1412
1438
|
if (this.disposed) {
|
|
1413
|
-
|
|
1439
|
+
this.refuseUpgrade(socket, "HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\nContent-Length: 11\r\n\r\nunavailable");
|
|
1414
1440
|
return;
|
|
1415
1441
|
}
|
|
1416
1442
|
const expected = `Bearer ${token}`;
|
|
1417
1443
|
const header = req.headers.authorization;
|
|
1418
1444
|
if (header === void 0 || !timingSafeEqual(header, expected)) {
|
|
1419
|
-
|
|
1445
|
+
this.refuseUpgrade(socket, "HTTP/1.1 401 Unauthorized\r\nConnection: close\r\nContent-Length: 12\r\n\r\nunauthorized");
|
|
1420
1446
|
return;
|
|
1421
1447
|
}
|
|
1422
1448
|
this.server.handleUpgrade(req, socket, head, (websocket) => {
|
|
@@ -1497,9 +1523,9 @@ var InterconnectService = class extends Service {
|
|
|
1497
1523
|
}
|
|
1498
1524
|
if (frame.type === "query-result") {
|
|
1499
1525
|
const pending = this.pendingMessages.get(frame.reqId);
|
|
1500
|
-
if (pending !== void 0 && pending.kind === "query"
|
|
1501
|
-
;
|
|
1502
|
-
pending.resolve?.(
|
|
1526
|
+
if (pending !== void 0 && pending.kind === "query") {
|
|
1527
|
+
const answer = pending.accept(frame.result);
|
|
1528
|
+
if (answer !== void 0) pending.resolve?.(answer);
|
|
1503
1529
|
}
|
|
1504
1530
|
return;
|
|
1505
1531
|
}
|