dsh-interconnect 0.11.2 → 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 +55 -26
- package/lib/interconnect/index.js +55 -26
- 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 -1
- package/lib/types/interconnect/index.d.ts.map +1 -1
- package/lib/types/interconnect/types.d.ts +29 -12
- 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
|
@@ -853,14 +853,37 @@ var listResultSchema = Schema.object({
|
|
|
853
853
|
status: Schema.string().required(false)
|
|
854
854
|
})).required()
|
|
855
855
|
});
|
|
856
|
-
var
|
|
856
|
+
var withoutNullFields = (value) => {
|
|
857
|
+
if (Array.isArray(value)) return value.map((item) => withoutNullFields(item));
|
|
858
|
+
if (value === null || typeof value !== "object") return value;
|
|
859
|
+
return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== null).map(([key, field]) => [key, withoutNullFields(field)]));
|
|
860
|
+
};
|
|
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 parseQueryResult = (schema, result) => {
|
|
857
863
|
try {
|
|
858
864
|
const [parsed] = Schema.resolve(result, schema, {});
|
|
859
|
-
return parsed
|
|
865
|
+
return parsed;
|
|
860
866
|
} catch {
|
|
861
|
-
return
|
|
867
|
+
return void 0;
|
|
862
868
|
}
|
|
863
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
|
+
};
|
|
864
887
|
var queryResultSchema = Schema.union([
|
|
865
888
|
pingResultSchema,
|
|
866
889
|
listResultSchema,
|
|
@@ -1119,7 +1142,7 @@ var InterconnectService = class extends Service {
|
|
|
1119
1142
|
const state = this.linkStates.get(instanceId);
|
|
1120
1143
|
if (state === void 0 || !state.writable()) return void 0;
|
|
1121
1144
|
const reqId = `q${++this.reqIdCounter}-${randomUUID()}`;
|
|
1122
|
-
const accept = query.kind === "ping" ?
|
|
1145
|
+
const accept = query.kind === "ping" ? projectPingResult : projectListResult;
|
|
1123
1146
|
try {
|
|
1124
1147
|
return await this.waitForResult(reqId, "query", (failure) => {
|
|
1125
1148
|
const wrote = state.sendFrame({ type: "query", reqId, query });
|
|
@@ -1136,16 +1159,12 @@ var InterconnectService = class extends Service {
|
|
|
1136
1159
|
this.pendingMessages.delete(reqId);
|
|
1137
1160
|
reject(new Error(`interconnect: no result for ${reqId} within ${this.requestTimeoutMs}ms`));
|
|
1138
1161
|
}, this.requestTimeoutMs);
|
|
1139
|
-
|
|
1140
|
-
timer
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
this.pendingMessages.delete(reqId);
|
|
1146
|
-
resolve2(result);
|
|
1147
|
-
}
|
|
1148
|
-
});
|
|
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 });
|
|
1149
1168
|
send((error) => {
|
|
1150
1169
|
clearTimeout(timer);
|
|
1151
1170
|
this.pendingMessages.delete(reqId);
|
|
@@ -1200,7 +1219,7 @@ var InterconnectService = class extends Service {
|
|
|
1200
1219
|
this.attachSocket(socket);
|
|
1201
1220
|
},
|
|
1202
1221
|
this.ctx.logger,
|
|
1203
|
-
() => this.
|
|
1222
|
+
() => this.resolveToken(),
|
|
1204
1223
|
instanceId,
|
|
1205
1224
|
trimBase(origin)
|
|
1206
1225
|
);
|
|
@@ -1389,8 +1408,18 @@ var InterconnectService = class extends Service {
|
|
|
1389
1408
|
const credential = await this.ctx.credentials.resolve(credentialRef(INTERCONNECT_TOKEN_REF));
|
|
1390
1409
|
return credential === void 0 || credential.value.length === 0 ? void 0 : credential.value;
|
|
1391
1410
|
}
|
|
1392
|
-
|
|
1393
|
-
|
|
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
|
+
});
|
|
1394
1423
|
}
|
|
1395
1424
|
/** Inbound WebSocket upgrade: authenticate the bearer header, then accept. */
|
|
1396
1425
|
async handleUpgrade(req, socket, head) {
|
|
@@ -1399,21 +1428,21 @@ var InterconnectService = class extends Service {
|
|
|
1399
1428
|
token = await this.resolveToken();
|
|
1400
1429
|
} catch (error) {
|
|
1401
1430
|
this.ctx.logger.warn(`interconnect: upgrade token read failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1402
|
-
|
|
1431
|
+
this.refuseUpgrade(socket, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
|
|
1403
1432
|
return;
|
|
1404
1433
|
}
|
|
1405
1434
|
if (token === void 0) {
|
|
1406
|
-
|
|
1435
|
+
this.refuseUpgrade(socket, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
|
|
1407
1436
|
return;
|
|
1408
1437
|
}
|
|
1409
1438
|
if (this.disposed) {
|
|
1410
|
-
|
|
1439
|
+
this.refuseUpgrade(socket, "HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\nContent-Length: 11\r\n\r\nunavailable");
|
|
1411
1440
|
return;
|
|
1412
1441
|
}
|
|
1413
1442
|
const expected = `Bearer ${token}`;
|
|
1414
1443
|
const header = req.headers.authorization;
|
|
1415
1444
|
if (header === void 0 || !timingSafeEqual(header, expected)) {
|
|
1416
|
-
|
|
1445
|
+
this.refuseUpgrade(socket, "HTTP/1.1 401 Unauthorized\r\nConnection: close\r\nContent-Length: 12\r\n\r\nunauthorized");
|
|
1417
1446
|
return;
|
|
1418
1447
|
}
|
|
1419
1448
|
this.server.handleUpgrade(req, socket, head, (websocket) => {
|
|
@@ -1458,7 +1487,7 @@ var InterconnectService = class extends Service {
|
|
|
1458
1487
|
const text = typeof data === "string" ? data : Array.isArray(data) ? Buffer.concat(data).toString("utf8") : Buffer.isBuffer(data) ? data.toString("utf8") : Buffer.from(data).toString("utf8");
|
|
1459
1488
|
let parsed;
|
|
1460
1489
|
try {
|
|
1461
|
-
parsed = Schema.resolve(JSON.parse(text), linkFrameSchema, {})[0];
|
|
1490
|
+
parsed = withoutNullFields(Schema.resolve(JSON.parse(text), linkFrameSchema, {})[0]);
|
|
1462
1491
|
} catch {
|
|
1463
1492
|
this.ctx.logger.warn("interconnect: dropping malformed link frame");
|
|
1464
1493
|
return;
|
|
@@ -1488,15 +1517,15 @@ var InterconnectService = class extends Service {
|
|
|
1488
1517
|
const pending = this.pendingMessages.get(frame.reqId);
|
|
1489
1518
|
if (pending !== void 0 && pending.kind === "msg") {
|
|
1490
1519
|
;
|
|
1491
|
-
pending.resolve?.(frame.result);
|
|
1520
|
+
pending.resolve?.(projectMsgResult(frame.result));
|
|
1492
1521
|
}
|
|
1493
1522
|
return;
|
|
1494
1523
|
}
|
|
1495
1524
|
if (frame.type === "query-result") {
|
|
1496
1525
|
const pending = this.pendingMessages.get(frame.reqId);
|
|
1497
|
-
if (pending !== void 0 && pending.kind === "query"
|
|
1498
|
-
;
|
|
1499
|
-
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);
|
|
1500
1529
|
}
|
|
1501
1530
|
return;
|
|
1502
1531
|
}
|
|
@@ -853,14 +853,37 @@ var listResultSchema = Schema.object({
|
|
|
853
853
|
status: Schema.string().required(false)
|
|
854
854
|
})).required()
|
|
855
855
|
});
|
|
856
|
-
var
|
|
856
|
+
var withoutNullFields = (value) => {
|
|
857
|
+
if (Array.isArray(value)) return value.map((item) => withoutNullFields(item));
|
|
858
|
+
if (value === null || typeof value !== "object") return value;
|
|
859
|
+
return Object.fromEntries(Object.entries(value).filter(([, field]) => field !== null).map(([key, field]) => [key, withoutNullFields(field)]));
|
|
860
|
+
};
|
|
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 parseQueryResult = (schema, result) => {
|
|
857
863
|
try {
|
|
858
864
|
const [parsed] = Schema.resolve(result, schema, {});
|
|
859
|
-
return parsed
|
|
865
|
+
return parsed;
|
|
860
866
|
} catch {
|
|
861
|
-
return
|
|
867
|
+
return void 0;
|
|
862
868
|
}
|
|
863
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
|
+
};
|
|
864
887
|
var queryResultSchema = Schema.union([
|
|
865
888
|
pingResultSchema,
|
|
866
889
|
listResultSchema,
|
|
@@ -1119,7 +1142,7 @@ var InterconnectService = class extends Service {
|
|
|
1119
1142
|
const state = this.linkStates.get(instanceId);
|
|
1120
1143
|
if (state === void 0 || !state.writable()) return void 0;
|
|
1121
1144
|
const reqId = `q${++this.reqIdCounter}-${randomUUID()}`;
|
|
1122
|
-
const accept = query.kind === "ping" ?
|
|
1145
|
+
const accept = query.kind === "ping" ? projectPingResult : projectListResult;
|
|
1123
1146
|
try {
|
|
1124
1147
|
return await this.waitForResult(reqId, "query", (failure) => {
|
|
1125
1148
|
const wrote = state.sendFrame({ type: "query", reqId, query });
|
|
@@ -1136,16 +1159,12 @@ var InterconnectService = class extends Service {
|
|
|
1136
1159
|
this.pendingMessages.delete(reqId);
|
|
1137
1160
|
reject(new Error(`interconnect: no result for ${reqId} within ${this.requestTimeoutMs}ms`));
|
|
1138
1161
|
}, this.requestTimeoutMs);
|
|
1139
|
-
|
|
1140
|
-
timer
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
this.pendingMessages.delete(reqId);
|
|
1146
|
-
resolve2(result);
|
|
1147
|
-
}
|
|
1148
|
-
});
|
|
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 });
|
|
1149
1168
|
send((error) => {
|
|
1150
1169
|
clearTimeout(timer);
|
|
1151
1170
|
this.pendingMessages.delete(reqId);
|
|
@@ -1200,7 +1219,7 @@ var InterconnectService = class extends Service {
|
|
|
1200
1219
|
this.attachSocket(socket);
|
|
1201
1220
|
},
|
|
1202
1221
|
this.ctx.logger,
|
|
1203
|
-
() => this.
|
|
1222
|
+
() => this.resolveToken(),
|
|
1204
1223
|
instanceId,
|
|
1205
1224
|
trimBase(origin)
|
|
1206
1225
|
);
|
|
@@ -1389,8 +1408,18 @@ var InterconnectService = class extends Service {
|
|
|
1389
1408
|
const credential = await this.ctx.credentials.resolve(credentialRef(INTERCONNECT_TOKEN_REF));
|
|
1390
1409
|
return credential === void 0 || credential.value.length === 0 ? void 0 : credential.value;
|
|
1391
1410
|
}
|
|
1392
|
-
|
|
1393
|
-
|
|
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
|
+
});
|
|
1394
1423
|
}
|
|
1395
1424
|
/** Inbound WebSocket upgrade: authenticate the bearer header, then accept. */
|
|
1396
1425
|
async handleUpgrade(req, socket, head) {
|
|
@@ -1399,21 +1428,21 @@ var InterconnectService = class extends Service {
|
|
|
1399
1428
|
token = await this.resolveToken();
|
|
1400
1429
|
} catch (error) {
|
|
1401
1430
|
this.ctx.logger.warn(`interconnect: upgrade token read failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1402
|
-
|
|
1431
|
+
this.refuseUpgrade(socket, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
|
|
1403
1432
|
return;
|
|
1404
1433
|
}
|
|
1405
1434
|
if (token === void 0) {
|
|
1406
|
-
|
|
1435
|
+
this.refuseUpgrade(socket, "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 9\r\n\r\nforbidden");
|
|
1407
1436
|
return;
|
|
1408
1437
|
}
|
|
1409
1438
|
if (this.disposed) {
|
|
1410
|
-
|
|
1439
|
+
this.refuseUpgrade(socket, "HTTP/1.1 503 Service Unavailable\r\nConnection: close\r\nContent-Length: 11\r\n\r\nunavailable");
|
|
1411
1440
|
return;
|
|
1412
1441
|
}
|
|
1413
1442
|
const expected = `Bearer ${token}`;
|
|
1414
1443
|
const header = req.headers.authorization;
|
|
1415
1444
|
if (header === void 0 || !timingSafeEqual(header, expected)) {
|
|
1416
|
-
|
|
1445
|
+
this.refuseUpgrade(socket, "HTTP/1.1 401 Unauthorized\r\nConnection: close\r\nContent-Length: 12\r\n\r\nunauthorized");
|
|
1417
1446
|
return;
|
|
1418
1447
|
}
|
|
1419
1448
|
this.server.handleUpgrade(req, socket, head, (websocket) => {
|
|
@@ -1458,7 +1487,7 @@ var InterconnectService = class extends Service {
|
|
|
1458
1487
|
const text = typeof data === "string" ? data : Array.isArray(data) ? Buffer.concat(data).toString("utf8") : Buffer.isBuffer(data) ? data.toString("utf8") : Buffer.from(data).toString("utf8");
|
|
1459
1488
|
let parsed;
|
|
1460
1489
|
try {
|
|
1461
|
-
parsed = Schema.resolve(JSON.parse(text), linkFrameSchema, {})[0];
|
|
1490
|
+
parsed = withoutNullFields(Schema.resolve(JSON.parse(text), linkFrameSchema, {})[0]);
|
|
1462
1491
|
} catch {
|
|
1463
1492
|
this.ctx.logger.warn("interconnect: dropping malformed link frame");
|
|
1464
1493
|
return;
|
|
@@ -1488,15 +1517,15 @@ var InterconnectService = class extends Service {
|
|
|
1488
1517
|
const pending = this.pendingMessages.get(frame.reqId);
|
|
1489
1518
|
if (pending !== void 0 && pending.kind === "msg") {
|
|
1490
1519
|
;
|
|
1491
|
-
pending.resolve?.(frame.result);
|
|
1520
|
+
pending.resolve?.(projectMsgResult(frame.result));
|
|
1492
1521
|
}
|
|
1493
1522
|
return;
|
|
1494
1523
|
}
|
|
1495
1524
|
if (frame.type === "query-result") {
|
|
1496
1525
|
const pending = this.pendingMessages.get(frame.reqId);
|
|
1497
|
-
if (pending !== void 0 && pending.kind === "query"
|
|
1498
|
-
;
|
|
1499
|
-
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);
|
|
1500
1529
|
}
|
|
1501
1530
|
return;
|
|
1502
1531
|
}
|