@dev-anywhere/proxy 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/serve.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/serve.ts","../src/serve/session-manager.ts","../src/serve/relay-connection.ts","../src/serve/message-queue.ts","../src/serve/handlers/control-messages.ts","../src/serve/session-history.ts","../src/serve/command-discovery.ts","../src/serve/path-errors.ts","../src/serve/worker-registry.ts","../src/serve/session-termination.ts","../src/serve/clipboard-image-upload.ts","../src/serve/file-download.ts","../src/serve/file-upload.ts","../src/serve/image-preview.ts","../src/serve/pty-input.ts","../src/serve/relay-input-handlers.ts","../src/serve/relay-history-handlers.ts","../src/serve/relay-permission-handlers.ts","../src/serve/relay-resource-handlers.ts","../src/serve/relay-session-create-handler.ts","../src/serve/hosted-pty-registry.ts","../src/serve/relay-router.ts","../src/serve/json-observer.ts","../src/serve/permission-broker.ts","../src/serve/hook-payload-helpers.ts","../src/serve/hook-event-router.ts","../src/serve/agent-status-registry.ts","../src/serve/pty-state-guard.ts","../src/serve/pty-semantic-lifecycle.ts","../src/serve/pty-session-bridge.ts","../src/serve/session-broadcast.ts","../src/serve/event-bridge.ts","../src/serve/service-files.ts","../src/serve/terminal-ipc.ts","../src/serve/hook-registry.ts","../src/serve/hook-server.ts","../src/serve/provider-hook-runtime.ts","../src/serve/shutdown.ts"],"sourcesContent":["import { createServer, type Socket } from \"node:net\";\nimport { unlinkSync, writeFileSync, chmodSync, rmSync } from \"node:fs\";\nimport { serializeControl } from \"@dev-anywhere/shared\";\nimport { flushLogger } from \"@dev-anywhere/shared/logger\";\nimport { serviceLogger } from \"./common/logger.js\";\nimport { SessionManager } from \"./serve/session-manager.js\";\nimport { RelayConnection } from \"./serve/relay-connection.js\";\nimport {\n SOCK_PATH,\n PID_PATH,\n STOPPED_PATH,\n SESSIONS_PATH,\n PROXY_ID_PATH,\n PROFILE_NAME,\n ensureProfileWorkspace,\n sessionPaths,\n} from \"./common/paths.js\";\nimport { buildProviderEnv, loadConfig } from \"./common/config.js\";\nimport { serializeIpc } from \"./ipc/ipc-protocol.js\";\nimport { createControlMessageHandlers } from \"./serve/handlers/control-messages.js\";\nimport { WorkerRegistry } from \"./serve/worker-registry.js\";\nimport { RelayRouter } from \"./serve/relay-router.js\";\nimport { JsonObserver } from \"./serve/json-observer.js\";\nimport { PermissionBroker } from \"./serve/permission-broker.js\";\nimport { HookEventRouter } from \"./serve/hook-event-router.js\";\nimport { AgentStatusRegistry } from \"./serve/agent-status-registry.js\";\nimport { HostedPtyRegistry } from \"./serve/hosted-pty-registry.js\";\nimport { applyPtyStateToSession, type PtySessionBridgeDeps } from \"./serve/pty-session-bridge.js\";\nimport { broadcastSessionList, broadcastSessionSync } from \"./serve/session-broadcast.js\";\nimport { createEventBridge } from \"./serve/event-bridge.js\";\nimport { cleanupStaleResources, getProxyName } from \"./serve/service-files.js\";\nimport { handleTerminalConnection } from \"./serve/terminal-ipc.js\";\nimport { createProviderHookRuntime } from \"./serve/provider-hook-runtime.js\";\nimport { createServeShutdown } from \"./serve/shutdown.js\";\nimport type { ProviderId } from \"./providers/types.js\";\n\nfunction resolveInterruptedApprovals(\n permissionBroker: PermissionBroker,\n hookEventRouter: HookEventRouter,\n relay: RelayConnection,\n sessionId: string,\n): void {\n const approvals = permissionBroker.listSession(sessionId);\n if (approvals.length === 0) return;\n\n const message = \"Permission request was interrupted in the PTY.\";\n for (const approval of approvals) {\n if (!permissionBroker.resolve(approval.requestId, { behavior: \"deny\", message })) continue;\n hookEventRouter.onPermissionResolved(\n approval.sessionId,\n approval.provider,\n approval.requestId,\n \"deny\",\n { toolName: approval.toolName, toolInput: approval.input },\n );\n relay.sendRaw(\n serializeControl({\n type: \"permission_decision_result\",\n sessionId: approval.sessionId,\n requestId: approval.requestId,\n outcome: \"deny\",\n delivered: true,\n message,\n }),\n );\n }\n serviceLogger.info(\n { sessionId, count: approvals.length },\n \"Pending approvals cleared after PTY interruption\",\n );\n}\n\nexport interface ServiceOptions {\n relayUrl?: string;\n relayName?: string;\n}\n\nfunction parseServiceOptions(argv: readonly string[]): ServiceOptions {\n const options: ServiceOptions = {};\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === \"--relay\") {\n const relayName = argv[i + 1];\n if (!relayName || relayName.startsWith(\"-\")) {\n throw new Error(\"Missing value for --relay\");\n }\n options.relayName = relayName;\n i++;\n continue;\n }\n if (arg.startsWith(\"--relay=\")) {\n const relayName = arg.slice(\"--relay=\".length);\n if (!relayName) throw new Error(\"Missing value for --relay\");\n options.relayName = relayName;\n }\n }\n return options;\n}\n\nexport async function startService(options?: ServiceOptions): Promise<void> {\n ensureProfileWorkspace();\n await cleanupStaleResources();\n try {\n unlinkSync(STOPPED_PATH);\n } catch {\n // STOPPED 文件不存在时忽略\n }\n\n const permissionBroker = new PermissionBroker();\n const agentStatusRegistry = new AgentStatusRegistry();\n let unregisterHookSession: (sessionId: string) => void = () => {};\n const sessionManager = new SessionManager({\n persistPath: SESSIONS_PATH,\n onSessionRemoved: (id, context) => {\n if (!context?.preserveProviderHooks) {\n unregisterHookSession(id);\n }\n permissionBroker.cleanupSession(id, \"session removed\");\n agentStatusRegistry.delete(id);\n const paths = sessionPaths(id);\n try {\n rmSync(paths.dir, { recursive: true, force: true });\n } catch {\n // 会话目录清理失败不影响主流程\n }\n },\n });\n sessionManager.startReaper();\n\n const terminalSockets = new Map<string, Socket>();\n const proxyName = getProxyName();\n\n // 连接中转服务器:优先用调用方传入的 relayUrl,否则从配置文件读取\n // relay 是 proxy 存在的必要前提,未配置直接 fail-fast,不再支持\"本地独立\"模式\n let proxyConfig = loadConfig({ relayName: options?.relayName });\n const getProviderEnv = (): NodeJS.ProcessEnv => buildProviderEnv(proxyConfig, process.env);\n const getAgentCliSuggestions = (): Partial<Record<ProviderId, string[]>> =>\n proxyConfig.agentCliSuggestions;\n const getPreviewRoots = (): string[] => proxyConfig.previewRoots;\n const setAgentCliPath = (provider: ProviderId, path: string): void => {\n const field = provider === \"claude\" ? \"claudeBin\" : \"codexBin\";\n const existing = proxyConfig.agentCliSuggestions[provider] ?? [];\n proxyConfig = {\n ...proxyConfig,\n [field]: path,\n agentCliSuggestions: {\n ...proxyConfig.agentCliSuggestions,\n [provider]: [path, ...existing.filter((candidate) => candidate !== path)],\n },\n sources: {\n ...proxyConfig.sources,\n [field]: \"file\",\n },\n };\n };\n const relayUrl = options?.relayUrl ?? proxyConfig.relayUrl;\n const relayToken = proxyConfig.relayToken;\n const statusConfig = {\n profile: PROFILE_NAME,\n relayName: proxyConfig.relayName,\n relayNameSource: proxyConfig.sources.relayName,\n relayUrl,\n relayUrlSource: proxyConfig.sources.relayUrl,\n relayTokenSource: proxyConfig.sources.relayToken,\n hookPort: proxyConfig.hookPort ?? 17654,\n hookPortSource: proxyConfig.sources.hookPort,\n };\n if (!relayUrl) {\n const msg = `Relay URL is required. Set relays.${proxyConfig.relayName}.url in ~/.dev-anywhere/config.json or pass --relay <name>.`;\n serviceLogger.error(msg);\n console.error(msg);\n await flushLogger(serviceLogger);\n process.exit(1);\n }\n const relayConnection = new RelayConnection(relayUrl, {\n name: proxyName,\n token: relayToken,\n proxyIdPath: PROXY_ID_PATH,\n });\n const relaySend = (data: string): void => relayConnection.sendRaw(data);\n const controlHandlers = createControlMessageHandlers(relaySend, sessionManager);\n\n const eventBridge = createEventBridge({\n sessionManager,\n relayConnection,\n agentStatusRegistry,\n controlHandlers,\n permissionBroker,\n });\n const jsonObserver = new JsonObserver({\n changeSessionState: eventBridge.changeSessionState,\n emitAgentStatus: eventBridge.emitAgentStatus,\n });\n const hookRuntime = await createProviderHookRuntime({\n hookPort: proxyConfig.hookPort,\n permissionBroker,\n sessionManager,\n relayConnection,\n agentStatusRegistry,\n changeSessionState: eventBridge.changeSessionState,\n });\n unregisterHookSession = (sessionId) => hookRuntime.hookRegistry.unregisterSession(sessionId);\n\n // WorkerRegistry 建在 relay 之后、listener 之前;构造期订阅 envelope_dropped 事件\n const workerRegistry = new WorkerRegistry({\n sessionManager,\n permissionBroker,\n relayConnection,\n jsonObserver,\n touchSessionActivity: eventBridge.touchSessionActivity,\n getProviderEnv,\n });\n const ptyBridgeDeps: PtySessionBridgeDeps = {\n changeSessionState: eventBridge.changeSessionState,\n getSession: (sessionId) => sessionManager.getSession(sessionId),\n getPendingApprovalCount: (sessionId) => permissionBroker.listSession(sessionId).length,\n resolveInterruptedApprovals: (sessionId) =>\n resolveInterruptedApprovals(\n permissionBroker,\n hookRuntime.hookEventRouter,\n relayConnection,\n sessionId,\n ),\n emitAgentStatus: eventBridge.emitAgentStatus,\n };\n const hostedPtyRegistry = new HostedPtyRegistry({\n sessionManager,\n relayConnection,\n getProviderEnv,\n touchSessionActivity: eventBridge.touchSessionActivity,\n applyPtyStateToSession: (sessionId, ptyState) =>\n applyPtyStateToSession(ptyBridgeDeps, sessionId, ptyState),\n onSessionClosed: eventBridge.cleanupSessionResources,\n });\n\n relayConnection.connect();\n serviceLogger.info(\n {\n relayName: proxyConfig.relayName,\n profile: PROFILE_NAME,\n relayUrl,\n proxyName,\n tokenSet: !!relayToken,\n relayUrlSource: proxyConfig.sources.relayUrl,\n },\n \"Connecting to relay server\",\n );\n\n const relayRouter = new RelayRouter({\n sessionManager,\n workerRegistry,\n controlHandlers,\n relayConnection,\n relaySend,\n terminalSockets,\n hostedPtyRegistry,\n broadcastSessionList: () => broadcastSessionList(relayConnection, sessionManager),\n broadcastSessionSync: (session) => broadcastSessionSync(relayConnection, session),\n jsonObserver,\n createHookContext: hookRuntime.createHookContext,\n cleanupHookContext: (sessionId) => hookRuntime.hookRegistry.unregisterSession(sessionId),\n permissionBroker,\n hookEventRouter: hookRuntime.hookEventRouter,\n agentStatusRegistry,\n getProviderEnv,\n getAgentCliSuggestions,\n setAgentCliPath,\n getPreviewRoots,\n });\n\n relayConnection.on(\"message\", (msg: Record<string, unknown>) => relayRouter.handle(msg));\n relayConnection.on(\"connected\", () => {\n // fire-and-forget 但显式吞掉 rejection,否则 reinitializeOnReconnect 内部任意 IO 异常\n // 或 schema 校验错误会变 unhandledRejection,Node 默认终止整个 serve 进程。\n // 失败影响面: agent-cli-status / proxy_register_response 后的状态推送丢失, client 在\n // reconnect 后看到陈旧状态。属于服务降级而非健康降级, 用 error 级别让 ops 能接到告警。\n void controlHandlers.reinitializeOnReconnect().catch((err: unknown) => {\n serviceLogger.error(\n {\n error: err instanceof Error ? err.message : String(err),\n stack: err instanceof Error ? err.stack : undefined,\n },\n \"reinitializeOnReconnect failed: client may see stale state until next manual sync\",\n );\n });\n broadcastBridgeStatus(true);\n });\n relayConnection.on(\"disconnected\", () => {\n broadcastBridgeStatus(false);\n });\n\n // 把 relay 连接状态广播给所有已注册的 terminal,终端进程会 stderr 打 banner 提示用户\n function broadcastBridgeStatus(connected: boolean): void {\n const msg = serializeIpc({ type: \"bridge_status\", connected });\n for (const [, sock] of terminalSockets) {\n if (sock.writable) sock.write(msg);\n }\n }\n\n await workerRegistry.reconnectAll();\n\n const server = createServer((socket) => {\n handleTerminalConnection(socket, {\n sessionManager,\n workerRegistry,\n terminalSockets,\n hostedPtyRegistry,\n relayConnection,\n controlHandlers,\n agentStatusRegistry,\n permissionBroker,\n hookEventRouter: hookRuntime.hookEventRouter,\n createHookContext: hookRuntime.createHookContext,\n emitAgentStatus: eventBridge.emitAgentStatus,\n cleanupSessionResources: eventBridge.cleanupSessionResources,\n config: statusConfig,\n resolveInterruptedApprovals: (sessionId) =>\n resolveInterruptedApprovals(\n permissionBroker,\n hookRuntime.hookEventRouter,\n relayConnection,\n sessionId,\n ),\n });\n });\n\n server.listen(SOCK_PATH, () => {\n writeFileSync(PID_PATH, String(process.pid));\n chmodSync(SOCK_PATH, 0o600);\n serviceLogger.info({ pid: process.pid, sock: SOCK_PATH }, \"Service started\");\n });\n\n const shutdown = createServeShutdown({\n logger: serviceLogger,\n sessionManagerStopReaper: () => sessionManager.stopReaper(),\n relayRouterDestroy: () => relayRouter.destroy(),\n hookServerClose: () => hookRuntime.hookServer.close(),\n relayConnectionClose: () => relayConnection.close(),\n workerRegistryDestroyAll: () => workerRegistry.destroyAll(),\n hostedPtyRegistryDestroyAll: () => hostedPtyRegistry.destroyAll(),\n ipcServerClose: () => server.close(),\n sockPath: SOCK_PATH,\n pidPath: PID_PATH,\n });\n\n process.on(\"SIGTERM\", () => {\n void shutdown();\n });\n process.on(\"SIGINT\", () => {\n void shutdown();\n });\n}\n\nconst isMainModule =\n process.argv[1] && (process.argv[1].endsWith(\"serve.js\") || process.argv[1].endsWith(\"serve.ts\"));\n\nif (isMainModule) {\n startService(parseServiceOptions(process.argv.slice(2))).catch(async (err) => {\n const message = err instanceof Error ? err.message : String(err);\n serviceLogger.error({ err: message }, \"Service failed to start\");\n console.error(message);\n await flushLogger(serviceLogger);\n process.exit(1);\n });\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { nanoid } from \"nanoid\";\nimport { defineFSM, SessionState } from \"@dev-anywhere/shared\";\nimport { atomicWriteFileSync } from \"../common/atomic-write.js\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport type { ProviderId } from \"../providers/index.js\";\n\nexport interface SessionInfo {\n id: string;\n mode: \"pty\" | \"json\";\n provider: ProviderId;\n ptyOwner?: \"local-terminal\" | \"proxy-hosted\";\n state: SessionState;\n createdAt: number;\n updatedAt: number;\n name?: string;\n cwd: string;\n // Claude CLI 自己生成的 session ID,和上面 id 字段无关\n // 用途:定位 ~/.claude/projects/<encoded-cwd>/<claudeSessionId>.jsonl 历史文件 / 支持 --resume\n claudeSessionId?: string;\n pid: number;\n}\n\ninterface SessionManagerOptions {\n persistPath: string;\n reaperIntervalMs?: number;\n onSessionRemoved?: (id: string, context?: SessionRemoveContext) => void;\n}\n\ninterface SessionRemoveContext {\n preserveProviderHooks?: boolean;\n}\n\n// 两个观察通道的合法转换表分离:PTY 看 OSC 信号、JSON 看 stream-json 事件,各自的状态空间和规则不同。\n// terminated 是终态,不允许任何转出。\n\n// PTY 观察通道:从终端 OSC 0/9 信号 + idle timer 推导状态。\n// ERROR 在 PTY 观察通道不可达:PTY 错误体现为终端 ANSI 内容,proxy 不建模观察器失联。\nconst PTY_TRANSITIONS: Record<SessionState, readonly SessionState[]> = {\n [SessionState.IDLE]: [\n // claude 开始响应用户输入 → handlePtyData 首字节翻 working\n SessionState.WORKING,\n // provider hook 是语义事件,可能比 PTY 字节观察更早到达;PermissionRequest 可直接进入审批等待。\n SessionState.WAITING_APPROVAL,\n // 终态兜底;现阶段 terminated 走 terminateSession 直接删 map 不经 updateState,本边未被触发\n SessionState.TERMINATED,\n ],\n [SessionState.WORKING]: [\n // 5s 静默且 currentPtyState === \"working\" → idle timer 推 turn_complete\n SessionState.IDLE,\n // claude 发 OSC 9 \"needs your permission: <tool>\" → handlePtyData 推 approval_wait\n SessionState.WAITING_APPROVAL,\n // 终态兜底\n SessionState.TERMINATED,\n ],\n [SessionState.WAITING_APPROVAL]: [\n // 审批解除后 provider 可能继续工作,也可能直接结束本轮。\n // 真实 Claude 拒绝工具审批后会直接发 turn_complete,因此 WAITING_APPROVAL -> IDLE 是合法边。\n SessionState.WORKING,\n SessionState.IDLE,\n // 终态兜底\n SessionState.TERMINATED,\n ],\n // PTY 永不进入 ERROR;本行仅为满足 Record<SessionState,_> 枚举完整性保留\n [SessionState.ERROR]: [SessionState.TERMINATED],\n [SessionState.TERMINATED]: [],\n};\n\n// JSON 观察通道:从 stream-json 事件 + relay 入站消息推导状态。\n// 注意:turn 结束时 result.is_error === true 不走 ERROR——它属于 turn 内部错误,观察通道本身健康,仍按 onTurnResult → IDLE 处理。\nconst JSON_TRANSITIONS: Record<SessionState, readonly SessionState[]> = {\n [SessionState.IDLE]: [\n // 用户在 relay/web 端发消息 → onTurnStart,turn 开始\n SessionState.WORKING,\n // 空闲期观察通道失联(worker socket 死但 pid 仍在等)→ onChannelBroken\n SessionState.ERROR,\n // 终态兜底;同 PTY,当前不经 updateState\n SessionState.TERMINATED,\n ],\n [SessionState.WORKING]: [\n // stream-json result event → onTurnResult,turn 结束\n SessionState.IDLE,\n // claude 发 control_request → onApprovalRequested,阻塞等审批\n SessionState.WAITING_APPROVAL,\n // turn 进行中通道失联 → onChannelBroken\n SessionState.ERROR,\n // 终态兜底\n SessionState.TERMINATED,\n ],\n [SessionState.WAITING_APPROVAL]: [\n // 粒度丢失:审批解除后 claude 继续跑,proxy 观察不到中间的 WORKING 信号,\n // 直到 result event 才感知 → onTurnResult 一次性从 WAITING_APPROVAL 跳到 IDLE。\n // 因此不列 WAITING_APPROVAL → WORKING 这条边。\n SessionState.IDLE,\n // 审批死锁:control_response 写 worker stdin 失败 → onChannelBroken。\n // 这是 ERROR 态最明确的落地场景,让 UI 能区分 \"正在等用户决定\" 和 \"审批通道坏了\"。\n SessionState.ERROR,\n // 终态兜底\n SessionState.TERMINATED,\n ],\n [SessionState.ERROR]: [\n // 观察通道坏了之后只能 terminate,不回 IDLE/WORKING——恢复机制未实现\n SessionState.TERMINATED,\n ],\n [SessionState.TERMINATED]: [],\n};\n\nconst ptyFSM = defineFSM(PTY_TRANSITIONS);\nconst jsonFSM = defineFSM(JSON_TRANSITIONS);\n\nfunction fsmForMode(mode: \"pty\" | \"json\"): ReturnType<typeof defineFSM<SessionState>> {\n return mode === \"pty\" ? ptyFSM : jsonFSM;\n}\n\nfunction isProviderId(value: unknown): value is ProviderId {\n return value === \"claude\" || value === \"codex\";\n}\n\nexport class SessionManager {\n private sessions: Map<string, SessionInfo> = new Map();\n private reaperTimer: NodeJS.Timeout | null = null;\n private readonly persistPath: string;\n private readonly reaperIntervalMs: number;\n private readonly onSessionRemoved?: (id: string, context?: SessionRemoveContext) => void;\n\n constructor(options: SessionManagerOptions) {\n this.persistPath = options.persistPath;\n this.reaperIntervalMs = options.reaperIntervalMs ?? 60000;\n this.onSessionRemoved = options.onSessionRemoved;\n this.load();\n }\n\n createSession(\n mode: \"pty\" | \"json\",\n cwd: string,\n pid: number,\n name?: string,\n id?: string,\n provider: ProviderId = \"claude\",\n ptyOwner?: \"local-terminal\" | \"proxy-hosted\",\n ): SessionInfo {\n const now = Date.now();\n const info: SessionInfo = {\n id: id ?? nanoid(),\n mode,\n provider,\n ...(mode === \"pty\" && ptyOwner !== undefined ? { ptyOwner } : {}),\n state: SessionState.IDLE,\n createdAt: now,\n updatedAt: now,\n cwd,\n pid,\n ...(name !== undefined ? { name } : {}),\n };\n this.sessions.set(info.id, info);\n this.save();\n serviceLogger.info({ sessionId: info.id, mode, provider, ptyOwner, name }, \"Session created\");\n return info;\n }\n\n listSessions(): SessionInfo[] {\n return Array.from(this.sessions.values()).sort((a, b) => b.createdAt - a.createdAt);\n }\n\n getSession(id: string): SessionInfo | undefined {\n return this.sessions.get(id);\n }\n\n updateState(id: string, newState: SessionState): boolean {\n const session = this.sessions.get(id);\n if (!session) {\n // session 不存在是调用方 bug,不是观察竞态,保留 throw\n throw new Error(`Session not found: ${id}`);\n }\n const oldState = session.state;\n if (oldState === newState) return false;\n const fsm = fsmForMode(session.mode);\n if (!fsm.canTransition(oldState, newState)) {\n // 吸收态之后的残余转换来自进程竞态,降噪到 debug;\n // 其他非法转换属于协议违反或 bug,保持 warn 可观测\n const level = fsm.isAbsorbing(oldState) ? \"debug\" : \"warn\";\n serviceLogger[level](\n { sessionId: id, from: oldState, to: newState, mode: session.mode },\n level === \"debug\"\n ? \"State change after absorbing state (residual, likely race)\"\n : \"Invalid state transition rejected by FSM\",\n );\n return false;\n }\n session.state = newState;\n session.updatedAt = Date.now();\n this.save();\n serviceLogger.info({ sessionId: id, from: oldState, to: newState }, \"Session state changed\");\n return true;\n }\n\n touchSession(id: string, now: number = Date.now(), minIntervalMs = 0): boolean {\n const session = this.sessions.get(id);\n if (!session) return false;\n if (now - session.updatedAt < minIntervalMs) return false;\n session.updatedAt = now;\n this.save();\n return true;\n }\n\n terminateSession(id: string, context?: SessionRemoveContext): { success: boolean; pid?: number } {\n const session = this.sessions.get(id);\n if (!session) {\n return { success: false };\n }\n const pid = session.pid;\n this.sessions.delete(id);\n this.save();\n serviceLogger.info({ sessionId: id, mode: session.mode, pid }, \"Session terminated\");\n // 隔离 callback 异常: hook unregister / permission broker / 文件系统操作任意一步抛\n // 都不能让 terminateSession 把异常抛回调用方, 否则 socket close handler 上的后续\n // cleanupSessionResources + broadcastSessionList 会被吞掉, web 看到 session 残留。\n try {\n this.onSessionRemoved?.(id, context);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n serviceLogger.warn(\n {\n sessionId: id,\n err: { message: error.message, stack: error.stack, cause: error.cause },\n },\n \"onSessionRemoved callback threw; session already removed from registry\",\n );\n }\n return { success: true, pid };\n }\n\n terminateAll(): number[] {\n const pids: number[] = [];\n const ids = Array.from(this.sessions.keys());\n for (const id of ids) {\n const session = this.sessions.get(id)!;\n if (session.mode === \"json\" && session.pid !== undefined) {\n pids.push(session.pid);\n }\n this.sessions.delete(id);\n this.onSessionRemoved?.(id);\n }\n this.save();\n return pids;\n }\n\n setClaudeSessionId(id: string, claudeSessionId: string): void {\n const session = this.sessions.get(id);\n if (!session) {\n throw new Error(`Session not found: ${id}`);\n }\n session.claudeSessionId = claudeSessionId;\n this.save();\n }\n\n setPid(id: string, pid: number): void {\n const session = this.sessions.get(id);\n if (!session) {\n throw new Error(`Session not found: ${id}`);\n }\n session.pid = pid;\n this.save();\n }\n\n startReaper(intervalMs: number = this.reaperIntervalMs): void {\n this.stopReaper();\n this.reaperTimer = setInterval(() => this.reap(), intervalMs);\n }\n\n stopReaper(): void {\n if (this.reaperTimer) {\n clearInterval(this.reaperTimer);\n this.reaperTimer = null;\n }\n }\n\n private reap(): void {\n const toRemove: Array<{ id: string; reason: string }> = [];\n // 检查 JSON 会话的子进程是否存活\n // PTY 会话的生命周期由 IPC socket close 事件管理,不需要 reaper 参与\n for (const session of this.sessions.values()) {\n if (\n session.mode === \"json\" &&\n session.pid !== undefined &&\n session.state !== SessionState.TERMINATED\n ) {\n if (!this.isProcessAlive(session.pid)) {\n toRemove.push({ id: session.id, reason: `JSON worker process ${session.pid} is dead` });\n }\n }\n }\n for (const { id, reason } of toRemove) {\n serviceLogger.warn({ sessionId: id, reason }, \"Reaping stale session\");\n this.terminateSession(id);\n }\n }\n\n private isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n }\n\n private save(): void {\n // state 是对 claude 的观察值,进程死后无意义,不落盘。磁盘上只留 identity(id/mode/cwd/pid/...)。\n const persisted = Array.from(this.sessions.values()).map((s) => ({\n id: s.id,\n mode: s.mode,\n provider: s.provider,\n createdAt: s.createdAt,\n updatedAt: s.updatedAt,\n cwd: s.cwd,\n pid: s.pid,\n ...(s.name !== undefined ? { name: s.name } : {}),\n ...(s.claudeSessionId !== undefined ? { claudeSessionId: s.claudeSessionId } : {}),\n }));\n const data = JSON.stringify(persisted, null, 2);\n atomicWriteFileSync(this.persistPath, data, { ensureDir: true });\n }\n\n private load(): void {\n if (!existsSync(this.persistPath)) {\n return;\n }\n const raw = readFileSync(this.persistPath, \"utf-8\");\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n // 文件被截断 / 部分写入 / 手改成非法 JSON: 抛错会让 daemon 起不来, 用户没法 self-serve。\n // fail-soft 到空状态——已运行的 session 通过 reconnectAll 走 worker.sock 探活恢复, 还活着的\n // worker 仍能被 attach。失败仅意味着 session 名字 / cwd 等元数据丢失, 不至于阻塞启动。\n serviceLogger.warn(\n { path: this.persistPath, error: String(err) },\n \"Session persistence file unparseable, starting with empty state\",\n );\n return;\n }\n if (!Array.isArray(parsed)) {\n serviceLogger.warn(\n { path: this.persistPath },\n \"Session persistence file has unexpected format (not array), starting with empty state\",\n );\n return;\n }\n for (const item of parsed) {\n // state 字段不该落盘(见 save 注释)。遇到说明 schema 不匹配(旧版本数据 / 手改文件 / save bug);\n // 跳过该条而不是 throw,否则一条坏数据让所有 session 都加载不了,serve 起不来。\n if (item && typeof item === \"object\" && \"state\" in item) {\n const sessionId = String((item as { id?: unknown }).id);\n serviceLogger.warn(\n { sessionId },\n \"Session persistence record has unexpected state field, skipping\",\n );\n this.onSessionRemoved?.(sessionId);\n continue;\n }\n const info = item as Omit<SessionInfo, \"state\"> & { state?: SessionState };\n if (!isProviderId(info.provider)) {\n const sessionId = String(info.id);\n this.onSessionRemoved?.(sessionId);\n serviceLogger.warn(\n { sessionId, provider: info.provider },\n \"Session persistence file has invalid provider; cleaning session\",\n );\n continue;\n }\n if (info.mode === \"pty\") {\n if (info.pid && this.isProcessAlive(info.pid)) {\n // terminal 进程仍存活,会重连,保留磁盘数据但不加载到内存\n serviceLogger.info(\n { sessionId: info.id, pid: info.pid },\n \"PTY session skipped on load, terminal alive\",\n );\n } else {\n // terminal 进程已死,清理数据\n this.onSessionRemoved?.(info.id);\n serviceLogger.info(\n { sessionId: info.id, pid: info.pid },\n \"PTY session cleaned on load, terminal dead\",\n );\n }\n continue;\n }\n // JSON 会话:检查 worker 进程是否存活,无 PID 或进程已死则清理\n if (info.pid && this.isProcessAlive(info.pid)) {\n // 加载回内存时 state 重置为 IDLE,等后续观察通道信号刷新\n this.sessions.set(info.id, { ...info, state: SessionState.IDLE });\n } else {\n this.onSessionRemoved?.(info.id);\n serviceLogger.info(\n { sessionId: info.id, pid: info.pid },\n \"JSON session cleaned on load, worker dead\",\n );\n }\n }\n // 清理后回写磁盘,避免已清理的会话在下次启动时重复处理\n this.save();\n if (this.sessions.size > 0) {\n serviceLogger.info({ count: this.sessions.size }, \"Sessions restored from persistence\");\n }\n }\n}\n","import WebSocket from \"ws\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { nanoid } from \"nanoid\";\nimport { EventEmitter } from \"node:events\";\nimport { createFSM, serializeControl, type MessageEnvelope } from \"@dev-anywhere/shared\";\nimport { atomicWriteFileSync } from \"../common/atomic-write.js\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { MemoryMessageQueue } from \"./message-queue.js\";\n\n// 默认 proxyId 存储路径\nconst DEFAULT_PROXY_ID_PATH = join(homedir(), \".dev-anywhere\", \"proxy-id\");\n\n// 指数退避上限 30 秒\nconst MAX_BACKOFF_MS = 30000;\n// 退避基数 1 秒\nconst BASE_BACKOFF_MS = 1000;\n// 消息队列上限\nconst MAX_QUEUE_SIZE = 10000;\n// 来自 relay 的 JSON 控制消息最大长度(1MB)。挡住恶意/被攻陷的 relay 推超长 JSON 在\n// JSON.parse 前就 OOM proxy daemon。\nconst MAX_JSON_MESSAGE_SIZE = 1 * 1024 * 1024;\n\nexport const RelayConnectionState = {\n DISCONNECTED: \"disconnected\",\n CONNECTING: \"connecting\",\n REGISTERING: \"registering\",\n SYNCED: \"synced\",\n WAITING_RECONNECT: \"waiting_reconnect\",\n CLOSED: \"closed\",\n} as const;\nexport type RelayConnectionState = (typeof RelayConnectionState)[keyof typeof RelayConnectionState];\n\n// 合法的 WS 连接状态转移\n// CLOSED 是终态;connect 流转: DISCONNECTED → CONNECTING → REGISTERING → SYNCED\n// 断线: SYNCED/REGISTERING/CONNECTING → WAITING_RECONNECT → CONNECTING\n// 主动关: 任意 → CLOSED\nconst RELAY_TRANSITIONS: Record<RelayConnectionState, readonly RelayConnectionState[]> = {\n [RelayConnectionState.DISCONNECTED]: [\n RelayConnectionState.CONNECTING,\n RelayConnectionState.CLOSED,\n ],\n [RelayConnectionState.CONNECTING]: [\n RelayConnectionState.REGISTERING,\n RelayConnectionState.WAITING_RECONNECT,\n RelayConnectionState.CLOSED,\n ],\n [RelayConnectionState.REGISTERING]: [\n RelayConnectionState.SYNCED,\n RelayConnectionState.WAITING_RECONNECT,\n RelayConnectionState.CLOSED,\n ],\n [RelayConnectionState.SYNCED]: [\n RelayConnectionState.WAITING_RECONNECT,\n RelayConnectionState.CLOSED,\n ],\n [RelayConnectionState.WAITING_RECONNECT]: [\n RelayConnectionState.CONNECTING,\n RelayConnectionState.CLOSED,\n ],\n [RelayConnectionState.CLOSED]: [],\n};\n\ninterface RelayConnectionOptions {\n // 自定义 proxyId 文件路径,测试时使用临时目录\n proxyIdPath?: string;\n // proxy 显示名称,注册时发送给 relay\n name?: string;\n // 公网 relay 的 /proxy 端点预共享 token, relay 侧 RELAY_PROXY_TOKEN 对应\n token?: string;\n}\n\n// 管理代理到中转服务器的出站 WebSocket 连接,支持自动重连和消息队列\nexport class RelayConnection extends EventEmitter {\n private ws: WebSocket | null = null;\n private proxyId: string;\n private relayUrl: string;\n private queue: MemoryMessageQueue = new MemoryMessageQueue();\n private reconnectAttempt: number = 0;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private fsm = createFSM({\n initial: RelayConnectionState.DISCONNECTED as RelayConnectionState,\n transitions: RELAY_TRANSITIONS,\n onTransition: (from, to) =>\n serviceLogger.info({ from, to }, \"RelayConnection state transition\"),\n onRejected: (from, to, isAbsorbing) =>\n serviceLogger[isAbsorbing ? \"debug\" : \"warn\"](\n { from, to },\n isAbsorbing\n ? \"Late event after absorbing state, ignored\"\n : \"Invalid relay connection transition rejected\",\n ),\n });\n private name?: string;\n private token?: string;\n\n constructor(relayUrl: string, options?: RelayConnectionOptions) {\n super();\n this.relayUrl = relayUrl;\n this.proxyId = this.loadOrCreateProxyId(options?.proxyIdPath ?? DEFAULT_PROXY_ID_PATH);\n this.name = options?.name;\n this.token = options?.token;\n }\n\n // 从文件读取或生成新的 proxyId,生成后持久化到文件\n private loadOrCreateProxyId(idPath: string): string {\n if (existsSync(idPath)) {\n const existing = readFileSync(idPath, \"utf-8\").trim();\n if (existing.length > 0) {\n return existing;\n }\n }\n\n const id = nanoid(21);\n atomicWriteFileSync(idPath, id, { ensureDir: true });\n return id;\n }\n\n // 连接到 relay server\n connect(): void {\n if (!this.fsm.tryTransitionTo(RelayConnectionState.CONNECTING)) return;\n this.doConnect();\n }\n\n // 实际建立 WebSocket 连接的内部方法\n private doConnect(): void {\n try {\n const base = this.relayUrl.replace(/\\/$/, \"\") + \"/proxy\";\n const url = this.token ? `${base}?token=${encodeURIComponent(this.token)}` : base;\n this.ws = new WebSocket(url);\n\n this.ws.on(\"open\", () => {\n // open 属异步回调,若同步 close() 已先切 CLOSED,REGISTERING 会被拒,需跳过后续 register\n if (!this.fsm.tryTransitionTo(RelayConnectionState.REGISTERING)) return;\n serviceLogger.info(\n { proxyId: this.proxyId, url: base, tokenSet: !!this.token },\n \"Connected to relay server\",\n );\n this.ws!.send(\n serializeControl({\n type: \"proxy_register\",\n proxyId: this.proxyId,\n ...(this.name ? { name: this.name } : {}),\n }),\n );\n });\n\n this.ws.on(\"message\", (data) => {\n const buf = data as Buffer;\n if (buf.length > MAX_JSON_MESSAGE_SIZE) {\n serviceLogger.warn(\n { size: buf.length },\n \"JSON message from relay rejected: exceeds max size\",\n );\n return;\n }\n const raw = buf.toString();\n let msg: Record<string, unknown>;\n try {\n msg = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n serviceLogger.warn({ error: String(err) }, \"Non-JSON message from relay, dropped\");\n return;\n }\n if (msg.type === \"proxy_register_response\") {\n serviceLogger.info({ status: msg.status }, \"Received register response\");\n if (!this.fsm.tryTransitionTo(RelayConnectionState.SYNCED)) return;\n this.reconnectAttempt = 0;\n this.flushQueue();\n this.emit(\"connected\");\n return;\n }\n this.emit(\"message\", msg);\n });\n\n this.ws.on(\"close\", (code: number, reason: Buffer) => {\n this.ws = null;\n const closeMeta = { code, reason: reason.toString() || undefined };\n if (this.fsm.current() !== RelayConnectionState.CLOSED) {\n this.fsm.tryTransitionTo(RelayConnectionState.WAITING_RECONNECT);\n serviceLogger.info(closeMeta, \"Relay connection closed unexpectedly\");\n this.emit(\"disconnected\");\n this.scheduleReconnect();\n } else {\n serviceLogger.info(closeMeta, \"Relay connection closed\");\n }\n });\n\n this.ws.on(\"error\", (err) => {\n serviceLogger.error({ error: String(err) }, \"Relay connection error\");\n });\n } catch (err) {\n serviceLogger.error({ error: String(err) }, \"Failed to create relay connection\");\n if (this.fsm.current() !== RelayConnectionState.CLOSED) {\n this.fsm.tryTransitionTo(RelayConnectionState.WAITING_RECONNECT);\n this.scheduleReconnect();\n }\n }\n }\n\n // 将队列中缓存的消息依次发送到 relay\n private flushQueue(): void {\n for (const raw of this.queue.drain()) {\n this.ws?.send(raw);\n }\n }\n\n // 计算全抖动指数退避延迟并调度重连\n private scheduleReconnect(): void {\n const backoff =\n Math.random() *\n Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * Math.pow(2, this.reconnectAttempt));\n serviceLogger.info(\n { attempt: this.reconnectAttempt + 1, backoffMs: Math.round(backoff) },\n \"Scheduling reconnect\",\n );\n this.reconnectTimer = setTimeout(() => {\n this.reconnectAttempt++;\n // 必须先回 CONNECTING 才能让 open handler 合法转到 REGISTERING;\n // 若 close() 抢先切 CLOSED(clearTimeout 理论上拦得住,保险再守一层),跳过重连\n if (!this.fsm.tryTransitionTo(RelayConnectionState.CONNECTING)) return;\n this.doConnect();\n }, backoff);\n }\n\n // 发送 MessageEnvelope 到 relay,离线时自动入队\n sendEnvelope(envelope: MessageEnvelope): void {\n const raw = JSON.stringify(envelope);\n this.sendRaw(raw);\n }\n\n // 发送 binary PTY 帧到 relay,断线时直接丢弃不入队\n // 接受 Uint8Array 而非强制 Buffer:encodeBinaryFrame 在 shared 包返回 Uint8Array,\n // ws.send 在底层同样支持 Uint8Array,无需额外 Buffer.from 拷贝。\n sendBinary(data: Uint8Array): void {\n if (\n this.fsm.current() === RelayConnectionState.SYNCED &&\n this.ws?.readyState === WebSocket.OPEN\n ) {\n this.ws.send(data);\n }\n // binary 帧无队列,断线丢弃\n }\n\n // 发送原始 JSON 字符串到 relay,根据 connectionState 决定直发、入队或丢弃\n sendRaw(raw: string): void {\n if (\n this.fsm.current() === RelayConnectionState.SYNCED &&\n this.ws?.readyState === WebSocket.OPEN\n ) {\n this.ws.send(raw);\n } else if (this.fsm.current() === RelayConnectionState.CLOSED) {\n serviceLogger.warn(\"Message discarded: connection is closed\");\n } else {\n if (this.queue.size() >= MAX_QUEUE_SIZE) {\n const dropped = this.queue.dropOldest();\n serviceLogger.warn(\n { maxSize: MAX_QUEUE_SIZE },\n \"Message queue overflow, oldest message dropped\",\n );\n // 通知订阅方(WorkerRegistry)补偿被丢的 envelope,例如清理 pending 审批\n if (dropped !== null) this.emit(\"envelope_dropped\", dropped);\n }\n this.queue.enqueue(raw);\n serviceLogger.debug({ queueSize: this.queue.size() }, \"Message queued during disconnect\");\n }\n }\n\n // 主动关闭连接,发送 proxy_disconnect 通知 relay 立即清理,不触发重连\n close(): void {\n // 幂等:已 CLOSED 直接跳过,避免 FSM 抛 closed -> closed\n if (this.fsm.is(RelayConnectionState.CLOSED)) return;\n this.fsm.tryTransitionTo(RelayConnectionState.CLOSED);\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.ws) {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.send(serializeControl({ type: \"proxy_disconnect\", proxyId: this.proxyId }));\n }\n this.ws.close();\n this.ws = null;\n }\n }\n\n // 获取当前 proxyId\n getProxyId(): string {\n return this.proxyId;\n }\n\n // 获取连接状态摘要,用于 CLI status 输出\n getStatus(): {\n connected: boolean;\n connectionState: RelayConnectionState;\n proxyId: string;\n reconnectAttempt: number;\n queueDepth: number;\n } {\n return {\n connected: this.fsm.current() === RelayConnectionState.SYNCED,\n connectionState: this.fsm.current(),\n proxyId: this.proxyId,\n reconnectAttempt: this.reconnectAttempt,\n queueDepth: this.queue.size(),\n };\n }\n}\n","// 发送队列只负责内存背压和有序 drain;持久化恢复由 relay/proxy 重拉协议承担。\ninterface MessageQueue {\n enqueue(raw: string): void;\n drain(): string[];\n size(): number;\n clear(): void;\n dropOldest(): string | null;\n}\n\nexport class MemoryMessageQueue implements MessageQueue {\n private items: string[] = [];\n\n enqueue(raw: string): void {\n this.items.push(raw);\n }\n\n drain(): string[] {\n const all = this.items;\n this.items = [];\n return all;\n }\n\n size(): number {\n return this.items.length;\n }\n\n clear(): void {\n this.items = [];\n }\n\n // 丢弃最旧消息,返回被丢弃的 raw 供 caller 做补偿(例如清理对应 pending 审批)\n dropOldest(): string | null {\n return this.items.shift() ?? null;\n }\n}\n","import { readdir, mkdir } from \"node:fs/promises\";\nimport { join, isAbsolute, normalize } from \"node:path\";\nimport { ControlErrorCode, serializeControl } from \"@dev-anywhere/shared\";\nimport type { SessionManager } from \"../session-manager.js\";\nimport { scanSessionHistory } from \"../session-history.js\";\nimport { discoverCommands } from \"../command-discovery.js\";\nimport { serviceLogger } from \"../../common/logger.js\";\nimport { classifyPathError } from \"../path-errors.js\";\n\nexport interface ControlMessageHandlers {\n handleDirListRequest(msg: { path: string; requestId?: string }): Promise<void>;\n handleDirCreateRequest(msg: { path: string; requestId?: string }): Promise<void>;\n handleSessionHistoryRequest(msg: { requestId?: string }): Promise<void>;\n handleSessionResourcesRequest(msg: {\n sessionId: string;\n requestId?: string;\n workDir: string;\n }): Promise<void>;\n pushCommandList(sessionId: string, workDir: string): Promise<void>;\n pushFileTree(sessionId: string, workDir: string): Promise<void>;\n reinitializeOnReconnect(): Promise<void>;\n cleanup(sessionId: string): void;\n}\n\n// 每个 session 的定时器和资源\ninterface SessionResources {\n commandRefreshTimer?: NodeJS.Timeout;\n fileTreeWorkDir?: string;\n}\n\n// 命令刷新间隔 6 小时\nconst COMMAND_REFRESH_MS = 6 * 60 * 60 * 1000;\n\n// 路径安全校验:拒绝相对路径和路径遍历\nfunction isPathSafe(path: string): boolean {\n if (!isAbsolute(path)) return false;\n const normalized = normalize(path);\n // 检查 normalize 后是否仍包含 ..(理论上不会,但做防御)\n if (normalized.includes(\"..\")) return false;\n return true;\n}\n\n// picker 展示忽略规则: dotfile + node_modules\n// listDirectory (按需) 与 getFileTree (预热) 必须共用, 否则逐层下钻会暴露 node_modules\nconst HIDDEN_ENTRY_NAMES = new Set([\"node_modules\"]);\nfunction isPickerVisible(name: string): boolean {\n return !name.startsWith(\".\") && !HIDDEN_ENTRY_NAMES.has(name);\n}\n\n// 目录优先 + 字母序, picker 侧依赖这个顺序做键盘导航默认选中\nfunction sortEntries(\n a: { isDir: boolean; name: string },\n b: { isDir: boolean; name: string },\n): number {\n if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;\n return a.name.localeCompare(b.name);\n}\n\nasync function scanDir(dirPath: string): Promise<Array<{ name: string; isDir: boolean }>> {\n const entries = await readdir(dirPath, { withFileTypes: true });\n return entries\n .filter((e) => isPickerVisible(e.name))\n .map((e) => ({ name: e.name, isDir: e.isDirectory() }))\n .sort(sortEntries);\n}\n\n// 预热 cwd + 直接子目录两层, 按目录分组返回, 前端写入 tree[path] 后逐层 picker 直接命中\ninterface FileTreeGroup {\n path: string;\n entries: Array<{ name: string; isDir: boolean }>;\n}\n\nasync function getFileTree(rootPath: string): Promise<FileTreeGroup[]> {\n const groups: FileTreeGroup[] = [];\n\n let rootEntries: Array<{ name: string; isDir: boolean }>;\n try {\n rootEntries = await scanDir(rootPath);\n } catch {\n return groups;\n }\n groups.push({ path: rootPath, entries: rootEntries });\n\n for (const sub of rootEntries) {\n if (!sub.isDir) continue;\n const subPath = join(rootPath, sub.name);\n try {\n const subEntries = await scanDir(subPath);\n groups.push({ path: subPath, entries: subEntries });\n } catch {\n // 无法读取子目录, 跳过这一层分组 (picker 会在点击时触发 dir_list_request 补齐)\n }\n }\n\n return groups;\n}\n\nexport function createControlMessageHandlers(\n send: (data: string) => void,\n sessionManager: SessionManager,\n): ControlMessageHandlers {\n const sessionResources = new Map<string, SessionResources>();\n\n function getResources(sessionId: string): SessionResources {\n let res = sessionResources.get(sessionId);\n if (!res) {\n res = {};\n sessionResources.set(sessionId, res);\n }\n return res;\n }\n\n function scheduleCommandRefresh(sessionId: string, workDir: string): void {\n const resources = getResources(sessionId);\n if (resources.commandRefreshTimer) {\n clearInterval(resources.commandRefreshTimer);\n }\n resources.commandRefreshTimer = setInterval(async () => {\n try {\n const commands = await discoverCommands(workDir);\n send(\n serializeControl({\n type: \"command_list_push\",\n commands,\n }),\n );\n serviceLogger.debug({ sessionId, count: commands.length }, \"Command list refreshed\");\n } catch (err) {\n serviceLogger.warn({ sessionId, error: String(err) }, \"Command refresh failed\");\n }\n }, COMMAND_REFRESH_MS);\n }\n\n return {\n async handleDirListRequest(msg: { path: string; requestId?: string }): Promise<void> {\n if (!isPathSafe(msg.path)) {\n send(\n serializeControl({\n type: \"dir_list_response\",\n requestId: msg.requestId,\n path: msg.path,\n entries: [],\n errorCode: ControlErrorCode.INVALID_PATH,\n error: \"Invalid path: must be absolute and must not contain path traversal\",\n }),\n );\n serviceLogger.warn({ path: msg.path }, \"Rejected dir_list_request: unsafe path\");\n return;\n }\n\n try {\n const entries = await scanDir(msg.path);\n send(\n serializeControl({\n type: \"dir_list_response\",\n requestId: msg.requestId,\n path: msg.path,\n entries,\n }),\n );\n serviceLogger.debug({ path: msg.path, count: entries.length }, \"Dir list response sent\");\n } catch (err) {\n send(\n serializeControl({\n type: \"dir_list_response\",\n requestId: msg.requestId,\n path: msg.path,\n entries: [],\n errorCode: classifyPathError(err),\n error: String(err),\n }),\n );\n serviceLogger.warn({ path: msg.path, error: String(err) }, \"Dir list request failed\");\n }\n },\n\n async handleDirCreateRequest(msg: { path: string; requestId?: string }): Promise<void> {\n if (!isPathSafe(msg.path)) {\n send(\n serializeControl({\n type: \"dir_create_response\",\n requestId: msg.requestId,\n path: msg.path,\n success: false,\n errorCode: ControlErrorCode.INVALID_PATH,\n error: \"Invalid path: must be absolute and must not contain path traversal\",\n }),\n );\n serviceLogger.warn({ path: msg.path }, \"Rejected dir_create_request: unsafe path\");\n return;\n }\n\n try {\n await mkdir(msg.path, { recursive: true });\n send(\n serializeControl({\n type: \"dir_create_response\",\n requestId: msg.requestId,\n path: msg.path,\n success: true,\n }),\n );\n serviceLogger.info({ path: msg.path }, \"Directory created\");\n } catch (err) {\n send(\n serializeControl({\n type: \"dir_create_response\",\n requestId: msg.requestId,\n path: msg.path,\n success: false,\n errorCode: classifyPathError(err),\n error: String(err),\n }),\n );\n serviceLogger.warn({ path: msg.path, error: String(err) }, \"Dir create failed\");\n }\n },\n\n async handleSessionHistoryRequest(msg: { requestId?: string }): Promise<void> {\n try {\n const sessions = await scanSessionHistory();\n send(\n serializeControl({\n type: \"session_history_response\",\n requestId: msg.requestId,\n sessions,\n }),\n );\n serviceLogger.debug({ count: sessions.length }, \"Session history response sent\");\n } catch (err) {\n send(\n serializeControl({\n type: \"session_history_response\",\n requestId: msg.requestId,\n sessions: [],\n }),\n );\n serviceLogger.warn({ error: String(err) }, \"Session history scan failed\");\n }\n },\n\n async handleSessionResourcesRequest(msg: {\n sessionId: string;\n requestId?: string;\n workDir: string;\n }): Promise<void> {\n getResources(msg.sessionId).fileTreeWorkDir = msg.workDir;\n scheduleCommandRefresh(msg.sessionId, msg.workDir);\n\n const [commandsResult, groupsResult] = await Promise.allSettled([\n discoverCommands(msg.workDir),\n getFileTree(msg.workDir),\n ]);\n const commands = commandsResult.status === \"fulfilled\" ? commandsResult.value : [];\n const groups = groupsResult.status === \"fulfilled\" ? groupsResult.value : [];\n const failedReason =\n commandsResult.status === \"rejected\"\n ? commandsResult.reason\n : groupsResult.status === \"rejected\"\n ? groupsResult.reason\n : undefined;\n\n send(\n serializeControl({\n type: \"session_resources_response\",\n requestId: msg.requestId,\n sessionId: msg.sessionId,\n commands,\n groups,\n ...(failedReason\n ? {\n errorCode: classifyPathError(failedReason),\n error: String(failedReason),\n }\n : {}),\n }),\n );\n serviceLogger.info(\n { sessionId: msg.sessionId, commandCount: commands.length, groupCount: groups.length },\n \"Session resources snapshot sent\",\n );\n },\n\n async pushCommandList(sessionId: string, workDir: string): Promise<void> {\n try {\n const commands = await discoverCommands(workDir);\n send(\n serializeControl({\n type: \"command_list_push\",\n commands,\n }),\n );\n serviceLogger.info({ sessionId, count: commands.length, workDir }, \"Command list pushed\");\n } catch (err) {\n serviceLogger.warn({ sessionId, error: String(err) }, \"Command discovery failed\");\n }\n\n // 6 小时定时刷新\n scheduleCommandRefresh(sessionId, workDir);\n },\n\n async pushFileTree(sessionId: string, workDir: string): Promise<void> {\n const resources = getResources(sessionId);\n resources.fileTreeWorkDir = workDir;\n\n try {\n const groups = await getFileTree(workDir);\n send(\n serializeControl({\n type: \"file_tree_push\",\n groups,\n }),\n );\n serviceLogger.debug(\n { sessionId, path: workDir, groupCount: groups.length },\n \"File tree pushed\",\n );\n } catch (err) {\n serviceLogger.warn({ sessionId, error: String(err) }, \"File tree push failed\");\n }\n },\n\n // relay 重连时同步 session 列表并重新推送控制数据\n async reinitializeOnReconnect(): Promise<void> {\n const activeSessions = sessionManager.listSessions().filter((s) => s.state !== \"terminated\");\n\n // 先同步 session 列表,relay 据此建立 proxy-session 关联\n if (activeSessions.length > 0) {\n send(\n serializeControl({\n type: \"session_sync\",\n sessions: activeSessions.map((s) => ({\n id: s.id,\n mode: s.mode,\n provider: s.provider,\n ...(s.ptyOwner !== undefined ? { ptyOwner: s.ptyOwner } : {}),\n state: s.state,\n })),\n }),\n );\n serviceLogger.info({ count: activeSessions.length }, \"Session list synced to relay\");\n }\n\n for (const session of activeSessions) {\n const resources = sessionResources.get(session.id);\n const workDir = resources?.fileTreeWorkDir;\n if (workDir) {\n try {\n const commands = await discoverCommands(workDir);\n send(\n serializeControl({\n type: \"command_list_push\",\n commands,\n }),\n );\n const groups = await getFileTree(workDir);\n send(\n serializeControl({\n type: \"file_tree_push\",\n groups,\n }),\n );\n serviceLogger.info(\n { sessionId: session.id },\n \"Reinitialized control data after reconnect\",\n );\n } catch (err) {\n serviceLogger.warn(\n { sessionId: session.id, error: String(err) },\n \"Reinitialize failed\",\n );\n }\n }\n }\n },\n\n cleanup(sessionId: string): void {\n const resources = sessionResources.get(sessionId);\n if (resources) {\n if (resources.commandRefreshTimer) {\n clearInterval(resources.commandRefreshTimer);\n }\n sessionResources.delete(sessionId);\n }\n },\n };\n}\n","import { readdir, stat, access, open } from \"node:fs/promises\";\nimport { createReadStream } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createInterface } from \"node:readline\";\n\ninterface SessionHistoryEntry {\n id: string;\n title: string;\n projectDir: string;\n updatedAt: number;\n provider: \"claude\" | \"codex\";\n}\n\nconst claudeProjectsDir = (): string => join(homedir(), \".claude\", \"projects\");\nconst codexSessionsDir = (): string => join(homedir(), \".codex\", \"sessions\");\nconst UNTITLED_SESSION_TITLE = \"未命名会话\";\nconst MAX_HISTORY_TITLE_LENGTH = 40;\nconst IGNORED_SLASH_COMMANDS = new Set([\n \"/clear\",\n \"/model\",\n \"/compact\",\n \"/help\",\n \"/config\",\n \"/logout\",\n]);\nconst XMLISH_NOISE_PREFIXES = [\n \"environment\",\n \"system\",\n \"developer\",\n \"assistant\",\n \"user\",\n \"tool\",\n \"context\",\n];\nconst INTERNAL_TITLE_PATTERNS = [\n /^the following is the codex agent history\\b/i,\n /^codex agent history\\b/i,\n /^conversation summary\\b/i,\n];\n\n// 扫描 ~/.claude/projects/ 获取 Claude Code 会话历史\n// 实际目录结构: ~/.claude/projects/<encoded-project-path>/<session-id>.jsonl\nexport async function scanSessionHistory(): Promise<SessionHistoryEntry[]> {\n const entries = [...(await scanClaudeSessionHistory()), ...(await scanCodexSessionHistory())];\n entries.sort((a, b) => b.updatedAt - a.updatedAt);\n // 按 provider + title + projectDir 去重,resume 产生的多个 session 只保留最新的\n const seen = new Set<string>();\n return entries.filter((e) => {\n const key = `${e.provider}::${e.projectDir}::${e.title}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\nasync function scanClaudeSessionHistory(): Promise<SessionHistoryEntry[]> {\n const entries: SessionHistoryEntry[] = [];\n let projectDirs: string[];\n try {\n projectDirs = await readdir(claudeProjectsDir());\n } catch {\n return [];\n }\n\n for (const encodedDir of projectDirs) {\n const projectPath = join(claudeProjectsDir(), encodedDir);\n\n let files: string[];\n try {\n files = await readdir(projectPath);\n } catch {\n continue;\n }\n\n for (const file of files) {\n if (!file.endsWith(\".jsonl\")) continue;\n\n const filePath = join(projectPath, file);\n try {\n const fileStat = await stat(filePath);\n const sessionId = file.replace(/\\.jsonl$/, \"\");\n const { title, cwd } = await extractTitleAndCwd(filePath);\n\n entries.push({\n id: sessionId,\n title: title || UNTITLED_SESSION_TITLE,\n projectDir: cwd || \"/\" + encodedDir.replace(/^-/, \"\").split(\"-\").join(\"/\"),\n updatedAt: fileStat.mtimeMs,\n provider: \"claude\",\n });\n } catch {\n continue;\n }\n }\n }\n\n return entries;\n}\n\nasync function scanCodexSessionHistory(): Promise<SessionHistoryEntry[]> {\n const files = await collectJsonlFiles(codexSessionsDir());\n const entries: SessionHistoryEntry[] = [];\n for (const filePath of files) {\n try {\n const fileStat = await stat(filePath);\n const meta = await extractCodexTitleAndCwd(filePath);\n if (!meta.id) continue;\n entries.push({\n id: meta.id,\n title: meta.title || UNTITLED_SESSION_TITLE,\n projectDir: meta.cwd || homedir(),\n updatedAt: fileStat.mtimeMs,\n provider: \"codex\",\n });\n } catch {\n continue;\n }\n }\n return entries;\n}\n\ninterface SessionMessage {\n role: \"user\" | \"assistant\";\n text: string;\n timestamp?: number;\n cursor?: string;\n}\n\ninterface SessionMessagesPage {\n messages: SessionMessage[];\n hasMore: boolean;\n nextBefore?: string;\n}\n\ninterface SessionMessagesPageOptions {\n limit?: number;\n before?: string;\n}\n\nconst DEFAULT_HISTORY_PAGE_LIMIT = 50;\nconst MAX_HISTORY_PAGE_LIMIT = 200;\nconst HISTORY_READ_CHUNK_BYTES = 64 * 1024;\nconst HISTORY_CURSOR_PREFIX = \"b:\";\n\nfunction normalizeHistoryPageLimit(limit: unknown): number {\n if (typeof limit !== \"number\" || !Number.isFinite(limit)) return DEFAULT_HISTORY_PAGE_LIMIT;\n return Math.max(1, Math.min(MAX_HISTORY_PAGE_LIMIT, Math.floor(limit)));\n}\n\nfunction encodeHistoryCursor(offset: number): string {\n return `${HISTORY_CURSOR_PREFIX}${Math.max(0, Math.floor(offset))}`;\n}\n\nfunction decodeHistoryCursor(cursor: string | undefined, fileSize: number): number {\n if (!cursor) return fileSize;\n const raw = cursor.startsWith(HISTORY_CURSOR_PREFIX)\n ? cursor.slice(HISTORY_CURSOR_PREFIX.length)\n : cursor;\n const parsed = Number(raw);\n if (!Number.isInteger(parsed) || parsed < 0) return fileSize;\n return Math.min(parsed, fileSize);\n}\n\n// claudeSessionId 由 claude 自身生成(UUID),但既然落盘后会被拼进文件路径,\n// 防御性正则确保任何来源的不规范值都不会越过 ~/.claude/projects/<dir>/ 边界。\n// 允许字母数字、下划线、短横线,足以覆盖 UUID 与历史 fixture,禁止 . / \\ \\0 等路径字符。\nconst SAFE_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]+$/;\n\nasync function findClaudeSessionFile(claudeSessionId: string): Promise<string | null> {\n if (!SAFE_SESSION_ID_PATTERN.test(claudeSessionId)) return null;\n\n let projectDirs: string[];\n try {\n projectDirs = await readdir(claudeProjectsDir());\n } catch {\n return null;\n }\n\n for (const encodedDir of projectDirs) {\n const filePath = join(claudeProjectsDir(), encodedDir, `${claudeSessionId}.jsonl`);\n try {\n await access(filePath);\n return filePath;\n } catch {\n continue;\n }\n }\n\n return null;\n}\n\nfunction extractConversationMessageFromJson(obj: unknown): Omit<SessionMessage, \"cursor\"> | null {\n if (!obj || typeof obj !== \"object\") return null;\n const record = obj as {\n type?: unknown;\n isMeta?: unknown;\n message?: unknown;\n timestamp?: unknown;\n };\n if (record.type === \"user\") {\n if (record.isMeta) return null;\n const text = extractConversationText(record.message);\n if (!text) return null;\n const ts =\n typeof record.timestamp === \"string\" ? new Date(record.timestamp).getTime() : undefined;\n return { role: \"user\", text, timestamp: ts };\n }\n if (record.type === \"assistant\") {\n const text = extractConversationText(record.message);\n if (!text) return null;\n const ts =\n typeof record.timestamp === \"string\" ? new Date(record.timestamp).getTime() : undefined;\n return { role: \"assistant\", text, timestamp: ts };\n }\n return null;\n}\n\nfunction splitLineSegments(\n block: Buffer,\n blockStart: number,\n): Array<{ start: number; line: Buffer }> {\n const segments: Array<{ start: number; line: Buffer }> = [];\n let start = 0;\n for (let i = 0; i < block.length; i += 1) {\n if (block[i] !== 10) continue;\n segments.push({ start: blockStart + start, line: block.subarray(start, i) });\n start = i + 1;\n }\n segments.push({ start: blockStart + start, line: block.subarray(start) });\n return segments;\n}\n\nfunction stripCarriageReturn(line: Buffer): Buffer {\n return line.length > 0 && line[line.length - 1] === 13 ? line.subarray(0, -1) : line;\n}\n\nasync function readSessionMessagesPageFromFile(\n filePath: string,\n options: SessionMessagesPageOptions = {},\n): Promise<SessionMessagesPage> {\n const limit = normalizeHistoryPageLimit(options.limit);\n const file = await open(filePath, \"r\");\n try {\n const fileStat = await file.stat();\n const endOffset = decodeHistoryCursor(options.before, fileStat.size);\n if (endOffset <= 0) return { messages: [], hasMore: false };\n\n let position = endOffset;\n let carry: Buffer = Buffer.alloc(0);\n const collected: SessionMessage[] = [];\n\n while (position > 0 && collected.length <= limit) {\n const readSize = Math.min(HISTORY_READ_CHUNK_BYTES, position);\n position -= readSize;\n const chunk = Buffer.alloc(readSize);\n await file.read(chunk, 0, readSize, position);\n\n const block = carry.length > 0 ? Buffer.concat([chunk, carry]) : chunk;\n const segments = splitLineSegments(block, position);\n const firstCompleteIndex = position > 0 ? 1 : 0;\n carry = position > 0 ? (segments[0]?.line ?? Buffer.alloc(0)) : Buffer.alloc(0);\n\n for (let i = segments.length - 1; i >= firstCompleteIndex; i -= 1) {\n const segment = segments[i];\n if (!segment) continue;\n const line = stripCarriageReturn(segment.line);\n if (line.length === 0) continue;\n try {\n const parsed = JSON.parse(line.toString(\"utf-8\"));\n const message = extractConversationMessageFromJson(parsed);\n if (!message) continue;\n collected.push({ ...message, cursor: encodeHistoryCursor(segment.start) });\n if (collected.length > limit) break;\n } catch {\n /* skip malformed lines */\n }\n }\n }\n\n const page = collected.slice(0, limit).reverse();\n const hasMore = collected.length > limit;\n return {\n messages: page,\n hasMore,\n ...(hasMore && page[0]?.cursor ? { nextBefore: page[0].cursor } : {}),\n };\n } finally {\n await file.close();\n }\n}\n\n// 从 JSONL 文件中提取 user/assistant 对话消息用于恢复时展示历史\nexport async function readSessionMessages(claudeSessionId: string): Promise<SessionMessage[]> {\n const filePath = await findClaudeSessionFile(claudeSessionId);\n if (!filePath) return [];\n\n const messages: SessionMessage[] = [];\n return new Promise((resolve) => {\n const rl = createInterface({\n input: createReadStream(filePath, { encoding: \"utf-8\" }),\n crlfDelay: Infinity,\n });\n\n rl.on(\"line\", (line) => {\n if (!line.trim()) return;\n try {\n const message = extractConversationMessageFromJson(JSON.parse(line));\n if (message) messages.push(message);\n } catch {\n /* skip */\n }\n });\n\n rl.on(\"close\", () => resolve(messages));\n rl.on(\"error\", () => resolve(messages));\n });\n}\n\nexport async function readSessionMessagesPage(\n claudeSessionId: string,\n options: SessionMessagesPageOptions = {},\n): Promise<SessionMessagesPage> {\n const filePath = await findClaudeSessionFile(claudeSessionId);\n if (!filePath) return { messages: [], hasMore: false };\n return readSessionMessagesPageFromFile(filePath, options);\n}\n\n// 从 message 字段提取文本,统一处理多种格式\nfunction collapseWhitespace(text: string): string {\n return text.replace(/\\s+/g, \" \").trim();\n}\n\nfunction truncateTitle(text: string): string {\n const chars = Array.from(text);\n return chars.length > MAX_HISTORY_TITLE_LENGTH\n ? `${chars.slice(0, MAX_HISTORY_TITLE_LENGTH).join(\"\")}...`\n : text;\n}\n\nfunction isXmlishNoise(text: string): boolean {\n const match = text.match(/^<([A-Za-z][\\w:-]*)\\b/);\n if (!match) return false;\n const tag = match[1].toLowerCase();\n return XMLISH_NOISE_PREFIXES.some((prefix) => tag === prefix || tag.startsWith(`${prefix}_`));\n}\n\nexport function normalizeHistoryTitle(raw: string | null | undefined): string | null {\n if (!raw) return null;\n const text = collapseWhitespace(raw);\n if (text.length < 2) return null;\n if (text.startsWith(\"<\") || isXmlishNoise(text)) return null;\n if (INTERNAL_TITLE_PATTERNS.some((pattern) => pattern.test(text))) return null;\n\n const slashCommand = text.match(/^\\/\\S+/)?.[0];\n if (slashCommand && IGNORED_SLASH_COMMANDS.has(slashCommand)) return null;\n\n return truncateTitle(text);\n}\n\nfunction extractSlashCommand(text: string): string | null {\n const nameMatch = text.match(/<command-name>([^<]+)<\\/command-name>/);\n if (!nameMatch) return null;\n const argsMatch = text.match(/<command-args>([^<]+)<\\/command-args>/);\n const args = argsMatch ? argsMatch[1].trim() : \"\";\n return normalizeHistoryTitle(args ? `${nameMatch[1]} ${args}` : nameMatch[1]);\n}\n\nfunction extractMessageText(msg: unknown): string | null {\n if (typeof msg === \"string\") {\n const cmd = extractSlashCommand(msg);\n if (cmd) return cmd;\n return normalizeHistoryTitle(msg);\n }\n\n if (msg && typeof msg === \"object\" && \"content\" in msg) {\n const content = (msg as { content: unknown }).content;\n if (typeof content === \"string\") {\n const cmd = extractSlashCommand(content);\n if (cmd) return cmd;\n return normalizeHistoryTitle(content);\n }\n if (Array.isArray(content)) {\n const texts = content\n .filter(\n (b: { type?: string; text?: string }) => b.type === \"text\" && typeof b.text === \"string\",\n )\n .map((b: { text: string }) => b.text);\n const joined = texts.join(\"\\n\").trim();\n return normalizeHistoryTitle(joined);\n }\n }\n\n if (Array.isArray(msg)) {\n const texts = msg\n .filter(\n (b: { type?: string; text?: string }) => b.type === \"text\" && typeof b.text === \"string\",\n )\n .map((b: { text: string }) => b.text);\n const joined = texts.join(\"\\n\").trim();\n return normalizeHistoryTitle(joined);\n }\n\n return null;\n}\n\nfunction normalizeConversationText(text: string): string | null {\n const trimmed = text.trim();\n if (!trimmed) return null;\n return trimmed;\n}\n\n// 对话正文恢复必须保留换行和 Markdown 结构;不能复用标题归一化逻辑。\nfunction extractConversationText(msg: unknown): string | null {\n if (typeof msg === \"string\") {\n const cmd = extractSlashCommand(msg);\n if (cmd) return cmd;\n return normalizeConversationText(msg);\n }\n\n if (msg && typeof msg === \"object\" && \"content\" in msg) {\n const content = (msg as { content: unknown }).content;\n if (typeof content === \"string\") {\n const cmd = extractSlashCommand(content);\n if (cmd) return cmd;\n return normalizeConversationText(content);\n }\n if (Array.isArray(content)) {\n const texts = content\n .filter(\n (b: { type?: string; text?: string }) => b.type === \"text\" && typeof b.text === \"string\",\n )\n .map((b: { text: string }) => b.text);\n return normalizeConversationText(texts.join(\"\\n\"));\n }\n }\n\n if (Array.isArray(msg)) {\n const texts = msg\n .filter(\n (b: { type?: string; text?: string }) => b.type === \"text\" && typeof b.text === \"string\",\n )\n .map((b: { text: string }) => b.text);\n return normalizeConversationText(texts.join(\"\\n\"));\n }\n\n return null;\n}\n\n// 从 JSONL 文件头部提取 cwd 和第一条有效用户文本消息作为标题\n// cwd 从任意行的 cwd 字段获取,title 从第一条 user 消息获取\nasync function extractTitleAndCwd(\n filePath: string,\n): Promise<{ title: string | null; cwd: string | null }> {\n return new Promise((resolve) => {\n const rl = createInterface({\n input: createReadStream(filePath, { encoding: \"utf-8\" }),\n crlfDelay: Infinity,\n });\n let resolved = false;\n let cwd: string | null = null;\n let title: string | null = null;\n\n rl.on(\"line\", (line) => {\n if (resolved) return;\n if (!line.trim()) return;\n\n try {\n const obj = JSON.parse(line);\n if (!cwd && typeof obj.cwd === \"string\") {\n cwd = obj.cwd;\n }\n if (!title && obj.type === \"user\" && !obj.isMeta) {\n const text = extractMessageText(obj.message);\n if (text) title = text;\n }\n if (cwd && title) {\n resolved = true;\n rl.close();\n }\n } catch {\n /* skip malformed lines */\n }\n });\n\n rl.on(\"close\", () => {\n if (!resolved) resolve({ title, cwd });\n else resolve({ title, cwd });\n });\n rl.on(\"error\", () => resolve({ title, cwd }));\n });\n}\n\nasync function collectJsonlFiles(root: string): Promise<string[]> {\n let entries: Array<{ name: string; isDirectory(): boolean; isFile(): boolean }>;\n try {\n entries = await readdir(root, { withFileTypes: true });\n } catch {\n return [];\n }\n\n const files: string[] = [];\n for (const entry of entries) {\n const child = join(root, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await collectJsonlFiles(child)));\n } else if (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n files.push(child);\n }\n }\n return files;\n}\n\nasync function extractCodexTitleAndCwd(\n filePath: string,\n): Promise<{ id: string | null; title: string | null; cwd: string | null }> {\n return new Promise((resolve) => {\n const rl = createInterface({\n input: createReadStream(filePath, { encoding: \"utf-8\" }),\n crlfDelay: Infinity,\n });\n let id: string | null = null;\n let cwd: string | null = null;\n let title: string | null = null;\n\n rl.on(\"line\", (line) => {\n if (!line.trim()) return;\n try {\n const obj = JSON.parse(line);\n if (obj.type === \"session_meta\" && obj.payload) {\n if (!id && typeof obj.payload.id === \"string\") id = obj.payload.id;\n if (!cwd && typeof obj.payload.cwd === \"string\") cwd = obj.payload.cwd;\n }\n if (!title && obj.type === \"response_item\") {\n const text = extractCodexUserText(obj.payload);\n if (text) title = text;\n }\n if (id && cwd && title) rl.close();\n } catch {\n /* skip malformed lines */\n }\n });\n\n rl.on(\"close\", () => resolve({ id, title, cwd }));\n rl.on(\"error\", () => resolve({ id, title, cwd }));\n });\n}\n\nfunction extractCodexUserText(payload: unknown): string | null {\n if (!payload || typeof payload !== \"object\") return null;\n const item = payload as { type?: unknown; role?: unknown; content?: unknown };\n if (item.type !== \"message\" || item.role !== \"user\") return null;\n if (typeof item.content === \"string\") return normalizeHistoryTitle(item.content);\n if (!Array.isArray(item.content)) return null;\n const texts = item.content\n .map((block: unknown) => {\n if (!block || typeof block !== \"object\") return \"\";\n const typed = block as { type?: unknown; text?: unknown };\n return typed.type === \"input_text\" && typeof typed.text === \"string\" ? typed.text : \"\";\n })\n .filter(Boolean);\n const joined = texts.join(\"\\n\").trim();\n return normalizeHistoryTitle(joined);\n}\n","import { readdirSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\ninterface CommandEntry {\n name: string;\n description: string;\n argumentHint?: string;\n source: string;\n}\n\nconst REPL_BUILTINS: CommandEntry[] = [\n { name: \"/compact\", description: \"Compact conversation history\", source: \"builtin\" },\n { name: \"/status\", description: \"Show session status\", source: \"builtin\" },\n { name: \"/cost\", description: \"Show token usage and cost\", source: \"builtin\" },\n { name: \"/clear\", description: \"Clear conversation history\", source: \"builtin\" },\n {\n name: \"/model\",\n description: \"Switch AI model\",\n argumentHint: \"model name (e.g., Haiku, Sonnet)\",\n source: \"builtin\",\n },\n { name: \"/help\", description: \"Show available commands\", source: \"builtin\" },\n { name: \"/memory\", description: \"Edit CLAUDE.md memory\", source: \"builtin\" },\n { name: \"/review\", description: \"Review diff of changes\", source: \"builtin\" },\n { name: \"/vim\", description: \"Enter vim mode\", source: \"builtin\" },\n { name: \"/terminal-setup\", description: \"Configure terminal integration\", source: \"builtin\" },\n { name: \"/permissions\", description: \"View and manage permissions\", source: \"builtin\" },\n { name: \"/allowed-tools\", description: \"View allowed tools\", source: \"builtin\" },\n {\n name: \"/add-dir\",\n description: \"Add working directory\",\n argumentHint: \"directory path\",\n source: \"builtin\",\n },\n { name: \"/init\", description: \"Initialize CLAUDE.md in project\", source: \"builtin\" },\n { name: \"/listen\", description: \"Listen for multi-turn responses\", source: \"builtin\" },\n { name: \"/pr-comments\", description: \"View PR comments\", source: \"builtin\" },\n { name: \"/release-notes\", description: \"Generate release notes\", source: \"builtin\" },\n { name: \"/ide\", description: \"Open IDE integration\", source: \"builtin\" },\n];\n\nconst COMMAND_BLACKLIST = new Set([\n \"/login\",\n \"/logout\",\n \"/config\",\n \"/plugin\",\n \"/mcp\",\n \"/install\",\n \"/setup-token\",\n \"/doctor\",\n \"/update\",\n \"/upgrade\",\n \"/memory\",\n \"/vim\",\n \"/terminal-setup\",\n \"/permissions\",\n \"/allowed-tools\",\n \"/ide\",\n \"/listen\",\n]);\n\ninterface DiscoverOptions {\n homeDir?: string;\n}\n\n/**\n * 从 SKILL.md 内容中解析 YAML frontmatter 的 name/description/argument-hint\n */\nexport function parseSkillFrontmatter(content: string): {\n name?: string;\n description?: string;\n argumentHint?: string;\n} {\n const match = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---/);\n if (!match) return {};\n\n const yaml = match[1];\n const result: { name?: string; description?: string; argumentHint?: string } = {};\n\n const nameMatch = yaml.match(/^name:\\s*(.+)$/m);\n if (nameMatch) result.name = nameMatch[1].trim();\n\n const descMatch = yaml.match(/^description:\\s*(.+)$/m);\n if (descMatch) result.description = descMatch[1].trim();\n\n const hintMatch = yaml.match(/^argument-hint:\\s*(.+)$/m);\n if (hintMatch) result.argumentHint = hintMatch[1].trim();\n\n return result;\n}\n\n/**\n * 扫描 skills 目录,每个子目录下的 SKILL.md 解析为一条命令\n */\nfunction scanSkillsDir(dirPath: string, source: string): CommandEntry[] {\n let entries: string[];\n try {\n entries = readdirSync(dirPath, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n return [];\n }\n\n const commands: CommandEntry[] = [];\n for (const name of entries) {\n const skillPath = join(dirPath, name, \"SKILL.md\");\n try {\n const content = readFileSync(skillPath, \"utf-8\");\n const parsed = parseSkillFrontmatter(content);\n commands.push({\n name: `/${parsed.name ?? name}`,\n description: parsed.description ?? \"\",\n argumentHint: parsed.argumentHint,\n source,\n });\n } catch {\n // SKILL.md 不存在或不可读,跳过\n }\n }\n return commands;\n}\n\n/**\n * 扫描 commands 目录,每个 .md 文件名即为命令名\n */\nfunction scanCommandsDir(dirPath: string, source: string): CommandEntry[] {\n let entries: string[];\n try {\n entries = readdirSync(dirPath).filter((f) => f.endsWith(\".md\"));\n } catch {\n return [];\n }\n\n const commands: CommandEntry[] = [];\n for (const filename of entries) {\n const cmdName = filename.replace(/\\.md$/, \"\");\n try {\n const content = readFileSync(join(dirPath, filename), \"utf-8\");\n const firstLine = content.split(\"\\n\")[0].trim();\n commands.push({\n name: `/${cmdName}`,\n description: firstLine,\n source,\n });\n } catch {\n commands.push({\n name: `/${cmdName}`,\n description: \"\",\n source,\n });\n }\n }\n return commands;\n}\n\n/**\n * 扫描插件目录中的 skills 和 commands 子目录\n */\nfunction scanPluginDirs(homeDir: string): CommandEntry[] {\n const pluginCacheDir = join(homeDir, \".claude\", \"plugins\", \"cache\");\n let pluginNames: string[];\n try {\n pluginNames = readdirSync(pluginCacheDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n return [];\n }\n\n const commands: CommandEntry[] = [];\n for (const pluginName of pluginNames) {\n const pluginDir = join(pluginCacheDir, pluginName);\n const skillCmds = scanSkillsDir(join(pluginDir, \"skills\"), \"plugin-skill\");\n const cmdCmds = scanCommandsDir(join(pluginDir, \"commands\"), \"plugin-command\");\n commands.push(...skillCmds, ...cmdCmds);\n }\n return commands;\n}\n\n/**\n * 发现所有可用的斜杠命令,合并多个来源并过滤黑名单\n *\n * 来源优先级: project > user > plugin > builtin\n * 同名命令按优先级高的保留\n */\nexport async function discoverCommands(\n workDir: string,\n options?: DiscoverOptions,\n): Promise<CommandEntry[]> {\n const homeDir = options?.homeDir ?? homedir();\n\n const builtins = REPL_BUILTINS.filter((c) => !COMMAND_BLACKLIST.has(c.name));\n const userSkills = scanSkillsDir(join(homeDir, \".claude\", \"skills\"), \"user-skill\");\n const projectSkills = scanSkillsDir(join(workDir, \".claude\", \"skills\"), \"project-skill\");\n const userCommands = scanCommandsDir(join(homeDir, \".claude\", \"commands\"), \"user-command\");\n const projectCommands = scanCommandsDir(join(workDir, \".claude\", \"commands\"), \"project-command\");\n const pluginCommands = scanPluginDirs(homeDir);\n\n // 按优先级从低到高合并,同名命令后者覆盖前者\n const commandMap = new Map<string, CommandEntry>();\n for (const cmd of builtins) commandMap.set(cmd.name, cmd);\n for (const cmd of pluginCommands) commandMap.set(cmd.name, cmd);\n for (const cmd of userSkills) commandMap.set(cmd.name, cmd);\n for (const cmd of userCommands) commandMap.set(cmd.name, cmd);\n for (const cmd of projectSkills) commandMap.set(cmd.name, cmd);\n for (const cmd of projectCommands) commandMap.set(cmd.name, cmd);\n\n // 再次过滤黑名单,防止外部来源引入黑名单命令\n const result: CommandEntry[] = [];\n for (const cmd of commandMap.values()) {\n if (!COMMAND_BLACKLIST.has(cmd.name)) {\n result.push(cmd);\n }\n }\n\n return result;\n}\n","import { ControlErrorCode } from \"@dev-anywhere/shared\";\n\nfunction getFsErrorCode(err: unknown): string | undefined {\n return typeof err === \"object\" && err !== null && \"code\" in err\n ? String((err as { code?: unknown }).code)\n : undefined;\n}\n\nexport function classifyPathError(err: unknown): ControlErrorCode {\n switch (getFsErrorCode(err)) {\n case \"ENOENT\":\n return ControlErrorCode.PATH_NOT_FOUND;\n case \"ENOTDIR\":\n return ControlErrorCode.PATH_NOT_DIRECTORY;\n case \"EACCES\":\n case \"EPERM\":\n return ControlErrorCode.PATH_ACCESS_DENIED;\n default:\n return ControlErrorCode.UNKNOWN;\n }\n}\n","import { connect, type Socket } from \"node:net\";\nimport { unlinkSync, existsSync, readdirSync } from \"node:fs\";\nimport type { ChildProcess } from \"node:child_process\";\nimport { buildMessage, serializeControl } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport {\n ContentBlockDeltaSchema,\n IGNORED_EVENT_TYPES,\n KnownContentBlockSchema,\n StreamJsonEventSchema,\n} from \"../common/stream-json-schema.js\";\nimport { DATA_DIR, sessionPaths } from \"../common/paths.js\";\nimport { spawnScript } from \"../common/env.js\";\nimport { getSeqCounterFor } from \"../common/seq-counter.js\";\nimport { createWorkerReader, serializeWorkerMsg, type WorkerMessage } from \"../ipc/ipc-protocol.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { JsonObserver } from \"./json-observer.js\";\nimport type { ProviderHookContext } from \"../providers/index.js\";\nimport type { PermissionBroker, PermissionDecision } from \"./permission-broker.js\";\n\ninterface WorkerRegistryDeps {\n sessionManager: SessionManager;\n permissionBroker: PermissionBroker;\n relayConnection: RelayConnection;\n // JSON 观察通道状态机;forwardEvent / forwardApprovalRequest 据此推状态变迁\n jsonObserver: JsonObserver;\n touchSessionActivity?: (sessionId: string) => boolean;\n getProviderEnv: () => NodeJS.ProcessEnv;\n nextSeq?: (sessionId: string) => number;\n}\n\ninterface SpawnOptions {\n cwd?: string;\n resumeSessionId?: string;\n permissionMode?: string;\n // 开启后 worker spawn claude 带 --include-partial-messages,forwardEvent 处理 stream_event delta;\n // aggregated assistant 的 text/thinking 会被跳过避免和 delta 重复\n streamDelta?: boolean;\n hook?: ProviderHookContext;\n}\n\n// 管理 session → worker socket 的映射,封装全部 worker IO:\n// - spawn / connect / reconnectAll / destroyAll 生命周期入口\n// - send(sessionId, msg) 统一出口\n// - worker_event 路由、worker_approval_request 转发、worker_exit 清理都在内部闭环\nexport class WorkerRegistry {\n private sockets = new Map<string, Socket>();\n private children = new Map<string, ChildProcess>();\n // 记录哪些 session 是 spawn 时带 --stream-delta 的;forwardEvent 据此决定是否跳过 aggregated 去重\n private streamDeltaSessions = new Set<string>();\n\n constructor(private deps: WorkerRegistryDeps) {\n // relay queue 溢出时,被 drop 的 envelope 不会到达 client;若是 tool_use_request,\n // 主动清 pending 审批并回 worker env-failure deny,避免 worker 永挂、permission broker 泄漏。\n deps.relayConnection.on(\"envelope_dropped\", (raw: string) => this.onEnvelopeDropped(raw));\n }\n\n private onEnvelopeDropped(raw: string): void {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return;\n }\n if (\n !parsed ||\n typeof parsed !== \"object\" ||\n (parsed as { type?: unknown }).type !== \"tool_use_request\"\n ) {\n return;\n }\n const envelope = parsed as {\n sessionId?: unknown;\n payload?: { toolId?: unknown };\n };\n const sessionId = typeof envelope.sessionId === \"string\" ? envelope.sessionId : null;\n const requestId =\n envelope.payload && typeof envelope.payload.toolId === \"string\"\n ? envelope.payload.toolId\n : null;\n if (!sessionId || !requestId) return;\n if (\n !this.deps.permissionBroker.resolve(requestId, {\n behavior: \"deny\",\n message: \"Approval request was dropped due to relay queue overflow.\",\n })\n ) {\n return;\n }\n\n serviceLogger.warn(\n { sessionId, requestId },\n \"Tool approval request lost to relay queue overflow, denying worker\",\n );\n }\n\n spawn(sessionId: string, options?: SpawnOptions): number {\n const paths = sessionPaths(sessionId);\n const args: string[] = [sessionId, paths.workerSock];\n if (options?.cwd) args.push(\"--cwd\", options.cwd);\n if (options?.resumeSessionId) args.push(\"--resume\", options.resumeSessionId);\n // 远程场景默认 default,每个工具都需审批,覆盖用户全局 claude settings 的 defaultMode\n args.push(\"--permission-mode\", options?.permissionMode ?? \"default\");\n if (options?.streamDelta) {\n args.push(\"--stream-delta\");\n this.streamDeltaSessions.add(sessionId);\n }\n if (options?.hook) {\n args.push(\n \"--hook-provider\",\n options.hook.provider,\n \"--hook-url\",\n options.hook.hookUrl,\n \"--hook-marker\",\n options.hook.marker,\n );\n }\n args.push(\"--\");\n\n const providerEnv = this.deps.getProviderEnv();\n const child = spawnScript(\"session-worker\", args, {\n logger: serviceLogger,\n env: options?.hook\n ? { ...providerEnv, DEV_ANYWHERE_HOOK_TOKEN: options.hook.token }\n : providerEnv,\n });\n const workerPid = child.pid!;\n this.children.set(sessionId, child);\n serviceLogger.info(\n { sessionId, workerPid, cwd: options?.cwd, resume: options?.resumeSessionId },\n \"Worker process spawned\",\n );\n return workerPid;\n }\n\n connect(sessionId: string, sockPath: string): Promise<Socket | null> {\n return new Promise((resolve) => {\n const sock = connect(sockPath);\n sock.on(\"connect\", () => {\n this.sockets.set(sessionId, sock);\n createWorkerReader(\n sock,\n (msg) => this.handleWorkerMessage(sessionId, msg),\n (err, line) => {\n // 单条 worker NDJSON 行 schema 校验失败:warn 而非断连。Claude/Codex CLI 增量\n // 加新事件类型时不该把整个 session 推进 ERROR;连接保持开放,下一条仍继续解析。\n serviceLogger.warn(\n { sessionId, err: err.message, lineLen: line.length },\n \"Worker IPC message dropped (parse/schema error)\",\n );\n },\n );\n sock.on(\"close\", () => this.onDisconnect(sessionId));\n sock.on(\"error\", () => this.onDisconnect(sessionId));\n resolve(sock);\n });\n sock.on(\"error\", () => resolve(null));\n });\n }\n\n // 枚举 DATA_DIR 下所有 session 目录,尝试连接存活的 worker.sock;失败则清理 stale socket。\n async reconnectAll(): Promise<void> {\n if (!existsSync(DATA_DIR)) return;\n\n const dirs = readdirSync(DATA_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());\n\n for (const dir of dirs) {\n const sessionId = dir.name;\n const paths = sessionPaths(sessionId);\n if (!existsSync(paths.workerSock)) continue;\n\n const sock = await this.connect(sessionId, paths.workerSock);\n if (sock) {\n if (!this.deps.sessionManager.getSession(sessionId)) {\n // worker.sock 可连通但 SessionManager 无该 session 记录\n // (sessions.json 写盘失败或文件丢失,但 worker 仍存活),属于孤儿 worker,直接终止。\n serviceLogger.warn(\n { sessionId },\n \"Orphaned worker found without session data, terminating\",\n );\n sock.end();\n this.sockets.delete(sessionId);\n continue;\n }\n serviceLogger.info({ sessionId }, \"Reconnected to existing worker\");\n } else {\n try {\n unlinkSync(paths.workerSock);\n } catch {\n // socket 文件可能已被删除\n }\n serviceLogger.info({ sessionId }, \"Cleaned up stale worker socket\");\n }\n }\n }\n\n has(sessionId: string): boolean {\n return this.sockets.has(sessionId);\n }\n\n delete(sessionId: string): void {\n this.children.delete(sessionId);\n this.sockets.delete(sessionId);\n this.streamDeltaSessions.delete(sessionId);\n }\n\n terminateProcess(sessionId: string, signal: NodeJS.Signals = \"SIGTERM\"): boolean {\n const child = this.children.get(sessionId);\n const sock = this.sockets.get(sessionId);\n sock?.destroy();\n this.sockets.delete(sessionId);\n this.streamDeltaSessions.delete(sessionId);\n this.children.delete(sessionId);\n if (!child || child.killed) return false;\n return child.kill(signal);\n }\n\n // 向指定 session 的 worker 写 WorkerMessage;socket 缺失或不可写返回 false 由 caller 决定日志。\n send(sessionId: string, msg: WorkerMessage): boolean {\n const sock = this.sockets.get(sessionId);\n if (!sock?.writable) return false;\n sock.write(serializeWorkerMsg(msg));\n return true;\n }\n\n destroyAll(): void {\n for (const [, ws] of this.sockets) {\n ws.destroy();\n }\n this.sockets.clear();\n }\n\n private handleWorkerMessage(sessionId: string, msg: WorkerMessage): void {\n switch (msg.type) {\n case \"worker_ready\":\n serviceLogger.info({ sessionId, pid: msg.pid }, \"Worker ready\");\n break;\n\n case \"worker_event\":\n try {\n this.forwardEvent(sessionId, msg.seq, msg.event);\n } catch (err) {\n serviceLogger.debug(\n { sessionId, error: String(err) },\n \"Failed to forward event to relay\",\n );\n }\n serviceLogger.debug({ sessionId, eventType: msg.event.type }, \"JSON session event\");\n break;\n\n case \"worker_exit\":\n this.deps.sessionManager.terminateSession(sessionId);\n this.delete(sessionId);\n serviceLogger.info({ sessionId, exitCode: msg.code }, \"JSON session exited\");\n break;\n\n case \"worker_approval_request\":\n this.forwardApprovalRequest(sessionId, msg);\n break;\n\n case \"worker_claude_session_id\":\n this.deps.sessionManager.setClaudeSessionId(sessionId, msg.sessionId);\n serviceLogger.info(\n { sessionId, claudeSessionId: msg.sessionId },\n \"Claude session ID captured\",\n );\n break;\n }\n }\n\n // worker 连接断开或异常时的统一清理入口。仅记录一份,不再区分 close vs error 语义。\n private onDisconnect(sessionId: string): void {\n this.sockets.delete(sessionId);\n this.deps.permissionBroker.cleanupSession(sessionId, \"Worker disconnected\");\n // worker_exit 消息走 handleWorkerMessage,那条路径已经 terminateSession 把 session 从 manager\n // 中删掉——onDisconnect 紧随其后到达时 getSession 返回 undefined,不触发 ERROR 转换。\n // 只有\"未经 worker_exit 即断连\"(进程崩溃 / 内核 OOM kill / IPC socket 损坏)才会进入这里:\n // session 仍然在 manager 中,必须立即把状态推到 ERROR,否则 UI 看到的状态会停留在\n // WORKING / WAITING_APPROVAL 直到 reaper 60s 周期触发,期间用户既无法手动中止也无法重启。\n if (this.deps.sessionManager.getSession(sessionId)) {\n this.deps.jsonObserver.onChannelBroken(sessionId);\n }\n }\n\n // 对齐 Claude CLI stream-json 输出,按 type 分发:\n // stream_event.content_block_delta → 增量 text/thinking envelope(仅 streamDelta 会话产生)\n // assistant.content[].text → assistant_message envelope(streamDelta 下跳过,避免重复)\n // assistant.content[].thinking → thinking envelope(streamDelta 下跳过)\n // assistant.content[].tool_use → assistant_tool_use envelope\n // user.content[].tool_result → tool_result envelope\n // result → turn_result control + 会话状态回 IDLE\n // system/rate_limit_event/其他 → 静默忽略\n // schema 未识别的 event/block 以 warn 暴露,作为 Claude CLI 协议变化的 runtime canary。\n private forwardEvent(sessionId: string, seq: number, event: Record<string, unknown>): void {\n const relay = this.deps.relayConnection;\n const parsed = StreamJsonEventSchema.safeParse(event);\n if (!parsed.success) {\n const rawType = typeof event.type === \"string\" ? event.type : \"<missing>\";\n if (IGNORED_EVENT_TYPES.has(rawType)) {\n serviceLogger.debug({ sessionId, type: rawType }, \"Dropped ignored stream-json event\");\n return;\n }\n serviceLogger.warn(\n { sessionId, type: rawType, issues: parsed.error.issues.slice(0, 3) },\n \"Unknown stream-json event type; Claude CLI schema may have changed\",\n );\n return;\n }\n const ev = parsed.data;\n this.deps.touchSessionActivity?.(sessionId);\n const isStreamDeltaSession = this.streamDeltaSessions.has(sessionId);\n\n if (ev.type === \"stream_event\") {\n const delta = ContentBlockDeltaSchema.safeParse(ev.event);\n if (!delta.success) return; // 非 content_block_delta 的内层事件(message_start 等)忽略\n const d = delta.data.delta;\n if (d.type === \"text_delta\" && d.text) {\n relay.sendEnvelope(\n buildMessage(\n \"assistant_message\",\n sessionId,\n seq,\n { text: d.text, isPartial: true },\n \"proxy\",\n ),\n );\n } else if (d.type === \"thinking_delta\" && d.thinking) {\n relay.sendEnvelope(buildMessage(\"thinking\", sessionId, seq, { text: d.thinking }, \"proxy\"));\n }\n return;\n }\n\n if (ev.type === \"assistant\") {\n for (const raw of ev.message.content) {\n const blockParse = KnownContentBlockSchema.safeParse(raw);\n if (!blockParse.success) {\n const rawType =\n raw && typeof raw === \"object\"\n ? ((raw as Record<string, unknown>).type as string | undefined)\n : undefined;\n serviceLogger.warn(\n { sessionId, seq, blockType: rawType ?? \"<missing>\" },\n \"Unknown assistant content block; Claude CLI schema may have changed\",\n );\n continue;\n }\n const block = blockParse.data;\n if (block.type === \"text\") {\n // streamDelta 下增量已经发过了,aggregated 全文跳过避免重复\n if (!isStreamDeltaSession && block.text) {\n relay.sendEnvelope(\n buildMessage(\n \"assistant_message\",\n sessionId,\n seq,\n { text: block.text, isPartial: true },\n \"proxy\",\n ),\n );\n }\n } else if (block.type === \"thinking\") {\n // Opus extended thinking 明文被 Anthropic 服务端 redact 时 block.thinking 为空字符串,\n // 不转发;session WORKING 状态已经覆盖\"Claude 在思考\"信号,redacted envelope 无新信息\n if (!isStreamDeltaSession && block.thinking) {\n relay.sendEnvelope(\n buildMessage(\"thinking\", sessionId, seq, { text: block.thinking }, \"proxy\"),\n );\n }\n } else if (block.type === \"tool_use\") {\n relay.sendEnvelope(\n buildMessage(\n \"assistant_tool_use\",\n sessionId,\n seq,\n { toolName: block.name, toolId: block.id, parameters: block.input },\n \"proxy\",\n ),\n );\n }\n }\n return;\n }\n\n if (ev.type === \"user\") {\n for (const raw of ev.message.content) {\n const blockParse = KnownContentBlockSchema.safeParse(raw);\n if (!blockParse.success) continue;\n const block = blockParse.data;\n if (block.type !== \"tool_result\") continue;\n relay.sendEnvelope(\n buildMessage(\n \"tool_result\",\n sessionId,\n seq,\n { toolId: block.tool_use_id, result: block.content, isError: block.is_error ?? false },\n \"proxy\",\n ),\n );\n }\n return;\n }\n\n if (ev.type === \"result\") {\n const resultText = typeof ev.result === \"string\" ? ev.result : undefined;\n relay.sendRaw(\n serializeControl({\n type: \"turn_result\",\n sessionId,\n success: ev.subtype === \"success\",\n isError: ev.is_error ?? false,\n ...(resultText ? { result: resultText } : {}),\n }),\n );\n this.deps.jsonObserver.onTurnResult(sessionId);\n }\n }\n\n private forwardApprovalRequest(\n sessionId: string,\n msg: Extract<WorkerMessage, { type: \"worker_approval_request\" }>,\n ): void {\n serviceLogger.info(\n { sessionId, toolName: msg.toolName, requestId: msg.requestId },\n \"Tool approval forwarding to relay\",\n );\n this.deps.jsonObserver.onApprovalRequested(sessionId);\n try {\n const approvalSeq = this.deps.nextSeq?.(sessionId) ?? getSeqCounterFor(sessionId).next();\n const envelope = buildMessage(\n \"tool_use_request\",\n sessionId,\n approvalSeq,\n {\n toolName: msg.toolName,\n toolId: msg.requestId,\n parameters: msg.input,\n },\n \"proxy\",\n );\n const session = this.deps.sessionManager.getSession(sessionId);\n const registered = this.deps.permissionBroker.registerWorkerRequest(\n {\n requestId: msg.requestId,\n provider: session?.provider ?? \"claude\",\n sessionId,\n toolName: msg.toolName,\n input: msg.input,\n },\n (decision: PermissionDecision) => {\n this.send(sessionId, {\n type: \"worker_approval_response\",\n requestId: msg.requestId,\n behavior: decision.behavior,\n ...(decision.message ? { message: decision.message } : {}),\n });\n },\n );\n if (!registered) return;\n this.deps.relayConnection.sendEnvelope(envelope);\n } catch (err) {\n const resolved = this.deps.permissionBroker.resolve(msg.requestId, {\n behavior: \"deny\",\n message: \"Failed to forward approval request to relay.\",\n });\n if (!resolved) {\n this.send(sessionId, {\n type: \"worker_approval_response\",\n requestId: msg.requestId,\n behavior: \"deny\",\n message: \"Failed to forward approval request to relay.\",\n });\n }\n // envelope 构造失败回 deny,避免 worker 无限等待。\n serviceLogger.warn(\n { sessionId, error: String(err) },\n \"Failed to forward tool approval to relay, denying\",\n );\n }\n }\n}\n","import type { Socket } from \"node:net\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { serializeIpc } from \"../ipc/ipc-protocol.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\nimport type { ControlMessageHandlers } from \"./handlers/control-messages.js\";\nimport type { HostedPtyRegistry } from \"./hosted-pty-registry.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport type { WorkerRegistry } from \"./worker-registry.js\";\n\ntype SessionTerminationAction =\n | \"detach_local_terminal\"\n | \"terminate_hosted_pty\"\n | \"terminate_json_worker\"\n | \"not_found\";\n\ninterface TerminateSessionDeps {\n sessionManager: SessionManager;\n workerRegistry: WorkerRegistry;\n controlHandlers: ControlMessageHandlers;\n terminalSockets: Map<string, Socket>;\n hostedPtyRegistry: HostedPtyRegistry;\n agentStatusRegistry: AgentStatusRegistry;\n // 同步终止路径必须广播 session list,否则 web 看到幽灵 row。hosted PTY 终止异步走\n // child.onExit → onSessionClosed → cleanupSessionResources 内部已广播,因此 hosted\n // 路径不在此处调用。所有同步路径(detach_local_terminal / terminate_json_worker)\n // 由本函数收口调用,调用方不必手动补。\n broadcastSessionList: () => void;\n}\n\nexport function terminateSessionByOwnership(\n deps: TerminateSessionDeps,\n sessionId: string,\n): { success: boolean; action: SessionTerminationAction } {\n const session = deps.sessionManager.getSession(sessionId);\n\n if (session?.mode === \"pty\" && session.ptyOwner === \"local-terminal\") {\n const terminalSocket = deps.terminalSockets.get(sessionId);\n if (terminalSocket?.writable) {\n terminalSocket.write(serializeIpc({ type: \"pty_detach\", sessionId }));\n }\n deps.terminalSockets.delete(sessionId);\n const result = deps.sessionManager.terminateSession(sessionId, {\n preserveProviderHooks: true,\n });\n deps.controlHandlers.cleanup(sessionId);\n deps.agentStatusRegistry.delete(sessionId);\n deps.broadcastSessionList();\n serviceLogger.info(\n { sessionId, success: result.success },\n \"Local terminal session detached from remote view\",\n );\n return { success: result.success, action: \"detach_local_terminal\" };\n }\n\n if (session?.mode === \"pty\" && session.ptyOwner === \"proxy-hosted\") {\n const success = deps.hostedPtyRegistry.terminate(sessionId);\n serviceLogger.info({ sessionId, success }, \"Hosted PTY termination requested\");\n return { success, action: \"terminate_hosted_pty\" };\n }\n\n if (session?.mode === \"json\") {\n deps.workerRegistry.send(sessionId, { type: \"worker_stop\" });\n deps.workerRegistry.delete(sessionId);\n const result = deps.sessionManager.terminateSession(sessionId);\n deps.controlHandlers.cleanup(sessionId);\n deps.agentStatusRegistry.delete(sessionId);\n deps.broadcastSessionList();\n serviceLogger.info({ sessionId, success: result.success }, \"JSON worker session terminated\");\n return { success: result.success, action: \"terminate_json_worker\" };\n }\n\n const hostedTerminated = deps.hostedPtyRegistry.terminate(sessionId);\n if (hostedTerminated) {\n return { success: true, action: \"terminate_hosted_pty\" };\n }\n return { success: false, action: \"not_found\" };\n}\n","import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { isAbsolute, join, relative, resolve } from \"node:path\";\nimport { nanoid } from \"nanoid\";\nimport { ControlErrorCode } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { DATA_DIR } from \"../common/paths.js\";\n\nconst MAX_CLIPBOARD_IMAGE_BYTES = 10 * 1024 * 1024;\nconst MAX_CLIPBOARD_IMAGE_BASE64_LENGTH = Math.ceil(MAX_CLIPBOARD_IMAGE_BYTES / 3) * 4;\nconst IMAGE_EXTENSIONS: ReadonlyMap<string, string> = new Map([\n [\"image/png\", \"png\"],\n [\"image/jpeg\", \"jpg\"],\n [\"image/webp\", \"webp\"],\n [\"image/gif\", \"gif\"],\n] as const);\n\nexport type ClipboardImageUploadRequest = {\n sessionId: string;\n mimeType: string;\n dataBase64: string;\n fileName?: string;\n};\n\ntype ClipboardImageUploadResult = {\n success: boolean;\n // 失败时不填,避免空字符串通过 schema 的 z.string().optional() 校验。\n path?: string;\n error?: string;\n errorCode?: ControlErrorCode;\n};\n\ntype ClipboardImageUploadOptions = {\n dataDir?: string;\n cwd?: string;\n now?: () => number;\n randomSuffix?: () => string;\n};\n\nfunction formatTimestamp(ms: number): string {\n const [date, time = \"000000\"] = new Date(ms)\n .toISOString()\n .replace(/\\.\\d{3}Z$/, \"\")\n .split(\"T\");\n return `${date.replace(/-/g, \"\")}-${time.replace(/:/g, \"\")}`;\n}\n\nfunction normalizeBase64(input: string): string {\n return input.replace(/^data:[^;]+;base64,/i, \"\").replace(/\\s/g, \"\");\n}\n\nfunction decodeBase64Image(dataBase64: string): Buffer {\n const normalized = normalizeBase64(dataBase64);\n if (normalized.length > MAX_CLIPBOARD_IMAGE_BASE64_LENGTH) {\n throw new Error(\"图片超过 10MB 限制\");\n }\n if (!normalized || !/^[A-Za-z0-9+/]*={0,2}$/.test(normalized)) {\n throw new Error(\"图片数据不是有效的 base64\");\n }\n const buffer = Buffer.from(normalized, \"base64\");\n if (buffer.length === 0) throw new Error(\"图片数据为空\");\n if (buffer.length > MAX_CLIPBOARD_IMAGE_BYTES) {\n throw new Error(\"图片超过 10MB 限制\");\n }\n return buffer;\n}\n\nfunction resolveChildDir(rootPath: string, ...segments: string[]): string {\n const root = resolve(rootPath);\n const uploadDir = resolve(root, ...segments);\n const relativePath = relative(root, uploadDir);\n if (!relativePath || relativePath.startsWith(\"..\") || isAbsolute(relativePath)) {\n throw new Error(\"会话路径无效\");\n }\n return uploadDir;\n}\n\nfunction resolveSessionClipboardDir(dataDir: string, sessionId: string): string {\n return resolveChildDir(dataDir, sessionId, \"clipboard\");\n}\n\nfunction normalizeGitignoreLine(line: string): string {\n return line.trim().replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n}\n\nfunction ensureProjectClipboardIgnored(cwd: string): void {\n const gitignorePath = join(cwd, \".gitignore\");\n if (!existsSync(gitignorePath)) return;\n\n try {\n const current = readFileSync(gitignorePath, \"utf-8\");\n const alreadyIgnored = current\n .split(/\\r?\\n/)\n .some((line) => normalizeGitignoreLine(line) === \".dev-anywhere\");\n if (alreadyIgnored) return;\n\n const separator = current.length > 0 && !current.endsWith(\"\\n\") ? \"\\n\" : \"\";\n writeFileSync(gitignorePath, `${current}${separator}.dev-anywhere/\\n`);\n } catch {\n // Ignore-file updates are best-effort; image upload should still succeed.\n }\n}\n\nfunction trySaveProjectClipboardImage(options: {\n cwd?: string;\n sessionId: string;\n fileName: string;\n buffer: Buffer;\n}): ClipboardImageUploadResult | null {\n if (!options.cwd) return null;\n\n try {\n const cwd = resolve(options.cwd);\n if (!statSync(cwd).isDirectory()) return null;\n const clipboardRoot = resolve(cwd, \".dev-anywhere\", \"clipboard\");\n const uploadDir = resolveChildDir(clipboardRoot, options.sessionId);\n const path = join(uploadDir, options.fileName);\n\n mkdirSync(uploadDir, { recursive: true });\n writeFileSync(path, options.buffer, { mode: 0o600 });\n ensureProjectClipboardIgnored(cwd);\n return { success: true, path: relative(cwd, path) };\n } catch (err) {\n serviceLogger.warn(\n { sessionId: options.sessionId, cwd: options.cwd, error: String(err) },\n \"Project clipboard image write failed; falling back to data dir\",\n );\n return null;\n }\n}\n\nexport function saveClipboardImageUpload(\n request: ClipboardImageUploadRequest,\n options: ClipboardImageUploadOptions = {},\n): ClipboardImageUploadResult {\n const extension = IMAGE_EXTENSIONS.get(request.mimeType);\n if (!extension) {\n return {\n success: false,\n error: \"不支持这种图片格式\",\n errorCode: ControlErrorCode.UNKNOWN,\n };\n }\n\n try {\n const buffer = decodeBase64Image(request.dataBase64);\n const now = options.now ?? Date.now;\n const suffix = options.randomSuffix?.() ?? nanoid(6);\n const fileName = `pasted-${formatTimestamp(now())}-${suffix}.${extension}`;\n const projectResult = trySaveProjectClipboardImage({\n cwd: options.cwd,\n sessionId: request.sessionId,\n fileName,\n buffer,\n });\n if (projectResult) return projectResult;\n\n const dataDir = options.dataDir ?? DATA_DIR;\n const uploadDir = resolveSessionClipboardDir(dataDir, request.sessionId);\n const path = join(uploadDir, fileName);\n\n mkdirSync(uploadDir, { recursive: true });\n writeFileSync(path, buffer, { mode: 0o600 });\n return { success: true, path };\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? err.message : String(err),\n errorCode: ControlErrorCode.UNKNOWN,\n };\n }\n}\n","import { readFileSync, realpathSync, statSync } from \"node:fs\";\nimport { extname, isAbsolute, resolve } from \"node:path\";\nimport { ControlErrorCode } from \"@dev-anywhere/shared\";\nimport type { ControlErrorCode as ControlErrorCodeType } from \"@dev-anywhere/shared\";\nimport { classifyPathError } from \"./path-errors.js\";\n\n// 单租户场景下不做白名单, 由 size cap 兜底; 100MB 已经远超日常 log/diff/截图。\nconst MAX_FILE_DOWNLOAD_BYTES = 100 * 1024 * 1024;\n\ntype FileDownloadRequest = {\n sessionId: string;\n path: string;\n};\n\nexport type FileDownloadResult = {\n success: boolean;\n sessionId: string;\n path: string;\n mimeType?: string;\n dataBase64?: string;\n size?: number;\n error?: string;\n errorCode?: ControlErrorCodeType;\n};\n\ntype FileDownloadOptions = {\n cwd: string;\n maxBytes?: number;\n};\n\nconst EXT_MIME_MAP: Record<string, string> = {\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".svg\": \"image/svg+xml\",\n \".bmp\": \"image/bmp\",\n \".pdf\": \"application/pdf\",\n \".json\": \"application/json\",\n \".xml\": \"application/xml\",\n \".html\": \"text/html\",\n \".htm\": \"text/html\",\n \".txt\": \"text/plain\",\n \".md\": \"text/markdown\",\n \".log\": \"text/plain\",\n \".csv\": \"text/csv\",\n \".js\": \"application/javascript\",\n \".mjs\": \"application/javascript\",\n \".ts\": \"application/typescript\",\n \".tsx\": \"application/typescript\",\n \".zip\": \"application/zip\",\n \".tar\": \"application/x-tar\",\n \".gz\": \"application/gzip\",\n \".mp4\": \"video/mp4\",\n \".mp3\": \"audio/mpeg\",\n \".wav\": \"audio/wav\",\n};\n\nfunction guessMimeType(filePath: string): string {\n const ext = extname(filePath).toLowerCase();\n return EXT_MIME_MAP[ext] ?? \"application/octet-stream\";\n}\n\nfunction resolveDownloadPath(rawPath: string, cwd: string): string {\n const candidate = isAbsolute(rawPath) ? resolve(rawPath) : resolve(cwd, rawPath);\n return realpathSync(candidate);\n}\n\nfunction errorCode(err: unknown): ControlErrorCodeType {\n if (\n err instanceof Error &&\n \"errorCode\" in err &&\n typeof (err as { errorCode?: unknown }).errorCode === \"string\"\n ) {\n return (err as { errorCode: ControlErrorCodeType }).errorCode;\n }\n return classifyPathError(err);\n}\n\nexport function loadFileDownload(\n request: FileDownloadRequest,\n options: FileDownloadOptions,\n): FileDownloadResult {\n try {\n const resolvedPath = resolveDownloadPath(request.path, options.cwd);\n const stat = statSync(resolvedPath);\n if (!stat.isFile()) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: \"路径不是普通文件\",\n errorCode: ControlErrorCode.INVALID_PATH,\n };\n }\n const maxBytes = options.maxBytes ?? MAX_FILE_DOWNLOAD_BYTES;\n if (stat.size > maxBytes) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: `文件超过 ${Math.round(maxBytes / 1024 / 1024)}MB 限制`,\n errorCode: ControlErrorCode.UNKNOWN,\n };\n }\n\n const buffer = readFileSync(resolvedPath);\n return {\n success: true,\n sessionId: request.sessionId,\n path: request.path,\n mimeType: guessMimeType(resolvedPath),\n dataBase64: buffer.toString(\"base64\"),\n size: buffer.length,\n };\n } catch (err) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: err instanceof Error ? err.message : String(err),\n errorCode: errorCode(err),\n };\n }\n}\n","import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { basename, isAbsolute, join, relative, resolve } from \"node:path\";\nimport { nanoid } from \"nanoid\";\nimport { ControlErrorCode } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { DATA_DIR } from \"../common/paths.js\";\n\n// 单租户场景下文件上传上限大于剪贴板图片: 视频 / 日志包等可能 30-50MB。\nconst MAX_FILE_UPLOAD_BYTES = 100 * 1024 * 1024;\nconst MAX_FILE_UPLOAD_BASE64_LENGTH = Math.ceil(MAX_FILE_UPLOAD_BYTES / 3) * 4;\nconst SAFE_FILENAME_RE = /^[A-Za-z0-9._-]+$/;\n\nexport type FileUploadRequest = {\n sessionId: string;\n mimeType: string;\n dataBase64: string;\n fileName: string;\n};\n\nexport type FileUploadResult = {\n success: boolean;\n // 失败时不填,避免空字符串通过 schema 的 z.string().optional() 校验。\n path?: string;\n error?: string;\n errorCode?: ControlErrorCode;\n};\n\ntype FileUploadOptions = {\n dataDir?: string;\n cwd?: string;\n now?: () => number;\n randomSuffix?: () => string;\n};\n\nfunction normalizeBase64(input: string): string {\n return input.replace(/^data:[^;]+;base64,/i, \"\").replace(/\\s/g, \"\");\n}\n\nfunction decodeBase64File(dataBase64: string): Buffer {\n const normalized = normalizeBase64(dataBase64);\n if (normalized.length > MAX_FILE_UPLOAD_BASE64_LENGTH) {\n throw new Error(\"文件超过 100MB 限制\");\n }\n if (!normalized || !/^[A-Za-z0-9+/]*={0,2}$/.test(normalized)) {\n throw new Error(\"文件数据不是有效的 base64\");\n }\n const buffer = Buffer.from(normalized, \"base64\");\n if (buffer.length === 0) throw new Error(\"文件数据为空\");\n if (buffer.length > MAX_FILE_UPLOAD_BYTES) {\n throw new Error(\"文件超过 100MB 限制\");\n }\n return buffer;\n}\n\nfunction sanitizeFileName(fileName: string, fallbackPrefix: string, suffix: string): string {\n const base = basename(fileName).trim();\n if (base && SAFE_FILENAME_RE.test(base)) return base;\n // 含路径分隔符 / 非 ASCII / 控制字符等不安全名: 拆出扩展名 (若仍合法) 并接随机后缀。\n const extMatch = base.match(/\\.([A-Za-z0-9]{1,6})$/);\n const ext = extMatch ? `.${extMatch[1]}` : \"\";\n return `${fallbackPrefix}-${suffix}${ext}`;\n}\n\nfunction resolveChildDir(rootPath: string, ...segments: string[]): string {\n const root = resolve(rootPath);\n const target = resolve(root, ...segments);\n const rel = relative(root, target);\n if (!rel || rel.startsWith(\"..\") || isAbsolute(rel)) {\n throw new Error(\"会话路径无效\");\n }\n return target;\n}\n\nfunction normalizeGitignoreLine(line: string): string {\n return line.trim().replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n}\n\nfunction ensureProjectUploadIgnored(cwd: string): void {\n const gitignorePath = join(cwd, \".gitignore\");\n if (!existsSync(gitignorePath)) return;\n try {\n const current = readFileSync(gitignorePath, \"utf-8\");\n const alreadyIgnored = current\n .split(/\\r?\\n/)\n .some((line) => normalizeGitignoreLine(line) === \".dev-anywhere\");\n if (alreadyIgnored) return;\n const separator = current.length > 0 && !current.endsWith(\"\\n\") ? \"\\n\" : \"\";\n writeFileSync(gitignorePath, `${current}${separator}.dev-anywhere/\\n`);\n } catch {\n // best-effort\n }\n}\n\nfunction trySaveProjectUpload(options: {\n cwd?: string;\n sessionId: string;\n fileName: string;\n buffer: Buffer;\n}): FileUploadResult | null {\n if (!options.cwd) return null;\n try {\n const cwd = resolve(options.cwd);\n if (!statSync(cwd).isDirectory()) return null;\n const uploadsRoot = resolve(cwd, \".dev-anywhere\", \"uploads\");\n const uploadDir = resolveChildDir(uploadsRoot, options.sessionId);\n const path = join(uploadDir, options.fileName);\n mkdirSync(uploadDir, { recursive: true });\n writeFileSync(path, options.buffer, { mode: 0o600 });\n ensureProjectUploadIgnored(cwd);\n return { success: true, path: relative(cwd, path) };\n } catch (err) {\n serviceLogger.warn(\n { sessionId: options.sessionId, cwd: options.cwd, error: String(err) },\n \"Project upload write failed; falling back to data dir\",\n );\n return null;\n }\n}\n\nexport async function saveFileUpload(\n request: FileUploadRequest,\n options: FileUploadOptions = {},\n): Promise<FileUploadResult> {\n try {\n const buffer = decodeBase64File(request.dataBase64);\n const now = options.now ?? Date.now;\n const suffix = options.randomSuffix?.() ?? nanoid(6);\n const stamped = new Date(now())\n .toISOString()\n .replace(/[-:T.Z]/g, \"\")\n .slice(0, 14);\n const fileName = sanitizeFileName(request.fileName, `upload-${stamped}`, suffix);\n\n const projectResult = trySaveProjectUpload({\n cwd: options.cwd,\n sessionId: request.sessionId,\n fileName,\n buffer,\n });\n if (projectResult) return projectResult;\n\n const dataDir = options.dataDir ?? DATA_DIR;\n const uploadDir = resolveChildDir(dataDir, request.sessionId, \"uploads\");\n const path = join(uploadDir, fileName);\n mkdirSync(uploadDir, { recursive: true });\n writeFileSync(path, buffer, { mode: 0o600 });\n return { success: true, path };\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? err.message : String(err),\n errorCode: ControlErrorCode.UNKNOWN,\n };\n }\n}\n","import { readFileSync, realpathSync, statSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport { ControlErrorCode } from \"@dev-anywhere/shared\";\nimport type { ControlErrorCode as ControlErrorCodeType } from \"@dev-anywhere/shared\";\nimport { classifyPathError } from \"./path-errors.js\";\n\nconst MAX_IMAGE_PREVIEW_BYTES = 10 * 1024 * 1024;\n\ntype ImagePreviewRequest = {\n sessionId: string;\n path: string;\n};\n\ntype ImagePreviewResult = {\n success: boolean;\n sessionId: string;\n path: string;\n mimeType?: \"image/png\" | \"image/jpeg\" | \"image/webp\" | \"image/gif\";\n dataBase64?: string;\n size?: number;\n error?: string;\n errorCode?: ControlErrorCodeType;\n};\n\ntype ImagePreviewOptions = {\n cwd: string;\n tmpDir?: string;\n previewRoots?: string[];\n maxBytes?: number;\n};\n\nfunction isInsideRoot(realFilePath: string, realRootPath: string): boolean {\n const rel = relative(realRootPath, realFilePath);\n return rel === \"\" || (!rel.startsWith(\"..\") && !isAbsolute(rel));\n}\n\nfunction allowedRoots(options: ImagePreviewOptions): string[] {\n return [options.cwd, options.tmpDir ?? tmpdir(), ...(options.previewRoots ?? [])]\n .map((root) => root.trim())\n .filter(Boolean)\n .flatMap((root) => {\n try {\n return [realpathSync(root)];\n } catch {\n return [];\n }\n });\n}\n\nfunction resolvePreviewPath(rawPath: string, options: ImagePreviewOptions): string {\n const candidate = isAbsolute(rawPath) ? resolve(rawPath) : resolve(options.cwd, rawPath);\n const realCandidate = realpathSync(candidate);\n if (!allowedRoots(options).some((root) => isInsideRoot(realCandidate, root))) {\n throw Object.assign(new Error(\"图片路径不在允许预览的目录内\"), {\n errorCode: ControlErrorCode.INVALID_PATH,\n });\n }\n return realCandidate;\n}\n\nfunction detectImageMime(buffer: Buffer): ImagePreviewResult[\"mimeType\"] | undefined {\n if (\n buffer.length >= 8 &&\n buffer[0] === 0x89 &&\n buffer[1] === 0x50 &&\n buffer[2] === 0x4e &&\n buffer[3] === 0x47 &&\n buffer[4] === 0x0d &&\n buffer[5] === 0x0a &&\n buffer[6] === 0x1a &&\n buffer[7] === 0x0a\n ) {\n return \"image/png\";\n }\n if (buffer.length >= 3 && buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) {\n return \"image/jpeg\";\n }\n if (\n buffer.length >= 12 &&\n buffer.subarray(0, 4).toString(\"ascii\") === \"RIFF\" &&\n buffer.subarray(8, 12).toString(\"ascii\") === \"WEBP\"\n ) {\n return \"image/webp\";\n }\n if (\n buffer.length >= 6 &&\n (buffer.subarray(0, 6).toString(\"ascii\") === \"GIF87a\" ||\n buffer.subarray(0, 6).toString(\"ascii\") === \"GIF89a\")\n ) {\n return \"image/gif\";\n }\n return undefined;\n}\n\nfunction errorCode(err: unknown): ControlErrorCodeType {\n if (\n err instanceof Error &&\n \"errorCode\" in err &&\n typeof (err as { errorCode?: unknown }).errorCode === \"string\"\n ) {\n return (err as { errorCode: ControlErrorCodeType }).errorCode;\n }\n return classifyPathError(err);\n}\n\nexport function loadImagePreview(\n request: ImagePreviewRequest,\n options: ImagePreviewOptions,\n): ImagePreviewResult {\n try {\n const resolvedPath = resolvePreviewPath(request.path, options);\n const stat = statSync(resolvedPath);\n if (!stat.isFile()) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: \"路径不是图片文件\",\n errorCode: ControlErrorCode.INVALID_PATH,\n };\n }\n const maxBytes = options.maxBytes ?? MAX_IMAGE_PREVIEW_BYTES;\n if (stat.size > maxBytes) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: \"图片超过 10MB 限制\",\n errorCode: ControlErrorCode.UNKNOWN,\n };\n }\n\n const buffer = readFileSync(resolvedPath);\n const mimeType = detectImageMime(buffer);\n if (!mimeType) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: \"不支持这种图片格式\",\n errorCode: ControlErrorCode.UNKNOWN,\n };\n }\n\n return {\n success: true,\n sessionId: request.sessionId,\n path: request.path,\n mimeType,\n dataBase64: buffer.toString(\"base64\"),\n size: buffer.length,\n };\n } catch (err) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: err instanceof Error ? err.message : String(err),\n errorCode: errorCode(err),\n };\n }\n}\n","import { serializeIpc } from \"../ipc/ipc-protocol.js\";\n\nexport function serializeRawPtyInput(sessionId: string, data: string): string {\n return serializeIpc({ type: \"pty_input\", sessionId, data });\n}\n","import type { Socket } from \"node:net\";\nimport {\n ControlErrorCode,\n MessageEnvelopeSchema,\n serializeControl,\n type ControlMessage,\n type Envelope,\n} from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { saveClipboardImageUpload } from \"./clipboard-image-upload.js\";\nimport { loadFileDownload } from \"./file-download.js\";\nimport { saveFileUpload } from \"./file-upload.js\";\nimport { loadImagePreview } from \"./image-preview.js\";\nimport { serializeRawPtyInput } from \"./pty-input.js\";\nimport type { HostedPtyRegistry } from \"./hosted-pty-registry.js\";\nimport type { JsonObserver } from \"./json-observer.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport type { WorkerRegistry } from \"./worker-registry.js\";\n\ninterface RelayInputHandlersDeps {\n sessionManager: SessionManager;\n workerRegistry: WorkerRegistry;\n relayConnection: RelayConnection;\n terminalSockets: Map<string, Socket>;\n hostedPtyRegistry: HostedPtyRegistry;\n jsonObserver: JsonObserver;\n previewRoots?: string[];\n}\n\nexport class RelayInputHandlers {\n constructor(private readonly deps: RelayInputHandlersDeps) {}\n\n onUserInput(msg: Envelope<\"user_input\">): void {\n const { sessionId } = msg;\n if (!sessionId) return;\n\n const session = this.deps.sessionManager.getSession(sessionId);\n if (!session) {\n serviceLogger.warn({ sessionId }, \"Remote input dropped: session not found\");\n return;\n }\n\n const text = msg.payload.text;\n\n if (session.mode === \"json\") {\n // 必须先 send 成功再 onTurnStart:onTurnStart 把 session 推到 WORKING,但 send 失败时\n // 没有 onTurnResult / onChannelBroken 回到 IDLE,session 会卡 WORKING 直到 60s reaper。\n const sent = this.deps.workerRegistry.send(sessionId, {\n type: \"worker_input\",\n content: text,\n });\n if (!sent) {\n serviceLogger.warn({ sessionId }, \"Remote input dropped: JSON worker socket not available\");\n return;\n }\n this.deps.jsonObserver.onTurnStart(sessionId);\n const messageId =\n msg.payload.messageId && msg.payload.messageId.length > 0\n ? msg.payload.messageId\n : `${sessionId}-user-${msg.timestamp}`;\n this.deps.relayConnection.sendEnvelope(\n MessageEnvelopeSchema.parse({\n type: \"user_input\",\n sessionId,\n seq: msg.seq,\n timestamp: msg.timestamp,\n source: \"proxy\",\n version: msg.version,\n payload: { text, messageId },\n }),\n );\n serviceLogger.info({ sessionId }, \"Remote input forwarded to JSON worker\");\n return;\n }\n\n serviceLogger.warn(\n { sessionId, mode: session.mode },\n \"Remote batch input dropped: PTY sessions require remote_input_raw\",\n );\n }\n\n onRemoteInputRaw(msg: ControlMessage<\"remote_input_raw\">): void {\n const { sessionId, data } = msg;\n if (!sessionId || data === undefined) return;\n\n const ts = this.deps.terminalSockets.get(sessionId);\n if (!ts?.writable && this.deps.hostedPtyRegistry.write(sessionId, data)) {\n serviceLogger.info(\n { sessionId, bytes: data.length },\n \"Raw PTY input forwarded to hosted PTY\",\n );\n return;\n }\n if (!ts?.writable) {\n serviceLogger.warn({ sessionId }, \"Raw PTY input dropped: terminal socket unavailable\");\n return;\n }\n ts.write(serializeRawPtyInput(sessionId, data));\n serviceLogger.info({ sessionId, bytes: data.length }, \"Raw PTY input forwarded\");\n }\n\n onClipboardImageUpload(msg: ControlMessage<\"clipboard_image_upload\">): void {\n const { sessionId, requestId } = msg;\n if (!sessionId) return;\n\n const session = this.deps.sessionManager.getSession(sessionId);\n if (!session) {\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"clipboard_image_upload_response\",\n requestId,\n sessionId,\n success: false,\n error: \"会话不存在\",\n errorCode: ControlErrorCode.SESSION_NOT_FOUND,\n }),\n );\n serviceLogger.warn({ sessionId }, \"Clipboard image upload rejected: session not found\");\n return;\n }\n\n const result = saveClipboardImageUpload(\n {\n sessionId,\n mimeType: msg.mimeType,\n dataBase64: msg.dataBase64,\n fileName: msg.fileName,\n },\n {\n cwd: session.cwd,\n },\n );\n\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"clipboard_image_upload_response\",\n requestId,\n sessionId,\n ...result,\n }),\n );\n serviceLogger.info({ sessionId, success: result.success }, \"Clipboard image upload handled\");\n }\n\n onImagePreviewRequest(msg: ControlMessage<\"image_preview_request\">): void {\n const { sessionId, requestId, path } = msg;\n if (!sessionId || !path) return;\n\n const session = this.deps.sessionManager.getSession(sessionId);\n if (!session) {\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"image_preview_response\",\n requestId,\n sessionId,\n success: false,\n path,\n error: \"会话不存在\",\n errorCode: ControlErrorCode.SESSION_NOT_FOUND,\n }),\n );\n serviceLogger.warn({ sessionId }, \"Image preview rejected: session not found\");\n return;\n }\n\n const result = loadImagePreview(\n { sessionId, path },\n {\n cwd: session.cwd,\n previewRoots: this.deps.previewRoots,\n },\n );\n\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"image_preview_response\",\n requestId,\n ...result,\n }),\n );\n if (result.success) {\n serviceLogger.info({ sessionId, path, size: result.size }, \"Image preview handled\");\n } else {\n serviceLogger.warn(\n { sessionId, path, errorCode: result.errorCode, error: result.error },\n \"Image preview failed\",\n );\n }\n }\n\n onFileDownloadRequest(msg: ControlMessage<\"file_download_request\">): void {\n const { sessionId, requestId, path } = msg;\n if (!sessionId || !path) return;\n\n const session = this.deps.sessionManager.getSession(sessionId);\n if (!session) {\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"file_download_response\",\n requestId,\n sessionId,\n success: false,\n path,\n error: \"会话不存在\",\n errorCode: ControlErrorCode.SESSION_NOT_FOUND,\n }),\n );\n serviceLogger.warn({ sessionId }, \"File download rejected: session not found\");\n return;\n }\n\n const result = loadFileDownload({ sessionId, path }, { cwd: session.cwd });\n\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"file_download_response\",\n requestId,\n ...result,\n }),\n );\n if (result.success) {\n serviceLogger.info({ sessionId, path, size: result.size }, \"File download handled\");\n } else {\n // 失败必带 errorCode + error, 否则只看 success=false 不知道是 ENOENT / EACCES / 超大 / 不是文件。\n serviceLogger.warn(\n { sessionId, path, errorCode: result.errorCode, error: result.error },\n \"File download failed\",\n );\n }\n }\n\n async onFileUploadRequest(msg: ControlMessage<\"file_upload_request\">): Promise<void> {\n const { sessionId, requestId, mimeType, dataBase64, fileName } = msg;\n if (!sessionId) return;\n\n const session = this.deps.sessionManager.getSession(sessionId);\n if (!session) {\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"file_upload_response\",\n requestId,\n sessionId,\n success: false,\n error: \"会话不存在\",\n errorCode: ControlErrorCode.SESSION_NOT_FOUND,\n }),\n );\n serviceLogger.warn({ sessionId }, \"File upload rejected: session not found\");\n return;\n }\n\n const result = await saveFileUpload(\n { sessionId, mimeType, dataBase64, fileName },\n { cwd: session.cwd },\n );\n\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"file_upload_response\",\n requestId,\n sessionId,\n ...result,\n }),\n );\n if (result.success) {\n serviceLogger.info({ sessionId, fileName, path: result.path }, \"File upload handled\");\n } else {\n serviceLogger.warn(\n { sessionId, fileName, errorCode: result.errorCode, error: result.error },\n \"File upload failed\",\n );\n }\n }\n}\n","import { serializeControl, type ControlMessage } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport type { PermissionBroker } from \"./permission-broker.js\";\nimport type { RelaySend } from \"./relay-router-types.js\";\nimport { readSessionMessagesPage } from \"./session-history.js\";\nimport type { SessionManager } from \"./session-manager.js\";\n\ninterface RelayHistoryHandlersDeps {\n relaySend: RelaySend;\n sessionManager: SessionManager;\n permissionBroker: PermissionBroker;\n}\n\nexport class RelayHistoryHandlers {\n constructor(private readonly deps: RelayHistoryHandlersDeps) {}\n\n onSessionMessagesRequest(msg: ControlMessage<\"session_messages_request\">): void {\n const { sessionId: sid, requestId, before, limit } = msg;\n if (!sid) return;\n\n const session = this.deps.sessionManager.getSession(sid);\n if (session?.claudeSessionId) {\n readSessionMessagesPage(session.claudeSessionId, { before, limit })\n .then((page) => {\n this.deps.relaySend(\n serializeControl({\n type: \"session_history_messages\",\n requestId,\n sessionId: sid,\n ...(before !== undefined ? { before } : {}),\n messages: page.messages,\n hasMore: page.hasMore,\n ...(page.nextBefore !== undefined ? { nextBefore: page.nextBefore } : {}),\n }),\n );\n serviceLogger.info(\n {\n sessionId: sid,\n before,\n hasMore: page.hasMore,\n nextBefore: page.nextBefore,\n messageCount: page.messages.length,\n },\n \"History message page sent on request\",\n );\n })\n .catch((err) => {\n serviceLogger.warn(\n { sessionId: sid, error: String(err) },\n \"Failed to read session history page on request\",\n );\n this.deps.relaySend(\n serializeControl({\n type: \"session_history_messages\",\n requestId,\n sessionId: sid,\n ...(before !== undefined ? { before } : {}),\n messages: [],\n hasMore: false,\n }),\n );\n });\n } else {\n this.deps.relaySend(\n serializeControl({\n type: \"session_history_messages\",\n requestId,\n sessionId: sid,\n ...(before !== undefined ? { before } : {}),\n messages: [],\n hasMore: false,\n }),\n );\n }\n\n const approvals = this.deps.permissionBroker.listSession(sid).map((approval) => ({\n requestId: approval.requestId,\n toolName: approval.toolName,\n input: approval.input,\n }));\n this.deps.relaySend(\n serializeControl({ type: \"pending_approvals_push\", sessionId: sid, approvals }),\n );\n serviceLogger.info({ sessionId: sid, count: approvals.length }, \"Pending approvals pushed\");\n }\n}\n","import { serializeControl, type ControlMessage } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport type { HookEventRouter } from \"./hook-event-router.js\";\nimport type { PermissionBroker } from \"./permission-broker.js\";\nimport type { RelaySend } from \"./relay-router-types.js\";\nimport type { WorkerRegistry } from \"./worker-registry.js\";\n\ninterface RelayPermissionHandlersDeps {\n relaySend: RelaySend;\n permissionBroker: PermissionBroker;\n hookEventRouter: HookEventRouter;\n workerRegistry: WorkerRegistry;\n}\n\nexport class RelayPermissionHandlers {\n constructor(private readonly deps: RelayPermissionHandlersDeps) {}\n\n onToolApprove(msg: ControlMessage<\"tool_approve\">): void {\n const { sessionId, payload } = msg;\n if (!sessionId || !payload?.toolId) return;\n\n const pending = this.deps.permissionBroker.get(payload.toolId);\n if (!pending) {\n this.pushPermissionDecisionResult(\n sessionId,\n payload.toolId,\n \"allow\",\n false,\n \"Permission request is no longer pending.\",\n );\n return;\n }\n if (!this.deps.permissionBroker.resolve(payload.toolId, { behavior: \"allow\" })) {\n this.pushPermissionDecisionResult(\n pending.sessionId,\n payload.toolId,\n \"allow\",\n false,\n \"Permission request is no longer pending.\",\n );\n return;\n }\n this.deps.hookEventRouter.onPermissionResolved(\n pending.sessionId,\n pending.provider,\n payload.toolId,\n \"allow\",\n { toolName: pending.toolName, toolInput: pending.input },\n );\n\n if (pending.source === \"worker\" && payload.whitelistTool) {\n const toolName = pending.toolName;\n if (toolName) {\n const whitelisted = this.deps.workerRegistry.send(pending.sessionId, {\n type: \"worker_whitelist_add\",\n toolName,\n });\n if (whitelisted) {\n serviceLogger.info(\n { sessionId: pending.sessionId, toolName },\n \"Tool added to session whitelist via relay\",\n );\n }\n }\n }\n this.pushPermissionDecisionResult(pending.sessionId, payload.toolId, \"allow\", true);\n serviceLogger.info(\n { sessionId, toolId: payload.toolId, whitelistTool: payload.whitelistTool },\n \"Tool approved via relay\",\n );\n }\n\n onToolDeny(msg: ControlMessage<\"tool_deny\">): void {\n const { sessionId, payload } = msg;\n if (!sessionId || !payload?.toolId) return;\n\n const reason = payload.reason ?? \"Denied by remote user\";\n const pending = this.deps.permissionBroker.get(payload.toolId);\n if (!pending) {\n this.pushPermissionDecisionResult(\n sessionId,\n payload.toolId,\n \"deny\",\n false,\n \"Permission request is no longer pending.\",\n );\n return;\n }\n if (\n !this.deps.permissionBroker.resolve(payload.toolId, {\n behavior: \"deny\",\n message: reason,\n })\n ) {\n this.pushPermissionDecisionResult(\n pending.sessionId,\n payload.toolId,\n \"deny\",\n false,\n \"Permission request is no longer pending.\",\n );\n return;\n }\n this.deps.hookEventRouter.onPermissionResolved(\n pending.sessionId,\n pending.provider,\n payload.toolId,\n \"deny\",\n { toolName: pending.toolName, toolInput: pending.input },\n );\n this.pushPermissionDecisionResult(pending.sessionId, payload.toolId, \"deny\", true, reason);\n serviceLogger.info({ sessionId, toolId: payload.toolId }, \"Tool denied via relay\");\n }\n\n onPermissionRequestDelivered(msg: ControlMessage<\"permission_request_delivered\">): void {\n const { sessionId: sid, requestId } = msg;\n if (!sid || !requestId) return;\n const marked = this.deps.permissionBroker.markDelivered(requestId);\n serviceLogger.info({ sessionId: sid, requestId, marked }, \"Permission request delivered\");\n }\n\n private pushPermissionDecisionResult(\n sessionId: string,\n requestId: string,\n outcome: \"allow\" | \"deny\",\n delivered: boolean,\n message?: string,\n ): void {\n this.deps.relaySend(\n serializeControl({\n type: \"permission_decision_result\",\n sessionId,\n requestId,\n outcome,\n delivered,\n ...(message ? { message } : {}),\n }),\n );\n }\n}\n","import { homedir } from \"node:os\";\nimport { accessSync, constants, statSync } from \"node:fs\";\nimport { ControlErrorCode, serializeControl, type ControlMessage } from \"@dev-anywhere/shared\";\nimport type { ControlMessageHandlers } from \"./handlers/control-messages.js\";\nimport type { RelaySend } from \"./relay-router-types.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { saveAgentCliPath } from \"../common/config.js\";\nimport { detectAgentCliStatus } from \"../providers/index.js\";\nimport type { ProviderId } from \"../providers/types.js\";\n\ninterface RelayResourceHandlersDeps {\n relaySend: RelaySend;\n controlHandlers: ControlMessageHandlers;\n sessionManager: SessionManager;\n getProviderEnv: () => NodeJS.ProcessEnv;\n getAgentCliSuggestions: () => Partial<Record<ProviderId, string[]>>;\n setAgentCliPath: (provider: ProviderId, path: string) => void;\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction validateExecutablePath(path: string): string {\n const normalized = path.trim();\n if (!normalized.startsWith(\"/\")) throw new Error(\"CLI 路径必须是绝对路径\");\n const stat = statSync(normalized);\n if (!stat.isFile()) throw new Error(\"CLI 路径不是可执行文件\");\n accessSync(normalized, constants.X_OK);\n return normalized;\n}\n\nexport class RelayResourceHandlers {\n constructor(private readonly deps: RelayResourceHandlersDeps) {}\n\n onProxyInfoRequest(msg: ControlMessage<\"proxy_info_request\">): void {\n this.deps.relaySend(\n serializeControl({\n type: \"proxy_info\",\n requestId: msg.requestId,\n homePath: homedir() || \"/\",\n agentCli: detectAgentCliStatus(this.deps.getProviderEnv(), {\n suggestions: this.deps.getAgentCliSuggestions(),\n }),\n }),\n );\n }\n\n onAgentCliConfigUpdate(msg: ControlMessage<\"agent_cli_config_update\">): void {\n const { requestId, provider } = msg;\n const rawPath = msg.path;\n\n if (provider !== \"claude\" && provider !== \"codex\") {\n this.deps.relaySend(\n serializeControl({\n type: \"agent_cli_config_update_response\",\n requestId,\n provider: \"claude\",\n errorCode: ControlErrorCode.PROVIDER_UNSUPPORTED,\n error: \"Unsupported Agent CLI.\",\n }),\n );\n return;\n }\n\n try {\n const path = validateExecutablePath(rawPath ?? \"\");\n saveAgentCliPath(provider, path);\n this.deps.setAgentCliPath(provider, path);\n const agentCli = detectAgentCliStatus(this.deps.getProviderEnv(), {\n suggestions: this.deps.getAgentCliSuggestions(),\n });\n this.deps.relaySend(\n serializeControl({\n type: \"agent_cli_config_update_response\",\n requestId,\n provider,\n agentCli,\n }),\n );\n serviceLogger.info({ provider, path }, \"Agent CLI path updated\");\n } catch (err) {\n const error = errorMessage(err);\n this.deps.relaySend(\n serializeControl({\n type: \"agent_cli_config_update_response\",\n requestId,\n provider,\n errorCode: ControlErrorCode.INVALID_PATH,\n error,\n }),\n );\n serviceLogger.warn({ provider, path: rawPath, error }, \"Agent CLI path update rejected\");\n }\n }\n\n onDirListRequest(msg: ControlMessage<\"dir_list_request\">): void {\n this.deps.controlHandlers.handleDirListRequest({\n path: msg.path ?? \"\",\n requestId: msg.requestId,\n });\n }\n\n onDirCreateRequest(msg: ControlMessage<\"dir_create_request\">): void {\n this.deps.controlHandlers.handleDirCreateRequest({\n path: msg.path ?? \"\",\n requestId: msg.requestId,\n });\n }\n\n onSessionResourcesRequest(msg: ControlMessage<\"session_resources_request\">): void {\n const sid = msg.sessionId;\n if (!sid) return;\n\n const session = this.deps.sessionManager.getSession(sid);\n if (!session?.cwd) {\n serviceLogger.warn({ sessionId: sid }, \"Session resources request: no cwd available\");\n this.deps.relaySend(\n serializeControl({\n type: \"session_resources_response\",\n requestId: msg.requestId,\n sessionId: sid,\n commands: [],\n groups: [],\n errorCode: ControlErrorCode.SESSION_NOT_FOUND,\n error: \"Session not found or cwd unavailable\",\n }),\n );\n return;\n }\n this.deps.controlHandlers.handleSessionResourcesRequest({\n sessionId: sid,\n requestId: msg.requestId,\n workDir: session.cwd,\n });\n serviceLogger.info({ sessionId: sid, cwd: session.cwd }, \"Session resources requested\");\n }\n}\n","import { rmSync, statSync } from \"node:fs\";\nimport { isAbsolute } from \"node:path\";\nimport { nanoid } from \"nanoid\";\nimport { ControlErrorCode, serializeControl, type ControlMessage } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { sessionPaths, tildify } from \"../common/paths.js\";\nimport type { ProviderHookContext, ProviderId } from \"../providers/index.js\";\nimport type { ControlMessageHandlers } from \"./handlers/control-messages.js\";\nimport { buildHostedPtyArgs, type HostedPtyRegistry } from \"./hosted-pty-registry.js\";\nimport type { PermissionBroker } from \"./permission-broker.js\";\nimport type { RelaySend } from \"./relay-router-types.js\";\nimport type { SessionInfo, SessionManager } from \"./session-manager.js\";\nimport { readSessionMessagesPage } from \"./session-history.js\";\nimport type { WorkerRegistry } from \"./worker-registry.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\nimport { classifyPathError } from \"./path-errors.js\";\n\ninterface RelaySessionCreateHandlerDeps {\n relaySend: RelaySend;\n workerRegistry: WorkerRegistry;\n sessionManager: SessionManager;\n hostedPtyRegistry: HostedPtyRegistry;\n controlHandlers: ControlMessageHandlers;\n permissionBroker: PermissionBroker;\n agentStatusRegistry: AgentStatusRegistry;\n getProviderEnv: () => NodeJS.ProcessEnv;\n createHookContext: (\n sessionId: string,\n provider: ProviderHookContext[\"provider\"],\n ) => ProviderHookContext;\n cleanupHookContext: (sessionId: string) => void;\n broadcastSessionSync: (session: SessionInfo) => void;\n broadcastSessionList: () => void;\n}\n\ninterface SessionCwdValidationError {\n message: string;\n code: ControlErrorCode;\n}\n\nfunction validateSessionCwd(cwd: unknown): SessionCwdValidationError | null {\n if (typeof cwd !== \"string\" || !cwd.trim()) {\n return { message: \"请输入工作目录\", code: ControlErrorCode.INVALID_PATH };\n }\n const trimmed = cwd.trim();\n if (!isAbsolute(trimmed)) {\n return { message: \"工作目录必须是绝对路径\", code: ControlErrorCode.INVALID_PATH };\n }\n try {\n const stat = statSync(trimmed);\n return stat.isDirectory()\n ? null\n : { message: \"工作目录不是目录\", code: ControlErrorCode.PATH_NOT_DIRECTORY };\n } catch (err) {\n return {\n message: `工作目录不存在或不可访问: ${trimmed}`,\n code: classifyPathError(err),\n };\n }\n}\n\nexport class RelaySessionCreateHandler {\n // 跟踪每个 pendingId 当前挂起的 retry timer。SIGTERM 抵达时 destroy() 会 clear\n // 这些 timer 并执行 cleanupPendingJsonSession,否则 worker 子进程在窗口期内可能成为孤儿\n // (setTimeout 回调命中时 workerRegistry 已经 destroyAll,但 worker 进程并未被 kill)。\n private readonly pendingTimers = new Map<string, NodeJS.Timeout>();\n\n constructor(private readonly deps: RelaySessionCreateHandlerDeps) {}\n\n destroy(): void {\n for (const [pendingId, timer] of this.pendingTimers) {\n clearTimeout(timer);\n this.cleanupPendingJsonSession(pendingId);\n }\n this.pendingTimers.clear();\n }\n\n onSessionCreate(msg: ControlMessage<\"session_create\">): void {\n const { requestId, cwd } = msg;\n const cwdError = validateSessionCwd(cwd);\n if (cwdError) {\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId,\n error: cwdError.message,\n errorCode: cwdError.code,\n }),\n );\n serviceLogger.warn({ cwd }, \"Session create rejected: invalid cwd\");\n return;\n }\n const sessionCwd = typeof cwd === \"string\" ? cwd.trim() : \"\";\n\n const provider = msg.provider;\n const mode = msg.mode ?? \"json\";\n const permissionMode = msg.permissionMode;\n if (mode === \"pty\") {\n this.createHostedPtySession(msg, sessionCwd, provider ?? \"claude\", permissionMode);\n return;\n }\n\n if (provider !== \"claude\") {\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId,\n errorCode: ControlErrorCode.PROVIDER_UNSUPPORTED,\n error:\n provider === \"codex\"\n ? \"Codex chat sessions are not supported yet; start a Codex terminal session instead.\"\n : \"Unsupported provider for JSON session.\",\n }),\n );\n serviceLogger.warn({ provider }, \"JSON session create rejected for unsupported provider\");\n return;\n }\n\n const resumeSessionId = msg.resumeSessionId;\n // streamDelta 不在 session_create 协议字段里:当前没有客户端发起 delta 模式,\n // 默认关闭即可。后续若要恢复增量推送,需先在 RelayControlSchema 加字段。\n const streamDelta = false;\n const name = tildify(sessionCwd);\n const pendingId = nanoid();\n const hook = this.deps.createHookContext(pendingId, provider);\n const workerPid = this.deps.workerRegistry.spawn(pendingId, {\n cwd: sessionCwd,\n resumeSessionId,\n permissionMode,\n streamDelta,\n hook,\n });\n\n const paths = sessionPaths(pendingId);\n let attempt = 0;\n const maxRetries = 20;\n const scheduleAttempt = (delayMs: number): void => {\n const timer = setTimeout(tryConnect, delayMs);\n this.pendingTimers.set(pendingId, timer);\n };\n const tryConnect = () => {\n this.pendingTimers.delete(pendingId);\n attempt++;\n this.deps.workerRegistry.connect(pendingId, paths.workerSock).then((sock) => {\n if (sock) {\n const session = this.deps.sessionManager.createSession(\n \"json\",\n sessionCwd,\n workerPid,\n name,\n pendingId,\n provider,\n );\n if (resumeSessionId) {\n this.deps.sessionManager.setClaudeSessionId(session.id, resumeSessionId);\n }\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId,\n sessionId: session.id,\n }),\n );\n if (resumeSessionId) {\n this.pushHistoryMessages(session.id, resumeSessionId);\n }\n serviceLogger.info(\n { sessionId: session.id, cwd: sessionCwd },\n \"JSON session created via relay\",\n );\n this.deps.controlHandlers.pushCommandList(session.id, sessionCwd);\n this.deps.broadcastSessionSync(session);\n this.deps.broadcastSessionList();\n } else if (attempt < maxRetries) {\n scheduleAttempt(Math.min(100 * attempt, 2000));\n } else {\n this.cleanupPendingJsonSession(pendingId);\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId,\n sessionId: pendingId,\n errorCode: ControlErrorCode.WORKER_START_FAILED,\n error: \"Worker failed to start\",\n }),\n );\n serviceLogger.error({ sessionId: pendingId }, \"Worker connection timeout via relay\");\n }\n });\n };\n scheduleAttempt(100);\n }\n\n private cleanupPendingJsonSession(sessionId: string): void {\n const killed = this.deps.workerRegistry.terminateProcess(sessionId);\n const paths = sessionPaths(sessionId);\n rmSync(paths.dir, { recursive: true, force: true });\n this.deps.cleanupHookContext(sessionId);\n this.deps.permissionBroker.cleanupSession(sessionId, \"Worker failed to start\");\n this.deps.agentStatusRegistry.delete(sessionId);\n serviceLogger.warn(\n { sessionId, killed },\n \"Cleaned up pending JSON session after startup failure\",\n );\n }\n\n private createHostedPtySession(\n msg: ControlMessage<\"session_create\">,\n cwd: string,\n provider: ProviderId,\n permissionMode?: string,\n ): void {\n if (provider !== \"claude\" && provider !== \"codex\") {\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId: msg.requestId,\n errorCode: ControlErrorCode.PROVIDER_UNSUPPORTED,\n error: \"Unsupported provider for PTY session.\",\n }),\n );\n return;\n }\n\n const resumeSessionId = msg.resumeSessionId;\n const pendingId = nanoid();\n const name = tildify(cwd);\n const hook = this.deps.createHookContext(pendingId, provider);\n try {\n const pid = this.deps.hostedPtyRegistry.start({\n sessionId: pendingId,\n provider,\n cwd,\n args: buildHostedPtyArgs(provider, resumeSessionId),\n permissionMode,\n hook,\n });\n const session = this.deps.sessionManager.createSession(\n \"pty\",\n cwd,\n pid,\n name,\n pendingId,\n provider,\n \"proxy-hosted\",\n );\n if (resumeSessionId && provider === \"claude\") {\n this.deps.sessionManager.setClaudeSessionId(session.id, resumeSessionId);\n }\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId: msg.requestId,\n sessionId: session.id,\n mode: \"pty\",\n provider,\n ptyOwner: \"proxy-hosted\",\n }),\n );\n this.deps.controlHandlers.pushCommandList(session.id, cwd);\n this.deps.controlHandlers.pushFileTree(session.id, cwd);\n this.deps.broadcastSessionSync(session);\n this.deps.broadcastSessionList();\n serviceLogger.info({ sessionId: session.id, provider, cwd }, \"Hosted PTY session created\");\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId: msg.requestId,\n errorCode: ControlErrorCode.PROCESS_START_FAILED,\n error,\n }),\n );\n const providerEnv = this.deps.getProviderEnv();\n serviceLogger.warn(\n {\n provider,\n cwd,\n error,\n claudeBin: providerEnv.CLAUDE_BIN,\n codexBin: providerEnv.CODEX_BIN,\n path: providerEnv.PATH,\n },\n \"Hosted PTY session create failed\",\n );\n }\n }\n\n private pushHistoryMessages(sessionId: string, resumeSessionId: string): void {\n readSessionMessagesPage(resumeSessionId)\n .then((page) => {\n if (page.messages.length === 0) return;\n this.deps.relaySend(\n serializeControl({\n type: \"session_history_messages\",\n sessionId,\n messages: page.messages,\n hasMore: page.hasMore,\n ...(page.nextBefore !== undefined ? { nextBefore: page.nextBefore } : {}),\n }),\n );\n serviceLogger.info(\n {\n sessionId,\n resumeSessionId,\n messageCount: page.messages.length,\n hasMore: page.hasMore,\n },\n \"History message page sent for resumed session\",\n );\n })\n .catch((err) => {\n serviceLogger.warn(\n { sessionId, error: String(err) },\n \"Failed to read session history page\",\n );\n });\n }\n}\n","import * as pty from \"node-pty\";\nimport type { IPty } from \"node-pty\";\nimport {\n SessionState,\n encodeBinaryFrame,\n serializeControl,\n type PtySemanticState,\n} from \"@dev-anywhere/shared\";\nimport pkg from \"@xterm/headless\";\nconst { Terminal: HeadlessTerminal } = pkg;\nimport { SerializeAddon } from \"@xterm/addon-serialize\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { extractOscSequences, extractOscSignals } from \"../common/osc-extractor.js\";\nimport { decidePtySemanticTransition } from \"../common/pty-semantic-machine.js\";\nimport {\n CLAUDE_PROVIDER,\n CODEX_PROVIDER,\n type ProviderAdapter,\n type ProviderHookContext,\n type ProviderId,\n} from \"../providers/index.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { SessionManager } from \"./session-manager.js\";\n\nconst DEFAULT_COLS = 80;\nconst DEFAULT_ROWS = 24;\nconst IDLE_CHECK_INTERVAL_MS = 3_000;\nconst IDLE_THRESHOLD_MS = 3_000;\n\nconst PROVIDERS: Record<ProviderId, ProviderAdapter> = {\n claude: CLAUDE_PROVIDER,\n codex: CODEX_PROVIDER,\n};\n\nconst HOSTED_PTY_TERM = \"xterm-256color\";\nconst HOSTED_PTY_COLORTERM = \"truecolor\";\n\ninterface HostedPtyRegistryDeps {\n sessionManager: SessionManager;\n relayConnection: RelayConnection;\n getProviderEnv: () => NodeJS.ProcessEnv;\n touchSessionActivity: (sessionId: string) => boolean;\n // PTY → Session FSM 的翻译副作用(changeSessionState、清理 interrupted approvals、推\n // agent status 等)由 bridge 收口;hosted 与 terminal-ipc 共用同一实现。\n applyPtyStateToSession: (sessionId: string, ptyState: PtySemanticState) => void;\n onSessionClosed: (sessionId: string) => void;\n}\n\ninterface HostedPtyStartOptions {\n sessionId: string;\n provider: ProviderId;\n cwd: string;\n args: string[];\n permissionMode?: string;\n hook: ProviderHookContext;\n cols?: number;\n rows?: number;\n}\n\ninterface HostedPtySession {\n child: IPty;\n terminal: InstanceType<typeof HeadlessTerminal>;\n serializeAddon: SerializeAddon;\n idleTimer: NodeJS.Timeout;\n lastOutputTime: number;\n currentState: PtySemanticState;\n outputSeq: number;\n}\n\nexport function buildHostedPtyArgs(provider: ProviderId, resumeSessionId?: string): string[] {\n if (!resumeSessionId) return [];\n return provider === \"codex\" ? [\"resume\", resumeSessionId] : [\"--resume\", resumeSessionId];\n}\n\nexport function normalizeHostedPtyEnv(env: NodeJS.ProcessEnv): Record<string, string> {\n const normalized: Record<string, string> = {};\n for (const [key, value] of Object.entries(env)) {\n if (value === undefined) continue;\n normalized[key] = value;\n }\n\n delete normalized.NO_COLOR;\n if (normalized.CLICOLOR === \"0\") {\n delete normalized.CLICOLOR;\n }\n\n normalized.TERM = HOSTED_PTY_TERM;\n normalized.COLORTERM = HOSTED_PTY_COLORTERM;\n normalized.CLICOLOR = \"1\";\n return normalized;\n}\n\nexport class HostedPtyRegistry {\n private sessions = new Map<string, HostedPtySession>();\n\n constructor(private readonly deps: HostedPtyRegistryDeps) {}\n\n start(options: HostedPtyStartOptions): number {\n const provider = PROVIDERS[options.provider];\n const cols = options.cols ?? DEFAULT_COLS;\n const rows = options.rows ?? DEFAULT_ROWS;\n const command = provider.buildTerminalCommand(\n { args: options.args, permissionMode: options.permissionMode, hook: options.hook },\n this.deps.getProviderEnv(),\n );\n const env = normalizeHostedPtyEnv(command.env);\n const child = pty.spawn(command.command, command.args, {\n name: HOSTED_PTY_TERM,\n cols,\n rows,\n cwd: options.cwd,\n env,\n });\n\n const terminal = new HeadlessTerminal({ cols, rows, scrollback: 5000, allowProposedApi: true });\n const serializeAddon = new SerializeAddon();\n terminal.loadAddon(serializeAddon);\n void import(\"@xterm/addon-unicode-graphemes\")\n .then(({ UnicodeGraphemesAddon }) => terminal.loadAddon(new UnicodeGraphemesAddon()))\n .catch((err) => {\n serviceLogger.warn(\n { sessionId: options.sessionId, error: String(err) },\n \"Unicode addon unavailable\",\n );\n });\n\n const hosted: HostedPtySession = {\n child,\n terminal,\n serializeAddon,\n idleTimer: setInterval(() => this.checkIdle(options.sessionId), IDLE_CHECK_INTERVAL_MS),\n lastOutputTime: 0,\n currentState: \"turn_complete\",\n outputSeq: 0,\n };\n this.sessions.set(options.sessionId, hosted);\n\n child.onData((data) => this.handleData(options.sessionId, data));\n child.onExit(({ exitCode, signal }) => {\n const code = signal ? 128 + signal : exitCode;\n serviceLogger.info({ sessionId: options.sessionId, code }, \"Hosted PTY exited\");\n this.close(options.sessionId, { kill: false, notify: true });\n });\n\n serviceLogger.info(\n {\n sessionId: options.sessionId,\n provider: options.provider,\n command: command.command,\n pid: child.pid,\n cwd: options.cwd,\n cols,\n rows,\n },\n \"Hosted PTY started\",\n );\n return child.pid;\n }\n\n has(sessionId: string): boolean {\n return this.sessions.has(sessionId);\n }\n\n write(sessionId: string, data: string): boolean {\n const hosted = this.sessions.get(sessionId);\n if (!hosted) return false;\n hosted.child.write(data);\n return true;\n }\n\n resize(sessionId: string, cols: number, rows: number): boolean {\n const hosted = this.sessions.get(sessionId);\n if (!hosted) return false;\n hosted.child.resize(cols, rows);\n hosted.terminal.resize(cols, rows);\n this.deps.relayConnection.sendRaw(\n serializeControl({ type: \"terminal_resize\", sessionId, cols, rows }),\n );\n serviceLogger.info({ sessionId, cols, rows }, \"Hosted PTY resized\");\n return true;\n }\n\n snapshot(sessionId: string, requestId?: string): boolean {\n const hosted = this.sessions.get(sessionId);\n if (!hosted) return false;\n const data = hosted.serializeAddon.serialize();\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"session_snapshot\",\n sessionId,\n cols: hosted.terminal.cols,\n rows: hosted.terminal.rows,\n data,\n outputSeq: hosted.outputSeq,\n ...(requestId !== undefined ? { requestId } : {}),\n }),\n );\n serviceLogger.info(\n { sessionId, cols: hosted.terminal.cols, rows: hosted.terminal.rows, bytes: data.length },\n \"Hosted PTY snapshot sent\",\n );\n return true;\n }\n\n terminate(sessionId: string): boolean {\n return this.close(sessionId, { kill: true, notify: true });\n }\n\n destroyAll(): void {\n for (const sessionId of Array.from(this.sessions.keys())) {\n this.close(sessionId, { kill: true, notify: false });\n }\n }\n\n private handleData(sessionId: string, data: string): void {\n const hosted = this.sessions.get(sessionId);\n if (!hosted) return;\n hosted.lastOutputTime = Date.now();\n hosted.outputSeq += 1;\n hosted.terminal.write(data);\n this.deps.touchSessionActivity(sessionId);\n this.sendBinary(sessionId, Buffer.from(data, \"utf-8\"), hosted.outputSeq);\n\n const oscSequences = extractOscSequences(data);\n const session = this.deps.sessionManager.getSession(sessionId);\n const signal = extractOscSignals(data, session?.provider);\n if (oscSequences.length > 0) {\n serviceLogger.debug(\n {\n sessionId,\n oscSequences,\n signal,\n },\n \"Hosted PTY OSC sequences parsed\",\n );\n }\n if (signal?.title) {\n this.sendTerminalTitle(sessionId, signal.title);\n }\n\n // 语义决策走统一 common/pty-semantic-machine;hosted 端在 emit 时多做两件事:\n // 1. 把 PTY semantic state 翻译成 session JSON FSM 转换;2. turn_complete 时触发 onTurnComplete 回调。\n const decision = decidePtySemanticTransition({\n currentState: hosted.currentState,\n signal: signal ?? null,\n sessionStateIsWaitingApproval: session?.state === SessionState.WAITING_APPROVAL,\n });\n hosted.currentState = decision.nextState;\n if (!decision.emit) return;\n\n this.sendPtyState(sessionId, decision.nextState, decision.meta);\n this.deps.applyPtyStateToSession(sessionId, decision.nextState);\n }\n\n private checkIdle(sessionId: string): void {\n const hosted = this.sessions.get(sessionId);\n if (!hosted) return;\n if (hosted.lastOutputTime === 0 || Date.now() - hosted.lastOutputTime <= IDLE_THRESHOLD_MS) {\n return;\n }\n hosted.lastOutputTime = 0;\n if (hosted.currentState !== \"working\") return;\n hosted.currentState = \"turn_complete\";\n this.sendPtyState(sessionId, \"turn_complete\");\n this.deps.applyPtyStateToSession(sessionId, \"turn_complete\");\n }\n\n private sendPtyState(\n sessionId: string,\n state: PtySemanticState,\n meta?: { title?: string; tool?: string },\n ): void {\n const payload = {\n state,\n ...(meta?.title !== undefined ? { title: meta.title } : {}),\n ...(meta?.tool !== undefined ? { tool: meta.tool } : {}),\n };\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"pty_state\",\n sessionId,\n payload,\n }),\n );\n const logPayload = { sessionId, ...payload };\n if (state === \"approval_wait\" || state === \"turn_complete\") {\n serviceLogger.info(logPayload, \"Hosted PTY semantic event pushed\");\n } else {\n serviceLogger.debug(logPayload, \"Hosted PTY semantic event pushed\");\n }\n }\n\n private sendTerminalTitle(sessionId: string, title: string): void {\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"terminal_title\",\n sessionId,\n title,\n }),\n );\n }\n\n private sendBinary(sessionId: string, data: Buffer, outputSeq: number): void {\n this.deps.relayConnection.sendBinary(encodeBinaryFrame(sessionId, outputSeq, data));\n }\n\n private close(sessionId: string, options: { kill: boolean; notify: boolean }): boolean {\n const hosted = this.sessions.get(sessionId);\n if (!hosted) return false;\n this.sessions.delete(sessionId);\n clearInterval(hosted.idleTimer);\n if (options.kill) {\n try {\n hosted.child.kill();\n } catch {\n // PTY may already have exited.\n }\n }\n hosted.terminal.dispose();\n if (options.notify) {\n this.sendPtyState(sessionId, \"turn_complete\");\n this.deps.sessionManager.terminateSession(sessionId);\n this.deps.onSessionClosed(sessionId);\n }\n return true;\n }\n}\n","import type { Socket } from \"node:net\";\nimport {\n MessageEnvelopeSchema,\n RelayControlSchema,\n SessionState,\n serializeControl,\n type AgentStatusPayload,\n type ControlMessage,\n type RelayControlMessage,\n} from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { serializeIpc } from \"../ipc/ipc-protocol.js\";\nimport type { SessionInfo, SessionManager } from \"./session-manager.js\";\nimport type { WorkerRegistry } from \"./worker-registry.js\";\nimport type { ControlMessageHandlers } from \"./handlers/control-messages.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { JsonObserver } from \"./json-observer.js\";\nimport type { ProviderHookContext } from \"../providers/index.js\";\nimport type { PermissionBroker } from \"./permission-broker.js\";\nimport type { HookEventRouter } from \"./hook-event-router.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\nimport type { HostedPtyRegistry } from \"./hosted-pty-registry.js\";\nimport { terminateSessionByOwnership } from \"./session-termination.js\";\nimport { RelayInputHandlers } from \"./relay-input-handlers.js\";\nimport { RelayHistoryHandlers } from \"./relay-history-handlers.js\";\nimport { RelayPermissionHandlers } from \"./relay-permission-handlers.js\";\nimport { RelayResourceHandlers } from \"./relay-resource-handlers.js\";\nimport { RelaySessionCreateHandler } from \"./relay-session-create-handler.js\";\nimport type { RelaySend } from \"./relay-router-types.js\";\n\ninterface RelayRouterDeps {\n sessionManager: SessionManager;\n workerRegistry: WorkerRegistry;\n controlHandlers: ControlMessageHandlers;\n relayConnection: RelayConnection;\n relaySend: RelaySend;\n terminalSockets: Map<string, Socket>;\n hostedPtyRegistry: HostedPtyRegistry;\n broadcastSessionList: () => void;\n broadcastSessionSync: (session: SessionInfo) => void;\n // user_input 注入触发 turn 开始(JSON 观察器)\n jsonObserver: JsonObserver;\n createHookContext: (\n sessionId: string,\n provider: ProviderHookContext[\"provider\"],\n ) => ProviderHookContext;\n cleanupHookContext: (sessionId: string) => void;\n permissionBroker: PermissionBroker;\n hookEventRouter: HookEventRouter;\n agentStatusRegistry: AgentStatusRegistry;\n getProviderEnv: () => NodeJS.ProcessEnv;\n getAgentCliSuggestions: () => Partial<Record<ProviderHookContext[\"provider\"], string[]>>;\n setAgentCliPath: (provider: ProviderHookContext[\"provider\"], path: string) => void;\n getPreviewRoots?: () => string[];\n}\n\n// 按 type 分发入站 relay 消息到独立 handler。未知 type warn 不丢,schema 逐步收紧。\nexport class RelayRouter {\n private readonly historyHandlers: RelayHistoryHandlers;\n private readonly inputHandlers: RelayInputHandlers;\n private readonly permissionHandlers: RelayPermissionHandlers;\n private readonly resourceHandlers: RelayResourceHandlers;\n private readonly sessionCreateHandler: RelaySessionCreateHandler;\n\n constructor(private deps: RelayRouterDeps) {\n this.historyHandlers = new RelayHistoryHandlers({\n relaySend: deps.relaySend,\n sessionManager: deps.sessionManager,\n permissionBroker: deps.permissionBroker,\n });\n this.inputHandlers = new RelayInputHandlers({\n sessionManager: deps.sessionManager,\n workerRegistry: deps.workerRegistry,\n relayConnection: deps.relayConnection,\n terminalSockets: deps.terminalSockets,\n hostedPtyRegistry: deps.hostedPtyRegistry,\n jsonObserver: deps.jsonObserver,\n previewRoots: deps.getPreviewRoots?.(),\n });\n this.resourceHandlers = new RelayResourceHandlers({\n relaySend: deps.relaySend,\n controlHandlers: deps.controlHandlers,\n sessionManager: deps.sessionManager,\n getProviderEnv: deps.getProviderEnv,\n getAgentCliSuggestions: deps.getAgentCliSuggestions,\n setAgentCliPath: deps.setAgentCliPath,\n });\n this.permissionHandlers = new RelayPermissionHandlers({\n relaySend: deps.relaySend,\n permissionBroker: deps.permissionBroker,\n hookEventRouter: deps.hookEventRouter,\n workerRegistry: deps.workerRegistry,\n });\n this.sessionCreateHandler = new RelaySessionCreateHandler({\n relaySend: deps.relaySend,\n workerRegistry: deps.workerRegistry,\n sessionManager: deps.sessionManager,\n hostedPtyRegistry: deps.hostedPtyRegistry,\n controlHandlers: deps.controlHandlers,\n permissionBroker: deps.permissionBroker,\n agentStatusRegistry: deps.agentStatusRegistry,\n getProviderEnv: deps.getProviderEnv,\n createHookContext: deps.createHookContext,\n cleanupHookContext: deps.cleanupHookContext,\n broadcastSessionSync: deps.broadcastSessionSync,\n broadcastSessionList: deps.broadcastSessionList,\n });\n }\n\n // shutdown 链路上提供单一 destroy 入口:把 sessionCreateHandler 内部 pending retry timer 清掉\n // 并 cleanup 已 spawn 但未 connect 的 worker 子进程,避免在 SIGTERM 之后变成孤儿。\n destroy(): void {\n this.sessionCreateHandler.destroy();\n }\n\n // 入站消息统一入口:proxy 收两类消息——relay control 与 envelope(user_input 这一种)。\n // 先按 envelope 试解析(discriminated union),失败再按 control 解析;各 handler 拿到\n // 强类型窄化后的消息,不再需要 `as string | undefined` / `as { ... }` 裸 cast。\n handle(rawMsg: Record<string, unknown>): void {\n const asEnvelope = MessageEnvelopeSchema.safeParse(rawMsg);\n if (asEnvelope.success && asEnvelope.data.type === \"user_input\") {\n try {\n this.inputHandlers.onUserInput(asEnvelope.data);\n } catch (err) {\n serviceLogger.warn({ type: \"user_input\", error: String(err) }, \"Relay handler threw\");\n }\n return;\n }\n\n const asControl = RelayControlSchema.safeParse(rawMsg);\n if (!asControl.success) {\n serviceLogger.warn(\n {\n type: typeof rawMsg.type === \"string\" ? rawMsg.type : \"<missing>\",\n controlIssues: asControl.error.issues.slice(0, 3),\n },\n \"Relay message rejected by both envelope and control schemas\",\n );\n return;\n }\n const msg = asControl.data;\n try {\n this.dispatch(msg);\n } catch (err) {\n serviceLogger.warn({ type: msg.type, error: String(err) }, \"Relay handler threw\");\n }\n }\n\n private dispatch(msg: RelayControlMessage): void {\n switch (msg.type) {\n case \"remote_input_raw\":\n this.inputHandlers.onRemoteInputRaw(msg);\n return;\n case \"clipboard_image_upload\":\n this.inputHandlers.onClipboardImageUpload(msg);\n return;\n case \"image_preview_request\":\n this.inputHandlers.onImagePreviewRequest(msg);\n return;\n case \"file_download_request\":\n this.inputHandlers.onFileDownloadRequest(msg);\n return;\n case \"file_upload_request\":\n void this.inputHandlers.onFileUploadRequest(msg);\n return;\n case \"tool_approve\":\n this.permissionHandlers.onToolApprove(msg);\n return;\n case \"tool_deny\":\n this.permissionHandlers.onToolDeny(msg);\n return;\n case \"proxy_info_request\":\n this.resourceHandlers.onProxyInfoRequest(msg);\n return;\n case \"agent_cli_config_update\":\n this.resourceHandlers.onAgentCliConfigUpdate(msg);\n return;\n case \"dir_list_request\":\n this.resourceHandlers.onDirListRequest(msg);\n return;\n case \"dir_create_request\":\n this.resourceHandlers.onDirCreateRequest(msg);\n return;\n case \"session_create\":\n this.sessionCreateHandler.onSessionCreate(msg);\n return;\n case \"session_messages_request\":\n this.historyHandlers.onSessionMessagesRequest(msg);\n return;\n case \"session_resources_request\":\n this.resourceHandlers.onSessionResourcesRequest(msg);\n return;\n case \"agent_status_request\":\n this.onAgentStatusRequest(msg);\n return;\n case \"permission_request_delivered\":\n this.permissionHandlers.onPermissionRequestDelivered(msg);\n return;\n case \"session_terminate\":\n this.onSessionTerminate(msg);\n return;\n case \"session_worker_abort\":\n this.onSessionWorkerAbort(msg);\n return;\n case \"session_history_request\":\n this.deps.controlHandlers.handleSessionHistoryRequest({ requestId: msg.requestId });\n return;\n case \"session_list\":\n this.onSessionList();\n return;\n case \"permission_mode_change\":\n this.onPermissionModeChange(msg);\n return;\n case \"session_subscribe\":\n this.onSessionSubscribe(msg);\n return;\n case \"terminal_resize_request\":\n this.onTerminalResizeRequest(msg);\n return;\n default:\n // proxy_to_client 方向的 control 消息由 client/relay 处理,这里直接忽略。\n return;\n }\n }\n\n private onAgentStatusRequest(msg: ControlMessage<\"agent_status_request\">): void {\n const sid = msg.sessionId;\n const requestId = msg.requestId;\n if (sid) {\n const status = this.deps.agentStatusRegistry.get(sid);\n const statuses =\n status && this.deps.sessionManager.getSession(sid)\n ? [{ sessionId: sid, payload: status }]\n : [];\n this.deps.relaySend(serializeControl({ type: \"agent_status_response\", requestId, statuses }));\n serviceLogger.info({ sessionId: sid, count: statuses.length }, \"Agent status snapshot sent\");\n return;\n }\n\n const statuses: Array<{ sessionId: string; payload: AgentStatusPayload }> = [];\n for (const { sessionId, status } of this.deps.agentStatusRegistry.list()) {\n if (!this.deps.sessionManager.getSession(sessionId)) continue;\n statuses.push({ sessionId, payload: status });\n }\n this.deps.relaySend(serializeControl({ type: \"agent_status_response\", requestId, statuses }));\n serviceLogger.info({ count: statuses.length }, \"Agent status snapshot sent\");\n }\n\n private onSessionTerminate(msg: ControlMessage<\"session_terminate\">): void {\n const sid = msg.sessionId;\n if (!sid) return;\n\n const result = terminateSessionByOwnership(this.deps, sid);\n serviceLogger.info(\n { sessionId: sid, success: result.success, action: result.action },\n \"Session termination handled via relay\",\n );\n }\n\n private onSessionWorkerAbort(msg: ControlMessage<\"session_worker_abort\">): void {\n const sid = msg.sessionId;\n if (!sid) return;\n\n const session = this.deps.sessionManager.getSession(sid);\n if (!session) {\n serviceLogger.warn({ sessionId: sid }, \"session_worker_abort: session not found\");\n return;\n }\n if (session.state === SessionState.TERMINATED) {\n serviceLogger.info({ sessionId: sid }, \"session_worker_abort: already terminated, dropping\");\n return;\n }\n\n if (session.mode === \"pty\") {\n // PTY 会话直接把 Ctrl+C 写入 PTY stdin,避免杀掉 terminal wrapper 进程\n const ts = this.deps.terminalSockets.get(sid);\n if (this.deps.hostedPtyRegistry.write(sid, \"\\x03\")) {\n serviceLogger.info({ sessionId: sid }, \"session_worker_abort: Ctrl+C sent to hosted PTY\");\n } else if (ts?.writable) {\n ts.write(serializeIpc({ type: \"pty_input\", sessionId: sid, data: \"\\x03\" }));\n serviceLogger.info({ sessionId: sid }, \"session_worker_abort: Ctrl+C sent to PTY\");\n } else {\n serviceLogger.warn(\n { sessionId: sid },\n \"session_worker_abort: PTY terminal socket unavailable\",\n );\n }\n return;\n }\n\n try {\n process.kill(session.pid, \"SIGINT\");\n serviceLogger.info(\n { sessionId: sid, pid: session.pid },\n \"session_worker_abort: SIGINT sent to worker\",\n );\n } catch (err) {\n serviceLogger.warn(\n { sessionId: sid, pid: session.pid, error: String(err) },\n \"session_worker_abort: kill failed\",\n );\n }\n }\n\n private onSessionList(): void {\n this.deps.broadcastSessionList();\n serviceLogger.info(\"Session list sent via relay\");\n }\n\n private onPermissionModeChange(msg: ControlMessage<\"permission_mode_change\">): void {\n const sid = msg.sessionId;\n const mode = msg.mode;\n if (!sid) {\n serviceLogger.info(\n { mode },\n \"Permission mode change received via relay (global, no sessionId)\",\n );\n return;\n }\n\n const session = this.deps.sessionManager.getSession(sid);\n if (session?.mode !== \"pty\") {\n serviceLogger.info(\n { sessionId: sid, mode },\n \"Permission mode change received for JSON session (no-op, not supported)\",\n );\n return;\n }\n\n // PTY 会话:发 Shift+Tab (CSI Z) 让 claude CLI 循环 permission mode\n // mode 字段当前保留但不使用 —— Claude CLI 仅支持循环键,无法一键直选档位\n const ts = this.deps.terminalSockets.get(sid);\n if (this.deps.hostedPtyRegistry.write(sid, \"\\x1b[Z\")) {\n serviceLogger.info(\n { sessionId: sid, mode },\n \"Permission mode cycle: Shift+Tab sent to hosted PTY\",\n );\n } else if (ts?.writable) {\n ts.write(serializeIpc({ type: \"pty_input\", sessionId: sid, data: \"\\x1b[Z\" }));\n serviceLogger.info({ sessionId: sid, mode }, \"Permission mode cycle: Shift+Tab sent to PTY\");\n } else {\n serviceLogger.warn(\n { sessionId: sid },\n \"Permission mode cycle: PTY terminal socket unavailable\",\n );\n }\n }\n\n private onSessionSubscribe(msg: ControlMessage<\"session_subscribe\">): void {\n const sid = msg.sessionId;\n const requestId = msg.requestId;\n if (!sid) return;\n\n const ts = this.deps.terminalSockets.get(sid);\n if (this.deps.hostedPtyRegistry.snapshot(sid, requestId)) {\n serviceLogger.info({ sessionId: sid, requestId }, \"Subscribe handled by hosted PTY\");\n } else if (ts?.writable) {\n ts.write(serializeIpc({ type: \"pty_subscribe\", sessionId: sid, requestId }));\n serviceLogger.info({ sessionId: sid, requestId }, \"Subscribe forwarded to terminal\");\n } else {\n serviceLogger.warn({ sessionId: sid }, \"Subscribe failed: terminal socket not available\");\n }\n }\n\n private onTerminalResizeRequest(msg: ControlMessage<\"terminal_resize_request\">): void {\n const sid = msg.sessionId;\n const cols = msg.cols;\n const rows = msg.rows;\n if (!sid || !cols || !rows) return;\n if (!this.deps.hostedPtyRegistry.resize(sid, cols, rows)) {\n serviceLogger.debug({ sessionId: sid, cols, rows }, \"Resize request ignored: not hosted PTY\");\n }\n }\n}\n","import { SessionState, type AgentStatusPayload } from \"@dev-anywhere/shared\";\n\ninterface JsonObserverDeps {\n changeSessionState: (sessionId: string, next: SessionState) => boolean;\n emitAgentStatus?: (sessionId: string, phase: AgentStatusPayload[\"phase\"]) => void;\n}\n\n// JSON 观察通道:把 worker 转发的 stream-json 事件翻译成 SessionState。\n// ERROR 态表达 \"worker 进程活着,但 proxy 观察/控制通道已坏\"(control_response 写入失败、\n// stream 连续 parse 失败、未来可能的 heartbeat 超时等);turn 内部的 is_error=true 不是观察失联,\n// 不触发 ERROR,仍按 onTurnResult 回 IDLE 处理。\nexport class JsonObserver {\n constructor(private deps: JsonObserverDeps) {}\n\n // 用户消息注入 worker(relay-router 收到 user_input)→ 进入 WORKING\n onTurnStart(sessionId: string): void {\n this.deps.changeSessionState(sessionId, SessionState.WORKING);\n this.deps.emitAgentStatus?.(sessionId, \"thinking\");\n }\n\n // stream-json result event 到达 → turn 结束回 IDLE\n onTurnResult(sessionId: string): void {\n this.deps.changeSessionState(sessionId, SessionState.IDLE);\n this.deps.emitAgentStatus?.(sessionId, \"idle\");\n }\n\n // control_request event 到达 → worker 阻塞等审批\n onApprovalRequested(sessionId: string): void {\n this.deps.changeSessionState(sessionId, SessionState.WAITING_APPROVAL);\n this.deps.emitAgentStatus?.(sessionId, \"waiting_permission\");\n }\n\n // 观察通道失联 → ERROR,待人工干预或后续 terminate\n onChannelBroken(sessionId: string): void {\n this.deps.changeSessionState(sessionId, SessionState.ERROR);\n }\n}\n","import { serviceLogger } from \"../common/logger.js\";\nimport type { HookProviderId } from \"./hook-registry.js\";\n\ninterface PermissionRequest {\n requestId: string;\n sessionId: string;\n provider: HookProviderId;\n toolName: string;\n input: Record<string, unknown>;\n}\n\nexport interface PermissionDecision {\n behavior: \"allow\" | \"deny\";\n message?: string;\n}\n\ninterface PendingPermission extends PermissionRequest {\n source: \"hook\" | \"worker\";\n resolve: (decision: PermissionDecision) => void;\n createdAt: number;\n deliveredAt?: number;\n}\n\ntype PendingPermissionView = Omit<PendingPermission, \"resolve\">;\n\nconst DUPLICATE_DECISION: PermissionDecision = {\n behavior: \"deny\",\n message: \"Duplicate permission request id.\",\n};\n\nfunction snapshot(pending: PendingPermission): PendingPermissionView {\n return {\n requestId: pending.requestId,\n sessionId: pending.sessionId,\n provider: pending.provider,\n source: pending.source,\n toolName: pending.toolName,\n input: pending.input,\n createdAt: pending.createdAt,\n ...(pending.deliveredAt !== undefined ? { deliveredAt: pending.deliveredAt } : {}),\n };\n}\n\nexport class PermissionBroker {\n private readonly pending = new Map<string, PendingPermission>();\n\n request(request: PermissionRequest): Promise<PermissionDecision> {\n if (this.pending.has(request.requestId)) {\n return Promise.resolve(DUPLICATE_DECISION);\n }\n return new Promise((resolve) => {\n this.pending.set(request.requestId, {\n ...request,\n source: \"hook\",\n resolve,\n createdAt: Date.now(),\n });\n });\n }\n\n registerWorkerRequest(\n request: PermissionRequest,\n onDecision: (decision: PermissionDecision) => void,\n ): boolean {\n if (this.pending.has(request.requestId)) {\n onDecision(DUPLICATE_DECISION);\n return false;\n }\n this.pending.set(request.requestId, {\n ...request,\n source: \"worker\",\n resolve: onDecision,\n createdAt: Date.now(),\n });\n return true;\n }\n\n resolve(requestId: string, decision: PermissionDecision): boolean {\n const pending = this.pending.get(requestId);\n if (!pending) return false;\n this.pending.delete(requestId);\n pending.resolve(decision);\n return true;\n }\n\n markDelivered(requestId: string): boolean {\n const pending = this.pending.get(requestId);\n if (!pending) return false;\n pending.deliveredAt = Date.now();\n return true;\n }\n\n get(requestId: string): PendingPermissionView | null {\n const pending = this.pending.get(requestId);\n return pending ? snapshot(pending) : null;\n }\n\n cleanupSession(sessionId: string, reason: string): void {\n for (const [requestId, pending] of this.pending) {\n if (pending.sessionId !== sessionId) continue;\n this.pending.delete(requestId);\n pending.resolve({ behavior: \"deny\", message: reason });\n serviceLogger.info({ sessionId, requestId, reason }, \"Pending hook permission dropped\");\n }\n }\n\n listSession(sessionId: string): PendingPermissionView[] {\n const out: PendingPermissionView[] = [];\n for (const pending of this.pending.values()) {\n if (pending.sessionId !== sessionId) continue;\n out.push(snapshot(pending));\n }\n return out;\n }\n}\n","import { providerValues, type ProviderId } from \"@dev-anywhere/shared\";\n\n// hook 入站 payload 解析的两个零依赖工具,hook-server 和 hook-event-router 共用。\n// 都按容错语义工作:解析失败返回中性默认值(null / 空对象 / \"unknown\")而非抛错,\n// 让上层 dispatch 层决定是否拒绝。\n\n// payload 任意 unknown 收窄到 Record;不是 object / 是数组时返回空对象。\nexport function asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n// provider 字符串收窄到 ProviderId;不在白名单返回 null。复用 shared 端的 providerValues 避免漂移。\nexport function asProvider(value: unknown): ProviderId | null {\n return (providerValues as readonly string[]).includes(value as string)\n ? (value as ProviderId)\n : null;\n}\n\n// 从 hook payload 提取 toolName,兼容 camelCase(toolName)和 snake_case(tool_name)。\nexport function toolNameFromPayload(payload: Record<string, unknown>): string {\n return typeof payload.toolName === \"string\"\n ? payload.toolName\n : typeof payload.tool_name === \"string\"\n ? payload.tool_name\n : \"unknown\";\n}\n\n// 从 hook payload 提取 input/tool_input 子结构,统一为 Record。\nexport function toolInputFromPayload(payload: Record<string, unknown>): Record<string, unknown> {\n return asRecord(payload.input ?? payload.tool_input);\n}\n","import {\n buildMessage,\n serializeControl,\n SessionState,\n type AgentStatusPayload,\n} from \"@dev-anywhere/shared\";\nimport { getSeqCounterFor } from \"../common/seq-counter.js\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { AuthenticatedHookEvent } from \"./hook-server.js\";\nimport type { HookProviderId } from \"./hook-registry.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\n\ninterface HookEventRouterDeps {\n relayConnection: RelayConnection;\n agentStatusRegistry: AgentStatusRegistry;\n changeSessionState: (sessionId: string, next: SessionState) => boolean;\n // session.mode 决定审批解除后的转换目标:JSON 模式不允许 WAITING_APPROVAL → WORKING\n // (观察通道粒度问题,见 session-manager.ts JSON_TRANSITIONS 注释),需让 onTurnResult 直接 → IDLE。\n getSessionMode?: (sessionId: string) => \"json\" | \"pty\" | undefined;\n nextSeq?: (sessionId: string) => number;\n}\n\nimport { toolInputFromPayload, toolNameFromPayload } from \"./hook-payload-helpers.js\";\n\nexport class HookEventRouter {\n constructor(private readonly deps: HookEventRouterDeps) {}\n\n handle(event: AuthenticatedHookEvent): void {\n switch (event.event) {\n case \"SessionStart\":\n this.deps.changeSessionState(event.sessionId, SessionState.IDLE);\n this.forwardAgentStatus(event, \"idle\");\n break;\n case \"UserPromptSubmit\":\n this.deps.changeSessionState(event.sessionId, SessionState.WORKING);\n this.forwardAgentStatus(event, \"thinking\");\n break;\n case \"PostToolUse\":\n case \"PostToolUseFailure\":\n this.deps.changeSessionState(event.sessionId, SessionState.WORKING);\n this.forwardAgentStatus(event, \"outputting\");\n break;\n case \"Stop\":\n this.deps.changeSessionState(event.sessionId, SessionState.IDLE);\n this.forwardAgentStatus(event, \"idle\");\n break;\n case \"PermissionRequest\":\n this.forwardPermissionRequest(event);\n break;\n case \"PreToolUse\":\n this.forwardToolUse(event);\n break;\n default:\n serviceLogger.debug(\n { sessionId: event.sessionId, provider: event.provider, event: event.event },\n \"Unknown provider hook event ignored\",\n );\n break;\n }\n }\n\n onPermissionResolved(\n sessionId: string,\n provider: HookProviderId,\n requestId: string,\n outcome: \"allow\" | \"deny\",\n context?: { toolName?: string; toolInput?: Record<string, unknown> },\n ): void {\n // 状态机走向按 outcome × mode 分四档(详见 session-manager.ts 的 JSON_TRANSITIONS / PTY_TRANSITIONS 边表):\n // - deny:双通道都直接回 IDLE,本轮终结\n // - allow + PTY:claude 继续输出,OSC 信号将驱动后续状态,先 → WORKING\n // - allow + JSON:观察粒度不到中间 WORKING,主动转换会被 FSM 拒绝;\n // 交给 onTurnResult 一次性 WAITING_APPROVAL → IDLE\n if (outcome === \"deny\") {\n this.deps.changeSessionState(sessionId, SessionState.IDLE);\n } else {\n const mode = this.deps.getSessionMode?.(sessionId);\n if (mode === \"pty\") {\n this.deps.changeSessionState(sessionId, SessionState.WORKING);\n }\n // mode === \"json\" 或 undefined:不主动转换状态,交给后续观察事件\n }\n this.forwardAgentStatus(\n {\n sessionId,\n provider,\n event: \"PermissionResolved\",\n requestId,\n payload: {},\n },\n outcome === \"allow\" ? \"tool_use\" : \"idle\",\n {\n toolName: context?.toolName,\n toolInput: context?.toolInput,\n permissionResolution: { requestId, outcome },\n },\n );\n serviceLogger.info({ sessionId, requestId, outcome }, \"Hook permission resolved\");\n }\n\n private forwardPermissionRequest(event: AuthenticatedHookEvent): void {\n const requestId = event.requestId ?? `${event.sessionId}:${Date.now()}`;\n const toolName = toolNameFromPayload(event.payload);\n const input = toolInputFromPayload(event.payload);\n\n this.deps.changeSessionState(event.sessionId, SessionState.WAITING_APPROVAL);\n this.forwardAgentStatus(event, \"waiting_permission\", {\n toolName,\n toolInput: input,\n permissionRequest: {\n requestId,\n toolName,\n input,\n },\n });\n\n const seq = this.deps.nextSeq?.(event.sessionId) ?? getSeqCounterFor(event.sessionId).next();\n const envelope = buildMessage(\n \"tool_use_request\",\n event.sessionId,\n seq,\n {\n toolName,\n toolId: requestId,\n parameters: input,\n },\n \"proxy\",\n );\n this.deps.relayConnection.sendEnvelope(envelope);\n }\n\n private forwardToolUse(event: AuthenticatedHookEvent): void {\n const toolName = toolNameFromPayload(event.payload);\n const input = toolInputFromPayload(event.payload);\n this.forwardAgentStatus(event, \"tool_use\", {\n toolName,\n toolInput: input,\n });\n }\n\n private forwardAgentStatus(\n event: AuthenticatedHookEvent,\n phase: AgentStatusPayload[\"phase\"],\n extra?: Partial<AgentStatusPayload>,\n ): void {\n const payload: AgentStatusPayload = {\n provider: event.provider,\n phase,\n seq: this.nextSeq(event.sessionId),\n updatedAt: Date.now(),\n ...extra,\n };\n this.deps.agentStatusRegistry.set(event.sessionId, payload);\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"agent_status\",\n sessionId: event.sessionId,\n payload,\n }),\n );\n }\n\n private nextSeq(sessionId: string): number {\n return this.deps.nextSeq?.(sessionId) ?? getSeqCounterFor(sessionId).next();\n }\n}\n","import type { AgentStatusPayload } from \"@dev-anywhere/shared\";\n\nexport class AgentStatusRegistry {\n private readonly statuses = new Map<string, AgentStatusPayload>();\n\n set(sessionId: string, status: AgentStatusPayload): void {\n const current = this.statuses.get(sessionId);\n if (current && current.seq > status.seq) return;\n this.statuses.set(sessionId, status);\n }\n\n get(sessionId: string): AgentStatusPayload | null {\n return this.statuses.get(sessionId) ?? null;\n }\n\n list(): Array<{ sessionId: string; status: AgentStatusPayload }> {\n return Array.from(this.statuses, ([sessionId, status]) => ({ sessionId, status }));\n }\n\n delete(sessionId: string): void {\n this.statuses.delete(sessionId);\n }\n}\n","import { SessionState } from \"@dev-anywhere/shared\";\nimport type { SessionInfo } from \"./session-manager.js\";\n\nexport function shouldPromotePtyActivityToWorking(\n session: SessionInfo | undefined,\n pendingApprovalCount: number,\n): boolean {\n if (!session || session.mode !== \"pty\") return false;\n if (pendingApprovalCount > 0) return false;\n return session.state === SessionState.IDLE || session.state === SessionState.WAITING_APPROVAL;\n}\n","import { SessionState, type PtySemanticState } from \"@dev-anywhere/shared\";\n\nexport function resolvePtySemanticSessionTransitions(\n currentState: SessionState | undefined,\n semanticState: PtySemanticState,\n): SessionState[] {\n if (semanticState !== \"turn_complete\") return [];\n\n if (currentState === SessionState.WAITING_APPROVAL) {\n return [SessionState.IDLE];\n }\n\n if (currentState === SessionState.WORKING) {\n return [SessionState.IDLE];\n }\n\n return [];\n}\n","import { SessionState, type PtySemanticState } from \"@dev-anywhere/shared\";\nimport { shouldPromotePtyActivityToWorking } from \"./pty-state-guard.js\";\nimport { resolvePtySemanticSessionTransitions } from \"./pty-semantic-lifecycle.js\";\nimport type { SessionInfo } from \"./session-manager.js\";\n\n// 把 PTY 语义状态投影到 Session FSM 转换 + 关联副作用。\n//\n// hosted-pty-registry(hosted PTY 模式,serve 进程内直接持有 PTY)和 terminal-ipc(local PTY\n// 模式,worker 进程经 IPC 通知 serve)以前各写一份 PTY → Session 翻译,行为不一致:hosted 缺\n// shouldPromotePtyActivityToWorking guard、turn_complete 直调 changeSessionState(IDLE) 而非\n// 走 resolvePtySemanticSessionTransitions helper。这里收一份,两侧共用。\n//\n// 与 decidePtySemanticTransition 的关系:那是 PTY 推断层(字节流→PTY semantic),输入是 OSC\n// 信号 + 局部状态,输出 PTY semantic transition;本函数是翻译层(PTY semantic→SessionState),\n// 输入是 PTY 决策结果,输出是 SessionState 转换 + 关联副作用(清理 interrupted approvals、推\n// agent status idle 等)。两层职责正交。\n\nexport interface PtySessionBridgeDeps {\n changeSessionState: (sessionId: string, next: SessionState) => boolean;\n getSession: (sessionId: string) => SessionInfo | undefined;\n getPendingApprovalCount: (sessionId: string) => number;\n resolveInterruptedApprovals: (sessionId: string) => void;\n emitAgentStatus: (sessionId: string, phase: \"idle\") => void;\n}\n\nexport function applyPtyStateToSession(\n deps: PtySessionBridgeDeps,\n sessionId: string,\n ptyState: PtySemanticState,\n): void {\n // 单点拒绝非法源 state: session 不在 map 里 (已 terminate 删除) 或 state===TERMINATED 时,\n // 跳过所有 bridge 副作用——changeSessionState 会被 FSM 各自拒绝, 但 resolveInterruptedApprovals /\n // emitAgentStatus 等下游回调没有自己的 guard, 否则会对 zombie session 触发空跑或冗余事件。\n // 这一层把 \"session 处于非法源状态\" 的判定收口到此, 不依赖每个回调自己重新检查。\n const session = deps.getSession(sessionId);\n if (!session || session.state === SessionState.TERMINATED) return;\n\n switch (ptyState) {\n case \"approval_wait\":\n deps.changeSessionState(sessionId, SessionState.WAITING_APPROVAL);\n break;\n case \"working\": {\n const pending = deps.getPendingApprovalCount(sessionId);\n if (shouldPromotePtyActivityToWorking(session, pending)) {\n deps.changeSessionState(sessionId, SessionState.WORKING);\n }\n break;\n }\n case \"turn_complete\": {\n deps.resolveInterruptedApprovals(sessionId);\n const transitions = resolvePtySemanticSessionTransitions(session.state, ptyState);\n for (const next of transitions) {\n deps.changeSessionState(sessionId, next);\n }\n deps.emitAgentStatus(sessionId, \"idle\");\n break;\n }\n }\n}\n","import { buildMessage, serializeControl, SessionState } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { SessionInfo, SessionManager } from \"./session-manager.js\";\n\nconst ACTIVITY_STATUS_PUSH_INTERVAL_MS = 15_000;\n\nfunction toSessionListPayload(s: SessionInfo) {\n return {\n sessionId: s.id,\n mode: s.mode,\n provider: s.provider,\n ...(s.ptyOwner !== undefined ? { ptyOwner: s.ptyOwner } : {}),\n state: s.state,\n lastActive: s.updatedAt,\n ...(s.name !== undefined ? { name: s.name } : {}),\n };\n}\n\nfunction pushSessionStatus(\n relay: RelayConnection,\n sessionManager: SessionManager,\n sessionId: string,\n): void {\n const session = sessionManager.getSession(sessionId);\n if (!session) return;\n try {\n const envelope = buildMessage(\n \"session_status\",\n session.id,\n Date.now(),\n { sessionId: session.id, state: session.state, lastActive: session.updatedAt },\n \"proxy\",\n );\n relay.sendEnvelope(envelope);\n } catch (err) {\n serviceLogger.debug({ sessionId, error: String(err) }, \"Failed to push session_status\");\n }\n}\n\nexport function broadcastSessionList(relay: RelayConnection, sessionManager: SessionManager): void {\n // session_list 是 envelope(payload 携带 sessions 数组),走 buildMessage 才能保证\n // version / timestamp / source 字段与其它 envelope 一致;旧代码手写 version: \"1\" 与\n // buildMessage 默认的 \"1.0\" 不符,会让任何对 envelope schema 严格校验的地方报错。\n const envelope = buildMessage(\n \"session_list\",\n null,\n 0,\n { sessions: sessionManager.listSessions().map(toSessionListPayload) },\n \"proxy\",\n );\n relay.sendEnvelope(envelope);\n}\n\nexport function broadcastSessionSync(relay: RelayConnection, session: SessionInfo): void {\n relay.sendRaw(\n serializeControl({\n type: \"session_sync\",\n sessions: [\n {\n id: session.id,\n mode: session.mode,\n provider: session.provider,\n ...(session.ptyOwner !== undefined ? { ptyOwner: session.ptyOwner } : {}),\n state: session.state,\n },\n ],\n }),\n );\n}\n\nexport function changeSessionState(\n sessionManager: SessionManager,\n relay: RelayConnection,\n sessionId: string,\n next: SessionState,\n): boolean {\n if (!sessionManager.getSession(sessionId)) return false;\n const changed = sessionManager.updateState(sessionId, next);\n if (changed) pushSessionStatus(relay, sessionManager, sessionId);\n return changed;\n}\n\nexport function touchSessionActivity(\n sessionManager: SessionManager,\n relay: RelayConnection,\n sessionId: string,\n now: number = Date.now(),\n): boolean {\n const touched = sessionManager.touchSession(sessionId, now, ACTIVITY_STATUS_PUSH_INTERVAL_MS);\n if (touched) pushSessionStatus(relay, sessionManager, sessionId);\n return touched;\n}\n","import { serializeControl, type AgentStatusPayload, type SessionState } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { disposeSeqCounter, getSeqCounterFor } from \"../common/seq-counter.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\nimport type { ControlMessageHandlers } from \"./handlers/control-messages.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport {\n broadcastSessionList,\n changeSessionState,\n touchSessionActivity,\n} from \"./session-broadcast.js\";\n\ninterface EventBridgeDeps {\n sessionManager: SessionManager;\n relayConnection: RelayConnection;\n agentStatusRegistry: AgentStatusRegistry;\n controlHandlers: ControlMessageHandlers;\n permissionBroker: { cleanupSession: (sessionId: string, reason: string) => void };\n}\n\ninterface EventBridge {\n // 推动 session FSM 转换并广播 session_status;observer 通道按 session.mode 分别走 PTY/JSON 转换表。\n changeSessionState: (sessionId: string, next: SessionState) => boolean;\n // 节流式 lastActive 更新;与 changeSessionState 共用底层 push 逻辑。\n touchSessionActivity: (sessionId: string) => boolean;\n // 把 agent_status 推到 relay 并写到 registry,用于 client 重连后查询。\n emitAgentStatus: (sessionId: string, phase: AgentStatusPayload[\"phase\"]) => void;\n // session 关闭时三件套清理:取消 control handlers 周期任务 / 删 agent_status / 广播会话列表。\n // session 本身的 manager.delete 由调用方负责(不同路径删的时机不同)。\n cleanupSessionResources: (sessionId: string) => void;\n}\n\nexport function createEventBridge(deps: EventBridgeDeps): EventBridge {\n const changeState = (sessionId: string, next: SessionState): boolean =>\n changeSessionState(deps.sessionManager, deps.relayConnection, sessionId, next);\n\n const touchActivity = (sessionId: string): boolean =>\n touchSessionActivity(deps.sessionManager, deps.relayConnection, sessionId);\n\n const emitAgentStatus = (sessionId: string, phase: AgentStatusPayload[\"phase\"]): void => {\n const session = deps.sessionManager.getSession(sessionId);\n if (!session) return;\n const payload: AgentStatusPayload = {\n provider: session.provider,\n phase,\n seq: getSeqCounterFor(sessionId).next(),\n updatedAt: Date.now(),\n };\n deps.agentStatusRegistry.set(sessionId, payload);\n deps.relayConnection.sendRaw(serializeControl({ type: \"agent_status\", sessionId, payload }));\n };\n\n const cleanupSessionResources = (sessionId: string): void => {\n // 每步独立 try/catch: 任意中间步骤抛异常都不能阻断最后的 broadcastSessionList。\n // 一旦广播丢失, web 不知道 session 已删, 列表残留 + 后续给该 session 的请求全\n // hang 到超时。\n const safe = (fn: () => void, step: string): void => {\n try {\n fn();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n serviceLogger.warn(\n {\n sessionId,\n step,\n err: { message: error.message, stack: error.stack, cause: error.cause },\n },\n \"Session cleanup step failed; continuing\",\n );\n }\n };\n safe(() => deps.controlHandlers.cleanup(sessionId), \"controlHandlers.cleanup\");\n safe(() => deps.agentStatusRegistry.delete(sessionId), \"agentStatusRegistry.delete\");\n safe(() => disposeSeqCounter(sessionId), \"disposeSeqCounter\");\n // hosted PTY 走的是 onSessionClosed = cleanupSessionResources, 不经过 worker socket\n // close → onDisconnect → permissionBroker.cleanupSession 那条链, 否则 hosted 模式下\n // 待审批工具会在 child 退出后留在 broker 永不释放, 客户端的 approval card 永远卡住。\n safe(\n () => deps.permissionBroker.cleanupSession(sessionId, \"Session closed\"),\n \"permissionBroker.cleanupSession\",\n );\n safe(\n () => broadcastSessionList(deps.relayConnection, deps.sessionManager),\n \"broadcastSessionList\",\n );\n };\n\n return {\n changeSessionState: changeState,\n touchSessionActivity: touchActivity,\n emitAgentStatus,\n cleanupSessionResources,\n };\n}\n","import { execSync } from \"node:child_process\";\nimport { existsSync, readFileSync, unlinkSync } from \"node:fs\";\nimport { hostname } from \"node:os\";\nimport { connect, type Socket } from \"node:net\";\nimport { flushLogger } from \"@dev-anywhere/shared/logger\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { DEFAULT_PROXY_PROFILE, PID_PATH, PROFILE_NAME, SOCK_PATH } from \"../common/paths.js\";\n\nfunction tryConnectSocket(sockPath: string): Promise<Socket | null> {\n return new Promise((resolve) => {\n const s = connect(sockPath);\n s.on(\"connect\", () => resolve(s));\n s.on(\"error\", () => resolve(null));\n });\n}\n\nexport function isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function cleanupStaleResources(): Promise<void> {\n if (existsSync(SOCK_PATH)) {\n const existing = await tryConnectSocket(SOCK_PATH);\n if (existing) {\n existing.destroy();\n const msg = `Another service is already running on ${SOCK_PATH}`;\n serviceLogger.error(msg);\n console.error(msg);\n await flushLogger(serviceLogger);\n process.exit(1);\n }\n unlinkSync(SOCK_PATH);\n serviceLogger.info(\"Removed stale socket file\");\n }\n\n if (existsSync(PID_PATH)) {\n const pidStr = readFileSync(PID_PATH, \"utf-8\").trim();\n const pid = parseInt(pidStr, 10);\n if (!isNaN(pid) && isProcessAlive(pid)) {\n const msg = `Another service is already running with PID ${pid}`;\n serviceLogger.error(msg);\n console.error(msg);\n await flushLogger(serviceLogger);\n process.exit(1);\n }\n unlinkSync(PID_PATH);\n serviceLogger.info(\"Removed stale PID file\");\n }\n}\n\nexport function formatProxyNameForProfile(baseName: string, profileName = PROFILE_NAME): string {\n return profileName === DEFAULT_PROXY_PROFILE ? baseName : `${baseName} (${profileName})`;\n}\n\nexport function getProxyName(): string {\n const explicitName = process.env.DEV_ANYWHERE_PROXY_NAME?.trim();\n if (explicitName) return explicitName;\n\n return formatProxyNameForProfile(getComputerName() || hostname());\n}\n\nfunction getComputerName(): string | null {\n try {\n return (\n execSync(\"scutil --get ComputerName\", { stdio: [\"pipe\", \"pipe\", \"ignore\"] })\n .toString()\n .trim() || null\n );\n } catch {\n return null;\n }\n}\n","import type { Socket } from \"node:net\";\nimport { encodeBinaryFrame, serializeControl, type AgentStatusPayload } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { createIpcReader, serializeIpc, type IpcMessage } from \"../ipc/ipc-protocol.js\";\nimport type { ProviderHookContext } from \"../providers/index.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\nimport type { ControlMessageHandlers } from \"./handlers/control-messages.js\";\nimport type { HookEventRouter } from \"./hook-event-router.js\";\nimport type { HostedPtyRegistry } from \"./hosted-pty-registry.js\";\nimport type { PermissionBroker } from \"./permission-broker.js\";\nimport { applyPtyStateToSession } from \"./pty-session-bridge.js\";\nimport type { PtySessionBridgeDeps } from \"./pty-session-bridge.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport {\n broadcastSessionList,\n broadcastSessionSync,\n changeSessionState,\n touchSessionActivity,\n} from \"./session-broadcast.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport { terminateSessionByOwnership } from \"./session-termination.js\";\nimport type { WorkerRegistry } from \"./worker-registry.js\";\nimport { isProcessAlive } from \"./service-files.js\";\n\ninterface TerminalConnectionDeps {\n sessionManager: SessionManager;\n workerRegistry: WorkerRegistry;\n terminalSockets: Map<string, Socket>;\n hostedPtyRegistry: HostedPtyRegistry;\n relayConnection: RelayConnection;\n controlHandlers: ControlMessageHandlers;\n agentStatusRegistry: AgentStatusRegistry;\n permissionBroker: PermissionBroker;\n hookEventRouter: HookEventRouter;\n createHookContext: (\n sessionId: string,\n provider: ProviderHookContext[\"provider\"],\n ) => ProviderHookContext;\n emitAgentStatus: (sessionId: string, phase: AgentStatusPayload[\"phase\"]) => void;\n resolveInterruptedApprovals: (sessionId: string) => void;\n cleanupSessionResources: (sessionId: string) => void;\n config: Extract<IpcMessage, { type: \"service_status_response\" }>[\"config\"];\n}\n\nexport function handleTerminalConnection(socket: Socket, deps: TerminalConnectionDeps): void {\n const {\n sessionManager,\n workerRegistry,\n terminalSockets,\n hostedPtyRegistry,\n relayConnection,\n controlHandlers,\n agentStatusRegistry,\n permissionBroker,\n createHookContext,\n emitAgentStatus,\n resolveInterruptedApprovals,\n cleanupSessionResources,\n config,\n } = deps;\n\n const bridgeDeps: PtySessionBridgeDeps = {\n changeSessionState: (sessionId, next) =>\n changeSessionState(sessionManager, relayConnection, sessionId, next),\n getSession: (sessionId) => sessionManager.getSession(sessionId),\n getPendingApprovalCount: (sessionId) => permissionBroker.listSession(sessionId).length,\n resolveInterruptedApprovals,\n emitAgentStatus,\n };\n\n createIpcReader(\n socket,\n (msg: IpcMessage) => {\n switch (msg.type) {\n case \"session_create_request\": {\n if (msg.mode !== \"pty\") {\n socket.write(\n serializeIpc({\n type: \"session_create_response\",\n sessionId: \"\",\n error: `Unsupported mode via IPC: ${msg.mode}`,\n }),\n );\n break;\n }\n const provider = msg.provider;\n const existing = msg.sessionId ? sessionManager.getSession(msg.sessionId) : undefined;\n const session =\n existing ??\n sessionManager.createSession(\n \"pty\",\n msg.cwd,\n msg.pid,\n msg.name,\n msg.sessionId,\n provider,\n \"local-terminal\",\n );\n if (existing) {\n sessionManager.setPid(session.id, msg.pid);\n }\n socket.write(\n serializeIpc({\n type: \"session_create_response\",\n sessionId: session.id,\n hook: createHookContext(session.id, provider),\n }),\n );\n serviceLogger.info(\n { sessionId: session.id, mode: \"pty\", provider },\n \"PTY session created\",\n );\n break;\n }\n\n case \"service_status_request\": {\n const relayStatus = relayConnection.getStatus();\n const sessions = sessionManager.listSessions();\n socket.write(\n serializeIpc({\n type: \"service_status_response\",\n config,\n relay: relayStatus,\n sessions: sessions.map((s) => ({\n id: s.id,\n mode: s.mode,\n provider: s.provider,\n state: s.state,\n createdAt: new Date(s.createdAt).toISOString(),\n ...(s.name !== undefined ? { name: s.name } : {}),\n hasWorker: workerRegistry.has(s.id),\n })),\n }),\n );\n break;\n }\n\n case \"pty_title_change\": {\n if (!sessionManager.getSession(msg.sessionId)) break;\n relayConnection.sendRaw(\n serializeControl({\n type: \"terminal_title\",\n sessionId: msg.sessionId,\n title: msg.title,\n }),\n );\n break;\n }\n\n case \"pty_semantic_event\": {\n if (!sessionManager.getSession(msg.sessionId)) break;\n const logPayload = {\n sessionId: msg.sessionId,\n state: msg.state,\n ...(msg.title !== undefined ? { title: msg.title } : {}),\n ...(msg.tool !== undefined ? { tool: msg.tool } : {}),\n };\n if (msg.state === \"approval_wait\" || msg.state === \"turn_complete\") {\n serviceLogger.info(logPayload, \"PTY semantic event received\");\n } else {\n serviceLogger.debug(logPayload, \"PTY semantic event received\");\n }\n applyPtyStateToSession(bridgeDeps, msg.sessionId, msg.state);\n relayConnection.sendRaw(\n serializeControl({\n type: \"pty_state\",\n sessionId: msg.sessionId,\n payload: {\n state: msg.state,\n ...(msg.title !== undefined ? { title: msg.title } : {}),\n ...(msg.tool !== undefined ? { tool: msg.tool } : {}),\n },\n }),\n );\n break;\n }\n\n case \"pty_resize\": {\n if (!sessionManager.getSession(msg.sessionId)) break;\n relayConnection.sendRaw(\n serializeControl({\n type: \"terminal_resize\",\n sessionId: msg.sessionId,\n cols: msg.cols,\n rows: msg.rows,\n }),\n );\n break;\n }\n\n case \"session_terminate_request\": {\n const result = terminateSessionByOwnership(\n {\n sessionManager,\n workerRegistry,\n controlHandlers,\n terminalSockets,\n hostedPtyRegistry,\n agentStatusRegistry,\n broadcastSessionList: () => broadcastSessionList(relayConnection, sessionManager),\n },\n msg.sessionId,\n );\n socket.write(\n serializeIpc({\n type: \"session_terminate_response\",\n sessionId: msg.sessionId,\n success: result.success,\n }),\n );\n serviceLogger.info(\n { sessionId: msg.sessionId, success: result.success, action: result.action },\n \"Session termination request handled\",\n );\n break;\n }\n\n case \"pty_register\": {\n if (!sessionManager.getSession(msg.sessionId)) {\n serviceLogger.warn(\n { sessionId: msg.sessionId },\n \"PTY register ignored: session missing\",\n );\n break;\n }\n sessionManager.setPid(msg.sessionId, msg.pid);\n terminalSockets.set(msg.sessionId, socket);\n socket.write(\n serializeIpc({\n type: \"bridge_status\",\n connected: relayConnection.getStatus().connected,\n }),\n );\n const session = sessionManager.getSession(msg.sessionId);\n if (session) {\n broadcastSessionSync(relayConnection, session);\n }\n broadcastSessionList(relayConnection, sessionManager);\n serviceLogger.info({ sessionId: msg.sessionId }, \"PTY session registered\");\n break;\n }\n\n case \"pty_deregister\": {\n relayConnection.sendRaw(\n serializeControl({\n type: \"pty_state\",\n sessionId: msg.sessionId,\n payload: { state: \"turn_complete\" },\n }),\n );\n sessionManager.terminateSession(msg.sessionId);\n terminalSockets.delete(msg.sessionId);\n cleanupSessionResources(msg.sessionId);\n serviceLogger.info({ sessionId: msg.sessionId }, \"PTY session deregistered\");\n break;\n }\n\n case \"pty_input\": {\n if (!sessionManager.getSession(msg.sessionId)) break;\n const targetSocket = terminalSockets.get(msg.sessionId);\n if (hostedPtyRegistry.write(msg.sessionId, msg.data)) {\n break;\n }\n if (targetSocket?.writable) {\n targetSocket.write(\n serializeIpc({\n type: \"pty_input\",\n sessionId: msg.sessionId,\n data: msg.data,\n }),\n );\n }\n break;\n }\n\n case \"session_status_update\": {\n changeSessionState(sessionManager, relayConnection, msg.sessionId, msg.state);\n break;\n }\n\n case \"pty_snapshot\": {\n if (!sessionManager.getSession(msg.sessionId)) break;\n relayConnection.sendRaw(\n serializeControl({\n type: \"session_snapshot\",\n sessionId: msg.sessionId,\n cols: msg.cols,\n rows: msg.rows,\n data: msg.data,\n outputSeq: msg.outputSeq,\n ...(msg.requestId !== undefined ? { requestId: msg.requestId } : {}),\n }),\n );\n serviceLogger.info(\n { sessionId: msg.sessionId, cols: msg.cols, rows: msg.rows },\n \"Session snapshot forwarded to relay\",\n );\n break;\n }\n\n default: {\n serviceLogger.warn({ type: (msg as IpcMessage).type }, \"Unhandled IPC message type\");\n }\n }\n },\n (sessionId, data, outputSeq) => {\n if (!sessionManager.getSession(sessionId)) return;\n touchSessionActivity(sessionManager, relayConnection, sessionId);\n relayConnection.sendBinary(encodeBinaryFrame(sessionId, outputSeq, data));\n },\n (err, line) => {\n // 单条 IPC 行 schema 失败时 warn-skip,不让它升级为 socket error 触发整个 terminal 断开。\n // err.cause 暴露 JSON.parse / zod 的原始报错; line 截断 200 字符避免日志爆掉但留够字段\n // 取证。\n const cause = err instanceof Error ? err.cause : undefined;\n serviceLogger.warn(\n {\n err: err.message,\n cause: cause instanceof Error ? cause.message : cause,\n lineLen: line.length,\n linePreview: line.slice(0, 200),\n },\n \"Terminal IPC message dropped (parse/schema error)\",\n );\n },\n );\n\n socket.on(\"close\", () => {\n for (const [sessionId, terminalSocket] of terminalSockets) {\n if (terminalSocket === socket) {\n terminalSockets.delete(sessionId);\n const session = sessionManager.getSession(sessionId);\n if (!session) {\n serviceLogger.info({ sessionId }, \"Terminal socket closed, session already cleaned\");\n continue;\n }\n if (session.mode === \"pty\" && session.pid && isProcessAlive(session.pid)) {\n serviceLogger.info(\n { sessionId, pid: session.pid },\n \"Terminal socket closed but process alive, skipping cleanup\",\n );\n continue;\n }\n relayConnection.sendRaw(\n serializeControl({\n type: \"pty_state\",\n sessionId,\n payload: { state: \"turn_complete\" },\n }),\n );\n sessionManager.terminateSession(sessionId);\n cleanupSessionResources(sessionId);\n serviceLogger.info(\n { sessionId },\n \"PTY session cleaned up on socket close (crash fallback)\",\n );\n }\n }\n });\n\n socket.on(\"error\", (err) => {\n serviceLogger.warn({ error: String(err) }, \"Client socket error\");\n });\n}\n","import { createHash, randomBytes } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { z } from \"zod\";\nimport { serviceLogger } from \"../common/logger.js\";\n\nexport type HookProviderId = \"claude\" | \"codex\";\n\ninterface HookSessionBinding {\n sessionId: string;\n provider: HookProviderId;\n marker: string;\n tokenHash: string;\n createdAt: number;\n expiresAt?: number;\n}\n\ninterface HookSessionCredentials {\n sessionId: string;\n provider: HookProviderId;\n marker: string;\n token: string;\n}\n\ninterface VerifyOptions {\n sessionId: string;\n marker: string;\n token: string;\n provider?: HookProviderId;\n now?: number;\n}\n\ninterface HookRegistryOptions {\n persistPath?: string;\n}\n\nconst PersistedHookSessionBindingSchema = z.object({\n sessionId: z.string(),\n provider: z.enum([\"claude\", \"codex\"]),\n marker: z.string(),\n tokenHash: z.string(),\n createdAt: z.number(),\n expiresAt: z.number().optional(),\n});\n\nconst PersistedHookRegistrySchema = z.object({\n version: z.literal(1),\n bindings: z.array(PersistedHookSessionBindingSchema),\n});\n\nfunction hashToken(token: string): string {\n return createHash(\"sha256\").update(token).digest(\"hex\");\n}\n\nfunction randomSecret(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\nexport class HookRegistry {\n private readonly bindingsBySession = new Map<string, HookSessionBinding>();\n private readonly persistPath?: string;\n\n constructor(options: HookRegistryOptions = {}) {\n this.persistPath = options.persistPath;\n this.load();\n }\n\n registerSession(\n sessionId: string,\n provider: HookProviderId,\n options: { ttlMs?: number; now?: number } = {},\n ): HookSessionCredentials {\n const now = options.now ?? Date.now();\n const token = randomSecret();\n const marker = randomSecret();\n this.bindingsBySession.set(sessionId, {\n sessionId,\n provider,\n marker,\n tokenHash: hashToken(token),\n createdAt: now,\n ...(options.ttlMs ? { expiresAt: now + options.ttlMs } : {}),\n });\n this.save();\n return { sessionId, provider, marker, token };\n }\n\n verify(options: VerifyOptions): HookSessionBinding | null {\n const binding = this.bindingsBySession.get(options.sessionId);\n if (!binding) return null;\n if (options.provider && binding.provider !== options.provider) return null;\n if (binding.marker !== options.marker) return null;\n if (binding.tokenHash !== hashToken(options.token)) return null;\n if (binding.expiresAt && (options.now ?? Date.now()) > binding.expiresAt) return null;\n return binding;\n }\n\n getSession(sessionId: string): HookSessionBinding | null {\n return this.bindingsBySession.get(sessionId) ?? null;\n }\n\n unregisterSession(sessionId: string): void {\n if (this.bindingsBySession.delete(sessionId)) {\n this.save();\n }\n }\n\n private load(): void {\n if (!this.persistPath || !existsSync(this.persistPath)) return;\n try {\n const parsed = PersistedHookRegistrySchema.parse(\n JSON.parse(readFileSync(this.persistPath, \"utf8\")),\n );\n this.bindingsBySession.clear();\n for (const binding of parsed.bindings) {\n this.bindingsBySession.set(binding.sessionId, binding);\n }\n } catch (err) {\n serviceLogger.warn(\n { path: this.persistPath, error: String(err) },\n \"Failed to load hook registry state\",\n );\n }\n }\n\n private save(): void {\n if (!this.persistPath) return;\n try {\n mkdirSync(dirname(this.persistPath), { recursive: true });\n const tmpPath = `${this.persistPath}.${process.pid}.${Date.now()}.tmp`;\n writeFileSync(\n tmpPath,\n JSON.stringify(\n {\n version: 1,\n bindings: Array.from(this.bindingsBySession.values()),\n },\n null,\n 2,\n ),\n );\n renameSync(tmpPath, this.persistPath);\n } catch (err) {\n serviceLogger.warn(\n { path: this.persistPath, error: String(err) },\n \"Failed to persist hook registry state\",\n );\n }\n }\n}\n","import { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { HookRegistry, type HookProviderId } from \"./hook-registry.js\";\nimport { PermissionBroker } from \"./permission-broker.js\";\nimport { asProvider, asRecord, toolNameFromPayload } from \"./hook-payload-helpers.js\";\n\ninterface HookServerOptions {\n port: number;\n registry: HookRegistry;\n permissionBroker: PermissionBroker;\n host?: string;\n maxBodyBytes?: number;\n isSessionActive?: (sessionId: string) => boolean;\n onEvent?: (event: AuthenticatedHookEvent) => void;\n}\n\nexport interface AuthenticatedHookEvent {\n sessionId: string;\n provider: HookProviderId;\n event: string;\n requestId?: string;\n payload: Record<string, unknown>;\n}\n\ninterface HookRequestBody {\n sessionId?: unknown;\n provider?: unknown;\n marker?: unknown;\n event?: unknown;\n requestId?: unknown;\n payload?: unknown;\n}\n\nfunction getBearerToken(req: IncomingMessage): string | null {\n const header = req.headers.authorization;\n if (!header?.startsWith(\"Bearer \")) return null;\n return header.slice(\"Bearer \".length).trim() || null;\n}\n\nexport class HookServer {\n private server: Server | null = null;\n private readonly host: string;\n private readonly maxBodyBytes: number;\n\n constructor(private readonly options: HookServerOptions) {\n this.host = options.host ?? \"127.0.0.1\";\n this.maxBodyBytes = options.maxBodyBytes ?? 1024 * 1024;\n }\n\n start(): Promise<void> {\n if (this.server) return Promise.resolve();\n this.server = createServer((req, res) => {\n this.handle(req, res).catch((err) => {\n serviceLogger.error({ err: String(err) }, \"Hook request failed\");\n this.writeJson(res, 500, { error: \"internal_error\" });\n });\n });\n\n return new Promise((resolve, reject) => {\n const onError = (err: Error) => {\n this.server?.off(\"listening\", onListening);\n reject(err);\n };\n const onListening = () => {\n this.server?.off(\"error\", onError);\n serviceLogger.info({ host: this.host, port: this.options.port }, \"Hook server listening\");\n resolve();\n };\n this.server!.once(\"error\", onError);\n this.server!.once(\"listening\", onListening);\n this.server!.listen(this.options.port, this.host);\n });\n }\n\n close(): Promise<void> {\n if (!this.server) return Promise.resolve();\n const server = this.server;\n this.server = null;\n return new Promise((resolve, reject) => {\n server.close((err) => (err ? reject(err) : resolve()));\n });\n }\n\n getListeningPort(): number | null {\n const address = this.server?.address();\n if (!address || typeof address === \"string\") return null;\n return (address as AddressInfo).port;\n }\n\n private async handle(req: IncomingMessage, res: ServerResponse): Promise<void> {\n if (req.method !== \"POST\" || req.url !== \"/hook\") {\n this.writeJson(res, 404, { error: \"not_found\" });\n return;\n }\n\n const token = getBearerToken(req);\n if (!token) {\n this.writeJson(res, 401, { error: \"missing_token\" });\n return;\n }\n\n const body = await this.readBody(req);\n const parsed = JSON.parse(body) as HookRequestBody;\n const provider = asProvider(parsed.provider);\n if (\n typeof parsed.sessionId !== \"string\" ||\n typeof parsed.marker !== \"string\" ||\n typeof parsed.event !== \"string\" ||\n !provider\n ) {\n this.writeJson(res, 400, { error: \"invalid_hook_payload\" });\n return;\n }\n\n const binding = this.options.registry.verify({\n sessionId: parsed.sessionId,\n marker: parsed.marker,\n token,\n provider,\n });\n if (!binding) {\n this.writeJson(res, 403, { error: \"invalid_hook_credentials\" });\n return;\n }\n if (this.options.isSessionActive && !this.options.isSessionActive(binding.sessionId)) {\n serviceLogger.info(\n { sessionId: binding.sessionId, provider: binding.provider, event: parsed.event },\n \"Provider hook ignored for inactive session\",\n );\n this.writeProviderResponse(res, this.toNeutralProviderResponse(provider, parsed.event));\n return;\n }\n\n const payload = asRecord(parsed.payload);\n const requestId =\n typeof parsed.requestId === \"string\"\n ? parsed.requestId\n : typeof payload.tool_use_id === \"string\"\n ? payload.tool_use_id\n : undefined;\n const event: AuthenticatedHookEvent = {\n sessionId: binding.sessionId,\n provider: binding.provider,\n event: parsed.event,\n ...(requestId !== undefined ? { requestId } : {}),\n payload,\n };\n\n if (event.event === \"PermissionRequest\") {\n await this.handlePermissionRequest(event, res);\n return;\n }\n\n this.options.onEvent?.(event);\n this.writeProviderResponse(res, this.toNeutralProviderResponse(event.provider, event.event));\n }\n\n private async handlePermissionRequest(\n event: AuthenticatedHookEvent,\n res: ServerResponse,\n ): Promise<void> {\n const requestId =\n event.requestId ??\n (typeof event.payload.tool_use_id === \"string\" ? event.payload.tool_use_id : undefined) ??\n `${event.sessionId}:${Date.now()}`;\n const toolName = toolNameFromPayload(event.payload);\n const input = asRecord(event.payload.input ?? event.payload.tool_input);\n\n this.options.onEvent?.({ ...event, requestId });\n const decision = await this.options.permissionBroker.request({\n requestId,\n sessionId: event.sessionId,\n provider: event.provider,\n toolName,\n input,\n });\n this.writeJson(res, 200, this.toProviderDecision(event.event, decision));\n }\n\n private toProviderDecision(\n eventName: string,\n decision: { behavior: \"allow\" | \"deny\"; message?: string },\n ): object {\n if (eventName === \"PreToolUse\") {\n return {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: decision.behavior,\n ...(decision.message ? { permissionDecisionReason: decision.message } : {}),\n },\n };\n }\n\n return {\n hookSpecificOutput: {\n hookEventName: \"PermissionRequest\",\n decision,\n },\n };\n }\n\n private toNeutralProviderResponse(provider: HookProviderId, eventName: string): object | null {\n if (eventName === \"PreToolUse\") {\n if (provider === \"codex\") {\n return null;\n }\n return {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"defer\",\n },\n };\n }\n\n return null;\n }\n\n private writeProviderResponse(res: ServerResponse, payload: object | null): void {\n if (res.headersSent) return;\n if (payload === null) {\n res.writeHead(200);\n res.end();\n return;\n }\n this.writeJson(res, 200, payload);\n }\n\n private readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n let body = \"\";\n let size = 0;\n req.setEncoding(\"utf8\");\n req.on(\"data\", (chunk: string) => {\n size += Buffer.byteLength(chunk);\n if (size > this.maxBodyBytes) {\n reject(new Error(\"hook body too large\"));\n req.destroy();\n return;\n }\n body += chunk;\n });\n req.on(\"end\", () => resolve(body));\n req.on(\"error\", reject);\n });\n }\n\n private writeJson(res: ServerResponse, statusCode: number, payload: object): void {\n if (res.headersSent) return;\n res.writeHead(statusCode, { \"content-type\": \"application/json; charset=utf-8\" });\n res.end(JSON.stringify(payload));\n }\n}\n","import { flushLogger } from \"@dev-anywhere/shared/logger\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { HOOK_REGISTRY_PATH } from \"../common/paths.js\";\nimport type { ProviderHookContext } from \"../providers/index.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\nimport { HookEventRouter } from \"./hook-event-router.js\";\nimport { HookRegistry } from \"./hook-registry.js\";\nimport { HookServer } from \"./hook-server.js\";\nimport type { PermissionBroker } from \"./permission-broker.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport type { SessionState } from \"@dev-anywhere/shared\";\n\ninterface ProviderHookRuntimeOptions {\n hookPort?: number;\n permissionBroker: PermissionBroker;\n sessionManager: SessionManager;\n relayConnection: RelayConnection;\n agentStatusRegistry: AgentStatusRegistry;\n changeSessionState: (sessionId: string, next: SessionState) => boolean;\n}\n\ninterface ProviderHookRuntime {\n hookRegistry: HookRegistry;\n hookEventRouter: HookEventRouter;\n hookServer: HookServer;\n createHookContext: (\n sessionId: string,\n provider: ProviderHookContext[\"provider\"],\n ) => ProviderHookContext;\n}\n\nexport async function createProviderHookRuntime(\n options: ProviderHookRuntimeOptions,\n): Promise<ProviderHookRuntime> {\n const hookRegistry = new HookRegistry({ persistPath: HOOK_REGISTRY_PATH });\n const hookEventRouter = new HookEventRouter({\n relayConnection: options.relayConnection,\n agentStatusRegistry: options.agentStatusRegistry,\n changeSessionState: options.changeSessionState,\n getSessionMode: (sessionId) => options.sessionManager.getSession(sessionId)?.mode,\n });\n const port = options.hookPort ?? 17654;\n const hookServer = new HookServer({\n port,\n registry: hookRegistry,\n permissionBroker: options.permissionBroker,\n isSessionActive: (sessionId) => !!options.sessionManager.getSession(sessionId),\n onEvent: (event) => {\n serviceLogger.info(\n {\n sessionId: event.sessionId,\n provider: event.provider,\n event: event.event,\n requestId: event.requestId,\n },\n \"Provider hook event received\",\n );\n hookEventRouter.handle(event);\n },\n });\n\n try {\n await hookServer.start();\n } catch (err) {\n const msg = `Failed to start hook server on 127.0.0.1:${port}: ${String(err)}`;\n serviceLogger.error(msg);\n console.error(msg);\n await flushLogger(serviceLogger);\n process.exit(1);\n }\n\n const hookUrl = `http://127.0.0.1:${hookServer.getListeningPort() ?? port}/hook`;\n const createHookContext: ProviderHookRuntime[\"createHookContext\"] = (sessionId, provider) => {\n const credentials = hookRegistry.registerSession(sessionId, provider);\n return {\n provider,\n sessionId,\n hookUrl,\n marker: credentials.marker,\n token: credentials.token,\n };\n };\n\n return {\n hookRegistry,\n hookEventRouter,\n hookServer,\n createHookContext,\n };\n}\n","import { unlinkSync } from \"node:fs\";\nimport { flushLogger, type Logger } from \"@dev-anywhere/shared/logger\";\n\n// 收尾步骤里依赖的所有外部资源都通过 deps 注入;shutdown 函数本身只负责正确顺序与\n// 单次执行守卫,便于以纯单元测试的方式覆盖双信号场景而不必拉起整个 service。\nexport interface ServeShutdownDeps {\n logger: Logger;\n sessionManagerStopReaper: () => void;\n relayRouterDestroy: () => void;\n hookServerClose: () => Promise<void>;\n relayConnectionClose: () => void;\n workerRegistryDestroyAll: () => void;\n hostedPtyRegistryDestroyAll: () => void;\n ipcServerClose: () => void;\n sockPath: string;\n pidPath: string;\n exit?: (code: number) => void;\n}\n\n// 双重 SIGTERM / SIGTERM+SIGINT 并发触发时,第二次直接返回。\n// 真实场景:systemd TimeoutStopSec 到期再发一次 SIGTERM;用户连按两次 Ctrl+C 同样会双触发。\n// 没有守卫时,第二路 process.exit(0) 可能在第一路 await flushLogger 完成前先返回,截断日志。\nexport function createServeShutdown(deps: ServeShutdownDeps): () => Promise<void> {\n const exit = deps.exit ?? ((code: number) => process.exit(code));\n let shuttingDown = false;\n return async () => {\n if (shuttingDown) return;\n shuttingDown = true;\n deps.logger.info(\"Shutting down service\");\n deps.sessionManagerStopReaper();\n // 先 destroy router:清掉 pending session-create retry timer,并 cleanupPendingJsonSession\n // 把已 spawn 但还未 connect 的 worker 子进程收掉,否则进入 destroyAll 时这批子进程\n // 会在 timer 命中后失去 parent 引用变孤儿(只有 sock destroy,没人发 SIGTERM 给 worker)。\n deps.relayRouterDestroy();\n await deps.hookServerClose();\n deps.relayConnectionClose();\n deps.workerRegistryDestroyAll();\n deps.hostedPtyRegistryDestroyAll();\n deps.ipcServerClose();\n try {\n unlinkSync(deps.sockPath);\n } catch {\n // 关闭时 socket 文件可能已被删除\n }\n try {\n unlinkSync(deps.pidPath);\n } catch {\n // 关闭时 PID 文件可能已被删除\n }\n await flushLogger(deps.logger);\n exit(0);\n };\n}\n"],"mappings":"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","names":["createServer","unlinkSync","writeFileSync","rmSync","readFileSync","existsSync","nanoid","existsSync","readFileSync","nanoid","readdir","join","join","homedir","resolve","join","readFileSync","homedir","join","readdir","join","existsSync","readdirSync","resolve","existsSync","readdirSync","existsSync","readFileSync","isAbsolute","join","nanoid","isAbsolute","join","existsSync","readFileSync","nanoid","readFileSync","statSync","isAbsolute","resolve","isAbsolute","resolve","stat","statSync","readFileSync","existsSync","mkdirSync","readFileSync","statSync","writeFileSync","isAbsolute","join","relative","resolve","nanoid","normalizeBase64","resolveChildDir","resolve","relative","isAbsolute","normalizeGitignoreLine","join","existsSync","readFileSync","writeFileSync","statSync","mkdirSync","nanoid","readFileSync","realpathSync","statSync","isAbsolute","relative","resolve","relative","isAbsolute","realpathSync","resolve","errorCode","stat","statSync","readFileSync","homedir","statSync","stat","statSync","homedir","statSync","isAbsolute","nanoid","isAbsolute","stat","statSync","nanoid","statuses","resolve","existsSync","readFileSync","unlinkSync","connect","resolve","connect","existsSync","unlinkSync","readFileSync","resolveInterruptedApprovals","existsSync","mkdirSync","readFileSync","writeFileSync","existsSync","readFileSync","mkdirSync","writeFileSync","resolve","unlinkSync","unlinkSync","unlinkSync","rmSync","createServer","writeFileSync"]}
1
+ {"version":3,"sources":["../src/serve.ts","../src/serve/session-manager.ts","../src/serve/relay-connection.ts","../src/serve/message-queue.ts","../src/serve/handlers/control-messages.ts","../src/serve/session-history.ts","../src/serve/command-discovery.ts","../src/serve/path-errors.ts","../src/serve/worker-registry.ts","../src/serve/session-termination.ts","../src/serve/clipboard-image-upload.ts","../src/serve/file-download.ts","../src/serve/file-upload.ts","../src/serve/image-preview.ts","../src/serve/pty-input.ts","../src/serve/relay-input-handlers.ts","../src/serve/relay-history-handlers.ts","../src/serve/relay-permission-handlers.ts","../src/serve/relay-resource-handlers.ts","../src/serve/relay-session-create-handler.ts","../src/serve/hosted-pty-registry.ts","../src/serve/relay-router.ts","../src/serve/json-observer.ts","../src/serve/permission-broker.ts","../src/serve/hook-payload-helpers.ts","../src/serve/hook-event-router.ts","../src/serve/agent-status-registry.ts","../src/serve/pty-state-guard.ts","../src/serve/pty-semantic-lifecycle.ts","../src/serve/pty-session-bridge.ts","../src/serve/session-broadcast.ts","../src/serve/event-bridge.ts","../src/serve/service-files.ts","../src/serve/terminal-ipc.ts","../src/serve/hook-registry.ts","../src/serve/hook-server.ts","../src/serve/provider-hook-runtime.ts","../src/serve/shutdown.ts"],"sourcesContent":["import { createServer, type Socket } from \"node:net\";\nimport { unlinkSync, writeFileSync, chmodSync, rmSync } from \"node:fs\";\nimport { serializeControl } from \"@dev-anywhere/shared\";\nimport { flushLogger } from \"@dev-anywhere/shared/logger\";\nimport { serviceLogger } from \"./common/logger.js\";\nimport { SessionManager } from \"./serve/session-manager.js\";\nimport { RelayConnection } from \"./serve/relay-connection.js\";\nimport {\n SOCK_PATH,\n PID_PATH,\n STOPPED_PATH,\n SESSIONS_PATH,\n PROXY_ID_PATH,\n PROFILE_NAME,\n ensureProfileWorkspace,\n sessionPaths,\n} from \"./common/paths.js\";\nimport { buildProviderEnv, loadConfig } from \"./common/config.js\";\nimport { serializeIpc } from \"./ipc/ipc-protocol.js\";\nimport { createControlMessageHandlers } from \"./serve/handlers/control-messages.js\";\nimport { WorkerRegistry } from \"./serve/worker-registry.js\";\nimport { RelayRouter } from \"./serve/relay-router.js\";\nimport { JsonObserver } from \"./serve/json-observer.js\";\nimport { PermissionBroker } from \"./serve/permission-broker.js\";\nimport { HookEventRouter } from \"./serve/hook-event-router.js\";\nimport { AgentStatusRegistry } from \"./serve/agent-status-registry.js\";\nimport { HostedPtyRegistry } from \"./serve/hosted-pty-registry.js\";\nimport { applyPtyStateToSession, type PtySessionBridgeDeps } from \"./serve/pty-session-bridge.js\";\nimport { broadcastSessionList, broadcastSessionSync } from \"./serve/session-broadcast.js\";\nimport { createEventBridge } from \"./serve/event-bridge.js\";\nimport { cleanupStaleResources, getProxyName } from \"./serve/service-files.js\";\nimport { handleTerminalConnection } from \"./serve/terminal-ipc.js\";\nimport { createProviderHookRuntime } from \"./serve/provider-hook-runtime.js\";\nimport { createServeShutdown } from \"./serve/shutdown.js\";\nimport type { ProviderId } from \"./providers/types.js\";\n\nfunction resolveInterruptedApprovals(\n permissionBroker: PermissionBroker,\n hookEventRouter: HookEventRouter,\n relay: RelayConnection,\n sessionId: string,\n): void {\n const approvals = permissionBroker.listSession(sessionId);\n if (approvals.length === 0) return;\n\n const message = \"Permission request was interrupted in the PTY.\";\n for (const approval of approvals) {\n if (!permissionBroker.resolve(approval.requestId, { behavior: \"deny\", message })) continue;\n hookEventRouter.onPermissionResolved(\n approval.sessionId,\n approval.provider,\n approval.requestId,\n \"deny\",\n { toolName: approval.toolName, toolInput: approval.input },\n );\n relay.sendRaw(\n serializeControl({\n type: \"permission_decision_result\",\n sessionId: approval.sessionId,\n requestId: approval.requestId,\n outcome: \"deny\",\n delivered: true,\n message,\n }),\n );\n }\n serviceLogger.info(\n { sessionId, count: approvals.length },\n \"Pending approvals cleared after PTY interruption\",\n );\n}\n\nexport interface ServiceOptions {\n relayUrl?: string;\n relayName?: string;\n}\n\nfunction parseServiceOptions(argv: readonly string[]): ServiceOptions {\n const options: ServiceOptions = {};\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === \"--relay\") {\n const relayName = argv[i + 1];\n if (!relayName || relayName.startsWith(\"-\")) {\n throw new Error(\"Missing value for --relay\");\n }\n options.relayName = relayName;\n i++;\n continue;\n }\n if (arg.startsWith(\"--relay=\")) {\n const relayName = arg.slice(\"--relay=\".length);\n if (!relayName) throw new Error(\"Missing value for --relay\");\n options.relayName = relayName;\n }\n }\n return options;\n}\n\nexport async function startService(options?: ServiceOptions): Promise<void> {\n ensureProfileWorkspace();\n await cleanupStaleResources();\n try {\n unlinkSync(STOPPED_PATH);\n } catch {\n // STOPPED 文件不存在时忽略\n }\n\n const permissionBroker = new PermissionBroker();\n const agentStatusRegistry = new AgentStatusRegistry();\n let unregisterHookSession: (sessionId: string) => void = () => {};\n const sessionManager = new SessionManager({\n persistPath: SESSIONS_PATH,\n onSessionRemoved: (id, context) => {\n if (!context?.preserveProviderHooks) {\n unregisterHookSession(id);\n }\n permissionBroker.cleanupSession(id, \"session removed\");\n agentStatusRegistry.delete(id);\n const paths = sessionPaths(id);\n try {\n rmSync(paths.dir, { recursive: true, force: true });\n } catch {\n // 会话目录清理失败不影响主流程\n }\n },\n });\n sessionManager.startReaper();\n\n const terminalSockets = new Map<string, Socket>();\n const proxyName = getProxyName();\n\n // 连接中转服务器:优先用调用方传入的 relayUrl,否则从配置文件读取\n // relay 是 proxy 存在的必要前提,未配置直接 fail-fast,不再支持\"本地独立\"模式\n let proxyConfig = loadConfig({ relayName: options?.relayName });\n const getProviderEnv = (): NodeJS.ProcessEnv => buildProviderEnv(proxyConfig, process.env);\n const getAgentCliSuggestions = (): Partial<Record<ProviderId, string[]>> =>\n proxyConfig.agentCliSuggestions;\n const getPreviewRoots = (): string[] => proxyConfig.previewRoots;\n const setAgentCliPath = (provider: ProviderId, path: string): void => {\n const field = provider === \"claude\" ? \"claudeBin\" : \"codexBin\";\n const existing = proxyConfig.agentCliSuggestions[provider] ?? [];\n proxyConfig = {\n ...proxyConfig,\n [field]: path,\n agentCliSuggestions: {\n ...proxyConfig.agentCliSuggestions,\n [provider]: [path, ...existing.filter((candidate) => candidate !== path)],\n },\n sources: {\n ...proxyConfig.sources,\n [field]: \"file\",\n },\n };\n };\n const relayUrl = options?.relayUrl ?? proxyConfig.relayUrl;\n const relayToken = proxyConfig.relayToken;\n const statusConfig = {\n profile: PROFILE_NAME,\n relayName: proxyConfig.relayName,\n relayNameSource: proxyConfig.sources.relayName,\n relayUrl,\n relayUrlSource: proxyConfig.sources.relayUrl,\n relayTokenSource: proxyConfig.sources.relayToken,\n hookPort: proxyConfig.hookPort ?? 17654,\n hookPortSource: proxyConfig.sources.hookPort,\n };\n if (!relayUrl) {\n const msg = `Relay URL is required. Set relays.${proxyConfig.relayName}.url in ~/.dev-anywhere/config.json or pass --relay <name>.`;\n serviceLogger.error(msg);\n console.error(msg);\n await flushLogger(serviceLogger);\n process.exit(1);\n }\n const relayConnection = new RelayConnection(relayUrl, {\n name: proxyName,\n token: relayToken,\n proxyIdPath: PROXY_ID_PATH,\n });\n const relaySend = (data: string): void => relayConnection.sendRaw(data);\n const controlHandlers = createControlMessageHandlers(relaySend, sessionManager);\n\n const eventBridge = createEventBridge({\n sessionManager,\n relayConnection,\n agentStatusRegistry,\n controlHandlers,\n permissionBroker,\n });\n const jsonObserver = new JsonObserver({\n changeSessionState: eventBridge.changeSessionState,\n emitAgentStatus: eventBridge.emitAgentStatus,\n });\n const hookRuntime = await createProviderHookRuntime({\n hookPort: proxyConfig.hookPort,\n permissionBroker,\n sessionManager,\n relayConnection,\n agentStatusRegistry,\n changeSessionState: eventBridge.changeSessionState,\n });\n unregisterHookSession = (sessionId) => hookRuntime.hookRegistry.unregisterSession(sessionId);\n\n // WorkerRegistry 建在 relay 之后、listener 之前;构造期订阅 envelope_dropped 事件\n const workerRegistry = new WorkerRegistry({\n sessionManager,\n permissionBroker,\n relayConnection,\n jsonObserver,\n touchSessionActivity: eventBridge.touchSessionActivity,\n getProviderEnv,\n });\n const ptyBridgeDeps: PtySessionBridgeDeps = {\n changeSessionState: eventBridge.changeSessionState,\n getSession: (sessionId) => sessionManager.getSession(sessionId),\n getPendingApprovalCount: (sessionId) => permissionBroker.listSession(sessionId).length,\n resolveInterruptedApprovals: (sessionId) =>\n resolveInterruptedApprovals(\n permissionBroker,\n hookRuntime.hookEventRouter,\n relayConnection,\n sessionId,\n ),\n emitAgentStatus: eventBridge.emitAgentStatus,\n };\n const hostedPtyRegistry = new HostedPtyRegistry({\n sessionManager,\n relayConnection,\n getProviderEnv,\n touchSessionActivity: eventBridge.touchSessionActivity,\n applyPtyStateToSession: (sessionId, ptyState) =>\n applyPtyStateToSession(ptyBridgeDeps, sessionId, ptyState),\n onSessionClosed: eventBridge.cleanupSessionResources,\n });\n\n relayConnection.connect();\n serviceLogger.info(\n {\n relayName: proxyConfig.relayName,\n profile: PROFILE_NAME,\n relayUrl,\n proxyName,\n tokenSet: !!relayToken,\n relayUrlSource: proxyConfig.sources.relayUrl,\n },\n \"Connecting to relay server\",\n );\n\n const relayRouter = new RelayRouter({\n sessionManager,\n workerRegistry,\n controlHandlers,\n relayConnection,\n relaySend,\n terminalSockets,\n hostedPtyRegistry,\n broadcastSessionList: () => broadcastSessionList(relayConnection, sessionManager),\n broadcastSessionSync: (session) => broadcastSessionSync(relayConnection, session),\n jsonObserver,\n createHookContext: hookRuntime.createHookContext,\n cleanupHookContext: (sessionId) => hookRuntime.hookRegistry.unregisterSession(sessionId),\n permissionBroker,\n hookEventRouter: hookRuntime.hookEventRouter,\n agentStatusRegistry,\n getProviderEnv,\n getAgentCliSuggestions,\n setAgentCliPath,\n getPreviewRoots,\n });\n\n relayConnection.on(\"message\", (msg: Record<string, unknown>) => relayRouter.handle(msg));\n relayConnection.on(\"connected\", () => {\n // fire-and-forget 但显式吞掉 rejection,否则 reinitializeOnReconnect 内部任意 IO 异常\n // 或 schema 校验错误会变 unhandledRejection,Node 默认终止整个 serve 进程。\n // 失败影响面: agent-cli-status / proxy_register_response 后的状态推送丢失, client 在\n // reconnect 后看到陈旧状态。属于服务降级而非健康降级, 用 error 级别让 ops 能接到告警。\n void controlHandlers.reinitializeOnReconnect().catch((err: unknown) => {\n serviceLogger.error(\n {\n error: err instanceof Error ? err.message : String(err),\n stack: err instanceof Error ? err.stack : undefined,\n },\n \"reinitializeOnReconnect failed: client may see stale state until next manual sync\",\n );\n });\n broadcastBridgeStatus(true);\n });\n relayConnection.on(\"disconnected\", () => {\n broadcastBridgeStatus(false);\n });\n\n // 把 relay 连接状态广播给所有已注册的 terminal,终端进程会 stderr 打 banner 提示用户\n function broadcastBridgeStatus(connected: boolean): void {\n const msg = serializeIpc({ type: \"bridge_status\", connected });\n for (const [, sock] of terminalSockets) {\n if (sock.writable) sock.write(msg);\n }\n }\n\n await workerRegistry.reconnectAll();\n\n const server = createServer((socket) => {\n handleTerminalConnection(socket, {\n sessionManager,\n workerRegistry,\n terminalSockets,\n hostedPtyRegistry,\n relayConnection,\n controlHandlers,\n agentStatusRegistry,\n permissionBroker,\n hookEventRouter: hookRuntime.hookEventRouter,\n createHookContext: hookRuntime.createHookContext,\n emitAgentStatus: eventBridge.emitAgentStatus,\n cleanupSessionResources: eventBridge.cleanupSessionResources,\n config: statusConfig,\n resolveInterruptedApprovals: (sessionId) =>\n resolveInterruptedApprovals(\n permissionBroker,\n hookRuntime.hookEventRouter,\n relayConnection,\n sessionId,\n ),\n });\n });\n\n server.listen(SOCK_PATH, () => {\n writeFileSync(PID_PATH, String(process.pid));\n chmodSync(SOCK_PATH, 0o600);\n serviceLogger.info({ pid: process.pid, sock: SOCK_PATH }, \"Service started\");\n });\n\n const shutdown = createServeShutdown({\n logger: serviceLogger,\n sessionManagerStopReaper: () => sessionManager.stopReaper(),\n relayRouterDestroy: () => relayRouter.destroy(),\n hookServerClose: () => hookRuntime.hookServer.close(),\n relayConnectionClose: () => relayConnection.close(),\n workerRegistryDestroyAll: () => workerRegistry.destroyAll(),\n hostedPtyRegistryDestroyAll: () => hostedPtyRegistry.destroyAll(),\n ipcServerClose: () => server.close(),\n sockPath: SOCK_PATH,\n pidPath: PID_PATH,\n });\n\n process.on(\"SIGTERM\", () => {\n void shutdown();\n });\n process.on(\"SIGINT\", () => {\n void shutdown();\n });\n}\n\nconst isMainModule =\n process.argv[1] && (process.argv[1].endsWith(\"serve.js\") || process.argv[1].endsWith(\"serve.ts\"));\n\nif (isMainModule) {\n startService(parseServiceOptions(process.argv.slice(2))).catch(async (err) => {\n const message = err instanceof Error ? err.message : String(err);\n serviceLogger.error({ err: message }, \"Service failed to start\");\n console.error(message);\n await flushLogger(serviceLogger);\n process.exit(1);\n });\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { nanoid } from \"nanoid\";\nimport { defineFSM, SessionState } from \"@dev-anywhere/shared\";\nimport { atomicWriteFileSync } from \"../common/atomic-write.js\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport type { ProviderId } from \"../providers/index.js\";\n\nexport interface SessionInfo {\n id: string;\n mode: \"pty\" | \"json\";\n provider: ProviderId;\n ptyOwner?: \"local-terminal\" | \"proxy-hosted\";\n state: SessionState;\n createdAt: number;\n updatedAt: number;\n name?: string;\n cwd: string;\n // Claude CLI 自己生成的 session ID,和上面 id 字段无关\n // 用途:定位 ~/.claude/projects/<encoded-cwd>/<claudeSessionId>.jsonl 历史文件 / 支持 --resume\n claudeSessionId?: string;\n pid: number;\n}\n\ninterface SessionManagerOptions {\n persistPath: string;\n reaperIntervalMs?: number;\n onSessionRemoved?: (id: string, context?: SessionRemoveContext) => void;\n}\n\ninterface SessionRemoveContext {\n preserveProviderHooks?: boolean;\n}\n\n// 两个观察通道的合法转换表分离:PTY 看 OSC 信号、JSON 看 stream-json 事件,各自的状态空间和规则不同。\n// terminated 是终态,不允许任何转出。\n\n// PTY 观察通道:从终端 OSC 0/9 信号 + idle timer 推导状态。\n// ERROR 在 PTY 观察通道不可达:PTY 错误体现为终端 ANSI 内容,proxy 不建模观察器失联。\nconst PTY_TRANSITIONS: Record<SessionState, readonly SessionState[]> = {\n [SessionState.IDLE]: [\n // claude 开始响应用户输入 → handlePtyData 首字节翻 working\n SessionState.WORKING,\n // provider hook 是语义事件,可能比 PTY 字节观察更早到达;PermissionRequest 可直接进入审批等待。\n SessionState.WAITING_APPROVAL,\n // 终态兜底;现阶段 terminated 走 terminateSession 直接删 map 不经 updateState,本边未被触发\n SessionState.TERMINATED,\n ],\n [SessionState.WORKING]: [\n // 5s 静默且 currentPtyState === \"working\" → idle timer 推 turn_complete\n SessionState.IDLE,\n // claude 发 OSC 9 \"needs your permission: <tool>\" → handlePtyData 推 approval_wait\n SessionState.WAITING_APPROVAL,\n // 终态兜底\n SessionState.TERMINATED,\n ],\n [SessionState.WAITING_APPROVAL]: [\n // 审批解除后 provider 可能继续工作,也可能直接结束本轮。\n // 真实 Claude 拒绝工具审批后会直接发 turn_complete,因此 WAITING_APPROVAL -> IDLE 是合法边。\n SessionState.WORKING,\n SessionState.IDLE,\n // 终态兜底\n SessionState.TERMINATED,\n ],\n // PTY 永不进入 ERROR;本行仅为满足 Record<SessionState,_> 枚举完整性保留\n [SessionState.ERROR]: [SessionState.TERMINATED],\n [SessionState.TERMINATED]: [],\n};\n\n// JSON 观察通道:从 stream-json 事件 + relay 入站消息推导状态。\n// 注意:turn 结束时 result.is_error === true 不走 ERROR——它属于 turn 内部错误,观察通道本身健康,仍按 onTurnResult → IDLE 处理。\nconst JSON_TRANSITIONS: Record<SessionState, readonly SessionState[]> = {\n [SessionState.IDLE]: [\n // 用户在 relay/web 端发消息 → onTurnStart,turn 开始\n SessionState.WORKING,\n // 空闲期观察通道失联(worker socket 死但 pid 仍在等)→ onChannelBroken\n SessionState.ERROR,\n // 终态兜底;同 PTY,当前不经 updateState\n SessionState.TERMINATED,\n ],\n [SessionState.WORKING]: [\n // stream-json result event → onTurnResult,turn 结束\n SessionState.IDLE,\n // claude 发 control_request → onApprovalRequested,阻塞等审批\n SessionState.WAITING_APPROVAL,\n // turn 进行中通道失联 → onChannelBroken\n SessionState.ERROR,\n // 终态兜底\n SessionState.TERMINATED,\n ],\n [SessionState.WAITING_APPROVAL]: [\n // 粒度丢失:审批解除后 claude 继续跑,proxy 观察不到中间的 WORKING 信号,\n // 直到 result event 才感知 → onTurnResult 一次性从 WAITING_APPROVAL 跳到 IDLE。\n // 因此不列 WAITING_APPROVAL → WORKING 这条边。\n SessionState.IDLE,\n // 审批死锁:control_response 写 worker stdin 失败 → onChannelBroken。\n // 这是 ERROR 态最明确的落地场景,让 UI 能区分 \"正在等用户决定\" 和 \"审批通道坏了\"。\n SessionState.ERROR,\n // 终态兜底\n SessionState.TERMINATED,\n ],\n [SessionState.ERROR]: [\n // 观察通道坏了之后只能 terminate,不回 IDLE/WORKING——恢复机制未实现\n SessionState.TERMINATED,\n ],\n [SessionState.TERMINATED]: [],\n};\n\nconst ptyFSM = defineFSM(PTY_TRANSITIONS);\nconst jsonFSM = defineFSM(JSON_TRANSITIONS);\n\nfunction fsmForMode(mode: \"pty\" | \"json\"): ReturnType<typeof defineFSM<SessionState>> {\n return mode === \"pty\" ? ptyFSM : jsonFSM;\n}\n\nfunction isProviderId(value: unknown): value is ProviderId {\n return value === \"claude\" || value === \"codex\";\n}\n\nexport class SessionManager {\n private sessions: Map<string, SessionInfo> = new Map();\n private reaperTimer: NodeJS.Timeout | null = null;\n private readonly persistPath: string;\n private readonly reaperIntervalMs: number;\n private readonly onSessionRemoved?: (id: string, context?: SessionRemoveContext) => void;\n\n constructor(options: SessionManagerOptions) {\n this.persistPath = options.persistPath;\n this.reaperIntervalMs = options.reaperIntervalMs ?? 60000;\n this.onSessionRemoved = options.onSessionRemoved;\n this.load();\n }\n\n createSession(\n mode: \"pty\" | \"json\",\n cwd: string,\n pid: number,\n name?: string,\n id?: string,\n provider: ProviderId = \"claude\",\n ptyOwner?: \"local-terminal\" | \"proxy-hosted\",\n ): SessionInfo {\n const now = Date.now();\n const info: SessionInfo = {\n id: id ?? nanoid(),\n mode,\n provider,\n ...(mode === \"pty\" && ptyOwner !== undefined ? { ptyOwner } : {}),\n state: SessionState.IDLE,\n createdAt: now,\n updatedAt: now,\n cwd,\n pid,\n ...(name !== undefined ? { name } : {}),\n };\n this.sessions.set(info.id, info);\n this.save();\n serviceLogger.info({ sessionId: info.id, mode, provider, ptyOwner, name }, \"Session created\");\n return info;\n }\n\n listSessions(): SessionInfo[] {\n return Array.from(this.sessions.values()).sort((a, b) => b.createdAt - a.createdAt);\n }\n\n getSession(id: string): SessionInfo | undefined {\n return this.sessions.get(id);\n }\n\n updateState(id: string, newState: SessionState): boolean {\n const session = this.sessions.get(id);\n if (!session) {\n // session 不存在是调用方 bug,不是观察竞态,保留 throw\n throw new Error(`Session not found: ${id}`);\n }\n const oldState = session.state;\n if (oldState === newState) return false;\n const fsm = fsmForMode(session.mode);\n if (!fsm.canTransition(oldState, newState)) {\n // 吸收态之后的残余转换来自进程竞态,降噪到 debug;\n // 其他非法转换属于协议违反或 bug,保持 warn 可观测\n const level = fsm.isAbsorbing(oldState) ? \"debug\" : \"warn\";\n serviceLogger[level](\n { sessionId: id, from: oldState, to: newState, mode: session.mode },\n level === \"debug\"\n ? \"State change after absorbing state (residual, likely race)\"\n : \"Invalid state transition rejected by FSM\",\n );\n return false;\n }\n session.state = newState;\n session.updatedAt = Date.now();\n this.save();\n serviceLogger.info({ sessionId: id, from: oldState, to: newState }, \"Session state changed\");\n return true;\n }\n\n touchSession(id: string, now: number = Date.now(), minIntervalMs = 0): boolean {\n const session = this.sessions.get(id);\n if (!session) return false;\n if (now - session.updatedAt < minIntervalMs) return false;\n session.updatedAt = now;\n this.save();\n return true;\n }\n\n terminateSession(id: string, context?: SessionRemoveContext): { success: boolean; pid?: number } {\n const session = this.sessions.get(id);\n if (!session) {\n return { success: false };\n }\n const pid = session.pid;\n this.sessions.delete(id);\n this.save();\n serviceLogger.info({ sessionId: id, mode: session.mode, pid }, \"Session terminated\");\n // 隔离 callback 异常: hook unregister / permission broker / 文件系统操作任意一步抛\n // 都不能让 terminateSession 把异常抛回调用方, 否则 socket close handler 上的后续\n // cleanupSessionResources + broadcastSessionList 会被吞掉, web 看到 session 残留。\n try {\n this.onSessionRemoved?.(id, context);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n serviceLogger.warn(\n {\n sessionId: id,\n err: { message: error.message, stack: error.stack, cause: error.cause },\n },\n \"onSessionRemoved callback threw; session already removed from registry\",\n );\n }\n return { success: true, pid };\n }\n\n terminateAll(): number[] {\n const pids: number[] = [];\n const ids = Array.from(this.sessions.keys());\n for (const id of ids) {\n const session = this.sessions.get(id)!;\n if (session.mode === \"json\" && session.pid !== undefined) {\n pids.push(session.pid);\n }\n this.sessions.delete(id);\n this.onSessionRemoved?.(id);\n }\n this.save();\n return pids;\n }\n\n setClaudeSessionId(id: string, claudeSessionId: string): void {\n const session = this.sessions.get(id);\n if (!session) {\n throw new Error(`Session not found: ${id}`);\n }\n session.claudeSessionId = claudeSessionId;\n this.save();\n }\n\n setPid(id: string, pid: number): void {\n const session = this.sessions.get(id);\n if (!session) {\n throw new Error(`Session not found: ${id}`);\n }\n session.pid = pid;\n this.save();\n }\n\n startReaper(intervalMs: number = this.reaperIntervalMs): void {\n this.stopReaper();\n this.reaperTimer = setInterval(() => this.reap(), intervalMs);\n }\n\n stopReaper(): void {\n if (this.reaperTimer) {\n clearInterval(this.reaperTimer);\n this.reaperTimer = null;\n }\n }\n\n private reap(): void {\n const toRemove: Array<{ id: string; reason: string }> = [];\n // 检查 JSON 会话的子进程是否存活\n // PTY 会话的生命周期由 IPC socket close 事件管理,不需要 reaper 参与\n for (const session of this.sessions.values()) {\n if (\n session.mode === \"json\" &&\n session.pid !== undefined &&\n session.state !== SessionState.TERMINATED\n ) {\n if (!this.isProcessAlive(session.pid)) {\n toRemove.push({ id: session.id, reason: `JSON worker process ${session.pid} is dead` });\n }\n }\n }\n for (const { id, reason } of toRemove) {\n serviceLogger.warn({ sessionId: id, reason }, \"Reaping stale session\");\n this.terminateSession(id);\n }\n }\n\n private isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n }\n\n private save(): void {\n // state 是对 claude 的观察值,进程死后无意义,不落盘。磁盘上只留 identity(id/mode/cwd/pid/...)。\n const persisted = Array.from(this.sessions.values()).map((s) => ({\n id: s.id,\n mode: s.mode,\n provider: s.provider,\n createdAt: s.createdAt,\n updatedAt: s.updatedAt,\n cwd: s.cwd,\n pid: s.pid,\n ...(s.name !== undefined ? { name: s.name } : {}),\n ...(s.claudeSessionId !== undefined ? { claudeSessionId: s.claudeSessionId } : {}),\n }));\n const data = JSON.stringify(persisted, null, 2);\n atomicWriteFileSync(this.persistPath, data, { ensureDir: true });\n }\n\n private load(): void {\n if (!existsSync(this.persistPath)) {\n return;\n }\n const raw = readFileSync(this.persistPath, \"utf-8\");\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n // 文件被截断 / 部分写入 / 手改成非法 JSON: 抛错会让 daemon 起不来, 用户没法 self-serve。\n // fail-soft 到空状态——已运行的 session 通过 reconnectAll 走 worker.sock 探活恢复, 还活着的\n // worker 仍能被 attach。失败仅意味着 session 名字 / cwd 等元数据丢失, 不至于阻塞启动。\n serviceLogger.warn(\n { path: this.persistPath, error: String(err) },\n \"Session persistence file unparseable, starting with empty state\",\n );\n return;\n }\n if (!Array.isArray(parsed)) {\n serviceLogger.warn(\n { path: this.persistPath },\n \"Session persistence file has unexpected format (not array), starting with empty state\",\n );\n return;\n }\n for (const item of parsed) {\n // state 字段不该落盘(见 save 注释)。遇到说明 schema 不匹配(旧版本数据 / 手改文件 / save bug);\n // 跳过该条而不是 throw,否则一条坏数据让所有 session 都加载不了,serve 起不来。\n if (item && typeof item === \"object\" && \"state\" in item) {\n const sessionId = String((item as { id?: unknown }).id);\n serviceLogger.warn(\n { sessionId },\n \"Session persistence record has unexpected state field, skipping\",\n );\n this.onSessionRemoved?.(sessionId);\n continue;\n }\n const info = item as Omit<SessionInfo, \"state\"> & { state?: SessionState };\n if (!isProviderId(info.provider)) {\n const sessionId = String(info.id);\n this.onSessionRemoved?.(sessionId);\n serviceLogger.warn(\n { sessionId, provider: info.provider },\n \"Session persistence file has invalid provider; cleaning session\",\n );\n continue;\n }\n if (info.mode === \"pty\") {\n if (info.pid && this.isProcessAlive(info.pid)) {\n // terminal 进程仍存活,会重连,保留磁盘数据但不加载到内存\n serviceLogger.info(\n { sessionId: info.id, pid: info.pid },\n \"PTY session skipped on load, terminal alive\",\n );\n } else {\n // terminal 进程已死,清理数据\n this.onSessionRemoved?.(info.id);\n serviceLogger.info(\n { sessionId: info.id, pid: info.pid },\n \"PTY session cleaned on load, terminal dead\",\n );\n }\n continue;\n }\n // JSON 会话:检查 worker 进程是否存活,无 PID 或进程已死则清理\n if (info.pid && this.isProcessAlive(info.pid)) {\n // 加载回内存时 state 重置为 IDLE,等后续观察通道信号刷新\n this.sessions.set(info.id, { ...info, state: SessionState.IDLE });\n } else {\n this.onSessionRemoved?.(info.id);\n serviceLogger.info(\n { sessionId: info.id, pid: info.pid },\n \"JSON session cleaned on load, worker dead\",\n );\n }\n }\n // 清理后回写磁盘,避免已清理的会话在下次启动时重复处理\n this.save();\n if (this.sessions.size > 0) {\n serviceLogger.info({ count: this.sessions.size }, \"Sessions restored from persistence\");\n }\n }\n}\n","import WebSocket from \"ws\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { nanoid } from \"nanoid\";\nimport { EventEmitter } from \"node:events\";\nimport { createFSM, serializeControl, type MessageEnvelope } from \"@dev-anywhere/shared\";\nimport { atomicWriteFileSync } from \"../common/atomic-write.js\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { MemoryMessageQueue } from \"./message-queue.js\";\n\n// 默认 proxyId 存储路径\nconst DEFAULT_PROXY_ID_PATH = join(homedir(), \".dev-anywhere\", \"proxy-id\");\n\n// 指数退避上限 30 秒\nconst MAX_BACKOFF_MS = 30000;\n// 退避基数 1 秒\nconst BASE_BACKOFF_MS = 1000;\n// 消息队列上限\nconst MAX_QUEUE_SIZE = 10000;\n// 来自 relay 的 JSON 控制消息最大长度(1MB)。挡住恶意/被攻陷的 relay 推超长 JSON 在\n// JSON.parse 前就 OOM proxy daemon。\nconst MAX_JSON_MESSAGE_SIZE = 1 * 1024 * 1024;\n\nexport const RelayConnectionState = {\n DISCONNECTED: \"disconnected\",\n CONNECTING: \"connecting\",\n REGISTERING: \"registering\",\n SYNCED: \"synced\",\n WAITING_RECONNECT: \"waiting_reconnect\",\n CLOSED: \"closed\",\n} as const;\nexport type RelayConnectionState = (typeof RelayConnectionState)[keyof typeof RelayConnectionState];\n\n// 合法的 WS 连接状态转移\n// CLOSED 是终态;connect 流转: DISCONNECTED → CONNECTING → REGISTERING → SYNCED\n// 断线: SYNCED/REGISTERING/CONNECTING → WAITING_RECONNECT → CONNECTING\n// 主动关: 任意 → CLOSED\nconst RELAY_TRANSITIONS: Record<RelayConnectionState, readonly RelayConnectionState[]> = {\n [RelayConnectionState.DISCONNECTED]: [\n RelayConnectionState.CONNECTING,\n RelayConnectionState.CLOSED,\n ],\n [RelayConnectionState.CONNECTING]: [\n RelayConnectionState.REGISTERING,\n RelayConnectionState.WAITING_RECONNECT,\n RelayConnectionState.CLOSED,\n ],\n [RelayConnectionState.REGISTERING]: [\n RelayConnectionState.SYNCED,\n RelayConnectionState.WAITING_RECONNECT,\n RelayConnectionState.CLOSED,\n ],\n [RelayConnectionState.SYNCED]: [\n RelayConnectionState.WAITING_RECONNECT,\n RelayConnectionState.CLOSED,\n ],\n [RelayConnectionState.WAITING_RECONNECT]: [\n RelayConnectionState.CONNECTING,\n RelayConnectionState.CLOSED,\n ],\n [RelayConnectionState.CLOSED]: [],\n};\n\ninterface RelayConnectionOptions {\n // 自定义 proxyId 文件路径,测试时使用临时目录\n proxyIdPath?: string;\n // proxy 显示名称,注册时发送给 relay\n name?: string;\n // 公网 relay 的 /proxy 端点预共享 token, relay 侧 RELAY_PROXY_TOKEN 对应\n token?: string;\n}\n\n// 管理代理到中转服务器的出站 WebSocket 连接,支持自动重连和消息队列\nexport class RelayConnection extends EventEmitter {\n private ws: WebSocket | null = null;\n private proxyId: string;\n private relayUrl: string;\n private queue: MemoryMessageQueue = new MemoryMessageQueue();\n private reconnectAttempt: number = 0;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private fsm = createFSM({\n initial: RelayConnectionState.DISCONNECTED as RelayConnectionState,\n transitions: RELAY_TRANSITIONS,\n onTransition: (from, to) =>\n serviceLogger.info({ from, to }, \"RelayConnection state transition\"),\n onRejected: (from, to, isAbsorbing) =>\n serviceLogger[isAbsorbing ? \"debug\" : \"warn\"](\n { from, to },\n isAbsorbing\n ? \"Late event after absorbing state, ignored\"\n : \"Invalid relay connection transition rejected\",\n ),\n });\n private name?: string;\n private token?: string;\n\n constructor(relayUrl: string, options?: RelayConnectionOptions) {\n super();\n this.relayUrl = relayUrl;\n this.proxyId = this.loadOrCreateProxyId(options?.proxyIdPath ?? DEFAULT_PROXY_ID_PATH);\n this.name = options?.name;\n this.token = options?.token;\n }\n\n // 从文件读取或生成新的 proxyId,生成后持久化到文件\n private loadOrCreateProxyId(idPath: string): string {\n if (existsSync(idPath)) {\n const existing = readFileSync(idPath, \"utf-8\").trim();\n if (existing.length > 0) {\n return existing;\n }\n }\n\n const id = nanoid(21);\n atomicWriteFileSync(idPath, id, { ensureDir: true });\n return id;\n }\n\n // 连接到 relay server\n connect(): void {\n if (!this.fsm.tryTransitionTo(RelayConnectionState.CONNECTING)) return;\n this.doConnect();\n }\n\n // 实际建立 WebSocket 连接的内部方法\n private doConnect(): void {\n try {\n const base = this.relayUrl.replace(/\\/$/, \"\") + \"/proxy\";\n const url = this.token ? `${base}?token=${encodeURIComponent(this.token)}` : base;\n this.ws = new WebSocket(url);\n\n this.ws.on(\"open\", () => {\n // open 属异步回调,若同步 close() 已先切 CLOSED,REGISTERING 会被拒,需跳过后续 register\n if (!this.fsm.tryTransitionTo(RelayConnectionState.REGISTERING)) return;\n serviceLogger.info(\n { proxyId: this.proxyId, url: base, tokenSet: !!this.token },\n \"Connected to relay server\",\n );\n this.ws!.send(\n serializeControl({\n type: \"proxy_register\",\n proxyId: this.proxyId,\n ...(this.name ? { name: this.name } : {}),\n }),\n );\n });\n\n this.ws.on(\"message\", (data) => {\n const buf = data as Buffer;\n if (buf.length > MAX_JSON_MESSAGE_SIZE) {\n serviceLogger.warn(\n { size: buf.length },\n \"JSON message from relay rejected: exceeds max size\",\n );\n return;\n }\n const raw = buf.toString();\n let msg: Record<string, unknown>;\n try {\n msg = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n serviceLogger.warn({ error: String(err) }, \"Non-JSON message from relay, dropped\");\n return;\n }\n if (msg.type === \"proxy_register_response\") {\n serviceLogger.info({ status: msg.status }, \"Received register response\");\n if (!this.fsm.tryTransitionTo(RelayConnectionState.SYNCED)) return;\n this.reconnectAttempt = 0;\n this.flushQueue();\n this.emit(\"connected\");\n return;\n }\n this.emit(\"message\", msg);\n });\n\n this.ws.on(\"close\", (code: number, reason: Buffer) => {\n this.ws = null;\n const closeMeta = { code, reason: reason.toString() || undefined };\n if (this.fsm.current() !== RelayConnectionState.CLOSED) {\n this.fsm.tryTransitionTo(RelayConnectionState.WAITING_RECONNECT);\n serviceLogger.info(closeMeta, \"Relay connection closed unexpectedly\");\n this.emit(\"disconnected\");\n this.scheduleReconnect();\n } else {\n serviceLogger.info(closeMeta, \"Relay connection closed\");\n }\n });\n\n this.ws.on(\"error\", (err) => {\n serviceLogger.error({ error: String(err) }, \"Relay connection error\");\n });\n } catch (err) {\n serviceLogger.error({ error: String(err) }, \"Failed to create relay connection\");\n if (this.fsm.current() !== RelayConnectionState.CLOSED) {\n this.fsm.tryTransitionTo(RelayConnectionState.WAITING_RECONNECT);\n this.scheduleReconnect();\n }\n }\n }\n\n // 将队列中缓存的消息依次发送到 relay\n private flushQueue(): void {\n for (const raw of this.queue.drain()) {\n this.ws?.send(raw);\n }\n }\n\n // 计算全抖动指数退避延迟并调度重连\n private scheduleReconnect(): void {\n const backoff =\n Math.random() *\n Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * Math.pow(2, this.reconnectAttempt));\n serviceLogger.info(\n { attempt: this.reconnectAttempt + 1, backoffMs: Math.round(backoff) },\n \"Scheduling reconnect\",\n );\n this.reconnectTimer = setTimeout(() => {\n this.reconnectAttempt++;\n // 必须先回 CONNECTING 才能让 open handler 合法转到 REGISTERING;\n // 若 close() 抢先切 CLOSED(clearTimeout 理论上拦得住,保险再守一层),跳过重连\n if (!this.fsm.tryTransitionTo(RelayConnectionState.CONNECTING)) return;\n this.doConnect();\n }, backoff);\n }\n\n // 发送 MessageEnvelope 到 relay,离线时自动入队\n sendEnvelope(envelope: MessageEnvelope): void {\n const raw = JSON.stringify(envelope);\n this.sendRaw(raw);\n }\n\n // 发送 binary PTY 帧到 relay,断线时直接丢弃不入队\n // 接受 Uint8Array 而非强制 Buffer:encodeBinaryFrame 在 shared 包返回 Uint8Array,\n // ws.send 在底层同样支持 Uint8Array,无需额外 Buffer.from 拷贝。\n sendBinary(data: Uint8Array): void {\n if (\n this.fsm.current() === RelayConnectionState.SYNCED &&\n this.ws?.readyState === WebSocket.OPEN\n ) {\n this.ws.send(data);\n }\n // binary 帧无队列,断线丢弃\n }\n\n // 发送原始 JSON 字符串到 relay,根据 connectionState 决定直发、入队或丢弃\n sendRaw(raw: string): void {\n if (\n this.fsm.current() === RelayConnectionState.SYNCED &&\n this.ws?.readyState === WebSocket.OPEN\n ) {\n this.ws.send(raw);\n } else if (this.fsm.current() === RelayConnectionState.CLOSED) {\n serviceLogger.warn(\"Message discarded: connection is closed\");\n } else {\n if (this.queue.size() >= MAX_QUEUE_SIZE) {\n const dropped = this.queue.dropOldest();\n serviceLogger.warn(\n { maxSize: MAX_QUEUE_SIZE },\n \"Message queue overflow, oldest message dropped\",\n );\n // 通知订阅方(WorkerRegistry)补偿被丢的 envelope,例如清理 pending 审批\n if (dropped !== null) this.emit(\"envelope_dropped\", dropped);\n }\n this.queue.enqueue(raw);\n serviceLogger.debug({ queueSize: this.queue.size() }, \"Message queued during disconnect\");\n }\n }\n\n // 主动关闭连接,发送 proxy_disconnect 通知 relay 立即清理,不触发重连\n close(): void {\n // 幂等:已 CLOSED 直接跳过,避免 FSM 抛 closed -> closed\n if (this.fsm.is(RelayConnectionState.CLOSED)) return;\n this.fsm.tryTransitionTo(RelayConnectionState.CLOSED);\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.ws) {\n if (this.ws.readyState === WebSocket.OPEN) {\n this.ws.send(serializeControl({ type: \"proxy_disconnect\", proxyId: this.proxyId }));\n }\n this.ws.close();\n this.ws = null;\n }\n }\n\n // 获取当前 proxyId\n getProxyId(): string {\n return this.proxyId;\n }\n\n // 获取连接状态摘要,用于 CLI status 输出\n getStatus(): {\n connected: boolean;\n connectionState: RelayConnectionState;\n proxyId: string;\n reconnectAttempt: number;\n queueDepth: number;\n } {\n return {\n connected: this.fsm.current() === RelayConnectionState.SYNCED,\n connectionState: this.fsm.current(),\n proxyId: this.proxyId,\n reconnectAttempt: this.reconnectAttempt,\n queueDepth: this.queue.size(),\n };\n }\n}\n","// 发送队列只负责内存背压和有序 drain;持久化恢复由 relay/proxy 重拉协议承担。\ninterface MessageQueue {\n enqueue(raw: string): void;\n drain(): string[];\n size(): number;\n clear(): void;\n dropOldest(): string | null;\n}\n\nexport class MemoryMessageQueue implements MessageQueue {\n private items: string[] = [];\n\n enqueue(raw: string): void {\n this.items.push(raw);\n }\n\n drain(): string[] {\n const all = this.items;\n this.items = [];\n return all;\n }\n\n size(): number {\n return this.items.length;\n }\n\n clear(): void {\n this.items = [];\n }\n\n // 丢弃最旧消息,返回被丢弃的 raw 供 caller 做补偿(例如清理对应 pending 审批)\n dropOldest(): string | null {\n return this.items.shift() ?? null;\n }\n}\n","import { readdir, mkdir } from \"node:fs/promises\";\nimport { join, isAbsolute, normalize } from \"node:path\";\nimport { ControlErrorCode, serializeControl } from \"@dev-anywhere/shared\";\nimport type { SessionManager } from \"../session-manager.js\";\nimport { scanSessionHistory } from \"../session-history.js\";\nimport { discoverCommands } from \"../command-discovery.js\";\nimport { serviceLogger } from \"../../common/logger.js\";\nimport { classifyPathError } from \"../path-errors.js\";\n\nexport interface ControlMessageHandlers {\n handleDirListRequest(msg: { path: string; requestId?: string }): Promise<void>;\n handleDirCreateRequest(msg: { path: string; requestId?: string }): Promise<void>;\n handleSessionHistoryRequest(msg: { requestId?: string }): Promise<void>;\n handleSessionResourcesRequest(msg: {\n sessionId: string;\n requestId?: string;\n workDir: string;\n }): Promise<void>;\n pushCommandList(sessionId: string, workDir: string): Promise<void>;\n pushFileTree(sessionId: string, workDir: string): Promise<void>;\n reinitializeOnReconnect(): Promise<void>;\n cleanup(sessionId: string): void;\n}\n\n// 每个 session 的定时器和资源\ninterface SessionResources {\n commandRefreshTimer?: NodeJS.Timeout;\n fileTreeWorkDir?: string;\n}\n\n// 命令刷新间隔 6 小时\nconst COMMAND_REFRESH_MS = 6 * 60 * 60 * 1000;\n\n// 路径安全校验:拒绝相对路径和路径遍历\nfunction isPathSafe(path: string): boolean {\n if (!isAbsolute(path)) return false;\n const normalized = normalize(path);\n // 检查 normalize 后是否仍包含 ..(理论上不会,但做防御)\n if (normalized.includes(\"..\")) return false;\n return true;\n}\n\n// picker 展示忽略规则: dotfile + node_modules\n// listDirectory (按需) 与 getFileTree (预热) 必须共用, 否则逐层下钻会暴露 node_modules\nconst HIDDEN_ENTRY_NAMES = new Set([\"node_modules\"]);\nfunction isPickerVisible(name: string): boolean {\n return !name.startsWith(\".\") && !HIDDEN_ENTRY_NAMES.has(name);\n}\n\n// 目录优先 + 字母序, picker 侧依赖这个顺序做键盘导航默认选中\nfunction sortEntries(\n a: { isDir: boolean; name: string },\n b: { isDir: boolean; name: string },\n): number {\n if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;\n return a.name.localeCompare(b.name);\n}\n\nasync function scanDir(dirPath: string): Promise<Array<{ name: string; isDir: boolean }>> {\n const entries = await readdir(dirPath, { withFileTypes: true });\n return entries\n .filter((e) => isPickerVisible(e.name))\n .map((e) => ({ name: e.name, isDir: e.isDirectory() }))\n .sort(sortEntries);\n}\n\n// 预热 cwd + 直接子目录两层, 按目录分组返回, 前端写入 tree[path] 后逐层 picker 直接命中\ninterface FileTreeGroup {\n path: string;\n entries: Array<{ name: string; isDir: boolean }>;\n}\n\nasync function getFileTree(rootPath: string): Promise<FileTreeGroup[]> {\n const groups: FileTreeGroup[] = [];\n\n let rootEntries: Array<{ name: string; isDir: boolean }>;\n try {\n rootEntries = await scanDir(rootPath);\n } catch {\n return groups;\n }\n groups.push({ path: rootPath, entries: rootEntries });\n\n for (const sub of rootEntries) {\n if (!sub.isDir) continue;\n const subPath = join(rootPath, sub.name);\n try {\n const subEntries = await scanDir(subPath);\n groups.push({ path: subPath, entries: subEntries });\n } catch {\n // 无法读取子目录, 跳过这一层分组 (picker 会在点击时触发 dir_list_request 补齐)\n }\n }\n\n return groups;\n}\n\nexport function createControlMessageHandlers(\n send: (data: string) => void,\n sessionManager: SessionManager,\n): ControlMessageHandlers {\n const sessionResources = new Map<string, SessionResources>();\n\n function getResources(sessionId: string): SessionResources {\n let res = sessionResources.get(sessionId);\n if (!res) {\n res = {};\n sessionResources.set(sessionId, res);\n }\n return res;\n }\n\n function scheduleCommandRefresh(sessionId: string, workDir: string): void {\n const resources = getResources(sessionId);\n if (resources.commandRefreshTimer) {\n clearInterval(resources.commandRefreshTimer);\n }\n resources.commandRefreshTimer = setInterval(async () => {\n try {\n const commands = await discoverCommands(workDir);\n send(\n serializeControl({\n type: \"command_list_push\",\n commands,\n }),\n );\n serviceLogger.debug({ sessionId, count: commands.length }, \"Command list refreshed\");\n } catch (err) {\n serviceLogger.warn({ sessionId, error: String(err) }, \"Command refresh failed\");\n }\n }, COMMAND_REFRESH_MS);\n }\n\n return {\n async handleDirListRequest(msg: { path: string; requestId?: string }): Promise<void> {\n if (!isPathSafe(msg.path)) {\n send(\n serializeControl({\n type: \"dir_list_response\",\n requestId: msg.requestId,\n path: msg.path,\n entries: [],\n errorCode: ControlErrorCode.INVALID_PATH,\n error: \"Invalid path: must be absolute and must not contain path traversal\",\n }),\n );\n serviceLogger.warn({ path: msg.path }, \"Rejected dir_list_request: unsafe path\");\n return;\n }\n\n try {\n const entries = await scanDir(msg.path);\n send(\n serializeControl({\n type: \"dir_list_response\",\n requestId: msg.requestId,\n path: msg.path,\n entries,\n }),\n );\n serviceLogger.debug({ path: msg.path, count: entries.length }, \"Dir list response sent\");\n } catch (err) {\n send(\n serializeControl({\n type: \"dir_list_response\",\n requestId: msg.requestId,\n path: msg.path,\n entries: [],\n errorCode: classifyPathError(err),\n error: String(err),\n }),\n );\n serviceLogger.warn({ path: msg.path, error: String(err) }, \"Dir list request failed\");\n }\n },\n\n async handleDirCreateRequest(msg: { path: string; requestId?: string }): Promise<void> {\n if (!isPathSafe(msg.path)) {\n send(\n serializeControl({\n type: \"dir_create_response\",\n requestId: msg.requestId,\n path: msg.path,\n success: false,\n errorCode: ControlErrorCode.INVALID_PATH,\n error: \"Invalid path: must be absolute and must not contain path traversal\",\n }),\n );\n serviceLogger.warn({ path: msg.path }, \"Rejected dir_create_request: unsafe path\");\n return;\n }\n\n try {\n await mkdir(msg.path, { recursive: true });\n send(\n serializeControl({\n type: \"dir_create_response\",\n requestId: msg.requestId,\n path: msg.path,\n success: true,\n }),\n );\n serviceLogger.info({ path: msg.path }, \"Directory created\");\n } catch (err) {\n send(\n serializeControl({\n type: \"dir_create_response\",\n requestId: msg.requestId,\n path: msg.path,\n success: false,\n errorCode: classifyPathError(err),\n error: String(err),\n }),\n );\n serviceLogger.warn({ path: msg.path, error: String(err) }, \"Dir create failed\");\n }\n },\n\n async handleSessionHistoryRequest(msg: { requestId?: string }): Promise<void> {\n try {\n const sessions = await scanSessionHistory();\n send(\n serializeControl({\n type: \"session_history_response\",\n requestId: msg.requestId,\n sessions,\n }),\n );\n serviceLogger.debug({ count: sessions.length }, \"Session history response sent\");\n } catch (err) {\n send(\n serializeControl({\n type: \"session_history_response\",\n requestId: msg.requestId,\n sessions: [],\n }),\n );\n serviceLogger.warn({ error: String(err) }, \"Session history scan failed\");\n }\n },\n\n async handleSessionResourcesRequest(msg: {\n sessionId: string;\n requestId?: string;\n workDir: string;\n }): Promise<void> {\n getResources(msg.sessionId).fileTreeWorkDir = msg.workDir;\n scheduleCommandRefresh(msg.sessionId, msg.workDir);\n\n const [commandsResult, groupsResult] = await Promise.allSettled([\n discoverCommands(msg.workDir),\n getFileTree(msg.workDir),\n ]);\n const commands = commandsResult.status === \"fulfilled\" ? commandsResult.value : [];\n const groups = groupsResult.status === \"fulfilled\" ? groupsResult.value : [];\n const failedReason =\n commandsResult.status === \"rejected\"\n ? commandsResult.reason\n : groupsResult.status === \"rejected\"\n ? groupsResult.reason\n : undefined;\n\n send(\n serializeControl({\n type: \"session_resources_response\",\n requestId: msg.requestId,\n sessionId: msg.sessionId,\n commands,\n groups,\n ...(failedReason\n ? {\n errorCode: classifyPathError(failedReason),\n error: String(failedReason),\n }\n : {}),\n }),\n );\n serviceLogger.info(\n { sessionId: msg.sessionId, commandCount: commands.length, groupCount: groups.length },\n \"Session resources snapshot sent\",\n );\n },\n\n async pushCommandList(sessionId: string, workDir: string): Promise<void> {\n try {\n const commands = await discoverCommands(workDir);\n send(\n serializeControl({\n type: \"command_list_push\",\n commands,\n }),\n );\n serviceLogger.info({ sessionId, count: commands.length, workDir }, \"Command list pushed\");\n } catch (err) {\n serviceLogger.warn({ sessionId, error: String(err) }, \"Command discovery failed\");\n }\n\n // 6 小时定时刷新\n scheduleCommandRefresh(sessionId, workDir);\n },\n\n async pushFileTree(sessionId: string, workDir: string): Promise<void> {\n const resources = getResources(sessionId);\n resources.fileTreeWorkDir = workDir;\n\n try {\n const groups = await getFileTree(workDir);\n send(\n serializeControl({\n type: \"file_tree_push\",\n groups,\n }),\n );\n serviceLogger.debug(\n { sessionId, path: workDir, groupCount: groups.length },\n \"File tree pushed\",\n );\n } catch (err) {\n serviceLogger.warn({ sessionId, error: String(err) }, \"File tree push failed\");\n }\n },\n\n // relay 重连时同步 session 列表并重新推送控制数据\n async reinitializeOnReconnect(): Promise<void> {\n const activeSessions = sessionManager.listSessions().filter((s) => s.state !== \"terminated\");\n\n // 先同步 session 列表,relay 据此建立 proxy-session 关联\n if (activeSessions.length > 0) {\n send(\n serializeControl({\n type: \"session_sync\",\n sessions: activeSessions.map((s) => ({\n id: s.id,\n mode: s.mode,\n provider: s.provider,\n ...(s.ptyOwner !== undefined ? { ptyOwner: s.ptyOwner } : {}),\n state: s.state,\n })),\n }),\n );\n serviceLogger.info({ count: activeSessions.length }, \"Session list synced to relay\");\n }\n\n for (const session of activeSessions) {\n const resources = sessionResources.get(session.id);\n const workDir = resources?.fileTreeWorkDir;\n if (workDir) {\n try {\n const commands = await discoverCommands(workDir);\n send(\n serializeControl({\n type: \"command_list_push\",\n commands,\n }),\n );\n const groups = await getFileTree(workDir);\n send(\n serializeControl({\n type: \"file_tree_push\",\n groups,\n }),\n );\n serviceLogger.info(\n { sessionId: session.id },\n \"Reinitialized control data after reconnect\",\n );\n } catch (err) {\n serviceLogger.warn(\n { sessionId: session.id, error: String(err) },\n \"Reinitialize failed\",\n );\n }\n }\n }\n },\n\n cleanup(sessionId: string): void {\n const resources = sessionResources.get(sessionId);\n if (resources) {\n if (resources.commandRefreshTimer) {\n clearInterval(resources.commandRefreshTimer);\n }\n sessionResources.delete(sessionId);\n }\n },\n };\n}\n","import { readdir, stat, access, open } from \"node:fs/promises\";\nimport { createReadStream } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createInterface } from \"node:readline\";\n\ninterface SessionHistoryEntry {\n id: string;\n title: string;\n projectDir: string;\n updatedAt: number;\n provider: \"claude\" | \"codex\";\n}\n\nconst claudeProjectsDir = (): string => join(homedir(), \".claude\", \"projects\");\nconst codexSessionsDir = (): string => join(homedir(), \".codex\", \"sessions\");\nconst UNTITLED_SESSION_TITLE = \"未命名会话\";\nconst MAX_HISTORY_TITLE_LENGTH = 40;\nconst IGNORED_SLASH_COMMANDS = new Set([\n \"/clear\",\n \"/model\",\n \"/compact\",\n \"/help\",\n \"/config\",\n \"/logout\",\n]);\nconst XMLISH_NOISE_PREFIXES = [\n \"environment\",\n \"system\",\n \"developer\",\n \"assistant\",\n \"user\",\n \"tool\",\n \"context\",\n];\nconst INTERNAL_TITLE_PATTERNS = [\n /^the following is the codex agent history\\b/i,\n /^codex agent history\\b/i,\n /^conversation summary\\b/i,\n];\n\n// 扫描 ~/.claude/projects/ 获取 Claude Code 会话历史\n// 实际目录结构: ~/.claude/projects/<encoded-project-path>/<session-id>.jsonl\nexport async function scanSessionHistory(): Promise<SessionHistoryEntry[]> {\n const entries = [...(await scanClaudeSessionHistory()), ...(await scanCodexSessionHistory())];\n entries.sort((a, b) => b.updatedAt - a.updatedAt);\n // 按 provider + title + projectDir 去重,resume 产生的多个 session 只保留最新的\n const seen = new Set<string>();\n return entries.filter((e) => {\n const key = `${e.provider}::${e.projectDir}::${e.title}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\nasync function scanClaudeSessionHistory(): Promise<SessionHistoryEntry[]> {\n const entries: SessionHistoryEntry[] = [];\n let projectDirs: string[];\n try {\n projectDirs = await readdir(claudeProjectsDir());\n } catch {\n return [];\n }\n\n for (const encodedDir of projectDirs) {\n const projectPath = join(claudeProjectsDir(), encodedDir);\n\n let files: string[];\n try {\n files = await readdir(projectPath);\n } catch {\n continue;\n }\n\n for (const file of files) {\n if (!file.endsWith(\".jsonl\")) continue;\n\n const filePath = join(projectPath, file);\n try {\n const fileStat = await stat(filePath);\n const sessionId = file.replace(/\\.jsonl$/, \"\");\n const { title, cwd } = await extractTitleAndCwd(filePath);\n\n entries.push({\n id: sessionId,\n title: title || UNTITLED_SESSION_TITLE,\n projectDir: cwd || \"/\" + encodedDir.replace(/^-/, \"\").split(\"-\").join(\"/\"),\n updatedAt: fileStat.mtimeMs,\n provider: \"claude\",\n });\n } catch {\n continue;\n }\n }\n }\n\n return entries;\n}\n\nasync function scanCodexSessionHistory(): Promise<SessionHistoryEntry[]> {\n const files = await collectJsonlFiles(codexSessionsDir());\n const entries: SessionHistoryEntry[] = [];\n for (const filePath of files) {\n try {\n const fileStat = await stat(filePath);\n const meta = await extractCodexTitleAndCwd(filePath);\n if (!meta.id) continue;\n entries.push({\n id: meta.id,\n title: meta.title || UNTITLED_SESSION_TITLE,\n projectDir: meta.cwd || homedir(),\n updatedAt: fileStat.mtimeMs,\n provider: \"codex\",\n });\n } catch {\n continue;\n }\n }\n return entries;\n}\n\ninterface SessionMessage {\n role: \"user\" | \"assistant\";\n text: string;\n timestamp?: number;\n cursor?: string;\n}\n\ninterface SessionMessagesPage {\n messages: SessionMessage[];\n hasMore: boolean;\n nextBefore?: string;\n}\n\ninterface SessionMessagesPageOptions {\n limit?: number;\n before?: string;\n}\n\nconst DEFAULT_HISTORY_PAGE_LIMIT = 50;\nconst MAX_HISTORY_PAGE_LIMIT = 200;\nconst HISTORY_READ_CHUNK_BYTES = 64 * 1024;\nconst HISTORY_CURSOR_PREFIX = \"b:\";\n\nfunction normalizeHistoryPageLimit(limit: unknown): number {\n if (typeof limit !== \"number\" || !Number.isFinite(limit)) return DEFAULT_HISTORY_PAGE_LIMIT;\n return Math.max(1, Math.min(MAX_HISTORY_PAGE_LIMIT, Math.floor(limit)));\n}\n\nfunction encodeHistoryCursor(offset: number): string {\n return `${HISTORY_CURSOR_PREFIX}${Math.max(0, Math.floor(offset))}`;\n}\n\nfunction decodeHistoryCursor(cursor: string | undefined, fileSize: number): number {\n if (!cursor) return fileSize;\n const raw = cursor.startsWith(HISTORY_CURSOR_PREFIX)\n ? cursor.slice(HISTORY_CURSOR_PREFIX.length)\n : cursor;\n const parsed = Number(raw);\n if (!Number.isInteger(parsed) || parsed < 0) return fileSize;\n return Math.min(parsed, fileSize);\n}\n\n// claudeSessionId 由 claude 自身生成(UUID),但既然落盘后会被拼进文件路径,\n// 防御性正则确保任何来源的不规范值都不会越过 ~/.claude/projects/<dir>/ 边界。\n// 允许字母数字、下划线、短横线,足以覆盖 UUID 与历史 fixture,禁止 . / \\ \\0 等路径字符。\nconst SAFE_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]+$/;\n\nasync function findClaudeSessionFile(claudeSessionId: string): Promise<string | null> {\n if (!SAFE_SESSION_ID_PATTERN.test(claudeSessionId)) return null;\n\n let projectDirs: string[];\n try {\n projectDirs = await readdir(claudeProjectsDir());\n } catch {\n return null;\n }\n\n for (const encodedDir of projectDirs) {\n const filePath = join(claudeProjectsDir(), encodedDir, `${claudeSessionId}.jsonl`);\n try {\n await access(filePath);\n return filePath;\n } catch {\n continue;\n }\n }\n\n return null;\n}\n\nfunction extractConversationMessageFromJson(obj: unknown): Omit<SessionMessage, \"cursor\"> | null {\n if (!obj || typeof obj !== \"object\") return null;\n const record = obj as {\n type?: unknown;\n isMeta?: unknown;\n message?: unknown;\n timestamp?: unknown;\n };\n if (record.type === \"user\") {\n if (record.isMeta) return null;\n const text = extractConversationText(record.message);\n if (!text) return null;\n const ts =\n typeof record.timestamp === \"string\" ? new Date(record.timestamp).getTime() : undefined;\n return { role: \"user\", text, timestamp: ts };\n }\n if (record.type === \"assistant\") {\n const text = extractConversationText(record.message);\n if (!text) return null;\n const ts =\n typeof record.timestamp === \"string\" ? new Date(record.timestamp).getTime() : undefined;\n return { role: \"assistant\", text, timestamp: ts };\n }\n return null;\n}\n\nfunction splitLineSegments(\n block: Buffer,\n blockStart: number,\n): Array<{ start: number; line: Buffer }> {\n const segments: Array<{ start: number; line: Buffer }> = [];\n let start = 0;\n for (let i = 0; i < block.length; i += 1) {\n if (block[i] !== 10) continue;\n segments.push({ start: blockStart + start, line: block.subarray(start, i) });\n start = i + 1;\n }\n segments.push({ start: blockStart + start, line: block.subarray(start) });\n return segments;\n}\n\nfunction stripCarriageReturn(line: Buffer): Buffer {\n return line.length > 0 && line[line.length - 1] === 13 ? line.subarray(0, -1) : line;\n}\n\nasync function readSessionMessagesPageFromFile(\n filePath: string,\n options: SessionMessagesPageOptions = {},\n): Promise<SessionMessagesPage> {\n const limit = normalizeHistoryPageLimit(options.limit);\n const file = await open(filePath, \"r\");\n try {\n const fileStat = await file.stat();\n const endOffset = decodeHistoryCursor(options.before, fileStat.size);\n if (endOffset <= 0) return { messages: [], hasMore: false };\n\n let position = endOffset;\n let carry: Buffer = Buffer.alloc(0);\n const collected: SessionMessage[] = [];\n\n while (position > 0 && collected.length <= limit) {\n const readSize = Math.min(HISTORY_READ_CHUNK_BYTES, position);\n position -= readSize;\n const chunk = Buffer.alloc(readSize);\n await file.read(chunk, 0, readSize, position);\n\n const block = carry.length > 0 ? Buffer.concat([chunk, carry]) : chunk;\n const segments = splitLineSegments(block, position);\n const firstCompleteIndex = position > 0 ? 1 : 0;\n carry = position > 0 ? (segments[0]?.line ?? Buffer.alloc(0)) : Buffer.alloc(0);\n\n for (let i = segments.length - 1; i >= firstCompleteIndex; i -= 1) {\n const segment = segments[i];\n if (!segment) continue;\n const line = stripCarriageReturn(segment.line);\n if (line.length === 0) continue;\n try {\n const parsed = JSON.parse(line.toString(\"utf-8\"));\n const message = extractConversationMessageFromJson(parsed);\n if (!message) continue;\n collected.push({ ...message, cursor: encodeHistoryCursor(segment.start) });\n if (collected.length > limit) break;\n } catch {\n /* skip malformed lines */\n }\n }\n }\n\n const page = collected.slice(0, limit).reverse();\n const hasMore = collected.length > limit;\n return {\n messages: page,\n hasMore,\n ...(hasMore && page[0]?.cursor ? { nextBefore: page[0].cursor } : {}),\n };\n } finally {\n await file.close();\n }\n}\n\n// 从 JSONL 文件中提取 user/assistant 对话消息用于恢复时展示历史\nexport async function readSessionMessages(claudeSessionId: string): Promise<SessionMessage[]> {\n const filePath = await findClaudeSessionFile(claudeSessionId);\n if (!filePath) return [];\n\n const messages: SessionMessage[] = [];\n return new Promise((resolve) => {\n const rl = createInterface({\n input: createReadStream(filePath, { encoding: \"utf-8\" }),\n crlfDelay: Infinity,\n });\n\n rl.on(\"line\", (line) => {\n if (!line.trim()) return;\n try {\n const message = extractConversationMessageFromJson(JSON.parse(line));\n if (message) messages.push(message);\n } catch {\n /* skip */\n }\n });\n\n rl.on(\"close\", () => resolve(messages));\n rl.on(\"error\", () => resolve(messages));\n });\n}\n\nexport async function readSessionMessagesPage(\n claudeSessionId: string,\n options: SessionMessagesPageOptions = {},\n): Promise<SessionMessagesPage> {\n const filePath = await findClaudeSessionFile(claudeSessionId);\n if (!filePath) return { messages: [], hasMore: false };\n return readSessionMessagesPageFromFile(filePath, options);\n}\n\n// 从 message 字段提取文本,统一处理多种格式\nfunction collapseWhitespace(text: string): string {\n return text.replace(/\\s+/g, \" \").trim();\n}\n\nfunction truncateTitle(text: string): string {\n const chars = Array.from(text);\n return chars.length > MAX_HISTORY_TITLE_LENGTH\n ? `${chars.slice(0, MAX_HISTORY_TITLE_LENGTH).join(\"\")}...`\n : text;\n}\n\nfunction isXmlishNoise(text: string): boolean {\n const match = text.match(/^<([A-Za-z][\\w:-]*)\\b/);\n if (!match) return false;\n const tag = match[1].toLowerCase();\n return XMLISH_NOISE_PREFIXES.some((prefix) => tag === prefix || tag.startsWith(`${prefix}_`));\n}\n\nexport function normalizeHistoryTitle(raw: string | null | undefined): string | null {\n if (!raw) return null;\n const text = collapseWhitespace(raw);\n if (text.length < 2) return null;\n if (text.startsWith(\"<\") || isXmlishNoise(text)) return null;\n if (INTERNAL_TITLE_PATTERNS.some((pattern) => pattern.test(text))) return null;\n\n const slashCommand = text.match(/^\\/\\S+/)?.[0];\n if (slashCommand && IGNORED_SLASH_COMMANDS.has(slashCommand)) return null;\n\n return truncateTitle(text);\n}\n\nfunction extractSlashCommand(text: string): string | null {\n const nameMatch = text.match(/<command-name>([^<]+)<\\/command-name>/);\n if (!nameMatch) return null;\n const argsMatch = text.match(/<command-args>([^<]+)<\\/command-args>/);\n const args = argsMatch ? argsMatch[1].trim() : \"\";\n return normalizeHistoryTitle(args ? `${nameMatch[1]} ${args}` : nameMatch[1]);\n}\n\nfunction extractMessageText(msg: unknown): string | null {\n if (typeof msg === \"string\") {\n const cmd = extractSlashCommand(msg);\n if (cmd) return cmd;\n return normalizeHistoryTitle(msg);\n }\n\n if (msg && typeof msg === \"object\" && \"content\" in msg) {\n const content = (msg as { content: unknown }).content;\n if (typeof content === \"string\") {\n const cmd = extractSlashCommand(content);\n if (cmd) return cmd;\n return normalizeHistoryTitle(content);\n }\n if (Array.isArray(content)) {\n const texts = content\n .filter(\n (b: { type?: string; text?: string }) => b.type === \"text\" && typeof b.text === \"string\",\n )\n .map((b: { text: string }) => b.text);\n const joined = texts.join(\"\\n\").trim();\n return normalizeHistoryTitle(joined);\n }\n }\n\n if (Array.isArray(msg)) {\n const texts = msg\n .filter(\n (b: { type?: string; text?: string }) => b.type === \"text\" && typeof b.text === \"string\",\n )\n .map((b: { text: string }) => b.text);\n const joined = texts.join(\"\\n\").trim();\n return normalizeHistoryTitle(joined);\n }\n\n return null;\n}\n\nfunction normalizeConversationText(text: string): string | null {\n const trimmed = text.trim();\n if (!trimmed) return null;\n return trimmed;\n}\n\n// 对话正文恢复必须保留换行和 Markdown 结构;不能复用标题归一化逻辑。\nfunction extractConversationText(msg: unknown): string | null {\n if (typeof msg === \"string\") {\n const cmd = extractSlashCommand(msg);\n if (cmd) return cmd;\n return normalizeConversationText(msg);\n }\n\n if (msg && typeof msg === \"object\" && \"content\" in msg) {\n const content = (msg as { content: unknown }).content;\n if (typeof content === \"string\") {\n const cmd = extractSlashCommand(content);\n if (cmd) return cmd;\n return normalizeConversationText(content);\n }\n if (Array.isArray(content)) {\n const texts = content\n .filter(\n (b: { type?: string; text?: string }) => b.type === \"text\" && typeof b.text === \"string\",\n )\n .map((b: { text: string }) => b.text);\n return normalizeConversationText(texts.join(\"\\n\"));\n }\n }\n\n if (Array.isArray(msg)) {\n const texts = msg\n .filter(\n (b: { type?: string; text?: string }) => b.type === \"text\" && typeof b.text === \"string\",\n )\n .map((b: { text: string }) => b.text);\n return normalizeConversationText(texts.join(\"\\n\"));\n }\n\n return null;\n}\n\n// 从 JSONL 文件头部提取 cwd 和第一条有效用户文本消息作为标题\n// cwd 从任意行的 cwd 字段获取,title 从第一条 user 消息获取\nasync function extractTitleAndCwd(\n filePath: string,\n): Promise<{ title: string | null; cwd: string | null }> {\n return new Promise((resolve) => {\n const rl = createInterface({\n input: createReadStream(filePath, { encoding: \"utf-8\" }),\n crlfDelay: Infinity,\n });\n let resolved = false;\n let cwd: string | null = null;\n let title: string | null = null;\n\n rl.on(\"line\", (line) => {\n if (resolved) return;\n if (!line.trim()) return;\n\n try {\n const obj = JSON.parse(line);\n if (!cwd && typeof obj.cwd === \"string\") {\n cwd = obj.cwd;\n }\n if (!title && obj.type === \"user\" && !obj.isMeta) {\n const text = extractMessageText(obj.message);\n if (text) title = text;\n }\n if (cwd && title) {\n resolved = true;\n rl.close();\n }\n } catch {\n /* skip malformed lines */\n }\n });\n\n rl.on(\"close\", () => {\n if (!resolved) resolve({ title, cwd });\n else resolve({ title, cwd });\n });\n rl.on(\"error\", () => resolve({ title, cwd }));\n });\n}\n\nasync function collectJsonlFiles(root: string): Promise<string[]> {\n let entries: Array<{ name: string; isDirectory(): boolean; isFile(): boolean }>;\n try {\n entries = await readdir(root, { withFileTypes: true });\n } catch {\n return [];\n }\n\n const files: string[] = [];\n for (const entry of entries) {\n const child = join(root, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await collectJsonlFiles(child)));\n } else if (entry.isFile() && entry.name.endsWith(\".jsonl\")) {\n files.push(child);\n }\n }\n return files;\n}\n\nasync function extractCodexTitleAndCwd(\n filePath: string,\n): Promise<{ id: string | null; title: string | null; cwd: string | null }> {\n return new Promise((resolve) => {\n const rl = createInterface({\n input: createReadStream(filePath, { encoding: \"utf-8\" }),\n crlfDelay: Infinity,\n });\n let id: string | null = null;\n let cwd: string | null = null;\n let title: string | null = null;\n\n rl.on(\"line\", (line) => {\n if (!line.trim()) return;\n try {\n const obj = JSON.parse(line);\n if (obj.type === \"session_meta\" && obj.payload) {\n if (!id && typeof obj.payload.id === \"string\") id = obj.payload.id;\n if (!cwd && typeof obj.payload.cwd === \"string\") cwd = obj.payload.cwd;\n }\n if (!title && obj.type === \"response_item\") {\n const text = extractCodexUserText(obj.payload);\n if (text) title = text;\n }\n if (id && cwd && title) rl.close();\n } catch {\n /* skip malformed lines */\n }\n });\n\n rl.on(\"close\", () => resolve({ id, title, cwd }));\n rl.on(\"error\", () => resolve({ id, title, cwd }));\n });\n}\n\nfunction extractCodexUserText(payload: unknown): string | null {\n if (!payload || typeof payload !== \"object\") return null;\n const item = payload as { type?: unknown; role?: unknown; content?: unknown };\n if (item.type !== \"message\" || item.role !== \"user\") return null;\n if (typeof item.content === \"string\") return normalizeHistoryTitle(item.content);\n if (!Array.isArray(item.content)) return null;\n const texts = item.content\n .map((block: unknown) => {\n if (!block || typeof block !== \"object\") return \"\";\n const typed = block as { type?: unknown; text?: unknown };\n return typed.type === \"input_text\" && typeof typed.text === \"string\" ? typed.text : \"\";\n })\n .filter(Boolean);\n const joined = texts.join(\"\\n\").trim();\n return normalizeHistoryTitle(joined);\n}\n","import { readdirSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\ninterface CommandEntry {\n name: string;\n description: string;\n argumentHint?: string;\n source: string;\n}\n\nconst REPL_BUILTINS: CommandEntry[] = [\n { name: \"/compact\", description: \"Compact conversation history\", source: \"builtin\" },\n { name: \"/status\", description: \"Show session status\", source: \"builtin\" },\n { name: \"/cost\", description: \"Show token usage and cost\", source: \"builtin\" },\n { name: \"/clear\", description: \"Clear conversation history\", source: \"builtin\" },\n {\n name: \"/model\",\n description: \"Switch AI model\",\n argumentHint: \"model name (e.g., Haiku, Sonnet)\",\n source: \"builtin\",\n },\n { name: \"/help\", description: \"Show available commands\", source: \"builtin\" },\n { name: \"/memory\", description: \"Edit CLAUDE.md memory\", source: \"builtin\" },\n { name: \"/review\", description: \"Review diff of changes\", source: \"builtin\" },\n { name: \"/vim\", description: \"Enter vim mode\", source: \"builtin\" },\n { name: \"/terminal-setup\", description: \"Configure terminal integration\", source: \"builtin\" },\n { name: \"/permissions\", description: \"View and manage permissions\", source: \"builtin\" },\n { name: \"/allowed-tools\", description: \"View allowed tools\", source: \"builtin\" },\n {\n name: \"/add-dir\",\n description: \"Add working directory\",\n argumentHint: \"directory path\",\n source: \"builtin\",\n },\n { name: \"/init\", description: \"Initialize CLAUDE.md in project\", source: \"builtin\" },\n { name: \"/listen\", description: \"Listen for multi-turn responses\", source: \"builtin\" },\n { name: \"/pr-comments\", description: \"View PR comments\", source: \"builtin\" },\n { name: \"/release-notes\", description: \"Generate release notes\", source: \"builtin\" },\n { name: \"/ide\", description: \"Open IDE integration\", source: \"builtin\" },\n];\n\nconst COMMAND_BLACKLIST = new Set([\n \"/login\",\n \"/logout\",\n \"/config\",\n \"/plugin\",\n \"/mcp\",\n \"/install\",\n \"/setup-token\",\n \"/doctor\",\n \"/update\",\n \"/upgrade\",\n \"/memory\",\n \"/vim\",\n \"/terminal-setup\",\n \"/permissions\",\n \"/allowed-tools\",\n \"/ide\",\n \"/listen\",\n]);\n\ninterface DiscoverOptions {\n homeDir?: string;\n}\n\n/**\n * 从 SKILL.md 内容中解析 YAML frontmatter 的 name/description/argument-hint\n */\nexport function parseSkillFrontmatter(content: string): {\n name?: string;\n description?: string;\n argumentHint?: string;\n} {\n const match = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---/);\n if (!match) return {};\n\n const yaml = match[1];\n const result: { name?: string; description?: string; argumentHint?: string } = {};\n\n const nameMatch = yaml.match(/^name:\\s*(.+)$/m);\n if (nameMatch) result.name = nameMatch[1].trim();\n\n const descMatch = yaml.match(/^description:\\s*(.+)$/m);\n if (descMatch) result.description = descMatch[1].trim();\n\n const hintMatch = yaml.match(/^argument-hint:\\s*(.+)$/m);\n if (hintMatch) result.argumentHint = hintMatch[1].trim();\n\n return result;\n}\n\n/**\n * 扫描 skills 目录,每个子目录下的 SKILL.md 解析为一条命令\n */\nfunction scanSkillsDir(dirPath: string, source: string): CommandEntry[] {\n let entries: string[];\n try {\n entries = readdirSync(dirPath, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n return [];\n }\n\n const commands: CommandEntry[] = [];\n for (const name of entries) {\n const skillPath = join(dirPath, name, \"SKILL.md\");\n try {\n const content = readFileSync(skillPath, \"utf-8\");\n const parsed = parseSkillFrontmatter(content);\n commands.push({\n name: `/${parsed.name ?? name}`,\n description: parsed.description ?? \"\",\n argumentHint: parsed.argumentHint,\n source,\n });\n } catch {\n // SKILL.md 不存在或不可读,跳过\n }\n }\n return commands;\n}\n\n/**\n * 扫描 commands 目录,每个 .md 文件名即为命令名\n */\nfunction scanCommandsDir(dirPath: string, source: string): CommandEntry[] {\n let entries: string[];\n try {\n entries = readdirSync(dirPath).filter((f) => f.endsWith(\".md\"));\n } catch {\n return [];\n }\n\n const commands: CommandEntry[] = [];\n for (const filename of entries) {\n const cmdName = filename.replace(/\\.md$/, \"\");\n try {\n const content = readFileSync(join(dirPath, filename), \"utf-8\");\n const firstLine = content.split(\"\\n\")[0].trim();\n commands.push({\n name: `/${cmdName}`,\n description: firstLine,\n source,\n });\n } catch {\n commands.push({\n name: `/${cmdName}`,\n description: \"\",\n source,\n });\n }\n }\n return commands;\n}\n\n/**\n * 扫描插件目录中的 skills 和 commands 子目录\n */\nfunction scanPluginDirs(homeDir: string): CommandEntry[] {\n const pluginCacheDir = join(homeDir, \".claude\", \"plugins\", \"cache\");\n let pluginNames: string[];\n try {\n pluginNames = readdirSync(pluginCacheDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n return [];\n }\n\n const commands: CommandEntry[] = [];\n for (const pluginName of pluginNames) {\n const pluginDir = join(pluginCacheDir, pluginName);\n const skillCmds = scanSkillsDir(join(pluginDir, \"skills\"), \"plugin-skill\");\n const cmdCmds = scanCommandsDir(join(pluginDir, \"commands\"), \"plugin-command\");\n commands.push(...skillCmds, ...cmdCmds);\n }\n return commands;\n}\n\n/**\n * 发现所有可用的斜杠命令,合并多个来源并过滤黑名单\n *\n * 来源优先级: project > user > plugin > builtin\n * 同名命令按优先级高的保留\n */\nexport async function discoverCommands(\n workDir: string,\n options?: DiscoverOptions,\n): Promise<CommandEntry[]> {\n const homeDir = options?.homeDir ?? homedir();\n\n const builtins = REPL_BUILTINS.filter((c) => !COMMAND_BLACKLIST.has(c.name));\n const userSkills = scanSkillsDir(join(homeDir, \".claude\", \"skills\"), \"user-skill\");\n const projectSkills = scanSkillsDir(join(workDir, \".claude\", \"skills\"), \"project-skill\");\n const userCommands = scanCommandsDir(join(homeDir, \".claude\", \"commands\"), \"user-command\");\n const projectCommands = scanCommandsDir(join(workDir, \".claude\", \"commands\"), \"project-command\");\n const pluginCommands = scanPluginDirs(homeDir);\n\n // 按优先级从低到高合并,同名命令后者覆盖前者\n const commandMap = new Map<string, CommandEntry>();\n for (const cmd of builtins) commandMap.set(cmd.name, cmd);\n for (const cmd of pluginCommands) commandMap.set(cmd.name, cmd);\n for (const cmd of userSkills) commandMap.set(cmd.name, cmd);\n for (const cmd of userCommands) commandMap.set(cmd.name, cmd);\n for (const cmd of projectSkills) commandMap.set(cmd.name, cmd);\n for (const cmd of projectCommands) commandMap.set(cmd.name, cmd);\n\n // 再次过滤黑名单,防止外部来源引入黑名单命令\n const result: CommandEntry[] = [];\n for (const cmd of commandMap.values()) {\n if (!COMMAND_BLACKLIST.has(cmd.name)) {\n result.push(cmd);\n }\n }\n\n return result;\n}\n","import { ControlErrorCode } from \"@dev-anywhere/shared\";\n\nfunction getFsErrorCode(err: unknown): string | undefined {\n return typeof err === \"object\" && err !== null && \"code\" in err\n ? String((err as { code?: unknown }).code)\n : undefined;\n}\n\nexport function classifyPathError(err: unknown): ControlErrorCode {\n switch (getFsErrorCode(err)) {\n case \"ENOENT\":\n return ControlErrorCode.PATH_NOT_FOUND;\n case \"ENOTDIR\":\n return ControlErrorCode.PATH_NOT_DIRECTORY;\n case \"EACCES\":\n case \"EPERM\":\n return ControlErrorCode.PATH_ACCESS_DENIED;\n default:\n return ControlErrorCode.UNKNOWN;\n }\n}\n","import { connect, type Socket } from \"node:net\";\nimport { unlinkSync, existsSync, readdirSync } from \"node:fs\";\nimport type { ChildProcess } from \"node:child_process\";\nimport { buildMessage, serializeControl } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport {\n ContentBlockDeltaSchema,\n IGNORED_EVENT_TYPES,\n KnownContentBlockSchema,\n StreamJsonEventSchema,\n} from \"../common/stream-json-schema.js\";\nimport { DATA_DIR, sessionPaths } from \"../common/paths.js\";\nimport { spawnScript } from \"../common/env.js\";\nimport { getSeqCounterFor } from \"../common/seq-counter.js\";\nimport { createWorkerReader, serializeWorkerMsg, type WorkerMessage } from \"../ipc/ipc-protocol.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { JsonObserver } from \"./json-observer.js\";\nimport type { ProviderHookContext } from \"../providers/index.js\";\nimport type { PermissionBroker, PermissionDecision } from \"./permission-broker.js\";\n\ninterface WorkerRegistryDeps {\n sessionManager: SessionManager;\n permissionBroker: PermissionBroker;\n relayConnection: RelayConnection;\n // JSON 观察通道状态机;forwardEvent / forwardApprovalRequest 据此推状态变迁\n jsonObserver: JsonObserver;\n touchSessionActivity?: (sessionId: string) => boolean;\n getProviderEnv: () => NodeJS.ProcessEnv;\n nextSeq?: (sessionId: string) => number;\n}\n\ninterface SpawnOptions {\n cwd?: string;\n resumeSessionId?: string;\n permissionMode?: string;\n // 开启后 worker spawn claude 带 --include-partial-messages,forwardEvent 处理 stream_event delta;\n // aggregated assistant 的 text/thinking 会被跳过避免和 delta 重复\n streamDelta?: boolean;\n hook?: ProviderHookContext;\n}\n\n// 管理 session → worker socket 的映射,封装全部 worker IO:\n// - spawn / connect / reconnectAll / destroyAll 生命周期入口\n// - send(sessionId, msg) 统一出口\n// - worker_event 路由、worker_approval_request 转发、worker_exit 清理都在内部闭环\nexport class WorkerRegistry {\n private sockets = new Map<string, Socket>();\n private children = new Map<string, ChildProcess>();\n // 记录哪些 session 是 spawn 时带 --stream-delta 的;forwardEvent 据此决定是否跳过 aggregated 去重\n private streamDeltaSessions = new Set<string>();\n\n constructor(private deps: WorkerRegistryDeps) {\n // relay queue 溢出时,被 drop 的 envelope 不会到达 client;若是 tool_use_request,\n // 主动清 pending 审批并回 worker env-failure deny,避免 worker 永挂、permission broker 泄漏。\n deps.relayConnection.on(\"envelope_dropped\", (raw: string) => this.onEnvelopeDropped(raw));\n }\n\n private onEnvelopeDropped(raw: string): void {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return;\n }\n if (\n !parsed ||\n typeof parsed !== \"object\" ||\n (parsed as { type?: unknown }).type !== \"tool_use_request\"\n ) {\n return;\n }\n const envelope = parsed as {\n sessionId?: unknown;\n payload?: { toolId?: unknown };\n };\n const sessionId = typeof envelope.sessionId === \"string\" ? envelope.sessionId : null;\n const requestId =\n envelope.payload && typeof envelope.payload.toolId === \"string\"\n ? envelope.payload.toolId\n : null;\n if (!sessionId || !requestId) return;\n if (\n !this.deps.permissionBroker.resolve(requestId, {\n behavior: \"deny\",\n message: \"Approval request was dropped due to relay queue overflow.\",\n })\n ) {\n return;\n }\n\n serviceLogger.warn(\n { sessionId, requestId },\n \"Tool approval request lost to relay queue overflow, denying worker\",\n );\n }\n\n spawn(sessionId: string, options?: SpawnOptions): number {\n const paths = sessionPaths(sessionId);\n const args: string[] = [sessionId, paths.workerSock];\n if (options?.cwd) args.push(\"--cwd\", options.cwd);\n if (options?.resumeSessionId) args.push(\"--resume\", options.resumeSessionId);\n // 远程场景默认 default,每个工具都需审批,覆盖用户全局 claude settings 的 defaultMode\n args.push(\"--permission-mode\", options?.permissionMode ?? \"default\");\n if (options?.streamDelta) {\n args.push(\"--stream-delta\");\n this.streamDeltaSessions.add(sessionId);\n }\n if (options?.hook) {\n args.push(\n \"--hook-provider\",\n options.hook.provider,\n \"--hook-url\",\n options.hook.hookUrl,\n \"--hook-marker\",\n options.hook.marker,\n );\n }\n args.push(\"--\");\n\n const providerEnv = this.deps.getProviderEnv();\n const child = spawnScript(\"session-worker\", args, {\n logger: serviceLogger,\n env: options?.hook\n ? { ...providerEnv, DEV_ANYWHERE_HOOK_TOKEN: options.hook.token }\n : providerEnv,\n });\n const workerPid = child.pid!;\n this.children.set(sessionId, child);\n serviceLogger.info(\n { sessionId, workerPid, cwd: options?.cwd, resume: options?.resumeSessionId },\n \"Worker process spawned\",\n );\n return workerPid;\n }\n\n connect(sessionId: string, sockPath: string): Promise<Socket | null> {\n return new Promise((resolve) => {\n const sock = connect(sockPath);\n sock.on(\"connect\", () => {\n this.sockets.set(sessionId, sock);\n createWorkerReader(\n sock,\n (msg) => this.handleWorkerMessage(sessionId, msg),\n (err, line) => {\n // 单条 worker NDJSON 行 schema 校验失败:warn 而非断连。Claude/Codex CLI 增量\n // 加新事件类型时不该把整个 session 推进 ERROR;连接保持开放,下一条仍继续解析。\n serviceLogger.warn(\n { sessionId, err: err.message, lineLen: line.length },\n \"Worker IPC message dropped (parse/schema error)\",\n );\n },\n );\n sock.on(\"close\", () => this.onDisconnect(sessionId));\n sock.on(\"error\", () => this.onDisconnect(sessionId));\n resolve(sock);\n });\n sock.on(\"error\", () => resolve(null));\n });\n }\n\n // 枚举 DATA_DIR 下所有 session 目录,尝试连接存活的 worker.sock;失败则清理 stale socket。\n async reconnectAll(): Promise<void> {\n if (!existsSync(DATA_DIR)) return;\n\n const dirs = readdirSync(DATA_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());\n\n for (const dir of dirs) {\n const sessionId = dir.name;\n const paths = sessionPaths(sessionId);\n if (!existsSync(paths.workerSock)) continue;\n\n const sock = await this.connect(sessionId, paths.workerSock);\n if (sock) {\n if (!this.deps.sessionManager.getSession(sessionId)) {\n // worker.sock 可连通但 SessionManager 无该 session 记录\n // (sessions.json 写盘失败或文件丢失,但 worker 仍存活),属于孤儿 worker,直接终止。\n serviceLogger.warn(\n { sessionId },\n \"Orphaned worker found without session data, terminating\",\n );\n sock.end();\n this.sockets.delete(sessionId);\n continue;\n }\n serviceLogger.info({ sessionId }, \"Reconnected to existing worker\");\n } else {\n try {\n unlinkSync(paths.workerSock);\n } catch {\n // socket 文件可能已被删除\n }\n serviceLogger.info({ sessionId }, \"Cleaned up stale worker socket\");\n }\n }\n }\n\n has(sessionId: string): boolean {\n return this.sockets.has(sessionId);\n }\n\n delete(sessionId: string): void {\n this.children.delete(sessionId);\n this.sockets.delete(sessionId);\n this.streamDeltaSessions.delete(sessionId);\n }\n\n terminateProcess(sessionId: string, signal: NodeJS.Signals = \"SIGTERM\"): boolean {\n const child = this.children.get(sessionId);\n const sock = this.sockets.get(sessionId);\n sock?.destroy();\n this.sockets.delete(sessionId);\n this.streamDeltaSessions.delete(sessionId);\n this.children.delete(sessionId);\n if (!child || child.killed) return false;\n return child.kill(signal);\n }\n\n // 向指定 session 的 worker 写 WorkerMessage;socket 缺失或不可写返回 false 由 caller 决定日志。\n send(sessionId: string, msg: WorkerMessage): boolean {\n const sock = this.sockets.get(sessionId);\n if (!sock?.writable) return false;\n sock.write(serializeWorkerMsg(msg));\n return true;\n }\n\n destroyAll(): void {\n for (const [, ws] of this.sockets) {\n ws.destroy();\n }\n this.sockets.clear();\n }\n\n private handleWorkerMessage(sessionId: string, msg: WorkerMessage): void {\n switch (msg.type) {\n case \"worker_ready\":\n serviceLogger.info({ sessionId, pid: msg.pid }, \"Worker ready\");\n break;\n\n case \"worker_event\":\n try {\n this.forwardEvent(sessionId, msg.seq, msg.event);\n } catch (err) {\n serviceLogger.debug(\n { sessionId, error: String(err) },\n \"Failed to forward event to relay\",\n );\n }\n serviceLogger.debug({ sessionId, eventType: msg.event.type }, \"JSON session event\");\n break;\n\n case \"worker_exit\":\n this.deps.sessionManager.terminateSession(sessionId);\n this.delete(sessionId);\n serviceLogger.info({ sessionId, exitCode: msg.code }, \"JSON session exited\");\n break;\n\n case \"worker_approval_request\":\n this.forwardApprovalRequest(sessionId, msg);\n break;\n\n case \"worker_claude_session_id\":\n this.deps.sessionManager.setClaudeSessionId(sessionId, msg.sessionId);\n serviceLogger.info(\n { sessionId, claudeSessionId: msg.sessionId },\n \"Claude session ID captured\",\n );\n break;\n }\n }\n\n // worker 连接断开或异常时的统一清理入口。仅记录一份,不再区分 close vs error 语义。\n private onDisconnect(sessionId: string): void {\n this.sockets.delete(sessionId);\n this.deps.permissionBroker.cleanupSession(sessionId, \"Worker disconnected\");\n // worker_exit 消息走 handleWorkerMessage,那条路径已经 terminateSession 把 session 从 manager\n // 中删掉——onDisconnect 紧随其后到达时 getSession 返回 undefined,不触发 ERROR 转换。\n // 只有\"未经 worker_exit 即断连\"(进程崩溃 / 内核 OOM kill / IPC socket 损坏)才会进入这里:\n // session 仍然在 manager 中,必须立即把状态推到 ERROR,否则 UI 看到的状态会停留在\n // WORKING / WAITING_APPROVAL 直到 reaper 60s 周期触发,期间用户既无法手动中止也无法重启。\n if (this.deps.sessionManager.getSession(sessionId)) {\n this.deps.jsonObserver.onChannelBroken(sessionId);\n }\n }\n\n // 对齐 Claude CLI stream-json 输出,按 type 分发:\n // stream_event.content_block_delta → 增量 text/thinking envelope(仅 streamDelta 会话产生)\n // assistant.content[].text → assistant_message envelope(streamDelta 下跳过,避免重复)\n // assistant.content[].thinking → thinking envelope(streamDelta 下跳过)\n // assistant.content[].tool_use → assistant_tool_use envelope\n // user.content[].tool_result → tool_result envelope\n // result → turn_result control + 会话状态回 IDLE\n // system/rate_limit_event/其他 → 静默忽略\n // schema 未识别的 event/block 以 warn 暴露,作为 Claude CLI 协议变化的 runtime canary。\n private forwardEvent(sessionId: string, seq: number, event: Record<string, unknown>): void {\n const relay = this.deps.relayConnection;\n const parsed = StreamJsonEventSchema.safeParse(event);\n if (!parsed.success) {\n const rawType = typeof event.type === \"string\" ? event.type : \"<missing>\";\n if (IGNORED_EVENT_TYPES.has(rawType)) {\n serviceLogger.debug({ sessionId, type: rawType }, \"Dropped ignored stream-json event\");\n return;\n }\n serviceLogger.warn(\n { sessionId, type: rawType, issues: parsed.error.issues.slice(0, 3) },\n \"Unknown stream-json event type; Claude CLI schema may have changed\",\n );\n return;\n }\n const ev = parsed.data;\n this.deps.touchSessionActivity?.(sessionId);\n const isStreamDeltaSession = this.streamDeltaSessions.has(sessionId);\n\n if (ev.type === \"stream_event\") {\n const delta = ContentBlockDeltaSchema.safeParse(ev.event);\n if (!delta.success) return; // 非 content_block_delta 的内层事件(message_start 等)忽略\n const d = delta.data.delta;\n if (d.type === \"text_delta\" && d.text) {\n relay.sendEnvelope(\n buildMessage(\n \"assistant_message\",\n sessionId,\n seq,\n { text: d.text, isPartial: true },\n \"proxy\",\n ),\n );\n } else if (d.type === \"thinking_delta\" && d.thinking) {\n relay.sendEnvelope(buildMessage(\"thinking\", sessionId, seq, { text: d.thinking }, \"proxy\"));\n }\n return;\n }\n\n if (ev.type === \"assistant\") {\n for (const raw of ev.message.content) {\n const blockParse = KnownContentBlockSchema.safeParse(raw);\n if (!blockParse.success) {\n const rawType =\n raw && typeof raw === \"object\"\n ? ((raw as Record<string, unknown>).type as string | undefined)\n : undefined;\n serviceLogger.warn(\n { sessionId, seq, blockType: rawType ?? \"<missing>\" },\n \"Unknown assistant content block; Claude CLI schema may have changed\",\n );\n continue;\n }\n const block = blockParse.data;\n if (block.type === \"text\") {\n // streamDelta 下增量已经发过了,aggregated 全文跳过避免重复\n if (!isStreamDeltaSession && block.text) {\n relay.sendEnvelope(\n buildMessage(\n \"assistant_message\",\n sessionId,\n seq,\n { text: block.text, isPartial: true },\n \"proxy\",\n ),\n );\n }\n } else if (block.type === \"thinking\") {\n // Opus extended thinking 明文被 Anthropic 服务端 redact 时 block.thinking 为空字符串,\n // 不转发;session WORKING 状态已经覆盖\"Claude 在思考\"信号,redacted envelope 无新信息\n if (!isStreamDeltaSession && block.thinking) {\n relay.sendEnvelope(\n buildMessage(\"thinking\", sessionId, seq, { text: block.thinking }, \"proxy\"),\n );\n }\n } else if (block.type === \"tool_use\") {\n relay.sendEnvelope(\n buildMessage(\n \"assistant_tool_use\",\n sessionId,\n seq,\n { toolName: block.name, toolId: block.id, parameters: block.input },\n \"proxy\",\n ),\n );\n }\n }\n return;\n }\n\n if (ev.type === \"user\") {\n for (const raw of ev.message.content) {\n const blockParse = KnownContentBlockSchema.safeParse(raw);\n if (!blockParse.success) continue;\n const block = blockParse.data;\n if (block.type !== \"tool_result\") continue;\n relay.sendEnvelope(\n buildMessage(\n \"tool_result\",\n sessionId,\n seq,\n { toolId: block.tool_use_id, result: block.content, isError: block.is_error ?? false },\n \"proxy\",\n ),\n );\n }\n return;\n }\n\n if (ev.type === \"result\") {\n const resultText = typeof ev.result === \"string\" ? ev.result : undefined;\n relay.sendRaw(\n serializeControl({\n type: \"turn_result\",\n sessionId,\n success: ev.subtype === \"success\",\n isError: ev.is_error ?? false,\n ...(resultText ? { result: resultText } : {}),\n }),\n );\n this.deps.jsonObserver.onTurnResult(sessionId);\n }\n }\n\n private forwardApprovalRequest(\n sessionId: string,\n msg: Extract<WorkerMessage, { type: \"worker_approval_request\" }>,\n ): void {\n serviceLogger.info(\n { sessionId, toolName: msg.toolName, requestId: msg.requestId },\n \"Tool approval forwarding to relay\",\n );\n this.deps.jsonObserver.onApprovalRequested(sessionId);\n try {\n const approvalSeq = this.deps.nextSeq?.(sessionId) ?? getSeqCounterFor(sessionId).next();\n const envelope = buildMessage(\n \"tool_use_request\",\n sessionId,\n approvalSeq,\n {\n toolName: msg.toolName,\n toolId: msg.requestId,\n parameters: msg.input,\n },\n \"proxy\",\n );\n const session = this.deps.sessionManager.getSession(sessionId);\n const registered = this.deps.permissionBroker.registerWorkerRequest(\n {\n requestId: msg.requestId,\n provider: session?.provider ?? \"claude\",\n sessionId,\n toolName: msg.toolName,\n input: msg.input,\n },\n (decision: PermissionDecision) => {\n this.send(sessionId, {\n type: \"worker_approval_response\",\n requestId: msg.requestId,\n behavior: decision.behavior,\n ...(decision.message ? { message: decision.message } : {}),\n });\n },\n );\n if (!registered) return;\n this.deps.relayConnection.sendEnvelope(envelope);\n } catch (err) {\n const resolved = this.deps.permissionBroker.resolve(msg.requestId, {\n behavior: \"deny\",\n message: \"Failed to forward approval request to relay.\",\n });\n if (!resolved) {\n this.send(sessionId, {\n type: \"worker_approval_response\",\n requestId: msg.requestId,\n behavior: \"deny\",\n message: \"Failed to forward approval request to relay.\",\n });\n }\n // envelope 构造失败回 deny,避免 worker 无限等待。\n serviceLogger.warn(\n { sessionId, error: String(err) },\n \"Failed to forward tool approval to relay, denying\",\n );\n }\n }\n}\n","import type { Socket } from \"node:net\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { serializeIpc } from \"../ipc/ipc-protocol.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\nimport type { ControlMessageHandlers } from \"./handlers/control-messages.js\";\nimport type { HostedPtyRegistry } from \"./hosted-pty-registry.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport type { WorkerRegistry } from \"./worker-registry.js\";\n\ntype SessionTerminationAction =\n | \"detach_local_terminal\"\n | \"terminate_hosted_pty\"\n | \"terminate_json_worker\"\n | \"not_found\";\n\ninterface TerminateSessionDeps {\n sessionManager: SessionManager;\n workerRegistry: WorkerRegistry;\n controlHandlers: ControlMessageHandlers;\n terminalSockets: Map<string, Socket>;\n hostedPtyRegistry: HostedPtyRegistry;\n agentStatusRegistry: AgentStatusRegistry;\n // 同步终止路径必须广播 session list,否则 web 看到幽灵 row。hosted PTY 终止异步走\n // child.onExit → onSessionClosed → cleanupSessionResources 内部已广播,因此 hosted\n // 路径不在此处调用。所有同步路径(detach_local_terminal / terminate_json_worker)\n // 由本函数收口调用,调用方不必手动补。\n broadcastSessionList: () => void;\n}\n\nexport function terminateSessionByOwnership(\n deps: TerminateSessionDeps,\n sessionId: string,\n): { success: boolean; action: SessionTerminationAction } {\n const session = deps.sessionManager.getSession(sessionId);\n\n if (session?.mode === \"pty\" && session.ptyOwner === \"local-terminal\") {\n const terminalSocket = deps.terminalSockets.get(sessionId);\n if (terminalSocket?.writable) {\n terminalSocket.write(serializeIpc({ type: \"pty_detach\", sessionId }));\n }\n deps.terminalSockets.delete(sessionId);\n const result = deps.sessionManager.terminateSession(sessionId, {\n preserveProviderHooks: true,\n });\n deps.controlHandlers.cleanup(sessionId);\n deps.agentStatusRegistry.delete(sessionId);\n deps.broadcastSessionList();\n serviceLogger.info(\n { sessionId, success: result.success },\n \"Local terminal session detached from remote view\",\n );\n return { success: result.success, action: \"detach_local_terminal\" };\n }\n\n if (session?.mode === \"pty\" && session.ptyOwner === \"proxy-hosted\") {\n const success = deps.hostedPtyRegistry.terminate(sessionId);\n serviceLogger.info({ sessionId, success }, \"Hosted PTY termination requested\");\n return { success, action: \"terminate_hosted_pty\" };\n }\n\n if (session?.mode === \"json\") {\n deps.workerRegistry.send(sessionId, { type: \"worker_stop\" });\n deps.workerRegistry.delete(sessionId);\n const result = deps.sessionManager.terminateSession(sessionId);\n deps.controlHandlers.cleanup(sessionId);\n deps.agentStatusRegistry.delete(sessionId);\n deps.broadcastSessionList();\n serviceLogger.info({ sessionId, success: result.success }, \"JSON worker session terminated\");\n return { success: result.success, action: \"terminate_json_worker\" };\n }\n\n const hostedTerminated = deps.hostedPtyRegistry.terminate(sessionId);\n if (hostedTerminated) {\n return { success: true, action: \"terminate_hosted_pty\" };\n }\n return { success: false, action: \"not_found\" };\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { nanoid } from \"nanoid\";\nimport { ControlErrorCode } from \"@dev-anywhere/shared\";\n\n// 落系统临时目录避免污染 user repo / 触发 .gitignore: CLI agent 看到绝对路径不受 cwd\n// 内 ignore 规则限制。文件名平铺单层、6 位随机后缀, 不带 sessionId / 时间戳, 控制 mention\n// 长度 (用户在 prompt 里看到 @<path> 越短越好)。\nconst DEFAULT_DATA_DIR = join(tmpdir(), \"dev-anywhere\");\nconst MAX_CLIPBOARD_IMAGE_BYTES = 10 * 1024 * 1024;\nconst MAX_CLIPBOARD_IMAGE_BASE64_LENGTH = Math.ceil(MAX_CLIPBOARD_IMAGE_BYTES / 3) * 4;\nconst IMAGE_EXTENSIONS: ReadonlyMap<string, string> = new Map([\n [\"image/png\", \"png\"],\n [\"image/jpeg\", \"jpg\"],\n [\"image/webp\", \"webp\"],\n [\"image/gif\", \"gif\"],\n] as const);\n\nexport type ClipboardImageUploadRequest = {\n sessionId: string;\n mimeType: string;\n dataBase64: string;\n fileName?: string;\n};\n\ntype ClipboardImageUploadResult = {\n success: boolean;\n // 失败时不填,避免空字符串通过 schema 的 z.string().optional() 校验。\n path?: string;\n error?: string;\n errorCode?: (typeof ControlErrorCode)[keyof typeof ControlErrorCode];\n};\n\ntype ClipboardImageUploadOptions = {\n dataDir?: string;\n randomSuffix?: () => string;\n};\n\nfunction normalizeBase64(input: string): string {\n return input.replace(/^data:[^;]+;base64,/i, \"\").replace(/\\s/g, \"\");\n}\n\nfunction decodeBase64Image(dataBase64: string): Buffer {\n const normalized = normalizeBase64(dataBase64);\n if (normalized.length > MAX_CLIPBOARD_IMAGE_BASE64_LENGTH) {\n throw new Error(\"图片超过 10MB 限制\");\n }\n if (!normalized || !/^[A-Za-z0-9+/]*={0,2}$/.test(normalized)) {\n throw new Error(\"图片数据不是有效的 base64\");\n }\n const buffer = Buffer.from(normalized, \"base64\");\n if (buffer.length === 0) throw new Error(\"图片数据为空\");\n if (buffer.length > MAX_CLIPBOARD_IMAGE_BYTES) {\n throw new Error(\"图片超过 10MB 限制\");\n }\n return buffer;\n}\n\nexport function saveClipboardImageUpload(\n request: ClipboardImageUploadRequest,\n options: ClipboardImageUploadOptions = {},\n): ClipboardImageUploadResult {\n const extension = IMAGE_EXTENSIONS.get(request.mimeType);\n if (!extension) {\n return {\n success: false,\n error: \"不支持这种图片格式\",\n errorCode: ControlErrorCode.UNKNOWN,\n };\n }\n\n try {\n const buffer = decodeBase64Image(request.dataBase64);\n const suffix = options.randomSuffix?.() ?? nanoid(6);\n const fileName = `paste-${suffix}.${extension}`;\n const dataDir = options.dataDir ?? DEFAULT_DATA_DIR;\n const path = join(dataDir, fileName);\n\n mkdirSync(dataDir, { recursive: true });\n writeFileSync(path, buffer, { mode: 0o600 });\n return { success: true, path };\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? err.message : String(err),\n errorCode: ControlErrorCode.UNKNOWN,\n };\n }\n}\n","import { readFileSync, realpathSync, statSync } from \"node:fs\";\nimport { extname, isAbsolute, resolve } from \"node:path\";\nimport { ControlErrorCode } from \"@dev-anywhere/shared\";\nimport type { ControlErrorCode as ControlErrorCodeType } from \"@dev-anywhere/shared\";\nimport { classifyPathError } from \"./path-errors.js\";\n\n// 单租户场景下不做白名单, 由 size cap 兜底; 100MB 已经远超日常 log/diff/截图。\nconst MAX_FILE_DOWNLOAD_BYTES = 100 * 1024 * 1024;\n\ntype FileDownloadRequest = {\n sessionId: string;\n path: string;\n};\n\nexport type FileDownloadResult = {\n success: boolean;\n sessionId: string;\n path: string;\n mimeType?: string;\n dataBase64?: string;\n size?: number;\n error?: string;\n errorCode?: ControlErrorCodeType;\n};\n\ntype FileDownloadOptions = {\n cwd: string;\n maxBytes?: number;\n};\n\nconst EXT_MIME_MAP: Record<string, string> = {\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".svg\": \"image/svg+xml\",\n \".bmp\": \"image/bmp\",\n \".pdf\": \"application/pdf\",\n \".json\": \"application/json\",\n \".xml\": \"application/xml\",\n \".html\": \"text/html\",\n \".htm\": \"text/html\",\n \".txt\": \"text/plain\",\n \".md\": \"text/markdown\",\n \".log\": \"text/plain\",\n \".csv\": \"text/csv\",\n \".js\": \"application/javascript\",\n \".mjs\": \"application/javascript\",\n \".ts\": \"application/typescript\",\n \".tsx\": \"application/typescript\",\n \".zip\": \"application/zip\",\n \".tar\": \"application/x-tar\",\n \".gz\": \"application/gzip\",\n \".mp4\": \"video/mp4\",\n \".mp3\": \"audio/mpeg\",\n \".wav\": \"audio/wav\",\n};\n\nfunction guessMimeType(filePath: string): string {\n const ext = extname(filePath).toLowerCase();\n return EXT_MIME_MAP[ext] ?? \"application/octet-stream\";\n}\n\nfunction resolveDownloadPath(rawPath: string, cwd: string): string {\n const candidate = isAbsolute(rawPath) ? resolve(rawPath) : resolve(cwd, rawPath);\n return realpathSync(candidate);\n}\n\nfunction errorCode(err: unknown): ControlErrorCodeType {\n if (\n err instanceof Error &&\n \"errorCode\" in err &&\n typeof (err as { errorCode?: unknown }).errorCode === \"string\"\n ) {\n return (err as { errorCode: ControlErrorCodeType }).errorCode;\n }\n return classifyPathError(err);\n}\n\nexport function loadFileDownload(\n request: FileDownloadRequest,\n options: FileDownloadOptions,\n): FileDownloadResult {\n try {\n const resolvedPath = resolveDownloadPath(request.path, options.cwd);\n const stat = statSync(resolvedPath);\n if (!stat.isFile()) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: \"路径不是普通文件\",\n errorCode: ControlErrorCode.INVALID_PATH,\n };\n }\n const maxBytes = options.maxBytes ?? MAX_FILE_DOWNLOAD_BYTES;\n if (stat.size > maxBytes) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: `文件超过 ${Math.round(maxBytes / 1024 / 1024)}MB 限制`,\n errorCode: ControlErrorCode.UNKNOWN,\n };\n }\n\n const buffer = readFileSync(resolvedPath);\n return {\n success: true,\n sessionId: request.sessionId,\n path: request.path,\n mimeType: guessMimeType(resolvedPath),\n dataBase64: buffer.toString(\"base64\"),\n size: buffer.length,\n };\n } catch (err) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: err instanceof Error ? err.message : String(err),\n errorCode: errorCode(err),\n };\n }\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { extname, join } from \"node:path\";\nimport { nanoid } from \"nanoid\";\nimport { ControlErrorCode } from \"@dev-anywhere/shared\";\n\n// 落系统临时目录避免污染 user repo / 触发 .gitignore: CLI agent 看到绝对路径不受 cwd\n// 内 ignore 规则限制。文件名平铺单层 + 6 位随机后缀, 控制 mention 长度。\n// 用户原 fileName 只取扩展, 不保留主干 (中文 / 长名 / 空格都会让 mention 变长)。\nconst DEFAULT_DATA_DIR = join(tmpdir(), \"dev-anywhere\");\nconst MAX_FILE_UPLOAD_BYTES = 100 * 1024 * 1024;\nconst MAX_FILE_UPLOAD_BASE64_LENGTH = Math.ceil(MAX_FILE_UPLOAD_BYTES / 3) * 4;\nconst SAFE_EXT_RE = /^[A-Za-z0-9]{1,6}$/;\n\nexport type FileUploadRequest = {\n sessionId: string;\n mimeType: string;\n dataBase64: string;\n fileName: string;\n};\n\nexport type FileUploadResult = {\n success: boolean;\n // 失败时不填,避免空字符串通过 schema 的 z.string().optional() 校验。\n path?: string;\n error?: string;\n errorCode?: (typeof ControlErrorCode)[keyof typeof ControlErrorCode];\n};\n\ntype FileUploadOptions = {\n dataDir?: string;\n randomSuffix?: () => string;\n};\n\nfunction normalizeBase64(input: string): string {\n return input.replace(/^data:[^;]+;base64,/i, \"\").replace(/\\s/g, \"\");\n}\n\nfunction decodeBase64File(dataBase64: string): Buffer {\n const normalized = normalizeBase64(dataBase64);\n if (normalized.length > MAX_FILE_UPLOAD_BASE64_LENGTH) {\n throw new Error(\"文件超过 100MB 限制\");\n }\n if (!normalized || !/^[A-Za-z0-9+/]*={0,2}$/.test(normalized)) {\n throw new Error(\"文件数据不是有效的 base64\");\n }\n const buffer = Buffer.from(normalized, \"base64\");\n if (buffer.length === 0) throw new Error(\"文件数据为空\");\n if (buffer.length > MAX_FILE_UPLOAD_BYTES) {\n throw new Error(\"文件超过 100MB 限制\");\n }\n return buffer;\n}\n\nfunction safeExtension(fileName: string): string {\n // 单段扩展, 多扩展 (.tar.gz / .min.js) 只保留最后一段; agent 看 mimeType 即可,\n // 路径里只需要保留类型 hint。非 ASCII / 含路径分隔符等的扩展直接丢。\n const raw = extname(fileName).slice(1).toLowerCase();\n return SAFE_EXT_RE.test(raw) ? `.${raw}` : \"\";\n}\n\nexport async function saveFileUpload(\n request: FileUploadRequest,\n options: FileUploadOptions = {},\n): Promise<FileUploadResult> {\n try {\n const buffer = decodeBase64File(request.dataBase64);\n const suffix = options.randomSuffix?.() ?? nanoid(6);\n const fileName = `up-${suffix}${safeExtension(request.fileName)}`;\n const dataDir = options.dataDir ?? DEFAULT_DATA_DIR;\n const path = join(dataDir, fileName);\n\n mkdirSync(dataDir, { recursive: true });\n writeFileSync(path, buffer, { mode: 0o600 });\n return { success: true, path };\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? err.message : String(err),\n errorCode: ControlErrorCode.UNKNOWN,\n };\n }\n}\n","import { readFileSync, realpathSync, statSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport { ControlErrorCode } from \"@dev-anywhere/shared\";\nimport type { ControlErrorCode as ControlErrorCodeType } from \"@dev-anywhere/shared\";\nimport { classifyPathError } from \"./path-errors.js\";\n\nconst MAX_IMAGE_PREVIEW_BYTES = 10 * 1024 * 1024;\n\ntype ImagePreviewRequest = {\n sessionId: string;\n path: string;\n};\n\ntype ImagePreviewResult = {\n success: boolean;\n sessionId: string;\n path: string;\n mimeType?: \"image/png\" | \"image/jpeg\" | \"image/webp\" | \"image/gif\";\n dataBase64?: string;\n size?: number;\n error?: string;\n errorCode?: ControlErrorCodeType;\n};\n\ntype ImagePreviewOptions = {\n cwd: string;\n tmpDir?: string;\n previewRoots?: string[];\n maxBytes?: number;\n};\n\nfunction isInsideRoot(realFilePath: string, realRootPath: string): boolean {\n const rel = relative(realRootPath, realFilePath);\n return rel === \"\" || (!rel.startsWith(\"..\") && !isAbsolute(rel));\n}\n\nfunction allowedRoots(options: ImagePreviewOptions): string[] {\n return [options.cwd, options.tmpDir ?? tmpdir(), ...(options.previewRoots ?? [])]\n .map((root) => root.trim())\n .filter(Boolean)\n .flatMap((root) => {\n try {\n return [realpathSync(root)];\n } catch {\n return [];\n }\n });\n}\n\nfunction resolvePreviewPath(rawPath: string, options: ImagePreviewOptions): string {\n const candidate = isAbsolute(rawPath) ? resolve(rawPath) : resolve(options.cwd, rawPath);\n const realCandidate = realpathSync(candidate);\n if (!allowedRoots(options).some((root) => isInsideRoot(realCandidate, root))) {\n throw Object.assign(new Error(\"图片路径不在允许预览的目录内\"), {\n errorCode: ControlErrorCode.INVALID_PATH,\n });\n }\n return realCandidate;\n}\n\nfunction detectImageMime(buffer: Buffer): ImagePreviewResult[\"mimeType\"] | undefined {\n if (\n buffer.length >= 8 &&\n buffer[0] === 0x89 &&\n buffer[1] === 0x50 &&\n buffer[2] === 0x4e &&\n buffer[3] === 0x47 &&\n buffer[4] === 0x0d &&\n buffer[5] === 0x0a &&\n buffer[6] === 0x1a &&\n buffer[7] === 0x0a\n ) {\n return \"image/png\";\n }\n if (buffer.length >= 3 && buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) {\n return \"image/jpeg\";\n }\n if (\n buffer.length >= 12 &&\n buffer.subarray(0, 4).toString(\"ascii\") === \"RIFF\" &&\n buffer.subarray(8, 12).toString(\"ascii\") === \"WEBP\"\n ) {\n return \"image/webp\";\n }\n if (\n buffer.length >= 6 &&\n (buffer.subarray(0, 6).toString(\"ascii\") === \"GIF87a\" ||\n buffer.subarray(0, 6).toString(\"ascii\") === \"GIF89a\")\n ) {\n return \"image/gif\";\n }\n return undefined;\n}\n\nfunction errorCode(err: unknown): ControlErrorCodeType {\n if (\n err instanceof Error &&\n \"errorCode\" in err &&\n typeof (err as { errorCode?: unknown }).errorCode === \"string\"\n ) {\n return (err as { errorCode: ControlErrorCodeType }).errorCode;\n }\n return classifyPathError(err);\n}\n\nexport function loadImagePreview(\n request: ImagePreviewRequest,\n options: ImagePreviewOptions,\n): ImagePreviewResult {\n try {\n const resolvedPath = resolvePreviewPath(request.path, options);\n const stat = statSync(resolvedPath);\n if (!stat.isFile()) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: \"路径不是图片文件\",\n errorCode: ControlErrorCode.INVALID_PATH,\n };\n }\n const maxBytes = options.maxBytes ?? MAX_IMAGE_PREVIEW_BYTES;\n if (stat.size > maxBytes) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: \"图片超过 10MB 限制\",\n errorCode: ControlErrorCode.UNKNOWN,\n };\n }\n\n const buffer = readFileSync(resolvedPath);\n const mimeType = detectImageMime(buffer);\n if (!mimeType) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: \"不支持这种图片格式\",\n errorCode: ControlErrorCode.UNKNOWN,\n };\n }\n\n return {\n success: true,\n sessionId: request.sessionId,\n path: request.path,\n mimeType,\n dataBase64: buffer.toString(\"base64\"),\n size: buffer.length,\n };\n } catch (err) {\n return {\n success: false,\n sessionId: request.sessionId,\n path: request.path,\n error: err instanceof Error ? err.message : String(err),\n errorCode: errorCode(err),\n };\n }\n}\n","import { serializeIpc } from \"../ipc/ipc-protocol.js\";\n\nexport function serializeRawPtyInput(sessionId: string, data: string): string {\n return serializeIpc({ type: \"pty_input\", sessionId, data });\n}\n","import type { Socket } from \"node:net\";\nimport {\n ControlErrorCode,\n MessageEnvelopeSchema,\n serializeControl,\n type ControlMessage,\n type Envelope,\n} from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { saveClipboardImageUpload } from \"./clipboard-image-upload.js\";\nimport { loadFileDownload } from \"./file-download.js\";\nimport { saveFileUpload } from \"./file-upload.js\";\nimport { loadImagePreview } from \"./image-preview.js\";\nimport { serializeRawPtyInput } from \"./pty-input.js\";\nimport type { HostedPtyRegistry } from \"./hosted-pty-registry.js\";\nimport type { JsonObserver } from \"./json-observer.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport type { WorkerRegistry } from \"./worker-registry.js\";\n\ninterface RelayInputHandlersDeps {\n sessionManager: SessionManager;\n workerRegistry: WorkerRegistry;\n relayConnection: RelayConnection;\n terminalSockets: Map<string, Socket>;\n hostedPtyRegistry: HostedPtyRegistry;\n jsonObserver: JsonObserver;\n previewRoots?: string[];\n}\n\nexport class RelayInputHandlers {\n constructor(private readonly deps: RelayInputHandlersDeps) {}\n\n onUserInput(msg: Envelope<\"user_input\">): void {\n const { sessionId } = msg;\n if (!sessionId) return;\n\n const session = this.deps.sessionManager.getSession(sessionId);\n if (!session) {\n serviceLogger.warn({ sessionId }, \"Remote input dropped: session not found\");\n return;\n }\n\n const text = msg.payload.text;\n\n if (session.mode === \"json\") {\n // 必须先 send 成功再 onTurnStart:onTurnStart 把 session 推到 WORKING,但 send 失败时\n // 没有 onTurnResult / onChannelBroken 回到 IDLE,session 会卡 WORKING 直到 60s reaper。\n const sent = this.deps.workerRegistry.send(sessionId, {\n type: \"worker_input\",\n content: text,\n });\n if (!sent) {\n serviceLogger.warn({ sessionId }, \"Remote input dropped: JSON worker socket not available\");\n return;\n }\n this.deps.jsonObserver.onTurnStart(sessionId);\n const messageId =\n msg.payload.messageId && msg.payload.messageId.length > 0\n ? msg.payload.messageId\n : `${sessionId}-user-${msg.timestamp}`;\n this.deps.relayConnection.sendEnvelope(\n MessageEnvelopeSchema.parse({\n type: \"user_input\",\n sessionId,\n seq: msg.seq,\n timestamp: msg.timestamp,\n source: \"proxy\",\n version: msg.version,\n payload: { text, messageId },\n }),\n );\n serviceLogger.info({ sessionId }, \"Remote input forwarded to JSON worker\");\n return;\n }\n\n serviceLogger.warn(\n { sessionId, mode: session.mode },\n \"Remote batch input dropped: PTY sessions require remote_input_raw\",\n );\n }\n\n onRemoteInputRaw(msg: ControlMessage<\"remote_input_raw\">): void {\n const { sessionId, data } = msg;\n if (!sessionId || data === undefined) return;\n\n const ts = this.deps.terminalSockets.get(sessionId);\n if (!ts?.writable && this.deps.hostedPtyRegistry.write(sessionId, data)) {\n serviceLogger.info(\n { sessionId, bytes: data.length },\n \"Raw PTY input forwarded to hosted PTY\",\n );\n return;\n }\n if (!ts?.writable) {\n serviceLogger.warn({ sessionId }, \"Raw PTY input dropped: terminal socket unavailable\");\n return;\n }\n ts.write(serializeRawPtyInput(sessionId, data));\n serviceLogger.info({ sessionId, bytes: data.length }, \"Raw PTY input forwarded\");\n }\n\n onClipboardImageUpload(msg: ControlMessage<\"clipboard_image_upload\">): void {\n const { sessionId, requestId } = msg;\n if (!sessionId) return;\n\n if (!this.deps.sessionManager.getSession(sessionId)) {\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"clipboard_image_upload_response\",\n requestId,\n sessionId,\n success: false,\n error: \"会话不存在\",\n errorCode: ControlErrorCode.SESSION_NOT_FOUND,\n }),\n );\n serviceLogger.warn({ sessionId }, \"Clipboard image upload rejected: session not found\");\n return;\n }\n\n const result = saveClipboardImageUpload({\n sessionId,\n mimeType: msg.mimeType,\n dataBase64: msg.dataBase64,\n fileName: msg.fileName,\n });\n\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"clipboard_image_upload_response\",\n requestId,\n sessionId,\n ...result,\n }),\n );\n serviceLogger.info({ sessionId, success: result.success }, \"Clipboard image upload handled\");\n }\n\n onImagePreviewRequest(msg: ControlMessage<\"image_preview_request\">): void {\n const { sessionId, requestId, path } = msg;\n if (!sessionId || !path) return;\n\n const session = this.deps.sessionManager.getSession(sessionId);\n if (!session) {\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"image_preview_response\",\n requestId,\n sessionId,\n success: false,\n path,\n error: \"会话不存在\",\n errorCode: ControlErrorCode.SESSION_NOT_FOUND,\n }),\n );\n serviceLogger.warn({ sessionId }, \"Image preview rejected: session not found\");\n return;\n }\n\n const result = loadImagePreview(\n { sessionId, path },\n {\n cwd: session.cwd,\n previewRoots: this.deps.previewRoots,\n },\n );\n\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"image_preview_response\",\n requestId,\n ...result,\n }),\n );\n if (result.success) {\n serviceLogger.info({ sessionId, path, size: result.size }, \"Image preview handled\");\n } else {\n serviceLogger.warn(\n { sessionId, path, errorCode: result.errorCode, error: result.error },\n \"Image preview failed\",\n );\n }\n }\n\n onFileDownloadRequest(msg: ControlMessage<\"file_download_request\">): void {\n const { sessionId, requestId, path } = msg;\n if (!sessionId || !path) return;\n\n const session = this.deps.sessionManager.getSession(sessionId);\n if (!session) {\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"file_download_response\",\n requestId,\n sessionId,\n success: false,\n path,\n error: \"会话不存在\",\n errorCode: ControlErrorCode.SESSION_NOT_FOUND,\n }),\n );\n serviceLogger.warn({ sessionId }, \"File download rejected: session not found\");\n return;\n }\n\n const result = loadFileDownload({ sessionId, path }, { cwd: session.cwd });\n\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"file_download_response\",\n requestId,\n ...result,\n }),\n );\n if (result.success) {\n serviceLogger.info({ sessionId, path, size: result.size }, \"File download handled\");\n } else {\n // 失败必带 errorCode + error, 否则只看 success=false 不知道是 ENOENT / EACCES / 超大 / 不是文件。\n serviceLogger.warn(\n { sessionId, path, errorCode: result.errorCode, error: result.error },\n \"File download failed\",\n );\n }\n }\n\n async onFileUploadRequest(msg: ControlMessage<\"file_upload_request\">): Promise<void> {\n const { sessionId, requestId, mimeType, dataBase64, fileName } = msg;\n if (!sessionId) return;\n\n if (!this.deps.sessionManager.getSession(sessionId)) {\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"file_upload_response\",\n requestId,\n sessionId,\n success: false,\n error: \"会话不存在\",\n errorCode: ControlErrorCode.SESSION_NOT_FOUND,\n }),\n );\n serviceLogger.warn({ sessionId }, \"File upload rejected: session not found\");\n return;\n }\n\n const result = await saveFileUpload({ sessionId, mimeType, dataBase64, fileName });\n\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"file_upload_response\",\n requestId,\n sessionId,\n ...result,\n }),\n );\n if (result.success) {\n serviceLogger.info({ sessionId, fileName, path: result.path }, \"File upload handled\");\n } else {\n serviceLogger.warn(\n { sessionId, fileName, errorCode: result.errorCode, error: result.error },\n \"File upload failed\",\n );\n }\n }\n}\n","import { serializeControl, type ControlMessage } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport type { PermissionBroker } from \"./permission-broker.js\";\nimport type { RelaySend } from \"./relay-router-types.js\";\nimport { readSessionMessagesPage } from \"./session-history.js\";\nimport type { SessionManager } from \"./session-manager.js\";\n\ninterface RelayHistoryHandlersDeps {\n relaySend: RelaySend;\n sessionManager: SessionManager;\n permissionBroker: PermissionBroker;\n}\n\nexport class RelayHistoryHandlers {\n constructor(private readonly deps: RelayHistoryHandlersDeps) {}\n\n onSessionMessagesRequest(msg: ControlMessage<\"session_messages_request\">): void {\n const { sessionId: sid, requestId, before, limit } = msg;\n if (!sid) return;\n\n const session = this.deps.sessionManager.getSession(sid);\n if (session?.claudeSessionId) {\n readSessionMessagesPage(session.claudeSessionId, { before, limit })\n .then((page) => {\n this.deps.relaySend(\n serializeControl({\n type: \"session_history_messages\",\n requestId,\n sessionId: sid,\n ...(before !== undefined ? { before } : {}),\n messages: page.messages,\n hasMore: page.hasMore,\n ...(page.nextBefore !== undefined ? { nextBefore: page.nextBefore } : {}),\n }),\n );\n serviceLogger.info(\n {\n sessionId: sid,\n before,\n hasMore: page.hasMore,\n nextBefore: page.nextBefore,\n messageCount: page.messages.length,\n },\n \"History message page sent on request\",\n );\n })\n .catch((err) => {\n serviceLogger.warn(\n { sessionId: sid, error: String(err) },\n \"Failed to read session history page on request\",\n );\n this.deps.relaySend(\n serializeControl({\n type: \"session_history_messages\",\n requestId,\n sessionId: sid,\n ...(before !== undefined ? { before } : {}),\n messages: [],\n hasMore: false,\n }),\n );\n });\n } else {\n this.deps.relaySend(\n serializeControl({\n type: \"session_history_messages\",\n requestId,\n sessionId: sid,\n ...(before !== undefined ? { before } : {}),\n messages: [],\n hasMore: false,\n }),\n );\n }\n\n const approvals = this.deps.permissionBroker.listSession(sid).map((approval) => ({\n requestId: approval.requestId,\n toolName: approval.toolName,\n input: approval.input,\n }));\n this.deps.relaySend(\n serializeControl({ type: \"pending_approvals_push\", sessionId: sid, approvals }),\n );\n serviceLogger.info({ sessionId: sid, count: approvals.length }, \"Pending approvals pushed\");\n }\n}\n","import { serializeControl, type ControlMessage } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport type { HookEventRouter } from \"./hook-event-router.js\";\nimport type { PermissionBroker } from \"./permission-broker.js\";\nimport type { RelaySend } from \"./relay-router-types.js\";\nimport type { WorkerRegistry } from \"./worker-registry.js\";\n\ninterface RelayPermissionHandlersDeps {\n relaySend: RelaySend;\n permissionBroker: PermissionBroker;\n hookEventRouter: HookEventRouter;\n workerRegistry: WorkerRegistry;\n}\n\nexport class RelayPermissionHandlers {\n constructor(private readonly deps: RelayPermissionHandlersDeps) {}\n\n onToolApprove(msg: ControlMessage<\"tool_approve\">): void {\n const { sessionId, payload } = msg;\n if (!sessionId || !payload?.toolId) return;\n\n const pending = this.deps.permissionBroker.get(payload.toolId);\n if (!pending) {\n this.pushPermissionDecisionResult(\n sessionId,\n payload.toolId,\n \"allow\",\n false,\n \"Permission request is no longer pending.\",\n );\n return;\n }\n if (!this.deps.permissionBroker.resolve(payload.toolId, { behavior: \"allow\" })) {\n this.pushPermissionDecisionResult(\n pending.sessionId,\n payload.toolId,\n \"allow\",\n false,\n \"Permission request is no longer pending.\",\n );\n return;\n }\n this.deps.hookEventRouter.onPermissionResolved(\n pending.sessionId,\n pending.provider,\n payload.toolId,\n \"allow\",\n { toolName: pending.toolName, toolInput: pending.input },\n );\n\n if (pending.source === \"worker\" && payload.whitelistTool) {\n const toolName = pending.toolName;\n if (toolName) {\n const whitelisted = this.deps.workerRegistry.send(pending.sessionId, {\n type: \"worker_whitelist_add\",\n toolName,\n });\n if (whitelisted) {\n serviceLogger.info(\n { sessionId: pending.sessionId, toolName },\n \"Tool added to session whitelist via relay\",\n );\n }\n }\n }\n this.pushPermissionDecisionResult(pending.sessionId, payload.toolId, \"allow\", true);\n serviceLogger.info(\n { sessionId, toolId: payload.toolId, whitelistTool: payload.whitelistTool },\n \"Tool approved via relay\",\n );\n }\n\n onToolDeny(msg: ControlMessage<\"tool_deny\">): void {\n const { sessionId, payload } = msg;\n if (!sessionId || !payload?.toolId) return;\n\n const reason = payload.reason ?? \"Denied by remote user\";\n const pending = this.deps.permissionBroker.get(payload.toolId);\n if (!pending) {\n this.pushPermissionDecisionResult(\n sessionId,\n payload.toolId,\n \"deny\",\n false,\n \"Permission request is no longer pending.\",\n );\n return;\n }\n if (\n !this.deps.permissionBroker.resolve(payload.toolId, {\n behavior: \"deny\",\n message: reason,\n })\n ) {\n this.pushPermissionDecisionResult(\n pending.sessionId,\n payload.toolId,\n \"deny\",\n false,\n \"Permission request is no longer pending.\",\n );\n return;\n }\n this.deps.hookEventRouter.onPermissionResolved(\n pending.sessionId,\n pending.provider,\n payload.toolId,\n \"deny\",\n { toolName: pending.toolName, toolInput: pending.input },\n );\n this.pushPermissionDecisionResult(pending.sessionId, payload.toolId, \"deny\", true, reason);\n serviceLogger.info({ sessionId, toolId: payload.toolId }, \"Tool denied via relay\");\n }\n\n onPermissionRequestDelivered(msg: ControlMessage<\"permission_request_delivered\">): void {\n const { sessionId: sid, requestId } = msg;\n if (!sid || !requestId) return;\n const marked = this.deps.permissionBroker.markDelivered(requestId);\n serviceLogger.info({ sessionId: sid, requestId, marked }, \"Permission request delivered\");\n }\n\n private pushPermissionDecisionResult(\n sessionId: string,\n requestId: string,\n outcome: \"allow\" | \"deny\",\n delivered: boolean,\n message?: string,\n ): void {\n this.deps.relaySend(\n serializeControl({\n type: \"permission_decision_result\",\n sessionId,\n requestId,\n outcome,\n delivered,\n ...(message ? { message } : {}),\n }),\n );\n }\n}\n","import { homedir } from \"node:os\";\nimport { accessSync, constants, statSync } from \"node:fs\";\nimport { ControlErrorCode, serializeControl, type ControlMessage } from \"@dev-anywhere/shared\";\nimport type { ControlMessageHandlers } from \"./handlers/control-messages.js\";\nimport type { RelaySend } from \"./relay-router-types.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { saveAgentCliPath } from \"../common/config.js\";\nimport { detectAgentCliStatus } from \"../providers/index.js\";\nimport type { ProviderId } from \"../providers/types.js\";\n\ninterface RelayResourceHandlersDeps {\n relaySend: RelaySend;\n controlHandlers: ControlMessageHandlers;\n sessionManager: SessionManager;\n getProviderEnv: () => NodeJS.ProcessEnv;\n getAgentCliSuggestions: () => Partial<Record<ProviderId, string[]>>;\n setAgentCliPath: (provider: ProviderId, path: string) => void;\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction validateExecutablePath(path: string): string {\n const normalized = path.trim();\n if (!normalized.startsWith(\"/\")) throw new Error(\"CLI 路径必须是绝对路径\");\n const stat = statSync(normalized);\n if (!stat.isFile()) throw new Error(\"CLI 路径不是可执行文件\");\n accessSync(normalized, constants.X_OK);\n return normalized;\n}\n\nexport class RelayResourceHandlers {\n constructor(private readonly deps: RelayResourceHandlersDeps) {}\n\n onProxyInfoRequest(msg: ControlMessage<\"proxy_info_request\">): void {\n this.deps.relaySend(\n serializeControl({\n type: \"proxy_info\",\n requestId: msg.requestId,\n homePath: homedir() || \"/\",\n agentCli: detectAgentCliStatus(this.deps.getProviderEnv(), {\n suggestions: this.deps.getAgentCliSuggestions(),\n }),\n }),\n );\n }\n\n onAgentCliConfigUpdate(msg: ControlMessage<\"agent_cli_config_update\">): void {\n const { requestId, provider } = msg;\n const rawPath = msg.path;\n\n if (provider !== \"claude\" && provider !== \"codex\") {\n this.deps.relaySend(\n serializeControl({\n type: \"agent_cli_config_update_response\",\n requestId,\n provider: \"claude\",\n errorCode: ControlErrorCode.PROVIDER_UNSUPPORTED,\n error: \"Unsupported Agent CLI.\",\n }),\n );\n return;\n }\n\n try {\n const path = validateExecutablePath(rawPath ?? \"\");\n saveAgentCliPath(provider, path);\n this.deps.setAgentCliPath(provider, path);\n const agentCli = detectAgentCliStatus(this.deps.getProviderEnv(), {\n suggestions: this.deps.getAgentCliSuggestions(),\n });\n this.deps.relaySend(\n serializeControl({\n type: \"agent_cli_config_update_response\",\n requestId,\n provider,\n agentCli,\n }),\n );\n serviceLogger.info({ provider, path }, \"Agent CLI path updated\");\n } catch (err) {\n const error = errorMessage(err);\n this.deps.relaySend(\n serializeControl({\n type: \"agent_cli_config_update_response\",\n requestId,\n provider,\n errorCode: ControlErrorCode.INVALID_PATH,\n error,\n }),\n );\n serviceLogger.warn({ provider, path: rawPath, error }, \"Agent CLI path update rejected\");\n }\n }\n\n onDirListRequest(msg: ControlMessage<\"dir_list_request\">): void {\n this.deps.controlHandlers.handleDirListRequest({\n path: msg.path ?? \"\",\n requestId: msg.requestId,\n });\n }\n\n onDirCreateRequest(msg: ControlMessage<\"dir_create_request\">): void {\n this.deps.controlHandlers.handleDirCreateRequest({\n path: msg.path ?? \"\",\n requestId: msg.requestId,\n });\n }\n\n onSessionResourcesRequest(msg: ControlMessage<\"session_resources_request\">): void {\n const sid = msg.sessionId;\n if (!sid) return;\n\n const session = this.deps.sessionManager.getSession(sid);\n if (!session?.cwd) {\n serviceLogger.warn({ sessionId: sid }, \"Session resources request: no cwd available\");\n this.deps.relaySend(\n serializeControl({\n type: \"session_resources_response\",\n requestId: msg.requestId,\n sessionId: sid,\n commands: [],\n groups: [],\n errorCode: ControlErrorCode.SESSION_NOT_FOUND,\n error: \"Session not found or cwd unavailable\",\n }),\n );\n return;\n }\n this.deps.controlHandlers.handleSessionResourcesRequest({\n sessionId: sid,\n requestId: msg.requestId,\n workDir: session.cwd,\n });\n serviceLogger.info({ sessionId: sid, cwd: session.cwd }, \"Session resources requested\");\n }\n}\n","import { rmSync, statSync } from \"node:fs\";\nimport { isAbsolute } from \"node:path\";\nimport { nanoid } from \"nanoid\";\nimport { ControlErrorCode, serializeControl, type ControlMessage } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { sessionPaths, tildify } from \"../common/paths.js\";\nimport type { ProviderHookContext, ProviderId } from \"../providers/index.js\";\nimport type { ControlMessageHandlers } from \"./handlers/control-messages.js\";\nimport { buildHostedPtyArgs, type HostedPtyRegistry } from \"./hosted-pty-registry.js\";\nimport type { PermissionBroker } from \"./permission-broker.js\";\nimport type { RelaySend } from \"./relay-router-types.js\";\nimport type { SessionInfo, SessionManager } from \"./session-manager.js\";\nimport { readSessionMessagesPage } from \"./session-history.js\";\nimport type { WorkerRegistry } from \"./worker-registry.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\nimport { classifyPathError } from \"./path-errors.js\";\n\ninterface RelaySessionCreateHandlerDeps {\n relaySend: RelaySend;\n workerRegistry: WorkerRegistry;\n sessionManager: SessionManager;\n hostedPtyRegistry: HostedPtyRegistry;\n controlHandlers: ControlMessageHandlers;\n permissionBroker: PermissionBroker;\n agentStatusRegistry: AgentStatusRegistry;\n getProviderEnv: () => NodeJS.ProcessEnv;\n createHookContext: (\n sessionId: string,\n provider: ProviderHookContext[\"provider\"],\n ) => ProviderHookContext;\n cleanupHookContext: (sessionId: string) => void;\n broadcastSessionSync: (session: SessionInfo) => void;\n broadcastSessionList: () => void;\n}\n\ninterface SessionCwdValidationError {\n message: string;\n code: ControlErrorCode;\n}\n\nfunction validateSessionCwd(cwd: unknown): SessionCwdValidationError | null {\n if (typeof cwd !== \"string\" || !cwd.trim()) {\n return { message: \"请输入工作目录\", code: ControlErrorCode.INVALID_PATH };\n }\n const trimmed = cwd.trim();\n if (!isAbsolute(trimmed)) {\n return { message: \"工作目录必须是绝对路径\", code: ControlErrorCode.INVALID_PATH };\n }\n try {\n const stat = statSync(trimmed);\n return stat.isDirectory()\n ? null\n : { message: \"工作目录不是目录\", code: ControlErrorCode.PATH_NOT_DIRECTORY };\n } catch (err) {\n return {\n message: `工作目录不存在或不可访问: ${trimmed}`,\n code: classifyPathError(err),\n };\n }\n}\n\nexport class RelaySessionCreateHandler {\n // 跟踪每个 pendingId 当前挂起的 retry timer。SIGTERM 抵达时 destroy() 会 clear\n // 这些 timer 并执行 cleanupPendingJsonSession,否则 worker 子进程在窗口期内可能成为孤儿\n // (setTimeout 回调命中时 workerRegistry 已经 destroyAll,但 worker 进程并未被 kill)。\n private readonly pendingTimers = new Map<string, NodeJS.Timeout>();\n\n constructor(private readonly deps: RelaySessionCreateHandlerDeps) {}\n\n destroy(): void {\n for (const [pendingId, timer] of this.pendingTimers) {\n clearTimeout(timer);\n this.cleanupPendingJsonSession(pendingId);\n }\n this.pendingTimers.clear();\n }\n\n onSessionCreate(msg: ControlMessage<\"session_create\">): void {\n const { requestId, cwd } = msg;\n const cwdError = validateSessionCwd(cwd);\n if (cwdError) {\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId,\n error: cwdError.message,\n errorCode: cwdError.code,\n }),\n );\n serviceLogger.warn({ cwd }, \"Session create rejected: invalid cwd\");\n return;\n }\n const sessionCwd = typeof cwd === \"string\" ? cwd.trim() : \"\";\n\n const provider = msg.provider;\n const mode = msg.mode ?? \"json\";\n const permissionMode = msg.permissionMode;\n if (mode === \"pty\") {\n this.createHostedPtySession(msg, sessionCwd, provider ?? \"claude\", permissionMode);\n return;\n }\n\n if (provider !== \"claude\") {\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId,\n errorCode: ControlErrorCode.PROVIDER_UNSUPPORTED,\n error:\n provider === \"codex\"\n ? \"Codex chat sessions are not supported yet; start a Codex terminal session instead.\"\n : \"Unsupported provider for JSON session.\",\n }),\n );\n serviceLogger.warn({ provider }, \"JSON session create rejected for unsupported provider\");\n return;\n }\n\n const resumeSessionId = msg.resumeSessionId;\n // streamDelta 不在 session_create 协议字段里:当前没有客户端发起 delta 模式,\n // 默认关闭即可。后续若要恢复增量推送,需先在 RelayControlSchema 加字段。\n const streamDelta = false;\n const name = tildify(sessionCwd);\n const pendingId = nanoid();\n const hook = this.deps.createHookContext(pendingId, provider);\n const workerPid = this.deps.workerRegistry.spawn(pendingId, {\n cwd: sessionCwd,\n resumeSessionId,\n permissionMode,\n streamDelta,\n hook,\n });\n\n const paths = sessionPaths(pendingId);\n let attempt = 0;\n const maxRetries = 20;\n const scheduleAttempt = (delayMs: number): void => {\n const timer = setTimeout(tryConnect, delayMs);\n this.pendingTimers.set(pendingId, timer);\n };\n const tryConnect = () => {\n this.pendingTimers.delete(pendingId);\n attempt++;\n this.deps.workerRegistry.connect(pendingId, paths.workerSock).then((sock) => {\n if (sock) {\n const session = this.deps.sessionManager.createSession(\n \"json\",\n sessionCwd,\n workerPid,\n name,\n pendingId,\n provider,\n );\n if (resumeSessionId) {\n this.deps.sessionManager.setClaudeSessionId(session.id, resumeSessionId);\n }\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId,\n sessionId: session.id,\n }),\n );\n if (resumeSessionId) {\n this.pushHistoryMessages(session.id, resumeSessionId);\n }\n serviceLogger.info(\n { sessionId: session.id, cwd: sessionCwd },\n \"JSON session created via relay\",\n );\n this.deps.controlHandlers.pushCommandList(session.id, sessionCwd);\n this.deps.broadcastSessionSync(session);\n this.deps.broadcastSessionList();\n } else if (attempt < maxRetries) {\n scheduleAttempt(Math.min(100 * attempt, 2000));\n } else {\n this.cleanupPendingJsonSession(pendingId);\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId,\n sessionId: pendingId,\n errorCode: ControlErrorCode.WORKER_START_FAILED,\n error: \"Worker failed to start\",\n }),\n );\n serviceLogger.error({ sessionId: pendingId }, \"Worker connection timeout via relay\");\n }\n });\n };\n scheduleAttempt(100);\n }\n\n private cleanupPendingJsonSession(sessionId: string): void {\n const killed = this.deps.workerRegistry.terminateProcess(sessionId);\n const paths = sessionPaths(sessionId);\n rmSync(paths.dir, { recursive: true, force: true });\n this.deps.cleanupHookContext(sessionId);\n this.deps.permissionBroker.cleanupSession(sessionId, \"Worker failed to start\");\n this.deps.agentStatusRegistry.delete(sessionId);\n serviceLogger.warn(\n { sessionId, killed },\n \"Cleaned up pending JSON session after startup failure\",\n );\n }\n\n private createHostedPtySession(\n msg: ControlMessage<\"session_create\">,\n cwd: string,\n provider: ProviderId,\n permissionMode?: string,\n ): void {\n if (provider !== \"claude\" && provider !== \"codex\") {\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId: msg.requestId,\n errorCode: ControlErrorCode.PROVIDER_UNSUPPORTED,\n error: \"Unsupported provider for PTY session.\",\n }),\n );\n return;\n }\n\n const resumeSessionId = msg.resumeSessionId;\n const pendingId = nanoid();\n const name = tildify(cwd);\n const hook = this.deps.createHookContext(pendingId, provider);\n try {\n const pid = this.deps.hostedPtyRegistry.start({\n sessionId: pendingId,\n provider,\n cwd,\n args: buildHostedPtyArgs(provider, resumeSessionId),\n permissionMode,\n hook,\n });\n const session = this.deps.sessionManager.createSession(\n \"pty\",\n cwd,\n pid,\n name,\n pendingId,\n provider,\n \"proxy-hosted\",\n );\n if (resumeSessionId && provider === \"claude\") {\n this.deps.sessionManager.setClaudeSessionId(session.id, resumeSessionId);\n }\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId: msg.requestId,\n sessionId: session.id,\n mode: \"pty\",\n provider,\n ptyOwner: \"proxy-hosted\",\n }),\n );\n this.deps.controlHandlers.pushCommandList(session.id, cwd);\n this.deps.controlHandlers.pushFileTree(session.id, cwd);\n this.deps.broadcastSessionSync(session);\n this.deps.broadcastSessionList();\n serviceLogger.info({ sessionId: session.id, provider, cwd }, \"Hosted PTY session created\");\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n this.deps.relaySend(\n serializeControl({\n type: \"session_create_response\",\n requestId: msg.requestId,\n errorCode: ControlErrorCode.PROCESS_START_FAILED,\n error,\n }),\n );\n const providerEnv = this.deps.getProviderEnv();\n serviceLogger.warn(\n {\n provider,\n cwd,\n error,\n claudeBin: providerEnv.CLAUDE_BIN,\n codexBin: providerEnv.CODEX_BIN,\n path: providerEnv.PATH,\n },\n \"Hosted PTY session create failed\",\n );\n }\n }\n\n private pushHistoryMessages(sessionId: string, resumeSessionId: string): void {\n readSessionMessagesPage(resumeSessionId)\n .then((page) => {\n if (page.messages.length === 0) return;\n this.deps.relaySend(\n serializeControl({\n type: \"session_history_messages\",\n sessionId,\n messages: page.messages,\n hasMore: page.hasMore,\n ...(page.nextBefore !== undefined ? { nextBefore: page.nextBefore } : {}),\n }),\n );\n serviceLogger.info(\n {\n sessionId,\n resumeSessionId,\n messageCount: page.messages.length,\n hasMore: page.hasMore,\n },\n \"History message page sent for resumed session\",\n );\n })\n .catch((err) => {\n serviceLogger.warn(\n { sessionId, error: String(err) },\n \"Failed to read session history page\",\n );\n });\n }\n}\n","import * as pty from \"node-pty\";\nimport type { IPty } from \"node-pty\";\nimport {\n SessionState,\n encodeBinaryFrame,\n serializeControl,\n type PtySemanticState,\n} from \"@dev-anywhere/shared\";\nimport pkg from \"@xterm/headless\";\nconst { Terminal: HeadlessTerminal } = pkg;\nimport { SerializeAddon } from \"@xterm/addon-serialize\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { extractOscSequences, extractOscSignals } from \"../common/osc-extractor.js\";\nimport { decidePtySemanticTransition } from \"../common/pty-semantic-machine.js\";\nimport {\n CLAUDE_PROVIDER,\n CODEX_PROVIDER,\n type ProviderAdapter,\n type ProviderHookContext,\n type ProviderId,\n} from \"../providers/index.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { SessionManager } from \"./session-manager.js\";\n\nconst DEFAULT_COLS = 80;\nconst DEFAULT_ROWS = 24;\nconst IDLE_CHECK_INTERVAL_MS = 3_000;\nconst IDLE_THRESHOLD_MS = 3_000;\n\nconst PROVIDERS: Record<ProviderId, ProviderAdapter> = {\n claude: CLAUDE_PROVIDER,\n codex: CODEX_PROVIDER,\n};\n\nconst HOSTED_PTY_TERM = \"xterm-256color\";\nconst HOSTED_PTY_COLORTERM = \"truecolor\";\n\ninterface HostedPtyRegistryDeps {\n sessionManager: SessionManager;\n relayConnection: RelayConnection;\n getProviderEnv: () => NodeJS.ProcessEnv;\n touchSessionActivity: (sessionId: string) => boolean;\n // PTY → Session FSM 的翻译副作用(changeSessionState、清理 interrupted approvals、推\n // agent status 等)由 bridge 收口;hosted 与 terminal-ipc 共用同一实现。\n applyPtyStateToSession: (sessionId: string, ptyState: PtySemanticState) => void;\n onSessionClosed: (sessionId: string) => void;\n}\n\ninterface HostedPtyStartOptions {\n sessionId: string;\n provider: ProviderId;\n cwd: string;\n args: string[];\n permissionMode?: string;\n hook: ProviderHookContext;\n cols?: number;\n rows?: number;\n}\n\ninterface HostedPtySession {\n child: IPty;\n terminal: InstanceType<typeof HeadlessTerminal>;\n serializeAddon: SerializeAddon;\n idleTimer: NodeJS.Timeout;\n lastOutputTime: number;\n currentState: PtySemanticState;\n outputSeq: number;\n}\n\nexport function buildHostedPtyArgs(provider: ProviderId, resumeSessionId?: string): string[] {\n if (!resumeSessionId) return [];\n return provider === \"codex\" ? [\"resume\", resumeSessionId] : [\"--resume\", resumeSessionId];\n}\n\nexport function normalizeHostedPtyEnv(env: NodeJS.ProcessEnv): Record<string, string> {\n const normalized: Record<string, string> = {};\n for (const [key, value] of Object.entries(env)) {\n if (value === undefined) continue;\n normalized[key] = value;\n }\n\n delete normalized.NO_COLOR;\n if (normalized.CLICOLOR === \"0\") {\n delete normalized.CLICOLOR;\n }\n\n normalized.TERM = HOSTED_PTY_TERM;\n normalized.COLORTERM = HOSTED_PTY_COLORTERM;\n normalized.CLICOLOR = \"1\";\n return normalized;\n}\n\nexport class HostedPtyRegistry {\n private sessions = new Map<string, HostedPtySession>();\n\n constructor(private readonly deps: HostedPtyRegistryDeps) {}\n\n start(options: HostedPtyStartOptions): number {\n const provider = PROVIDERS[options.provider];\n const cols = options.cols ?? DEFAULT_COLS;\n const rows = options.rows ?? DEFAULT_ROWS;\n const command = provider.buildTerminalCommand(\n { args: options.args, permissionMode: options.permissionMode, hook: options.hook },\n this.deps.getProviderEnv(),\n );\n const env = normalizeHostedPtyEnv(command.env);\n const child = pty.spawn(command.command, command.args, {\n name: HOSTED_PTY_TERM,\n cols,\n rows,\n cwd: options.cwd,\n env,\n });\n\n const terminal = new HeadlessTerminal({ cols, rows, scrollback: 5000, allowProposedApi: true });\n const serializeAddon = new SerializeAddon();\n terminal.loadAddon(serializeAddon);\n void import(\"@xterm/addon-unicode-graphemes\")\n .then(({ UnicodeGraphemesAddon }) => terminal.loadAddon(new UnicodeGraphemesAddon()))\n .catch((err) => {\n serviceLogger.warn(\n { sessionId: options.sessionId, error: String(err) },\n \"Unicode addon unavailable\",\n );\n });\n\n const hosted: HostedPtySession = {\n child,\n terminal,\n serializeAddon,\n idleTimer: setInterval(() => this.checkIdle(options.sessionId), IDLE_CHECK_INTERVAL_MS),\n lastOutputTime: 0,\n currentState: \"turn_complete\",\n outputSeq: 0,\n };\n this.sessions.set(options.sessionId, hosted);\n\n child.onData((data) => this.handleData(options.sessionId, data));\n child.onExit(({ exitCode, signal }) => {\n const code = signal ? 128 + signal : exitCode;\n serviceLogger.info({ sessionId: options.sessionId, code }, \"Hosted PTY exited\");\n this.close(options.sessionId, { kill: false, notify: true });\n });\n\n serviceLogger.info(\n {\n sessionId: options.sessionId,\n provider: options.provider,\n command: command.command,\n pid: child.pid,\n cwd: options.cwd,\n cols,\n rows,\n },\n \"Hosted PTY started\",\n );\n return child.pid;\n }\n\n has(sessionId: string): boolean {\n return this.sessions.has(sessionId);\n }\n\n write(sessionId: string, data: string): boolean {\n const hosted = this.sessions.get(sessionId);\n if (!hosted) return false;\n hosted.child.write(data);\n return true;\n }\n\n resize(sessionId: string, cols: number, rows: number): boolean {\n const hosted = this.sessions.get(sessionId);\n if (!hosted) return false;\n hosted.child.resize(cols, rows);\n hosted.terminal.resize(cols, rows);\n this.deps.relayConnection.sendRaw(\n serializeControl({ type: \"terminal_resize\", sessionId, cols, rows }),\n );\n serviceLogger.info({ sessionId, cols, rows }, \"Hosted PTY resized\");\n return true;\n }\n\n snapshot(sessionId: string, requestId?: string): boolean {\n const hosted = this.sessions.get(sessionId);\n if (!hosted) return false;\n const data = hosted.serializeAddon.serialize();\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"session_snapshot\",\n sessionId,\n cols: hosted.terminal.cols,\n rows: hosted.terminal.rows,\n data,\n outputSeq: hosted.outputSeq,\n ...(requestId !== undefined ? { requestId } : {}),\n }),\n );\n serviceLogger.info(\n { sessionId, cols: hosted.terminal.cols, rows: hosted.terminal.rows, bytes: data.length },\n \"Hosted PTY snapshot sent\",\n );\n return true;\n }\n\n terminate(sessionId: string): boolean {\n return this.close(sessionId, { kill: true, notify: true });\n }\n\n destroyAll(): void {\n for (const sessionId of Array.from(this.sessions.keys())) {\n this.close(sessionId, { kill: true, notify: false });\n }\n }\n\n private handleData(sessionId: string, data: string): void {\n const hosted = this.sessions.get(sessionId);\n if (!hosted) return;\n hosted.lastOutputTime = Date.now();\n hosted.outputSeq += 1;\n hosted.terminal.write(data);\n this.deps.touchSessionActivity(sessionId);\n this.sendBinary(sessionId, Buffer.from(data, \"utf-8\"), hosted.outputSeq);\n\n const oscSequences = extractOscSequences(data);\n const session = this.deps.sessionManager.getSession(sessionId);\n const signal = extractOscSignals(data, session?.provider);\n if (oscSequences.length > 0) {\n serviceLogger.debug(\n {\n sessionId,\n oscSequences,\n signal,\n },\n \"Hosted PTY OSC sequences parsed\",\n );\n }\n if (signal?.title) {\n this.sendTerminalTitle(sessionId, signal.title);\n }\n\n // 语义决策走统一 common/pty-semantic-machine;hosted 端在 emit 时多做两件事:\n // 1. 把 PTY semantic state 翻译成 session JSON FSM 转换;2. turn_complete 时触发 onTurnComplete 回调。\n const decision = decidePtySemanticTransition({\n currentState: hosted.currentState,\n signal: signal ?? null,\n sessionStateIsWaitingApproval: session?.state === SessionState.WAITING_APPROVAL,\n });\n hosted.currentState = decision.nextState;\n if (!decision.emit) return;\n\n this.sendPtyState(sessionId, decision.nextState, decision.meta);\n this.deps.applyPtyStateToSession(sessionId, decision.nextState);\n }\n\n private checkIdle(sessionId: string): void {\n const hosted = this.sessions.get(sessionId);\n if (!hosted) return;\n if (hosted.lastOutputTime === 0 || Date.now() - hosted.lastOutputTime <= IDLE_THRESHOLD_MS) {\n return;\n }\n hosted.lastOutputTime = 0;\n if (hosted.currentState !== \"working\") return;\n hosted.currentState = \"turn_complete\";\n this.sendPtyState(sessionId, \"turn_complete\");\n this.deps.applyPtyStateToSession(sessionId, \"turn_complete\");\n }\n\n private sendPtyState(\n sessionId: string,\n state: PtySemanticState,\n meta?: { title?: string; tool?: string },\n ): void {\n const payload = {\n state,\n ...(meta?.title !== undefined ? { title: meta.title } : {}),\n ...(meta?.tool !== undefined ? { tool: meta.tool } : {}),\n };\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"pty_state\",\n sessionId,\n payload,\n }),\n );\n const logPayload = { sessionId, ...payload };\n if (state === \"approval_wait\" || state === \"turn_complete\") {\n serviceLogger.info(logPayload, \"Hosted PTY semantic event pushed\");\n } else {\n serviceLogger.debug(logPayload, \"Hosted PTY semantic event pushed\");\n }\n }\n\n private sendTerminalTitle(sessionId: string, title: string): void {\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"terminal_title\",\n sessionId,\n title,\n }),\n );\n }\n\n private sendBinary(sessionId: string, data: Buffer, outputSeq: number): void {\n this.deps.relayConnection.sendBinary(encodeBinaryFrame(sessionId, outputSeq, data));\n }\n\n private close(sessionId: string, options: { kill: boolean; notify: boolean }): boolean {\n const hosted = this.sessions.get(sessionId);\n if (!hosted) return false;\n this.sessions.delete(sessionId);\n clearInterval(hosted.idleTimer);\n if (options.kill) {\n try {\n hosted.child.kill();\n } catch {\n // PTY may already have exited.\n }\n }\n hosted.terminal.dispose();\n if (options.notify) {\n this.sendPtyState(sessionId, \"turn_complete\");\n this.deps.sessionManager.terminateSession(sessionId);\n this.deps.onSessionClosed(sessionId);\n }\n return true;\n }\n}\n","import type { Socket } from \"node:net\";\nimport {\n MessageEnvelopeSchema,\n RelayControlSchema,\n SessionState,\n serializeControl,\n type AgentStatusPayload,\n type ControlMessage,\n type RelayControlMessage,\n} from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { serializeIpc } from \"../ipc/ipc-protocol.js\";\nimport type { SessionInfo, SessionManager } from \"./session-manager.js\";\nimport type { WorkerRegistry } from \"./worker-registry.js\";\nimport type { ControlMessageHandlers } from \"./handlers/control-messages.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { JsonObserver } from \"./json-observer.js\";\nimport type { ProviderHookContext } from \"../providers/index.js\";\nimport type { PermissionBroker } from \"./permission-broker.js\";\nimport type { HookEventRouter } from \"./hook-event-router.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\nimport type { HostedPtyRegistry } from \"./hosted-pty-registry.js\";\nimport { terminateSessionByOwnership } from \"./session-termination.js\";\nimport { RelayInputHandlers } from \"./relay-input-handlers.js\";\nimport { RelayHistoryHandlers } from \"./relay-history-handlers.js\";\nimport { RelayPermissionHandlers } from \"./relay-permission-handlers.js\";\nimport { RelayResourceHandlers } from \"./relay-resource-handlers.js\";\nimport { RelaySessionCreateHandler } from \"./relay-session-create-handler.js\";\nimport type { RelaySend } from \"./relay-router-types.js\";\n\ninterface RelayRouterDeps {\n sessionManager: SessionManager;\n workerRegistry: WorkerRegistry;\n controlHandlers: ControlMessageHandlers;\n relayConnection: RelayConnection;\n relaySend: RelaySend;\n terminalSockets: Map<string, Socket>;\n hostedPtyRegistry: HostedPtyRegistry;\n broadcastSessionList: () => void;\n broadcastSessionSync: (session: SessionInfo) => void;\n // user_input 注入触发 turn 开始(JSON 观察器)\n jsonObserver: JsonObserver;\n createHookContext: (\n sessionId: string,\n provider: ProviderHookContext[\"provider\"],\n ) => ProviderHookContext;\n cleanupHookContext: (sessionId: string) => void;\n permissionBroker: PermissionBroker;\n hookEventRouter: HookEventRouter;\n agentStatusRegistry: AgentStatusRegistry;\n getProviderEnv: () => NodeJS.ProcessEnv;\n getAgentCliSuggestions: () => Partial<Record<ProviderHookContext[\"provider\"], string[]>>;\n setAgentCliPath: (provider: ProviderHookContext[\"provider\"], path: string) => void;\n getPreviewRoots?: () => string[];\n}\n\n// 按 type 分发入站 relay 消息到独立 handler。未知 type warn 不丢,schema 逐步收紧。\nexport class RelayRouter {\n private readonly historyHandlers: RelayHistoryHandlers;\n private readonly inputHandlers: RelayInputHandlers;\n private readonly permissionHandlers: RelayPermissionHandlers;\n private readonly resourceHandlers: RelayResourceHandlers;\n private readonly sessionCreateHandler: RelaySessionCreateHandler;\n\n constructor(private deps: RelayRouterDeps) {\n this.historyHandlers = new RelayHistoryHandlers({\n relaySend: deps.relaySend,\n sessionManager: deps.sessionManager,\n permissionBroker: deps.permissionBroker,\n });\n this.inputHandlers = new RelayInputHandlers({\n sessionManager: deps.sessionManager,\n workerRegistry: deps.workerRegistry,\n relayConnection: deps.relayConnection,\n terminalSockets: deps.terminalSockets,\n hostedPtyRegistry: deps.hostedPtyRegistry,\n jsonObserver: deps.jsonObserver,\n previewRoots: deps.getPreviewRoots?.(),\n });\n this.resourceHandlers = new RelayResourceHandlers({\n relaySend: deps.relaySend,\n controlHandlers: deps.controlHandlers,\n sessionManager: deps.sessionManager,\n getProviderEnv: deps.getProviderEnv,\n getAgentCliSuggestions: deps.getAgentCliSuggestions,\n setAgentCliPath: deps.setAgentCliPath,\n });\n this.permissionHandlers = new RelayPermissionHandlers({\n relaySend: deps.relaySend,\n permissionBroker: deps.permissionBroker,\n hookEventRouter: deps.hookEventRouter,\n workerRegistry: deps.workerRegistry,\n });\n this.sessionCreateHandler = new RelaySessionCreateHandler({\n relaySend: deps.relaySend,\n workerRegistry: deps.workerRegistry,\n sessionManager: deps.sessionManager,\n hostedPtyRegistry: deps.hostedPtyRegistry,\n controlHandlers: deps.controlHandlers,\n permissionBroker: deps.permissionBroker,\n agentStatusRegistry: deps.agentStatusRegistry,\n getProviderEnv: deps.getProviderEnv,\n createHookContext: deps.createHookContext,\n cleanupHookContext: deps.cleanupHookContext,\n broadcastSessionSync: deps.broadcastSessionSync,\n broadcastSessionList: deps.broadcastSessionList,\n });\n }\n\n // shutdown 链路上提供单一 destroy 入口:把 sessionCreateHandler 内部 pending retry timer 清掉\n // 并 cleanup 已 spawn 但未 connect 的 worker 子进程,避免在 SIGTERM 之后变成孤儿。\n destroy(): void {\n this.sessionCreateHandler.destroy();\n }\n\n // 入站消息统一入口:proxy 收两类消息——relay control 与 envelope(user_input 这一种)。\n // 先按 envelope 试解析(discriminated union),失败再按 control 解析;各 handler 拿到\n // 强类型窄化后的消息,不再需要 `as string | undefined` / `as { ... }` 裸 cast。\n handle(rawMsg: Record<string, unknown>): void {\n const asEnvelope = MessageEnvelopeSchema.safeParse(rawMsg);\n if (asEnvelope.success && asEnvelope.data.type === \"user_input\") {\n try {\n this.inputHandlers.onUserInput(asEnvelope.data);\n } catch (err) {\n serviceLogger.warn({ type: \"user_input\", error: String(err) }, \"Relay handler threw\");\n }\n return;\n }\n\n const asControl = RelayControlSchema.safeParse(rawMsg);\n if (!asControl.success) {\n serviceLogger.warn(\n {\n type: typeof rawMsg.type === \"string\" ? rawMsg.type : \"<missing>\",\n controlIssues: asControl.error.issues.slice(0, 3),\n },\n \"Relay message rejected by both envelope and control schemas\",\n );\n return;\n }\n const msg = asControl.data;\n try {\n this.dispatch(msg);\n } catch (err) {\n serviceLogger.warn({ type: msg.type, error: String(err) }, \"Relay handler threw\");\n }\n }\n\n private dispatch(msg: RelayControlMessage): void {\n switch (msg.type) {\n case \"remote_input_raw\":\n this.inputHandlers.onRemoteInputRaw(msg);\n return;\n case \"clipboard_image_upload\":\n this.inputHandlers.onClipboardImageUpload(msg);\n return;\n case \"image_preview_request\":\n this.inputHandlers.onImagePreviewRequest(msg);\n return;\n case \"file_download_request\":\n this.inputHandlers.onFileDownloadRequest(msg);\n return;\n case \"file_upload_request\":\n void this.inputHandlers.onFileUploadRequest(msg);\n return;\n case \"tool_approve\":\n this.permissionHandlers.onToolApprove(msg);\n return;\n case \"tool_deny\":\n this.permissionHandlers.onToolDeny(msg);\n return;\n case \"proxy_info_request\":\n this.resourceHandlers.onProxyInfoRequest(msg);\n return;\n case \"agent_cli_config_update\":\n this.resourceHandlers.onAgentCliConfigUpdate(msg);\n return;\n case \"dir_list_request\":\n this.resourceHandlers.onDirListRequest(msg);\n return;\n case \"dir_create_request\":\n this.resourceHandlers.onDirCreateRequest(msg);\n return;\n case \"session_create\":\n this.sessionCreateHandler.onSessionCreate(msg);\n return;\n case \"session_messages_request\":\n this.historyHandlers.onSessionMessagesRequest(msg);\n return;\n case \"session_resources_request\":\n this.resourceHandlers.onSessionResourcesRequest(msg);\n return;\n case \"agent_status_request\":\n this.onAgentStatusRequest(msg);\n return;\n case \"permission_request_delivered\":\n this.permissionHandlers.onPermissionRequestDelivered(msg);\n return;\n case \"session_terminate\":\n this.onSessionTerminate(msg);\n return;\n case \"session_worker_abort\":\n this.onSessionWorkerAbort(msg);\n return;\n case \"session_history_request\":\n this.deps.controlHandlers.handleSessionHistoryRequest({ requestId: msg.requestId });\n return;\n case \"session_list\":\n this.onSessionList();\n return;\n case \"permission_mode_change\":\n this.onPermissionModeChange(msg);\n return;\n case \"session_subscribe\":\n this.onSessionSubscribe(msg);\n return;\n case \"terminal_resize_request\":\n this.onTerminalResizeRequest(msg);\n return;\n default:\n // proxy_to_client 方向的 control 消息由 client/relay 处理,这里直接忽略。\n return;\n }\n }\n\n private onAgentStatusRequest(msg: ControlMessage<\"agent_status_request\">): void {\n const sid = msg.sessionId;\n const requestId = msg.requestId;\n if (sid) {\n const status = this.deps.agentStatusRegistry.get(sid);\n const statuses =\n status && this.deps.sessionManager.getSession(sid)\n ? [{ sessionId: sid, payload: status }]\n : [];\n this.deps.relaySend(serializeControl({ type: \"agent_status_response\", requestId, statuses }));\n serviceLogger.info({ sessionId: sid, count: statuses.length }, \"Agent status snapshot sent\");\n return;\n }\n\n const statuses: Array<{ sessionId: string; payload: AgentStatusPayload }> = [];\n for (const { sessionId, status } of this.deps.agentStatusRegistry.list()) {\n if (!this.deps.sessionManager.getSession(sessionId)) continue;\n statuses.push({ sessionId, payload: status });\n }\n this.deps.relaySend(serializeControl({ type: \"agent_status_response\", requestId, statuses }));\n serviceLogger.info({ count: statuses.length }, \"Agent status snapshot sent\");\n }\n\n private onSessionTerminate(msg: ControlMessage<\"session_terminate\">): void {\n const sid = msg.sessionId;\n if (!sid) return;\n\n const result = terminateSessionByOwnership(this.deps, sid);\n serviceLogger.info(\n { sessionId: sid, success: result.success, action: result.action },\n \"Session termination handled via relay\",\n );\n }\n\n private onSessionWorkerAbort(msg: ControlMessage<\"session_worker_abort\">): void {\n const sid = msg.sessionId;\n if (!sid) return;\n\n const session = this.deps.sessionManager.getSession(sid);\n if (!session) {\n serviceLogger.warn({ sessionId: sid }, \"session_worker_abort: session not found\");\n return;\n }\n if (session.state === SessionState.TERMINATED) {\n serviceLogger.info({ sessionId: sid }, \"session_worker_abort: already terminated, dropping\");\n return;\n }\n\n if (session.mode === \"pty\") {\n // PTY 会话直接把 Ctrl+C 写入 PTY stdin,避免杀掉 terminal wrapper 进程\n const ts = this.deps.terminalSockets.get(sid);\n if (this.deps.hostedPtyRegistry.write(sid, \"\\x03\")) {\n serviceLogger.info({ sessionId: sid }, \"session_worker_abort: Ctrl+C sent to hosted PTY\");\n } else if (ts?.writable) {\n ts.write(serializeIpc({ type: \"pty_input\", sessionId: sid, data: \"\\x03\" }));\n serviceLogger.info({ sessionId: sid }, \"session_worker_abort: Ctrl+C sent to PTY\");\n } else {\n serviceLogger.warn(\n { sessionId: sid },\n \"session_worker_abort: PTY terminal socket unavailable\",\n );\n }\n return;\n }\n\n try {\n process.kill(session.pid, \"SIGINT\");\n serviceLogger.info(\n { sessionId: sid, pid: session.pid },\n \"session_worker_abort: SIGINT sent to worker\",\n );\n } catch (err) {\n serviceLogger.warn(\n { sessionId: sid, pid: session.pid, error: String(err) },\n \"session_worker_abort: kill failed\",\n );\n }\n }\n\n private onSessionList(): void {\n this.deps.broadcastSessionList();\n serviceLogger.info(\"Session list sent via relay\");\n }\n\n private onPermissionModeChange(msg: ControlMessage<\"permission_mode_change\">): void {\n const sid = msg.sessionId;\n const mode = msg.mode;\n if (!sid) {\n serviceLogger.info(\n { mode },\n \"Permission mode change received via relay (global, no sessionId)\",\n );\n return;\n }\n\n const session = this.deps.sessionManager.getSession(sid);\n if (session?.mode !== \"pty\") {\n serviceLogger.info(\n { sessionId: sid, mode },\n \"Permission mode change received for JSON session (no-op, not supported)\",\n );\n return;\n }\n\n // PTY 会话:发 Shift+Tab (CSI Z) 让 claude CLI 循环 permission mode\n // mode 字段当前保留但不使用 —— Claude CLI 仅支持循环键,无法一键直选档位\n const ts = this.deps.terminalSockets.get(sid);\n if (this.deps.hostedPtyRegistry.write(sid, \"\\x1b[Z\")) {\n serviceLogger.info(\n { sessionId: sid, mode },\n \"Permission mode cycle: Shift+Tab sent to hosted PTY\",\n );\n } else if (ts?.writable) {\n ts.write(serializeIpc({ type: \"pty_input\", sessionId: sid, data: \"\\x1b[Z\" }));\n serviceLogger.info({ sessionId: sid, mode }, \"Permission mode cycle: Shift+Tab sent to PTY\");\n } else {\n serviceLogger.warn(\n { sessionId: sid },\n \"Permission mode cycle: PTY terminal socket unavailable\",\n );\n }\n }\n\n private onSessionSubscribe(msg: ControlMessage<\"session_subscribe\">): void {\n const sid = msg.sessionId;\n const requestId = msg.requestId;\n if (!sid) return;\n\n const ts = this.deps.terminalSockets.get(sid);\n if (this.deps.hostedPtyRegistry.snapshot(sid, requestId)) {\n serviceLogger.info({ sessionId: sid, requestId }, \"Subscribe handled by hosted PTY\");\n } else if (ts?.writable) {\n ts.write(serializeIpc({ type: \"pty_subscribe\", sessionId: sid, requestId }));\n serviceLogger.info({ sessionId: sid, requestId }, \"Subscribe forwarded to terminal\");\n } else {\n serviceLogger.warn({ sessionId: sid }, \"Subscribe failed: terminal socket not available\");\n }\n }\n\n private onTerminalResizeRequest(msg: ControlMessage<\"terminal_resize_request\">): void {\n const sid = msg.sessionId;\n const cols = msg.cols;\n const rows = msg.rows;\n if (!sid || !cols || !rows) return;\n if (!this.deps.hostedPtyRegistry.resize(sid, cols, rows)) {\n serviceLogger.debug({ sessionId: sid, cols, rows }, \"Resize request ignored: not hosted PTY\");\n }\n }\n}\n","import { SessionState, type AgentStatusPayload } from \"@dev-anywhere/shared\";\n\ninterface JsonObserverDeps {\n changeSessionState: (sessionId: string, next: SessionState) => boolean;\n emitAgentStatus?: (sessionId: string, phase: AgentStatusPayload[\"phase\"]) => void;\n}\n\n// JSON 观察通道:把 worker 转发的 stream-json 事件翻译成 SessionState。\n// ERROR 态表达 \"worker 进程活着,但 proxy 观察/控制通道已坏\"(control_response 写入失败、\n// stream 连续 parse 失败、未来可能的 heartbeat 超时等);turn 内部的 is_error=true 不是观察失联,\n// 不触发 ERROR,仍按 onTurnResult 回 IDLE 处理。\nexport class JsonObserver {\n constructor(private deps: JsonObserverDeps) {}\n\n // 用户消息注入 worker(relay-router 收到 user_input)→ 进入 WORKING\n onTurnStart(sessionId: string): void {\n this.deps.changeSessionState(sessionId, SessionState.WORKING);\n this.deps.emitAgentStatus?.(sessionId, \"thinking\");\n }\n\n // stream-json result event 到达 → turn 结束回 IDLE\n onTurnResult(sessionId: string): void {\n this.deps.changeSessionState(sessionId, SessionState.IDLE);\n this.deps.emitAgentStatus?.(sessionId, \"idle\");\n }\n\n // control_request event 到达 → worker 阻塞等审批\n onApprovalRequested(sessionId: string): void {\n this.deps.changeSessionState(sessionId, SessionState.WAITING_APPROVAL);\n this.deps.emitAgentStatus?.(sessionId, \"waiting_permission\");\n }\n\n // 观察通道失联 → ERROR,待人工干预或后续 terminate\n onChannelBroken(sessionId: string): void {\n this.deps.changeSessionState(sessionId, SessionState.ERROR);\n }\n}\n","import { serviceLogger } from \"../common/logger.js\";\nimport type { HookProviderId } from \"./hook-registry.js\";\n\ninterface PermissionRequest {\n requestId: string;\n sessionId: string;\n provider: HookProviderId;\n toolName: string;\n input: Record<string, unknown>;\n}\n\nexport interface PermissionDecision {\n behavior: \"allow\" | \"deny\";\n message?: string;\n}\n\ninterface PendingPermission extends PermissionRequest {\n source: \"hook\" | \"worker\";\n resolve: (decision: PermissionDecision) => void;\n createdAt: number;\n deliveredAt?: number;\n}\n\ntype PendingPermissionView = Omit<PendingPermission, \"resolve\">;\n\nconst DUPLICATE_DECISION: PermissionDecision = {\n behavior: \"deny\",\n message: \"Duplicate permission request id.\",\n};\n\nfunction snapshot(pending: PendingPermission): PendingPermissionView {\n return {\n requestId: pending.requestId,\n sessionId: pending.sessionId,\n provider: pending.provider,\n source: pending.source,\n toolName: pending.toolName,\n input: pending.input,\n createdAt: pending.createdAt,\n ...(pending.deliveredAt !== undefined ? { deliveredAt: pending.deliveredAt } : {}),\n };\n}\n\nexport class PermissionBroker {\n private readonly pending = new Map<string, PendingPermission>();\n\n request(request: PermissionRequest): Promise<PermissionDecision> {\n if (this.pending.has(request.requestId)) {\n return Promise.resolve(DUPLICATE_DECISION);\n }\n return new Promise((resolve) => {\n this.pending.set(request.requestId, {\n ...request,\n source: \"hook\",\n resolve,\n createdAt: Date.now(),\n });\n });\n }\n\n registerWorkerRequest(\n request: PermissionRequest,\n onDecision: (decision: PermissionDecision) => void,\n ): boolean {\n if (this.pending.has(request.requestId)) {\n onDecision(DUPLICATE_DECISION);\n return false;\n }\n this.pending.set(request.requestId, {\n ...request,\n source: \"worker\",\n resolve: onDecision,\n createdAt: Date.now(),\n });\n return true;\n }\n\n resolve(requestId: string, decision: PermissionDecision): boolean {\n const pending = this.pending.get(requestId);\n if (!pending) return false;\n this.pending.delete(requestId);\n pending.resolve(decision);\n return true;\n }\n\n markDelivered(requestId: string): boolean {\n const pending = this.pending.get(requestId);\n if (!pending) return false;\n pending.deliveredAt = Date.now();\n return true;\n }\n\n get(requestId: string): PendingPermissionView | null {\n const pending = this.pending.get(requestId);\n return pending ? snapshot(pending) : null;\n }\n\n cleanupSession(sessionId: string, reason: string): void {\n for (const [requestId, pending] of this.pending) {\n if (pending.sessionId !== sessionId) continue;\n this.pending.delete(requestId);\n pending.resolve({ behavior: \"deny\", message: reason });\n serviceLogger.info({ sessionId, requestId, reason }, \"Pending hook permission dropped\");\n }\n }\n\n listSession(sessionId: string): PendingPermissionView[] {\n const out: PendingPermissionView[] = [];\n for (const pending of this.pending.values()) {\n if (pending.sessionId !== sessionId) continue;\n out.push(snapshot(pending));\n }\n return out;\n }\n}\n","import { providerValues, type ProviderId } from \"@dev-anywhere/shared\";\n\n// hook 入站 payload 解析的两个零依赖工具,hook-server 和 hook-event-router 共用。\n// 都按容错语义工作:解析失败返回中性默认值(null / 空对象 / \"unknown\")而非抛错,\n// 让上层 dispatch 层决定是否拒绝。\n\n// payload 任意 unknown 收窄到 Record;不是 object / 是数组时返回空对象。\nexport function asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n// provider 字符串收窄到 ProviderId;不在白名单返回 null。复用 shared 端的 providerValues 避免漂移。\nexport function asProvider(value: unknown): ProviderId | null {\n return (providerValues as readonly string[]).includes(value as string)\n ? (value as ProviderId)\n : null;\n}\n\n// 从 hook payload 提取 toolName,兼容 camelCase(toolName)和 snake_case(tool_name)。\nexport function toolNameFromPayload(payload: Record<string, unknown>): string {\n return typeof payload.toolName === \"string\"\n ? payload.toolName\n : typeof payload.tool_name === \"string\"\n ? payload.tool_name\n : \"unknown\";\n}\n\n// 从 hook payload 提取 input/tool_input 子结构,统一为 Record。\nexport function toolInputFromPayload(payload: Record<string, unknown>): Record<string, unknown> {\n return asRecord(payload.input ?? payload.tool_input);\n}\n","import {\n buildMessage,\n serializeControl,\n SessionState,\n type AgentStatusPayload,\n} from \"@dev-anywhere/shared\";\nimport { getSeqCounterFor } from \"../common/seq-counter.js\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { AuthenticatedHookEvent } from \"./hook-server.js\";\nimport type { HookProviderId } from \"./hook-registry.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\n\ninterface HookEventRouterDeps {\n relayConnection: RelayConnection;\n agentStatusRegistry: AgentStatusRegistry;\n changeSessionState: (sessionId: string, next: SessionState) => boolean;\n // session.mode 决定审批解除后的转换目标:JSON 模式不允许 WAITING_APPROVAL → WORKING\n // (观察通道粒度问题,见 session-manager.ts JSON_TRANSITIONS 注释),需让 onTurnResult 直接 → IDLE。\n getSessionMode?: (sessionId: string) => \"json\" | \"pty\" | undefined;\n nextSeq?: (sessionId: string) => number;\n}\n\nimport { toolInputFromPayload, toolNameFromPayload } from \"./hook-payload-helpers.js\";\n\nexport class HookEventRouter {\n constructor(private readonly deps: HookEventRouterDeps) {}\n\n handle(event: AuthenticatedHookEvent): void {\n switch (event.event) {\n case \"SessionStart\":\n this.deps.changeSessionState(event.sessionId, SessionState.IDLE);\n this.forwardAgentStatus(event, \"idle\");\n break;\n case \"UserPromptSubmit\":\n this.deps.changeSessionState(event.sessionId, SessionState.WORKING);\n this.forwardAgentStatus(event, \"thinking\");\n break;\n case \"PostToolUse\":\n case \"PostToolUseFailure\":\n this.deps.changeSessionState(event.sessionId, SessionState.WORKING);\n this.forwardAgentStatus(event, \"outputting\");\n break;\n case \"Stop\":\n this.deps.changeSessionState(event.sessionId, SessionState.IDLE);\n this.forwardAgentStatus(event, \"idle\");\n break;\n case \"PermissionRequest\":\n this.forwardPermissionRequest(event);\n break;\n case \"PreToolUse\":\n this.forwardToolUse(event);\n break;\n default:\n serviceLogger.debug(\n { sessionId: event.sessionId, provider: event.provider, event: event.event },\n \"Unknown provider hook event ignored\",\n );\n break;\n }\n }\n\n onPermissionResolved(\n sessionId: string,\n provider: HookProviderId,\n requestId: string,\n outcome: \"allow\" | \"deny\",\n context?: { toolName?: string; toolInput?: Record<string, unknown> },\n ): void {\n // 状态机走向按 outcome × mode 分四档(详见 session-manager.ts 的 JSON_TRANSITIONS / PTY_TRANSITIONS 边表):\n // - deny:双通道都直接回 IDLE,本轮终结\n // - allow + PTY:claude 继续输出,OSC 信号将驱动后续状态,先 → WORKING\n // - allow + JSON:观察粒度不到中间 WORKING,主动转换会被 FSM 拒绝;\n // 交给 onTurnResult 一次性 WAITING_APPROVAL → IDLE\n if (outcome === \"deny\") {\n this.deps.changeSessionState(sessionId, SessionState.IDLE);\n } else {\n const mode = this.deps.getSessionMode?.(sessionId);\n if (mode === \"pty\") {\n this.deps.changeSessionState(sessionId, SessionState.WORKING);\n }\n // mode === \"json\" 或 undefined:不主动转换状态,交给后续观察事件\n }\n this.forwardAgentStatus(\n {\n sessionId,\n provider,\n event: \"PermissionResolved\",\n requestId,\n payload: {},\n },\n outcome === \"allow\" ? \"tool_use\" : \"idle\",\n {\n toolName: context?.toolName,\n toolInput: context?.toolInput,\n permissionResolution: { requestId, outcome },\n },\n );\n serviceLogger.info({ sessionId, requestId, outcome }, \"Hook permission resolved\");\n }\n\n private forwardPermissionRequest(event: AuthenticatedHookEvent): void {\n const requestId = event.requestId ?? `${event.sessionId}:${Date.now()}`;\n const toolName = toolNameFromPayload(event.payload);\n const input = toolInputFromPayload(event.payload);\n\n this.deps.changeSessionState(event.sessionId, SessionState.WAITING_APPROVAL);\n this.forwardAgentStatus(event, \"waiting_permission\", {\n toolName,\n toolInput: input,\n permissionRequest: {\n requestId,\n toolName,\n input,\n },\n });\n\n const seq = this.deps.nextSeq?.(event.sessionId) ?? getSeqCounterFor(event.sessionId).next();\n const envelope = buildMessage(\n \"tool_use_request\",\n event.sessionId,\n seq,\n {\n toolName,\n toolId: requestId,\n parameters: input,\n },\n \"proxy\",\n );\n this.deps.relayConnection.sendEnvelope(envelope);\n }\n\n private forwardToolUse(event: AuthenticatedHookEvent): void {\n const toolName = toolNameFromPayload(event.payload);\n const input = toolInputFromPayload(event.payload);\n this.forwardAgentStatus(event, \"tool_use\", {\n toolName,\n toolInput: input,\n });\n }\n\n private forwardAgentStatus(\n event: AuthenticatedHookEvent,\n phase: AgentStatusPayload[\"phase\"],\n extra?: Partial<AgentStatusPayload>,\n ): void {\n const payload: AgentStatusPayload = {\n provider: event.provider,\n phase,\n seq: this.nextSeq(event.sessionId),\n updatedAt: Date.now(),\n ...extra,\n };\n this.deps.agentStatusRegistry.set(event.sessionId, payload);\n this.deps.relayConnection.sendRaw(\n serializeControl({\n type: \"agent_status\",\n sessionId: event.sessionId,\n payload,\n }),\n );\n }\n\n private nextSeq(sessionId: string): number {\n return this.deps.nextSeq?.(sessionId) ?? getSeqCounterFor(sessionId).next();\n }\n}\n","import type { AgentStatusPayload } from \"@dev-anywhere/shared\";\n\nexport class AgentStatusRegistry {\n private readonly statuses = new Map<string, AgentStatusPayload>();\n\n set(sessionId: string, status: AgentStatusPayload): void {\n const current = this.statuses.get(sessionId);\n if (current && current.seq > status.seq) return;\n this.statuses.set(sessionId, status);\n }\n\n get(sessionId: string): AgentStatusPayload | null {\n return this.statuses.get(sessionId) ?? null;\n }\n\n list(): Array<{ sessionId: string; status: AgentStatusPayload }> {\n return Array.from(this.statuses, ([sessionId, status]) => ({ sessionId, status }));\n }\n\n delete(sessionId: string): void {\n this.statuses.delete(sessionId);\n }\n}\n","import { SessionState } from \"@dev-anywhere/shared\";\nimport type { SessionInfo } from \"./session-manager.js\";\n\nexport function shouldPromotePtyActivityToWorking(\n session: SessionInfo | undefined,\n pendingApprovalCount: number,\n): boolean {\n if (!session || session.mode !== \"pty\") return false;\n if (pendingApprovalCount > 0) return false;\n return session.state === SessionState.IDLE || session.state === SessionState.WAITING_APPROVAL;\n}\n","import { SessionState, type PtySemanticState } from \"@dev-anywhere/shared\";\n\nexport function resolvePtySemanticSessionTransitions(\n currentState: SessionState | undefined,\n semanticState: PtySemanticState,\n): SessionState[] {\n if (semanticState !== \"turn_complete\") return [];\n\n if (currentState === SessionState.WAITING_APPROVAL) {\n return [SessionState.IDLE];\n }\n\n if (currentState === SessionState.WORKING) {\n return [SessionState.IDLE];\n }\n\n return [];\n}\n","import { SessionState, type PtySemanticState } from \"@dev-anywhere/shared\";\nimport { shouldPromotePtyActivityToWorking } from \"./pty-state-guard.js\";\nimport { resolvePtySemanticSessionTransitions } from \"./pty-semantic-lifecycle.js\";\nimport type { SessionInfo } from \"./session-manager.js\";\n\n// 把 PTY 语义状态投影到 Session FSM 转换 + 关联副作用。\n//\n// hosted-pty-registry(hosted PTY 模式,serve 进程内直接持有 PTY)和 terminal-ipc(local PTY\n// 模式,worker 进程经 IPC 通知 serve)以前各写一份 PTY → Session 翻译,行为不一致:hosted 缺\n// shouldPromotePtyActivityToWorking guard、turn_complete 直调 changeSessionState(IDLE) 而非\n// 走 resolvePtySemanticSessionTransitions helper。这里收一份,两侧共用。\n//\n// 与 decidePtySemanticTransition 的关系:那是 PTY 推断层(字节流→PTY semantic),输入是 OSC\n// 信号 + 局部状态,输出 PTY semantic transition;本函数是翻译层(PTY semantic→SessionState),\n// 输入是 PTY 决策结果,输出是 SessionState 转换 + 关联副作用(清理 interrupted approvals、推\n// agent status idle 等)。两层职责正交。\n\nexport interface PtySessionBridgeDeps {\n changeSessionState: (sessionId: string, next: SessionState) => boolean;\n getSession: (sessionId: string) => SessionInfo | undefined;\n getPendingApprovalCount: (sessionId: string) => number;\n resolveInterruptedApprovals: (sessionId: string) => void;\n emitAgentStatus: (sessionId: string, phase: \"idle\") => void;\n}\n\nexport function applyPtyStateToSession(\n deps: PtySessionBridgeDeps,\n sessionId: string,\n ptyState: PtySemanticState,\n): void {\n // 单点拒绝非法源 state: session 不在 map 里 (已 terminate 删除) 或 state===TERMINATED 时,\n // 跳过所有 bridge 副作用——changeSessionState 会被 FSM 各自拒绝, 但 resolveInterruptedApprovals /\n // emitAgentStatus 等下游回调没有自己的 guard, 否则会对 zombie session 触发空跑或冗余事件。\n // 这一层把 \"session 处于非法源状态\" 的判定收口到此, 不依赖每个回调自己重新检查。\n const session = deps.getSession(sessionId);\n if (!session || session.state === SessionState.TERMINATED) return;\n\n switch (ptyState) {\n case \"approval_wait\":\n deps.changeSessionState(sessionId, SessionState.WAITING_APPROVAL);\n break;\n case \"working\": {\n const pending = deps.getPendingApprovalCount(sessionId);\n if (shouldPromotePtyActivityToWorking(session, pending)) {\n deps.changeSessionState(sessionId, SessionState.WORKING);\n }\n break;\n }\n case \"turn_complete\": {\n deps.resolveInterruptedApprovals(sessionId);\n const transitions = resolvePtySemanticSessionTransitions(session.state, ptyState);\n for (const next of transitions) {\n deps.changeSessionState(sessionId, next);\n }\n deps.emitAgentStatus(sessionId, \"idle\");\n break;\n }\n }\n}\n","import { buildMessage, serializeControl, SessionState } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { SessionInfo, SessionManager } from \"./session-manager.js\";\n\nconst ACTIVITY_STATUS_PUSH_INTERVAL_MS = 15_000;\n\nfunction toSessionListPayload(s: SessionInfo) {\n return {\n sessionId: s.id,\n mode: s.mode,\n provider: s.provider,\n ...(s.ptyOwner !== undefined ? { ptyOwner: s.ptyOwner } : {}),\n state: s.state,\n lastActive: s.updatedAt,\n ...(s.name !== undefined ? { name: s.name } : {}),\n };\n}\n\nfunction pushSessionStatus(\n relay: RelayConnection,\n sessionManager: SessionManager,\n sessionId: string,\n): void {\n const session = sessionManager.getSession(sessionId);\n if (!session) return;\n try {\n const envelope = buildMessage(\n \"session_status\",\n session.id,\n Date.now(),\n { sessionId: session.id, state: session.state, lastActive: session.updatedAt },\n \"proxy\",\n );\n relay.sendEnvelope(envelope);\n } catch (err) {\n serviceLogger.debug({ sessionId, error: String(err) }, \"Failed to push session_status\");\n }\n}\n\nexport function broadcastSessionList(relay: RelayConnection, sessionManager: SessionManager): void {\n // session_list 是 envelope(payload 携带 sessions 数组),走 buildMessage 才能保证\n // version / timestamp / source 字段与其它 envelope 一致;旧代码手写 version: \"1\" 与\n // buildMessage 默认的 \"1.0\" 不符,会让任何对 envelope schema 严格校验的地方报错。\n const envelope = buildMessage(\n \"session_list\",\n null,\n 0,\n { sessions: sessionManager.listSessions().map(toSessionListPayload) },\n \"proxy\",\n );\n relay.sendEnvelope(envelope);\n}\n\nexport function broadcastSessionSync(relay: RelayConnection, session: SessionInfo): void {\n relay.sendRaw(\n serializeControl({\n type: \"session_sync\",\n sessions: [\n {\n id: session.id,\n mode: session.mode,\n provider: session.provider,\n ...(session.ptyOwner !== undefined ? { ptyOwner: session.ptyOwner } : {}),\n state: session.state,\n },\n ],\n }),\n );\n}\n\nexport function changeSessionState(\n sessionManager: SessionManager,\n relay: RelayConnection,\n sessionId: string,\n next: SessionState,\n): boolean {\n if (!sessionManager.getSession(sessionId)) return false;\n const changed = sessionManager.updateState(sessionId, next);\n if (changed) pushSessionStatus(relay, sessionManager, sessionId);\n return changed;\n}\n\nexport function touchSessionActivity(\n sessionManager: SessionManager,\n relay: RelayConnection,\n sessionId: string,\n now: number = Date.now(),\n): boolean {\n const touched = sessionManager.touchSession(sessionId, now, ACTIVITY_STATUS_PUSH_INTERVAL_MS);\n if (touched) pushSessionStatus(relay, sessionManager, sessionId);\n return touched;\n}\n","import { serializeControl, type AgentStatusPayload, type SessionState } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { disposeSeqCounter, getSeqCounterFor } from \"../common/seq-counter.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\nimport type { ControlMessageHandlers } from \"./handlers/control-messages.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport {\n broadcastSessionList,\n changeSessionState,\n touchSessionActivity,\n} from \"./session-broadcast.js\";\n\ninterface EventBridgeDeps {\n sessionManager: SessionManager;\n relayConnection: RelayConnection;\n agentStatusRegistry: AgentStatusRegistry;\n controlHandlers: ControlMessageHandlers;\n permissionBroker: { cleanupSession: (sessionId: string, reason: string) => void };\n}\n\ninterface EventBridge {\n // 推动 session FSM 转换并广播 session_status;observer 通道按 session.mode 分别走 PTY/JSON 转换表。\n changeSessionState: (sessionId: string, next: SessionState) => boolean;\n // 节流式 lastActive 更新;与 changeSessionState 共用底层 push 逻辑。\n touchSessionActivity: (sessionId: string) => boolean;\n // 把 agent_status 推到 relay 并写到 registry,用于 client 重连后查询。\n emitAgentStatus: (sessionId: string, phase: AgentStatusPayload[\"phase\"]) => void;\n // session 关闭时三件套清理:取消 control handlers 周期任务 / 删 agent_status / 广播会话列表。\n // session 本身的 manager.delete 由调用方负责(不同路径删的时机不同)。\n cleanupSessionResources: (sessionId: string) => void;\n}\n\nexport function createEventBridge(deps: EventBridgeDeps): EventBridge {\n const changeState = (sessionId: string, next: SessionState): boolean =>\n changeSessionState(deps.sessionManager, deps.relayConnection, sessionId, next);\n\n const touchActivity = (sessionId: string): boolean =>\n touchSessionActivity(deps.sessionManager, deps.relayConnection, sessionId);\n\n const emitAgentStatus = (sessionId: string, phase: AgentStatusPayload[\"phase\"]): void => {\n const session = deps.sessionManager.getSession(sessionId);\n if (!session) return;\n const payload: AgentStatusPayload = {\n provider: session.provider,\n phase,\n seq: getSeqCounterFor(sessionId).next(),\n updatedAt: Date.now(),\n };\n deps.agentStatusRegistry.set(sessionId, payload);\n deps.relayConnection.sendRaw(serializeControl({ type: \"agent_status\", sessionId, payload }));\n };\n\n const cleanupSessionResources = (sessionId: string): void => {\n // 每步独立 try/catch: 任意中间步骤抛异常都不能阻断最后的 broadcastSessionList。\n // 一旦广播丢失, web 不知道 session 已删, 列表残留 + 后续给该 session 的请求全\n // hang 到超时。\n const safe = (fn: () => void, step: string): void => {\n try {\n fn();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n serviceLogger.warn(\n {\n sessionId,\n step,\n err: { message: error.message, stack: error.stack, cause: error.cause },\n },\n \"Session cleanup step failed; continuing\",\n );\n }\n };\n safe(() => deps.controlHandlers.cleanup(sessionId), \"controlHandlers.cleanup\");\n safe(() => deps.agentStatusRegistry.delete(sessionId), \"agentStatusRegistry.delete\");\n safe(() => disposeSeqCounter(sessionId), \"disposeSeqCounter\");\n // hosted PTY 走的是 onSessionClosed = cleanupSessionResources, 不经过 worker socket\n // close → onDisconnect → permissionBroker.cleanupSession 那条链, 否则 hosted 模式下\n // 待审批工具会在 child 退出后留在 broker 永不释放, 客户端的 approval card 永远卡住。\n safe(\n () => deps.permissionBroker.cleanupSession(sessionId, \"Session closed\"),\n \"permissionBroker.cleanupSession\",\n );\n safe(\n () => broadcastSessionList(deps.relayConnection, deps.sessionManager),\n \"broadcastSessionList\",\n );\n };\n\n return {\n changeSessionState: changeState,\n touchSessionActivity: touchActivity,\n emitAgentStatus,\n cleanupSessionResources,\n };\n}\n","import { execSync } from \"node:child_process\";\nimport { existsSync, readFileSync, unlinkSync } from \"node:fs\";\nimport { hostname } from \"node:os\";\nimport { connect, type Socket } from \"node:net\";\nimport { flushLogger } from \"@dev-anywhere/shared/logger\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { DEFAULT_PROXY_PROFILE, PID_PATH, PROFILE_NAME, SOCK_PATH } from \"../common/paths.js\";\n\nfunction tryConnectSocket(sockPath: string): Promise<Socket | null> {\n return new Promise((resolve) => {\n const s = connect(sockPath);\n s.on(\"connect\", () => resolve(s));\n s.on(\"error\", () => resolve(null));\n });\n}\n\nexport function isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function cleanupStaleResources(): Promise<void> {\n if (existsSync(SOCK_PATH)) {\n const existing = await tryConnectSocket(SOCK_PATH);\n if (existing) {\n existing.destroy();\n const msg = `Another service is already running on ${SOCK_PATH}`;\n serviceLogger.error(msg);\n console.error(msg);\n await flushLogger(serviceLogger);\n process.exit(1);\n }\n unlinkSync(SOCK_PATH);\n serviceLogger.info(\"Removed stale socket file\");\n }\n\n if (existsSync(PID_PATH)) {\n const pidStr = readFileSync(PID_PATH, \"utf-8\").trim();\n const pid = parseInt(pidStr, 10);\n if (!isNaN(pid) && isProcessAlive(pid)) {\n const msg = `Another service is already running with PID ${pid}`;\n serviceLogger.error(msg);\n console.error(msg);\n await flushLogger(serviceLogger);\n process.exit(1);\n }\n unlinkSync(PID_PATH);\n serviceLogger.info(\"Removed stale PID file\");\n }\n}\n\nexport function formatProxyNameForProfile(baseName: string, profileName = PROFILE_NAME): string {\n return profileName === DEFAULT_PROXY_PROFILE ? baseName : `${baseName} (${profileName})`;\n}\n\nexport function getProxyName(): string {\n const explicitName = process.env.DEV_ANYWHERE_PROXY_NAME?.trim();\n if (explicitName) return explicitName;\n\n return formatProxyNameForProfile(getComputerName() || hostname());\n}\n\nfunction getComputerName(): string | null {\n try {\n return (\n execSync(\"scutil --get ComputerName\", { stdio: [\"pipe\", \"pipe\", \"ignore\"] })\n .toString()\n .trim() || null\n );\n } catch {\n return null;\n }\n}\n","import type { Socket } from \"node:net\";\nimport { encodeBinaryFrame, serializeControl, type AgentStatusPayload } from \"@dev-anywhere/shared\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { createIpcReader, serializeIpc, type IpcMessage } from \"../ipc/ipc-protocol.js\";\nimport type { ProviderHookContext } from \"../providers/index.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\nimport type { ControlMessageHandlers } from \"./handlers/control-messages.js\";\nimport type { HookEventRouter } from \"./hook-event-router.js\";\nimport type { HostedPtyRegistry } from \"./hosted-pty-registry.js\";\nimport type { PermissionBroker } from \"./permission-broker.js\";\nimport { applyPtyStateToSession } from \"./pty-session-bridge.js\";\nimport type { PtySessionBridgeDeps } from \"./pty-session-bridge.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport {\n broadcastSessionList,\n broadcastSessionSync,\n changeSessionState,\n touchSessionActivity,\n} from \"./session-broadcast.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport { terminateSessionByOwnership } from \"./session-termination.js\";\nimport type { WorkerRegistry } from \"./worker-registry.js\";\nimport { isProcessAlive } from \"./service-files.js\";\n\ninterface TerminalConnectionDeps {\n sessionManager: SessionManager;\n workerRegistry: WorkerRegistry;\n terminalSockets: Map<string, Socket>;\n hostedPtyRegistry: HostedPtyRegistry;\n relayConnection: RelayConnection;\n controlHandlers: ControlMessageHandlers;\n agentStatusRegistry: AgentStatusRegistry;\n permissionBroker: PermissionBroker;\n hookEventRouter: HookEventRouter;\n createHookContext: (\n sessionId: string,\n provider: ProviderHookContext[\"provider\"],\n ) => ProviderHookContext;\n emitAgentStatus: (sessionId: string, phase: AgentStatusPayload[\"phase\"]) => void;\n resolveInterruptedApprovals: (sessionId: string) => void;\n cleanupSessionResources: (sessionId: string) => void;\n config: Extract<IpcMessage, { type: \"service_status_response\" }>[\"config\"];\n}\n\nexport function handleTerminalConnection(socket: Socket, deps: TerminalConnectionDeps): void {\n const {\n sessionManager,\n workerRegistry,\n terminalSockets,\n hostedPtyRegistry,\n relayConnection,\n controlHandlers,\n agentStatusRegistry,\n permissionBroker,\n createHookContext,\n emitAgentStatus,\n resolveInterruptedApprovals,\n cleanupSessionResources,\n config,\n } = deps;\n\n const bridgeDeps: PtySessionBridgeDeps = {\n changeSessionState: (sessionId, next) =>\n changeSessionState(sessionManager, relayConnection, sessionId, next),\n getSession: (sessionId) => sessionManager.getSession(sessionId),\n getPendingApprovalCount: (sessionId) => permissionBroker.listSession(sessionId).length,\n resolveInterruptedApprovals,\n emitAgentStatus,\n };\n\n createIpcReader(\n socket,\n (msg: IpcMessage) => {\n switch (msg.type) {\n case \"session_create_request\": {\n if (msg.mode !== \"pty\") {\n socket.write(\n serializeIpc({\n type: \"session_create_response\",\n sessionId: \"\",\n error: `Unsupported mode via IPC: ${msg.mode}`,\n }),\n );\n break;\n }\n const provider = msg.provider;\n const existing = msg.sessionId ? sessionManager.getSession(msg.sessionId) : undefined;\n const session =\n existing ??\n sessionManager.createSession(\n \"pty\",\n msg.cwd,\n msg.pid,\n msg.name,\n msg.sessionId,\n provider,\n \"local-terminal\",\n );\n if (existing) {\n sessionManager.setPid(session.id, msg.pid);\n }\n socket.write(\n serializeIpc({\n type: \"session_create_response\",\n sessionId: session.id,\n hook: createHookContext(session.id, provider),\n }),\n );\n serviceLogger.info(\n { sessionId: session.id, mode: \"pty\", provider },\n \"PTY session created\",\n );\n break;\n }\n\n case \"service_status_request\": {\n const relayStatus = relayConnection.getStatus();\n const sessions = sessionManager.listSessions();\n socket.write(\n serializeIpc({\n type: \"service_status_response\",\n config,\n relay: relayStatus,\n sessions: sessions.map((s) => ({\n id: s.id,\n mode: s.mode,\n provider: s.provider,\n state: s.state,\n createdAt: new Date(s.createdAt).toISOString(),\n ...(s.name !== undefined ? { name: s.name } : {}),\n hasWorker: workerRegistry.has(s.id),\n })),\n }),\n );\n break;\n }\n\n case \"pty_title_change\": {\n if (!sessionManager.getSession(msg.sessionId)) break;\n relayConnection.sendRaw(\n serializeControl({\n type: \"terminal_title\",\n sessionId: msg.sessionId,\n title: msg.title,\n }),\n );\n break;\n }\n\n case \"pty_semantic_event\": {\n if (!sessionManager.getSession(msg.sessionId)) break;\n const logPayload = {\n sessionId: msg.sessionId,\n state: msg.state,\n ...(msg.title !== undefined ? { title: msg.title } : {}),\n ...(msg.tool !== undefined ? { tool: msg.tool } : {}),\n };\n if (msg.state === \"approval_wait\" || msg.state === \"turn_complete\") {\n serviceLogger.info(logPayload, \"PTY semantic event received\");\n } else {\n serviceLogger.debug(logPayload, \"PTY semantic event received\");\n }\n applyPtyStateToSession(bridgeDeps, msg.sessionId, msg.state);\n relayConnection.sendRaw(\n serializeControl({\n type: \"pty_state\",\n sessionId: msg.sessionId,\n payload: {\n state: msg.state,\n ...(msg.title !== undefined ? { title: msg.title } : {}),\n ...(msg.tool !== undefined ? { tool: msg.tool } : {}),\n },\n }),\n );\n break;\n }\n\n case \"pty_resize\": {\n if (!sessionManager.getSession(msg.sessionId)) break;\n relayConnection.sendRaw(\n serializeControl({\n type: \"terminal_resize\",\n sessionId: msg.sessionId,\n cols: msg.cols,\n rows: msg.rows,\n }),\n );\n break;\n }\n\n case \"session_terminate_request\": {\n const result = terminateSessionByOwnership(\n {\n sessionManager,\n workerRegistry,\n controlHandlers,\n terminalSockets,\n hostedPtyRegistry,\n agentStatusRegistry,\n broadcastSessionList: () => broadcastSessionList(relayConnection, sessionManager),\n },\n msg.sessionId,\n );\n socket.write(\n serializeIpc({\n type: \"session_terminate_response\",\n sessionId: msg.sessionId,\n success: result.success,\n }),\n );\n serviceLogger.info(\n { sessionId: msg.sessionId, success: result.success, action: result.action },\n \"Session termination request handled\",\n );\n break;\n }\n\n case \"pty_register\": {\n if (!sessionManager.getSession(msg.sessionId)) {\n serviceLogger.warn(\n { sessionId: msg.sessionId },\n \"PTY register ignored: session missing\",\n );\n break;\n }\n sessionManager.setPid(msg.sessionId, msg.pid);\n terminalSockets.set(msg.sessionId, socket);\n socket.write(\n serializeIpc({\n type: \"bridge_status\",\n connected: relayConnection.getStatus().connected,\n }),\n );\n const session = sessionManager.getSession(msg.sessionId);\n if (session) {\n broadcastSessionSync(relayConnection, session);\n }\n broadcastSessionList(relayConnection, sessionManager);\n serviceLogger.info({ sessionId: msg.sessionId }, \"PTY session registered\");\n break;\n }\n\n case \"pty_deregister\": {\n relayConnection.sendRaw(\n serializeControl({\n type: \"pty_state\",\n sessionId: msg.sessionId,\n payload: { state: \"turn_complete\" },\n }),\n );\n sessionManager.terminateSession(msg.sessionId);\n terminalSockets.delete(msg.sessionId);\n cleanupSessionResources(msg.sessionId);\n serviceLogger.info({ sessionId: msg.sessionId }, \"PTY session deregistered\");\n break;\n }\n\n case \"pty_input\": {\n if (!sessionManager.getSession(msg.sessionId)) break;\n const targetSocket = terminalSockets.get(msg.sessionId);\n if (hostedPtyRegistry.write(msg.sessionId, msg.data)) {\n break;\n }\n if (targetSocket?.writable) {\n targetSocket.write(\n serializeIpc({\n type: \"pty_input\",\n sessionId: msg.sessionId,\n data: msg.data,\n }),\n );\n }\n break;\n }\n\n case \"session_status_update\": {\n changeSessionState(sessionManager, relayConnection, msg.sessionId, msg.state);\n break;\n }\n\n case \"pty_snapshot\": {\n if (!sessionManager.getSession(msg.sessionId)) break;\n relayConnection.sendRaw(\n serializeControl({\n type: \"session_snapshot\",\n sessionId: msg.sessionId,\n cols: msg.cols,\n rows: msg.rows,\n data: msg.data,\n outputSeq: msg.outputSeq,\n ...(msg.requestId !== undefined ? { requestId: msg.requestId } : {}),\n }),\n );\n serviceLogger.info(\n { sessionId: msg.sessionId, cols: msg.cols, rows: msg.rows },\n \"Session snapshot forwarded to relay\",\n );\n break;\n }\n\n default: {\n serviceLogger.warn({ type: (msg as IpcMessage).type }, \"Unhandled IPC message type\");\n }\n }\n },\n (sessionId, data, outputSeq) => {\n if (!sessionManager.getSession(sessionId)) return;\n touchSessionActivity(sessionManager, relayConnection, sessionId);\n relayConnection.sendBinary(encodeBinaryFrame(sessionId, outputSeq, data));\n },\n (err, line) => {\n // 单条 IPC 行 schema 失败时 warn-skip,不让它升级为 socket error 触发整个 terminal 断开。\n // err.cause 暴露 JSON.parse / zod 的原始报错; line 截断 200 字符避免日志爆掉但留够字段\n // 取证。\n const cause = err instanceof Error ? err.cause : undefined;\n serviceLogger.warn(\n {\n err: err.message,\n cause: cause instanceof Error ? cause.message : cause,\n lineLen: line.length,\n linePreview: line.slice(0, 200),\n },\n \"Terminal IPC message dropped (parse/schema error)\",\n );\n },\n );\n\n socket.on(\"close\", () => {\n for (const [sessionId, terminalSocket] of terminalSockets) {\n if (terminalSocket === socket) {\n terminalSockets.delete(sessionId);\n const session = sessionManager.getSession(sessionId);\n if (!session) {\n serviceLogger.info({ sessionId }, \"Terminal socket closed, session already cleaned\");\n continue;\n }\n if (session.mode === \"pty\" && session.pid && isProcessAlive(session.pid)) {\n serviceLogger.info(\n { sessionId, pid: session.pid },\n \"Terminal socket closed but process alive, skipping cleanup\",\n );\n continue;\n }\n relayConnection.sendRaw(\n serializeControl({\n type: \"pty_state\",\n sessionId,\n payload: { state: \"turn_complete\" },\n }),\n );\n sessionManager.terminateSession(sessionId);\n cleanupSessionResources(sessionId);\n serviceLogger.info(\n { sessionId },\n \"PTY session cleaned up on socket close (crash fallback)\",\n );\n }\n }\n });\n\n socket.on(\"error\", (err) => {\n serviceLogger.warn({ error: String(err) }, \"Client socket error\");\n });\n}\n","import { createHash, randomBytes } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { z } from \"zod\";\nimport { serviceLogger } from \"../common/logger.js\";\n\nexport type HookProviderId = \"claude\" | \"codex\";\n\ninterface HookSessionBinding {\n sessionId: string;\n provider: HookProviderId;\n marker: string;\n tokenHash: string;\n createdAt: number;\n expiresAt?: number;\n}\n\ninterface HookSessionCredentials {\n sessionId: string;\n provider: HookProviderId;\n marker: string;\n token: string;\n}\n\ninterface VerifyOptions {\n sessionId: string;\n marker: string;\n token: string;\n provider?: HookProviderId;\n now?: number;\n}\n\ninterface HookRegistryOptions {\n persistPath?: string;\n}\n\nconst PersistedHookSessionBindingSchema = z.object({\n sessionId: z.string(),\n provider: z.enum([\"claude\", \"codex\"]),\n marker: z.string(),\n tokenHash: z.string(),\n createdAt: z.number(),\n expiresAt: z.number().optional(),\n});\n\nconst PersistedHookRegistrySchema = z.object({\n version: z.literal(1),\n bindings: z.array(PersistedHookSessionBindingSchema),\n});\n\nfunction hashToken(token: string): string {\n return createHash(\"sha256\").update(token).digest(\"hex\");\n}\n\nfunction randomSecret(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\nexport class HookRegistry {\n private readonly bindingsBySession = new Map<string, HookSessionBinding>();\n private readonly persistPath?: string;\n\n constructor(options: HookRegistryOptions = {}) {\n this.persistPath = options.persistPath;\n this.load();\n }\n\n registerSession(\n sessionId: string,\n provider: HookProviderId,\n options: { ttlMs?: number; now?: number } = {},\n ): HookSessionCredentials {\n const now = options.now ?? Date.now();\n const token = randomSecret();\n const marker = randomSecret();\n this.bindingsBySession.set(sessionId, {\n sessionId,\n provider,\n marker,\n tokenHash: hashToken(token),\n createdAt: now,\n ...(options.ttlMs ? { expiresAt: now + options.ttlMs } : {}),\n });\n this.save();\n return { sessionId, provider, marker, token };\n }\n\n verify(options: VerifyOptions): HookSessionBinding | null {\n const binding = this.bindingsBySession.get(options.sessionId);\n if (!binding) return null;\n if (options.provider && binding.provider !== options.provider) return null;\n if (binding.marker !== options.marker) return null;\n if (binding.tokenHash !== hashToken(options.token)) return null;\n if (binding.expiresAt && (options.now ?? Date.now()) > binding.expiresAt) return null;\n return binding;\n }\n\n getSession(sessionId: string): HookSessionBinding | null {\n return this.bindingsBySession.get(sessionId) ?? null;\n }\n\n unregisterSession(sessionId: string): void {\n if (this.bindingsBySession.delete(sessionId)) {\n this.save();\n }\n }\n\n private load(): void {\n if (!this.persistPath || !existsSync(this.persistPath)) return;\n try {\n const parsed = PersistedHookRegistrySchema.parse(\n JSON.parse(readFileSync(this.persistPath, \"utf8\")),\n );\n this.bindingsBySession.clear();\n for (const binding of parsed.bindings) {\n this.bindingsBySession.set(binding.sessionId, binding);\n }\n } catch (err) {\n serviceLogger.warn(\n { path: this.persistPath, error: String(err) },\n \"Failed to load hook registry state\",\n );\n }\n }\n\n private save(): void {\n if (!this.persistPath) return;\n try {\n mkdirSync(dirname(this.persistPath), { recursive: true });\n const tmpPath = `${this.persistPath}.${process.pid}.${Date.now()}.tmp`;\n writeFileSync(\n tmpPath,\n JSON.stringify(\n {\n version: 1,\n bindings: Array.from(this.bindingsBySession.values()),\n },\n null,\n 2,\n ),\n );\n renameSync(tmpPath, this.persistPath);\n } catch (err) {\n serviceLogger.warn(\n { path: this.persistPath, error: String(err) },\n \"Failed to persist hook registry state\",\n );\n }\n }\n}\n","import { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { HookRegistry, type HookProviderId } from \"./hook-registry.js\";\nimport { PermissionBroker } from \"./permission-broker.js\";\nimport { asProvider, asRecord, toolNameFromPayload } from \"./hook-payload-helpers.js\";\n\ninterface HookServerOptions {\n port: number;\n registry: HookRegistry;\n permissionBroker: PermissionBroker;\n host?: string;\n maxBodyBytes?: number;\n isSessionActive?: (sessionId: string) => boolean;\n onEvent?: (event: AuthenticatedHookEvent) => void;\n}\n\nexport interface AuthenticatedHookEvent {\n sessionId: string;\n provider: HookProviderId;\n event: string;\n requestId?: string;\n payload: Record<string, unknown>;\n}\n\ninterface HookRequestBody {\n sessionId?: unknown;\n provider?: unknown;\n marker?: unknown;\n event?: unknown;\n requestId?: unknown;\n payload?: unknown;\n}\n\nfunction getBearerToken(req: IncomingMessage): string | null {\n const header = req.headers.authorization;\n if (!header?.startsWith(\"Bearer \")) return null;\n return header.slice(\"Bearer \".length).trim() || null;\n}\n\nexport class HookServer {\n private server: Server | null = null;\n private readonly host: string;\n private readonly maxBodyBytes: number;\n\n constructor(private readonly options: HookServerOptions) {\n this.host = options.host ?? \"127.0.0.1\";\n this.maxBodyBytes = options.maxBodyBytes ?? 1024 * 1024;\n }\n\n start(): Promise<void> {\n if (this.server) return Promise.resolve();\n this.server = createServer((req, res) => {\n this.handle(req, res).catch((err) => {\n serviceLogger.error({ err: String(err) }, \"Hook request failed\");\n this.writeJson(res, 500, { error: \"internal_error\" });\n });\n });\n\n return new Promise((resolve, reject) => {\n const onError = (err: Error) => {\n this.server?.off(\"listening\", onListening);\n reject(err);\n };\n const onListening = () => {\n this.server?.off(\"error\", onError);\n serviceLogger.info({ host: this.host, port: this.options.port }, \"Hook server listening\");\n resolve();\n };\n this.server!.once(\"error\", onError);\n this.server!.once(\"listening\", onListening);\n this.server!.listen(this.options.port, this.host);\n });\n }\n\n close(): Promise<void> {\n if (!this.server) return Promise.resolve();\n const server = this.server;\n this.server = null;\n return new Promise((resolve, reject) => {\n server.close((err) => (err ? reject(err) : resolve()));\n });\n }\n\n getListeningPort(): number | null {\n const address = this.server?.address();\n if (!address || typeof address === \"string\") return null;\n return (address as AddressInfo).port;\n }\n\n private async handle(req: IncomingMessage, res: ServerResponse): Promise<void> {\n if (req.method !== \"POST\" || req.url !== \"/hook\") {\n this.writeJson(res, 404, { error: \"not_found\" });\n return;\n }\n\n const token = getBearerToken(req);\n if (!token) {\n this.writeJson(res, 401, { error: \"missing_token\" });\n return;\n }\n\n const body = await this.readBody(req);\n const parsed = JSON.parse(body) as HookRequestBody;\n const provider = asProvider(parsed.provider);\n if (\n typeof parsed.sessionId !== \"string\" ||\n typeof parsed.marker !== \"string\" ||\n typeof parsed.event !== \"string\" ||\n !provider\n ) {\n this.writeJson(res, 400, { error: \"invalid_hook_payload\" });\n return;\n }\n\n const binding = this.options.registry.verify({\n sessionId: parsed.sessionId,\n marker: parsed.marker,\n token,\n provider,\n });\n if (!binding) {\n this.writeJson(res, 403, { error: \"invalid_hook_credentials\" });\n return;\n }\n if (this.options.isSessionActive && !this.options.isSessionActive(binding.sessionId)) {\n serviceLogger.info(\n { sessionId: binding.sessionId, provider: binding.provider, event: parsed.event },\n \"Provider hook ignored for inactive session\",\n );\n this.writeProviderResponse(res, this.toNeutralProviderResponse(provider, parsed.event));\n return;\n }\n\n const payload = asRecord(parsed.payload);\n const requestId =\n typeof parsed.requestId === \"string\"\n ? parsed.requestId\n : typeof payload.tool_use_id === \"string\"\n ? payload.tool_use_id\n : undefined;\n const event: AuthenticatedHookEvent = {\n sessionId: binding.sessionId,\n provider: binding.provider,\n event: parsed.event,\n ...(requestId !== undefined ? { requestId } : {}),\n payload,\n };\n\n if (event.event === \"PermissionRequest\") {\n await this.handlePermissionRequest(event, res);\n return;\n }\n\n this.options.onEvent?.(event);\n this.writeProviderResponse(res, this.toNeutralProviderResponse(event.provider, event.event));\n }\n\n private async handlePermissionRequest(\n event: AuthenticatedHookEvent,\n res: ServerResponse,\n ): Promise<void> {\n const requestId =\n event.requestId ??\n (typeof event.payload.tool_use_id === \"string\" ? event.payload.tool_use_id : undefined) ??\n `${event.sessionId}:${Date.now()}`;\n const toolName = toolNameFromPayload(event.payload);\n const input = asRecord(event.payload.input ?? event.payload.tool_input);\n\n this.options.onEvent?.({ ...event, requestId });\n const decision = await this.options.permissionBroker.request({\n requestId,\n sessionId: event.sessionId,\n provider: event.provider,\n toolName,\n input,\n });\n this.writeJson(res, 200, this.toProviderDecision(event.event, decision));\n }\n\n private toProviderDecision(\n eventName: string,\n decision: { behavior: \"allow\" | \"deny\"; message?: string },\n ): object {\n if (eventName === \"PreToolUse\") {\n return {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: decision.behavior,\n ...(decision.message ? { permissionDecisionReason: decision.message } : {}),\n },\n };\n }\n\n return {\n hookSpecificOutput: {\n hookEventName: \"PermissionRequest\",\n decision,\n },\n };\n }\n\n private toNeutralProviderResponse(provider: HookProviderId, eventName: string): object | null {\n if (eventName === \"PreToolUse\") {\n if (provider === \"codex\") {\n return null;\n }\n return {\n hookSpecificOutput: {\n hookEventName: \"PreToolUse\",\n permissionDecision: \"defer\",\n },\n };\n }\n\n return null;\n }\n\n private writeProviderResponse(res: ServerResponse, payload: object | null): void {\n if (res.headersSent) return;\n if (payload === null) {\n res.writeHead(200);\n res.end();\n return;\n }\n this.writeJson(res, 200, payload);\n }\n\n private readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n let body = \"\";\n let size = 0;\n req.setEncoding(\"utf8\");\n req.on(\"data\", (chunk: string) => {\n size += Buffer.byteLength(chunk);\n if (size > this.maxBodyBytes) {\n reject(new Error(\"hook body too large\"));\n req.destroy();\n return;\n }\n body += chunk;\n });\n req.on(\"end\", () => resolve(body));\n req.on(\"error\", reject);\n });\n }\n\n private writeJson(res: ServerResponse, statusCode: number, payload: object): void {\n if (res.headersSent) return;\n res.writeHead(statusCode, { \"content-type\": \"application/json; charset=utf-8\" });\n res.end(JSON.stringify(payload));\n }\n}\n","import { flushLogger } from \"@dev-anywhere/shared/logger\";\nimport { serviceLogger } from \"../common/logger.js\";\nimport { HOOK_REGISTRY_PATH } from \"../common/paths.js\";\nimport type { ProviderHookContext } from \"../providers/index.js\";\nimport type { AgentStatusRegistry } from \"./agent-status-registry.js\";\nimport { HookEventRouter } from \"./hook-event-router.js\";\nimport { HookRegistry } from \"./hook-registry.js\";\nimport { HookServer } from \"./hook-server.js\";\nimport type { PermissionBroker } from \"./permission-broker.js\";\nimport type { RelayConnection } from \"./relay-connection.js\";\nimport type { SessionManager } from \"./session-manager.js\";\nimport type { SessionState } from \"@dev-anywhere/shared\";\n\ninterface ProviderHookRuntimeOptions {\n hookPort?: number;\n permissionBroker: PermissionBroker;\n sessionManager: SessionManager;\n relayConnection: RelayConnection;\n agentStatusRegistry: AgentStatusRegistry;\n changeSessionState: (sessionId: string, next: SessionState) => boolean;\n}\n\ninterface ProviderHookRuntime {\n hookRegistry: HookRegistry;\n hookEventRouter: HookEventRouter;\n hookServer: HookServer;\n createHookContext: (\n sessionId: string,\n provider: ProviderHookContext[\"provider\"],\n ) => ProviderHookContext;\n}\n\nexport async function createProviderHookRuntime(\n options: ProviderHookRuntimeOptions,\n): Promise<ProviderHookRuntime> {\n const hookRegistry = new HookRegistry({ persistPath: HOOK_REGISTRY_PATH });\n const hookEventRouter = new HookEventRouter({\n relayConnection: options.relayConnection,\n agentStatusRegistry: options.agentStatusRegistry,\n changeSessionState: options.changeSessionState,\n getSessionMode: (sessionId) => options.sessionManager.getSession(sessionId)?.mode,\n });\n const port = options.hookPort ?? 17654;\n const hookServer = new HookServer({\n port,\n registry: hookRegistry,\n permissionBroker: options.permissionBroker,\n isSessionActive: (sessionId) => !!options.sessionManager.getSession(sessionId),\n onEvent: (event) => {\n serviceLogger.info(\n {\n sessionId: event.sessionId,\n provider: event.provider,\n event: event.event,\n requestId: event.requestId,\n },\n \"Provider hook event received\",\n );\n hookEventRouter.handle(event);\n },\n });\n\n try {\n await hookServer.start();\n } catch (err) {\n const msg = `Failed to start hook server on 127.0.0.1:${port}: ${String(err)}`;\n serviceLogger.error(msg);\n console.error(msg);\n await flushLogger(serviceLogger);\n process.exit(1);\n }\n\n const hookUrl = `http://127.0.0.1:${hookServer.getListeningPort() ?? port}/hook`;\n const createHookContext: ProviderHookRuntime[\"createHookContext\"] = (sessionId, provider) => {\n const credentials = hookRegistry.registerSession(sessionId, provider);\n return {\n provider,\n sessionId,\n hookUrl,\n marker: credentials.marker,\n token: credentials.token,\n };\n };\n\n return {\n hookRegistry,\n hookEventRouter,\n hookServer,\n createHookContext,\n };\n}\n","import { unlinkSync } from \"node:fs\";\nimport { flushLogger, type Logger } from \"@dev-anywhere/shared/logger\";\n\n// 收尾步骤里依赖的所有外部资源都通过 deps 注入;shutdown 函数本身只负责正确顺序与\n// 单次执行守卫,便于以纯单元测试的方式覆盖双信号场景而不必拉起整个 service。\nexport interface ServeShutdownDeps {\n logger: Logger;\n sessionManagerStopReaper: () => void;\n relayRouterDestroy: () => void;\n hookServerClose: () => Promise<void>;\n relayConnectionClose: () => void;\n workerRegistryDestroyAll: () => void;\n hostedPtyRegistryDestroyAll: () => void;\n ipcServerClose: () => void;\n sockPath: string;\n pidPath: string;\n exit?: (code: number) => void;\n}\n\n// 双重 SIGTERM / SIGTERM+SIGINT 并发触发时,第二次直接返回。\n// 真实场景:systemd TimeoutStopSec 到期再发一次 SIGTERM;用户连按两次 Ctrl+C 同样会双触发。\n// 没有守卫时,第二路 process.exit(0) 可能在第一路 await flushLogger 完成前先返回,截断日志。\nexport function createServeShutdown(deps: ServeShutdownDeps): () => Promise<void> {\n const exit = deps.exit ?? ((code: number) => process.exit(code));\n let shuttingDown = false;\n return async () => {\n if (shuttingDown) return;\n shuttingDown = true;\n deps.logger.info(\"Shutting down service\");\n deps.sessionManagerStopReaper();\n // 先 destroy router:清掉 pending session-create retry timer,并 cleanupPendingJsonSession\n // 把已 spawn 但还未 connect 的 worker 子进程收掉,否则进入 destroyAll 时这批子进程\n // 会在 timer 命中后失去 parent 引用变孤儿(只有 sock destroy,没人发 SIGTERM 给 worker)。\n deps.relayRouterDestroy();\n await deps.hookServerClose();\n deps.relayConnectionClose();\n deps.workerRegistryDestroyAll();\n deps.hostedPtyRegistryDestroyAll();\n deps.ipcServerClose();\n try {\n unlinkSync(deps.sockPath);\n } catch {\n // 关闭时 socket 文件可能已被删除\n }\n try {\n unlinkSync(deps.pidPath);\n } catch {\n // 关闭时 PID 文件可能已被删除\n }\n await flushLogger(deps.logger);\n exit(0);\n };\n}\n"],"mappings":"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","names":["createServer","unlinkSync","writeFileSync","rmSync","readFileSync","existsSync","nanoid","existsSync","readFileSync","nanoid","readdir","join","join","homedir","resolve","join","readFileSync","homedir","join","readdir","join","existsSync","readdirSync","resolve","existsSync","readdirSync","join","nanoid","join","nanoid","readFileSync","isAbsolute","isAbsolute","stat","readFileSync","mkdirSync","writeFileSync","tmpdir","extname","join","nanoid","DEFAULT_DATA_DIR","join","tmpdir","normalizeBase64","extname","nanoid","mkdirSync","writeFileSync","readFileSync","realpathSync","statSync","tmpdir","isAbsolute","resolve","isAbsolute","tmpdir","realpathSync","resolve","errorCode","stat","statSync","readFileSync","homedir","statSync","stat","statSync","homedir","statSync","isAbsolute","nanoid","isAbsolute","stat","statSync","nanoid","statuses","resolve","existsSync","readFileSync","unlinkSync","connect","resolve","connect","existsSync","unlinkSync","readFileSync","resolveInterruptedApprovals","existsSync","mkdirSync","readFileSync","writeFileSync","existsSync","readFileSync","mkdirSync","writeFileSync","resolve","unlinkSync","unlinkSync","unlinkSync","rmSync","createServer","writeFileSync"]}