tellrun 1.2.0 → 1.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.
package/README.md CHANGED
@@ -8,18 +8,15 @@ Run Tellrun on your server and use Telegram on your phone or desktop to send cod
8
8
 
9
9
  [npm](https://www.npmjs.com/package/tellrun) · [GitHub](https://github.com/chaoyue1998/tellrun) · [Report an issue](https://github.com/chaoyue1998/tellrun/issues)
10
10
 
11
- [Quick start](#install-and-connect-codex-to-telegram) · [Telegram commands](#telegram-commands) · [Configuration](#configuration-and-data) · [FAQ](#faq)
11
+ [Quick start](#install-and-connect-codex-to-telegram) · [Telegram commands](#telegram-commands) · [Terminal commands](#terminal-commands) · [Configuration](#configuration-and-data) · [FAQ](#faq) · [Local development](#local-development)
12
12
 
13
13
  ## Features
14
14
 
15
15
  - **Remote coding**: send tasks in a private Telegram chat and let Codex execute them on your server.
16
16
  - **Continuous conversations**: keep the current conversation context, select a project with `/cwd`, and start fresh with `/new`.
17
17
  - **Live feedback**: see typing indicators and streaming replies, and send additional instructions while a task is running.
18
- - **Task control**: check status and recent results, stop a task and pause new work, and retain conversation and task records across restarts.
19
- - **Personal deployment**: only the configured user's private messages are accepted. Long polling requires no webhook or inbound port configuration.
20
-
21
- Currently supports **Telegram + Codex** for one user, with one task at a time. A session picker, a queue of separate tasks, approval and structured question interfaces, and image or file input are not yet supported.
22
-
18
+ - **Task control**: check status and the latest result, stop a task and pause new work, and retain the current conversation and task state across restarts.
19
+ - **Approvals and questions**: use buttons for command, file, and permission approvals; select options or reply directly to questions, with answers submitted after all questions are answered.
23
20
 
24
21
  ## Install and connect Codex to Telegram
25
22
 
@@ -27,10 +24,6 @@ Currently supports **Telegram + Codex** for one user, with one task at a time. A
27
24
 
28
25
  - **Node.js ≥ 24.5.0**.
29
26
  - **[Codex CLI](https://github.com/openai/codex)**: install and sign in as the same operating system user that will run Tellrun. Confirm that Codex works in your terminal.
30
- - **Telegram Bot Token**: send `/newbot` to [@BotFather](https://t.me/BotFather), follow the prompts, and save the token. See the [official Telegram tutorial](https://core.telegram.org/bots/tutorial#obtain-your-bot-token).
31
- - **Your numeric Telegram user ID**: this is not your `@username`. See [how to find it](#how-do-i-find-my-telegram-user-id).
32
-
33
- Background service management requires **Linux with systemd user services**. Use `tellrun run` in the foreground for local development or with another process manager.
34
27
 
35
28
  ### 2. Install and configure
36
29
 
@@ -39,20 +32,6 @@ npm install -g tellrun
39
32
  tellrun setup
40
33
  ```
41
34
 
42
- Enter your Bot Token, numeric user ID, and Codex command or executable path. On Linux, the setup wizard can start the background service and optionally enable it at boot.
43
-
44
- If you choose to save the configuration without starting the service, run this later:
45
-
46
- ```sh
47
- tellrun start --enable
48
- ```
49
-
50
- To run in the foreground instead:
51
-
52
- ```sh
53
- tellrun run
54
- ```
55
-
56
35
  ### 3. Start chatting in Telegram
57
36
 
58
37
  Open a private chat with your bot and send these messages one at a time. Replace the example path with an existing absolute project path on your server:
@@ -63,13 +42,7 @@ Open a private chat with your bot and send these messages one at a time. Replace
63
42
  Help me understand this project. Explain its main modules and how to run it.
64
43
  ```
65
44
 
66
- Send more text to continue the same conversation. Messages sent while a task is running become additional instructions for that task. For example:
67
-
68
- ```text
69
- Focus on the login flow. Analyze the problem and suggest changes first.
70
- ```
71
-
72
- Without `/cwd`, the working directory defaults to the directory where the service starts. The systemd service starts in the current user's home directory. Changing directories starts a new conversation.
45
+ Without `/cwd`, the working directory defaults to the directory where the service starts. Background services start in the current user's home directory. Changing directories starts a new conversation.
73
46
 
74
47
  ## Telegram commands
75
48
 
@@ -80,33 +53,30 @@ Without `/cwd`, the working directory defaults to the directory where the servic
80
53
  | `/cwd /absolute/path` | Change the working directory and start a new conversation |
81
54
  | `/new` | Start a new conversation while keeping the working directory |
82
55
  | `/status` | Show connection, conversation, directory, and task status |
83
- | `/last` | Show the latest task's status and result |
56
+ | `/last` | Show the latest finished task's status and result |
84
57
  | `/stop` | Request that the current task stop and pause new tasks |
85
58
  | `/resume` | Accept new tasks again without rerunning an interrupted task |
86
59
 
87
60
  Wait for the current task to finish before changing directories or starting a new conversation. After `/stop`, check the final status, then send `/resume` followed by your next task.
88
61
 
89
- ## Background service
62
+ ## Terminal commands
90
63
 
91
- These commands manage a Linux systemd user service:
64
+ Run these commands in the terminal on the computer or server running Tellrun.
92
65
 
93
- | Command | Purpose |
94
- | --------------------------- | ------------------------------------------------------------ |
95
- | `tellrun start` | Start the background service |
96
- | `tellrun start --enable` | Start the service and enable it at boot |
97
- | `tellrun status` | Show service status and data paths |
98
- | `tellrun logs` | Show the latest 100 log entries |
99
- | `tellrun logs -f` | Follow logs |
100
- | `tellrun restart` | Restart with the current configuration or installed version |
101
- | `tellrun stop` | Stop the background service |
102
- | `tellrun service uninstall` | Stop and remove the service, keeping configuration and state |
103
-
104
- On startup, Tellrun checks and enables user lingering so the service can continue after you disconnect from SSH. If permissions are insufficient, the CLI prints the command to run on your server. Use `tellrun --help` for full CLI help.
66
+ | Command | Purpose |
67
+ | --------------------------- | ----------------------------------------------------------------------- |
68
+ | `tellrun start` | Start the background service |
69
+ | `tellrun start --enable` | Start and enable automatic startup: at boot on Linux, at login on macOS |
70
+ | `tellrun status` | Show service status and data paths |
71
+ | `tellrun logs` | Show the latest 100 log entries |
72
+ | `tellrun logs -f` | Follow logs |
73
+ | `tellrun restart` | Restart with the current configuration or installed version |
74
+ | `tellrun stop` | Stop the background service |
75
+ | `tellrun service uninstall` | Stop and remove the service, keeping configuration and state |
76
+ | `tellrun --help` | Show command help |
105
77
 
106
78
  ### Upgrade
107
79
 
108
- Wait for the current task to finish, then run:
109
-
110
80
  ```sh
111
81
  tellrun stop
112
82
  npm install -g tellrun@latest
@@ -119,57 +89,37 @@ Configuration and state live in `~/.tellrun/` and are preserved when upgrading t
119
89
 
120
90
  Run `tellrun setup` to create or update your configuration. Leave the Bot Token blank to keep the existing value.
121
91
 
122
- | Path | Contents |
123
- | ---------------------------- | ------------------------------------------------------------------------------------- |
124
- | `~/.tellrun/config.json` | Bot Token, authorized user ID, Codex path, and optional proxy |
125
- | `~/.tellrun/state.json` | Current conversation, working directory, task inputs and results, and pending replies |
126
- | `~/.tellrun/tellrun.service` | Linux background service definition |
92
+ | Path | Contents |
93
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
94
+ | `~/.tellrun/config.json` | Bot Token, authorized user ID, Codex path, and optional proxy |
95
+ | `~/.tellrun/state.json` | Current conversation, directory, active task, latest result, pending interactions and replies, and unconfirmed update IDs |
96
+ | `~/.tellrun/tellrun.service` | Linux background service definition |
97
+ | `~/.tellrun/tellrun.plist` | macOS background service definition |
98
+ | `~/.tellrun/tellrun.log` | macOS service diagnostics; view with `tellrun logs` |
127
99
 
128
100
  Codex uses the same operating system user's authentication and model configuration, and manages its own full conversation history. Tellrun does not maintain a project directory allowlist. Access depends on operating system permissions and the Codex sandbox; Tellrun defaults to workspace write access and approvals on request.
129
101
 
130
- ### Environment variables
131
-
132
- When running in the foreground or managing the process yourself, you can override configuration with environment variables:
133
-
134
- | Variable | Purpose |
135
- | -------------------- | ----------------------------------------------------------------------------- |
136
- | `TELEGRAM_BOT_TOKEN` | Bot token |
137
- | `TELEGRAM_OWNER_ID` | Numeric ID of the user allowed to interact with the bot |
138
- | `CODEX_PATH` | Codex command name or executable path |
139
- | `TELEGRAM_PROXY_URL` | HTTP/HTTPS proxy for Telegram; overrides the configuration file when nonempty |
102
+ Tellrun keeps only active tasks and each chat's latest result for `/last`. Confirmed update IDs and closed interactions are removed once they are no longer needed; pending replies remain until sent or permanently rejected. Task history stays with Codex; Tellrun logs only service startup, shutdown, and connection or storage diagnostics.
140
103
 
141
- The built-in background service does not automatically load your current terminal's environment variables or a project's `.env` file. Save the required configuration with `tellrun setup`.
104
+ ### Configuration parameters
142
105
 
143
- ### Telegram proxy
106
+ Parameters in `~/.tellrun/config.json`:
144
107
 
145
- To use a proxy, add `"proxyUrl": "http://127.0.0.1:7890"` to the existing `telegram` object in `~/.tellrun/config.json`, then restart the service. Adjust the address and port to match your proxy. Set it to `""` to explicitly connect directly.
108
+ | Parameter | Type | Requirement / default behavior | Description |
109
+ | ------------------- | ---------------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
110
+ | `telegram.token` | String | Required; no default | Bot token obtained from BotFather. |
111
+ | `telegram.ownerId` | Positive integer or numeric string | Required; no default | Your numeric Telegram user ID for authorized access, not your `@username`. A numeric value is recommended. |
112
+ | `telegram.proxyUrl` | String | Optional; inherits environment proxy settings when omitted | Supports `http://` or `https://` proxy URLs; set to `""` to connect directly. |
113
+ | `codexPath` | String | Optional; defaults to `"codex"` | Codex command name or executable path. Names are resolved through `PATH`; `tellrun setup` saves the resolved absolute path. |
146
114
 
147
- Proxy precedence is: nonempty `TELEGRAM_PROXY_URL` → `telegram.proxyUrl` in the configuration file → the running process's `https_proxy` / `HTTPS_PROXY`. Proxy connections honor `no_proxy` / `NO_PROXY`; lowercase variables take precedence. This setting only affects Telegram. Codex uses its own process's proxy environment.
115
+ Configuration is read when the process starts. Run `tellrun restart` after editing the file manually.
148
116
 
149
117
  ## FAQ
150
118
 
151
- ### Can I use Codex for coding from my phone?
152
-
153
- Yes. Install Codex CLI and Tellrun on your server, then chat with your bot through Telegram on your phone or desktop. Code inspection, file edits, and commands run on the server. Telegram is the interface for sending tasks, reading replies, and continuing the conversation.
154
-
155
- ### How do I find my Telegram user ID?
156
-
157
- Before starting Tellrun, send a private message to your new bot. Use Telegram's [getUpdates](https://core.telegram.org/bots/api#getupdates) endpoint and find `message.from.id` for that message:
158
-
159
- ```text
160
- https://api.telegram.org/bot<BOT_TOKEN>/getUpdates
161
- ```
162
-
163
- Replace `<BOT_TOKEN>` with your token. If Tellrun is already running, stop it first to avoid polling conflicts.
164
-
165
119
  ### Why is the bot not replying?
166
120
 
167
121
  Check that you are using a private chat and that your configured user ID is correct. Inspect `tellrun logs`, or the terminal output if running in the foreground. The same bot cannot be used by multiple polling instances at once, and Tellrun cannot start while the bot has an active webhook.
168
122
 
169
- ### What happens when a task needs approval?
170
-
171
- Telegram interfaces for approvals and structured questions are not available yet. Tellrun interrupts the task and explains why. Handle the request locally first, check the task state, then send `/resume` and a new message with the work you want to continue.
172
-
173
123
  ### Will tasks run again after a restart?
174
124
 
175
125
  Tasks that have not been dispatched are retained. Tasks with uncertain execution status are not automatically rerun. Check `/status` and `/last`, then use `/resume` to accept new tasks and send a new message.
@@ -196,12 +146,6 @@ Choose to save configuration without starting a background service in the setup
196
146
 
197
147
  When asking Codex to modify Tellrun itself, run `npm run build` followed by `npm start` so source changes do not trigger a restart during the task.
198
148
 
199
- ## Documentation and feedback
200
-
201
- - [Architecture and technical design](https://github.com/chaoyue1998/tellrun/blob/main/docs/architecture.md) (Chinese): implementation, storage, and deployment details.
202
- - [Product requirements and roadmap](https://github.com/chaoyue1998/tellrun/blob/main/docs/prd.md) (Chinese): includes planned features; this README describes what is currently available.
203
- - [GitHub Issues](https://github.com/chaoyue1998/tellrun/issues): report bugs or suggest improvements. Include versions, reproduction steps, and logs with credentials removed.
204
-
205
149
  ## License
206
150
 
207
151
  [MIT](https://github.com/chaoyue1998/tellrun/blob/main/LICENSE)
package/README.zh-CN.md CHANGED
@@ -8,18 +8,15 @@
8
8
 
9
9
  [npm](https://www.npmjs.com/package/tellrun) · [GitHub](https://github.com/chaoyue1998/tellrun) · [问题反馈](https://github.com/chaoyue1998/tellrun/issues)
10
10
 
11
- [快速开始](#安装并通过-telegram-使用-codex) · [Telegram 命令](#telegram-命令) · [配置](#配置与数据) · [常见问题](#常见问题)
11
+ [快速开始](#安装并通过-telegram-使用-codex) · [Telegram 命令](#telegram-命令) · [终端命令](#终端命令) · [配置](#配置与数据) · [常见问题](#常见问题) · [本地开发](#本地开发)
12
12
 
13
13
  ## 功能
14
14
 
15
15
  - **远程编码**:在 Telegram 私聊中发送任务,由服务器上的 Codex 执行。
16
16
  - **连续对话**:保留当前会话上下文,通过 `/cwd` 指定项目目录,通过 `/new` 开始新会话。
17
17
  - **即时反馈**:显示输入状态与流式回复,执行期间可以继续发送文字补充要求。
18
- - **任务控制**:查看状态和最近结果,停止任务并暂停接收;重启后保留会话与任务记录。
19
- - **个人部署**:仅响应指定用户的私聊,使用长轮询接收消息,无需配置 Webhook 或开放入站端口。
20
-
21
- 当前支持 **Telegram + Codex**,面向单用户,每次处理一个任务。多会话列表、独立任务排队、审批与结构化提问交互、图片和文件输入暂未支持。
22
-
18
+ - **任务控制**:查看状态和最近结果,停止任务并暂停接收;重启后保留当前会话与任务状态。
19
+ - **审批与提问**:通过按钮处理命令、文件和权限审批;点击选项或明确回复对应问题,多题全部回答后提交。
23
20
 
24
21
  ## 安装并通过 Telegram 使用 Codex
25
22
 
@@ -27,10 +24,6 @@
27
24
 
28
25
  - **Node.js ≥ 24.5.0**。
29
26
  - **[Codex CLI](https://github.com/openai/codex)**:在运行 Tellrun 的同一系统用户下安装并完成登录,确认可以在终端正常使用。
30
- - **Telegram Bot Token**:向 [@BotFather](https://t.me/BotFather) 发送 `/newbot`,按提示创建机器人并获取 Token。参见 [Telegram 官方教程](https://core.telegram.org/bots/tutorial#obtain-your-bot-token)。
31
- - **本人 Telegram 数字用户 ID**:不是 `@用户名`,参见[获取方法](#如何获取-telegram-用户-id)。
32
-
33
- 后台管理需要支持 **systemd 用户服务的 Linux**。本地调试或使用其他进程管理器时,可通过 `tellrun run` 前台运行。
34
27
 
35
28
  ### 2. 安装并配置
36
29
 
@@ -39,20 +32,6 @@ npm install -g tellrun
39
32
  tellrun setup
40
33
  ```
41
34
 
42
- 按向导填写 Bot Token、本人数字用户 ID 和 Codex 命令或可执行文件路径。Linux 下可以在配置完成后直接启动后台服务,并选择开机自启。
43
-
44
- 如果选择了仅保存配置,之后运行:
45
-
46
- ```sh
47
- tellrun start --enable
48
- ```
49
-
50
- 前台运行时使用:
51
-
52
- ```sh
53
- tellrun run
54
- ```
55
-
56
35
  ### 3. 在 Telegram 中开始对话
57
36
 
58
37
  打开与机器人的私聊,依次发送以下消息,将示例目录替换为服务器上实际存在的项目绝对路径:
@@ -63,13 +42,7 @@ tellrun run
63
42
  帮我了解这个项目,说明主要模块和启动方式。
64
43
  ```
65
44
 
66
- 之后直接发送文字即可继续当前会话;任务执行中发送的新消息会补充到当前任务。例如:
67
-
68
- ```text
69
- 重点看一下登录流程,先分析问题并给出修改建议。
70
- ```
71
-
72
- 未设置 `/cwd` 时,工作目录默认为服务启动目录;systemd 服务默认使用当前用户的主目录。切换目录会开始新会话。
45
+ 未设置 `/cwd` 时,工作目录默认为服务启动目录;后台服务默认使用当前用户的主目录。切换目录会开始新会话。
73
46
 
74
47
  ## Telegram 命令
75
48
 
@@ -80,33 +53,30 @@ tellrun run
80
53
  | `/cwd /absolute/path` | 切换工作目录并开始新会话 |
81
54
  | `/new` | 开始新会话,保留工作目录 |
82
55
  | `/status` | 查看连接、会话、目录和任务状态 |
83
- | `/last` | 查看最近任务的状态与结果 |
56
+ | `/last` | 查看最近已结束任务的状态与结果 |
84
57
  | `/stop` | 请求停止当前任务,并暂停接收新任务 |
85
58
  | `/resume` | 恢复接收新任务,不会重新执行此前中断的任务 |
86
59
 
87
60
  切换目录或新建会话前,需要等待当前任务结束。使用 `/stop` 后,先检查最终状态,再发送 `/resume` 和新的任务内容。
88
61
 
89
- ## 后台服务
62
+ ## 终端命令
90
63
 
91
- 以下命令用于 Linux systemd 用户服务:
64
+ 在运行 Tellrun 的电脑或服务器上执行以下命令。
92
65
 
93
- | 命令 | 作用 |
94
- | --------------------------- | ------------------------------ |
95
- | `tellrun start` | 启动后台服务 |
96
- | `tellrun start --enable` | 启动服务并启用开机自启 |
97
- | `tellrun status` | 查看服务状态与数据路径 |
98
- | `tellrun logs` | 查看最近 100 条日志 |
99
- | `tellrun logs -f` | 持续跟随日志 |
100
- | `tellrun restart` | 重启服务,加载新配置或新版本 |
101
- | `tellrun stop` | 停止后台服务 |
102
- | `tellrun service uninstall` | 停止并移除服务,保留配置和状态 |
103
-
104
- 启动时会检查并启用用户 linger,使服务在退出 SSH 后继续运行。若权限不足,CLI 会显示需要在服务器执行的命令。完整命令帮助见 `tellrun --help`。
66
+ | 命令 | 作用 |
67
+ | --------------------------- | ------------------------------------------------ |
68
+ | `tellrun start` | 启动后台服务 |
69
+ | `tellrun start --enable` | 启动服务并启用自动启动:Linux 开机、macOS 登录时 |
70
+ | `tellrun status` | 查看服务状态与数据路径 |
71
+ | `tellrun logs` | 查看最近 100 条日志 |
72
+ | `tellrun logs -f` | 持续跟随日志 |
73
+ | `tellrun restart` | 重启服务,加载新配置或新版本 |
74
+ | `tellrun stop` | 停止后台服务 |
75
+ | `tellrun service uninstall` | 停止并移除服务,保留配置和状态 |
76
+ | `tellrun --help` | 查看命令帮助 |
105
77
 
106
78
  ### 升级
107
79
 
108
- 等待当前任务结束后执行:
109
-
110
80
  ```sh
111
81
  tellrun stop
112
82
  npm install -g tellrun@latest
@@ -119,57 +89,37 @@ tellrun restart
119
89
 
120
90
  运行 `tellrun setup` 创建或更新配置,已有 Bot Token 可留空保留原值。
121
91
 
122
- | 路径 | 内容 |
123
- | ---------------------------- | ---------------------------------------------- |
124
- | `~/.tellrun/config.json` | Bot Token、授权用户 ID、Codex 路径和可选代理 |
125
- | `~/.tellrun/state.json` | 当前会话、工作目录、任务输入与结果、待发送回复 |
126
- | `~/.tellrun/tellrun.service` | Linux 后台服务定义 |
92
+ | 路径 | 内容 |
93
+ | ---------------------------- | ------------------------------------------------------------------------ |
94
+ | `~/.tellrun/config.json` | Bot Token、授权用户 ID、Codex 路径和可选代理 |
95
+ | `~/.tellrun/state.json` | 当前会话、工作目录、活动任务、最近结果、待处理交互与回复、未确认事件标识 |
96
+ | `~/.tellrun/tellrun.service` | Linux 后台服务定义 |
97
+ | `~/.tellrun/tellrun.plist` | macOS 后台服务定义 |
98
+ | `~/.tellrun/tellrun.log` | macOS 服务诊断日志,可通过 `tellrun logs` 查看 |
127
99
 
128
100
  Codex 使用同一系统用户的登录和模型配置,完整会话历史由 Codex 管理。Tellrun 不设置项目目录白名单;可访问范围由运行用户权限和 Codex 沙箱共同决定,默认采用工作区写入与按需审批策略。
129
101
 
130
- ### 环境变量
131
-
132
- 前台运行或自行托管进程时,可以使用环境变量覆盖配置:
133
-
134
- | 变量 | 用途 |
135
- | -------------------- | ----------------------------------------------- |
136
- | `TELEGRAM_BOT_TOKEN` | 机器人 Token |
137
- | `TELEGRAM_OWNER_ID` | 允许操作机器人的数字用户 ID |
138
- | `CODEX_PATH` | Codex 命令名或可执行文件路径 |
139
- | `TELEGRAM_PROXY_URL` | Telegram 的 HTTP/HTTPS 代理,非空时覆盖配置文件 |
102
+ Tellrun 只保留活动任务和每个聊天的最近结果,供 `/last` 查看。已确认的去重标识、关闭的交互在不再需要时回收;待发送回复保留到发送成功或永久拒绝。任务历史由 Codex 保存,Tellrun 日志仅用于服务启停、连接和存储故障诊断。
140
103
 
141
- 内置后台服务不会自动读取当前终端的环境变量或项目 `.env`,请使用 `tellrun setup` 保存必要配置。
104
+ ### 配置参数
142
105
 
143
- ### Telegram 代理
106
+ `~/.tellrun/config.json` 的参数配置:
144
107
 
145
- 需要代理时,在已有 `~/.tellrun/config.json` 的 `telegram` 对象中添加 `"proxyUrl": "http://127.0.0.1:7890"`,再重启服务。端口和地址按实际代理调整;设置为 `""` 表示明确直连。
108
+ | 参数 | 类型 | 要求 / 默认行为 | 说明 |
109
+ | ------------------- | ------------------ | ------------------------------ | ------------------------------------------------------------------------------------------ |
110
+ | `telegram.token` | 字符串 | 必填,无默认值 | 从 BotFather 获取的机器人 Token。 |
111
+ | `telegram.ownerId` | 正整数或数字字符串 | 必填,无默认值 | 本人 Telegram 数字用户 ID,用于授权访问,不是 `@用户名`。建议使用数字形式。 |
112
+ | `telegram.proxyUrl` | 字符串 | 可选;省略时继承运行环境的代理 | 支持 `http://` 或 `https://` 代理地址;设为 `""` 时明确直连。 |
113
+ | `codexPath` | 字符串 | 可选,默认 `"codex"` | Codex 命令名或可执行文件路径。命令名按 `PATH` 查找;`tellrun setup` 保存解析后的绝对路径。 |
146
114
 
147
- 代理优先级为:非空 `TELEGRAM_PROXY_URL` → 配置文件中的 `telegram.proxyUrl` → 运行进程的 `https_proxy` / `HTTPS_PROXY`。使用代理时遵循 `no_proxy` / `NO_PROXY`,同名变量以小写优先。此配置仅影响 Telegram;Codex 使用其进程自身的代理环境。
115
+ 配置在进程启动时读取。手动修改后运行 `tellrun restart`。
148
116
 
149
117
  ## 常见问题
150
118
 
151
- ### 可以在手机上使用 Codex 编程吗?
152
-
153
- 可以。先在服务器上安装 Codex CLI 和 Tellrun,再通过手机或电脑上的 Telegram 与机器人私聊。代码读取、修改和命令执行都在服务器上完成,Telegram 用于发送任务、查看回复和继续对话。
154
-
155
- ### 如何获取 Telegram 用户 ID?
156
-
157
- 在启动 Tellrun 前,先向新建的机器人发送一条私聊消息,再通过 Telegram 的 [getUpdates](https://core.telegram.org/bots/api#getupdates) 接口查看该消息的 `message.from.id`:
158
-
159
- ```text
160
- https://api.telegram.org/bot<BOT_TOKEN>/getUpdates
161
- ```
162
-
163
- 将 `<BOT_TOKEN>` 替换为自己的 Token。若 Tellrun 已在运行,先停止服务,避免同时轮询。
164
-
165
119
  ### 机器人没有回复怎么办?
166
120
 
167
121
  确认是在私聊中使用、配置的用户 ID 正确,并查看 `tellrun logs`;前台运行时查看终端输出。同一个 Bot 不能同时由多个轮询实例使用,也不能在已有 Webhook 的情况下启动 Tellrun。
168
122
 
169
- ### 任务需要审批时会怎样?
170
-
171
- 当前还没有 Telegram 审批和结构化提问界面。遇到这类请求时,Tellrun 会中断任务并提示原因。请先在本机处理,确认状态后发送 `/resume`,再提交需要继续的内容。
172
-
173
123
  ### 服务重启后会重新执行任务吗?
174
124
 
175
125
  尚未派发的任务会保留;执行状态不确定的任务不会自动重跑。通过 `/status` 和 `/last` 检查结果后,发送 `/resume` 恢复接收,再发送新消息。
@@ -196,12 +146,6 @@ npm run dev
196
146
 
197
147
  如果让 Codex 修改 Tellrun 自身,使用 `npm run build` 后执行 `npm start`,避免源码监听重启打断任务。
198
148
 
199
- ## 文档与反馈
200
-
201
- - [架构与技术选型](https://github.com/chaoyue1998/tellrun/blob/main/docs/architecture.md):实现、存储和部署细节。
202
- - [产品需求与规划](https://github.com/chaoyue1998/tellrun/blob/main/docs/prd.md):包含尚未实现的功能,当前能力以本 README 为准。
203
- - [GitHub Issues](https://github.com/chaoyue1998/tellrun/issues):提交问题或建议,请附上版本、复现步骤和去除凭据后的日志。
204
-
205
149
  ## 许可证
206
150
 
207
151
  [MIT](https://github.com/chaoyue1998/tellrun/blob/main/LICENSE)
@@ -0,0 +1,126 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { z } from "zod";
3
+ /** 文件事件与审批提示共用变更格式;审批前必须能展示实际路径与差异。 */
4
+ export const fileChangesSchema = z.array(z.object({
5
+ path: z.string(),
6
+ kind: z.object({ type: z.enum(["add", "delete", "update"]), move_path: z.string().nullish() }),
7
+ diff: z.string(),
8
+ }));
9
+ /** 转换审批与提问请求;格式错误抛出异常,中断原因交给调用方应答并清理任务。 */
10
+ export function parseCodexInteraction(method, value, cwd, fileChanges) {
11
+ const interaction = {
12
+ id: randomUUID(),
13
+ kind: "approval",
14
+ text: "",
15
+ expiresAt: Date.now() + 10 * 60_000,
16
+ blocking: true,
17
+ choices: ["accept", "decline"],
18
+ questions: [],
19
+ };
20
+ let permissions;
21
+ let fileReview;
22
+ if (method === "item/commandExecution/requestApproval") {
23
+ const params = z
24
+ .object({
25
+ turnId: z.string(),
26
+ kind: z.enum(["command", "writeStdin"]).default("command"),
27
+ command: z.string().nullish(),
28
+ cwd: z.string().nullish(),
29
+ reason: z.string().nullish(),
30
+ networkApprovalContext: z.object({ host: z.string(), protocol: z.string() }).nullish(),
31
+ additionalPermissions: z.unknown().optional(),
32
+ availableDecisions: z.array(z.unknown()).nullish(),
33
+ })
34
+ .parse(value);
35
+ interaction.text = `${params.kind === "writeStdin" ? "Terminal input" : "Command"}\nDirectory: ${params.cwd ?? cwd}\n${params.command ?? "Command unavailable."}${params.reason ? `\nReason: ${params.reason}` : ""}${params.networkApprovalContext ? `\nNetwork: ${params.networkApprovalContext.protocol}://${params.networkApprovalContext.host}` : ""}${params.additionalPermissions ? `\nPermissions: ${JSON.stringify(params.additionalPermissions, null, 2)}` : ""}`;
36
+ interaction.choices = interaction.choices.filter((choice) => (params.availableDecisions == null || params.availableDecisions.includes(choice)) &&
37
+ (choice !== "accept" || !!params.command || !!params.networkApprovalContext));
38
+ }
39
+ else if (method === "item/fileChange/requestApproval") {
40
+ const params = z
41
+ .object({
42
+ turnId: z.string(),
43
+ itemId: z.string(),
44
+ reason: z.string().nullish(),
45
+ grantRoot: z.string().nullish(),
46
+ })
47
+ .parse(value);
48
+ const changes = fileChanges.get(params.itemId);
49
+ if (changes?.length)
50
+ fileReview = { itemId: params.itemId, changes: JSON.stringify(changes) };
51
+ interaction.text = `File changes\nDirectory: ${cwd}${params.reason ? `\nReason: ${params.reason}` : ""}${params.grantRoot ? `\nWrite access: ${params.grantRoot}` : ""}\n${changes?.length ? changes.map((change) => `${change.kind.type}: ${change.path}${change.kind.move_path ? ` → ${change.kind.move_path}` : ""}\n${change.diff}`).join("\n\n") : "Details unavailable. Decline or stop."}`;
52
+ if (!changes?.length)
53
+ interaction.choices = ["decline"];
54
+ }
55
+ else if (method === "item/permissions/requestApproval") {
56
+ const params = z
57
+ .object({
58
+ turnId: z.string(),
59
+ cwd: z.string(),
60
+ reason: z.string().nullish(),
61
+ permissions: z.strictObject({
62
+ network: z.strictObject({ enabled: z.boolean().nullable() }).nullish(),
63
+ fileSystem: z
64
+ .strictObject({
65
+ read: z.array(z.string()).nullable(),
66
+ write: z.array(z.string()).nullable(),
67
+ globScanMaxDepth: z.number().int().nonnegative().optional(),
68
+ entries: z.array(z.unknown()).optional(),
69
+ })
70
+ .nullish(),
71
+ }),
72
+ })
73
+ .parse(value);
74
+ permissions = Object.fromEntries(Object.entries(params.permissions).filter(([, value]) => value != null));
75
+ interaction.text = `Permissions (this turn only)\nDirectory: ${params.cwd}${params.reason ? `\nReason: ${params.reason}` : ""}\n${JSON.stringify(permissions, null, 2)}`;
76
+ }
77
+ else if (method === "item/tool/requestUserInput") {
78
+ const params = z
79
+ .object({
80
+ turnId: z.string(),
81
+ isBlocking: z.boolean().default(true),
82
+ autoResolutionMs: z.number().int().nonnegative().nullish(),
83
+ questions: z
84
+ .array(z.object({
85
+ id: z.string().min(1),
86
+ header: z.string(),
87
+ question: z.string(),
88
+ isOther: z.boolean().default(false),
89
+ isSecret: z.boolean().default(false),
90
+ options: z
91
+ .array(z.object({ label: z.string().min(1), description: z.string() }))
92
+ .max(20)
93
+ .nullable(),
94
+ }))
95
+ .min(1)
96
+ .max(20),
97
+ })
98
+ .parse(value);
99
+ if (params.questions.some((question) => question.isSecret))
100
+ return { error: "Secret input unsupported. Handle locally, then /resume." };
101
+ if (new Set(params.questions.map((question) => question.id)).size !== params.questions.length)
102
+ throw new Error("Duplicate question IDs.");
103
+ interaction.kind = "question";
104
+ interaction.blocking = params.isBlocking;
105
+ interaction.choices = [];
106
+ interaction.expiresAt = Math.min(interaction.expiresAt, Date.now() + (params.autoResolutionMs ?? 10 * 60_000));
107
+ interaction.questions = params.questions.map((question) => ({
108
+ id: question.id,
109
+ header: question.header,
110
+ question: question.question,
111
+ options: question.options ?? [],
112
+ allowText: question.isOther || !question.options?.length,
113
+ }));
114
+ }
115
+ else {
116
+ return { error: "Unsupported request. Handle locally, then /resume.", unsupported: true };
117
+ }
118
+ if (JSON.stringify(interaction).length > 64_000)
119
+ return { error: "Request too large. Handle locally, then /resume." };
120
+ return {
121
+ request: interaction,
122
+ ...(permissions ? { permissions } : {}),
123
+ ...(fileReview ? { fileReview } : {}),
124
+ };
125
+ }
126
+ //# sourceMappingURL=codex-interaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-interaction.js","sourceRoot":"","sources":["../../src/agents/codex-interaction.ts"],"names":[],"mappings":"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"}