@foxden-app/foxclaw 0.7.2 → 0.10.0
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/.env.example +14 -0
- package/CHANGELOG.md +129 -0
- package/README.md +22 -2
- package/README_EN.md +22 -2
- package/dist/antigravity/adapter.d.ts +17 -0
- package/dist/antigravity/adapter.js +256 -0
- package/dist/antigravity/auth.d.ts +121 -0
- package/dist/antigravity/auth.js +896 -0
- package/dist/antigravity/client.d.ts +32 -0
- package/dist/antigravity/client.js +238 -0
- package/dist/antigravity/controller.d.ts +96 -0
- package/dist/antigravity/controller.js +1922 -0
- package/dist/antigravity/conversations.d.ts +29 -0
- package/dist/antigravity/conversations.js +289 -0
- package/dist/antigravity/events.d.ts +101 -0
- package/dist/antigravity/events.js +114 -0
- package/dist/antigravity/runtime.d.ts +47 -0
- package/dist/antigravity/runtime.js +82 -0
- package/dist/auth/cross_node_sync.js +2 -2
- package/dist/channels/telegram/telegram_channel_adapter.d.ts +10 -3
- package/dist/channels/telegram/telegram_channel_adapter.js +2 -1
- package/dist/channels/weixin/weixin_channel_adapter.d.ts +5 -1
- package/dist/codex_app/adapter.d.ts +20 -0
- package/dist/codex_app/adapter.js +213 -0
- package/dist/codex_app/client.d.ts +2 -1
- package/dist/codex_app/client.js +47 -4
- package/dist/codex_app/force_takeover.d.ts +12 -0
- package/dist/codex_app/force_takeover.js +27 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.js +42 -0
- package/dist/controller/controller.d.ts +15 -5
- package/dist/controller/controller.js +252 -20
- package/dist/core/attachments.d.ts +11 -0
- package/dist/core/attachments.js +56 -0
- package/dist/core/engine_spi.d.ts +76 -0
- package/dist/core/engine_spi.js +1 -0
- package/dist/core/orchestrator.d.ts +101 -0
- package/dist/core/orchestrator.js +1404 -0
- package/dist/core/stream_preview.d.ts +29 -0
- package/dist/core/stream_preview.js +146 -0
- package/dist/core/turn_queue.d.ts +19 -0
- package/dist/core/turn_queue.js +41 -0
- package/dist/i18n.d.ts +36 -6
- package/dist/i18n.js +81 -6
- package/dist/main.js +190 -20
- package/dist/opencode/adapter.d.ts +11 -0
- package/dist/opencode/adapter.js +168 -0
- package/dist/store/database.d.ts +41 -0
- package/dist/store/database.js +242 -43
- package/dist/store/token_usage.d.ts +23 -0
- package/dist/store/token_usage.js +61 -0
- package/dist/telegram/api.js +34 -1
- package/dist/telegram/bot_home.d.ts +3 -0
- package/dist/telegram/bot_home.js +107 -0
- package/dist/telegram/gateway.d.ts +5 -0
- package/dist/telegram/gateway.js +21 -0
- package/dist/types.d.ts +1 -0
- package/dist/update.d.ts +6 -0
- package/dist/update.js +78 -6
- package/dist/voice/target.js +2 -1
- package/dist/voice/tts.js +4 -2
- package/docs/devlogs//346/212/200/346/234/257/351/200/232/350/256/257/02./347/273/237/344/270/200/351/200/232/351/201/223/350/260/203/345/272/246/345/231/250/344/270/216/345/217/257/346/217/222/346/213/224/345/274/225/346/223/216SPI/357/274/232FoxClaw/344/273/216/345/215/225/344/275/223/350/265/260/345/220/221/345/244/232Agent/345/272/225/345/272/247.md +192 -0
- package/docs/devlogs//347/254/254/344/270/200/344/272/272/347/247/260/347/211/210/02./347/257/207/344/272/214_/345/275/223/344/270/200/344/270/252/346/241/245/346/216/245/345/231/250/345/274/200/345/247/213/345/255/246/344/274/232/345/220/254/346/207/202/346/211/200/346/234/211Agent.md +45 -0
- package/docs/user-manual.md +11 -1
- package/docs/zh/2026-09-05-bot-home-migration.md +26 -0
- package/docs/zh/2026-09-05-reliability-review.md +53 -0
- package/docs/zh/troubleshooting.md +12 -0
- package/docs/zh/user-manual.md +13 -1
- package/package.json +2 -2
- package/scripts/force-takeover.py +118 -0
- package/scripts/force-takeover.test.py +122 -0
package/.env.example
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Required for new installs: one or more bot tokens for this host.
|
|
2
2
|
# Comma-separated bot tokens. By default, each bot gets an independent Codex runtime/auth selection.
|
|
3
|
+
# Homes use ~/.foxclaw/codex/telegram/@TelegramUsername/home (old numeric paths remain as links).
|
|
3
4
|
TG_BOT_TOKENS=<telegram_bot_token>
|
|
4
5
|
# Backward-compatible single-runtime setup.
|
|
5
6
|
# In TG_BOT_TOKENS mode, if this exact token is also present in TG_BOT_TOKENS,
|
|
@@ -71,6 +72,19 @@ CODEX_CLI_BIN=/absolute/path/to/codex
|
|
|
71
72
|
# OPENCODE_SERVER_STATE_PATH=
|
|
72
73
|
# OPENCODE_SERVER_LOG_PATH=
|
|
73
74
|
|
|
75
|
+
# Optional Google Antigravity (AGY) bridge. This starts independent Telegram bot(s)
|
|
76
|
+
# alongside existing Codex/OpenCode runtimes; never reuse a TG_BOT_TOKEN here.
|
|
77
|
+
# FoxClaw communicates with `agy` via stdio stream-json and supports multi-account
|
|
78
|
+
# rotation from ~/.gemini/antigravity-cli/antigravity-oauth-token_*.
|
|
79
|
+
# Multi-bot mode: comma-separated bot tokens. The default/first bot shares the terminal's
|
|
80
|
+
# ~/.gemini/antigravity-cli home, while additional bots get isolated ~/.foxclaw/antigravity/telegram/@bot/home.
|
|
81
|
+
# ANTIGRAVITY_BOT_TOKENS=<bot_token_1>,<bot_token_2>
|
|
82
|
+
# Single-bot backward compatibility:
|
|
83
|
+
# ANTIGRAVITY_BOT_TOKEN=<different_telegram_bot_token>
|
|
84
|
+
# ANTIGRAVITY_CLI_BIN=/absolute/path/to/agy
|
|
85
|
+
# ANTIGRAVITY_AUTH_DIR=~/.gemini/antigravity-cli
|
|
86
|
+
# ANTIGRAVITY_DEFAULT_MODEL=gemini-3.8-flash-high
|
|
87
|
+
|
|
74
88
|
# Optional cross-node Codex auth sync.
|
|
75
89
|
# Disabled by default. When enabled, FoxClaw uses Telegram Bot-to-Bot private
|
|
76
90
|
# messages to move encrypted auth bundles between nodes. Enable Bot-to-Bot
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,135 @@
|
|
|
2
2
|
|
|
3
3
|
All notable FoxClaw changes are listed here. Each release note is bilingual so GitHub Releases and the npm package are useful to both Chinese and English readers.
|
|
4
4
|
|
|
5
|
+
## 0.10.0 - 2026-09-27
|
|
6
|
+
|
|
7
|
+
### 中文
|
|
8
|
+
- **Antigravity 多 Bot 原生隔离与同机共存**:
|
|
9
|
+
- 支持通过 `ANTIGRAVITY_BOT_TOKENS` 启动多个独立 Telegram Bot,每个 Bot 自动拥有完全隔离的工作空间目录(`~/.gemini/antigravity-cli/telegram_bots/<bot-id>/`),独立记忆会话历史与进程状态,彻底杜绝多 Bot 互相踩踏与符号链接覆盖冲突。
|
|
10
|
+
- 默认主 Bot 保持与终端默认环境无缝衔接,终端与 Telegram 会话可相互无缝接手。
|
|
11
|
+
- **Telegram 执行小结折叠增强与长任务防穿透分片**:
|
|
12
|
+
- 任务执行小结加入精准耗时换算(秒/分/小时)与全维度 Token 消耗细分(总计、输入、输出、缓存,自动换算 K/M/G 单位)。
|
|
13
|
+
- 完整保留普通任务中全部十几个阶段小节(≤ 25 项调用完整保留无损呈现,展开可回溯全流程),超大规模任务(> 25 项)智能首尾收敛折叠。
|
|
14
|
+
- 新增 `combineSummaryAndResponse` 彻底杜绝超长任务分片切断折叠块导致工具调用裸露冠在最终答复上方的问题。
|
|
15
|
+
- **全后端 `/update` 一键联动自升级**:
|
|
16
|
+
- 无论处于 Codex 还是 Antigravity 运行模式,均可直接发送 `/update` 触发全链路升级。
|
|
17
|
+
- 自动联动升级 Antigravity CLI(`agy update`)、Codex CLI(`pnpm add -g @openai/codex`)以及 FoxClaw 自身,并自动平滑重载服务。
|
|
18
|
+
|
|
19
|
+
### English
|
|
20
|
+
- **Native Multi-Bot Isolation for Antigravity**:
|
|
21
|
+
- Supports multiple concurrent Telegram bot instances via `ANTIGRAVITY_BOT_TOKENS` with fully isolated runtime homes (`~/.gemini/antigravity-cli/telegram_bots/<bot-id>/`), eliminating directory collision and symlink overwrites while keeping the primary bot synchronized with the local terminal environment.
|
|
22
|
+
- **Enhanced Collapsible Execution Summary & Leak-Proof Chunking**:
|
|
23
|
+
- Execution summaries now include precise elapsed durations and four-dimensional token metrics (Total, Input, Output, Cached).
|
|
24
|
+
- Preserves all intermediate step summaries (up to 25 items) inside expandable folds for complete visibility, while gracefully bounding massive tool runs (> 25 items).
|
|
25
|
+
- New `combineSummaryAndResponse` eliminates blockquote tearing across Telegram chunk boundaries, preventing uncollapsed tool items from spilling onto the final answer.
|
|
26
|
+
- **Universal `/update` with Full Backend Synchronization**:
|
|
27
|
+
- `/update` is now available across all engine modes (Codex & Antigravity), seamlessly updating FoxClaw, the Antigravity CLI (`agy update`), and Codex CLI in one step.
|
|
28
|
+
|
|
29
|
+
## 0.9.0 - 2026-09-26
|
|
30
|
+
|
|
31
|
+
### 中文
|
|
32
|
+
- **统一多后端运行环境热切换**:
|
|
33
|
+
- 前端控制面板支持 `Codex`、`Google Antigravity (AGY)`、`OpenCode` 无缝平滑热切换,会话状态与配置自动记忆绑定。
|
|
34
|
+
- 支持多 Bot 隔离实例下各 Bot 独立选择和切换后端,互不干扰。
|
|
35
|
+
- **全端功能体验对齐原版 Codex 标准**:
|
|
36
|
+
- `/auth` 面板向原版 Codex 规范完全看齐,支持设备登录、授权检测、自动轮换与暂停/修复功能。
|
|
37
|
+
- `/threads` 会话面板对齐原版 Codex 功能按钮(会话切换、历史归档、工作目录配置等)。
|
|
38
|
+
- **交互面板智能收拢与过期自动清理**:
|
|
39
|
+
- 设置类与状态类面板展示超过 5 分钟自动静默清理,保持聊天面板干净整洁。
|
|
40
|
+
- 任务执行过程实时动态刷新(轮次、工具调用次数、耗时),完成后自动收拢折叠为小结引用块。
|
|
41
|
+
- **自更新体系全面支持 Antigravity CLI 同步升级**:
|
|
42
|
+
- `/update` 命令与集群自动更新联动同步执行 `agy update`,确保 FoxClaw、Codex 与 Antigravity CLI 全技术栈同步保持最新。
|
|
43
|
+
- **全局 Token 统计与即时观测(常态化持久化)**:
|
|
44
|
+
- `/status` 状态面板常态化显示全后端累积 Token 用量,格式化为人类友好单位(`总计 X.XXG (输入 X.XXG, 输出 XX.XXM, 缓存 X.XXG)`)。
|
|
45
|
+
- 底层基于 SQLite 持久化记录,无需每次耗时扫盘,日常实时累加,想看直接秒出。
|
|
46
|
+
|
|
47
|
+
### English
|
|
48
|
+
- **Unified Multi-Backend Hot-Switching**:
|
|
49
|
+
- Seamlessly switch between `Codex`, `Google Antigravity (AGY)`, and `OpenCode` from the Telegram UI with per-backend state and setting persistence.
|
|
50
|
+
- Multi-bot isolated setups support independent backend switching for each bot instance.
|
|
51
|
+
- **Full UX Alignment with Codex Conventions**:
|
|
52
|
+
- `/auth` and `/threads` menus redesigned to match the established Codex interaction standards (device login, candidate repair, session archival).
|
|
53
|
+
- **Auto-Cleanup & Expired Panel Pruning**:
|
|
54
|
+
- Transient setup and status panels are automatically removed after 5 minutes to keep chat history clean.
|
|
55
|
+
- Live execution displays dynamic progress counters and collapses into expandable summaries upon completion.
|
|
56
|
+
- **Synchronous Antigravity CLI Self-Updates**:
|
|
57
|
+
- `/update` seamlessly upgrades the `agy` CLI alongside FoxClaw and Codex.
|
|
58
|
+
- **Persistent Global Token Usage Metrics in `/status`**:
|
|
59
|
+
- Displays cumulative token metrics formatted in metric units (`Total X.XXG (Input X.XXG, Output XX.XXM, Cached X.XXG)`).
|
|
60
|
+
- Persisted continuously in SQLite for instant on-demand status reporting without filesystem latency.
|
|
61
|
+
|
|
62
|
+
## 0.8.0 - 2026-09-24
|
|
63
|
+
|
|
64
|
+
### 中文
|
|
65
|
+
- **统一通道调度器(UnifiedChannelOrchestrator)**:架构级重构,将与通道与引擎无关的通用交互全数下沉。集成 Turn 抢占与并发排队(`TurnQueue`)、700ms 智能节流流式编辑预览(`StreamPreviewController`)、工具调用动态折叠(`<blockquote expandable>`)、长消息 4000 字符安全分块(`chunkTelegramMessage`)、活跃态 Typing 维持,以及跨重启崩溃自愈恢复。
|
|
66
|
+
- **可插拔引擎服务提供者接口(IEngineAdapter SPI)**:定义面向多 Agent 的标准接口,涵盖执行、中断、状态汇报、交互键盘分发与模型参数控制,新增 Agent 接入成本降低 85%。
|
|
67
|
+
- **Google Antigravity (`agy`) 引擎深度接入与适配器**:
|
|
68
|
+
- 独立 Telegram 机器人接入,支持与既有 Codex / OpenCode 并行运行。
|
|
69
|
+
- **Token 预检与静默刷新(Token Preflight)**:执行前智能计算 OAuth Access Token 有效期,临期自动静默刷新,防止长任务因凭据过期中断。
|
|
70
|
+
- **Google 503 容量饱和指数退避重试**:遇到瞬时 `503 Service Unavailable` 或 `MODEL_CAPACITY_EXHAUSTED` 时,自动进行 1s、2s、4s 指数退避重试。
|
|
71
|
+
- **配额超限自动冷却轮换(Quota Cooldown Rotation)**:遭遇真实 ResourceExhausted 限额时,自动将当前账号标记冷却并轮换至候选池下一可用账号继续执行。
|
|
72
|
+
- **Boost 模式支持**:支持通过 `/boost` 开启或引导深度推理与多角度验证。
|
|
73
|
+
- **5h/7d 配额真实语义可观测性重构**:
|
|
74
|
+
- 彻底厘清 Google CloudCode PA 配额窗口语义,去除时间倒计时混淆,全面规范为 `100%|80%|account` 格式的百分比 Badge。
|
|
75
|
+
- 在 `/auth` 候选切换按钮、`/status` 面板与 `/setup` 概览中完整呈现 5 小时突发额度与 7 天长期额度。
|
|
76
|
+
- **OpenCode 适配器加固**:适配器流式 Delta 空安全增强,统一纳入调度器生命周期管理。
|
|
77
|
+
- **架构代码精简与全量测试**:`AntigravityBridgeCore` 样板代码精简 83%,全仓库 449 个自动化测试 100% 通过。
|
|
78
|
+
|
|
79
|
+
### English
|
|
80
|
+
- **UnifiedChannelOrchestrator**: Major architecture refactoring decoupling channel orchestration from underlying AI engines. Centralizes turn queueing (`TurnQueue`), 700ms throttled streaming previews (`StreamPreviewController`), collapsible tool blocks (`<blockquote expandable>`), safe 4000-char message chunking (`chunkTelegramMessage`), typing indicators, and crash recovery across daemon restarts.
|
|
81
|
+
- **Pluggable Engine SPI (`IEngineAdapter`)**: Standardized interface for multi-agent execution, cancellation, status summaries, inline keyboard actions, and model/effort switching, reducing new engine onboarding cost by 85%.
|
|
82
|
+
- **Google Antigravity (`agy`) Engine Integration & Adapter**:
|
|
83
|
+
- Independent Telegram bot support running concurrently alongside Codex and OpenCode.
|
|
84
|
+
- **Token Preflight & Silent Refresh**: Proactively calculates OAuth access token TTL before turn execution, silently refreshing when under 5 minutes to prevent expired token aborts during long runs.
|
|
85
|
+
- **Google 503 Capacity Exponential Backoff**: Automatically executes 1s, 2s, 4s exponential backoff retries upon encountering transient `503 Service Unavailable` or `MODEL_CAPACITY_EXHAUSTED`.
|
|
86
|
+
- **Quota Cooldown Rotation**: Automatically cools down and rotates to the next available Google OAuth candidate upon hitting true ResourceExhausted limits.
|
|
87
|
+
- **Boost Mode**: Supported `/boost` to guide or inject deep reasoning effort.
|
|
88
|
+
- **5h/7d Quota Metric & Observability Redesign**:
|
|
89
|
+
- Clarified Google CloudCode PA quota window semantics from ambiguous countdowns to explicit percentage badges: `100%|80%|account`.
|
|
90
|
+
- Fully exposed 5h burst and 7d long-term quota percentages across `/auth` candidate buttons, `/status`, and `/setup` panels.
|
|
91
|
+
- **OpenCode Adapter Hardening**: Stream delta null-safety hardening and unified engine lifecycle management.
|
|
92
|
+
- **Refactoring Quality & Test Coverage**: Eliminated 83% boilerplate in `AntigravityBridgeCore` with 100% pass rate across all 449 automated tests.
|
|
93
|
+
|
|
94
|
+
## 0.7.4 - 2026-09-24
|
|
95
|
+
|
|
96
|
+
### 中文
|
|
97
|
+
- 新增 Google Antigravity (`agy`) 引擎支持:通过 `ANTIGRAVITY_BOT_TOKEN` 接入独立的 Telegram 机器人,与既有 Codex / OpenCode 并行运行。
|
|
98
|
+
- 会话管理与既有 Thread 选定:支持 `/threads` 查询 Antigravity 历史会话(读取 `conversation_summaries.db`),支持 `/open <编号|ID>` 选定并绑定既有会话上下文,支持 `/new [目录]` 开启新会话。
|
|
99
|
+
- 交互式控制面板:支持 `/setup` 统一管理与查看当前模型、思考深度(Effort)、活跃账号池、绑定会话与工作目录,支持内联按钮原地切换与刷新。
|
|
100
|
+
- 富文本流式渲染与消息折叠:支持 Markdown 富文本转译、700ms 节流编辑流式输出、工具执行过程折叠收起(`<blockquote expandable>`)、长消息自动按 Telegram 限制分段(`chunkTelegramMessage`),以及执行期间定期维持 Typing 状态。
|
|
101
|
+
- 支持 Antigravity 多账号池管理:扫描 `~/.gemini/antigravity-cli/antigravity-oauth-token_*` 候选池,免网络即时从 `id_token` 解码 Google 账号邮箱,支持 `/auth` 面板切换;遇到 429 或配额超限(ResourceExhausted)时自动轮换下一个可用账号并重试。
|
|
102
|
+
- 支持 `/models` 切换 Gemini 3.8/3.7/3.1、Claude 等模型,支持 `/effort` 设置思考深度,支持 `/interrupt` 实时中断任务。
|
|
103
|
+
- `foxclaw doctor` 增加 Antigravity CLI、独立 Token 与凭据检测。
|
|
104
|
+
|
|
105
|
+
### English
|
|
106
|
+
- Added Google Antigravity (`agy`) engine support: run an independent Telegram bot alongside Codex/OpenCode via `ANTIGRAVITY_BOT_TOKEN`.
|
|
107
|
+
- Thread and Conversation Management: supported `/threads` to browse historical Antigravity conversations (via `conversation_summaries.db`), `/open <num|id>` to bind to an existing thread context, and `/new [dir]` to start a fresh conversation.
|
|
108
|
+
- Interactive Control Panel: supported `/setup` to inspect and configure the current model, reasoning effort, active account, bound thread, and directory via in-place inline keyboards.
|
|
109
|
+
- Rich Streaming and Tool Folding: supported safe rich Markdown rendering, 700ms throttled streaming edits, tool execution folding (`<blockquote expandable>`), automated multi-chunk message splitting (`chunkTelegramMessage`), and active Telegram typing loop.
|
|
110
|
+
- Added Antigravity multi-account candidate pool management: scans `~/.gemini/antigravity-cli/antigravity-oauth-token_*`, extracts Google account email directly from `id_token` JWT payload without network calls, provides `/auth` switching panel, and automatically rotates to the next available account upon 429 / ResourceExhausted errors.
|
|
111
|
+
- Supported `/models` to switch models (Gemini 3.8/3.7/3.1, Claude Sonnet, etc.), `/effort` for reasoning effort, and `/interrupt` to cancel running turns.
|
|
112
|
+
- Extended `foxclaw doctor` with Antigravity CLI availability, dedicated bot token, and OAuth token checks.
|
|
113
|
+
|
|
114
|
+
## 0.7.3 - 2026-09-05
|
|
115
|
+
|
|
116
|
+
### 中文
|
|
117
|
+
- 修复代理或网络不稳时 Telegram、Codex RPC 和 WebSocket 可能长期无响应的问题:请求有明确截止时间,活动会话断连、恢复失败和 writer 冲突都会给出可继续操作的错误,不重放结果未知的请求。
|
|
118
|
+
- 登录、auth add 和 auth repair 流程增加取消按钮;取消失败也释放本地流程,旧按钮和其他聊天不能误取消当前登录。
|
|
119
|
+
- 新增 `/cli` 与 `foxclaw resume`,可让终端连接桥的同一 Codex app-server;`/watch` 继续只读,外部 CLI 的消息可用原生线程队列安全排队。
|
|
120
|
+
- 新增 `/takeover --force <消息>`:可信 Telegram 用户二次确认后,可精确停止占用目标 thread 的本机交互式 CLI,验证锁释放并恢复原 thread 后再提交消息。进程、锁或 thread 身份变化时明确拒绝,不删除锁、不改写 session 文件。
|
|
121
|
+
- 多 bot Codex 数据目录默认使用真实 `@Telegram用户名`。迁移既有数字目录并保留兼容链接,共享终端的 bot 使用同名目录入口指向原 home。
|
|
122
|
+
- 并行读取用户名,断网重启沿用已有名称;媒体发送通过稳定身份记录保持正确路由,目录冲突明确报错。
|
|
123
|
+
- auth 问号、修复和删除操作会重新读取最新状态;安全同步文案区分“已发送”和“远端已导入”,状态更新未生效时不再报告虚假成功。
|
|
124
|
+
|
|
125
|
+
### English
|
|
126
|
+
- Bound Telegram, Codex RPC, and WebSocket waits so unstable proxy or network links fail explicitly instead of hanging. Active-session disconnects, recovery failures, and writer conflicts now include actionable recovery guidance without replaying ambiguous requests.
|
|
127
|
+
- Added cancel buttons to login, auth-add, and auth-repair flows. Local state is released even when cancellation fails, while stale or foreign-chat buttons cannot cancel the current login.
|
|
128
|
+
- Added `/cli` and `foxclaw resume` to connect a terminal to the bridge's Codex app-server. `/watch` remains read-only and external CLI prompts use Codex's native thread queue.
|
|
129
|
+
- Added `/takeover --force <message>` for a trusted Telegram user to confirm a precise local interactive-CLI handoff. FoxClaw revalidates process, lock, and thread identity, verifies lock release, resumes the original thread, and only then submits the prompt; it never deletes locks or edits session files.
|
|
130
|
+
- Name multi-bot Codex homes after verified Telegram usernames, migrating numeric directories with compatibility links and preserving shared terminal homes.
|
|
131
|
+
- Resolve usernames in parallel, reuse stored paths offline, preserve media routing through stable identity metadata, and reject directory conflicts.
|
|
132
|
+
- Auth repair/question-mark/delete actions now refresh current state first. Safe-sync wording distinguishes dispatch from confirmed remote import, and unapplied state changes no longer report false success.
|
|
133
|
+
|
|
5
134
|
## 0.7.2 - 2026-08-30
|
|
6
135
|
|
|
7
136
|
### 中文
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**狸爪:一个更适配编程需求的移动端 Codex 控制器。**
|
|
6
6
|
|
|
7
|
-
FoxClaw(狸爪)的目标很直接:让你用手机控制本机的 Codex 或
|
|
7
|
+
FoxClaw(狸爪)的目标很直接:让你用手机控制本机的 Codex、OpenCode 或 Antigravity (AGY),把移动端变成一个真正能用的 web coding 入口。Telegram 或微信负责交互,`codex app-server` / `opencode serve` / `agy` 负责本地执行;你发任务、看进度、批审批、切线程,Agent 在电脑上继续写代码。
|
|
8
8
|
|
|
9
9
|
这适合你离开办公桌去吃饭、在旅途中、在跑步机上,或者陪小孩逛公园的时候继续工作。人可以离开电脑,Codex 不必停;它会把关键进展、错误、审批请求和最终结果同步回手机。
|
|
10
10
|
|
|
@@ -30,7 +30,7 @@ FoxClaw(狸爪)的目标很直接:让你用手机控制本机的 Codex 或
|
|
|
30
30
|
|
|
31
31
|

|
|
32
32
|
|
|
33
|
-
手机端不是简单转发消息,而是给 Codex 的常用工作流做了 Telegram 面板:`/setup` 调模型、推理强度、Fast tier、权限和 Agent/Plan 模式;`/auth` 管理多个 Codex 登录候选,触发限制时自动轮转;`/threads`、`/watch`
|
|
33
|
+
手机端不是简单转发消息,而是给 Codex 的常用工作流做了 Telegram 面板:`/setup` 调模型、推理强度、Fast tier、权限和 Agent/Plan 模式;`/auth` 管理多个 Codex 登录候选,触发限制时自动轮转;`/threads`、`/watch` 和审批按钮用于切线程、观察进度和处理权限请求。终端 CLI 卡住且持有 thread 时,可信用户还可用 `/takeover --force <消息>` 核对 PID、目录并二次确认,将同一 thread 安全交给桥继续执行。
|
|
34
34
|
|
|
35
35
|
## 从这里开始
|
|
36
36
|
|
|
@@ -136,6 +136,26 @@ OpenCode Bot 保留与 Codex Bot 相同的主要操作习惯:直接发消息
|
|
|
136
136
|
|
|
137
137
|
OpenCode 自身的 Provider 登录仍在本机终端完成,例如 `opencode auth`。运行 `foxclaw doctor` 会在配置了 OpenCode Bot 时额外检查 OpenCode CLI 和 token 隔离。
|
|
138
138
|
|
|
139
|
+
### Antigravity (AGY) 独立 Bot
|
|
140
|
+
|
|
141
|
+
如果你使用 Google Antigravity,只需在 `@BotFather` 处再创建一个独立 Bot,并在 `~/.foxclaw/.env` 中配置:
|
|
142
|
+
|
|
143
|
+
```dotenv
|
|
144
|
+
ANTIGRAVITY_BOT_TOKEN=8308303236:AAFZ32Pus2wrzpRq7-Bp2JQMt7lsrt5kKpo
|
|
145
|
+
# 可选;默认为 PATH 中的 agy
|
|
146
|
+
ANTIGRAVITY_CLI_BIN=/home/wuya/.gemini/antigravity-cli/bin/agy
|
|
147
|
+
# 可选;默认 gemini-3.8-flash-high
|
|
148
|
+
ANTIGRAVITY_DEFAULT_MODEL=gemini-3.8-flash-high
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Antigravity Bot 保留与 Codex / OpenCode 对齐的高频操作习惯:
|
|
152
|
+
- **消息交互与流式思考**:直接向机器人发送文本或任务,支持 700ms 节流流式输出与思考过程实时显示。
|
|
153
|
+
- **消息折叠与工具进度**:执行过程中的工具调用与历史执行细节自动通过 Telegram 可折叠引用块(`<blockquote expandable>`)折叠收起,避免刷屏;输出超长文本自动按限制无损分段。
|
|
154
|
+
- **`/setup` 控制面板**:在同一消息内通过内联按钮原地切换模型、思考深度(Effort)、账号与会话。
|
|
155
|
+
- **`/threads` 与 `/open <编号|ID>`**:直接读取 `conversation_summaries.db` 列出近期历史会话并选定绑定,无缝继续上下文;发送 `/new [目录]` 开启全新会话。
|
|
156
|
+
- **`/auth` 多账号管理与自动轮换**:自动扫描 `antigravity-oauth-token_*` 候选池并即时解码 Google 邮箱,支持手动切换;遇到 429 或配额限制自动轮换下一个账号并重试任务。
|
|
157
|
+
- **`/models` 与 `/effort`**:切换 Gemini 3.8/3.7/3.1、Claude Sonnet、Claude Opus 等模型与推理深度。
|
|
158
|
+
|
|
139
159
|
## 多账号切换
|
|
140
160
|
|
|
141
161
|
FoxClaw 的一大特色是自动多账号切换。当一个账号触发用量限制时,FoxClaw 会自动切换到下一个可用账号继续工作。
|
package/README_EN.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**A mobile Codex controller built for real programming workflows.**
|
|
6
6
|
|
|
7
|
-
FoxClaw turns your phone into a practical web coding cockpit for local Codex or
|
|
7
|
+
FoxClaw turns your phone into a practical web coding cockpit for local Codex, OpenCode, or Antigravity (AGY). Telegram or Weixin handles the chat interface, `codex app-server` / `opencode serve` / `agy` handles local execution, and you can send tasks, inspect progress, approve actions, switch sessions, and keep working without opening a laptop.
|
|
8
8
|
|
|
9
9
|
It is built for the moments when you leave your desk for lunch, commute, travel, use a treadmill, or take the kids to the park. You can step away from the keyboard while Codex keeps coding and sends progress, errors, approval requests, and final results back to your phone.
|
|
10
10
|
|
|
@@ -30,7 +30,7 @@ No public server required. FoxClaw runs on your own computer, talks to `codex ap
|
|
|
30
30
|
|
|
31
31
|

|
|
32
32
|
|
|
33
|
-
FoxClaw is more than message forwarding. It provides Telegram panels for Codex workflows: `/setup` adjusts model, reasoning, Fast tier, access, and Agent/Plan mode; `/auth` manages multiple Codex auth candidates and rotates them on usage limits; `/threads`, `/watch`, and approval buttons handle thread switching, progress monitoring, and permission requests.
|
|
33
|
+
FoxClaw is more than message forwarding. It provides Telegram panels for Codex workflows: `/setup` adjusts model, reasoning, Fast tier, access, and Agent/Plan mode; `/auth` manages multiple Codex auth candidates and rotates them on usage limits; `/threads`, `/watch`, and approval buttons handle thread switching, progress monitoring, and permission requests. If a local interactive Codex CLI is stuck while holding a thread, the trusted Telegram user can use `/takeover --force <message>`, verify its PID and working directory, and explicitly confirm a safe handoff to the bridge.
|
|
34
34
|
|
|
35
35
|
## Start Here
|
|
36
36
|
|
|
@@ -136,6 +136,26 @@ The OpenCode bot preserves the main Codex-bot habits: plain messages and attachm
|
|
|
136
136
|
|
|
137
137
|
Provider authentication remains an OpenCode terminal operation, such as `opencode auth`. When an OpenCode bot is configured, `foxclaw doctor` also checks the OpenCode CLI and token isolation.
|
|
138
138
|
|
|
139
|
+
### Antigravity (AGY) Dedicated Bot
|
|
140
|
+
|
|
141
|
+
If you use Google Antigravity, create an independent bot with `@BotFather` and configure:
|
|
142
|
+
|
|
143
|
+
```dotenv
|
|
144
|
+
ANTIGRAVITY_BOT_TOKEN=8308303236:AAFZ32Pus2wrzpRq7-Bp2JQMt7lsrt5kKpo
|
|
145
|
+
# Optional; defaults to PATH lookup
|
|
146
|
+
ANTIGRAVITY_CLI_BIN=/home/wuya/.gemini/antigravity-cli/bin/agy
|
|
147
|
+
# Optional; default gemini-3.8-flash-high
|
|
148
|
+
ANTIGRAVITY_DEFAULT_MODEL=gemini-3.8-flash-high
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The Antigravity Bot preserves the same intuitive workflows as Codex and OpenCode:
|
|
152
|
+
- **Interactive Messaging & Streaming**: Send prompts directly to the bot, with 700ms throttled streaming output and live reasoning progress.
|
|
153
|
+
- **Message Folding & Progress Details**: Tool calls and execution traces are neatly folded using Telegram expandable blockquotes (`<blockquote expandable>`), keeping conversations clean; long responses are automatically split without truncation (`chunkTelegramMessage`).
|
|
154
|
+
- **`/setup` Control Panel**: In-place inline keyboard to view and switch models, reasoning effort, active accounts, and bound conversation.
|
|
155
|
+
- **`/threads` & `/open <number|id>`**: Directly reads `conversation_summaries.db` to list recent sessions and bind to existing thread contexts; use `/new [dir]` to start a fresh conversation.
|
|
156
|
+
- **`/auth` Multi-Account Management**: Automatically scans `antigravity-oauth-token_*` candidate tokens and decodes Google emails from JWT payloads; automatically rotates to the next account on 429 or quota limit errors.
|
|
157
|
+
- **`/models` & `/effort`**: Switch between Gemini 3.8/3.7/3.1, Claude Sonnet/Opus, and adjust reasoning effort.
|
|
158
|
+
|
|
139
159
|
## Multi-Account Rotation
|
|
140
160
|
|
|
141
161
|
A key FoxClaw feature is automatic multi-account switching. When one account's usage limit is triggered, FoxClaw automatically switches to the next available account and continues working.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { IEngineAdapter, EngineModel, EngineTurnRequest, EngineTurnExecution, EngineTurnErrorContext } from '../core/engine_spi.js';
|
|
2
|
+
import { type AntigravityAppClient } from './client.js';
|
|
3
|
+
import type { AntigravityAuthManager } from './auth.js';
|
|
4
|
+
import type { Logger } from '../logger.js';
|
|
5
|
+
export declare class AntigravityEngineAdapter implements IEngineAdapter {
|
|
6
|
+
readonly id = "antigravity";
|
|
7
|
+
readonly name = "Google Antigravity (AGY)";
|
|
8
|
+
private readonly client;
|
|
9
|
+
private readonly defaultModel;
|
|
10
|
+
private readonly auth;
|
|
11
|
+
private readonly logger;
|
|
12
|
+
constructor(client: AntigravityAppClient, defaultModel?: string, auth?: AntigravityAuthManager, logger?: Logger);
|
|
13
|
+
listModels(): Promise<EngineModel[]>;
|
|
14
|
+
executeTurn(request: EngineTurnRequest): EngineTurnExecution;
|
|
15
|
+
preflightTurn(_request: EngineTurnRequest): Promise<void>;
|
|
16
|
+
handleTurnError(ctx: EngineTurnErrorContext): Promise<boolean>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { normalizeAgyModelId } from './client.js';
|
|
4
|
+
import { isCapacityOrUnavailableError, isVerificationError, isQuotaOrAuthError, } from './events.js';
|
|
5
|
+
import { buildAttachmentPrompt } from '../telegram/media.js';
|
|
6
|
+
function extractToolSummary(name, parameters) {
|
|
7
|
+
if (!parameters)
|
|
8
|
+
return undefined;
|
|
9
|
+
if (typeof parameters.CommandLine === 'string') {
|
|
10
|
+
return `$ ${parameters.CommandLine.slice(0, 70)}`;
|
|
11
|
+
}
|
|
12
|
+
if (typeof parameters.TargetFile === 'string') {
|
|
13
|
+
return path.basename(parameters.TargetFile);
|
|
14
|
+
}
|
|
15
|
+
if (typeof parameters.AbsolutePath === 'string') {
|
|
16
|
+
return path.basename(parameters.AbsolutePath);
|
|
17
|
+
}
|
|
18
|
+
if (typeof parameters.path === 'string') {
|
|
19
|
+
return path.basename(parameters.path);
|
|
20
|
+
}
|
|
21
|
+
if (typeof parameters.query === 'string') {
|
|
22
|
+
return parameters.query.slice(0, 60);
|
|
23
|
+
}
|
|
24
|
+
if (typeof parameters.Url === 'string') {
|
|
25
|
+
return parameters.Url.slice(0, 60);
|
|
26
|
+
}
|
|
27
|
+
if (typeof parameters.toolSummary === 'string') {
|
|
28
|
+
return parameters.toolSummary.slice(0, 60);
|
|
29
|
+
}
|
|
30
|
+
if (typeof parameters.toolAction === 'string') {
|
|
31
|
+
return parameters.toolAction.slice(0, 60);
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
export class AntigravityEngineAdapter {
|
|
36
|
+
id = 'antigravity';
|
|
37
|
+
name = 'Google Antigravity (AGY)';
|
|
38
|
+
client;
|
|
39
|
+
defaultModel;
|
|
40
|
+
auth;
|
|
41
|
+
logger;
|
|
42
|
+
constructor(client, defaultModel = 'gemini-3.8-flash', auth, logger) {
|
|
43
|
+
this.client = client;
|
|
44
|
+
this.defaultModel = defaultModel;
|
|
45
|
+
this.auth = auth;
|
|
46
|
+
this.logger = logger;
|
|
47
|
+
}
|
|
48
|
+
async listModels() {
|
|
49
|
+
const rawModels = await this.client.listModels();
|
|
50
|
+
const seen = new Set();
|
|
51
|
+
const result = [];
|
|
52
|
+
for (const raw of rawModels) {
|
|
53
|
+
const m = normalizeAgyModelId(raw);
|
|
54
|
+
if (seen.has(m))
|
|
55
|
+
continue;
|
|
56
|
+
seen.add(m);
|
|
57
|
+
let displayName = m;
|
|
58
|
+
if (m === 'gemini-3.8-flash')
|
|
59
|
+
displayName = 'Gemini 3.8 Flash';
|
|
60
|
+
else if (m === 'gemini-3.7-flash')
|
|
61
|
+
displayName = 'Gemini 3.7 Flash';
|
|
62
|
+
else if (m === 'gemini-3.6-flash')
|
|
63
|
+
displayName = 'Gemini 3.6 Flash';
|
|
64
|
+
else if (m === 'gemini-3.1-pro')
|
|
65
|
+
displayName = 'Gemini 3.1 Pro';
|
|
66
|
+
else if (m === 'claude-sonnet-4-6')
|
|
67
|
+
displayName = 'Claude Sonnet 4.6';
|
|
68
|
+
else if (m === 'claude-opus-4-6')
|
|
69
|
+
displayName = 'Claude Opus 4.6';
|
|
70
|
+
else if (m === 'gpt-oss-120b')
|
|
71
|
+
displayName = 'GPT-OSS 120B';
|
|
72
|
+
else
|
|
73
|
+
displayName = m.replace(/^gemini-/, 'Gemini ').replace(/^claude-/, 'Claude ');
|
|
74
|
+
result.push({
|
|
75
|
+
id: m,
|
|
76
|
+
name: displayName,
|
|
77
|
+
description: m.includes('flash')
|
|
78
|
+
? 'Fast, high-throughput model'
|
|
79
|
+
: m.includes('pro')
|
|
80
|
+
? 'Deep reasoning & complex tasks'
|
|
81
|
+
: undefined,
|
|
82
|
+
isDefault: m === this.defaultModel || (this.defaultModel.startsWith(m) && !m.includes('-')),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
executeTurn(request) {
|
|
88
|
+
const emitter = new EventEmitter();
|
|
89
|
+
let prompt = request.prompt;
|
|
90
|
+
if (request.stagedAttachments && request.stagedAttachments.length > 0 && !request.prompt.includes('Telegram attachments:')) {
|
|
91
|
+
prompt = buildAttachmentPrompt(request.prompt, request.stagedAttachments);
|
|
92
|
+
}
|
|
93
|
+
const targetModel = (!request.model || request.model === 'default') ? this.defaultModel : request.model;
|
|
94
|
+
const agTurn = this.client.executeTurn({
|
|
95
|
+
prompt,
|
|
96
|
+
conversationId: request.threadId,
|
|
97
|
+
cwd: request.cwd,
|
|
98
|
+
model: targetModel,
|
|
99
|
+
effort: request.effort ?? null,
|
|
100
|
+
});
|
|
101
|
+
agTurn.on('event', (ev) => {
|
|
102
|
+
switch (ev.kind) {
|
|
103
|
+
case 'text':
|
|
104
|
+
emitter.emit('delta', ev.delta);
|
|
105
|
+
break;
|
|
106
|
+
case 'tool':
|
|
107
|
+
emitter.emit('tool', {
|
|
108
|
+
name: ev.toolName,
|
|
109
|
+
args: ev.parameters,
|
|
110
|
+
output: ev.output,
|
|
111
|
+
status: ev.state === 'ACTIVE' ? 'running' : ev.state === 'ERROR' ? 'failed' : 'completed',
|
|
112
|
+
stepIndex: ev.stepIndex,
|
|
113
|
+
summary: extractToolSummary(ev.toolName, ev.parameters),
|
|
114
|
+
});
|
|
115
|
+
break;
|
|
116
|
+
case 'result': {
|
|
117
|
+
const res = {
|
|
118
|
+
kind: 'result',
|
|
119
|
+
status: ev.status === 'SUCCESS' ? 'SUCCESS' : 'ERROR',
|
|
120
|
+
response: ev.response || ev.error || (ev.status === 'ERROR' ? 'Antigravity execution failed' : ''),
|
|
121
|
+
error: ev.error || (ev.status === 'ERROR' ? 'Antigravity execution failed' : undefined),
|
|
122
|
+
conversationId: ev.conversationId,
|
|
123
|
+
usage: ev.usage
|
|
124
|
+
? {
|
|
125
|
+
inputTokens: ev.usage.input_tokens,
|
|
126
|
+
outputTokens: ev.usage.output_tokens,
|
|
127
|
+
cachedTokens: ev.usage.cache_read_tokens,
|
|
128
|
+
totalTokens: ev.usage.total_tokens,
|
|
129
|
+
}
|
|
130
|
+
: undefined,
|
|
131
|
+
};
|
|
132
|
+
emitter.emit('result', res);
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
agTurn.on('error', (err) => {
|
|
138
|
+
emitter.emit('error', err);
|
|
139
|
+
});
|
|
140
|
+
agTurn.on('exit', (code) => {
|
|
141
|
+
emitter.emit('exit', code);
|
|
142
|
+
});
|
|
143
|
+
return {
|
|
144
|
+
turnId: agTurn.conversationId ?? undefined,
|
|
145
|
+
cancel: () => agTurn.cancel(),
|
|
146
|
+
waitForResult: async () => {
|
|
147
|
+
const ev = await agTurn.waitForResult();
|
|
148
|
+
if (!ev || ev.kind !== 'result')
|
|
149
|
+
return null;
|
|
150
|
+
return {
|
|
151
|
+
kind: 'result',
|
|
152
|
+
status: ev.status === 'SUCCESS' ? 'SUCCESS' : 'ERROR',
|
|
153
|
+
response: ev.response || ev.error || (ev.status === 'ERROR' ? 'Antigravity execution failed' : ''),
|
|
154
|
+
error: ev.error || (ev.status === 'ERROR' ? 'Antigravity execution failed' : undefined),
|
|
155
|
+
conversationId: ev.conversationId,
|
|
156
|
+
usage: ev.usage
|
|
157
|
+
? {
|
|
158
|
+
inputTokens: ev.usage.input_tokens,
|
|
159
|
+
outputTokens: ev.usage.output_tokens,
|
|
160
|
+
cachedTokens: ev.usage.cache_read_tokens,
|
|
161
|
+
totalTokens: ev.usage.total_tokens,
|
|
162
|
+
}
|
|
163
|
+
: undefined,
|
|
164
|
+
};
|
|
165
|
+
},
|
|
166
|
+
on: (event, listener) => {
|
|
167
|
+
emitter.on(event, listener);
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
async preflightTurn(_request) {
|
|
172
|
+
void _request;
|
|
173
|
+
if (this.auth) {
|
|
174
|
+
try {
|
|
175
|
+
await this.auth.ensureActiveTokenFresh(300);
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
this.logger?.warn('antigravity.preflight_refresh_failed', { error: String(err) });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async handleTurnError(ctx) {
|
|
183
|
+
const isVerification = isVerificationError(ctx.error);
|
|
184
|
+
const isQuota = !isVerification && isQuotaOrAuthError(ctx.error);
|
|
185
|
+
if (isVerification && this.auth && ctx.retryCount < 3) {
|
|
186
|
+
try {
|
|
187
|
+
const active = await this.auth.getActiveAccount();
|
|
188
|
+
const failingName = active?.name || 'unknown';
|
|
189
|
+
const failingEmail = active?.email || failingName;
|
|
190
|
+
this.auth.pauseAccount(failingName);
|
|
191
|
+
this.auth.markCooldown(failingName, 60 * 60 * 1000);
|
|
192
|
+
if (active?.email) {
|
|
193
|
+
this.auth.pauseAccount(active.email);
|
|
194
|
+
this.auth.markCooldown(active.email, 60 * 60 * 1000);
|
|
195
|
+
}
|
|
196
|
+
const rotated = await this.auth.rotateNextCandidate(failingName);
|
|
197
|
+
const nextAccountName = rotated.account.email || rotated.account.name;
|
|
198
|
+
await ctx.sendMessage(ctx.request.locale === 'zh'
|
|
199
|
+
? `🛡️ **检测到 Google 安全验证风控 (Verification Required)**\n\n` +
|
|
200
|
+
`• **受限账号**: \`${failingEmail}\` (已自动暂停并冷却)\n` +
|
|
201
|
+
`• **故障恢复**: 已无感切换至备用账号 \`${nextAccountName}\`,正在继续执行您的任务…\n\n` +
|
|
202
|
+
`💡 **如何人工解除该账号风控**:\n` +
|
|
203
|
+
`1. 可在终端/CLI 执行 \`agy -p "hi"\`,在弹出的浏览器完成人机验证;\n` +
|
|
204
|
+
`2. 或在 Telegram 发送 \`/login\` 重新授权该账号后即可恢复。`
|
|
205
|
+
: `🛡️ **Google Security Verification Triggered (Verification Required)**\n\n` +
|
|
206
|
+
`• **Affected**: \`${failingEmail}\` (paused & cooling)\n` +
|
|
207
|
+
`• **Failover**: Switched to \`${nextAccountName}\`, continuing your task…\n\n` +
|
|
208
|
+
`💡 **How to resolve**: Complete verification via \`agy -p "hi"\` in browser or run \`/login\` in Telegram.`);
|
|
209
|
+
await ctx.retryTurn(ctx.retryCount + 1);
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
this.logger?.warn('antigravity.verification_rotation_failed', {
|
|
214
|
+
error: err instanceof Error ? err.message : String(err),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (isQuota && this.auth && ctx.retryCount < 3) {
|
|
219
|
+
try {
|
|
220
|
+
const active = await this.auth.getActiveAccount();
|
|
221
|
+
if (active) {
|
|
222
|
+
this.auth.markCooldown(active.name, 30 * 60 * 1000);
|
|
223
|
+
if (active.email)
|
|
224
|
+
this.auth.markCooldown(active.email, 30 * 60 * 1000);
|
|
225
|
+
}
|
|
226
|
+
const rotated = await this.auth.rotateNextCandidate(active?.name);
|
|
227
|
+
await ctx.sendMessage(ctx.request.locale === 'zh'
|
|
228
|
+
? `⚠️ 检测到当前账号配额超限或凭据失效,已标记进入 30 分钟冷却,自动切换到账号 \`${rotated.account.email || rotated.account.name}\` 并重试…`
|
|
229
|
+
: `⚠️ Quota or auth issue detected. Marked account into 30m cooldown, switched to \`${rotated.account.email || rotated.account.name}\` and retrying…`);
|
|
230
|
+
await ctx.retryTurn(ctx.retryCount + 1);
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
catch (rotateErr) {
|
|
234
|
+
this.logger?.warn('antigravity.rotation_failed', {
|
|
235
|
+
error: rotateErr instanceof Error ? rotateErr.message : String(rotateErr),
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const isCapacity = isCapacityOrUnavailableError(ctx.error);
|
|
240
|
+
if (isCapacity && ctx.retryCount < 2) {
|
|
241
|
+
await ctx.sendMessage(ctx.request.locale === 'zh'
|
|
242
|
+
? `⏳ Google 模型服务临时繁忙 (503 Unavailable),等待 2 秒后自动重试 (第 ${ctx.retryCount + 1}/2 次)…`
|
|
243
|
+
: `⏳ Google model temporarily unavailable (503), retrying in 2s (attempt ${ctx.retryCount + 1}/2)…`);
|
|
244
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
245
|
+
await ctx.retryTurn(ctx.retryCount + 1);
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
if (isCapacity) {
|
|
249
|
+
await ctx.sendMessage(ctx.request.locale === 'zh'
|
|
250
|
+
? `⚠️ **Google 模型容量繁忙 (503 UNAVAILABLE)**\n\nGoogle 服务端当前模型 (\`${ctx.request.model}\`) 暂时无可用容量。\n\n💡 **建议方案**:\n• 稍等 1 分钟后重试\n• 发送 /setup 或 /models 切换为 \`gemini-3.1-pro-high\` 继续`
|
|
251
|
+
: `⚠️ **Model Capacity Unavailable (503)**\n\nGoogle servers have no capacity for \`${ctx.request.model}\` right now.\n\n💡 Tip: Wait 1 minute or switch model via /setup or /models.`);
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|