Files
codex-app-server/cloud/console-react
host 962bb50a1c feat: browser stream waits through round gaps instead of disconnecting
Stale bridge state used to end the stream on the first stale read,
so every round gap and every worker restart killed the watch (the
2026-10-30 demo watch died this way when a 15:03 deploy restarted
the worker). Now a stale state switches to a waiting heartbeat
every 5s while the task is still alive (running/queued/paused/
interrupted), and frames resume when the next round's MCP starts
polling. The stream ends only on a terminal task state, a missing
task row, or 10 minutes of continuous staleness. The panel shows
a waiting status and keeps the last frame. Also force-close the
SSE connection on stream end: HTTP/1.1 keep-alive otherwise left
terminal streams hanging with no EOF.
2026-10-30 15:14:37 +08:00
..

Codex 命令台 · React 版

生产级 React 重写,替代 cloud/console.html 的旧单页脚本。视觉以 Muse 网页端/桌面端截图为准(2026-10-29 改版,取代此前的 v3 瓷墨印): 白底、细线分隔、无大标题栏;左侧图标栏 + 列表、中间对话、右侧 个人资料/今天/详情。颜色:文字 #1d2129、灰 #667085、线 #e4e7ec、助手气泡 #f2f2f2、用户气泡 #bfe0ff、蓝 #3b82f6、 已连接绿 #22c55e。

怎么改(只改 src,不改产物)

  • 改一个模块:只动 src/sections/<模块>.tsx,需要新数据时先在 src/api/types.ts 加类型、再在对应 src/hooks/use*.ts 里加 loader, 不要在组件里拼 URL 或各写一份请求。
  • 改通用样式/组件:src/tokens.css(唯一 token 源)或 src/components/ 下的共享组件(Card、DataList、StatusPill、 ChatList/ChatBubble/ChatInput、IconRail)。
  • 改 API:只动 src/api/paths.ts(全部路径集中在这里)和 src/api/client.ts(api()、错误文案、key 存取)。组件里不许出现 手拼的 /tasks/... 字符串。
  • 构建产物(dist/、console-react.html)不许手改。以后改页面 只改 src 再构建,产物永远由构建命令生成。

构建(一键,自动)

npm install
npm run build:console

npm run build:console = tsc -b && vite build(vite-plugin-singlefile 把 JS/CSS 全部内联成一个 dist/index.html),再由 scripts/copy-build.mjs 原样拷到 ../console-redesign/console-react.html(审阅用),并打印字节数与 sha256。脚本不改动产物内容。

自动部署链路(不留手工步骤)

改 src → npm run build:console → 产物拷到 cloud/console.html → 提交 → 部署

具体到仓库里的一步(由主 agent 在审阅后执行,本项目不部署生产):

cp console-redesign/console-react.html <仓库>/cloud/console.html
# 或在仓库分支里直接用构建产物覆盖 cloud/console.html
git add cloud/console.html && git commit -m "console: React 版"
# 随后走仓库已有的 PR → CI → 合并 → deploy.py git 模式部署

服务端无需改动:cloud/server.py 的 GET /console 本来就是每次请求 现读 cloud/console.html 这一个文件,无静态资源路由、无需重启。 产物是单文件、无外部 /assets/*、无外部字体/图片引用(唯一的 url(...) 是内联 SVG 下拉箭头),交给主 agent 就能直接部署到现有 /console 页面。

怎么验证

npm run build:console   # 必须 0 退出,tsc 无错误
ls -l ../console-redesign/console-react.html
sha256sum ../console-redesign/console-react.html
grep -E 'src="/assets|href="/assets' ../console-redesign/console-react.html  # 应无输出

部署后另行核对(主 agent 执行):公网 /console 的字节数/sha 与 构建产物一致、/health 正常、4 个服务 active。不要用真实 API key 做验证调用;本地构建验证不需要 key。

布局(Muse 式四栏联动)

  • 最左图标栏 IconRail(56px):每模块一个内联 SVG 图标,点 图标切换模块;手机上隐藏。
  • 左侧列表 TaskListSection(260px):顶部圆角搜索框(即原有 任务搜索),主要聊天 标题 + +(切到任务视图),状态/执行器 筛选紧凑行在下,任务列表行是 prompt 单行截断 + 状态点/id/状态/ 执行器,选中行灰底高亮。
  • 中间 ConversationSection(弹性宽,内容居中收敛 760px):顶部 只有返回箭头(回任务视图)和右侧栏开关,没有大标题栏;任务视图 时是对话主角——实时链路时间线在上、继续会话聊天框在下,气泡为 助手灰左、用户浅蓝右;非任务模块视图时是该模块的标准内容。
  • 右侧 RightPanel(300px):顶部 X 关闭本栏,圆形头像(字母 C, 无外部图片)+ Codex 命令台 + 真实连接状态(有 key 为绿字 已连接,否则未连接)+ 健康/配额/刷新时间;图标快捷行;今天 列表(最近 10 条任务,勾选圈 + 粗标题 + 灰副标题 + 右侧时间, 点一条切到该任务);下方可折叠的详情区:任务视图时是 TaskDetailPanel,模块视图时是该模块的上下文(项目→成员、 环境→仓库绑定、技能→MCP 操作、Workers/管理区/API Key→健康与 配额、自动化↔分诊互为上下文)。
  • 联动:左点任务 → showDetail(id) → 中间对话与右侧详情同时切到 该任务;左点模块 → 中间与右侧同时切到该模块。平板(≤1240px) 右侧整行落到下方;手机(≤900px)单列,依次是图标栏(隐藏)、 列表、对话、详情,聊天输入条 position: sticky; bottom: 0,在 手机上也固定在底部可用。

对话与详情的分工(什么进对话、什么进详情)

  • 进对话(src/components/chat/ 共享组件):任务 prompt(用户气泡)、 trace 的 message 条目(助手气泡)、exec/file/tool/call(工具气泡)、 plan/reasoning/agent-start/agent-done(居中状态提示)、继续/后续 消息(用户气泡)。流式追加、底部自动跟随、往上翻暂停跟随。
  • 进详情(右侧,不塞进气泡):状态/模型/强度/执行器/项目/turn/ 主机/PR/更新、结果/错误文本、fork/重试/取消/合并/提 PR/查成本、 评审、审批、协调子任务、子代理、线程操作、自动化、分诊。
  • 聊天发送只用已有接口,不造新接口:终态任务 → POST /tasks/:id/continue(只发 message,其余选项留空即沿用任务原值, 与 live 继续表单全留空等价);运行中/排队中 → POST /tasks/:id/followups(mode: "direct");merged/merge-failed 不可发送。trace 映射只按 live 的 kind 分角色,trace 条目本身没有 时间戳与发送方字段,所以不编造时间顺序:followup 气泡排在 trace 助手消息之后,而不是假装交错排序。

目录与职责

src/
  api/        client.ts(api()、key、错误文案、工具函数)
              paths.ts(全部 API 路径,唯一来源)
              types.ts(全部实体与表单类型)
  hooks/      useCatalog.ts(key/配额/健康/项目/worker/环境/成员/技能/模型/仓库/管理区)
              useTaskList.ts(任务列表、筛选、派发)
              useTaskDetail.ts(任务详情、trace SSE、聊天发送、全部详情子列表)
              useProjectTools.ts(自动化、分诊)
              useConsoleStore.ts(胶水:把上面合成 ConsoleCtx + 自动刷新策略)
              useAutoRefresh.ts(10s/60s 定时器、select 焦点保护)
  components/ Card.tsx、lists.tsx(DataList/StatusPill)、notes.tsx、
              controls.tsx、IconRail.tsx(含 ModuleIcon)、chat/(Chat.tsx、mapTrace.ts)
  sections/   一个模块一个文件:ApiKey、Admin、Projects、MembersPanel、
              Env、EnvContextPanel、Workers、Skills、SkillsToolsPanel、
              Automations、Triage、TaskList(左栏列表)、
              Conversation(中间对话组合)、TaskDetailPanel(右侧详情)、
              RightPanel(右侧个人资料/今天/详情)、
              Dispatch(派发,挂在对话上方作为新会话入口)
              detail/(TracePanel、ContinuePanel、DetailActions、
              ReviewPanel、DetailLists、ProjectToolsPanels)
  context.ts  ConsoleCtx 类型与 React context(不含业务逻辑)
  App.tsx     只做布局与组合:四栏 + Provider + 定时器接入
  tokens.css  唯一 token 源:颜色、圆角、间距、40px 控件、断点

已知边界(诚实版)

  • 左栏任务列表是紧凑行(id/状态/prompt/执行器),live 的 9 列 表格(turn、root turn、主机、PR、更新)不在左栏,改在右侧详情 里展示——这是 300px 左栏放不下表格的取舍,不是功能删除。
  • 继续聊天只发 message:live 继续表单里的模型/强度/权限/子任务 覆盖项在聊天框里没有入口,发送即沿用任务原值;需要覆盖时仍 是后端原有语义,未改动。
  • 本地构建验证未接真实后端(无 key 调用),上线前主 agent 需 独立用真实页面验一遍。