@foxden-app/foxclaw 0.5.58 → 0.5.61
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 +3 -0
- package/CHANGELOG.md +32 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +1 -0
- package/dist/controller/controller.d.ts +5 -1
- package/dist/controller/controller.js +178 -59
- package/dist/i18n.d.ts +12 -0
- package/dist/i18n.js +12 -0
- package/dist/store/database.d.ts +3 -1
- package/dist/store/database.js +14 -2
- package/package.json +1 -1
package/.env.example
CHANGED
|
@@ -38,6 +38,9 @@ TELEGRAM_PREVIEW_THROTTLE_MS=800
|
|
|
38
38
|
# Set to false if you prefer to keep "read files / edited files / ran command"
|
|
39
39
|
# detail cards in the chat history.
|
|
40
40
|
# TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL=true
|
|
41
|
+
# Delete time-sensitive interactive panels such as /auth, /setup, and /threads after 5 minutes.
|
|
42
|
+
# The timer restarts whenever the panel is refreshed. Set to 0 to disable.
|
|
43
|
+
# TELEGRAM_PANEL_TTL_MS=300000
|
|
41
44
|
THREAD_LIST_LIMIT=10
|
|
42
45
|
CODEX_CLI_BIN=/absolute/path/to/codex
|
|
43
46
|
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
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.5.61 - 2026-06-21
|
|
6
|
+
|
|
7
|
+
### 中文
|
|
8
|
+
- 将 `/auth`、`/setup`、`/threads` 等交互面板的默认自动清理时间从 30 分钟改为 5 分钟,并把 `/where`、旧 model/access 设置面板和 `/config` 面板纳入同一类时效清理。
|
|
9
|
+
- `/config` 现在明确定位为 FoxClaw 自身设置面板,新增“最终回复后删除操作明细”开关;可通过按钮或 `/config delete_tool_details <on|off>` 修改,并写回 `TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL`。
|
|
10
|
+
- `/config` 展示当前 `TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL` 和 `TELEGRAM_PANEL_TTL_MS`,方便确认聊天清理策略是否生效。
|
|
11
|
+
|
|
12
|
+
### English
|
|
13
|
+
- Changed the default cleanup timeout for interactive panels such as `/auth`, `/setup`, and `/threads` from 30 minutes to 5 minutes, and applied the same stale-panel cleanup to `/where`, legacy model/access settings panels, and `/config`.
|
|
14
|
+
- `/config` is now the FoxClaw runtime settings panel and includes a "delete operation details after final reply" toggle. It can be changed via the button or `/config delete_tool_details <on|off>`, writing `TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL`.
|
|
15
|
+
- `/config` shows the current `TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL` and `TELEGRAM_PANEL_TTL_MS` values so cleanup behavior is visible.
|
|
16
|
+
|
|
17
|
+
## 0.5.60 - 2026-06-21
|
|
18
|
+
|
|
19
|
+
### 中文
|
|
20
|
+
- 修复 `/auth` 刷新时间测试对 Asia/Shanghai 时区的硬编码,使 Linux、macOS 和 UTC CI runner 都按各自本地时区验证;功能内容与 0.5.59 一并发布。
|
|
21
|
+
|
|
22
|
+
### English
|
|
23
|
+
- Fixed the `/auth` reset-time test to respect the runner's local timezone across Linux, macOS, and UTC CI environments. This release publishes the feature set introduced in 0.5.59.
|
|
24
|
+
|
|
25
|
+
## 0.5.59 - 2026-06-21
|
|
26
|
+
|
|
27
|
+
### 中文
|
|
28
|
+
- `/auth` 富文本表格新增 Quota A、Quota B 的下次刷新时间;精确时间随额度快照持久化并可在节点间共享,旧快照安全显示 `-`。
|
|
29
|
+
- 修复 `/auth` 按钮回调后退化为普通消息的问题:切换、启停、筛选、翻页、修复、刷新和安全同步现在始终使用 RichMessage 编辑。
|
|
30
|
+
- 新增时效面板自动清理:`/auth`、`/threads` 和统一 `/setup` 面板默认空闲 30 分钟后删除,每次交互重新计时;可用 `TELEGRAM_PANEL_TTL_MS` 调整,设为 `0` 可关闭。
|
|
31
|
+
|
|
32
|
+
### English
|
|
33
|
+
- Added next-reset columns for Quota A and Quota B in the `/auth` RichMessage table. Exact reset timestamps now persist with quota snapshots and can be shared across nodes; legacy snapshots safely show `-`.
|
|
34
|
+
- Fixed `/auth` callback refreshes degrading to plain messages. Switch, toggle, filter, pagination, repair, refresh, and safe-sync actions now consistently edit the panel as a RichMessage.
|
|
35
|
+
- Added automatic cleanup for time-sensitive panels. `/auth`, `/threads`, and unified `/setup` panels are deleted after 30 minutes of inactivity by default, with interaction resetting the timer. Configure `TELEGRAM_PANEL_TTL_MS`, or set it to `0` to disable cleanup.
|
|
36
|
+
|
|
5
37
|
## 0.5.58 - 2026-06-21
|
|
6
38
|
|
|
7
39
|
### 中文
|
package/dist/config.d.ts
CHANGED
package/dist/config.js
CHANGED
|
@@ -78,6 +78,7 @@ export function loadConfig() {
|
|
|
78
78
|
telegramPollIntervalMs: intEnv('TELEGRAM_POLL_INTERVAL_MS', 1200),
|
|
79
79
|
telegramPreviewThrottleMs: intEnv('TELEGRAM_PREVIEW_THROTTLE_MS', 800),
|
|
80
80
|
telegramDeleteToolDetailsAfterFinal: boolEnv('TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL', true),
|
|
81
|
+
telegramPanelTtlMs: intEnv('TELEGRAM_PANEL_TTL_MS', 5 * 60_000),
|
|
81
82
|
threadListLimit: intEnv('THREAD_LIST_LIMIT', 10),
|
|
82
83
|
statusPath: DEFAULT_STATUS_PATH,
|
|
83
84
|
logPath: DEFAULT_LOG_PATH,
|
|
@@ -96,6 +96,7 @@ export declare class BridgeSessionCore {
|
|
|
96
96
|
private proactiveAuthRefreshTimer;
|
|
97
97
|
private proactiveAuthRefreshInProgress;
|
|
98
98
|
private proactiveAuthRefreshStatus;
|
|
99
|
+
private stalePanelDeleteTimers;
|
|
99
100
|
private attachedThreads;
|
|
100
101
|
private botUsername;
|
|
101
102
|
private lastError;
|
|
@@ -236,6 +237,9 @@ export declare class BridgeSessionCore {
|
|
|
236
237
|
private editMessage;
|
|
237
238
|
private editHtmlMessage;
|
|
238
239
|
private editRichHtmlMessage;
|
|
240
|
+
private editRichInternalMessage;
|
|
241
|
+
private editAuthPanelMessage;
|
|
242
|
+
private scheduleStalePanelDeletion;
|
|
239
243
|
private deleteMessage;
|
|
240
244
|
private sendTyping;
|
|
241
245
|
private sendObservedCliUserMessage;
|
|
@@ -348,7 +352,7 @@ export declare class BridgeSessionCore {
|
|
|
348
352
|
private handleFeaturesCommand;
|
|
349
353
|
private handleConfigCommand;
|
|
350
354
|
private handleConfigToggleCallback;
|
|
351
|
-
private
|
|
355
|
+
private setFoxClawBooleanConfig;
|
|
352
356
|
private formatConfigToggleUpdate;
|
|
353
357
|
private handleRequirementsCommand;
|
|
354
358
|
private handleProviderCommand;
|
|
@@ -152,6 +152,7 @@ export class BridgeSessionCore {
|
|
|
152
152
|
proactiveAuthRefreshTimer = null;
|
|
153
153
|
proactiveAuthRefreshInProgress = false;
|
|
154
154
|
proactiveAuthRefreshStatus = null;
|
|
155
|
+
stalePanelDeleteTimers = new Map();
|
|
155
156
|
attachedThreads = new Set();
|
|
156
157
|
botUsername = null;
|
|
157
158
|
lastError = null;
|
|
@@ -276,6 +277,10 @@ export class BridgeSessionCore {
|
|
|
276
277
|
this.clearRestartPreviewRecoveryTimers();
|
|
277
278
|
this.clearSelfUpdateStatusPoll();
|
|
278
279
|
this.clearProactiveAuthRefreshTimer();
|
|
280
|
+
for (const timer of this.stalePanelDeleteTimers.values()) {
|
|
281
|
+
clearTimeout(timer);
|
|
282
|
+
}
|
|
283
|
+
this.stalePanelDeleteTimers.clear();
|
|
279
284
|
await this.app.stop({ terminateServer: false });
|
|
280
285
|
this.updateStatus();
|
|
281
286
|
}
|
|
@@ -1083,9 +1088,9 @@ export class BridgeSessionCore {
|
|
|
1083
1088
|
await this.handleSetupCallback(event, setupMatch[1], setupMatch[2], locale);
|
|
1084
1089
|
return;
|
|
1085
1090
|
}
|
|
1086
|
-
const configMatch = /^config:auth_auto_delete:(on|off)$/.exec(event.data);
|
|
1091
|
+
const configMatch = /^config:(auth_auto_delete|delete_tool_details):(on|off)$/.exec(event.data);
|
|
1087
1092
|
if (configMatch) {
|
|
1088
|
-
await this.handleConfigToggleCallback(event, configMatch[1] === 'on', locale);
|
|
1093
|
+
await this.handleConfigToggleCallback(event, configMatch[1], configMatch[2] === 'on', locale);
|
|
1089
1094
|
return;
|
|
1090
1095
|
}
|
|
1091
1096
|
const voiceMatch = /^voice:([a-f0-9]+)$/.exec(event.data);
|
|
@@ -3431,6 +3436,35 @@ export class BridgeSessionCore {
|
|
|
3431
3436
|
await this.editHtmlMessage(scopeId, messageId, fallbackHtml, inlineKeyboard);
|
|
3432
3437
|
}
|
|
3433
3438
|
}
|
|
3439
|
+
async editRichInternalMessage(scopeId, messageId, title, text, inlineKeyboard) {
|
|
3440
|
+
if (scopeId.startsWith(BRIDGE_SCOPE_WEIXIN_PREFIX)) {
|
|
3441
|
+
await this.editMessage(scopeId, messageId, text, inlineKeyboard);
|
|
3442
|
+
return;
|
|
3443
|
+
}
|
|
3444
|
+
await this.editRichHtmlMessage(scopeId, messageId, formatRichInternalMessage(title, text), escapeTelegramHtml(text), inlineKeyboard);
|
|
3445
|
+
}
|
|
3446
|
+
async editAuthPanelMessage(scopeId, messageId, text, inlineKeyboard) {
|
|
3447
|
+
await this.editRichInternalMessage(scopeId, messageId, '/auth', text, inlineKeyboard);
|
|
3448
|
+
this.scheduleStalePanelDeletion(scopeId, messageId);
|
|
3449
|
+
}
|
|
3450
|
+
scheduleStalePanelDeletion(scopeId, messageId) {
|
|
3451
|
+
if (this.config.telegramPanelTtlMs <= 0 || parseWeixinBridgeScope(scopeId)) {
|
|
3452
|
+
return;
|
|
3453
|
+
}
|
|
3454
|
+
const key = `${scopeId}:${messageId}`;
|
|
3455
|
+
const existing = this.stalePanelDeleteTimers.get(key);
|
|
3456
|
+
if (existing) {
|
|
3457
|
+
clearTimeout(existing);
|
|
3458
|
+
}
|
|
3459
|
+
const timer = setTimeout(() => {
|
|
3460
|
+
this.stalePanelDeleteTimers.delete(key);
|
|
3461
|
+
void this.deleteMessage(scopeId, messageId).catch(error => {
|
|
3462
|
+
this.logger.warn('telegram.stale_panel_delete_failed', { scopeId, messageId, error: toErrorMeta(error) });
|
|
3463
|
+
});
|
|
3464
|
+
}, this.config.telegramPanelTtlMs);
|
|
3465
|
+
timer.unref();
|
|
3466
|
+
this.stalePanelDeleteTimers.set(key, timer);
|
|
3467
|
+
}
|
|
3434
3468
|
async deleteMessage(scopeId, messageId) {
|
|
3435
3469
|
await this.messaging.deleteMessage(scopeId, messageId);
|
|
3436
3470
|
}
|
|
@@ -4859,6 +4893,7 @@ export class BridgeSessionCore {
|
|
|
4859
4893
|
const authListMessage = renderAuthListMessage(locale, state, this.authDisplayBotLabel(), parseWeixinBridgeScope(scopeId) !== null, record);
|
|
4860
4894
|
const messageId = await this.sendRichInternalMessage(scopeId, '/auth', authListMessage, authChoiceKeyboard(locale, record));
|
|
4861
4895
|
record.messageId = messageId;
|
|
4896
|
+
this.scheduleStalePanelDeletion(scopeId, messageId);
|
|
4862
4897
|
}
|
|
4863
4898
|
async handleAuthSyncCommand(scopeId, locale, args) {
|
|
4864
4899
|
const action = args[0]?.toLowerCase() ?? 'status';
|
|
@@ -5548,46 +5583,73 @@ export class BridgeSessionCore {
|
|
|
5548
5583
|
await this.sendMessage(scopeId, t(locale, 'config_auth_auto_delete_usage'));
|
|
5549
5584
|
return;
|
|
5550
5585
|
}
|
|
5551
|
-
const update = await this.
|
|
5586
|
+
const update = await this.setFoxClawBooleanConfig('auth_auto_delete', enabled);
|
|
5587
|
+
const binding = this.store.getBinding(scopeId);
|
|
5588
|
+
const result = await this.app.readConfig(binding?.cwd ?? this.config.defaultCwd, true);
|
|
5589
|
+
const sentMessageId = await this.sendMessage(scopeId, `${this.formatConfigToggleUpdate(locale, update)}\n\n${formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats())}`, configKeyboard(locale, this.config));
|
|
5590
|
+
this.scheduleStalePanelDeletion(scopeId, sentMessageId);
|
|
5591
|
+
return;
|
|
5592
|
+
}
|
|
5593
|
+
if (['delete_tool_details', 'delete-tool-details', 'tool_details', 'tool-details'].includes(action)) {
|
|
5594
|
+
const enabled = parseConfigBooleanArg(args[1]);
|
|
5595
|
+
if (enabled === null) {
|
|
5596
|
+
await this.sendMessage(scopeId, t(locale, 'config_delete_tool_details_usage'));
|
|
5597
|
+
return;
|
|
5598
|
+
}
|
|
5599
|
+
const update = await this.setFoxClawBooleanConfig('delete_tool_details', enabled);
|
|
5552
5600
|
const binding = this.store.getBinding(scopeId);
|
|
5553
5601
|
const result = await this.app.readConfig(binding?.cwd ?? this.config.defaultCwd, true);
|
|
5554
|
-
await this.sendMessage(scopeId, `${this.formatConfigToggleUpdate(locale, update)}\n\n${formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats())}`, configKeyboard(locale, this.config));
|
|
5602
|
+
const sentMessageId = await this.sendMessage(scopeId, `${this.formatConfigToggleUpdate(locale, update)}\n\n${formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats())}`, configKeyboard(locale, this.config));
|
|
5603
|
+
this.scheduleStalePanelDeletion(scopeId, sentMessageId);
|
|
5555
5604
|
return;
|
|
5556
5605
|
}
|
|
5557
5606
|
const binding = this.store.getBinding(scopeId);
|
|
5558
5607
|
const result = await this.app.readConfig(binding?.cwd ?? this.config.defaultCwd, true);
|
|
5559
|
-
await this.sendMessage(scopeId, formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats()), configKeyboard(locale, this.config));
|
|
5608
|
+
const sentMessageId = await this.sendMessage(scopeId, formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats()), configKeyboard(locale, this.config));
|
|
5609
|
+
this.scheduleStalePanelDeletion(scopeId, sentMessageId);
|
|
5560
5610
|
}
|
|
5561
|
-
async handleConfigToggleCallback(event, enabled, locale) {
|
|
5562
|
-
const update = await this.
|
|
5611
|
+
async handleConfigToggleCallback(event, key, enabled, locale) {
|
|
5612
|
+
const update = await this.setFoxClawBooleanConfig(key, enabled);
|
|
5563
5613
|
await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'decision_recorded'));
|
|
5564
5614
|
const binding = this.store.getBinding(event.scopeId);
|
|
5565
5615
|
const result = await this.app.readConfig(binding?.cwd ?? this.config.defaultCwd, true);
|
|
5566
5616
|
const message = `${this.formatConfigToggleUpdate(locale, update)}\n\n${formatConfigMessage(locale, result, this.config, this.store.getCodexAuthPoolStats())}`;
|
|
5567
5617
|
if (event.messageId !== null) {
|
|
5568
5618
|
await this.editMessage(event.scopeId, event.messageId, message, configKeyboard(locale, this.config));
|
|
5619
|
+
this.scheduleStalePanelDeletion(event.scopeId, event.messageId);
|
|
5569
5620
|
}
|
|
5570
5621
|
else {
|
|
5571
|
-
await this.sendMessage(event.scopeId, message, configKeyboard(locale, this.config));
|
|
5622
|
+
const sentMessageId = await this.sendMessage(event.scopeId, message, configKeyboard(locale, this.config));
|
|
5623
|
+
this.scheduleStalePanelDeletion(event.scopeId, sentMessageId);
|
|
5572
5624
|
}
|
|
5573
5625
|
}
|
|
5574
|
-
async
|
|
5575
|
-
|
|
5626
|
+
async setFoxClawBooleanConfig(key, enabled) {
|
|
5627
|
+
const envKey = key === 'auth_auto_delete'
|
|
5628
|
+
? 'AUTH_AUTO_DELETE_NEEDS_REPAIR'
|
|
5629
|
+
: 'TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL';
|
|
5630
|
+
if (key === 'auth_auto_delete') {
|
|
5631
|
+
this.config.authAutoDeleteNeedsRepair = enabled;
|
|
5632
|
+
}
|
|
5633
|
+
else {
|
|
5634
|
+
this.config.telegramDeleteToolDetailsAfterFinal = enabled;
|
|
5635
|
+
}
|
|
5576
5636
|
const envPath = this.config.envPath;
|
|
5577
5637
|
if (!envPath) {
|
|
5578
|
-
return { enabled, envPath: null, envUpdated: false, envError: null };
|
|
5638
|
+
return { key, enabled, envKey, envPath: null, envUpdated: false, envError: null };
|
|
5579
5639
|
}
|
|
5580
5640
|
try {
|
|
5581
|
-
await writeEnvBoolean(envPath,
|
|
5582
|
-
return { enabled, envPath, envUpdated: true, envError: null };
|
|
5641
|
+
await writeEnvBoolean(envPath, envKey, enabled);
|
|
5642
|
+
return { key, enabled, envKey, envPath, envUpdated: true, envError: null };
|
|
5583
5643
|
}
|
|
5584
5644
|
catch (error) {
|
|
5585
|
-
this.logger.warn('config.env_update_failed', { key:
|
|
5586
|
-
return { enabled, envPath, envUpdated: false, envError: formatUserError(error) };
|
|
5645
|
+
this.logger.warn('config.env_update_failed', { key: envKey, envPath, error: toErrorMeta(error) });
|
|
5646
|
+
return { key, enabled, envKey, envPath, envUpdated: false, envError: formatUserError(error) };
|
|
5587
5647
|
}
|
|
5588
5648
|
}
|
|
5589
5649
|
formatConfigToggleUpdate(locale, update) {
|
|
5590
|
-
const lines = [t(locale, '
|
|
5650
|
+
const lines = [t(locale, update.key === 'auth_auto_delete' ? 'config_auth_auto_delete_updated' : 'config_delete_tool_details_updated', {
|
|
5651
|
+
value: t(locale, update.enabled ? 'yes' : 'no'),
|
|
5652
|
+
})];
|
|
5591
5653
|
if (update.envError) {
|
|
5592
5654
|
lines.push(t(locale, 'config_env_update_failed', { value: update.envPath ?? t(locale, 'unknown'), error: update.envError }));
|
|
5593
5655
|
}
|
|
@@ -5664,12 +5726,12 @@ export class BridgeSessionCore {
|
|
|
5664
5726
|
}
|
|
5665
5727
|
await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'auth_sync_safe_starting'));
|
|
5666
5728
|
if (record.messageId !== null) {
|
|
5667
|
-
await this.
|
|
5729
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, t(locale, 'auth_sync_safe_starting'), []);
|
|
5668
5730
|
}
|
|
5669
5731
|
const result = await this.runAuthSafeSyncAll();
|
|
5670
5732
|
if (!result) {
|
|
5671
5733
|
if (record.messageId !== null) {
|
|
5672
|
-
await this.
|
|
5734
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, t(locale, 'auth_sync_disabled'), authChoiceKeyboard(locale, record));
|
|
5673
5735
|
}
|
|
5674
5736
|
return;
|
|
5675
5737
|
}
|
|
@@ -5679,7 +5741,7 @@ export class BridgeSessionCore {
|
|
|
5679
5741
|
record.createdAt = Date.now();
|
|
5680
5742
|
clampCodexAuthListOffset(record);
|
|
5681
5743
|
if (record.messageId !== null) {
|
|
5682
|
-
await this.
|
|
5744
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, `${t(locale, 'auth_sync_safe_done', {
|
|
5683
5745
|
localSynced: result.localSynced,
|
|
5684
5746
|
localSkipped: result.localSkipped,
|
|
5685
5747
|
sent: result.sent,
|
|
@@ -5695,7 +5757,7 @@ export class BridgeSessionCore {
|
|
|
5695
5757
|
}
|
|
5696
5758
|
await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'auth_refresh_all_confirm_short'));
|
|
5697
5759
|
if (record.messageId !== null) {
|
|
5698
|
-
await this.
|
|
5760
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, t(locale, 'auth_refresh_all_confirm_message'), authRefreshAllConfirmKeyboard(locale, record));
|
|
5699
5761
|
}
|
|
5700
5762
|
return;
|
|
5701
5763
|
}
|
|
@@ -5706,7 +5768,7 @@ export class BridgeSessionCore {
|
|
|
5706
5768
|
record.candidates = state.candidates;
|
|
5707
5769
|
record.createdAt = Date.now();
|
|
5708
5770
|
if (record.messageId !== null) {
|
|
5709
|
-
await this.
|
|
5771
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, renderAuthListMessage(locale, state, this.authDisplayBotLabel(), parseWeixinBridgeScope(event.scopeId) !== null, record), authChoiceKeyboard(locale, record));
|
|
5710
5772
|
}
|
|
5711
5773
|
return;
|
|
5712
5774
|
}
|
|
@@ -5717,12 +5779,12 @@ export class BridgeSessionCore {
|
|
|
5717
5779
|
}
|
|
5718
5780
|
await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'auth_refresh_all_starting'));
|
|
5719
5781
|
if (record.messageId !== null) {
|
|
5720
|
-
await this.
|
|
5782
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, t(locale, 'auth_refresh_all_starting'), []);
|
|
5721
5783
|
}
|
|
5722
5784
|
const lease = await this.coordinator?.acquireAuthRefreshLease?.('auth refresh all');
|
|
5723
5785
|
if (lease && !lease.ok) {
|
|
5724
5786
|
if (record.messageId !== null) {
|
|
5725
|
-
await this.
|
|
5787
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, t(locale, 'auth_refresh_all_lease_failed', { error: lease.reason ?? t(locale, 'unknown') }), authChoiceKeyboard(locale, record));
|
|
5726
5788
|
}
|
|
5727
5789
|
return;
|
|
5728
5790
|
}
|
|
@@ -5738,7 +5800,7 @@ export class BridgeSessionCore {
|
|
|
5738
5800
|
record.candidates = state.candidates;
|
|
5739
5801
|
record.createdAt = Date.now();
|
|
5740
5802
|
if (record.messageId !== null) {
|
|
5741
|
-
await this.
|
|
5803
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, `${formatAuthRefreshAllResult(locale, result)}\n\n${renderAuthListMessage(locale, state, this.authDisplayBotLabel(), parseWeixinBridgeScope(event.scopeId) !== null, record)}`, authChoiceKeyboard(locale, record));
|
|
5742
5804
|
}
|
|
5743
5805
|
return;
|
|
5744
5806
|
}
|
|
@@ -5775,7 +5837,7 @@ export class BridgeSessionCore {
|
|
|
5775
5837
|
clampCodexAuthListOffset(record);
|
|
5776
5838
|
await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'decision_recorded'));
|
|
5777
5839
|
if (record.messageId !== null) {
|
|
5778
|
-
await this.
|
|
5840
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, renderAuthListMessage(locale, state, this.authDisplayBotLabel(), parseWeixinBridgeScope(event.scopeId) !== null, record), authChoiceKeyboard(locale, record));
|
|
5779
5841
|
}
|
|
5780
5842
|
}
|
|
5781
5843
|
async handleAuthRepairMenuCallback(event, localId, index, locale) {
|
|
@@ -5795,7 +5857,7 @@ export class BridgeSessionCore {
|
|
|
5795
5857
|
}
|
|
5796
5858
|
await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'auth_repair_actions_short'));
|
|
5797
5859
|
if (record.messageId !== null) {
|
|
5798
|
-
await this.
|
|
5860
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, t(locale, 'auth_repair_actions_message', { value: formatCodexAuthCandidateDisplayName(candidate.name) }), authRepairKeyboard(locale, record, index));
|
|
5799
5861
|
}
|
|
5800
5862
|
}
|
|
5801
5863
|
async handleAuthRepairActionCallback(event, localId, action, index, locale) {
|
|
@@ -5820,7 +5882,7 @@ export class BridgeSessionCore {
|
|
|
5820
5882
|
record.createdAt = Date.now();
|
|
5821
5883
|
clampCodexAuthListOffset(record);
|
|
5822
5884
|
if (record.messageId !== null) {
|
|
5823
|
-
await this.
|
|
5885
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, renderAuthListMessage(locale, state, this.authDisplayBotLabel(), parseWeixinBridgeScope(event.scopeId) !== null, record), authChoiceKeyboard(locale, record));
|
|
5824
5886
|
}
|
|
5825
5887
|
return;
|
|
5826
5888
|
}
|
|
@@ -5831,7 +5893,7 @@ export class BridgeSessionCore {
|
|
|
5831
5893
|
if (action === 'login') {
|
|
5832
5894
|
await this.messaging.answerCallback(event.callbackQueryId, t(locale, 'login_device_started'));
|
|
5833
5895
|
if (record.messageId !== null) {
|
|
5834
|
-
await this.
|
|
5896
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, t(locale, 'auth_repair_login_preparing', { value: candidate.name }), []);
|
|
5835
5897
|
}
|
|
5836
5898
|
await this.startAuthRepairLogin(event.scopeId, locale, candidate);
|
|
5837
5899
|
return;
|
|
@@ -5843,7 +5905,7 @@ export class BridgeSessionCore {
|
|
|
5843
5905
|
record.createdAt = Date.now();
|
|
5844
5906
|
clampCodexAuthListOffset(record);
|
|
5845
5907
|
if (record.messageId !== null) {
|
|
5846
|
-
await this.
|
|
5908
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, `${t(locale, 'auth_candidate_deleted', { value: candidate.name })}${restarted ? `\n${t(locale, 'auth_delete_current_restarted')}` : ''}\n\n${renderAuthListMessage(locale, state, this.authDisplayBotLabel(), parseWeixinBridgeScope(event.scopeId) !== null, record)}`, authChoiceKeyboard(locale, record));
|
|
5847
5909
|
}
|
|
5848
5910
|
}
|
|
5849
5911
|
async handleAuthToggleCallback(event, localId, index, locale) {
|
|
@@ -5868,7 +5930,7 @@ export class BridgeSessionCore {
|
|
|
5868
5930
|
clampCodexAuthListOffset(record);
|
|
5869
5931
|
await this.messaging.answerCallback(event.callbackQueryId, t(locale, disabled ? 'auth_candidate_disabled_short' : 'auth_candidate_enabled_short'));
|
|
5870
5932
|
if (record.messageId !== null) {
|
|
5871
|
-
await this.
|
|
5933
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, renderAuthListMessage(locale, state, this.authDisplayBotLabel(), parseWeixinBridgeScope(event.scopeId) !== null, record), authChoiceKeyboard(locale, record));
|
|
5872
5934
|
}
|
|
5873
5935
|
}
|
|
5874
5936
|
async handleAuthSwitchCallback(event, localId, index, locale) {
|
|
@@ -5898,7 +5960,7 @@ export class BridgeSessionCore {
|
|
|
5898
5960
|
const switchLabels = await this.readCodexAuthSwitchLabels(candidate);
|
|
5899
5961
|
const switchingMessage = t(locale, 'auth_switching', this.codexAuthSwitchParams(locale, switchLabels.fromLabel, switchLabels.toLabel));
|
|
5900
5962
|
if (record.messageId !== null) {
|
|
5901
|
-
await this.
|
|
5963
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, switchingMessage, []);
|
|
5902
5964
|
}
|
|
5903
5965
|
const outcome = await this.switchCodexAuthAndRestart(event.scopeId, locale, candidate, false, false);
|
|
5904
5966
|
const state = await this.listCodexAuthState();
|
|
@@ -5906,7 +5968,7 @@ export class BridgeSessionCore {
|
|
|
5906
5968
|
record.createdAt = Date.now();
|
|
5907
5969
|
clampCodexAuthListOffset(record);
|
|
5908
5970
|
if (record.messageId !== null) {
|
|
5909
|
-
await this.
|
|
5971
|
+
await this.editAuthPanelMessage(event.scopeId, record.messageId, [
|
|
5910
5972
|
...this.formatAuthSwitchValidationLines(locale, outcome),
|
|
5911
5973
|
renderAuthListMessage(locale, state, this.authDisplayBotLabel(), parseWeixinBridgeScope(event.scopeId) !== null, record),
|
|
5912
5974
|
].filter(Boolean).join('\n\n'), authChoiceKeyboard(locale, record));
|
|
@@ -6839,6 +6901,7 @@ export class BridgeSessionCore {
|
|
|
6839
6901
|
? buildThreadsKeyboard(locale, threadLikes, binding.threadId)
|
|
6840
6902
|
: buildThreadListKeyboard(locale, threadLikes, listState, binding.threadId);
|
|
6841
6903
|
await this.editHtmlMessage(scopeId, event.messageId, text, keyboard);
|
|
6904
|
+
this.scheduleStalePanelDeletion(scopeId, event.messageId);
|
|
6842
6905
|
}
|
|
6843
6906
|
let callbackText = t(locale, 'thread_opened');
|
|
6844
6907
|
if (this.config.codexAppSyncOnOpen) {
|
|
@@ -7171,9 +7234,11 @@ export class BridgeSessionCore {
|
|
|
7171
7234
|
}
|
|
7172
7235
|
if (messageId !== undefined) {
|
|
7173
7236
|
await this.editMessage(scopeId, messageId, text, whereKeyboard(locale, false));
|
|
7237
|
+
this.scheduleStalePanelDeletion(scopeId, messageId);
|
|
7174
7238
|
return;
|
|
7175
7239
|
}
|
|
7176
|
-
await this.sendMessage(scopeId, text, whereKeyboard(locale, false));
|
|
7240
|
+
const sentMessageId = await this.sendMessage(scopeId, text, whereKeyboard(locale, false));
|
|
7241
|
+
this.scheduleStalePanelDeletion(scopeId, sentMessageId);
|
|
7177
7242
|
return;
|
|
7178
7243
|
}
|
|
7179
7244
|
const readyBinding = await this.ensureThreadReady(scopeId, binding);
|
|
@@ -7185,9 +7250,11 @@ export class BridgeSessionCore {
|
|
|
7185
7250
|
}
|
|
7186
7251
|
if (messageId !== undefined) {
|
|
7187
7252
|
await this.editMessage(scopeId, messageId, text, whereKeyboard(locale, false));
|
|
7253
|
+
this.scheduleStalePanelDeletion(scopeId, messageId);
|
|
7188
7254
|
return;
|
|
7189
7255
|
}
|
|
7190
|
-
await this.sendMessage(scopeId, text, whereKeyboard(locale, false));
|
|
7256
|
+
const sentMessageId = await this.sendMessage(scopeId, text, whereKeyboard(locale, false));
|
|
7257
|
+
this.scheduleStalePanelDeletion(scopeId, sentMessageId);
|
|
7191
7258
|
return;
|
|
7192
7259
|
}
|
|
7193
7260
|
let text = formatWhereMessage(locale, thread, settings, this.config.defaultCwd, access, fastStatus);
|
|
@@ -7196,9 +7263,11 @@ export class BridgeSessionCore {
|
|
|
7196
7263
|
}
|
|
7197
7264
|
if (messageId !== undefined) {
|
|
7198
7265
|
await this.editMessage(scopeId, messageId, text, whereKeyboard(locale, true));
|
|
7266
|
+
this.scheduleStalePanelDeletion(scopeId, messageId);
|
|
7199
7267
|
return;
|
|
7200
7268
|
}
|
|
7201
|
-
await this.sendMessage(scopeId, text, whereKeyboard(locale, true));
|
|
7269
|
+
const sentMessageId = await this.sendMessage(scopeId, text, whereKeyboard(locale, true));
|
|
7270
|
+
this.scheduleStalePanelDeletion(scopeId, sentMessageId);
|
|
7202
7271
|
}
|
|
7203
7272
|
async handleThreadListNavigationCallback(event, action, locale) {
|
|
7204
7273
|
const state = this.threadListPresentationState.get(event.scopeId) ?? {
|
|
@@ -7289,9 +7358,11 @@ export class BridgeSessionCore {
|
|
|
7289
7358
|
: buildThreadListKeyboard(locale, forDisplay, presentationState, binding?.threadId ?? null);
|
|
7290
7359
|
if (messageId !== undefined) {
|
|
7291
7360
|
await this.editHtmlMessage(scopeId, messageId, text, keyboard);
|
|
7361
|
+
this.scheduleStalePanelDeletion(scopeId, messageId);
|
|
7292
7362
|
return;
|
|
7293
7363
|
}
|
|
7294
|
-
await this.sendHtmlMessage(scopeId, text, keyboard);
|
|
7364
|
+
const sentMessageId = await this.sendHtmlMessage(scopeId, text, keyboard);
|
|
7365
|
+
this.scheduleStalePanelDeletion(scopeId, sentMessageId);
|
|
7295
7366
|
}
|
|
7296
7367
|
async showModelSettingsPanel(scopeId, messageId, locale = this.localeForChat(scopeId)) {
|
|
7297
7368
|
const models = await this.app.listModels();
|
|
@@ -7304,9 +7375,11 @@ export class BridgeSessionCore {
|
|
|
7304
7375
|
const keyboard = buildModelSettingsKeyboard(locale, models, settings);
|
|
7305
7376
|
if (messageId !== undefined) {
|
|
7306
7377
|
await this.editHtmlMessage(scopeId, messageId, text, keyboard);
|
|
7378
|
+
this.scheduleStalePanelDeletion(scopeId, messageId);
|
|
7307
7379
|
return;
|
|
7308
7380
|
}
|
|
7309
|
-
await this.sendHtmlMessage(scopeId, text, keyboard);
|
|
7381
|
+
const sentMessageId = await this.sendHtmlMessage(scopeId, text, keyboard);
|
|
7382
|
+
this.scheduleStalePanelDeletion(scopeId, sentMessageId);
|
|
7310
7383
|
}
|
|
7311
7384
|
async showSetupPanel(scopeId, focus, messageId, locale = this.localeForChat(scopeId)) {
|
|
7312
7385
|
const models = await this.app.listModels();
|
|
@@ -7321,9 +7394,11 @@ export class BridgeSessionCore {
|
|
|
7321
7394
|
const keyboard = buildSetupPanelKeyboard(locale, { focus, models, settings, access });
|
|
7322
7395
|
if (messageId !== undefined) {
|
|
7323
7396
|
await this.editHtmlMessage(scopeId, messageId, text, keyboard);
|
|
7397
|
+
this.scheduleStalePanelDeletion(scopeId, messageId);
|
|
7324
7398
|
return;
|
|
7325
7399
|
}
|
|
7326
|
-
await this.sendHtmlMessage(scopeId, text, keyboard);
|
|
7400
|
+
const sentMessageId = await this.sendHtmlMessage(scopeId, text, keyboard);
|
|
7401
|
+
this.scheduleStalePanelDeletion(scopeId, sentMessageId);
|
|
7327
7402
|
}
|
|
7328
7403
|
async showAccessSettingsPanel(scopeId, messageId, locale = this.localeForChat(scopeId)) {
|
|
7329
7404
|
const access = this.resolveEffectiveAccess(scopeId);
|
|
@@ -7335,9 +7410,11 @@ export class BridgeSessionCore {
|
|
|
7335
7410
|
const keyboard = buildAccessSettingsKeyboard(locale, access);
|
|
7336
7411
|
if (messageId !== undefined) {
|
|
7337
7412
|
await this.editHtmlMessage(scopeId, messageId, text, keyboard);
|
|
7413
|
+
this.scheduleStalePanelDeletion(scopeId, messageId);
|
|
7338
7414
|
return;
|
|
7339
7415
|
}
|
|
7340
|
-
await this.sendHtmlMessage(scopeId, text, keyboard);
|
|
7416
|
+
const sentMessageId = await this.sendHtmlMessage(scopeId, text, keyboard);
|
|
7417
|
+
this.scheduleStalePanelDeletion(scopeId, sentMessageId);
|
|
7341
7418
|
}
|
|
7342
7419
|
async handleSettingsCallback(event, kind, rawValue, locale) {
|
|
7343
7420
|
await this.handleSetupCallback(event, kind, rawValue, locale);
|
|
@@ -9004,12 +9081,14 @@ function parseRichAuthCandidateRow(line) {
|
|
|
9004
9081
|
if (!name) {
|
|
9005
9082
|
return null;
|
|
9006
9083
|
}
|
|
9007
|
-
const quotas = parts.map(
|
|
9084
|
+
const quotas = parts.map(parseRichAuthQuotaCell);
|
|
9008
9085
|
const statusParts = splitRichAuthStatus(status);
|
|
9009
9086
|
return {
|
|
9010
9087
|
index: match[1],
|
|
9011
|
-
quotaA: quotas[0] ?? '-',
|
|
9012
|
-
|
|
9088
|
+
quotaA: quotas[0]?.value ?? '-',
|
|
9089
|
+
quotaAReset: quotas[0]?.reset ?? '-',
|
|
9090
|
+
quotaB: quotas[1]?.value ?? '-',
|
|
9091
|
+
quotaBReset: quotas[1]?.reset ?? '-',
|
|
9013
9092
|
name,
|
|
9014
9093
|
current,
|
|
9015
9094
|
enabled: statusParts.health === 'disabled' || statusParts.health === '已禁用'
|
|
@@ -9021,12 +9100,14 @@ function parseRichAuthCandidateRow(line) {
|
|
|
9021
9100
|
function formatRichAuthCandidateTable(rows) {
|
|
9022
9101
|
return [
|
|
9023
9102
|
'<table bordered striped>',
|
|
9024
|
-
'<tr><th>#</th><th>Quota A</th><th>Quota B</th><th>Auth</th><th>Current</th><th>Plan</th><th>Health</th><th>Last refresh</th><th>Expiry</th><th>Risk</th><th>Command</th></tr>',
|
|
9103
|
+
'<tr><th>#</th><th>Quota A</th><th>A reset</th><th>Quota B</th><th>B reset</th><th>Auth</th><th>Current</th><th>Plan</th><th>Health</th><th>Last refresh</th><th>Expiry</th><th>Risk</th><th>Command</th></tr>',
|
|
9025
9104
|
...rows.map(row => [
|
|
9026
9105
|
'<tr>',
|
|
9027
9106
|
`<td>${escapeTelegramHtml(row.index)}</td>`,
|
|
9028
9107
|
`<td>${escapeTelegramHtml(row.quotaA)}</td>`,
|
|
9108
|
+
`<td>${escapeTelegramHtml(row.quotaAReset)}</td>`,
|
|
9029
9109
|
`<td>${escapeTelegramHtml(row.quotaB)}</td>`,
|
|
9110
|
+
`<td>${escapeTelegramHtml(row.quotaBReset)}</td>`,
|
|
9030
9111
|
`<td>${formatRichAuthCommandLink(`/auth use ${row.index}`, row.name)}</td>`,
|
|
9031
9112
|
`<td>${row.current ? 'yes' : '-'}</td>`,
|
|
9032
9113
|
`<td>${escapeTelegramHtml(row.plan)}</td>`,
|
|
@@ -9077,16 +9158,17 @@ function formatRichAuthRisk(health) {
|
|
|
9077
9158
|
}
|
|
9078
9159
|
return '-';
|
|
9079
9160
|
}
|
|
9080
|
-
function
|
|
9161
|
+
function parseRichAuthQuotaCell(value) {
|
|
9081
9162
|
const trimmed = value.trim();
|
|
9082
9163
|
if (!trimmed || trimmed === '--' || trimmed === '—') {
|
|
9083
|
-
return '-';
|
|
9164
|
+
return { value: '-', reset: '-' };
|
|
9084
9165
|
}
|
|
9085
|
-
const [
|
|
9166
|
+
const [quotaPart, reset = '-'] = trimmed.split('@', 2);
|
|
9167
|
+
const [windowLabel, percent] = quotaPart.split(':');
|
|
9086
9168
|
if (!windowLabel || percent === undefined) {
|
|
9087
|
-
return
|
|
9169
|
+
return { value: quotaPart, reset };
|
|
9088
9170
|
}
|
|
9089
|
-
return `${percent}
|
|
9171
|
+
return { value: `${percent}%`, reset };
|
|
9090
9172
|
}
|
|
9091
9173
|
function formatRichAuthCommandCell(row) {
|
|
9092
9174
|
const commands = [formatRichAuthCommandLink(`/auth use ${row.index}`, 'use')];
|
|
@@ -9314,6 +9396,12 @@ function formatConfigMessage(locale, result, appConfig, authPoolStats) {
|
|
|
9314
9396
|
value: t(locale, appConfig.authAutoDeleteNeedsRepair ? 'yes' : 'no'),
|
|
9315
9397
|
}));
|
|
9316
9398
|
lines.push(`AUTH_AUTO_DELETE_NEEDS_REPAIR=${appConfig.authAutoDeleteNeedsRepair ? 'true' : 'false'}`);
|
|
9399
|
+
lines.push(t(locale, 'config_delete_tool_details_after_final', {
|
|
9400
|
+
value: t(locale, appConfig.telegramDeleteToolDetailsAfterFinal ? 'yes' : 'no'),
|
|
9401
|
+
}));
|
|
9402
|
+
lines.push(`TELEGRAM_DELETE_TOOL_DETAILS_AFTER_FINAL=${appConfig.telegramDeleteToolDetailsAfterFinal ? 'true' : 'false'}`);
|
|
9403
|
+
lines.push(t(locale, 'config_panel_ttl', { value: formatDurationMs(locale, appConfig.telegramPanelTtlMs) }));
|
|
9404
|
+
lines.push(`TELEGRAM_PANEL_TTL_MS=${appConfig.telegramPanelTtlMs}`);
|
|
9317
9405
|
lines.push(formatCodexAuthPoolSummary(locale, authPoolStats));
|
|
9318
9406
|
return lines.join('\n');
|
|
9319
9407
|
}
|
|
@@ -9328,11 +9416,32 @@ function isInvalidCodexAuthDeleteReason(reason) {
|
|
|
9328
9416
|
return reason === AUTH_DELETE_REASON_NEEDS_REPAIR;
|
|
9329
9417
|
}
|
|
9330
9418
|
function configKeyboard(locale, appConfig) {
|
|
9331
|
-
const
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9419
|
+
const authAutoDeleteEnabled = appConfig.authAutoDeleteNeedsRepair;
|
|
9420
|
+
const deleteToolDetailsEnabled = appConfig.telegramDeleteToolDetailsAfterFinal;
|
|
9421
|
+
return [
|
|
9422
|
+
[{
|
|
9423
|
+
text: t(locale, authAutoDeleteEnabled ? 'button_config_auth_auto_delete_off' : 'button_config_auth_auto_delete_on'),
|
|
9424
|
+
callback_data: `config:auth_auto_delete:${authAutoDeleteEnabled ? 'off' : 'on'}`,
|
|
9425
|
+
}],
|
|
9426
|
+
[{
|
|
9427
|
+
text: t(locale, deleteToolDetailsEnabled ? 'button_config_delete_tool_details_off' : 'button_config_delete_tool_details_on'),
|
|
9428
|
+
callback_data: `config:delete_tool_details:${deleteToolDetailsEnabled ? 'off' : 'on'}`,
|
|
9429
|
+
}],
|
|
9430
|
+
];
|
|
9431
|
+
}
|
|
9432
|
+
function formatDurationMs(locale, value) {
|
|
9433
|
+
if (value <= 0) {
|
|
9434
|
+
return locale === 'zh' ? '关闭' : 'disabled';
|
|
9435
|
+
}
|
|
9436
|
+
if (value % 60_000 === 0) {
|
|
9437
|
+
const minutes = value / 60_000;
|
|
9438
|
+
return locale === 'zh' ? `${minutes} 分钟` : `${minutes} min`;
|
|
9439
|
+
}
|
|
9440
|
+
if (value % 1000 === 0) {
|
|
9441
|
+
const seconds = value / 1000;
|
|
9442
|
+
return locale === 'zh' ? `${seconds} 秒` : `${seconds} sec`;
|
|
9443
|
+
}
|
|
9444
|
+
return `${value}ms`;
|
|
9336
9445
|
}
|
|
9337
9446
|
function parseConfigBooleanArg(value) {
|
|
9338
9447
|
const normalized = value?.trim().toLowerCase();
|
|
@@ -11135,8 +11244,10 @@ function authQuotaSnapshotFromRateLimit(snapshot, accountId = null, quotaIdentit
|
|
|
11135
11244
|
planType: snapshot.planType,
|
|
11136
11245
|
primaryWindowDurationMins: snapshot.primary?.windowDurationMins ?? null,
|
|
11137
11246
|
primaryRemainingPercent: snapshot.primary ? remainingUsagePercent(snapshot.primary.usedPercent) : null,
|
|
11247
|
+
primaryResetsAt: snapshot.primary?.resetsAt ?? null,
|
|
11138
11248
|
secondaryWindowDurationMins: snapshot.secondary?.windowDurationMins ?? null,
|
|
11139
11249
|
secondaryRemainingPercent: snapshot.secondary ? remainingUsagePercent(snapshot.secondary.usedPercent) : null,
|
|
11250
|
+
secondaryResetsAt: snapshot.secondary?.resetsAt ?? null,
|
|
11140
11251
|
};
|
|
11141
11252
|
}
|
|
11142
11253
|
function codexAuthQuotaSnapshotFromRecord(record) {
|
|
@@ -11147,8 +11258,10 @@ function codexAuthQuotaSnapshotFromRecord(record) {
|
|
|
11147
11258
|
planType: record.planType,
|
|
11148
11259
|
primaryWindowDurationMins: record.primaryWindowDurationMins,
|
|
11149
11260
|
primaryRemainingPercent: record.primaryRemainingPercent,
|
|
11261
|
+
primaryResetsAt: record.primaryResetsAt,
|
|
11150
11262
|
secondaryWindowDurationMins: record.secondaryWindowDurationMins,
|
|
11151
11263
|
secondaryRemainingPercent: record.secondaryRemainingPercent,
|
|
11264
|
+
secondaryResetsAt: record.secondaryResetsAt,
|
|
11152
11265
|
};
|
|
11153
11266
|
}
|
|
11154
11267
|
function mergeCodexAuthQuotaSnapshots(current, incoming) {
|
|
@@ -11171,8 +11284,10 @@ function isFiniteCodexAuthQuotaSnapshotRecord(record) {
|
|
|
11171
11284
|
&& isNullableString(record.planType)
|
|
11172
11285
|
&& isNullableFiniteNumber(record.primaryWindowDurationMins)
|
|
11173
11286
|
&& isNullableFiniteNumber(record.primaryRemainingPercent)
|
|
11287
|
+
&& isNullableFiniteNumber(record.primaryResetsAt)
|
|
11174
11288
|
&& isNullableFiniteNumber(record.secondaryWindowDurationMins)
|
|
11175
|
-
&& isNullableFiniteNumber(record.secondaryRemainingPercent)
|
|
11289
|
+
&& isNullableFiniteNumber(record.secondaryRemainingPercent)
|
|
11290
|
+
&& isNullableFiniteNumber(record.secondaryResetsAt);
|
|
11176
11291
|
}
|
|
11177
11292
|
function remainingUsagePercent(usedPercent) {
|
|
11178
11293
|
if (!Number.isFinite(usedPercent)) {
|
|
@@ -11185,12 +11300,12 @@ function formatAuthQuotaPrefix(locale, snapshot) {
|
|
|
11185
11300
|
return '--';
|
|
11186
11301
|
}
|
|
11187
11302
|
const windows = [
|
|
11188
|
-
[snapshot.primaryWindowDurationMins, snapshot.primaryRemainingPercent, 'primary'],
|
|
11189
|
-
[snapshot.secondaryWindowDurationMins, snapshot.secondaryRemainingPercent, 'secondary'],
|
|
11303
|
+
[snapshot.primaryWindowDurationMins, snapshot.primaryRemainingPercent, snapshot.primaryResetsAt, 'primary'],
|
|
11304
|
+
[snapshot.secondaryWindowDurationMins, snapshot.secondaryRemainingPercent, snapshot.secondaryResetsAt, 'secondary'],
|
|
11190
11305
|
];
|
|
11191
11306
|
const values = windows
|
|
11192
|
-
.filter(([duration, remaining]) => duration !== null || remaining !== null)
|
|
11193
|
-
.map(([duration, remaining, fallback]) => (`${formatCompactRateLimitWindowLabel(locale, duration, fallback)}:${remaining === null ? '--' : formatUsagePercent(remaining)}`));
|
|
11307
|
+
.filter(([duration, remaining, resetsAt]) => duration !== null || remaining !== null || resetsAt !== null)
|
|
11308
|
+
.map(([duration, remaining, resetsAt, fallback]) => (`${formatCompactRateLimitWindowLabel(locale, duration, fallback)}:${remaining === null ? '--' : formatUsagePercent(remaining)}${resetsAt === null ? '' : `@${formatLocalTimestamp(resetsAt)}`}`));
|
|
11194
11309
|
return values.length > 0 ? values.join('|') : '--';
|
|
11195
11310
|
}
|
|
11196
11311
|
function formatAuthQuotaButtonPrefix(snapshot) {
|
|
@@ -11216,8 +11331,10 @@ function isCodexAuthQuotaSnapshot(value) {
|
|
|
11216
11331
|
&& (snapshot.planType === undefined || isNullableString(snapshot.planType))
|
|
11217
11332
|
&& (snapshot.primaryWindowDurationMins === undefined || isNullableFiniteNumber(snapshot.primaryWindowDurationMins))
|
|
11218
11333
|
&& isNullableFiniteNumber(snapshot.primaryRemainingPercent)
|
|
11334
|
+
&& (snapshot.primaryResetsAt === undefined || isNullableFiniteNumber(snapshot.primaryResetsAt))
|
|
11219
11335
|
&& (snapshot.secondaryWindowDurationMins === undefined || isNullableFiniteNumber(snapshot.secondaryWindowDurationMins))
|
|
11220
|
-
&& isNullableFiniteNumber(snapshot.secondaryRemainingPercent)
|
|
11336
|
+
&& isNullableFiniteNumber(snapshot.secondaryRemainingPercent)
|
|
11337
|
+
&& (snapshot.secondaryResetsAt === undefined || isNullableFiniteNumber(snapshot.secondaryResetsAt));
|
|
11221
11338
|
}
|
|
11222
11339
|
function normalizeCodexAuthQuotaSnapshot(snapshot) {
|
|
11223
11340
|
return {
|
|
@@ -11227,8 +11344,10 @@ function normalizeCodexAuthQuotaSnapshot(snapshot) {
|
|
|
11227
11344
|
planType: snapshot.planType ?? null,
|
|
11228
11345
|
primaryWindowDurationMins: snapshot.primaryWindowDurationMins ?? null,
|
|
11229
11346
|
primaryRemainingPercent: snapshot.primaryRemainingPercent,
|
|
11347
|
+
primaryResetsAt: snapshot.primaryResetsAt ?? null,
|
|
11230
11348
|
secondaryWindowDurationMins: snapshot.secondaryWindowDurationMins ?? null,
|
|
11231
11349
|
secondaryRemainingPercent: snapshot.secondaryRemainingPercent,
|
|
11350
|
+
secondaryResetsAt: snapshot.secondaryResetsAt ?? null,
|
|
11232
11351
|
};
|
|
11233
11352
|
}
|
|
11234
11353
|
function chatGptAuthMetadataCompatible(left, right) {
|
package/dist/i18n.d.ts
CHANGED
|
@@ -282,6 +282,8 @@ declare const MESSAGES: {
|
|
|
282
282
|
readonly button_auth_filter_attention: "Attention";
|
|
283
283
|
readonly button_config_auth_auto_delete_on: "Auto-delete on";
|
|
284
284
|
readonly button_config_auth_auto_delete_off: "Auto-delete off";
|
|
285
|
+
readonly button_config_delete_tool_details_on: "Delete tool details";
|
|
286
|
+
readonly button_config_delete_tool_details_off: "Keep tool details";
|
|
285
287
|
readonly another_turn_running: "Another turn is already running. Use /interrupt, /takeover, /queue, or wait.";
|
|
286
288
|
readonly working: "Working...";
|
|
287
289
|
readonly usage_open: "Usage: /open <n>";
|
|
@@ -617,6 +619,10 @@ declare const MESSAGES: {
|
|
|
617
619
|
readonly config_auth_auto_delete_needs_repair: "Auto-delete unrecoverable auth candidates: {value}";
|
|
618
620
|
readonly config_auth_auto_delete_updated: "Auto-delete unrecoverable auth candidates set to: {value}";
|
|
619
621
|
readonly config_auth_auto_delete_usage: "Usage: /config auth_auto_delete <on|off>";
|
|
622
|
+
readonly config_delete_tool_details_after_final: "Delete operation details after final reply: {value}";
|
|
623
|
+
readonly config_delete_tool_details_updated: "Delete operation details after final reply set to: {value}";
|
|
624
|
+
readonly config_delete_tool_details_usage: "Usage: /config delete_tool_details <on|off>";
|
|
625
|
+
readonly config_panel_ttl: "Interactive panel cleanup: {value}";
|
|
620
626
|
readonly config_env_update_failed: "Runtime setting changed, but updating {value} failed: {error}";
|
|
621
627
|
readonly requirements_title: "Config requirements";
|
|
622
628
|
readonly requirements_empty: "No config requirements are configured.";
|
|
@@ -982,6 +988,8 @@ declare const MESSAGES: {
|
|
|
982
988
|
readonly button_auth_filter_attention: "需关注";
|
|
983
989
|
readonly button_config_auth_auto_delete_on: "开启自动剔除";
|
|
984
990
|
readonly button_config_auth_auto_delete_off: "关闭自动剔除";
|
|
991
|
+
readonly button_config_delete_tool_details_on: "删除操作明细";
|
|
992
|
+
readonly button_config_delete_tool_details_off: "保留操作明细";
|
|
985
993
|
readonly another_turn_running: "已经有一个回复在进行中。请先等待,或使用 /interrupt、/takeover、/queue。";
|
|
986
994
|
readonly working: "处理中...";
|
|
987
995
|
readonly usage_open: "用法:/open <编号>";
|
|
@@ -1317,6 +1325,10 @@ declare const MESSAGES: {
|
|
|
1317
1325
|
readonly config_auth_auto_delete_needs_repair: "自动剔除无法恢复的 auth 候选:{value}";
|
|
1318
1326
|
readonly config_auth_auto_delete_updated: "自动剔除无法恢复的 auth 候选已设置为:{value}";
|
|
1319
1327
|
readonly config_auth_auto_delete_usage: "用法:/config auth_auto_delete <on|off>";
|
|
1328
|
+
readonly config_delete_tool_details_after_final: "最终回复后删除操作明细:{value}";
|
|
1329
|
+
readonly config_delete_tool_details_updated: "最终回复后删除操作明细已设置为:{value}";
|
|
1330
|
+
readonly config_delete_tool_details_usage: "用法:/config delete_tool_details <on|off>";
|
|
1331
|
+
readonly config_panel_ttl: "交互面板自动清理:{value}";
|
|
1320
1332
|
readonly config_env_update_failed: "运行时设置已改变,但更新 {value} 失败:{error}";
|
|
1321
1333
|
readonly requirements_title: "配置要求";
|
|
1322
1334
|
readonly requirements_empty: "当前没有配置要求。";
|
package/dist/i18n.js
CHANGED
|
@@ -280,6 +280,8 @@ const MESSAGES = {
|
|
|
280
280
|
button_auth_filter_attention: 'Attention',
|
|
281
281
|
button_config_auth_auto_delete_on: 'Auto-delete on',
|
|
282
282
|
button_config_auth_auto_delete_off: 'Auto-delete off',
|
|
283
|
+
button_config_delete_tool_details_on: 'Delete tool details',
|
|
284
|
+
button_config_delete_tool_details_off: 'Keep tool details',
|
|
283
285
|
another_turn_running: 'Another turn is already running. Use /interrupt, /takeover, /queue, or wait.',
|
|
284
286
|
working: 'Working...',
|
|
285
287
|
usage_open: 'Usage: /open <n>',
|
|
@@ -615,6 +617,10 @@ const MESSAGES = {
|
|
|
615
617
|
config_auth_auto_delete_needs_repair: 'Auto-delete unrecoverable auth candidates: {value}',
|
|
616
618
|
config_auth_auto_delete_updated: 'Auto-delete unrecoverable auth candidates set to: {value}',
|
|
617
619
|
config_auth_auto_delete_usage: 'Usage: /config auth_auto_delete <on|off>',
|
|
620
|
+
config_delete_tool_details_after_final: 'Delete operation details after final reply: {value}',
|
|
621
|
+
config_delete_tool_details_updated: 'Delete operation details after final reply set to: {value}',
|
|
622
|
+
config_delete_tool_details_usage: 'Usage: /config delete_tool_details <on|off>',
|
|
623
|
+
config_panel_ttl: 'Interactive panel cleanup: {value}',
|
|
618
624
|
config_env_update_failed: 'Runtime setting changed, but updating {value} failed: {error}',
|
|
619
625
|
requirements_title: 'Config requirements',
|
|
620
626
|
requirements_empty: 'No config requirements are configured.',
|
|
@@ -980,6 +986,8 @@ const MESSAGES = {
|
|
|
980
986
|
button_auth_filter_attention: '需关注',
|
|
981
987
|
button_config_auth_auto_delete_on: '开启自动剔除',
|
|
982
988
|
button_config_auth_auto_delete_off: '关闭自动剔除',
|
|
989
|
+
button_config_delete_tool_details_on: '删除操作明细',
|
|
990
|
+
button_config_delete_tool_details_off: '保留操作明细',
|
|
983
991
|
another_turn_running: '已经有一个回复在进行中。请先等待,或使用 /interrupt、/takeover、/queue。',
|
|
984
992
|
working: '处理中...',
|
|
985
993
|
usage_open: '用法:/open <编号>',
|
|
@@ -1315,6 +1323,10 @@ const MESSAGES = {
|
|
|
1315
1323
|
config_auth_auto_delete_needs_repair: '自动剔除无法恢复的 auth 候选:{value}',
|
|
1316
1324
|
config_auth_auto_delete_updated: '自动剔除无法恢复的 auth 候选已设置为:{value}',
|
|
1317
1325
|
config_auth_auto_delete_usage: '用法:/config auth_auto_delete <on|off>',
|
|
1326
|
+
config_delete_tool_details_after_final: '最终回复后删除操作明细:{value}',
|
|
1327
|
+
config_delete_tool_details_updated: '最终回复后删除操作明细已设置为:{value}',
|
|
1328
|
+
config_delete_tool_details_usage: '用法:/config delete_tool_details <on|off>',
|
|
1329
|
+
config_panel_ttl: '交互面板自动清理:{value}',
|
|
1318
1330
|
config_env_update_failed: '运行时设置已改变,但更新 {value} 失败:{error}',
|
|
1319
1331
|
requirements_title: '配置要求',
|
|
1320
1332
|
requirements_empty: '当前没有配置要求。',
|
package/dist/store/database.d.ts
CHANGED
|
@@ -35,8 +35,10 @@ export interface CodexAuthQuotaSnapshotRecord {
|
|
|
35
35
|
planType: string | null;
|
|
36
36
|
primaryWindowDurationMins: number | null;
|
|
37
37
|
primaryRemainingPercent: number | null;
|
|
38
|
+
primaryResetsAt: number | null;
|
|
38
39
|
secondaryWindowDurationMins: number | null;
|
|
39
40
|
secondaryRemainingPercent: number | null;
|
|
41
|
+
secondaryResetsAt: number | null;
|
|
40
42
|
updatedAt: number;
|
|
41
43
|
}
|
|
42
44
|
export interface CodexAuthPoolStats {
|
|
@@ -136,7 +138,7 @@ export declare class BridgeStore {
|
|
|
136
138
|
recordCodexAuthCandidateInvalidDelete(name: string, reason?: string | null): void;
|
|
137
139
|
recordCodexAuthCandidateRemoved(name: string, reason?: string | null): void;
|
|
138
140
|
getCodexAuthPoolStats(): CodexAuthPoolStats;
|
|
139
|
-
setCodexAuthQuotaSnapshot(runtimeId: string, candidateName: string, accountId: string, quotaIdentityId: string, snapshot: Pick<CodexAuthQuotaSnapshotRecord, 'capturedAtMs' | 'planType' | 'primaryWindowDurationMins' | 'primaryRemainingPercent' | 'secondaryWindowDurationMins' | 'secondaryRemainingPercent'>): void;
|
|
141
|
+
setCodexAuthQuotaSnapshot(runtimeId: string, candidateName: string, accountId: string, quotaIdentityId: string, snapshot: Pick<CodexAuthQuotaSnapshotRecord, 'capturedAtMs' | 'planType' | 'primaryWindowDurationMins' | 'primaryRemainingPercent' | 'secondaryWindowDurationMins' | 'secondaryRemainingPercent'> & Partial<Pick<CodexAuthQuotaSnapshotRecord, 'primaryResetsAt' | 'secondaryResetsAt'>>): void;
|
|
140
142
|
listCodexAuthQuotaSnapshots(quotaIdentityIds: string[]): CodexAuthQuotaSnapshotRecord[];
|
|
141
143
|
private ensureColumn;
|
|
142
144
|
}
|
package/dist/store/database.js
CHANGED
|
@@ -191,8 +191,10 @@ export class BridgeStore {
|
|
|
191
191
|
plan_type TEXT,
|
|
192
192
|
primary_window_duration_mins REAL,
|
|
193
193
|
primary_remaining_percent REAL,
|
|
194
|
+
primary_resets_at INTEGER,
|
|
194
195
|
secondary_window_duration_mins REAL,
|
|
195
196
|
secondary_remaining_percent REAL,
|
|
197
|
+
secondary_resets_at INTEGER,
|
|
196
198
|
updated_at INTEGER NOT NULL,
|
|
197
199
|
PRIMARY KEY (runtime_id, candidate_name)
|
|
198
200
|
);
|
|
@@ -225,7 +227,9 @@ export class BridgeStore {
|
|
|
225
227
|
this.ensureColumn('codex_auth_candidate_runtime', 'state', "TEXT NOT NULL DEFAULT 'active'");
|
|
226
228
|
this.ensureColumn('codex_auth_quota_snapshots', 'plan_type', 'TEXT');
|
|
227
229
|
this.ensureColumn('codex_auth_quota_snapshots', 'primary_window_duration_mins', 'REAL');
|
|
230
|
+
this.ensureColumn('codex_auth_quota_snapshots', 'primary_resets_at', 'INTEGER');
|
|
228
231
|
this.ensureColumn('codex_auth_quota_snapshots', 'secondary_window_duration_mins', 'REAL');
|
|
232
|
+
this.ensureColumn('codex_auth_quota_snapshots', 'secondary_resets_at', 'INTEGER');
|
|
229
233
|
this.ensureColumn('codex_auth_quota_snapshots', 'quota_identity_id', "TEXT NOT NULL DEFAULT ''");
|
|
230
234
|
this.ensureColumn('codex_auth_pool_history', 'invalid_delete_count', 'INTEGER NOT NULL DEFAULT 0');
|
|
231
235
|
this.db.prepare(`
|
|
@@ -997,11 +1001,13 @@ export class BridgeStore {
|
|
|
997
1001
|
plan_type,
|
|
998
1002
|
primary_window_duration_mins,
|
|
999
1003
|
primary_remaining_percent,
|
|
1004
|
+
primary_resets_at,
|
|
1000
1005
|
secondary_window_duration_mins,
|
|
1001
1006
|
secondary_remaining_percent,
|
|
1007
|
+
secondary_resets_at,
|
|
1002
1008
|
updated_at
|
|
1003
1009
|
)
|
|
1004
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1010
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1005
1011
|
ON CONFLICT(runtime_id, candidate_name) DO UPDATE SET
|
|
1006
1012
|
account_id = excluded.account_id,
|
|
1007
1013
|
quota_identity_id = excluded.quota_identity_id,
|
|
@@ -1009,10 +1015,12 @@ export class BridgeStore {
|
|
|
1009
1015
|
plan_type = excluded.plan_type,
|
|
1010
1016
|
primary_window_duration_mins = excluded.primary_window_duration_mins,
|
|
1011
1017
|
primary_remaining_percent = excluded.primary_remaining_percent,
|
|
1018
|
+
primary_resets_at = excluded.primary_resets_at,
|
|
1012
1019
|
secondary_window_duration_mins = excluded.secondary_window_duration_mins,
|
|
1013
1020
|
secondary_remaining_percent = excluded.secondary_remaining_percent,
|
|
1021
|
+
secondary_resets_at = excluded.secondary_resets_at,
|
|
1014
1022
|
updated_at = excluded.updated_at
|
|
1015
|
-
`).run(runtimeId, candidateName, accountId, quotaIdentityId, snapshot.capturedAtMs, snapshot.planType, snapshot.primaryWindowDurationMins, snapshot.primaryRemainingPercent, snapshot.secondaryWindowDurationMins, snapshot.secondaryRemainingPercent, Date.now());
|
|
1023
|
+
`).run(runtimeId, candidateName, accountId, quotaIdentityId, snapshot.capturedAtMs, snapshot.planType, snapshot.primaryWindowDurationMins, snapshot.primaryRemainingPercent, snapshot.primaryResetsAt ?? null, snapshot.secondaryWindowDurationMins, snapshot.secondaryRemainingPercent, snapshot.secondaryResetsAt ?? null, Date.now());
|
|
1016
1024
|
}
|
|
1017
1025
|
listCodexAuthQuotaSnapshots(quotaIdentityIds) {
|
|
1018
1026
|
const uniqueQuotaIdentityIds = [...new Set(quotaIdentityIds.filter(Boolean))];
|
|
@@ -1030,8 +1038,10 @@ export class BridgeStore {
|
|
|
1030
1038
|
plan_type,
|
|
1031
1039
|
primary_window_duration_mins,
|
|
1032
1040
|
primary_remaining_percent,
|
|
1041
|
+
primary_resets_at,
|
|
1033
1042
|
secondary_window_duration_mins,
|
|
1034
1043
|
secondary_remaining_percent,
|
|
1044
|
+
secondary_resets_at,
|
|
1035
1045
|
updated_at
|
|
1036
1046
|
FROM codex_auth_quota_snapshots
|
|
1037
1047
|
WHERE quota_identity_id IN (${placeholders})
|
|
@@ -1045,8 +1055,10 @@ export class BridgeStore {
|
|
|
1045
1055
|
planType: nullableString(row.plan_type),
|
|
1046
1056
|
primaryWindowDurationMins: nullableNumber(row.primary_window_duration_mins),
|
|
1047
1057
|
primaryRemainingPercent: nullableNumber(row.primary_remaining_percent),
|
|
1058
|
+
primaryResetsAt: nullableNumber(row.primary_resets_at),
|
|
1048
1059
|
secondaryWindowDurationMins: nullableNumber(row.secondary_window_duration_mins),
|
|
1049
1060
|
secondaryRemainingPercent: nullableNumber(row.secondary_remaining_percent),
|
|
1061
|
+
secondaryResetsAt: nullableNumber(row.secondary_resets_at),
|
|
1050
1062
|
updatedAt: Number(row.updated_at),
|
|
1051
1063
|
}));
|
|
1052
1064
|
}
|