tellrun 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,171 @@
1
- # Temporary Holding Version
1
+ # Tellrun
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 通过 IM 与服务器上的编码 Agent 对话,提交开发任务、查看进度、处理审批并继续会话。
4
+
5
+ Tellrun 取自 Tell + Run,意为说出需求、交给 Agent 执行,npm 包名与 CLI 命令均为 `tellrun`。
6
+
7
+ ## 首版目标
8
+
9
+ - 个人使用,仅授权本人操作。
10
+ - 通过 `npm install -g tellrun` 安装,命令行交互配置后在服务器后台运行。
11
+ - Telegram 私聊接入,采用长轮询。
12
+ - Codex 通过 `app-server` 接入。
13
+ - 支持多会话、任务排队、审批、停止和异常恢复。
14
+ - 暂不提供项目选择,Tellrun 不设置项目目录白名单。
15
+ - 后续逐步接入其他 IM、Claude 等 Agent。
16
+
17
+ ## 文档
18
+
19
+ - [产品需求文档](docs/prd.md):范围、交互流程、命令、功能和验收要求。
20
+ - [架构与技术选型](docs/architecture.md):架构图、框架与库、模块、数据模型、协议、恢复和部署方案。
21
+
22
+ 当前已接通 Telegram → Codex app-server → 结果回复,支持指定工作目录、单会话续聊、新建会话、停止、任务去重和重启恢复。配置与运行状态使用 JSON 文件保存;多会话选择、任务排队、进度推送和审批界面尚未接入。
23
+
24
+ ## 安装与使用
25
+
26
+ 先安装 Node.js 24.5.0 或更高版本、Codex,并完成 Codex 登录。CLI、配置向导和 Linux systemd 用户服务管理已接入;本轮未执行 npm 发布,发布后可按以下流程使用。
27
+
28
+ ```sh
29
+ npm install -g tellrun
30
+ tellrun setup # 交互配置 Telegram 和 Codex,完成后可直接启动后台服务
31
+ tellrun start # 后续启动
32
+ tellrun status # 查看后台服务状态
33
+ tellrun logs # 查看日志
34
+ tellrun logs -f # 持续跟随日志
35
+ tellrun restart # 重启服务
36
+ tellrun stop # 停止后台服务
37
+ ```
38
+
39
+ CLI 采用 Commander,配置向导采用 Clack。Bot Token 使用明文输入,已有配置时留空保留原值。`setup` 校验配置与 Codex 可执行文件后原子保存,取消时保留原配置;可选择立即启动或重启后台服务,并启用开机自启。后续也可使用 `tellrun start --enable` 启用自启。配置、运行状态、单实例锁和服务定义都保存在当前用户的 `~/.tellrun/`,全局 npm 包升级保留这些数据。
40
+
41
+ 后台运行需要 Linux systemd 用户服务。启动时检查并启用用户 linger,确保退出 SSH 后继续运行;权限不足会给出需要在服务器执行的命令。`tellrun run` 提供前台运行,适用于本地调试或外部进程管理器。`tellrun service uninstall` 停止并移除服务,保留配置、状态文件和用户 linger 设置。
42
+
43
+ 使用 nvm 安装 Node.js 时,Tellrun 会把当前 Node 所在目录补到 Codex 子进程的 `PATH` 前面,保证后台启动也能找到 `node`。
44
+
45
+ 尚未发布时,可在源码目录执行 `npm ci`、`npm pack`,再通过 `npm install -g ./tellrun-1.0.1.tgz` 安装本地包。打包自动构建并包含运行依赖声明。升级前停止服务并备份数据,升级后运行 `tellrun restart` 更新 Node 与 CLI 入口路径。具体布局和生命周期见[架构文档](docs/architecture.md#分发与命令行入口)。
46
+
47
+ ## 技术方案
48
+
49
+ Node.js 24 + TypeScript(ESM);grammY 接入 Telegram;Node.js 文件接口保存 JSON 配置和状态;Zod 校验配置与状态文件,Pino 输出日志,Biome 负责格式与静态检查。按当前要求,不添加测试文件、测试命令或测试依赖。
50
+
51
+ 使用 Node.js 自带的 npm 管理依赖,提交 `package-lock.json`,通过 `npm ci` 安装。需要 Node.js 24.5.0 或更高版本,以使用[内置代理支持](https://nodejs.org/docs/latest-v24.x/api/http.html#built-in-proxy-support);`.nvmrc` 固定开发环境为 Node.js 24.22.1,使用现有 nvm 切换运行时。
52
+
53
+ ## 目录与扩展
54
+
55
+ ```text
56
+ src/
57
+ ├── cli.ts # 全局命令与交互配置向导
58
+ ├── systemd.ts # systemd 用户服务的启停、状态和日志
59
+ ├── main.ts # 组件组装、启动和退出
60
+ ├── config.ts # 用户配置、环境变量覆盖与校验
61
+ ├── logger.ts # 结构化日志与敏感字段隐藏
62
+ ├── agents/
63
+ │ └── codex.ts # Codex app-server 连接、续聊、执行和中断
64
+ ├── im/
65
+ │ └── telegram.ts # Telegram 鉴权、消息转换和发送
66
+ ├── core/
67
+ │ ├── relay.ts # 命令处理与单任务执行
68
+ │ └── types.ts # 跨模块共用的消息与 Agent 接口
69
+ └── storage/
70
+ ├── store.ts # 状态读写、恢复和单实例锁
71
+ ├── schema.ts # JSON 状态结构校验
72
+ └── json.ts # 配置与状态共用的原子写入
73
+ ```
74
+
75
+ 新增 IM 时,在 `im/` 添加连接器,把授权后的消息转换为 `IncomingMessage` 并调用核心入口。新增 Agent 时,在 `agents/` 实现 `Agent` 的执行和停止接口;审批接口随实际交互能力扩展。核心层不使用 grammY 或 Codex 协议类型,不引入插件注册器、基类或依赖注入框架。
76
+
77
+ `config.json` 保存 Telegram 凭据、本人 ID、Codex 路径和可选的 `telegram.proxyUrl`。
78
+ `state.json` 保存聊天与当前会话、工作目录、任务输入与最终结果、入站去重标识和待发送回复;发送成功或被永久拒绝后移除回复副本。Codex 自己管理完整会话历史,Tellrun 不保存流式文本、思考过程、工具原始输出或 RPC 等待状态。
79
+
80
+ JSON 更新先写同目录临时文件,刷盘后原子替换。`state.json.lock/` 是临时单实例锁目录;正常退出清理,崩溃后检查原进程已退出再回收。文件损坏或写入失败会停止服务,不自动覆盖成空状态。
81
+
82
+ ## 当前源码开发启动
83
+
84
+ 先安装 nvm,并确保 `codex --version` 可以运行。
85
+
86
+ ```sh
87
+ nvm install
88
+ nvm use
89
+ npm ci
90
+ cp .env.example .env
91
+ ```
92
+
93
+ 编辑 `.env`,填写 `TELEGRAM_BOT_TOKEN` 和本人 `TELEGRAM_OWNER_ID`;用户 ID 必须是十进制正整数。`.env` 已加入 Git 忽略规则。
94
+
95
+ 当前只有以上两项必填配置,也可运行 `node dist/cli.js setup` 写入用户配置。配置与状态分别放在 `~/.tellrun/config.json`、`~/.tellrun/state.json`,开发运行与全局安装使用同一目录和单实例锁。Codex 优先使用 `CODEX_PATH`,其次使用配置文件路径,均未设置时通过 PATH 查找。日志级别固定为 `info`。无需项目清单;会话默认使用服务启动目录,可在 Telegram 中用 `/cwd` 切换,不设置目录白名单。
96
+
97
+ `telegram.proxyUrl` 为可选配置,由用户按需填写。Telegram 代理优先使用非空 `TELEGRAM_PROXY_URL`,其次使用配置文件中的 `telegram.proxyUrl`;配置文件中空字符串表示明确直连。两处均未指定时,读取运行环境的 `HTTPS_PROXY` / `https_proxy`;使用代理时遵循 `NO_PROXY` / `no_proxy`(小写优先)。代理 URL 不写入服务定义或命令行参数。
98
+
99
+ ```sh
100
+ npm run dev
101
+ ```
102
+
103
+ 启动时会取得单实例锁、加载并校验状态文件、检查 Codex CLI,再核实 Telegram 身份并启动轮询。仅响应配置的本人私聊;发给其他机器人的命令会被忽略,不支持的媒体会收到提示。若已有 Webhook 或出现轮询冲突,服务退出并给出诊断,不自动接管其他部署。
104
+
105
+ 在 Telegram 中直接发送消息即可:
106
+
107
+ ```text
108
+ hi
109
+ ```
110
+
111
+ 未指定工作目录的会话会记住服务启动目录;systemd 服务默认使用当前用户的主目录。需要切换时发送 `/cwd /你的项目绝对路径`,目录变化会开始新会话。后续文本沿用当前 Codex 会话。`/new` 新建会话并保留目录,`/status` 查看状态,`/last` 查看最近结果;`/stop` 停止当前任务并暂停接收,确认结果后用 `/resume` 恢复。当前全局只接受一个未结束任务,忙碌时新消息会明确提示未提交。
112
+
113
+ Codex 沿用本机登录、模型配置和终端代理;默认使用工作区写入与按需审批。尚未支持的审批或结构化提问会中断任务并说明原因。CLI 版本检查不代表模型可用,实际调用失败会保存状态并回复。
114
+
115
+ 入站标识、任务和回执作为同一份快照保存,重复 Update 不会再次执行。重启会保留尚未派发的任务;执行中断且结果不确定的任务不会自动重跑,需检查后 `/resume`。关停等待 Agent 结束后释放状态锁。
116
+
117
+ 普通 Telegram 请求连同重试最多等待 15 秒,长轮询最多 40 秒。可恢复的发送失败会保留 outbox 并退避重试,429 遵守服务端等待时间;永久拒绝会记录日志并将回复移出队列,任务结果仍可用 `/last` 查看。发送成功但响应丢失时可能重复通知,但不会重跑任务。轮询或存储故障会停止服务,待人工或进程管理器重启。
118
+
119
+ 开发服务本身时使用 `npm run dev`。让 Agent 修改代码时可用 `npm run build && npm start`,避免源码监听重启打断正在执行的任务。
120
+
121
+ ## 本地开发
122
+
123
+ 在项目目录执行:
124
+
125
+ ```sh
126
+ npm ci # 首次安装依赖
127
+ npm run build # 生成 CLI 入口并设置执行权限
128
+ npm link
129
+ tellrun --help
130
+ tellrun setup
131
+ tellrun run
132
+ ```
133
+
134
+ Linux 下本地调试时,在 `setup` 中选择仅保存配置,再通过 `tellrun run` 前台运行。
135
+
136
+ 修改 TypeScript 后,重新执行 `npm run build`,再运行命令即可,不用重复 `npm link`。完成首次构建后,也可以单独开一个终端自动编译:
137
+
138
+ ```sh
139
+ npx tsc -p tsconfig.build.json --watch
140
+ ```
141
+
142
+ 自动编译不会重启正在运行的进程,修改后需要手动重启 `tellrun run`。如果只调试业务逻辑,可以直接使用现有的 `npm run dev`,它会监听源码并自动重启。
143
+
144
+ 发布前,使用实际安装包验证文件是否齐全:
145
+
146
+ ```sh
147
+ npm pack # 已配置自动构建
148
+ npm install -g ./tellrun-1.0.1.tgz
149
+ tellrun --help
150
+ ```
151
+
152
+ 压缩包文件名随版本号变化,以 `npm pack` 输出为准。安装压缩包后,全局命令会改用安装包中的代码;继续通过源码调试时,在项目目录重新执行 `npm link`。
153
+
154
+ ## 开发命令
155
+
156
+ | 命令 | 用途 |
157
+ | ---------------- | ------------------------ |
158
+ | `npm run dev` | 监听源码并重启 |
159
+ | `npm run check` | 格式、静态检查与类型检查 |
160
+ | `npm run format` | 格式化并应用安全修复 |
161
+ | `npm run build` | 编译到 `dist/` |
162
+ | `npm start` | 运行编译产物 |
163
+
164
+
165
+ 上述源码开发命令从项目根目录运行,用户配置和运行状态始终保存在用户目录。
166
+
167
+ 已在本机 Node.js 24.22.1、Codex CLI 0.165.0 验证真实回复、跨进程续聊与中断。文件存储验证覆盖去重、单实例锁、重启恢复、损坏文件与写入失败;打包检查确认包含所需编译产物。后台命令仅在隔离命令环境中检查了调用顺序和特殊路径;目标 Linux 的真实 systemd 启停、退出 SSH、开机自启及完整 M0 验证仍待完成。[Codex app-server 官方文档](https://learn.chatgpt.com/docs/app-server)
168
+
169
+ ## 文档位置
170
+
171
+ 设计文档保存在项目的 `docs/` 目录。原始 [产品 Pages 草案](https://chatgpt.com/space/page_8cd777a7402c8191aa928ab888cb8df8) 和 [架构 Pages 草案](https://chatgpt.com/space/page_c0f7aa829c508191b8712e93ee49b06c) 保留,后续本地修改不会自动同步到 Pages。
@@ -0,0 +1,294 @@
1
+ import { execFile, spawn } from "node:child_process";
2
+ import { delimiter, dirname } from "node:path";
3
+ import { promisify } from "node:util";
4
+ import { z } from "zod";
5
+ /** 由 Codex 0.165.0 的 generate-ts 核对;只校验实际消费的协议字段。 */
6
+ const turnSchema = z.object({
7
+ id: z.string().min(1),
8
+ status: z.enum(["inProgress", "completed", "failed", "interrupted"]),
9
+ });
10
+ /** 每次任务使用独立 app-server 连接,续聊使用已持久化的 thread ID。 */
11
+ export async function createCodexAgent(command = "codex") {
12
+ /** 保留运行时、认证和代理环境,不向子进程传入 Telegram 凭据。 */
13
+ const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => /^(PATH|HOME|USER|LOGNAME|SHELL|LANG|LC_.*|TERM|TMPDIR|TEMP|TMP|SYSTEMROOT|WINDIR|CODEX_HOME|OPENAI_API_KEY|OPENAI_BASE_URL|HTTP_PROXY|HTTPS_PROXY|ALL_PROXY|NO_PROXY|http_proxy|https_proxy|all_proxy|no_proxy|SSL_CERT_FILE|SSL_CERT_DIR)$/.test(key)));
14
+ /** npm 版 Codex 通过 env 查找 node;后台服务也应复用运行 Tellrun 的 Node。 */
15
+ env.PATH = [dirname(process.execPath), env.PATH].filter(Boolean).join(delimiter);
16
+ const { stdout } = await promisify(execFile)(command, ["--version"], {
17
+ timeout: 5_000,
18
+ maxBuffer: 4_096,
19
+ env,
20
+ });
21
+ if (!/^codex-cli \S+$/.test(stdout.trim()))
22
+ throw new Error("Codex CLI 返回了无法识别的版本信息");
23
+ let active;
24
+ return {
25
+ name: "Codex",
26
+ version: stdout.trim().replace(/^codex-cli /, ""),
27
+ async run(input) {
28
+ if (active)
29
+ throw new Error("Codex 已有任务正在执行");
30
+ const controller = new AbortController();
31
+ active = controller;
32
+ const child = spawn(command, ["app-server"], {
33
+ cwd: input.cwd,
34
+ env,
35
+ detached: process.platform !== "win32",
36
+ stdio: ["pipe", "pipe", "ignore"],
37
+ });
38
+ const closed = Promise.withResolvers();
39
+ const completed = Promise.withResolvers();
40
+ const pending = new Map();
41
+ const messages = new Map();
42
+ let serial = 0;
43
+ let buffer = "";
44
+ let sessionId;
45
+ let runId;
46
+ let dispatched = false;
47
+ let settled = false;
48
+ let stopTimer;
49
+ /** 只接受第一个终态,避免退出事件覆盖已经确认的执行结果。 */
50
+ function finish(result) {
51
+ if (settled)
52
+ return;
53
+ settled = true;
54
+ completed.resolve(result);
55
+ }
56
+ /** POSIX 下同时结束受管理进程组,避免切换任务后遗留旧命令。 */
57
+ function terminate(signal) {
58
+ if (!child.pid)
59
+ return;
60
+ try {
61
+ if (process.platform === "win32")
62
+ child.kill(signal);
63
+ else
64
+ process.kill(-child.pid, signal);
65
+ }
66
+ catch (error) {
67
+ if (!(error instanceof Error && "code" in error && error.code === "ESRCH")) {
68
+ finish({
69
+ status: "interrupted",
70
+ text: "无法确认 Codex 子进程已停止,请检查本地进程。",
71
+ });
72
+ }
73
+ }
74
+ }
75
+ /** 连接结束时释放所有 RPC 等待,取消初始化也不必等待请求超时。 */
76
+ function rejectPending(text) {
77
+ for (const item of pending.values()) {
78
+ clearTimeout(item.timer);
79
+ item.reject(new Error(text));
80
+ }
81
+ pending.clear();
82
+ }
83
+ /** 通信故障不等于执行失败;保留不确定状态并结束当前连接。 */
84
+ function fail(text) {
85
+ if (settled)
86
+ return;
87
+ finish({ status: "interrupted", text });
88
+ rejectPending(text);
89
+ controller.abort();
90
+ }
91
+ /** 请求按 ID 关联且有超时;一次仅有一个业务请求及一个中断请求在途。 */
92
+ function request(method, params) {
93
+ if (controller.signal.aborted && method !== "turn/interrupt")
94
+ return Promise.reject(new Error("任务已停止"));
95
+ return new Promise((resolve, reject) => {
96
+ const id = ++serial;
97
+ pending.set(id, {
98
+ resolve,
99
+ reject,
100
+ method,
101
+ timer: setTimeout(() => fail(`Codex ${method} 请求超时,未自动重试。`), 30_000),
102
+ });
103
+ child.stdin.write(`${JSON.stringify({ id, method, params })}\n`, (error) => {
104
+ if (error)
105
+ fail("无法写入 Codex 连接,执行状态待确认。");
106
+ });
107
+ });
108
+ }
109
+ /** 通知可能先于 turn/start 响应;只记录当前任务的首个 run ID。 */
110
+ function recordRun(id) {
111
+ if (runId && runId !== id)
112
+ return false;
113
+ if (!runId) {
114
+ runId = id;
115
+ input.onStarted();
116
+ }
117
+ return true;
118
+ }
119
+ controller.signal.addEventListener("abort", () => {
120
+ if (settled)
121
+ return;
122
+ stopTimer = setTimeout(() => fail("停止请求未得到确认,已结束 Codex 连接。请检查工作目录后发送 /resume。"), 5_000);
123
+ if (sessionId && runId) {
124
+ void request("turn/interrupt", { threadId: sessionId, turnId: runId }).catch(() => terminate("SIGTERM"));
125
+ }
126
+ else
127
+ terminate("SIGTERM");
128
+ }, { once: true });
129
+ child.on("error", () => fail("无法启动 Codex app-server,请检查 CLI 和工作目录。"));
130
+ child.stdin.on("error", () => fail("Codex 输入连接已关闭,执行状态待确认。"));
131
+ child.on("close", (code) => {
132
+ closed.resolve();
133
+ if (controller.signal.aborted && !dispatched)
134
+ finish({ status: "cancelled", text: "任务在开始前已停止。" });
135
+ if (!settled)
136
+ fail(`Codex 连接已退出(${code ?? "signal"}),请检查本机认证与运行环境。`);
137
+ else
138
+ rejectPending("Codex 连接已关闭");
139
+ });
140
+ child.stdout.setEncoding("utf8");
141
+ child.stdout.on("data", (chunk) => {
142
+ if (settled)
143
+ return;
144
+ buffer += chunk;
145
+ if (buffer.length > 8_000_000)
146
+ return fail("Codex 协议缓冲超出限制,任务已中断。");
147
+ const lines = buffer.split("\n");
148
+ buffer = lines.pop() ?? "";
149
+ for (const line of lines) {
150
+ if (!line.trim() || settled)
151
+ continue;
152
+ try {
153
+ const message = z
154
+ .object({
155
+ id: z.union([z.number(), z.string()]).optional(),
156
+ method: z.string().optional(),
157
+ params: z.unknown().optional(),
158
+ result: z.unknown().optional(),
159
+ error: z.object({ code: z.number() }).optional(),
160
+ })
161
+ .parse(JSON.parse(line));
162
+ if (message.id !== undefined && message.method) {
163
+ /** 暂未接入审批与结构化提问界面,明确拒绝并停止,不能静默批准。 */
164
+ child.stdin.write(`${JSON.stringify({ id: message.id, error: { code: -32601, message: "This client does not support interactive requests yet" } })}\n`);
165
+ fail("任务需要审批或补充输入,当前版本尚未接入该交互,已中断。请在本机处理后发送 /resume,或调整任务后重新提交。");
166
+ }
167
+ else if (typeof message.id === "number") {
168
+ const item = pending.get(message.id);
169
+ if (!item)
170
+ continue;
171
+ clearTimeout(item.timer);
172
+ pending.delete(message.id);
173
+ if (message.error)
174
+ item.reject(new Error(`Codex ${item.method} 请求被拒绝(${message.error.code})。请检查本机 Codex 配置与认证。`));
175
+ else
176
+ item.resolve(message.result);
177
+ }
178
+ else if (message.method === "item/completed") {
179
+ const event = z
180
+ .object({
181
+ threadId: z.string(),
182
+ turnId: z.string(),
183
+ item: z.object({
184
+ type: z.string(),
185
+ id: z.string(),
186
+ text: z.string().optional(),
187
+ phase: z.string().nullish(),
188
+ }),
189
+ })
190
+ .parse(message.params);
191
+ if (event.threadId !== sessionId || !recordRun(event.turnId))
192
+ continue;
193
+ if (event.item.type === "agentMessage" && event.item.text !== undefined) {
194
+ messages.set(event.item.id, { text: event.item.text, phase: event.item.phase });
195
+ if ([...messages.values()].reduce((sum, item) => sum + item.text.length, 0) > 256_000) {
196
+ fail("Codex 文本输出超出限制,任务已中断;可在本机查看会话记录。");
197
+ }
198
+ }
199
+ }
200
+ else if (message.method === "turn/started" || message.method === "turn/completed") {
201
+ const event = z
202
+ .object({ threadId: z.string(), turn: turnSchema })
203
+ .parse(message.params);
204
+ if (event.threadId !== sessionId || !recordRun(event.turn.id))
205
+ continue;
206
+ if (message.method === "turn/completed") {
207
+ const final = [...messages.values()].filter((item) => item.phase === "final_answer");
208
+ finish({
209
+ status: event.turn.status === "completed"
210
+ ? "completed"
211
+ : event.turn.status === "failed"
212
+ ? "failed"
213
+ : controller.signal.aborted
214
+ ? "cancelled"
215
+ : "interrupted",
216
+ text: (final.length ? final : [...messages.values()])
217
+ .map((item) => item.text)
218
+ .join("\n\n") ||
219
+ (event.turn.status === "completed"
220
+ ? "Codex 已完成,本轮没有文本输出。"
221
+ : event.turn.status === "failed"
222
+ ? "Codex 未能完成本轮任务,请检查本机认证、模型访问与网络。"
223
+ : "任务已中断。已完成的文件修改不会自动撤销。"),
224
+ });
225
+ }
226
+ }
227
+ }
228
+ catch {
229
+ fail("Codex 协议响应或状态保存失败,任务已中断,请检查 CLI 版本与本地存储。");
230
+ }
231
+ }
232
+ });
233
+ try {
234
+ await request("initialize", {
235
+ clientInfo: { name: "tellrun", title: "Tellrun", version: "1.0.1" },
236
+ capabilities: null,
237
+ });
238
+ child.stdin.write(`${JSON.stringify({ method: "initialized" })}\n`);
239
+ const response = z.object({ thread: z.object({ id: z.string().min(1) }) }).parse(await request(input.sessionId ? "thread/resume" : "thread/start", {
240
+ ...(input.sessionId ? { threadId: input.sessionId, excludeTurns: true } : {}),
241
+ cwd: input.cwd,
242
+ approvalPolicy: "on-request",
243
+ sandbox: "workspace-write",
244
+ }));
245
+ sessionId = response.thread.id;
246
+ input.onSession(sessionId);
247
+ if (controller.signal.aborted)
248
+ return { status: "cancelled", text: "任务在开始前已停止。" };
249
+ dispatched = true;
250
+ const started = await Promise.race([
251
+ request("turn/start", {
252
+ threadId: sessionId,
253
+ input: [{ type: "text", text: input.prompt, text_elements: [] }],
254
+ }),
255
+ completed.promise.then(() => null),
256
+ ]);
257
+ if (started !== null)
258
+ recordRun(z.object({ turn: turnSchema }).parse(started).turn.id);
259
+ return await completed.promise;
260
+ }
261
+ catch (error) {
262
+ if (!settled)
263
+ finish({
264
+ status: dispatched ? "interrupted" : controller.signal.aborted ? "cancelled" : "failed",
265
+ text: error instanceof Error && error.message.startsWith("Codex ")
266
+ ? error.message
267
+ : "Codex 任务未能正常完成,请检查本地运行环境。",
268
+ });
269
+ return await completed.promise;
270
+ }
271
+ finally {
272
+ clearTimeout(stopTimer);
273
+ rejectPending("Codex 连接已结束");
274
+ terminate("SIGTERM");
275
+ const force = setTimeout(() => terminate("SIGKILL"), 3_000);
276
+ const deadline = setTimeout(() => closed.reject(new Error("无法确认 Codex 子进程退出,服务必须停止")), 8_000);
277
+ try {
278
+ await closed.promise;
279
+ terminate("SIGKILL");
280
+ }
281
+ finally {
282
+ clearTimeout(force);
283
+ clearTimeout(deadline);
284
+ active = undefined;
285
+ }
286
+ }
287
+ },
288
+ /** 中断当前任务;run 会等待终态或子进程退出后才返回。 */
289
+ stop() {
290
+ active?.abort();
291
+ },
292
+ };
293
+ }
294
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/agents/codex.ts"],"names":[],"mappings":"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"}