@foxden-app/foxclaw 0.5.34 → 0.5.36
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 +20 -0
- package/dist/controller/controller.d.ts +1 -0
- package/dist/controller/controller.js +214 -14
- package/dist/i18n.d.ts +2 -0
- package/dist/i18n.js +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
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.36 - 2026-06-17
|
|
6
|
+
|
|
7
|
+
### 中文
|
|
8
|
+
- `/auth` 的 Telegram RichMessage 候选区从旧的竖线分隔文本整理为横向表格,拆出额度窗口、auth 名称、当前标记、套餐、健康状态、最近刷新、过期时间和风险提示列。
|
|
9
|
+
- ChatGPT auth 候选会从 access token `exp` 读取过期时间,并以 UTC `YYYY-MM-DD HH:mmZ` 展示;拿不到过期字段时保持 `-`,原始文本 fallback 仍可展开查看。
|
|
10
|
+
|
|
11
|
+
### English
|
|
12
|
+
- Refined the Telegram RichMessage `/auth` candidate area from pipe-delimited text into a horizontally scrollable table with quota windows, auth name, current marker, plan, health, last refresh, expiry, and risk columns.
|
|
13
|
+
- ChatGPT auth candidates now read the access token `exp` value and display expiry as UTC `YYYY-MM-DD HH:mmZ`; candidates without an expiry field show `-`, with the expandable plain-text fallback preserved.
|
|
14
|
+
|
|
15
|
+
## 0.5.35 - 2026-06-17
|
|
16
|
+
|
|
17
|
+
### 中文
|
|
18
|
+
- `/status`、`/auth`、`/auth sync status/events/trace` 在 Telegram 上优先使用 RichMessage 展示,内部状态会自动整理成标题、表格、列表和可展开原文。
|
|
19
|
+
- 保留微信和 RichMessage 失败 fallback:微信继续走原文本通道,Telegram rich 发送失败时退回原 HTML/plain 内容,不影响按钮和回调。
|
|
20
|
+
|
|
21
|
+
### English
|
|
22
|
+
- `/status`, `/auth`, and `/auth sync status/events/trace` now prefer RichMessage on Telegram, rendering internal diagnostics as headings, tables, lists, and expandable plain-text details.
|
|
23
|
+
- Weixin and RichMessage failure fallbacks are preserved: Weixin keeps the previous text path, and Telegram falls back without breaking buttons or callbacks.
|
|
24
|
+
|
|
5
25
|
## 0.5.34 - 2026-06-17
|
|
6
26
|
|
|
7
27
|
### 中文
|
|
@@ -229,6 +229,7 @@ export declare class BridgeSessionCore {
|
|
|
229
229
|
private sendHtmlMessage;
|
|
230
230
|
private sendRichHtmlMessage;
|
|
231
231
|
private sendRichMarkdownMessage;
|
|
232
|
+
private sendRichInternalMessage;
|
|
232
233
|
private editMessage;
|
|
233
234
|
private editHtmlMessage;
|
|
234
235
|
private editRichHtmlMessage;
|
|
@@ -3,7 +3,8 @@ import fs from 'node:fs/promises';
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { normalizeLocale, t } from '../i18n.js';
|
|
6
|
-
import { chatGptAuthMetadataMatchesCandidateName, parseChatGptAuthMetadata, readChatGptAuthMetadata, } from '../auth/mirror.js';
|
|
6
|
+
import { chatGptAuthMetadataMatchesCandidateName, parseChatGptAuthMetadata, readChatGptAuthRecord, readChatGptAuthMetadata, } from '../auth/mirror.js';
|
|
7
|
+
import { readAccessTokenExpiresAtMs } from '../auth/cross_node_sync.js';
|
|
7
8
|
import { parseCommand } from './commands.js';
|
|
8
9
|
import { buildAccessSettingsKeyboard, buildModelSettingsKeyboard, buildSetupPanelKeyboard, buildThreadListKeyboard, buildThreadsKeyboard, clampEffortToModel, formatAccessPresetLabel, formatActiveTurnMessageModeLabel, formatAccessSettingsMessage, formatApprovalPolicyLabel, formatCollaborationModeLabel, formatModelSettingsMessage, formatSandboxModeLabel, formatServiceTierStatusLabel, formatSetupPanelMessage, formatThreadContextSummary, formatThreadsMessage, formatWeixinAccessCopyPaste, formatWeixinModelCopyPaste, formatWeixinThreadsCopyPaste, formatWeixinWhereNavCopyPaste, formatWhereMessage, normalizeRequestedEffort, resolveCurrentModel, resolveActiveTurnMessageMode, resolveRequestedModel, } from './presentation.js';
|
|
9
10
|
import { clampServiceTierToModel, resolveFastTierForModel } from './service_tier.js';
|
|
@@ -508,7 +509,7 @@ export class BridgeSessionCore {
|
|
|
508
509
|
}
|
|
509
510
|
lines.push(...codexUsageLines);
|
|
510
511
|
lines.push(...codexLocalUsageLines);
|
|
511
|
-
await this.
|
|
512
|
+
await this.sendRichInternalMessage(scopeId, '/status', lines.join('\n'));
|
|
512
513
|
return;
|
|
513
514
|
}
|
|
514
515
|
case 'account': {
|
|
@@ -3377,6 +3378,12 @@ export class BridgeSessionCore {
|
|
|
3377
3378
|
async sendRichMarkdownMessage(scopeId, text, inlineKeyboard) {
|
|
3378
3379
|
return this.sendRichHtmlMessage(scopeId, renderTelegramMarkdownRichHtml(text), escapeTelegramHtml(text), inlineKeyboard);
|
|
3379
3380
|
}
|
|
3381
|
+
async sendRichInternalMessage(scopeId, title, text, inlineKeyboard) {
|
|
3382
|
+
if (scopeId.startsWith(BRIDGE_SCOPE_WEIXIN_PREFIX)) {
|
|
3383
|
+
return this.sendMessage(scopeId, text, inlineKeyboard);
|
|
3384
|
+
}
|
|
3385
|
+
return this.sendRichHtmlMessage(scopeId, formatRichInternalMessage(title, text), escapeTelegramHtml(text), inlineKeyboard);
|
|
3386
|
+
}
|
|
3380
3387
|
async editMessage(scopeId, messageId, text, inlineKeyboard) {
|
|
3381
3388
|
await this.messaging.editPlain(scopeId, messageId, text, inlineKeyboard);
|
|
3382
3389
|
}
|
|
@@ -4572,17 +4579,20 @@ export class BridgeSessionCore {
|
|
|
4572
4579
|
await this.refreshCurrentCodexAuthQuota(state);
|
|
4573
4580
|
const record = createPendingAuthChoiceList(scopeId, state.candidates, listRequest);
|
|
4574
4581
|
this.pendingAuthChoiceLists.set(record.localId, record);
|
|
4575
|
-
const
|
|
4582
|
+
const authListMessage = renderAuthListMessage(locale, state, this.authDisplayBotLabel(), parseWeixinBridgeScope(scopeId) !== null, record);
|
|
4583
|
+
const messageId = await this.sendRichInternalMessage(scopeId, '/auth', authListMessage, authChoiceKeyboard(locale, record));
|
|
4576
4584
|
record.messageId = messageId;
|
|
4577
4585
|
}
|
|
4578
4586
|
async handleAuthSyncCommand(scopeId, locale, args) {
|
|
4579
4587
|
const action = args[0]?.toLowerCase() ?? 'status';
|
|
4580
4588
|
if (action === 'status') {
|
|
4581
|
-
|
|
4589
|
+
const message = formatAuthSyncStatus(locale, this.coordinator?.getAuthSyncStatus?.() ?? null, this.getRuntimeStatus().authProactiveRefresh ?? null);
|
|
4590
|
+
await this.sendRichInternalMessage(scopeId, '/auth sync status', message);
|
|
4582
4591
|
return;
|
|
4583
4592
|
}
|
|
4584
4593
|
if (action === 'events') {
|
|
4585
|
-
|
|
4594
|
+
const message = formatAuthSyncEvents(locale, this.coordinator?.getAuthSyncStatus?.() ?? null, args.slice(1).join(' ').trim() || null);
|
|
4595
|
+
await this.sendRichInternalMessage(scopeId, '/auth sync events', message);
|
|
4586
4596
|
return;
|
|
4587
4597
|
}
|
|
4588
4598
|
if (action === 'trace') {
|
|
@@ -4591,7 +4601,7 @@ export class BridgeSessionCore {
|
|
|
4591
4601
|
await this.sendMessage(scopeId, t(locale, 'auth_sync_trace_missing'));
|
|
4592
4602
|
return;
|
|
4593
4603
|
}
|
|
4594
|
-
await this.
|
|
4604
|
+
await this.sendRichInternalMessage(scopeId, '/auth sync trace', formatAuthSyncTrace(locale, this.coordinator?.getAuthSyncStatus?.() ?? null, requestId));
|
|
4595
4605
|
return;
|
|
4596
4606
|
}
|
|
4597
4607
|
if (action === 'test') {
|
|
@@ -6225,19 +6235,20 @@ export class BridgeSessionCore {
|
|
|
6225
6235
|
}
|
|
6226
6236
|
async readCodexAuthCandidateQuotaIdentities(candidates) {
|
|
6227
6237
|
const entries = await Promise.all(candidates.map(async (candidate) => {
|
|
6228
|
-
const
|
|
6229
|
-
const metadataMatchesName =
|
|
6230
|
-
? chatGptAuthMetadataMatchesCandidateName(candidate.name,
|
|
6238
|
+
const record = await readChatGptAuthRecord(candidate.path);
|
|
6239
|
+
const metadataMatchesName = record
|
|
6240
|
+
? chatGptAuthMetadataMatchesCandidateName(candidate.name, record)
|
|
6231
6241
|
: false;
|
|
6232
|
-
candidate.credentialKind =
|
|
6242
|
+
candidate.credentialKind = record && metadataMatchesName
|
|
6233
6243
|
? 'chatgpt'
|
|
6234
6244
|
: await isCodexApiKeyAuthCandidate(candidate.path)
|
|
6235
6245
|
? 'api-key'
|
|
6236
6246
|
: 'invalid';
|
|
6237
|
-
candidate.credentialLastRefreshMs =
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6247
|
+
candidate.credentialLastRefreshMs = record && metadataMatchesName ? record.lastRefreshMs : null;
|
|
6248
|
+
candidate.credentialExpiresAtMs = record && metadataMatchesName ? readAccessTokenExpiresAtMs(record.raw) : null;
|
|
6249
|
+
return [candidate.name, record && metadataMatchesName ? {
|
|
6250
|
+
accountId: record.accountId,
|
|
6251
|
+
quotaIdentityId: record.quotaIdentityId,
|
|
6241
6252
|
} : null];
|
|
6242
6253
|
}));
|
|
6243
6254
|
const quotaIdentities = new Map();
|
|
@@ -8440,6 +8451,183 @@ function formatRichDemoFallbackMessage(locale) {
|
|
|
8440
8451
|
telegramPreCode('sendRichMessage({ rich_message: { html } })', 'typescript'),
|
|
8441
8452
|
].join('\n');
|
|
8442
8453
|
}
|
|
8454
|
+
function formatRichInternalMessage(title, text) {
|
|
8455
|
+
const sections = splitRichInternalSections(text);
|
|
8456
|
+
const body = sections.map(formatRichInternalSection).filter(Boolean).join('\n');
|
|
8457
|
+
return [
|
|
8458
|
+
`<h3>${escapeTelegramHtml(title)}</h3>`,
|
|
8459
|
+
body || `<p>${escapeTelegramHtml(text)}</p>`,
|
|
8460
|
+
telegramDetails('Plain text', telegramPreCode(text, 'text')),
|
|
8461
|
+
'<footer>FoxClaw · RichMessage</footer>',
|
|
8462
|
+
].join('\n');
|
|
8463
|
+
}
|
|
8464
|
+
function splitRichInternalSections(text) {
|
|
8465
|
+
const sections = [];
|
|
8466
|
+
let current = [];
|
|
8467
|
+
for (const rawLine of text.split('\n')) {
|
|
8468
|
+
const line = rawLine.trimEnd();
|
|
8469
|
+
if (!line.trim()) {
|
|
8470
|
+
if (current.length > 0) {
|
|
8471
|
+
sections.push(current);
|
|
8472
|
+
current = [];
|
|
8473
|
+
}
|
|
8474
|
+
continue;
|
|
8475
|
+
}
|
|
8476
|
+
current.push(line);
|
|
8477
|
+
}
|
|
8478
|
+
if (current.length > 0) {
|
|
8479
|
+
sections.push(current);
|
|
8480
|
+
}
|
|
8481
|
+
return sections;
|
|
8482
|
+
}
|
|
8483
|
+
function formatRichInternalSection(lines) {
|
|
8484
|
+
const candidateRows = lines
|
|
8485
|
+
.map(parseRichAuthCandidateRow)
|
|
8486
|
+
.filter((row) => row !== null);
|
|
8487
|
+
if (candidateRows.length > 0) {
|
|
8488
|
+
const nonCandidateLines = lines.filter(line => parseRichAuthCandidateRow(line) === null);
|
|
8489
|
+
return [
|
|
8490
|
+
nonCandidateLines.length > 0 ? formatRichInternalSectionWithoutCandidates(nonCandidateLines) : '',
|
|
8491
|
+
formatRichAuthCandidateTable(candidateRows),
|
|
8492
|
+
].filter(Boolean).join('\n');
|
|
8493
|
+
}
|
|
8494
|
+
return formatRichInternalSectionWithoutCandidates(lines);
|
|
8495
|
+
}
|
|
8496
|
+
function formatRichInternalSectionWithoutCandidates(lines) {
|
|
8497
|
+
const rows = lines
|
|
8498
|
+
.map(parseRichInternalKeyValue)
|
|
8499
|
+
.filter((row) => row !== null);
|
|
8500
|
+
if (rows.length >= Math.max(2, Math.floor(lines.length * 0.6))) {
|
|
8501
|
+
return [
|
|
8502
|
+
'<table bordered striped>',
|
|
8503
|
+
...rows.map(([key, value]) => (`<tr><td>${escapeTelegramHtml(key)}</td><td>${formatRichInternalValue(value)}</td></tr>`)),
|
|
8504
|
+
'</table>',
|
|
8505
|
+
].join('\n');
|
|
8506
|
+
}
|
|
8507
|
+
return [
|
|
8508
|
+
'<ul>',
|
|
8509
|
+
...lines.map(line => `<li>${formatRichInternalValue(line.replace(/^\s*[-*]\s+/, '').replace(/^\s*\d+[.)]\s+/, ''))}</li>`),
|
|
8510
|
+
'</ul>',
|
|
8511
|
+
].join('\n');
|
|
8512
|
+
}
|
|
8513
|
+
function parseRichAuthCandidateRow(line) {
|
|
8514
|
+
const match = line.match(/^\s*(\d+)\.\s+(.+)$/);
|
|
8515
|
+
if (!match) {
|
|
8516
|
+
return null;
|
|
8517
|
+
}
|
|
8518
|
+
let body = match[2].trim();
|
|
8519
|
+
let status = '';
|
|
8520
|
+
const statusMatch = body.match(/^(.*?)\s+(\[[^\]]+\])$/);
|
|
8521
|
+
if (statusMatch) {
|
|
8522
|
+
body = statusMatch[1].trim();
|
|
8523
|
+
status = statusMatch[2].replace(/^\[|\]$/g, '');
|
|
8524
|
+
}
|
|
8525
|
+
const current = body.endsWith(' *');
|
|
8526
|
+
if (current) {
|
|
8527
|
+
body = body.slice(0, -2).trimEnd();
|
|
8528
|
+
}
|
|
8529
|
+
const parts = body.split('|');
|
|
8530
|
+
const name = parts.pop()?.trim() ?? '';
|
|
8531
|
+
if (!name) {
|
|
8532
|
+
return null;
|
|
8533
|
+
}
|
|
8534
|
+
const quotas = parts.map(formatRichAuthQuotaCell);
|
|
8535
|
+
const statusParts = splitRichAuthStatus(status);
|
|
8536
|
+
return {
|
|
8537
|
+
index: match[1],
|
|
8538
|
+
quotaA: quotas[0] ?? '-',
|
|
8539
|
+
quotaB: quotas[1] ?? '-',
|
|
8540
|
+
name,
|
|
8541
|
+
current,
|
|
8542
|
+
...statusParts,
|
|
8543
|
+
};
|
|
8544
|
+
}
|
|
8545
|
+
function formatRichAuthCandidateTable(rows) {
|
|
8546
|
+
return [
|
|
8547
|
+
'<table bordered striped>',
|
|
8548
|
+
'<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></tr>',
|
|
8549
|
+
...rows.map(row => [
|
|
8550
|
+
'<tr>',
|
|
8551
|
+
`<td>${escapeTelegramHtml(row.index)}</td>`,
|
|
8552
|
+
`<td>${escapeTelegramHtml(row.quotaA)}</td>`,
|
|
8553
|
+
`<td>${escapeTelegramHtml(row.quotaB)}</td>`,
|
|
8554
|
+
`<td>${escapeTelegramHtml(row.name)}</td>`,
|
|
8555
|
+
`<td>${row.current ? 'yes' : '-'}</td>`,
|
|
8556
|
+
`<td>${escapeTelegramHtml(row.plan)}</td>`,
|
|
8557
|
+
`<td>${escapeTelegramHtml(row.health)}</td>`,
|
|
8558
|
+
`<td>${escapeTelegramHtml(row.refresh)}</td>`,
|
|
8559
|
+
`<td>${escapeTelegramHtml(row.expiry)}</td>`,
|
|
8560
|
+
`<td>${escapeTelegramHtml(row.risk)}</td>`,
|
|
8561
|
+
'</tr>',
|
|
8562
|
+
].join('')),
|
|
8563
|
+
'</table>',
|
|
8564
|
+
].join('\n');
|
|
8565
|
+
}
|
|
8566
|
+
function splitRichAuthStatus(status) {
|
|
8567
|
+
const parts = status.split(' · ').map(part => part.trim()).filter(Boolean);
|
|
8568
|
+
const refreshIndex = parts.findIndex(part => /^(refreshed|刷新于)\b/i.test(part));
|
|
8569
|
+
const refresh = refreshIndex >= 0 ? parts.splice(refreshIndex, 1)[0] : '-';
|
|
8570
|
+
const expiryIndex = parts.findIndex(part => /^(expires|过期于)\b/i.test(part));
|
|
8571
|
+
const expiry = expiryIndex >= 0 ? parts.splice(expiryIndex, 1)[0] : '-';
|
|
8572
|
+
const plan = parts.length > 1 ? parts.shift() : '-';
|
|
8573
|
+
const health = parts.shift() ?? '-';
|
|
8574
|
+
return {
|
|
8575
|
+
plan,
|
|
8576
|
+
health,
|
|
8577
|
+
refresh,
|
|
8578
|
+
expiry,
|
|
8579
|
+
risk: formatRichAuthRisk(health),
|
|
8580
|
+
};
|
|
8581
|
+
}
|
|
8582
|
+
function formatRichAuthRisk(health) {
|
|
8583
|
+
if (/not recently refreshed|长期未刷新/i.test(health)) {
|
|
8584
|
+
return `stale >${CODEX_AUTH_STALE_CREDENTIAL_DAYS}d`;
|
|
8585
|
+
}
|
|
8586
|
+
if (/needs login repair|需要登录修复/i.test(health)) {
|
|
8587
|
+
return 'repair';
|
|
8588
|
+
}
|
|
8589
|
+
if (/quota exhausted|额度耗尽/i.test(health)) {
|
|
8590
|
+
return 'quota exhausted';
|
|
8591
|
+
}
|
|
8592
|
+
if (/invalid|无效/i.test(health)) {
|
|
8593
|
+
return 'invalid';
|
|
8594
|
+
}
|
|
8595
|
+
if (/\blow\b|偏低|低/i.test(health)) {
|
|
8596
|
+
return 'low quota';
|
|
8597
|
+
}
|
|
8598
|
+
if (/unknown|未知/i.test(health)) {
|
|
8599
|
+
return 'unknown';
|
|
8600
|
+
}
|
|
8601
|
+
return '-';
|
|
8602
|
+
}
|
|
8603
|
+
function formatRichAuthQuotaCell(value) {
|
|
8604
|
+
const trimmed = value.trim();
|
|
8605
|
+
if (!trimmed || trimmed === '--' || trimmed === '—') {
|
|
8606
|
+
return '-';
|
|
8607
|
+
}
|
|
8608
|
+
const [windowLabel, percent] = trimmed.split(':');
|
|
8609
|
+
if (!windowLabel || percent === undefined) {
|
|
8610
|
+
return trimmed;
|
|
8611
|
+
}
|
|
8612
|
+
return `${windowLabel} ${percent}%`;
|
|
8613
|
+
}
|
|
8614
|
+
function parseRichInternalKeyValue(line) {
|
|
8615
|
+
const separator = line.includes(':') ? ':' : ':';
|
|
8616
|
+
const index = line.indexOf(separator);
|
|
8617
|
+
if (index <= 0) {
|
|
8618
|
+
return null;
|
|
8619
|
+
}
|
|
8620
|
+
const key = line.slice(0, index).trim();
|
|
8621
|
+
const value = line.slice(index + separator.length).trim();
|
|
8622
|
+
if (!key || !value || key.length > 80) {
|
|
8623
|
+
return null;
|
|
8624
|
+
}
|
|
8625
|
+
return [key, value];
|
|
8626
|
+
}
|
|
8627
|
+
function formatRichInternalValue(value) {
|
|
8628
|
+
const escaped = escapeTelegramHtml(value);
|
|
8629
|
+
return escaped.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
8630
|
+
}
|
|
8443
8631
|
function clipRichMessageText(value, limit) {
|
|
8444
8632
|
if (value.length <= limit) {
|
|
8445
8633
|
return value;
|
|
@@ -9197,6 +9385,7 @@ async function listCodexAuthState(disabledNames = new Set(), candidateStates = n
|
|
|
9197
9385
|
mtimeMs: stat.mtimeMs,
|
|
9198
9386
|
credentialKind: 'invalid',
|
|
9199
9387
|
credentialLastRefreshMs: null,
|
|
9388
|
+
credentialExpiresAtMs: null,
|
|
9200
9389
|
quota: null,
|
|
9201
9390
|
});
|
|
9202
9391
|
}
|
|
@@ -9541,6 +9730,11 @@ function formatCodexAuthCandidateStatus(locale, candidate) {
|
|
|
9541
9730
|
value: formatCompactAge(locale, Date.now() - candidate.credentialLastRefreshMs),
|
|
9542
9731
|
}));
|
|
9543
9732
|
}
|
|
9733
|
+
if (candidate.credentialExpiresAtMs !== null) {
|
|
9734
|
+
details.push(t(locale, 'auth_candidate_expires_at', {
|
|
9735
|
+
value: formatUtcDateTime(candidate.credentialExpiresAtMs),
|
|
9736
|
+
}));
|
|
9737
|
+
}
|
|
9544
9738
|
return `[${details.join(' · ')}]`;
|
|
9545
9739
|
}
|
|
9546
9740
|
function formatCodexAuthFilter(locale, filter) {
|
|
@@ -10387,6 +10581,12 @@ function formatCompactAge(locale, ageMs) {
|
|
|
10387
10581
|
const roundedMinutes = Math.floor(minutes);
|
|
10388
10582
|
return locale === 'zh' ? `${roundedMinutes}分钟前` : `${roundedMinutes}m ago`;
|
|
10389
10583
|
}
|
|
10584
|
+
function formatUtcDateTime(timestampMs) {
|
|
10585
|
+
if (!Number.isFinite(timestampMs)) {
|
|
10586
|
+
return '-';
|
|
10587
|
+
}
|
|
10588
|
+
return `${new Date(timestampMs).toISOString().slice(0, 16).replace('T', ' ')}Z`;
|
|
10589
|
+
}
|
|
10390
10590
|
function formatUsagePercent(value) {
|
|
10391
10591
|
if (!Number.isFinite(value)) {
|
|
10392
10592
|
return '?';
|
package/dist/i18n.d.ts
CHANGED
|
@@ -173,6 +173,7 @@ declare const MESSAGES: {
|
|
|
173
173
|
readonly 'auth_candidate_health_api-key': "API key";
|
|
174
174
|
readonly auth_candidate_health_invalid: "invalid auth file";
|
|
175
175
|
readonly auth_candidate_last_refresh: "refreshed {value}";
|
|
176
|
+
readonly auth_candidate_expires_at: "expires {value}";
|
|
176
177
|
readonly auth_candidate_enabled: "Enabled auth candidate for auto-rotation: {value}";
|
|
177
178
|
readonly auth_candidate_disabled: "Disabled auth candidate for auto-rotation: {value}";
|
|
178
179
|
readonly auth_candidate_enabled_short: "Auth enabled";
|
|
@@ -871,6 +872,7 @@ declare const MESSAGES: {
|
|
|
871
872
|
readonly 'auth_candidate_health_api-key': "API key";
|
|
872
873
|
readonly auth_candidate_health_invalid: "auth 文件无效";
|
|
873
874
|
readonly auth_candidate_last_refresh: "刷新于 {value}";
|
|
875
|
+
readonly auth_candidate_expires_at: "过期于 {value}";
|
|
874
876
|
readonly auth_candidate_enabled: "已启用 auth 候选自动轮换:{value}";
|
|
875
877
|
readonly auth_candidate_disabled: "已禁用 auth 候选自动轮换:{value}";
|
|
876
878
|
readonly auth_candidate_enabled_short: "auth 已启用";
|
package/dist/i18n.js
CHANGED
|
@@ -171,6 +171,7 @@ const MESSAGES = {
|
|
|
171
171
|
'auth_candidate_health_api-key': 'API key',
|
|
172
172
|
auth_candidate_health_invalid: 'invalid auth file',
|
|
173
173
|
auth_candidate_last_refresh: 'refreshed {value}',
|
|
174
|
+
auth_candidate_expires_at: 'expires {value}',
|
|
174
175
|
auth_candidate_enabled: 'Enabled auth candidate for auto-rotation: {value}',
|
|
175
176
|
auth_candidate_disabled: 'Disabled auth candidate for auto-rotation: {value}',
|
|
176
177
|
auth_candidate_enabled_short: 'Auth enabled',
|
|
@@ -869,6 +870,7 @@ const MESSAGES = {
|
|
|
869
870
|
'auth_candidate_health_api-key': 'API key',
|
|
870
871
|
auth_candidate_health_invalid: 'auth 文件无效',
|
|
871
872
|
auth_candidate_last_refresh: '刷新于 {value}',
|
|
873
|
+
auth_candidate_expires_at: '过期于 {value}',
|
|
872
874
|
auth_candidate_enabled: '已启用 auth 候选自动轮换:{value}',
|
|
873
875
|
auth_candidate_disabled: '已禁用 auth 候选自动轮换:{value}',
|
|
874
876
|
auth_candidate_enabled_short: 'auth 已启用',
|