@dev-anywhere/relay 0.4.63 → 0.4.65
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/dist/{chunk-4V3WSB2Y.js → chunk-H7OL7UK7.js} +172 -11
- package/dist/chunk-H7OL7UK7.js.map +1 -0
- package/dist/handlers/client.d.ts +6 -1
- package/dist/handlers/client.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/registry.d.ts +19 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-4V3WSB2Y.js.map +0 -1
|
@@ -62,6 +62,7 @@ import { z as z4 } from "zod";
|
|
|
62
62
|
var providerValues = ["claude", "codex"];
|
|
63
63
|
var ptyOwnerValues = ["local-terminal", "proxy-hosted"];
|
|
64
64
|
var sessionModeValues = ["pty", "json"];
|
|
65
|
+
var sessionKindValues = ["agent", "terminal"];
|
|
65
66
|
|
|
66
67
|
// ../../packages/shared/dist/constants/pty.js
|
|
67
68
|
var PtySemanticState = {
|
|
@@ -94,6 +95,7 @@ var agentStatusPhaseValues = [
|
|
|
94
95
|
];
|
|
95
96
|
var SessionInfoSchema = z4.object({
|
|
96
97
|
sessionId: IdSchema,
|
|
98
|
+
kind: z4.enum(sessionKindValues).optional(),
|
|
97
99
|
name: z4.string().optional(),
|
|
98
100
|
// cwd 只用于展示完整路径/tooltip,不作为前端路由或权限判断来源。
|
|
99
101
|
cwd: z4.string().optional(),
|
|
@@ -129,6 +131,9 @@ var SessionStatusPayloadSchema = z4.object({
|
|
|
129
131
|
});
|
|
130
132
|
var PtyStatePayloadSchema = z4.object({
|
|
131
133
|
state: z4.enum(ptySemanticStateValues),
|
|
134
|
+
// Semantic event sequence. Present for runtime PTY semantic events; optional for
|
|
135
|
+
// legacy cleanup messages from older proxies.
|
|
136
|
+
seq: z4.number().int().nonnegative().optional(),
|
|
132
137
|
title: z4.string().optional(),
|
|
133
138
|
tool: z4.string().optional()
|
|
134
139
|
});
|
|
@@ -502,6 +507,14 @@ var ProxyInfoSchema = z8.object({
|
|
|
502
507
|
online: z8.boolean(),
|
|
503
508
|
sessions: z8.array(z8.string()).optional()
|
|
504
509
|
});
|
|
510
|
+
var RelayClientInfoSchema = z8.object({
|
|
511
|
+
clientId: IdSchema,
|
|
512
|
+
proxyId: IdSchema.optional(),
|
|
513
|
+
connectedAt: z8.number().int().nonnegative(),
|
|
514
|
+
current: z8.boolean().optional(),
|
|
515
|
+
userAgent: z8.string().optional(),
|
|
516
|
+
remoteAddress: z8.string().optional()
|
|
517
|
+
});
|
|
505
518
|
var AgentCliAvailabilitySchema = z8.object({
|
|
506
519
|
available: z8.boolean(),
|
|
507
520
|
command: z8.string().optional(),
|
|
@@ -568,6 +581,21 @@ var relayControlDefinitions = [
|
|
|
568
581
|
...RequestIdShape,
|
|
569
582
|
proxies: z8.array(ProxyInfoSchema)
|
|
570
583
|
}),
|
|
584
|
+
control("relay_client_list_request", RequestIdShape),
|
|
585
|
+
control("relay_client_list_response", {
|
|
586
|
+
...RequestIdShape,
|
|
587
|
+
clients: z8.array(RelayClientInfoSchema)
|
|
588
|
+
}),
|
|
589
|
+
control("relay_client_kick", { ...RequiredRequestIdShape, clientId: IdSchema }),
|
|
590
|
+
control("relay_client_kick_response", {
|
|
591
|
+
...RequiredRequestIdShape,
|
|
592
|
+
clientId: IdSchema,
|
|
593
|
+
success: z8.boolean(),
|
|
594
|
+
...RequestErrorShape
|
|
595
|
+
}),
|
|
596
|
+
control("relay_client_kicked", {
|
|
597
|
+
reason: z8.string().optional()
|
|
598
|
+
}),
|
|
571
599
|
control("proxy_select", { ...RequestIdShape, proxyId: IdSchema }),
|
|
572
600
|
control("proxy_select_response", {
|
|
573
601
|
...RequestIdShape,
|
|
@@ -807,9 +835,10 @@ var relayControlDefinitions = [
|
|
|
807
835
|
// 远程创建 JSON 会话,client -> proxy -> response
|
|
808
836
|
control("session_create", {
|
|
809
837
|
...RequestIdShape,
|
|
810
|
-
|
|
838
|
+
kind: z8.enum(sessionKindValues).optional(),
|
|
839
|
+
cwd: z8.string().optional(),
|
|
811
840
|
name: z8.string().optional(),
|
|
812
|
-
provider: z8.enum(providerValues),
|
|
841
|
+
provider: z8.enum(providerValues).optional(),
|
|
813
842
|
mode: z8.enum(sessionModeValues).optional(),
|
|
814
843
|
resumeSessionId: z8.string().optional(),
|
|
815
844
|
// 透传给 claude CLI 的 --permission-mode, undefined 时 proxy 兜底为 "default"
|
|
@@ -821,6 +850,7 @@ var relayControlDefinitions = [
|
|
|
821
850
|
sessionId: IdSchema.optional(),
|
|
822
851
|
name: z8.string().optional(),
|
|
823
852
|
nameLocked: z8.boolean().optional(),
|
|
853
|
+
kind: z8.enum(sessionKindValues).optional(),
|
|
824
854
|
mode: z8.enum(sessionModeValues).optional(),
|
|
825
855
|
provider: z8.enum(providerValues).optional(),
|
|
826
856
|
ptyOwner: z8.enum(ptyOwnerValues).optional(),
|
|
@@ -899,6 +929,7 @@ var relayControlDefinitions = [
|
|
|
899
929
|
control("session_sync", {
|
|
900
930
|
sessions: z8.array(z8.object({
|
|
901
931
|
id: z8.string(),
|
|
932
|
+
kind: z8.enum(sessionKindValues).optional(),
|
|
902
933
|
mode: z8.enum(sessionModeValues),
|
|
903
934
|
provider: z8.enum(providerValues),
|
|
904
935
|
ptyOwner: z8.enum(ptyOwnerValues).optional(),
|
|
@@ -931,6 +962,11 @@ function isClientToProxyRelayControlType(type) {
|
|
|
931
962
|
return ClientToProxyRelayControlTypes.has(type);
|
|
932
963
|
}
|
|
933
964
|
|
|
965
|
+
// ../../packages/shared/dist/constants/relay-close-codes.js
|
|
966
|
+
var RelayCloseCode = {
|
|
967
|
+
CLIENT_KICKED: 4401
|
|
968
|
+
};
|
|
969
|
+
|
|
934
970
|
// ../../packages/shared/dist/binary-frame.js
|
|
935
971
|
var SID_LEN_BYTES = 1;
|
|
936
972
|
var SEQ_BYTES = 4;
|
|
@@ -974,7 +1010,7 @@ var proxyConnectionFSM = defineFSM({
|
|
|
974
1010
|
var RelayRegistry = class {
|
|
975
1011
|
proxyStates = /* @__PURE__ */ new Map();
|
|
976
1012
|
clientBindings = /* @__PURE__ */ new Map();
|
|
977
|
-
connectedClients = /* @__PURE__ */ new
|
|
1013
|
+
connectedClients = /* @__PURE__ */ new Map();
|
|
978
1014
|
registerProxy(proxyId, ws, name) {
|
|
979
1015
|
const existing = this.proxyStates.get(proxyId);
|
|
980
1016
|
if (existing) {
|
|
@@ -1132,21 +1168,43 @@ var RelayRegistry = class {
|
|
|
1132
1168
|
}
|
|
1133
1169
|
return count;
|
|
1134
1170
|
}
|
|
1135
|
-
addClientWs(ws) {
|
|
1136
|
-
this.connectedClients.
|
|
1171
|
+
addClientWs(ws, metadata = {}) {
|
|
1172
|
+
this.connectedClients.set(ws, {
|
|
1173
|
+
connectedAt: metadata.connectedAt ?? Date.now(),
|
|
1174
|
+
...metadata.clientId !== void 0 ? { clientId: metadata.clientId } : {},
|
|
1175
|
+
...metadata.userAgent !== void 0 ? { userAgent: metadata.userAgent } : {},
|
|
1176
|
+
...metadata.remoteAddress !== void 0 ? { remoteAddress: metadata.remoteAddress } : {}
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
updateConnectedClientId(ws, clientId) {
|
|
1180
|
+
const metadata = this.connectedClients.get(ws);
|
|
1181
|
+
if (metadata) {
|
|
1182
|
+
metadata.clientId = clientId;
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
this.addClientWs(ws, { clientId });
|
|
1137
1186
|
}
|
|
1138
1187
|
removeClientWs(ws) {
|
|
1139
1188
|
this.connectedClients.delete(ws);
|
|
1140
1189
|
}
|
|
1141
1190
|
getAllClientWs() {
|
|
1142
1191
|
const clients = [];
|
|
1143
|
-
for (const ws of this.connectedClients) {
|
|
1192
|
+
for (const ws of this.connectedClients.keys()) {
|
|
1144
1193
|
if (ws.readyState === WebSocket.OPEN) {
|
|
1145
1194
|
clients.push(ws);
|
|
1146
1195
|
}
|
|
1147
1196
|
}
|
|
1148
1197
|
return clients;
|
|
1149
1198
|
}
|
|
1199
|
+
getConnectedClientSockets(clientId) {
|
|
1200
|
+
const sockets = [];
|
|
1201
|
+
for (const [ws, metadata] of this.connectedClients) {
|
|
1202
|
+
if (metadata.clientId === clientId && ws.readyState === WebSocket.OPEN) {
|
|
1203
|
+
sockets.push(ws);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
return sockets;
|
|
1207
|
+
}
|
|
1150
1208
|
// 获取单个 proxy 的详细状态信息
|
|
1151
1209
|
getProxyDetail(proxyId) {
|
|
1152
1210
|
const state = this.proxyStates.get(proxyId);
|
|
@@ -1173,6 +1231,22 @@ var RelayRegistry = class {
|
|
|
1173
1231
|
}
|
|
1174
1232
|
return details;
|
|
1175
1233
|
}
|
|
1234
|
+
getConnectedClientDetails(currentClientId) {
|
|
1235
|
+
const details = [];
|
|
1236
|
+
for (const [ws, metadata] of this.connectedClients) {
|
|
1237
|
+
if (ws.readyState !== WebSocket.OPEN || !metadata.clientId) continue;
|
|
1238
|
+
const binding = this.clientBindings.get(metadata.clientId);
|
|
1239
|
+
details.push({
|
|
1240
|
+
clientId: metadata.clientId,
|
|
1241
|
+
...binding?.proxyId !== void 0 ? { proxyId: binding.proxyId } : {},
|
|
1242
|
+
connectedAt: metadata.connectedAt,
|
|
1243
|
+
...metadata.clientId === currentClientId ? { current: true } : {},
|
|
1244
|
+
...metadata.userAgent !== void 0 ? { userAgent: metadata.userAgent } : {},
|
|
1245
|
+
...metadata.remoteAddress !== void 0 ? { remoteAddress: metadata.remoteAddress } : {}
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
return details;
|
|
1249
|
+
}
|
|
1176
1250
|
};
|
|
1177
1251
|
|
|
1178
1252
|
// src/health.ts
|
|
@@ -2332,6 +2406,7 @@ function handleVoiceConfigControl(msg, ws, store, logger, providers) {
|
|
|
2332
2406
|
var MAX_JSON_MESSAGE_SIZE2 = 1 * 1024 * 1024;
|
|
2333
2407
|
function handleClientRegister(clientId, clientWs, registry, logger) {
|
|
2334
2408
|
clientWs.clientId = clientId;
|
|
2409
|
+
registry.updateConnectedClientId(clientWs, clientId);
|
|
2335
2410
|
const binding = registry.getClientBinding(clientId);
|
|
2336
2411
|
if (!binding) {
|
|
2337
2412
|
clientWs.send(
|
|
@@ -2366,6 +2441,72 @@ function handleClientRegister(clientId, clientWs, registry, logger) {
|
|
|
2366
2441
|
);
|
|
2367
2442
|
logger.info({ clientId, proxyId, status: "restored" }, "Client registered");
|
|
2368
2443
|
}
|
|
2444
|
+
function handleRelayClientListRequest(clientWs, registry, requestId) {
|
|
2445
|
+
clientWs.send(
|
|
2446
|
+
JSON.stringify({
|
|
2447
|
+
type: "relay_client_list_response",
|
|
2448
|
+
requestId,
|
|
2449
|
+
clients: registry.getConnectedClientDetails(clientWs.clientId)
|
|
2450
|
+
})
|
|
2451
|
+
);
|
|
2452
|
+
}
|
|
2453
|
+
function handleRelayClientKick(clientWs, registry, logger, requestId, targetClientId) {
|
|
2454
|
+
if (targetClientId === clientWs.clientId) {
|
|
2455
|
+
clientWs.send(
|
|
2456
|
+
JSON.stringify({
|
|
2457
|
+
type: "relay_client_kick_response",
|
|
2458
|
+
requestId,
|
|
2459
|
+
clientId: targetClientId,
|
|
2460
|
+
success: false,
|
|
2461
|
+
errorCode: ControlErrorCode.UNKNOWN,
|
|
2462
|
+
error: "\u4E0D\u80FD\u65AD\u5F00\u5F53\u524D\u5BA2\u6237\u7AEF"
|
|
2463
|
+
})
|
|
2464
|
+
);
|
|
2465
|
+
return;
|
|
2466
|
+
}
|
|
2467
|
+
const targets = registry.getConnectedClientSockets(targetClientId);
|
|
2468
|
+
if (targets.length === 0) {
|
|
2469
|
+
clientWs.send(
|
|
2470
|
+
JSON.stringify({
|
|
2471
|
+
type: "relay_client_kick_response",
|
|
2472
|
+
requestId,
|
|
2473
|
+
clientId: targetClientId,
|
|
2474
|
+
success: false,
|
|
2475
|
+
errorCode: ControlErrorCode.UNKNOWN,
|
|
2476
|
+
error: "\u5BA2\u6237\u7AEF\u4E0D\u5728\u7EBF"
|
|
2477
|
+
})
|
|
2478
|
+
);
|
|
2479
|
+
return;
|
|
2480
|
+
}
|
|
2481
|
+
const kickedMessage = JSON.stringify({
|
|
2482
|
+
type: "relay_client_kicked",
|
|
2483
|
+
reason: "\u7531\u5BA2\u6237\u7AEF\u7BA1\u7406\u65AD\u5F00"
|
|
2484
|
+
});
|
|
2485
|
+
for (const target of targets) {
|
|
2486
|
+
try {
|
|
2487
|
+
target.send(kickedMessage);
|
|
2488
|
+
target.close(RelayCloseCode.CLIENT_KICKED, "client kicked");
|
|
2489
|
+
} catch (err) {
|
|
2490
|
+
logger.warn({ err, clientId: targetClientId }, "Failed to close kicked client");
|
|
2491
|
+
target.terminate();
|
|
2492
|
+
} finally {
|
|
2493
|
+
registry.removeClientWs(target);
|
|
2494
|
+
registry.unbindClientById(targetClientId);
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
clientWs.send(
|
|
2498
|
+
JSON.stringify({
|
|
2499
|
+
type: "relay_client_kick_response",
|
|
2500
|
+
requestId,
|
|
2501
|
+
clientId: targetClientId,
|
|
2502
|
+
success: true
|
|
2503
|
+
})
|
|
2504
|
+
);
|
|
2505
|
+
logger.info(
|
|
2506
|
+
{ byClientId: clientWs.clientId, targetClientId, targetCount: targets.length },
|
|
2507
|
+
"Relay client kicked"
|
|
2508
|
+
);
|
|
2509
|
+
}
|
|
2369
2510
|
function rejectNotBound(ws) {
|
|
2370
2511
|
ws.send(
|
|
2371
2512
|
JSON.stringify({
|
|
@@ -2402,10 +2543,10 @@ function sendRelayProxyProbeFailure(ws, requestId, error, chaos) {
|
|
|
2402
2543
|
}
|
|
2403
2544
|
ws.send(response);
|
|
2404
2545
|
}
|
|
2405
|
-
function handleClientConnection(ws, registry, logger, chaos, voiceConfigStore, voiceProviders) {
|
|
2546
|
+
function handleClientConnection(ws, registry, logger, chaos, voiceConfigStore, voiceProviders, connectionInfo = {}) {
|
|
2406
2547
|
const clientWs = ws;
|
|
2407
2548
|
clientWs.isAlive = true;
|
|
2408
|
-
registry.addClientWs(clientWs);
|
|
2549
|
+
registry.addClientWs(clientWs, connectionInfo);
|
|
2409
2550
|
clientWs.on("pong", () => {
|
|
2410
2551
|
clientWs.isAlive = true;
|
|
2411
2552
|
});
|
|
@@ -2432,6 +2573,14 @@ function handleClientConnection(ws, registry, logger, chaos, voiceConfigStore, v
|
|
|
2432
2573
|
handleClientRegister(msg.clientId, clientWs, registry, logger);
|
|
2433
2574
|
return;
|
|
2434
2575
|
}
|
|
2576
|
+
if (msg.type === "relay_client_list_request") {
|
|
2577
|
+
handleRelayClientListRequest(clientWs, registry, msg.requestId);
|
|
2578
|
+
return;
|
|
2579
|
+
}
|
|
2580
|
+
if (msg.type === "relay_client_kick") {
|
|
2581
|
+
handleRelayClientKick(clientWs, registry, logger, msg.requestId, msg.clientId);
|
|
2582
|
+
return;
|
|
2583
|
+
}
|
|
2435
2584
|
if (msg.type === "proxy_list_request") {
|
|
2436
2585
|
const proxies = registry.listProxiesWithName().map((p) => ({
|
|
2437
2586
|
...p,
|
|
@@ -3149,6 +3298,15 @@ function createVoiceProviderRegistry(adapters) {
|
|
|
3149
3298
|
// src/server.ts
|
|
3150
3299
|
var MODULE_DIR = dirname3(fileURLToPath2(import.meta.url));
|
|
3151
3300
|
var PACKAGED_FONTS_DIR = resolve(MODULE_DIR, "../assets/fonts");
|
|
3301
|
+
function firstHeaderValue(value) {
|
|
3302
|
+
if (Array.isArray(value)) return value[0];
|
|
3303
|
+
return value;
|
|
3304
|
+
}
|
|
3305
|
+
function requestRemoteAddress(request) {
|
|
3306
|
+
const forwardedFor = firstHeaderValue(request.headers["x-forwarded-for"]);
|
|
3307
|
+
const firstForwarded = forwardedFor?.split(",")[0]?.trim();
|
|
3308
|
+
return firstForwarded || request.socket.remoteAddress;
|
|
3309
|
+
}
|
|
3152
3310
|
function createRelayServer(options) {
|
|
3153
3311
|
const { heartbeatInterval = 3e4, logger, dataDir, proxyToken, clientToken, chaos } = options;
|
|
3154
3312
|
const proxyTokenRequired = typeof proxyToken === "string" && proxyToken.length > 0;
|
|
@@ -3294,8 +3452,11 @@ function createRelayServer(options) {
|
|
|
3294
3452
|
proxyWss.on("connection", (ws) => {
|
|
3295
3453
|
handleProxyConnection(ws, registry, logger, relayChaos);
|
|
3296
3454
|
});
|
|
3297
|
-
clientWss.on("connection", (ws) => {
|
|
3298
|
-
handleClientConnection(ws, registry, logger, relayChaos, voiceConfigStore, voiceProviders
|
|
3455
|
+
clientWss.on("connection", (ws, request) => {
|
|
3456
|
+
handleClientConnection(ws, registry, logger, relayChaos, voiceConfigStore, voiceProviders, {
|
|
3457
|
+
userAgent: firstHeaderValue(request.headers["user-agent"]),
|
|
3458
|
+
remoteAddress: requestRemoteAddress(request)
|
|
3459
|
+
});
|
|
3299
3460
|
});
|
|
3300
3461
|
voiceAsrWss.on("connection", (ws) => {
|
|
3301
3462
|
handleVoiceAsrConnection(ws, voiceConfigStore, logger, voiceProviders);
|
|
@@ -3365,4 +3526,4 @@ export {
|
|
|
3365
3526
|
parseRelayChaosFromEnv,
|
|
3366
3527
|
createRelayServer
|
|
3367
3528
|
};
|
|
3368
|
-
//# sourceMappingURL=chunk-
|
|
3529
|
+
//# sourceMappingURL=chunk-H7OL7UK7.js.map
|