tellrun 1.2.0 → 1.2.5

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,16 @@ 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.
20
+ - **Permission modes**: use `/permissions` to choose one of four Codex modes, saved for subsequent tasks.
23
21
 
24
22
  ## Install and connect Codex to Telegram
25
23
 
@@ -27,10 +25,6 @@ Currently supports **Telegram + Codex** for one user, with one task at a time. A
27
25
 
28
26
  - **Node.js ≥ 24.5.0**.
29
27
  - **[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
28
 
35
29
  ### 2. Install and configure
36
30
 
@@ -39,20 +33,6 @@ npm install -g tellrun
39
33
  tellrun setup
40
34
  ```
41
35
 
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
36
  ### 3. Start chatting in Telegram
57
37
 
58
38
  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,49 +43,47 @@ Open a private chat with your bot and send these messages one at a time. Replace
63
43
  Help me understand this project. Explain its main modules and how to run it.
64
44
  ```
65
45
 
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.
46
+ 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
47
 
74
48
  ## Telegram commands
75
49
 
50
+ At startup, Tellrun registers an English command menu. Use the menu button beside the chat input or type `/` to select a command.
51
+
76
52
  | Command | Purpose |
77
53
  | --------------------- | ------------------------------------------------------------ |
78
54
  | `/start`, `/help` | Show usage instructions |
79
55
  | `/cwd` | Show the current working directory |
80
56
  | `/cwd /absolute/path` | Change the working directory and start a new conversation |
81
57
  | `/new` | Start a new conversation while keeping the working directory |
58
+ | `/permissions` | Choose the permission mode for subsequent tasks |
82
59
  | `/status` | Show connection, conversation, directory, and task status |
83
- | `/last` | Show the latest task's status and result |
60
+ | `/last` | Show the latest finished task's status and result |
84
61
  | `/stop` | Request that the current task stop and pause new tasks |
85
62
  | `/resume` | Accept new tasks again without rerunning an interrupted task |
86
63
 
87
64
  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
65
 
89
- ## Background service
66
+ `/permissions` offers the four presets from Codex CLI 0.165.0: Ask for approval (default), Approve for me (Codex automatic review), Full Access (no Codex sandbox or execution approvals), and Read Only (approval required for edits or network access). Full Access remains subject to operating system permissions.
90
67
 
91
- These commands manage a Linux systemd user service:
68
+ Selections apply to subsequently submitted tasks; the current task and its follow-up messages retain their original mode. `/status` shows both modes when they differ. Selections survive service restarts and `/new`; changing the working directory resets the mode to its default. Menus expire after 10 minutes and are invalidated when reopened, on a new conversation, on a directory change, or after a restart. Codex validates the selected mode when starting a task. If it rejects the configuration or returns different settings, the task does not start.
92
69
 
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 |
70
+ ## Terminal commands
103
71
 
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.
72
+ Run these commands in the terminal on the computer or server running Tellrun.
105
73
 
106
- ### Upgrade
74
+ | Command | Purpose |
75
+ | --------------------------- | ----------------------------------------------------------------------- |
76
+ | `tellrun start` | Start the background service |
77
+ | `tellrun start --enable` | Start and enable automatic startup: at boot on Linux, at login on macOS |
78
+ | `tellrun status` | Show service status and data paths |
79
+ | `tellrun logs` | Show the latest 100 log entries |
80
+ | `tellrun logs -f` | Follow logs |
81
+ | `tellrun restart` | Restart with the current configuration or installed version |
82
+ | `tellrun stop` | Stop the background service |
83
+ | `tellrun service uninstall` | Stop and remove the service, keeping configuration and state |
84
+ | `tellrun --help` | Show command help |
107
85
 
108
- Wait for the current task to finish, then run:
86
+ ### Upgrade
109
87
 
110
88
  ```sh
111
89
  tellrun stop
@@ -119,57 +97,37 @@ Configuration and state live in `~/.tellrun/` and are preserved when upgrading t
119
97
 
120
98
  Run `tellrun setup` to create or update your configuration. Leave the Bot Token blank to keep the existing value.
121
99
 
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 |
100
+ | Path | Contents |
101
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
102
+ | `~/.tellrun/config.json` | Bot Token, authorized user ID, Codex path, and optional proxy |
103
+ | `~/.tellrun/state.json` | Current conversation, directory, active task, latest result, pending interactions and replies, and unconfirmed update IDs |
104
+ | `~/.tellrun/tellrun.service` | Linux background service definition |
105
+ | `~/.tellrun/tellrun.plist` | macOS background service definition |
106
+ | `~/.tellrun/tellrun.log` | macOS service diagnostics; view with `tellrun logs` |
127
107
 
128
108
  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
109
 
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 |
110
+ 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
111
 
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`.
112
+ ### Configuration parameters
142
113
 
143
- ### Telegram proxy
114
+ Parameters in `~/.tellrun/config.json`:
144
115
 
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.
116
+ | Parameter | Type | Requirement / default behavior | Description |
117
+ | ------------------- | ---------------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
118
+ | `telegram.token` | String | Required; no default | Bot token obtained from BotFather. |
119
+ | `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. |
120
+ | `telegram.proxyUrl` | String | Optional; inherits environment proxy settings when omitted | Supports `http://` or `https://` proxy URLs; set to `""` to connect directly. |
121
+ | `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
122
 
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.
123
+ Configuration is read when the process starts. Run `tellrun restart` after editing the file manually.
148
124
 
149
125
  ## FAQ
150
126
 
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
127
  ### Why is the bot not replying?
166
128
 
167
129
  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
130
 
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
131
  ### Will tasks run again after a restart?
174
132
 
175
133
  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 +154,6 @@ Choose to save configuration without starting a background service in the setup
196
154
 
197
155
  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
156
 
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
157
  ## License
206
158
 
207
159
  [MIT](https://github.com/chaoyue1998/tellrun/blob/main/LICENSE)
package/README.zh-CN.md CHANGED
@@ -8,18 +8,16 @@
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
+ - **审批与提问**:通过按钮处理命令、文件和权限审批;点击选项或明确回复对应问题,多题全部回答后提交。
20
+ - **权限选择**:通过 `/permissions` 选择 Codex 的四种权限模式,选择会保存并用于后续任务。
23
21
 
24
22
  ## 安装并通过 Telegram 使用 Codex
25
23
 
@@ -27,10 +25,6 @@
27
25
 
28
26
  - **Node.js ≥ 24.5.0**。
29
27
  - **[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
28
 
35
29
  ### 2. 安装并配置
36
30
 
@@ -39,20 +33,6 @@ npm install -g tellrun
39
33
  tellrun setup
40
34
  ```
41
35
 
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
36
  ### 3. 在 Telegram 中开始对话
57
37
 
58
38
  打开与机器人的私聊,依次发送以下消息,将示例目录替换为服务器上实际存在的项目绝对路径:
@@ -63,49 +43,47 @@ tellrun run
63
43
  帮我了解这个项目,说明主要模块和启动方式。
64
44
  ```
65
45
 
66
- 之后直接发送文字即可继续当前会话;任务执行中发送的新消息会补充到当前任务。例如:
67
-
68
- ```text
69
- 重点看一下登录流程,先分析问题并给出修改建议。
70
- ```
71
-
72
- 未设置 `/cwd` 时,工作目录默认为服务启动目录;systemd 服务默认使用当前用户的主目录。切换目录会开始新会话。
46
+ 未设置 `/cwd` 时,工作目录默认为服务启动目录;后台服务默认使用当前用户的主目录。切换目录会开始新会话。
73
47
 
74
48
  ## Telegram 命令
75
49
 
50
+ 服务启动时自动注册英文命令菜单,可通过聊天输入框旁的菜单按钮或输入 `/` 选择命令。
51
+
76
52
  | 命令 | 作用 |
77
53
  | --------------------- | ------------------------------------------ |
78
54
  | `/start`、`/help` | 查看使用说明 |
79
55
  | `/cwd` | 查看当前工作目录 |
80
56
  | `/cwd /absolute/path` | 切换工作目录并开始新会话 |
81
57
  | `/new` | 开始新会话,保留工作目录 |
58
+ | `/permissions` | 选择后续任务的权限模式 |
82
59
  | `/status` | 查看连接、会话、目录和任务状态 |
83
- | `/last` | 查看最近任务的状态与结果 |
60
+ | `/last` | 查看最近已结束任务的状态与结果 |
84
61
  | `/stop` | 请求停止当前任务,并暂停接收新任务 |
85
62
  | `/resume` | 恢复接收新任务,不会重新执行此前中断的任务 |
86
63
 
87
64
  切换目录或新建会话前,需要等待当前任务结束。使用 `/stop` 后,先检查最终状态,再发送 `/resume` 和新的任务内容。
88
65
 
89
- ## 后台服务
66
+ `/permissions` 提供与 Codex CLI 0.165.0 对应的四种模式:Ask for approval(默认,额外权限由你审批)、Approve for me(由 Codex 自动审查)、Full Access(不使用 Codex 沙箱和执行审批)、Read Only(只读,修改或联网时请求审批)。Full Access 仍受系统用户权限约束。
90
67
 
91
- 以下命令用于 Linux systemd 用户服务:
68
+ 选择在下一次提交任务时使用,当前任务及其补充消息保留原权限;`/status` 会在两者不同时分别显示。选择跨服务重启保留,`/new` 沿用选择,切换工作目录恢复默认。菜单有效期为 10 分钟,重新打开、新建会话、切换目录或重启后旧按钮失效。模式受本机 Codex 版本与托管策略限制,启动任务时由 Codex 校验;被拒绝或返回设置不符时不启动任务。
92
69
 
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` | 停止并移除服务,保留配置和状态 |
70
+ ## 终端命令
103
71
 
104
- 启动时会检查并启用用户 linger,使服务在退出 SSH 后继续运行。若权限不足,CLI 会显示需要在服务器执行的命令。完整命令帮助见 `tellrun --help`。
72
+ 在运行 Tellrun 的电脑或服务器上执行以下命令。
105
73
 
106
- ### 升级
74
+ | 命令 | 作用 |
75
+ | --------------------------- | ------------------------------------------------ |
76
+ | `tellrun start` | 启动后台服务 |
77
+ | `tellrun start --enable` | 启动服务并启用自动启动:Linux 开机、macOS 登录时 |
78
+ | `tellrun status` | 查看服务状态与数据路径 |
79
+ | `tellrun logs` | 查看最近 100 条日志 |
80
+ | `tellrun logs -f` | 持续跟随日志 |
81
+ | `tellrun restart` | 重启服务,加载新配置或新版本 |
82
+ | `tellrun stop` | 停止后台服务 |
83
+ | `tellrun service uninstall` | 停止并移除服务,保留配置和状态 |
84
+ | `tellrun --help` | 查看命令帮助 |
107
85
 
108
- 等待当前任务结束后执行:
86
+ ### 升级
109
87
 
110
88
  ```sh
111
89
  tellrun stop
@@ -119,57 +97,37 @@ tellrun restart
119
97
 
120
98
  运行 `tellrun setup` 创建或更新配置,已有 Bot Token 可留空保留原值。
121
99
 
122
- | 路径 | 内容 |
123
- | ---------------------------- | ---------------------------------------------- |
124
- | `~/.tellrun/config.json` | Bot Token、授权用户 ID、Codex 路径和可选代理 |
125
- | `~/.tellrun/state.json` | 当前会话、工作目录、任务输入与结果、待发送回复 |
126
- | `~/.tellrun/tellrun.service` | Linux 后台服务定义 |
100
+ | 路径 | 内容 |
101
+ | ---------------------------- | ------------------------------------------------------------------------ |
102
+ | `~/.tellrun/config.json` | Bot Token、授权用户 ID、Codex 路径和可选代理 |
103
+ | `~/.tellrun/state.json` | 当前会话、工作目录、活动任务、最近结果、待处理交互与回复、未确认事件标识 |
104
+ | `~/.tellrun/tellrun.service` | Linux 后台服务定义 |
105
+ | `~/.tellrun/tellrun.plist` | macOS 后台服务定义 |
106
+ | `~/.tellrun/tellrun.log` | macOS 服务诊断日志,可通过 `tellrun logs` 查看 |
127
107
 
128
108
  Codex 使用同一系统用户的登录和模型配置,完整会话历史由 Codex 管理。Tellrun 不设置项目目录白名单;可访问范围由运行用户权限和 Codex 沙箱共同决定,默认采用工作区写入与按需审批策略。
129
109
 
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 代理,非空时覆盖配置文件 |
110
+ Tellrun 只保留活动任务和每个聊天的最近结果,供 `/last` 查看。已确认的去重标识、关闭的交互在不再需要时回收;待发送回复保留到发送成功或永久拒绝。任务历史由 Codex 保存,Tellrun 日志仅用于服务启停、连接和存储故障诊断。
140
111
 
141
- 内置后台服务不会自动读取当前终端的环境变量或项目 `.env`,请使用 `tellrun setup` 保存必要配置。
112
+ ### 配置参数
142
113
 
143
- ### Telegram 代理
114
+ `~/.tellrun/config.json` 的参数配置:
144
115
 
145
- 需要代理时,在已有 `~/.tellrun/config.json` 的 `telegram` 对象中添加 `"proxyUrl": "http://127.0.0.1:7890"`,再重启服务。端口和地址按实际代理调整;设置为 `""` 表示明确直连。
116
+ | 参数 | 类型 | 要求 / 默认行为 | 说明 |
117
+ | ------------------- | ------------------ | ------------------------------ | ------------------------------------------------------------------------------------------ |
118
+ | `telegram.token` | 字符串 | 必填,无默认值 | 从 BotFather 获取的机器人 Token。 |
119
+ | `telegram.ownerId` | 正整数或数字字符串 | 必填,无默认值 | 本人 Telegram 数字用户 ID,用于授权访问,不是 `@用户名`。建议使用数字形式。 |
120
+ | `telegram.proxyUrl` | 字符串 | 可选;省略时继承运行环境的代理 | 支持 `http://` 或 `https://` 代理地址;设为 `""` 时明确直连。 |
121
+ | `codexPath` | 字符串 | 可选,默认 `"codex"` | Codex 命令名或可执行文件路径。命令名按 `PATH` 查找;`tellrun setup` 保存解析后的绝对路径。 |
146
122
 
147
- 代理优先级为:非空 `TELEGRAM_PROXY_URL` → 配置文件中的 `telegram.proxyUrl` → 运行进程的 `https_proxy` / `HTTPS_PROXY`。使用代理时遵循 `no_proxy` / `NO_PROXY`,同名变量以小写优先。此配置仅影响 Telegram;Codex 使用其进程自身的代理环境。
123
+ 配置在进程启动时读取。手动修改后运行 `tellrun restart`。
148
124
 
149
125
  ## 常见问题
150
126
 
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
127
  ### 机器人没有回复怎么办?
166
128
 
167
129
  确认是在私聊中使用、配置的用户 ID 正确,并查看 `tellrun logs`;前台运行时查看终端输出。同一个 Bot 不能同时由多个轮询实例使用,也不能在已有 Webhook 的情况下启动 Tellrun。
168
130
 
169
- ### 任务需要审批时会怎样?
170
-
171
- 当前还没有 Telegram 审批和结构化提问界面。遇到这类请求时,Tellrun 会中断任务并提示原因。请先在本机处理,确认状态后发送 `/resume`,再提交需要继续的内容。
172
-
173
131
  ### 服务重启后会重新执行任务吗?
174
132
 
175
133
  尚未派发的任务会保留;执行状态不确定的任务不会自动重跑。通过 `/status` 和 `/last` 检查结果后,发送 `/resume` 恢复接收,再发送新消息。
@@ -196,12 +154,6 @@ npm run dev
196
154
 
197
155
  如果让 Codex 修改 Tellrun 自身,使用 `npm run build` 后执行 `npm start`,避免源码监听重启打断任务。
198
156
 
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
157
  ## 许可证
206
158
 
207
159
  [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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"}