@dev-anywhere/proxy 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ import {
10
10
  LineBuffer,
11
11
  createWorkerReader,
12
12
  serializeWorkerMsg
13
- } from "./chunk-NZZXBVO2.js";
13
+ } from "./chunk-L6J5QCFH.js";
14
14
  import "./chunk-NQDJ6QAM.js";
15
15
  import "./chunk-PWG6K5QB.js";
16
16
 
@@ -6,10 +6,10 @@ import {
6
6
  decidePtySemanticTransition,
7
7
  extractOscSequences,
8
8
  extractOscSignals
9
- } from "./chunk-OBYEKZWC.js";
9
+ } from "./chunk-Q7AQFYHG.js";
10
10
  import {
11
11
  spawnScript
12
- } from "./chunk-ZUWAB67J.js";
12
+ } from "./chunk-IHUQNJF6.js";
13
13
  import {
14
14
  CLAUDE_PROVIDER,
15
15
  CODEX_PROVIDER
@@ -19,7 +19,7 @@ import {
19
19
  createIpcReader,
20
20
  encodeBinaryIpcFrame,
21
21
  serializeIpc
22
- } from "./chunk-NZZXBVO2.js";
22
+ } from "./chunk-L6J5QCFH.js";
23
23
  import {
24
24
  terminalLogger
25
25
  } from "./chunk-7UOPAMX7.js";
@@ -218,14 +218,10 @@ async function ensureService(autoStart = true) {
218
218
  if (!autoStart) throw new Error("Service is not running");
219
219
  if (existsSync(STOPPED_PATH)) unlinkSync(STOPPED_PATH);
220
220
  terminalLogger.info("Auto-starting serve daemon");
221
- const child = spawnScript(
222
- new URL("../serve", import.meta.url),
223
- ["--profile", PROFILE_NAME, ...daemonRelayArgs()],
224
- {
225
- env: { ...process.env },
226
- logger: terminalLogger
227
- }
228
- );
221
+ const child = spawnScript("serve", ["--profile", PROFILE_NAME, ...daemonRelayArgs()], {
222
+ env: { ...process.env },
223
+ logger: terminalLogger
224
+ });
229
225
  let childFailed = false;
230
226
  let exitCode = null;
231
227
  let exitSignal = null;
@@ -565,10 +561,7 @@ var TerminalSession = class {
565
561
  this.socket,
566
562
  (msg) => {
567
563
  if (msg.type === "pty_input" && msg.sessionId === this.sessionId) {
568
- terminalLogger.debug(
569
- { sessionId: this.sessionId, bytes: msg.data.length },
570
- "Remote input received"
571
- );
564
+ terminalLogger.debug({ sessionId: this.sessionId, bytes: msg.data.length }, "Remote input received");
572
565
  this.ptyManager?.write(msg.data);
573
566
  } else if (msg.type === "pty_detach" && msg.sessionId === this.sessionId) {
574
567
  this.detachRemoteView();
@@ -723,4 +716,4 @@ async function startTerminal(providerArgs, providerId = providerFromEnv()) {
723
716
  export {
724
717
  startTerminal
725
718
  };
726
- //# sourceMappingURL=terminal-YGYIRO7H.js.map
719
+ //# sourceMappingURL=terminal-THJIKZQG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/terminal.ts","../src/terminal/tty.ts","../src/terminal/pty-manager.ts","../src/terminal/cwd.ts","../src/terminal/serve-bootstrap.ts","../src/terminal/idle-checker.ts","../src/terminal/serve-socket-swap.ts","../src/terminal/state.ts"],"sourcesContent":["import type { Socket } from \"node:net\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport { readTtySize, notifyUser } from \"./terminal/tty.js\";\nimport { PtyManager } from \"./terminal/pty-manager.js\";\nimport { resolveTerminalCwd } from \"./terminal/cwd.js\";\nimport { ensureService, tryConnect, waitForMessage } from \"./terminal/serve-bootstrap.js\";\nimport { createIdleChecker, type IdleChecker } from \"./terminal/idle-checker.js\";\nimport { swapServeSocket } from \"./terminal/serve-socket-swap.js\";\nimport pkg from \"@xterm/headless\";\nconst { Terminal: HeadlessTerminal } = pkg;\nimport { SerializeAddon } from \"@xterm/addon-serialize\";\nimport { UnicodeGraphemesAddon } from \"@xterm/addon-unicode-graphemes\";\nimport { extractOscSequences, extractOscSignals } from \"./common/osc-extractor.js\";\nimport { createFSM, type PtySemanticState } from \"@dev-anywhere/shared\";\nimport { decidePtySemanticTransition } from \"./common/pty-semantic-machine.js\";\nimport { TerminalState, TERMINAL_TRANSITIONS, createExitHandler } from \"./terminal/state.js\";\nimport { existsSync } from \"node:fs\";\nimport { SOCK_PATH, STOPPED_PATH, tildify } from \"./common/paths.js\";\nimport {\n createIpcReader,\n serializeIpc,\n encodeBinaryIpcFrame,\n type IpcMessage,\n} from \"./ipc/ipc-protocol.js\";\nimport { terminalLogger as log } from \"./common/logger.js\";\nimport {\n CLAUDE_PROVIDER,\n CODEX_PROVIDER,\n type ProviderAdapter,\n type ProviderHookContext,\n type ProviderId,\n} from \"./providers/index.js\";\n\n// idle 检测:超过 IDLE_THRESHOLD_MS 无输出则翻转 working -> turn_complete\nconst IDLE_CHECK_INTERVAL_MS = 3_000;\nconst IDLE_THRESHOLD_MS = 3_000;\n\n// serve 连接断开后的重连重试参数\nconst RECONNECT_INITIAL_DELAY_MS = 1_000;\nconst RECONNECT_MAX_DELAY_MS = 5_000;\n// 连续 spawn 失败到达阈值后停止自动 spawn,降为被动 tryConnect 轮询。\n// 作用:环境异常(端口占用、依赖缺失、权限不足)时避免反复拉起短命子进程把日志刷爆。\nconst SPAWN_FAILURE_THRESHOLD = 3;\n\nconst PROVIDERS: Record<ProviderId, ProviderAdapter> = {\n claude: CLAUDE_PROVIDER,\n codex: CODEX_PROVIDER,\n};\n\nclass TerminalSession {\n private readonly fsm = createFSM<TerminalState>({\n initial: TerminalState.INIT,\n transitions: TERMINAL_TRANSITIONS,\n onTransition: (from, to) => log.info({ from, to }, \"Terminal state transition\"),\n });\n private readonly sessionCwd = resolveTerminalCwd();\n // socket 在 run() 中连上 serve 后首次赋值;reconnect 会重新赋值为新实例\n private socket!: Socket;\n private sessionId: string | null = null;\n private hookContext: ProviderHookContext | null = null;\n private ptyManager: PtyManager | null = null;\n private lastOutputTime = 0;\n private idleChecker: IdleChecker | null = null;\n private currentPtyState: PtySemanticState = \"turn_complete\";\n // headless terminal 在本进程维护,用于按需 serialize() 给远程 client\n private headlessTerminal: InstanceType<typeof HeadlessTerminal> | null = null;\n private serializeAddon: SerializeAddon | null = null;\n private outputSeq = 0;\n private remoteDetached = false;\n // 记录上次 bridge 连接状态,避免重连抖动重复打印 banner;\n // 初值 null 确保首次状态变更(无论 true/false)都触发一次输出\n private lastBridgeConnected: boolean | null = null;\n // 收尾函数在 run() 里创建一次,PTY 退出与 SIGTERM 共用;内部通过 fsm EXITED 检查短路\n private cleanupAndExit!: (code: number) => void;\n\n constructor(\n private readonly provider: ProviderAdapter,\n private readonly providerArgs: string[],\n ) {}\n\n async run(): Promise<void> {\n log.info(\"Terminal starting\");\n this.fsm.transitionTo(TerminalState.CONNECTING_SERVICE);\n this.socket = await ensureService();\n\n await this.createSession();\n this.initHeadlessTerminal();\n this.cleanupAndExit = createExitHandler({\n fsm: this.fsm,\n getSocket: () => this.socket,\n getSessionId: () => this.sessionId,\n stopIdleChecker: () => this.idleChecker?.stop(),\n disposeRenderResources: () => {\n this.headlessTerminal?.dispose();\n this.headlessTerminal = null;\n this.serializeAddon = null;\n },\n });\n\n this.setupSocketHandlers();\n this.startPtyManager();\n\n this.socket.write(\n serializeIpc({ type: \"pty_register\", sessionId: this.sessionId!, pid: process.pid }),\n );\n this.replayCurrentPtyState();\n this.fsm.transitionTo(TerminalState.RUNNING);\n this.setupIdleCheck();\n\n process.on(\"SIGTERM\", () => {\n log.info({ sessionId: this.sessionId }, \"SIGTERM received, shutting down\");\n this.cleanupAndExit(143);\n });\n }\n\n private async createSession(): Promise<void> {\n this.fsm.transitionTo(TerminalState.CREATING_SESSION);\n const responsePromise = waitForMessage(this.socket, \"session_create_response\");\n this.socket.write(\n serializeIpc({\n type: \"session_create_request\",\n mode: \"pty\",\n provider: this.provider.id,\n cwd: this.sessionCwd,\n name: tildify(this.sessionCwd),\n pid: process.pid,\n }),\n );\n const response = await responsePromise;\n if (response.error) {\n throw new Error(`Failed to create session: ${response.error}`);\n }\n this.sessionId = response.sessionId;\n this.hookContext = response.hook ?? null;\n }\n\n private initHeadlessTerminal(): void {\n const { cols, rows } = readTtySize(process.stdout);\n log.info(\n { sessionId: this.sessionId, cols, rows },\n \"Session created, initializing headless terminal\",\n );\n this.headlessTerminal = new HeadlessTerminal({\n cols,\n rows,\n scrollback: 5000,\n allowProposedApi: true,\n });\n this.serializeAddon = new SerializeAddon();\n // UnicodeGraphemesAddon activate() 里会设置 activeVersion = '15-graphemes'\n this.headlessTerminal.loadAddon(this.serializeAddon);\n this.headlessTerminal.loadAddon(new UnicodeGraphemesAddon());\n }\n\n private startPtyManager(): void {\n this.ptyManager = new PtyManager({\n provider: this.provider,\n providerArgs: this.providerArgs,\n cwd: this.sessionCwd,\n hook: this.hookContext ?? undefined,\n tap: (data) => this.handlePtyData(data),\n stdin: process.stdin,\n stdout: process.stdout,\n onResize: (newCols, newRows) => {\n if (this.headlessTerminal) this.headlessTerminal.resize(newCols, newRows);\n if (this.socket.writable && this.sessionId) {\n this.socket.write(\n serializeIpc({\n type: \"pty_resize\",\n sessionId: this.sessionId,\n cols: newCols,\n rows: newRows,\n }),\n );\n }\n },\n onSessionExit: (code: number) => {\n log.info({ sessionId: this.sessionId, exitCode: code }, \"PTY exited, cleaning up\");\n this.cleanupAndExit(code);\n },\n });\n this.ptyManager.start();\n log.info({ sessionId: this.sessionId }, \"PTY started with headless terminal\");\n }\n\n // PTY 的每一帧输出都要:追到 headless terminal 状态、推 binary IPC、提取 provider 语义事件\n private handlePtyData(data: string): void {\n this.lastOutputTime = Date.now();\n this.outputSeq += 1;\n\n if (this.headlessTerminal) this.headlessTerminal.write(data);\n\n if (!this.remoteDetached && this.socket.writable && this.sessionId) {\n this.socket.write(\n encodeBinaryIpcFrame(this.sessionId, Buffer.from(data, \"utf-8\"), this.outputSeq),\n );\n }\n\n const oscSequences = extractOscSequences(data);\n const signal = extractOscSignals(data, this.provider.id);\n if (oscSequences.length > 0) {\n log.debug(\n {\n sessionId: this.sessionId,\n oscSequences,\n signal,\n },\n \"PTY OSC sequences parsed\",\n );\n }\n if (signal?.title) {\n this.sendTerminalTitle(signal.title);\n }\n\n // 语义状态机决策(六条规则)抽到 common/pty-semantic-machine:terminal 进程仅 emit 事件,\n // session FSM 副作用由 serve 端在收到 pty_state IPC 后驱动。\n const decision = decidePtySemanticTransition({\n currentState: this.currentPtyState,\n signal: signal ?? null,\n });\n this.currentPtyState = decision.nextState;\n if (decision.emit) {\n this.sendPtyState(decision.nextState, decision.meta);\n }\n }\n\n private sendTerminalTitle(title: string): void {\n if (this.remoteDetached || !this.socket.writable || !this.sessionId) return;\n this.socket.write(\n serializeIpc({\n type: \"pty_title_change\",\n sessionId: this.sessionId,\n title,\n }),\n );\n }\n\n private sendPtyState(state: PtySemanticState, meta?: { title?: string; tool?: string }): void {\n if (this.remoteDetached || !this.socket.writable || !this.sessionId) return;\n this.socket.write(\n serializeIpc({\n type: \"pty_semantic_event\",\n sessionId: this.sessionId,\n state,\n ...(meta?.title !== undefined ? { title: meta.title } : {}),\n ...(meta?.tool !== undefined ? { tool: meta.tool } : {}),\n }),\n );\n log.info(\n { sessionId: this.sessionId, state, title: meta?.title, tool: meta?.tool },\n \"PTY semantic event pushed\",\n );\n }\n\n private replayCurrentPtyState(): void {\n if (this.currentPtyState === \"turn_complete\") return;\n this.sendPtyState(this.currentPtyState);\n }\n\n private handleBridgeStatus(connected: boolean): void {\n if (this.remoteDetached) return;\n if (this.lastBridgeConnected === connected) return;\n this.lastBridgeConnected = connected;\n log.info({ connected }, \"Bridge status changed, notifying user\");\n notifyUser(connected ? \"relay online\" : \"relay offline — remote viewing unavailable\");\n }\n\n private setupSocketHandlers(): void {\n createIpcReader(\n this.socket,\n (msg: IpcMessage) => {\n if (msg.type === \"pty_input\" && msg.sessionId === this.sessionId) {\n log.debug({ sessionId: this.sessionId, bytes: msg.data.length }, \"Remote input received\");\n this.ptyManager?.write(msg.data);\n } else if (msg.type === \"pty_detach\" && msg.sessionId === this.sessionId) {\n this.detachRemoteView();\n } else if (msg.type === \"bridge_status\") {\n this.handleBridgeStatus(msg.connected);\n } else if (msg.type === \"pty_subscribe\" && msg.sessionId === this.sessionId) {\n if (this.serializeAddon && this.headlessTerminal) {\n const data = this.serializeAddon.serialize();\n this.socket.write(\n serializeIpc({\n type: \"pty_snapshot\",\n sessionId: msg.sessionId,\n cols: this.headlessTerminal.cols,\n rows: this.headlessTerminal.rows,\n data,\n outputSeq: this.outputSeq,\n requestId: msg.requestId,\n }),\n );\n log.info(\n {\n sessionId: this.sessionId,\n cols: this.headlessTerminal.cols,\n rows: this.headlessTerminal.rows,\n bytes: data.length,\n },\n \"Snapshot sent via IPC\",\n );\n }\n }\n },\n undefined,\n (err, line) => {\n log.warn(\n { err: err.message, lineLen: line.length },\n \"Serve IPC message dropped (parse/schema error)\",\n );\n },\n );\n\n this.socket.on(\"close\", () => {\n log.info(\"Serve socket closed\");\n if (this.remoteDetached) {\n log.info(\"Remote view detached, skipping serve reconnect\");\n return;\n }\n if (!this.fsm.isIn([TerminalState.RECONNECTING, TerminalState.EXITED])) {\n this.fsm.transitionTo(TerminalState.RECONNECTING);\n this.reconnectToServe();\n }\n });\n\n // socket error 通常和 close 成对出现;这里只记 warn 避免静默吞错,重连仍由 close handler 触发\n this.socket.on(\"error\", (err) => {\n log.warn({ err: err.message }, \"Serve socket error\");\n });\n }\n\n // 超过 IDLE_THRESHOLD_MS 无 PTY 输出则从 working 翻回 turn_complete\n private setupIdleCheck(): void {\n this.idleChecker?.stop();\n this.idleChecker = createIdleChecker({\n intervalMs: IDLE_CHECK_INTERVAL_MS,\n thresholdMs: IDLE_THRESHOLD_MS,\n getLastOutputTime: () => this.lastOutputTime,\n setLastOutputTime: (value) => {\n this.lastOutputTime = value;\n },\n getCurrentState: () => this.currentPtyState,\n onIdle: () => {\n this.currentPtyState = \"turn_complete\";\n this.sendPtyState(\"turn_complete\");\n },\n });\n this.idleChecker.start();\n }\n\n private async reconnectToServe(): Promise<void> {\n log.info(\"Serve connection lost, starting reconnection\");\n\n // 两条路径都不该再继续 spawn daemon:\n // - STOPPED=true:用户主动 dev-anywhere stop,不要对抗用户意图。\n // - consecutiveSpawnFailures 跨过阈值:说明环境有持续性问题,spawn 再多也白搭。\n // 进入 passive 后仅做 tryConnect 等待,daemon 起来或用户 dev-anywhere start 后自动恢复。\n let consecutiveSpawnFailures = 0;\n\n for (let i = 0; ; i++) {\n if (this.remoteDetached) return;\n await sleep(Math.min(RECONNECT_INITIAL_DELAY_MS * (i + 1), RECONNECT_MAX_DELAY_MS));\n\n const stopped = existsSync(STOPPED_PATH);\n const degraded = consecutiveSpawnFailures >= SPAWN_FAILURE_THRESHOLD;\n const passive = stopped || degraded;\n\n try {\n log.debug({ attempt: i + 1, stopped, degraded }, \"Reconnect attempt\");\n const newSocket = passive ? await tryConnect(SOCK_PATH) : await ensureService();\n if (!newSocket) continue;\n\n if (degraded) notifyUser(\"serve daemon reachable, reconnected\");\n consecutiveSpawnFailures = 0;\n\n this.socket = swapServeSocket(this.socket, newSocket);\n log.info({ attempt: i + 1, sessionId: this.sessionId }, \"Reconnected to serve\");\n\n this.setupSocketHandlers();\n\n if (this.sessionId) {\n this.fsm.transitionTo(TerminalState.CREATING_SESSION);\n this.socket.write(\n serializeIpc({\n type: \"session_create_request\",\n mode: \"pty\",\n provider: this.provider.id,\n cwd: this.sessionCwd,\n name: tildify(this.sessionCwd),\n pid: process.pid,\n sessionId: this.sessionId,\n }),\n );\n const resp = await waitForMessage(this.socket, \"session_create_response\");\n if (!resp.error) {\n this.sessionId = resp.sessionId;\n this.socket.write(\n serializeIpc({ type: \"pty_register\", sessionId: this.sessionId, pid: process.pid }),\n );\n this.replayCurrentPtyState();\n this.fsm.transitionTo(TerminalState.RUNNING);\n log.info({ sessionId: this.sessionId }, \"Session re-registered after reconnect\");\n }\n } else {\n this.fsm.transitionTo(TerminalState.RUNNING);\n }\n\n return;\n } catch (err) {\n // passive 模式走 tryConnect,失败返回 null 不抛;这里只可能是 ensureService spawn 失败\n if (!passive) {\n consecutiveSpawnFailures++;\n if (consecutiveSpawnFailures === SPAWN_FAILURE_THRESHOLD) {\n notifyUser(\n `serve daemon spawn failed ${SPAWN_FAILURE_THRESHOLD}x — auto-spawn disabled; check environment or run 'dev-anywhere start'`,\n );\n }\n }\n log.debug(\n { err: err instanceof Error ? err.message : err, attempt: i + 1, degraded },\n \"Reconnect attempt failed\",\n );\n }\n }\n }\n\n private detachRemoteView(): void {\n const sessionId = this.sessionId;\n if (!sessionId) return;\n this.remoteDetached = true;\n this.sessionId = null;\n this.hookContext = null;\n this.currentPtyState = \"turn_complete\";\n log.info({ sessionId }, \"Remote view detached; local PTY keeps running\");\n notifyUser(\"remote viewing detached\");\n if (this.socket.writable) this.socket.end();\n }\n}\n\nfunction providerFromEnv(): ProviderId {\n return process.env.DEV_ANYWHERE_PROVIDER === \"codex\" ? \"codex\" : \"claude\";\n}\n\nexport async function startTerminal(\n providerArgs: string[],\n providerId: ProviderId = providerFromEnv(),\n): Promise<void> {\n await new TerminalSession(PROVIDERS[providerId], providerArgs).run();\n}\n","// 读 stdout cols/rows,非 TTY 抛错。\nexport function readTtySize(stream: NodeJS.WriteStream): { cols: number; rows: number } {\n const { columns, rows } = stream;\n if (columns === undefined || rows === undefined) {\n throw new Error(\n \"stdout is not an interactive TTY (columns/rows undefined); dev-anywhere requires running in a real terminal\",\n );\n }\n return { cols: columns, rows };\n}\n\n// 发一条 OSC 9 iTerm2-style 系统通知 + 响铃。iTerm2 / kitty / wezterm 等会弹出带 message\n// 的系统通知;不认 OSC 9 的终端会忽略转义序列只剩下 BEL 响铃。\n// 用此而非 stderr banner 的原因:dev-anywhere 对 Claude PTY 画面保持透明是硬约束,\n// banner 会挤掉 Claude 的渲染行,OSC 9 不占画面,BEL 是纯听觉信号。\nexport function notifyUser(message: string): void {\n process.stderr.write(`\\x1b]9;${message}\\x07`);\n}\n\n// Provider TUI 可能开启 bracketed paste、application cursor/keypad、mouse tracking、\n// xterm modifyOtherKeys 或 kitty keyboard protocol。若 provider 被远程终止或异常退出,\n// 这些模式可能来不及自行恢复,外层 shell 会把 Ctrl-C 显示成 \";5;99~\" 一类残留序列。\nexport function restoreHostTerminalModes(stream: NodeJS.WriteStream): void {\n if (!stream.isTTY) return;\n const restoreSequences = [\n \"\\x1b[?1l\", // application cursor keys off\n \"\\x1b>\", // application keypad off\n \"\\x1b[?1000l\",\n \"\\x1b[?1002l\",\n \"\\x1b[?1003l\",\n \"\\x1b[?1004l\",\n \"\\x1b[?1006l\",\n \"\\x1b[?1015l\",\n \"\\x1b[?2004l\", // bracketed paste off\n \"\\x1b[>4;0m\", // xterm modifyOtherKeys off\n \"\\x1b[<u\", // kitty keyboard protocol off\n ].join(\"\");\n stream.write(restoreSequences);\n}\n","import * as pty from \"node-pty\";\nimport type { IPty } from \"node-pty\";\nimport type { ProviderAdapter, ProviderHookContext } from \"../providers/index.js\";\nimport { readTtySize, restoreHostTerminalModes } from \"./tty.js\";\n\ninterface PtyManagerOptions {\n provider: ProviderAdapter;\n providerArgs: string[];\n cwd: string;\n hook?: ProviderHookContext;\n tap: (data: string) => void;\n stdin: NodeJS.ReadStream;\n stdout: NodeJS.WriteStream;\n onSessionExit?: (code: number) => void | Promise<void>;\n onResize?: (cols: number, rows: number) => void;\n}\n\nexport class PtyManager {\n private child: IPty | null = null;\n private resizeTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly provider: ProviderAdapter;\n private readonly providerArgs: string[];\n private readonly cwd: string;\n private readonly hook?: ProviderHookContext;\n private readonly tap: (data: string) => void;\n private readonly stdin: NodeJS.ReadStream;\n private readonly stdout: NodeJS.WriteStream;\n private readonly onSessionExit?: (code: number) => void;\n private readonly onResize?: (cols: number, rows: number) => void;\n\n constructor(options: PtyManagerOptions) {\n this.provider = options.provider;\n this.providerArgs = options.providerArgs;\n this.cwd = options.cwd;\n this.hook = options.hook;\n this.tap = options.tap;\n this.stdin = options.stdin;\n this.stdout = options.stdout;\n this.onSessionExit = options.onSessionExit;\n this.onResize = options.onResize;\n }\n\n start(): void {\n const { cols, rows } = readTtySize(this.stdout);\n\n const command = this.provider.buildTerminalCommand(\n { args: this.providerArgs, hook: this.hook },\n process.env,\n );\n const child = pty.spawn(command.command, command.args, {\n name: process.env.TERM ?? \"xterm-256color\",\n cols,\n rows,\n cwd: this.cwd,\n env: command.env as Record<string, string>,\n });\n this.child = child;\n\n // raw mode 仅在 stdin 为 TTY 时开启\n const isInteractive = this.stdin.isTTY === true;\n if (isInteractive) {\n this.stdin.setRawMode(true);\n }\n this.stdin.resume();\n\n // stdin -> PTY\n this.stdin.on(\"data\", (data: Buffer) => {\n child.write(data.toString());\n });\n\n // PTY -> stdout + tap\n child.onData((data: string) => this.handleData(data));\n\n // resize 防抖,50ms 窗口合并快速连续的尺寸变化。timer 持有在实例字段上,cleanup 时\n // clearTimeout 防止 PTY 退出后 callback 还在等待,导致 child.resize 调用已 kill 的子进程。\n this.stdout.on(\"resize\", () => {\n if (this.resizeTimer) clearTimeout(this.resizeTimer);\n this.resizeTimer = setTimeout(() => {\n this.resizeTimer = null;\n if (!this.child) return;\n const { cols: newCols, rows: newRows } = readTtySize(this.stdout);\n this.child.resize(newCols, newRows);\n this.onResize?.(newCols, newRows);\n }, 50);\n });\n\n // 子进程退出,按 Unix 惯例处理信号退出码,通过回调通知调用方\n child.onExit(({ exitCode, signal }) => {\n if (isInteractive) this.disableRawModeQuiet();\n restoreHostTerminalModes(this.stdout);\n const code = signal ? 128 + signal : exitCode;\n this.onSessionExit?.(code);\n });\n\n // stdin 结束时写入 EOF 控制字符到 PTY\n this.stdin.on(\"end\", () => {\n child.write(\"\\x04\");\n });\n }\n\n /**\n * PTY 数据到达时的统一处理:OSC 9 修复 + 输出到终端 + 传给 tap\n */\n private handleData(data: string): void {\n // PTY 的 onlcr 会把 OSC 序列里的 \\n 转成 \\r\\n,还原为 \\n\n const fixed = data.replace(\n // eslint-disable-next-line no-control-regex\n /\\x1b\\]9;([\\s\\S]*?)\\x07/g,\n (_, content: string) => `\\x1b]9;${content.replace(/\\r\\n/g, \"\\n\")}\\x07`,\n );\n this.stdout.write(fixed);\n this.tap(data);\n }\n\n // 向 PTY 子进程写入数据,用于远程输入注入\n write(data: string): void {\n this.child?.write(data);\n }\n\n // 关闭 raw mode 并吞掉 stdin 已关闭场景下的异常。退出 / cleanup 两条路径共用。\n private disableRawModeQuiet(): void {\n try {\n this.stdin.setRawMode(false);\n } catch {\n // stdin 可能已关闭\n }\n }\n\n cleanup(exitCode: number): void {\n if (this.resizeTimer) {\n clearTimeout(this.resizeTimer);\n this.resizeTimer = null;\n }\n if (this.stdin.isTTY) this.disableRawModeQuiet();\n restoreHostTerminalModes(this.stdout);\n if (this.child) {\n try {\n this.child.kill();\n } catch {\n // 子进程可能已退出\n }\n this.child = null;\n }\n this.onSessionExit?.(exitCode);\n }\n}\n","import { statSync } from \"node:fs\";\n\nfunction isDirectory(path: string | undefined): path is string {\n if (!path) return false;\n try {\n return statSync(path).isDirectory();\n } catch {\n return false;\n }\n}\n\nexport function resolveTerminalCwd(env: NodeJS.ProcessEnv = process.env): string {\n const candidates = [env.DEV_ANYWHERE_CWD, env.INIT_CWD, env.PWD, process.cwd()];\n return candidates.find(isDirectory) ?? process.cwd();\n}\n","import { connect, type Socket } from \"node:net\";\nimport { existsSync, unlinkSync } from \"node:fs\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport { SOCK_PATH, STOPPED_PATH, SERVICE_LOG_PATH, PROFILE_NAME } from \"../common/paths.js\";\nimport { spawnScript } from \"../common/env.js\";\nimport { daemonRelayArgs } from \"../common/daemon-env.js\";\nimport { createIpcReader, type IpcMessage } from \"../ipc/ipc-protocol.js\";\nimport { terminalLogger as log } from \"../common/logger.js\";\n\n// serve daemon 自动拉起的连接重试参数\nconst ENSURE_SERVICE_MAX_RETRIES = 20;\nconst ENSURE_SERVICE_INITIAL_DELAY_MS = 100;\nconst ENSURE_SERVICE_MAX_DELAY_MS = 2_000;\n\n// 等待特定类型 IPC 消息的默认超时\nconst WAIT_FOR_MESSAGE_TIMEOUT_MS = 10_000;\n\n// 单次 socket 连接尝试:连上 resolve socket,连不上 resolve null(不抛异常)。\nexport function tryConnect(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\n// 接到 serve daemon 的 unix socket 上:先尝试连接已有进程,连不上则拉起一个新 daemon\n// 子进程并轮询直到 socket 就绪。autoStart=false 用于命令式 status 查询,禁止 spawn。\nexport async function ensureService(autoStart = true): Promise<Socket> {\n const existing = await tryConnect(SOCK_PATH);\n if (existing) {\n log.info(\"Connected to existing service\");\n return existing;\n }\n\n if (!autoStart) throw new Error(\"Service is not running\");\n\n if (existsSync(STOPPED_PATH)) unlinkSync(STOPPED_PATH);\n\n log.info(\"Auto-starting serve daemon\");\n const child = spawnScript(\"serve\", [\"--profile\", PROFILE_NAME, ...daemonRelayArgs()], {\n env: { ...process.env },\n logger: log,\n });\n\n // 监听 daemon 失败信号,让下面的 tryConnect 轮询能在 daemon 启动时就崩的场景下立刻抛诊断。\n // - 'exit':进程启动成功后又退出(配置错误、端口占用、内部崩溃),带 code/signal。\n // - 'error':spawn 本身失败(ENOENT 找不到 tsx/node 等),Node 文档说此时 'exit' may or may not 跟着 fire,\n // 所以显式监听补完备性。spawnScript 内部另装了一对只管日志的监听器,跟这里互不影响。\n let childFailed = false;\n let exitCode: number | null = null;\n let exitSignal: NodeJS.Signals | null = null;\n let spawnError: Error | null = null;\n child.once(\"exit\", (code, signal) => {\n childFailed = true;\n exitCode = code;\n exitSignal = signal;\n });\n child.once(\"error\", (err) => {\n childFailed = true;\n spawnError = err;\n });\n\n for (let i = 0; i < ENSURE_SERVICE_MAX_RETRIES; i++) {\n const delay = Math.min(ENSURE_SERVICE_INITIAL_DELAY_MS * (i + 1), ENSURE_SERVICE_MAX_DELAY_MS);\n await sleep(delay);\n\n if (childFailed) {\n log.error(\n { code: exitCode, signal: exitSignal, err: spawnError && String(spawnError) },\n \"Serve daemon failed to start\",\n );\n const detail = spawnError\n ? `spawn error=${String(spawnError)}`\n : `code=${exitCode}, signal=${exitSignal}`;\n throw new Error(\n `Serve daemon failed to start (${detail}). Check ${SERVICE_LOG_PATH} for details.`,\n );\n }\n\n const socket = await tryConnect(SOCK_PATH);\n if (socket) {\n log.info({ attempt: i + 1 }, \"Connected to service after retry\");\n return socket;\n }\n }\n\n log.error({ maxRetries: ENSURE_SERVICE_MAX_RETRIES }, \"Failed to connect to service\");\n throw new Error(\n `Failed to connect to dev-anywhere service after ${ENSURE_SERVICE_MAX_RETRIES} retries. Check ${SERVICE_LOG_PATH} for details.`,\n );\n}\n\n// 等待指定类型的 IPC 消息一次。`createIpcReader` 注册临时 listener,匹配后立即清理。\n// 超时返回 reject;调用方需自己保证 socket 不会同时被另一个 listener 持有否则消息可能被吃掉。\nexport function waitForMessage<T extends IpcMessage[\"type\"]>(\n socket: Socket,\n messageType: T,\n): Promise<Extract<IpcMessage, { type: T }>> {\n return new Promise((resolve, reject) => {\n let timeout: NodeJS.Timeout | null = null;\n const dispose = createIpcReader(socket, (msg: IpcMessage) => {\n if (msg.type === messageType) {\n if (timeout) clearTimeout(timeout);\n dispose();\n resolve(msg as Extract<IpcMessage, { type: T }>);\n }\n });\n timeout = setTimeout(() => {\n dispose();\n reject(new Error(`Timeout waiting for ${messageType}`));\n }, WAIT_FOR_MESSAGE_TIMEOUT_MS);\n });\n}\n","// PTY 闲置检测:每 IDLE_CHECK_INTERVAL_MS 抽一次,发现自上次输出已超过 IDLE_THRESHOLD_MS\n// 且当前局部状态仍是 working 时,把状态翻到 turn_complete 并触发 emit。\n//\n// 抽出来主要好处:terminal.ts / hosted-pty-registry.ts 都跑同样的 working→turn_complete\n// 退化逻辑,未来若把 hosted 也接入这个 checker,就只剩一份实现。\n\ninterface IdleCheckerOptions {\n // 检测周期\n intervalMs: number;\n // 超过这个时长无新输出即触发 turn_complete\n thresholdMs: number;\n // 读取最近一次 PTY 输出时间戳;返回 0 表示已经走过 turn_complete 不要重复触发\n getLastOutputTime: () => number;\n // 用于\"重置最近输出时间到 0\",保证下次再触发前必须有真实新输出\n setLastOutputTime: (value: number) => void;\n // 读当前局部 PTY 状态\n getCurrentState: () => \"working\" | \"turn_complete\" | \"approval_wait\";\n // 仅在 currentState === \"working\" 时触发;onIdle 内部决定具体怎么落地(emit IPC / 改 state)\n onIdle: () => void;\n}\n\nexport interface IdleChecker {\n start(): void;\n stop(): void;\n}\n\nexport function createIdleChecker(options: IdleCheckerOptions): IdleChecker {\n let timer: NodeJS.Timeout | null = null;\n\n const tick = (): void => {\n const last = options.getLastOutputTime();\n if (last <= 0) return;\n if (Date.now() - last <= options.thresholdMs) return;\n options.setLastOutputTime(0);\n if (options.getCurrentState() !== \"working\") return;\n options.onIdle();\n };\n\n return {\n start(): void {\n if (timer) clearInterval(timer);\n timer = setInterval(tick, options.intervalMs);\n },\n stop(): void {\n if (timer) {\n clearInterval(timer);\n timer = null;\n }\n },\n };\n}\n","import type { Socket } from \"node:net\";\n\n// terminal 进程在 reconnectToServe 成功后用新 socket 替换旧的。旧 socket 已被 close 事件\n// 触发过,但 createIpcReader 内部 pipe 的 LineBuffer + 在原 socket 上注册的 close/error/data\n// listener 仍持有引用;不显式 removeAllListeners 会让它们随着每次 reconnect 单调累积,\n// 长跑(夜间 / 网络抖动频繁)下成为内存泄漏。\nexport function swapServeSocket(prev: Socket, next: Socket): Socket {\n prev.removeAllListeners();\n return next;\n}\n","import type { Socket } from \"node:net\";\nimport { createFSM } from \"@dev-anywhere/shared\";\nimport { serializeIpc } from \"../ipc/ipc-protocol.js\";\n\n// terminal 进程生命周期状态\nexport const TerminalState = {\n INIT: \"init\",\n CONNECTING_SERVICE: \"connecting_service\",\n CREATING_SESSION: \"creating_session\",\n RUNNING: \"running\",\n RECONNECTING: \"reconnecting\",\n EXITED: \"exited\",\n} as const;\nexport type TerminalState = (typeof TerminalState)[keyof typeof TerminalState];\n\n// 允许的状态转换。CREATING_SESSION/RUNNING 下可被 socket close 打断进入 RECONNECTING;\n// 任意非终态都可能被 PTY 退出或 SIGTERM 打断进入 EXITED。\nexport const TERMINAL_TRANSITIONS: Record<TerminalState, readonly TerminalState[]> = {\n init: [\"connecting_service\"],\n connecting_service: [\"creating_session\", \"exited\"],\n creating_session: [\"running\", \"reconnecting\", \"exited\"],\n running: [\"reconnecting\", \"exited\"],\n reconnecting: [\"creating_session\", \"running\", \"exited\"],\n exited: [],\n};\n\n// 下面几个依赖是 getter 而非值:因为它们在 terminal.ts 里是 let 变量,在 handler 创建之后还会变——\n// socket 在 reconnect 时被重新赋值为新实例,sessionId 在 session_create 成功后才有值,\n// idleChecker 在 setupIdleCheck 跑完才赋值。直接传值只会记录 handler 构造那一刻的旧值。\ninterface ExitHandlerDeps {\n fsm: ReturnType<typeof createFSM<TerminalState>>;\n getSocket: () => Socket;\n getSessionId: () => string | null;\n // 退出时要停掉的 idle checker;handler 创建时尚未实例化,故传 getter\n stopIdleChecker: () => void;\n // 释放 xterm headless 渲染资源(HeadlessTerminal、SerializeAddon、UnicodeGraphemesAddon\n // 等)。process 退出后 OS 会回收文件描述符等系统资源,但 wasm 模块状态、addon listener\n // 留到 process 末期会让多次 spawn-exit 循环(如测试场景)累积内存。\n disposeRenderResources?: () => void;\n // 测试注入点,production 默认 process.exit\n exit?: (code: number) => void;\n}\n\n// 构造统一的收尾函数:转 EXITED → 停 idle 定时器 → 给 serve 发 pty_deregister → 退进程。\n// onSessionExit 与 SIGTERM handler 共享同一实例;Ctrl+C 两连击或 PTY 退出与 SIGTERM 竞争时,\n// 第二次调用通过 fsm EXITED 检查直接短路。\nexport function createExitHandler(deps: ExitHandlerDeps): (code: number) => void {\n const exit = deps.exit ?? ((code: number) => process.exit(code));\n return (code: number) => {\n if (deps.fsm.is(TerminalState.EXITED)) return;\n deps.fsm.transitionTo(TerminalState.EXITED);\n deps.stopIdleChecker();\n deps.disposeRenderResources?.();\n const socket = deps.getSocket();\n const sessionId = deps.getSessionId();\n if (socket.writable && sessionId) {\n socket.end(serializeIpc({ type: \"pty_deregister\", sessionId }), () => exit(code));\n } else {\n exit(code);\n }\n };\n}\n"],"mappings":"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","names":["sleep","existsSync","sleep","existsSync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev-anywhere/proxy",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Transparent local proxy for AI coding CLIs that bridges local sessions to a web/PWA client via a relay server.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -52,8 +52,8 @@
52
52
  "@types/node": "^25.5.2",
53
53
  "@types/ws": "^8.18.1",
54
54
  "vitest": "^4.1.2",
55
- "@dev-anywhere/shared": "0.2.2",
56
- "@dev-anywhere/relay": "0.2.2"
55
+ "@dev-anywhere/shared": "0.2.4",
56
+ "@dev-anywhere/relay": "0.2.4"
57
57
  },
58
58
  "scripts": {
59
59
  "build": "tsup",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../packages/shared/src/schemas/envelope.ts","../../../packages/shared/src/schemas/id.ts","../../../packages/shared/src/schemas/chat.ts","../../../packages/shared/src/schemas/tool.ts","../../../packages/shared/src/schemas/session.ts","../../../packages/shared/src/constants/enums.ts","../../../packages/shared/src/constants/pty.ts","../../../packages/shared/src/schemas/system.ts","../../../packages/shared/src/builders/index.ts","../../../packages/shared/src/state-machine.ts","../../../packages/shared/src/schemas/relay-control.ts","../../../packages/shared/src/constants/relay-errors.ts","../../../packages/shared/src/constants/control-errors.ts","../../../packages/shared/src/constants/session.ts","../../../packages/shared/src/binary-frame.ts","../src/ipc/ipc-protocol.ts","../src/ipc/line-buffer.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { IdSchema } from \"./id.js\";\nimport {\n UserInputPayloadSchema,\n AssistantMessagePayloadSchema,\n ThinkingPayloadSchema,\n} from \"./chat.js\";\nimport { ToolUseRequestPayloadSchema, ToolResultPayloadSchema } from \"./tool.js\";\nimport {\n SessionCreatePayloadSchema,\n SessionListPayloadSchema,\n SessionSwitchPayloadSchema,\n SessionTerminatePayloadSchema,\n SessionStatusPayloadSchema,\n} from \"./session.js\";\nimport {\n HeartbeatPayloadSchema,\n AuthPayloadSchema,\n SyncRequestPayloadSchema,\n SyncResponsePayloadSchema,\n} from \"./system.js\";\n\n// 信封基础字段:序列号、时间戳、来源、协议版本——所有 envelope 共用。\nconst BaseEnvelopeFields = {\n seq: z.number().int().nonnegative(),\n timestamp: z.number(),\n source: z.enum([\"proxy\", \"client\"]),\n version: z.string(),\n};\n\n// session-scoped envelope: 携带 sessionId 路由到具体会话, 大多数业务 envelope 走这里。\nconst SessionedEnvelopeFields = {\n ...BaseEnvelopeFields,\n sessionId: IdSchema,\n};\n\n// 按 type 字段区分的 discriminatedUnion 信封\nexport const MessageEnvelopeSchema = z.discriminatedUnion(\"type\", [\n // chat (3)\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"user_input\"),\n payload: UserInputPayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"assistant_message\"),\n payload: AssistantMessagePayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"thinking\"),\n payload: ThinkingPayloadSchema,\n }),\n // tool (4): 工具审批决策属于 relay control,不进入会话消息信封。\n // tool_use_request: 审批流请求(proxy → client),toolId 是 approval requestId\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"tool_use_request\"),\n payload: ToolUseRequestPayloadSchema,\n }),\n // tool_result: 工具执行结果(proxy → client),toolId 对应 assistant_tool_use / tool_use_request 的 toolId\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"tool_result\"),\n payload: ToolResultPayloadSchema,\n }),\n // assistant_tool_use: 纯展示型工具调用(proxy → client),区别于 tool_use_request 无审批语义\n // payload 结构复用 ToolUseRequestPayloadSchema;toolId 是 Claude 分配的 tool_use id\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"assistant_tool_use\"),\n payload: ToolUseRequestPayloadSchema,\n }),\n // session (5)\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"session_create\"),\n payload: SessionCreatePayloadSchema,\n }),\n // session_list 是全局广播 (列出所有 session), 不绑定具体 sessionId, 不携带该字段。\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"session_list\"),\n payload: SessionListPayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"session_switch\"),\n payload: SessionSwitchPayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"session_terminate\"),\n payload: SessionTerminatePayloadSchema,\n }),\n z.object({\n ...SessionedEnvelopeFields,\n type: z.literal(\"session_status\"),\n payload: SessionStatusPayloadSchema,\n }),\n // system (5): 心跳 / 认证 / 同步——全局, 无 sessionId\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"heartbeat\"),\n payload: HeartbeatPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"auth\"),\n payload: AuthPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"sync_request\"),\n payload: SyncRequestPayloadSchema,\n }),\n z.object({\n ...BaseEnvelopeFields,\n type: z.literal(\"sync_response\"),\n payload: SyncResponsePayloadSchema,\n }),\n]);\n\nexport type MessageEnvelope = z.infer<typeof MessageEnvelopeSchema>;\n\nexport type MessageType = MessageEnvelope[\"type\"];\n\nexport type MessageSource = MessageEnvelope[\"source\"];\n","import { z } from \"zod\";\n\n// 所有跨进程传递的标识符 (proxyId / clientId / sessionId / requestId / toolId) 的统一上限。\n// 设计原因:\n// - nanoid (proxy/session/client 服务端默认生成) = 21 字符\n// - claude tool_use id (toolu_<base64ish>) ≈ 30-40 字符\n// - codex tool id 类似量级\n// 256 给协议演进留余量, 同时挡住 wire 上来的恶意超长 ID:\n// - Map<id, _> key 占用 (memory DoS)\n// - 日志行膨胀 (operator/磁盘压力)\n// - 拼到 disk path (虽然当前没有此路径, defense in depth)\nconst MAX_ID_LENGTH = 256;\n\nexport const IdSchema = z.string().min(1).max(MAX_ID_LENGTH);\n","import { z } from \"zod\";\n\n// 用户输入消息\nexport const UserInputPayloadSchema = z.object({\n text: z.string().min(1),\n messageId: z.string().min(1).optional(),\n});\n\nexport type UserInputPayload = z.infer<typeof UserInputPayloadSchema>;\n\n// 助手回复消息,isPartial 标识是否为流式中间结果\nexport const AssistantMessagePayloadSchema = z.object({\n text: z.string(),\n isPartial: z.boolean(),\n});\n\nexport type AssistantMessagePayload = z.infer<typeof AssistantMessagePayloadSchema>;\n\n// 思考过程消息\nexport const ThinkingPayloadSchema = z.object({\n text: z.string(),\n});\n\nexport type ThinkingPayload = z.infer<typeof ThinkingPayloadSchema>;\n","import { z } from \"zod\";\nimport { IdSchema } from \"./id.js\";\n\n// 工具调用请求\nexport const ToolUseRequestPayloadSchema = z.object({\n toolName: z.string(),\n toolId: IdSchema,\n parameters: z.record(z.string(), z.unknown()),\n});\n\nexport type ToolUseRequestPayload = z.infer<typeof ToolUseRequestPayloadSchema>;\n\n// 工具调用批准,whitelistTool 为 true 时将该工具加入会话级白名单自动审批\nexport const ToolApprovePayloadSchema = z.object({\n toolId: IdSchema,\n whitelistTool: z.boolean().optional(),\n});\n\nexport type ToolApprovePayload = z.infer<typeof ToolApprovePayloadSchema>;\n\n// 工具调用拒绝\nexport const ToolDenyPayloadSchema = z.object({\n toolId: IdSchema,\n reason: z.string().optional(),\n});\n\nexport type ToolDenyPayload = z.infer<typeof ToolDenyPayloadSchema>;\n\n// 工具调用结果\nexport const ToolResultPayloadSchema = z.object({\n toolId: IdSchema,\n result: z.unknown(),\n isError: z.boolean(),\n});\n\nexport type ToolResultPayload = z.infer<typeof ToolResultPayloadSchema>;\n","import { z } from \"zod\";\nimport { providerValues, ptyOwnerValues, sessionModeValues } from \"../constants/enums.js\";\nimport { ptySemanticStateValues } from \"../constants/pty.js\";\nimport { IdSchema } from \"./id.js\";\n\nexport const sessionStateValues = [\n \"idle\",\n \"working\",\n \"waiting_approval\",\n \"error\",\n \"terminated\",\n] as const;\nconst agentStatusPhaseValues = [\n \"idle\",\n \"thinking\",\n \"tool_use\",\n \"outputting\",\n \"waiting_permission\",\n \"error\",\n] as const;\n\n// 会话信息,用于会话列表展示\n// lastActive: 最近一次状态变更或运行时活动时间戳 (ms), 用于列表\"最近活动 N 分钟前\"显示, 可选\nexport const SessionInfoSchema = z.object({\n sessionId: IdSchema,\n name: z.string().optional(),\n state: z.enum(sessionStateValues),\n mode: z.enum(sessionModeValues).optional(),\n provider: z.enum(providerValues),\n // PTY 尺寸所有权:\n // - local-terminal: 本地 terminal 进程持有真实 PTY,Web 只按原始 cols/rows 展示\n // - proxy-hosted: serve 内托管 PTY,Web 可按视口请求 resize\n ptyOwner: z.enum(ptyOwnerValues).optional(),\n lastActive: z.number().optional(),\n});\nexport type SessionInfo = z.infer<typeof SessionInfoSchema>;\n\n// 创建会话\n// streamDelta: client 端系统设置\"逐字流式\"toggle,true 时 proxy spawn 带 --include-partial-messages\nexport const SessionCreatePayloadSchema = z.object({\n name: z.string().optional(),\n cwd: z.string().optional(),\n streamDelta: z.boolean().optional(),\n});\n\nexport type SessionCreatePayload = z.infer<typeof SessionCreatePayloadSchema>;\n\n// 会话列表\nexport const SessionListPayloadSchema = z.object({\n sessions: z.array(SessionInfoSchema),\n});\n\nexport type SessionListPayload = z.infer<typeof SessionListPayloadSchema>;\n\n// 切换会话\nexport const SessionSwitchPayloadSchema = z.object({\n sessionId: IdSchema,\n});\n\nexport type SessionSwitchPayload = z.infer<typeof SessionSwitchPayloadSchema>;\n\n// 终止会话\nexport const SessionTerminatePayloadSchema = z.object({\n sessionId: IdSchema,\n});\n\nexport type SessionTerminatePayload = z.infer<typeof SessionTerminatePayloadSchema>;\n\n// 会话状态变更\n// lastActive: 触发本次状态迁移或活动刷新的时间戳 (ms),用于列表相对时间显示。\nexport const SessionStatusPayloadSchema = z.object({\n sessionId: IdSchema,\n state: z.enum(sessionStateValues),\n lastActive: z.number(),\n});\n\nexport type SessionStatusPayload = z.infer<typeof SessionStatusPayloadSchema>;\n\n// PTY 语义状态事件,描述当前 PTY 处于何种状态\nexport const PtyStatePayloadSchema = z.object({\n state: z.enum(ptySemanticStateValues),\n title: z.string().optional(),\n tool: z.string().optional(),\n});\nexport type PtyStatePayload = z.infer<typeof PtyStatePayloadSchema>;\n\nexport const AgentStatusPayloadSchema = z.object({\n provider: z.enum(providerValues),\n phase: z.enum(agentStatusPhaseValues),\n seq: z.number().int().nonnegative(),\n updatedAt: z.number(),\n toolName: z.string().optional(),\n toolInput: z.record(z.string(), z.unknown()).optional(),\n permissionRequest: z\n .object({\n requestId: IdSchema,\n toolName: z.string(),\n input: z.record(z.string(), z.unknown()),\n })\n .optional(),\n permissionResolution: z\n .object({\n requestId: IdSchema,\n outcome: z.enum([\"allow\", \"deny\"]),\n })\n .optional(),\n summary: z.string().optional(),\n});\nexport type AgentStatusPayload = z.infer<typeof AgentStatusPayloadSchema>;\n","// 协议层共用的枚举字面量集合。Zod schema、TypeScript 类型、运行时常量都从这里派生,\n// 避免在 relay-control / session / chat / web 各自手写 z.enum([\"claude\", \"codex\"]) 漂移。\n\nexport const providerValues = [\"claude\", \"codex\"] as const;\nexport type ProviderId = (typeof providerValues)[number];\n\nexport const ptyOwnerValues = [\"local-terminal\", \"proxy-hosted\"] as const;\nexport type PtyOwner = (typeof ptyOwnerValues)[number];\n\nexport const sessionModeValues = [\"pty\", \"json\"] as const;\nexport type SessionMode = (typeof sessionModeValues)[number];\n","// PTY 局部语义状态。仅承载明确语义信号; title/spinner 变化时事件的 state 取 null,\n// 让上层不参与 FSM 切换, 只走 title 推送通道。\n// 单一 source of truth: osc-extractor / ipc-protocol / shared schema 都从这里取。\nexport const PtySemanticState = {\n WORKING: \"working\",\n TURN_COMPLETE: \"turn_complete\",\n APPROVAL_WAIT: \"approval_wait\",\n} as const;\n\nexport type PtySemanticState = (typeof PtySemanticState)[keyof typeof PtySemanticState];\n\n// zod / 类型字面量场景下复用的 const tuple, z.enum 直接吃。\nexport const ptySemanticStateValues = [\n PtySemanticState.WORKING,\n PtySemanticState.TURN_COMPLETE,\n PtySemanticState.APPROVAL_WAIT,\n] as const;\n","import { z } from \"zod\";\n\n// 心跳消息,空 payload\nexport const HeartbeatPayloadSchema = z.object({});\n\nexport type HeartbeatPayload = z.infer<typeof HeartbeatPayloadSchema>;\n\n// 认证消息,支持配对码和 token 两种方式\nexport const AuthPayloadSchema = z.object({\n pairingCode: z.string().optional(),\n token: z.string().optional(),\n});\n\nexport type AuthPayload = z.infer<typeof AuthPayloadSchema>;\n\n// 同步请求,客户端发送已收到的最大序列号\nexport const SyncRequestPayloadSchema = z.object({\n lastSeq: z.number().int().nonnegative(),\n});\n\nexport type SyncRequestPayload = z.infer<typeof SyncRequestPayloadSchema>;\n\n// 同步响应,使用 z.unknown 数组避免循环引用;恢复协议稳定后再收紧类型\nexport const SyncResponsePayloadSchema = z.object({\n messages: z.array(z.record(z.string(), z.unknown())),\n});\n\nexport type SyncResponsePayload = z.infer<typeof SyncResponsePayloadSchema>;\n","import type { MessageEnvelope } from \"../schemas/envelope.js\";\nimport { MessageEnvelopeSchema } from \"../schemas/envelope.js\";\nimport type { RelayControlMessage } from \"../schemas/relay-control.js\";\n\n// 构建经过 schema 验证的消息信封\n// seq 由调用方提供,必须与 EventStore per-session seq 一致,保证 proxy 和 relay 对账可靠\n// sessionId 仅 session-scoped envelope 携带; 全局广播 (session_list / heartbeat / auth /\n// sync_*) 传 null 或省略, 否则要求合法 ID。\nexport function buildMessage<T extends MessageEnvelope[\"type\"]>(\n type: T,\n sessionId: string | null,\n seq: number,\n payload: Extract<MessageEnvelope, { type: T }>[\"payload\"],\n source: \"proxy\" | \"client\",\n): Extract<MessageEnvelope, { type: T }> {\n const envelope = {\n seq,\n ...(sessionId !== null ? { sessionId } : {}),\n type,\n payload,\n timestamp: Date.now(),\n source,\n version: \"1.0\",\n };\n return MessageEnvelopeSchema.parse(envelope) as Extract<MessageEnvelope, { type: T }>;\n}\n\n// 序列化 relay control 消息。借助 RelayControlMessage 这个 discriminated union,\n// 调用方传入 { type, ... } 时 TypeScript 会按 type 反查出该消息允许携带的字段集,\n// 替代之前散落的 sendRaw(JSON.stringify({ type: \"...\", ... })) 模式 —— 后者在编译期\n// 完全没有约束,任何字段拼写错误 / 字段漏填都到 wire 上才暴露。\n//\n// 这里没有再做一次 RelayControlSchema.parse(msg):proxy 在热路径每条 envelope 都要发,\n// 重复 zod 解析对吞吐有压力;编译期类型检查已经能挡住大部分错误。\n// 真要做运行时校验时,调用 RelayControlSchema.parse(msg) 再 stringify 即可。\nexport function serializeControl(msg: RelayControlMessage): string {\n return JSON.stringify(msg);\n}\n","// 有限状态机 helper\n//\n// 提供显式转换表的小型 FSM,区分两类调用模式:\n// - transitionTo(throw):同步确定性流程里调用,非法转移代表 bug,立即暴露\n// - tryTransitionTo(bool):异步事件回调里调用,非法转移可能是吸收态残余事件,调用方按\n// isInAbsorbingState() 分级日志\n//\n// 吸收态采用传递闭包定义:终态(transitions=[])或所有出边都指向吸收态的状态都算吸收。\n// 这样 SessionState.ERROR (→[TERMINATED]) 和 RelayConnectionState.CLOSED ([]) 都被\n// 自动识别为吸收态,不用在 caller 里硬编码状态名。\n//\n// onTransition/onRejected 仅做日志/观测,不应 throw。\n\ninterface FSMDef<S extends string> {\n initial: S;\n // from-state → 允许转入的 to-state 列表;终态对应空数组\n transitions: Record<S, readonly S[]>;\n // 合法转换发生后触发,典型用法是结构化日志\n onTransition?: (from: S, to: S) => void;\n // tryTransitionTo 非法转移时触发;isAbsorbing 指示 from 是否吸收态(晚到残余 vs. 真非法)\n onRejected?: (from: S, to: S, isAbsorbing: boolean) => void;\n}\n\ninterface FSM<S extends string> {\n current(): S;\n is(state: S): boolean;\n isIn(states: readonly S[]): boolean;\n canTransitionTo(to: S): boolean;\n // 非法转换抛 Error;同步流程里当 assert 用\n transitionTo(to: S): void;\n // 非法转换返回 false,不抛;异步回调里用,配合 isInAbsorbingState 分级日志\n tryTransitionTo(to: S): boolean;\n // 当前是否在吸收态(传递闭包)\n isInAbsorbingState(): boolean;\n}\n\n// 计算吸收态集合:终态 + 所有出边都指向吸收态的状态,迭代至不动点\nfunction computeAbsorbingSet<S extends string>(transitions: Record<S, readonly S[]>): Set<S> {\n const absorbing = new Set<S>();\n const entries = Object.entries(transitions) as Array<[S, readonly S[]]>;\n for (const [s, outs] of entries) {\n if (outs.length === 0) absorbing.add(s);\n }\n let changed = true;\n while (changed) {\n changed = false;\n for (const [s, outs] of entries) {\n if (absorbing.has(s)) continue;\n if (outs.length > 0 && outs.every((t) => absorbing.has(t))) {\n absorbing.add(s);\n changed = true;\n }\n }\n }\n return absorbing;\n}\n\nexport function createFSM<S extends string>(def: FSMDef<S>): FSM<S> {\n let state = def.initial;\n const absorbing = computeAbsorbingSet(def.transitions);\n const tryTransitionTo = (to: S): boolean => {\n const allowed = def.transitions[state];\n if (!allowed?.includes(to)) {\n def.onRejected?.(state, to, absorbing.has(state));\n return false;\n }\n const from = state;\n state = to;\n def.onTransition?.(from, to);\n return true;\n };\n return {\n current: () => state,\n is: (s) => state === s,\n isIn: (ss) => ss.includes(state),\n canTransitionTo: (to) => def.transitions[state]?.includes(to) ?? false,\n transitionTo: (to) => {\n if (!tryTransitionTo(to)) {\n throw new Error(`Invalid FSM transition: ${state} -> ${to}`);\n }\n },\n tryTransitionTo,\n isInAbsorbingState: () => absorbing.has(state),\n };\n}\n\n// 无内部 state 的 FSM 视图,供 state 存在外部(如 SessionInfo、DB 行)的 per-instance 场景使用。\n// 调用方自行传入 from,canTransition 校验;吸收态判定通过 isAbsorbing(state) 提供。\ninterface StatelessFSM<S extends string> {\n canTransition(from: S, to: S): boolean;\n isAbsorbing(state: S): boolean;\n}\n\nexport function defineFSM<S extends string>(transitions: Record<S, readonly S[]>): StatelessFSM<S> {\n const absorbing = computeAbsorbingSet(transitions);\n return {\n canTransition: (from, to) => transitions[from]?.includes(to) ?? false,\n isAbsorbing: (state) => absorbing.has(state),\n };\n}\n","import { z } from \"zod\";\nimport { IdSchema } from \"./id.js\";\nimport { AgentStatusPayloadSchema, PtyStatePayloadSchema, sessionStateValues } from \"./session.js\";\nimport { ToolApprovePayloadSchema, ToolDenyPayloadSchema } from \"./tool.js\";\nimport { RelayErrorCode } from \"../constants/relay-errors.js\";\nimport { ControlErrorCode } from \"../constants/control-errors.js\";\nimport {\n providerValues,\n ptyOwnerValues,\n sessionModeValues,\n} from \"../constants/enums.js\";\n\n// 控制消息中复用的子类型\nexport const ProxyInfoSchema = z.object({\n proxyId: IdSchema,\n name: z.string().optional(),\n online: z.boolean(),\n sessions: z.array(z.string()).optional(),\n});\nexport type ProxyInfo = z.infer<typeof ProxyInfoSchema>;\n\nexport const AgentCliAvailabilitySchema = z.object({\n available: z.boolean(),\n command: z.string().optional(),\n error: z.string().optional(),\n suggestions: z.array(z.string()).optional(),\n});\nexport type AgentCliAvailability = z.infer<typeof AgentCliAvailabilitySchema>;\n\nexport const AgentCliStatusSchema = z.object({\n claude: AgentCliAvailabilitySchema,\n codex: AgentCliAvailabilitySchema,\n});\nexport type AgentCliStatus = z.infer<typeof AgentCliStatusSchema>;\n\nexport const DirEntrySchema = z.object({ name: z.string(), isDir: z.boolean() });\nexport type DirEntry = z.infer<typeof DirEntrySchema>;\n\nexport const FileTreeGroupSchema = z.object({\n path: z.string(),\n entries: z.array(DirEntrySchema),\n});\nexport type FileTreeGroup = z.infer<typeof FileTreeGroupSchema>;\n\nexport const CommandEntrySchema = z.object({\n name: z.string(),\n description: z.string(),\n argumentHint: z.string().optional(),\n source: z.string(),\n});\nexport type CommandEntry = z.infer<typeof CommandEntrySchema>;\n\nexport const HistorySessionSchema = z.object({\n id: z.string(),\n title: z.string(),\n projectDir: z.string(),\n updatedAt: z.number(),\n provider: z.enum(providerValues).optional(),\n});\nexport type HistorySession = z.infer<typeof HistorySessionSchema>;\n\nconst SessionHistoryMessageSchema = z.object({\n role: z.enum([\"user\", \"assistant\"]),\n text: z.string(),\n timestamp: z.number().optional(),\n cursor: z.string().optional(),\n});\n\ntype RelayControlDirection = \"proxy_to_client\" | \"client_to_proxy\";\ntype EmptyShape = Record<never, never>;\nconst RequestIdShape = { requestId: IdSchema.optional() };\nconst ControlErrorCodeSchema = z.enum(\n Object.values(ControlErrorCode) as [ControlErrorCode, ...ControlErrorCode[]],\n);\nconst RequestErrorShape = {\n error: z.string().optional(),\n errorCode: ControlErrorCodeSchema.optional(),\n};\nconst ClipboardImageMimeTypeSchema = z.enum([\"image/png\", \"image/jpeg\", \"image/webp\", \"image/gif\"]);\n\ntype ControlDefinition<T extends string, S extends z.ZodRawShape> = {\n type: T;\n directions: ReadonlySet<RelayControlDirection>;\n schema: z.ZodObject<{ type: z.ZodLiteral<T> } & S>;\n};\n\nfunction control<T extends string>(type: T): ControlDefinition<T, EmptyShape>;\nfunction control<T extends string>(\n type: T,\n shape: undefined,\n directions: RelayControlDirection | RelayControlDirection[],\n): ControlDefinition<T, EmptyShape>;\nfunction control<T extends string, S extends z.ZodRawShape>(\n type: T,\n shape: S,\n directions?: RelayControlDirection | RelayControlDirection[],\n): ControlDefinition<T, S>;\nfunction control<T extends string, S extends z.ZodRawShape>(\n type: T,\n shape?: S,\n directions?: RelayControlDirection | RelayControlDirection[],\n): ControlDefinition<T, S | EmptyShape> {\n return {\n type,\n directions: new Set(Array.isArray(directions) ? directions : directions ? [directions] : []),\n schema: z.object({\n type: z.literal(type),\n ...(shape ?? {}),\n }) as z.ZodObject<{ type: z.ZodLiteral<T> } & (S | EmptyShape)>,\n };\n}\n\n// 中转服务器控制消息,独立于 MessageEnvelope 的传输层协议\nconst relayControlDefinitions = [\n control(\"proxy_register\", {\n proxyId: IdSchema,\n name: z.string().optional(),\n }),\n control(\"proxy_register_response\", {\n status: z.enum([\"new\", \"reconnected\"]),\n }),\n control(\"proxy_list_request\", RequestIdShape),\n control(\"proxy_list_response\", {\n ...RequestIdShape,\n proxies: z.array(ProxyInfoSchema),\n }),\n control(\"proxy_select\", { ...RequestIdShape, proxyId: IdSchema }),\n control(\"proxy_select_response\", {\n ...RequestIdShape,\n success: z.boolean(),\n proxyId: IdSchema.optional(),\n ...RequestErrorShape,\n }),\n control(\"relay_error\", {\n code: z.enum(Object.values(RelayErrorCode) as [RelayErrorCode, ...RelayErrorCode[]]),\n message: z.string(),\n // 可选 requestId: relay 把 client 发来 raw 的 requestId 字段透传回来,\n // client 侧 waitForMessage 据此把对应 pending request 立即拒掉而不必等到 timeout。\n requestId: IdSchema.optional(),\n }),\n\n // 客户端注册协议\n control(\"client_register\", {\n clientId: IdSchema,\n }),\n control(\"client_register_response\", {\n status: z.enum([\"restored\", \"proxy_offline\", \"new\"]),\n proxyId: IdSchema.optional(),\n }),\n\n // Proxy 离线通知\n control(\"proxy_offline\", {\n proxyId: IdSchema,\n }),\n\n // Proxy 主动断开,relay 立即清理资源\n control(\"proxy_disconnect\", {\n proxyId: IdSchema,\n }),\n\n // Proxy 重连后通知 client 恢复\n control(\"proxy_online\", {\n proxyId: IdSchema,\n }),\n\n // 目录列表请求与响应\n control(\n \"dir_list_request\",\n {\n proxyId: IdSchema.optional(),\n ...RequestIdShape,\n path: z.string(),\n },\n \"client_to_proxy\",\n ),\n control(\n \"dir_list_response\",\n { ...RequestIdShape, ...RequestErrorShape, entries: z.array(DirEntrySchema), path: z.string() },\n \"proxy_to_client\",\n ),\n\n // 目录创建请求与响应\n control(\"dir_create_request\", { ...RequestIdShape, path: z.string() }, \"client_to_proxy\"),\n control(\n \"dir_create_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n path: z.string(),\n success: z.boolean(),\n },\n \"proxy_to_client\",\n ),\n\n // 命令列表推送,proxy 将可用命令列表推给 client\n control(\"command_list_push\", { commands: z.array(CommandEntrySchema) }, \"proxy_to_client\"),\n\n // 文件树推送: 按目录分组, 首组 path 即为 session cwd\n // 前端直接把每组写入 tree[path], 与 dir_list_response 共享 cache slot\n control(\n \"file_tree_push\",\n {\n groups: z.array(FileTreeGroupSchema),\n },\n \"proxy_to_client\",\n ),\n\n // 会话列表请求与权限模式变更\n control(\"session_list\", undefined, [\"client_to_proxy\", \"proxy_to_client\"]),\n control(\n \"permission_mode_change\",\n {\n mode: z.enum([\"default\", \"auto_accept\", \"plan\"]),\n // sessionId 可选:传入时 proxy 按该会话的 mode 分叉(PTY 发 Tab ANSI),未传走全局日志行为\n sessionId: IdSchema.optional(),\n },\n \"client_to_proxy\",\n ),\n\n // 会话历史浏览\n control(\"session_history_request\", RequestIdShape, \"client_to_proxy\"),\n control(\n \"session_history_response\",\n { ...RequestIdShape, sessions: z.array(HistorySessionSchema) },\n \"proxy_to_client\",\n ),\n\n // PTY 语义状态,从 Envelope 迁移到 Control 层\n control(\n \"pty_state\",\n { sessionId: IdSchema, payload: PtyStatePayloadSchema },\n \"proxy_to_client\",\n ),\n\n // Provider 语义状态,来自 Claude/Codex hook 等结构化事件,不从 PTY 字节推断\n control(\n \"agent_status\",\n { sessionId: IdSchema, payload: AgentStatusPayloadSchema },\n \"proxy_to_client\",\n ),\n\n // 终端标题变化,proxy -> client\n control(\"terminal_title\", { sessionId: IdSchema, title: z.string() }, \"proxy_to_client\"),\n\n // 终端尺寸变化,proxy -> client\n control(\n \"terminal_resize\",\n { sessionId: IdSchema, cols: z.number().int().positive(), rows: z.number().int().positive() },\n \"proxy_to_client\",\n ),\n control(\n \"terminal_resize_request\",\n { sessionId: IdSchema, cols: z.number().int().positive(), rows: z.number().int().positive() },\n \"client_to_proxy\",\n ),\n\n // 远程终止 JSON 会话,client -> proxy\n control(\"session_terminate\", { sessionId: IdSchema }, \"client_to_proxy\"),\n\n // 中断当前 turn,client -> proxy,SIGINT 到 worker 进程让 claude CLI abort 当前流\n control(\"session_worker_abort\", { sessionId: IdSchema }, \"client_to_proxy\"),\n\n // turn 完成信号,proxy -> client,对应 claude stream-json 的 result 事件\n control(\n \"turn_result\",\n {\n sessionId: IdSchema,\n success: z.boolean(),\n isError: z.boolean(),\n // stream-json result.result 是本轮最终文本。assistant_message 流丢失或 CLI 未发增量时,\n // Web 用它作为 JSON 模式兜底展示,避免 turn 已结束但界面空白。\n result: z.string().optional(),\n },\n \"proxy_to_client\",\n ),\n\n // 客户端发送到 PTY 的原始字节(ANSI 序列),不追加换行\n control(\n \"remote_input_raw\",\n { sessionId: IdSchema, data: z.string() },\n \"client_to_proxy\",\n ),\n control(\n \"clipboard_image_upload\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\n mimeType: ClipboardImageMimeTypeSchema,\n dataBase64: z.string().min(1),\n fileName: z.string().optional(),\n },\n \"client_to_proxy\",\n ),\n control(\n \"clipboard_image_upload_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\n success: z.boolean(),\n // success=false 时 proxy 没有有效 path 可填;保持 optional 以避免占位空字符串通过校验。\n path: z.string().optional(),\n },\n \"proxy_to_client\",\n ),\n control(\n \"image_preview_request\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\n path: z.string().min(1),\n },\n \"client_to_proxy\",\n ),\n control(\n \"image_preview_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\n success: z.boolean(),\n // 同 clipboard_image_upload_response:失败时 proxy 不一定有路径。\n path: z.string().optional(),\n mimeType: ClipboardImageMimeTypeSchema.optional(),\n dataBase64: z.string().optional(),\n size: z.number().int().nonnegative().optional(),\n },\n \"proxy_to_client\",\n ),\n // 任意文件下载: 与 image_preview 形状对称, 只是 mimeType 不限定为图片;\n // 单租户场景下 path 任意 (不受 previewRoots 限制), 由 proxy 端 size cap 兜底。\n control(\n \"file_download_request\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\n path: z.string().min(1),\n },\n \"client_to_proxy\",\n ),\n control(\n \"file_download_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\n success: z.boolean(),\n path: z.string().optional(),\n mimeType: z.string().optional(),\n dataBase64: z.string().optional(),\n size: z.number().int().nonnegative().optional(),\n },\n \"proxy_to_client\",\n ),\n // 任意文件上传: 复用 clipboard_image_upload 的形状, mimeType 放开 + fileName 必填,\n // 由 proxy 端写入 session cwd 的 .dev-anywhere/uploads/ 子目录, 返回相对路径供 web 拼成 @path。\n control(\n \"file_upload_request\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\n mimeType: z.string().min(1),\n dataBase64: z.string().min(1),\n fileName: z.string().min(1),\n },\n \"client_to_proxy\",\n ),\n control(\n \"file_upload_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\n success: z.boolean(),\n path: z.string().optional(),\n },\n \"proxy_to_client\",\n ),\n\n // 客户端询问 proxy 的环境信息 (home 路径等), client -> proxy -> response\n // FilePathPicker 用 homePath 作为 select 模式下的默认起点, 新建会话时打开即可浏览\n control(\"proxy_info_request\", RequestIdShape, \"client_to_proxy\"),\n control(\n \"proxy_info\",\n { ...RequestIdShape, homePath: z.string(), agentCli: AgentCliStatusSchema },\n \"proxy_to_client\",\n ),\n control(\n \"agent_cli_config_update\",\n { ...RequestIdShape, provider: z.enum(providerValues), path: z.string().min(1) },\n \"client_to_proxy\",\n ),\n control(\n \"agent_cli_config_update_response\",\n {\n ...RequestIdShape,\n provider: z.enum(providerValues),\n agentCli: AgentCliStatusSchema.optional(),\n ...RequestErrorShape,\n },\n \"proxy_to_client\",\n ),\n\n // 远程创建 JSON 会话,client -> proxy -> response\n control(\n \"session_create\",\n {\n ...RequestIdShape,\n cwd: z.string(),\n provider: z.enum(providerValues),\n mode: z.enum(sessionModeValues).optional(),\n resumeSessionId: z.string().optional(),\n // 透传给 claude CLI 的 --permission-mode, undefined 时 proxy 兜底为 \"default\"\n permissionMode: z\n .enum([\"default\", \"auto\", \"acceptEdits\", \"plan\", \"bypassPermissions\", \"dontAsk\"])\n .optional(),\n },\n \"client_to_proxy\",\n ),\n control(\n \"session_create_response\",\n {\n ...RequestIdShape,\n // 失败路径只送 errorCode/error, sessionId 此时无语义。成功路径才有 id。\n sessionId: IdSchema.optional(),\n mode: z.enum(sessionModeValues).optional(),\n provider: z.enum(providerValues).optional(),\n ptyOwner: z.enum(ptyOwnerValues).optional(),\n ...RequestErrorShape,\n },\n \"proxy_to_client\",\n ),\n\n // 客户端请求会话历史消息,client -> proxy\n control(\n \"session_messages_request\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\n limit: z.number().int().min(1).max(200).optional(),\n before: z.string().optional(),\n },\n \"client_to_proxy\",\n ),\n\n // 客户端请求会话资源(命令列表 + 文件树),client -> proxy\n control(\n \"session_resources_request\",\n { ...RequestIdShape, sessionId: IdSchema },\n \"client_to_proxy\",\n ),\n control(\n \"session_resources_response\",\n {\n ...RequestIdShape,\n ...RequestErrorShape,\n sessionId: IdSchema,\n commands: z.array(CommandEntrySchema),\n groups: z.array(FileTreeGroupSchema),\n },\n \"proxy_to_client\",\n ),\n\n // 客户端请求当前 provider 语义状态;不经 relay 缓存,由 proxy 返回当前值\n control(\n \"agent_status_request\",\n { ...RequestIdShape, sessionId: IdSchema.optional() },\n \"client_to_proxy\",\n ),\n control(\n \"agent_status_response\",\n {\n ...RequestIdShape,\n statuses: z.array(z.object({ sessionId: IdSchema, payload: AgentStatusPayloadSchema })),\n },\n \"proxy_to_client\",\n ),\n\n // 客户端确认已收到审批请求;proxy 只记录送达状态,不把它当成用户决策\n control(\n \"permission_request_delivered\",\n { sessionId: IdSchema, requestId: IdSchema },\n \"client_to_proxy\",\n ),\n control(\n \"tool_approve\",\n { sessionId: IdSchema, payload: ToolApprovePayloadSchema },\n \"client_to_proxy\",\n ),\n control(\n \"tool_deny\",\n { sessionId: IdSchema, payload: ToolDenyPayloadSchema },\n \"client_to_proxy\",\n ),\n\n // proxy 确认用户决策已进入 provider/worker 路径;web 用它更新审批卡片状态\n control(\n \"permission_decision_result\",\n {\n sessionId: IdSchema,\n requestId: IdSchema,\n outcome: z.enum([\"allow\", \"deny\"]),\n delivered: z.boolean(),\n message: z.string().optional(),\n },\n \"proxy_to_client\",\n ),\n\n // proxy 推送当前 pending 的工具审批列表,client 据此恢复审批卡片\n control(\n \"pending_approvals_push\",\n {\n sessionId: IdSchema,\n approvals: z.array(\n z.object({\n requestId: IdSchema,\n toolName: z.string(),\n input: z.record(z.string(), z.unknown()),\n }),\n ),\n },\n \"proxy_to_client\",\n ),\n\n // 恢复会话时推送历史消息,proxy -> client\n control(\n \"session_history_messages\",\n {\n ...RequestIdShape,\n sessionId: IdSchema,\n before: z.string().optional(),\n messages: z.array(SessionHistoryMessageSchema),\n hasMore: z.boolean().optional(),\n nextBefore: z.string().optional(),\n },\n \"proxy_to_client\",\n ),\n\n // proxy 重连后同步活跃 session 列表给 relay。session_sync 由 relay 自消费(更新 proxy-session\n // 关联)不转发给 client,因此**没有** direction 标注——RelayControlDirection 只描述转发流。\n control(\"session_sync\", {\n sessions: z.array(\n z.object({\n id: z.string(),\n mode: z.enum(sessionModeValues),\n provider: z.enum(providerValues),\n ptyOwner: z.enum(ptyOwnerValues).optional(),\n state: z.enum(sessionStateValues),\n }),\n ),\n }),\n\n // PTY 会话订阅,client -> proxy,触发 terminal serialize() 返回当前状态\n control(\n \"session_subscribe\",\n { sessionId: IdSchema, requestId: IdSchema.optional() },\n \"client_to_proxy\",\n ),\n\n // PTY 会话快照,proxy -> client,serialize() 的全量终端状态\n control(\n \"session_snapshot\",\n {\n sessionId: IdSchema,\n cols: z.number().int().positive(),\n rows: z.number().int().positive(),\n data: z.string(),\n outputSeq: z.number().int().nonnegative(),\n requestId: IdSchema.optional(),\n },\n \"proxy_to_client\",\n ),\n] as const;\n\nconst relayControlSchemas = relayControlDefinitions.map((definition) => definition.schema) as [\n (typeof relayControlDefinitions)[number][\"schema\"],\n ...Array<(typeof relayControlDefinitions)[number][\"schema\"]>,\n];\n\nexport const RelayControlSchema = z.discriminatedUnion(\"type\", relayControlSchemas);\n\nexport type RelayControlMessage = z.infer<typeof RelayControlSchema>;\nexport type RelayControlType = RelayControlMessage[\"type\"];\n\nexport const ProxyToClientRelayControlTypes = new Set(\n relayControlDefinitions\n .filter((definition) => definition.directions.has(\"proxy_to_client\"))\n .map((definition) => definition.type),\n);\n\nexport function isProxyToClientRelayControlType(type: RelayControlType): boolean {\n return ProxyToClientRelayControlTypes.has(type);\n}\n\nexport const ClientToProxyRelayControlTypes = new Set(\n relayControlDefinitions\n .filter((definition) => definition.directions.has(\"client_to_proxy\"))\n .map((definition) => definition.type),\n);\n\nexport function isClientToProxyRelayControlType(type: RelayControlType): boolean {\n return ClientToProxyRelayControlTypes.has(type);\n}\n","// relay 实际发出的 6 个错误码。schema 侧 RelayControlSchema.relay_error.code 用 z.enum 收紧,\n// handler 侧用这个常量引用避免裸字面量拼错。\nexport const RelayErrorCode = {\n NOT_REGISTERED: \"NOT_REGISTERED\",\n NOT_BOUND: \"NOT_BOUND\",\n PROXY_OFFLINE: \"PROXY_OFFLINE\",\n INVALID_MESSAGE: \"INVALID_MESSAGE\",\n UNSUPPORTED: \"UNSUPPORTED\",\n INVALID_RANGE: \"INVALID_RANGE\",\n} as const;\n\nexport type RelayErrorCode = (typeof RelayErrorCode)[keyof typeof RelayErrorCode];\n","export const ControlErrorCode = {\n INVALID_PATH: \"INVALID_PATH\",\n PATH_NOT_FOUND: \"PATH_NOT_FOUND\",\n PATH_NOT_DIRECTORY: \"PATH_NOT_DIRECTORY\",\n PATH_ACCESS_DENIED: \"PATH_ACCESS_DENIED\",\n PROXY_OFFLINE: \"PROXY_OFFLINE\",\n SESSION_NOT_FOUND: \"SESSION_NOT_FOUND\",\n PROVIDER_UNSUPPORTED: \"PROVIDER_UNSUPPORTED\",\n WORKER_START_FAILED: \"WORKER_START_FAILED\",\n PROCESS_START_FAILED: \"PROCESS_START_FAILED\",\n UNKNOWN: \"UNKNOWN\",\n} as const;\n\nexport type ControlErrorCode = (typeof ControlErrorCode)[keyof typeof ControlErrorCode];\n","// 会话状态枚举\nexport const SessionState = {\n IDLE: \"idle\",\n WORKING: \"working\",\n WAITING_APPROVAL: \"waiting_approval\",\n ERROR: \"error\",\n TERMINATED: \"terminated\",\n} as const;\n\nexport type SessionState = (typeof SessionState)[keyof typeof SessionState];\n","// PTY binary 帧 wire 格式(跨 proxy / relay / web 三方通用):\n// [1B sessionId_len][sessionId UTF-8][4B outputSeq uint32LE][PTY data]\n// 任一方手写偏移量都极易在协议演进时漂移,统一走这里编/解码避免分叉。\n//\n// 用 Uint8Array 而非 Buffer:shared 包必须可以在浏览器构建里跑,\n// 不能依赖 Node-only 的 Buffer 全局。Node 调用方拿到的也是 Uint8Array\n// (Buffer 是 Uint8Array 的子类,反向兼容),传给 ws.send 等 API 没问题。\n\nexport interface DecodedBinaryFrame {\n sessionId: string;\n outputSeq: number;\n data: Uint8Array;\n}\n\nconst SID_LEN_BYTES = 1;\nconst SEQ_BYTES = 4;\nconst HEADER_FIXED_BYTES = SID_LEN_BYTES + SEQ_BYTES;\n\nexport function encodeBinaryFrame(\n sessionId: string,\n outputSeq: number,\n data: Uint8Array,\n): Uint8Array {\n const sidBytes = new TextEncoder().encode(sessionId);\n if (sidBytes.length === 0 || sidBytes.length > 255) {\n throw new RangeError(\n `sessionId byte length must be 1-255, got ${sidBytes.length} (sessionId=${sessionId})`,\n );\n }\n if (!Number.isInteger(outputSeq) || outputSeq < 0 || outputSeq > 0xffffffff) {\n throw new RangeError(`outputSeq must be a uint32, got ${outputSeq}`);\n }\n\n const frame = new Uint8Array(SID_LEN_BYTES + sidBytes.length + SEQ_BYTES + data.length);\n frame[0] = sidBytes.length;\n frame.set(sidBytes, SID_LEN_BYTES);\n const seqOffset = SID_LEN_BYTES + sidBytes.length;\n // little-endian 与 Node 端 Buffer.writeUInt32LE / 浏览器 DataView setUint32(_, _, true) 保持一致\n new DataView(frame.buffer, frame.byteOffset + seqOffset, SEQ_BYTES).setUint32(\n 0,\n outputSeq,\n true,\n );\n frame.set(data, seqOffset + SEQ_BYTES);\n return frame;\n}\n\n// 解码失败一律返回 null,调用方按业务诉求决定是丢帧还是关连接。\nexport function decodeBinaryFrame(view: Uint8Array): DecodedBinaryFrame | null {\n if (view.length < SID_LEN_BYTES + SEQ_BYTES) return null;\n const sidLen = view[0];\n if (sidLen === 0) return null;\n if (view.length < SID_LEN_BYTES + sidLen + SEQ_BYTES) return null;\n\n const sessionId = new TextDecoder().decode(view.subarray(SID_LEN_BYTES, SID_LEN_BYTES + sidLen));\n const seqOffset = SID_LEN_BYTES + sidLen;\n const outputSeq = new DataView(view.buffer, view.byteOffset + seqOffset, SEQ_BYTES).getUint32(\n 0,\n true,\n );\n const data = view.subarray(seqOffset + SEQ_BYTES);\n return { sessionId, outputSeq, data };\n}\n\n// 帧头长度(不含 payload data):调用方在做 chunk 拆分 / 估算缓冲时用得到。\nexport function binaryFrameHeaderLength(sessionId: string): number {\n const sidBytes = new TextEncoder().encode(sessionId);\n return HEADER_FIXED_BYTES + sidBytes.length;\n}\n","import { z } from \"zod\";\nimport {\n SessionState,\n encodeBinaryFrame,\n decodeBinaryFrame,\n ptySemanticStateValues,\n} from \"@dev-anywhere/shared\";\nimport { LineBuffer } from \"./line-buffer.js\";\n\n// IPC binary 帧标记字节,0x00 不可能是 JSON 行的首字节(JSON 以 '{' 开头)\nexport const IPC_BINARY_MARKER = 0x00;\n\n// IPC binary 帧外层 = [1B marker][4B payload_len uint32LE] + 内层 PTY 帧(来自 shared/binary-frame)。\n// 内层格式([1B sid_len][sid][4B seq][data])由 encodeBinaryFrame 统一管理,\n// 避免与 hosted-pty-registry / terminal-ipc / web 各自手写偏移量分叉。\nexport function encodeBinaryIpcFrame(sessionId: string, data: Buffer, outputSeq: number): Buffer {\n const inner = encodeBinaryFrame(sessionId, outputSeq, data);\n const frame = Buffer.alloc(1 + 4 + inner.length);\n frame[0] = IPC_BINARY_MARKER;\n frame.writeUInt32LE(inner.length, 1);\n frame.set(inner, 5);\n return frame;\n}\n\nconst sessionStateValues = Object.values(SessionState) as [SessionState, ...SessionState[]];\n\nconst ProviderHookContextSchema = z.object({\n provider: z.enum([\"claude\", \"codex\"]),\n sessionId: z.string(),\n hookUrl: z.string(),\n marker: z.string(),\n token: z.string(),\n});\n\n// IPC 消息 schema,客户端与服务端通过 Unix domain socket 使用 NDJSON 通信\nexport const IpcMessageSchema = z.discriminatedUnion(\"type\", [\n // 客户端请求创建新会话,sessionId 可选用于重连时复用\n z.object({\n type: z.literal(\"session_create_request\"),\n name: z.string().optional(),\n mode: z.enum([\"pty\", \"json\"]),\n provider: z.enum([\"claude\", \"codex\"]),\n cwd: z.string(),\n pid: z.number(),\n sessionId: z.string().optional(),\n }),\n\n // 服务端响应创建会话\n z.object({\n type: z.literal(\"session_create_response\"),\n sessionId: z.string(),\n error: z.string().optional(),\n hook: ProviderHookContextSchema.optional(),\n }),\n\n // 客户端请求终止会话\n z.object({\n type: z.literal(\"session_terminate_request\"),\n sessionId: z.string(),\n }),\n\n // 服务端响应终止会话\n z.object({\n type: z.literal(\"session_terminate_response\"),\n sessionId: z.string(),\n success: z.boolean(),\n }),\n\n // 客户端向服务端注册 PTY 会话\n z.object({\n type: z.literal(\"pty_register\"),\n sessionId: z.string(),\n pid: z.number(),\n }),\n\n // 客户端取消注册 PTY 会话\n z.object({\n type: z.literal(\"pty_deregister\"),\n sessionId: z.string(),\n }),\n\n // 输入,从服务端转发到客户端的 PTY stdin(手机远程输入注入)\n z.object({\n type: z.literal(\"pty_input\"),\n sessionId: z.string(),\n data: z.string(),\n }),\n\n // serve → terminal:Web 端移除本地终端会话时,只断开远程视图,不杀本地 CLI。\n z.object({\n type: z.literal(\"pty_detach\"),\n sessionId: z.string(),\n }),\n\n // 服务端广播会话状态变更\n z.object({\n type: z.literal(\"session_status_update\"),\n sessionId: z.string(),\n state: z.enum(sessionStateValues),\n }),\n\n // 错误响应\n z.object({\n type: z.literal(\"error\"),\n message: z.string(),\n code: z.string().optional(),\n }),\n\n // 客户端请求服务状态(含 relay 连接信息和 worker 状态)\n z.object({\n type: z.literal(\"service_status_request\"),\n }),\n\n // 服务端响应增强版服务状态\n z.object({\n type: z.literal(\"service_status_response\"),\n config: z.object({\n profile: z.string().optional(),\n relayName: z.string(),\n relayNameSource: z.enum([\"cli\", \"profile\"]),\n relayUrl: z.string().optional(),\n relayUrlSource: z.enum([\"env\", \"file\", \"none\"]),\n relayTokenSource: z.enum([\"env\", \"file\", \"none\"]),\n hookPort: z.number(),\n hookPortSource: z.enum([\"env\", \"file\", \"default\"]),\n }),\n relay: z\n .object({\n connected: z.boolean(),\n proxyId: z.string(),\n reconnectAttempt: z.number(),\n queueDepth: z.number(),\n })\n .nullable(),\n sessions: z.array(\n z.object({\n id: z.string(),\n mode: z.enum([\"pty\", \"json\"]),\n state: z.enum(sessionStateValues),\n createdAt: z.string(),\n name: z.string().optional(),\n hasWorker: z.boolean(),\n }),\n ),\n }),\n\n // terminal → serve:终端标题变化,由 xterm onTitleChange 触发\n z.object({\n type: z.literal(\"pty_title_change\"),\n sessionId: z.string(),\n title: z.string(),\n }),\n\n // terminal → serve:local runtime 观察到的 PTY 语义事件。\n z.object({\n type: z.literal(\"pty_semantic_event\"),\n sessionId: z.string(),\n state: z.enum(ptySemanticStateValues),\n title: z.string().optional(),\n tool: z.string().optional(),\n }),\n\n // terminal → serve:终端尺寸变化\n z.object({\n type: z.literal(\"pty_resize\"),\n sessionId: z.string(),\n cols: z.number(),\n rows: z.number(),\n }),\n\n // serve → terminal:请求 HeadlessTerminal serialize() 快照\n z.object({\n type: z.literal(\"pty_subscribe\"),\n sessionId: z.string(),\n requestId: z.string().optional(),\n }),\n\n // terminal → serve:serialize() 结果\n z.object({\n type: z.literal(\"pty_snapshot\"),\n sessionId: z.string(),\n cols: z.number(),\n rows: z.number(),\n data: z.string(),\n outputSeq: z.number().int().nonnegative(),\n requestId: z.string().optional(),\n }),\n\n // serve → terminal:relay 连接状态变更,供终端给用户显示 remote viewing 是否通畅\n z.object({\n type: z.literal(\"bridge_status\"),\n connected: z.boolean(),\n }),\n]);\n\n// serve 与 session-worker 之间的通信协议\nexport const WorkerMessageSchema = z.discriminatedUnion(\"type\", [\n // serve → worker: 发送用户输入给 claude\n z.object({\n type: z.literal(\"worker_input\"),\n content: z.string(),\n }),\n\n // serve → worker: 停止 claude 进程\n z.object({\n type: z.literal(\"worker_stop\"),\n }),\n\n // serve → worker: 工具审批响应\n z.object({\n type: z.literal(\"worker_approval_response\"),\n requestId: z.string(),\n behavior: z.enum([\"allow\", \"deny\"]),\n message: z.string().optional(),\n }),\n\n // worker → serve: claude 输出事件(带序列号)\n z.object({\n type: z.literal(\"worker_event\"),\n seq: z.number(),\n event: z.record(z.string(), z.unknown()),\n }),\n\n // worker → serve: claude 进程退出\n z.object({\n type: z.literal(\"worker_exit\"),\n code: z.number(),\n }),\n\n // worker → serve: 工具审批请求\n z.object({\n type: z.literal(\"worker_approval_request\"),\n requestId: z.string(),\n toolName: z.string(),\n input: z.record(z.string(), z.unknown()),\n }),\n\n // worker → serve: worker 就绪,claude 已启动\n z.object({\n type: z.literal(\"worker_ready\"),\n pid: z.number(),\n }),\n\n // worker → serve: 从 stream-json 的 system.init 事件捕获 Claude CLI 侧的 session ID\n // proxy 拿它来读 ~/.claude/projects/.../<id>.jsonl 历史或后续 --resume\n z.object({\n type: z.literal(\"worker_claude_session_id\"),\n sessionId: z.string(),\n }),\n\n // serve → worker: 将指定工具加入会话白名单,后续同名工具自动审批\n z.object({\n type: z.literal(\"worker_whitelist_add\"),\n toolName: z.string(),\n }),\n]);\n\nexport type WorkerMessage = z.infer<typeof WorkerMessageSchema>;\n\nexport function serializeWorkerMsg(msg: WorkerMessage): string {\n return JSON.stringify(msg) + \"\\n\";\n}\n\n// onProtocolError:单条 NDJSON 行 parse 失败 / schema 校验失败时回调(不会终止 reader)。\n// 历史上这里用 stream.emit(\"error\") 把传输层炸掉,触发 socket close 与 onDisconnect,等于让一条\n// 协议层的不兼容消息把整个 session 推进 ERROR 态——这是 Claude/Codex CLI 增加新事件类型时的真实风险。\n// 现在改为 callback:调用方自行决定 log + skip 还是断开,传输层保持开放。\nexport function createWorkerReader(\n stream: NodeJS.ReadableStream,\n onMessage: (msg: WorkerMessage) => void,\n onProtocolError?: (err: Error, line: string) => void,\n): void {\n const lineBuffer = new LineBuffer();\n lineBuffer.on(\"data\", (line: Buffer | string) => {\n const str = typeof line === \"string\" ? line : line.toString();\n if (str.length === 0) return;\n try {\n const raw = JSON.parse(str);\n const result = WorkerMessageSchema.safeParse(raw);\n if (result.success) {\n onMessage(result.data);\n } else {\n onProtocolError?.(\n new Error(`Worker message validation failed: ${result.error.message}`),\n str,\n );\n }\n } catch (err) {\n onProtocolError?.(new Error(\"Worker message parse error\", { cause: err }), str);\n }\n });\n (stream as NodeJS.ReadableStream).pipe(lineBuffer);\n}\n\nexport type IpcMessage = z.infer<typeof IpcMessageSchema>;\n\n// 将 IPC 消息序列化为 NDJSON 格式的字符串\nexport function serializeIpc(msg: IpcMessage): string {\n return JSON.stringify(msg) + \"\\n\";\n}\n\n// 混合协议 IPC 读取器,支持 NDJSON 控制消息和 binary PTY 帧。\n// binary 帧以 0x00 开头,NDJSON 行以 '{' 开头,通过首字节区分。\n// 返回 dispose 函数用于摘掉 'data' 监听,长连接可以忽略,一次性等待(如 waitForMessage)必须调用避免累积 listener 重复解析每条消息。\n// 同 createWorkerReader:onProtocolError 让协议层 parse / schema 错误不再走 stream.emit(\"error\"),\n// 由调用方决定如何处理(warn-skip / disconnect)。默认 silent drop 是为了向后兼容尚未挂回调的调用点。\nexport function createIpcReader(\n stream: NodeJS.ReadableStream,\n onMessage: (msg: IpcMessage) => void,\n onBinaryFrame?: (sessionId: string, data: Buffer, outputSeq: number) => void,\n onProtocolError?: (err: Error, line: string) => void,\n): () => void {\n let buf = Buffer.alloc(0);\n let disposed = false;\n\n // 解析状态机:不断消费 buf 中的完整消息\n function drain(): void {\n while (buf.length > 0) {\n if (buf[0] === IPC_BINARY_MARKER) {\n // binary 帧: [1B marker][4B payload_len LE][payload],需要至少 5 字节才能读取 header\n if (buf.length < 5) return;\n const payloadLen = buf.readUInt32LE(1);\n const totalFrameLen = 1 + 4 + payloadLen;\n if (buf.length < totalFrameLen) return;\n\n // payload 内层就是 shared 端定义的 PTY frame([sid_len][sid][seq][data]),\n // 解码同样走 decodeBinaryFrame 保持单一权威。\n const decoded = decodeBinaryFrame(buf.subarray(5, totalFrameLen));\n if (decoded && onBinaryFrame) {\n // ptyData copy 保留与旧代码一致的语义:调用方拿到的是独立 Buffer,\n // 不会被后续 buf reslice 影响。\n onBinaryFrame(decoded.sessionId, Buffer.from(decoded.data), decoded.outputSeq);\n }\n\n buf = buf.subarray(totalFrameLen);\n } else {\n // NDJSON 行: 找 \\n 分隔符\n const newlineIdx = buf.indexOf(0x0a); // '\\n'\n if (newlineIdx === -1) return;\n\n const line = buf.subarray(0, newlineIdx).toString(\"utf-8\");\n buf = buf.subarray(newlineIdx + 1);\n\n if (line.length === 0) continue;\n\n try {\n const raw = JSON.parse(line);\n const result = IpcMessageSchema.safeParse(raw);\n if (result.success) {\n onMessage(result.data);\n } else {\n onProtocolError?.(\n new Error(`IPC message validation failed: ${result.error.message}`),\n line,\n );\n }\n } catch (err) {\n onProtocolError?.(new Error(\"IPC message parse error\", { cause: err }), line);\n }\n }\n }\n }\n\n function onData(chunk: Buffer | string): void {\n if (disposed) return;\n const incoming = typeof chunk === \"string\" ? Buffer.from(chunk) : chunk;\n buf = Buffer.concat([buf, incoming]);\n drain();\n }\n\n stream.on(\"data\", onData);\n\n return () => {\n disposed = true;\n stream.off(\"data\", onData);\n };\n}\n","import { Transform, type TransformCallback } from \"node:stream\";\nimport { StringDecoder } from \"node:string_decoder\";\n\n// 将任意 data 事件分割为完整的 \\n 分隔行\n// Node.js data 事件不保证按行边界分割,此 Transform 保证每次 push 一个完整行\n//\n// 多字节 UTF-8 字符 (CJK 3 字节 / emoji 4 字节) 可能跨 chunk 边界。直接对单 chunk 调用\n// Buffer.toString() 会把不完整字节序列解码成 U+FFFD 替换字符——claude / codex CLI 输出\n// 中文 / emoji 时 stream-json 行变乱码, 解 JSON 失败被 schema 静默丢弃。StringDecoder\n// 跨 chunk 维护内部状态, 不完整字节缓存到下一次 write 拼上再解。\nexport class LineBuffer extends Transform {\n private buffer = \"\";\n private decoder = new StringDecoder(\"utf8\");\n\n _transform(chunk: Buffer | string, _encoding: BufferEncoding, callback: TransformCallback): void {\n this.buffer += typeof chunk === \"string\" ? chunk : this.decoder.write(chunk);\n const segments = this.buffer.split(\"\\n\");\n // 最后一段可能是不完整行,保留到 buffer\n this.buffer = segments.pop()!;\n\n for (const segment of segments) {\n if (segment.length > 0) {\n this.push(segment);\n }\n }\n callback();\n }\n\n _flush(callback: TransformCallback): void {\n // decoder.end() 把仍在缓存里的不完整字节序列以 U+FFFD 形式吐出, 避免静默丢字节\n const tail = this.decoder.end();\n if (tail.length > 0) this.buffer += tail;\n if (this.buffer.length > 0) {\n this.push(this.buffer);\n this.buffer = \"\";\n }\n callback();\n }\n}\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z","z","z","sessionStateValues","z"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/common/osc-extractor.ts","../src/common/pty-approval-state.ts","../src/common/pty-semantic-machine.ts"],"sourcesContent":["import type { PtySemanticState } from \"@dev-anywhere/shared\";\n\n// OSC 0: 窗口标题 -- ESC ] 0 ; <title> BEL/ST\n// OSC 9: 通知 -- ESC ] 9 ; <text> BEL/ST\n// 每次调用创建新的 regex 实例避免 g flag 导致的 lastIndex 状态泄漏\n// eslint-disable-next-line no-control-regex\nconst OSC_PATTERN = /\\x1b\\](\\d+);([^\\x07\\x1b]*?)(?:\\x07|\\x1b\\\\)/g;\n\ntype PtySignalProvider = \"claude\" | \"codex\";\n\ninterface PtyStateEvent {\n state: PtySemanticState | null;\n title?: string;\n tool?: string;\n}\n\ninterface OscSequence {\n code: number;\n text: string;\n}\n\nexport function extractOscSequences(rawData: string): OscSequence[] {\n const regex = new RegExp(OSC_PATTERN.source, OSC_PATTERN.flags);\n const matches: OscSequence[] = [];\n\n let match: RegExpExecArray | null;\n while ((match = regex.exec(rawData)) !== null) {\n matches.push({ code: parseInt(match[1], 10), text: match[2] });\n }\n\n return matches;\n}\n\nfunction lastSequence(matches: OscSequence[], code: number): OscSequence | undefined {\n for (let i = matches.length - 1; i >= 0; i -= 1) {\n if (matches[i].code === code) return matches[i];\n }\n return undefined;\n}\n\nfunction isCodexActionRequiredTitle(title: string): boolean {\n return /\\bAction Required\\b/i.test(title);\n}\n\n// 从 PTY 原始数据中提取 OSC 语义信号。\n// OSC 9 优先级高于 OSC 0,无匹配时返回 null。\n// 仅 OSC 0(spinner/标题)返回 { state: null, title },让调用方推 title 但不动 FSM。\nexport function extractOscSignals(\n rawData: string,\n provider?: PtySignalProvider,\n): PtyStateEvent | null {\n const matches = extractOscSequences(rawData);\n\n if (matches.length === 0) return null;\n\n const osc0 = lastSequence(matches, 0);\n\n // OSC 9 优先级更高,包含具体的语义信号;同帧 OSC 0 仍保留 title 给 UI。\n const osc9 = lastSequence(matches, 9);\n if (osc9) {\n if (osc9.text.includes(\"waiting for your input\") || osc9.text.trim() === \"4;0;\") {\n return { state: \"turn_complete\", ...(osc0 ? { title: osc0.text } : {}) };\n }\n if (osc9.text.includes(\"needs your permission\")) {\n const toolMatch = osc9.text.match(/permission.*?:\\s*(\\S+)/);\n return {\n state: \"approval_wait\",\n ...(osc0 ? { title: osc0.text } : {}),\n ...(toolMatch?.[1] ? { tool: toolMatch[1] } : {}),\n };\n }\n }\n\n if (provider === \"codex\" && osc0 && isCodexActionRequiredTitle(osc0.text)) {\n return { state: \"approval_wait\", title: osc0.text };\n }\n\n // 仅 OSC 0:标题/spinner 更新,没有明确语义信号。state=null 让上层只推 title。\n if (osc0 && !osc9) {\n return { state: null, title: osc0.text };\n }\n\n return null;\n}\n","import type { PtySemanticState } from \"@dev-anywhere/shared\";\n\n// signalState 三态:undefined 表示本帧没有任何 signal;null 表示有 signal 但只承载 title(OSC 0\n// only,无明确语义);其它值是明确语义状态。title-only signal 也视为对 approval_wait 的释放\n// 信号——codex 取消审批后 OSC 0 标题脱离 \"Action Required\" 即走这条路径。\nexport function shouldReleaseApprovalWait(options: {\n currentState: PtySemanticState;\n signalState: PtySemanticState | null | undefined;\n}): boolean {\n if (options.currentState !== \"approval_wait\") return false;\n if (options.signalState === undefined) return false;\n return options.signalState !== \"approval_wait\";\n}\n\nexport function stateAfterApprovalRelease(\n signalState: PtySemanticState | null,\n): PtySemanticState {\n // title-only 释放(signal.state===null)= codex 取消审批语义,等用户下一轮输入。\n if (signalState === null) return \"turn_complete\";\n return signalState !== \"approval_wait\" ? signalState : \"working\";\n}\n","import type { PtySemanticState } from \"@dev-anywhere/shared\";\nimport { shouldReleaseApprovalWait, stateAfterApprovalRelease } from \"./pty-approval-state.js\";\n\n// PTY 语义状态机的纯决策层。从一段 PTY 输出抽到的 OSC 信号 + 当前 PTY 局部状态出发,\n// 决定下一态以及是否要 emit 一次 pty_state 事件。\n// 副作用(changeSessionState、onTurnComplete、IPC 写)由调用方负责,本模块零 IO,便于单测。\n//\n// signal.state 三态:明确语义(working/turn_complete/approval_wait)、null(仅 OSC 0 title\n// 更新无语义)、signal 整体为 null(本帧无 OSC)。title-only signal 在 approval_wait 上下文\n// 下视为释放信号(codex 取消审批),其它上下文不参与 FSM 决策。\n\ninterface PtySignal {\n state: PtySemanticState | null;\n title?: string;\n tool?: string;\n}\n\ninterface PtyTransitionInput {\n currentState: PtySemanticState;\n signal: PtySignal | null;\n // hosted-pty 在 JSON FSM 维度也维护 session.state;当外部 hook 已先把 session 推到\n // WAITING_APPROVAL 而本地 currentState 还停在 working 时,本帧仍应被解释为审批等待中。\n // terminal.ts 没有这个上下文,传 false / undefined 即可。\n sessionStateIsWaitingApproval?: boolean;\n}\n\ninterface PtyTransitionResult {\n // 决策后应当采用的 currentState 值;与 input.currentState 相同表示局部状态不变。\n nextState: PtySemanticState;\n // 是否要把这次决策作为一次 pty_state 事件外发(含 hosted 端的 sessionFsm 副作用)。\n emit: boolean;\n // 透传给 emit 事件的 meta;emit=false 时忽略。\n meta?: { title?: string; tool?: string };\n}\n\nfunction withMeta(signal: PtySignal | null | undefined): { title?: string; tool?: string } {\n return {\n ...(signal?.title !== undefined ? { title: signal.title } : {}),\n ...(signal?.tool !== undefined ? { tool: signal.tool } : {}),\n };\n}\n\nexport function decidePtySemanticTransition(input: PtyTransitionInput): PtyTransitionResult {\n const { currentState, signal, sessionStateIsWaitingApproval } = input;\n\n // 1. 显式 approval_wait 信号:进入 / 维持审批等待。\n if (signal?.state === \"approval_wait\") {\n return { nextState: \"approval_wait\", emit: true, meta: withMeta(signal) };\n }\n\n // 2. 已在 approval_wait 且收到非 approval_wait 信号(含 title-only):审批解除,\n // 落到 stateAfterApprovalRelease 决定的状态上。\n if (\n shouldReleaseApprovalWait({\n currentState,\n signalState: signal === null ? undefined : signal.state,\n })\n ) {\n return {\n nextState: stateAfterApprovalRelease(signal!.state),\n emit: true,\n meta: withMeta(signal),\n };\n }\n\n // 3. 审批上下文兜底:currentState=approval_wait 或 sessionState 已是 WAITING_APPROVAL 但还没收到\n // 解除信号。此时即便有其它信号也不应让 PTY 状态偏离审批等待,仍 re-emit approval_wait。\n // turn_complete 不在此列:它是合法的解除终点(由 #2 处理)。\n const inApprovalContext =\n currentState === \"approval_wait\" || sessionStateIsWaitingApproval === true;\n if (inApprovalContext && signal?.state !== \"turn_complete\") {\n return { nextState: \"approval_wait\", emit: true, meta: withMeta(signal) };\n }\n\n // 4. title-only signal(state=null,且不在审批上下文):不参与 FSM 决策,title 由调用方\n // 单独经 sendTerminalTitle 推。这里 emit=false 避免 spam pty_state。\n if (signal !== null && signal.state === null) {\n return { nextState: currentState, emit: false };\n }\n\n // 5. 任意非 working 显式信号(turn_complete):直接落到 signal.state。\n if (signal && signal.state !== null && signal.state !== \"working\") {\n return { nextState: signal.state, emit: true, meta: withMeta(signal) };\n }\n\n // 6. 隐式 working:当前不在 working 且本帧没有显式信号或信号 state=working,则推到 working。\n if (currentState !== \"working\") {\n return { nextState: \"working\", emit: true };\n }\n\n // 7. 无变化:current==working 且没有有效信号。不产生事件。\n return { nextState: currentState, emit: false };\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/common/env.ts"],"sourcesContent":["import { spawn, type ChildProcess, type SpawnOptions } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\nimport type { Logger } from \"pino\";\n\n// 运行环境判断。tsup 打包时通过 define 把 process.env.NODE_ENV 替换为 \"production\",\n// IS_DEV 随之静态折叠为 false,dev 分支被 dead-code elimination 删除。\n// node --import tsx 跑源码时 NODE_ENV 通常未设置,默认 \"development\",IS_DEV 为 true。\nconst IS_DEV = (process.env.NODE_ENV ?? \"development\") !== \"production\";\n\ninterface SpawnScriptOptions extends Omit<SpawnOptions, \"detached\"> {\n // 默认 true。设为 false 时父进程退出会把子进程一并带走。\n unref?: boolean;\n // 传入后启用\"现场记录\":子进程 stderr 按行 pipe 进 logger.warn(stderr 原文语义模糊,\n // 可能只是 deprecation warning),非零退出和 spawn 失败进 logger.error(确定性失败)。\n // 需要自己精确控制 stderr 的调用方(例如 startDaemon 要按超时展示)不要在这里传 logger,\n // 改为显式 `stdio: [\"ignore\", \"ignore\", \"pipe\"]` 自行挂 handler。\n logger?: Logger;\n}\n\n/**\n * spawn 一个 Node 脚本作为后台 detached 子进程。\n *\n * scriptBaseUrl 是**不带扩展名**的脚本 URL;helper 根据 IS_DEV 自动补扩展名并选运行时:\n * dev: 执行 `node --import tsx <path>.ts`\n * prod: 执行 `node <path>.js`\n *\n * 调用方用 `new URL(\"./相对路径\", import.meta.url)` 构造,不需要预先计算 __dirname。\n *\n * @example\n * // terminal.ts 拉起 serve daemon,自动把子进程 stderr 和异常退出接入 terminalLogger:\n * spawnScript(new URL(\"./serve\", import.meta.url), [], { logger: terminalLogger });\n *\n * // serve.ts 拉起 session-worker 并传额外参数:\n * spawnScript(new URL(\"./session-worker\", import.meta.url), [sessionId, sockPath], { logger });\n */\nexport function spawnScript(\n scriptBaseUrl: URL,\n args: readonly string[] = [],\n options: SpawnScriptOptions = {},\n): ChildProcess {\n const { unref = true, logger, ...rest } = options;\n // logger 传入时默认打开 stderr pipe;调用方显式传了 stdio 则尊重调用方。\n const stdio = rest.stdio ?? (logger ? [\"ignore\", \"ignore\", \"pipe\"] : \"ignore\");\n\n const basePath = fileURLToPath(scriptBaseUrl);\n const scriptPath = `${basePath}${IS_DEV ? \".ts\" : \".js\"}`;\n const runtimeArgs = IS_DEV ? [\"--import\", \"tsx\", scriptPath, ...args] : [scriptPath, ...args];\n const child = spawn(process.execPath, runtimeArgs, {\n detached: true,\n ...rest,\n stdio,\n });\n\n if (logger) {\n if (child.stderr) {\n child.stderr.setEncoding(\"utf-8\");\n let buf = \"\";\n const emit = (line: string) => {\n const trimmed = line.trim();\n if (trimmed) logger.warn({ pid: child.pid, src: \"child-stderr\" }, trimmed);\n };\n child.stderr.on(\"data\", (chunk: string) => {\n buf += chunk;\n const lines = buf.split(\"\\n\");\n buf = lines.pop() ?? \"\";\n for (const line of lines) emit(line);\n });\n // 子进程结束时 buf 里可能还有不带 \\n 的残段(比如 stderr 最后一行无换行就退出),\n // 这里 flush 一次保证捕获 100% 行内容,不被\"没等到下一个 \\n\"吞掉。\n child.stderr.on(\"end\", () => {\n if (buf) {\n emit(buf);\n buf = \"\";\n }\n });\n }\n child.once(\"error\", (err) => {\n logger.error({ pid: child.pid, err: String(err) }, \"spawn failed\");\n });\n child.once(\"exit\", (code, signal) => {\n if (code !== 0 && code !== null) {\n logger.error({ pid: child.pid, code, signal }, \"child exited abnormally\");\n }\n });\n }\n\n if (unref) child.unref();\n return child;\n}\n"],"mappings":";;;AAAA,SAAS,aAAmD;AAC5D,SAAS,qBAAqB;AAM9B,IAAM,SAAU;AA4BT,SAAS,YACd,eACA,OAA0B,CAAC,GAC3B,UAA8B,CAAC,GACjB;AACd,QAAM,EAAE,QAAQ,MAAM,QAAQ,GAAG,KAAK,IAAI;AAE1C,QAAM,QAAQ,KAAK,UAAU,SAAS,CAAC,UAAU,UAAU,MAAM,IAAI;AAErE,QAAM,WAAW,cAAc,aAAa;AAC5C,QAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,QAAQ,KAAK;AACvD,QAAM,cAAc,SAAS,CAAC,YAAY,OAAO,YAAY,GAAG,IAAI,IAAI,CAAC,YAAY,GAAG,IAAI;AAC5F,QAAM,QAAQ,MAAM,QAAQ,UAAU,aAAa;AAAA,IACjD,UAAU;AAAA,IACV,GAAG;AAAA,IACH;AAAA,EACF,CAAC;AAED,MAAI,QAAQ;AACV,QAAI,MAAM,QAAQ;AAChB,YAAM,OAAO,YAAY,OAAO;AAChC,UAAI,MAAM;AACV,YAAM,OAAO,CAAC,SAAiB;AAC7B,cAAM,UAAU,KAAK,KAAK;AAC1B,YAAI,QAAS,QAAO,KAAK,EAAE,KAAK,MAAM,KAAK,KAAK,eAAe,GAAG,OAAO;AAAA,MAC3E;AACA,YAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,eAAO;AACP,cAAM,QAAQ,IAAI,MAAM,IAAI;AAC5B,cAAM,MAAM,IAAI,KAAK;AACrB,mBAAW,QAAQ,MAAO,MAAK,IAAI;AAAA,MACrC,CAAC;AAGD,YAAM,OAAO,GAAG,OAAO,MAAM;AAC3B,YAAI,KAAK;AACP,eAAK,GAAG;AACR,gBAAM;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH;AACA,UAAM,KAAK,SAAS,CAAC,QAAQ;AAC3B,aAAO,MAAM,EAAE,KAAK,MAAM,KAAK,KAAK,OAAO,GAAG,EAAE,GAAG,cAAc;AAAA,IACnE,CAAC;AACD,UAAM,KAAK,QAAQ,CAAC,MAAM,WAAW;AACnC,UAAI,SAAS,KAAK,SAAS,MAAM;AAC/B,eAAO,MAAM,EAAE,KAAK,MAAM,KAAK,MAAM,OAAO,GAAG,yBAAyB;AAAA,MAC1E;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,MAAO,OAAM,MAAM;AACvB,SAAO;AACT;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/terminal.ts","../src/terminal/tty.ts","../src/terminal/pty-manager.ts","../src/terminal/cwd.ts","../src/terminal/serve-bootstrap.ts","../src/terminal/idle-checker.ts","../src/terminal/serve-socket-swap.ts","../src/terminal/state.ts"],"sourcesContent":["import type { Socket } from \"node:net\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport { readTtySize, notifyUser } from \"./terminal/tty.js\";\nimport { PtyManager } from \"./terminal/pty-manager.js\";\nimport { resolveTerminalCwd } from \"./terminal/cwd.js\";\nimport { ensureService, tryConnect, waitForMessage } from \"./terminal/serve-bootstrap.js\";\nimport { createIdleChecker, type IdleChecker } from \"./terminal/idle-checker.js\";\nimport { swapServeSocket } from \"./terminal/serve-socket-swap.js\";\nimport pkg from \"@xterm/headless\";\nconst { Terminal: HeadlessTerminal } = pkg;\nimport { SerializeAddon } from \"@xterm/addon-serialize\";\nimport { UnicodeGraphemesAddon } from \"@xterm/addon-unicode-graphemes\";\nimport { extractOscSequences, extractOscSignals } from \"./common/osc-extractor.js\";\nimport { createFSM, type PtySemanticState } from \"@dev-anywhere/shared\";\nimport { decidePtySemanticTransition } from \"./common/pty-semantic-machine.js\";\nimport { TerminalState, TERMINAL_TRANSITIONS, createExitHandler } from \"./terminal/state.js\";\nimport { existsSync } from \"node:fs\";\nimport { SOCK_PATH, STOPPED_PATH, tildify } from \"./common/paths.js\";\nimport {\n createIpcReader,\n serializeIpc,\n encodeBinaryIpcFrame,\n type IpcMessage,\n} from \"./ipc/ipc-protocol.js\";\nimport { terminalLogger as log } from \"./common/logger.js\";\nimport {\n CLAUDE_PROVIDER,\n CODEX_PROVIDER,\n type ProviderAdapter,\n type ProviderHookContext,\n type ProviderId,\n} from \"./providers/index.js\";\n\n// idle 检测:超过 IDLE_THRESHOLD_MS 无输出则翻转 working -> turn_complete\nconst IDLE_CHECK_INTERVAL_MS = 3_000;\nconst IDLE_THRESHOLD_MS = 3_000;\n\n// serve 连接断开后的重连重试参数\nconst RECONNECT_INITIAL_DELAY_MS = 1_000;\nconst RECONNECT_MAX_DELAY_MS = 5_000;\n// 连续 spawn 失败到达阈值后停止自动 spawn,降为被动 tryConnect 轮询。\n// 作用:环境异常(端口占用、依赖缺失、权限不足)时避免反复拉起短命子进程把日志刷爆。\nconst SPAWN_FAILURE_THRESHOLD = 3;\n\nconst PROVIDERS: Record<ProviderId, ProviderAdapter> = {\n claude: CLAUDE_PROVIDER,\n codex: CODEX_PROVIDER,\n};\n\nclass TerminalSession {\n private readonly fsm = createFSM<TerminalState>({\n initial: TerminalState.INIT,\n transitions: TERMINAL_TRANSITIONS,\n onTransition: (from, to) => log.info({ from, to }, \"Terminal state transition\"),\n });\n private readonly sessionCwd = resolveTerminalCwd();\n // socket 在 run() 中连上 serve 后首次赋值;reconnect 会重新赋值为新实例\n private socket!: Socket;\n private sessionId: string | null = null;\n private hookContext: ProviderHookContext | null = null;\n private ptyManager: PtyManager | null = null;\n private lastOutputTime = 0;\n private idleChecker: IdleChecker | null = null;\n private currentPtyState: PtySemanticState = \"turn_complete\";\n // headless terminal 在本进程维护,用于按需 serialize() 给远程 client\n private headlessTerminal: InstanceType<typeof HeadlessTerminal> | null = null;\n private serializeAddon: SerializeAddon | null = null;\n private outputSeq = 0;\n private remoteDetached = false;\n // 记录上次 bridge 连接状态,避免重连抖动重复打印 banner;\n // 初值 null 确保首次状态变更(无论 true/false)都触发一次输出\n private lastBridgeConnected: boolean | null = null;\n // 收尾函数在 run() 里创建一次,PTY 退出与 SIGTERM 共用;内部通过 fsm EXITED 检查短路\n private cleanupAndExit!: (code: number) => void;\n\n constructor(\n private readonly provider: ProviderAdapter,\n private readonly providerArgs: string[],\n ) {}\n\n async run(): Promise<void> {\n log.info(\"Terminal starting\");\n this.fsm.transitionTo(TerminalState.CONNECTING_SERVICE);\n this.socket = await ensureService();\n\n await this.createSession();\n this.initHeadlessTerminal();\n this.cleanupAndExit = createExitHandler({\n fsm: this.fsm,\n getSocket: () => this.socket,\n getSessionId: () => this.sessionId,\n stopIdleChecker: () => this.idleChecker?.stop(),\n disposeRenderResources: () => {\n this.headlessTerminal?.dispose();\n this.headlessTerminal = null;\n this.serializeAddon = null;\n },\n });\n\n this.setupSocketHandlers();\n this.startPtyManager();\n\n this.socket.write(\n serializeIpc({ type: \"pty_register\", sessionId: this.sessionId!, pid: process.pid }),\n );\n this.replayCurrentPtyState();\n this.fsm.transitionTo(TerminalState.RUNNING);\n this.setupIdleCheck();\n\n process.on(\"SIGTERM\", () => {\n log.info({ sessionId: this.sessionId }, \"SIGTERM received, shutting down\");\n this.cleanupAndExit(143);\n });\n }\n\n private async createSession(): Promise<void> {\n this.fsm.transitionTo(TerminalState.CREATING_SESSION);\n const responsePromise = waitForMessage(this.socket, \"session_create_response\");\n this.socket.write(\n serializeIpc({\n type: \"session_create_request\",\n mode: \"pty\",\n provider: this.provider.id,\n cwd: this.sessionCwd,\n name: tildify(this.sessionCwd),\n pid: process.pid,\n }),\n );\n const response = await responsePromise;\n if (response.error) {\n throw new Error(`Failed to create session: ${response.error}`);\n }\n this.sessionId = response.sessionId;\n this.hookContext = response.hook ?? null;\n }\n\n private initHeadlessTerminal(): void {\n const { cols, rows } = readTtySize(process.stdout);\n log.info(\n { sessionId: this.sessionId, cols, rows },\n \"Session created, initializing headless terminal\",\n );\n this.headlessTerminal = new HeadlessTerminal({\n cols,\n rows,\n scrollback: 5000,\n allowProposedApi: true,\n });\n this.serializeAddon = new SerializeAddon();\n // UnicodeGraphemesAddon activate() 里会设置 activeVersion = '15-graphemes'\n this.headlessTerminal.loadAddon(this.serializeAddon);\n this.headlessTerminal.loadAddon(new UnicodeGraphemesAddon());\n }\n\n private startPtyManager(): void {\n this.ptyManager = new PtyManager({\n provider: this.provider,\n providerArgs: this.providerArgs,\n cwd: this.sessionCwd,\n hook: this.hookContext ?? undefined,\n tap: (data) => this.handlePtyData(data),\n stdin: process.stdin,\n stdout: process.stdout,\n onResize: (newCols, newRows) => {\n if (this.headlessTerminal) this.headlessTerminal.resize(newCols, newRows);\n if (this.socket.writable && this.sessionId) {\n this.socket.write(\n serializeIpc({\n type: \"pty_resize\",\n sessionId: this.sessionId,\n cols: newCols,\n rows: newRows,\n }),\n );\n }\n },\n onSessionExit: (code: number) => {\n log.info({ sessionId: this.sessionId, exitCode: code }, \"PTY exited, cleaning up\");\n this.cleanupAndExit(code);\n },\n });\n this.ptyManager.start();\n log.info({ sessionId: this.sessionId }, \"PTY started with headless terminal\");\n }\n\n // PTY 的每一帧输出都要:追到 headless terminal 状态、推 binary IPC、提取 provider 语义事件\n private handlePtyData(data: string): void {\n this.lastOutputTime = Date.now();\n this.outputSeq += 1;\n\n if (this.headlessTerminal) this.headlessTerminal.write(data);\n\n if (!this.remoteDetached && this.socket.writable && this.sessionId) {\n this.socket.write(\n encodeBinaryIpcFrame(this.sessionId, Buffer.from(data, \"utf-8\"), this.outputSeq),\n );\n }\n\n const oscSequences = extractOscSequences(data);\n const signal = extractOscSignals(data, this.provider.id);\n if (oscSequences.length > 0) {\n log.debug(\n {\n sessionId: this.sessionId,\n oscSequences,\n signal,\n },\n \"PTY OSC sequences parsed\",\n );\n }\n if (signal?.title) {\n this.sendTerminalTitle(signal.title);\n }\n\n // 语义状态机决策(六条规则)抽到 common/pty-semantic-machine:terminal 进程仅 emit 事件,\n // session FSM 副作用由 serve 端在收到 pty_state IPC 后驱动。\n const decision = decidePtySemanticTransition({\n currentState: this.currentPtyState,\n signal: signal ?? null,\n });\n this.currentPtyState = decision.nextState;\n if (decision.emit) {\n this.sendPtyState(decision.nextState, decision.meta);\n }\n }\n\n private sendTerminalTitle(title: string): void {\n if (this.remoteDetached || !this.socket.writable || !this.sessionId) return;\n this.socket.write(\n serializeIpc({\n type: \"pty_title_change\",\n sessionId: this.sessionId,\n title,\n }),\n );\n }\n\n private sendPtyState(state: PtySemanticState, meta?: { title?: string; tool?: string }): void {\n if (this.remoteDetached || !this.socket.writable || !this.sessionId) return;\n this.socket.write(\n serializeIpc({\n type: \"pty_semantic_event\",\n sessionId: this.sessionId,\n state,\n ...(meta?.title !== undefined ? { title: meta.title } : {}),\n ...(meta?.tool !== undefined ? { tool: meta.tool } : {}),\n }),\n );\n log.info(\n { sessionId: this.sessionId, state, title: meta?.title, tool: meta?.tool },\n \"PTY semantic event pushed\",\n );\n }\n\n private replayCurrentPtyState(): void {\n if (this.currentPtyState === \"turn_complete\") return;\n this.sendPtyState(this.currentPtyState);\n }\n\n private handleBridgeStatus(connected: boolean): void {\n if (this.remoteDetached) return;\n if (this.lastBridgeConnected === connected) return;\n this.lastBridgeConnected = connected;\n log.info({ connected }, \"Bridge status changed, notifying user\");\n notifyUser(connected ? \"relay online\" : \"relay offline — remote viewing unavailable\");\n }\n\n private setupSocketHandlers(): void {\n createIpcReader(\n this.socket,\n (msg: IpcMessage) => {\n if (msg.type === \"pty_input\" && msg.sessionId === this.sessionId) {\n log.debug(\n { sessionId: this.sessionId, bytes: msg.data.length },\n \"Remote input received\",\n );\n this.ptyManager?.write(msg.data);\n } else if (msg.type === \"pty_detach\" && msg.sessionId === this.sessionId) {\n this.detachRemoteView();\n } else if (msg.type === \"bridge_status\") {\n this.handleBridgeStatus(msg.connected);\n } else if (msg.type === \"pty_subscribe\" && msg.sessionId === this.sessionId) {\n if (this.serializeAddon && this.headlessTerminal) {\n const data = this.serializeAddon.serialize();\n this.socket.write(\n serializeIpc({\n type: \"pty_snapshot\",\n sessionId: msg.sessionId,\n cols: this.headlessTerminal.cols,\n rows: this.headlessTerminal.rows,\n data,\n outputSeq: this.outputSeq,\n requestId: msg.requestId,\n }),\n );\n log.info(\n {\n sessionId: this.sessionId,\n cols: this.headlessTerminal.cols,\n rows: this.headlessTerminal.rows,\n bytes: data.length,\n },\n \"Snapshot sent via IPC\",\n );\n }\n }\n },\n undefined,\n (err, line) => {\n log.warn(\n { err: err.message, lineLen: line.length },\n \"Serve IPC message dropped (parse/schema error)\",\n );\n },\n );\n\n this.socket.on(\"close\", () => {\n log.info(\"Serve socket closed\");\n if (this.remoteDetached) {\n log.info(\"Remote view detached, skipping serve reconnect\");\n return;\n }\n if (!this.fsm.isIn([TerminalState.RECONNECTING, TerminalState.EXITED])) {\n this.fsm.transitionTo(TerminalState.RECONNECTING);\n this.reconnectToServe();\n }\n });\n\n // socket error 通常和 close 成对出现;这里只记 warn 避免静默吞错,重连仍由 close handler 触发\n this.socket.on(\"error\", (err) => {\n log.warn({ err: err.message }, \"Serve socket error\");\n });\n }\n\n // 超过 IDLE_THRESHOLD_MS 无 PTY 输出则从 working 翻回 turn_complete\n private setupIdleCheck(): void {\n this.idleChecker?.stop();\n this.idleChecker = createIdleChecker({\n intervalMs: IDLE_CHECK_INTERVAL_MS,\n thresholdMs: IDLE_THRESHOLD_MS,\n getLastOutputTime: () => this.lastOutputTime,\n setLastOutputTime: (value) => {\n this.lastOutputTime = value;\n },\n getCurrentState: () => this.currentPtyState,\n onIdle: () => {\n this.currentPtyState = \"turn_complete\";\n this.sendPtyState(\"turn_complete\");\n },\n });\n this.idleChecker.start();\n }\n\n private async reconnectToServe(): Promise<void> {\n log.info(\"Serve connection lost, starting reconnection\");\n\n // 两条路径都不该再继续 spawn daemon:\n // - STOPPED=true:用户主动 dev-anywhere stop,不要对抗用户意图。\n // - consecutiveSpawnFailures 跨过阈值:说明环境有持续性问题,spawn 再多也白搭。\n // 进入 passive 后仅做 tryConnect 等待,daemon 起来或用户 dev-anywhere start 后自动恢复。\n let consecutiveSpawnFailures = 0;\n\n for (let i = 0; ; i++) {\n if (this.remoteDetached) return;\n await sleep(Math.min(RECONNECT_INITIAL_DELAY_MS * (i + 1), RECONNECT_MAX_DELAY_MS));\n\n const stopped = existsSync(STOPPED_PATH);\n const degraded = consecutiveSpawnFailures >= SPAWN_FAILURE_THRESHOLD;\n const passive = stopped || degraded;\n\n try {\n log.debug({ attempt: i + 1, stopped, degraded }, \"Reconnect attempt\");\n const newSocket = passive ? await tryConnect(SOCK_PATH) : await ensureService();\n if (!newSocket) continue;\n\n if (degraded) notifyUser(\"serve daemon reachable, reconnected\");\n consecutiveSpawnFailures = 0;\n\n this.socket = swapServeSocket(this.socket, newSocket);\n log.info({ attempt: i + 1, sessionId: this.sessionId }, \"Reconnected to serve\");\n\n this.setupSocketHandlers();\n\n if (this.sessionId) {\n this.fsm.transitionTo(TerminalState.CREATING_SESSION);\n this.socket.write(\n serializeIpc({\n type: \"session_create_request\",\n mode: \"pty\",\n provider: this.provider.id,\n cwd: this.sessionCwd,\n name: tildify(this.sessionCwd),\n pid: process.pid,\n sessionId: this.sessionId,\n }),\n );\n const resp = await waitForMessage(this.socket, \"session_create_response\");\n if (!resp.error) {\n this.sessionId = resp.sessionId;\n this.socket.write(\n serializeIpc({ type: \"pty_register\", sessionId: this.sessionId, pid: process.pid }),\n );\n this.replayCurrentPtyState();\n this.fsm.transitionTo(TerminalState.RUNNING);\n log.info({ sessionId: this.sessionId }, \"Session re-registered after reconnect\");\n }\n } else {\n this.fsm.transitionTo(TerminalState.RUNNING);\n }\n\n return;\n } catch (err) {\n // passive 模式走 tryConnect,失败返回 null 不抛;这里只可能是 ensureService spawn 失败\n if (!passive) {\n consecutiveSpawnFailures++;\n if (consecutiveSpawnFailures === SPAWN_FAILURE_THRESHOLD) {\n notifyUser(\n `serve daemon spawn failed ${SPAWN_FAILURE_THRESHOLD}x — auto-spawn disabled; check environment or run 'dev-anywhere start'`,\n );\n }\n }\n log.debug(\n { err: err instanceof Error ? err.message : err, attempt: i + 1, degraded },\n \"Reconnect attempt failed\",\n );\n }\n }\n }\n\n private detachRemoteView(): void {\n const sessionId = this.sessionId;\n if (!sessionId) return;\n this.remoteDetached = true;\n this.sessionId = null;\n this.hookContext = null;\n this.currentPtyState = \"turn_complete\";\n log.info({ sessionId }, \"Remote view detached; local PTY keeps running\");\n notifyUser(\"remote viewing detached\");\n if (this.socket.writable) this.socket.end();\n }\n}\n\nfunction providerFromEnv(): ProviderId {\n return process.env.DEV_ANYWHERE_PROVIDER === \"codex\" ? \"codex\" : \"claude\";\n}\n\nexport async function startTerminal(\n providerArgs: string[],\n providerId: ProviderId = providerFromEnv(),\n): Promise<void> {\n await new TerminalSession(PROVIDERS[providerId], providerArgs).run();\n}\n","// 读 stdout cols/rows,非 TTY 抛错。\nexport function readTtySize(stream: NodeJS.WriteStream): { cols: number; rows: number } {\n const { columns, rows } = stream;\n if (columns === undefined || rows === undefined) {\n throw new Error(\n \"stdout is not an interactive TTY (columns/rows undefined); dev-anywhere requires running in a real terminal\",\n );\n }\n return { cols: columns, rows };\n}\n\n// 发一条 OSC 9 iTerm2-style 系统通知 + 响铃。iTerm2 / kitty / wezterm 等会弹出带 message\n// 的系统通知;不认 OSC 9 的终端会忽略转义序列只剩下 BEL 响铃。\n// 用此而非 stderr banner 的原因:dev-anywhere 对 Claude PTY 画面保持透明是硬约束,\n// banner 会挤掉 Claude 的渲染行,OSC 9 不占画面,BEL 是纯听觉信号。\nexport function notifyUser(message: string): void {\n process.stderr.write(`\\x1b]9;${message}\\x07`);\n}\n\n// Provider TUI 可能开启 bracketed paste、application cursor/keypad、mouse tracking、\n// xterm modifyOtherKeys 或 kitty keyboard protocol。若 provider 被远程终止或异常退出,\n// 这些模式可能来不及自行恢复,外层 shell 会把 Ctrl-C 显示成 \";5;99~\" 一类残留序列。\nexport function restoreHostTerminalModes(stream: NodeJS.WriteStream): void {\n if (!stream.isTTY) return;\n const restoreSequences = [\n \"\\x1b[?1l\", // application cursor keys off\n \"\\x1b>\", // application keypad off\n \"\\x1b[?1000l\",\n \"\\x1b[?1002l\",\n \"\\x1b[?1003l\",\n \"\\x1b[?1004l\",\n \"\\x1b[?1006l\",\n \"\\x1b[?1015l\",\n \"\\x1b[?2004l\", // bracketed paste off\n \"\\x1b[>4;0m\", // xterm modifyOtherKeys off\n \"\\x1b[<u\", // kitty keyboard protocol off\n ].join(\"\");\n stream.write(restoreSequences);\n}\n","import * as pty from \"node-pty\";\nimport type { IPty } from \"node-pty\";\nimport type { ProviderAdapter, ProviderHookContext } from \"../providers/index.js\";\nimport { readTtySize, restoreHostTerminalModes } from \"./tty.js\";\n\ninterface PtyManagerOptions {\n provider: ProviderAdapter;\n providerArgs: string[];\n cwd: string;\n hook?: ProviderHookContext;\n tap: (data: string) => void;\n stdin: NodeJS.ReadStream;\n stdout: NodeJS.WriteStream;\n onSessionExit?: (code: number) => void | Promise<void>;\n onResize?: (cols: number, rows: number) => void;\n}\n\nexport class PtyManager {\n private child: IPty | null = null;\n private resizeTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly provider: ProviderAdapter;\n private readonly providerArgs: string[];\n private readonly cwd: string;\n private readonly hook?: ProviderHookContext;\n private readonly tap: (data: string) => void;\n private readonly stdin: NodeJS.ReadStream;\n private readonly stdout: NodeJS.WriteStream;\n private readonly onSessionExit?: (code: number) => void;\n private readonly onResize?: (cols: number, rows: number) => void;\n\n constructor(options: PtyManagerOptions) {\n this.provider = options.provider;\n this.providerArgs = options.providerArgs;\n this.cwd = options.cwd;\n this.hook = options.hook;\n this.tap = options.tap;\n this.stdin = options.stdin;\n this.stdout = options.stdout;\n this.onSessionExit = options.onSessionExit;\n this.onResize = options.onResize;\n }\n\n start(): void {\n const { cols, rows } = readTtySize(this.stdout);\n\n const command = this.provider.buildTerminalCommand(\n { args: this.providerArgs, hook: this.hook },\n process.env,\n );\n const child = pty.spawn(command.command, command.args, {\n name: process.env.TERM ?? \"xterm-256color\",\n cols,\n rows,\n cwd: this.cwd,\n env: command.env as Record<string, string>,\n });\n this.child = child;\n\n // raw mode 仅在 stdin 为 TTY 时开启\n const isInteractive = this.stdin.isTTY === true;\n if (isInteractive) {\n this.stdin.setRawMode(true);\n }\n this.stdin.resume();\n\n // stdin -> PTY\n this.stdin.on(\"data\", (data: Buffer) => {\n child.write(data.toString());\n });\n\n // PTY -> stdout + tap\n child.onData((data: string) => this.handleData(data));\n\n // resize 防抖,50ms 窗口合并快速连续的尺寸变化。timer 持有在实例字段上,cleanup 时\n // clearTimeout 防止 PTY 退出后 callback 还在等待,导致 child.resize 调用已 kill 的子进程。\n this.stdout.on(\"resize\", () => {\n if (this.resizeTimer) clearTimeout(this.resizeTimer);\n this.resizeTimer = setTimeout(() => {\n this.resizeTimer = null;\n if (!this.child) return;\n const { cols: newCols, rows: newRows } = readTtySize(this.stdout);\n this.child.resize(newCols, newRows);\n this.onResize?.(newCols, newRows);\n }, 50);\n });\n\n // 子进程退出,按 Unix 惯例处理信号退出码,通过回调通知调用方\n child.onExit(({ exitCode, signal }) => {\n if (isInteractive) this.disableRawModeQuiet();\n restoreHostTerminalModes(this.stdout);\n const code = signal ? 128 + signal : exitCode;\n this.onSessionExit?.(code);\n });\n\n // stdin 结束时写入 EOF 控制字符到 PTY\n this.stdin.on(\"end\", () => {\n child.write(\"\\x04\");\n });\n }\n\n /**\n * PTY 数据到达时的统一处理:OSC 9 修复 + 输出到终端 + 传给 tap\n */\n private handleData(data: string): void {\n // PTY 的 onlcr 会把 OSC 序列里的 \\n 转成 \\r\\n,还原为 \\n\n const fixed = data.replace(\n // eslint-disable-next-line no-control-regex\n /\\x1b\\]9;([\\s\\S]*?)\\x07/g,\n (_, content: string) => `\\x1b]9;${content.replace(/\\r\\n/g, \"\\n\")}\\x07`,\n );\n this.stdout.write(fixed);\n this.tap(data);\n }\n\n // 向 PTY 子进程写入数据,用于远程输入注入\n write(data: string): void {\n this.child?.write(data);\n }\n\n // 关闭 raw mode 并吞掉 stdin 已关闭场景下的异常。退出 / cleanup 两条路径共用。\n private disableRawModeQuiet(): void {\n try {\n this.stdin.setRawMode(false);\n } catch {\n // stdin 可能已关闭\n }\n }\n\n cleanup(exitCode: number): void {\n if (this.resizeTimer) {\n clearTimeout(this.resizeTimer);\n this.resizeTimer = null;\n }\n if (this.stdin.isTTY) this.disableRawModeQuiet();\n restoreHostTerminalModes(this.stdout);\n if (this.child) {\n try {\n this.child.kill();\n } catch {\n // 子进程可能已退出\n }\n this.child = null;\n }\n this.onSessionExit?.(exitCode);\n }\n}\n","import { statSync } from \"node:fs\";\n\nfunction isDirectory(path: string | undefined): path is string {\n if (!path) return false;\n try {\n return statSync(path).isDirectory();\n } catch {\n return false;\n }\n}\n\nexport function resolveTerminalCwd(env: NodeJS.ProcessEnv = process.env): string {\n const candidates = [env.DEV_ANYWHERE_CWD, env.INIT_CWD, env.PWD, process.cwd()];\n return candidates.find(isDirectory) ?? process.cwd();\n}\n","import { connect, type Socket } from \"node:net\";\nimport { existsSync, unlinkSync } from \"node:fs\";\nimport { setTimeout as sleep } from \"node:timers/promises\";\nimport { SOCK_PATH, STOPPED_PATH, SERVICE_LOG_PATH, PROFILE_NAME } from \"../common/paths.js\";\nimport { spawnScript } from \"../common/env.js\";\nimport { daemonRelayArgs } from \"../common/daemon-env.js\";\nimport { createIpcReader, type IpcMessage } from \"../ipc/ipc-protocol.js\";\nimport { terminalLogger as log } from \"../common/logger.js\";\n\n// serve daemon 自动拉起的连接重试参数\nconst ENSURE_SERVICE_MAX_RETRIES = 20;\nconst ENSURE_SERVICE_INITIAL_DELAY_MS = 100;\nconst ENSURE_SERVICE_MAX_DELAY_MS = 2_000;\n\n// 等待特定类型 IPC 消息的默认超时\nconst WAIT_FOR_MESSAGE_TIMEOUT_MS = 10_000;\n\n// 单次 socket 连接尝试:连上 resolve socket,连不上 resolve null(不抛异常)。\nexport function tryConnect(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\n// 接到 serve daemon 的 unix socket 上:先尝试连接已有进程,连不上则拉起一个新 daemon\n// 子进程并轮询直到 socket 就绪。autoStart=false 用于命令式 status 查询,禁止 spawn。\nexport async function ensureService(autoStart = true): Promise<Socket> {\n const existing = await tryConnect(SOCK_PATH);\n if (existing) {\n log.info(\"Connected to existing service\");\n return existing;\n }\n\n if (!autoStart) throw new Error(\"Service is not running\");\n\n if (existsSync(STOPPED_PATH)) unlinkSync(STOPPED_PATH);\n\n log.info(\"Auto-starting serve daemon\");\n const child = spawnScript(\n new URL(\"../serve\", import.meta.url),\n [\"--profile\", PROFILE_NAME, ...daemonRelayArgs()],\n {\n env: { ...process.env },\n logger: log,\n },\n );\n\n // 监听 daemon 失败信号,让下面的 tryConnect 轮询能在 daemon 启动时就崩的场景下立刻抛诊断。\n // - 'exit':进程启动成功后又退出(配置错误、端口占用、内部崩溃),带 code/signal。\n // - 'error':spawn 本身失败(ENOENT 找不到 tsx/node 等),Node 文档说此时 'exit' may or may not 跟着 fire,\n // 所以显式监听补完备性。spawnScript 内部另装了一对只管日志的监听器,跟这里互不影响。\n let childFailed = false;\n let exitCode: number | null = null;\n let exitSignal: NodeJS.Signals | null = null;\n let spawnError: Error | null = null;\n child.once(\"exit\", (code, signal) => {\n childFailed = true;\n exitCode = code;\n exitSignal = signal;\n });\n child.once(\"error\", (err) => {\n childFailed = true;\n spawnError = err;\n });\n\n for (let i = 0; i < ENSURE_SERVICE_MAX_RETRIES; i++) {\n const delay = Math.min(ENSURE_SERVICE_INITIAL_DELAY_MS * (i + 1), ENSURE_SERVICE_MAX_DELAY_MS);\n await sleep(delay);\n\n if (childFailed) {\n log.error(\n { code: exitCode, signal: exitSignal, err: spawnError && String(spawnError) },\n \"Serve daemon failed to start\",\n );\n const detail = spawnError\n ? `spawn error=${String(spawnError)}`\n : `code=${exitCode}, signal=${exitSignal}`;\n throw new Error(\n `Serve daemon failed to start (${detail}). Check ${SERVICE_LOG_PATH} for details.`,\n );\n }\n\n const socket = await tryConnect(SOCK_PATH);\n if (socket) {\n log.info({ attempt: i + 1 }, \"Connected to service after retry\");\n return socket;\n }\n }\n\n log.error({ maxRetries: ENSURE_SERVICE_MAX_RETRIES }, \"Failed to connect to service\");\n throw new Error(\n `Failed to connect to dev-anywhere service after ${ENSURE_SERVICE_MAX_RETRIES} retries. Check ${SERVICE_LOG_PATH} for details.`,\n );\n}\n\n// 等待指定类型的 IPC 消息一次。`createIpcReader` 注册临时 listener,匹配后立即清理。\n// 超时返回 reject;调用方需自己保证 socket 不会同时被另一个 listener 持有否则消息可能被吃掉。\nexport function waitForMessage<T extends IpcMessage[\"type\"]>(\n socket: Socket,\n messageType: T,\n): Promise<Extract<IpcMessage, { type: T }>> {\n return new Promise((resolve, reject) => {\n let timeout: NodeJS.Timeout | null = null;\n const dispose = createIpcReader(socket, (msg: IpcMessage) => {\n if (msg.type === messageType) {\n if (timeout) clearTimeout(timeout);\n dispose();\n resolve(msg as Extract<IpcMessage, { type: T }>);\n }\n });\n timeout = setTimeout(() => {\n dispose();\n reject(new Error(`Timeout waiting for ${messageType}`));\n }, WAIT_FOR_MESSAGE_TIMEOUT_MS);\n });\n}\n","// PTY 闲置检测:每 IDLE_CHECK_INTERVAL_MS 抽一次,发现自上次输出已超过 IDLE_THRESHOLD_MS\n// 且当前局部状态仍是 working 时,把状态翻到 turn_complete 并触发 emit。\n//\n// 抽出来主要好处:terminal.ts / hosted-pty-registry.ts 都跑同样的 working→turn_complete\n// 退化逻辑,未来若把 hosted 也接入这个 checker,就只剩一份实现。\n\ninterface IdleCheckerOptions {\n // 检测周期\n intervalMs: number;\n // 超过这个时长无新输出即触发 turn_complete\n thresholdMs: number;\n // 读取最近一次 PTY 输出时间戳;返回 0 表示已经走过 turn_complete 不要重复触发\n getLastOutputTime: () => number;\n // 用于\"重置最近输出时间到 0\",保证下次再触发前必须有真实新输出\n setLastOutputTime: (value: number) => void;\n // 读当前局部 PTY 状态\n getCurrentState: () => \"working\" | \"turn_complete\" | \"approval_wait\";\n // 仅在 currentState === \"working\" 时触发;onIdle 内部决定具体怎么落地(emit IPC / 改 state)\n onIdle: () => void;\n}\n\nexport interface IdleChecker {\n start(): void;\n stop(): void;\n}\n\nexport function createIdleChecker(options: IdleCheckerOptions): IdleChecker {\n let timer: NodeJS.Timeout | null = null;\n\n const tick = (): void => {\n const last = options.getLastOutputTime();\n if (last <= 0) return;\n if (Date.now() - last <= options.thresholdMs) return;\n options.setLastOutputTime(0);\n if (options.getCurrentState() !== \"working\") return;\n options.onIdle();\n };\n\n return {\n start(): void {\n if (timer) clearInterval(timer);\n timer = setInterval(tick, options.intervalMs);\n },\n stop(): void {\n if (timer) {\n clearInterval(timer);\n timer = null;\n }\n },\n };\n}\n","import type { Socket } from \"node:net\";\n\n// terminal 进程在 reconnectToServe 成功后用新 socket 替换旧的。旧 socket 已被 close 事件\n// 触发过,但 createIpcReader 内部 pipe 的 LineBuffer + 在原 socket 上注册的 close/error/data\n// listener 仍持有引用;不显式 removeAllListeners 会让它们随着每次 reconnect 单调累积,\n// 长跑(夜间 / 网络抖动频繁)下成为内存泄漏。\nexport function swapServeSocket(prev: Socket, next: Socket): Socket {\n prev.removeAllListeners();\n return next;\n}\n","import type { Socket } from \"node:net\";\nimport { createFSM } from \"@dev-anywhere/shared\";\nimport { serializeIpc } from \"../ipc/ipc-protocol.js\";\n\n// terminal 进程生命周期状态\nexport const TerminalState = {\n INIT: \"init\",\n CONNECTING_SERVICE: \"connecting_service\",\n CREATING_SESSION: \"creating_session\",\n RUNNING: \"running\",\n RECONNECTING: \"reconnecting\",\n EXITED: \"exited\",\n} as const;\nexport type TerminalState = (typeof TerminalState)[keyof typeof TerminalState];\n\n// 允许的状态转换。CREATING_SESSION/RUNNING 下可被 socket close 打断进入 RECONNECTING;\n// 任意非终态都可能被 PTY 退出或 SIGTERM 打断进入 EXITED。\nexport const TERMINAL_TRANSITIONS: Record<TerminalState, readonly TerminalState[]> = {\n init: [\"connecting_service\"],\n connecting_service: [\"creating_session\", \"exited\"],\n creating_session: [\"running\", \"reconnecting\", \"exited\"],\n running: [\"reconnecting\", \"exited\"],\n reconnecting: [\"creating_session\", \"running\", \"exited\"],\n exited: [],\n};\n\n// 下面几个依赖是 getter 而非值:因为它们在 terminal.ts 里是 let 变量,在 handler 创建之后还会变——\n// socket 在 reconnect 时被重新赋值为新实例,sessionId 在 session_create 成功后才有值,\n// idleChecker 在 setupIdleCheck 跑完才赋值。直接传值只会记录 handler 构造那一刻的旧值。\ninterface ExitHandlerDeps {\n fsm: ReturnType<typeof createFSM<TerminalState>>;\n getSocket: () => Socket;\n getSessionId: () => string | null;\n // 退出时要停掉的 idle checker;handler 创建时尚未实例化,故传 getter\n stopIdleChecker: () => void;\n // 释放 xterm headless 渲染资源(HeadlessTerminal、SerializeAddon、UnicodeGraphemesAddon\n // 等)。process 退出后 OS 会回收文件描述符等系统资源,但 wasm 模块状态、addon listener\n // 留到 process 末期会让多次 spawn-exit 循环(如测试场景)累积内存。\n disposeRenderResources?: () => void;\n // 测试注入点,production 默认 process.exit\n exit?: (code: number) => void;\n}\n\n// 构造统一的收尾函数:转 EXITED → 停 idle 定时器 → 给 serve 发 pty_deregister → 退进程。\n// onSessionExit 与 SIGTERM handler 共享同一实例;Ctrl+C 两连击或 PTY 退出与 SIGTERM 竞争时,\n// 第二次调用通过 fsm EXITED 检查直接短路。\nexport function createExitHandler(deps: ExitHandlerDeps): (code: number) => void {\n const exit = deps.exit ?? ((code: number) => process.exit(code));\n return (code: number) => {\n if (deps.fsm.is(TerminalState.EXITED)) return;\n deps.fsm.transitionTo(TerminalState.EXITED);\n deps.stopIdleChecker();\n deps.disposeRenderResources?.();\n const socket = deps.getSocket();\n const sessionId = deps.getSessionId();\n if (socket.writable && sessionId) {\n socket.end(serializeIpc({ type: \"pty_deregister\", sessionId }), () => exit(code));\n } else {\n exit(code);\n }\n };\n}\n"],"mappings":"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","names":["sleep","existsSync","sleep","existsSync"]}