@foxden-app/foxclaw 0.3.17 → 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.
Files changed (42) hide show
  1. package/.env.example +5 -2
  2. package/README.md +14 -4
  3. package/README_EN.md +14 -4
  4. package/dist/auth/mirror.d.ts +25 -0
  5. package/dist/auth/mirror.js +199 -0
  6. package/dist/codex_app/client.d.ts +2 -1
  7. package/dist/codex_app/client.js +10 -2
  8. package/dist/codex_app/local_usage.d.ts +7 -6
  9. package/dist/codex_app/local_usage.js +55 -66
  10. package/dist/config.d.ts +7 -0
  11. package/dist/config.js +18 -1
  12. package/dist/controller/controller.d.ts +14 -2
  13. package/dist/controller/controller.js +110 -34
  14. package/dist/core/bridge_scope.d.ts +5 -2
  15. package/dist/core/bridge_scope.js +7 -3
  16. package/dist/i18n.d.ts +4 -4
  17. package/dist/i18n.js +4 -4
  18. package/dist/main.js +144 -9
  19. package/dist/store/database.d.ts +4 -2
  20. package/dist/store/database.js +42 -6
  21. package/dist/telegram/addressing.d.ts +1 -0
  22. package/dist/telegram/addressing.js +3 -0
  23. package/dist/telegram/gateway.d.ts +4 -1
  24. package/dist/telegram/gateway.js +23 -5
  25. package/dist/types.d.ts +7 -0
  26. package/dist/update.d.ts +5 -0
  27. package/dist/update.js +93 -5
  28. package/docs/agent-assisted-install.md +4 -4
  29. package/docs/install-for-beginners.md +3 -3
  30. package/docs/troubleshooting.md +1 -1
  31. package/docs/user-manual.md +11 -11
  32. package/docs/zh/agent-assisted-install.md +4 -4
  33. package/docs/zh/foxclaw-skill.md +1 -1
  34. package/docs/zh/install-for-beginners.md +3 -3
  35. package/docs/zh/troubleshooting.md +1 -1
  36. package/docs/zh/user-manual.md +11 -11
  37. package/package.json +1 -1
  38. package/skills/foxclaw/SKILL.md +26 -19
  39. package/skills/foxclaw/references/telegram-setup.md +5 -4
  40. package/skills/foxclaw/scripts/bootstrap_host.py +11 -8
  41. package/skills/foxclaw/scripts/bootstrap_remote.py +8 -4
  42. package/skills/npm-publish/SKILL.md +3 -0
@@ -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,12 +192,15 @@ 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`.
198
+ - Treat `workflow_dispatch` only as a retry path from an existing matching release tag; do not manually run publishing from `main`.
193
199
  - Configure npmjs.com trusted publishing for `foxden-app/foxclaw` and workflow `publish.yml`; do not store npm tokens if OIDC trusted publishing is available.
194
200
  - Temporary fallback: store an npm automation/bypass-2FA token as the GitHub Actions secret `NPM_TOKEN`. Never print the token or commit it.
195
201
  - Check `npm whoami` and `npm view @foxden-app/foxclaw version`.
196
202
  - If the target version is already published, bump with `npm version patch --no-git-tag-version` before validation and commit.
203
+ - If a workflow fails before `npm publish`, inspect that failed GitHub Actions step before attributing it to npm trusted publishing.
197
204
  - Manual fallback only: run `BROWSER=true npm publish` in a TTY.
198
205
  - If npm returns `ENEEDAUTH`, report that npm publish is blocked by registry login and leave the package unreported as published.
199
206
  - Never print npm tokens or `.npmrc` auth values.
@@ -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,
@@ -73,6 +73,8 @@ Use this path when the repo has `.github/workflows/publish.yml` and npmjs.com ha
73
73
  npm view <package-name> version
74
74
  ```
75
75
 
76
+ If the workflow provides `workflow_dispatch` as a recovery path, dispatch it only from an existing release tag whose version matches `package.json`. Do not dispatch publishing from a branch containing an unpublished version unless the workflow explicitly resolves and validates a release tag.
77
+
76
78
  Do not store or print npm tokens when trusted publishing is available. If trusted publishing is not configured on npmjs.com, the workflow can use a GitHub Actions secret named `NPM_TOKEN` as a temporary fallback. Store only automation/bypass-2FA tokens there, never paste tokens in chat or commit them.
77
79
 
78
80
  ### Manual Publish
@@ -124,4 +126,5 @@ Press ENTER to open in the browser...
124
126
  - If `xdg-open` fails because the environment has no browser, rerun with `BROWSER=true npm publish`.
125
127
  - If the auth link expires or the publish process exits, rerun `BROWSER=true npm publish` to generate a new link.
126
128
  - If the user provides a classic authenticator OTP instead of using the web link, publish can be retried with `npm publish --otp <code>`, but prefer web auth when the user asks for a clickable confirmation link.
129
+ - If a GitHub Actions release fails before the `npm publish` step, such as during checkout or action download authentication, do not diagnose it as a trusted publishing rejection. Inspect the failed step and GitHub Actions status first.
127
130
  - Never print npm tokens or `.npmrc` auth values.