必须用 WebSocket 建立本地 Codex CLI 与浏览器的双向流式通道:先运行 codex run --json --server 启动服务,再在前端 new WebSocket("ws://127.0.0.1:8080") 连接,按 type 分发 item.delta/item.completed 等消息,配合防 XSS 渲染、重连机制与 pendingItems 状态追踪确保实时性与可靠性。
☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 多模态理解力帮你轻松跨越从0到1的创作门槛☜☜☜

要在网页中实时接收 Codex 执行结果或状态更新,必须绕过传统 HTTP 轮询,建立从本地 Codex CLI 到浏览器的低延迟、双向、流式通道;否则页面永远只能手动刷新才能看到新输出,失去“实时性”本质。
用 WebSocket 桥接本地 Codex 与浏览器
这一步是核心通路,不能跳过任何环节。WebSocket 是唯一能承载 Codex 流式 JSON 输出(如 item.started→item.delta→item.completed)的协议,HTTP 请求无法持续接收增量响应。
第一步:确保 Codex CLI 已启动并处于监听模式——运行 codex run --json --server,终端应显示 “Server listening on ws://127.0.0.1:8080”。注意:不加 --server 参数则无 WebSocket 服务,后续所有前端连接都会失败。
第二步:在网页中新建 WebSocket 实例,地址必须与上一步完全一致:const ws = new WebSocket("ws://127.0.0.1:8080");。若端口被占用或地址写成 http://,浏览器控制台将报错 "Error during WebSocket handshake",且不会重连。
第三步:监听 message 事件,对收到的原始 JSON 字符串做类型分发。Codex 的每条消息都是标准 JSON 对象,但 type 字段决定语义:item.delta 表示流式文本片段,item.completed 表示一个完整单元结束,command_execution 表示正在执行命令。必须按 type 区分处理,否则页面会把命令日志和最终答案混在一起显示。
前端页面渲染实时内容
方法一:原生 DOM 追加(适合调试与轻量展示)
为防止 XSS,不要直接 innerHTML 插入未过滤的 text 字段。先创建 <div class="log-line"></div> 元素,再用 textContent = data.item.text 写入,最后 append 到容器。这样既保留换行与空格,又杜绝脚本注入。
通过本地 Codex 或 OpenClaw OAuth 凭证直接调用 ChatGPT/Codex Responses 的 image_generation 工具来生成或编辑光栅图像,然后保存
方法二:Vue 3 响应式渲染(适合集成进管理后台)
定义 ref 数组 logs: ref([]),在 WebSocket 的 onmessage 回调中执行 logs.value.push({ type: data.type, text: data.item?.text || '' })。模板中用 v-for 渲染,配合 :class="`log-${item.type}`" 动态着色。注意:必须用 push 而非 replace 整个数组,否则 Vue 无法触发视图更新。
方法三:React + useEffect Hook(适合已有 React 项目)
用 useState 初始化 logEntries: [],再用 useEffect 设置 WebSocket 连接。关键点在于 cleanup 函数必须调用 ws.close(),否则每次组件重渲染都会新建连接,导致浏览器快速耗尽 socket 句柄。这是实际开发中最常被忽略的内存泄漏源头。
处理 Codex 流式输出中的中断与重连
第一步:监听 WebSocket 的 onclose 事件,在回调中启动指数退避重连——首次 1 秒后重试,失败则 2 秒、4 秒、8 秒……上限设为 30 秒。硬编码固定间隔会导致本地 Codex 重启时大量无效连接涌向已关闭端口。
第二步:在每次成功 reconnect 后,向 Codex 发送一条心跳指令:ws.send(JSON.stringify({ type: "ping", timestamp: Date.now() }))。Codex 收到后会返回 pong 响应,确认通道活性。没有这步验证,页面可能误以为连接正常,实则消息早已堆积在内核缓冲区无法送达。
第三步:前端需维护一个 pendingItems Map,key 为 item.id,value 为 { startedAt, lastDelta, completedAt }。当收到 item.completed 却找不到对应 started 记录时,说明中间有消息丢失,应标记该条目为“不完整”,并在 UI 中以灰色斜体显示,避免误导用户。

















