5.2 KiB
codexClient.cjs 说明
文件作用
这个文件是后端与本机 Codex CLI / @openai/codex-sdk 的适配层。它负责检查登录状态、启动登录、退出登录、构造 Codex 请求线程、执行普通回复和流式回复,并把这些能力导出给 Express 路由使用。
主要常量和状态
PROJECT_ROOT:项目根目录。CODEX_WORKSPACE:专门给 Codex SDK 使用的工作目录.codex-chat-workspace。DEFAULT_MODEL:来自环境变量CODEX_MODEL的默认模型。DEFAULT_REASONING_EFFORT:来自环境变量CODEX_REASONING_EFFORT的默认推理强度。AUTH_STATUS_CACHE_MS:登录状态缓存时间。sdkPromise:缓存动态导入的 Codex SDK。authStatusCache:缓存最近一次登录状态。loginState:保存当前或最近一次登录命令的运行状态和日志。
文件加载时会创建 CODEX_WORKSPACE 目录。
主要函数
getCodexBin()
查找 Codex CLI 可执行文件。优先使用项目内 node_modules/.bin/codex,找不到时回退到全局 codex 命令。
getCodexHomeHint()
返回 Codex 本地配置目录提示。优先使用 CODEX_HOME 环境变量,否则使用用户目录下的 .codex。
appendLoginLine(chunk)
把登录命令输出追加到 loginState.lines。它会跳过空行,并且只保留最近 120 行,避免日志无限增长。
runCodexCommand(args, options)
执行 Codex CLI 命令的通用封装:
- 使用
spawn启动命令。 - 在
CODEX_WORKSPACE下运行。 - 收集 stdout 和 stderr。
- 支持传入 stdin。
- 支持超时,超时后终止子进程。
- 最终返回退出码、信号、输出和错误对象。
它主要用于登录状态检查和退出登录。
loadCodexSdk()
动态导入 @openai/codex-sdk,并用 sdkPromise 缓存导入结果,避免重复加载。
normalizeInput(input)
清洗前端传入的消息数组:
- 只保留对象类型且有字符串
role的项。 - 将
content转为字符串。 - 去掉空内容消息。
roleTitle(role)
把请求角色转换成写入提示词的标题:
developer或system显示为开发者指令。assistant显示为 assistant。- 其他角色显示为 user。
buildCodexPrompt(input)
把结构化消息数组转换成单个 Codex prompt:
- 先标准化输入。
- 将每条消息转换成
### 角色标题加正文的文本块。 - 在开头加入本地网页聊天工具的约束说明。
- 要求模型只回复最后一条 user 消息,不混入其他会话上下文。
normalizeModel(model)
合并请求体模型名和环境变量默认模型,并返回修剪后的字符串。
normalizeReasoningEffort(value)
校验推理强度,只允许 minimal、low、medium、high、xhigh。非法值会回退到 medium。
createThreadOptions(body)
创建 Codex SDK thread 运行选项:
- 使用只读沙箱。
- 禁用审批。
- 固定工作目录为
CODEX_WORKSPACE。 - 跳过 Git 仓库检查。
- 禁用网络和 Web 搜索。
- 透传推理强度和可选模型。
这些设置降低本地聊天代理对用户文件和网络的影响范围。
createCodexClient()
创建 Codex SDK 客户端,并关闭原始 agent reasoning 输出,让前端只展示面向用户的最终回答。
hasUsableAuth(status)
判断 Codex 是否可用:既要安装 CLI,也要处于已登录状态。
getAuthStatus(options)
检查 Codex 登录状态:
- 默认使用短期缓存,
force为真时强制刷新。 - 执行
codex login status。 - 根据退出码判断是否已登录。
- 返回安装状态、登录状态、认证模式输出、提示消息、Codex home 和检查时间。
ensureLoggedIn()
在执行聊天请求前校验 Codex 可用性。CLI 未安装时抛出 503 错误;未登录时抛出 401 错误。
startLogin(options)
启动 Codex 登录流程:
- 如果已有登录进程在跑,直接返回当前登录状态。
- 默认启动
codex login。 deviceAuth为真时追加--device-auth。- 保存进程 pid、开始时间、结束时间、退出码、信号和输出日志。
- 登录进程结束后清空认证状态缓存。
getLoginState()
返回当前登录状态快照,供前端轮询展示。
logoutCodex()
执行 codex logout,清空认证状态缓存,并返回命令结果。
runCodexOnce(body, signal)
执行一次性聊天请求:
- 先确认 Codex 已登录。
- 用
buildCodexPrompt生成 prompt。 - prompt 为空时抛出 400 错误。
- 创建 Codex client 和 thread。
- 调用
thread.run(prompt, { signal })。 - 返回最终文本、thread id 和 usage。
runCodexStream(body, signal, handlers)
执行流式聊天请求:
- 校验登录和 prompt。
- 调用
thread.runStreamed(prompt, { signal })。 - 遍历 SDK 返回的事件流。
- 对 agent message 计算增量文本,并调用
handlers.onDelta(delta)。 - 记录
turn.completed的 usage。 - 遇到 SDK 错误事件时抛出后端错误。
- 返回完整文本、thread id 和 usage。
导出接口
该文件导出:
getAuthStatusgetLoginStatelogoutCodexrunCodexOncerunCodexStreamstartLogin
这些函数由 server/index.cjs 的 API 路由调用。