@dev-anywhere/relay 0.5.30 → 0.5.31
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/assets/web/assets/{fvad-xd_70pC7.js → fvad-vwVWAhiJ.js} +1 -1
- package/assets/web/assets/{index-9-GJJ9SK.js → index-BDXlCg2u.js} +2 -2
- package/assets/web/index.html +1 -1
- package/assets/web/sw.js +1 -1
- package/dist/{chunk-KREYSOOZ.js → chunk-JV6RDQ6G.js} +6 -6
- package/dist/{chunk-KREYSOOZ.js.map → chunk-JV6RDQ6G.js.map} +1 -1
- package/dist/index.js +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts","../src/registry.ts","../../../packages/shared/src/schemas/envelope.ts","../../../packages/shared/src/schemas/id.ts","../../../packages/shared/src/schemas/chat.ts","../../../packages/shared/src/schemas/tool.ts","../../../packages/shared/src/schemas/session.ts","../../../packages/shared/src/constants/enums.ts","../../../packages/shared/src/constants/pty.ts","../../../packages/shared/src/schemas/system.ts","../../../packages/shared/src/schemas/voice.ts","../../../packages/shared/src/schemas/voice-stream.ts","../../../packages/shared/src/builders/index.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/constants/session-create.ts","../../../packages/shared/src/constants/relay-close-codes.ts","../../../packages/shared/src/constants/wire-limits.ts","../../../packages/shared/src/binary-frame.ts","../../../packages/shared/src/voice-audio-codec.ts","../../../packages/shared/src/state-machine.ts","../src/health.ts","../src/version.ts","../src/handlers/proxy.ts","../src/router.ts","../src/latency-probes.ts","../src/handlers/client.ts","../src/voice/bailian-asr.ts","../src/voice/bailian-endpoints.ts","../src/voice/bailian-tts.ts","../src/voice/pcm-resample.ts","../src/voice/config-test.ts","../src/voice/client-controls.ts","../src/heartbeat.ts","../src/chaos.ts","../src/voice/config-store.ts","../src/voice/asr-ws.ts","../src/voice/tts-ws.ts","../src/voice/capabilities.ts","../src/voice/bailian-provider.ts","../src/voice/provider.ts","../src/remote-file-bridge.ts","../src/web-app.ts"],"sourcesContent":["import express from \"express\";\nimport { existsSync } from \"node:fs\";\nimport { createServer, type IncomingMessage, 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/logger\";\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\";\nimport { createVoiceConfigStore } from \"./voice/config-store.js\";\nimport { handleVoiceAsrConnection, type VoiceAsrClientFactory } from \"./voice/asr-ws.js\";\nimport { handleVoiceTtsConnection, type VoiceTtsClientFactory } from \"./voice/tts-ws.js\";\nimport { createBailianVoiceProvider } from \"./voice/bailian-provider.js\";\nimport type { VoiceCapabilitiesProvider } from \"./voice/capabilities.js\";\nimport type { VoiceConfigTester } from \"./voice/config-test.js\";\nimport { createVoiceProviderRegistry, type VoiceProviderRegistry } from \"./voice/provider.js\";\nimport { RemoteFileBridge } from \"./remote-file-bridge.js\";\nimport { mountWebApp } from \"./web-app.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 // ws upgrade 时校验的 Origin 列表 (匹配 request.headers.origin)。空数组 / undefined 不\n // 校验, 向后兼容现有部署 (本地 dev 跨端口 / Capacitor 等)。设置后只放白名单, 阻挡\n // CSWSH——攻击者站点诱导用户浏览器直接发 ws upgrade 到 relay 时, 没有合法 Origin。\n allowedOrigins?: readonly string[];\n chaos?: RelayChaosOptions;\n fontAssetDir?: string;\n webAssetDir?: string | false;\n voiceDefaults?: {\n region?: \"cn\" | \"intl\";\n asrModel?: string;\n ttsModel?: string;\n ttsVoice?: string;\n };\n voiceAsrClientFactory?: VoiceAsrClientFactory;\n voiceTtsClientFactory?: VoiceTtsClientFactory;\n voiceCapabilitiesProvider?: VoiceCapabilitiesProvider;\n voiceConfigTester?: VoiceConfigTester;\n voiceProviderRegistry?: VoiceProviderRegistry;\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\");\nconst PACKAGED_WEB_DIR = resolve(MODULE_DIR, \"../assets/web\");\n\nfunction firstHeaderValue(value: string | string[] | undefined): string | undefined {\n if (Array.isArray(value)) return value[0];\n return value;\n}\n\nfunction requestRemoteAddress(request: IncomingMessage): string | undefined {\n const forwardedFor = firstHeaderValue(request.headers[\"x-forwarded-for\"]);\n const firstForwarded = forwardedFor?.split(\",\")[0]?.trim();\n return firstForwarded || request.socket.remoteAddress;\n}\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 const allowedOriginsSet =\n options.allowedOrigins && options.allowedOrigins.length > 0\n ? new Set(options.allowedOrigins)\n : null;\n const checkOrigin = (origin: string | undefined): boolean => {\n if (!allowedOriginsSet) return true;\n return typeof origin === \"string\" && allowedOriginsSet.has(origin);\n };\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 remoteFileBridge = new RemoteFileBridge({ registry, logger });\n const voiceConfigStore = createVoiceConfigStore({\n dataDir,\n defaults: options.voiceDefaults,\n });\n const voiceProviders =\n options.voiceProviderRegistry ??\n createVoiceProviderRegistry([\n createBailianVoiceProvider({\n asrClientFactory: options.voiceAsrClientFactory,\n ttsClientFactory: options.voiceTtsClientFactory,\n capabilitiesProvider: options.voiceCapabilitiesProvider,\n configTester: options.voiceConfigTester,\n }),\n ]);\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 app.disable(\"x-powered-by\");\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(\n healthRouter(registry, {\n proxyTokenRequired,\n clientTokenRequired,\n validateClientToken: (token) => token === clientToken,\n validateProxyToken: (token) => token === proxyToken,\n getClientToken: () => clientToken ?? null,\n }),\n );\n app.get(\"/api/remote-files/:token\", (req, res) => {\n remoteFileBridge.handleHttpRequest(req, res);\n });\n app.put(\"/api/remote-uploads/:token\", (req, res) => {\n remoteFileBridge.handleUploadHttpRequest(req, res);\n });\n const webAssetDir =\n options.webAssetDir === false ? undefined : (options.webAssetDir ?? PACKAGED_WEB_DIR);\n if (webAssetDir) {\n mountWebApp(app, { webAssetDir, logger });\n }\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 const voiceAsrWss = new WebSocketServer({ noServer: true });\n const voiceTtsWss = 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 const origin = request.headers.origin;\n\n if (!checkOrigin(origin)) {\n logger.warn(\n { ip: request.socket.remoteAddress, origin: origin ?? \"(missing)\", pathname },\n \"rejected upgrade: origin not in allowedOrigins\",\n );\n socket.write(\"HTTP/1.1 403 Forbidden\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\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\" || pathname === \"/voice/asr\" || pathname === \"/voice/tts\") {\n if (clientTokenRequired) {\n const token = url.searchParams.get(\"token\");\n if (token !== clientToken) {\n logger.warn(\n { ip: request.socket.remoteAddress, pathname },\n \"rejected client-side upgrade: invalid token\",\n );\n socket.write(\"HTTP/1.1 401 Unauthorized\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\n }\n if (pathname === \"/client\") {\n clientWss.handleUpgrade(request, socket, head, (ws) => {\n clientWss.emit(\"connection\", ws, request);\n });\n } else if (pathname === \"/voice/asr\") {\n voiceAsrWss.handleUpgrade(request, socket, head, (ws) => {\n voiceAsrWss.emit(\"connection\", ws, request);\n });\n } else {\n voiceTtsWss.handleUpgrade(request, socket, head, (ws) => {\n voiceTtsWss.emit(\"connection\", ws, request);\n });\n }\n return;\n }\n\n socket.destroy();\n });\n\n proxyWss.on(\"connection\", (ws) => {\n handleProxyConnection(ws, registry, logger, relayChaos, remoteFileBridge);\n });\n\n clientWss.on(\"connection\", (ws, request) => {\n handleClientConnection(\n ws,\n registry,\n logger,\n relayChaos,\n voiceConfigStore,\n voiceProviders,\n remoteFileBridge,\n {\n userAgent: firstHeaderValue(request.headers[\"user-agent\"]),\n remoteAddress: requestRemoteAddress(request),\n },\n );\n });\n\n voiceAsrWss.on(\"connection\", (ws) => {\n handleVoiceAsrConnection(ws, voiceConfigStore, logger, voiceProviders);\n });\n\n voiceTtsWss.on(\"connection\", (ws) => {\n handleVoiceTtsConnection(ws, voiceConfigStore, logger, voiceProviders);\n });\n\n const proxyHeartbeat = setupHeartbeat(proxyWss, heartbeatInterval, {\n logger,\n peerType: \"proxy\",\n describePeer: (ws) => ({ proxyId: (ws as { proxyId?: string }).proxyId }),\n });\n const clientHeartbeat = setupHeartbeat(clientWss, heartbeatInterval, {\n logger,\n peerType: \"client\",\n describePeer: (ws) => ({\n clientId: (ws as { clientId?: string }).clientId,\n boundProxyId: (ws as { boundProxyId?: string }).boundProxyId,\n }),\n });\n const voiceAsrHeartbeat = setupHeartbeat(voiceAsrWss, heartbeatInterval, {\n logger,\n peerType: \"voice-asr\",\n });\n const voiceTtsHeartbeat = setupHeartbeat(voiceTtsWss, heartbeatInterval, {\n logger,\n peerType: \"voice-tts\",\n });\n\n async function close(): Promise<void> {\n clearInterval(proxyHeartbeat);\n clearInterval(clientHeartbeat);\n clearInterval(voiceAsrHeartbeat);\n clearInterval(voiceTtsHeartbeat);\n\n for (const ws of proxyWss.clients) {\n ws.terminate();\n }\n for (const ws of clientWss.clients) {\n ws.terminate();\n }\n for (const ws of voiceAsrWss.clients) {\n ws.terminate();\n }\n for (const ws of voiceTtsWss.clients) {\n ws.terminate();\n }\n\n await Promise.all([\n new Promise<void>((resolve, reject) => {\n proxyWss.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n }),\n new Promise<void>((resolve, reject) => {\n clientWss.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n }),\n new Promise<void>((resolve, reject) => {\n voiceAsrWss.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n }),\n new Promise<void>((resolve, reject) => {\n voiceTtsWss.close((err) => {\n if (err) reject(err);\n else resolve();\n });\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\";\nimport { defineFSM } from \"@dev-anywhere/shared\";\n\n// 显式代理连接状态,取代 ws null 检查\ntype ProxyConnectionState = \"online\" | \"offline\";\n\n// 显式客户端连接状态,跟踪注册和绑定\ntype ClientConnectionState = \"registered\" | \"bound\";\n\n// proxy 连接 FSM: 仅 online <-> offline 双向流转。registerProxy 走 reconnect 路径直接写\n// online (相当于 offline -> online), transitionProxy 走 close 路径走 online -> offline。\n// 同状态停留 (online -> online / offline -> offline) 视为非法, 由 canTransition 拦下。\nconst proxyConnectionFSM = defineFSM<ProxyConnectionState>({\n online: [\"offline\"],\n offline: [\"online\"],\n});\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\ninterface ClientConnectionMetadata {\n clientId?: string;\n connectedAt: number;\n userAgent?: string;\n platform?: string;\n maxTouchPoints?: number;\n browserName?: string;\n osName?: string;\n deviceKind?: \"desktop\" | \"tablet\" | \"phone\" | \"unknown\";\n remoteAddress?: string;\n}\n\n// 代理注册、客户端绑定、宽限期管理\nexport class RelayRegistry {\n private proxyStates = new Map<string, ProxyState>();\n private clientBindings = new Map<string, ClientBinding>();\n private connectedClients = new Map<WebSocket, ClientConnectionMetadata>();\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 // 显式状态转换,校验 from 状态匹配后更新 connectionState\n transitionProxy(proxyId: string, from: ProxyConnectionState, to: ProxyConnectionState): void {\n if (!proxyConnectionFSM.canTransition(from, to)) {\n throw new Error(`Invalid proxy transition: ${from} -> ${to}`);\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 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 setSessionsForProxy(proxyId: string, sessionIds: readonly string[]): void {\n const state = this.proxyStates.get(proxyId);\n if (state) {\n state.sessions = new Set(sessionIds);\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 // 连接关闭只能释放自己持有的绑定。旧连接的 close 可能晚于新连接注册,\n // 此时不能把已经切换到新 WebSocket 的绑定清空。\n unbindClientSocket(clientId: string, ws: WebSocket): boolean {\n const binding = this.clientBindings.get(clientId);\n if (!binding || binding.ws !== ws) return false;\n binding.ws = null;\n return true;\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 // 计数依据:ws 还存活且处于 OPEN。仅 `if (binding.ws)` 不够:close handler 漏调\n // unbindClientById 时 ws 对象残留,会让 /status 永久报告虚高 clientCount。\n let count = 0;\n for (const [, binding] of this.clientBindings) {\n if (binding.ws && binding.ws.readyState === WebSocket.OPEN) count++;\n }\n return count;\n }\n\n addClientWs(\n ws: WebSocket,\n metadata: Partial<Omit<ClientConnectionMetadata, \"connectedAt\">> & {\n connectedAt?: number;\n } = {},\n ): void {\n this.connectedClients.set(ws, {\n connectedAt: metadata.connectedAt ?? Date.now(),\n ...(metadata.clientId !== undefined ? { clientId: metadata.clientId } : {}),\n ...(metadata.userAgent !== undefined ? { userAgent: metadata.userAgent } : {}),\n ...(metadata.platform !== undefined ? { platform: metadata.platform } : {}),\n ...(metadata.maxTouchPoints !== undefined ? { maxTouchPoints: metadata.maxTouchPoints } : {}),\n ...(metadata.browserName !== undefined ? { browserName: metadata.browserName } : {}),\n ...(metadata.osName !== undefined ? { osName: metadata.osName } : {}),\n ...(metadata.deviceKind !== undefined ? { deviceKind: metadata.deviceKind } : {}),\n ...(metadata.remoteAddress !== undefined ? { remoteAddress: metadata.remoteAddress } : {}),\n });\n }\n\n updateConnectedClientId(ws: WebSocket, clientId: string): void {\n const metadata = this.connectedClients.get(ws);\n if (metadata) {\n metadata.clientId = clientId;\n return;\n }\n this.addClientWs(ws, { clientId });\n }\n\n updateConnectedClientMetadata(\n ws: WebSocket,\n metadata: Partial<Omit<ClientConnectionMetadata, \"connectedAt\">>,\n ): void {\n const current = this.connectedClients.get(ws);\n if (!current) {\n this.addClientWs(ws, metadata);\n return;\n }\n if (metadata.clientId !== undefined) current.clientId = metadata.clientId;\n if (metadata.userAgent !== undefined) current.userAgent = metadata.userAgent;\n if (metadata.platform !== undefined) current.platform = metadata.platform;\n if (metadata.maxTouchPoints !== undefined) current.maxTouchPoints = metadata.maxTouchPoints;\n if (metadata.browserName !== undefined) current.browserName = metadata.browserName;\n if (metadata.osName !== undefined) current.osName = metadata.osName;\n if (metadata.deviceKind !== undefined) current.deviceKind = metadata.deviceKind;\n if (metadata.remoteAddress !== undefined) current.remoteAddress = metadata.remoteAddress;\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.keys()) {\n if (ws.readyState === WebSocket.OPEN) {\n clients.push(ws);\n }\n }\n return clients;\n }\n\n getConnectedClientSockets(clientId: string): WebSocket[] {\n const sockets: WebSocket[] = [];\n for (const [ws, metadata] of this.connectedClients) {\n if (metadata.clientId === clientId && ws.readyState === WebSocket.OPEN) {\n sockets.push(ws);\n }\n }\n return sockets;\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 getConnectedClientDetails(currentClientId?: string): Array<{\n clientId: string;\n proxyId?: string;\n connectedAt: number;\n current?: boolean;\n userAgent?: string;\n platform?: string;\n maxTouchPoints?: number;\n browserName: string;\n osName: string;\n deviceKind: \"desktop\" | \"tablet\" | \"phone\" | \"unknown\";\n remoteAddress?: string;\n }> {\n const details: Array<{\n clientId: string;\n proxyId?: string;\n connectedAt: number;\n current?: boolean;\n userAgent?: string;\n platform?: string;\n maxTouchPoints?: number;\n browserName: string;\n osName: string;\n deviceKind: \"desktop\" | \"tablet\" | \"phone\" | \"unknown\";\n remoteAddress?: string;\n }> = [];\n for (const [ws, metadata] of this.connectedClients) {\n if (ws.readyState !== WebSocket.OPEN || !metadata.clientId) continue;\n if (!metadata.browserName || !metadata.osName || !metadata.deviceKind) continue;\n const binding = this.clientBindings.get(metadata.clientId);\n details.push({\n clientId: metadata.clientId,\n ...(binding?.proxyId !== undefined ? { proxyId: binding.proxyId } : {}),\n connectedAt: metadata.connectedAt,\n ...(metadata.clientId === currentClientId ? { current: true } : {}),\n ...(metadata.userAgent !== undefined ? { userAgent: metadata.userAgent } : {}),\n ...(metadata.platform !== undefined ? { platform: metadata.platform } : {}),\n ...(metadata.maxTouchPoints !== undefined\n ? { maxTouchPoints: metadata.maxTouchPoints }\n : {}),\n browserName: metadata.browserName,\n osName: metadata.osName,\n deviceKind: metadata.deviceKind,\n ...(metadata.remoteAddress !== undefined ? { remoteAddress: metadata.remoteAddress } : {}),\n });\n }\n return details;\n }\n}\n","import { z } from \"zod\";\nimport { IdSchema } from \"./id.js\";\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// 信封基础字段:序列号、时间戳、来源、协议版本——所有 envelope 共用。\nconst BaseEnvelopeFields = {\n seq: z.number().int().nonnegative(),\n timestamp: z.number(),\n source: z.enum([\"proxy\", \"client\"]),\n version: z.string(),\n};\n\n// session-scoped envelope: 携带 sessionId 路由到具体会话, 大多数业务 envelope 走这里。\nconst SessionedEnvelopeFields = {\n ...BaseEnvelopeFields,\n sessionId: IdSchema,\n};\n\n// 按 type 字段区分的 discriminatedUnion 信封\nexport const MessageEnvelopeSchema = z.discriminatedUnion(\"type\", [\n // chat (3)\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"user_input\"),\n payload: UserInputPayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"assistant_message\"),\n payload: AssistantMessagePayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\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 ...SessionedEnvelopeFields,\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 ...SessionedEnvelopeFields,\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 ...SessionedEnvelopeFields,\n type: z.literal(\"assistant_tool_use\"),\n payload: ToolUseRequestPayloadSchema,\n }),\n // session (5)\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"session_create\"),\n payload: SessionCreatePayloadSchema,\n }),\n // session_list 是全局广播 (列出所有 session), 不绑定具体 sessionId, 不携带该字段。\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"session_list\"),\n payload: SessionListPayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"session_switch\"),\n payload: SessionSwitchPayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"session_terminate\"),\n payload: SessionTerminatePayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"session_status\"),\n payload: SessionStatusPayloadSchema,\n }),\n // system (5): 心跳 / 认证 / 同步——全局, 无 sessionId\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// 所有跨进程传递的标识符 (proxyId / clientId / sessionId / requestId / toolId) 的统一上限。\n// 设计原因:\n// - nanoid (proxy/session/client 服务端默认生成) = 21 字符\n// - claude tool_use id (toolu_<base64ish>) ≈ 30-40 字符\n// - codex tool id 类似量级\n// 256 给协议演进留余量, 同时挡住 wire 上来的恶意超长 ID:\n// - Map<id, _> key 占用 (memory DoS)\n// - 日志行膨胀 (operator/磁盘压力)\n// - 拼到 disk path (虽然当前没有此路径, defense in depth)\nconst MAX_ID_LENGTH = 256;\n\nexport const IdSchema = z.string().min(1).max(MAX_ID_LENGTH);\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\";\nimport { IdSchema } from \"./id.js\";\n\n// 工具调用请求\nexport const ToolUseRequestPayloadSchema = z.object({\n toolName: z.string(),\n toolId: IdSchema,\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: IdSchema,\n whitelistTool: z.boolean().optional(),\n});\n\nexport type ToolApprovePayload = z.infer<typeof ToolApprovePayloadSchema>;\n\n// 工具调用拒绝\nexport const ToolDenyPayloadSchema = z.object({\n toolId: IdSchema,\n reason: z.string().optional(),\n});\n\nexport type ToolDenyPayload = z.infer<typeof ToolDenyPayloadSchema>;\n\n// 工具调用结果\nexport const ToolResultPayloadSchema = z.object({\n toolId: IdSchema,\n result: z.unknown(),\n isError: z.boolean(),\n});\n\nexport type ToolResultPayload = z.infer<typeof ToolResultPayloadSchema>;\n","import { z } from \"zod\";\nimport {\n providerValues,\n ptyOwnerValues,\n sessionKindValues,\n sessionModeValues,\n} from \"../constants/enums.js\";\nimport { ptySemanticStateValues } from \"../constants/pty.js\";\nimport { IdSchema } from \"./id.js\";\n\nexport const sessionStateValues = [\n \"idle\",\n \"working\",\n \"compacting\",\n \"waiting_approval\",\n \"error\",\n \"terminated\",\n] 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: IdSchema,\n kind: z.enum(sessionKindValues).optional(),\n name: z.string().optional(),\n // cwd 只用于展示完整路径/tooltip,不作为前端路由或权限判断来源。\n cwd: z.string().optional(),\n // true 表示 name 是用户显式命名,PTY UI 不再让 OSC terminal_title 覆盖它。\n nameLocked: z.boolean().optional(),\n state: z.enum(sessionStateValues),\n mode: z.enum(sessionModeValues).optional(),\n provider: z.enum(providerValues),\n // PTY 进程所有权。两种 PTY 的 cols/rows 都由会话端持有,Web 仅按服务端快照展示,\n // 浏览器视口变化不会隐式改变真实 PTY 几何。\n // - local-terminal: 本地 terminal worker 持有 PTY\n // - proxy-hosted: serve 内 HostedPtyRegistry 持有 PTY\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: IdSchema,\n});\n\nexport type SessionSwitchPayload = z.infer<typeof SessionSwitchPayloadSchema>;\n\n// 终止会话\nexport const SessionTerminatePayloadSchema = z.object({\n sessionId: IdSchema,\n});\n\nexport type SessionTerminatePayload = z.infer<typeof SessionTerminatePayloadSchema>;\n\n// 会话状态变更\n// lastActive: 触发本次状态迁移或活动刷新的时间戳 (ms),用于列表相对时间显示。\nexport const SessionStatusPayloadSchema = z.object({\n sessionId: IdSchema,\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(ptySemanticStateValues),\n // Semantic event sequence. Present for runtime PTY semantic events; optional for\n // legacy cleanup messages from older proxies.\n seq: z.number().int().nonnegative().optional(),\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: IdSchema,\n toolName: z.string(),\n input: z.record(z.string(), z.unknown()),\n })\n .optional(),\n permissionResolution: z\n .object({\n requestId: IdSchema,\n outcome: z.enum([\"allow\", \"deny\"]),\n })\n .optional(),\n summary: z.string().optional(),\n});\nexport type AgentStatusPayload = z.infer<typeof AgentStatusPayloadSchema>;\n","// 协议层共用的枚举字面量集合。Zod schema、TypeScript 类型、运行时常量都从这里派生,\n// 避免在 relay-control / session / chat / web 各自手写 z.enum([\"claude\", \"codex\"]) 漂移。\n\nexport const providerValues = [\"claude\", \"codex\"] as const;\nexport type ProviderId = (typeof providerValues)[number];\n\nexport const ptyOwnerValues = [\"local-terminal\", \"proxy-hosted\"] as const;\nexport type PtyOwner = (typeof ptyOwnerValues)[number];\n\nexport const sessionModeValues = [\"pty\", \"json\"] as const;\nexport type SessionMode = (typeof sessionModeValues)[number];\n\nexport const sessionKindValues = [\"agent\", \"terminal\"] as const;\nexport type SessionKind = (typeof sessionKindValues)[number];\n","// PTY 局部语义状态。仅承载明确语义信号; title/spinner 变化时事件的 state 取 null,\n// 让上层不参与 FSM 切换, 只走 title 推送通道。\n// 单一 source of truth: osc-extractor / ipc-protocol / shared schema 都从这里取。\nexport const PtySemanticState = {\n WORKING: \"working\",\n TURN_COMPLETE: \"turn_complete\",\n APPROVAL_WAIT: \"approval_wait\",\n} as const;\n\nexport type PtySemanticState = (typeof PtySemanticState)[keyof typeof PtySemanticState];\n\n// zod / 类型字面量场景下复用的 const tuple, z.enum 直接吃。\nexport const ptySemanticStateValues = [\n PtySemanticState.WORKING,\n PtySemanticState.TURN_COMPLETE,\n PtySemanticState.APPROVAL_WAIT,\n] as const;\n\n// Web 创建 PTY 时可以按可用视口放大初始几何,但不能低于传统 80x24。\n// 这个底线既保证常见 TUI 布局,也避免 Claude Code / Codex 等 CLI 因终端过窄\n// 省略二维码或启动信息。上限用于拦截异常客户端提交的不合理 PTY 尺寸。\nexport const PTY_INITIAL_MIN_COLS = 80;\nexport const PTY_INITIAL_MIN_ROWS = 24;\nexport const PTY_INITIAL_MAX_COLS = 500;\nexport const PTY_INITIAL_MAX_ROWS = 200;\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\";\n\nexport const voiceProviderValues = [\"aliyun-bailian\"] as const;\nexport const voiceRegionValues = [\"cn\", \"intl\"] as const;\nexport const voiceOptionSourceValues = [\"official\", \"custom\"] as const;\nexport const voiceOptionGenderValues = [\"male\", \"female\", \"unknown\"] as const;\n\nexport const VoiceProviderConfigSchema = z\n .object({\n provider: z.enum(voiceProviderValues),\n configured: z.boolean(),\n region: z.enum(voiceRegionValues),\n asrModel: z.string().min(1),\n ttsModel: z.string().min(1),\n ttsVoice: z.string().min(1),\n turnIdleSeconds: z.number().int().positive().safe().default(3),\n })\n .strict();\nexport type VoiceProviderConfig = z.infer<typeof VoiceProviderConfigSchema>;\n\nexport const VoiceConfigUpdateSchema = z\n .object({\n provider: z.enum(voiceProviderValues).optional(),\n apiKey: z.string().min(1).optional(),\n clearApiKey: z.boolean().optional(),\n region: z.enum(voiceRegionValues).optional(),\n asrModel: z.string().min(1).optional(),\n ttsModel: z.string().min(1).optional(),\n ttsVoice: z.string().min(1).optional(),\n turnIdleSeconds: z.number().int().positive().safe().optional(),\n })\n .strict();\nexport type VoiceConfigUpdate = z.infer<typeof VoiceConfigUpdateSchema>;\n\nexport const VoiceOptionSchema = z\n .object({\n value: z.string().min(1),\n label: z.string().min(1),\n description: z.string().min(1).optional(),\n gender: z.enum(voiceOptionGenderValues).optional(),\n age: z.string().min(1).optional(),\n model: z.string().min(1).optional(),\n source: z.enum(voiceOptionSourceValues),\n })\n .strict();\nexport type VoiceOption = z.infer<typeof VoiceOptionSchema>;\n\nexport const VoiceCapabilitiesSchema = z\n .object({\n asrModels: z.array(VoiceOptionSchema),\n ttsModels: z.array(VoiceOptionSchema),\n ttsVoices: z.array(VoiceOptionSchema),\n fetchedAt: z.number().optional(),\n })\n .strict();\nexport type VoiceCapabilities = z.infer<typeof VoiceCapabilitiesSchema>;\n\n// Official snapshot from Alibaba Cloud Bailian docs, captured 2026-05-18.\n// Alibaba does not currently expose a clean API-key-scoped catalog endpoint for these system models.\nconst BUNDLED_BAILIAN_ASR_MODELS: VoiceOption[] = [\n {\n value: \"qwen3-asr-flash-realtime\",\n label: \"Qwen3 ASR Flash Realtime\",\n source: \"official\",\n },\n {\n value: \"qwen3-asr-flash-realtime-2026-02-10\",\n label: \"Qwen3 ASR Flash Realtime · 2026-02-10\",\n source: \"official\",\n },\n {\n value: \"qwen3-asr-flash-realtime-2025-10-27\",\n label: \"Qwen3 ASR Flash Realtime · 2025-10-27\",\n source: \"official\",\n },\n];\n\nconst BUNDLED_BAILIAN_TTS_MODELS: VoiceOption[] = [\n {\n value: \"cosyvoice-v3-flash\",\n label: \"CosyVoice V3 Flash · 系统音色\",\n source: \"official\",\n },\n {\n value: \"cosyvoice-v3-plus\",\n label: \"CosyVoice V3 Plus · 系统音色\",\n source: \"official\",\n },\n {\n value: \"cosyvoice-v3.5-flash\",\n label: \"CosyVoice V3.5 Flash · 自定义音色\",\n source: \"official\",\n },\n {\n value: \"cosyvoice-v3.5-plus\",\n label: \"CosyVoice V3.5 Plus · 自定义音色\",\n source: \"official\",\n },\n];\n\nconst BUNDLED_BAILIAN_TTS_VOICES: VoiceOption[] = [\n {\n value: \"longanyang\",\n label: \"龙安洋 · 男 · 阳光大男孩 · 年龄 20-30\",\n gender: \"male\",\n age: \"20-30\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longanhuan\",\n label: \"龙安欢 · 女 · 欢脱元气 · 年龄 20-30\",\n gender: \"female\",\n age: \"20-30\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longhuhu_v3\",\n label: \"龙呼呼 · 女 · 天真烂漫女童 · 年龄 6-10\",\n gender: \"female\",\n age: \"6-10\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longpaopao_v3\",\n label: \"龙泡泡 · 未知 · 飞天泡泡音 · 年龄 6-15\",\n gender: \"unknown\",\n age: \"6-15\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longjielidou_v3\",\n label: \"龙杰力豆 · 男 · 阳光顽皮 · 年龄 10\",\n gender: \"male\",\n age: \"10\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longxian_v3\",\n label: \"龙仙 · 女 · 豪放可爱 · 年龄 12\",\n gender: \"female\",\n age: \"12\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longling_v3\",\n label: \"龙铃 · 女 · 稚气呆板 · 年龄 10\",\n gender: \"female\",\n age: \"10\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longjiaxin_v3\",\n label: \"龙嘉欣 · 女 · 优雅粤语 · 年龄 30-35\",\n gender: \"female\",\n age: \"30-35\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longanyue_v3\",\n label: \"龙安粤 · 男 · 欢脱粤语 · 年龄 25-35\",\n gender: \"male\",\n age: \"25-35\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longlaotie_v3\",\n label: \"龙老铁 · 男 · 东北直率 · 年龄 25-30\",\n gender: \"male\",\n age: \"25-30\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longanyang\",\n label: \"龙安洋 · 男 · 阳光大男孩 · 年龄 20-30\",\n gender: \"male\",\n age: \"20-30\",\n model: \"cosyvoice-v3-plus\",\n source: \"official\",\n },\n {\n value: \"longanhuan\",\n label: \"龙安欢 · 女 · 欢脱元气 · 年龄 20-30\",\n gender: \"female\",\n age: \"20-30\",\n model: \"cosyvoice-v3-plus\",\n source: \"official\",\n },\n];\n\nfunction cloneVoiceOption(option: VoiceOption): VoiceOption {\n return { ...option };\n}\n\nexport function createBundledBailianVoiceCapabilities(fetchedAt?: number): VoiceCapabilities {\n return {\n asrModels: BUNDLED_BAILIAN_ASR_MODELS.map(cloneVoiceOption),\n ttsModels: BUNDLED_BAILIAN_TTS_MODELS.map(cloneVoiceOption),\n ttsVoices: BUNDLED_BAILIAN_TTS_VOICES.map(cloneVoiceOption),\n ...(typeof fetchedAt === \"number\" ? { fetchedAt } : {}),\n };\n}\n\nexport const VoiceSummaryReasonSchema = z.enum([\n \"code\",\n \"table\",\n \"diff\",\n \"log\",\n \"stack_trace\",\n \"long_list\",\n \"long_text\",\n \"mixed\",\n \"approval\",\n]);\nexport type VoiceSummaryReason = z.infer<typeof VoiceSummaryReasonSchema>;\n","import { z } from \"zod\";\n\nexport const VoiceAsrAudioEncodingSchema = z.enum([\"pcm_s16le\", \"mulaw\"]);\n\nexport const VoiceAsrStartMessageSchema = z.object({\n type: z.literal(\"start\"),\n sessionId: z.string().min(1),\n attemptId: z.string().min(1),\n sampleRate: z.number().int().positive(),\n encoding: VoiceAsrAudioEncodingSchema,\n});\n\nexport const VoiceAsrStopMessageSchema = z.object({\n type: z.literal(\"stop\"),\n attemptId: z.string().min(1),\n});\n\nexport const VoiceAsrClientMessageSchema = z.discriminatedUnion(\"type\", [\n VoiceAsrStartMessageSchema,\n VoiceAsrStopMessageSchema,\n]);\n\nexport const VoiceAsrServerMessageSchema = z.discriminatedUnion(\"type\", [\n z.object({ type: z.literal(\"ready\"), attemptId: z.string().min(1) }),\n z.object({\n type: z.literal(\"audio_ack\"),\n attemptId: z.string().min(1),\n encodedBytes: z.number().int().nonnegative(),\n pcmBytes: z.number().int().nonnegative(),\n chunks: z.number().int().nonnegative(),\n }),\n z.object({ type: z.literal(\"partial\"), attemptId: z.string().min(1), text: z.string() }),\n z.object({ type: z.literal(\"final\"), attemptId: z.string().min(1), text: z.string() }),\n z.object({\n type: z.literal(\"closed\"),\n attemptId: z.string().min(1),\n code: z.number().int().optional(),\n reason: z.string().optional(),\n }),\n z.object({\n type: z.literal(\"error\"),\n attemptId: z.string().min(1),\n error: z.string().optional(),\n errorCode: z.string().optional(),\n }),\n]);\n\nexport type VoiceAsrAudioEncoding = z.infer<typeof VoiceAsrAudioEncodingSchema>;\nexport type VoiceAsrStartMessage = z.infer<typeof VoiceAsrStartMessageSchema>;\nexport type VoiceAsrStopMessage = z.infer<typeof VoiceAsrStopMessageSchema>;\nexport type VoiceAsrClientMessage = z.infer<typeof VoiceAsrClientMessageSchema>;\nexport type VoiceAsrServerMessage = z.infer<typeof VoiceAsrServerMessageSchema>;\n","import type { MessageEnvelope } from \"../schemas/envelope.js\";\nimport { MessageEnvelopeSchema } from \"../schemas/envelope.js\";\nimport type { RelayControlMessage } from \"../schemas/relay-control.js\";\n\n// 构建经过 schema 验证的消息信封\n// seq 由调用方提供,必须与 EventStore per-session seq 一致,保证 proxy 和 relay 对账可靠\n// sessionId 仅 session-scoped envelope 携带; 全局广播 (session_list / heartbeat / auth /\n// sync_*) 传 null 或省略, 否则要求合法 ID。\nexport function buildMessage<T extends MessageEnvelope[\"type\"]>(\n type: T,\n sessionId: string | null,\n seq: number,\n payload: Extract<MessageEnvelope, { type: T }>[\"payload\"],\n source: \"proxy\" | \"client\",\n): Extract<MessageEnvelope, { type: T }> {\n const envelope = {\n seq,\n ...(sessionId !== null ? { sessionId } : {}),\n type,\n payload,\n timestamp: Date.now(),\n source,\n version: \"1.0\",\n };\n return MessageEnvelopeSchema.parse(envelope) as Extract<MessageEnvelope, { type: T }>;\n}\n\n// 序列化 relay control 消息。借助 RelayControlMessage 这个 discriminated union,\n// 调用方传入 { type, ... } 时 TypeScript 会按 type 反查出该消息允许携带的字段集,\n// 替代之前散落的 sendRaw(JSON.stringify({ type: \"...\", ... })) 模式 —— 后者在编译期\n// 完全没有约束,任何字段拼写错误 / 字段漏填都到 wire 上才暴露。\n//\n// 这里没有再做一次 RelayControlSchema.parse(msg):proxy 在热路径每条 envelope 都要发,\n// 重复 zod 解析对吞吐有压力;编译期类型检查已经能挡住大部分错误。\n// 真要做运行时校验时,调用 RelayControlSchema.parse(msg) 再 stringify 即可。\nexport function serializeControl(msg: RelayControlMessage): string {\n return JSON.stringify(msg);\n}\n","import { z } from \"zod\";\nimport { IdSchema } from \"./id.js\";\nimport { AgentStatusPayloadSchema, PtyStatePayloadSchema, sessionStateValues } from \"./session.js\";\nimport { ToolApprovePayloadSchema, ToolDenyPayloadSchema } from \"./tool.js\";\nimport {\n VoiceCapabilitiesSchema,\n VoiceConfigUpdateSchema,\n VoiceProviderConfigSchema,\n VoiceSummaryReasonSchema,\n voiceRegionValues,\n} from \"./voice.js\";\nimport { RelayErrorCode } from \"../constants/relay-errors.js\";\nimport { ControlErrorCode } from \"../constants/control-errors.js\";\nimport {\n providerValues,\n ptyOwnerValues,\n sessionKindValues,\n sessionModeValues,\n} from \"../constants/enums.js\";\nimport { PTY_INITIAL_MAX_COLS, PTY_INITIAL_MAX_ROWS } from \"../constants/pty.js\";\n\n// 控制消息中复用的子类型\nexport const ProxyInfoSchema = z.object({\n proxyId: IdSchema,\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 RelayClientInfoSchema = z.object({\n clientId: IdSchema,\n proxyId: IdSchema.optional(),\n connectedAt: z.number().int().nonnegative(),\n current: z.boolean().optional(),\n userAgent: z.string().optional(),\n platform: z.string().optional(),\n maxTouchPoints: z.number().int().nonnegative().optional(),\n browserName: z.string().min(1),\n osName: z.string().min(1),\n deviceKind: z.enum([\"desktop\", \"tablet\", \"phone\", \"unknown\"]),\n remoteAddress: z.string().optional(),\n});\nexport type RelayClientInfo = z.infer<typeof RelayClientInfoSchema>;\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(providerValues).optional(),\n preferredMode: z.enum(sessionModeValues).optional(),\n});\nexport type HistorySession = z.infer<typeof HistorySessionSchema>;\n\nconst SessionHistoryPositionShape = {\n timestamp: z.number().optional(),\n cursor: z.string().optional(),\n};\n\nexport const SessionHistoryMessageSchema = z.discriminatedUnion(\"role\", [\n z.object({\n role: z.enum([\"user\", \"assistant\", \"system\"]),\n text: z.string(),\n ...SessionHistoryPositionShape,\n }),\n z.object({\n role: z.literal(\"activity\"),\n text: z.string(),\n toolId: IdSchema,\n toolName: z.string(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n status: z.enum([\"running\", \"done\", \"error\"]),\n ...SessionHistoryPositionShape,\n }),\n]);\nexport type SessionHistoryMessage = z.infer<typeof SessionHistoryMessageSchema>;\n\ntype RelayControlDirection = \"proxy_to_client\" | \"client_to_proxy\";\ntype EmptyShape = Record<never, never>;\nconst RequestIdShape = { requestId: IdSchema.optional() };\nconst RequiredRequestIdShape = { requestId: IdSchema };\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 RemoteFileDispositionSchema = z.enum([\"inline\", \"download\"]);\nconst RemoteFileUploadKindSchema = z.enum([\"clipboard_image\", \"file\"]);\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: IdSchema,\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(\"relay_client_list_request\", RequestIdShape),\n control(\"relay_client_list_response\", {\n ...RequestIdShape,\n clients: z.array(RelayClientInfoSchema),\n }),\n control(\"relay_client_kick\", { ...RequiredRequestIdShape, clientId: IdSchema }),\n control(\"relay_client_kick_response\", {\n ...RequiredRequestIdShape,\n clientId: IdSchema,\n success: z.boolean(),\n ...RequestErrorShape,\n }),\n control(\"relay_client_kicked\", {\n reason: z.string().optional(),\n }),\n control(\"remote_file_url_request\", {\n ...RequiredRequestIdShape,\n sessionId: IdSchema,\n path: z.string().min(1),\n disposition: RemoteFileDispositionSchema,\n }),\n control(\"remote_file_url_response\", {\n ...RequiredRequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\n path: z.string().optional(),\n success: z.boolean(),\n url: z.string().optional(),\n expiresAt: z.number().int().nonnegative().optional(),\n }),\n control(\"remote_file_metadata_request\", {\n ...RequiredRequestIdShape,\n sessionId: IdSchema,\n path: z.string().min(1),\n }),\n control(\"remote_file_metadata_response\", {\n ...RequiredRequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\n path: z.string().optional(),\n success: z.boolean(),\n mimeType: z.string().optional(),\n size: z.number().int().nonnegative().optional(),\n fileName: z.string().optional(),\n }),\n control(\"remote_file_stream_request\", {\n streamId: IdSchema,\n sessionId: IdSchema,\n path: z.string().min(1),\n disposition: RemoteFileDispositionSchema,\n }),\n control(\"remote_file_stream_response\", {\n ...RequestErrorShape,\n streamId: IdSchema,\n sessionId: IdSchema,\n success: z.boolean(),\n path: z.string().optional(),\n mimeType: z.string().optional(),\n size: z.number().int().nonnegative().optional(),\n fileName: z.string().optional(),\n }),\n control(\"remote_file_stream_complete\", {\n ...RequestErrorShape,\n streamId: IdSchema,\n success: z.boolean(),\n }),\n control(\"remote_file_stream_cancel\", {\n streamId: IdSchema,\n }),\n control(\"remote_file_upload_url_request\", {\n ...RequiredRequestIdShape,\n sessionId: IdSchema,\n kind: RemoteFileUploadKindSchema,\n fileName: z.string().optional(),\n mimeType: z.string().min(1),\n size: z.number().int().nonnegative().optional(),\n }),\n control(\"remote_file_upload_url_response\", {\n ...RequiredRequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\n success: z.boolean(),\n uploadUrl: z.string().optional(),\n expiresAt: z.number().int().nonnegative().optional(),\n }),\n control(\"remote_file_upload_stream_request\", {\n uploadId: IdSchema,\n sessionId: IdSchema,\n kind: RemoteFileUploadKindSchema,\n fileName: z.string().optional(),\n mimeType: z.string().min(1),\n size: z.number().int().nonnegative().optional(),\n }),\n control(\"remote_file_upload_stream_complete\", {\n uploadId: IdSchema,\n }),\n control(\"remote_file_upload_stream_cancel\", {\n uploadId: IdSchema,\n }),\n control(\"remote_file_upload_stream_response\", {\n ...RequestErrorShape,\n uploadId: IdSchema,\n sessionId: IdSchema,\n success: z.boolean(),\n path: z.string().optional(),\n }),\n control(\"proxy_select\", { ...RequestIdShape, proxyId: IdSchema }),\n control(\"proxy_select_response\", {\n ...RequestIdShape,\n success: z.boolean(),\n proxyId: IdSchema.optional(),\n ...RequestErrorShape,\n }),\n control(\"relay_error\", {\n code: z.enum(Object.values(RelayErrorCode) as [RelayErrorCode, ...RelayErrorCode[]]),\n message: z.string(),\n // 可选 requestId: relay 把 client 发来 raw 的 requestId 字段透传回来,\n // client 侧 waitForMessage 据此把对应 pending request 立即拒掉而不必等到 timeout。\n requestId: IdSchema.optional(),\n }),\n\n // Voice Pilot config is relay-local: client reads/updates the relay's stored provider settings.\n control(\"voice_config_request\", RequestIdShape),\n control(\"voice_config_response\", {\n ...RequestIdShape,\n ...RequestErrorShape,\n config: VoiceProviderConfigSchema.optional(),\n }),\n control(\"voice_config_update\", {\n ...RequestIdShape,\n config: VoiceConfigUpdateSchema,\n }),\n control(\"voice_config_update_response\", {\n ...RequestIdShape,\n ...RequestErrorShape,\n success: z.boolean(),\n config: VoiceProviderConfigSchema.optional(),\n }),\n control(\"voice_config_test\", {\n ...RequestIdShape,\n config: VoiceConfigUpdateSchema.optional(),\n }),\n control(\"voice_config_test_response\", {\n ...RequestIdShape,\n ...RequestErrorShape,\n success: z.boolean(),\n audioBase64: z.string().optional(),\n audioSampleRate: z.number().int().positive().optional(),\n audioEncoding: z.literal(\"pcm_s16le\").optional(),\n transcript: z.string().optional(),\n }),\n control(\"voice_capabilities_request\", {\n ...RequestIdShape,\n region: z.enum(voiceRegionValues).optional(),\n }),\n control(\"voice_capabilities_response\", {\n ...RequestIdShape,\n ...RequestErrorShape,\n capabilities: VoiceCapabilitiesSchema.optional(),\n }),\n\n // Lightweight latency probes. These measure synthetic round-trip latency for the transport\n // segments and intentionally stay separate from PTY input echo tracing.\n control(\"latency_web_relay_ping\", RequiredRequestIdShape),\n control(\"latency_web_relay_pong\", {\n ...RequiredRequestIdShape,\n relayNow: z.number().optional(),\n }),\n control(\"latency_relay_proxy_request\", RequiredRequestIdShape),\n control(\"latency_relay_proxy_response\", {\n ...RequiredRequestIdShape,\n success: z.boolean(),\n rttMs: z.number().nonnegative().optional(),\n error: z.string().optional(),\n }),\n control(\"latency_relay_proxy_ping\", {\n ...RequiredRequestIdShape,\n relayNow: z.number().optional(),\n }),\n control(\"latency_relay_proxy_pong\", {\n ...RequiredRequestIdShape,\n proxyNow: z.number().optional(),\n }),\n control(\"latency_web_proxy_ping\", RequiredRequestIdShape, \"client_to_proxy\"),\n control(\n \"latency_web_proxy_pong\",\n { ...RequiredRequestIdShape, proxyNow: z.number().optional() },\n \"proxy_to_client\",\n ),\n\n // 客户端注册协议\n control(\"client_register\", {\n clientId: IdSchema,\n userAgent: z.string().optional(),\n platform: z.string().optional(),\n maxTouchPoints: z.number().int().nonnegative().optional(),\n browserName: z.string().min(1),\n osName: z.string().min(1),\n deviceKind: z.enum([\"desktop\", \"tablet\", \"phone\", \"unknown\"]),\n }),\n control(\"client_register_response\", {\n status: z.enum([\"restored\", \"proxy_offline\", \"new\"]),\n proxyId: IdSchema.optional(),\n }),\n\n // Proxy 离线通知\n control(\"proxy_offline\", {\n proxyId: IdSchema,\n }),\n\n // Proxy 主动断开,relay 立即清理资源\n control(\"proxy_disconnect\", {\n proxyId: IdSchema,\n }),\n\n // Proxy 重连后通知 client 恢复\n control(\"proxy_online\", {\n proxyId: IdSchema,\n }),\n\n // 目录列表请求与响应\n control(\n \"dir_list_request\",\n {\n proxyId: IdSchema.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: IdSchema.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(\"pty_state\", { sessionId: IdSchema, payload: PtyStatePayloadSchema }, \"proxy_to_client\"),\n\n // Provider 语义状态,来自 Claude/Codex hook 等结构化事件,不从 PTY 字节推断\n control(\n \"agent_status\",\n { sessionId: IdSchema, payload: AgentStatusPayloadSchema },\n \"proxy_to_client\",\n ),\n\n // 终端标题变化,proxy -> client\n control(\"terminal_title\", { sessionId: IdSchema, title: z.string() }, \"proxy_to_client\"),\n\n // 终端尺寸变化,proxy -> client\n control(\n \"terminal_resize\",\n { sessionId: IdSchema, cols: z.number().int().positive(), rows: z.number().int().positive() },\n \"proxy_to_client\",\n ),\n control(\n \"terminal_resize_request\",\n { sessionId: IdSchema, 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: IdSchema }, \"client_to_proxy\"),\n control(\n \"session_rename\",\n { ...RequestIdShape, sessionId: IdSchema, name: z.string() },\n \"client_to_proxy\",\n ),\n control(\n \"session_rename_response\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\n success: z.boolean(),\n name: z.string().optional(),\n ...RequestErrorShape,\n },\n \"proxy_to_client\",\n ),\n\n // 中断当前 turn,client -> proxy;JSON 由 session-worker 打断 Claude 子进程并保留会话。\n control(\"session_worker_abort\", { sessionId: IdSchema }, \"client_to_proxy\"),\n\n // turn 完成信号,proxy -> client,对应 claude stream-json 的 result 事件\n control(\n \"turn_result\",\n {\n sessionId: IdSchema,\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: IdSchema, data: z.string(), traceId: IdSchema.optional() },\n \"client_to_proxy\",\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(providerValues), 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(providerValues),\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 kind: z.enum(sessionKindValues).optional(),\n cwd: z.string().optional(),\n name: z.string().optional(),\n provider: z.enum(providerValues).optional(),\n mode: z.enum(sessionModeValues).optional(),\n resumeSessionId: z.string().optional(),\n // 仅用于新建 PTY 的一次性初始几何。创建后尺寸由会话端持有,浏览器刷新、\n // 重连或换设备不会隐式重排既有终端内容。\n cols: z.number().int().positive().max(PTY_INITIAL_MAX_COLS).optional(),\n rows: z.number().int().positive().max(PTY_INITIAL_MAX_ROWS).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 // 失败路径只送 errorCode/error, sessionId 此时无语义。成功路径才有 id。\n sessionId: IdSchema.optional(),\n name: z.string().optional(),\n nameLocked: z.boolean().optional(),\n kind: z.enum(sessionKindValues).optional(),\n mode: z.enum(sessionModeValues).optional(),\n provider: z.enum(providerValues).optional(),\n ptyOwner: z.enum(ptyOwnerValues).optional(),\n ...RequestErrorShape,\n },\n \"proxy_to_client\",\n ),\n\n // 客户端请求会话历史消息,client -> proxy\n control(\n \"session_messages_request\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\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: IdSchema },\n \"client_to_proxy\",\n ),\n control(\n \"session_resources_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\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: IdSchema.optional() },\n \"client_to_proxy\",\n ),\n control(\n \"agent_status_response\",\n {\n ...RequestIdShape,\n statuses: z.array(z.object({ sessionId: IdSchema, payload: AgentStatusPayloadSchema })),\n },\n \"proxy_to_client\",\n ),\n\n // 客户端确认已收到审批请求;proxy 只记录送达状态,不把它当成用户决策\n control(\n \"permission_request_delivered\",\n { sessionId: IdSchema, requestId: IdSchema },\n \"client_to_proxy\",\n ),\n control(\n \"tool_approve\",\n { sessionId: IdSchema, payload: ToolApprovePayloadSchema },\n \"client_to_proxy\",\n ),\n control(\"tool_deny\", { sessionId: IdSchema, payload: ToolDenyPayloadSchema }, \"client_to_proxy\"),\n\n // proxy 确认用户决策已进入 provider/worker 路径;web 用它更新审批卡片状态\n control(\n \"permission_decision_result\",\n {\n sessionId: IdSchema,\n requestId: IdSchema,\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: IdSchema,\n approvals: z.array(\n z.object({\n requestId: IdSchema,\n toolName: z.string(),\n input: z.record(z.string(), z.unknown()),\n }),\n ),\n },\n \"proxy_to_client\",\n ),\n\n // Voice Pilot speech summaries are produced by proxy-side Claude Code so it can read project context.\n control(\n \"voice_summary_request\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\n messageId: IdSchema,\n text: z.string().min(1),\n reason: VoiceSummaryReasonSchema,\n },\n \"client_to_proxy\",\n ),\n control(\n \"voice_summary_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\n messageId: IdSchema,\n success: z.boolean(),\n summary: z.string().min(1).optional(),\n },\n \"proxy_to_client\",\n ),\n\n // 恢复会话时推送历史消息,proxy -> client\n control(\n \"session_history_messages\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\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。session_sync 由 relay 自消费(更新 proxy-session\n // 关联)不转发给 client,因此**没有** direction 标注——RelayControlDirection 只描述转发流。\n control(\"session_sync\", {\n sessions: z.array(\n z.object({\n id: z.string(),\n kind: z.enum(sessionKindValues).optional(),\n mode: z.enum(sessionModeValues),\n provider: z.enum(providerValues),\n ptyOwner: z.enum(ptyOwnerValues).optional(),\n cwd: z.string().optional(),\n name: z.string().optional(),\n nameLocked: z.boolean().optional(),\n state: z.enum(sessionStateValues),\n }),\n ),\n }),\n\n // PTY 会话订阅,client -> proxy,触发 terminal serialize() 返回当前状态\n control(\n \"session_subscribe\",\n { sessionId: IdSchema, requestId: IdSchema.optional() },\n \"client_to_proxy\",\n ),\n\n // PTY 会话快照,proxy -> client,serialize() 的全量终端状态\n control(\n \"session_snapshot\",\n {\n sessionId: IdSchema,\n cols: z.number().int().positive(),\n rows: z.number().int().positive(),\n data: z.string(),\n outputSeq: z.number().int().nonnegative(),\n requestId: IdSchema.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","// Session creation is a two-sided deadline contract. The proxy must finish startup and\n// respond before its deadline; the browser waits a little longer so the failure response\n// can cross the relay instead of racing an equal local timeout.\nexport const SESSION_CREATE_SERVER_DEADLINE_MS = 30_000;\nexport const SESSION_CREATE_RESPONSE_GRACE_MS = 5_000;\nexport const SESSION_CREATE_CLIENT_TIMEOUT_MS =\n SESSION_CREATE_SERVER_DEADLINE_MS + SESSION_CREATE_RESPONSE_GRACE_MS;\n","// WebSocket close codes in the private 4000-4999 range.\nexport const RelayCloseCode = {\n CLIENT_KICKED: 4401,\n CLIENT_PROTOCOL_REJECTED: 4402,\n} as const;\n","// WebSocket wire limits shared by relay and proxy.\n//\n// PTY initial snapshots use JSON control messages because xterm serialize()\n// returns ANSI text. A long 5000-line scrollback can legitimately exceed 1 MiB,\n// so the JSON cap must cover terminal snapshots while still bounding parse cost.\nexport const RELAY_JSON_MESSAGE_MAX_BYTES = 8 * 1024 * 1024;\n\n// Raw PTY output and remote-file chunks use binary frames. Keep this aligned\n// with the relay-side frame guard.\nexport const RELAY_BINARY_FRAME_MAX_BYTES = 10 * 1024 * 1024;\n","// PTY binary 帧 wire 格式(跨 proxy / relay / web 三方通用):\n// [1B sessionId_len][sessionId UTF-8][4B outputSeq uint32LE][PTY data]\n// 任一方手写偏移量都极易在协议演进时漂移,统一走这里编/解码避免分叉。\n//\n// 用 Uint8Array 而非 Buffer:shared 包必须可以在浏览器构建里跑,\n// 不能依赖 Node-only 的 Buffer 全局。Node 调用方拿到的也是 Uint8Array\n// (Buffer 是 Uint8Array 的子类,反向兼容),传给 ws.send 等 API 没问题。\n\nexport interface DecodedBinaryFrame {\n sessionId: string;\n outputSeq: number;\n data: Uint8Array;\n}\n\nexport interface DecodedFileStreamFrame {\n streamId: string;\n chunkSeq: number;\n data: Uint8Array;\n}\n\nconst SID_LEN_BYTES = 1;\nconst SEQ_BYTES = 4;\nconst HEADER_FIXED_BYTES = SID_LEN_BYTES + SEQ_BYTES;\nconst FILE_STREAM_MARKER = 0;\nconst FILE_STREAM_ID_LEN_BYTES = 1;\nconst FILE_STREAM_HEADER_FIXED_BYTES = 1 /* marker */ + FILE_STREAM_ID_LEN_BYTES + SEQ_BYTES;\n\nexport function encodeBinaryFrame(\n sessionId: string,\n outputSeq: number,\n data: Uint8Array,\n): Uint8Array {\n const sidBytes = new TextEncoder().encode(sessionId);\n if (sidBytes.length === 0 || sidBytes.length > 255) {\n throw new RangeError(\n `sessionId byte length must be 1-255, got ${sidBytes.length} (sessionId=${sessionId})`,\n );\n }\n if (!Number.isInteger(outputSeq) || outputSeq < 0 || outputSeq > 0xffffffff) {\n throw new RangeError(`outputSeq must be a uint32, got ${outputSeq}`);\n }\n\n const frame = new Uint8Array(SID_LEN_BYTES + sidBytes.length + SEQ_BYTES + data.length);\n frame[0] = sidBytes.length;\n frame.set(sidBytes, SID_LEN_BYTES);\n const seqOffset = SID_LEN_BYTES + sidBytes.length;\n // little-endian 与 Node 端 Buffer.writeUInt32LE / 浏览器 DataView setUint32(_, _, true) 保持一致\n new DataView(frame.buffer, frame.byteOffset + seqOffset, SEQ_BYTES).setUint32(0, outputSeq, true);\n frame.set(data, seqOffset + SEQ_BYTES);\n return frame;\n}\n\n// 解码失败一律返回 null,调用方按业务诉求决定是丢帧还是关连接。\nexport function decodeBinaryFrame(view: Uint8Array): DecodedBinaryFrame | null {\n if (view.length < SID_LEN_BYTES + SEQ_BYTES) return null;\n const sidLen = view[0];\n if (sidLen === 0) return null;\n if (view.length < SID_LEN_BYTES + sidLen + SEQ_BYTES) return null;\n\n const sessionId = new TextDecoder().decode(view.subarray(SID_LEN_BYTES, SID_LEN_BYTES + sidLen));\n const seqOffset = SID_LEN_BYTES + sidLen;\n const outputSeq = new DataView(view.buffer, view.byteOffset + seqOffset, SEQ_BYTES).getUint32(\n 0,\n true,\n );\n const data = view.subarray(seqOffset + SEQ_BYTES);\n return { sessionId, outputSeq, data };\n}\n\n// 帧头长度(不含 payload data):调用方在做 chunk 拆分 / 估算缓冲时用得到。\nexport function binaryFrameHeaderLength(sessionId: string): number {\n const sidBytes = new TextEncoder().encode(sessionId);\n return HEADER_FIXED_BYTES + sidBytes.length;\n}\n\nexport function encodeFileStreamFrame(\n streamId: string,\n chunkSeq: number,\n data: Uint8Array,\n): Uint8Array {\n const streamIdBytes = new TextEncoder().encode(streamId);\n if (streamIdBytes.length === 0 || streamIdBytes.length > 255) {\n throw new RangeError(\n `streamId byte length must be 1-255, got ${streamIdBytes.length} (streamId=${streamId})`,\n );\n }\n if (!Number.isInteger(chunkSeq) || chunkSeq < 0 || chunkSeq > 0xffffffff) {\n throw new RangeError(`chunkSeq must be a uint32, got ${chunkSeq}`);\n }\n\n const frame = new Uint8Array(\n 1 + FILE_STREAM_ID_LEN_BYTES + streamIdBytes.length + SEQ_BYTES + data.length,\n );\n frame[0] = FILE_STREAM_MARKER;\n frame[1] = streamIdBytes.length;\n frame.set(streamIdBytes, 1 + FILE_STREAM_ID_LEN_BYTES);\n const seqOffset = 1 + FILE_STREAM_ID_LEN_BYTES + streamIdBytes.length;\n new DataView(frame.buffer, frame.byteOffset + seqOffset, SEQ_BYTES).setUint32(0, chunkSeq, true);\n frame.set(data, seqOffset + SEQ_BYTES);\n return frame;\n}\n\nexport function decodeFileStreamFrame(view: Uint8Array): DecodedFileStreamFrame | null {\n if (view.length < FILE_STREAM_HEADER_FIXED_BYTES) return null;\n if (view[0] !== FILE_STREAM_MARKER) return null;\n const streamIdLen = view[1];\n if (streamIdLen === 0) return null;\n if (view.length < 1 + FILE_STREAM_ID_LEN_BYTES + streamIdLen + SEQ_BYTES) return null;\n\n const streamId = new TextDecoder().decode(\n view.subarray(1 + FILE_STREAM_ID_LEN_BYTES, 1 + FILE_STREAM_ID_LEN_BYTES + streamIdLen),\n );\n const seqOffset = 1 + FILE_STREAM_ID_LEN_BYTES + streamIdLen;\n const chunkSeq = new DataView(view.buffer, view.byteOffset + seqOffset, SEQ_BYTES).getUint32(\n 0,\n true,\n );\n const data = view.subarray(seqOffset + SEQ_BYTES);\n return { streamId, chunkSeq, data };\n}\n\nexport function fileStreamFrameHeaderLength(streamId: string): number {\n const streamIdBytes = new TextEncoder().encode(streamId);\n return FILE_STREAM_HEADER_FIXED_BYTES + streamIdBytes.length;\n}\n","const MU_LAW_BIAS = 0x84;\nconst MU_LAW_CLIP = 32635;\n\nfunction encodeSample(sampleValue: number): number {\n let sample = sampleValue;\n const sign = sample < 0 ? 0x80 : 0;\n if (sample < 0) sample = -sample;\n sample = Math.min(sample, MU_LAW_CLIP) + MU_LAW_BIAS;\n\n let exponent = 7;\n for (let mask = 0x4000; exponent > 0 && (sample & mask) === 0; mask >>= 1) {\n exponent -= 1;\n }\n const mantissa = (sample >> (exponent + 3)) & 0x0f;\n return ~(sign | (exponent << 4) | mantissa) & 0xff;\n}\n\nfunction decodeSample(encodedValue: number): number {\n const value = ~encodedValue & 0xff;\n const sign = value & 0x80;\n const exponent = (value >> 4) & 0x07;\n const mantissa = value & 0x0f;\n const magnitude = ((mantissa << 3) + MU_LAW_BIAS) * 2 ** exponent - MU_LAW_BIAS;\n return sign ? -magnitude : magnitude;\n}\n\nexport function encodePcm16ToMuLaw(pcm: Uint8Array): Uint8Array {\n const sampleCount = Math.floor(pcm.byteLength / 2);\n const encoded = new Uint8Array(sampleCount);\n const view = new DataView(pcm.buffer, pcm.byteOffset, pcm.byteLength);\n for (let index = 0; index < sampleCount; index += 1) {\n encoded[index] = encodeSample(view.getInt16(index * 2, true));\n }\n return encoded;\n}\n\nexport function decodeMuLawToPcm16(encoded: Uint8Array): Uint8Array {\n const pcm = new Uint8Array(encoded.byteLength * 2);\n const view = new DataView(pcm.buffer);\n for (let index = 0; index < encoded.byteLength; index += 1) {\n view.setInt16(index * 2, decodeSample(encoded[index] ?? 0xff), true);\n }\n return pcm;\n}\n","// 有限状态机 helper\n//\n// 提供显式转换表的小型 FSM,区分两类调用模式:\n// - transitionTo(throw):同步确定性流程里调用,非法转移代表 bug,立即暴露\n// - tryTransitionTo(bool):异步事件回调里调用,非法转移可能是吸收态残余事件,调用方按\n// isInAbsorbingState() 分级日志\n//\n// 吸收态采用传递闭包定义:终态(transitions=[])或所有出边都指向吸收态的状态都算吸收。\n// 这样 SessionState.ERROR (→[TERMINATED]) 和 RelayConnectionState.CLOSED ([]) 都被\n// 自动识别为吸收态,不用在 caller 里硬编码状态名。\n//\n// onTransition/onRejected 仅做日志/观测,不应 throw。\n\ninterface FSMDef<S extends string> {\n initial: S;\n // from-state → 允许转入的 to-state 列表;终态对应空数组\n transitions: Record<S, readonly S[]>;\n // 合法转换发生后触发,典型用法是结构化日志\n onTransition?: (from: S, to: S) => void;\n // tryTransitionTo 非法转移时触发;isAbsorbing 指示 from 是否吸收态(晚到残余 vs. 真非法)\n onRejected?: (from: S, to: S, isAbsorbing: boolean) => void;\n}\n\ninterface FSM<S extends string> {\n current(): S;\n is(state: S): boolean;\n isIn(states: readonly S[]): boolean;\n canTransitionTo(to: S): boolean;\n // 非法转换抛 Error;同步流程里当 assert 用\n transitionTo(to: S): void;\n // 非法转换返回 false,不抛;异步回调里用,配合 isInAbsorbingState 分级日志\n tryTransitionTo(to: S): boolean;\n // 当前是否在吸收态(传递闭包)\n isInAbsorbingState(): boolean;\n}\n\n// 计算吸收态集合:终态 + 所有出边都指向吸收态的状态,迭代至不动点\nfunction computeAbsorbingSet<S extends string>(transitions: Record<S, readonly S[]>): Set<S> {\n const absorbing = new Set<S>();\n const entries = Object.entries(transitions) as Array<[S, readonly S[]]>;\n for (const [s, outs] of entries) {\n if (outs.length === 0) absorbing.add(s);\n }\n let changed = true;\n while (changed) {\n changed = false;\n for (const [s, outs] of entries) {\n if (absorbing.has(s)) continue;\n if (outs.length > 0 && outs.every((t) => absorbing.has(t))) {\n absorbing.add(s);\n changed = true;\n }\n }\n }\n return absorbing;\n}\n\nexport function createFSM<S extends string>(def: FSMDef<S>): FSM<S> {\n let state = def.initial;\n const absorbing = computeAbsorbingSet(def.transitions);\n const tryTransitionTo = (to: S): boolean => {\n const allowed = def.transitions[state];\n if (!allowed?.includes(to)) {\n def.onRejected?.(state, to, absorbing.has(state));\n return false;\n }\n const from = state;\n state = to;\n def.onTransition?.(from, to);\n return true;\n };\n return {\n current: () => state,\n is: (s) => state === s,\n isIn: (ss) => ss.includes(state),\n canTransitionTo: (to) => def.transitions[state]?.includes(to) ?? false,\n transitionTo: (to) => {\n if (!tryTransitionTo(to)) {\n throw new Error(`Invalid FSM transition: ${state} -> ${to}`);\n }\n },\n tryTransitionTo,\n isInAbsorbingState: () => absorbing.has(state),\n };\n}\n\n// 无内部 state 的 FSM 视图,供 state 存在外部(如 SessionInfo、DB 行)的 per-instance 场景使用。\n// 调用方自行传入 from,canTransition 校验;吸收态判定通过 isAbsorbing(state) 提供。\ninterface StatelessFSM<S extends string> {\n canTransition(from: S, to: S): boolean;\n isAbsorbing(state: S): boolean;\n}\n\nexport function defineFSM<S extends string>(transitions: Record<S, readonly S[]>): StatelessFSM<S> {\n const absorbing = computeAbsorbingSet(transitions);\n return {\n canTransition: (from, to) => transitions[from]?.includes(to) ?? false,\n isAbsorbing: (state) => absorbing.has(state),\n };\n}\n","import { Router } from \"express\";\nimport type { RelayRegistry } from \"./registry.js\";\nimport { RELAY_VERSION } from \"./version.js\";\n\ninterface HealthRouterOptions {\n proxyTokenRequired?: boolean;\n clientTokenRequired?: boolean;\n validateClientToken?: (token: string | null) => boolean;\n validateProxyToken?: (token: string | null) => boolean;\n // proxyToken 验证通过且 client token 已配置时返回当前值;否则返回 null。\n getClientToken?: () => string | null;\n}\n\nfunction bearerToken(authHeader: string | undefined): string | null {\n const match = /^Bearer\\s+(.+)$/i.exec(authHeader ?? \"\");\n return match?.[1] ?? null;\n}\n\n// 健康检查和状态查询路由\nexport function healthRouter(registry: RelayRegistry, options: HealthRouterOptions = {}): Router {\n const router = Router();\n const proxyTokenRequired = options.proxyTokenRequired ?? false;\n const clientTokenRequired = options.clientTokenRequired ?? false;\n\n router.get(\"/health\", (_req, res) => {\n res.json({\n status: \"ok\",\n version: RELAY_VERSION,\n uptime: process.uptime(),\n auth: {\n proxyTokenRequired,\n clientTokenRequired,\n },\n });\n });\n\n router.get(\"/api/auth/client\", (req, res) => {\n if (!clientTokenRequired) {\n res.status(204).end();\n return;\n }\n const token = bearerToken(req.get(\"authorization\"));\n if (options.validateClientToken?.(token)) {\n res.status(204).end();\n return;\n }\n res.status(401).json({ error: \"invalid_client_token\" });\n });\n\n // 已认证 proxy(凭 proxyToken)查询当前生效的 client token,避免运维者必须 ssh 上来读 .env。\n // 行为:proxy token 关闭时直接 401(防止开放 relay 公开 token);proxy token 不匹配 401;\n // 校验通过且 client token 已配置返回 { clientToken };client token 未配置返回 204。\n router.get(\"/api/admin/client-token\", (req, res) => {\n if (!proxyTokenRequired) {\n res.status(401).json({ error: \"proxy_token_required\" });\n return;\n }\n const token = bearerToken(req.get(\"authorization\"));\n if (!options.validateProxyToken?.(token)) {\n res.status(401).json({ error: \"invalid_proxy_token\" });\n return;\n }\n const clientToken = options.getClientToken?.() ?? null;\n if (!clientToken) {\n res.status(204).end();\n return;\n }\n res.json({ clientToken });\n });\n\n // proxyToken 配置时需要 Bearer 校验,否则放行(dev 模式)。\n // /api/status、/api/proxies、/api/clients 暴露拓扑/绑定信息,公网 relay 必须鉴权防遍历探测。\n function requireProxyTokenIfConfigured(\n req: import(\"express\").Request,\n res: import(\"express\").Response,\n ): boolean {\n if (!proxyTokenRequired) return true;\n const token = bearerToken(req.get(\"authorization\"));\n if (options.validateProxyToken?.(token)) return true;\n res.status(401).json({ error: \"invalid_proxy_token\" });\n return false;\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 if (!requireProxyTokenIfConfigured(req, res)) return;\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 if (!requireProxyTokenIfConfigured(req, res)) return;\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 if (!requireProxyTokenIfConfigured(req, res)) return;\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 {\n isProxyToClientRelayControlType,\n RELAY_BINARY_FRAME_MAX_BYTES,\n RelayErrorCode,\n RELAY_JSON_MESSAGE_MAX_BYTES,\n serializeControl,\n} from \"@dev-anywhere/shared\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { RelayRegistry } from \"../registry.js\";\nimport { parseMessage, routeProxyMessage } from \"../router.js\";\nimport type { RelayChaos } from \"../chaos.js\";\nimport { completeRelayProxyLatencyProbe } from \"../latency-probes.js\";\nimport type { RemoteFileBridge } from \"../remote-file-bridge.js\";\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\nfunction rejectNotRegistered(ws: ProxySocket): void {\n ws.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.NOT_REGISTERED,\n message: \"Proxy must register before sending messages\",\n }),\n );\n}\n\n// 处理代理端 WebSocket 连接生命周期\nexport function handleProxyConnection(\n ws: WebSocket,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n remoteFileBridge?: RemoteFileBridge,\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 > RELAY_BINARY_FRAME_MAX_BYTES) {\n logger.warn({ size: data.length }, \"Binary frame rejected: invalid size\");\n return;\n }\n if (proxyWs.proxyId && remoteFileBridge?.handleProxyBinary(proxyWs.proxyId, data)) {\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 if (data.length > RELAY_JSON_MESSAGE_MAX_BYTES) {\n logger.warn(\n { size: data.length, proxyId: proxyWs.proxyId },\n \"JSON message rejected: exceeds max size\",\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 serializeControl({\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.unregisterProxy(proxyWs.proxyId);\n logger.info(\n { proxyId: proxyWs.proxyId },\n \"Proxy gracefully disconnected, resources cleaned\",\n );\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;\n registry.setSessionsForProxy(\n proxyWs.proxyId,\n sessions.map((s) => s.id),\n );\n logger.info({ proxyId: proxyWs.proxyId, count: sessions.length }, \"Session sync received\");\n return;\n }\n\n if (result.kind === \"control\" && result.message.type === \"latency_relay_proxy_pong\") {\n if (!proxyWs.proxyId) {\n rejectNotRegistered(proxyWs);\n return;\n }\n const completed = completeRelayProxyLatencyProbe({\n proxyId: proxyWs.proxyId,\n requestId: result.message.requestId,\n logger,\n });\n if (!completed) {\n logger.debug(\n { proxyId: proxyWs.proxyId, requestId: result.message.requestId },\n \"Unmatched relay-proxy latency pong ignored\",\n );\n }\n return;\n }\n\n // proxy 发给 client 的控制消息:直接转发给绑定的客户端,不进 session buffer\n if (result.kind === \"control\") {\n if (proxyWs.proxyId && result.message.type === \"remote_file_stream_response\") {\n remoteFileBridge?.handleProxyControl(proxyWs.proxyId, result.message);\n return;\n }\n if (proxyWs.proxyId && result.message.type === \"remote_file_metadata_response\") {\n remoteFileBridge?.handleProxyControl(proxyWs.proxyId, result.message);\n return;\n }\n if (proxyWs.proxyId && result.message.type === \"remote_file_stream_complete\") {\n remoteFileBridge?.handleProxyControl(proxyWs.proxyId, result.message);\n return;\n }\n if (proxyWs.proxyId && result.message.type === \"remote_file_upload_stream_response\") {\n remoteFileBridge?.handleProxyControl(proxyWs.proxyId, result.message);\n return;\n }\n if (isProxyToClientRelayControlType(result.message.type)) {\n if (!proxyWs.proxyId) {\n rejectNotRegistered(proxyWs);\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 rejectNotRegistered(proxyWs);\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\", (code: number, reason: Buffer) => {\n if (!proxyWs.proxyId) return;\n const closeMeta = { code, reason: reason.toString() || undefined };\n // 同 proxyId 重连场景:registerProxy 会 terminate 旧 ws、把 registry 指向新 ws,\n // 旧 ws 的 close 异步触发到达这里时,registry.getProxy(proxyId) 已是新 ws 实例。\n // 此时若仍执行 transitionProxy(\"online\", \"offline\"),会把新连接的状态翻回离线并广播一次假离线。\n // 仅当 registry 当前持有的 ws 仍是我们自己(或 entry 已被 proxy_disconnect 清掉)时才走离线流程。\n const current = registry.getProxy(proxyWs.proxyId);\n if (current && current !== proxyWs) {\n logger.info(\n { proxyId: proxyWs.proxyId, ...closeMeta },\n \"Old proxy ws closed after being superseded by reconnect, skipping offline transition\",\n );\n return;\n }\n notifyClientsProxyOffline(proxyWs.proxyId, registry, logger, chaos);\n try {\n registry.transitionProxy(proxyWs.proxyId, \"online\", \"offline\");\n } catch (err) {\n // 期望路径: proxy_disconnect 已清理 entry, 或 entry 已 offline——transitionProxy 抛\n // \"Proxy not found\" / state mismatch。debug 级别记录 err 以便真有 FSM bug 时能定位。\n logger.debug(\n { proxyId: proxyWs.proxyId, err: String(err) },\n \"transitionProxy on close skipped\",\n );\n }\n logger.info(\n { proxyId: proxyWs.proxyId, ...closeMeta },\n \"Proxy disconnected, state preserved for reconnect\",\n );\n broadcastProxyList(registry, chaos);\n });\n\n proxyWs.on(\"error\", (err) => {\n logger.error({ err, proxyId: proxyWs.proxyId }, \"Proxy WebSocket error\");\n });\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/logger\";\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// 从 raw JSON 抓 requestId, 用来给 invalid message 拼回 relay_error 时填充——这样\n// client waitForMessage 能用 requestId 立即拒对应的 pending Promise, 不必等 timeout。\nfunction extractRequestIdFromRaw(raw: string): string | undefined {\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\" && typeof parsed.requestId === \"string\") {\n return parsed.requestId;\n }\n } catch {\n // raw 不是合法 JSON, 直接没 requestId\n }\n return undefined;\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\n // session-scoped envelope 在 proxy 与 sessionId 间建立关联; 全局广播 (session_list /\n // heartbeat / auth / sync_*) 没有 sessionId, 直接广播给所有 client。\n if (\"sessionId\" in message) {\n registry.addSessionToProxy(proxyId, message.sessionId);\n }\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 // 把拒绝原因带 requestId 发回 client, 让 waitForMessage 立刻拒 pending Promise。\n // 不发回的话, 调用方只看到 \"上传超时\" / \"请求超时\" 这种泛用 timeout, 不知道\n // 实际是 schema 不认。常见触发场景: 客户端有新协议, relay 在旧 build 上还没认。\n const requestId = extractRequestIdFromRaw(raw);\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.INVALID_MESSAGE,\n message: result.error,\n ...(requestId ? { requestId } : {}),\n }),\n );\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 { performance } from \"node:perf_hooks\";\nimport { WebSocket } from \"ws\";\nimport { serializeControl, type ControlMessage } from \"@dev-anywhere/shared\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { RelayChaos } from \"./chaos.js\";\n\nconst DEFAULT_RELAY_PROXY_PROBE_TIMEOUT_MS = 3_000;\n\ninterface RelayProxyProbe {\n proxyId: string;\n requestId: string;\n clientWs: WebSocket;\n startedAt: number;\n timer: ReturnType<typeof setTimeout>;\n chaos?: RelayChaos;\n}\n\nconst pendingRelayProxyProbes = new Map<string, RelayProxyProbe>();\n\nfunction keyFor(proxyId: string, requestId: string): string {\n return `${proxyId}:${requestId}`;\n}\n\nfunction sendClientResponse(\n clientWs: WebSocket,\n response: ControlMessage<\"latency_relay_proxy_response\">,\n chaos?: RelayChaos,\n): void {\n if (clientWs.readyState !== WebSocket.OPEN) return;\n const raw = serializeControl(response);\n if (chaos) {\n chaos.send(clientWs, raw, {\n direction: \"proxy_to_client\",\n type: \"latency_relay_proxy_response\",\n });\n return;\n }\n clientWs.send(raw);\n}\n\nexport function startRelayProxyLatencyProbe({\n requestId,\n proxyId,\n proxyWs,\n clientWs,\n logger,\n chaos,\n timeoutMs = DEFAULT_RELAY_PROXY_PROBE_TIMEOUT_MS,\n}: {\n requestId: string;\n proxyId: string;\n proxyWs: WebSocket;\n clientWs: WebSocket;\n logger: Logger;\n chaos?: RelayChaos;\n timeoutMs?: number;\n}): void {\n const key = keyFor(proxyId, requestId);\n const existing = pendingRelayProxyProbes.get(key);\n if (existing) {\n clearTimeout(existing.timer);\n pendingRelayProxyProbes.delete(key);\n }\n\n const startedAt = performance.now();\n const timer = setTimeout(() => {\n pendingRelayProxyProbes.delete(key);\n sendClientResponse(\n clientWs,\n {\n type: \"latency_relay_proxy_response\",\n requestId,\n success: false,\n error: \"Relay 到开发机测速超时\",\n },\n chaos,\n );\n logger.warn({ proxyId, requestId }, \"Relay-proxy latency probe timed out\");\n }, timeoutMs);\n\n pendingRelayProxyProbes.set(key, {\n proxyId,\n requestId,\n clientWs,\n startedAt,\n timer,\n chaos,\n });\n\n const raw = serializeControl({\n type: \"latency_relay_proxy_ping\",\n requestId,\n relayNow: Date.now(),\n });\n if (chaos) {\n chaos.send(proxyWs, raw, { direction: \"client_to_proxy\", type: \"latency_relay_proxy_ping\" });\n return;\n }\n proxyWs.send(raw);\n}\n\nexport function completeRelayProxyLatencyProbe({\n proxyId,\n requestId,\n logger,\n}: {\n proxyId: string;\n requestId: string;\n logger: Logger;\n}): boolean {\n const key = keyFor(proxyId, requestId);\n const pending = pendingRelayProxyProbes.get(key);\n if (!pending) return false;\n\n pendingRelayProxyProbes.delete(key);\n clearTimeout(pending.timer);\n const rttMs = performance.now() - pending.startedAt;\n\n sendClientResponse(\n pending.clientWs,\n {\n type: \"latency_relay_proxy_response\",\n requestId,\n success: true,\n rttMs,\n },\n pending.chaos,\n );\n logger.debug({ proxyId, requestId, rttMs }, \"Relay-proxy latency probe completed\");\n return true;\n}\n","import { WebSocket } from \"ws\";\nimport {\n ControlErrorCode,\n isClientToProxyRelayControlType,\n RelayCloseCode,\n RelayErrorCode,\n RELAY_JSON_MESSAGE_MAX_BYTES,\n serializeControl,\n type ControlErrorCodeType,\n} from \"@dev-anywhere/shared\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { RelayRegistry } from \"../registry.js\";\nimport { parseMessage, routeClientMessage } from \"../router.js\";\nimport type { RelayChaos } from \"../chaos.js\";\nimport { handleVoiceConfigControl } from \"../voice/client-controls.js\";\nimport type { VoiceConfigStore } from \"../voice/config-store.js\";\nimport type { VoiceProviderRegistry } from \"../voice/provider.js\";\nimport { startRelayProxyLatencyProbe } from \"../latency-probes.js\";\nimport type { RemoteFileBridge } from \"../remote-file-bridge.js\";\n\n// 扩展 WebSocket 实例存储客户端元数据\ninterface ClientSocket extends WebSocket {\n isAlive: boolean;\n clientId?: string;\n boundProxyId?: string;\n}\n\ninterface ClientConnectionInfo {\n userAgent?: string;\n remoteAddress?: string;\n}\n\ninterface ClientRegisterInfo {\n clientId: string;\n userAgent?: string;\n platform?: string;\n maxTouchPoints?: number;\n browserName: string;\n osName: string;\n deviceKind: \"desktop\" | \"tablet\" | \"phone\" | \"unknown\";\n}\n\nfunction isMalformedClientRegister(raw: string): boolean {\n try {\n const parsed = JSON.parse(raw) as { type?: unknown } | null;\n return parsed !== null && typeof parsed === \"object\" && parsed.type === \"client_register\";\n } catch {\n return false;\n }\n}\n\n// 处理 client_register 消息:三种状态 restored / proxy_offline / new。\n// relay 不缓存输出;恢复由 proxy 重新推送 session_list/agent_status/snapshot 等状态。\nfunction handleClientRegister(\n registration: ClientRegisterInfo,\n clientWs: ClientSocket,\n registry: RelayRegistry,\n logger: Logger,\n): void {\n const { clientId } = registration;\n clientWs.clientId = clientId;\n registry.updateConnectedClientMetadata(clientWs, {\n clientId,\n ...(registration.userAgent !== undefined ? { userAgent: registration.userAgent } : {}),\n ...(registration.platform !== undefined ? { platform: registration.platform } : {}),\n ...(registration.maxTouchPoints !== undefined\n ? { maxTouchPoints: registration.maxTouchPoints }\n : {}),\n browserName: registration.browserName,\n osName: registration.osName,\n deviceKind: registration.deviceKind,\n });\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\nfunction handleRelayClientListRequest(\n clientWs: ClientSocket,\n registry: RelayRegistry,\n requestId: string | undefined,\n): void {\n clientWs.send(\n JSON.stringify({\n type: \"relay_client_list_response\",\n requestId,\n clients: registry.getConnectedClientDetails(clientWs.clientId),\n }),\n );\n}\n\nfunction handleRelayClientKick(\n clientWs: ClientSocket,\n registry: RelayRegistry,\n logger: Logger,\n requestId: string,\n targetClientId: string,\n): void {\n if (targetClientId === clientWs.clientId) {\n clientWs.send(\n JSON.stringify({\n type: \"relay_client_kick_response\",\n requestId,\n clientId: targetClientId,\n success: false,\n errorCode: ControlErrorCode.UNKNOWN,\n error: \"不能断开当前客户端\",\n }),\n );\n return;\n }\n\n const targets = registry.getConnectedClientSockets(targetClientId);\n if (targets.length === 0) {\n clientWs.send(\n JSON.stringify({\n type: \"relay_client_kick_response\",\n requestId,\n clientId: targetClientId,\n success: false,\n errorCode: ControlErrorCode.UNKNOWN,\n error: \"客户端不在线\",\n }),\n );\n return;\n }\n\n const kickedMessage = JSON.stringify({\n type: \"relay_client_kicked\",\n reason: \"由客户端管理断开\",\n });\n for (const target of targets) {\n try {\n target.send(kickedMessage);\n target.close(RelayCloseCode.CLIENT_KICKED, \"client kicked\");\n } catch (err) {\n logger.warn({ err, clientId: targetClientId }, \"Failed to close kicked client\");\n target.terminate();\n } finally {\n registry.removeClientWs(target);\n registry.unbindClientById(targetClientId);\n }\n }\n\n clientWs.send(\n JSON.stringify({\n type: \"relay_client_kick_response\",\n requestId,\n clientId: targetClientId,\n success: true,\n }),\n );\n logger.info(\n { byClientId: clientWs.clientId, targetClientId, targetCount: targets.length },\n \"Relay client kicked\",\n );\n}\n\nfunction rejectNotBound(ws: ClientSocket): void {\n ws.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}\n\nfunction rejectNotRegistered(ws: ClientSocket, requestId: string | undefined): void {\n ws.send(\n JSON.stringify({\n type: \"relay_error\",\n requestId,\n code: RelayErrorCode.NOT_REGISTERED,\n message: \"Client must register before selecting a proxy\",\n }),\n );\n}\n\nfunction closeRejectedClientProtocol(ws: ClientSocket): void {\n ws.close(RelayCloseCode.CLIENT_PROTOCOL_REJECTED, \"client protocol rejected\");\n}\n\nfunction rejectProxySelect(ws: ClientSocket, requestId: string | undefined, proxyId: string): void {\n ws.send(\n JSON.stringify({\n type: \"proxy_select_response\",\n requestId,\n success: false,\n errorCode: ControlErrorCode.PROXY_OFFLINE,\n error: `Proxy not online: ${proxyId}`,\n }),\n );\n}\n\nfunction sendRelayProxyProbeFailure(\n ws: ClientSocket,\n requestId: string,\n error: string,\n chaos?: RelayChaos,\n): void {\n const response = serializeControl({\n type: \"latency_relay_proxy_response\",\n requestId,\n success: false,\n error,\n });\n if (chaos) {\n chaos.send(ws, response, {\n direction: \"proxy_to_client\",\n type: \"latency_relay_proxy_response\",\n });\n return;\n }\n ws.send(response);\n}\n\nfunction sendRemoteFileUrlFailure(\n ws: ClientSocket,\n requestId: string,\n sessionId: string,\n error: string,\n errorCode: ControlErrorCodeType = ControlErrorCode.UNKNOWN,\n): void {\n ws.send(\n serializeControl({\n type: \"remote_file_url_response\",\n requestId,\n sessionId,\n success: false,\n error,\n errorCode,\n }),\n );\n}\n\nfunction sendRemoteFileUploadUrlFailure(\n ws: ClientSocket,\n requestId: string,\n sessionId: string,\n error: string,\n errorCode: ControlErrorCodeType = ControlErrorCode.UNKNOWN,\n): void {\n ws.send(\n serializeControl({\n type: \"remote_file_upload_url_response\",\n requestId,\n sessionId,\n success: false,\n error,\n errorCode,\n }),\n );\n}\n\n// 处理远程客户端 WebSocket 连接生命周期\nexport function handleClientConnection(\n ws: WebSocket,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n voiceConfigStore?: VoiceConfigStore,\n voiceProviders?: VoiceProviderRegistry,\n remoteFileBridge?: RemoteFileBridge,\n connectionInfo: ClientConnectionInfo = {},\n): void {\n const clientWs = ws as ClientSocket;\n clientWs.isAlive = true;\n registry.addClientWs(clientWs, connectionInfo);\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 if (data.length > RELAY_JSON_MESSAGE_MAX_BYTES) {\n logger.warn(\n { size: data.length, clientId: clientWs.clientId },\n \"JSON message rejected: exceeds max size\",\n );\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, clientWs, registry, logger);\n return;\n }\n\n if (msg.type === \"relay_client_list_request\") {\n handleRelayClientListRequest(clientWs, registry, msg.requestId);\n return;\n }\n\n if (msg.type === \"relay_client_kick\") {\n handleRelayClientKick(clientWs, registry, logger, msg.requestId, msg.clientId);\n return;\n }\n\n if (msg.type === \"remote_file_url_request\") {\n if (!remoteFileBridge) {\n sendRemoteFileUrlFailure(clientWs, msg.requestId, msg.sessionId, \"文件流服务不可用\");\n return;\n }\n if (!clientWs.clientId) {\n sendRemoteFileUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n \"客户端未注册\",\n ControlErrorCode.UNKNOWN,\n );\n return;\n }\n const targetProxyId = clientWs.boundProxyId;\n if (!targetProxyId || !registry.isProxyOnline(targetProxyId)) {\n sendRemoteFileUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n \"当前未连接开发机\",\n ControlErrorCode.PROXY_OFFLINE,\n );\n return;\n }\n const ownerProxyId = registry.getProxyForSession(msg.sessionId);\n if (ownerProxyId && ownerProxyId !== targetProxyId) {\n sendRemoteFileUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n \"会话不属于当前开发机\",\n ControlErrorCode.SESSION_NOT_FOUND,\n );\n return;\n }\n void remoteFileBridge\n .createUrl({\n clientId: clientWs.clientId,\n proxyId: targetProxyId,\n sessionId: msg.sessionId,\n path: msg.path,\n disposition: msg.disposition,\n })\n .then((result) => {\n if (clientWs.readyState !== WebSocket.OPEN) return;\n if (!result.success) {\n sendRemoteFileUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n result.error,\n result.errorCode,\n );\n return;\n }\n clientWs.send(\n serializeControl({\n type: \"remote_file_url_response\",\n requestId: msg.requestId,\n sessionId: msg.sessionId,\n path: result.path,\n success: true,\n url: result.url,\n expiresAt: result.expiresAt,\n }),\n );\n })\n .catch((err: unknown) => {\n if (clientWs.readyState !== WebSocket.OPEN) return;\n sendRemoteFileUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n err instanceof Error ? err.message : String(err),\n );\n });\n return;\n }\n\n if (msg.type === \"remote_file_upload_url_request\") {\n if (!remoteFileBridge) {\n sendRemoteFileUploadUrlFailure(clientWs, msg.requestId, msg.sessionId, \"上传服务不可用\");\n return;\n }\n if (!clientWs.clientId) {\n sendRemoteFileUploadUrlFailure(clientWs, msg.requestId, msg.sessionId, \"客户端未注册\");\n return;\n }\n const targetProxyId = clientWs.boundProxyId;\n if (!targetProxyId || !registry.isProxyOnline(targetProxyId)) {\n sendRemoteFileUploadUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n \"当前未连接开发机\",\n ControlErrorCode.PROXY_OFFLINE,\n );\n return;\n }\n const ownerProxyId = registry.getProxyForSession(msg.sessionId);\n if (ownerProxyId && ownerProxyId !== targetProxyId) {\n sendRemoteFileUploadUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n \"会话不属于当前开发机\",\n ControlErrorCode.SESSION_NOT_FOUND,\n );\n return;\n }\n const { uploadUrl, expiresAt } = remoteFileBridge.createUploadUrl({\n clientId: clientWs.clientId,\n proxyId: targetProxyId,\n sessionId: msg.sessionId,\n kind: msg.kind,\n fileName: msg.fileName,\n mimeType: msg.mimeType,\n size: msg.size,\n });\n clientWs.send(\n serializeControl({\n type: \"remote_file_upload_url_response\",\n requestId: msg.requestId,\n sessionId: msg.sessionId,\n success: true,\n uploadUrl,\n expiresAt,\n }),\n );\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 if (msg.type === \"latency_web_relay_ping\") {\n const response = serializeControl({\n type: \"latency_web_relay_pong\",\n requestId: msg.requestId,\n relayNow: Date.now(),\n });\n if (chaos) {\n chaos.send(clientWs, response, {\n direction: \"proxy_to_client\",\n type: \"latency_web_relay_pong\",\n });\n } else {\n clientWs.send(response);\n }\n return;\n }\n\n if (msg.type === \"latency_relay_proxy_request\") {\n const targetProxyId = clientWs.boundProxyId;\n if (!targetProxyId) {\n sendRelayProxyProbeFailure(clientWs, msg.requestId, \"当前未连接开发机\", chaos);\n return;\n }\n const proxyWs = registry.getProxy(targetProxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) {\n sendRelayProxyProbeFailure(\n clientWs,\n msg.requestId,\n `开发机 ${targetProxyId} 不在线`,\n chaos,\n );\n return;\n }\n startRelayProxyLatencyProbe({\n requestId: msg.requestId,\n proxyId: targetProxyId,\n proxyWs,\n clientWs,\n logger,\n chaos,\n });\n return;\n }\n\n if (\n voiceConfigStore &&\n handleVoiceConfigControl(msg, clientWs, voiceConfigStore, logger, voiceProviders)\n ) {\n return;\n }\n\n // client → proxy 透传:relay 不处理内容,直接转发给绑定的 proxy。\n // 路由 key 永远是 clientWs.boundProxyId, 不能被消息字段里 client 自填的 proxyId 覆盖\n // (那条路径让绑到 p1 的 client 通过 dir_list_request{proxyId:\"p2\"} 读到别的 proxy 的目录)。\n if (isClientToProxyRelayControlType(msg.type)) {\n const targetProxyId = clientWs.boundProxyId;\n if (!targetProxyId) {\n rejectNotBound(clientWs);\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 (!clientWs.clientId) {\n rejectNotRegistered(clientWs, msg.requestId);\n closeRejectedClientProtocol(clientWs);\n return;\n }\n if (!registry.isProxyOnline(msg.proxyId)) {\n rejectProxySelect(clientWs, msg.requestId, msg.proxyId);\n return;\n }\n const bound = registry.bindClientById(clientWs.clientId, msg.proxyId, clientWs);\n if (!bound) {\n rejectProxySelect(clientWs, msg.requestId, msg.proxyId);\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 rejectNotBound(clientWs);\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 if (isMalformedClientRegister(raw)) {\n closeRejectedClientProtocol(clientWs);\n }\n });\n\n clientWs.on(\"close\", (code: number, reason: Buffer) => {\n registry.removeClientWs(clientWs);\n // 清掉 binding.ws 引用:保留绑定关系(重连时还能恢复 proxyId 关联),但释放对已关闭 ws 对象的强引用,\n // 避免高频重连下 clientBindings Map 长期持有死 ws 对象阻止 GC,同时让 countClients 数字不再虚高。\n if (clientWs.clientId) {\n registry.unbindClientSocket(clientWs.clientId, clientWs);\n }\n logger.info(\n {\n clientId: clientWs.clientId,\n boundProxyId: clientWs.boundProxyId,\n code,\n reason: reason.toString() || undefined,\n },\n \"Client disconnected\",\n );\n });\n\n clientWs.on(\"error\", (err) => {\n logger.error({ err }, \"Client WebSocket error\");\n });\n}\n","import { EventEmitter } from \"node:events\";\nimport { nanoid } from \"nanoid\";\nimport { WebSocket } from \"ws\";\nimport { bailianRealtimeUrl, type BailianRegion } from \"./bailian-endpoints.js\";\n\ntype SocketOptions = { headers?: Record<string, string> };\ntype ProviderSocket = EventEmitter & {\n readyState: number;\n send: (data: string | Buffer) => void;\n close: (code?: number, reason?: string) => void;\n};\n\ntype BailianAsrSocketFactory = (url: string, options: SocketOptions) => ProviderSocket;\n\nexport interface BailianAsrConfig {\n apiKey: string;\n region: BailianRegion;\n model: string;\n sampleRate: number;\n language: string;\n}\n\nexport interface BailianAsrClient {\n on(event: \"ready\", handler: () => void): this;\n on(event: \"partial\" | \"final\", handler: (text: string) => void): this;\n on(event: \"error\", handler: (error: Error) => void): this;\n on(event: \"closed\", handler: (code?: number, reason?: string) => void): this;\n sendPcm(chunk: Buffer): void;\n stop(): void;\n close(): void;\n}\n\ninterface BailianAsrClientOptions {\n socketFactory?: BailianAsrSocketFactory;\n eventIdFactory?: () => string;\n}\n\nconst OPEN = 1;\nconst END_OF_SPEECH_SILENCE_MS = 1200;\n// Voice Pilot already uses an aggressive client-side VAD. Keep the provider-side\n// gate less sensitive so sustained household noise is not transcribed as speech.\nconst NOISE_RESISTANT_SERVER_VAD_THRESHOLD = 0.8;\n\nfunction defaultSocketFactory(url: string, options: SocketOptions): ProviderSocket {\n return new WebSocket(url, options) as ProviderSocket;\n}\n\nfunction extractRealtimePreview(payload: unknown): string | null {\n if (!payload || typeof payload !== \"object\") return null;\n const record = payload as Record<string, unknown>;\n if (typeof record.text === \"string\" || typeof record.stash === \"string\") {\n const text = typeof record.text === \"string\" ? record.text : \"\";\n const stash = typeof record.stash === \"string\" ? record.stash : \"\";\n const preview = `${text}${stash}`;\n return preview.length > 0 ? preview : null;\n }\n const candidates = [\n record.text,\n record.transcript,\n record.delta,\n record.output &&\n typeof record.output === \"object\" &&\n (record.output as Record<string, unknown>).text,\n ];\n for (const candidate of candidates) {\n if (typeof candidate === \"string\" && candidate.length > 0) return candidate;\n }\n return null;\n}\n\nfunction extractFinalText(payload: unknown): string | null {\n if (!payload || typeof payload !== \"object\") return null;\n const record = payload as Record<string, unknown>;\n const candidates = [\n record.transcript,\n record.text,\n record.output &&\n typeof record.output === \"object\" &&\n (record.output as Record<string, unknown>).text,\n ];\n for (const candidate of candidates) {\n if (typeof candidate === \"string\" && candidate.length > 0) return candidate;\n }\n return null;\n}\n\nfunction extractError(payload: unknown): Error {\n if (!payload || typeof payload !== \"object\") return new Error(\"Bailian ASR error\");\n const record = payload as Record<string, unknown>;\n const nested = record.error && typeof record.error === \"object\" ? record.error : null;\n let message = \"Bailian ASR error\";\n if (nested && typeof (nested as Record<string, unknown>).message === \"string\") {\n message = (nested as Record<string, string>).message;\n } else if (typeof record.message === \"string\") {\n message = record.message;\n }\n return new Error(message);\n}\n\nclass BailianAsrClientImpl extends EventEmitter implements BailianAsrClient {\n private socket: ProviderSocket;\n private isOpen = false;\n private isReady = false;\n private hasFinalTranscript = false;\n private pending: string[] = [];\n\n constructor(\n private readonly config: BailianAsrConfig,\n socketFactory: BailianAsrSocketFactory,\n private readonly eventIdFactory: () => string,\n ) {\n super();\n this.socket = socketFactory(bailianRealtimeUrl(config.region, config.model), {\n headers: {\n Authorization: `bearer ${config.apiKey}`,\n \"OpenAI-Beta\": \"realtime=v1\",\n },\n });\n this.socket.on(\"open\", () => this.handleOpen());\n this.socket.on(\"message\", (data: unknown) => this.handleMessage(data));\n this.socket.on(\"error\", (err: unknown) =>\n this.emit(\"error\", err instanceof Error ? err : new Error(String(err))),\n );\n this.socket.on(\"close\", (code?: number, reason?: Buffer) =>\n this.emit(\"closed\", code, reason?.toString(\"utf8\")),\n );\n }\n\n sendPcm(chunk: Buffer): void {\n this.sendWhenReady({\n event_id: this.eventIdFactory(),\n type: \"input_audio_buffer.append\",\n audio: chunk.toString(\"base64\"),\n });\n }\n\n stop(): void {\n this.sendWhenReady({\n event_id: this.eventIdFactory(),\n type: \"session.finish\",\n });\n }\n\n close(): void {\n this.socket.close();\n }\n\n private handleOpen(): void {\n this.isOpen = true;\n this.sendNow({\n event_id: this.eventIdFactory(),\n type: \"session.update\",\n session: {\n modalities: [\"text\"],\n input_audio_format: \"pcm\",\n sample_rate: this.config.sampleRate,\n input_audio_transcription: {\n language: this.config.language,\n },\n turn_detection: {\n type: \"server_vad\",\n threshold: NOISE_RESISTANT_SERVER_VAD_THRESHOLD,\n silence_duration_ms: END_OF_SPEECH_SILENCE_MS,\n },\n },\n });\n }\n\n private handleMessage(data: unknown): void {\n const text =\n typeof data === \"string\" ? data : Buffer.isBuffer(data) ? data.toString(\"utf8\") : \"\";\n if (!text) return;\n let payload: unknown;\n try {\n payload = JSON.parse(text);\n } catch {\n return;\n }\n const record = payload as { type?: unknown };\n const type = typeof record.type === \"string\" ? record.type : \"\";\n if (type.includes(\"error\")) {\n this.emit(\"error\", extractError(payload));\n return;\n }\n if (type === \"conversation.item.input_audio_transcription.failed\") {\n this.emit(\"error\", extractError(payload));\n return;\n }\n if (type === \"session.updated\") {\n this.isReady = true;\n this.emit(\"ready\");\n this.flushPending();\n return;\n }\n if (type === \"conversation.item.input_audio_transcription.text\") {\n const preview = extractRealtimePreview(payload);\n if (preview) this.emit(\"partial\", preview);\n return;\n }\n if (type === \"conversation.item.input_audio_transcription.completed\") {\n const transcript = extractFinalText(payload);\n if (transcript) {\n this.hasFinalTranscript = true;\n this.emit(\"final\", transcript);\n }\n return;\n }\n if (type === \"session.finished\") {\n const transcript = extractFinalText(payload);\n if (transcript && !this.hasFinalTranscript) {\n this.hasFinalTranscript = true;\n this.emit(\"final\", transcript);\n }\n this.socket.close(1000, \"session-finished\");\n }\n }\n\n private sendWhenReady(payload: unknown): void {\n const message = JSON.stringify(payload);\n if ((this.isOpen || this.socket.readyState === OPEN) && this.isReady) {\n this.socket.send(message);\n return;\n }\n this.pending.push(message);\n }\n\n private sendNow(payload: unknown): void {\n this.socket.send(JSON.stringify(payload));\n }\n\n private flushPending(): void {\n for (const message of this.pending) {\n this.socket.send(message);\n }\n this.pending = [];\n }\n}\n\nexport function createBailianAsrClient(\n config: BailianAsrConfig,\n options: BailianAsrClientOptions = {},\n): BailianAsrClient {\n return new BailianAsrClientImpl(\n config,\n options.socketFactory ?? defaultSocketFactory,\n options.eventIdFactory ?? (() => `event_${nanoid()}`),\n );\n}\n","export type BailianRegion = \"cn\" | \"intl\";\n\nconst BAILIAN_HOSTS: Record<BailianRegion, string> = {\n cn: \"wss://dashscope.aliyuncs.com\",\n intl: \"wss://dashscope-intl.aliyuncs.com\",\n};\n\nexport function bailianRealtimeUrl(region: BailianRegion, model?: string): string {\n const url = `${BAILIAN_HOSTS[region]}/api-ws/v1/realtime`;\n return model ? `${url}?model=${encodeURIComponent(model)}` : url;\n}\n\nexport function bailianInferenceUrl(region: BailianRegion): string {\n return `${BAILIAN_HOSTS[region]}/api-ws/v1/inference`;\n}\n","import { EventEmitter } from \"node:events\";\nimport { nanoid } from \"nanoid\";\nimport { WebSocket } from \"ws\";\nimport { bailianInferenceUrl, type BailianRegion } from \"./bailian-endpoints.js\";\n\ntype SocketOptions = { headers?: Record<string, string> };\ntype ProviderSocket = EventEmitter & {\n readyState: number;\n send: (data: string | Buffer) => void;\n close: (code?: number, reason?: string) => void;\n};\n\ntype BailianTtsSocketFactory = (url: string, options: SocketOptions) => ProviderSocket;\n\nexport interface BailianTtsConfig {\n apiKey: string;\n region: BailianRegion;\n model: string;\n voice: string;\n sampleRate: number;\n}\n\nexport interface BailianTtsClient {\n on(event: \"started\" | \"finished\", handler: () => void): this;\n on(event: \"audio\", handler: (chunk: Buffer) => void): this;\n on(event: \"error\", handler: (error: Error) => void): this;\n on(event: \"closed\", handler: (code?: number, reason?: string) => void): this;\n speak(text: string): void;\n close(): void;\n}\n\ninterface BailianTtsClientOptions {\n socketFactory?: BailianTtsSocketFactory;\n taskIdFactory?: () => string;\n}\n\nconst OPEN = 1;\n\nfunction defaultSocketFactory(url: string, options: SocketOptions): ProviderSocket {\n return new WebSocket(url, options) as ProviderSocket;\n}\n\nfunction errorFromPayload(payload: unknown): Error {\n if (!payload || typeof payload !== \"object\") return new Error(\"Bailian TTS error\");\n const record = payload as Record<string, unknown>;\n const header = record.header && typeof record.header === \"object\" ? record.header : null;\n let message = \"Bailian TTS error\";\n if (header && typeof (header as Record<string, unknown>).error_message === \"string\") {\n message = (header as Record<string, string>).error_message;\n } else if (typeof record.message === \"string\") {\n message = record.message;\n }\n return new Error(message);\n}\n\nfunction eventFromPayload(payload: unknown): string {\n if (!payload || typeof payload !== \"object\") return \"\";\n const record = payload as Record<string, unknown>;\n const header = record.header && typeof record.header === \"object\" ? record.header : null;\n const event = header ? (header as Record<string, unknown>).event : undefined;\n return typeof event === \"string\" ? event : \"\";\n}\n\nclass BailianTtsClientImpl extends EventEmitter implements BailianTtsClient {\n private socket: ProviderSocket;\n private isOpen = false;\n private current: { taskId: string; text: string } | null = null;\n\n constructor(\n private readonly config: BailianTtsConfig,\n socketFactory: BailianTtsSocketFactory,\n private readonly taskIdFactory: () => string,\n ) {\n super();\n this.socket = socketFactory(bailianInferenceUrl(config.region), {\n headers: { Authorization: `bearer ${config.apiKey}` },\n });\n this.socket.on(\"open\", () => this.handleOpen());\n this.socket.on(\"message\", (data: unknown, isBinary?: boolean) =>\n this.handleMessage(data, isBinary),\n );\n this.socket.on(\"error\", (err: unknown) =>\n this.emit(\"error\", err instanceof Error ? err : new Error(String(err))),\n );\n this.socket.on(\"close\", (code?: number, reason?: Buffer) =>\n this.emit(\"closed\", code, reason?.toString(\"utf8\")),\n );\n }\n\n speak(text: string): void {\n if (this.current) {\n throw new Error(\"Bailian TTS is already speaking\");\n }\n this.current = { taskId: this.taskIdFactory(), text };\n if (this.isOpen || this.socket.readyState === OPEN) {\n this.sendRunTask();\n }\n }\n\n close(): void {\n this.socket.close();\n }\n\n private handleOpen(): void {\n this.isOpen = true;\n if (this.current) this.sendRunTask();\n }\n\n private handleMessage(data: unknown, isBinary = false): void {\n const text = this.tryDecodeText(data, isBinary);\n if (!text) {\n const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data as ArrayBuffer);\n this.emit(\"audio\", chunk);\n return;\n }\n let payload: unknown;\n try {\n payload = JSON.parse(text);\n } catch {\n const chunk = Buffer.isBuffer(data) ? data : Buffer.from(text);\n this.emit(\"audio\", chunk);\n return;\n }\n\n const event = eventFromPayload(payload);\n if (event === \"task-started\") {\n this.emit(\"started\");\n this.sendTextAndFinish();\n return;\n }\n if (event === \"task-finished\") {\n this.emit(\"finished\");\n this.current = null;\n return;\n }\n if (event === \"task-failed\" || event === \"task-error\") {\n this.emit(\"error\", errorFromPayload(payload));\n this.current = null;\n }\n }\n\n private tryDecodeText(data: unknown, isBinary: boolean): string | null {\n if (typeof data === \"string\") return data;\n if (isBinary) return null;\n if (Buffer.isBuffer(data)) return data.toString(\"utf8\");\n return null;\n }\n\n private sendRunTask(): void {\n if (!this.current) return;\n this.socket.send(\n JSON.stringify({\n header: {\n action: \"run-task\",\n task_id: this.current.taskId,\n streaming: \"duplex\",\n },\n payload: {\n task_group: \"audio\",\n task: \"tts\",\n function: \"SpeechSynthesizer\",\n model: this.config.model,\n input: {},\n parameters: {\n text_type: \"PlainText\",\n voice: this.config.voice,\n format: \"pcm\",\n sample_rate: this.config.sampleRate,\n },\n },\n }),\n );\n }\n\n private sendTextAndFinish(): void {\n if (!this.current) return;\n this.socket.send(\n JSON.stringify({\n header: { action: \"continue-task\", task_id: this.current.taskId },\n payload: { input: { text: this.current.text } },\n }),\n );\n this.socket.send(\n JSON.stringify({\n header: { action: \"finish-task\", task_id: this.current.taskId },\n payload: { input: {} },\n }),\n );\n }\n}\n\nexport function createBailianTtsClient(\n config: BailianTtsConfig,\n options: BailianTtsClientOptions = {},\n): BailianTtsClient {\n return new BailianTtsClientImpl(\n config,\n options.socketFactory ?? defaultSocketFactory,\n options.taskIdFactory ?? (() => nanoid()),\n );\n}\n","import waveResampler from \"wave-resampler\";\n\nconst RESAMPLE_DETAILS = {\n method: \"sinc\",\n LPF: true,\n LPFType: \"FIR\",\n} as const;\n\nexport function resampleMonoPcmS16le(\n audio: Buffer,\n inputSampleRate: number,\n outputSampleRate: number,\n): Buffer {\n if (audio.byteLength % 2 !== 0) {\n throw new Error(\"PCM 音频数据长度无效\");\n }\n if (!validSampleRate(inputSampleRate) || !validSampleRate(outputSampleRate)) {\n throw new Error(\"PCM 音频采样率无效\");\n }\n if (inputSampleRate === outputSampleRate) return Buffer.from(audio);\n\n const input = new Int16Array(audio.byteLength / 2);\n for (let index = 0; index < input.length; index += 1) {\n input[index] = audio.readInt16LE(index * 2);\n }\n\n const samples = waveResampler.resample(\n input,\n inputSampleRate,\n outputSampleRate,\n RESAMPLE_DETAILS,\n );\n const output = Buffer.alloc(samples.length * 2);\n for (let index = 0; index < samples.length; index += 1) {\n const sample = samples[index];\n if (typeof sample !== \"number\" || !Number.isFinite(sample)) {\n throw new Error(\"PCM 音频重采样失败\");\n }\n output.writeInt16LE(Math.max(-32768, Math.min(32767, Math.round(sample))), index * 2);\n }\n return output;\n}\n\nfunction validSampleRate(sampleRate: number): boolean {\n return Number.isSafeInteger(sampleRate) && sampleRate > 0;\n}\n","import type { VoiceConfigUpdate } from \"@dev-anywhere/shared\";\nimport {\n createBailianAsrClient,\n type BailianAsrClient,\n type BailianAsrConfig,\n} from \"./bailian-asr.js\";\nimport {\n createBailianTtsClient,\n type BailianTtsClient,\n type BailianTtsConfig,\n} from \"./bailian-tts.js\";\nimport type { StoredVoiceConfig } from \"./config-store.js\";\nimport { resampleMonoPcmS16le } from \"./pcm-resample.js\";\n\nexport interface VoiceConfigTestResult {\n audio?: Buffer;\n sampleRate?: number;\n transcript?: string;\n}\n\nexport interface VoiceConfigTester {\n test: (config: StoredVoiceConfig) => Promise<VoiceConfigTestResult>;\n}\n\ninterface BailianVoiceConfigTesterOptions {\n ttsClientFactory?: (config: BailianTtsConfig) => BailianTtsClient;\n asrClientFactory?: (config: BailianAsrConfig) => BailianAsrClient;\n sampleText?: string;\n timeoutMs?: number;\n}\n\nconst TTS_TEST_SAMPLE_RATE = 24000;\nconst ASR_TEST_SAMPLE_RATE = 16000;\nconst ASR_TEST_CHUNK_BYTES = 3200;\nconst ASR_TEST_CHUNK_INTERVAL_MS = 100;\n\nexport function mergeVoiceConfigForTest(\n current: StoredVoiceConfig,\n update?: VoiceConfigUpdate,\n): StoredVoiceConfig {\n return {\n ...current,\n provider: \"aliyun-bailian\",\n ...(update?.clearApiKey ? { apiKey: undefined } : {}),\n ...(update?.apiKey ? { apiKey: update.apiKey } : {}),\n ...(update?.region ? { region: update.region } : {}),\n ...(update?.asrModel ? { asrModel: update.asrModel } : {}),\n ...(update?.ttsModel ? { ttsModel: update.ttsModel } : {}),\n ...(update?.ttsVoice ? { ttsVoice: update.ttsVoice } : {}),\n ...(update?.turnIdleSeconds ? { turnIdleSeconds: update.turnIdleSeconds } : {}),\n };\n}\n\nexport function createBailianVoiceConfigTester(\n options: BailianVoiceConfigTesterOptions = {},\n): VoiceConfigTester {\n const ttsClientFactory = options.ttsClientFactory ?? createBailianTtsClient;\n const asrClientFactory = options.asrClientFactory ?? createBailianAsrClient;\n const sampleText = options.sampleText ?? \"语音助手测试\";\n const timeoutMs = options.timeoutMs ?? 8000;\n\n return {\n async test(config) {\n if (!config.apiKey) {\n return Promise.reject(new Error(\"请先填写阿里云百炼 API Key\"));\n }\n const audio = await synthesizeTestAudio({\n config,\n sampleText,\n timeoutMs,\n clientFactory: ttsClientFactory,\n });\n const recognitionAudio = resampleMonoPcmS16le(\n audio,\n TTS_TEST_SAMPLE_RATE,\n ASR_TEST_SAMPLE_RATE,\n );\n const transcript = await recognizeTestAudio({\n config,\n audio: recognitionAudio,\n sampleText,\n timeoutMs,\n clientFactory: asrClientFactory,\n });\n return { audio, sampleRate: TTS_TEST_SAMPLE_RATE, transcript };\n },\n };\n}\n\nfunction synthesizeTestAudio(options: {\n config: StoredVoiceConfig;\n sampleText: string;\n timeoutMs: number;\n clientFactory: (config: BailianTtsConfig) => BailianTtsClient;\n}): Promise<Buffer> {\n const { config, sampleText, timeoutMs, clientFactory } = options;\n const client = clientFactory({\n apiKey: config.apiKey!,\n region: config.region,\n model: config.ttsModel,\n voice: config.ttsVoice,\n sampleRate: TTS_TEST_SAMPLE_RATE,\n });\n\n return new Promise<Buffer>((resolve, reject) => {\n let settled = false;\n const chunks: Buffer[] = [];\n const timer = setTimeout(() => {\n settle(new Error(\"TTS 测试超时\"));\n }, timeoutMs);\n\n function settle(error?: Error, audio?: Buffer): void {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n client.close();\n if (error) reject(error);\n else resolve(audio ?? Buffer.alloc(0));\n }\n\n client.on(\"audio\", (chunk) => {\n if (chunk.length > 0) chunks.push(chunk);\n });\n client.on(\"finished\", () => {\n if (chunks.length === 0) {\n settle(new Error(\"TTS 测试没有返回音频\"));\n return;\n }\n settle(undefined, Buffer.concat(chunks));\n });\n client.on(\"error\", (error) => settle(error));\n client.on(\"closed\", () => {\n if (!settled) settle(new Error(\"TTS 测试连接已关闭\"));\n });\n\n try {\n client.speak(sampleText);\n } catch (err) {\n settle(err instanceof Error ? err : new Error(\"TTS 测试启动失败\"));\n }\n });\n}\n\nfunction recognizeTestAudio(options: {\n config: StoredVoiceConfig;\n audio: Buffer;\n sampleText: string;\n timeoutMs: number;\n clientFactory: (config: BailianAsrConfig) => BailianAsrClient;\n}): Promise<string> {\n const { config, audio, sampleText, timeoutMs, clientFactory } = options;\n const client = clientFactory({\n apiKey: config.apiKey!,\n region: config.region,\n model: config.asrModel,\n sampleRate: ASR_TEST_SAMPLE_RATE,\n language: \"zh\",\n });\n\n return new Promise<string>((resolve, reject) => {\n let settled = false;\n let streamTimer: ReturnType<typeof setTimeout> | null = null;\n const timer = setTimeout(() => {\n settle(new Error(\"STT 测试超时\"));\n }, timeoutMs);\n\n function settle(error?: Error, transcript?: string): void {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (streamTimer) clearTimeout(streamTimer);\n client.close();\n if (error) reject(error);\n else resolve(transcript ?? \"\");\n }\n\n client.on(\"ready\", () => {\n let offset = 0;\n const sendNextChunk = () => {\n if (settled) return;\n const chunk = audio.subarray(offset, offset + ASR_TEST_CHUNK_BYTES);\n if (chunk.length > 0) {\n client.sendPcm(chunk);\n offset += chunk.length;\n }\n if (offset >= audio.length) {\n client.stop();\n return;\n }\n streamTimer = setTimeout(sendNextChunk, ASR_TEST_CHUNK_INTERVAL_MS);\n };\n sendNextChunk();\n });\n client.on(\"final\", (transcript) => {\n if (matchesExpectedTranscript(transcript, sampleText)) {\n settle(undefined, transcript);\n return;\n }\n settle(new Error(`STT 测试识别结果不匹配:${transcript}`));\n });\n client.on(\"error\", (error) => settle(error));\n client.on(\"closed\", () => {\n if (!settled) settle(new Error(\"STT 测试连接已关闭\"));\n });\n });\n}\n\nfunction matchesExpectedTranscript(actual: string, expected: string): boolean {\n return normalizeTranscript(actual).includes(normalizeTranscript(expected));\n}\n\nfunction normalizeTranscript(text: string): string {\n return text.replace(/[^\\p{Script=Han}a-zA-Z0-9]/gu, \"\").toLowerCase();\n}\n","import { ControlErrorCode, type RelayControlMessage } from \"@dev-anywhere/shared\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { WebSocket } from \"ws\";\nimport type { VoiceConfigStore } from \"./config-store.js\";\nimport { mergeVoiceConfigForTest } from \"./config-test.js\";\nimport type { VoiceProviderRegistry } from \"./provider.js\";\n\nexport function handleVoiceConfigControl(\n msg: RelayControlMessage,\n ws: WebSocket,\n store: VoiceConfigStore,\n logger: Logger,\n providers?: VoiceProviderRegistry,\n): boolean {\n if (msg.type === \"voice_config_request\") {\n ws.send(\n JSON.stringify({\n type: \"voice_config_response\",\n requestId: msg.requestId,\n config: store.read(),\n }),\n );\n return true;\n }\n\n if (msg.type === \"voice_config_update\") {\n try {\n const config = store.update(msg.config);\n ws.send(\n JSON.stringify({\n type: \"voice_config_update_response\",\n requestId: msg.requestId,\n success: true,\n config,\n }),\n );\n } catch (err) {\n logger.warn({ err }, \"Voice config update failed\");\n ws.send(\n JSON.stringify({\n type: \"voice_config_update_response\",\n requestId: msg.requestId,\n success: false,\n errorCode: ControlErrorCode.UNKNOWN,\n error: err instanceof Error ? err.message : \"Voice config update failed\",\n }),\n );\n }\n return true;\n }\n\n if (msg.type === \"voice_capabilities_request\") {\n if (!providers) {\n ws.send(\n JSON.stringify({\n type: \"voice_capabilities_response\",\n requestId: msg.requestId,\n errorCode: ControlErrorCode.UNKNOWN,\n error: \"Voice capabilities provider is not available\",\n }),\n );\n return true;\n }\n\n const config = { ...store.readSecret(), ...(msg.region ? { region: msg.region } : {}) };\n let provider;\n try {\n provider = providers.current(config);\n } catch (err) {\n logger.warn({ err }, \"Voice capabilities provider resolution failed\");\n ws.send(\n JSON.stringify({\n type: \"voice_capabilities_response\",\n requestId: msg.requestId,\n errorCode: ControlErrorCode.UNKNOWN,\n error: err instanceof Error ? err.message : \"Voice capabilities request failed\",\n }),\n );\n return true;\n }\n\n void provider\n .readCapabilities(config)\n .then((capabilities) => {\n ws.send(\n JSON.stringify({\n type: \"voice_capabilities_response\",\n requestId: msg.requestId,\n capabilities,\n }),\n );\n })\n .catch((err: unknown) => {\n logger.warn({ err }, \"Voice capabilities request failed\");\n ws.send(\n JSON.stringify({\n type: \"voice_capabilities_response\",\n requestId: msg.requestId,\n errorCode: ControlErrorCode.UNKNOWN,\n error: err instanceof Error ? err.message : \"Voice capabilities request failed\",\n }),\n );\n });\n return true;\n }\n\n if (msg.type === \"voice_config_test\") {\n if (!providers) {\n ws.send(\n JSON.stringify({\n type: \"voice_config_test_response\",\n requestId: msg.requestId,\n success: false,\n errorCode: ControlErrorCode.UNKNOWN,\n error: \"Voice config tester is not available\",\n }),\n );\n return true;\n }\n\n const testConfig = mergeVoiceConfigForTest(store.readSecret(), msg.config);\n let provider;\n try {\n provider = providers.current(testConfig);\n } catch (err) {\n logger.warn({ err }, \"Voice config test provider resolution failed\");\n ws.send(\n JSON.stringify({\n type: \"voice_config_test_response\",\n requestId: msg.requestId,\n success: false,\n errorCode: ControlErrorCode.UNKNOWN,\n error: err instanceof Error ? err.message : \"Voice config test failed\",\n }),\n );\n return true;\n }\n\n void provider\n .testConfig(testConfig)\n .then((result) => {\n ws.send(\n JSON.stringify({\n type: \"voice_config_test_response\",\n requestId: msg.requestId,\n success: true,\n ...(result.audio ? { audioBase64: result.audio.toString(\"base64\") } : {}),\n ...(result.sampleRate ? { audioSampleRate: result.sampleRate } : {}),\n ...(result.audio ? { audioEncoding: \"pcm_s16le\" } : {}),\n ...(result.transcript ? { transcript: result.transcript } : {}),\n }),\n );\n })\n .catch((err: unknown) => {\n logger.warn({ err }, \"Voice config test failed\");\n ws.send(\n JSON.stringify({\n type: \"voice_config_test_response\",\n requestId: msg.requestId,\n success: false,\n errorCode: ControlErrorCode.UNKNOWN,\n error: err instanceof Error ? err.message : \"Voice config test failed\",\n }),\n );\n });\n return true;\n }\n\n return false;\n}\n","import type { WebSocket, WebSocketServer } from \"ws\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\n\ninterface HeartbeatSocket extends WebSocket {\n isAlive?: boolean;\n}\n\ninterface HeartbeatOptions {\n logger?: Pick<Logger, \"warn\">;\n peerType?: string;\n describePeer?: (ws: WebSocket) => Record<string, unknown>;\n}\n\nfunction markAlive(ws: WebSocket): void {\n (ws as HeartbeatSocket).isAlive = true;\n}\n\n// 通用 WebSocket 心跳检测,检测死连接并 terminate\n// terminate 触发 close 事件,后续由各自的 close handler 处理恢复逻辑\nexport function setupHeartbeat(\n wss: WebSocketServer,\n interval = 30000,\n options: HeartbeatOptions = {},\n): NodeJS.Timeout {\n wss.on(\"connection\", (ws) => {\n markAlive(ws);\n ws.on(\"pong\", () => {\n markAlive(ws);\n });\n });\n\n return setInterval(() => {\n for (const ws of wss.clients) {\n const sock = ws as HeartbeatSocket;\n if (sock.isAlive === false) {\n options.logger?.warn(\n {\n peerType: options.peerType,\n ...(options.describePeer?.(ws) ?? {}),\n },\n \"WebSocket heartbeat timeout\",\n );\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/logger\";\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","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport {\n VoiceConfigUpdateSchema,\n VoiceProviderConfigSchema,\n type VoiceConfigUpdate,\n type VoiceProviderConfig,\n} from \"@dev-anywhere/shared\";\n\nexport interface StoredVoiceConfig {\n provider: \"aliyun-bailian\";\n apiKey?: string;\n region: \"cn\" | \"intl\";\n asrModel: string;\n ttsModel: string;\n ttsVoice: string;\n turnIdleSeconds: number;\n}\n\ninterface VoiceConfigStoreOptions {\n dataDir?: string;\n defaults?: Partial<Omit<StoredVoiceConfig, \"provider\" | \"apiKey\">>;\n}\n\nexport interface VoiceConfigStore {\n read: () => VoiceProviderConfig;\n update: (update: VoiceConfigUpdate) => VoiceProviderConfig;\n readSecret: () => StoredVoiceConfig;\n}\n\nconst DEFAULT_STORED_CONFIG: StoredVoiceConfig = {\n provider: \"aliyun-bailian\",\n region: \"cn\",\n asrModel: \"qwen3-asr-flash-realtime\",\n ttsModel: \"cosyvoice-v3-flash\",\n ttsVoice: \"longanyang\",\n turnIdleSeconds: 3,\n};\n\nfunction redacted(config: StoredVoiceConfig): VoiceProviderConfig {\n return VoiceProviderConfigSchema.parse({\n provider: config.provider,\n configured: Boolean(config.apiKey),\n region: config.region,\n asrModel: config.asrModel,\n ttsModel: config.ttsModel,\n ttsVoice: config.ttsVoice,\n turnIdleSeconds: config.turnIdleSeconds,\n });\n}\n\nfunction mergeDefaults(defaults: VoiceConfigStoreOptions[\"defaults\"]): StoredVoiceConfig {\n return {\n ...DEFAULT_STORED_CONFIG,\n ...defaults,\n };\n}\n\nfunction parseStoredConfig(raw: unknown, fallback: StoredVoiceConfig): StoredVoiceConfig {\n if (!raw || typeof raw !== \"object\") return fallback;\n const candidate = raw as Partial<StoredVoiceConfig>;\n return {\n ...fallback,\n provider: \"aliyun-bailian\",\n ...(typeof candidate.apiKey === \"string\" && candidate.apiKey.length > 0\n ? { apiKey: candidate.apiKey }\n : { apiKey: undefined }),\n ...(candidate.region === \"cn\" || candidate.region === \"intl\"\n ? { region: candidate.region }\n : {}),\n ...(typeof candidate.asrModel === \"string\" && candidate.asrModel.length > 0\n ? { asrModel: candidate.asrModel }\n : {}),\n ...(typeof candidate.ttsModel === \"string\" && candidate.ttsModel.length > 0\n ? { ttsModel: candidate.ttsModel }\n : {}),\n ...(typeof candidate.ttsVoice === \"string\" && candidate.ttsVoice.length > 0\n ? { ttsVoice: candidate.ttsVoice }\n : {}),\n ...(typeof candidate.turnIdleSeconds === \"number\" &&\n Number.isSafeInteger(candidate.turnIdleSeconds) &&\n candidate.turnIdleSeconds > 0\n ? { turnIdleSeconds: candidate.turnIdleSeconds }\n : {}),\n };\n}\n\nexport function createVoiceConfigStore(options: VoiceConfigStoreOptions = {}): VoiceConfigStore {\n const fallback = mergeDefaults(options.defaults);\n const filePath = options.dataDir ? join(options.dataDir, \"voice-config.json\") : null;\n let memoryConfig: StoredVoiceConfig = fallback;\n\n function load(): StoredVoiceConfig {\n if (!filePath) return memoryConfig;\n if (!existsSync(filePath)) return fallback;\n try {\n return parseStoredConfig(JSON.parse(readFileSync(filePath, \"utf8\")), fallback);\n } catch {\n return fallback;\n }\n }\n\n function save(config: StoredVoiceConfig): void {\n if (!filePath) {\n memoryConfig = config;\n return;\n }\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, `${JSON.stringify(config, null, 2)}\\n`, { mode: 0o600 });\n }\n\n return {\n read() {\n return redacted(load());\n },\n update(update) {\n const parsed = VoiceConfigUpdateSchema.parse(update);\n const current = load();\n const next: StoredVoiceConfig = {\n ...current,\n provider: \"aliyun-bailian\",\n ...(parsed.clearApiKey ? { apiKey: undefined } : {}),\n ...(parsed.apiKey ? { apiKey: parsed.apiKey } : {}),\n ...(parsed.region ? { region: parsed.region } : {}),\n ...(parsed.asrModel ? { asrModel: parsed.asrModel } : {}),\n ...(parsed.ttsModel ? { ttsModel: parsed.ttsModel } : {}),\n ...(parsed.ttsVoice ? { ttsVoice: parsed.ttsVoice } : {}),\n ...(parsed.turnIdleSeconds ? { turnIdleSeconds: parsed.turnIdleSeconds } : {}),\n };\n save(next);\n return redacted(next);\n },\n readSecret() {\n return load();\n },\n };\n}\n","import { WebSocket, type RawData } from \"ws\";\nimport {\n VoiceAsrClientMessageSchema,\n decodeMuLawToPcm16,\n type VoiceAsrStartMessage,\n} from \"@dev-anywhere/shared\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { BailianAsrClient, BailianAsrConfig } from \"./bailian-asr.js\";\nimport type { StoredVoiceConfig, VoiceConfigStore } from \"./config-store.js\";\nimport type { VoiceAsrProviderClient, VoiceProviderRegistry } from \"./provider.js\";\n\nexport type VoiceAsrClientFactory = (config: BailianAsrConfig) => BailianAsrClient;\n\ninterface ActiveAsrStats {\n sessionId: string;\n attemptId: string;\n sampleRate: number;\n provider: StoredVoiceConfig[\"provider\"];\n region: StoredVoiceConfig[\"region\"];\n asrModel: string;\n encoding: VoiceAsrStartMessage[\"encoding\"];\n startedAt: number;\n readyAt: number | null;\n firstPcmAt: number | null;\n encodedBytes: number;\n pcmBytes: number;\n pcmChunks: number;\n partialEvents: number;\n finalEvents: number;\n finalChars: number;\n}\n\nfunction sendJson(ws: WebSocket, payload: unknown): void {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(payload));\n }\n}\n\nfunction toBuffer(data: RawData): Buffer {\n if (Buffer.isBuffer(data)) return data;\n if (data instanceof ArrayBuffer) return Buffer.from(data);\n return Buffer.concat(data);\n}\n\nfunction parseJson(data: RawData): unknown | null {\n try {\n return JSON.parse(toBuffer(data).toString(\"utf8\"));\n } catch {\n return null;\n }\n}\n\nfunction buildStats(payload: VoiceAsrStartMessage, config: StoredVoiceConfig): ActiveAsrStats {\n return {\n sessionId: payload.sessionId,\n attemptId: payload.attemptId,\n sampleRate: payload.sampleRate,\n provider: config.provider,\n region: config.region,\n asrModel: config.asrModel,\n encoding: payload.encoding,\n startedAt: Date.now(),\n readyAt: null,\n firstPcmAt: null,\n encodedBytes: 0,\n pcmBytes: 0,\n pcmChunks: 0,\n partialEvents: 0,\n finalEvents: 0,\n finalChars: 0,\n };\n}\n\nfunction statsLogFields(stats: ActiveAsrStats): Record<string, unknown> {\n const now = Date.now();\n return {\n sessionId: stats.sessionId,\n attemptId: stats.attemptId,\n sampleRate: stats.sampleRate,\n provider: stats.provider,\n region: stats.region,\n asrModel: stats.asrModel,\n encoding: stats.encoding,\n encodedBytes: stats.encodedBytes,\n pcmBytes: stats.pcmBytes,\n pcmChunks: stats.pcmChunks,\n partialEvents: stats.partialEvents,\n finalEvents: stats.finalEvents,\n finalChars: stats.finalChars,\n durationMs: now - stats.startedAt,\n readyMs: stats.readyAt === null ? null : stats.readyAt - stats.startedAt,\n firstPcmMs: stats.firstPcmAt === null ? null : stats.firstPcmAt - stats.startedAt,\n };\n}\n\nexport function handleVoiceAsrConnection(\n ws: WebSocket,\n store: VoiceConfigStore,\n logger: Logger,\n providers: VoiceProviderRegistry,\n): void {\n let provider: VoiceAsrProviderClient | null = null;\n let activeStats: ActiveAsrStats | null = null;\n\n function isCurrent(\n candidate: VoiceAsrProviderClient,\n stats: ActiveAsrStats,\n event: string,\n ): boolean {\n if (provider === candidate && activeStats === stats) return true;\n logger.debug(\n { attemptId: stats.attemptId, activeAttemptId: activeStats?.attemptId ?? null, event },\n \"Ignored stale Voice ASR provider event\",\n );\n return false;\n }\n\n function detachProvider(reason: string): void {\n const previousProvider = provider;\n const previousStats = activeStats;\n provider = null;\n activeStats = null;\n if (previousStats) {\n logger.info({ ...statsLogFields(previousStats), reason }, \"Voice ASR attempt detached\");\n }\n previousProvider?.close();\n }\n\n function start(payload: VoiceAsrStartMessage): void {\n const config = store.readSecret();\n if (!config.apiKey) {\n sendJson(ws, {\n type: \"error\",\n attemptId: payload.attemptId,\n errorCode: \"not_configured\",\n error: \"Voice provider is not configured\",\n });\n return;\n }\n detachProvider(\"replaced\");\n const stats = buildStats(payload, config);\n let currentProvider: VoiceAsrProviderClient;\n try {\n currentProvider = providers.current(config).createAsrClient(config, {\n sampleRate: stats.sampleRate,\n language: \"zh\",\n });\n } catch (error) {\n logger.warn({ ...statsLogFields(stats), err: error }, \"Voice ASR provider creation failed\");\n sendJson(ws, {\n type: \"error\",\n attemptId: payload.attemptId,\n error: error instanceof Error ? error.message : \"ASR failed\",\n });\n return;\n }\n provider = currentProvider;\n activeStats = stats;\n logger.info(statsLogFields(stats), \"Voice ASR attempt started\");\n currentProvider.on(\"ready\", () => {\n if (!isCurrent(currentProvider, stats, \"ready\")) return;\n stats.readyAt = Date.now();\n logger.info(statsLogFields(stats), \"Voice ASR provider ready\");\n sendJson(ws, { type: \"ready\", attemptId: stats.attemptId });\n });\n currentProvider.on(\"partial\", (text) => {\n if (!isCurrent(currentProvider, stats, \"partial\")) return;\n stats.partialEvents += 1;\n sendJson(ws, { type: \"partial\", attemptId: stats.attemptId, text });\n });\n currentProvider.on(\"final\", (text) => {\n if (!isCurrent(currentProvider, stats, \"final\")) return;\n stats.finalEvents += 1;\n stats.finalChars += text.length;\n sendJson(ws, { type: \"final\", attemptId: stats.attemptId, text });\n });\n currentProvider.on(\"error\", (error) => {\n if (!isCurrent(currentProvider, stats, \"error\")) return;\n logger.warn({ ...statsLogFields(stats), err: error }, \"Voice ASR provider reported an error\");\n sendJson(ws, {\n type: \"error\",\n attemptId: stats.attemptId,\n error: error.message || \"ASR failed\",\n });\n });\n currentProvider.on(\"closed\", (code, reason) => {\n if (!isCurrent(currentProvider, stats, \"closed\")) return;\n provider = null;\n activeStats = null;\n logger.info({ ...statsLogFields(stats), code, reason }, \"Voice ASR provider closed\");\n sendJson(ws, { type: \"closed\", attemptId: stats.attemptId, code, reason });\n });\n }\n\n ws.on(\"message\", (data, isBinary) => {\n if (isBinary) {\n const encodedChunk = toBuffer(data);\n if (provider && activeStats) {\n const pcmChunk =\n activeStats.encoding === \"mulaw\"\n ? Buffer.from(decodeMuLawToPcm16(encodedChunk))\n : encodedChunk;\n if (activeStats.firstPcmAt === null) activeStats.firstPcmAt = Date.now();\n activeStats.encodedBytes += encodedChunk.byteLength;\n activeStats.pcmBytes += pcmChunk.byteLength;\n activeStats.pcmChunks += 1;\n provider.sendPcm(pcmChunk);\n sendJson(ws, {\n type: \"audio_ack\",\n attemptId: activeStats.attemptId,\n encodedBytes: activeStats.encodedBytes,\n pcmBytes: activeStats.pcmBytes,\n chunks: activeStats.pcmChunks,\n });\n }\n return;\n }\n\n const parsed = VoiceAsrClientMessageSchema.safeParse(parseJson(data));\n if (!parsed.success) {\n logger.debug({ issues: parsed.error.issues }, \"Ignored invalid Voice ASR client message\");\n return;\n }\n const payload = parsed.data;\n if (payload.type === \"start\") {\n start(payload);\n return;\n }\n if (payload.type === \"stop\") {\n if (!provider || !activeStats || activeStats.attemptId !== payload.attemptId) {\n logger.debug(\n { attemptId: payload.attemptId, activeAttemptId: activeStats?.attemptId ?? null },\n \"Ignored stale Voice ASR stop\",\n );\n return;\n }\n logger.info(statsLogFields(activeStats), \"Voice ASR stop requested\");\n provider.stop();\n }\n });\n\n ws.on(\"close\", (code, reason) => {\n if (activeStats) {\n logger.info(\n { ...statsLogFields(activeStats), code, reason: reason.toString(\"utf8\") },\n \"Voice ASR client websocket closed\",\n );\n }\n detachProvider(\"client-websocket-closed\");\n });\n ws.on(\"error\", (err) => {\n logger.warn(\n { ...(activeStats ? statsLogFields(activeStats) : {}), err },\n \"Voice ASR websocket error\",\n );\n detachProvider(\"client-websocket-error\");\n });\n}\n","import { WebSocket, type RawData } from \"ws\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { BailianTtsClient, BailianTtsConfig } from \"./bailian-tts.js\";\nimport type { StoredVoiceConfig, VoiceConfigStore } from \"./config-store.js\";\nimport type { VoiceProviderRegistry, VoiceTtsProviderClient } from \"./provider.js\";\n\nexport type VoiceTtsClientFactory = (config: BailianTtsConfig) => BailianTtsClient;\n\ninterface SpeakMessage {\n type: \"speak\";\n requestId: string;\n text: string;\n}\n\ninterface ActiveTtsStats {\n requestId: string;\n textChars: number;\n provider: StoredVoiceConfig[\"provider\"];\n region: StoredVoiceConfig[\"region\"];\n ttsModel: string;\n ttsVoice: string;\n startedAt: number;\n firstAudioAt: number | null;\n audioBytes: number;\n audioChunks: number;\n}\n\nfunction sendJson(ws: WebSocket, payload: unknown): void {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(payload));\n }\n}\n\nfunction parseJson(data: RawData): unknown | null {\n try {\n const buffer = Buffer.isBuffer(data)\n ? data\n : data instanceof ArrayBuffer\n ? Buffer.from(data)\n : Buffer.concat(data);\n return JSON.parse(buffer.toString(\"utf8\"));\n } catch {\n return null;\n }\n}\n\nfunction isSpeakMessage(payload: unknown): payload is SpeakMessage {\n if (!payload || typeof payload !== \"object\") return false;\n const record = payload as Record<string, unknown>;\n return (\n record.type === \"speak\" &&\n typeof record.requestId === \"string\" &&\n typeof record.text === \"string\" &&\n record.text.length > 0\n );\n}\n\nfunction nowMs(): number {\n return Date.now();\n}\n\nfunction buildStats(requestId: string, text: string, config: StoredVoiceConfig): ActiveTtsStats {\n return {\n requestId,\n textChars: text.length,\n provider: config.provider,\n region: config.region,\n ttsModel: config.ttsModel,\n ttsVoice: config.ttsVoice,\n startedAt: nowMs(),\n firstAudioAt: null,\n audioBytes: 0,\n audioChunks: 0,\n };\n}\n\nfunction statsLogFields(stats: ActiveTtsStats): Record<string, unknown> {\n const currentTime = nowMs();\n return {\n requestId: stats.requestId,\n textChars: stats.textChars,\n provider: stats.provider,\n region: stats.region,\n ttsModel: stats.ttsModel,\n ttsVoice: stats.ttsVoice,\n audioBytes: stats.audioBytes,\n audioChunks: stats.audioChunks,\n durationMs: currentTime - stats.startedAt,\n firstAudioMs: stats.firstAudioAt === null ? null : stats.firstAudioAt - stats.startedAt,\n };\n}\n\nfunction closeReasonText(reason: unknown): string {\n if (Buffer.isBuffer(reason)) return reason.toString(\"utf8\");\n return typeof reason === \"string\" ? reason : \"\";\n}\n\nexport function handleVoiceTtsConnection(\n ws: WebSocket,\n store: VoiceConfigStore,\n logger: Logger,\n providers: VoiceProviderRegistry,\n): void {\n let provider: VoiceTtsProviderClient | null = null;\n let providerConfig: StoredVoiceConfig | null = null;\n let activeRequestId: string | null = null;\n let activeStats: ActiveTtsStats | null = null;\n\n function ensureProvider(): { client: VoiceTtsProviderClient; config: StoredVoiceConfig } | null {\n if (provider && providerConfig) return { client: provider, config: providerConfig };\n const config = store.readSecret();\n if (!config.apiKey) {\n sendJson(ws, {\n type: \"error\",\n errorCode: \"not_configured\",\n error: \"Voice provider is not configured\",\n });\n return null;\n }\n provider = providers.current(config).createTtsClient(config, {\n sampleRate: 24000,\n });\n providerConfig = config;\n provider.on(\"started\", () => {\n if (activeStats) logger.info(statsLogFields(activeStats), \"Voice TTS started\");\n sendJson(ws, { type: \"started\", requestId: activeRequestId });\n });\n provider.on(\"audio\", (chunk) => {\n if (activeStats) {\n if (activeStats.firstAudioAt === null) activeStats.firstAudioAt = nowMs();\n activeStats.audioBytes += chunk.byteLength;\n activeStats.audioChunks += 1;\n }\n if (ws.readyState === WebSocket.OPEN) ws.send(chunk);\n });\n provider.on(\"finished\", () => {\n if (activeStats) logger.info(statsLogFields(activeStats), \"Voice TTS finished\");\n sendJson(ws, { type: \"finished\", requestId: activeRequestId });\n activeRequestId = null;\n activeStats = null;\n });\n provider.on(\"error\", (error) => {\n if (activeStats) {\n logger.warn(\n { ...statsLogFields(activeStats), err: error },\n \"Voice TTS provider reported an error\",\n );\n }\n sendJson(ws, {\n type: \"error\",\n requestId: activeRequestId,\n error: error.message || \"TTS failed\",\n });\n activeRequestId = null;\n activeStats = null;\n });\n provider.on(\"closed\", (code, reason) => {\n if (activeStats) {\n logger.warn(\n { ...statsLogFields(activeStats), code, reason },\n \"Voice TTS provider closed before finishing\",\n );\n sendJson(ws, {\n type: \"error\",\n requestId: activeRequestId,\n errorCode: \"provider_closed\",\n error: \"Voice TTS provider closed before finishing\",\n });\n activeRequestId = null;\n activeStats = null;\n } else {\n logger.info({ code, reason }, \"Voice TTS provider closed\");\n provider = null;\n providerConfig = null;\n return;\n }\n sendJson(ws, { type: \"closed\", code, reason });\n provider = null;\n providerConfig = null;\n });\n return { client: provider, config };\n }\n\n ws.on(\"message\", (data) => {\n const payload = parseJson(data);\n if (!isSpeakMessage(payload)) return;\n if (activeRequestId) {\n sendJson(ws, {\n type: \"error\",\n requestId: payload.requestId,\n errorCode: \"busy\",\n error: \"Voice TTS is already speaking\",\n });\n return;\n }\n const ensured = ensureProvider();\n if (!ensured) return;\n const { client, config } = ensured;\n activeRequestId = payload.requestId;\n activeStats = buildStats(payload.requestId, payload.text, config);\n logger.info(statsLogFields(activeStats), \"Voice TTS request received\");\n try {\n client.speak(payload.text);\n } catch (err) {\n if (activeStats) {\n logger.warn(\n { ...statsLogFields(activeStats), err },\n \"Voice TTS request failed before provider accepted it\",\n );\n }\n sendJson(ws, {\n type: \"error\",\n requestId: payload.requestId,\n error: err instanceof Error ? err.message : \"Voice TTS failed\",\n });\n activeRequestId = null;\n activeStats = null;\n }\n });\n\n ws.on(\"close\", (code?: number, reason?: Buffer) => {\n if (activeStats) {\n logger.warn(\n { ...statsLogFields(activeStats), code, reason: closeReasonText(reason) },\n \"Voice TTS client websocket closed before finishing\",\n );\n }\n const currentProvider = provider;\n provider = null;\n providerConfig = null;\n activeRequestId = null;\n activeStats = null;\n currentProvider?.close();\n });\n ws.on(\"error\", (err) => {\n logger.warn({ err }, \"Voice TTS websocket error\");\n provider?.close();\n provider = null;\n providerConfig = null;\n activeRequestId = null;\n activeStats = null;\n });\n}\n","import {\n createBundledBailianVoiceCapabilities,\n type VoiceCapabilities,\n type VoiceOption,\n} from \"@dev-anywhere/shared\";\nimport type { StoredVoiceConfig } from \"./config-store.js\";\n\ntype FetchResponse = {\n ok: boolean;\n json: () => Promise<unknown>;\n};\ntype FetchLike = (\n url: string,\n init?: {\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n },\n) => Promise<FetchResponse>;\n\nexport interface VoiceCapabilitiesProvider {\n read: (config: StoredVoiceConfig) => Promise<VoiceCapabilities>;\n}\n\ninterface BailianVoiceCapabilitiesProviderOptions {\n fetchImpl?: FetchLike;\n now?: () => number;\n}\n\nconst CUSTOMIZATION_ENDPOINTS: Record<StoredVoiceConfig[\"region\"], string> = {\n cn: \"https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization\",\n intl: \"https://dashscope-intl.aliyuncs.com/api/v1/services/audio/tts/customization\",\n};\n\nfunction modelFromCustomCosyVoiceId(voiceId: string): string | undefined {\n const match = voiceId.match(/^(cosyvoice-v3(?:\\.5)?-(?:flash|plus))-/);\n return match?.[1];\n}\n\nasync function fetchCustomVoices(\n fetchImpl: FetchLike,\n config: StoredVoiceConfig,\n): Promise<VoiceOption[]> {\n if (!config.apiKey) return [];\n const response = await fetchImpl(CUSTOMIZATION_ENDPOINTS[config.region], {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${config.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n model: \"voice-enrollment\",\n input: {\n action: \"list_voice\",\n page_size: 100,\n page_index: 0,\n },\n }),\n });\n if (!response.ok) return [];\n const payload = response.json() as Promise<{\n output?: {\n voice_list?: Array<{\n voice_id?: string;\n status?: string;\n voice_prompt?: string;\n }>;\n };\n }>;\n const voiceList = (await payload).output?.voice_list ?? [];\n return voiceList\n .filter((voice) => !voice.status || voice.status === \"OK\")\n .flatMap((voice): VoiceOption[] => {\n if (!voice.voice_id) return [];\n const model = modelFromCustomCosyVoiceId(voice.voice_id);\n return [\n {\n value: voice.voice_id,\n label: [voice.voice_id, \"自定义\", voice.voice_prompt].filter(Boolean).join(\" · \"),\n ...(model ? { model } : {}),\n source: \"custom\",\n },\n ];\n });\n}\n\nexport function createBailianVoiceCapabilitiesProvider(\n options: BailianVoiceCapabilitiesProviderOptions = {},\n): VoiceCapabilitiesProvider {\n const fetchImpl = options.fetchImpl ?? (globalThis.fetch as unknown as FetchLike);\n const now = options.now ?? Date.now;\n\n return {\n async read(config) {\n let customVoices: VoiceOption[];\n try {\n customVoices = await fetchCustomVoices(fetchImpl, config);\n } catch {\n customVoices = [];\n }\n\n const bundled = createBundledBailianVoiceCapabilities(now());\n return {\n ...bundled,\n ttsVoices: [...bundled.ttsVoices, ...customVoices],\n };\n },\n };\n}\n","import {\n createBailianAsrClient,\n type BailianAsrClient,\n type BailianAsrConfig,\n} from \"./bailian-asr.js\";\nimport {\n createBailianTtsClient,\n type BailianTtsClient,\n type BailianTtsConfig,\n} from \"./bailian-tts.js\";\nimport {\n createBailianVoiceCapabilitiesProvider,\n type VoiceCapabilitiesProvider,\n} from \"./capabilities.js\";\nimport { createBailianVoiceConfigTester, type VoiceConfigTester } from \"./config-test.js\";\nimport type { StoredVoiceConfig } from \"./config-store.js\";\nimport type { VoiceProviderAdapter } from \"./provider.js\";\n\ninterface BailianVoiceProviderOptions {\n asrClientFactory?: (config: BailianAsrConfig) => BailianAsrClient;\n ttsClientFactory?: (config: BailianTtsConfig) => BailianTtsClient;\n capabilitiesProvider?: VoiceCapabilitiesProvider;\n configTester?: VoiceConfigTester;\n}\n\nfunction requireApiKey(config: StoredVoiceConfig): string {\n if (!config.apiKey) throw new Error(\"Voice provider is not configured\");\n return config.apiKey;\n}\n\nexport function createBailianVoiceProvider(\n options: BailianVoiceProviderOptions = {},\n): VoiceProviderAdapter {\n const asrClientFactory = options.asrClientFactory ?? createBailianAsrClient;\n const ttsClientFactory = options.ttsClientFactory ?? createBailianTtsClient;\n const capabilitiesProvider =\n options.capabilitiesProvider ?? createBailianVoiceCapabilitiesProvider();\n const configTester =\n options.configTester ??\n createBailianVoiceConfigTester({\n asrClientFactory,\n ttsClientFactory,\n });\n\n return {\n id: \"aliyun-bailian\",\n createAsrClient(config, clientOptions) {\n return asrClientFactory({\n apiKey: requireApiKey(config),\n region: config.region,\n model: config.asrModel,\n sampleRate: clientOptions.sampleRate,\n language: clientOptions.language,\n });\n },\n createTtsClient(config, clientOptions) {\n return ttsClientFactory({\n apiKey: requireApiKey(config),\n region: config.region,\n model: config.ttsModel,\n voice: config.ttsVoice,\n sampleRate: clientOptions.sampleRate,\n });\n },\n readCapabilities(config) {\n return capabilitiesProvider.read(config);\n },\n testConfig(config) {\n return configTester.test(config);\n },\n };\n}\n","import type { VoiceCapabilities, VoiceProviderConfig } from \"@dev-anywhere/shared\";\nimport type { StoredVoiceConfig } from \"./config-store.js\";\nimport type { VoiceConfigTestResult } from \"./config-test.js\";\n\nexport type VoiceProviderId = VoiceProviderConfig[\"provider\"];\n\nexport interface VoiceAsrProviderClient {\n on(event: \"ready\", handler: () => void): this;\n on(event: \"partial\" | \"final\", handler: (text: string) => void): this;\n on(event: \"error\", handler: (error: Error) => void): this;\n on(event: \"closed\", handler: (code?: number, reason?: string) => void): this;\n sendPcm(chunk: Buffer): void;\n stop(): void;\n close(): void;\n}\n\nexport interface VoiceTtsProviderClient {\n on(event: \"started\" | \"finished\", handler: () => void): this;\n on(event: \"audio\", handler: (chunk: Buffer) => void): this;\n on(event: \"error\", handler: (error: Error) => void): this;\n on(event: \"closed\", handler: (code?: number, reason?: string) => void): this;\n speak(text: string): void;\n close(): void;\n}\n\nexport interface VoiceAsrProviderOptions {\n sampleRate: number;\n language: string;\n}\n\nexport interface VoiceTtsProviderOptions {\n sampleRate: number;\n}\n\nexport interface VoiceProviderAdapter {\n id: VoiceProviderId;\n createAsrClient: (\n config: StoredVoiceConfig,\n options: VoiceAsrProviderOptions,\n ) => VoiceAsrProviderClient;\n createTtsClient: (\n config: StoredVoiceConfig,\n options: VoiceTtsProviderOptions,\n ) => VoiceTtsProviderClient;\n readCapabilities: (config: StoredVoiceConfig) => Promise<VoiceCapabilities>;\n testConfig: (config: StoredVoiceConfig) => Promise<VoiceConfigTestResult>;\n}\n\nexport interface VoiceProviderRegistry {\n current: (config: StoredVoiceConfig) => VoiceProviderAdapter;\n require: (providerId: string) => VoiceProviderAdapter;\n}\n\nexport function createVoiceProviderRegistry(\n adapters: readonly VoiceProviderAdapter[],\n): VoiceProviderRegistry {\n const byId = new Map<string, VoiceProviderAdapter>();\n for (const adapter of adapters) {\n if (byId.has(adapter.id)) {\n throw new Error(`Duplicate voice provider: ${adapter.id}`);\n }\n byId.set(adapter.id, adapter);\n }\n\n function require(providerId: string): VoiceProviderAdapter {\n const adapter = byId.get(providerId);\n if (!adapter) throw new Error(`Unsupported voice provider: ${providerId}`);\n return adapter;\n }\n\n return {\n current(config) {\n return require(config.provider);\n },\n require,\n };\n}\n","import type { Request, Response } from \"express\";\nimport { nanoid } from \"nanoid\";\nimport { WebSocket } from \"ws\";\nimport {\n ControlErrorCode,\n decodeFileStreamFrame,\n encodeFileStreamFrame,\n serializeControl,\n type ControlMessage,\n} from \"@dev-anywhere/shared\";\nimport type { ControlErrorCode as ControlErrorCodeType } from \"@dev-anywhere/shared\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { RelayRegistry } from \"./registry.js\";\n\nconst REMOTE_FILE_TOKEN_TTL_MS = 24 * 60 * 60 * 1000;\nconst REMOTE_FILE_METADATA_TIMEOUT_MS = 30 * 1000;\nconst REMOTE_FILE_UPLOAD_RESPONSE_TIMEOUT_MS = 60 * 1000;\n\ntype RemoteFileDisposition = \"inline\" | \"download\";\ntype RemoteFileUploadKind = \"clipboard_image\" | \"file\";\n\ninterface RemoteFileToken {\n token: string;\n clientId: string;\n proxyId: string;\n sessionId: string;\n path: string;\n disposition: RemoteFileDisposition;\n expiresAt: number;\n}\n\ntype RemoteFileUrlResult =\n | { success: true; path: string; url: string; expiresAt: number }\n | { success: false; path: string; error: string; errorCode?: ControlErrorCodeType };\n\ninterface PendingUrlMetadata {\n requestId: string;\n clientId: string;\n proxyId: string;\n sessionId: string;\n path: string;\n disposition: RemoteFileDisposition;\n timer: NodeJS.Timeout;\n resolve: (result: RemoteFileUrlResult) => void;\n}\n\ninterface PendingHttpStream {\n streamId: string;\n token: RemoteFileToken;\n res: Response;\n metadataTimer: NodeJS.Timeout;\n headersSent: boolean;\n finished: boolean;\n}\n\ninterface RemoteFileUploadToken {\n token: string;\n clientId: string;\n proxyId: string;\n sessionId: string;\n kind: RemoteFileUploadKind;\n fileName?: string;\n mimeType: string;\n size?: number;\n expiresAt: number;\n}\n\ninterface PendingHttpUpload {\n uploadId: string;\n token: RemoteFileUploadToken;\n res: Response;\n chunkSeq: number;\n requestEnded: boolean;\n finished: boolean;\n responseTimer: NodeJS.Timeout | null;\n}\n\ninterface RemoteFileBridgeDeps {\n registry: RelayRegistry;\n logger: Logger;\n}\n\nfunction encodeContentDisposition(disposition: RemoteFileDisposition, fileName: string): string {\n const type = disposition === \"download\" ? \"attachment\" : \"inline\";\n const fallback =\n fileName\n .replace(/[^\\x20-\\x7e]/g, \"_\")\n .replace(/[\"\\\\/\\r\\n]/g, \"_\")\n .replace(/_+/g, \"_\")\n .trim() || \"download\";\n const encodedFileName = encodeURIComponent(fileName).replace(\n /['()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n return `${type}; filename=\"${fallback}\"; filename*=UTF-8''${encodedFileName}`;\n}\n\nfunction statusForErrorCode(errorCode?: string): number {\n switch (errorCode) {\n case ControlErrorCode.SESSION_NOT_FOUND:\n case ControlErrorCode.PATH_NOT_FOUND:\n return 404;\n case ControlErrorCode.INVALID_PATH:\n case ControlErrorCode.PATH_NOT_DIRECTORY:\n return 400;\n case ControlErrorCode.PATH_ACCESS_DENIED:\n return 403;\n default:\n return 500;\n }\n}\n\nexport class RemoteFileBridge {\n private readonly tokens = new Map<string, RemoteFileToken>();\n private readonly uploadTokens = new Map<string, RemoteFileUploadToken>();\n private readonly pendingUrlMetadata = new Map<string, PendingUrlMetadata>();\n private readonly pendingStreams = new Map<string, PendingHttpStream>();\n private readonly pendingUploads = new Map<string, PendingHttpUpload>();\n\n constructor(private readonly deps: RemoteFileBridgeDeps) {}\n\n createUrl(input: {\n clientId: string;\n proxyId: string;\n sessionId: string;\n path: string;\n disposition: RemoteFileDisposition;\n }): Promise<RemoteFileUrlResult> {\n this.cleanupExpiredTokens();\n const proxyWs = this.deps.registry.getProxy(input.proxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) {\n return Promise.resolve({\n success: false,\n path: input.path,\n error: \"当前未连接开发机\",\n errorCode: ControlErrorCode.PROXY_OFFLINE,\n });\n }\n\n const requestId = nanoid(21);\n return new Promise<RemoteFileUrlResult>((resolve) => {\n const timer = setTimeout(() => {\n this.pendingUrlMetadata.delete(requestId);\n resolve({\n success: false,\n path: input.path,\n error: \"读取文件元数据超时\",\n errorCode: ControlErrorCode.UNKNOWN,\n });\n }, REMOTE_FILE_METADATA_TIMEOUT_MS);\n\n this.pendingUrlMetadata.set(requestId, {\n requestId,\n clientId: input.clientId,\n proxyId: input.proxyId,\n sessionId: input.sessionId,\n path: input.path,\n disposition: input.disposition,\n timer,\n resolve,\n });\n\n proxyWs.send(\n serializeControl({\n type: \"remote_file_metadata_request\",\n requestId,\n sessionId: input.sessionId,\n path: input.path,\n }),\n );\n this.deps.logger.info(\n { requestId, proxyId: input.proxyId, sessionId: input.sessionId, path: input.path },\n \"Remote file URL metadata requested\",\n );\n });\n }\n\n createUploadUrl(input: {\n clientId: string;\n proxyId: string;\n sessionId: string;\n kind: RemoteFileUploadKind;\n fileName?: string;\n mimeType: string;\n size?: number;\n }): { uploadUrl: string; expiresAt: number } {\n this.cleanupExpiredTokens();\n const token = nanoid(32);\n const expiresAt = Date.now() + REMOTE_FILE_TOKEN_TTL_MS;\n this.uploadTokens.set(token, {\n token,\n clientId: input.clientId,\n proxyId: input.proxyId,\n sessionId: input.sessionId,\n kind: input.kind,\n fileName: input.fileName,\n mimeType: input.mimeType,\n size: input.size,\n expiresAt,\n });\n return { uploadUrl: `/api/remote-uploads/${token}`, expiresAt };\n }\n\n handleHttpRequest(req: Request, res: Response): void {\n const tokenValue = req.params.token;\n const token = typeof tokenValue === \"string\" ? this.tokens.get(tokenValue) : undefined;\n if (!token || token.expiresAt <= Date.now()) {\n if (typeof tokenValue === \"string\") this.tokens.delete(tokenValue);\n res.status(404).json({ error: \"remote_file_url_expired\" });\n return;\n }\n\n const proxyWs = this.deps.registry.getProxy(token.proxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) {\n res.status(502).json({ error: \"proxy_offline\" });\n return;\n }\n\n const streamId = nanoid(21);\n const metadataTimer = setTimeout(() => {\n this.failStream(streamId, 504, \"读取文件元数据超时\");\n }, REMOTE_FILE_METADATA_TIMEOUT_MS);\n const pending: PendingHttpStream = {\n streamId,\n token,\n res,\n metadataTimer,\n headersSent: false,\n finished: false,\n };\n this.pendingStreams.set(streamId, pending);\n\n res.on(\"close\", () => {\n if (pending.finished) return;\n pending.finished = true;\n this.pendingStreams.delete(streamId);\n clearTimeout(metadataTimer);\n this.sendCancel(token.proxyId, streamId);\n this.deps.logger.info({ streamId, proxyId: token.proxyId }, \"Remote file HTTP stream closed\");\n });\n\n proxyWs.send(\n serializeControl({\n type: \"remote_file_stream_request\",\n streamId,\n sessionId: token.sessionId,\n path: token.path,\n disposition: token.disposition,\n }),\n );\n this.deps.logger.info(\n { streamId, proxyId: token.proxyId, sessionId: token.sessionId, path: token.path },\n \"Remote file HTTP stream requested\",\n );\n }\n\n handleUploadHttpRequest(req: Request, res: Response): void {\n const tokenValue = req.params.token;\n const token = typeof tokenValue === \"string\" ? this.uploadTokens.get(tokenValue) : undefined;\n if (!token || token.expiresAt <= Date.now()) {\n if (typeof tokenValue === \"string\") this.uploadTokens.delete(tokenValue);\n res.status(404).json({ error: \"remote_file_upload_url_expired\" });\n return;\n }\n\n const proxyWs = this.deps.registry.getProxy(token.proxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) {\n res.status(502).json({ error: \"proxy_offline\" });\n return;\n }\n\n const uploadId = nanoid(21);\n const pending: PendingHttpUpload = {\n uploadId,\n token,\n res,\n chunkSeq: 0,\n requestEnded: false,\n finished: false,\n responseTimer: null,\n };\n this.pendingUploads.set(uploadId, pending);\n\n proxyWs.send(\n serializeControl({\n type: \"remote_file_upload_stream_request\",\n uploadId,\n sessionId: token.sessionId,\n kind: token.kind,\n mimeType: token.mimeType,\n ...(token.fileName ? { fileName: token.fileName } : {}),\n ...(token.size !== undefined ? { size: token.size } : {}),\n }),\n );\n\n req.on(\"data\", (chunk: Buffer) => {\n if (pending.finished) return;\n req.pause();\n const frame = encodeFileStreamFrame(uploadId, pending.chunkSeq, Buffer.from(chunk));\n pending.chunkSeq += 1;\n proxyWs.send(frame, { binary: true }, (err) => {\n if (err) {\n this.failUpload(uploadId, 502, err.message);\n return;\n }\n req.resume();\n });\n });\n\n req.on(\"end\", () => {\n if (pending.finished) return;\n pending.requestEnded = true;\n pending.responseTimer = setTimeout(() => {\n this.failUpload(uploadId, 504, \"等待上传写入结果超时\");\n }, REMOTE_FILE_UPLOAD_RESPONSE_TIMEOUT_MS);\n proxyWs.send(serializeControl({ type: \"remote_file_upload_stream_complete\", uploadId }));\n });\n\n req.on(\"error\", (err) => {\n this.failUpload(uploadId, 400, err.message);\n });\n\n req.on(\"close\", () => {\n if (pending.finished || pending.requestEnded) return;\n this.failUpload(uploadId, 499, \"上传请求已断开\");\n });\n\n this.deps.logger.info(\n { uploadId, proxyId: token.proxyId, sessionId: token.sessionId, kind: token.kind },\n \"Remote file HTTP upload requested\",\n );\n }\n\n handleProxyControl(proxyId: string, msg: ControlMessage<\"remote_file_stream_response\">): boolean;\n handleProxyControl(proxyId: string, msg: ControlMessage<\"remote_file_stream_complete\">): boolean;\n handleProxyControl(\n proxyId: string,\n msg: ControlMessage<\"remote_file_metadata_response\">,\n ): boolean;\n handleProxyControl(\n proxyId: string,\n msg: ControlMessage<\"remote_file_upload_stream_response\">,\n ): boolean;\n handleProxyControl(\n proxyId: string,\n msg:\n | ControlMessage<\"remote_file_stream_response\">\n | ControlMessage<\"remote_file_stream_complete\">\n | ControlMessage<\"remote_file_metadata_response\">\n | ControlMessage<\"remote_file_upload_stream_response\">,\n ): boolean {\n if (msg.type === \"remote_file_upload_stream_response\") {\n return this.handleUploadResponse(proxyId, msg);\n }\n\n if (msg.type === \"remote_file_metadata_response\") {\n return this.handleUrlMetadataResponse(proxyId, msg);\n }\n\n const pending = this.pendingStreams.get(msg.streamId);\n if (!pending) return true;\n if (pending.token.proxyId !== proxyId) {\n this.deps.logger.warn(\n { streamId: msg.streamId, expectedProxyId: pending.token.proxyId, proxyId },\n \"Remote file stream control ignored: proxy mismatch\",\n );\n return true;\n }\n\n if (msg.type === \"remote_file_stream_response\") {\n this.handleStreamResponse(pending, msg);\n return true;\n }\n\n this.handleStreamComplete(pending, msg);\n return true;\n }\n\n private issueUrlToken(input: {\n clientId: string;\n proxyId: string;\n sessionId: string;\n path: string;\n disposition: RemoteFileDisposition;\n }): { url: string; expiresAt: number } {\n const token = nanoid(32);\n const expiresAt = Date.now() + REMOTE_FILE_TOKEN_TTL_MS;\n this.tokens.set(token, {\n token,\n clientId: input.clientId,\n proxyId: input.proxyId,\n sessionId: input.sessionId,\n path: input.path,\n disposition: input.disposition,\n expiresAt,\n });\n return { url: `/api/remote-files/${token}`, expiresAt };\n }\n\n private handleUrlMetadataResponse(\n proxyId: string,\n msg: ControlMessage<\"remote_file_metadata_response\">,\n ): boolean {\n const pending = this.pendingUrlMetadata.get(msg.requestId);\n if (!pending) return true;\n if (pending.proxyId !== proxyId) {\n this.deps.logger.warn(\n { requestId: msg.requestId, expectedProxyId: pending.proxyId, proxyId },\n \"Remote file metadata ignored: proxy mismatch\",\n );\n return true;\n }\n\n this.pendingUrlMetadata.delete(msg.requestId);\n clearTimeout(pending.timer);\n\n if (!msg.success) {\n pending.resolve({\n success: false,\n path: pending.path,\n error: msg.error ?? \"读取文件失败\",\n ...(msg.errorCode ? { errorCode: msg.errorCode } : {}),\n });\n return true;\n }\n\n const { url, expiresAt } = this.issueUrlToken(pending);\n pending.resolve({\n success: true,\n path: pending.path,\n url,\n expiresAt,\n });\n return true;\n }\n\n handleProxyBinary(proxyId: string, data: Buffer): boolean {\n const decoded = decodeFileStreamFrame(data);\n if (!decoded) return false;\n\n const pending = this.pendingStreams.get(decoded.streamId);\n if (!pending) return true;\n if (pending.token.proxyId !== proxyId) {\n this.deps.logger.warn(\n { streamId: decoded.streamId, expectedProxyId: pending.token.proxyId, proxyId },\n \"Remote file stream chunk ignored: proxy mismatch\",\n );\n return true;\n }\n if (!pending.headersSent || pending.finished) return true;\n\n pending.res.write(Buffer.from(decoded.data));\n return true;\n }\n\n private handleStreamResponse(\n pending: PendingHttpStream,\n msg: ControlMessage<\"remote_file_stream_response\">,\n ): void {\n clearTimeout(pending.metadataTimer);\n if (!msg.success) {\n this.failStream(\n pending.streamId,\n statusForErrorCode(msg.errorCode),\n msg.error ?? \"读取文件失败\",\n msg.errorCode,\n );\n return;\n }\n\n try {\n pending.res.status(200);\n pending.res.setHeader(\"Content-Type\", msg.mimeType ?? \"application/octet-stream\");\n pending.res.setHeader(\"Cache-Control\", \"no-store\");\n pending.res.setHeader(\"X-Content-Type-Options\", \"nosniff\");\n if (msg.size !== undefined) {\n pending.res.setHeader(\"Content-Length\", String(msg.size));\n }\n pending.res.setHeader(\n \"Content-Disposition\",\n encodeContentDisposition(pending.token.disposition, msg.fileName ?? \"download\"),\n );\n pending.res.flushHeaders();\n pending.headersSent = true;\n } catch (error) {\n pending.headersSent = pending.res.headersSent;\n this.deps.logger.error(\n { err: error, streamId: pending.streamId, fileName: msg.fileName },\n \"Remote file response headers rejected\",\n );\n this.failStream(pending.streamId, 500, \"文件下载响应无效\");\n }\n }\n\n private handleStreamComplete(\n pending: PendingHttpStream,\n msg: ControlMessage<\"remote_file_stream_complete\">,\n ): void {\n clearTimeout(pending.metadataTimer);\n pending.finished = true;\n this.pendingStreams.delete(pending.streamId);\n if (!msg.success) {\n if (!pending.headersSent) {\n this.writeJsonError(\n pending.res,\n statusForErrorCode(msg.errorCode),\n msg.error ?? \"读取文件失败\",\n msg.errorCode,\n );\n } else {\n pending.res.destroy(new Error(msg.error ?? \"Remote file stream failed\"));\n }\n return;\n }\n pending.res.end();\n }\n\n private handleUploadResponse(\n proxyId: string,\n msg: ControlMessage<\"remote_file_upload_stream_response\">,\n ): boolean {\n const pending = this.pendingUploads.get(msg.uploadId);\n if (!pending) return true;\n if (pending.token.proxyId !== proxyId) {\n this.deps.logger.warn(\n { uploadId: msg.uploadId, expectedProxyId: pending.token.proxyId, proxyId },\n \"Remote file upload response ignored: proxy mismatch\",\n );\n return true;\n }\n pending.finished = true;\n this.pendingUploads.delete(msg.uploadId);\n if (pending.responseTimer) clearTimeout(pending.responseTimer);\n if (!msg.success) {\n this.writeJsonError(\n pending.res,\n statusForErrorCode(msg.errorCode),\n msg.error ?? \"上传文件失败\",\n msg.errorCode,\n );\n return true;\n }\n pending.res.status(200).json({\n sessionId: msg.sessionId,\n success: true,\n path: msg.path,\n });\n return true;\n }\n\n private failStream(streamId: string, status: number, error: string, errorCode?: string): void {\n const pending = this.pendingStreams.get(streamId);\n if (!pending) return;\n pending.finished = true;\n this.pendingStreams.delete(streamId);\n clearTimeout(pending.metadataTimer);\n this.sendCancel(pending.token.proxyId, streamId);\n if (!pending.headersSent) {\n this.writeJsonError(pending.res, status, error, errorCode);\n } else {\n pending.res.destroy(new Error(error));\n }\n }\n\n private failUpload(uploadId: string, status: number, error: string, errorCode?: string): void {\n const pending = this.pendingUploads.get(uploadId);\n if (!pending) return;\n pending.finished = true;\n this.pendingUploads.delete(uploadId);\n if (pending.responseTimer) clearTimeout(pending.responseTimer);\n this.sendUploadCancel(pending.token.proxyId, uploadId);\n if (!pending.res.headersSent) {\n this.writeJsonError(pending.res, status, error, errorCode);\n } else {\n pending.res.destroy(new Error(error));\n }\n }\n\n private writeJsonError(res: Response, status: number, error: string, errorCode?: string): void {\n res.status(status).json({\n error,\n ...(errorCode ? { errorCode } : {}),\n });\n }\n\n private sendCancel(proxyId: string, streamId: string): void {\n const proxyWs = this.deps.registry.getProxy(proxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) return;\n proxyWs.send(serializeControl({ type: \"remote_file_stream_cancel\", streamId }));\n }\n\n private sendUploadCancel(proxyId: string, uploadId: string): void {\n const proxyWs = this.deps.registry.getProxy(proxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) return;\n proxyWs.send(serializeControl({ type: \"remote_file_upload_stream_cancel\", uploadId }));\n }\n\n private cleanupExpiredTokens(): void {\n const now = Date.now();\n for (const [token, meta] of this.tokens) {\n if (meta.expiresAt <= now) this.tokens.delete(token);\n }\n for (const [token, meta] of this.uploadTokens) {\n if (meta.expiresAt <= now) this.uploadTokens.delete(token);\n }\n }\n}\n","import compression from \"compression\";\nimport express, { type Express, type Request, type Response } from \"express\";\nimport { existsSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\n\nconst IMMUTABLE_CACHE_SECONDS = 365 * 24 * 60 * 60;\nconst SHORT_CACHE_SECONDS = 60 * 60;\n\nconst RELAY_ROUTE_ROOTS = new Set([\n \"/api\",\n \"/client\",\n \"/fonts\",\n \"/health\",\n \"/proxy\",\n \"/status\",\n \"/voice\",\n]);\n\nfunction isRelayRoute(pathname: string): boolean {\n for (const root of RELAY_ROUTE_ROOTS) {\n if (pathname === root || pathname.startsWith(`${root}/`)) return true;\n }\n return false;\n}\n\nfunction setStaticCacheHeaders(response: Response, filePath: string): void {\n const fileName = basename(filePath);\n const normalizedPath = filePath.replaceAll(\"\\\\\", \"/\");\n if (normalizedPath.includes(\"/assets/\") || /^workbox-.+\\.js$/.test(fileName)) {\n response.setHeader(\"Cache-Control\", `public, max-age=${IMMUTABLE_CACHE_SECONDS}, immutable`);\n return;\n }\n\n if (\n fileName === \"index.html\" ||\n fileName === \"sw.js\" ||\n fileName === \"registerSW.js\" ||\n fileName === \"manifest.webmanifest\"\n ) {\n response.setHeader(\"Cache-Control\", \"no-cache, no-store, must-revalidate\");\n return;\n }\n\n response.setHeader(\"Cache-Control\", `public, max-age=${SHORT_CACHE_SECONDS}`);\n}\n\nexport function mountWebApp(\n app: Express,\n options: {\n webAssetDir: string;\n logger: Logger;\n },\n): boolean {\n const { webAssetDir, logger } = options;\n const indexPath = join(webAssetDir, \"index.html\");\n if (!existsSync(indexPath)) {\n logger.warn({ webAssetDir }, \"Web assets not found; Relay will run without the Web UI\");\n return false;\n }\n\n app.use(compression());\n app.use(\n express.static(webAssetDir, {\n index: false,\n fallthrough: true,\n setHeaders: setStaticCacheHeaders,\n }),\n );\n\n app.use((request: Request, response: Response, next) => {\n if (request.method !== \"GET\" && request.method !== \"HEAD\") {\n next();\n return;\n }\n\n if (isRelayRoute(request.path)) {\n response.status(404).json({ error: \"not_found\" });\n return;\n }\n\n if (!request.accepts(\"html\")) {\n next();\n return;\n }\n\n response.setHeader(\"Cache-Control\", \"no-cache, no-store, must-revalidate\");\n response.sendFile(\"index.html\", { root: webAssetDir });\n });\n\n logger.info({ webAssetDir }, \"Web UI mounted\");\n return true;\n}\n"],"mappings":"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","names":["express","existsSync","dirname","fileURLToPath","z","z","z","z","z","z","z","z","z","z","z","z","WebSocket","WebSocket","WebSocket","WebSocket","WebSocket","WebSocket","WebSocket","WebSocket","EventEmitter","nanoid","WebSocket","OPEN","defaultSocketFactory","WebSocket","EventEmitter","nanoid","resolve","rejectNotRegistered","result","WebSocket","WebSocket","readFileSync","dirname","join","join","readFileSync","dirname","WebSocket","WebSocket","WebSocket","sendJson","parseJson","buildStats","statsLogFields","require","nanoid","WebSocket","WebSocket","nanoid","resolve","existsSync","join","dirname","fileURLToPath","express","existsSync","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/registry.ts","../../../packages/shared/src/schemas/envelope.ts","../../../packages/shared/src/schemas/id.ts","../../../packages/shared/src/schemas/chat.ts","../../../packages/shared/src/schemas/tool.ts","../../../packages/shared/src/schemas/session.ts","../../../packages/shared/src/constants/enums.ts","../../../packages/shared/src/constants/pty.ts","../../../packages/shared/src/schemas/system.ts","../../../packages/shared/src/schemas/voice.ts","../../../packages/shared/src/schemas/voice-stream.ts","../../../packages/shared/src/builders/index.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/constants/session-create.ts","../../../packages/shared/src/constants/relay-close-codes.ts","../../../packages/shared/src/constants/wire-limits.ts","../../../packages/shared/src/binary-frame.ts","../../../packages/shared/src/voice-audio-codec.ts","../../../packages/shared/src/state-machine.ts","../src/health.ts","../src/version.ts","../src/handlers/proxy.ts","../src/router.ts","../src/latency-probes.ts","../src/handlers/client.ts","../src/voice/bailian-asr.ts","../src/voice/bailian-endpoints.ts","../src/voice/bailian-tts.ts","../src/voice/pcm-resample.ts","../src/voice/config-test.ts","../src/voice/client-controls.ts","../src/heartbeat.ts","../src/chaos.ts","../src/voice/config-store.ts","../src/voice/asr-ws.ts","../src/voice/tts-ws.ts","../src/voice/capabilities.ts","../src/voice/bailian-provider.ts","../src/voice/provider.ts","../src/remote-file-bridge.ts","../src/web-app.ts"],"sourcesContent":["import express from \"express\";\nimport { existsSync } from \"node:fs\";\nimport { createServer, type IncomingMessage, 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/logger\";\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\";\nimport { createVoiceConfigStore } from \"./voice/config-store.js\";\nimport { handleVoiceAsrConnection, type VoiceAsrClientFactory } from \"./voice/asr-ws.js\";\nimport { handleVoiceTtsConnection, type VoiceTtsClientFactory } from \"./voice/tts-ws.js\";\nimport { createBailianVoiceProvider } from \"./voice/bailian-provider.js\";\nimport type { VoiceCapabilitiesProvider } from \"./voice/capabilities.js\";\nimport type { VoiceConfigTester } from \"./voice/config-test.js\";\nimport { createVoiceProviderRegistry, type VoiceProviderRegistry } from \"./voice/provider.js\";\nimport { RemoteFileBridge } from \"./remote-file-bridge.js\";\nimport { mountWebApp } from \"./web-app.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 // ws upgrade 时校验的 Origin 列表 (匹配 request.headers.origin)。空数组 / undefined 不\n // 校验, 向后兼容现有部署 (本地 dev 跨端口 / Capacitor 等)。设置后只放白名单, 阻挡\n // CSWSH——攻击者站点诱导用户浏览器直接发 ws upgrade 到 relay 时, 没有合法 Origin。\n allowedOrigins?: readonly string[];\n chaos?: RelayChaosOptions;\n fontAssetDir?: string;\n webAssetDir?: string | false;\n voiceDefaults?: {\n region?: \"cn\" | \"intl\";\n asrModel?: string;\n ttsModel?: string;\n ttsVoice?: string;\n };\n voiceAsrClientFactory?: VoiceAsrClientFactory;\n voiceTtsClientFactory?: VoiceTtsClientFactory;\n voiceCapabilitiesProvider?: VoiceCapabilitiesProvider;\n voiceConfigTester?: VoiceConfigTester;\n voiceProviderRegistry?: VoiceProviderRegistry;\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\");\nconst PACKAGED_WEB_DIR = resolve(MODULE_DIR, \"../assets/web\");\n\nfunction firstHeaderValue(value: string | string[] | undefined): string | undefined {\n if (Array.isArray(value)) return value[0];\n return value;\n}\n\nfunction requestRemoteAddress(request: IncomingMessage): string | undefined {\n const forwardedFor = firstHeaderValue(request.headers[\"x-forwarded-for\"]);\n const firstForwarded = forwardedFor?.split(\",\")[0]?.trim();\n return firstForwarded || request.socket.remoteAddress;\n}\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 const allowedOriginsSet =\n options.allowedOrigins && options.allowedOrigins.length > 0\n ? new Set(options.allowedOrigins)\n : null;\n const checkOrigin = (origin: string | undefined): boolean => {\n if (!allowedOriginsSet) return true;\n return typeof origin === \"string\" && allowedOriginsSet.has(origin);\n };\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 remoteFileBridge = new RemoteFileBridge({ registry, logger });\n const voiceConfigStore = createVoiceConfigStore({\n dataDir,\n defaults: options.voiceDefaults,\n });\n const voiceProviders =\n options.voiceProviderRegistry ??\n createVoiceProviderRegistry([\n createBailianVoiceProvider({\n asrClientFactory: options.voiceAsrClientFactory,\n ttsClientFactory: options.voiceTtsClientFactory,\n capabilitiesProvider: options.voiceCapabilitiesProvider,\n configTester: options.voiceConfigTester,\n }),\n ]);\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 app.disable(\"x-powered-by\");\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(\n healthRouter(registry, {\n proxyTokenRequired,\n clientTokenRequired,\n validateClientToken: (token) => token === clientToken,\n validateProxyToken: (token) => token === proxyToken,\n getClientToken: () => clientToken ?? null,\n }),\n );\n app.get(\"/api/remote-files/:token\", (req, res) => {\n remoteFileBridge.handleHttpRequest(req, res);\n });\n app.put(\"/api/remote-uploads/:token\", (req, res) => {\n remoteFileBridge.handleUploadHttpRequest(req, res);\n });\n const webAssetDir =\n options.webAssetDir === false ? undefined : (options.webAssetDir ?? PACKAGED_WEB_DIR);\n if (webAssetDir) {\n mountWebApp(app, { webAssetDir, logger });\n }\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 const voiceAsrWss = new WebSocketServer({ noServer: true });\n const voiceTtsWss = 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 const origin = request.headers.origin;\n\n if (!checkOrigin(origin)) {\n logger.warn(\n { ip: request.socket.remoteAddress, origin: origin ?? \"(missing)\", pathname },\n \"rejected upgrade: origin not in allowedOrigins\",\n );\n socket.write(\"HTTP/1.1 403 Forbidden\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\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\" || pathname === \"/voice/asr\" || pathname === \"/voice/tts\") {\n if (clientTokenRequired) {\n const token = url.searchParams.get(\"token\");\n if (token !== clientToken) {\n logger.warn(\n { ip: request.socket.remoteAddress, pathname },\n \"rejected client-side upgrade: invalid token\",\n );\n socket.write(\"HTTP/1.1 401 Unauthorized\\r\\n\\r\\n\");\n socket.destroy();\n return;\n }\n }\n if (pathname === \"/client\") {\n clientWss.handleUpgrade(request, socket, head, (ws) => {\n clientWss.emit(\"connection\", ws, request);\n });\n } else if (pathname === \"/voice/asr\") {\n voiceAsrWss.handleUpgrade(request, socket, head, (ws) => {\n voiceAsrWss.emit(\"connection\", ws, request);\n });\n } else {\n voiceTtsWss.handleUpgrade(request, socket, head, (ws) => {\n voiceTtsWss.emit(\"connection\", ws, request);\n });\n }\n return;\n }\n\n socket.destroy();\n });\n\n proxyWss.on(\"connection\", (ws) => {\n handleProxyConnection(ws, registry, logger, relayChaos, remoteFileBridge);\n });\n\n clientWss.on(\"connection\", (ws, request) => {\n handleClientConnection(\n ws,\n registry,\n logger,\n relayChaos,\n voiceConfigStore,\n voiceProviders,\n remoteFileBridge,\n {\n userAgent: firstHeaderValue(request.headers[\"user-agent\"]),\n remoteAddress: requestRemoteAddress(request),\n },\n );\n });\n\n voiceAsrWss.on(\"connection\", (ws) => {\n handleVoiceAsrConnection(ws, voiceConfigStore, logger, voiceProviders);\n });\n\n voiceTtsWss.on(\"connection\", (ws) => {\n handleVoiceTtsConnection(ws, voiceConfigStore, logger, voiceProviders);\n });\n\n const proxyHeartbeat = setupHeartbeat(proxyWss, heartbeatInterval, {\n logger,\n peerType: \"proxy\",\n describePeer: (ws) => ({ proxyId: (ws as { proxyId?: string }).proxyId }),\n });\n const clientHeartbeat = setupHeartbeat(clientWss, heartbeatInterval, {\n logger,\n peerType: \"client\",\n describePeer: (ws) => ({\n clientId: (ws as { clientId?: string }).clientId,\n boundProxyId: (ws as { boundProxyId?: string }).boundProxyId,\n }),\n });\n const voiceAsrHeartbeat = setupHeartbeat(voiceAsrWss, heartbeatInterval, {\n logger,\n peerType: \"voice-asr\",\n });\n const voiceTtsHeartbeat = setupHeartbeat(voiceTtsWss, heartbeatInterval, {\n logger,\n peerType: \"voice-tts\",\n });\n\n async function close(): Promise<void> {\n clearInterval(proxyHeartbeat);\n clearInterval(clientHeartbeat);\n clearInterval(voiceAsrHeartbeat);\n clearInterval(voiceTtsHeartbeat);\n\n for (const ws of proxyWss.clients) {\n ws.terminate();\n }\n for (const ws of clientWss.clients) {\n ws.terminate();\n }\n for (const ws of voiceAsrWss.clients) {\n ws.terminate();\n }\n for (const ws of voiceTtsWss.clients) {\n ws.terminate();\n }\n\n await Promise.all([\n new Promise<void>((resolve, reject) => {\n proxyWss.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n }),\n new Promise<void>((resolve, reject) => {\n clientWss.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n }),\n new Promise<void>((resolve, reject) => {\n voiceAsrWss.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n }),\n new Promise<void>((resolve, reject) => {\n voiceTtsWss.close((err) => {\n if (err) reject(err);\n else resolve();\n });\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\";\nimport { defineFSM } from \"@dev-anywhere/shared\";\n\n// 显式代理连接状态,取代 ws null 检查\ntype ProxyConnectionState = \"online\" | \"offline\";\n\n// 显式客户端连接状态,跟踪注册和绑定\ntype ClientConnectionState = \"registered\" | \"bound\";\n\n// proxy 连接 FSM: 仅 online <-> offline 双向流转。registerProxy 走 reconnect 路径直接写\n// online (相当于 offline -> online), transitionProxy 走 close 路径走 online -> offline。\n// 同状态停留 (online -> online / offline -> offline) 视为非法, 由 canTransition 拦下。\nconst proxyConnectionFSM = defineFSM<ProxyConnectionState>({\n online: [\"offline\"],\n offline: [\"online\"],\n});\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\ninterface ClientConnectionMetadata {\n clientId?: string;\n connectedAt: number;\n userAgent?: string;\n platform?: string;\n maxTouchPoints?: number;\n browserName?: string;\n osName?: string;\n deviceKind?: \"desktop\" | \"tablet\" | \"phone\" | \"unknown\";\n remoteAddress?: string;\n}\n\n// 代理注册、客户端绑定、宽限期管理\nexport class RelayRegistry {\n private proxyStates = new Map<string, ProxyState>();\n private clientBindings = new Map<string, ClientBinding>();\n private connectedClients = new Map<WebSocket, ClientConnectionMetadata>();\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 // 显式状态转换,校验 from 状态匹配后更新 connectionState\n transitionProxy(proxyId: string, from: ProxyConnectionState, to: ProxyConnectionState): void {\n if (!proxyConnectionFSM.canTransition(from, to)) {\n throw new Error(`Invalid proxy transition: ${from} -> ${to}`);\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 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 setSessionsForProxy(proxyId: string, sessionIds: readonly string[]): void {\n const state = this.proxyStates.get(proxyId);\n if (state) {\n state.sessions = new Set(sessionIds);\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 // 连接关闭只能释放自己持有的绑定。旧连接的 close 可能晚于新连接注册,\n // 此时不能把已经切换到新 WebSocket 的绑定清空。\n unbindClientSocket(clientId: string, ws: WebSocket): boolean {\n const binding = this.clientBindings.get(clientId);\n if (!binding || binding.ws !== ws) return false;\n binding.ws = null;\n return true;\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 // 计数依据:ws 还存活且处于 OPEN。仅 `if (binding.ws)` 不够:close handler 漏调\n // unbindClientById 时 ws 对象残留,会让 /status 永久报告虚高 clientCount。\n let count = 0;\n for (const [, binding] of this.clientBindings) {\n if (binding.ws && binding.ws.readyState === WebSocket.OPEN) count++;\n }\n return count;\n }\n\n addClientWs(\n ws: WebSocket,\n metadata: Partial<Omit<ClientConnectionMetadata, \"connectedAt\">> & {\n connectedAt?: number;\n } = {},\n ): void {\n this.connectedClients.set(ws, {\n connectedAt: metadata.connectedAt ?? Date.now(),\n ...(metadata.clientId !== undefined ? { clientId: metadata.clientId } : {}),\n ...(metadata.userAgent !== undefined ? { userAgent: metadata.userAgent } : {}),\n ...(metadata.platform !== undefined ? { platform: metadata.platform } : {}),\n ...(metadata.maxTouchPoints !== undefined ? { maxTouchPoints: metadata.maxTouchPoints } : {}),\n ...(metadata.browserName !== undefined ? { browserName: metadata.browserName } : {}),\n ...(metadata.osName !== undefined ? { osName: metadata.osName } : {}),\n ...(metadata.deviceKind !== undefined ? { deviceKind: metadata.deviceKind } : {}),\n ...(metadata.remoteAddress !== undefined ? { remoteAddress: metadata.remoteAddress } : {}),\n });\n }\n\n updateConnectedClientId(ws: WebSocket, clientId: string): void {\n const metadata = this.connectedClients.get(ws);\n if (metadata) {\n metadata.clientId = clientId;\n return;\n }\n this.addClientWs(ws, { clientId });\n }\n\n updateConnectedClientMetadata(\n ws: WebSocket,\n metadata: Partial<Omit<ClientConnectionMetadata, \"connectedAt\">>,\n ): void {\n const current = this.connectedClients.get(ws);\n if (!current) {\n this.addClientWs(ws, metadata);\n return;\n }\n if (metadata.clientId !== undefined) current.clientId = metadata.clientId;\n if (metadata.userAgent !== undefined) current.userAgent = metadata.userAgent;\n if (metadata.platform !== undefined) current.platform = metadata.platform;\n if (metadata.maxTouchPoints !== undefined) current.maxTouchPoints = metadata.maxTouchPoints;\n if (metadata.browserName !== undefined) current.browserName = metadata.browserName;\n if (metadata.osName !== undefined) current.osName = metadata.osName;\n if (metadata.deviceKind !== undefined) current.deviceKind = metadata.deviceKind;\n if (metadata.remoteAddress !== undefined) current.remoteAddress = metadata.remoteAddress;\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.keys()) {\n if (ws.readyState === WebSocket.OPEN) {\n clients.push(ws);\n }\n }\n return clients;\n }\n\n getConnectedClientSockets(clientId: string): WebSocket[] {\n const sockets: WebSocket[] = [];\n for (const [ws, metadata] of this.connectedClients) {\n if (metadata.clientId === clientId && ws.readyState === WebSocket.OPEN) {\n sockets.push(ws);\n }\n }\n return sockets;\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 getConnectedClientDetails(currentClientId?: string): Array<{\n clientId: string;\n proxyId?: string;\n connectedAt: number;\n current?: boolean;\n userAgent?: string;\n platform?: string;\n maxTouchPoints?: number;\n browserName: string;\n osName: string;\n deviceKind: \"desktop\" | \"tablet\" | \"phone\" | \"unknown\";\n remoteAddress?: string;\n }> {\n const details: Array<{\n clientId: string;\n proxyId?: string;\n connectedAt: number;\n current?: boolean;\n userAgent?: string;\n platform?: string;\n maxTouchPoints?: number;\n browserName: string;\n osName: string;\n deviceKind: \"desktop\" | \"tablet\" | \"phone\" | \"unknown\";\n remoteAddress?: string;\n }> = [];\n for (const [ws, metadata] of this.connectedClients) {\n if (ws.readyState !== WebSocket.OPEN || !metadata.clientId) continue;\n if (!metadata.browserName || !metadata.osName || !metadata.deviceKind) continue;\n const binding = this.clientBindings.get(metadata.clientId);\n details.push({\n clientId: metadata.clientId,\n ...(binding?.proxyId !== undefined ? { proxyId: binding.proxyId } : {}),\n connectedAt: metadata.connectedAt,\n ...(metadata.clientId === currentClientId ? { current: true } : {}),\n ...(metadata.userAgent !== undefined ? { userAgent: metadata.userAgent } : {}),\n ...(metadata.platform !== undefined ? { platform: metadata.platform } : {}),\n ...(metadata.maxTouchPoints !== undefined\n ? { maxTouchPoints: metadata.maxTouchPoints }\n : {}),\n browserName: metadata.browserName,\n osName: metadata.osName,\n deviceKind: metadata.deviceKind,\n ...(metadata.remoteAddress !== undefined ? { remoteAddress: metadata.remoteAddress } : {}),\n });\n }\n return details;\n }\n}\n","import { z } from \"zod\";\nimport { IdSchema } from \"./id.js\";\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// 信封基础字段:序列号、时间戳、来源、协议版本——所有 envelope 共用。\nconst BaseEnvelopeFields = {\n seq: z.number().int().nonnegative(),\n timestamp: z.number(),\n source: z.enum([\"proxy\", \"client\"]),\n version: z.string(),\n};\n\n// session-scoped envelope: 携带 sessionId 路由到具体会话, 大多数业务 envelope 走这里。\nconst SessionedEnvelopeFields = {\n ...BaseEnvelopeFields,\n sessionId: IdSchema,\n};\n\n// 按 type 字段区分的 discriminatedUnion 信封\nexport const MessageEnvelopeSchema = z.discriminatedUnion(\"type\", [\n // chat (3)\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"user_input\"),\n payload: UserInputPayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"assistant_message\"),\n payload: AssistantMessagePayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\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 ...SessionedEnvelopeFields,\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 ...SessionedEnvelopeFields,\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 ...SessionedEnvelopeFields,\n type: z.literal(\"assistant_tool_use\"),\n payload: ToolUseRequestPayloadSchema,\n }),\n // session (5)\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"session_create\"),\n payload: SessionCreatePayloadSchema,\n }),\n // session_list 是全局广播 (列出所有 session), 不绑定具体 sessionId, 不携带该字段。\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"session_list\"),\n payload: SessionListPayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"session_switch\"),\n payload: SessionSwitchPayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"session_terminate\"),\n payload: SessionTerminatePayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"session_status\"),\n payload: SessionStatusPayloadSchema,\n }),\n // system (5): 心跳 / 认证 / 同步——全局, 无 sessionId\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// 所有跨进程传递的标识符 (proxyId / clientId / sessionId / requestId / toolId) 的统一上限。\n// 设计原因:\n// - nanoid (proxy/session/client 服务端默认生成) = 21 字符\n// - claude tool_use id (toolu_<base64ish>) ≈ 30-40 字符\n// - codex tool id 类似量级\n// 256 给协议演进留余量, 同时挡住 wire 上来的恶意超长 ID:\n// - Map<id, _> key 占用 (memory DoS)\n// - 日志行膨胀 (operator/磁盘压力)\n// - 拼到 disk path (虽然当前没有此路径, defense in depth)\nconst MAX_ID_LENGTH = 256;\n\nexport const IdSchema = z.string().min(1).max(MAX_ID_LENGTH);\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\";\nimport { IdSchema } from \"./id.js\";\n\n// 工具调用请求\nexport const ToolUseRequestPayloadSchema = z.object({\n toolName: z.string(),\n toolId: IdSchema,\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: IdSchema,\n whitelistTool: z.boolean().optional(),\n});\n\nexport type ToolApprovePayload = z.infer<typeof ToolApprovePayloadSchema>;\n\n// 工具调用拒绝\nexport const ToolDenyPayloadSchema = z.object({\n toolId: IdSchema,\n reason: z.string().optional(),\n});\n\nexport type ToolDenyPayload = z.infer<typeof ToolDenyPayloadSchema>;\n\n// 工具调用结果\nexport const ToolResultPayloadSchema = z.object({\n toolId: IdSchema,\n result: z.unknown(),\n isError: z.boolean(),\n});\n\nexport type ToolResultPayload = z.infer<typeof ToolResultPayloadSchema>;\n","import { z } from \"zod\";\nimport {\n providerValues,\n ptyOwnerValues,\n sessionKindValues,\n sessionModeValues,\n} from \"../constants/enums.js\";\nimport { ptySemanticStateValues } from \"../constants/pty.js\";\nimport { IdSchema } from \"./id.js\";\n\nexport const sessionStateValues = [\n \"idle\",\n \"working\",\n \"compacting\",\n \"waiting_approval\",\n \"error\",\n \"terminated\",\n] 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: IdSchema,\n kind: z.enum(sessionKindValues).optional(),\n name: z.string().optional(),\n // cwd 只用于展示完整路径/tooltip,不作为前端路由或权限判断来源。\n cwd: z.string().optional(),\n // true 表示 name 是用户显式命名,PTY UI 不再让 OSC terminal_title 覆盖它。\n nameLocked: z.boolean().optional(),\n state: z.enum(sessionStateValues),\n mode: z.enum(sessionModeValues).optional(),\n provider: z.enum(providerValues),\n // PTY 进程所有权。两种 PTY 的 cols/rows 都由会话端持有,Web 仅按服务端快照展示,\n // 浏览器视口变化不会隐式改变真实 PTY 几何。\n // - local-terminal: 本地 terminal worker 持有 PTY\n // - proxy-hosted: serve 内 HostedPtyRegistry 持有 PTY\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: IdSchema,\n});\n\nexport type SessionSwitchPayload = z.infer<typeof SessionSwitchPayloadSchema>;\n\n// 终止会话\nexport const SessionTerminatePayloadSchema = z.object({\n sessionId: IdSchema,\n});\n\nexport type SessionTerminatePayload = z.infer<typeof SessionTerminatePayloadSchema>;\n\n// 会话状态变更\n// lastActive: 触发本次状态迁移或活动刷新的时间戳 (ms),用于列表相对时间显示。\nexport const SessionStatusPayloadSchema = z.object({\n sessionId: IdSchema,\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(ptySemanticStateValues),\n // Semantic event sequence. Present for runtime PTY semantic events; optional for\n // legacy cleanup messages from older proxies.\n seq: z.number().int().nonnegative().optional(),\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: IdSchema,\n toolName: z.string(),\n input: z.record(z.string(), z.unknown()),\n })\n .optional(),\n permissionResolution: z\n .object({\n requestId: IdSchema,\n outcome: z.enum([\"allow\", \"deny\"]),\n })\n .optional(),\n summary: z.string().optional(),\n});\nexport type AgentStatusPayload = z.infer<typeof AgentStatusPayloadSchema>;\n","// 协议层共用的枚举字面量集合。Zod schema、TypeScript 类型、运行时常量都从这里派生,\n// 避免在 relay-control / session / chat / web 各自手写 z.enum([\"claude\", \"codex\"]) 漂移。\n\nexport const providerValues = [\"claude\", \"codex\"] as const;\nexport type ProviderId = (typeof providerValues)[number];\n\nexport const ptyOwnerValues = [\"local-terminal\", \"proxy-hosted\"] as const;\nexport type PtyOwner = (typeof ptyOwnerValues)[number];\n\nexport const sessionModeValues = [\"pty\", \"json\"] as const;\nexport type SessionMode = (typeof sessionModeValues)[number];\n\nexport const sessionKindValues = [\"agent\", \"terminal\"] as const;\nexport type SessionKind = (typeof sessionKindValues)[number];\n","// PTY 局部语义状态。仅承载明确语义信号; title/spinner 变化时事件的 state 取 null,\n// 让上层不参与 FSM 切换, 只走 title 推送通道。\n// 单一 source of truth: osc-extractor / ipc-protocol / shared schema 都从这里取。\nexport const PtySemanticState = {\n WORKING: \"working\",\n TURN_COMPLETE: \"turn_complete\",\n APPROVAL_WAIT: \"approval_wait\",\n} as const;\n\nexport type PtySemanticState = (typeof PtySemanticState)[keyof typeof PtySemanticState];\n\n// zod / 类型字面量场景下复用的 const tuple, z.enum 直接吃。\nexport const ptySemanticStateValues = [\n PtySemanticState.WORKING,\n PtySemanticState.TURN_COMPLETE,\n PtySemanticState.APPROVAL_WAIT,\n] as const;\n\n// Web 创建 PTY 时可以按可用视口放大初始几何,但不能低于传统 80x24。\n// 这个底线既保证常见 TUI 布局,也避免 Claude Code / Codex 等 CLI 因终端过窄\n// 省略二维码或启动信息。上限用于拦截异常客户端提交的不合理 PTY 尺寸。\nexport const PTY_INITIAL_MIN_COLS = 80;\nexport const PTY_INITIAL_MIN_ROWS = 24;\nexport const PTY_INITIAL_MAX_COLS = 500;\nexport const PTY_INITIAL_MAX_ROWS = 200;\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\";\n\nexport const voiceProviderValues = [\"aliyun-bailian\"] as const;\nexport const voiceRegionValues = [\"cn\", \"intl\"] as const;\nexport const voiceOptionSourceValues = [\"official\", \"custom\"] as const;\nexport const voiceOptionGenderValues = [\"male\", \"female\", \"unknown\"] as const;\n\nexport const VoiceProviderConfigSchema = z\n .object({\n provider: z.enum(voiceProviderValues),\n configured: z.boolean(),\n region: z.enum(voiceRegionValues),\n asrModel: z.string().min(1),\n ttsModel: z.string().min(1),\n ttsVoice: z.string().min(1),\n turnIdleSeconds: z.number().int().positive().safe().default(3),\n })\n .strict();\nexport type VoiceProviderConfig = z.infer<typeof VoiceProviderConfigSchema>;\n\nexport const VoiceConfigUpdateSchema = z\n .object({\n provider: z.enum(voiceProviderValues).optional(),\n apiKey: z.string().min(1).optional(),\n clearApiKey: z.boolean().optional(),\n region: z.enum(voiceRegionValues).optional(),\n asrModel: z.string().min(1).optional(),\n ttsModel: z.string().min(1).optional(),\n ttsVoice: z.string().min(1).optional(),\n turnIdleSeconds: z.number().int().positive().safe().optional(),\n })\n .strict();\nexport type VoiceConfigUpdate = z.infer<typeof VoiceConfigUpdateSchema>;\n\nexport const VoiceOptionSchema = z\n .object({\n value: z.string().min(1),\n label: z.string().min(1),\n description: z.string().min(1).optional(),\n gender: z.enum(voiceOptionGenderValues).optional(),\n age: z.string().min(1).optional(),\n model: z.string().min(1).optional(),\n source: z.enum(voiceOptionSourceValues),\n })\n .strict();\nexport type VoiceOption = z.infer<typeof VoiceOptionSchema>;\n\nexport const VoiceCapabilitiesSchema = z\n .object({\n asrModels: z.array(VoiceOptionSchema),\n ttsModels: z.array(VoiceOptionSchema),\n ttsVoices: z.array(VoiceOptionSchema),\n fetchedAt: z.number().optional(),\n })\n .strict();\nexport type VoiceCapabilities = z.infer<typeof VoiceCapabilitiesSchema>;\n\n// Official snapshot from Alibaba Cloud Bailian docs, captured 2026-05-18.\n// Alibaba does not currently expose a clean API-key-scoped catalog endpoint for these system models.\nconst BUNDLED_BAILIAN_ASR_MODELS: VoiceOption[] = [\n {\n value: \"qwen3-asr-flash-realtime\",\n label: \"Qwen3 ASR Flash Realtime\",\n source: \"official\",\n },\n {\n value: \"qwen3-asr-flash-realtime-2026-02-10\",\n label: \"Qwen3 ASR Flash Realtime · 2026-02-10\",\n source: \"official\",\n },\n {\n value: \"qwen3-asr-flash-realtime-2025-10-27\",\n label: \"Qwen3 ASR Flash Realtime · 2025-10-27\",\n source: \"official\",\n },\n];\n\nconst BUNDLED_BAILIAN_TTS_MODELS: VoiceOption[] = [\n {\n value: \"cosyvoice-v3-flash\",\n label: \"CosyVoice V3 Flash · 系统音色\",\n source: \"official\",\n },\n {\n value: \"cosyvoice-v3-plus\",\n label: \"CosyVoice V3 Plus · 系统音色\",\n source: \"official\",\n },\n {\n value: \"cosyvoice-v3.5-flash\",\n label: \"CosyVoice V3.5 Flash · 自定义音色\",\n source: \"official\",\n },\n {\n value: \"cosyvoice-v3.5-plus\",\n label: \"CosyVoice V3.5 Plus · 自定义音色\",\n source: \"official\",\n },\n];\n\nconst BUNDLED_BAILIAN_TTS_VOICES: VoiceOption[] = [\n {\n value: \"longanyang\",\n label: \"龙安洋 · 男 · 阳光大男孩 · 年龄 20-30\",\n gender: \"male\",\n age: \"20-30\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longanhuan\",\n label: \"龙安欢 · 女 · 欢脱元气 · 年龄 20-30\",\n gender: \"female\",\n age: \"20-30\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longhuhu_v3\",\n label: \"龙呼呼 · 女 · 天真烂漫女童 · 年龄 6-10\",\n gender: \"female\",\n age: \"6-10\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longpaopao_v3\",\n label: \"龙泡泡 · 未知 · 飞天泡泡音 · 年龄 6-15\",\n gender: \"unknown\",\n age: \"6-15\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longjielidou_v3\",\n label: \"龙杰力豆 · 男 · 阳光顽皮 · 年龄 10\",\n gender: \"male\",\n age: \"10\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longxian_v3\",\n label: \"龙仙 · 女 · 豪放可爱 · 年龄 12\",\n gender: \"female\",\n age: \"12\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longling_v3\",\n label: \"龙铃 · 女 · 稚气呆板 · 年龄 10\",\n gender: \"female\",\n age: \"10\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longjiaxin_v3\",\n label: \"龙嘉欣 · 女 · 优雅粤语 · 年龄 30-35\",\n gender: \"female\",\n age: \"30-35\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longanyue_v3\",\n label: \"龙安粤 · 男 · 欢脱粤语 · 年龄 25-35\",\n gender: \"male\",\n age: \"25-35\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longlaotie_v3\",\n label: \"龙老铁 · 男 · 东北直率 · 年龄 25-30\",\n gender: \"male\",\n age: \"25-30\",\n model: \"cosyvoice-v3-flash\",\n source: \"official\",\n },\n {\n value: \"longanyang\",\n label: \"龙安洋 · 男 · 阳光大男孩 · 年龄 20-30\",\n gender: \"male\",\n age: \"20-30\",\n model: \"cosyvoice-v3-plus\",\n source: \"official\",\n },\n {\n value: \"longanhuan\",\n label: \"龙安欢 · 女 · 欢脱元气 · 年龄 20-30\",\n gender: \"female\",\n age: \"20-30\",\n model: \"cosyvoice-v3-plus\",\n source: \"official\",\n },\n];\n\nfunction cloneVoiceOption(option: VoiceOption): VoiceOption {\n return { ...option };\n}\n\nexport function createBundledBailianVoiceCapabilities(fetchedAt?: number): VoiceCapabilities {\n return {\n asrModels: BUNDLED_BAILIAN_ASR_MODELS.map(cloneVoiceOption),\n ttsModels: BUNDLED_BAILIAN_TTS_MODELS.map(cloneVoiceOption),\n ttsVoices: BUNDLED_BAILIAN_TTS_VOICES.map(cloneVoiceOption),\n ...(typeof fetchedAt === \"number\" ? { fetchedAt } : {}),\n };\n}\n\nexport const VoiceSummaryReasonSchema = z.enum([\n \"code\",\n \"table\",\n \"diff\",\n \"log\",\n \"stack_trace\",\n \"long_list\",\n \"long_text\",\n \"mixed\",\n \"approval\",\n]);\nexport type VoiceSummaryReason = z.infer<typeof VoiceSummaryReasonSchema>;\n","import { z } from \"zod\";\n\nexport const VoiceAsrAudioEncodingSchema = z.enum([\"pcm_s16le\", \"mulaw\"]);\n\nexport const VoiceAsrStartMessageSchema = z.object({\n type: z.literal(\"start\"),\n sessionId: z.string().min(1),\n attemptId: z.string().min(1),\n sampleRate: z.number().int().positive(),\n encoding: VoiceAsrAudioEncodingSchema,\n});\n\nexport const VoiceAsrStopMessageSchema = z.object({\n type: z.literal(\"stop\"),\n attemptId: z.string().min(1),\n});\n\nexport const VoiceAsrClientMessageSchema = z.discriminatedUnion(\"type\", [\n VoiceAsrStartMessageSchema,\n VoiceAsrStopMessageSchema,\n]);\n\nexport const VoiceAsrServerMessageSchema = z.discriminatedUnion(\"type\", [\n z.object({ type: z.literal(\"ready\"), attemptId: z.string().min(1) }),\n z.object({\n type: z.literal(\"audio_ack\"),\n attemptId: z.string().min(1),\n encodedBytes: z.number().int().nonnegative(),\n pcmBytes: z.number().int().nonnegative(),\n chunks: z.number().int().nonnegative(),\n }),\n z.object({ type: z.literal(\"partial\"), attemptId: z.string().min(1), text: z.string() }),\n z.object({ type: z.literal(\"final\"), attemptId: z.string().min(1), text: z.string() }),\n z.object({\n type: z.literal(\"closed\"),\n attemptId: z.string().min(1),\n code: z.number().int().optional(),\n reason: z.string().optional(),\n }),\n z.object({\n type: z.literal(\"error\"),\n attemptId: z.string().min(1),\n error: z.string().optional(),\n errorCode: z.string().optional(),\n }),\n]);\n\nexport type VoiceAsrAudioEncoding = z.infer<typeof VoiceAsrAudioEncodingSchema>;\nexport type VoiceAsrStartMessage = z.infer<typeof VoiceAsrStartMessageSchema>;\nexport type VoiceAsrStopMessage = z.infer<typeof VoiceAsrStopMessageSchema>;\nexport type VoiceAsrClientMessage = z.infer<typeof VoiceAsrClientMessageSchema>;\nexport type VoiceAsrServerMessage = z.infer<typeof VoiceAsrServerMessageSchema>;\n","import type { MessageEnvelope } from \"../schemas/envelope.js\";\nimport { MessageEnvelopeSchema } from \"../schemas/envelope.js\";\nimport type { RelayControlMessage } from \"../schemas/relay-control.js\";\n\n// 构建经过 schema 验证的消息信封\n// seq 由调用方提供,必须与 EventStore per-session seq 一致,保证 proxy 和 relay 对账可靠\n// sessionId 仅 session-scoped envelope 携带; 全局广播 (session_list / heartbeat / auth /\n// sync_*) 传 null 或省略, 否则要求合法 ID。\nexport function buildMessage<T extends MessageEnvelope[\"type\"]>(\n type: T,\n sessionId: string | null,\n seq: number,\n payload: Extract<MessageEnvelope, { type: T }>[\"payload\"],\n source: \"proxy\" | \"client\",\n): Extract<MessageEnvelope, { type: T }> {\n const envelope = {\n seq,\n ...(sessionId !== null ? { sessionId } : {}),\n type,\n payload,\n timestamp: Date.now(),\n source,\n version: \"1.0\",\n };\n return MessageEnvelopeSchema.parse(envelope) as Extract<MessageEnvelope, { type: T }>;\n}\n\n// 序列化 relay control 消息。借助 RelayControlMessage 这个 discriminated union,\n// 调用方传入 { type, ... } 时 TypeScript 会按 type 反查出该消息允许携带的字段集,\n// 替代之前散落的 sendRaw(JSON.stringify({ type: \"...\", ... })) 模式 —— 后者在编译期\n// 完全没有约束,任何字段拼写错误 / 字段漏填都到 wire 上才暴露。\n//\n// 这里没有再做一次 RelayControlSchema.parse(msg):proxy 在热路径每条 envelope 都要发,\n// 重复 zod 解析对吞吐有压力;编译期类型检查已经能挡住大部分错误。\n// 真要做运行时校验时,调用 RelayControlSchema.parse(msg) 再 stringify 即可。\nexport function serializeControl(msg: RelayControlMessage): string {\n return JSON.stringify(msg);\n}\n","import { z } from \"zod\";\nimport { IdSchema } from \"./id.js\";\nimport { AgentStatusPayloadSchema, PtyStatePayloadSchema, sessionStateValues } from \"./session.js\";\nimport { ToolApprovePayloadSchema, ToolDenyPayloadSchema } from \"./tool.js\";\nimport {\n VoiceCapabilitiesSchema,\n VoiceConfigUpdateSchema,\n VoiceProviderConfigSchema,\n VoiceSummaryReasonSchema,\n voiceRegionValues,\n} from \"./voice.js\";\nimport { RelayErrorCode } from \"../constants/relay-errors.js\";\nimport { ControlErrorCode } from \"../constants/control-errors.js\";\nimport {\n providerValues,\n ptyOwnerValues,\n sessionKindValues,\n sessionModeValues,\n} from \"../constants/enums.js\";\nimport { PTY_INITIAL_MAX_COLS, PTY_INITIAL_MAX_ROWS } from \"../constants/pty.js\";\n\n// 控制消息中复用的子类型\nexport const ProxyInfoSchema = z.object({\n proxyId: IdSchema,\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 RelayClientInfoSchema = z.object({\n clientId: IdSchema,\n proxyId: IdSchema.optional(),\n connectedAt: z.number().int().nonnegative(),\n current: z.boolean().optional(),\n userAgent: z.string().optional(),\n platform: z.string().optional(),\n maxTouchPoints: z.number().int().nonnegative().optional(),\n browserName: z.string().min(1),\n osName: z.string().min(1),\n deviceKind: z.enum([\"desktop\", \"tablet\", \"phone\", \"unknown\"]),\n remoteAddress: z.string().optional(),\n});\nexport type RelayClientInfo = z.infer<typeof RelayClientInfoSchema>;\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(providerValues).optional(),\n preferredMode: z.enum(sessionModeValues).optional(),\n});\nexport type HistorySession = z.infer<typeof HistorySessionSchema>;\n\nconst SessionHistoryPositionShape = {\n timestamp: z.number().optional(),\n cursor: z.string().optional(),\n};\n\nexport const SessionHistoryMessageSchema = z.discriminatedUnion(\"role\", [\n z.object({\n role: z.enum([\"user\", \"assistant\", \"system\"]),\n text: z.string(),\n ...SessionHistoryPositionShape,\n }),\n z.object({\n role: z.literal(\"activity\"),\n text: z.string(),\n toolId: IdSchema,\n toolName: z.string(),\n parameters: z.record(z.string(), z.unknown()).optional(),\n status: z.enum([\"running\", \"done\", \"error\"]),\n ...SessionHistoryPositionShape,\n }),\n]);\nexport type SessionHistoryMessage = z.infer<typeof SessionHistoryMessageSchema>;\n\ntype RelayControlDirection = \"proxy_to_client\" | \"client_to_proxy\";\ntype EmptyShape = Record<never, never>;\nconst RequestIdShape = { requestId: IdSchema.optional() };\nconst RequiredRequestIdShape = { requestId: IdSchema };\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 RemoteFileDispositionSchema = z.enum([\"inline\", \"download\"]);\nconst RemoteFileUploadKindSchema = z.enum([\"clipboard_image\", \"file\"]);\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: IdSchema,\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(\"relay_client_list_request\", RequestIdShape),\n control(\"relay_client_list_response\", {\n ...RequestIdShape,\n clients: z.array(RelayClientInfoSchema),\n }),\n control(\"relay_client_kick\", { ...RequiredRequestIdShape, clientId: IdSchema }),\n control(\"relay_client_kick_response\", {\n ...RequiredRequestIdShape,\n clientId: IdSchema,\n success: z.boolean(),\n ...RequestErrorShape,\n }),\n control(\"relay_client_kicked\", {\n reason: z.string().optional(),\n }),\n control(\"remote_file_url_request\", {\n ...RequiredRequestIdShape,\n sessionId: IdSchema,\n path: z.string().min(1),\n disposition: RemoteFileDispositionSchema,\n }),\n control(\"remote_file_url_response\", {\n ...RequiredRequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\n path: z.string().optional(),\n success: z.boolean(),\n url: z.string().optional(),\n expiresAt: z.number().int().nonnegative().optional(),\n }),\n control(\"remote_file_metadata_request\", {\n ...RequiredRequestIdShape,\n sessionId: IdSchema,\n path: z.string().min(1),\n }),\n control(\"remote_file_metadata_response\", {\n ...RequiredRequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\n path: z.string().optional(),\n success: z.boolean(),\n mimeType: z.string().optional(),\n size: z.number().int().nonnegative().optional(),\n fileName: z.string().optional(),\n }),\n control(\"remote_file_stream_request\", {\n streamId: IdSchema,\n sessionId: IdSchema,\n path: z.string().min(1),\n disposition: RemoteFileDispositionSchema,\n }),\n control(\"remote_file_stream_response\", {\n ...RequestErrorShape,\n streamId: IdSchema,\n sessionId: IdSchema,\n success: z.boolean(),\n path: z.string().optional(),\n mimeType: z.string().optional(),\n size: z.number().int().nonnegative().optional(),\n fileName: z.string().optional(),\n }),\n control(\"remote_file_stream_complete\", {\n ...RequestErrorShape,\n streamId: IdSchema,\n success: z.boolean(),\n }),\n control(\"remote_file_stream_cancel\", {\n streamId: IdSchema,\n }),\n control(\"remote_file_upload_url_request\", {\n ...RequiredRequestIdShape,\n sessionId: IdSchema,\n kind: RemoteFileUploadKindSchema,\n fileName: z.string().optional(),\n mimeType: z.string().min(1),\n size: z.number().int().nonnegative().optional(),\n }),\n control(\"remote_file_upload_url_response\", {\n ...RequiredRequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\n success: z.boolean(),\n uploadUrl: z.string().optional(),\n expiresAt: z.number().int().nonnegative().optional(),\n }),\n control(\"remote_file_upload_stream_request\", {\n uploadId: IdSchema,\n sessionId: IdSchema,\n kind: RemoteFileUploadKindSchema,\n fileName: z.string().optional(),\n mimeType: z.string().min(1),\n size: z.number().int().nonnegative().optional(),\n }),\n control(\"remote_file_upload_stream_complete\", {\n uploadId: IdSchema,\n }),\n control(\"remote_file_upload_stream_cancel\", {\n uploadId: IdSchema,\n }),\n control(\"remote_file_upload_stream_response\", {\n ...RequestErrorShape,\n uploadId: IdSchema,\n sessionId: IdSchema,\n success: z.boolean(),\n path: z.string().optional(),\n }),\n control(\"proxy_select\", { ...RequestIdShape, proxyId: IdSchema }),\n control(\"proxy_select_response\", {\n ...RequestIdShape,\n success: z.boolean(),\n proxyId: IdSchema.optional(),\n ...RequestErrorShape,\n }),\n control(\"relay_error\", {\n code: z.enum(Object.values(RelayErrorCode) as [RelayErrorCode, ...RelayErrorCode[]]),\n message: z.string(),\n // 可选 requestId: relay 把 client 发来 raw 的 requestId 字段透传回来,\n // client 侧 waitForMessage 据此把对应 pending request 立即拒掉而不必等到 timeout。\n requestId: IdSchema.optional(),\n }),\n\n // Voice Pilot config is relay-local: client reads/updates the relay's stored provider settings.\n control(\"voice_config_request\", RequestIdShape),\n control(\"voice_config_response\", {\n ...RequestIdShape,\n ...RequestErrorShape,\n config: VoiceProviderConfigSchema.optional(),\n }),\n control(\"voice_config_update\", {\n ...RequestIdShape,\n config: VoiceConfigUpdateSchema,\n }),\n control(\"voice_config_update_response\", {\n ...RequestIdShape,\n ...RequestErrorShape,\n success: z.boolean(),\n config: VoiceProviderConfigSchema.optional(),\n }),\n control(\"voice_config_test\", {\n ...RequestIdShape,\n config: VoiceConfigUpdateSchema.optional(),\n }),\n control(\"voice_config_test_response\", {\n ...RequestIdShape,\n ...RequestErrorShape,\n success: z.boolean(),\n audioBase64: z.string().optional(),\n audioSampleRate: z.number().int().positive().optional(),\n audioEncoding: z.literal(\"pcm_s16le\").optional(),\n transcript: z.string().optional(),\n }),\n control(\"voice_capabilities_request\", {\n ...RequestIdShape,\n region: z.enum(voiceRegionValues).optional(),\n }),\n control(\"voice_capabilities_response\", {\n ...RequestIdShape,\n ...RequestErrorShape,\n capabilities: VoiceCapabilitiesSchema.optional(),\n }),\n\n // Lightweight latency probes. These measure synthetic round-trip latency for the transport\n // segments and intentionally stay separate from PTY input echo tracing.\n control(\"latency_web_relay_ping\", RequiredRequestIdShape),\n control(\"latency_web_relay_pong\", {\n ...RequiredRequestIdShape,\n relayNow: z.number().optional(),\n }),\n control(\"latency_relay_proxy_request\", RequiredRequestIdShape),\n control(\"latency_relay_proxy_response\", {\n ...RequiredRequestIdShape,\n success: z.boolean(),\n rttMs: z.number().nonnegative().optional(),\n error: z.string().optional(),\n }),\n control(\"latency_relay_proxy_ping\", {\n ...RequiredRequestIdShape,\n relayNow: z.number().optional(),\n }),\n control(\"latency_relay_proxy_pong\", {\n ...RequiredRequestIdShape,\n proxyNow: z.number().optional(),\n }),\n control(\"latency_web_proxy_ping\", RequiredRequestIdShape, \"client_to_proxy\"),\n control(\n \"latency_web_proxy_pong\",\n { ...RequiredRequestIdShape, proxyNow: z.number().optional() },\n \"proxy_to_client\",\n ),\n\n // 客户端注册协议\n control(\"client_register\", {\n clientId: IdSchema,\n userAgent: z.string().optional(),\n platform: z.string().optional(),\n maxTouchPoints: z.number().int().nonnegative().optional(),\n browserName: z.string().min(1),\n osName: z.string().min(1),\n deviceKind: z.enum([\"desktop\", \"tablet\", \"phone\", \"unknown\"]),\n }),\n control(\"client_register_response\", {\n status: z.enum([\"restored\", \"proxy_offline\", \"new\"]),\n proxyId: IdSchema.optional(),\n }),\n\n // Proxy 离线通知\n control(\"proxy_offline\", {\n proxyId: IdSchema,\n }),\n\n // Proxy 主动断开,relay 立即清理资源\n control(\"proxy_disconnect\", {\n proxyId: IdSchema,\n }),\n\n // Proxy 重连后通知 client 恢复\n control(\"proxy_online\", {\n proxyId: IdSchema,\n }),\n\n // 目录列表请求与响应\n control(\n \"dir_list_request\",\n {\n proxyId: IdSchema.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: IdSchema.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(\"pty_state\", { sessionId: IdSchema, payload: PtyStatePayloadSchema }, \"proxy_to_client\"),\n\n // Provider 语义状态,来自 Claude/Codex hook 等结构化事件,不从 PTY 字节推断\n control(\n \"agent_status\",\n { sessionId: IdSchema, payload: AgentStatusPayloadSchema },\n \"proxy_to_client\",\n ),\n\n // 终端标题变化,proxy -> client\n control(\"terminal_title\", { sessionId: IdSchema, title: z.string() }, \"proxy_to_client\"),\n\n // 终端尺寸变化,proxy -> client\n control(\n \"terminal_resize\",\n { sessionId: IdSchema, cols: z.number().int().positive(), rows: z.number().int().positive() },\n \"proxy_to_client\",\n ),\n control(\n \"terminal_resize_request\",\n { sessionId: IdSchema, 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: IdSchema }, \"client_to_proxy\"),\n control(\n \"session_rename\",\n { ...RequestIdShape, sessionId: IdSchema, name: z.string() },\n \"client_to_proxy\",\n ),\n control(\n \"session_rename_response\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\n success: z.boolean(),\n name: z.string().optional(),\n ...RequestErrorShape,\n },\n \"proxy_to_client\",\n ),\n\n // 中断当前 turn,client -> proxy;JSON 由 session-worker 打断 Claude 子进程并保留会话。\n control(\"session_worker_abort\", { sessionId: IdSchema }, \"client_to_proxy\"),\n\n // turn 完成信号,proxy -> client,对应 claude stream-json 的 result 事件\n control(\n \"turn_result\",\n {\n sessionId: IdSchema,\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: IdSchema, data: z.string(), traceId: IdSchema.optional() },\n \"client_to_proxy\",\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(providerValues), 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(providerValues),\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 kind: z.enum(sessionKindValues).optional(),\n cwd: z.string().optional(),\n name: z.string().optional(),\n provider: z.enum(providerValues).optional(),\n mode: z.enum(sessionModeValues).optional(),\n resumeSessionId: z.string().optional(),\n // 仅用于新建 PTY 的一次性初始几何。创建后尺寸由会话端持有,浏览器刷新、\n // 重连或换设备不会隐式重排既有终端内容。\n cols: z.number().int().positive().max(PTY_INITIAL_MAX_COLS).optional(),\n rows: z.number().int().positive().max(PTY_INITIAL_MAX_ROWS).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 // 失败路径只送 errorCode/error, sessionId 此时无语义。成功路径才有 id。\n sessionId: IdSchema.optional(),\n name: z.string().optional(),\n nameLocked: z.boolean().optional(),\n kind: z.enum(sessionKindValues).optional(),\n mode: z.enum(sessionModeValues).optional(),\n provider: z.enum(providerValues).optional(),\n ptyOwner: z.enum(ptyOwnerValues).optional(),\n ...RequestErrorShape,\n },\n \"proxy_to_client\",\n ),\n\n // 客户端请求会话历史消息,client -> proxy\n control(\n \"session_messages_request\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\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: IdSchema },\n \"client_to_proxy\",\n ),\n control(\n \"session_resources_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\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: IdSchema.optional() },\n \"client_to_proxy\",\n ),\n control(\n \"agent_status_response\",\n {\n ...RequestIdShape,\n statuses: z.array(z.object({ sessionId: IdSchema, payload: AgentStatusPayloadSchema })),\n },\n \"proxy_to_client\",\n ),\n\n // 客户端确认已收到审批请求;proxy 只记录送达状态,不把它当成用户决策\n control(\n \"permission_request_delivered\",\n { sessionId: IdSchema, requestId: IdSchema },\n \"client_to_proxy\",\n ),\n control(\n \"tool_approve\",\n { sessionId: IdSchema, payload: ToolApprovePayloadSchema },\n \"client_to_proxy\",\n ),\n control(\"tool_deny\", { sessionId: IdSchema, payload: ToolDenyPayloadSchema }, \"client_to_proxy\"),\n\n // proxy 确认用户决策已进入 provider/worker 路径;web 用它更新审批卡片状态\n control(\n \"permission_decision_result\",\n {\n sessionId: IdSchema,\n requestId: IdSchema,\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: IdSchema,\n approvals: z.array(\n z.object({\n requestId: IdSchema,\n toolName: z.string(),\n input: z.record(z.string(), z.unknown()),\n }),\n ),\n },\n \"proxy_to_client\",\n ),\n\n // Voice Pilot speech summaries are produced by proxy-side Claude Code so it can read project context.\n control(\n \"voice_summary_request\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\n messageId: IdSchema,\n text: z.string().min(1),\n reason: VoiceSummaryReasonSchema,\n },\n \"client_to_proxy\",\n ),\n control(\n \"voice_summary_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\n messageId: IdSchema,\n success: z.boolean(),\n summary: z.string().min(1).optional(),\n },\n \"proxy_to_client\",\n ),\n\n // 恢复会话时推送历史消息,proxy -> client\n control(\n \"session_history_messages\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\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。session_sync 由 relay 自消费(更新 proxy-session\n // 关联)不转发给 client,因此**没有** direction 标注——RelayControlDirection 只描述转发流。\n control(\"session_sync\", {\n sessions: z.array(\n z.object({\n id: z.string(),\n kind: z.enum(sessionKindValues).optional(),\n mode: z.enum(sessionModeValues),\n provider: z.enum(providerValues),\n ptyOwner: z.enum(ptyOwnerValues).optional(),\n cwd: z.string().optional(),\n name: z.string().optional(),\n nameLocked: z.boolean().optional(),\n state: z.enum(sessionStateValues),\n }),\n ),\n }),\n\n // PTY 会话订阅,client -> proxy,触发 terminal serialize() 返回当前状态\n control(\n \"session_subscribe\",\n { sessionId: IdSchema, requestId: IdSchema.optional() },\n \"client_to_proxy\",\n ),\n\n // PTY 会话快照,proxy -> client,serialize() 的全量终端状态\n control(\n \"session_snapshot\",\n {\n sessionId: IdSchema,\n cols: z.number().int().positive(),\n rows: z.number().int().positive(),\n data: z.string(),\n outputSeq: z.number().int().nonnegative(),\n requestId: IdSchema.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","// Session creation is a two-sided deadline contract. The proxy must finish startup and\n// respond before its deadline; the browser waits a little longer so the failure response\n// can cross the relay instead of racing an equal local timeout.\nexport const SESSION_CREATE_SERVER_DEADLINE_MS = 30_000;\nexport const SESSION_CREATE_RESPONSE_GRACE_MS = 5_000;\nexport const SESSION_CREATE_CLIENT_TIMEOUT_MS =\n SESSION_CREATE_SERVER_DEADLINE_MS + SESSION_CREATE_RESPONSE_GRACE_MS;\n","// WebSocket close codes in the private 4000-4999 range.\nexport const RelayCloseCode = {\n CLIENT_KICKED: 4401,\n CLIENT_PROTOCOL_REJECTED: 4402,\n} as const;\n","// WebSocket wire limits shared by relay and proxy.\n//\n// PTY initial snapshots use JSON control messages because xterm serialize()\n// returns ANSI text. A long 5000-line scrollback can legitimately exceed 1 MiB,\n// so the JSON cap must cover terminal snapshots while still bounding parse cost.\nexport const RELAY_JSON_MESSAGE_MAX_BYTES = 8 * 1024 * 1024;\n\n// Raw PTY output and remote-file chunks use binary frames. Keep this aligned\n// with the relay-side frame guard.\nexport const RELAY_BINARY_FRAME_MAX_BYTES = 10 * 1024 * 1024;\n","// PTY binary 帧 wire 格式(跨 proxy / relay / web 三方通用):\n// [1B sessionId_len][sessionId UTF-8][4B outputSeq uint32LE][PTY data]\n// 任一方手写偏移量都极易在协议演进时漂移,统一走这里编/解码避免分叉。\n//\n// 用 Uint8Array 而非 Buffer:shared 包必须可以在浏览器构建里跑,\n// 不能依赖 Node-only 的 Buffer 全局。Node 调用方拿到的也是 Uint8Array\n// (Buffer 是 Uint8Array 的子类,反向兼容),传给 ws.send 等 API 没问题。\n\nexport interface DecodedBinaryFrame {\n sessionId: string;\n outputSeq: number;\n data: Uint8Array;\n}\n\nexport interface DecodedFileStreamFrame {\n streamId: string;\n chunkSeq: number;\n data: Uint8Array;\n}\n\nconst SID_LEN_BYTES = 1;\nconst SEQ_BYTES = 4;\nconst HEADER_FIXED_BYTES = SID_LEN_BYTES + SEQ_BYTES;\nconst FILE_STREAM_MARKER = 0;\nconst FILE_STREAM_ID_LEN_BYTES = 1;\nconst FILE_STREAM_HEADER_FIXED_BYTES = 1 /* marker */ + FILE_STREAM_ID_LEN_BYTES + SEQ_BYTES;\n\nexport function encodeBinaryFrame(\n sessionId: string,\n outputSeq: number,\n data: Uint8Array,\n): Uint8Array {\n const sidBytes = new TextEncoder().encode(sessionId);\n if (sidBytes.length === 0 || sidBytes.length > 255) {\n throw new RangeError(\n `sessionId byte length must be 1-255, got ${sidBytes.length} (sessionId=${sessionId})`,\n );\n }\n if (!Number.isInteger(outputSeq) || outputSeq < 0 || outputSeq > 0xffffffff) {\n throw new RangeError(`outputSeq must be a uint32, got ${outputSeq}`);\n }\n\n const frame = new Uint8Array(SID_LEN_BYTES + sidBytes.length + SEQ_BYTES + data.length);\n frame[0] = sidBytes.length;\n frame.set(sidBytes, SID_LEN_BYTES);\n const seqOffset = SID_LEN_BYTES + sidBytes.length;\n // little-endian 与 Node 端 Buffer.writeUInt32LE / 浏览器 DataView setUint32(_, _, true) 保持一致\n new DataView(frame.buffer, frame.byteOffset + seqOffset, SEQ_BYTES).setUint32(0, outputSeq, true);\n frame.set(data, seqOffset + SEQ_BYTES);\n return frame;\n}\n\n// 解码失败一律返回 null,调用方按业务诉求决定是丢帧还是关连接。\nexport function decodeBinaryFrame(view: Uint8Array): DecodedBinaryFrame | null {\n if (view.length < SID_LEN_BYTES + SEQ_BYTES) return null;\n const sidLen = view[0];\n if (sidLen === 0) return null;\n if (view.length < SID_LEN_BYTES + sidLen + SEQ_BYTES) return null;\n\n const sessionId = new TextDecoder().decode(view.subarray(SID_LEN_BYTES, SID_LEN_BYTES + sidLen));\n const seqOffset = SID_LEN_BYTES + sidLen;\n const outputSeq = new DataView(view.buffer, view.byteOffset + seqOffset, SEQ_BYTES).getUint32(\n 0,\n true,\n );\n const data = view.subarray(seqOffset + SEQ_BYTES);\n return { sessionId, outputSeq, data };\n}\n\n// 帧头长度(不含 payload data):调用方在做 chunk 拆分 / 估算缓冲时用得到。\nexport function binaryFrameHeaderLength(sessionId: string): number {\n const sidBytes = new TextEncoder().encode(sessionId);\n return HEADER_FIXED_BYTES + sidBytes.length;\n}\n\nexport function encodeFileStreamFrame(\n streamId: string,\n chunkSeq: number,\n data: Uint8Array,\n): Uint8Array {\n const streamIdBytes = new TextEncoder().encode(streamId);\n if (streamIdBytes.length === 0 || streamIdBytes.length > 255) {\n throw new RangeError(\n `streamId byte length must be 1-255, got ${streamIdBytes.length} (streamId=${streamId})`,\n );\n }\n if (!Number.isInteger(chunkSeq) || chunkSeq < 0 || chunkSeq > 0xffffffff) {\n throw new RangeError(`chunkSeq must be a uint32, got ${chunkSeq}`);\n }\n\n const frame = new Uint8Array(\n 1 + FILE_STREAM_ID_LEN_BYTES + streamIdBytes.length + SEQ_BYTES + data.length,\n );\n frame[0] = FILE_STREAM_MARKER;\n frame[1] = streamIdBytes.length;\n frame.set(streamIdBytes, 1 + FILE_STREAM_ID_LEN_BYTES);\n const seqOffset = 1 + FILE_STREAM_ID_LEN_BYTES + streamIdBytes.length;\n new DataView(frame.buffer, frame.byteOffset + seqOffset, SEQ_BYTES).setUint32(0, chunkSeq, true);\n frame.set(data, seqOffset + SEQ_BYTES);\n return frame;\n}\n\nexport function decodeFileStreamFrame(view: Uint8Array): DecodedFileStreamFrame | null {\n if (view.length < FILE_STREAM_HEADER_FIXED_BYTES) return null;\n if (view[0] !== FILE_STREAM_MARKER) return null;\n const streamIdLen = view[1];\n if (streamIdLen === 0) return null;\n if (view.length < 1 + FILE_STREAM_ID_LEN_BYTES + streamIdLen + SEQ_BYTES) return null;\n\n const streamId = new TextDecoder().decode(\n view.subarray(1 + FILE_STREAM_ID_LEN_BYTES, 1 + FILE_STREAM_ID_LEN_BYTES + streamIdLen),\n );\n const seqOffset = 1 + FILE_STREAM_ID_LEN_BYTES + streamIdLen;\n const chunkSeq = new DataView(view.buffer, view.byteOffset + seqOffset, SEQ_BYTES).getUint32(\n 0,\n true,\n );\n const data = view.subarray(seqOffset + SEQ_BYTES);\n return { streamId, chunkSeq, data };\n}\n\nexport function fileStreamFrameHeaderLength(streamId: string): number {\n const streamIdBytes = new TextEncoder().encode(streamId);\n return FILE_STREAM_HEADER_FIXED_BYTES + streamIdBytes.length;\n}\n","const MU_LAW_BIAS = 0x84;\nconst MU_LAW_CLIP = 32635;\n\nfunction encodeSample(sampleValue: number): number {\n let sample = sampleValue;\n const sign = sample < 0 ? 0x80 : 0;\n if (sample < 0) sample = -sample;\n sample = Math.min(sample, MU_LAW_CLIP) + MU_LAW_BIAS;\n\n let exponent = 7;\n for (let mask = 0x4000; exponent > 0 && (sample & mask) === 0; mask >>= 1) {\n exponent -= 1;\n }\n const mantissa = (sample >> (exponent + 3)) & 0x0f;\n return ~(sign | (exponent << 4) | mantissa) & 0xff;\n}\n\nfunction decodeSample(encodedValue: number): number {\n const value = ~encodedValue & 0xff;\n const sign = value & 0x80;\n const exponent = (value >> 4) & 0x07;\n const mantissa = value & 0x0f;\n const magnitude = ((mantissa << 3) + MU_LAW_BIAS) * 2 ** exponent - MU_LAW_BIAS;\n return sign ? -magnitude : magnitude;\n}\n\nexport function encodePcm16ToMuLaw(pcm: Uint8Array): Uint8Array {\n const sampleCount = Math.floor(pcm.byteLength / 2);\n const encoded = new Uint8Array(sampleCount);\n const view = new DataView(pcm.buffer, pcm.byteOffset, pcm.byteLength);\n for (let index = 0; index < sampleCount; index += 1) {\n encoded[index] = encodeSample(view.getInt16(index * 2, true));\n }\n return encoded;\n}\n\nexport function decodeMuLawToPcm16(encoded: Uint8Array): Uint8Array {\n const pcm = new Uint8Array(encoded.byteLength * 2);\n const view = new DataView(pcm.buffer);\n for (let index = 0; index < encoded.byteLength; index += 1) {\n view.setInt16(index * 2, decodeSample(encoded[index] ?? 0xff), true);\n }\n return pcm;\n}\n","// 有限状态机 helper\n//\n// 提供显式转换表的小型 FSM,区分两类调用模式:\n// - transitionTo(throw):同步确定性流程里调用,非法转移代表 bug,立即暴露\n// - tryTransitionTo(bool):异步事件回调里调用,非法转移可能是吸收态残余事件,调用方按\n// isInAbsorbingState() 分级日志\n//\n// 吸收态采用传递闭包定义:终态(transitions=[])或所有出边都指向吸收态的状态都算吸收。\n// 这样 SessionState.ERROR (→[TERMINATED]) 和 RelayConnectionState.CLOSED ([]) 都被\n// 自动识别为吸收态,不用在 caller 里硬编码状态名。\n//\n// onTransition/onRejected 仅做日志/观测,不应 throw。\n\ninterface FSMDef<S extends string> {\n initial: S;\n // from-state → 允许转入的 to-state 列表;终态对应空数组\n transitions: Record<S, readonly S[]>;\n // 合法转换发生后触发,典型用法是结构化日志\n onTransition?: (from: S, to: S) => void;\n // tryTransitionTo 非法转移时触发;isAbsorbing 指示 from 是否吸收态(晚到残余 vs. 真非法)\n onRejected?: (from: S, to: S, isAbsorbing: boolean) => void;\n}\n\ninterface FSM<S extends string> {\n current(): S;\n is(state: S): boolean;\n isIn(states: readonly S[]): boolean;\n canTransitionTo(to: S): boolean;\n // 非法转换抛 Error;同步流程里当 assert 用\n transitionTo(to: S): void;\n // 非法转换返回 false,不抛;异步回调里用,配合 isInAbsorbingState 分级日志\n tryTransitionTo(to: S): boolean;\n // 当前是否在吸收态(传递闭包)\n isInAbsorbingState(): boolean;\n}\n\n// 计算吸收态集合:终态 + 所有出边都指向吸收态的状态,迭代至不动点\nfunction computeAbsorbingSet<S extends string>(transitions: Record<S, readonly S[]>): Set<S> {\n const absorbing = new Set<S>();\n const entries = Object.entries(transitions) as Array<[S, readonly S[]]>;\n for (const [s, outs] of entries) {\n if (outs.length === 0) absorbing.add(s);\n }\n let changed = true;\n while (changed) {\n changed = false;\n for (const [s, outs] of entries) {\n if (absorbing.has(s)) continue;\n if (outs.length > 0 && outs.every((t) => absorbing.has(t))) {\n absorbing.add(s);\n changed = true;\n }\n }\n }\n return absorbing;\n}\n\nexport function createFSM<S extends string>(def: FSMDef<S>): FSM<S> {\n let state = def.initial;\n const absorbing = computeAbsorbingSet(def.transitions);\n const tryTransitionTo = (to: S): boolean => {\n const allowed = def.transitions[state];\n if (!allowed?.includes(to)) {\n def.onRejected?.(state, to, absorbing.has(state));\n return false;\n }\n const from = state;\n state = to;\n def.onTransition?.(from, to);\n return true;\n };\n return {\n current: () => state,\n is: (s) => state === s,\n isIn: (ss) => ss.includes(state),\n canTransitionTo: (to) => def.transitions[state]?.includes(to) ?? false,\n transitionTo: (to) => {\n if (!tryTransitionTo(to)) {\n throw new Error(`Invalid FSM transition: ${state} -> ${to}`);\n }\n },\n tryTransitionTo,\n isInAbsorbingState: () => absorbing.has(state),\n };\n}\n\n// 无内部 state 的 FSM 视图,供 state 存在外部(如 SessionInfo、DB 行)的 per-instance 场景使用。\n// 调用方自行传入 from,canTransition 校验;吸收态判定通过 isAbsorbing(state) 提供。\ninterface StatelessFSM<S extends string> {\n canTransition(from: S, to: S): boolean;\n isAbsorbing(state: S): boolean;\n}\n\nexport function defineFSM<S extends string>(transitions: Record<S, readonly S[]>): StatelessFSM<S> {\n const absorbing = computeAbsorbingSet(transitions);\n return {\n canTransition: (from, to) => transitions[from]?.includes(to) ?? false,\n isAbsorbing: (state) => absorbing.has(state),\n };\n}\n","import { Router } from \"express\";\nimport type { RelayRegistry } from \"./registry.js\";\nimport { RELAY_VERSION } from \"./version.js\";\n\ninterface HealthRouterOptions {\n proxyTokenRequired?: boolean;\n clientTokenRequired?: boolean;\n validateClientToken?: (token: string | null) => boolean;\n validateProxyToken?: (token: string | null) => boolean;\n // proxyToken 验证通过且 client token 已配置时返回当前值;否则返回 null。\n getClientToken?: () => string | null;\n}\n\nfunction bearerToken(authHeader: string | undefined): string | null {\n const match = /^Bearer\\s+(.+)$/i.exec(authHeader ?? \"\");\n return match?.[1] ?? null;\n}\n\n// 健康检查和状态查询路由\nexport function healthRouter(registry: RelayRegistry, options: HealthRouterOptions = {}): Router {\n const router = Router();\n const proxyTokenRequired = options.proxyTokenRequired ?? false;\n const clientTokenRequired = options.clientTokenRequired ?? false;\n\n router.get(\"/health\", (_req, res) => {\n res.json({\n status: \"ok\",\n version: RELAY_VERSION,\n uptime: process.uptime(),\n auth: {\n proxyTokenRequired,\n clientTokenRequired,\n },\n });\n });\n\n router.get(\"/api/auth/client\", (req, res) => {\n if (!clientTokenRequired) {\n res.status(204).end();\n return;\n }\n const token = bearerToken(req.get(\"authorization\"));\n if (options.validateClientToken?.(token)) {\n res.status(204).end();\n return;\n }\n res.status(401).json({ error: \"invalid_client_token\" });\n });\n\n // 已认证 proxy(凭 proxyToken)查询当前生效的 client token,避免运维者必须 ssh 上来读 .env。\n // 行为:proxy token 关闭时直接 401(防止开放 relay 公开 token);proxy token 不匹配 401;\n // 校验通过且 client token 已配置返回 { clientToken };client token 未配置返回 204。\n router.get(\"/api/admin/client-token\", (req, res) => {\n if (!proxyTokenRequired) {\n res.status(401).json({ error: \"proxy_token_required\" });\n return;\n }\n const token = bearerToken(req.get(\"authorization\"));\n if (!options.validateProxyToken?.(token)) {\n res.status(401).json({ error: \"invalid_proxy_token\" });\n return;\n }\n const clientToken = options.getClientToken?.() ?? null;\n if (!clientToken) {\n res.status(204).end();\n return;\n }\n res.json({ clientToken });\n });\n\n // proxyToken 配置时需要 Bearer 校验,否则放行(dev 模式)。\n // /api/status、/api/proxies、/api/clients 暴露拓扑/绑定信息,公网 relay 必须鉴权防遍历探测。\n function requireProxyTokenIfConfigured(\n req: import(\"express\").Request,\n res: import(\"express\").Response,\n ): boolean {\n if (!proxyTokenRequired) return true;\n const token = bearerToken(req.get(\"authorization\"));\n if (options.validateProxyToken?.(token)) return true;\n res.status(401).json({ error: \"invalid_proxy_token\" });\n return false;\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 if (!requireProxyTokenIfConfigured(req, res)) return;\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 if (!requireProxyTokenIfConfigured(req, res)) return;\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 if (!requireProxyTokenIfConfigured(req, res)) return;\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 {\n isProxyToClientRelayControlType,\n RELAY_BINARY_FRAME_MAX_BYTES,\n RelayErrorCode,\n RELAY_JSON_MESSAGE_MAX_BYTES,\n serializeControl,\n} from \"@dev-anywhere/shared\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { RelayRegistry } from \"../registry.js\";\nimport { parseMessage, routeProxyMessage } from \"../router.js\";\nimport type { RelayChaos } from \"../chaos.js\";\nimport { completeRelayProxyLatencyProbe } from \"../latency-probes.js\";\nimport type { RemoteFileBridge } from \"../remote-file-bridge.js\";\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\nfunction rejectNotRegistered(ws: ProxySocket): void {\n ws.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.NOT_REGISTERED,\n message: \"Proxy must register before sending messages\",\n }),\n );\n}\n\n// 处理代理端 WebSocket 连接生命周期\nexport function handleProxyConnection(\n ws: WebSocket,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n remoteFileBridge?: RemoteFileBridge,\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 > RELAY_BINARY_FRAME_MAX_BYTES) {\n logger.warn({ size: data.length }, \"Binary frame rejected: invalid size\");\n return;\n }\n if (proxyWs.proxyId && remoteFileBridge?.handleProxyBinary(proxyWs.proxyId, data)) {\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 if (data.length > RELAY_JSON_MESSAGE_MAX_BYTES) {\n logger.warn(\n { size: data.length, proxyId: proxyWs.proxyId },\n \"JSON message rejected: exceeds max size\",\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 serializeControl({\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.unregisterProxy(proxyWs.proxyId);\n logger.info(\n { proxyId: proxyWs.proxyId },\n \"Proxy gracefully disconnected, resources cleaned\",\n );\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;\n registry.setSessionsForProxy(\n proxyWs.proxyId,\n sessions.map((s) => s.id),\n );\n logger.info({ proxyId: proxyWs.proxyId, count: sessions.length }, \"Session sync received\");\n return;\n }\n\n if (result.kind === \"control\" && result.message.type === \"latency_relay_proxy_pong\") {\n if (!proxyWs.proxyId) {\n rejectNotRegistered(proxyWs);\n return;\n }\n const completed = completeRelayProxyLatencyProbe({\n proxyId: proxyWs.proxyId,\n requestId: result.message.requestId,\n logger,\n });\n if (!completed) {\n logger.debug(\n { proxyId: proxyWs.proxyId, requestId: result.message.requestId },\n \"Unmatched relay-proxy latency pong ignored\",\n );\n }\n return;\n }\n\n // proxy 发给 client 的控制消息:直接转发给绑定的客户端,不进 session buffer\n if (result.kind === \"control\") {\n if (proxyWs.proxyId && result.message.type === \"remote_file_stream_response\") {\n remoteFileBridge?.handleProxyControl(proxyWs.proxyId, result.message);\n return;\n }\n if (proxyWs.proxyId && result.message.type === \"remote_file_metadata_response\") {\n remoteFileBridge?.handleProxyControl(proxyWs.proxyId, result.message);\n return;\n }\n if (proxyWs.proxyId && result.message.type === \"remote_file_stream_complete\") {\n remoteFileBridge?.handleProxyControl(proxyWs.proxyId, result.message);\n return;\n }\n if (proxyWs.proxyId && result.message.type === \"remote_file_upload_stream_response\") {\n remoteFileBridge?.handleProxyControl(proxyWs.proxyId, result.message);\n return;\n }\n if (isProxyToClientRelayControlType(result.message.type)) {\n if (!proxyWs.proxyId) {\n rejectNotRegistered(proxyWs);\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 rejectNotRegistered(proxyWs);\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\", (code: number, reason: Buffer) => {\n if (!proxyWs.proxyId) return;\n const closeMeta = { code, reason: reason.toString() || undefined };\n // 同 proxyId 重连场景:registerProxy 会 terminate 旧 ws、把 registry 指向新 ws,\n // 旧 ws 的 close 异步触发到达这里时,registry.getProxy(proxyId) 已是新 ws 实例。\n // 此时若仍执行 transitionProxy(\"online\", \"offline\"),会把新连接的状态翻回离线并广播一次假离线。\n // 仅当 registry 当前持有的 ws 仍是我们自己(或 entry 已被 proxy_disconnect 清掉)时才走离线流程。\n const current = registry.getProxy(proxyWs.proxyId);\n if (current && current !== proxyWs) {\n logger.info(\n { proxyId: proxyWs.proxyId, ...closeMeta },\n \"Old proxy ws closed after being superseded by reconnect, skipping offline transition\",\n );\n return;\n }\n notifyClientsProxyOffline(proxyWs.proxyId, registry, logger, chaos);\n try {\n registry.transitionProxy(proxyWs.proxyId, \"online\", \"offline\");\n } catch (err) {\n // 期望路径: proxy_disconnect 已清理 entry, 或 entry 已 offline——transitionProxy 抛\n // \"Proxy not found\" / state mismatch。debug 级别记录 err 以便真有 FSM bug 时能定位。\n logger.debug(\n { proxyId: proxyWs.proxyId, err: String(err) },\n \"transitionProxy on close skipped\",\n );\n }\n logger.info(\n { proxyId: proxyWs.proxyId, ...closeMeta },\n \"Proxy disconnected, state preserved for reconnect\",\n );\n broadcastProxyList(registry, chaos);\n });\n\n proxyWs.on(\"error\", (err) => {\n logger.error({ err, proxyId: proxyWs.proxyId }, \"Proxy WebSocket error\");\n });\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/logger\";\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// 从 raw JSON 抓 requestId, 用来给 invalid message 拼回 relay_error 时填充——这样\n// client waitForMessage 能用 requestId 立即拒对应的 pending Promise, 不必等 timeout。\nfunction extractRequestIdFromRaw(raw: string): string | undefined {\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\" && typeof parsed.requestId === \"string\") {\n return parsed.requestId;\n }\n } catch {\n // raw 不是合法 JSON, 直接没 requestId\n }\n return undefined;\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\n // session-scoped envelope 在 proxy 与 sessionId 间建立关联; 全局广播 (session_list /\n // heartbeat / auth / sync_*) 没有 sessionId, 直接广播给所有 client。\n if (\"sessionId\" in message) {\n registry.addSessionToProxy(proxyId, message.sessionId);\n }\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 // 把拒绝原因带 requestId 发回 client, 让 waitForMessage 立刻拒 pending Promise。\n // 不发回的话, 调用方只看到 \"上传超时\" / \"请求超时\" 这种泛用 timeout, 不知道\n // 实际是 schema 不认。常见触发场景: 客户端有新协议, relay 在旧 build 上还没认。\n const requestId = extractRequestIdFromRaw(raw);\n clientWs.send(\n JSON.stringify({\n type: \"relay_error\",\n code: RelayErrorCode.INVALID_MESSAGE,\n message: result.error,\n ...(requestId ? { requestId } : {}),\n }),\n );\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 { performance } from \"node:perf_hooks\";\nimport { WebSocket } from \"ws\";\nimport { serializeControl, type ControlMessage } from \"@dev-anywhere/shared\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { RelayChaos } from \"./chaos.js\";\n\nconst DEFAULT_RELAY_PROXY_PROBE_TIMEOUT_MS = 3_000;\n\ninterface RelayProxyProbe {\n proxyId: string;\n requestId: string;\n clientWs: WebSocket;\n startedAt: number;\n timer: ReturnType<typeof setTimeout>;\n chaos?: RelayChaos;\n}\n\nconst pendingRelayProxyProbes = new Map<string, RelayProxyProbe>();\n\nfunction keyFor(proxyId: string, requestId: string): string {\n return `${proxyId}:${requestId}`;\n}\n\nfunction sendClientResponse(\n clientWs: WebSocket,\n response: ControlMessage<\"latency_relay_proxy_response\">,\n chaos?: RelayChaos,\n): void {\n if (clientWs.readyState !== WebSocket.OPEN) return;\n const raw = serializeControl(response);\n if (chaos) {\n chaos.send(clientWs, raw, {\n direction: \"proxy_to_client\",\n type: \"latency_relay_proxy_response\",\n });\n return;\n }\n clientWs.send(raw);\n}\n\nexport function startRelayProxyLatencyProbe({\n requestId,\n proxyId,\n proxyWs,\n clientWs,\n logger,\n chaos,\n timeoutMs = DEFAULT_RELAY_PROXY_PROBE_TIMEOUT_MS,\n}: {\n requestId: string;\n proxyId: string;\n proxyWs: WebSocket;\n clientWs: WebSocket;\n logger: Logger;\n chaos?: RelayChaos;\n timeoutMs?: number;\n}): void {\n const key = keyFor(proxyId, requestId);\n const existing = pendingRelayProxyProbes.get(key);\n if (existing) {\n clearTimeout(existing.timer);\n pendingRelayProxyProbes.delete(key);\n }\n\n const startedAt = performance.now();\n const timer = setTimeout(() => {\n pendingRelayProxyProbes.delete(key);\n sendClientResponse(\n clientWs,\n {\n type: \"latency_relay_proxy_response\",\n requestId,\n success: false,\n error: \"Relay 到开发机测速超时\",\n },\n chaos,\n );\n logger.warn({ proxyId, requestId }, \"Relay-proxy latency probe timed out\");\n }, timeoutMs);\n\n pendingRelayProxyProbes.set(key, {\n proxyId,\n requestId,\n clientWs,\n startedAt,\n timer,\n chaos,\n });\n\n const raw = serializeControl({\n type: \"latency_relay_proxy_ping\",\n requestId,\n relayNow: Date.now(),\n });\n if (chaos) {\n chaos.send(proxyWs, raw, { direction: \"client_to_proxy\", type: \"latency_relay_proxy_ping\" });\n return;\n }\n proxyWs.send(raw);\n}\n\nexport function completeRelayProxyLatencyProbe({\n proxyId,\n requestId,\n logger,\n}: {\n proxyId: string;\n requestId: string;\n logger: Logger;\n}): boolean {\n const key = keyFor(proxyId, requestId);\n const pending = pendingRelayProxyProbes.get(key);\n if (!pending) return false;\n\n pendingRelayProxyProbes.delete(key);\n clearTimeout(pending.timer);\n const rttMs = performance.now() - pending.startedAt;\n\n sendClientResponse(\n pending.clientWs,\n {\n type: \"latency_relay_proxy_response\",\n requestId,\n success: true,\n rttMs,\n },\n pending.chaos,\n );\n logger.debug({ proxyId, requestId, rttMs }, \"Relay-proxy latency probe completed\");\n return true;\n}\n","import { WebSocket } from \"ws\";\nimport {\n ControlErrorCode,\n isClientToProxyRelayControlType,\n RelayCloseCode,\n RelayErrorCode,\n RELAY_JSON_MESSAGE_MAX_BYTES,\n serializeControl,\n type ControlErrorCodeType,\n} from \"@dev-anywhere/shared\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { RelayRegistry } from \"../registry.js\";\nimport { parseMessage, routeClientMessage } from \"../router.js\";\nimport type { RelayChaos } from \"../chaos.js\";\nimport { handleVoiceConfigControl } from \"../voice/client-controls.js\";\nimport type { VoiceConfigStore } from \"../voice/config-store.js\";\nimport type { VoiceProviderRegistry } from \"../voice/provider.js\";\nimport { startRelayProxyLatencyProbe } from \"../latency-probes.js\";\nimport type { RemoteFileBridge } from \"../remote-file-bridge.js\";\n\n// 扩展 WebSocket 实例存储客户端元数据\ninterface ClientSocket extends WebSocket {\n isAlive: boolean;\n clientId?: string;\n boundProxyId?: string;\n}\n\ninterface ClientConnectionInfo {\n userAgent?: string;\n remoteAddress?: string;\n}\n\ninterface ClientRegisterInfo {\n clientId: string;\n userAgent?: string;\n platform?: string;\n maxTouchPoints?: number;\n browserName: string;\n osName: string;\n deviceKind: \"desktop\" | \"tablet\" | \"phone\" | \"unknown\";\n}\n\nfunction isMalformedClientRegister(raw: string): boolean {\n try {\n const parsed = JSON.parse(raw) as { type?: unknown } | null;\n return parsed !== null && typeof parsed === \"object\" && parsed.type === \"client_register\";\n } catch {\n return false;\n }\n}\n\n// 处理 client_register 消息:三种状态 restored / proxy_offline / new。\n// relay 不缓存输出;恢复由 proxy 重新推送 session_list/agent_status/snapshot 等状态。\nfunction handleClientRegister(\n registration: ClientRegisterInfo,\n clientWs: ClientSocket,\n registry: RelayRegistry,\n logger: Logger,\n): void {\n const { clientId } = registration;\n clientWs.clientId = clientId;\n registry.updateConnectedClientMetadata(clientWs, {\n clientId,\n ...(registration.userAgent !== undefined ? { userAgent: registration.userAgent } : {}),\n ...(registration.platform !== undefined ? { platform: registration.platform } : {}),\n ...(registration.maxTouchPoints !== undefined\n ? { maxTouchPoints: registration.maxTouchPoints }\n : {}),\n browserName: registration.browserName,\n osName: registration.osName,\n deviceKind: registration.deviceKind,\n });\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\nfunction handleRelayClientListRequest(\n clientWs: ClientSocket,\n registry: RelayRegistry,\n requestId: string | undefined,\n): void {\n clientWs.send(\n JSON.stringify({\n type: \"relay_client_list_response\",\n requestId,\n clients: registry.getConnectedClientDetails(clientWs.clientId),\n }),\n );\n}\n\nfunction handleRelayClientKick(\n clientWs: ClientSocket,\n registry: RelayRegistry,\n logger: Logger,\n requestId: string,\n targetClientId: string,\n): void {\n if (targetClientId === clientWs.clientId) {\n clientWs.send(\n JSON.stringify({\n type: \"relay_client_kick_response\",\n requestId,\n clientId: targetClientId,\n success: false,\n errorCode: ControlErrorCode.UNKNOWN,\n error: \"不能断开当前客户端\",\n }),\n );\n return;\n }\n\n const targets = registry.getConnectedClientSockets(targetClientId);\n if (targets.length === 0) {\n clientWs.send(\n JSON.stringify({\n type: \"relay_client_kick_response\",\n requestId,\n clientId: targetClientId,\n success: false,\n errorCode: ControlErrorCode.UNKNOWN,\n error: \"客户端不在线\",\n }),\n );\n return;\n }\n\n const kickedMessage = JSON.stringify({\n type: \"relay_client_kicked\",\n reason: \"由客户端管理断开\",\n });\n for (const target of targets) {\n try {\n target.send(kickedMessage);\n target.close(RelayCloseCode.CLIENT_KICKED, \"client kicked\");\n } catch (err) {\n logger.warn({ err, clientId: targetClientId }, \"Failed to close kicked client\");\n target.terminate();\n } finally {\n registry.removeClientWs(target);\n registry.unbindClientById(targetClientId);\n }\n }\n\n clientWs.send(\n JSON.stringify({\n type: \"relay_client_kick_response\",\n requestId,\n clientId: targetClientId,\n success: true,\n }),\n );\n logger.info(\n { byClientId: clientWs.clientId, targetClientId, targetCount: targets.length },\n \"Relay client kicked\",\n );\n}\n\nfunction rejectNotBound(ws: ClientSocket): void {\n ws.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}\n\nfunction rejectNotRegistered(ws: ClientSocket, requestId: string | undefined): void {\n ws.send(\n JSON.stringify({\n type: \"relay_error\",\n requestId,\n code: RelayErrorCode.NOT_REGISTERED,\n message: \"Client must register before selecting a proxy\",\n }),\n );\n}\n\nfunction closeRejectedClientProtocol(ws: ClientSocket): void {\n ws.close(RelayCloseCode.CLIENT_PROTOCOL_REJECTED, \"client protocol rejected\");\n}\n\nfunction rejectProxySelect(ws: ClientSocket, requestId: string | undefined, proxyId: string): void {\n ws.send(\n JSON.stringify({\n type: \"proxy_select_response\",\n requestId,\n success: false,\n errorCode: ControlErrorCode.PROXY_OFFLINE,\n error: `Proxy not online: ${proxyId}`,\n }),\n );\n}\n\nfunction sendRelayProxyProbeFailure(\n ws: ClientSocket,\n requestId: string,\n error: string,\n chaos?: RelayChaos,\n): void {\n const response = serializeControl({\n type: \"latency_relay_proxy_response\",\n requestId,\n success: false,\n error,\n });\n if (chaos) {\n chaos.send(ws, response, {\n direction: \"proxy_to_client\",\n type: \"latency_relay_proxy_response\",\n });\n return;\n }\n ws.send(response);\n}\n\nfunction sendRemoteFileUrlFailure(\n ws: ClientSocket,\n requestId: string,\n sessionId: string,\n error: string,\n errorCode: ControlErrorCodeType = ControlErrorCode.UNKNOWN,\n): void {\n ws.send(\n serializeControl({\n type: \"remote_file_url_response\",\n requestId,\n sessionId,\n success: false,\n error,\n errorCode,\n }),\n );\n}\n\nfunction sendRemoteFileUploadUrlFailure(\n ws: ClientSocket,\n requestId: string,\n sessionId: string,\n error: string,\n errorCode: ControlErrorCodeType = ControlErrorCode.UNKNOWN,\n): void {\n ws.send(\n serializeControl({\n type: \"remote_file_upload_url_response\",\n requestId,\n sessionId,\n success: false,\n error,\n errorCode,\n }),\n );\n}\n\n// 处理远程客户端 WebSocket 连接生命周期\nexport function handleClientConnection(\n ws: WebSocket,\n registry: RelayRegistry,\n logger: Logger,\n chaos?: RelayChaos,\n voiceConfigStore?: VoiceConfigStore,\n voiceProviders?: VoiceProviderRegistry,\n remoteFileBridge?: RemoteFileBridge,\n connectionInfo: ClientConnectionInfo = {},\n): void {\n const clientWs = ws as ClientSocket;\n clientWs.isAlive = true;\n registry.addClientWs(clientWs, connectionInfo);\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 if (data.length > RELAY_JSON_MESSAGE_MAX_BYTES) {\n logger.warn(\n { size: data.length, clientId: clientWs.clientId },\n \"JSON message rejected: exceeds max size\",\n );\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, clientWs, registry, logger);\n return;\n }\n\n if (msg.type === \"relay_client_list_request\") {\n handleRelayClientListRequest(clientWs, registry, msg.requestId);\n return;\n }\n\n if (msg.type === \"relay_client_kick\") {\n handleRelayClientKick(clientWs, registry, logger, msg.requestId, msg.clientId);\n return;\n }\n\n if (msg.type === \"remote_file_url_request\") {\n if (!remoteFileBridge) {\n sendRemoteFileUrlFailure(clientWs, msg.requestId, msg.sessionId, \"文件流服务不可用\");\n return;\n }\n if (!clientWs.clientId) {\n sendRemoteFileUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n \"客户端未注册\",\n ControlErrorCode.UNKNOWN,\n );\n return;\n }\n const targetProxyId = clientWs.boundProxyId;\n if (!targetProxyId || !registry.isProxyOnline(targetProxyId)) {\n sendRemoteFileUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n \"当前未连接开发机\",\n ControlErrorCode.PROXY_OFFLINE,\n );\n return;\n }\n const ownerProxyId = registry.getProxyForSession(msg.sessionId);\n if (ownerProxyId && ownerProxyId !== targetProxyId) {\n sendRemoteFileUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n \"会话不属于当前开发机\",\n ControlErrorCode.SESSION_NOT_FOUND,\n );\n return;\n }\n void remoteFileBridge\n .createUrl({\n clientId: clientWs.clientId,\n proxyId: targetProxyId,\n sessionId: msg.sessionId,\n path: msg.path,\n disposition: msg.disposition,\n })\n .then((result) => {\n if (clientWs.readyState !== WebSocket.OPEN) return;\n if (!result.success) {\n sendRemoteFileUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n result.error,\n result.errorCode,\n );\n return;\n }\n clientWs.send(\n serializeControl({\n type: \"remote_file_url_response\",\n requestId: msg.requestId,\n sessionId: msg.sessionId,\n path: result.path,\n success: true,\n url: result.url,\n expiresAt: result.expiresAt,\n }),\n );\n })\n .catch((err: unknown) => {\n if (clientWs.readyState !== WebSocket.OPEN) return;\n sendRemoteFileUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n err instanceof Error ? err.message : String(err),\n );\n });\n return;\n }\n\n if (msg.type === \"remote_file_upload_url_request\") {\n if (!remoteFileBridge) {\n sendRemoteFileUploadUrlFailure(clientWs, msg.requestId, msg.sessionId, \"上传服务不可用\");\n return;\n }\n if (!clientWs.clientId) {\n sendRemoteFileUploadUrlFailure(clientWs, msg.requestId, msg.sessionId, \"客户端未注册\");\n return;\n }\n const targetProxyId = clientWs.boundProxyId;\n if (!targetProxyId || !registry.isProxyOnline(targetProxyId)) {\n sendRemoteFileUploadUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n \"当前未连接开发机\",\n ControlErrorCode.PROXY_OFFLINE,\n );\n return;\n }\n const ownerProxyId = registry.getProxyForSession(msg.sessionId);\n if (ownerProxyId && ownerProxyId !== targetProxyId) {\n sendRemoteFileUploadUrlFailure(\n clientWs,\n msg.requestId,\n msg.sessionId,\n \"会话不属于当前开发机\",\n ControlErrorCode.SESSION_NOT_FOUND,\n );\n return;\n }\n const { uploadUrl, expiresAt } = remoteFileBridge.createUploadUrl({\n clientId: clientWs.clientId,\n proxyId: targetProxyId,\n sessionId: msg.sessionId,\n kind: msg.kind,\n fileName: msg.fileName,\n mimeType: msg.mimeType,\n size: msg.size,\n });\n clientWs.send(\n serializeControl({\n type: \"remote_file_upload_url_response\",\n requestId: msg.requestId,\n sessionId: msg.sessionId,\n success: true,\n uploadUrl,\n expiresAt,\n }),\n );\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 if (msg.type === \"latency_web_relay_ping\") {\n const response = serializeControl({\n type: \"latency_web_relay_pong\",\n requestId: msg.requestId,\n relayNow: Date.now(),\n });\n if (chaos) {\n chaos.send(clientWs, response, {\n direction: \"proxy_to_client\",\n type: \"latency_web_relay_pong\",\n });\n } else {\n clientWs.send(response);\n }\n return;\n }\n\n if (msg.type === \"latency_relay_proxy_request\") {\n const targetProxyId = clientWs.boundProxyId;\n if (!targetProxyId) {\n sendRelayProxyProbeFailure(clientWs, msg.requestId, \"当前未连接开发机\", chaos);\n return;\n }\n const proxyWs = registry.getProxy(targetProxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) {\n sendRelayProxyProbeFailure(\n clientWs,\n msg.requestId,\n `开发机 ${targetProxyId} 不在线`,\n chaos,\n );\n return;\n }\n startRelayProxyLatencyProbe({\n requestId: msg.requestId,\n proxyId: targetProxyId,\n proxyWs,\n clientWs,\n logger,\n chaos,\n });\n return;\n }\n\n if (\n voiceConfigStore &&\n handleVoiceConfigControl(msg, clientWs, voiceConfigStore, logger, voiceProviders)\n ) {\n return;\n }\n\n // client → proxy 透传:relay 不处理内容,直接转发给绑定的 proxy。\n // 路由 key 永远是 clientWs.boundProxyId, 不能被消息字段里 client 自填的 proxyId 覆盖\n // (那条路径让绑到 p1 的 client 通过 dir_list_request{proxyId:\"p2\"} 读到别的 proxy 的目录)。\n if (isClientToProxyRelayControlType(msg.type)) {\n const targetProxyId = clientWs.boundProxyId;\n if (!targetProxyId) {\n rejectNotBound(clientWs);\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 (!clientWs.clientId) {\n rejectNotRegistered(clientWs, msg.requestId);\n closeRejectedClientProtocol(clientWs);\n return;\n }\n if (!registry.isProxyOnline(msg.proxyId)) {\n rejectProxySelect(clientWs, msg.requestId, msg.proxyId);\n return;\n }\n const bound = registry.bindClientById(clientWs.clientId, msg.proxyId, clientWs);\n if (!bound) {\n rejectProxySelect(clientWs, msg.requestId, msg.proxyId);\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 rejectNotBound(clientWs);\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 if (isMalformedClientRegister(raw)) {\n closeRejectedClientProtocol(clientWs);\n }\n });\n\n clientWs.on(\"close\", (code: number, reason: Buffer) => {\n registry.removeClientWs(clientWs);\n // 清掉 binding.ws 引用:保留绑定关系(重连时还能恢复 proxyId 关联),但释放对已关闭 ws 对象的强引用,\n // 避免高频重连下 clientBindings Map 长期持有死 ws 对象阻止 GC,同时让 countClients 数字不再虚高。\n if (clientWs.clientId) {\n registry.unbindClientSocket(clientWs.clientId, clientWs);\n }\n logger.info(\n {\n clientId: clientWs.clientId,\n boundProxyId: clientWs.boundProxyId,\n code,\n reason: reason.toString() || undefined,\n },\n \"Client disconnected\",\n );\n });\n\n clientWs.on(\"error\", (err) => {\n logger.error({ err }, \"Client WebSocket error\");\n });\n}\n","import { EventEmitter } from \"node:events\";\nimport { nanoid } from \"nanoid\";\nimport { WebSocket } from \"ws\";\nimport { bailianRealtimeUrl, type BailianRegion } from \"./bailian-endpoints.js\";\n\ntype SocketOptions = { headers?: Record<string, string> };\ntype ProviderSocket = EventEmitter & {\n readyState: number;\n send: (data: string | Buffer) => void;\n close: (code?: number, reason?: string) => void;\n};\n\ntype BailianAsrSocketFactory = (url: string, options: SocketOptions) => ProviderSocket;\n\nexport interface BailianAsrConfig {\n apiKey: string;\n region: BailianRegion;\n model: string;\n sampleRate: number;\n language: string;\n}\n\nexport interface BailianAsrClient {\n on(event: \"ready\", handler: () => void): this;\n on(event: \"partial\" | \"final\", handler: (text: string) => void): this;\n on(event: \"error\", handler: (error: Error) => void): this;\n on(event: \"closed\", handler: (code?: number, reason?: string) => void): this;\n sendPcm(chunk: Buffer): void;\n stop(): void;\n close(): void;\n}\n\ninterface BailianAsrClientOptions {\n socketFactory?: BailianAsrSocketFactory;\n eventIdFactory?: () => string;\n}\n\nconst OPEN = 1;\nconst END_OF_SPEECH_SILENCE_MS = 1200;\n// Voice Pilot already uses an aggressive client-side VAD. Keep the provider-side\n// gate less sensitive so sustained household noise is not transcribed as speech.\nconst NOISE_RESISTANT_SERVER_VAD_THRESHOLD = 0.8;\n\nfunction defaultSocketFactory(url: string, options: SocketOptions): ProviderSocket {\n return new WebSocket(url, options) as ProviderSocket;\n}\n\nfunction extractRealtimePreview(payload: unknown): string | null {\n if (!payload || typeof payload !== \"object\") return null;\n const record = payload as Record<string, unknown>;\n if (typeof record.text === \"string\" || typeof record.stash === \"string\") {\n const text = typeof record.text === \"string\" ? record.text : \"\";\n const stash = typeof record.stash === \"string\" ? record.stash : \"\";\n const preview = `${text}${stash}`;\n return preview.length > 0 ? preview : null;\n }\n const candidates = [\n record.text,\n record.transcript,\n record.delta,\n record.output &&\n typeof record.output === \"object\" &&\n (record.output as Record<string, unknown>).text,\n ];\n for (const candidate of candidates) {\n if (typeof candidate === \"string\" && candidate.length > 0) return candidate;\n }\n return null;\n}\n\nfunction extractFinalText(payload: unknown): string | null {\n if (!payload || typeof payload !== \"object\") return null;\n const record = payload as Record<string, unknown>;\n const candidates = [\n record.transcript,\n record.text,\n record.output &&\n typeof record.output === \"object\" &&\n (record.output as Record<string, unknown>).text,\n ];\n for (const candidate of candidates) {\n if (typeof candidate === \"string\" && candidate.length > 0) return candidate;\n }\n return null;\n}\n\nfunction extractError(payload: unknown): Error {\n if (!payload || typeof payload !== \"object\") return new Error(\"Bailian ASR error\");\n const record = payload as Record<string, unknown>;\n const nested = record.error && typeof record.error === \"object\" ? record.error : null;\n let message = \"Bailian ASR error\";\n if (nested && typeof (nested as Record<string, unknown>).message === \"string\") {\n message = (nested as Record<string, string>).message;\n } else if (typeof record.message === \"string\") {\n message = record.message;\n }\n return new Error(message);\n}\n\nclass BailianAsrClientImpl extends EventEmitter implements BailianAsrClient {\n private socket: ProviderSocket;\n private isOpen = false;\n private isReady = false;\n private hasFinalTranscript = false;\n private pending: string[] = [];\n\n constructor(\n private readonly config: BailianAsrConfig,\n socketFactory: BailianAsrSocketFactory,\n private readonly eventIdFactory: () => string,\n ) {\n super();\n this.socket = socketFactory(bailianRealtimeUrl(config.region, config.model), {\n headers: {\n Authorization: `bearer ${config.apiKey}`,\n \"OpenAI-Beta\": \"realtime=v1\",\n },\n });\n this.socket.on(\"open\", () => this.handleOpen());\n this.socket.on(\"message\", (data: unknown) => this.handleMessage(data));\n this.socket.on(\"error\", (err: unknown) =>\n this.emit(\"error\", err instanceof Error ? err : new Error(String(err))),\n );\n this.socket.on(\"close\", (code?: number, reason?: Buffer) =>\n this.emit(\"closed\", code, reason?.toString(\"utf8\")),\n );\n }\n\n sendPcm(chunk: Buffer): void {\n this.sendWhenReady({\n event_id: this.eventIdFactory(),\n type: \"input_audio_buffer.append\",\n audio: chunk.toString(\"base64\"),\n });\n }\n\n stop(): void {\n this.sendWhenReady({\n event_id: this.eventIdFactory(),\n type: \"session.finish\",\n });\n }\n\n close(): void {\n this.socket.close();\n }\n\n private handleOpen(): void {\n this.isOpen = true;\n this.sendNow({\n event_id: this.eventIdFactory(),\n type: \"session.update\",\n session: {\n modalities: [\"text\"],\n input_audio_format: \"pcm\",\n sample_rate: this.config.sampleRate,\n input_audio_transcription: {\n language: this.config.language,\n },\n turn_detection: {\n type: \"server_vad\",\n threshold: NOISE_RESISTANT_SERVER_VAD_THRESHOLD,\n silence_duration_ms: END_OF_SPEECH_SILENCE_MS,\n },\n },\n });\n }\n\n private handleMessage(data: unknown): void {\n const text =\n typeof data === \"string\" ? data : Buffer.isBuffer(data) ? data.toString(\"utf8\") : \"\";\n if (!text) return;\n let payload: unknown;\n try {\n payload = JSON.parse(text);\n } catch {\n return;\n }\n const record = payload as { type?: unknown };\n const type = typeof record.type === \"string\" ? record.type : \"\";\n if (type.includes(\"error\")) {\n this.emit(\"error\", extractError(payload));\n return;\n }\n if (type === \"conversation.item.input_audio_transcription.failed\") {\n this.emit(\"error\", extractError(payload));\n return;\n }\n if (type === \"session.updated\") {\n this.isReady = true;\n this.emit(\"ready\");\n this.flushPending();\n return;\n }\n if (type === \"conversation.item.input_audio_transcription.text\") {\n const preview = extractRealtimePreview(payload);\n if (preview) this.emit(\"partial\", preview);\n return;\n }\n if (type === \"conversation.item.input_audio_transcription.completed\") {\n const transcript = extractFinalText(payload);\n if (transcript) {\n this.hasFinalTranscript = true;\n this.emit(\"final\", transcript);\n }\n return;\n }\n if (type === \"session.finished\") {\n const transcript = extractFinalText(payload);\n if (transcript && !this.hasFinalTranscript) {\n this.hasFinalTranscript = true;\n this.emit(\"final\", transcript);\n }\n this.socket.close(1000, \"session-finished\");\n }\n }\n\n private sendWhenReady(payload: unknown): void {\n const message = JSON.stringify(payload);\n if ((this.isOpen || this.socket.readyState === OPEN) && this.isReady) {\n this.socket.send(message);\n return;\n }\n this.pending.push(message);\n }\n\n private sendNow(payload: unknown): void {\n this.socket.send(JSON.stringify(payload));\n }\n\n private flushPending(): void {\n for (const message of this.pending) {\n this.socket.send(message);\n }\n this.pending = [];\n }\n}\n\nexport function createBailianAsrClient(\n config: BailianAsrConfig,\n options: BailianAsrClientOptions = {},\n): BailianAsrClient {\n return new BailianAsrClientImpl(\n config,\n options.socketFactory ?? defaultSocketFactory,\n options.eventIdFactory ?? (() => `event_${nanoid()}`),\n );\n}\n","export type BailianRegion = \"cn\" | \"intl\";\n\nconst BAILIAN_HOSTS: Record<BailianRegion, string> = {\n cn: \"wss://dashscope.aliyuncs.com\",\n intl: \"wss://dashscope-intl.aliyuncs.com\",\n};\n\nexport function bailianRealtimeUrl(region: BailianRegion, model?: string): string {\n const url = `${BAILIAN_HOSTS[region]}/api-ws/v1/realtime`;\n return model ? `${url}?model=${encodeURIComponent(model)}` : url;\n}\n\nexport function bailianInferenceUrl(region: BailianRegion): string {\n return `${BAILIAN_HOSTS[region]}/api-ws/v1/inference`;\n}\n","import { EventEmitter } from \"node:events\";\nimport { nanoid } from \"nanoid\";\nimport { WebSocket } from \"ws\";\nimport { bailianInferenceUrl, type BailianRegion } from \"./bailian-endpoints.js\";\n\ntype SocketOptions = { headers?: Record<string, string> };\ntype ProviderSocket = EventEmitter & {\n readyState: number;\n send: (data: string | Buffer) => void;\n close: (code?: number, reason?: string) => void;\n};\n\ntype BailianTtsSocketFactory = (url: string, options: SocketOptions) => ProviderSocket;\n\nexport interface BailianTtsConfig {\n apiKey: string;\n region: BailianRegion;\n model: string;\n voice: string;\n sampleRate: number;\n}\n\nexport interface BailianTtsClient {\n on(event: \"started\" | \"finished\", handler: () => void): this;\n on(event: \"audio\", handler: (chunk: Buffer) => void): this;\n on(event: \"error\", handler: (error: Error) => void): this;\n on(event: \"closed\", handler: (code?: number, reason?: string) => void): this;\n speak(text: string): void;\n close(): void;\n}\n\ninterface BailianTtsClientOptions {\n socketFactory?: BailianTtsSocketFactory;\n taskIdFactory?: () => string;\n}\n\nconst OPEN = 1;\n\nfunction defaultSocketFactory(url: string, options: SocketOptions): ProviderSocket {\n return new WebSocket(url, options) as ProviderSocket;\n}\n\nfunction errorFromPayload(payload: unknown): Error {\n if (!payload || typeof payload !== \"object\") return new Error(\"Bailian TTS error\");\n const record = payload as Record<string, unknown>;\n const header = record.header && typeof record.header === \"object\" ? record.header : null;\n let message = \"Bailian TTS error\";\n if (header && typeof (header as Record<string, unknown>).error_message === \"string\") {\n message = (header as Record<string, string>).error_message;\n } else if (typeof record.message === \"string\") {\n message = record.message;\n }\n return new Error(message);\n}\n\nfunction eventFromPayload(payload: unknown): string {\n if (!payload || typeof payload !== \"object\") return \"\";\n const record = payload as Record<string, unknown>;\n const header = record.header && typeof record.header === \"object\" ? record.header : null;\n const event = header ? (header as Record<string, unknown>).event : undefined;\n return typeof event === \"string\" ? event : \"\";\n}\n\nclass BailianTtsClientImpl extends EventEmitter implements BailianTtsClient {\n private socket: ProviderSocket;\n private isOpen = false;\n private current: { taskId: string; text: string } | null = null;\n\n constructor(\n private readonly config: BailianTtsConfig,\n socketFactory: BailianTtsSocketFactory,\n private readonly taskIdFactory: () => string,\n ) {\n super();\n this.socket = socketFactory(bailianInferenceUrl(config.region), {\n headers: { Authorization: `bearer ${config.apiKey}` },\n });\n this.socket.on(\"open\", () => this.handleOpen());\n this.socket.on(\"message\", (data: unknown, isBinary?: boolean) =>\n this.handleMessage(data, isBinary),\n );\n this.socket.on(\"error\", (err: unknown) =>\n this.emit(\"error\", err instanceof Error ? err : new Error(String(err))),\n );\n this.socket.on(\"close\", (code?: number, reason?: Buffer) =>\n this.emit(\"closed\", code, reason?.toString(\"utf8\")),\n );\n }\n\n speak(text: string): void {\n if (this.current) {\n throw new Error(\"Bailian TTS is already speaking\");\n }\n this.current = { taskId: this.taskIdFactory(), text };\n if (this.isOpen || this.socket.readyState === OPEN) {\n this.sendRunTask();\n }\n }\n\n close(): void {\n this.socket.close();\n }\n\n private handleOpen(): void {\n this.isOpen = true;\n if (this.current) this.sendRunTask();\n }\n\n private handleMessage(data: unknown, isBinary = false): void {\n const text = this.tryDecodeText(data, isBinary);\n if (!text) {\n const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data as ArrayBuffer);\n this.emit(\"audio\", chunk);\n return;\n }\n let payload: unknown;\n try {\n payload = JSON.parse(text);\n } catch {\n const chunk = Buffer.isBuffer(data) ? data : Buffer.from(text);\n this.emit(\"audio\", chunk);\n return;\n }\n\n const event = eventFromPayload(payload);\n if (event === \"task-started\") {\n this.emit(\"started\");\n this.sendTextAndFinish();\n return;\n }\n if (event === \"task-finished\") {\n this.emit(\"finished\");\n this.current = null;\n return;\n }\n if (event === \"task-failed\" || event === \"task-error\") {\n this.emit(\"error\", errorFromPayload(payload));\n this.current = null;\n }\n }\n\n private tryDecodeText(data: unknown, isBinary: boolean): string | null {\n if (typeof data === \"string\") return data;\n if (isBinary) return null;\n if (Buffer.isBuffer(data)) return data.toString(\"utf8\");\n return null;\n }\n\n private sendRunTask(): void {\n if (!this.current) return;\n this.socket.send(\n JSON.stringify({\n header: {\n action: \"run-task\",\n task_id: this.current.taskId,\n streaming: \"duplex\",\n },\n payload: {\n task_group: \"audio\",\n task: \"tts\",\n function: \"SpeechSynthesizer\",\n model: this.config.model,\n input: {},\n parameters: {\n text_type: \"PlainText\",\n voice: this.config.voice,\n format: \"pcm\",\n sample_rate: this.config.sampleRate,\n },\n },\n }),\n );\n }\n\n private sendTextAndFinish(): void {\n if (!this.current) return;\n this.socket.send(\n JSON.stringify({\n header: { action: \"continue-task\", task_id: this.current.taskId },\n payload: { input: { text: this.current.text } },\n }),\n );\n this.socket.send(\n JSON.stringify({\n header: { action: \"finish-task\", task_id: this.current.taskId },\n payload: { input: {} },\n }),\n );\n }\n}\n\nexport function createBailianTtsClient(\n config: BailianTtsConfig,\n options: BailianTtsClientOptions = {},\n): BailianTtsClient {\n return new BailianTtsClientImpl(\n config,\n options.socketFactory ?? defaultSocketFactory,\n options.taskIdFactory ?? (() => nanoid()),\n );\n}\n","import waveResampler from \"wave-resampler\";\n\nconst RESAMPLE_DETAILS = {\n method: \"sinc\",\n LPF: true,\n LPFType: \"FIR\",\n} as const;\n\nexport function resampleMonoPcmS16le(\n audio: Buffer,\n inputSampleRate: number,\n outputSampleRate: number,\n): Buffer {\n if (audio.byteLength % 2 !== 0) {\n throw new Error(\"PCM 音频数据长度无效\");\n }\n if (!validSampleRate(inputSampleRate) || !validSampleRate(outputSampleRate)) {\n throw new Error(\"PCM 音频采样率无效\");\n }\n if (inputSampleRate === outputSampleRate) return Buffer.from(audio);\n\n const input = new Int16Array(audio.byteLength / 2);\n for (let index = 0; index < input.length; index += 1) {\n input[index] = audio.readInt16LE(index * 2);\n }\n\n const samples = waveResampler.resample(\n input,\n inputSampleRate,\n outputSampleRate,\n RESAMPLE_DETAILS,\n );\n const output = Buffer.alloc(samples.length * 2);\n for (let index = 0; index < samples.length; index += 1) {\n const sample = samples[index];\n if (typeof sample !== \"number\" || !Number.isFinite(sample)) {\n throw new Error(\"PCM 音频重采样失败\");\n }\n output.writeInt16LE(Math.max(-32768, Math.min(32767, Math.round(sample))), index * 2);\n }\n return output;\n}\n\nfunction validSampleRate(sampleRate: number): boolean {\n return Number.isSafeInteger(sampleRate) && sampleRate > 0;\n}\n","import type { VoiceConfigUpdate } from \"@dev-anywhere/shared\";\nimport {\n createBailianAsrClient,\n type BailianAsrClient,\n type BailianAsrConfig,\n} from \"./bailian-asr.js\";\nimport {\n createBailianTtsClient,\n type BailianTtsClient,\n type BailianTtsConfig,\n} from \"./bailian-tts.js\";\nimport type { StoredVoiceConfig } from \"./config-store.js\";\nimport { resampleMonoPcmS16le } from \"./pcm-resample.js\";\n\nexport interface VoiceConfigTestResult {\n audio?: Buffer;\n sampleRate?: number;\n transcript?: string;\n}\n\nexport interface VoiceConfigTester {\n test: (config: StoredVoiceConfig) => Promise<VoiceConfigTestResult>;\n}\n\ninterface BailianVoiceConfigTesterOptions {\n ttsClientFactory?: (config: BailianTtsConfig) => BailianTtsClient;\n asrClientFactory?: (config: BailianAsrConfig) => BailianAsrClient;\n sampleText?: string;\n timeoutMs?: number;\n}\n\nconst TTS_TEST_SAMPLE_RATE = 24000;\nconst ASR_TEST_SAMPLE_RATE = 16000;\nconst ASR_TEST_CHUNK_BYTES = 3200;\nconst ASR_TEST_CHUNK_INTERVAL_MS = 100;\n\nexport function mergeVoiceConfigForTest(\n current: StoredVoiceConfig,\n update?: VoiceConfigUpdate,\n): StoredVoiceConfig {\n return {\n ...current,\n provider: \"aliyun-bailian\",\n ...(update?.clearApiKey ? { apiKey: undefined } : {}),\n ...(update?.apiKey ? { apiKey: update.apiKey } : {}),\n ...(update?.region ? { region: update.region } : {}),\n ...(update?.asrModel ? { asrModel: update.asrModel } : {}),\n ...(update?.ttsModel ? { ttsModel: update.ttsModel } : {}),\n ...(update?.ttsVoice ? { ttsVoice: update.ttsVoice } : {}),\n ...(update?.turnIdleSeconds ? { turnIdleSeconds: update.turnIdleSeconds } : {}),\n };\n}\n\nexport function createBailianVoiceConfigTester(\n options: BailianVoiceConfigTesterOptions = {},\n): VoiceConfigTester {\n const ttsClientFactory = options.ttsClientFactory ?? createBailianTtsClient;\n const asrClientFactory = options.asrClientFactory ?? createBailianAsrClient;\n const sampleText = options.sampleText ?? \"语音助手测试\";\n const timeoutMs = options.timeoutMs ?? 8000;\n\n return {\n async test(config) {\n if (!config.apiKey) {\n return Promise.reject(new Error(\"请先填写阿里云百炼 API Key\"));\n }\n const audio = await synthesizeTestAudio({\n config,\n sampleText,\n timeoutMs,\n clientFactory: ttsClientFactory,\n });\n const recognitionAudio = resampleMonoPcmS16le(\n audio,\n TTS_TEST_SAMPLE_RATE,\n ASR_TEST_SAMPLE_RATE,\n );\n const transcript = await recognizeTestAudio({\n config,\n audio: recognitionAudio,\n sampleText,\n timeoutMs,\n clientFactory: asrClientFactory,\n });\n return { audio, sampleRate: TTS_TEST_SAMPLE_RATE, transcript };\n },\n };\n}\n\nfunction synthesizeTestAudio(options: {\n config: StoredVoiceConfig;\n sampleText: string;\n timeoutMs: number;\n clientFactory: (config: BailianTtsConfig) => BailianTtsClient;\n}): Promise<Buffer> {\n const { config, sampleText, timeoutMs, clientFactory } = options;\n const client = clientFactory({\n apiKey: config.apiKey!,\n region: config.region,\n model: config.ttsModel,\n voice: config.ttsVoice,\n sampleRate: TTS_TEST_SAMPLE_RATE,\n });\n\n return new Promise<Buffer>((resolve, reject) => {\n let settled = false;\n const chunks: Buffer[] = [];\n const timer = setTimeout(() => {\n settle(new Error(\"TTS 测试超时\"));\n }, timeoutMs);\n\n function settle(error?: Error, audio?: Buffer): void {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n client.close();\n if (error) reject(error);\n else resolve(audio ?? Buffer.alloc(0));\n }\n\n client.on(\"audio\", (chunk) => {\n if (chunk.length > 0) chunks.push(chunk);\n });\n client.on(\"finished\", () => {\n if (chunks.length === 0) {\n settle(new Error(\"TTS 测试没有返回音频\"));\n return;\n }\n settle(undefined, Buffer.concat(chunks));\n });\n client.on(\"error\", (error) => settle(error));\n client.on(\"closed\", () => {\n if (!settled) settle(new Error(\"TTS 测试连接已关闭\"));\n });\n\n try {\n client.speak(sampleText);\n } catch (err) {\n settle(err instanceof Error ? err : new Error(\"TTS 测试启动失败\"));\n }\n });\n}\n\nfunction recognizeTestAudio(options: {\n config: StoredVoiceConfig;\n audio: Buffer;\n sampleText: string;\n timeoutMs: number;\n clientFactory: (config: BailianAsrConfig) => BailianAsrClient;\n}): Promise<string> {\n const { config, audio, sampleText, timeoutMs, clientFactory } = options;\n const client = clientFactory({\n apiKey: config.apiKey!,\n region: config.region,\n model: config.asrModel,\n sampleRate: ASR_TEST_SAMPLE_RATE,\n language: \"zh\",\n });\n\n return new Promise<string>((resolve, reject) => {\n let settled = false;\n let streamTimer: ReturnType<typeof setTimeout> | null = null;\n const timer = setTimeout(() => {\n settle(new Error(\"STT 测试超时\"));\n }, timeoutMs);\n\n function settle(error?: Error, transcript?: string): void {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n if (streamTimer) clearTimeout(streamTimer);\n client.close();\n if (error) reject(error);\n else resolve(transcript ?? \"\");\n }\n\n client.on(\"ready\", () => {\n let offset = 0;\n const sendNextChunk = () => {\n if (settled) return;\n const chunk = audio.subarray(offset, offset + ASR_TEST_CHUNK_BYTES);\n if (chunk.length > 0) {\n client.sendPcm(chunk);\n offset += chunk.length;\n }\n if (offset >= audio.length) {\n client.stop();\n return;\n }\n streamTimer = setTimeout(sendNextChunk, ASR_TEST_CHUNK_INTERVAL_MS);\n };\n sendNextChunk();\n });\n client.on(\"final\", (transcript) => {\n if (matchesExpectedTranscript(transcript, sampleText)) {\n settle(undefined, transcript);\n return;\n }\n settle(new Error(`STT 测试识别结果不匹配:${transcript}`));\n });\n client.on(\"error\", (error) => settle(error));\n client.on(\"closed\", () => {\n if (!settled) settle(new Error(\"STT 测试连接已关闭\"));\n });\n });\n}\n\nfunction matchesExpectedTranscript(actual: string, expected: string): boolean {\n return normalizeTranscript(actual).includes(normalizeTranscript(expected));\n}\n\nfunction normalizeTranscript(text: string): string {\n return text.replace(/[^\\p{Script=Han}a-zA-Z0-9]/gu, \"\").toLowerCase();\n}\n","import { ControlErrorCode, type RelayControlMessage } from \"@dev-anywhere/shared\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { WebSocket } from \"ws\";\nimport type { VoiceConfigStore } from \"./config-store.js\";\nimport { mergeVoiceConfigForTest } from \"./config-test.js\";\nimport type { VoiceProviderRegistry } from \"./provider.js\";\n\nexport function handleVoiceConfigControl(\n msg: RelayControlMessage,\n ws: WebSocket,\n store: VoiceConfigStore,\n logger: Logger,\n providers?: VoiceProviderRegistry,\n): boolean {\n if (msg.type === \"voice_config_request\") {\n ws.send(\n JSON.stringify({\n type: \"voice_config_response\",\n requestId: msg.requestId,\n config: store.read(),\n }),\n );\n return true;\n }\n\n if (msg.type === \"voice_config_update\") {\n try {\n const config = store.update(msg.config);\n ws.send(\n JSON.stringify({\n type: \"voice_config_update_response\",\n requestId: msg.requestId,\n success: true,\n config,\n }),\n );\n } catch (err) {\n logger.warn({ err }, \"Voice config update failed\");\n ws.send(\n JSON.stringify({\n type: \"voice_config_update_response\",\n requestId: msg.requestId,\n success: false,\n errorCode: ControlErrorCode.UNKNOWN,\n error: err instanceof Error ? err.message : \"Voice config update failed\",\n }),\n );\n }\n return true;\n }\n\n if (msg.type === \"voice_capabilities_request\") {\n if (!providers) {\n ws.send(\n JSON.stringify({\n type: \"voice_capabilities_response\",\n requestId: msg.requestId,\n errorCode: ControlErrorCode.UNKNOWN,\n error: \"Voice capabilities provider is not available\",\n }),\n );\n return true;\n }\n\n const config = { ...store.readSecret(), ...(msg.region ? { region: msg.region } : {}) };\n let provider;\n try {\n provider = providers.current(config);\n } catch (err) {\n logger.warn({ err }, \"Voice capabilities provider resolution failed\");\n ws.send(\n JSON.stringify({\n type: \"voice_capabilities_response\",\n requestId: msg.requestId,\n errorCode: ControlErrorCode.UNKNOWN,\n error: err instanceof Error ? err.message : \"Voice capabilities request failed\",\n }),\n );\n return true;\n }\n\n void provider\n .readCapabilities(config)\n .then((capabilities) => {\n ws.send(\n JSON.stringify({\n type: \"voice_capabilities_response\",\n requestId: msg.requestId,\n capabilities,\n }),\n );\n })\n .catch((err: unknown) => {\n logger.warn({ err }, \"Voice capabilities request failed\");\n ws.send(\n JSON.stringify({\n type: \"voice_capabilities_response\",\n requestId: msg.requestId,\n errorCode: ControlErrorCode.UNKNOWN,\n error: err instanceof Error ? err.message : \"Voice capabilities request failed\",\n }),\n );\n });\n return true;\n }\n\n if (msg.type === \"voice_config_test\") {\n if (!providers) {\n ws.send(\n JSON.stringify({\n type: \"voice_config_test_response\",\n requestId: msg.requestId,\n success: false,\n errorCode: ControlErrorCode.UNKNOWN,\n error: \"Voice config tester is not available\",\n }),\n );\n return true;\n }\n\n const testConfig = mergeVoiceConfigForTest(store.readSecret(), msg.config);\n let provider;\n try {\n provider = providers.current(testConfig);\n } catch (err) {\n logger.warn({ err }, \"Voice config test provider resolution failed\");\n ws.send(\n JSON.stringify({\n type: \"voice_config_test_response\",\n requestId: msg.requestId,\n success: false,\n errorCode: ControlErrorCode.UNKNOWN,\n error: err instanceof Error ? err.message : \"Voice config test failed\",\n }),\n );\n return true;\n }\n\n void provider\n .testConfig(testConfig)\n .then((result) => {\n ws.send(\n JSON.stringify({\n type: \"voice_config_test_response\",\n requestId: msg.requestId,\n success: true,\n ...(result.audio ? { audioBase64: result.audio.toString(\"base64\") } : {}),\n ...(result.sampleRate ? { audioSampleRate: result.sampleRate } : {}),\n ...(result.audio ? { audioEncoding: \"pcm_s16le\" } : {}),\n ...(result.transcript ? { transcript: result.transcript } : {}),\n }),\n );\n })\n .catch((err: unknown) => {\n logger.warn({ err }, \"Voice config test failed\");\n ws.send(\n JSON.stringify({\n type: \"voice_config_test_response\",\n requestId: msg.requestId,\n success: false,\n errorCode: ControlErrorCode.UNKNOWN,\n error: err instanceof Error ? err.message : \"Voice config test failed\",\n }),\n );\n });\n return true;\n }\n\n return false;\n}\n","import type { WebSocket, WebSocketServer } from \"ws\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\n\ninterface HeartbeatSocket extends WebSocket {\n isAlive?: boolean;\n}\n\ninterface HeartbeatOptions {\n logger?: Pick<Logger, \"warn\">;\n peerType?: string;\n describePeer?: (ws: WebSocket) => Record<string, unknown>;\n}\n\nfunction markAlive(ws: WebSocket): void {\n (ws as HeartbeatSocket).isAlive = true;\n}\n\n// 通用 WebSocket 心跳检测,检测死连接并 terminate\n// terminate 触发 close 事件,后续由各自的 close handler 处理恢复逻辑\nexport function setupHeartbeat(\n wss: WebSocketServer,\n interval = 30000,\n options: HeartbeatOptions = {},\n): NodeJS.Timeout {\n wss.on(\"connection\", (ws) => {\n markAlive(ws);\n ws.on(\"pong\", () => {\n markAlive(ws);\n });\n });\n\n return setInterval(() => {\n for (const ws of wss.clients) {\n const sock = ws as HeartbeatSocket;\n if (sock.isAlive === false) {\n options.logger?.warn(\n {\n peerType: options.peerType,\n ...(options.describePeer?.(ws) ?? {}),\n },\n \"WebSocket heartbeat timeout\",\n );\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/logger\";\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","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport {\n VoiceConfigUpdateSchema,\n VoiceProviderConfigSchema,\n type VoiceConfigUpdate,\n type VoiceProviderConfig,\n} from \"@dev-anywhere/shared\";\n\nexport interface StoredVoiceConfig {\n provider: \"aliyun-bailian\";\n apiKey?: string;\n region: \"cn\" | \"intl\";\n asrModel: string;\n ttsModel: string;\n ttsVoice: string;\n turnIdleSeconds: number;\n}\n\ninterface VoiceConfigStoreOptions {\n dataDir?: string;\n defaults?: Partial<Omit<StoredVoiceConfig, \"provider\" | \"apiKey\">>;\n}\n\nexport interface VoiceConfigStore {\n read: () => VoiceProviderConfig;\n update: (update: VoiceConfigUpdate) => VoiceProviderConfig;\n readSecret: () => StoredVoiceConfig;\n}\n\nconst DEFAULT_STORED_CONFIG: StoredVoiceConfig = {\n provider: \"aliyun-bailian\",\n region: \"cn\",\n asrModel: \"qwen3-asr-flash-realtime\",\n ttsModel: \"cosyvoice-v3-flash\",\n ttsVoice: \"longanyang\",\n turnIdleSeconds: 3,\n};\n\nfunction redacted(config: StoredVoiceConfig): VoiceProviderConfig {\n return VoiceProviderConfigSchema.parse({\n provider: config.provider,\n configured: Boolean(config.apiKey),\n region: config.region,\n asrModel: config.asrModel,\n ttsModel: config.ttsModel,\n ttsVoice: config.ttsVoice,\n turnIdleSeconds: config.turnIdleSeconds,\n });\n}\n\nfunction mergeDefaults(defaults: VoiceConfigStoreOptions[\"defaults\"]): StoredVoiceConfig {\n return {\n ...DEFAULT_STORED_CONFIG,\n ...defaults,\n };\n}\n\nfunction parseStoredConfig(raw: unknown, fallback: StoredVoiceConfig): StoredVoiceConfig {\n if (!raw || typeof raw !== \"object\") return fallback;\n const candidate = raw as Partial<StoredVoiceConfig>;\n return {\n ...fallback,\n provider: \"aliyun-bailian\",\n ...(typeof candidate.apiKey === \"string\" && candidate.apiKey.length > 0\n ? { apiKey: candidate.apiKey }\n : { apiKey: undefined }),\n ...(candidate.region === \"cn\" || candidate.region === \"intl\"\n ? { region: candidate.region }\n : {}),\n ...(typeof candidate.asrModel === \"string\" && candidate.asrModel.length > 0\n ? { asrModel: candidate.asrModel }\n : {}),\n ...(typeof candidate.ttsModel === \"string\" && candidate.ttsModel.length > 0\n ? { ttsModel: candidate.ttsModel }\n : {}),\n ...(typeof candidate.ttsVoice === \"string\" && candidate.ttsVoice.length > 0\n ? { ttsVoice: candidate.ttsVoice }\n : {}),\n ...(typeof candidate.turnIdleSeconds === \"number\" &&\n Number.isSafeInteger(candidate.turnIdleSeconds) &&\n candidate.turnIdleSeconds > 0\n ? { turnIdleSeconds: candidate.turnIdleSeconds }\n : {}),\n };\n}\n\nexport function createVoiceConfigStore(options: VoiceConfigStoreOptions = {}): VoiceConfigStore {\n const fallback = mergeDefaults(options.defaults);\n const filePath = options.dataDir ? join(options.dataDir, \"voice-config.json\") : null;\n let memoryConfig: StoredVoiceConfig = fallback;\n\n function load(): StoredVoiceConfig {\n if (!filePath) return memoryConfig;\n if (!existsSync(filePath)) return fallback;\n try {\n return parseStoredConfig(JSON.parse(readFileSync(filePath, \"utf8\")), fallback);\n } catch {\n return fallback;\n }\n }\n\n function save(config: StoredVoiceConfig): void {\n if (!filePath) {\n memoryConfig = config;\n return;\n }\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, `${JSON.stringify(config, null, 2)}\\n`, { mode: 0o600 });\n }\n\n return {\n read() {\n return redacted(load());\n },\n update(update) {\n const parsed = VoiceConfigUpdateSchema.parse(update);\n const current = load();\n const next: StoredVoiceConfig = {\n ...current,\n provider: \"aliyun-bailian\",\n ...(parsed.clearApiKey ? { apiKey: undefined } : {}),\n ...(parsed.apiKey ? { apiKey: parsed.apiKey } : {}),\n ...(parsed.region ? { region: parsed.region } : {}),\n ...(parsed.asrModel ? { asrModel: parsed.asrModel } : {}),\n ...(parsed.ttsModel ? { ttsModel: parsed.ttsModel } : {}),\n ...(parsed.ttsVoice ? { ttsVoice: parsed.ttsVoice } : {}),\n ...(parsed.turnIdleSeconds ? { turnIdleSeconds: parsed.turnIdleSeconds } : {}),\n };\n save(next);\n return redacted(next);\n },\n readSecret() {\n return load();\n },\n };\n}\n","import { WebSocket, type RawData } from \"ws\";\nimport {\n VoiceAsrClientMessageSchema,\n decodeMuLawToPcm16,\n type VoiceAsrStartMessage,\n} from \"@dev-anywhere/shared\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { BailianAsrClient, BailianAsrConfig } from \"./bailian-asr.js\";\nimport type { StoredVoiceConfig, VoiceConfigStore } from \"./config-store.js\";\nimport type { VoiceAsrProviderClient, VoiceProviderRegistry } from \"./provider.js\";\n\nexport type VoiceAsrClientFactory = (config: BailianAsrConfig) => BailianAsrClient;\n\ninterface ActiveAsrStats {\n sessionId: string;\n attemptId: string;\n sampleRate: number;\n provider: StoredVoiceConfig[\"provider\"];\n region: StoredVoiceConfig[\"region\"];\n asrModel: string;\n encoding: VoiceAsrStartMessage[\"encoding\"];\n startedAt: number;\n readyAt: number | null;\n firstPcmAt: number | null;\n encodedBytes: number;\n pcmBytes: number;\n pcmChunks: number;\n partialEvents: number;\n finalEvents: number;\n finalChars: number;\n}\n\nfunction sendJson(ws: WebSocket, payload: unknown): void {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(payload));\n }\n}\n\nfunction toBuffer(data: RawData): Buffer {\n if (Buffer.isBuffer(data)) return data;\n if (data instanceof ArrayBuffer) return Buffer.from(data);\n return Buffer.concat(data);\n}\n\nfunction parseJson(data: RawData): unknown | null {\n try {\n return JSON.parse(toBuffer(data).toString(\"utf8\"));\n } catch {\n return null;\n }\n}\n\nfunction buildStats(payload: VoiceAsrStartMessage, config: StoredVoiceConfig): ActiveAsrStats {\n return {\n sessionId: payload.sessionId,\n attemptId: payload.attemptId,\n sampleRate: payload.sampleRate,\n provider: config.provider,\n region: config.region,\n asrModel: config.asrModel,\n encoding: payload.encoding,\n startedAt: Date.now(),\n readyAt: null,\n firstPcmAt: null,\n encodedBytes: 0,\n pcmBytes: 0,\n pcmChunks: 0,\n partialEvents: 0,\n finalEvents: 0,\n finalChars: 0,\n };\n}\n\nfunction statsLogFields(stats: ActiveAsrStats): Record<string, unknown> {\n const now = Date.now();\n return {\n sessionId: stats.sessionId,\n attemptId: stats.attemptId,\n sampleRate: stats.sampleRate,\n provider: stats.provider,\n region: stats.region,\n asrModel: stats.asrModel,\n encoding: stats.encoding,\n encodedBytes: stats.encodedBytes,\n pcmBytes: stats.pcmBytes,\n pcmChunks: stats.pcmChunks,\n partialEvents: stats.partialEvents,\n finalEvents: stats.finalEvents,\n finalChars: stats.finalChars,\n durationMs: now - stats.startedAt,\n readyMs: stats.readyAt === null ? null : stats.readyAt - stats.startedAt,\n firstPcmMs: stats.firstPcmAt === null ? null : stats.firstPcmAt - stats.startedAt,\n };\n}\n\nexport function handleVoiceAsrConnection(\n ws: WebSocket,\n store: VoiceConfigStore,\n logger: Logger,\n providers: VoiceProviderRegistry,\n): void {\n let provider: VoiceAsrProviderClient | null = null;\n let activeStats: ActiveAsrStats | null = null;\n\n function isCurrent(\n candidate: VoiceAsrProviderClient,\n stats: ActiveAsrStats,\n event: string,\n ): boolean {\n if (provider === candidate && activeStats === stats) return true;\n logger.debug(\n { attemptId: stats.attemptId, activeAttemptId: activeStats?.attemptId ?? null, event },\n \"Ignored stale Voice ASR provider event\",\n );\n return false;\n }\n\n function detachProvider(reason: string): void {\n const previousProvider = provider;\n const previousStats = activeStats;\n provider = null;\n activeStats = null;\n if (previousStats) {\n logger.info({ ...statsLogFields(previousStats), reason }, \"Voice ASR attempt detached\");\n }\n previousProvider?.close();\n }\n\n function start(payload: VoiceAsrStartMessage): void {\n const config = store.readSecret();\n if (!config.apiKey) {\n sendJson(ws, {\n type: \"error\",\n attemptId: payload.attemptId,\n errorCode: \"not_configured\",\n error: \"Voice provider is not configured\",\n });\n return;\n }\n detachProvider(\"replaced\");\n const stats = buildStats(payload, config);\n let currentProvider: VoiceAsrProviderClient;\n try {\n currentProvider = providers.current(config).createAsrClient(config, {\n sampleRate: stats.sampleRate,\n language: \"zh\",\n });\n } catch (error) {\n logger.warn({ ...statsLogFields(stats), err: error }, \"Voice ASR provider creation failed\");\n sendJson(ws, {\n type: \"error\",\n attemptId: payload.attemptId,\n error: error instanceof Error ? error.message : \"ASR failed\",\n });\n return;\n }\n provider = currentProvider;\n activeStats = stats;\n logger.info(statsLogFields(stats), \"Voice ASR attempt started\");\n currentProvider.on(\"ready\", () => {\n if (!isCurrent(currentProvider, stats, \"ready\")) return;\n stats.readyAt = Date.now();\n logger.info(statsLogFields(stats), \"Voice ASR provider ready\");\n sendJson(ws, { type: \"ready\", attemptId: stats.attemptId });\n });\n currentProvider.on(\"partial\", (text) => {\n if (!isCurrent(currentProvider, stats, \"partial\")) return;\n stats.partialEvents += 1;\n sendJson(ws, { type: \"partial\", attemptId: stats.attemptId, text });\n });\n currentProvider.on(\"final\", (text) => {\n if (!isCurrent(currentProvider, stats, \"final\")) return;\n stats.finalEvents += 1;\n stats.finalChars += text.length;\n sendJson(ws, { type: \"final\", attemptId: stats.attemptId, text });\n });\n currentProvider.on(\"error\", (error) => {\n if (!isCurrent(currentProvider, stats, \"error\")) return;\n logger.warn({ ...statsLogFields(stats), err: error }, \"Voice ASR provider reported an error\");\n sendJson(ws, {\n type: \"error\",\n attemptId: stats.attemptId,\n error: error.message || \"ASR failed\",\n });\n });\n currentProvider.on(\"closed\", (code, reason) => {\n if (!isCurrent(currentProvider, stats, \"closed\")) return;\n provider = null;\n activeStats = null;\n logger.info({ ...statsLogFields(stats), code, reason }, \"Voice ASR provider closed\");\n sendJson(ws, { type: \"closed\", attemptId: stats.attemptId, code, reason });\n });\n }\n\n ws.on(\"message\", (data, isBinary) => {\n if (isBinary) {\n const encodedChunk = toBuffer(data);\n if (provider && activeStats) {\n const pcmChunk =\n activeStats.encoding === \"mulaw\"\n ? Buffer.from(decodeMuLawToPcm16(encodedChunk))\n : encodedChunk;\n if (activeStats.firstPcmAt === null) activeStats.firstPcmAt = Date.now();\n activeStats.encodedBytes += encodedChunk.byteLength;\n activeStats.pcmBytes += pcmChunk.byteLength;\n activeStats.pcmChunks += 1;\n provider.sendPcm(pcmChunk);\n sendJson(ws, {\n type: \"audio_ack\",\n attemptId: activeStats.attemptId,\n encodedBytes: activeStats.encodedBytes,\n pcmBytes: activeStats.pcmBytes,\n chunks: activeStats.pcmChunks,\n });\n }\n return;\n }\n\n const parsed = VoiceAsrClientMessageSchema.safeParse(parseJson(data));\n if (!parsed.success) {\n logger.debug({ issues: parsed.error.issues }, \"Ignored invalid Voice ASR client message\");\n return;\n }\n const payload = parsed.data;\n if (payload.type === \"start\") {\n start(payload);\n return;\n }\n if (payload.type === \"stop\") {\n if (!provider || !activeStats || activeStats.attemptId !== payload.attemptId) {\n logger.debug(\n { attemptId: payload.attemptId, activeAttemptId: activeStats?.attemptId ?? null },\n \"Ignored stale Voice ASR stop\",\n );\n return;\n }\n logger.info(statsLogFields(activeStats), \"Voice ASR stop requested\");\n provider.stop();\n }\n });\n\n ws.on(\"close\", (code, reason) => {\n if (activeStats) {\n logger.info(\n { ...statsLogFields(activeStats), code, reason: reason.toString(\"utf8\") },\n \"Voice ASR client websocket closed\",\n );\n }\n detachProvider(\"client-websocket-closed\");\n });\n ws.on(\"error\", (err) => {\n logger.warn(\n { ...(activeStats ? statsLogFields(activeStats) : {}), err },\n \"Voice ASR websocket error\",\n );\n detachProvider(\"client-websocket-error\");\n });\n}\n","import { WebSocket, type RawData } from \"ws\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { BailianTtsClient, BailianTtsConfig } from \"./bailian-tts.js\";\nimport type { StoredVoiceConfig, VoiceConfigStore } from \"./config-store.js\";\nimport type { VoiceProviderRegistry, VoiceTtsProviderClient } from \"./provider.js\";\n\nexport type VoiceTtsClientFactory = (config: BailianTtsConfig) => BailianTtsClient;\n\ninterface SpeakMessage {\n type: \"speak\";\n requestId: string;\n text: string;\n}\n\ninterface ActiveTtsStats {\n requestId: string;\n textChars: number;\n provider: StoredVoiceConfig[\"provider\"];\n region: StoredVoiceConfig[\"region\"];\n ttsModel: string;\n ttsVoice: string;\n startedAt: number;\n firstAudioAt: number | null;\n audioBytes: number;\n audioChunks: number;\n}\n\nfunction sendJson(ws: WebSocket, payload: unknown): void {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(payload));\n }\n}\n\nfunction parseJson(data: RawData): unknown | null {\n try {\n const buffer = Buffer.isBuffer(data)\n ? data\n : data instanceof ArrayBuffer\n ? Buffer.from(data)\n : Buffer.concat(data);\n return JSON.parse(buffer.toString(\"utf8\"));\n } catch {\n return null;\n }\n}\n\nfunction isSpeakMessage(payload: unknown): payload is SpeakMessage {\n if (!payload || typeof payload !== \"object\") return false;\n const record = payload as Record<string, unknown>;\n return (\n record.type === \"speak\" &&\n typeof record.requestId === \"string\" &&\n typeof record.text === \"string\" &&\n record.text.length > 0\n );\n}\n\nfunction nowMs(): number {\n return Date.now();\n}\n\nfunction buildStats(requestId: string, text: string, config: StoredVoiceConfig): ActiveTtsStats {\n return {\n requestId,\n textChars: text.length,\n provider: config.provider,\n region: config.region,\n ttsModel: config.ttsModel,\n ttsVoice: config.ttsVoice,\n startedAt: nowMs(),\n firstAudioAt: null,\n audioBytes: 0,\n audioChunks: 0,\n };\n}\n\nfunction statsLogFields(stats: ActiveTtsStats): Record<string, unknown> {\n const currentTime = nowMs();\n return {\n requestId: stats.requestId,\n textChars: stats.textChars,\n provider: stats.provider,\n region: stats.region,\n ttsModel: stats.ttsModel,\n ttsVoice: stats.ttsVoice,\n audioBytes: stats.audioBytes,\n audioChunks: stats.audioChunks,\n durationMs: currentTime - stats.startedAt,\n firstAudioMs: stats.firstAudioAt === null ? null : stats.firstAudioAt - stats.startedAt,\n };\n}\n\nfunction closeReasonText(reason: unknown): string {\n if (Buffer.isBuffer(reason)) return reason.toString(\"utf8\");\n return typeof reason === \"string\" ? reason : \"\";\n}\n\nexport function handleVoiceTtsConnection(\n ws: WebSocket,\n store: VoiceConfigStore,\n logger: Logger,\n providers: VoiceProviderRegistry,\n): void {\n let provider: VoiceTtsProviderClient | null = null;\n let providerConfig: StoredVoiceConfig | null = null;\n let activeRequestId: string | null = null;\n let activeStats: ActiveTtsStats | null = null;\n\n function ensureProvider(): { client: VoiceTtsProviderClient; config: StoredVoiceConfig } | null {\n if (provider && providerConfig) return { client: provider, config: providerConfig };\n const config = store.readSecret();\n if (!config.apiKey) {\n sendJson(ws, {\n type: \"error\",\n errorCode: \"not_configured\",\n error: \"Voice provider is not configured\",\n });\n return null;\n }\n provider = providers.current(config).createTtsClient(config, {\n sampleRate: 24000,\n });\n providerConfig = config;\n provider.on(\"started\", () => {\n if (activeStats) logger.info(statsLogFields(activeStats), \"Voice TTS started\");\n sendJson(ws, { type: \"started\", requestId: activeRequestId });\n });\n provider.on(\"audio\", (chunk) => {\n if (activeStats) {\n if (activeStats.firstAudioAt === null) activeStats.firstAudioAt = nowMs();\n activeStats.audioBytes += chunk.byteLength;\n activeStats.audioChunks += 1;\n }\n if (ws.readyState === WebSocket.OPEN) ws.send(chunk);\n });\n provider.on(\"finished\", () => {\n if (activeStats) logger.info(statsLogFields(activeStats), \"Voice TTS finished\");\n sendJson(ws, { type: \"finished\", requestId: activeRequestId });\n activeRequestId = null;\n activeStats = null;\n });\n provider.on(\"error\", (error) => {\n if (activeStats) {\n logger.warn(\n { ...statsLogFields(activeStats), err: error },\n \"Voice TTS provider reported an error\",\n );\n }\n sendJson(ws, {\n type: \"error\",\n requestId: activeRequestId,\n error: error.message || \"TTS failed\",\n });\n activeRequestId = null;\n activeStats = null;\n });\n provider.on(\"closed\", (code, reason) => {\n if (activeStats) {\n logger.warn(\n { ...statsLogFields(activeStats), code, reason },\n \"Voice TTS provider closed before finishing\",\n );\n sendJson(ws, {\n type: \"error\",\n requestId: activeRequestId,\n errorCode: \"provider_closed\",\n error: \"Voice TTS provider closed before finishing\",\n });\n activeRequestId = null;\n activeStats = null;\n } else {\n logger.info({ code, reason }, \"Voice TTS provider closed\");\n provider = null;\n providerConfig = null;\n return;\n }\n sendJson(ws, { type: \"closed\", code, reason });\n provider = null;\n providerConfig = null;\n });\n return { client: provider, config };\n }\n\n ws.on(\"message\", (data) => {\n const payload = parseJson(data);\n if (!isSpeakMessage(payload)) return;\n if (activeRequestId) {\n sendJson(ws, {\n type: \"error\",\n requestId: payload.requestId,\n errorCode: \"busy\",\n error: \"Voice TTS is already speaking\",\n });\n return;\n }\n const ensured = ensureProvider();\n if (!ensured) return;\n const { client, config } = ensured;\n activeRequestId = payload.requestId;\n activeStats = buildStats(payload.requestId, payload.text, config);\n logger.info(statsLogFields(activeStats), \"Voice TTS request received\");\n try {\n client.speak(payload.text);\n } catch (err) {\n if (activeStats) {\n logger.warn(\n { ...statsLogFields(activeStats), err },\n \"Voice TTS request failed before provider accepted it\",\n );\n }\n sendJson(ws, {\n type: \"error\",\n requestId: payload.requestId,\n error: err instanceof Error ? err.message : \"Voice TTS failed\",\n });\n activeRequestId = null;\n activeStats = null;\n }\n });\n\n ws.on(\"close\", (code?: number, reason?: Buffer) => {\n if (activeStats) {\n logger.warn(\n { ...statsLogFields(activeStats), code, reason: closeReasonText(reason) },\n \"Voice TTS client websocket closed before finishing\",\n );\n }\n const currentProvider = provider;\n provider = null;\n providerConfig = null;\n activeRequestId = null;\n activeStats = null;\n currentProvider?.close();\n });\n ws.on(\"error\", (err) => {\n logger.warn({ err }, \"Voice TTS websocket error\");\n provider?.close();\n provider = null;\n providerConfig = null;\n activeRequestId = null;\n activeStats = null;\n });\n}\n","import {\n createBundledBailianVoiceCapabilities,\n type VoiceCapabilities,\n type VoiceOption,\n} from \"@dev-anywhere/shared\";\nimport type { StoredVoiceConfig } from \"./config-store.js\";\n\ntype FetchResponse = {\n ok: boolean;\n json: () => Promise<unknown>;\n};\ntype FetchLike = (\n url: string,\n init?: {\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n },\n) => Promise<FetchResponse>;\n\nexport interface VoiceCapabilitiesProvider {\n read: (config: StoredVoiceConfig) => Promise<VoiceCapabilities>;\n}\n\ninterface BailianVoiceCapabilitiesProviderOptions {\n fetchImpl?: FetchLike;\n now?: () => number;\n}\n\nconst CUSTOMIZATION_ENDPOINTS: Record<StoredVoiceConfig[\"region\"], string> = {\n cn: \"https://dashscope.aliyuncs.com/api/v1/services/audio/tts/customization\",\n intl: \"https://dashscope-intl.aliyuncs.com/api/v1/services/audio/tts/customization\",\n};\n\nfunction modelFromCustomCosyVoiceId(voiceId: string): string | undefined {\n const match = voiceId.match(/^(cosyvoice-v3(?:\\.5)?-(?:flash|plus))-/);\n return match?.[1];\n}\n\nasync function fetchCustomVoices(\n fetchImpl: FetchLike,\n config: StoredVoiceConfig,\n): Promise<VoiceOption[]> {\n if (!config.apiKey) return [];\n const response = await fetchImpl(CUSTOMIZATION_ENDPOINTS[config.region], {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${config.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n model: \"voice-enrollment\",\n input: {\n action: \"list_voice\",\n page_size: 100,\n page_index: 0,\n },\n }),\n });\n if (!response.ok) return [];\n const payload = response.json() as Promise<{\n output?: {\n voice_list?: Array<{\n voice_id?: string;\n status?: string;\n voice_prompt?: string;\n }>;\n };\n }>;\n const voiceList = (await payload).output?.voice_list ?? [];\n return voiceList\n .filter((voice) => !voice.status || voice.status === \"OK\")\n .flatMap((voice): VoiceOption[] => {\n if (!voice.voice_id) return [];\n const model = modelFromCustomCosyVoiceId(voice.voice_id);\n return [\n {\n value: voice.voice_id,\n label: [voice.voice_id, \"自定义\", voice.voice_prompt].filter(Boolean).join(\" · \"),\n ...(model ? { model } : {}),\n source: \"custom\",\n },\n ];\n });\n}\n\nexport function createBailianVoiceCapabilitiesProvider(\n options: BailianVoiceCapabilitiesProviderOptions = {},\n): VoiceCapabilitiesProvider {\n const fetchImpl = options.fetchImpl ?? (globalThis.fetch as unknown as FetchLike);\n const now = options.now ?? Date.now;\n\n return {\n async read(config) {\n let customVoices: VoiceOption[];\n try {\n customVoices = await fetchCustomVoices(fetchImpl, config);\n } catch {\n customVoices = [];\n }\n\n const bundled = createBundledBailianVoiceCapabilities(now());\n return {\n ...bundled,\n ttsVoices: [...bundled.ttsVoices, ...customVoices],\n };\n },\n };\n}\n","import {\n createBailianAsrClient,\n type BailianAsrClient,\n type BailianAsrConfig,\n} from \"./bailian-asr.js\";\nimport {\n createBailianTtsClient,\n type BailianTtsClient,\n type BailianTtsConfig,\n} from \"./bailian-tts.js\";\nimport {\n createBailianVoiceCapabilitiesProvider,\n type VoiceCapabilitiesProvider,\n} from \"./capabilities.js\";\nimport { createBailianVoiceConfigTester, type VoiceConfigTester } from \"./config-test.js\";\nimport type { StoredVoiceConfig } from \"./config-store.js\";\nimport type { VoiceProviderAdapter } from \"./provider.js\";\n\ninterface BailianVoiceProviderOptions {\n asrClientFactory?: (config: BailianAsrConfig) => BailianAsrClient;\n ttsClientFactory?: (config: BailianTtsConfig) => BailianTtsClient;\n capabilitiesProvider?: VoiceCapabilitiesProvider;\n configTester?: VoiceConfigTester;\n}\n\nfunction requireApiKey(config: StoredVoiceConfig): string {\n if (!config.apiKey) throw new Error(\"Voice provider is not configured\");\n return config.apiKey;\n}\n\nexport function createBailianVoiceProvider(\n options: BailianVoiceProviderOptions = {},\n): VoiceProviderAdapter {\n const asrClientFactory = options.asrClientFactory ?? createBailianAsrClient;\n const ttsClientFactory = options.ttsClientFactory ?? createBailianTtsClient;\n const capabilitiesProvider =\n options.capabilitiesProvider ?? createBailianVoiceCapabilitiesProvider();\n const configTester =\n options.configTester ??\n createBailianVoiceConfigTester({\n asrClientFactory,\n ttsClientFactory,\n });\n\n return {\n id: \"aliyun-bailian\",\n createAsrClient(config, clientOptions) {\n return asrClientFactory({\n apiKey: requireApiKey(config),\n region: config.region,\n model: config.asrModel,\n sampleRate: clientOptions.sampleRate,\n language: clientOptions.language,\n });\n },\n createTtsClient(config, clientOptions) {\n return ttsClientFactory({\n apiKey: requireApiKey(config),\n region: config.region,\n model: config.ttsModel,\n voice: config.ttsVoice,\n sampleRate: clientOptions.sampleRate,\n });\n },\n readCapabilities(config) {\n return capabilitiesProvider.read(config);\n },\n testConfig(config) {\n return configTester.test(config);\n },\n };\n}\n","import type { VoiceCapabilities, VoiceProviderConfig } from \"@dev-anywhere/shared\";\nimport type { StoredVoiceConfig } from \"./config-store.js\";\nimport type { VoiceConfigTestResult } from \"./config-test.js\";\n\nexport type VoiceProviderId = VoiceProviderConfig[\"provider\"];\n\nexport interface VoiceAsrProviderClient {\n on(event: \"ready\", handler: () => void): this;\n on(event: \"partial\" | \"final\", handler: (text: string) => void): this;\n on(event: \"error\", handler: (error: Error) => void): this;\n on(event: \"closed\", handler: (code?: number, reason?: string) => void): this;\n sendPcm(chunk: Buffer): void;\n stop(): void;\n close(): void;\n}\n\nexport interface VoiceTtsProviderClient {\n on(event: \"started\" | \"finished\", handler: () => void): this;\n on(event: \"audio\", handler: (chunk: Buffer) => void): this;\n on(event: \"error\", handler: (error: Error) => void): this;\n on(event: \"closed\", handler: (code?: number, reason?: string) => void): this;\n speak(text: string): void;\n close(): void;\n}\n\nexport interface VoiceAsrProviderOptions {\n sampleRate: number;\n language: string;\n}\n\nexport interface VoiceTtsProviderOptions {\n sampleRate: number;\n}\n\nexport interface VoiceProviderAdapter {\n id: VoiceProviderId;\n createAsrClient: (\n config: StoredVoiceConfig,\n options: VoiceAsrProviderOptions,\n ) => VoiceAsrProviderClient;\n createTtsClient: (\n config: StoredVoiceConfig,\n options: VoiceTtsProviderOptions,\n ) => VoiceTtsProviderClient;\n readCapabilities: (config: StoredVoiceConfig) => Promise<VoiceCapabilities>;\n testConfig: (config: StoredVoiceConfig) => Promise<VoiceConfigTestResult>;\n}\n\nexport interface VoiceProviderRegistry {\n current: (config: StoredVoiceConfig) => VoiceProviderAdapter;\n require: (providerId: string) => VoiceProviderAdapter;\n}\n\nexport function createVoiceProviderRegistry(\n adapters: readonly VoiceProviderAdapter[],\n): VoiceProviderRegistry {\n const byId = new Map<string, VoiceProviderAdapter>();\n for (const adapter of adapters) {\n if (byId.has(adapter.id)) {\n throw new Error(`Duplicate voice provider: ${adapter.id}`);\n }\n byId.set(adapter.id, adapter);\n }\n\n function require(providerId: string): VoiceProviderAdapter {\n const adapter = byId.get(providerId);\n if (!adapter) throw new Error(`Unsupported voice provider: ${providerId}`);\n return adapter;\n }\n\n return {\n current(config) {\n return require(config.provider);\n },\n require,\n };\n}\n","import type { Request, Response } from \"express\";\nimport { nanoid } from \"nanoid\";\nimport { WebSocket } from \"ws\";\nimport {\n ControlErrorCode,\n decodeFileStreamFrame,\n encodeFileStreamFrame,\n serializeControl,\n type ControlMessage,\n} from \"@dev-anywhere/shared\";\nimport type { ControlErrorCode as ControlErrorCodeType } from \"@dev-anywhere/shared\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\nimport type { RelayRegistry } from \"./registry.js\";\n\nconst REMOTE_FILE_TOKEN_TTL_MS = 24 * 60 * 60 * 1000;\nconst REMOTE_FILE_METADATA_TIMEOUT_MS = 30 * 1000;\nconst REMOTE_FILE_UPLOAD_RESPONSE_TIMEOUT_MS = 60 * 1000;\n\ntype RemoteFileDisposition = \"inline\" | \"download\";\ntype RemoteFileUploadKind = \"clipboard_image\" | \"file\";\n\ninterface RemoteFileToken {\n token: string;\n clientId: string;\n proxyId: string;\n sessionId: string;\n path: string;\n disposition: RemoteFileDisposition;\n expiresAt: number;\n}\n\ntype RemoteFileUrlResult =\n | { success: true; path: string; url: string; expiresAt: number }\n | { success: false; path: string; error: string; errorCode?: ControlErrorCodeType };\n\ninterface PendingUrlMetadata {\n requestId: string;\n clientId: string;\n proxyId: string;\n sessionId: string;\n path: string;\n disposition: RemoteFileDisposition;\n timer: NodeJS.Timeout;\n resolve: (result: RemoteFileUrlResult) => void;\n}\n\ninterface PendingHttpStream {\n streamId: string;\n token: RemoteFileToken;\n res: Response;\n metadataTimer: NodeJS.Timeout;\n headersSent: boolean;\n finished: boolean;\n}\n\ninterface RemoteFileUploadToken {\n token: string;\n clientId: string;\n proxyId: string;\n sessionId: string;\n kind: RemoteFileUploadKind;\n fileName?: string;\n mimeType: string;\n size?: number;\n expiresAt: number;\n}\n\ninterface PendingHttpUpload {\n uploadId: string;\n token: RemoteFileUploadToken;\n res: Response;\n chunkSeq: number;\n requestEnded: boolean;\n finished: boolean;\n responseTimer: NodeJS.Timeout | null;\n}\n\ninterface RemoteFileBridgeDeps {\n registry: RelayRegistry;\n logger: Logger;\n}\n\nfunction encodeContentDisposition(disposition: RemoteFileDisposition, fileName: string): string {\n const type = disposition === \"download\" ? \"attachment\" : \"inline\";\n const fallback =\n fileName\n .replace(/[^\\x20-\\x7e]/g, \"_\")\n .replace(/[\"\\\\/\\r\\n]/g, \"_\")\n .replace(/_+/g, \"_\")\n .trim() || \"download\";\n const encodedFileName = encodeURIComponent(fileName).replace(\n /['()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n return `${type}; filename=\"${fallback}\"; filename*=UTF-8''${encodedFileName}`;\n}\n\nfunction statusForErrorCode(errorCode?: string): number {\n switch (errorCode) {\n case ControlErrorCode.SESSION_NOT_FOUND:\n case ControlErrorCode.PATH_NOT_FOUND:\n return 404;\n case ControlErrorCode.INVALID_PATH:\n case ControlErrorCode.PATH_NOT_DIRECTORY:\n return 400;\n case ControlErrorCode.PATH_ACCESS_DENIED:\n return 403;\n default:\n return 500;\n }\n}\n\nexport class RemoteFileBridge {\n private readonly tokens = new Map<string, RemoteFileToken>();\n private readonly uploadTokens = new Map<string, RemoteFileUploadToken>();\n private readonly pendingUrlMetadata = new Map<string, PendingUrlMetadata>();\n private readonly pendingStreams = new Map<string, PendingHttpStream>();\n private readonly pendingUploads = new Map<string, PendingHttpUpload>();\n\n constructor(private readonly deps: RemoteFileBridgeDeps) {}\n\n createUrl(input: {\n clientId: string;\n proxyId: string;\n sessionId: string;\n path: string;\n disposition: RemoteFileDisposition;\n }): Promise<RemoteFileUrlResult> {\n this.cleanupExpiredTokens();\n const proxyWs = this.deps.registry.getProxy(input.proxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) {\n return Promise.resolve({\n success: false,\n path: input.path,\n error: \"当前未连接开发机\",\n errorCode: ControlErrorCode.PROXY_OFFLINE,\n });\n }\n\n const requestId = nanoid(21);\n return new Promise<RemoteFileUrlResult>((resolve) => {\n const timer = setTimeout(() => {\n this.pendingUrlMetadata.delete(requestId);\n resolve({\n success: false,\n path: input.path,\n error: \"读取文件元数据超时\",\n errorCode: ControlErrorCode.UNKNOWN,\n });\n }, REMOTE_FILE_METADATA_TIMEOUT_MS);\n\n this.pendingUrlMetadata.set(requestId, {\n requestId,\n clientId: input.clientId,\n proxyId: input.proxyId,\n sessionId: input.sessionId,\n path: input.path,\n disposition: input.disposition,\n timer,\n resolve,\n });\n\n proxyWs.send(\n serializeControl({\n type: \"remote_file_metadata_request\",\n requestId,\n sessionId: input.sessionId,\n path: input.path,\n }),\n );\n this.deps.logger.info(\n { requestId, proxyId: input.proxyId, sessionId: input.sessionId, path: input.path },\n \"Remote file URL metadata requested\",\n );\n });\n }\n\n createUploadUrl(input: {\n clientId: string;\n proxyId: string;\n sessionId: string;\n kind: RemoteFileUploadKind;\n fileName?: string;\n mimeType: string;\n size?: number;\n }): { uploadUrl: string; expiresAt: number } {\n this.cleanupExpiredTokens();\n const token = nanoid(32);\n const expiresAt = Date.now() + REMOTE_FILE_TOKEN_TTL_MS;\n this.uploadTokens.set(token, {\n token,\n clientId: input.clientId,\n proxyId: input.proxyId,\n sessionId: input.sessionId,\n kind: input.kind,\n fileName: input.fileName,\n mimeType: input.mimeType,\n size: input.size,\n expiresAt,\n });\n return { uploadUrl: `/api/remote-uploads/${token}`, expiresAt };\n }\n\n handleHttpRequest(req: Request, res: Response): void {\n const tokenValue = req.params.token;\n const token = typeof tokenValue === \"string\" ? this.tokens.get(tokenValue) : undefined;\n if (!token || token.expiresAt <= Date.now()) {\n if (typeof tokenValue === \"string\") this.tokens.delete(tokenValue);\n res.status(404).json({ error: \"remote_file_url_expired\" });\n return;\n }\n\n const proxyWs = this.deps.registry.getProxy(token.proxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) {\n res.status(502).json({ error: \"proxy_offline\" });\n return;\n }\n\n const streamId = nanoid(21);\n const metadataTimer = setTimeout(() => {\n this.failStream(streamId, 504, \"读取文件元数据超时\");\n }, REMOTE_FILE_METADATA_TIMEOUT_MS);\n const pending: PendingHttpStream = {\n streamId,\n token,\n res,\n metadataTimer,\n headersSent: false,\n finished: false,\n };\n this.pendingStreams.set(streamId, pending);\n\n res.on(\"close\", () => {\n if (pending.finished) return;\n pending.finished = true;\n this.pendingStreams.delete(streamId);\n clearTimeout(metadataTimer);\n this.sendCancel(token.proxyId, streamId);\n this.deps.logger.info({ streamId, proxyId: token.proxyId }, \"Remote file HTTP stream closed\");\n });\n\n proxyWs.send(\n serializeControl({\n type: \"remote_file_stream_request\",\n streamId,\n sessionId: token.sessionId,\n path: token.path,\n disposition: token.disposition,\n }),\n );\n this.deps.logger.info(\n { streamId, proxyId: token.proxyId, sessionId: token.sessionId, path: token.path },\n \"Remote file HTTP stream requested\",\n );\n }\n\n handleUploadHttpRequest(req: Request, res: Response): void {\n const tokenValue = req.params.token;\n const token = typeof tokenValue === \"string\" ? this.uploadTokens.get(tokenValue) : undefined;\n if (!token || token.expiresAt <= Date.now()) {\n if (typeof tokenValue === \"string\") this.uploadTokens.delete(tokenValue);\n res.status(404).json({ error: \"remote_file_upload_url_expired\" });\n return;\n }\n\n const proxyWs = this.deps.registry.getProxy(token.proxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) {\n res.status(502).json({ error: \"proxy_offline\" });\n return;\n }\n\n const uploadId = nanoid(21);\n const pending: PendingHttpUpload = {\n uploadId,\n token,\n res,\n chunkSeq: 0,\n requestEnded: false,\n finished: false,\n responseTimer: null,\n };\n this.pendingUploads.set(uploadId, pending);\n\n proxyWs.send(\n serializeControl({\n type: \"remote_file_upload_stream_request\",\n uploadId,\n sessionId: token.sessionId,\n kind: token.kind,\n mimeType: token.mimeType,\n ...(token.fileName ? { fileName: token.fileName } : {}),\n ...(token.size !== undefined ? { size: token.size } : {}),\n }),\n );\n\n req.on(\"data\", (chunk: Buffer) => {\n if (pending.finished) return;\n req.pause();\n const frame = encodeFileStreamFrame(uploadId, pending.chunkSeq, Buffer.from(chunk));\n pending.chunkSeq += 1;\n proxyWs.send(frame, { binary: true }, (err) => {\n if (err) {\n this.failUpload(uploadId, 502, err.message);\n return;\n }\n req.resume();\n });\n });\n\n req.on(\"end\", () => {\n if (pending.finished) return;\n pending.requestEnded = true;\n pending.responseTimer = setTimeout(() => {\n this.failUpload(uploadId, 504, \"等待上传写入结果超时\");\n }, REMOTE_FILE_UPLOAD_RESPONSE_TIMEOUT_MS);\n proxyWs.send(serializeControl({ type: \"remote_file_upload_stream_complete\", uploadId }));\n });\n\n req.on(\"error\", (err) => {\n this.failUpload(uploadId, 400, err.message);\n });\n\n req.on(\"close\", () => {\n if (pending.finished || pending.requestEnded) return;\n this.failUpload(uploadId, 499, \"上传请求已断开\");\n });\n\n this.deps.logger.info(\n { uploadId, proxyId: token.proxyId, sessionId: token.sessionId, kind: token.kind },\n \"Remote file HTTP upload requested\",\n );\n }\n\n handleProxyControl(proxyId: string, msg: ControlMessage<\"remote_file_stream_response\">): boolean;\n handleProxyControl(proxyId: string, msg: ControlMessage<\"remote_file_stream_complete\">): boolean;\n handleProxyControl(\n proxyId: string,\n msg: ControlMessage<\"remote_file_metadata_response\">,\n ): boolean;\n handleProxyControl(\n proxyId: string,\n msg: ControlMessage<\"remote_file_upload_stream_response\">,\n ): boolean;\n handleProxyControl(\n proxyId: string,\n msg:\n | ControlMessage<\"remote_file_stream_response\">\n | ControlMessage<\"remote_file_stream_complete\">\n | ControlMessage<\"remote_file_metadata_response\">\n | ControlMessage<\"remote_file_upload_stream_response\">,\n ): boolean {\n if (msg.type === \"remote_file_upload_stream_response\") {\n return this.handleUploadResponse(proxyId, msg);\n }\n\n if (msg.type === \"remote_file_metadata_response\") {\n return this.handleUrlMetadataResponse(proxyId, msg);\n }\n\n const pending = this.pendingStreams.get(msg.streamId);\n if (!pending) return true;\n if (pending.token.proxyId !== proxyId) {\n this.deps.logger.warn(\n { streamId: msg.streamId, expectedProxyId: pending.token.proxyId, proxyId },\n \"Remote file stream control ignored: proxy mismatch\",\n );\n return true;\n }\n\n if (msg.type === \"remote_file_stream_response\") {\n this.handleStreamResponse(pending, msg);\n return true;\n }\n\n this.handleStreamComplete(pending, msg);\n return true;\n }\n\n private issueUrlToken(input: {\n clientId: string;\n proxyId: string;\n sessionId: string;\n path: string;\n disposition: RemoteFileDisposition;\n }): { url: string; expiresAt: number } {\n const token = nanoid(32);\n const expiresAt = Date.now() + REMOTE_FILE_TOKEN_TTL_MS;\n this.tokens.set(token, {\n token,\n clientId: input.clientId,\n proxyId: input.proxyId,\n sessionId: input.sessionId,\n path: input.path,\n disposition: input.disposition,\n expiresAt,\n });\n return { url: `/api/remote-files/${token}`, expiresAt };\n }\n\n private handleUrlMetadataResponse(\n proxyId: string,\n msg: ControlMessage<\"remote_file_metadata_response\">,\n ): boolean {\n const pending = this.pendingUrlMetadata.get(msg.requestId);\n if (!pending) return true;\n if (pending.proxyId !== proxyId) {\n this.deps.logger.warn(\n { requestId: msg.requestId, expectedProxyId: pending.proxyId, proxyId },\n \"Remote file metadata ignored: proxy mismatch\",\n );\n return true;\n }\n\n this.pendingUrlMetadata.delete(msg.requestId);\n clearTimeout(pending.timer);\n\n if (!msg.success) {\n pending.resolve({\n success: false,\n path: pending.path,\n error: msg.error ?? \"读取文件失败\",\n ...(msg.errorCode ? { errorCode: msg.errorCode } : {}),\n });\n return true;\n }\n\n const { url, expiresAt } = this.issueUrlToken(pending);\n pending.resolve({\n success: true,\n path: pending.path,\n url,\n expiresAt,\n });\n return true;\n }\n\n handleProxyBinary(proxyId: string, data: Buffer): boolean {\n const decoded = decodeFileStreamFrame(data);\n if (!decoded) return false;\n\n const pending = this.pendingStreams.get(decoded.streamId);\n if (!pending) return true;\n if (pending.token.proxyId !== proxyId) {\n this.deps.logger.warn(\n { streamId: decoded.streamId, expectedProxyId: pending.token.proxyId, proxyId },\n \"Remote file stream chunk ignored: proxy mismatch\",\n );\n return true;\n }\n if (!pending.headersSent || pending.finished) return true;\n\n pending.res.write(Buffer.from(decoded.data));\n return true;\n }\n\n private handleStreamResponse(\n pending: PendingHttpStream,\n msg: ControlMessage<\"remote_file_stream_response\">,\n ): void {\n clearTimeout(pending.metadataTimer);\n if (!msg.success) {\n this.failStream(\n pending.streamId,\n statusForErrorCode(msg.errorCode),\n msg.error ?? \"读取文件失败\",\n msg.errorCode,\n );\n return;\n }\n\n try {\n pending.res.status(200);\n pending.res.setHeader(\"Content-Type\", msg.mimeType ?? \"application/octet-stream\");\n pending.res.setHeader(\"Cache-Control\", \"no-store\");\n pending.res.setHeader(\"X-Content-Type-Options\", \"nosniff\");\n if (msg.size !== undefined) {\n pending.res.setHeader(\"Content-Length\", String(msg.size));\n }\n pending.res.setHeader(\n \"Content-Disposition\",\n encodeContentDisposition(pending.token.disposition, msg.fileName ?? \"download\"),\n );\n pending.res.flushHeaders();\n pending.headersSent = true;\n } catch (error) {\n pending.headersSent = pending.res.headersSent;\n this.deps.logger.error(\n { err: error, streamId: pending.streamId, fileName: msg.fileName },\n \"Remote file response headers rejected\",\n );\n this.failStream(pending.streamId, 500, \"文件下载响应无效\");\n }\n }\n\n private handleStreamComplete(\n pending: PendingHttpStream,\n msg: ControlMessage<\"remote_file_stream_complete\">,\n ): void {\n clearTimeout(pending.metadataTimer);\n pending.finished = true;\n this.pendingStreams.delete(pending.streamId);\n if (!msg.success) {\n if (!pending.headersSent) {\n this.writeJsonError(\n pending.res,\n statusForErrorCode(msg.errorCode),\n msg.error ?? \"读取文件失败\",\n msg.errorCode,\n );\n } else {\n pending.res.destroy(new Error(msg.error ?? \"Remote file stream failed\"));\n }\n return;\n }\n pending.res.end();\n }\n\n private handleUploadResponse(\n proxyId: string,\n msg: ControlMessage<\"remote_file_upload_stream_response\">,\n ): boolean {\n const pending = this.pendingUploads.get(msg.uploadId);\n if (!pending) return true;\n if (pending.token.proxyId !== proxyId) {\n this.deps.logger.warn(\n { uploadId: msg.uploadId, expectedProxyId: pending.token.proxyId, proxyId },\n \"Remote file upload response ignored: proxy mismatch\",\n );\n return true;\n }\n pending.finished = true;\n this.pendingUploads.delete(msg.uploadId);\n if (pending.responseTimer) clearTimeout(pending.responseTimer);\n if (!msg.success) {\n this.writeJsonError(\n pending.res,\n statusForErrorCode(msg.errorCode),\n msg.error ?? \"上传文件失败\",\n msg.errorCode,\n );\n return true;\n }\n pending.res.status(200).json({\n sessionId: msg.sessionId,\n success: true,\n path: msg.path,\n });\n return true;\n }\n\n private failStream(streamId: string, status: number, error: string, errorCode?: string): void {\n const pending = this.pendingStreams.get(streamId);\n if (!pending) return;\n pending.finished = true;\n this.pendingStreams.delete(streamId);\n clearTimeout(pending.metadataTimer);\n this.sendCancel(pending.token.proxyId, streamId);\n if (!pending.headersSent) {\n this.writeJsonError(pending.res, status, error, errorCode);\n } else {\n pending.res.destroy(new Error(error));\n }\n }\n\n private failUpload(uploadId: string, status: number, error: string, errorCode?: string): void {\n const pending = this.pendingUploads.get(uploadId);\n if (!pending) return;\n pending.finished = true;\n this.pendingUploads.delete(uploadId);\n if (pending.responseTimer) clearTimeout(pending.responseTimer);\n this.sendUploadCancel(pending.token.proxyId, uploadId);\n if (!pending.res.headersSent) {\n this.writeJsonError(pending.res, status, error, errorCode);\n } else {\n pending.res.destroy(new Error(error));\n }\n }\n\n private writeJsonError(res: Response, status: number, error: string, errorCode?: string): void {\n res.status(status).json({\n error,\n ...(errorCode ? { errorCode } : {}),\n });\n }\n\n private sendCancel(proxyId: string, streamId: string): void {\n const proxyWs = this.deps.registry.getProxy(proxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) return;\n proxyWs.send(serializeControl({ type: \"remote_file_stream_cancel\", streamId }));\n }\n\n private sendUploadCancel(proxyId: string, uploadId: string): void {\n const proxyWs = this.deps.registry.getProxy(proxyId);\n if (!proxyWs || proxyWs.readyState !== WebSocket.OPEN) return;\n proxyWs.send(serializeControl({ type: \"remote_file_upload_stream_cancel\", uploadId }));\n }\n\n private cleanupExpiredTokens(): void {\n const now = Date.now();\n for (const [token, meta] of this.tokens) {\n if (meta.expiresAt <= now) this.tokens.delete(token);\n }\n for (const [token, meta] of this.uploadTokens) {\n if (meta.expiresAt <= now) this.uploadTokens.delete(token);\n }\n }\n}\n","import compression from \"compression\";\nimport express, { type Express, type Request, type Response } from \"express\";\nimport { existsSync } from \"node:fs\";\nimport { basename, join, relative } from \"node:path\";\nimport type { Logger } from \"@dev-anywhere/shared/logger\";\n\nconst IMMUTABLE_CACHE_SECONDS = 365 * 24 * 60 * 60;\nconst SHORT_CACHE_SECONDS = 60 * 60;\n\nconst RELAY_ROUTE_ROOTS = new Set([\n \"/api\",\n \"/client\",\n \"/fonts\",\n \"/health\",\n \"/proxy\",\n \"/status\",\n \"/voice\",\n]);\n\nfunction isRelayRoute(pathname: string): boolean {\n for (const root of RELAY_ROUTE_ROOTS) {\n if (pathname === root || pathname.startsWith(`${root}/`)) return true;\n }\n return false;\n}\n\nfunction setStaticCacheHeaders(response: Response, filePath: string, webAssetDir: string): void {\n const fileName = basename(filePath);\n const relativePath = relative(webAssetDir, filePath).replaceAll(\"\\\\\", \"/\");\n if (relativePath.startsWith(\"assets/\") || /^workbox-.+\\.js$/.test(fileName)) {\n response.setHeader(\"Cache-Control\", `public, max-age=${IMMUTABLE_CACHE_SECONDS}, immutable`);\n return;\n }\n\n if (\n fileName === \"index.html\" ||\n fileName === \"sw.js\" ||\n fileName === \"registerSW.js\" ||\n fileName === \"manifest.webmanifest\"\n ) {\n response.setHeader(\"Cache-Control\", \"no-cache, no-store, must-revalidate\");\n return;\n }\n\n response.setHeader(\"Cache-Control\", `public, max-age=${SHORT_CACHE_SECONDS}`);\n}\n\nexport function mountWebApp(\n app: Express,\n options: {\n webAssetDir: string;\n logger: Logger;\n },\n): boolean {\n const { webAssetDir, logger } = options;\n const indexPath = join(webAssetDir, \"index.html\");\n if (!existsSync(indexPath)) {\n logger.warn({ webAssetDir }, \"Web assets not found; Relay will run without the Web UI\");\n return false;\n }\n\n app.use(compression());\n app.use(\n express.static(webAssetDir, {\n index: false,\n fallthrough: true,\n setHeaders: (response, filePath) => setStaticCacheHeaders(response, filePath, webAssetDir),\n }),\n );\n\n app.use((request: Request, response: Response, next) => {\n if (request.method !== \"GET\" && request.method !== \"HEAD\") {\n next();\n return;\n }\n\n if (isRelayRoute(request.path)) {\n response.status(404).json({ error: \"not_found\" });\n return;\n }\n\n if (!request.accepts(\"html\")) {\n next();\n return;\n }\n\n response.setHeader(\"Cache-Control\", \"no-cache, no-store, must-revalidate\");\n response.sendFile(\"index.html\", { root: webAssetDir });\n });\n\n logger.info({ webAssetDir }, \"Web UI mounted\");\n return true;\n}\n"],"mappings":"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","names":["express","existsSync","dirname","fileURLToPath","z","z","z","z","z","z","z","z","z","z","z","z","WebSocket","WebSocket","WebSocket","WebSocket","WebSocket","WebSocket","WebSocket","WebSocket","EventEmitter","nanoid","WebSocket","OPEN","defaultSocketFactory","WebSocket","EventEmitter","nanoid","resolve","rejectNotRegistered","result","WebSocket","WebSocket","readFileSync","dirname","join","join","readFileSync","dirname","WebSocket","WebSocket","WebSocket","sendJson","parseJson","buildStats","statsLogFields","require","nanoid","WebSocket","WebSocket","nanoid","resolve","existsSync","join","dirname","fileURLToPath","express","existsSync","resolve"]}
|