@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 +12 -0
- package/README.md +1 -1
- package/README_EN.md +1 -1
- package/dist/controller/controller.js +47 -4
- package/dist/i18n.d.ts +10 -2
- package/dist/i18n.js +10 -2
- package/docs/user-manual.md +3 -2
- package/docs/zh/user-manual.md +3 -2
- package/package.json +1 -1
- package/skills/foxclaw/SKILL.md +1 -1
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。',
|
package/docs/user-manual.md
CHANGED
|
@@ -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.
|
|
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
|
|
package/docs/zh/user-manual.md
CHANGED
|
@@ -408,7 +408,7 @@ Candidates: 2
|
|
|
408
408
|
[🔄 Reload auth] [🔁 刷新全部]
|
|
409
409
|
```
|
|
410
410
|
|
|
411
|
-
右侧 `✅` / `⏸️` 表示当前状态。点一下会切换启用/禁用,列表刷新后图标会随状态变化。点击候选会切换 auth、重启对应 runtime,并在原消息上刷新面板且保留按钮,因此可以立即连续切换。`刷新全部` 是维护操作:只有所有 Telegram runtime、微信 runtime、审批、待输入、登录流程和 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
package/skills/foxclaw/SKILL.md
CHANGED
|
@@ -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
|
|
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`
|