@foxden-app/foxclaw 0.3.18 → 0.3.19

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.
@@ -141,7 +141,7 @@ nano ~/.foxclaw/.env
141
141
  第一次私聊模式只需要重点填写这些值:
142
142
 
143
143
  ```dotenv
144
- TG_BOT_TOKEN=123456789:replace_with_your_bot_token
144
+ TG_BOT_TOKENS=123456789:replace_with_your_bot_token
145
145
  TG_ALLOWED_USER_ID=123456789
146
146
  TG_ALLOWED_CHAT_ID=
147
147
  TG_ALLOWED_TOPIC_ID=
@@ -174,7 +174,7 @@ foxclaw doctor
174
174
  ```text
175
175
  [OK] node >= 24
176
176
  [OK] codex cli available
177
- [OK] telegram bot token configured
177
+ [OK] telegram bot token(s) configured
178
178
  [OK] telegram allowed user configured
179
179
  [OK] default cwd exists
180
180
  ```
@@ -278,7 +278,7 @@ foxclaw uninstall-systemd
278
278
  foxclaw update
279
279
  ```
280
280
 
281
- 也可以在已授权的 Telegram 私聊里发送 `/update`。它会在当前没有运行中回复、审批或待确认问题时,完成升级、自检和服务重启,并在重启后回报结果。
281
+ 也可以在已授权的 Telegram 私聊里发送 `/update`。它会在所有 bot 都没有运行中回复、审批或待确认问题时,尝试升级 npm/pnpm 安装的 Codex CLI,完成 FoxClaw 升级、自检和服务重启,并在重启后回报结果。
282
282
 
283
283
  如果 `~/.foxclaw/.env` 已经存在,`foxclaw init` 会先询问是否更新 Telegram 和工作目录相关字段,其它配置保持不变。
284
284
 
@@ -20,7 +20,7 @@ journalctl --user -u foxclaw.service -f
20
20
  | --- | --- | --- |
21
21
  | `[FAIL] node >= 24` | 当前 shell 使用的是旧版 Node.js。 | 先用任意方式安装或切换到 Node.js 24+,再重新执行 `foxclaw doctor`。如果服务仍用旧 Node,从 Node 24+ 的 shell 里重新执行 `foxclaw start`。 |
22
22
  | `[FAIL] codex cli available` | `codex` 命令不在 PATH 里。 | 安装 Codex CLI 或修正 PATH,再确认 `codex --version` 可用。 |
23
- | `[FAIL] telegram bot token configured` | `.env` 里缺少 `TG_BOT_TOKEN`。 | 从 `@BotFather` 复制 token,填入 `.env`。 |
23
+ | `[FAIL] telegram bot token(s) configured` | `.env` 里没有 `TG_BOT_TOKENS`,也没有兼容变量 `TG_BOT_TOKEN`。 | 从 `@BotFather` 复制一个或多个 token,用逗号分隔填入 `TG_BOT_TOKENS`。 |
24
24
  | `[FAIL] telegram allowed user configured` | `.env` 里缺少 `TG_ALLOWED_USER_ID`。 | 从 `@userinfobot` 获取数字 ID,填入 `.env`。 |
25
25
  | `[FAIL] default cwd exists` | `DEFAULT_CWD` 指向不存在的目录。 | 创建该目录,或把 `DEFAULT_CWD` 改成一个真实存在的绝对路径。 |
26
26
 
@@ -100,7 +100,7 @@ foxclaw init
100
100
 
101
101
  ### 1.6 填写配置
102
102
 
103
- `foxclaw init` 会创建默认配置文件 `~/.foxclaw/.env`,并提示填写 Telegram bot tokenTelegram 数字用户 ID 和默认工作目录。如果当前 shell 里有 `HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY` 等代理变量,它也会询问是否写入 FoxClaw 配置。配置了 `HTTP_PROXY` 或 `HTTPS_PROXY` 后,FoxClaw 会在安装服务时显式传给 systemd/launchd,并启用 Node 的 env proxy。任何一项都可以直接回车跳过,之后再手动编辑:
103
+ `foxclaw init` 会创建默认配置文件 `~/.foxclaw/.env`,并提示填写一个或多个 Telegram bot token(逗号分隔)、Telegram 数字用户 ID 和默认工作目录。如果当前 shell 里有 `HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY` 等代理变量,它也会询问是否写入 FoxClaw 配置。配置了 `HTTP_PROXY` 或 `HTTPS_PROXY` 后,FoxClaw 会在安装服务时显式传给 systemd/launchd,并启用 Node 的 env proxy。任何一项都可以直接回车跳过,之后再手动编辑:
104
104
 
105
105
  ```bash
106
106
  $EDITOR ~/.foxclaw/.env
@@ -109,7 +109,7 @@ $EDITOR ~/.foxclaw/.env
109
109
  第一次私聊模式的最小配置:
110
110
 
111
111
  ```dotenv
112
- TG_BOT_TOKEN=123456789:replace_with_your_bot_token
112
+ TG_BOT_TOKENS=123456789:replace_with_your_bot_token
113
113
  TG_ALLOWED_USER_ID=123456789
114
114
  TG_ALLOWED_CHAT_ID=
115
115
  TG_ALLOWED_TOPIC_ID=
@@ -120,7 +120,7 @@ DEFAULT_SANDBOX_MODE=workspace-write
120
120
 
121
121
  字段说明:
122
122
 
123
- - `TG_BOT_TOKEN`:从 `@BotFather` 拿到的 bot token
123
+ - `TG_BOT_TOKENS`:从 `@BotFather` 拿到的一个或多个 bot token,多项用英文逗号分隔。旧版单 bot 配置 `TG_BOT_TOKEN` 仍兼容。
124
124
  - `TG_ALLOWED_USER_ID`:你的 Telegram 数字用户 ID。
125
125
  - `TG_ALLOWED_CHAT_ID`:第一次保持为空,表示私聊模式。
126
126
  - `TG_ALLOWED_TOPIC_ID`:第一次保持为空,表示不绑定 Telegram 话题。
@@ -136,7 +136,7 @@ foxclaw start
136
136
  foxclaw status
137
137
  ```
138
138
 
139
- 后续升级只需运行 `foxclaw update`;它会使用当前安装 FoxClaw 的 npm 或 pnpm,全局升级后执行自检并重启后台服务。
139
+ 后续升级只需运行 `foxclaw update`;它会使用当前安装 FoxClaw 的 npm 或 pnpm,尝试同时升级由 npm/pnpm 全局安装的 Codex CLI,然后执行自检并重启后台服务。
140
140
 
141
141
  Linux 查看服务日志:
142
142
 
@@ -233,7 +233,7 @@ TG_ALLOWED_TOPIC_ID=42
233
233
  - `/status`:查看 FoxClaw、app-server、当前绑定线程、模型、权限和 Codex 用量摘要。本地 session/Token/可见答复吞吐使用后台生成的历史快照,避免状态查询现场扫描大量日志;答复吞吐按完成轮次端到端耗时计算,排除推理 token,但包含等待与工具执行时间。
234
234
  - `/account`:查看当前 Codex 登录账号。
235
235
  - `/quota`:查看 Codex 用量和额度窗口。
236
- - `/update`:升级 FoxClaw、自检并重启服务;当前有运行中回复、审批或待确认问题时会拒绝执行,重启后会回报结果。
236
+ - `/update`:升级 FoxClaw,并尝试升级 npm/pnpm 安装的 Codex CLI,然后自检和重启服务;任意 bot runtime 有运行中回复、审批或待确认问题时都会拒绝执行,重启后会回报结果。
237
237
 
238
238
  ### 3.3 `/config`、`/requirements`、`/provider`
239
239
 
@@ -335,11 +335,11 @@ FoxClaw 的聊天是“绑定线程”的。你在手机上打开某个 Codex
335
335
 
336
336
  ## 6. Codex 登录和 auth 轮转
337
337
 
338
- 这是 FoxClaw 的特色功能。Codex 的登录状态通常保存在 `~/.codex/auth.json`。FoxClaw 把多个账号保存成候选文件,并通过切换 `auth.json` 指向哪个候选来换号。
338
+ 这是 FoxClaw 的特色功能。Codex 的登录状态通常保存在 `~/.codex/auth.json`。FoxClaw 把多个账号保存成候选文件,并通过切换 `auth.json` 指向哪个候选来换号。启用 `TG_BOT_TOKENS` 多 bot 模式后,每个 bot 使用独立 Codex home、独立 app-server 和独立当前候选,因此可以并行运行、单独切号;已验证的登录/刷新凭据会安全镜像到其他 bot home。
339
339
 
340
340
  ### 6.1 文件格式
341
341
 
342
- 候选文件放在 Codex auth 目录,默认是 `~/.codex/`。如果你设置了 `CODEX_AUTH_DIR`,则使用那个目录。
342
+ bot 兼容模式的候选文件放在 Codex auth 目录,默认是 `~/.codex/`。如果你设置了 `CODEX_AUTH_DIR`,则使用那个目录。多 bot 模式以这个目录作为候选源,并在 `~/.foxclaw/codex/telegram/bot<id>/home/` 下为每个 bot 保存隔离副本。
343
343
 
344
344
  推荐命名:
345
345
 
@@ -357,7 +357,7 @@ FoxClaw 识别这些候选文件名:
357
357
  - `auth.json.<name>`
358
358
  - `auth.json-<name>`
359
359
 
360
- `auth.json` 是 Codex 当前使用的文件。切换账号时,FoxClaw 会把 `auth.json` 指向某个候选文件。候选文件内容是 Codex CLI 生成的 JSON,FoxClaw 不依赖里面的私密字段,也不建议手写这些字段。
360
+ `auth.json` 是 Codex 当前使用的文件。切换账号时,FoxClaw 会把 `auth.json` 指向某个候选文件。候选文件内容是 Codex CLI 生成的 JSON,不建议手写这些字段。多 bot 镜像只在能校验同一账号标识且刷新时间更新时复制候选,避免同名候选意外覆盖成另一个账号。
361
361
 
362
362
  如果你已经有一个可用的 `auth.json`,可以先备份成候选:
363
363
 
@@ -416,7 +416,7 @@ Candidates: 2
416
416
  - `/auth disable <n>`:禁用第 n 个候选,自动轮转会跳过它。
417
417
  - `/auth reload` 或 `/auth_reload`:重启 app-server,重新加载当前 `auth.json`。
418
418
 
419
- 切换 auth 时,如果当前还有活跃 turn、待审批、待用户输入或 MCP elicitation,FoxClaw 会先拒绝切换,避免中途换号破坏正在进行的请求。
419
+ 切换 auth 时,如果当前 bot runtime 还有活跃 turn、待审批、待用户输入或 MCP elicitation,FoxClaw 会先拒绝切换,避免中途换号破坏正在进行的请求;另一个空闲 bot 不受影响。
420
420
 
421
421
  ### 6.4 自动轮转如何工作
422
422
 
@@ -452,7 +452,7 @@ auth.json_backup # 备用账号,可按需 enable/disable
452
452
 
453
453
  ## 8. 安全建议
454
454
 
455
- - 不要把 `TG_BOT_TOKEN`、`~/.codex/auth.json*` 或 `.env` 发给别人。
455
+ - 不要把 `TG_BOT_TOKENS`、`TG_BOT_TOKEN`、`~/.codex/auth.json*` 或 `.env` 发给别人。
456
456
  - 第一次不要把 `DEFAULT_CWD` 设成 `/`、`/home`、`/Users` 或整个 home。
457
457
  - 不确定权限时,用 `/permissions read-only` 或 `/setup` 选 `Read-only`。
458
458
  - 群组模式只允许配置的 `TG_ALLOWED_USER_ID` 操作,但仍建议只放进可信群。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxden-app/foxclaw",
3
- "version": "0.3.18",
3
+ "version": "0.3.19",
4
4
  "description": "Foxden local execution claw for controlling Codex from trusted chat interfaces.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -21,10 +21,11 @@ Follow this order every time:
21
21
 
22
22
  1. Decide whether this is a local Mac install or a remote Mac over SSH.
23
23
  2. Confirm where the bridge repo should live. If the repo is not already present, clone it or let bootstrap clone it.
24
- 3. Decide the Telegram mode before writing `.env`:
25
- - private chat only
26
- - one allowed group
27
- - one allowed topic inside one group
24
+ 3. Decide the Telegram mode before writing `.env`:
25
+ - private chat only
26
+ - one allowed group
27
+ - one allowed topic inside one group
28
+ - parallel bots on one host, using comma-separated tokens and independent Codex runtimes
28
29
  4. Collect the Telegram values and filesystem paths listed below.
29
30
  5. Run the correct bootstrap script with explicit arguments.
30
31
  6. Run post-install validation.
@@ -37,7 +38,7 @@ Do not stop after "install completed" if the user asked for a working bot.
37
38
 
38
39
  Collect these values before running bootstrap:
39
40
 
40
- - `TG_BOT_TOKEN`
41
+ - `TG_BOT_TOKENS` (one or more bot tokens separated by commas)
41
42
  - `TG_ALLOWED_USER_ID`
42
43
  - `DEFAULT_CWD`
43
44
 
@@ -61,20 +62,23 @@ Defaults:
61
62
 
62
63
  Telegram behavior:
63
64
 
64
- - private chat with `TG_ALLOWED_USER_ID` remains available even when `TG_ALLOWED_CHAT_ID` or `TG_ALLOWED_TOPIC_ID` is set
65
- - `TG_ALLOWED_CHAT_ID` and `TG_ALLOWED_TOPIC_ID` choose the default group or topic scope; they do not disable private chat
65
+ - private chat with `TG_ALLOWED_USER_ID` remains available even when `TG_ALLOWED_CHAT_ID` or `TG_ALLOWED_TOPIC_ID` is set
66
+ - `TG_ALLOWED_CHAT_ID` and `TG_ALLOWED_TOPIC_ID` choose the default group or topic scope; they do not disable private chat
67
+ - when `TG_BOT_TOKENS` contains multiple bots, one FoxClaw service starts independent Codex app-servers and auth selections per bot
68
+ - in a group with multiple configured bots, address a bot by mention, reply, or suffixed command such as `/status@botname`
66
69
 
67
70
  If `TG_ALLOWED_CHAT_ID` or `TG_ALLOWED_TOPIC_ID` is missing, read [references/telegram-setup.md](./references/telegram-setup.md) and explicitly guide the user through collecting it.
68
71
 
69
72
  ## Deployment Rules
70
73
 
71
- 1. If the user is deploying to a second Mac, prefer one bot per device.
72
- 2. If no unique bot token has been provided for the second Mac, bootstrap with `--no-start`.
73
- 3. If group or topic mode is involved, read [references/telegram-setup.md](./references/telegram-setup.md) before continuing.
74
- 4. Always explain to the user which values will be written into `.env` before starting bootstrap.
75
- 5. After bootstrap, check `codex login status`. If authentication is missing, tell the user to run `codex login` or open `codex app` on that Mac.
76
- 6. If the user only says "set it up" and has not given all required values, ask for the missing values directly instead of guessing Telegram IDs.
77
- 7. If the user wants a fully usable setup, continue until first-message validation is done or clearly blocked by Telegram-side prerequisites.
74
+ 1. If the user is deploying to a second Mac, do not reuse a token that another running host is polling.
75
+ 2. On one capable host, use multiple tokens in `TG_BOT_TOKENS` when the user wants parallel Codex conversations.
76
+ 3. If no unique bot token has been provided for another host, bootstrap with `--no-start`.
77
+ 4. If group or topic mode is involved, read [references/telegram-setup.md](./references/telegram-setup.md) before continuing.
78
+ 5. Always explain to the user which values will be written into `.env` before starting bootstrap.
79
+ 6. After bootstrap, check `codex login status`. If authentication is missing, tell the user to run `codex login` or open `codex app` on that Mac.
80
+ 7. If the user only says "set it up" and has not given all required values, ask for the missing values directly instead of guessing Telegram IDs.
81
+ 8. If the user wants a fully usable setup, continue until first-message validation is done or clearly blocked by Telegram-side prerequisites.
78
82
 
79
83
  ## What To Ask The User
80
84
 
@@ -82,7 +86,7 @@ Use short direct questions when values are missing. The minimum useful checklist
82
86
 
83
87
  1. Where should the repo live on the target Mac?
84
88
  2. Which directory should be the bridge's default working directory?
85
- 3. What is the Telegram bot token?
89
+ 3. What are the Telegram bot tokens? Use one token for a single conversation lane or multiple comma-separated tokens for parallel lanes.
86
90
  4. What is the Telegram numeric user id allowed to control the bridge?
87
91
  5. Are we using private chat only, a group, or a specific topic?
88
92
  6. If group/topic mode: what are the `TG_ALLOWED_CHAT_ID` and `TG_ALLOWED_TOPIC_ID` values?
@@ -95,7 +99,7 @@ Run:
95
99
 
96
100
  ```bash
97
101
  python3 "$CODEX_HOME/skills/foxclaw/scripts/bootstrap_host.py" \
98
- --tg-bot-token "<BOT_TOKEN>" \
102
+ --tg-bot-tokens "<BOT_TOKEN_OR_COMMA_SEPARATED_TOKENS>" \
99
103
  --tg-allowed-user-id "<USER_ID>" \
100
104
  --default-cwd "<ABSOLUTE_CWD>" \
101
105
  --tg-allowed-chat-id "<CHAT_ID>" \
@@ -117,7 +121,7 @@ Run:
117
121
  python3 "$CODEX_HOME/skills/foxclaw/scripts/bootstrap_remote.py" \
118
122
  --ssh-host "<USER@HOST>" \
119
123
  --install-dir "<REMOTE_INSTALL_DIR>" \
120
- --tg-bot-token "<BOT_TOKEN>" \
124
+ --tg-bot-tokens "<BOT_TOKEN_OR_COMMA_SEPARATED_TOKENS>" \
121
125
  --tg-allowed-user-id "<USER_ID>" \
122
126
  --default-cwd "<REMOTE_ABSOLUTE_CWD>" \
123
127
  --tg-allowed-chat-id "<CHAT_ID>" \
@@ -146,8 +150,8 @@ Do this whenever the bridge has been started:
146
150
 
147
151
  1. Tell the user exactly where to send the first test message:
148
152
  - private chat mode: send `/help` to the bot in private chat
149
- - group mode: send `/help@botname` or `/help` in the configured default scope
150
- - topic mode: send `/help` inside the configured topic, then send one plain-language message
153
+ - group mode: send `/help@botname` in the configured scope; in multi-bot mode all group traffic must explicitly address the intended bot
154
+ - topic mode: send `/help@botname` inside the configured topic, then send one message mentioning the bot
151
155
  2. After the user sends that message, verify the bridge is listening:
152
156
  - `node dist/main.js status`
153
157
  - launchd or service log if there is no reply
@@ -188,6 +192,7 @@ Use this checklist when the user asks for standard closing actions, release wrap
188
192
  - For macOS launchd, use the launchd install/start path from this skill and verify with `node dist/main.js status`.
189
193
  - Verify the running service reports the expected FoxClaw version in `status`.
190
194
  - If `doctor` fails only because `DEFAULT_CWD` is missing, report that separately; do not treat it as evidence that the service update failed.
195
+ - `/update` now attempts to update globally npm/pnpm-managed Codex CLI installations and refuses to restart while any configured bot runtime is busy.
191
196
  7. Publish to npm when requested:
192
197
  - Prefer GitHub Actions trusted publishing via `.github/workflows/publish.yml`: bump and commit the package version, push `main`, then push a matching `v<version>` tag. The tag version must match `package.json`.
193
198
  - Treat `workflow_dispatch` only as a retry path from an existing matching release tag; do not manually run publishing from `main`.
@@ -18,7 +18,7 @@ Rules:
18
18
 
19
19
  ## Required Values
20
20
 
21
- - `TG_BOT_TOKEN`
21
+ - `TG_BOT_TOKENS` (one token, or comma-separated tokens for parallel Codex runtimes)
22
22
  - `TG_ALLOWED_USER_ID`
23
23
  - `DEFAULT_CWD`
24
24
 
@@ -29,12 +29,13 @@ Optional values:
29
29
 
30
30
  Behavior:
31
31
 
32
- - No `TG_ALLOWED_CHAT_ID`: private-chat mode
32
+ - No `TG_ALLOWED_CHAT_ID`: private-chat mode
33
33
  - `TG_ALLOWED_CHAT_ID` only: the whole group becomes the default scope
34
34
  - `TG_ALLOWED_CHAT_ID` + `TG_ALLOWED_TOPIC_ID`: that topic becomes the default scope
35
- - Private chat with `TG_ALLOWED_USER_ID` still works in every mode above
35
+ - Private chat with `TG_ALLOWED_USER_ID` still works in every mode above
36
+ - Multiple tokens start independent Codex runtimes in one FoxClaw service; group messages must mention or reply to the intended bot
36
37
 
37
- If multiple bots share one group, keep the same `TG_ALLOWED_CHAT_ID` and give each bot a different `TG_ALLOWED_TOPIC_ID`.
38
+ If multiple bots share one group, keep the same `TG_ALLOWED_CHAT_ID`; use explicit `@botname` mentions or replies. Separate topics are still useful for organization.
38
39
 
39
40
  ## Path Guidance
40
41
 
@@ -24,8 +24,9 @@ def parse_args() -> argparse.Namespace:
24
24
  parser.add_argument("--repo-url")
25
25
  parser.add_argument("--repo-ref")
26
26
  parser.add_argument("--install-dir")
27
- parser.add_argument("--default-cwd")
28
- parser.add_argument("--tg-bot-token")
27
+ parser.add_argument("--default-cwd")
28
+ parser.add_argument("--tg-bot-tokens")
29
+ parser.add_argument("--tg-bot-token")
29
30
  parser.add_argument("--tg-allowed-user-id")
30
31
  parser.add_argument("--tg-allowed-chat-id")
31
32
  parser.add_argument("--tg-allowed-topic-id")
@@ -41,8 +42,9 @@ def merged_config(args: argparse.Namespace) -> dict:
41
42
  "repo_url": REPO_URL_DEFAULT,
42
43
  "repo_ref": REPO_REF_DEFAULT,
43
44
  "install_dir": os.path.expanduser("~/foxclaw"),
44
- "default_cwd": os.path.expanduser("~/foxclaw"),
45
- "tg_bot_token": None,
45
+ "default_cwd": os.path.expanduser("~/foxclaw"),
46
+ "tg_bot_tokens": None,
47
+ "tg_bot_token": None,
46
48
  "tg_allowed_user_id": None,
47
49
  "tg_allowed_chat_id": None,
48
50
  "tg_allowed_topic_id": None,
@@ -58,8 +60,9 @@ def merged_config(args: argparse.Namespace) -> dict:
58
60
  "repo_url": args.repo_url,
59
61
  "repo_ref": args.repo_ref,
60
62
  "install_dir": args.install_dir,
61
- "default_cwd": args.default_cwd,
62
- "tg_bot_token": args.tg_bot_token,
63
+ "default_cwd": args.default_cwd,
64
+ "tg_bot_tokens": args.tg_bot_tokens,
65
+ "tg_bot_token": args.tg_bot_token,
63
66
  "tg_allowed_user_id": args.tg_allowed_user_id,
64
67
  "tg_allowed_chat_id": args.tg_allowed_chat_id,
65
68
  "tg_allowed_topic_id": args.tg_allowed_topic_id,
@@ -252,7 +255,7 @@ def ensure_repo(config: dict) -> None:
252
255
  def write_env_file(config: dict, codex_bin: str) -> str:
253
256
  env_path = os.path.join(config["install_dir"], ".env")
254
257
  lines = [
255
- f"TG_BOT_TOKEN={config['tg_bot_token']}",
258
+ f"TG_BOT_TOKENS={config['tg_bot_tokens']}",
256
259
  f"TG_ALLOWED_USER_ID={config['tg_allowed_user_id']}",
257
260
  f"TG_ALLOWED_CHAT_ID={config['tg_allowed_chat_id'] or ''}",
258
261
  f"TG_ALLOWED_TOPIC_ID={config['tg_allowed_topic_id'] or ''}",
@@ -311,7 +314,7 @@ def main() -> None:
311
314
 
312
315
  args = parse_args()
313
316
  config = merged_config(args)
314
- config["tg_bot_token"] = require(config["tg_bot_token"], "TG_BOT_TOKEN")
317
+ config["tg_bot_tokens"] = require(config["tg_bot_tokens"] or config["tg_bot_token"], "TG_BOT_TOKENS")
315
318
  config["tg_allowed_user_id"] = require(config["tg_allowed_user_id"], "TG_ALLOWED_USER_ID")
316
319
  os.makedirs(config["default_cwd"], exist_ok=True)
317
320
 
@@ -14,7 +14,8 @@ def parse_args() -> argparse.Namespace:
14
14
  parser.add_argument("--repo-ref", default="main")
15
15
  parser.add_argument("--install-dir", default="~/foxclaw")
16
16
  parser.add_argument("--default-cwd", required=True)
17
- parser.add_argument("--tg-bot-token", required=True)
17
+ parser.add_argument("--tg-bot-tokens")
18
+ parser.add_argument("--tg-bot-token")
18
19
  parser.add_argument("--tg-allowed-user-id", required=True)
19
20
  parser.add_argument("--tg-allowed-chat-id")
20
21
  parser.add_argument("--tg-allowed-topic-id")
@@ -25,8 +26,11 @@ def parse_args() -> argparse.Namespace:
25
26
  return parser.parse_args()
26
27
 
27
28
 
28
- def main() -> None:
29
- args = parse_args()
29
+ def main() -> None:
30
+ args = parse_args()
31
+ tg_bot_tokens = args.tg_bot_tokens or args.tg_bot_token
32
+ if not tg_bot_tokens:
33
+ raise SystemExit("--tg-bot-tokens is required")
30
34
  script_path = os.path.join(os.path.dirname(__file__), "bootstrap_host.py")
31
35
  with open(script_path, "rb") as handle:
32
36
  script_bytes = handle.read()
@@ -36,7 +40,7 @@ def main() -> None:
36
40
  "repo_ref": args.repo_ref,
37
41
  "install_dir": args.install_dir,
38
42
  "default_cwd": args.default_cwd,
39
- "tg_bot_token": args.tg_bot_token,
43
+ "tg_bot_tokens": tg_bot_tokens,
40
44
  "tg_allowed_user_id": args.tg_allowed_user_id,
41
45
  "tg_allowed_chat_id": args.tg_allowed_chat_id,
42
46
  "tg_allowed_topic_id": args.tg_allowed_topic_id,