@foxden-app/foxclaw 0.4.7 → 0.4.8

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/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
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.4.8 - 2026-06-02
6
+
7
+ ### 中文
8
+ - `/auth` 面板的“刷新全部”改为两段式确认:首次点击只显示 refresh token 轮换风险,只有点击“接受风险并刷新”才会真正执行。
9
+ - `/auth refresh all` 同样只显示风险确认;需要 `/auth refresh all confirm` 或确认按钮才会开始刷新。
10
+ - 本地 CLI 测试会过滤 proxychains 初始化噪音,避免发布前校验被环境 stdout 污染误伤。
11
+
12
+ ### English
13
+ - Changed the `/auth` panel's Refresh all action to a two-step confirmation: the first tap only shows refresh-token rotation risk, and the refresh starts only after accepting the risk.
14
+ - `/auth refresh all` now shows the same risk confirmation; `/auth refresh all confirm` or the confirmation button is required to run it.
15
+ - Local CLI tests now filter proxychains initialization noise so release checks are not tripped by environment stdout injection.
16
+
5
17
  ## 0.4.7 - 2026-06-02
6
18
 
7
19
  ### 中文
package/README.md CHANGED
@@ -258,7 +258,7 @@ FoxClaw 会把 `codex app-server` 作为 detached 子进程启动,记录其 pi
258
258
  - `/status`、`/account`、`/quota`、`/update`
259
259
  - `/quota_nudge <credits|usage_limit> confirm`
260
260
  - `/login_device`、`/login_cancel [id]`、`/logout confirm`
261
- - `/auth [list|use <n>|enable <n>|disable <n>|reload|refresh all|add <name>]`
261
+ - `/auth [list|use <n>|enable <n>|disable <n>|reload|refresh all [confirm]|add <name>]`
262
262
  - `/threads [query]`、`/threads archived`、`/open <n>`
263
263
  - `/goal [objective|pause|resume|done|budget <tokens|off>|clear confirm]`
264
264
  - `/history [limit]`、`/files <query>`、`/remote`
package/README_EN.md CHANGED
@@ -258,7 +258,7 @@ No static Codex app-server port is required in normal installs.
258
258
  - `/status`, `/account`, `/quota`, `/update`
259
259
  - `/quota_nudge <credits|usage_limit> confirm`
260
260
  - `/login_device`, `/login_cancel [id]`, `/logout confirm`
261
- - `/auth [list|use <n>|enable <n>|disable <n>|reload|refresh all|add <name>]`
261
+ - `/auth [list|use <n>|enable <n>|disable <n>|reload|refresh all [confirm]|add <name>]`
262
262
  - `/threads [query]`, `/threads archived`, `/open <n>`
263
263
  - `/goal [objective|pause|resume|done|budget <tokens|off>|clear confirm]`
264
264
  - `/history [limit]`, `/files <query>`, `/remote`
@@ -1010,7 +1010,7 @@ export class BridgeSessionCore {
1010
1010
  await this.handleAuthToggleCallback(event, authToggleMatch[1], Number.parseInt(authToggleMatch[2], 10), locale);
1011
1011
  return;
1012
1012
  }
1013
- const authActionMatch = /^auth:([a-f0-9]+):(login_device|reload|refresh_all)$/.exec(event.data);
1013
+ const authActionMatch = /^auth:([a-f0-9]+):(login_device|reload|refresh_all_confirm|refresh_all_cancel|refresh_all)$/.exec(event.data);
1014
1014
  if (authActionMatch) {
1015
1015
  await this.handleAuthPanelActionCallback(event, authActionMatch[1], authActionMatch[2], locale);
1016
1016
  return;
@@ -3871,14 +3871,14 @@ export class BridgeSessionCore {
3871
3871
  }
3872
3872
  if (action === 'refresh') {
3873
3873
  if (args[1]?.toLowerCase() === 'all') {
3874
- await this.handleAuthRefreshAllCommand(scopeId, locale);
3874
+ await this.handleAuthRefreshAllCommand(scopeId, locale, args[2]?.toLowerCase() === 'confirm');
3875
3875
  return;
3876
3876
  }
3877
3877
  await this.sendMessage(scopeId, t(locale, 'usage_auth'));
3878
3878
  return;
3879
3879
  }
3880
3880
  if (action === 'refresh_all') {
3881
- await this.handleAuthRefreshAllCommand(scopeId, locale);
3881
+ await this.handleAuthRefreshAllCommand(scopeId, locale, args[1]?.toLowerCase() === 'confirm');
3882
3882
  return;
3883
3883
  }
3884
3884
  if (action === 'use') {
@@ -3910,11 +3910,26 @@ export class BridgeSessionCore {
3910
3910
  const messageId = await this.sendMessage(scopeId, renderAuthListMessage(locale, state, this.authDisplayBotLabel(), parseWeixinBridgeScope(scopeId) !== null), authChoiceKeyboard(locale, record));
3911
3911
  record.messageId = messageId;
3912
3912
  }
3913
- async handleAuthRefreshAllCommand(scopeId, locale) {
3913
+ async handleAuthRefreshAllCommand(scopeId, locale, confirmed = false) {
3914
3914
  if (!this.canRunGlobalAuthRefresh()) {
3915
3915
  await this.sendMessage(scopeId, t(locale, 'auth_refresh_all_blocked_active'));
3916
3916
  return;
3917
3917
  }
3918
+ if (!confirmed) {
3919
+ const state = await this.listCodexAuthState();
3920
+ await this.applySharedCodexAuthQuotaSnapshots(state);
3921
+ const record = {
3922
+ localId: crypto.randomBytes(8).toString('hex'),
3923
+ chatId: scopeId,
3924
+ messageId: null,
3925
+ candidates: state.candidates,
3926
+ createdAt: Date.now(),
3927
+ };
3928
+ this.pendingAuthChoiceLists.set(record.localId, record);
3929
+ const messageId = await this.sendMessage(scopeId, t(locale, 'auth_refresh_all_confirm_message'), authRefreshAllConfirmKeyboard(locale, record));
3930
+ record.messageId = messageId;
3931
+ return;
3932
+ }
3918
3933
  await this.sendMessage(scopeId, t(locale, 'auth_refresh_all_starting'));
3919
3934
  const result = await this.refreshAllCodexAuthCandidates();
3920
3935
  const state = await this.listCodexAuthState();
@@ -4403,6 +4418,28 @@ export class BridgeSessionCore {
4403
4418
  return;
4404
4419
  }
4405
4420
  if (action === 'refresh_all') {
4421
+ if (!this.canRunGlobalAuthRefresh()) {
4422
+ await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'auth_refresh_all_blocked_active'));
4423
+ return;
4424
+ }
4425
+ await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'auth_refresh_all_confirm_short'));
4426
+ if (record.messageId !== null) {
4427
+ await this.editMessage(event.scopeId, record.messageId, t(locale, 'auth_refresh_all_confirm_message'), authRefreshAllConfirmKeyboard(locale, record));
4428
+ }
4429
+ return;
4430
+ }
4431
+ if (action === 'refresh_all_cancel') {
4432
+ await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'auth_refresh_all_cancelled'));
4433
+ const state = await this.listCodexAuthState();
4434
+ await this.applySharedCodexAuthQuotaSnapshots(state);
4435
+ record.candidates = state.candidates;
4436
+ record.createdAt = Date.now();
4437
+ if (record.messageId !== null) {
4438
+ await this.editMessage(event.scopeId, record.messageId, renderAuthListMessage(locale, state, this.authDisplayBotLabel(), parseWeixinBridgeScope(event.scopeId) !== null), authChoiceKeyboard(locale, record));
4439
+ }
4440
+ return;
4441
+ }
4442
+ if (action === 'refresh_all_confirm') {
4406
4443
  if (!this.canRunGlobalAuthRefresh()) {
4407
4444
  await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'auth_refresh_all_blocked_active'));
4408
4445
  return;
@@ -7672,6 +7709,12 @@ function authChoiceKeyboard(locale, record) {
7672
7709
  ]);
7673
7710
  return rows;
7674
7711
  }
7712
+ function authRefreshAllConfirmKeyboard(locale, record) {
7713
+ return [
7714
+ [{ text: t(locale, 'button_auth_refresh_all_confirm'), callback_data: `auth:${record.localId}:refresh_all_confirm` }],
7715
+ [{ text: t(locale, 'button_cancel'), callback_data: `auth:${record.localId}:refresh_all_cancel` }],
7716
+ ];
7717
+ }
7675
7718
  function formatAuthRefreshAllResult(locale, result) {
7676
7719
  const lines = [t(locale, 'auth_refresh_all_done', {
7677
7720
  refreshed: String(result.refreshed.length),
package/dist/i18n.d.ts CHANGED
@@ -137,7 +137,7 @@ declare const MESSAGES: {
137
137
  readonly auth_reload_restarting: "Restarting Codex app-server to reload auth...";
138
138
  readonly auth_reload_done: "Codex app-server restarted. Current auth has been reloaded.";
139
139
  readonly auth_reload_blocked_active: "Cannot reload Codex auth while a turn, approval, or question is active. Wait or use /interrupt first.";
140
- readonly usage_auth: "Usage: /auth [list|use <n>|enable <n>|disable <n>|reload|refresh all|add <name>]";
140
+ readonly usage_auth: "Usage: /auth [list|use <n>|enable <n>|disable <n>|reload|refresh all [confirm]|add <name>]";
141
141
  readonly usage_auth_add: "Usage: /auth add <name>. Use letters, numbers, dot, dash, or underscore.";
142
142
  readonly auth_list_title: "Codex auth files:";
143
143
  readonly auth_bot: "Bot runtime: {value}";
@@ -158,6 +158,9 @@ declare const MESSAGES: {
158
158
  readonly auth_switching: "Switching Codex auth: {from} -> {to}...";
159
159
  readonly auth_switch_done: "Codex auth switched: {from} -> {to}.";
160
160
  readonly auth_recovered_newer_candidate: "Recovered a newer same-account credential for {value} from another Codex home before reload.";
161
+ readonly auth_refresh_all_confirm_short: "Review refresh all risk first.";
162
+ readonly auth_refresh_all_confirm_message: "Refresh all will rotate ChatGPT refresh tokens for every candidate. If OpenAI/Codex consumes a refresh token but the new token cannot be saved because of network, process, or disk failure, that candidate may require device login or phone verification again.\n\nOnly continue when every bot is idle and you accept this risk.";
163
+ readonly auth_refresh_all_cancelled: "Refresh all cancelled.";
161
164
  readonly auth_refresh_all_starting: "Refreshing all ChatGPT auth candidates. Every runtime must stay idle...";
162
165
  readonly auth_refresh_all_blocked_active: "Cannot refresh all auth candidates while any runtime, approval, input, login, or auth mirror write is active. Wait or use /interrupt first.";
163
166
  readonly auth_refresh_all_done: "Auth refresh all complete: {refreshed} refreshed, {skipped} skipped, {failed} failed.";
@@ -180,6 +183,7 @@ declare const MESSAGES: {
180
183
  readonly button_login_device: "🔑 Login";
181
184
  readonly button_auth_reload: "🔄 Reload auth";
182
185
  readonly button_auth_refresh_all: "🔁 Refresh all";
186
+ readonly button_auth_refresh_all_confirm: "⚠️ Accept risk & refresh";
183
187
  readonly button_auth_enable: "✅";
184
188
  readonly button_auth_disable: "⏸️";
185
189
  readonly another_turn_running: "Another turn is already running. Use /interrupt, /takeover, /queue, or wait.";
@@ -720,7 +724,7 @@ declare const MESSAGES: {
720
724
  readonly auth_reload_restarting: "正在重启 Codex app-server 以重新读取 auth...";
721
725
  readonly auth_reload_done: "Codex app-server 已重启,当前 auth 已重新读取。";
722
726
  readonly auth_reload_blocked_active: "当前有回复、审批或问题在进行中,不能重载 Codex auth。请先等待,或使用 /interrupt。";
723
- readonly usage_auth: "用法:/auth [list|use <编号>|enable <编号>|disable <编号>|reload|refresh all|add <名称>]";
727
+ readonly usage_auth: "用法:/auth [list|use <编号>|enable <编号>|disable <编号>|reload|refresh all [confirm]|add <名称>]";
724
728
  readonly usage_auth_add: "用法:/auth add <名称>。名称只能包含字母、数字、点、短横线或下划线。";
725
729
  readonly auth_list_title: "Codex auth 文件:";
726
730
  readonly auth_bot: "Bot runtime:{value}";
@@ -741,6 +745,9 @@ declare const MESSAGES: {
741
745
  readonly auth_switching: "正在切换 Codex auth:{from} -> {to}...";
742
746
  readonly auth_switch_done: "Codex auth 已切换:{from} -> {to}。";
743
747
  readonly auth_recovered_newer_candidate: "重载前已从其他 Codex home 恢复 {value} 的同账号较新凭据。";
748
+ readonly auth_refresh_all_confirm_short: "请先确认刷新全部风险。";
749
+ readonly auth_refresh_all_confirm_message: "刷新全部会轮换每个 ChatGPT 候选的 refresh token。如果 OpenAI/Codex 已经消费旧 refresh token,但因为网络、进程或磁盘故障导致新 token 没能成功保存,该候选可能需要重新设备登录,甚至重新手机号验证。\n\n只有在所有 bot 都空闲,并且你能接受这个风险时,才继续。";
750
+ readonly auth_refresh_all_cancelled: "已取消刷新全部。";
744
751
  readonly auth_refresh_all_starting: "正在刷新全部 ChatGPT auth 候选。执行期间所有 runtime 必须保持空闲...";
745
752
  readonly auth_refresh_all_blocked_active: "当前有任一 runtime、审批、待输入、登录或 auth 镜像写入在进行中,不能刷新全部 auth。请先等待,或使用 /interrupt。";
746
753
  readonly auth_refresh_all_done: "全部 auth 刷新完成:已刷新 {refreshed},已跳过 {skipped},失败 {failed}。";
@@ -763,6 +770,7 @@ declare const MESSAGES: {
763
770
  readonly button_login_device: "🔑 设备登录";
764
771
  readonly button_auth_reload: "🔄 重载 auth";
765
772
  readonly button_auth_refresh_all: "🔁 刷新全部";
773
+ readonly button_auth_refresh_all_confirm: "⚠️ 接受风险并刷新";
766
774
  readonly button_auth_enable: "✅";
767
775
  readonly button_auth_disable: "⏸️";
768
776
  readonly another_turn_running: "已经有一个回复在进行中。请先等待,或使用 /interrupt、/takeover、/queue。";
package/dist/i18n.js CHANGED
@@ -135,7 +135,7 @@ const MESSAGES = {
135
135
  auth_reload_restarting: 'Restarting Codex app-server to reload auth...',
136
136
  auth_reload_done: 'Codex app-server restarted. Current auth has been reloaded.',
137
137
  auth_reload_blocked_active: 'Cannot reload Codex auth while a turn, approval, or question is active. Wait or use /interrupt first.',
138
- usage_auth: 'Usage: /auth [list|use <n>|enable <n>|disable <n>|reload|refresh all|add <name>]',
138
+ usage_auth: 'Usage: /auth [list|use <n>|enable <n>|disable <n>|reload|refresh all [confirm]|add <name>]',
139
139
  usage_auth_add: 'Usage: /auth add <name>. Use letters, numbers, dot, dash, or underscore.',
140
140
  auth_list_title: 'Codex auth files:',
141
141
  auth_bot: 'Bot runtime: {value}',
@@ -156,6 +156,9 @@ const MESSAGES = {
156
156
  auth_switching: 'Switching Codex auth: {from} -> {to}...',
157
157
  auth_switch_done: 'Codex auth switched: {from} -> {to}.',
158
158
  auth_recovered_newer_candidate: 'Recovered a newer same-account credential for {value} from another Codex home before reload.',
159
+ auth_refresh_all_confirm_short: 'Review refresh all risk first.',
160
+ auth_refresh_all_confirm_message: 'Refresh all will rotate ChatGPT refresh tokens for every candidate. If OpenAI/Codex consumes a refresh token but the new token cannot be saved because of network, process, or disk failure, that candidate may require device login or phone verification again.\n\nOnly continue when every bot is idle and you accept this risk.',
161
+ auth_refresh_all_cancelled: 'Refresh all cancelled.',
159
162
  auth_refresh_all_starting: 'Refreshing all ChatGPT auth candidates. Every runtime must stay idle...',
160
163
  auth_refresh_all_blocked_active: 'Cannot refresh all auth candidates while any runtime, approval, input, login, or auth mirror write is active. Wait or use /interrupt first.',
161
164
  auth_refresh_all_done: 'Auth refresh all complete: {refreshed} refreshed, {skipped} skipped, {failed} failed.',
@@ -178,6 +181,7 @@ const MESSAGES = {
178
181
  button_login_device: '🔑 Login',
179
182
  button_auth_reload: '🔄 Reload auth',
180
183
  button_auth_refresh_all: '🔁 Refresh all',
184
+ button_auth_refresh_all_confirm: '⚠️ Accept risk & refresh',
181
185
  button_auth_enable: '✅',
182
186
  button_auth_disable: '⏸️',
183
187
  another_turn_running: 'Another turn is already running. Use /interrupt, /takeover, /queue, or wait.',
@@ -718,7 +722,7 @@ const MESSAGES = {
718
722
  auth_reload_restarting: '正在重启 Codex app-server 以重新读取 auth...',
719
723
  auth_reload_done: 'Codex app-server 已重启,当前 auth 已重新读取。',
720
724
  auth_reload_blocked_active: '当前有回复、审批或问题在进行中,不能重载 Codex auth。请先等待,或使用 /interrupt。',
721
- usage_auth: '用法:/auth [list|use <编号>|enable <编号>|disable <编号>|reload|refresh all|add <名称>]',
725
+ usage_auth: '用法:/auth [list|use <编号>|enable <编号>|disable <编号>|reload|refresh all [confirm]|add <名称>]',
722
726
  usage_auth_add: '用法:/auth add <名称>。名称只能包含字母、数字、点、短横线或下划线。',
723
727
  auth_list_title: 'Codex auth 文件:',
724
728
  auth_bot: 'Bot runtime:{value}',
@@ -739,6 +743,9 @@ const MESSAGES = {
739
743
  auth_switching: '正在切换 Codex auth:{from} -> {to}...',
740
744
  auth_switch_done: 'Codex auth 已切换:{from} -> {to}。',
741
745
  auth_recovered_newer_candidate: '重载前已从其他 Codex home 恢复 {value} 的同账号较新凭据。',
746
+ auth_refresh_all_confirm_short: '请先确认刷新全部风险。',
747
+ auth_refresh_all_confirm_message: '刷新全部会轮换每个 ChatGPT 候选的 refresh token。如果 OpenAI/Codex 已经消费旧 refresh token,但因为网络、进程或磁盘故障导致新 token 没能成功保存,该候选可能需要重新设备登录,甚至重新手机号验证。\n\n只有在所有 bot 都空闲,并且你能接受这个风险时,才继续。',
748
+ auth_refresh_all_cancelled: '已取消刷新全部。',
742
749
  auth_refresh_all_starting: '正在刷新全部 ChatGPT auth 候选。执行期间所有 runtime 必须保持空闲...',
743
750
  auth_refresh_all_blocked_active: '当前有任一 runtime、审批、待输入、登录或 auth 镜像写入在进行中,不能刷新全部 auth。请先等待,或使用 /interrupt。',
744
751
  auth_refresh_all_done: '全部 auth 刷新完成:已刷新 {refreshed},已跳过 {skipped},失败 {failed}。',
@@ -761,6 +768,7 @@ const MESSAGES = {
761
768
  button_login_device: '🔑 设备登录',
762
769
  button_auth_reload: '🔄 重载 auth',
763
770
  button_auth_refresh_all: '🔁 刷新全部',
771
+ button_auth_refresh_all_confirm: '⚠️ 接受风险并刷新',
764
772
  button_auth_enable: '✅',
765
773
  button_auth_disable: '⏸️',
766
774
  another_turn_running: '已经有一个回复在进行中。请先等待,或使用 /interrupt、/takeover、/queue。',
@@ -408,7 +408,7 @@ Quota remaining: 5h|7d|auth
408
408
  [🔄 Reload auth] [🔁 Refresh all]
409
409
  ```
410
410
 
411
- The right-side `✅` / `⏸️` button shows the current state. Tapping it toggles enabled/disabled, and the refreshed list shows the new state. Tapping a candidate switches auth, restarts that runtime, and refreshes the same panel with its buttons intact so you can switch again immediately. `Refresh all` is a maintenance action: it is allowed only when every Telegram runtime, the Weixin runtime, approvals, inputs, logins, and auth mirroring are idle. It then visits every ChatGPT candidate in the panel, asks Codex to force-refresh tokens with `account/read refreshToken=true`, verifies the result through the usage endpoint, mirrors successful candidates, restores the original current auth, and refreshes the panel with a summary. `--|--` means no quota snapshot has been observed for that candidate yet.
411
+ The right-side `✅` / `⏸️` button shows the current state. Tapping it toggles enabled/disabled, and the refreshed list shows the new state. Tapping a candidate switches auth, restarts that runtime, and refreshes the same panel with its buttons intact so you can switch again immediately. `Refresh all` is a maintenance action: it is allowed only when every Telegram runtime, the Weixin runtime, approvals, inputs, logins, and auth mirroring are idle. The first tap only shows a risk confirmation: ChatGPT refresh tokens are rotated, so if OpenAI/Codex consumes an old refresh token but the new token cannot be saved because of network, process, or disk failure, that candidate may require device login or phone verification again. After confirmation, FoxClaw visits every ChatGPT candidate in the panel, asks Codex to force-refresh tokens with `account/read refreshToken=true`, verifies the result through the usage endpoint, mirrors successful candidates, restores the original current auth, and refreshes the panel with a summary. `--|--` means no quota snapshot has been observed for that candidate yet.
412
412
 
413
413
  Equivalent commands:
414
414
 
@@ -417,7 +417,8 @@ Equivalent commands:
417
417
  - `/auth enable <n>`: let candidate n participate in auto-rotation.
418
418
  - `/auth disable <n>`: skip candidate n during auto-rotation.
419
419
  - `/auth reload` or `/auth_reload`: restart app-server and reload the current `auth.json`.
420
- - `/auth refresh all`: same as the panel's Refresh all button.
420
+ - `/auth refresh all`: show the same risk confirmation as the panel's Refresh all button.
421
+ - `/auth refresh all confirm`: run Refresh all after accepting the token-rotation risk.
421
422
 
422
423
  If the requesting bot runtime has active turns, pending approvals, pending user inputs, or MCP elicitations, FoxClaw refuses manual auth switching to avoid changing accounts mid-request; another idle bot is unaffected.
423
424
 
@@ -408,7 +408,7 @@ Candidates: 2
408
408
  [🔄 Reload auth] [🔁 刷新全部]
409
409
  ```
410
410
 
411
- 右侧 `✅` / `⏸️` 表示当前状态。点一下会切换启用/禁用,列表刷新后图标会随状态变化。点击候选会切换 auth、重启对应 runtime,并在原消息上刷新面板且保留按钮,因此可以立即连续切换。`刷新全部` 是维护操作:只有所有 Telegram runtime、微信 runtime、审批、待输入、登录流程和 auth 镜像写入都空闲时才允许执行。它会逐个访问面板里的 ChatGPT 候选,让 Codex 通过 `account/read refreshToken=true` 强制刷新 token,再用 usage 接口验证,成功后镜像到其他 bot home,最后恢复原本的当前 auth 并刷新面板摘要。`--|--` 表示该候选还没有额度历史快照。
411
+ 右侧 `✅` / `⏸️` 表示当前状态。点一下会切换启用/禁用,列表刷新后图标会随状态变化。点击候选会切换 auth、重启对应 runtime,并在原消息上刷新面板且保留按钮,因此可以立即连续切换。`刷新全部` 是维护操作:只有所有 Telegram runtime、微信 runtime、审批、待输入、登录流程和 auth 镜像写入都空闲时才允许执行。第一次点击只会显示风险确认:ChatGPT refresh token 会被轮换,如果 OpenAI/Codex 已经消费旧 refresh token,但因为网络、进程或磁盘故障导致新 token 没能成功保存,该候选可能需要重新设备登录,甚至重新手机号验证。确认后,它会逐个访问面板里的 ChatGPT 候选,让 Codex 通过 `account/read refreshToken=true` 强制刷新 token,再用 usage 接口验证,成功后镜像到其他 bot home,最后恢复原本的当前 auth 并刷新面板摘要。`--|--` 表示该候选还没有额度历史快照。
412
412
 
413
413
  命令等价用法:
414
414
 
@@ -417,7 +417,8 @@ Candidates: 2
417
417
  - `/auth enable <n>`:让第 n 个候选参与自动轮转。
418
418
  - `/auth disable <n>`:禁用第 n 个候选,自动轮转会跳过它。
419
419
  - `/auth reload` 或 `/auth_reload`:重启 app-server,重新加载当前 `auth.json`。
420
- - `/auth refresh all`:等同于面板上的“刷新全部”按钮。
420
+ - `/auth refresh all`:显示与面板“刷新全部”相同的风险确认。
421
+ - `/auth refresh all confirm`:接受 token 轮换风险后执行刷新全部。
421
422
 
422
423
  切换 auth 时,如果当前 bot runtime 还有活跃 turn、待审批、待用户输入或 MCP elicitation,FoxClaw 会先拒绝切换,避免中途换号破坏正在进行的请求;另一个空闲 bot 不受影响。
423
424
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxden-app/foxclaw",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
4
4
  "description": "Foxden local execution claw for controlling Codex from trusted chat interfaces.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -67,7 +67,7 @@ Telegram behavior:
67
67
  - when `TG_BOT_TOKENS` contains multiple bots, one FoxClaw service starts independent Codex app-servers and auth selections per bot
68
68
  - isolated Telegram runtimes use independent `CODEX_HOME` directories and file-backed credentials; they do not share Codex sessions
69
69
  - refreshed ChatGPT candidates are mirrored only after online usage-endpoint validation; before auth switch or reload, a runtime restores any newer same-account credential found in another Codex home
70
- - `/auth` includes a Refresh all panel button; it is a global-idle maintenance action that force-refreshes every ChatGPT candidate through Codex `account/read refreshToken=true`, validates usage, mirrors successful candidates, and restores the original current auth
70
+ - `/auth` includes a Refresh all panel button; it requires explicit token-rotation risk confirmation, then force-refreshes every ChatGPT candidate through Codex `account/read refreshToken=true`, validates usage, mirrors successful candidates, and restores the original current auth while every runtime is globally idle
71
71
  - if `TG_BOT_TOKEN` is also set to one exact token from `TG_BOT_TOKENS`, that bot uses the default/shared-terminal runtime instead of an isolated Telegram home
72
72
  - if Weixin is enabled alongside multiple Telegram bots, it remains on the default Codex runtime instead of borrowing a Telegram bot runtime
73
73
  - in a group with multiple configured bots, address a bot by mention, reply, or suffixed command such as `/status@botname`