@dev-anywhere/relay 0.0.5 → 0.1.0

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/README.md CHANGED
@@ -46,7 +46,7 @@ PORT=3100 dev-anywhere-relay
46
46
 
47
47
  **Production warning:** always set both `RELAY_PROXY_TOKEN` and `RELAY_CLIENT_TOKEN`. Without a client token, anyone who can reach the relay can connect to `/client`, list proxies, and attempt to bind to a proxy.
48
48
 
49
- For the bundled web client, open the app once with `?relayToken=<RELAY_CLIENT_TOKEN>` in the page URL. The token is stored in `sessionStorage` for that browser tab and appended to `/client` WebSocket connections.
49
+ For the bundled web client, open the app once with `?relayToken=<RELAY_CLIENT_TOKEN>` in the page URL. The token is stored in local browser storage and appended to `/client` WebSocket connections.
50
50
 
51
51
  ## TLS
52
52
 
@@ -295,7 +295,8 @@ import { z as z5 } from "zod";
295
295
  // ../../packages/shared/dist/schemas/chat.js
296
296
  import { z } from "zod";
297
297
  var UserInputPayloadSchema = z.object({
298
- text: z.string().min(1)
298
+ text: z.string().min(1),
299
+ messageId: z.string().min(1).optional()
299
300
  });
300
301
  var AssistantMessagePayloadSchema = z.object({
301
302
  text: z.string(),
@@ -564,12 +565,19 @@ var HistorySessionSchema = z6.object({
564
565
  updatedAt: z6.number(),
565
566
  provider: z6.enum(["claude", "codex"]).optional()
566
567
  });
568
+ var SessionHistoryMessageSchema = z6.object({
569
+ role: z6.enum(["user", "assistant"]),
570
+ text: z6.string(),
571
+ timestamp: z6.number().optional(),
572
+ cursor: z6.string().optional()
573
+ });
567
574
  var RequestIdShape = { requestId: z6.string().min(1).optional() };
568
575
  var ControlErrorCodeSchema = z6.enum(Object.values(ControlErrorCode));
569
576
  var RequestErrorShape = {
570
577
  error: z6.string().optional(),
571
578
  errorCode: ControlErrorCodeSchema.optional()
572
579
  };
580
+ var ClipboardImageMimeTypeSchema = z6.enum(["image/png", "image/jpeg", "image/webp", "image/gif"]);
573
581
  function control(type, shape, directions) {
574
582
  return {
575
583
  type,
@@ -680,6 +688,20 @@ var relayControlDefinitions = [
680
688
  }, "proxy_to_client"),
681
689
  // 客户端发送到 PTY 的原始字节(ANSI 序列),不追加换行
682
690
  control("remote_input_raw", { sessionId: z6.string().min(1), data: z6.string() }, "client_to_proxy"),
691
+ control("clipboard_image_upload", {
692
+ ...RequestIdShape,
693
+ sessionId: z6.string().min(1),
694
+ mimeType: ClipboardImageMimeTypeSchema,
695
+ dataBase64: z6.string().min(1),
696
+ fileName: z6.string().optional()
697
+ }, "client_to_proxy"),
698
+ control("clipboard_image_upload_response", {
699
+ ...RequestIdShape,
700
+ ...RequestErrorShape,
701
+ sessionId: z6.string().min(1),
702
+ success: z6.boolean(),
703
+ path: z6.string()
704
+ }, "proxy_to_client"),
683
705
  // 客户端询问 proxy 的环境信息 (home 路径等), client -> proxy -> response
684
706
  // FilePathPicker 用 homePath 作为 select 模式下的默认起点, 新建会话时打开即可浏览
685
707
  control("proxy_info_request", RequestIdShape, "client_to_proxy"),
@@ -710,7 +732,12 @@ var relayControlDefinitions = [
710
732
  ...RequestErrorShape
711
733
  }, "proxy_to_client"),
712
734
  // 客户端请求会话历史消息,client -> proxy
713
- control("session_messages_request", { ...RequestIdShape, sessionId: z6.string() }, "client_to_proxy"),
735
+ control("session_messages_request", {
736
+ ...RequestIdShape,
737
+ sessionId: z6.string(),
738
+ limit: z6.number().int().min(1).max(200).optional(),
739
+ before: z6.string().optional()
740
+ }, "client_to_proxy"),
714
741
  // 客户端请求会话资源(命令列表 + 文件树),client -> proxy
715
742
  control("session_resources_request", { ...RequestIdShape, sessionId: z6.string() }, "client_to_proxy"),
716
743
  control("session_resources_response", {
@@ -751,11 +778,10 @@ var relayControlDefinitions = [
751
778
  control("session_history_messages", {
752
779
  ...RequestIdShape,
753
780
  sessionId: z6.string(),
754
- messages: z6.array(z6.object({
755
- role: z6.enum(["user", "assistant"]),
756
- text: z6.string(),
757
- timestamp: z6.number().optional()
758
- }))
781
+ before: z6.string().optional(),
782
+ messages: z6.array(SessionHistoryMessageSchema),
783
+ hasMore: z6.boolean().optional(),
784
+ nextBefore: z6.string().optional()
759
785
  }, "proxy_to_client"),
760
786
  // proxy 重连后同步活跃 session 列表给 relay
761
787
  control("session_sync", {
@@ -1516,4 +1542,4 @@ export {
1516
1542
  parseRelayChaosFromEnv,
1517
1543
  createRelayServer
1518
1544
  };
1519
- //# sourceMappingURL=chunk-3PD64DNZ.js.map
1545
+ //# sourceMappingURL=chunk-UQFAAMXW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../src/registry.ts","../src/health.ts","../src/version.ts","../src/handlers/proxy.ts","../../../packages/shared/src/schemas/envelope.ts","../../../packages/shared/src/schemas/chat.ts","../../../packages/shared/src/schemas/tool.ts","../../../packages/shared/src/schemas/session.ts","../../../packages/shared/src/schemas/system.ts","../../../packages/shared/src/schemas/relay-control.ts","../../../packages/shared/src/constants/relay-errors.ts","../../../packages/shared/src/constants/control-errors.ts","../../../packages/shared/src/logger.ts","../src/router.ts","../src/handlers/client.ts","../src/heartbeat.ts","../src/chaos.ts"],"sourcesContent":["import express from \"express\";\nimport { existsSync } from \"node:fs\";\nimport { createServer, type Server } from \"node:http\";\nimport { homedir } from \"node:os\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { WebSocketServer } from \"ws\";\nimport type { Logger } from \"@dev-anywhere/shared\";\nimport { RelayRegistry } from \"./registry.js\";\nimport { healthRouter } from \"./health.js\";\nimport { handleProxyConnection } from \"./handlers/proxy.js\";\nimport { handleClientConnection } from \"./handlers/client.js\";\nimport { setupHeartbeat } from \"./heartbeat.js\";\nimport { createRelayChaos, type RelayChaosOptions } from \"./chaos.js\";\n\nexport interface RelayServerOptions {\n port?: number;\n heartbeatInterval?: number;\n logger: Logger;\n dataDir?: string;\n // proxy 注册预共享 token; 不传 / 空串则关闭鉴权 (开发默认)\n proxyToken?: string;\n // client 连接预共享 token; 不传 / 空串则关闭鉴权 (开发默认)\n clientToken?: string;\n chaos?: RelayChaosOptions;\n fontAssetDir?: string;\n}\n\nexport interface RelayServer {\n httpServer: Server;\n registry: RelayRegistry;\n close: () => Promise<void>;\n}\n\nconst MODULE_DIR = dirname(fileURLToPath(import.meta.url));\nconst PACKAGED_FONTS_DIR = resolve(MODULE_DIR, \"../assets/fonts\");\n\n// 创建中转服务器,Express HTTP + ws WebSocket 双端点\nexport function createRelayServer(options: RelayServerOptions): RelayServer {\n const { heartbeatInterval = 30000, logger, dataDir, proxyToken, clientToken, chaos } = options;\n const proxyTokenRequired = typeof proxyToken === \"string\" && proxyToken.length > 0;\n const clientTokenRequired = typeof clientToken === \"string\" && clientToken.length > 0;\n if (!proxyTokenRequired) {\n logger.warn(\n \"proxy auth token not set, /proxy endpoint is open — ok for dev, not for public relay\",\n );\n }\n if (!clientTokenRequired) {\n logger.warn(\n \"client auth token not set, /client endpoint is open — ok for dev, not for public relay\",\n );\n }\n\n const registry = new RelayRegistry();\n const relayChaos = chaos?.enabled ? createRelayChaos(chaos, logger) : undefined;\n if (chaos?.enabled) {\n logger.warn(\n {\n delayMs: chaos.delayMs,\n duplicate: chaos.duplicate,\n reorder: chaos.reorder,\n types: chaos.types ? [...chaos.types] : \"all\",\n },\n \"Relay chaos mode enabled\",\n );\n }\n const app = express();\n\n // 字体优先读持久化目录;Docker 镜像再回退到随包内置字体,避免空 volume 让 Web 字体 404。\n const fontsDir = dataDir ? `${dataDir}/fonts` : `${homedir()}/.dev-anywhere/relay-data/fonts`;\n const fontAssetDir = options.fontAssetDir ?? PACKAGED_FONTS_DIR;\n app.use(\n \"/fonts\",\n (req, res, next) => {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n next();\n },\n express.static(fontsDir, {\n maxAge: \"30d\",\n immutable: true,\n }),\n );\n if (existsSync(fontAssetDir)) {\n app.use(\n \"/fonts\",\n express.static(fontAssetDir, {\n maxAge: \"30d\",\n immutable: true,\n }),\n );\n }\n\n app.use(healthRouter(registry));\n\n // 使用 createServer 而非 app.listen,确保 WebSocket upgrade 可在同一端口上处理\n const httpServer = createServer(app);\n\n const proxyWss = new WebSocketServer({ noServer: true });\n const clientWss = new WebSocketServer({ noServer: true });\n\n httpServer.on(\"upgrade\", (request, socket, head) => {\n const url = new URL(request.url ?? \"/\", \"http://localhost\");\n const { pathname } = url;\n\n if (pathname === \"/proxy\") {\n if (proxyTokenRequired) {\n const token = url.searchParams.get(\"token\");\n if (token !== proxyToken) {\n logger.warn(\n { ip: request.socket.remoteAddress },\n \"rejected /proxy upgrade: invalid token\",\n );\n socket.write(\"HTTP/1.1 401 Unauthorized\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\n }\n proxyWss.handleUpgrade(request, socket, head, (ws) => {\n proxyWss.emit(\"connection\", ws, request);\n });\n return;\n }\n\n if (pathname === \"/client\") {\n if (clientTokenRequired) {\n const token = url.searchParams.get(\"token\");\n if (token !== clientToken) {\n logger.warn(\n { ip: request.socket.remoteAddress },\n \"rejected /client upgrade: invalid token\",\n );\n socket.write(\"HTTP/1.1 401 Unauthorized\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\n }\n clientWss.handleUpgrade(request, socket, head, (ws) => {\n clientWss.emit(\"connection\", ws, request);\n });\n return;\n }\n\n socket.destroy();\n });\n\n proxyWss.on(\"connection\", (ws) => {\n handleProxyConnection(ws, registry, logger, relayChaos);\n });\n\n clientWss.on(\"connection\", (ws) => {\n handleClientConnection(ws, registry, logger, relayChaos);\n });\n\n const proxyHeartbeat = setupHeartbeat(proxyWss, heartbeatInterval);\n const clientHeartbeat = setupHeartbeat(clientWss, heartbeatInterval);\n\n async function close(): Promise<void> {\n clearInterval(proxyHeartbeat);\n clearInterval(clientHeartbeat);\n\n for (const ws of proxyWss.clients) {\n ws.terminate();\n }\n for (const ws of clientWss.clients) {\n ws.terminate();\n }\n\n await new Promise<void>((resolve, reject) => {\n proxyWss.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n clientWss.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n httpServer.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n return { httpServer, registry, close };\n}\n","import { WebSocket } from \"ws\";\n\n// 显式代理连接状态,取代 ws null 检查\ntype ProxyConnectionState = \"online\" | \"offline\";\n\n// 显式客户端连接状态,跟踪注册和绑定\ntype ClientConnectionState = \"registered\" | \"bound\";\n\n// 代理连接状态,跟踪 ws、会话集合、离线时间、显示名称\ninterface ProxyState {\n ws: WebSocket | null;\n connectionState: ProxyConnectionState;\n sessions: Set<string>;\n disconnectedAt: number | null;\n name?: string;\n}\n\n// 客户端绑定状态,通过 clientId 而非 WebSocket 引用标识\ninterface ClientBinding {\n proxyId: string;\n ws: WebSocket | null;\n connectionState: ClientConnectionState;\n}\n\n// 代理注册、客户端绑定、宽限期管理\nexport class RelayRegistry {\n private proxyStates = new Map<string, ProxyState>();\n private clientBindings = new Map<string, ClientBinding>();\n private connectedClients = new Set<WebSocket>();\n\n registerProxy(proxyId: string, ws: WebSocket, name?: string): \"new\" | \"reconnected\" {\n const existing = this.proxyStates.get(proxyId);\n if (existing) {\n // 如果旧连接还活着,先终止\n if (existing.ws && existing.ws.readyState === WebSocket.OPEN) {\n existing.ws.terminate();\n }\n existing.ws = ws;\n existing.connectionState = \"online\";\n existing.disconnectedAt = null;\n if (name !== undefined) existing.name = name;\n return \"reconnected\";\n }\n\n this.proxyStates.set(proxyId, {\n ws,\n connectionState: \"online\",\n sessions: new Set(),\n disconnectedAt: null,\n name,\n });\n return \"new\";\n }\n\n // 标记 proxy 离线,保留所有状态等待重连,不设超时\n // 清理只在 proxy 主动退出(proxy_disconnect)或 relay 启动清理废弃数据时发生\n markProxyOffline(proxyId: string): void {\n const state = this.proxyStates.get(proxyId);\n if (!state) return;\n\n state.ws = null;\n state.connectionState = \"offline\";\n state.disconnectedAt = Date.now();\n }\n\n // 显式状态转换,校验 from 状态匹配后更新 connectionState\n transitionProxy(proxyId: string, from: ProxyConnectionState, to: ProxyConnectionState): void {\n if (from === to) {\n throw new Error(`Invalid proxy transition: ${from} -> ${to} (same state)`);\n }\n const state = this.proxyStates.get(proxyId);\n if (!state) {\n throw new Error(`Proxy not found: ${proxyId}`);\n }\n if (state.connectionState !== from) {\n throw new Error(\n `Proxy ${proxyId} state mismatch: expected ${from}, actual ${state.connectionState}`,\n );\n }\n state.connectionState = to;\n if (to === \"offline\") {\n state.ws = null;\n state.disconnectedAt = Date.now();\n }\n }\n\n // 显式客户端状态转换,校验 from 状态匹配后更新 connectionState\n transitionClient(clientId: string, from: ClientConnectionState, to: ClientConnectionState): void {\n if (from === to) {\n throw new Error(`Invalid client transition: ${from} -> ${to} (same state)`);\n }\n const binding = this.clientBindings.get(clientId);\n if (!binding) {\n throw new Error(`Client not found: ${clientId}`);\n }\n if (binding.connectionState !== from) {\n throw new Error(\n `Client ${clientId} state mismatch: expected ${from}, actual ${binding.connectionState}`,\n );\n }\n binding.connectionState = to;\n }\n\n getProxyConnectionState(proxyId: string): ProxyConnectionState | undefined {\n return this.proxyStates.get(proxyId)?.connectionState;\n }\n\n getClientConnectionState(clientId: string): ClientConnectionState | undefined {\n return this.clientBindings.get(clientId)?.connectionState;\n }\n\n // 彻底清理 proxy 状态和客户端绑定\n cleanupProxy(proxyId: string): void {\n const state = this.proxyStates.get(proxyId);\n if (!state) return;\n\n // 解绑所有绑定到该 proxy 的客户端\n for (const [clientId, binding] of this.clientBindings) {\n if (binding.proxyId === proxyId) {\n this.clientBindings.delete(clientId);\n }\n }\n\n this.proxyStates.delete(proxyId);\n }\n\n unregisterProxy(proxyId: string): void {\n this.cleanupProxy(proxyId);\n }\n\n getProxy(proxyId: string): WebSocket | undefined {\n const state = this.proxyStates.get(proxyId);\n return state?.ws ?? undefined;\n }\n\n isProxyOnline(proxyId: string): boolean {\n const state = this.proxyStates.get(proxyId);\n if (!state || state.connectionState !== \"online\") return false;\n // connectionState 声明在线但 ws 已失效时,记录警告并返回 false\n if (!state.ws || state.ws.readyState !== WebSocket.OPEN) return false;\n return true;\n }\n\n // proxy 是否存在(含宽限期中的)\n hasProxy(proxyId: string): boolean {\n return this.proxyStates.has(proxyId);\n }\n\n listProxies(): string[] {\n return Array.from(this.proxyStates.keys());\n }\n\n // 返回 proxyId、name、online 的列表,用于 proxy_list_response\n listProxiesWithName(): Array<{ proxyId: string; name?: string; online: boolean }> {\n return Array.from(this.proxyStates.entries()).map(([proxyId, state]) => ({\n proxyId,\n ...(state.name !== undefined ? { name: state.name } : {}),\n online: state.connectionState === \"online\",\n }));\n }\n\n getProxyName(proxyId: string): string | undefined {\n return this.proxyStates.get(proxyId)?.name;\n }\n\n // 将 sessionId 关联到 proxy\n addSessionToProxy(proxyId: string, sessionId: string): void {\n const state = this.proxyStates.get(proxyId);\n if (state) {\n state.sessions.add(sessionId);\n }\n }\n\n // 通过 sessionId 反查所属 proxyId\n getProxyForSession(sessionId: string): string | undefined {\n for (const [proxyId, state] of this.proxyStates) {\n if (state.sessions.has(sessionId)) {\n return proxyId;\n }\n }\n return undefined;\n }\n\n // 获取 proxy 关联的所有 sessionId\n getSessionsForProxy(proxyId: string): string[] {\n const state = this.proxyStates.get(proxyId);\n return state ? Array.from(state.sessions) : [];\n }\n\n // clientId 绑定方式\n bindClientById(clientId: string, proxyId: string, ws: WebSocket): boolean {\n if (!this.proxyStates.has(proxyId)) {\n return false;\n }\n this.clientBindings.set(clientId, { proxyId, ws, connectionState: \"bound\" });\n return true;\n }\n\n updateClientSocket(clientId: string, ws: WebSocket): void {\n const binding = this.clientBindings.get(clientId);\n if (binding) {\n binding.ws = ws;\n }\n }\n\n // 断开客户端 WebSocket 但保留绑定关系,重连时可恢复\n unbindClientById(clientId: string): void {\n const binding = this.clientBindings.get(clientId);\n if (binding) {\n binding.ws = null;\n }\n }\n\n getClientBinding(clientId: string): ClientBinding | undefined {\n return this.clientBindings.get(clientId);\n }\n\n // 获取绑定到指定 proxy 的所有活跃客户端 WebSocket\n getClientsForProxy(proxyId: string): WebSocket[] {\n const clients: WebSocket[] = [];\n for (const [, binding] of this.clientBindings) {\n if (binding.proxyId === proxyId && binding.ws && binding.ws.readyState === WebSocket.OPEN) {\n clients.push(binding.ws);\n }\n }\n return clients;\n }\n\n countClients(): number {\n let count = 0;\n for (const [, binding] of this.clientBindings) {\n if (binding.ws) count++;\n }\n return count;\n }\n\n addClientWs(ws: WebSocket): void {\n this.connectedClients.add(ws);\n }\n\n removeClientWs(ws: WebSocket): void {\n this.connectedClients.delete(ws);\n }\n\n getAllClientWs(): WebSocket[] {\n const clients: WebSocket[] = [];\n for (const ws of this.connectedClients) {\n if (ws.readyState === WebSocket.OPEN) {\n clients.push(ws);\n }\n }\n return clients;\n }\n\n // 获取单个 proxy 的详细状态信息\n getProxyDetail(proxyId: string):\n | {\n proxyId: string;\n name?: string;\n online: boolean;\n connectionState: ProxyConnectionState;\n sessions: string[];\n disconnectedAt: number | null;\n }\n | undefined {\n const state = this.proxyStates.get(proxyId);\n if (!state) return undefined;\n return {\n proxyId,\n ...(state.name !== undefined ? { name: state.name } : {}),\n online: state.connectionState === \"online\",\n connectionState: state.connectionState,\n sessions: Array.from(state.sessions),\n disconnectedAt: state.disconnectedAt,\n };\n }\n\n // 获取所有客户端绑定的详细信息\n getClientDetails(): Array<{\n clientId: string;\n proxyId: string;\n online: boolean;\n connectionState: ClientConnectionState;\n }> {\n const details: Array<{\n clientId: string;\n proxyId: string;\n online: boolean;\n connectionState: ClientConnectionState;\n }> = [];\n for (const [clientId, binding] of this.clientBindings) {\n details.push({\n clientId,\n proxyId: binding.proxyId,\n online:\n binding.ws !== null &&\n binding.ws !== undefined &&\n binding.ws.readyState === WebSocket.OPEN,\n connectionState: binding.connectionState,\n });\n }\n return details;\n }\n}\n","import { Router } from \"express\";\nimport type { RelayRegistry } from \"./registry.js\";\nimport { RELAY_VERSION } from \"./version.js\";\n\n// 健康检查和状态查询路由\nexport function healthRouter(registry: RelayRegistry): Router {\n const router = Router();\n\n router.get(\"/health\", (_req, res) => {\n res.json({\n status: \"ok\",\n version: RELAY_VERSION,\n uptime: process.uptime(),\n });\n });\n\n router.get(\"/status\", (_req, res) => {\n res.json({\n version: RELAY_VERSION,\n proxyCount: registry.listProxies().length,\n clientCount: registry.countClients(),\n uptime: process.uptime(),\n });\n });\n\n // 连接总览:proxy/client 计数、绑定关系\n router.get(\"/api/status\", (_req, res) => {\n res.json({\n version: RELAY_VERSION,\n proxyCount: registry.listProxies().length,\n clientCount: registry.countClients(),\n uptime: process.uptime(),\n bindings: registry.getClientDetails(),\n });\n });\n\n // 逐 proxy 详情:id、名称、在线状态、会话列表、离线时间\n router.get(\"/api/proxies\", (_req, res) => {\n const proxyIds = registry.listProxies();\n const details = proxyIds\n .map((id) => registry.getProxyDetail(id))\n .filter((d) => d !== undefined);\n res.json(details);\n });\n\n // 逐客户端详情:clientId、绑定的 proxyId、在线状态\n router.get(\"/api/clients\", (_req, res) => {\n res.json(registry.getClientDetails());\n });\n\n return router;\n}\n","import { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\ninterface PackageInfo {\n version?: string;\n}\n\nfunction readRelayVersion(): string {\n const packageJsonPath = join(dirname(fileURLToPath(import.meta.url)), \"..\", \"package.json\");\n const pkg = JSON.parse(readFileSync(packageJsonPath, \"utf-8\")) as PackageInfo;\n return pkg.version ?? \"unknown\";\n}\n\nexport const RELAY_VERSION = readRelayVersion();\n","import { WebSocket } from \"ws\";\nimport { isProxyToClientRelayControlType, RelayErrorCode, type Logger } from \"@dev-anywhere/shared\";\nimport type { RelayRegistry } from \"../registry.js\";\nimport { parseMessage, routeProxyMessage } from \"../router.js\";\nimport type { RelayChaos } from \"../chaos.js\";\n\n// binary 帧最大允许大小(10MB),超过的帧静默丢弃以防 DoS\nconst MAX_BINARY_FRAME_SIZE = 10 * 1024 * 1024;\n\n// 扩展 WebSocket 实例存储代理元数据\ninterface ProxySocket extends WebSocket {\n isAlive: boolean;\n proxyId?: string;\n}\n\n// 通知绑定到指定 proxy 的所有客户端 proxy 已离线\nfunction notifyClientsProxyOffline(\n proxyId: string,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n): void {\n const clients = registry.getClientsForProxy(proxyId);\n const msg = JSON.stringify({ type: \"proxy_offline\", proxyId });\n for (const clientWs of clients) {\n if (chaos) chaos.send(clientWs, msg, { direction: \"proxy_to_client\", type: \"proxy_offline\" });\n else clientWs.send(msg);\n }\n logger.info({ proxyId, clientCount: clients.length }, \"Notified clients of proxy offline\");\n}\n\n// 通知绑定到指定 proxy 的所有客户端 proxy 已上线\nfunction notifyClientsProxyOnline(\n proxyId: string,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n): void {\n const clients = registry.getClientsForProxy(proxyId);\n const msg = JSON.stringify({ type: \"proxy_online\", proxyId });\n for (const clientWs of clients) {\n if (chaos) chaos.send(clientWs, msg, { direction: \"proxy_to_client\", type: \"proxy_online\" });\n else clientWs.send(msg);\n }\n logger.info({ proxyId, clientCount: clients.length }, \"Notified clients of proxy online\");\n}\n\n// proxy 上线或下线时,将最新的 proxy 列表推送给所有已连接的 client。\n// 复用 proxy_list_response 消息类型,client 端已有对应处理逻辑,无需额外适配。\nfunction broadcastProxyList(registry: RelayRegistry, chaos?: RelayChaos): void {\n const proxies = registry.listProxiesWithName().map((p) => ({\n ...p,\n sessions: registry.getSessionsForProxy(p.proxyId),\n }));\n const msg = JSON.stringify({ type: \"proxy_list_response\", proxies });\n for (const clientWs of registry.getAllClientWs()) {\n if (chaos)\n chaos.send(clientWs, msg, { direction: \"proxy_to_client\", type: \"proxy_list_response\" });\n else clientWs.send(msg);\n }\n}\n\n// 处理代理端 WebSocket 连接生命周期\nexport function handleProxyConnection(\n ws: WebSocket,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n): void {\n const proxyWs = ws as ProxySocket;\n proxyWs.isAlive = true;\n\n proxyWs.on(\"pong\", () => {\n proxyWs.isAlive = true;\n });\n\n proxyWs.on(\"message\", (data: Buffer, isBinary: boolean) => {\n // Binary frames are pass-through; relay only reads the sessionId prefix for routing.\n if (isBinary) {\n if (data.length < 2 || data.length > MAX_BINARY_FRAME_SIZE) {\n logger.warn({ size: data.length }, \"Binary frame rejected: invalid size\");\n return;\n }\n const sessionIdLen = data[0];\n if (sessionIdLen === 0 || sessionIdLen > 255 || data.length < 1 + sessionIdLen) {\n logger.warn(\n { sessionIdLen, dataLen: data.length },\n \"Binary frame rejected: malformed sessionId prefix\",\n );\n return;\n }\n if (!proxyWs.proxyId) {\n logger.warn(\"Binary frame from unregistered proxy, dropped\");\n return;\n }\n\n // Forward the original buffer, including the sessionId prefix, so clients receive exact PTY bytes.\n const clients = registry.getClientsForProxy(proxyWs.proxyId);\n for (const clientWs of clients) {\n if (clientWs.readyState === WebSocket.OPEN) {\n clientWs.send(data);\n }\n }\n return;\n }\n\n const raw = data.toString();\n const result = parseMessage(raw);\n\n if (result.kind === \"control\" && result.message.type === \"proxy_register\") {\n const { proxyId, name } = result.message;\n const status = registry.registerProxy(proxyId, proxyWs, name);\n proxyWs.proxyId = proxyId;\n logger.info({ proxyId, status }, \"Proxy registered\");\n\n proxyWs.send(\n JSON.stringify({\n type: \"proxy_register_response\",\n status,\n }),\n );\n\n if (status === \"reconnected\") {\n notifyClientsProxyOnline(proxyId, registry, logger, chaos);\n }\n\n broadcastProxyList(registry, chaos);\n return;\n }\n\n if (result.kind === \"control\" && result.message.type === \"proxy_disconnect\") {\n if (proxyWs.proxyId) {\n notifyClientsProxyOffline(proxyWs.proxyId, registry, logger, chaos);\n registry.markProxyOffline(proxyWs.proxyId);\n logger.info({ proxyId: proxyWs.proxyId }, \"Proxy gracefully disconnected, marked offline\");\n proxyWs.proxyId = undefined;\n broadcastProxyList(registry, chaos);\n }\n return;\n }\n\n // proxy 重连后同步 session 列表,relay 据此建立 proxy-session 关联\n if (result.kind === \"control\" && result.message.type === \"session_sync\") {\n if (!proxyWs.proxyId) return;\n const sessions = result.message.sessions as Array<{ id: string }>;\n if (Array.isArray(sessions)) {\n for (const s of sessions) {\n registry.addSessionToProxy(proxyWs.proxyId, s.id);\n }\n logger.info({ proxyId: proxyWs.proxyId, count: sessions.length }, \"Session sync received\");\n }\n return;\n }\n\n // proxy 发给 client 的控制消息:直接转发给绑定的客户端,不进 session buffer\n if (result.kind === \"control\") {\n if (isProxyToClientRelayControlType(result.message.type)) {\n if (!proxyWs.proxyId) {\n proxyWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.NOT_REGISTERED,\n message: \"Proxy must register before sending messages\",\n }),\n );\n return;\n }\n const clients = registry.getClientsForProxy(proxyWs.proxyId);\n for (const clientWs of clients) {\n if (clientWs.readyState === WebSocket.OPEN) {\n if (chaos) {\n chaos.send(clientWs, raw, {\n direction: \"proxy_to_client\",\n type: result.message.type,\n });\n } else {\n clientWs.send(raw);\n }\n }\n }\n logger.info(\n { proxyId: proxyWs.proxyId, type: result.message.type, clientCount: clients.length },\n \"Forwarded control message from proxy to clients\",\n );\n return;\n }\n // 其他控制消息代理端不应发送\n logger.warn({ type: result.message.type }, \"Unexpected control message from proxy\");\n return;\n }\n\n if (result.kind === \"envelope\") {\n if (!proxyWs.proxyId) {\n proxyWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.NOT_REGISTERED,\n message: \"Proxy must register before sending messages\",\n }),\n );\n return;\n }\n routeProxyMessage(raw, proxyWs.proxyId, registry, logger, chaos);\n return;\n }\n\n if (result.kind === \"invalid\") {\n logger.error({ error: result.error, raw: raw.slice(0, 200) }, \"Invalid message from proxy\");\n proxyWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.INVALID_MESSAGE,\n message: `${result.error} | raw: ${raw.slice(0, 200)}`,\n }),\n );\n return;\n }\n });\n\n proxyWs.on(\"close\", () => {\n if (proxyWs.proxyId) {\n notifyClientsProxyOffline(proxyWs.proxyId, registry, logger, chaos);\n // 通过状态转换标记离线,保留所有状态等待重连\n // proxy_disconnect 已经清理过的情况下 transition 会抛异常,静默忽略\n try {\n registry.transitionProxy(proxyWs.proxyId, \"online\", \"offline\");\n } catch {\n // proxy 已被 proxy_disconnect 清理或已离线,跳过\n }\n logger.info(\n { proxyId: proxyWs.proxyId },\n \"Proxy disconnected, state preserved for reconnect\",\n );\n broadcastProxyList(registry, chaos);\n }\n });\n\n proxyWs.on(\"error\", (err) => {\n logger.error({ err, proxyId: proxyWs.proxyId }, \"Proxy WebSocket error\");\n });\n}\n","import { z } from \"zod\";\nimport {\n UserInputPayloadSchema,\n AssistantMessagePayloadSchema,\n ThinkingPayloadSchema,\n} from \"./chat.js\";\nimport { ToolUseRequestPayloadSchema, ToolResultPayloadSchema } from \"./tool.js\";\nimport {\n SessionCreatePayloadSchema,\n SessionListPayloadSchema,\n SessionSwitchPayloadSchema,\n SessionTerminatePayloadSchema,\n SessionStatusPayloadSchema,\n} from \"./session.js\";\nimport {\n HeartbeatPayloadSchema,\n AuthPayloadSchema,\n SyncRequestPayloadSchema,\n SyncResponsePayloadSchema,\n} from \"./system.js\";\n\n// 信封基础字段:序列号、会话ID、时间戳、来源、协议版本\nconst BaseEnvelopeFields = {\n seq: z.number().int().nonnegative(),\n sessionId: z.string(),\n timestamp: z.number(),\n source: z.enum([\"proxy\", \"client\"]),\n version: z.string(),\n};\n\n// 按 type 字段区分的 discriminatedUnion 信封\nexport const MessageEnvelopeSchema = z.discriminatedUnion(\"type\", [\n // chat (3)\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"user_input\"),\n payload: UserInputPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"assistant_message\"),\n payload: AssistantMessagePayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"thinking\"),\n payload: ThinkingPayloadSchema,\n }),\n // tool (4): 工具审批决策属于 relay control,不进入会话消息信封。\n // tool_use_request: 审批流请求(proxy → client),toolId 是 approval requestId\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"tool_use_request\"),\n payload: ToolUseRequestPayloadSchema,\n }),\n // tool_result: 工具执行结果(proxy → client),toolId 对应 assistant_tool_use / tool_use_request 的 toolId\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"tool_result\"),\n payload: ToolResultPayloadSchema,\n }),\n // assistant_tool_use: 纯展示型工具调用(proxy → client),区别于 tool_use_request 无审批语义\n // payload 结构复用 ToolUseRequestPayloadSchema;toolId 是 Claude 分配的 tool_use id\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"assistant_tool_use\"),\n payload: ToolUseRequestPayloadSchema,\n }),\n // session (5)\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"session_create\"),\n payload: SessionCreatePayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"session_list\"),\n payload: SessionListPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"session_switch\"),\n payload: SessionSwitchPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"session_terminate\"),\n payload: SessionTerminatePayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"session_status\"),\n payload: SessionStatusPayloadSchema,\n }),\n // system (5)\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"heartbeat\"),\n payload: HeartbeatPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"auth\"),\n payload: AuthPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"sync_request\"),\n payload: SyncRequestPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"sync_response\"),\n payload: SyncResponsePayloadSchema,\n }),\n]);\n\nexport type MessageEnvelope = z.infer<typeof MessageEnvelopeSchema>;\n\nexport type MessageType = MessageEnvelope[\"type\"];\n\nexport type MessageSource = MessageEnvelope[\"source\"];\n","import { z } from \"zod\";\n\n// 用户输入消息\nexport const UserInputPayloadSchema = z.object({\n text: z.string().min(1),\n messageId: z.string().min(1).optional(),\n});\n\nexport type UserInputPayload = z.infer<typeof UserInputPayloadSchema>;\n\n// 助手回复消息,isPartial 标识是否为流式中间结果\nexport const AssistantMessagePayloadSchema = z.object({\n text: z.string(),\n isPartial: z.boolean(),\n});\n\nexport type AssistantMessagePayload = z.infer<typeof AssistantMessagePayloadSchema>;\n\n// 思考过程消息\nexport const ThinkingPayloadSchema = z.object({\n text: z.string(),\n});\n\nexport type ThinkingPayload = z.infer<typeof ThinkingPayloadSchema>;\n","import { z } from \"zod\";\n\n// 工具调用请求\nexport const ToolUseRequestPayloadSchema = z.object({\n toolName: z.string(),\n toolId: z.string(),\n parameters: z.record(z.string(), z.unknown()),\n});\n\nexport type ToolUseRequestPayload = z.infer<typeof ToolUseRequestPayloadSchema>;\n\n// 工具调用批准,whitelistTool 为 true 时将该工具加入会话级白名单自动审批\nexport const ToolApprovePayloadSchema = z.object({\n toolId: z.string(),\n whitelistTool: z.boolean().optional(),\n});\n\nexport type ToolApprovePayload = z.infer<typeof ToolApprovePayloadSchema>;\n\n// 工具调用拒绝\nexport const ToolDenyPayloadSchema = z.object({\n toolId: z.string(),\n reason: z.string().optional(),\n});\n\nexport type ToolDenyPayload = z.infer<typeof ToolDenyPayloadSchema>;\n\n// 工具调用结果\nexport const ToolResultPayloadSchema = z.object({\n toolId: z.string(),\n result: z.unknown(),\n isError: z.boolean(),\n});\n\nexport type ToolResultPayload = z.infer<typeof ToolResultPayloadSchema>;\n","import { z } from \"zod\";\n\nconst sessionStateValues = [\"idle\", \"working\", \"waiting_approval\", \"error\", \"terminated\"] as const;\nconst providerValues = [\"claude\", \"codex\"] as const;\nconst ptyOwnerValues = [\"local-terminal\", \"proxy-hosted\"] as const;\nconst agentStatusPhaseValues = [\n \"idle\",\n \"thinking\",\n \"tool_use\",\n \"outputting\",\n \"waiting_permission\",\n \"error\",\n] as const;\n\n// 会话信息,用于会话列表展示\n// lastActive: 最近一次状态变更或运行时活动时间戳 (ms), 用于列表\"最近活动 N 分钟前\"显示, 可选\nexport const SessionInfoSchema = z.object({\n sessionId: z.string(),\n name: z.string().optional(),\n state: z.enum(sessionStateValues),\n mode: z.enum([\"pty\", \"json\"]).optional(),\n provider: z.enum(providerValues),\n // PTY 尺寸所有权:\n // - local-terminal: 本地 terminal 进程持有真实 PTY,Web 只按原始 cols/rows 展示\n // - proxy-hosted: serve 内托管 PTY,Web 可按视口请求 resize\n ptyOwner: z.enum(ptyOwnerValues).optional(),\n lastActive: z.number().optional(),\n});\nexport type SessionInfo = z.infer<typeof SessionInfoSchema>;\n\n// 创建会话\n// streamDelta: client 端系统设置\"逐字流式\"toggle,true 时 proxy spawn 带 --include-partial-messages\nexport const SessionCreatePayloadSchema = z.object({\n name: z.string().optional(),\n cwd: z.string().optional(),\n streamDelta: z.boolean().optional(),\n});\n\nexport type SessionCreatePayload = z.infer<typeof SessionCreatePayloadSchema>;\n\n// 会话列表\nexport const SessionListPayloadSchema = z.object({\n sessions: z.array(SessionInfoSchema),\n});\n\nexport type SessionListPayload = z.infer<typeof SessionListPayloadSchema>;\n\n// 切换会话\nexport const SessionSwitchPayloadSchema = z.object({\n sessionId: z.string(),\n});\n\nexport type SessionSwitchPayload = z.infer<typeof SessionSwitchPayloadSchema>;\n\n// 终止会话\nexport const SessionTerminatePayloadSchema = z.object({\n sessionId: z.string(),\n});\n\nexport type SessionTerminatePayload = z.infer<typeof SessionTerminatePayloadSchema>;\n\n// 会话状态变更\n// lastActive: 触发本次状态迁移或活动刷新的时间戳 (ms),用于列表相对时间显示。\nexport const SessionStatusPayloadSchema = z.object({\n sessionId: z.string(),\n state: z.enum(sessionStateValues),\n lastActive: z.number(),\n});\n\nexport type SessionStatusPayload = z.infer<typeof SessionStatusPayloadSchema>;\n\n// PTY 语义状态事件,描述当前 PTY 处于何种状态\nexport const PtyStatePayloadSchema = z.object({\n state: z.enum([\"working\", \"turn_complete\", \"approval_wait\", \"mid_pause\"]),\n title: z.string().optional(),\n tool: z.string().optional(),\n});\nexport type PtyStatePayload = z.infer<typeof PtyStatePayloadSchema>;\n\nexport const AgentStatusPayloadSchema = z.object({\n provider: z.enum(providerValues),\n phase: z.enum(agentStatusPhaseValues),\n seq: z.number().int().nonnegative(),\n updatedAt: z.number(),\n toolName: z.string().optional(),\n toolInput: z.record(z.string(), z.unknown()).optional(),\n permissionRequest: z\n .object({\n requestId: z.string(),\n toolName: z.string(),\n input: z.record(z.string(), z.unknown()),\n })\n .optional(),\n permissionResolution: z\n .object({\n requestId: z.string(),\n outcome: z.enum([\"allow\", \"deny\"]),\n })\n .optional(),\n summary: z.string().optional(),\n});\nexport type AgentStatusPayload = z.infer<typeof AgentStatusPayloadSchema>;\n","import { z } from \"zod\";\n\n// 心跳消息,空 payload\nexport const HeartbeatPayloadSchema = z.object({});\n\nexport type HeartbeatPayload = z.infer<typeof HeartbeatPayloadSchema>;\n\n// 认证消息,支持配对码和 token 两种方式\nexport const AuthPayloadSchema = z.object({\n pairingCode: z.string().optional(),\n token: z.string().optional(),\n});\n\nexport type AuthPayload = z.infer<typeof AuthPayloadSchema>;\n\n// 同步请求,客户端发送已收到的最大序列号\nexport const SyncRequestPayloadSchema = z.object({\n lastSeq: z.number().int().nonnegative(),\n});\n\nexport type SyncRequestPayload = z.infer<typeof SyncRequestPayloadSchema>;\n\n// 同步响应,使用 z.unknown 数组避免循环引用;恢复协议稳定后再收紧类型\nexport const SyncResponsePayloadSchema = z.object({\n messages: z.array(z.record(z.string(), z.unknown())),\n});\n\nexport type SyncResponsePayload = z.infer<typeof SyncResponsePayloadSchema>;\n","import { z } from \"zod\";\nimport { AgentStatusPayloadSchema, PtyStatePayloadSchema } from \"./session.js\";\nimport { ToolApprovePayloadSchema, ToolDenyPayloadSchema } from \"./tool.js\";\nimport { RelayErrorCode } from \"../constants/relay-errors.js\";\nimport { ControlErrorCode } from \"../constants/control-errors.js\";\n\n// 控制消息中复用的子类型\nexport const ProxyInfoSchema = z.object({\n proxyId: z.string(),\n name: z.string().optional(),\n online: z.boolean(),\n sessions: z.array(z.string()).optional(),\n});\nexport type ProxyInfo = z.infer<typeof ProxyInfoSchema>;\n\nexport const AgentCliAvailabilitySchema = z.object({\n available: z.boolean(),\n command: z.string().optional(),\n error: z.string().optional(),\n suggestions: z.array(z.string()).optional(),\n});\nexport type AgentCliAvailability = z.infer<typeof AgentCliAvailabilitySchema>;\n\nexport const AgentCliStatusSchema = z.object({\n claude: AgentCliAvailabilitySchema,\n codex: AgentCliAvailabilitySchema,\n});\nexport type AgentCliStatus = z.infer<typeof AgentCliStatusSchema>;\n\nexport const DirEntrySchema = z.object({ name: z.string(), isDir: z.boolean() });\nexport type DirEntry = z.infer<typeof DirEntrySchema>;\n\nexport const FileTreeGroupSchema = z.object({\n path: z.string(),\n entries: z.array(DirEntrySchema),\n});\nexport type FileTreeGroup = z.infer<typeof FileTreeGroupSchema>;\n\nexport const CommandEntrySchema = z.object({\n name: z.string(),\n description: z.string(),\n argumentHint: z.string().optional(),\n source: z.string(),\n});\nexport type CommandEntry = z.infer<typeof CommandEntrySchema>;\n\nexport const HistorySessionSchema = z.object({\n id: z.string(),\n title: z.string(),\n projectDir: z.string(),\n updatedAt: z.number(),\n provider: z.enum([\"claude\", \"codex\"]).optional(),\n});\nexport type HistorySession = z.infer<typeof HistorySessionSchema>;\n\nconst SessionHistoryMessageSchema = z.object({\n role: z.enum([\"user\", \"assistant\"]),\n text: z.string(),\n timestamp: z.number().optional(),\n cursor: z.string().optional(),\n});\n\ntype RelayControlDirection = \"proxy_to_client\" | \"client_to_proxy\";\ntype EmptyShape = Record<never, never>;\nconst RequestIdShape = { requestId: z.string().min(1).optional() };\nconst ControlErrorCodeSchema = z.enum(\n Object.values(ControlErrorCode) as [ControlErrorCode, ...ControlErrorCode[]],\n);\nconst RequestErrorShape = {\n error: z.string().optional(),\n errorCode: ControlErrorCodeSchema.optional(),\n};\nconst ClipboardImageMimeTypeSchema = z.enum([\"image/png\", \"image/jpeg\", \"image/webp\", \"image/gif\"]);\n\ntype ControlDefinition<T extends string, S extends z.ZodRawShape> = {\n type: T;\n directions: ReadonlySet<RelayControlDirection>;\n schema: z.ZodObject<{ type: z.ZodLiteral<T> } & S>;\n};\n\nfunction control<T extends string>(type: T): ControlDefinition<T, EmptyShape>;\nfunction control<T extends string>(\n type: T,\n shape: undefined,\n directions: RelayControlDirection | RelayControlDirection[],\n): ControlDefinition<T, EmptyShape>;\nfunction control<T extends string, S extends z.ZodRawShape>(\n type: T,\n shape: S,\n directions?: RelayControlDirection | RelayControlDirection[],\n): ControlDefinition<T, S>;\nfunction control<T extends string, S extends z.ZodRawShape>(\n type: T,\n shape?: S,\n directions?: RelayControlDirection | RelayControlDirection[],\n): ControlDefinition<T, S | EmptyShape> {\n return {\n type,\n directions: new Set(Array.isArray(directions) ? directions : directions ? [directions] : []),\n schema: z.object({\n type: z.literal(type),\n ...(shape ?? {}),\n }) as z.ZodObject<{ type: z.ZodLiteral<T> } & (S | EmptyShape)>,\n };\n}\n\n// 中转服务器控制消息,独立于 MessageEnvelope 的传输层协议\nconst relayControlDefinitions = [\n control(\"proxy_register\", {\n proxyId: z.string().min(1),\n name: z.string().optional(),\n }),\n control(\"proxy_register_response\", {\n status: z.enum([\"new\", \"reconnected\"]),\n }),\n control(\"proxy_list_request\", RequestIdShape),\n control(\"proxy_list_response\", {\n ...RequestIdShape,\n proxies: z.array(ProxyInfoSchema),\n }),\n control(\"proxy_select\", { ...RequestIdShape, proxyId: z.string().min(1) }),\n control(\"proxy_select_response\", {\n ...RequestIdShape,\n success: z.boolean(),\n proxyId: z.string().optional(),\n ...RequestErrorShape,\n }),\n control(\"relay_error\", {\n code: z.enum(Object.values(RelayErrorCode) as [RelayErrorCode, ...RelayErrorCode[]]),\n message: z.string(),\n }),\n\n // 客户端注册协议\n control(\"client_register\", {\n clientId: z.string().min(1),\n }),\n control(\"client_register_response\", {\n status: z.enum([\"restored\", \"proxy_offline\", \"new\"]),\n proxyId: z.string().optional(),\n }),\n\n // Proxy 离线通知\n control(\"proxy_offline\", {\n proxyId: z.string(),\n }),\n\n // Proxy 主动断开,relay 立即清理资源\n control(\"proxy_disconnect\", {\n proxyId: z.string().min(1),\n }),\n\n // Proxy 重连后通知 client 恢复\n control(\"proxy_online\", {\n proxyId: z.string().min(1),\n }),\n\n // 目录列表请求与响应\n control(\n \"dir_list_request\",\n {\n proxyId: z.string().min(1).optional(),\n ...RequestIdShape,\n path: z.string(),\n },\n \"client_to_proxy\",\n ),\n control(\n \"dir_list_response\",\n { ...RequestIdShape, ...RequestErrorShape, entries: z.array(DirEntrySchema), path: z.string() },\n \"proxy_to_client\",\n ),\n\n // 目录创建请求与响应\n control(\"dir_create_request\", { ...RequestIdShape, path: z.string() }, \"client_to_proxy\"),\n control(\n \"dir_create_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n path: z.string(),\n success: z.boolean(),\n },\n \"proxy_to_client\",\n ),\n\n // 命令列表推送,proxy 将可用命令列表推给 client\n control(\"command_list_push\", { commands: z.array(CommandEntrySchema) }, \"proxy_to_client\"),\n\n // 文件树推送: 按目录分组, 首组 path 即为 session cwd\n // 前端直接把每组写入 tree[path], 与 dir_list_response 共享 cache slot\n control(\n \"file_tree_push\",\n {\n groups: z.array(FileTreeGroupSchema),\n },\n \"proxy_to_client\",\n ),\n\n // 会话列表请求与权限模式变更\n control(\"session_list\", undefined, [\"client_to_proxy\", \"proxy_to_client\"]),\n control(\n \"permission_mode_change\",\n {\n mode: z.enum([\"default\", \"auto_accept\", \"plan\"]),\n // sessionId 可选:传入时 proxy 按该会话的 mode 分叉(PTY 发 Tab ANSI),未传走全局日志行为\n sessionId: z.string().optional(),\n },\n \"client_to_proxy\",\n ),\n\n // 会话历史浏览\n control(\"session_history_request\", RequestIdShape, \"client_to_proxy\"),\n control(\n \"session_history_response\",\n { ...RequestIdShape, sessions: z.array(HistorySessionSchema) },\n \"proxy_to_client\",\n ),\n\n // PTY 语义状态,从 Envelope 迁移到 Control 层\n control(\n \"pty_state\",\n { sessionId: z.string(), payload: PtyStatePayloadSchema },\n \"proxy_to_client\",\n ),\n\n // Provider 语义状态,来自 Claude/Codex hook 等结构化事件,不从 PTY 字节推断\n control(\n \"agent_status\",\n { sessionId: z.string(), payload: AgentStatusPayloadSchema },\n \"proxy_to_client\",\n ),\n\n // 终端标题变化,proxy -> client\n control(\"terminal_title\", { sessionId: z.string(), title: z.string() }, \"proxy_to_client\"),\n\n // 终端尺寸变化,proxy -> client\n control(\n \"terminal_resize\",\n { sessionId: z.string(), cols: z.number().int().positive(), rows: z.number().int().positive() },\n \"proxy_to_client\",\n ),\n control(\n \"terminal_resize_request\",\n { sessionId: z.string(), cols: z.number().int().positive(), rows: z.number().int().positive() },\n \"client_to_proxy\",\n ),\n\n // 远程终止 JSON 会话,client -> proxy\n control(\"session_terminate\", { sessionId: z.string() }, \"client_to_proxy\"),\n\n // 中断当前 turn,client -> proxy,SIGINT 到 worker 进程让 claude CLI abort 当前流\n control(\"session_worker_abort\", { sessionId: z.string() }, \"client_to_proxy\"),\n\n // turn 完成信号,proxy -> client,对应 claude stream-json 的 result 事件\n control(\n \"turn_result\",\n {\n sessionId: z.string(),\n success: z.boolean(),\n isError: z.boolean(),\n // stream-json result.result 是本轮最终文本。assistant_message 流丢失或 CLI 未发增量时,\n // Web 用它作为 JSON 模式兜底展示,避免 turn 已结束但界面空白。\n result: z.string().optional(),\n },\n \"proxy_to_client\",\n ),\n\n // 客户端发送到 PTY 的原始字节(ANSI 序列),不追加换行\n control(\n \"remote_input_raw\",\n { sessionId: z.string().min(1), data: z.string() },\n \"client_to_proxy\",\n ),\n control(\n \"clipboard_image_upload\",\n {\n ...RequestIdShape,\n sessionId: z.string().min(1),\n mimeType: ClipboardImageMimeTypeSchema,\n dataBase64: z.string().min(1),\n fileName: z.string().optional(),\n },\n \"client_to_proxy\",\n ),\n control(\n \"clipboard_image_upload_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n sessionId: z.string().min(1),\n success: z.boolean(),\n path: z.string(),\n },\n \"proxy_to_client\",\n ),\n\n // 客户端询问 proxy 的环境信息 (home 路径等), client -> proxy -> response\n // FilePathPicker 用 homePath 作为 select 模式下的默认起点, 新建会话时打开即可浏览\n control(\"proxy_info_request\", RequestIdShape, \"client_to_proxy\"),\n control(\n \"proxy_info\",\n { ...RequestIdShape, homePath: z.string(), agentCli: AgentCliStatusSchema },\n \"proxy_to_client\",\n ),\n control(\n \"agent_cli_config_update\",\n { ...RequestIdShape, provider: z.enum([\"claude\", \"codex\"]), path: z.string().min(1) },\n \"client_to_proxy\",\n ),\n control(\n \"agent_cli_config_update_response\",\n {\n ...RequestIdShape,\n provider: z.enum([\"claude\", \"codex\"]),\n agentCli: AgentCliStatusSchema.optional(),\n ...RequestErrorShape,\n },\n \"proxy_to_client\",\n ),\n\n // 远程创建 JSON 会话,client -> proxy -> response\n control(\n \"session_create\",\n {\n ...RequestIdShape,\n cwd: z.string(),\n provider: z.enum([\"claude\", \"codex\"]),\n mode: z.enum([\"json\", \"pty\"]).optional(),\n resumeSessionId: z.string().optional(),\n // 透传给 claude CLI 的 --permission-mode, undefined 时 proxy 兜底为 \"default\"\n permissionMode: z\n .enum([\"default\", \"auto\", \"acceptEdits\", \"plan\", \"bypassPermissions\", \"dontAsk\"])\n .optional(),\n },\n \"client_to_proxy\",\n ),\n control(\n \"session_create_response\",\n {\n ...RequestIdShape,\n sessionId: z.string(),\n mode: z.enum([\"json\", \"pty\"]).optional(),\n provider: z.enum([\"claude\", \"codex\"]).optional(),\n ptyOwner: z.enum([\"local-terminal\", \"proxy-hosted\"]).optional(),\n ...RequestErrorShape,\n },\n \"proxy_to_client\",\n ),\n\n // 客户端请求会话历史消息,client -> proxy\n control(\n \"session_messages_request\",\n {\n ...RequestIdShape,\n sessionId: z.string(),\n limit: z.number().int().min(1).max(200).optional(),\n before: z.string().optional(),\n },\n \"client_to_proxy\",\n ),\n\n // 客户端请求会话资源(命令列表 + 文件树),client -> proxy\n control(\n \"session_resources_request\",\n { ...RequestIdShape, sessionId: z.string() },\n \"client_to_proxy\",\n ),\n control(\n \"session_resources_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n sessionId: z.string(),\n commands: z.array(CommandEntrySchema),\n groups: z.array(FileTreeGroupSchema),\n },\n \"proxy_to_client\",\n ),\n\n // 客户端请求当前 provider 语义状态;不经 relay 缓存,由 proxy 返回当前值\n control(\n \"agent_status_request\",\n { ...RequestIdShape, sessionId: z.string().optional() },\n \"client_to_proxy\",\n ),\n control(\n \"agent_status_response\",\n {\n ...RequestIdShape,\n statuses: z.array(z.object({ sessionId: z.string(), payload: AgentStatusPayloadSchema })),\n },\n \"proxy_to_client\",\n ),\n\n // 客户端确认已收到审批请求;proxy 只记录送达状态,不把它当成用户决策\n control(\n \"permission_request_delivered\",\n { sessionId: z.string(), requestId: z.string() },\n \"client_to_proxy\",\n ),\n control(\n \"tool_approve\",\n { sessionId: z.string(), payload: ToolApprovePayloadSchema },\n \"client_to_proxy\",\n ),\n control(\n \"tool_deny\",\n { sessionId: z.string(), payload: ToolDenyPayloadSchema },\n \"client_to_proxy\",\n ),\n\n // proxy 确认用户决策已进入 provider/worker 路径;web 用它更新审批卡片状态\n control(\n \"permission_decision_result\",\n {\n sessionId: z.string(),\n requestId: z.string(),\n outcome: z.enum([\"allow\", \"deny\"]),\n delivered: z.boolean(),\n message: z.string().optional(),\n },\n \"proxy_to_client\",\n ),\n\n // proxy 推送当前 pending 的工具审批列表,client 据此恢复审批卡片\n control(\n \"pending_approvals_push\",\n {\n sessionId: z.string(),\n approvals: z.array(\n z.object({\n requestId: z.string(),\n toolName: z.string(),\n input: z.record(z.string(), z.unknown()),\n }),\n ),\n },\n \"proxy_to_client\",\n ),\n\n // 恢复会话时推送历史消息,proxy -> client\n control(\n \"session_history_messages\",\n {\n ...RequestIdShape,\n sessionId: z.string(),\n before: z.string().optional(),\n messages: z.array(SessionHistoryMessageSchema),\n hasMore: z.boolean().optional(),\n nextBefore: z.string().optional(),\n },\n \"proxy_to_client\",\n ),\n\n // proxy 重连后同步活跃 session 列表给 relay\n control(\"session_sync\", {\n sessions: z.array(\n z.object({\n id: z.string(),\n mode: z.enum([\"pty\", \"json\"]),\n provider: z.enum([\"claude\", \"codex\"]),\n ptyOwner: z.enum([\"local-terminal\", \"proxy-hosted\"]).optional(),\n state: z.string(),\n }),\n ),\n }),\n\n // PTY 会话订阅,client -> proxy,触发 terminal serialize() 返回当前状态\n control(\n \"session_subscribe\",\n { sessionId: z.string(), requestId: z.string().optional() },\n \"client_to_proxy\",\n ),\n\n // PTY 会话快照,proxy -> client,serialize() 的全量终端状态\n control(\n \"session_snapshot\",\n {\n sessionId: z.string(),\n cols: z.number().int().positive(),\n rows: z.number().int().positive(),\n data: z.string(),\n outputSeq: z.number().int().nonnegative(),\n requestId: z.string().optional(),\n },\n \"proxy_to_client\",\n ),\n] as const;\n\nconst relayControlSchemas = relayControlDefinitions.map((definition) => definition.schema) as [\n (typeof relayControlDefinitions)[number][\"schema\"],\n ...Array<(typeof relayControlDefinitions)[number][\"schema\"]>,\n];\n\nexport const RelayControlSchema = z.discriminatedUnion(\"type\", relayControlSchemas);\n\nexport type RelayControlMessage = z.infer<typeof RelayControlSchema>;\nexport type RelayControlType = RelayControlMessage[\"type\"];\n\nexport const ProxyToClientRelayControlTypes = new Set(\n relayControlDefinitions\n .filter((definition) => definition.directions.has(\"proxy_to_client\"))\n .map((definition) => definition.type),\n);\n\nexport function isProxyToClientRelayControlType(type: RelayControlType): boolean {\n return ProxyToClientRelayControlTypes.has(type);\n}\n\nexport const ClientToProxyRelayControlTypes = new Set(\n relayControlDefinitions\n .filter((definition) => definition.directions.has(\"client_to_proxy\"))\n .map((definition) => definition.type),\n);\n\nexport function isClientToProxyRelayControlType(type: RelayControlType): boolean {\n return ClientToProxyRelayControlTypes.has(type);\n}\n","// relay 实际发出的 6 个错误码。schema 侧 RelayControlSchema.relay_error.code 用 z.enum 收紧,\n// handler 侧用这个常量引用避免裸字面量拼错。\nexport const RelayErrorCode = {\n NOT_REGISTERED: \"NOT_REGISTERED\",\n NOT_BOUND: \"NOT_BOUND\",\n PROXY_OFFLINE: \"PROXY_OFFLINE\",\n INVALID_MESSAGE: \"INVALID_MESSAGE\",\n UNSUPPORTED: \"UNSUPPORTED\",\n INVALID_RANGE: \"INVALID_RANGE\",\n} as const;\n\nexport type RelayErrorCode = (typeof RelayErrorCode)[keyof typeof RelayErrorCode];\n","export const ControlErrorCode = {\n INVALID_PATH: \"INVALID_PATH\",\n PATH_NOT_FOUND: \"PATH_NOT_FOUND\",\n PATH_NOT_DIRECTORY: \"PATH_NOT_DIRECTORY\",\n PATH_ACCESS_DENIED: \"PATH_ACCESS_DENIED\",\n PROXY_OFFLINE: \"PROXY_OFFLINE\",\n SESSION_NOT_FOUND: \"SESSION_NOT_FOUND\",\n PROVIDER_UNSUPPORTED: \"PROVIDER_UNSUPPORTED\",\n WORKER_START_FAILED: \"WORKER_START_FAILED\",\n PROCESS_START_FAILED: \"PROCESS_START_FAILED\",\n UNKNOWN: \"UNKNOWN\",\n} as const;\n\nexport type ControlErrorCode = (typeof ControlErrorCode)[keyof typeof ControlErrorCode];\n","import {\n lstatSync,\n mkdirSync,\n readdirSync,\n renameSync,\n statSync,\n symlinkSync,\n unlinkSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { basename, join } from \"node:path\";\nimport pino from \"pino\";\n\nexport type { Logger } from \"pino\";\n\nexport interface CreateLoggerOptions {\n name: string;\n level?: string;\n logDir?: string;\n stdout?: boolean;\n silent?: boolean;\n}\n\nconst DEFAULT_LOG_DIR = `${homedir()}/.dev-anywhere/logs`;\nconst DEFAULT_LOG_RETENTION = 50;\n\nconst PROCESS_LOG_RUN_ID = sanitizeRunId(\n process.env.DEV_ANYWHERE_LOG_RUN_ID ??\n `${new Date().toISOString().replace(/[:.]/g, \"-\")}-${process.pid}`,\n);\n\nfunction sanitizeRunId(runId: string): string {\n return runId.replace(/[^a-zA-Z0-9._-]/g, \"_\");\n}\n\nfunction linkLatestLog(logDir: string, name: string, filePath: string, runId: string): void {\n const latestPath = join(logDir, `${name}.log`);\n\n try {\n const stat = lstatSync(latestPath);\n if (stat.isSymbolicLink()) {\n unlinkSync(latestPath);\n } else {\n renameSync(latestPath, join(logDir, `${name}-legacy-${runId}.log`));\n }\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"ENOENT\") return;\n }\n\n try {\n symlinkSync(basename(filePath), latestPath);\n } catch {\n // 日志本体仍然写入 run-specific 文件;latest 链接失败不应阻塞服务启动。\n }\n}\n\nfunction resolveRetention(): number {\n const raw = process.env.DEV_ANYWHERE_LOG_RETENTION;\n if (!raw) return DEFAULT_LOG_RETENTION;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_LOG_RETENTION;\n}\n\nfunction pruneOldLogs(logDir: string, name: string, currentFilePath: string): void {\n const keep = resolveRetention();\n if (keep === 0) return;\n\n const currentFileName = basename(currentFilePath);\n const prefix = `${name}-`;\n const candidates = readdirSync(logDir)\n .filter(\n (entry) => entry.startsWith(prefix) && entry.endsWith(\".log\") && entry !== currentFileName,\n )\n .map((entry) => {\n const path = join(logDir, entry);\n try {\n return { path, mtimeMs: statSync(path).mtimeMs };\n } catch {\n return null;\n }\n })\n .filter((entry): entry is { path: string; mtimeMs: number } => entry !== null)\n .sort((a, b) => b.mtimeMs - a.mtimeMs);\n\n for (const stale of candidates.slice(Math.max(0, keep - 1))) {\n try {\n unlinkSync(stale.path);\n } catch {\n // 日志清理失败不能影响主进程启动。\n }\n }\n}\n\nexport function createLogger(options: CreateLoggerOptions): pino.Logger {\n const {\n name,\n level = \"info\",\n logDir = DEFAULT_LOG_DIR,\n stdout = false,\n silent = false,\n } = options;\n\n if (silent) {\n return pino({ level: \"silent\" });\n }\n\n mkdirSync(logDir, { recursive: true });\n\n const runId = PROCESS_LOG_RUN_ID;\n const filePath = join(logDir, `${name}-${runId}.log`);\n linkLatestLog(logDir, name, filePath, runId);\n pruneOldLogs(logDir, name, filePath);\n const streams: pino.StreamEntry[] = [{ stream: pino.destination(filePath) }];\n\n if (stdout) {\n streams.unshift({ stream: process.stdout });\n }\n\n return pino({ level }, pino.multistream(streams));\n}\n","import { MessageEnvelopeSchema, RelayControlSchema, RelayErrorCode } from \"@dev-anywhere/shared\";\nimport type { RelayControlMessage } from \"@dev-anywhere/shared\";\nimport type { MessageEnvelope } from \"@dev-anywhere/shared\";\nimport { WebSocket } from \"ws\";\nimport type { Logger } from \"@dev-anywhere/shared\";\nimport type { RelayRegistry } from \"./registry.js\";\nimport type { RelayChaos } from \"./chaos.js\";\n\n// 消息解析结果:控制消息、信封消息或无效消息\ntype ParseResult =\n | { kind: \"control\"; message: RelayControlMessage }\n | { kind: \"envelope\"; message: MessageEnvelope; raw: string }\n | { kind: \"invalid\"; error: string };\n\n// 解析 WebSocket 消息,按优先级尝试 RelayControl 和 MessageEnvelope\nexport function parseMessage(data: string): ParseResult {\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch {\n return { kind: \"invalid\", error: \"Invalid JSON\" };\n }\n\n const controlResult = RelayControlSchema.safeParse(parsed);\n if (controlResult.success) {\n return { kind: \"control\", message: controlResult.data };\n }\n\n const envelopeResult = MessageEnvelopeSchema.safeParse(parsed);\n if (envelopeResult.success) {\n return { kind: \"envelope\", message: envelopeResult.data, raw: data };\n }\n\n return { kind: \"invalid\", error: \"Message matches neither RelayControl nor MessageEnvelope\" };\n}\n\n// 将 proxy 发来的 MessageEnvelope 转发给绑定的 client\n// relay 无状态,不缓冲消息,直接透传\nexport function routeProxyMessage(\n raw: string,\n proxyId: string,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n): void {\n const result = parseMessage(raw);\n\n if (result.kind === \"invalid\") {\n logger.warn({ proxyId, error: result.error }, \"Invalid message from proxy\");\n return;\n }\n\n if (result.kind === \"control\") {\n logger.warn({ proxyId }, \"Control message in routeProxyMessage, should be handled by handler\");\n return;\n }\n\n const { message } = result;\n const { sessionId } = message;\n\n // 跟踪该 proxy 拥有的 session\n registry.addSessionToProxy(proxyId, sessionId);\n\n // 转发给所有绑定的客户端\n const clients = registry.getClientsForProxy(proxyId);\n for (const clientWs of clients) {\n if (clientWs.readyState === WebSocket.OPEN) {\n if (chaos) chaos.send(clientWs, raw, { direction: \"proxy_to_client\", type: message.type });\n else clientWs.send(raw);\n }\n }\n}\n\n// 将 client 发来的 MessageEnvelope 转发给绑定的 proxy\n// proxyId 由调用方从 clientId 绑定中解析后传入\nexport function routeClientMessage(\n raw: string,\n proxyId: string,\n clientWs: WebSocket,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n): void {\n const result = parseMessage(raw);\n\n if (result.kind === \"invalid\") {\n logger.warn({ error: result.error }, \"Invalid message from client\");\n return;\n }\n\n if (result.kind === \"control\") {\n logger.warn(\"Control message in routeClientMessage, should be handled by handler\");\n return;\n }\n\n const proxyWs = registry.getProxy(proxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) {\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.PROXY_OFFLINE,\n message: `Proxy ${proxyId} is not available`,\n }),\n );\n return;\n }\n\n if (chaos) chaos.send(proxyWs, raw, { direction: \"client_to_proxy\", type: result.message.type });\n else proxyWs.send(raw);\n}\n","import { WebSocket } from \"ws\";\nimport {\n ControlErrorCode,\n isClientToProxyRelayControlType,\n RelayErrorCode,\n type Logger,\n} from \"@dev-anywhere/shared\";\nimport { nanoid } from \"nanoid\";\nimport type { RelayRegistry } from \"../registry.js\";\nimport { parseMessage, routeClientMessage } from \"../router.js\";\nimport type { RelayChaos } from \"../chaos.js\";\n\n// 扩展 WebSocket 实例存储客户端元数据\ninterface ClientSocket extends WebSocket {\n isAlive: boolean;\n clientId?: string;\n boundProxyId?: string;\n}\n\n// 处理 client_register 消息:三种状态 restored / proxy_offline / new。\n// relay 不缓存输出;恢复由 proxy 重新推送 session_list/agent_status/snapshot 等状态。\nfunction handleClientRegister(\n clientId: string,\n clientWs: ClientSocket,\n registry: RelayRegistry,\n logger: Logger,\n): void {\n clientWs.clientId = clientId;\n\n const binding = registry.getClientBinding(clientId);\n\n if (!binding) {\n clientWs.send(\n JSON.stringify({\n type: \"client_register_response\",\n status: \"new\",\n }),\n );\n logger.info({ clientId, status: \"new\" }, \"Client registered\");\n return;\n }\n\n const { proxyId } = binding;\n registry.updateClientSocket(clientId, clientWs);\n clientWs.boundProxyId = proxyId;\n\n if (!registry.isProxyOnline(proxyId)) {\n clientWs.send(\n JSON.stringify({\n type: \"client_register_response\",\n status: \"proxy_offline\",\n proxyId,\n }),\n );\n logger.info({ clientId, proxyId, status: \"proxy_offline\" }, \"Client registered\");\n return;\n }\n\n // proxy 在线,恢复绑定(relay 无状态,不做增量回放)\n clientWs.send(\n JSON.stringify({\n type: \"client_register_response\",\n status: \"restored\",\n proxyId,\n }),\n );\n\n logger.info({ clientId, proxyId, status: \"restored\" }, \"Client registered\");\n}\n\n// 处理远程客户端 WebSocket 连接生命周期\nexport function handleClientConnection(\n ws: WebSocket,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n): void {\n const clientWs = ws as ClientSocket;\n clientWs.isAlive = true;\n registry.addClientWs(clientWs);\n\n clientWs.on(\"pong\", () => {\n clientWs.isAlive = true;\n });\n\n clientWs.on(\"message\", (data: Buffer, isBinary: boolean) => {\n // Clients only send JSON control/envelope messages; binary frames from clients are ignored.\n if (isBinary) {\n return;\n }\n\n const raw = data.toString();\n const result = parseMessage(raw);\n\n if (result.kind === \"control\") {\n const msg = result.message;\n logger.info(\n { type: msg.type, clientId: clientWs.clientId, bound: clientWs.boundProxyId },\n \"Client message received\",\n );\n\n if (msg.type === \"client_register\") {\n handleClientRegister(msg.clientId, clientWs, registry, logger);\n return;\n }\n\n if (msg.type === \"proxy_list_request\") {\n const proxies = registry.listProxiesWithName().map((p) => ({\n ...p,\n sessions: registry.getSessionsForProxy(p.proxyId),\n }));\n const response = JSON.stringify({\n type: \"proxy_list_response\",\n requestId: msg.requestId,\n proxies,\n });\n if (chaos) {\n chaos.send(clientWs, response, {\n direction: \"proxy_to_client\",\n type: \"proxy_list_response\",\n });\n } else {\n clientWs.send(response);\n }\n return;\n }\n\n // client → proxy 透传:relay 不处理内容,直接转发给绑定的 proxy\n if (isClientToProxyRelayControlType(msg.type)) {\n const targetProxyId =\n (\"proxyId\" in msg ? (msg.proxyId as string) : undefined) || clientWs.boundProxyId;\n if (!targetProxyId) {\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.NOT_BOUND,\n message: \"Client is not bound to any proxy\",\n }),\n );\n return;\n }\n const proxyWs = registry.getProxy(targetProxyId);\n if (proxyWs && proxyWs.readyState === WebSocket.OPEN) {\n if (chaos) chaos.send(proxyWs, raw, { direction: \"client_to_proxy\", type: msg.type });\n else proxyWs.send(raw);\n } else {\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.PROXY_OFFLINE,\n message: `Proxy ${targetProxyId} is not available`,\n }),\n );\n }\n return;\n }\n\n if (msg.type === \"proxy_select\") {\n if (!registry.isProxyOnline(msg.proxyId)) {\n clientWs.send(\n JSON.stringify({\n type: \"proxy_select_response\",\n requestId: msg.requestId,\n success: false,\n errorCode: ControlErrorCode.PROXY_OFFLINE,\n error: `Proxy not online: ${msg.proxyId}`,\n }),\n );\n return;\n }\n // 没有 clientId 时自动分配,统一通过 clientId 绑定\n if (!clientWs.clientId) {\n clientWs.clientId = `anon-${nanoid(10)}`;\n }\n const bound = registry.bindClientById(clientWs.clientId, msg.proxyId, clientWs);\n if (!bound) {\n clientWs.send(\n JSON.stringify({\n type: \"proxy_select_response\",\n requestId: msg.requestId,\n success: false,\n errorCode: ControlErrorCode.PROXY_OFFLINE,\n error: `Proxy not online: ${msg.proxyId}`,\n }),\n );\n return;\n }\n clientWs.boundProxyId = msg.proxyId;\n const response = JSON.stringify({\n type: \"proxy_select_response\",\n requestId: msg.requestId,\n success: true,\n proxyId: msg.proxyId,\n });\n if (chaos) {\n chaos.send(clientWs, response, {\n direction: \"proxy_to_client\",\n type: \"proxy_select_response\",\n });\n } else {\n clientWs.send(response);\n }\n logger.info({ proxyId: msg.proxyId, clientId: clientWs.clientId }, \"Client bound to proxy\");\n return;\n }\n\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.UNSUPPORTED,\n message: `Unsupported control message: ${msg.type}`,\n }),\n );\n return;\n }\n\n if (result.kind === \"envelope\") {\n if (!clientWs.boundProxyId) {\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.NOT_BOUND,\n message: \"Client is not bound to any proxy\",\n }),\n );\n return;\n }\n routeClientMessage(raw, clientWs.boundProxyId, clientWs, registry, logger, chaos);\n return;\n }\n\n logger.error({ error: result.error, raw: raw.slice(0, 200) }, \"Invalid message from client\");\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.INVALID_MESSAGE,\n message: `${result.error} | raw: ${raw.slice(0, 200)}`,\n }),\n );\n });\n\n clientWs.on(\"close\", () => {\n registry.removeClientWs(clientWs);\n logger.info({ clientId: clientWs.clientId }, \"Client disconnected\");\n });\n\n clientWs.on(\"error\", (err) => {\n logger.error({ err }, \"Client WebSocket error\");\n });\n}\n","import type { WebSocket, WebSocketServer } from \"ws\";\n\ninterface HeartbeatSocket extends WebSocket {\n isAlive: boolean;\n}\n\n// 通用 WebSocket 心跳检测,检测死连接并 terminate\n// terminate 触发 close 事件,后续由各自的 close handler 处理恢复逻辑\nexport function setupHeartbeat(wss: WebSocketServer, interval = 30000): NodeJS.Timeout {\n return setInterval(() => {\n for (const ws of wss.clients) {\n const sock = ws as HeartbeatSocket;\n if (!sock.isAlive) {\n sock.terminate();\n continue;\n }\n sock.isAlive = false;\n sock.ping();\n }\n }, interval);\n}\n","import { WebSocket } from \"ws\";\nimport type { Logger } from \"@dev-anywhere/shared\";\n\nexport type RelayChaosDirection = \"client_to_proxy\" | \"proxy_to_client\";\n\nexport interface RelayChaosOptions {\n enabled: boolean;\n delayMs: number;\n duplicate: boolean;\n duplicateDelayMs: number;\n reorder: boolean;\n reorderDelayMs: number;\n types: Set<string> | undefined;\n}\n\nexport interface RelayChaosMeta {\n direction: RelayChaosDirection;\n type: string;\n}\n\nexport interface RelayChaos {\n send(ws: WebSocket, data: string | Buffer, meta: RelayChaosMeta): void;\n}\n\nexport function parseRelayChaosFromEnv(env: NodeJS.ProcessEnv): RelayChaosOptions {\n const enabled = env.DEV_ANYWHERE_RELAY_CHAOS === \"1\";\n const types = env.DEV_ANYWHERE_RELAY_CHAOS_TYPES?.split(\",\")\n .map((type) => type.trim())\n .filter(Boolean);\n\n return {\n enabled,\n delayMs: parseInt(env.DEV_ANYWHERE_RELAY_CHAOS_DELAY_MS ?? \"0\", 10),\n duplicate: env.DEV_ANYWHERE_RELAY_CHAOS_DUPLICATE === \"1\",\n duplicateDelayMs: parseInt(env.DEV_ANYWHERE_RELAY_CHAOS_DUPLICATE_DELAY_MS ?? \"10\", 10),\n reorder: env.DEV_ANYWHERE_RELAY_CHAOS_REORDER === \"1\",\n reorderDelayMs: parseInt(env.DEV_ANYWHERE_RELAY_CHAOS_REORDER_DELAY_MS ?? \"40\", 10),\n types: types && types.length > 0 ? new Set(types) : undefined,\n };\n}\n\nexport function createRelayChaos(options: RelayChaosOptions, logger: Logger): RelayChaos {\n let sequence = 0;\n\n function shouldAffect(meta: RelayChaosMeta): boolean {\n if (!options.enabled) return false;\n if (options.types && !options.types.has(meta.type)) return false;\n return true;\n }\n\n function sendNow(ws: WebSocket, data: string | Buffer): void {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(data);\n }\n }\n\n return {\n send(ws, data, meta) {\n if (!shouldAffect(meta)) {\n sendNow(ws, data);\n return;\n }\n\n sequence += 1;\n const reorderDelay = options.reorder && sequence % 2 === 1 ? options.reorderDelayMs : 0;\n const delayMs = Math.max(0, options.delayMs + reorderDelay);\n\n logger.warn(\n { direction: meta.direction, type: meta.type, delayMs, duplicate: options.duplicate },\n \"Relay chaos forwarding message\",\n );\n\n setTimeout(() => sendNow(ws, data), delayMs);\n\n if (options.duplicate) {\n setTimeout(() => sendNow(ws, data), delayMs + options.duplicateDelayMs);\n }\n },\n };\n}\n"],"mappings":"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","names":["homedir","dirname","fileURLToPath","WebSocket","z","z","z","z","z","z","z","join","WebSocket","WebSocket","WebSocket","WebSocket","WebSocket","dirname","fileURLToPath","homedir","resolve"]}
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  createLogger,
5
5
  createRelayServer,
6
6
  parseRelayChaosFromEnv
7
- } from "./chunk-3PD64DNZ.js";
7
+ } from "./chunk-UQFAAMXW.js";
8
8
 
9
9
  // src/index.ts
10
10
  import { homedir } from "os";
package/dist/server.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createRelayServer
4
- } from "./chunk-3PD64DNZ.js";
4
+ } from "./chunk-UQFAAMXW.js";
5
5
  export {
6
6
  createRelayServer
7
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev-anywhere/relay",
3
- "version": "0.0.5",
3
+ "version": "0.1.0",
4
4
  "description": "Relay server for DEV Anywhere — bridges local AI CLI proxies to remote web clients via WebSocket.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -10,7 +10,7 @@
10
10
  "dev-anywhere"
11
11
  ],
12
12
  "license": "MIT",
13
- "author": "catli <catli@futunn.com>",
13
+ "author": "catli",
14
14
  "homepage": "https://github.com/lichenxicatapple-blip/dev-anywhere",
15
15
  "repository": {
16
16
  "type": "git",
@@ -54,7 +54,7 @@
54
54
  "@types/node": "^22.15.30",
55
55
  "@types/ws": "^8.18.1",
56
56
  "vitest": "^4.1.2",
57
- "@dev-anywhere/shared": "0.0.0"
57
+ "@dev-anywhere/shared": "0.1.0"
58
58
  },
59
59
  "scripts": {
60
60
  "build": "tsup && rm -f tsconfig.build.tsbuildinfo && tsc -p tsconfig.build.json --emitDeclarationOnly --outDir dist",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/server.ts","../src/registry.ts","../src/health.ts","../src/version.ts","../src/handlers/proxy.ts","../../../packages/shared/src/schemas/envelope.ts","../../../packages/shared/src/schemas/chat.ts","../../../packages/shared/src/schemas/tool.ts","../../../packages/shared/src/schemas/session.ts","../../../packages/shared/src/schemas/system.ts","../../../packages/shared/src/schemas/relay-control.ts","../../../packages/shared/src/constants/relay-errors.ts","../../../packages/shared/src/constants/control-errors.ts","../../../packages/shared/src/logger.ts","../src/router.ts","../src/handlers/client.ts","../src/heartbeat.ts","../src/chaos.ts"],"sourcesContent":["import express from \"express\";\nimport { existsSync } from \"node:fs\";\nimport { createServer, type Server } from \"node:http\";\nimport { homedir } from \"node:os\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { WebSocketServer } from \"ws\";\nimport type { Logger } from \"@dev-anywhere/shared\";\nimport { RelayRegistry } from \"./registry.js\";\nimport { healthRouter } from \"./health.js\";\nimport { handleProxyConnection } from \"./handlers/proxy.js\";\nimport { handleClientConnection } from \"./handlers/client.js\";\nimport { setupHeartbeat } from \"./heartbeat.js\";\nimport { createRelayChaos, type RelayChaosOptions } from \"./chaos.js\";\n\nexport interface RelayServerOptions {\n port?: number;\n heartbeatInterval?: number;\n logger: Logger;\n dataDir?: string;\n // proxy 注册预共享 token; 不传 / 空串则关闭鉴权 (开发默认)\n proxyToken?: string;\n // client 连接预共享 token; 不传 / 空串则关闭鉴权 (开发默认)\n clientToken?: string;\n chaos?: RelayChaosOptions;\n fontAssetDir?: string;\n}\n\nexport interface RelayServer {\n httpServer: Server;\n registry: RelayRegistry;\n close: () => Promise<void>;\n}\n\nconst MODULE_DIR = dirname(fileURLToPath(import.meta.url));\nconst PACKAGED_FONTS_DIR = resolve(MODULE_DIR, \"../assets/fonts\");\n\n// 创建中转服务器,Express HTTP + ws WebSocket 双端点\nexport function createRelayServer(options: RelayServerOptions): RelayServer {\n const { heartbeatInterval = 30000, logger, dataDir, proxyToken, clientToken, chaos } = options;\n const proxyTokenRequired = typeof proxyToken === \"string\" && proxyToken.length > 0;\n const clientTokenRequired = typeof clientToken === \"string\" && clientToken.length > 0;\n if (!proxyTokenRequired) {\n logger.warn(\n \"proxy auth token not set, /proxy endpoint is open — ok for dev, not for public relay\",\n );\n }\n if (!clientTokenRequired) {\n logger.warn(\n \"client auth token not set, /client endpoint is open — ok for dev, not for public relay\",\n );\n }\n\n const registry = new RelayRegistry();\n const relayChaos = chaos?.enabled ? createRelayChaos(chaos, logger) : undefined;\n if (chaos?.enabled) {\n logger.warn(\n {\n delayMs: chaos.delayMs,\n duplicate: chaos.duplicate,\n reorder: chaos.reorder,\n types: chaos.types ? [...chaos.types] : \"all\",\n },\n \"Relay chaos mode enabled\",\n );\n }\n const app = express();\n\n // 字体优先读持久化目录;Docker 镜像再回退到随包内置字体,避免空 volume 让 Web 字体 404。\n const fontsDir = dataDir ? `${dataDir}/fonts` : `${homedir()}/.dev-anywhere/relay-data/fonts`;\n const fontAssetDir = options.fontAssetDir ?? PACKAGED_FONTS_DIR;\n app.use(\n \"/fonts\",\n (req, res, next) => {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n next();\n },\n express.static(fontsDir, {\n maxAge: \"30d\",\n immutable: true,\n }),\n );\n if (existsSync(fontAssetDir)) {\n app.use(\n \"/fonts\",\n express.static(fontAssetDir, {\n maxAge: \"30d\",\n immutable: true,\n }),\n );\n }\n\n app.use(healthRouter(registry));\n\n // 使用 createServer 而非 app.listen,确保 WebSocket upgrade 可在同一端口上处理\n const httpServer = createServer(app);\n\n const proxyWss = new WebSocketServer({ noServer: true });\n const clientWss = new WebSocketServer({ noServer: true });\n\n httpServer.on(\"upgrade\", (request, socket, head) => {\n const url = new URL(request.url ?? \"/\", \"http://localhost\");\n const { pathname } = url;\n\n if (pathname === \"/proxy\") {\n if (proxyTokenRequired) {\n const token = url.searchParams.get(\"token\");\n if (token !== proxyToken) {\n logger.warn(\n { ip: request.socket.remoteAddress },\n \"rejected /proxy upgrade: invalid token\",\n );\n socket.write(\"HTTP/1.1 401 Unauthorized\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\n }\n proxyWss.handleUpgrade(request, socket, head, (ws) => {\n proxyWss.emit(\"connection\", ws, request);\n });\n return;\n }\n\n if (pathname === \"/client\") {\n if (clientTokenRequired) {\n const token = url.searchParams.get(\"token\");\n if (token !== clientToken) {\n logger.warn(\n { ip: request.socket.remoteAddress },\n \"rejected /client upgrade: invalid token\",\n );\n socket.write(\"HTTP/1.1 401 Unauthorized\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\n }\n clientWss.handleUpgrade(request, socket, head, (ws) => {\n clientWss.emit(\"connection\", ws, request);\n });\n return;\n }\n\n socket.destroy();\n });\n\n proxyWss.on(\"connection\", (ws) => {\n handleProxyConnection(ws, registry, logger, relayChaos);\n });\n\n clientWss.on(\"connection\", (ws) => {\n handleClientConnection(ws, registry, logger, relayChaos);\n });\n\n const proxyHeartbeat = setupHeartbeat(proxyWss, heartbeatInterval);\n const clientHeartbeat = setupHeartbeat(clientWss, heartbeatInterval);\n\n async function close(): Promise<void> {\n clearInterval(proxyHeartbeat);\n clearInterval(clientHeartbeat);\n\n for (const ws of proxyWss.clients) {\n ws.terminate();\n }\n for (const ws of clientWss.clients) {\n ws.terminate();\n }\n\n await new Promise<void>((resolve, reject) => {\n proxyWss.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n clientWss.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n httpServer.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n return { httpServer, registry, close };\n}\n","import { WebSocket } from \"ws\";\n\n// 显式代理连接状态,取代 ws null 检查\ntype ProxyConnectionState = \"online\" | \"offline\";\n\n// 显式客户端连接状态,跟踪注册和绑定\ntype ClientConnectionState = \"registered\" | \"bound\";\n\n// 代理连接状态,跟踪 ws、会话集合、离线时间、显示名称\ninterface ProxyState {\n ws: WebSocket | null;\n connectionState: ProxyConnectionState;\n sessions: Set<string>;\n disconnectedAt: number | null;\n name?: string;\n}\n\n// 客户端绑定状态,通过 clientId 而非 WebSocket 引用标识\ninterface ClientBinding {\n proxyId: string;\n ws: WebSocket | null;\n connectionState: ClientConnectionState;\n}\n\n// 代理注册、客户端绑定、宽限期管理\nexport class RelayRegistry {\n private proxyStates = new Map<string, ProxyState>();\n private clientBindings = new Map<string, ClientBinding>();\n private connectedClients = new Set<WebSocket>();\n\n registerProxy(proxyId: string, ws: WebSocket, name?: string): \"new\" | \"reconnected\" {\n const existing = this.proxyStates.get(proxyId);\n if (existing) {\n // 如果旧连接还活着,先终止\n if (existing.ws && existing.ws.readyState === WebSocket.OPEN) {\n existing.ws.terminate();\n }\n existing.ws = ws;\n existing.connectionState = \"online\";\n existing.disconnectedAt = null;\n if (name !== undefined) existing.name = name;\n return \"reconnected\";\n }\n\n this.proxyStates.set(proxyId, {\n ws,\n connectionState: \"online\",\n sessions: new Set(),\n disconnectedAt: null,\n name,\n });\n return \"new\";\n }\n\n // 标记 proxy 离线,保留所有状态等待重连,不设超时\n // 清理只在 proxy 主动退出(proxy_disconnect)或 relay 启动清理废弃数据时发生\n markProxyOffline(proxyId: string): void {\n const state = this.proxyStates.get(proxyId);\n if (!state) return;\n\n state.ws = null;\n state.connectionState = \"offline\";\n state.disconnectedAt = Date.now();\n }\n\n // 显式状态转换,校验 from 状态匹配后更新 connectionState\n transitionProxy(proxyId: string, from: ProxyConnectionState, to: ProxyConnectionState): void {\n if (from === to) {\n throw new Error(`Invalid proxy transition: ${from} -> ${to} (same state)`);\n }\n const state = this.proxyStates.get(proxyId);\n if (!state) {\n throw new Error(`Proxy not found: ${proxyId}`);\n }\n if (state.connectionState !== from) {\n throw new Error(\n `Proxy ${proxyId} state mismatch: expected ${from}, actual ${state.connectionState}`,\n );\n }\n state.connectionState = to;\n if (to === \"offline\") {\n state.ws = null;\n state.disconnectedAt = Date.now();\n }\n }\n\n // 显式客户端状态转换,校验 from 状态匹配后更新 connectionState\n transitionClient(clientId: string, from: ClientConnectionState, to: ClientConnectionState): void {\n if (from === to) {\n throw new Error(`Invalid client transition: ${from} -> ${to} (same state)`);\n }\n const binding = this.clientBindings.get(clientId);\n if (!binding) {\n throw new Error(`Client not found: ${clientId}`);\n }\n if (binding.connectionState !== from) {\n throw new Error(\n `Client ${clientId} state mismatch: expected ${from}, actual ${binding.connectionState}`,\n );\n }\n binding.connectionState = to;\n }\n\n getProxyConnectionState(proxyId: string): ProxyConnectionState | undefined {\n return this.proxyStates.get(proxyId)?.connectionState;\n }\n\n getClientConnectionState(clientId: string): ClientConnectionState | undefined {\n return this.clientBindings.get(clientId)?.connectionState;\n }\n\n // 彻底清理 proxy 状态和客户端绑定\n cleanupProxy(proxyId: string): void {\n const state = this.proxyStates.get(proxyId);\n if (!state) return;\n\n // 解绑所有绑定到该 proxy 的客户端\n for (const [clientId, binding] of this.clientBindings) {\n if (binding.proxyId === proxyId) {\n this.clientBindings.delete(clientId);\n }\n }\n\n this.proxyStates.delete(proxyId);\n }\n\n unregisterProxy(proxyId: string): void {\n this.cleanupProxy(proxyId);\n }\n\n getProxy(proxyId: string): WebSocket | undefined {\n const state = this.proxyStates.get(proxyId);\n return state?.ws ?? undefined;\n }\n\n isProxyOnline(proxyId: string): boolean {\n const state = this.proxyStates.get(proxyId);\n if (!state || state.connectionState !== \"online\") return false;\n // connectionState 声明在线但 ws 已失效时,记录警告并返回 false\n if (!state.ws || state.ws.readyState !== WebSocket.OPEN) return false;\n return true;\n }\n\n // proxy 是否存在(含宽限期中的)\n hasProxy(proxyId: string): boolean {\n return this.proxyStates.has(proxyId);\n }\n\n listProxies(): string[] {\n return Array.from(this.proxyStates.keys());\n }\n\n // 返回 proxyId、name、online 的列表,用于 proxy_list_response\n listProxiesWithName(): Array<{ proxyId: string; name?: string; online: boolean }> {\n return Array.from(this.proxyStates.entries()).map(([proxyId, state]) => ({\n proxyId,\n ...(state.name !== undefined ? { name: state.name } : {}),\n online: state.connectionState === \"online\",\n }));\n }\n\n getProxyName(proxyId: string): string | undefined {\n return this.proxyStates.get(proxyId)?.name;\n }\n\n // 将 sessionId 关联到 proxy\n addSessionToProxy(proxyId: string, sessionId: string): void {\n const state = this.proxyStates.get(proxyId);\n if (state) {\n state.sessions.add(sessionId);\n }\n }\n\n // 通过 sessionId 反查所属 proxyId\n getProxyForSession(sessionId: string): string | undefined {\n for (const [proxyId, state] of this.proxyStates) {\n if (state.sessions.has(sessionId)) {\n return proxyId;\n }\n }\n return undefined;\n }\n\n // 获取 proxy 关联的所有 sessionId\n getSessionsForProxy(proxyId: string): string[] {\n const state = this.proxyStates.get(proxyId);\n return state ? Array.from(state.sessions) : [];\n }\n\n // clientId 绑定方式\n bindClientById(clientId: string, proxyId: string, ws: WebSocket): boolean {\n if (!this.proxyStates.has(proxyId)) {\n return false;\n }\n this.clientBindings.set(clientId, { proxyId, ws, connectionState: \"bound\" });\n return true;\n }\n\n updateClientSocket(clientId: string, ws: WebSocket): void {\n const binding = this.clientBindings.get(clientId);\n if (binding) {\n binding.ws = ws;\n }\n }\n\n // 断开客户端 WebSocket 但保留绑定关系,重连时可恢复\n unbindClientById(clientId: string): void {\n const binding = this.clientBindings.get(clientId);\n if (binding) {\n binding.ws = null;\n }\n }\n\n getClientBinding(clientId: string): ClientBinding | undefined {\n return this.clientBindings.get(clientId);\n }\n\n // 获取绑定到指定 proxy 的所有活跃客户端 WebSocket\n getClientsForProxy(proxyId: string): WebSocket[] {\n const clients: WebSocket[] = [];\n for (const [, binding] of this.clientBindings) {\n if (binding.proxyId === proxyId && binding.ws && binding.ws.readyState === WebSocket.OPEN) {\n clients.push(binding.ws);\n }\n }\n return clients;\n }\n\n countClients(): number {\n let count = 0;\n for (const [, binding] of this.clientBindings) {\n if (binding.ws) count++;\n }\n return count;\n }\n\n addClientWs(ws: WebSocket): void {\n this.connectedClients.add(ws);\n }\n\n removeClientWs(ws: WebSocket): void {\n this.connectedClients.delete(ws);\n }\n\n getAllClientWs(): WebSocket[] {\n const clients: WebSocket[] = [];\n for (const ws of this.connectedClients) {\n if (ws.readyState === WebSocket.OPEN) {\n clients.push(ws);\n }\n }\n return clients;\n }\n\n // 获取单个 proxy 的详细状态信息\n getProxyDetail(proxyId: string):\n | {\n proxyId: string;\n name?: string;\n online: boolean;\n connectionState: ProxyConnectionState;\n sessions: string[];\n disconnectedAt: number | null;\n }\n | undefined {\n const state = this.proxyStates.get(proxyId);\n if (!state) return undefined;\n return {\n proxyId,\n ...(state.name !== undefined ? { name: state.name } : {}),\n online: state.connectionState === \"online\",\n connectionState: state.connectionState,\n sessions: Array.from(state.sessions),\n disconnectedAt: state.disconnectedAt,\n };\n }\n\n // 获取所有客户端绑定的详细信息\n getClientDetails(): Array<{\n clientId: string;\n proxyId: string;\n online: boolean;\n connectionState: ClientConnectionState;\n }> {\n const details: Array<{\n clientId: string;\n proxyId: string;\n online: boolean;\n connectionState: ClientConnectionState;\n }> = [];\n for (const [clientId, binding] of this.clientBindings) {\n details.push({\n clientId,\n proxyId: binding.proxyId,\n online:\n binding.ws !== null &&\n binding.ws !== undefined &&\n binding.ws.readyState === WebSocket.OPEN,\n connectionState: binding.connectionState,\n });\n }\n return details;\n }\n}\n","import { Router } from \"express\";\nimport type { RelayRegistry } from \"./registry.js\";\nimport { RELAY_VERSION } from \"./version.js\";\n\n// 健康检查和状态查询路由\nexport function healthRouter(registry: RelayRegistry): Router {\n const router = Router();\n\n router.get(\"/health\", (_req, res) => {\n res.json({\n status: \"ok\",\n version: RELAY_VERSION,\n uptime: process.uptime(),\n });\n });\n\n router.get(\"/status\", (_req, res) => {\n res.json({\n version: RELAY_VERSION,\n proxyCount: registry.listProxies().length,\n clientCount: registry.countClients(),\n uptime: process.uptime(),\n });\n });\n\n // 连接总览:proxy/client 计数、绑定关系\n router.get(\"/api/status\", (_req, res) => {\n res.json({\n version: RELAY_VERSION,\n proxyCount: registry.listProxies().length,\n clientCount: registry.countClients(),\n uptime: process.uptime(),\n bindings: registry.getClientDetails(),\n });\n });\n\n // 逐 proxy 详情:id、名称、在线状态、会话列表、离线时间\n router.get(\"/api/proxies\", (_req, res) => {\n const proxyIds = registry.listProxies();\n const details = proxyIds\n .map((id) => registry.getProxyDetail(id))\n .filter((d) => d !== undefined);\n res.json(details);\n });\n\n // 逐客户端详情:clientId、绑定的 proxyId、在线状态\n router.get(\"/api/clients\", (_req, res) => {\n res.json(registry.getClientDetails());\n });\n\n return router;\n}\n","import { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\ninterface PackageInfo {\n version?: string;\n}\n\nfunction readRelayVersion(): string {\n const packageJsonPath = join(dirname(fileURLToPath(import.meta.url)), \"..\", \"package.json\");\n const pkg = JSON.parse(readFileSync(packageJsonPath, \"utf-8\")) as PackageInfo;\n return pkg.version ?? \"unknown\";\n}\n\nexport const RELAY_VERSION = readRelayVersion();\n","import { WebSocket } from \"ws\";\nimport { isProxyToClientRelayControlType, RelayErrorCode, type Logger } from \"@dev-anywhere/shared\";\nimport type { RelayRegistry } from \"../registry.js\";\nimport { parseMessage, routeProxyMessage } from \"../router.js\";\nimport type { RelayChaos } from \"../chaos.js\";\n\n// binary 帧最大允许大小(10MB),超过的帧静默丢弃以防 DoS\nconst MAX_BINARY_FRAME_SIZE = 10 * 1024 * 1024;\n\n// 扩展 WebSocket 实例存储代理元数据\ninterface ProxySocket extends WebSocket {\n isAlive: boolean;\n proxyId?: string;\n}\n\n// 通知绑定到指定 proxy 的所有客户端 proxy 已离线\nfunction notifyClientsProxyOffline(\n proxyId: string,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n): void {\n const clients = registry.getClientsForProxy(proxyId);\n const msg = JSON.stringify({ type: \"proxy_offline\", proxyId });\n for (const clientWs of clients) {\n if (chaos) chaos.send(clientWs, msg, { direction: \"proxy_to_client\", type: \"proxy_offline\" });\n else clientWs.send(msg);\n }\n logger.info({ proxyId, clientCount: clients.length }, \"Notified clients of proxy offline\");\n}\n\n// 通知绑定到指定 proxy 的所有客户端 proxy 已上线\nfunction notifyClientsProxyOnline(\n proxyId: string,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n): void {\n const clients = registry.getClientsForProxy(proxyId);\n const msg = JSON.stringify({ type: \"proxy_online\", proxyId });\n for (const clientWs of clients) {\n if (chaos) chaos.send(clientWs, msg, { direction: \"proxy_to_client\", type: \"proxy_online\" });\n else clientWs.send(msg);\n }\n logger.info({ proxyId, clientCount: clients.length }, \"Notified clients of proxy online\");\n}\n\n// proxy 上线或下线时,将最新的 proxy 列表推送给所有已连接的 client。\n// 复用 proxy_list_response 消息类型,client 端已有对应处理逻辑,无需额外适配。\nfunction broadcastProxyList(registry: RelayRegistry, chaos?: RelayChaos): void {\n const proxies = registry.listProxiesWithName().map((p) => ({\n ...p,\n sessions: registry.getSessionsForProxy(p.proxyId),\n }));\n const msg = JSON.stringify({ type: \"proxy_list_response\", proxies });\n for (const clientWs of registry.getAllClientWs()) {\n if (chaos)\n chaos.send(clientWs, msg, { direction: \"proxy_to_client\", type: \"proxy_list_response\" });\n else clientWs.send(msg);\n }\n}\n\n// 处理代理端 WebSocket 连接生命周期\nexport function handleProxyConnection(\n ws: WebSocket,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n): void {\n const proxyWs = ws as ProxySocket;\n proxyWs.isAlive = true;\n\n proxyWs.on(\"pong\", () => {\n proxyWs.isAlive = true;\n });\n\n proxyWs.on(\"message\", (data: Buffer, isBinary: boolean) => {\n // Binary frames are pass-through; relay only reads the sessionId prefix for routing.\n if (isBinary) {\n if (data.length < 2 || data.length > MAX_BINARY_FRAME_SIZE) {\n logger.warn({ size: data.length }, \"Binary frame rejected: invalid size\");\n return;\n }\n const sessionIdLen = data[0];\n if (sessionIdLen === 0 || sessionIdLen > 255 || data.length < 1 + sessionIdLen) {\n logger.warn(\n { sessionIdLen, dataLen: data.length },\n \"Binary frame rejected: malformed sessionId prefix\",\n );\n return;\n }\n if (!proxyWs.proxyId) {\n logger.warn(\"Binary frame from unregistered proxy, dropped\");\n return;\n }\n\n // Forward the original buffer, including the sessionId prefix, so clients receive exact PTY bytes.\n const clients = registry.getClientsForProxy(proxyWs.proxyId);\n for (const clientWs of clients) {\n if (clientWs.readyState === WebSocket.OPEN) {\n clientWs.send(data);\n }\n }\n return;\n }\n\n const raw = data.toString();\n const result = parseMessage(raw);\n\n if (result.kind === \"control\" && result.message.type === \"proxy_register\") {\n const { proxyId, name } = result.message;\n const status = registry.registerProxy(proxyId, proxyWs, name);\n proxyWs.proxyId = proxyId;\n logger.info({ proxyId, status }, \"Proxy registered\");\n\n proxyWs.send(\n JSON.stringify({\n type: \"proxy_register_response\",\n status,\n }),\n );\n\n if (status === \"reconnected\") {\n notifyClientsProxyOnline(proxyId, registry, logger, chaos);\n }\n\n broadcastProxyList(registry, chaos);\n return;\n }\n\n if (result.kind === \"control\" && result.message.type === \"proxy_disconnect\") {\n if (proxyWs.proxyId) {\n notifyClientsProxyOffline(proxyWs.proxyId, registry, logger, chaos);\n registry.markProxyOffline(proxyWs.proxyId);\n logger.info({ proxyId: proxyWs.proxyId }, \"Proxy gracefully disconnected, marked offline\");\n proxyWs.proxyId = undefined;\n broadcastProxyList(registry, chaos);\n }\n return;\n }\n\n // proxy 重连后同步 session 列表,relay 据此建立 proxy-session 关联\n if (result.kind === \"control\" && result.message.type === \"session_sync\") {\n if (!proxyWs.proxyId) return;\n const sessions = result.message.sessions as Array<{ id: string }>;\n if (Array.isArray(sessions)) {\n for (const s of sessions) {\n registry.addSessionToProxy(proxyWs.proxyId, s.id);\n }\n logger.info({ proxyId: proxyWs.proxyId, count: sessions.length }, \"Session sync received\");\n }\n return;\n }\n\n // proxy 发给 client 的控制消息:直接转发给绑定的客户端,不进 session buffer\n if (result.kind === \"control\") {\n if (isProxyToClientRelayControlType(result.message.type)) {\n if (!proxyWs.proxyId) {\n proxyWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.NOT_REGISTERED,\n message: \"Proxy must register before sending messages\",\n }),\n );\n return;\n }\n const clients = registry.getClientsForProxy(proxyWs.proxyId);\n for (const clientWs of clients) {\n if (clientWs.readyState === WebSocket.OPEN) {\n if (chaos) {\n chaos.send(clientWs, raw, {\n direction: \"proxy_to_client\",\n type: result.message.type,\n });\n } else {\n clientWs.send(raw);\n }\n }\n }\n logger.info(\n { proxyId: proxyWs.proxyId, type: result.message.type, clientCount: clients.length },\n \"Forwarded control message from proxy to clients\",\n );\n return;\n }\n // 其他控制消息代理端不应发送\n logger.warn({ type: result.message.type }, \"Unexpected control message from proxy\");\n return;\n }\n\n if (result.kind === \"envelope\") {\n if (!proxyWs.proxyId) {\n proxyWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.NOT_REGISTERED,\n message: \"Proxy must register before sending messages\",\n }),\n );\n return;\n }\n routeProxyMessage(raw, proxyWs.proxyId, registry, logger, chaos);\n return;\n }\n\n if (result.kind === \"invalid\") {\n logger.error({ error: result.error, raw: raw.slice(0, 200) }, \"Invalid message from proxy\");\n proxyWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.INVALID_MESSAGE,\n message: `${result.error} | raw: ${raw.slice(0, 200)}`,\n }),\n );\n return;\n }\n });\n\n proxyWs.on(\"close\", () => {\n if (proxyWs.proxyId) {\n notifyClientsProxyOffline(proxyWs.proxyId, registry, logger, chaos);\n // 通过状态转换标记离线,保留所有状态等待重连\n // proxy_disconnect 已经清理过的情况下 transition 会抛异常,静默忽略\n try {\n registry.transitionProxy(proxyWs.proxyId, \"online\", \"offline\");\n } catch {\n // proxy 已被 proxy_disconnect 清理或已离线,跳过\n }\n logger.info(\n { proxyId: proxyWs.proxyId },\n \"Proxy disconnected, state preserved for reconnect\",\n );\n broadcastProxyList(registry, chaos);\n }\n });\n\n proxyWs.on(\"error\", (err) => {\n logger.error({ err, proxyId: proxyWs.proxyId }, \"Proxy WebSocket error\");\n });\n}\n","import { z } from \"zod\";\nimport {\n UserInputPayloadSchema,\n AssistantMessagePayloadSchema,\n ThinkingPayloadSchema,\n} from \"./chat.js\";\nimport { ToolUseRequestPayloadSchema, ToolResultPayloadSchema } from \"./tool.js\";\nimport {\n SessionCreatePayloadSchema,\n SessionListPayloadSchema,\n SessionSwitchPayloadSchema,\n SessionTerminatePayloadSchema,\n SessionStatusPayloadSchema,\n} from \"./session.js\";\nimport {\n HeartbeatPayloadSchema,\n AuthPayloadSchema,\n SyncRequestPayloadSchema,\n SyncResponsePayloadSchema,\n} from \"./system.js\";\n\n// 信封基础字段:序列号、会话ID、时间戳、来源、协议版本\nconst BaseEnvelopeFields = {\n seq: z.number().int().nonnegative(),\n sessionId: z.string(),\n timestamp: z.number(),\n source: z.enum([\"proxy\", \"client\"]),\n version: z.string(),\n};\n\n// 按 type 字段区分的 discriminatedUnion 信封\nexport const MessageEnvelopeSchema = z.discriminatedUnion(\"type\", [\n // chat (3)\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"user_input\"),\n payload: UserInputPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"assistant_message\"),\n payload: AssistantMessagePayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"thinking\"),\n payload: ThinkingPayloadSchema,\n }),\n // tool (4): 工具审批决策属于 relay control,不进入会话消息信封。\n // tool_use_request: 审批流请求(proxy → client),toolId 是 approval requestId\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"tool_use_request\"),\n payload: ToolUseRequestPayloadSchema,\n }),\n // tool_result: 工具执行结果(proxy → client),toolId 对应 assistant_tool_use / tool_use_request 的 toolId\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"tool_result\"),\n payload: ToolResultPayloadSchema,\n }),\n // assistant_tool_use: 纯展示型工具调用(proxy → client),区别于 tool_use_request 无审批语义\n // payload 结构复用 ToolUseRequestPayloadSchema;toolId 是 Claude 分配的 tool_use id\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"assistant_tool_use\"),\n payload: ToolUseRequestPayloadSchema,\n }),\n // session (5)\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"session_create\"),\n payload: SessionCreatePayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"session_list\"),\n payload: SessionListPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"session_switch\"),\n payload: SessionSwitchPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"session_terminate\"),\n payload: SessionTerminatePayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"session_status\"),\n payload: SessionStatusPayloadSchema,\n }),\n // system (5)\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"heartbeat\"),\n payload: HeartbeatPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"auth\"),\n payload: AuthPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"sync_request\"),\n payload: SyncRequestPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"sync_response\"),\n payload: SyncResponsePayloadSchema,\n }),\n]);\n\nexport type MessageEnvelope = z.infer<typeof MessageEnvelopeSchema>;\n\nexport type MessageType = MessageEnvelope[\"type\"];\n\nexport type MessageSource = MessageEnvelope[\"source\"];\n","import { z } from \"zod\";\n\n// 用户输入消息\nexport const UserInputPayloadSchema = z.object({\n text: z.string().min(1),\n});\n\nexport type UserInputPayload = z.infer<typeof UserInputPayloadSchema>;\n\n// 助手回复消息,isPartial 标识是否为流式中间结果\nexport const AssistantMessagePayloadSchema = z.object({\n text: z.string(),\n isPartial: z.boolean(),\n});\n\nexport type AssistantMessagePayload = z.infer<typeof AssistantMessagePayloadSchema>;\n\n// 思考过程消息\nexport const ThinkingPayloadSchema = z.object({\n text: z.string(),\n});\n\nexport type ThinkingPayload = z.infer<typeof ThinkingPayloadSchema>;\n","import { z } from \"zod\";\n\n// 工具调用请求\nexport const ToolUseRequestPayloadSchema = z.object({\n toolName: z.string(),\n toolId: z.string(),\n parameters: z.record(z.string(), z.unknown()),\n});\n\nexport type ToolUseRequestPayload = z.infer<typeof ToolUseRequestPayloadSchema>;\n\n// 工具调用批准,whitelistTool 为 true 时将该工具加入会话级白名单自动审批\nexport const ToolApprovePayloadSchema = z.object({\n toolId: z.string(),\n whitelistTool: z.boolean().optional(),\n});\n\nexport type ToolApprovePayload = z.infer<typeof ToolApprovePayloadSchema>;\n\n// 工具调用拒绝\nexport const ToolDenyPayloadSchema = z.object({\n toolId: z.string(),\n reason: z.string().optional(),\n});\n\nexport type ToolDenyPayload = z.infer<typeof ToolDenyPayloadSchema>;\n\n// 工具调用结果\nexport const ToolResultPayloadSchema = z.object({\n toolId: z.string(),\n result: z.unknown(),\n isError: z.boolean(),\n});\n\nexport type ToolResultPayload = z.infer<typeof ToolResultPayloadSchema>;\n","import { z } from \"zod\";\n\nconst sessionStateValues = [\"idle\", \"working\", \"waiting_approval\", \"error\", \"terminated\"] as const;\nconst providerValues = [\"claude\", \"codex\"] as const;\nconst ptyOwnerValues = [\"local-terminal\", \"proxy-hosted\"] as const;\nconst agentStatusPhaseValues = [\n \"idle\",\n \"thinking\",\n \"tool_use\",\n \"outputting\",\n \"waiting_permission\",\n \"error\",\n] as const;\n\n// 会话信息,用于会话列表展示\n// lastActive: 最近一次状态变更/消息时间戳 (ms), 用于列表\"N 分钟前\"显示, 可选\nexport const SessionInfoSchema = z.object({\n sessionId: z.string(),\n name: z.string().optional(),\n state: z.enum(sessionStateValues),\n mode: z.enum([\"pty\", \"json\"]).optional(),\n provider: z.enum(providerValues),\n // PTY 尺寸所有权:\n // - local-terminal: 本地 terminal 进程持有真实 PTY,Web 只按原始 cols/rows 展示\n // - proxy-hosted: serve 内托管 PTY,Web 可按视口请求 resize\n ptyOwner: z.enum(ptyOwnerValues).optional(),\n lastActive: z.number().optional(),\n});\nexport type SessionInfo = z.infer<typeof SessionInfoSchema>;\n\n// 创建会话\n// streamDelta: client 端系统设置\"逐字流式\"toggle,true 时 proxy spawn 带 --include-partial-messages\nexport const SessionCreatePayloadSchema = z.object({\n name: z.string().optional(),\n cwd: z.string().optional(),\n streamDelta: z.boolean().optional(),\n});\n\nexport type SessionCreatePayload = z.infer<typeof SessionCreatePayloadSchema>;\n\n// 会话列表\nexport const SessionListPayloadSchema = z.object({\n sessions: z.array(SessionInfoSchema),\n});\n\nexport type SessionListPayload = z.infer<typeof SessionListPayloadSchema>;\n\n// 切换会话\nexport const SessionSwitchPayloadSchema = z.object({\n sessionId: z.string(),\n});\n\nexport type SessionSwitchPayload = z.infer<typeof SessionSwitchPayloadSchema>;\n\n// 终止会话\nexport const SessionTerminatePayloadSchema = z.object({\n sessionId: z.string(),\n});\n\nexport type SessionTerminatePayload = z.infer<typeof SessionTerminatePayloadSchema>;\n\n// 会话状态变更\n// lastActive: 触发本次状态迁移的时间戳 (ms),用于列表相对时间显示。\nexport const SessionStatusPayloadSchema = z.object({\n sessionId: z.string(),\n state: z.enum(sessionStateValues),\n lastActive: z.number(),\n});\n\nexport type SessionStatusPayload = z.infer<typeof SessionStatusPayloadSchema>;\n\n// PTY 语义状态事件,描述当前 PTY 处于何种状态\nexport const PtyStatePayloadSchema = z.object({\n state: z.enum([\"working\", \"turn_complete\", \"approval_wait\", \"mid_pause\"]),\n title: z.string().optional(),\n tool: z.string().optional(),\n});\nexport type PtyStatePayload = z.infer<typeof PtyStatePayloadSchema>;\n\nexport const AgentStatusPayloadSchema = z.object({\n provider: z.enum(providerValues),\n phase: z.enum(agentStatusPhaseValues),\n seq: z.number().int().nonnegative(),\n updatedAt: z.number(),\n toolName: z.string().optional(),\n toolInput: z.record(z.string(), z.unknown()).optional(),\n permissionRequest: z\n .object({\n requestId: z.string(),\n toolName: z.string(),\n input: z.record(z.string(), z.unknown()),\n })\n .optional(),\n permissionResolution: z\n .object({\n requestId: z.string(),\n outcome: z.enum([\"allow\", \"deny\"]),\n })\n .optional(),\n summary: z.string().optional(),\n});\nexport type AgentStatusPayload = z.infer<typeof AgentStatusPayloadSchema>;\n","import { z } from \"zod\";\n\n// 心跳消息,空 payload\nexport const HeartbeatPayloadSchema = z.object({});\n\nexport type HeartbeatPayload = z.infer<typeof HeartbeatPayloadSchema>;\n\n// 认证消息,支持配对码和 token 两种方式\nexport const AuthPayloadSchema = z.object({\n pairingCode: z.string().optional(),\n token: z.string().optional(),\n});\n\nexport type AuthPayload = z.infer<typeof AuthPayloadSchema>;\n\n// 同步请求,客户端发送已收到的最大序列号\nexport const SyncRequestPayloadSchema = z.object({\n lastSeq: z.number().int().nonnegative(),\n});\n\nexport type SyncRequestPayload = z.infer<typeof SyncRequestPayloadSchema>;\n\n// 同步响应,使用 z.unknown 数组避免循环引用;恢复协议稳定后再收紧类型\nexport const SyncResponsePayloadSchema = z.object({\n messages: z.array(z.record(z.string(), z.unknown())),\n});\n\nexport type SyncResponsePayload = z.infer<typeof SyncResponsePayloadSchema>;\n","import { z } from \"zod\";\nimport { AgentStatusPayloadSchema, PtyStatePayloadSchema } from \"./session.js\";\nimport { ToolApprovePayloadSchema, ToolDenyPayloadSchema } from \"./tool.js\";\nimport { RelayErrorCode } from \"../constants/relay-errors.js\";\nimport { ControlErrorCode } from \"../constants/control-errors.js\";\n\n// 控制消息中复用的子类型\nexport const ProxyInfoSchema = z.object({\n proxyId: z.string(),\n name: z.string().optional(),\n online: z.boolean(),\n sessions: z.array(z.string()).optional(),\n});\nexport type ProxyInfo = z.infer<typeof ProxyInfoSchema>;\n\nexport const AgentCliAvailabilitySchema = z.object({\n available: z.boolean(),\n command: z.string().optional(),\n error: z.string().optional(),\n suggestions: z.array(z.string()).optional(),\n});\nexport type AgentCliAvailability = z.infer<typeof AgentCliAvailabilitySchema>;\n\nexport const AgentCliStatusSchema = z.object({\n claude: AgentCliAvailabilitySchema,\n codex: AgentCliAvailabilitySchema,\n});\nexport type AgentCliStatus = z.infer<typeof AgentCliStatusSchema>;\n\nexport const DirEntrySchema = z.object({ name: z.string(), isDir: z.boolean() });\nexport type DirEntry = z.infer<typeof DirEntrySchema>;\n\nexport const FileTreeGroupSchema = z.object({\n path: z.string(),\n entries: z.array(DirEntrySchema),\n});\nexport type FileTreeGroup = z.infer<typeof FileTreeGroupSchema>;\n\nexport const CommandEntrySchema = z.object({\n name: z.string(),\n description: z.string(),\n argumentHint: z.string().optional(),\n source: z.string(),\n});\nexport type CommandEntry = z.infer<typeof CommandEntrySchema>;\n\nexport const HistorySessionSchema = z.object({\n id: z.string(),\n title: z.string(),\n projectDir: z.string(),\n updatedAt: z.number(),\n provider: z.enum([\"claude\", \"codex\"]).optional(),\n});\nexport type HistorySession = z.infer<typeof HistorySessionSchema>;\n\ntype RelayControlDirection = \"proxy_to_client\" | \"client_to_proxy\";\ntype EmptyShape = Record<never, never>;\nconst RequestIdShape = { requestId: z.string().min(1).optional() };\nconst ControlErrorCodeSchema = z.enum(\n Object.values(ControlErrorCode) as [ControlErrorCode, ...ControlErrorCode[]],\n);\nconst RequestErrorShape = {\n error: z.string().optional(),\n errorCode: ControlErrorCodeSchema.optional(),\n};\n\ntype ControlDefinition<T extends string, S extends z.ZodRawShape> = {\n type: T;\n directions: ReadonlySet<RelayControlDirection>;\n schema: z.ZodObject<{ type: z.ZodLiteral<T> } & S>;\n};\n\nfunction control<T extends string>(type: T): ControlDefinition<T, EmptyShape>;\nfunction control<T extends string>(\n type: T,\n shape: undefined,\n directions: RelayControlDirection | RelayControlDirection[],\n): ControlDefinition<T, EmptyShape>;\nfunction control<T extends string, S extends z.ZodRawShape>(\n type: T,\n shape: S,\n directions?: RelayControlDirection | RelayControlDirection[],\n): ControlDefinition<T, S>;\nfunction control<T extends string, S extends z.ZodRawShape>(\n type: T,\n shape?: S,\n directions?: RelayControlDirection | RelayControlDirection[],\n): ControlDefinition<T, S | EmptyShape> {\n return {\n type,\n directions: new Set(Array.isArray(directions) ? directions : directions ? [directions] : []),\n schema: z.object({\n type: z.literal(type),\n ...(shape ?? {}),\n }) as z.ZodObject<{ type: z.ZodLiteral<T> } & (S | EmptyShape)>,\n };\n}\n\n// 中转服务器控制消息,独立于 MessageEnvelope 的传输层协议\nconst relayControlDefinitions = [\n control(\"proxy_register\", {\n proxyId: z.string().min(1),\n name: z.string().optional(),\n }),\n control(\"proxy_register_response\", {\n status: z.enum([\"new\", \"reconnected\"]),\n }),\n control(\"proxy_list_request\", RequestIdShape),\n control(\"proxy_list_response\", {\n ...RequestIdShape,\n proxies: z.array(ProxyInfoSchema),\n }),\n control(\"proxy_select\", { ...RequestIdShape, proxyId: z.string().min(1) }),\n control(\"proxy_select_response\", {\n ...RequestIdShape,\n success: z.boolean(),\n proxyId: z.string().optional(),\n ...RequestErrorShape,\n }),\n control(\"relay_error\", {\n code: z.enum(Object.values(RelayErrorCode) as [RelayErrorCode, ...RelayErrorCode[]]),\n message: z.string(),\n }),\n\n // 客户端注册协议\n control(\"client_register\", {\n clientId: z.string().min(1),\n }),\n control(\"client_register_response\", {\n status: z.enum([\"restored\", \"proxy_offline\", \"new\"]),\n proxyId: z.string().optional(),\n }),\n\n // Proxy 离线通知\n control(\"proxy_offline\", {\n proxyId: z.string(),\n }),\n\n // Proxy 主动断开,relay 立即清理资源\n control(\"proxy_disconnect\", {\n proxyId: z.string().min(1),\n }),\n\n // Proxy 重连后通知 client 恢复\n control(\"proxy_online\", {\n proxyId: z.string().min(1),\n }),\n\n // 目录列表请求与响应\n control(\n \"dir_list_request\",\n {\n proxyId: z.string().min(1).optional(),\n ...RequestIdShape,\n path: z.string(),\n },\n \"client_to_proxy\",\n ),\n control(\n \"dir_list_response\",\n { ...RequestIdShape, ...RequestErrorShape, entries: z.array(DirEntrySchema), path: z.string() },\n \"proxy_to_client\",\n ),\n\n // 目录创建请求与响应\n control(\"dir_create_request\", { ...RequestIdShape, path: z.string() }, \"client_to_proxy\"),\n control(\n \"dir_create_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n path: z.string(),\n success: z.boolean(),\n },\n \"proxy_to_client\",\n ),\n\n // 命令列表推送,proxy 将可用命令列表推给 client\n control(\"command_list_push\", { commands: z.array(CommandEntrySchema) }, \"proxy_to_client\"),\n\n // 文件树推送: 按目录分组, 首组 path 即为 session cwd\n // 前端直接把每组写入 tree[path], 与 dir_list_response 共享 cache slot\n control(\n \"file_tree_push\",\n {\n groups: z.array(FileTreeGroupSchema),\n },\n \"proxy_to_client\",\n ),\n\n // 会话列表请求与权限模式变更\n control(\"session_list\", undefined, [\"client_to_proxy\", \"proxy_to_client\"]),\n control(\n \"permission_mode_change\",\n {\n mode: z.enum([\"default\", \"auto_accept\", \"plan\"]),\n // sessionId 可选:传入时 proxy 按该会话的 mode 分叉(PTY 发 Tab ANSI),未传走全局日志行为\n sessionId: z.string().optional(),\n },\n \"client_to_proxy\",\n ),\n\n // 会话历史浏览\n control(\"session_history_request\", RequestIdShape, \"client_to_proxy\"),\n control(\n \"session_history_response\",\n { ...RequestIdShape, sessions: z.array(HistorySessionSchema) },\n \"proxy_to_client\",\n ),\n\n // PTY 语义状态,从 Envelope 迁移到 Control 层\n control(\n \"pty_state\",\n { sessionId: z.string(), payload: PtyStatePayloadSchema },\n \"proxy_to_client\",\n ),\n\n // Provider 语义状态,来自 Claude/Codex hook 等结构化事件,不从 PTY 字节推断\n control(\n \"agent_status\",\n { sessionId: z.string(), payload: AgentStatusPayloadSchema },\n \"proxy_to_client\",\n ),\n\n // 终端标题变化,proxy -> client\n control(\"terminal_title\", { sessionId: z.string(), title: z.string() }, \"proxy_to_client\"),\n\n // 终端尺寸变化,proxy -> client\n control(\n \"terminal_resize\",\n { sessionId: z.string(), cols: z.number().int().positive(), rows: z.number().int().positive() },\n \"proxy_to_client\",\n ),\n control(\n \"terminal_resize_request\",\n { sessionId: z.string(), cols: z.number().int().positive(), rows: z.number().int().positive() },\n \"client_to_proxy\",\n ),\n\n // 远程终止 JSON 会话,client -> proxy\n control(\"session_terminate\", { sessionId: z.string() }, \"client_to_proxy\"),\n\n // 中断当前 turn,client -> proxy,SIGINT 到 worker 进程让 claude CLI abort 当前流\n control(\"session_worker_abort\", { sessionId: z.string() }, \"client_to_proxy\"),\n\n // turn 完成信号,proxy -> client,对应 claude stream-json 的 result 事件\n control(\n \"turn_result\",\n {\n sessionId: z.string(),\n success: z.boolean(),\n isError: z.boolean(),\n // stream-json result.result 是本轮最终文本。assistant_message 流丢失或 CLI 未发增量时,\n // Web 用它作为 JSON 模式兜底展示,避免 turn 已结束但界面空白。\n result: z.string().optional(),\n },\n \"proxy_to_client\",\n ),\n\n // 客户端发送到 PTY 的原始字节(ANSI 序列),不追加换行\n control(\n \"remote_input_raw\",\n { sessionId: z.string().min(1), data: z.string() },\n \"client_to_proxy\",\n ),\n\n // 客户端询问 proxy 的环境信息 (home 路径等), client -> proxy -> response\n // FilePathPicker 用 homePath 作为 select 模式下的默认起点, 新建会话时打开即可浏览\n control(\"proxy_info_request\", RequestIdShape, \"client_to_proxy\"),\n control(\n \"proxy_info\",\n { ...RequestIdShape, homePath: z.string(), agentCli: AgentCliStatusSchema },\n \"proxy_to_client\",\n ),\n control(\n \"agent_cli_config_update\",\n { ...RequestIdShape, provider: z.enum([\"claude\", \"codex\"]), path: z.string().min(1) },\n \"client_to_proxy\",\n ),\n control(\n \"agent_cli_config_update_response\",\n {\n ...RequestIdShape,\n provider: z.enum([\"claude\", \"codex\"]),\n agentCli: AgentCliStatusSchema.optional(),\n ...RequestErrorShape,\n },\n \"proxy_to_client\",\n ),\n\n // 远程创建 JSON 会话,client -> proxy -> response\n control(\n \"session_create\",\n {\n ...RequestIdShape,\n cwd: z.string(),\n provider: z.enum([\"claude\", \"codex\"]),\n mode: z.enum([\"json\", \"pty\"]).optional(),\n resumeSessionId: z.string().optional(),\n // 透传给 claude CLI 的 --permission-mode, undefined 时 proxy 兜底为 \"default\"\n permissionMode: z\n .enum([\"default\", \"auto\", \"acceptEdits\", \"plan\", \"bypassPermissions\", \"dontAsk\"])\n .optional(),\n },\n \"client_to_proxy\",\n ),\n control(\n \"session_create_response\",\n {\n ...RequestIdShape,\n sessionId: z.string(),\n mode: z.enum([\"json\", \"pty\"]).optional(),\n provider: z.enum([\"claude\", \"codex\"]).optional(),\n ptyOwner: z.enum([\"local-terminal\", \"proxy-hosted\"]).optional(),\n ...RequestErrorShape,\n },\n \"proxy_to_client\",\n ),\n\n // 客户端请求会话历史消息,client -> proxy\n control(\n \"session_messages_request\",\n { ...RequestIdShape, sessionId: z.string() },\n \"client_to_proxy\",\n ),\n\n // 客户端请求会话资源(命令列表 + 文件树),client -> proxy\n control(\n \"session_resources_request\",\n { ...RequestIdShape, sessionId: z.string() },\n \"client_to_proxy\",\n ),\n control(\n \"session_resources_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n sessionId: z.string(),\n commands: z.array(CommandEntrySchema),\n groups: z.array(FileTreeGroupSchema),\n },\n \"proxy_to_client\",\n ),\n\n // 客户端请求当前 provider 语义状态;不经 relay 缓存,由 proxy 返回当前值\n control(\n \"agent_status_request\",\n { ...RequestIdShape, sessionId: z.string().optional() },\n \"client_to_proxy\",\n ),\n control(\n \"agent_status_response\",\n {\n ...RequestIdShape,\n statuses: z.array(z.object({ sessionId: z.string(), payload: AgentStatusPayloadSchema })),\n },\n \"proxy_to_client\",\n ),\n\n // 客户端确认已收到审批请求;proxy 只记录送达状态,不把它当成用户决策\n control(\n \"permission_request_delivered\",\n { sessionId: z.string(), requestId: z.string() },\n \"client_to_proxy\",\n ),\n control(\n \"tool_approve\",\n { sessionId: z.string(), payload: ToolApprovePayloadSchema },\n \"client_to_proxy\",\n ),\n control(\n \"tool_deny\",\n { sessionId: z.string(), payload: ToolDenyPayloadSchema },\n \"client_to_proxy\",\n ),\n\n // proxy 确认用户决策已进入 provider/worker 路径;web 用它更新审批卡片状态\n control(\n \"permission_decision_result\",\n {\n sessionId: z.string(),\n requestId: z.string(),\n outcome: z.enum([\"allow\", \"deny\"]),\n delivered: z.boolean(),\n message: z.string().optional(),\n },\n \"proxy_to_client\",\n ),\n\n // proxy 推送当前 pending 的工具审批列表,client 据此恢复审批卡片\n control(\n \"pending_approvals_push\",\n {\n sessionId: z.string(),\n approvals: z.array(\n z.object({\n requestId: z.string(),\n toolName: z.string(),\n input: z.record(z.string(), z.unknown()),\n }),\n ),\n },\n \"proxy_to_client\",\n ),\n\n // 恢复会话时推送历史消息,proxy -> client\n control(\n \"session_history_messages\",\n {\n ...RequestIdShape,\n sessionId: z.string(),\n messages: z.array(\n z.object({\n role: z.enum([\"user\", \"assistant\"]),\n text: z.string(),\n timestamp: z.number().optional(),\n }),\n ),\n },\n \"proxy_to_client\",\n ),\n\n // proxy 重连后同步活跃 session 列表给 relay\n control(\"session_sync\", {\n sessions: z.array(\n z.object({\n id: z.string(),\n mode: z.enum([\"pty\", \"json\"]),\n provider: z.enum([\"claude\", \"codex\"]),\n ptyOwner: z.enum([\"local-terminal\", \"proxy-hosted\"]).optional(),\n state: z.string(),\n }),\n ),\n }),\n\n // PTY 会话订阅,client -> proxy,触发 terminal serialize() 返回当前状态\n control(\n \"session_subscribe\",\n { sessionId: z.string(), requestId: z.string().optional() },\n \"client_to_proxy\",\n ),\n\n // PTY 会话快照,proxy -> client,serialize() 的全量终端状态\n control(\n \"session_snapshot\",\n {\n sessionId: z.string(),\n cols: z.number().int().positive(),\n rows: z.number().int().positive(),\n data: z.string(),\n outputSeq: z.number().int().nonnegative(),\n requestId: z.string().optional(),\n },\n \"proxy_to_client\",\n ),\n] as const;\n\nconst relayControlSchemas = relayControlDefinitions.map((definition) => definition.schema) as [\n (typeof relayControlDefinitions)[number][\"schema\"],\n ...Array<(typeof relayControlDefinitions)[number][\"schema\"]>,\n];\n\nexport const RelayControlSchema = z.discriminatedUnion(\"type\", relayControlSchemas);\n\nexport type RelayControlMessage = z.infer<typeof RelayControlSchema>;\nexport type RelayControlType = RelayControlMessage[\"type\"];\n\nexport const ProxyToClientRelayControlTypes = new Set(\n relayControlDefinitions\n .filter((definition) => definition.directions.has(\"proxy_to_client\"))\n .map((definition) => definition.type),\n);\n\nexport function isProxyToClientRelayControlType(type: RelayControlType): boolean {\n return ProxyToClientRelayControlTypes.has(type);\n}\n\nexport const ClientToProxyRelayControlTypes = new Set(\n relayControlDefinitions\n .filter((definition) => definition.directions.has(\"client_to_proxy\"))\n .map((definition) => definition.type),\n);\n\nexport function isClientToProxyRelayControlType(type: RelayControlType): boolean {\n return ClientToProxyRelayControlTypes.has(type);\n}\n","// relay 实际发出的 6 个错误码。schema 侧 RelayControlSchema.relay_error.code 用 z.enum 收紧,\n// handler 侧用这个常量引用避免裸字面量拼错。\nexport const RelayErrorCode = {\n NOT_REGISTERED: \"NOT_REGISTERED\",\n NOT_BOUND: \"NOT_BOUND\",\n PROXY_OFFLINE: \"PROXY_OFFLINE\",\n INVALID_MESSAGE: \"INVALID_MESSAGE\",\n UNSUPPORTED: \"UNSUPPORTED\",\n INVALID_RANGE: \"INVALID_RANGE\",\n} as const;\n\nexport type RelayErrorCode = (typeof RelayErrorCode)[keyof typeof RelayErrorCode];\n","export const ControlErrorCode = {\n INVALID_PATH: \"INVALID_PATH\",\n PATH_NOT_FOUND: \"PATH_NOT_FOUND\",\n PATH_NOT_DIRECTORY: \"PATH_NOT_DIRECTORY\",\n PATH_ACCESS_DENIED: \"PATH_ACCESS_DENIED\",\n PROXY_OFFLINE: \"PROXY_OFFLINE\",\n SESSION_NOT_FOUND: \"SESSION_NOT_FOUND\",\n PROVIDER_UNSUPPORTED: \"PROVIDER_UNSUPPORTED\",\n WORKER_START_FAILED: \"WORKER_START_FAILED\",\n PROCESS_START_FAILED: \"PROCESS_START_FAILED\",\n UNKNOWN: \"UNKNOWN\",\n} as const;\n\nexport type ControlErrorCode = (typeof ControlErrorCode)[keyof typeof ControlErrorCode];\n","import {\n lstatSync,\n mkdirSync,\n readdirSync,\n renameSync,\n statSync,\n symlinkSync,\n unlinkSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { basename, join } from \"node:path\";\nimport pino from \"pino\";\n\nexport type { Logger } from \"pino\";\n\nexport interface CreateLoggerOptions {\n name: string;\n level?: string;\n logDir?: string;\n stdout?: boolean;\n silent?: boolean;\n}\n\nconst DEFAULT_LOG_DIR = `${homedir()}/.dev-anywhere/logs`;\nconst DEFAULT_LOG_RETENTION = 50;\n\nconst PROCESS_LOG_RUN_ID = sanitizeRunId(\n process.env.DEV_ANYWHERE_LOG_RUN_ID ??\n `${new Date().toISOString().replace(/[:.]/g, \"-\")}-${process.pid}`,\n);\n\nfunction sanitizeRunId(runId: string): string {\n return runId.replace(/[^a-zA-Z0-9._-]/g, \"_\");\n}\n\nfunction linkLatestLog(logDir: string, name: string, filePath: string, runId: string): void {\n const latestPath = join(logDir, `${name}.log`);\n\n try {\n const stat = lstatSync(latestPath);\n if (stat.isSymbolicLink()) {\n unlinkSync(latestPath);\n } else {\n renameSync(latestPath, join(logDir, `${name}-legacy-${runId}.log`));\n }\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"ENOENT\") return;\n }\n\n try {\n symlinkSync(basename(filePath), latestPath);\n } catch {\n // 日志本体仍然写入 run-specific 文件;latest 链接失败不应阻塞服务启动。\n }\n}\n\nfunction resolveRetention(): number {\n const raw = process.env.DEV_ANYWHERE_LOG_RETENTION;\n if (!raw) return DEFAULT_LOG_RETENTION;\n const parsed = Number.parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_LOG_RETENTION;\n}\n\nfunction pruneOldLogs(logDir: string, name: string, currentFilePath: string): void {\n const keep = resolveRetention();\n if (keep === 0) return;\n\n const currentFileName = basename(currentFilePath);\n const prefix = `${name}-`;\n const candidates = readdirSync(logDir)\n .filter(\n (entry) => entry.startsWith(prefix) && entry.endsWith(\".log\") && entry !== currentFileName,\n )\n .map((entry) => {\n const path = join(logDir, entry);\n try {\n return { path, mtimeMs: statSync(path).mtimeMs };\n } catch {\n return null;\n }\n })\n .filter((entry): entry is { path: string; mtimeMs: number } => entry !== null)\n .sort((a, b) => b.mtimeMs - a.mtimeMs);\n\n for (const stale of candidates.slice(Math.max(0, keep - 1))) {\n try {\n unlinkSync(stale.path);\n } catch {\n // 日志清理失败不能影响主进程启动。\n }\n }\n}\n\nexport function createLogger(options: CreateLoggerOptions): pino.Logger {\n const {\n name,\n level = \"info\",\n logDir = DEFAULT_LOG_DIR,\n stdout = false,\n silent = false,\n } = options;\n\n if (silent) {\n return pino({ level: \"silent\" });\n }\n\n mkdirSync(logDir, { recursive: true });\n\n const runId = PROCESS_LOG_RUN_ID;\n const filePath = join(logDir, `${name}-${runId}.log`);\n linkLatestLog(logDir, name, filePath, runId);\n pruneOldLogs(logDir, name, filePath);\n const streams: pino.StreamEntry[] = [{ stream: pino.destination(filePath) }];\n\n if (stdout) {\n streams.unshift({ stream: process.stdout });\n }\n\n return pino({ level }, pino.multistream(streams));\n}\n","import { MessageEnvelopeSchema, RelayControlSchema, RelayErrorCode } from \"@dev-anywhere/shared\";\nimport type { RelayControlMessage } from \"@dev-anywhere/shared\";\nimport type { MessageEnvelope } from \"@dev-anywhere/shared\";\nimport { WebSocket } from \"ws\";\nimport type { Logger } from \"@dev-anywhere/shared\";\nimport type { RelayRegistry } from \"./registry.js\";\nimport type { RelayChaos } from \"./chaos.js\";\n\n// 消息解析结果:控制消息、信封消息或无效消息\ntype ParseResult =\n | { kind: \"control\"; message: RelayControlMessage }\n | { kind: \"envelope\"; message: MessageEnvelope; raw: string }\n | { kind: \"invalid\"; error: string };\n\n// 解析 WebSocket 消息,按优先级尝试 RelayControl 和 MessageEnvelope\nexport function parseMessage(data: string): ParseResult {\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch {\n return { kind: \"invalid\", error: \"Invalid JSON\" };\n }\n\n const controlResult = RelayControlSchema.safeParse(parsed);\n if (controlResult.success) {\n return { kind: \"control\", message: controlResult.data };\n }\n\n const envelopeResult = MessageEnvelopeSchema.safeParse(parsed);\n if (envelopeResult.success) {\n return { kind: \"envelope\", message: envelopeResult.data, raw: data };\n }\n\n return { kind: \"invalid\", error: \"Message matches neither RelayControl nor MessageEnvelope\" };\n}\n\n// 将 proxy 发来的 MessageEnvelope 转发给绑定的 client\n// relay 无状态,不缓冲消息,直接透传\nexport function routeProxyMessage(\n raw: string,\n proxyId: string,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n): void {\n const result = parseMessage(raw);\n\n if (result.kind === \"invalid\") {\n logger.warn({ proxyId, error: result.error }, \"Invalid message from proxy\");\n return;\n }\n\n if (result.kind === \"control\") {\n logger.warn({ proxyId }, \"Control message in routeProxyMessage, should be handled by handler\");\n return;\n }\n\n const { message } = result;\n const { sessionId } = message;\n\n // 跟踪该 proxy 拥有的 session\n registry.addSessionToProxy(proxyId, sessionId);\n\n // 转发给所有绑定的客户端\n const clients = registry.getClientsForProxy(proxyId);\n for (const clientWs of clients) {\n if (clientWs.readyState === WebSocket.OPEN) {\n if (chaos) chaos.send(clientWs, raw, { direction: \"proxy_to_client\", type: message.type });\n else clientWs.send(raw);\n }\n }\n}\n\n// 将 client 发来的 MessageEnvelope 转发给绑定的 proxy\n// proxyId 由调用方从 clientId 绑定中解析后传入\nexport function routeClientMessage(\n raw: string,\n proxyId: string,\n clientWs: WebSocket,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n): void {\n const result = parseMessage(raw);\n\n if (result.kind === \"invalid\") {\n logger.warn({ error: result.error }, \"Invalid message from client\");\n return;\n }\n\n if (result.kind === \"control\") {\n logger.warn(\"Control message in routeClientMessage, should be handled by handler\");\n return;\n }\n\n const proxyWs = registry.getProxy(proxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) {\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.PROXY_OFFLINE,\n message: `Proxy ${proxyId} is not available`,\n }),\n );\n return;\n }\n\n if (chaos) chaos.send(proxyWs, raw, { direction: \"client_to_proxy\", type: result.message.type });\n else proxyWs.send(raw);\n}\n","import { WebSocket } from \"ws\";\nimport {\n ControlErrorCode,\n isClientToProxyRelayControlType,\n RelayErrorCode,\n type Logger,\n} from \"@dev-anywhere/shared\";\nimport { nanoid } from \"nanoid\";\nimport type { RelayRegistry } from \"../registry.js\";\nimport { parseMessage, routeClientMessage } from \"../router.js\";\nimport type { RelayChaos } from \"../chaos.js\";\n\n// 扩展 WebSocket 实例存储客户端元数据\ninterface ClientSocket extends WebSocket {\n isAlive: boolean;\n clientId?: string;\n boundProxyId?: string;\n}\n\n// 处理 client_register 消息:三种状态 restored / proxy_offline / new。\n// relay 不缓存输出;恢复由 proxy 重新推送 session_list/agent_status/snapshot 等状态。\nfunction handleClientRegister(\n clientId: string,\n clientWs: ClientSocket,\n registry: RelayRegistry,\n logger: Logger,\n): void {\n clientWs.clientId = clientId;\n\n const binding = registry.getClientBinding(clientId);\n\n if (!binding) {\n clientWs.send(\n JSON.stringify({\n type: \"client_register_response\",\n status: \"new\",\n }),\n );\n logger.info({ clientId, status: \"new\" }, \"Client registered\");\n return;\n }\n\n const { proxyId } = binding;\n registry.updateClientSocket(clientId, clientWs);\n clientWs.boundProxyId = proxyId;\n\n if (!registry.isProxyOnline(proxyId)) {\n clientWs.send(\n JSON.stringify({\n type: \"client_register_response\",\n status: \"proxy_offline\",\n proxyId,\n }),\n );\n logger.info({ clientId, proxyId, status: \"proxy_offline\" }, \"Client registered\");\n return;\n }\n\n // proxy 在线,恢复绑定(relay 无状态,不做增量回放)\n clientWs.send(\n JSON.stringify({\n type: \"client_register_response\",\n status: \"restored\",\n proxyId,\n }),\n );\n\n logger.info({ clientId, proxyId, status: \"restored\" }, \"Client registered\");\n}\n\n// 处理远程客户端 WebSocket 连接生命周期\nexport function handleClientConnection(\n ws: WebSocket,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n): void {\n const clientWs = ws as ClientSocket;\n clientWs.isAlive = true;\n registry.addClientWs(clientWs);\n\n clientWs.on(\"pong\", () => {\n clientWs.isAlive = true;\n });\n\n clientWs.on(\"message\", (data: Buffer, isBinary: boolean) => {\n // Clients only send JSON control/envelope messages; binary frames from clients are ignored.\n if (isBinary) {\n return;\n }\n\n const raw = data.toString();\n const result = parseMessage(raw);\n\n if (result.kind === \"control\") {\n const msg = result.message;\n logger.info(\n { type: msg.type, clientId: clientWs.clientId, bound: clientWs.boundProxyId },\n \"Client message received\",\n );\n\n if (msg.type === \"client_register\") {\n handleClientRegister(msg.clientId, clientWs, registry, logger);\n return;\n }\n\n if (msg.type === \"proxy_list_request\") {\n const proxies = registry.listProxiesWithName().map((p) => ({\n ...p,\n sessions: registry.getSessionsForProxy(p.proxyId),\n }));\n const response = JSON.stringify({\n type: \"proxy_list_response\",\n requestId: msg.requestId,\n proxies,\n });\n if (chaos) {\n chaos.send(clientWs, response, {\n direction: \"proxy_to_client\",\n type: \"proxy_list_response\",\n });\n } else {\n clientWs.send(response);\n }\n return;\n }\n\n // client → proxy 透传:relay 不处理内容,直接转发给绑定的 proxy\n if (isClientToProxyRelayControlType(msg.type)) {\n const targetProxyId =\n (\"proxyId\" in msg ? (msg.proxyId as string) : undefined) || clientWs.boundProxyId;\n if (!targetProxyId) {\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.NOT_BOUND,\n message: \"Client is not bound to any proxy\",\n }),\n );\n return;\n }\n const proxyWs = registry.getProxy(targetProxyId);\n if (proxyWs && proxyWs.readyState === WebSocket.OPEN) {\n if (chaos) chaos.send(proxyWs, raw, { direction: \"client_to_proxy\", type: msg.type });\n else proxyWs.send(raw);\n } else {\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.PROXY_OFFLINE,\n message: `Proxy ${targetProxyId} is not available`,\n }),\n );\n }\n return;\n }\n\n if (msg.type === \"proxy_select\") {\n if (!registry.isProxyOnline(msg.proxyId)) {\n clientWs.send(\n JSON.stringify({\n type: \"proxy_select_response\",\n requestId: msg.requestId,\n success: false,\n errorCode: ControlErrorCode.PROXY_OFFLINE,\n error: `Proxy not online: ${msg.proxyId}`,\n }),\n );\n return;\n }\n // 没有 clientId 时自动分配,统一通过 clientId 绑定\n if (!clientWs.clientId) {\n clientWs.clientId = `anon-${nanoid(10)}`;\n }\n const bound = registry.bindClientById(clientWs.clientId, msg.proxyId, clientWs);\n if (!bound) {\n clientWs.send(\n JSON.stringify({\n type: \"proxy_select_response\",\n requestId: msg.requestId,\n success: false,\n errorCode: ControlErrorCode.PROXY_OFFLINE,\n error: `Proxy not online: ${msg.proxyId}`,\n }),\n );\n return;\n }\n clientWs.boundProxyId = msg.proxyId;\n const response = JSON.stringify({\n type: \"proxy_select_response\",\n requestId: msg.requestId,\n success: true,\n proxyId: msg.proxyId,\n });\n if (chaos) {\n chaos.send(clientWs, response, {\n direction: \"proxy_to_client\",\n type: \"proxy_select_response\",\n });\n } else {\n clientWs.send(response);\n }\n logger.info({ proxyId: msg.proxyId, clientId: clientWs.clientId }, \"Client bound to proxy\");\n return;\n }\n\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.UNSUPPORTED,\n message: `Unsupported control message: ${msg.type}`,\n }),\n );\n return;\n }\n\n if (result.kind === \"envelope\") {\n if (!clientWs.boundProxyId) {\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.NOT_BOUND,\n message: \"Client is not bound to any proxy\",\n }),\n );\n return;\n }\n routeClientMessage(raw, clientWs.boundProxyId, clientWs, registry, logger, chaos);\n return;\n }\n\n logger.error({ error: result.error, raw: raw.slice(0, 200) }, \"Invalid message from client\");\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.INVALID_MESSAGE,\n message: `${result.error} | raw: ${raw.slice(0, 200)}`,\n }),\n );\n });\n\n clientWs.on(\"close\", () => {\n registry.removeClientWs(clientWs);\n logger.info({ clientId: clientWs.clientId }, \"Client disconnected\");\n });\n\n clientWs.on(\"error\", (err) => {\n logger.error({ err }, \"Client WebSocket error\");\n });\n}\n","import type { WebSocket, WebSocketServer } from \"ws\";\n\ninterface HeartbeatSocket extends WebSocket {\n isAlive: boolean;\n}\n\n// 通用 WebSocket 心跳检测,检测死连接并 terminate\n// terminate 触发 close 事件,后续由各自的 close handler 处理恢复逻辑\nexport function setupHeartbeat(wss: WebSocketServer, interval = 30000): NodeJS.Timeout {\n return setInterval(() => {\n for (const ws of wss.clients) {\n const sock = ws as HeartbeatSocket;\n if (!sock.isAlive) {\n sock.terminate();\n continue;\n }\n sock.isAlive = false;\n sock.ping();\n }\n }, interval);\n}\n","import { WebSocket } from \"ws\";\nimport type { Logger } from \"@dev-anywhere/shared\";\n\nexport type RelayChaosDirection = \"client_to_proxy\" | \"proxy_to_client\";\n\nexport interface RelayChaosOptions {\n enabled: boolean;\n delayMs: number;\n duplicate: boolean;\n duplicateDelayMs: number;\n reorder: boolean;\n reorderDelayMs: number;\n types: Set<string> | undefined;\n}\n\nexport interface RelayChaosMeta {\n direction: RelayChaosDirection;\n type: string;\n}\n\nexport interface RelayChaos {\n send(ws: WebSocket, data: string | Buffer, meta: RelayChaosMeta): void;\n}\n\nexport function parseRelayChaosFromEnv(env: NodeJS.ProcessEnv): RelayChaosOptions {\n const enabled = env.DEV_ANYWHERE_RELAY_CHAOS === \"1\";\n const types = env.DEV_ANYWHERE_RELAY_CHAOS_TYPES?.split(\",\")\n .map((type) => type.trim())\n .filter(Boolean);\n\n return {\n enabled,\n delayMs: parseInt(env.DEV_ANYWHERE_RELAY_CHAOS_DELAY_MS ?? \"0\", 10),\n duplicate: env.DEV_ANYWHERE_RELAY_CHAOS_DUPLICATE === \"1\",\n duplicateDelayMs: parseInt(env.DEV_ANYWHERE_RELAY_CHAOS_DUPLICATE_DELAY_MS ?? \"10\", 10),\n reorder: env.DEV_ANYWHERE_RELAY_CHAOS_REORDER === \"1\",\n reorderDelayMs: parseInt(env.DEV_ANYWHERE_RELAY_CHAOS_REORDER_DELAY_MS ?? \"40\", 10),\n types: types && types.length > 0 ? new Set(types) : undefined,\n };\n}\n\nexport function createRelayChaos(options: RelayChaosOptions, logger: Logger): RelayChaos {\n let sequence = 0;\n\n function shouldAffect(meta: RelayChaosMeta): boolean {\n if (!options.enabled) return false;\n if (options.types && !options.types.has(meta.type)) return false;\n return true;\n }\n\n function sendNow(ws: WebSocket, data: string | Buffer): void {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(data);\n }\n }\n\n return {\n send(ws, data, meta) {\n if (!shouldAffect(meta)) {\n sendNow(ws, data);\n return;\n }\n\n sequence += 1;\n const reorderDelay = options.reorder && sequence % 2 === 1 ? options.reorderDelayMs : 0;\n const delayMs = Math.max(0, options.delayMs + reorderDelay);\n\n logger.warn(\n { direction: meta.direction, type: meta.type, delayMs, duplicate: options.duplicate },\n \"Relay chaos forwarding message\",\n );\n\n setTimeout(() => sendNow(ws, data), delayMs);\n\n if (options.duplicate) {\n setTimeout(() => sendNow(ws, data), delayMs + options.duplicateDelayMs);\n }\n },\n };\n}\n"],"mappings":"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","names":["homedir","dirname","fileURLToPath","WebSocket","z","z","z","z","z","z","z","join","WebSocket","WebSocket","WebSocket","WebSocket","WebSocket","dirname","fileURLToPath","homedir","resolve"]}