@foxden-app/foxclaw 0.4.11 → 0.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,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.4.13 - 2026-06-03
6
+
7
+ ### 中文
8
+ - `/update` 完成回报新增 Codex CLI 版本变化,和 FoxClaw 版本变化并排行展示。
9
+ - `/auth` 文本列表继续保留额度窗口详情,候选按钮改为只显示两个剩余百分比数字,未知值用 `—`,减少窄屏按钮截断。
10
+
11
+ ### English
12
+ - Added Codex CLI from/to version reporting to completed `/update` messages alongside the FoxClaw version change.
13
+ - Kept quota-window detail in `/auth` text rows, while compacting candidate buttons to two remaining-percent numbers with `—` for unknown values.
14
+
15
+ ## 0.4.12 - 2026-06-02
16
+
17
+ ### 中文
18
+ - `/auth` 面板顶部、候选列表和切换按钮省略标准候选名中重复的 `auth.json_` 前缀,让额度与账号标识在窄屏 Telegram 客户端中更容易完整显示。
19
+ - 磁盘文件名、文件名搜索、候选编号、切换和镜像同步行为保持不变;非标准候选名仍原样展示。
20
+
21
+ ### English
22
+ - Omitted the repeated `auth.json_` prefix from the `/auth` panel header, candidate rows, and switch buttons so quota and account labels fit better in narrow Telegram clients.
23
+ - Kept filenames on disk, filename search, candidate numbering, switching, and mirroring unchanged. Non-standard candidate names still render verbatim.
24
+
5
25
  ## 0.4.11 - 2026-06-02
6
26
 
7
27
  ### 中文
package/README.md CHANGED
@@ -85,7 +85,7 @@ DEFAULT_SANDBOX_MODE=workspace-write
85
85
 
86
86
  配置文件默认在 `~/.foxclaw/.env`。想放别处的话设 `FOXCLAW_ENV=/path/to/.env`。
87
87
 
88
- `foxclaw start` 会自动检查环境并安装/重启后台服务。后续升级直接运行 `foxclaw update`,它会沿用当前的 npm/pnpm 全局安装方式,尝试同步升级 npm/pnpm 安装的 Codex CLI,再完成安装、自检和服务重启。
88
+ `foxclaw start` 会自动检查环境并安装/重启后台服务。后续升级直接运行 `foxclaw update`,它会沿用当前的 npm/pnpm 全局安装方式,尝试同步升级 npm/pnpm 安装的 Codex CLI,再完成安装、自检和服务重启,并在聊天回报 FoxClaw 与 Codex CLI 的版本变化。
89
89
 
90
90
  FoxClaw 只响应 `TG_ALLOWED_USER_ID` 的消息——把机器人拉进群不代表群里所有人都能用。
91
91
 
@@ -128,7 +128,7 @@ FoxClaw 的一大特色是自动多账号切换。当一个账号触发用量限
128
128
  3. 用 `/auth` 查看所有候选账号状态。
129
129
  4. 用 `/auth enable <n>` / `/auth disable <n>` 控制哪些账号参与自动轮换。
130
130
 
131
- 候选较多时,`/auth` 每页显示 8 个账号,并提供翻页、`全部 / 已启用 / 需关注` 筛选。也可以用 `/auth list <关键词>` 搜索文件名,或用 `/auth page <页码>` 直接跳页。额度窗口按 Codex 实际返回值展示,例如 `5h:20|7d:25` 或单月窗口 `30d:97`。
131
+ 候选较多时,`/auth` 每页显示 8 个账号,并提供翻页、`全部 / 已启用 / 需关注` 筛选。也可以用 `/auth list <关键词>` 搜索文件名,或用 `/auth page <页码>` 直接跳页。文本列表的额度窗口按 Codex 实际返回值展示,例如 `5h:20|7d:25` 或单月窗口 `30d:97`;按钮只显示紧凑的两个剩余数字,例如 `20|25`,未知值显示为 `—`。面板会省略候选名中重复的 `auth.json_` 前缀,磁盘文件名保持不变。
132
132
 
133
133
  当 Codex 报告用量限制错误时,FoxClaw 会自动:
134
134
  - 切换到下一个未失败的候选账号
package/README_EN.md CHANGED
@@ -85,7 +85,7 @@ DEFAULT_SANDBOX_MODE=workspace-write
85
85
 
86
86
  The default config file is `~/.foxclaw/.env`. Set `FOXCLAW_ENV=/path/to/.env` if you want to keep it somewhere else.
87
87
 
88
- `foxclaw start` runs checks and installs or restarts the background service. For later upgrades, run `foxclaw update`; it preserves the current npm/pnpm global-install method, attempts to update an npm/pnpm-managed Codex CLI, runs checks, and restarts the service.
88
+ `foxclaw start` runs checks and installs or restarts the background service. For later upgrades, run `foxclaw update`; it preserves the current npm/pnpm global-install method, attempts to update an npm/pnpm-managed Codex CLI, runs checks, restarts the service, and reports both FoxClaw and Codex CLI version changes in chat.
89
89
 
90
90
  FoxClaw accepts messages only from `TG_ALLOWED_USER_ID`. Putting the bot in a group does not make it available to every group member.
91
91
 
@@ -128,7 +128,7 @@ Setup:
128
128
  3. Use `/auth` to view all candidate account statuses.
129
129
  4. Use `/auth enable <n>` / `/auth disable <n>` to control which accounts participate in auto-rotation.
130
130
 
131
- For larger inventories, `/auth` shows 8 candidates per page with paging and `All / Enabled / Attention` filters. Use `/auth list <keyword>` to search filenames or `/auth page <n>` to jump directly. Quota windows use the values reported by Codex, for example `5h:20|7d:25` or a single monthly window such as `30d:97`.
131
+ For larger inventories, `/auth` shows 8 candidates per page with paging and `All / Enabled / Attention` filters. Use `/auth list <keyword>` to search filenames or `/auth page <n>` to jump directly. Candidate rows use the quota windows reported by Codex, for example `5h:20|7d:25` or a single monthly window such as `30d:97`; buttons use compact remaining numbers such as `20|25`, with `—` for unknown values. The panel omits the repeated `auth.json_` prefix from candidate labels without renaming files on disk.
132
132
 
133
133
  When Codex reports a usage-limit error, FoxClaw automatically:
134
134
  - Switches to the next non-failed candidate account
@@ -238,6 +238,7 @@ export declare class BridgeSessionCore {
238
238
  private clearSelfUpdateStatusPoll;
239
239
  private pollSelfUpdateStatus;
240
240
  private formatSelfUpdateResult;
241
+ private formatCodexUpdateResult;
241
242
  private handleAuthCommand;
242
243
  private handleAuthRefreshAllCommand;
243
244
  private handleAuthUseCommand;
@@ -433,8 +433,9 @@ export class BridgeSessionCore {
433
433
  to: serviceStatus.lastUpdate.toVersion ?? t(locale, 'unknown'),
434
434
  time: serviceStatus.lastUpdate.updatedAt,
435
435
  }));
436
- if (serviceStatus.lastUpdate.codexUpdate) {
437
- lines.push(t(locale, 'status_last_codex_update', { value: serviceStatus.lastUpdate.codexUpdate }));
436
+ const codexUpdateLine = this.formatCodexUpdateResult(serviceStatus.lastUpdate);
437
+ if (codexUpdateLine) {
438
+ lines.push(t(locale, 'status_last_codex_update', { value: codexUpdateLine }));
438
439
  }
439
440
  }
440
441
  else {
@@ -3871,15 +3872,25 @@ export class BridgeSessionCore {
3871
3872
  await this.selfUpdater?.clearStatus();
3872
3873
  }
3873
3874
  formatSelfUpdateResult(status) {
3875
+ const codexUpdateLine = this.formatCodexUpdateResult(status);
3874
3876
  if (status.state === 'succeeded') {
3875
3877
  const result = t(status.locale, 'update_succeeded', {
3876
3878
  from: status.fromVersion,
3877
3879
  to: status.toVersion ?? t(status.locale, 'unknown'),
3878
3880
  });
3879
- return status.codexUpdate ? `${result}\n${status.codexUpdate}` : result;
3881
+ return codexUpdateLine ? `${result}\n${codexUpdateLine}` : result;
3880
3882
  }
3881
3883
  const result = t(status.locale, 'update_failed', { error: status.error ?? t(status.locale, 'unknown') });
3882
- return status.codexUpdate ? `${result}\n${status.codexUpdate}` : result;
3884
+ return codexUpdateLine ? `${result}\n${codexUpdateLine}` : result;
3885
+ }
3886
+ formatCodexUpdateResult(status) {
3887
+ if (status.codexFromVersion || status.codexToVersion) {
3888
+ return t(status.locale, 'update_codex_result', {
3889
+ from: status.codexFromVersion ?? t(status.locale, 'unknown'),
3890
+ to: status.codexToVersion ?? t(status.locale, 'unknown'),
3891
+ });
3892
+ }
3893
+ return status.codexUpdate ?? null;
3883
3894
  }
3884
3895
  async handleAuthCommand(scopeId, locale, args) {
3885
3896
  const action = args[0]?.toLowerCase() ?? 'list';
@@ -7785,7 +7796,11 @@ function renderAuthListMessage(locale, state, botLabel = null, includeWeixinCopy
7785
7796
  if (botLabel) {
7786
7797
  lines.push(t(locale, 'auth_bot', { value: botLabel }));
7787
7798
  }
7788
- lines.push(t(locale, 'auth_current', { value: state.currentLabel ?? t(locale, 'none') }), t(locale, 'auth_dir', { value: state.authDir }));
7799
+ lines.push(t(locale, 'auth_current', {
7800
+ value: state.currentLabel
7801
+ ? formatCodexAuthCandidateDisplayName(state.currentLabel)
7802
+ : t(locale, 'none'),
7803
+ }), t(locale, 'auth_dir', { value: state.authDir }));
7789
7804
  if (state.candidates.length === 0) {
7790
7805
  lines.push(t(locale, 'auth_no_candidates'));
7791
7806
  if (includeWeixinCopyPaste) {
@@ -7817,7 +7832,7 @@ function renderAuthListMessage(locale, state, botLabel = null, includeWeixinCopy
7817
7832
  }
7818
7833
  page.visible.forEach(({ candidate, index }) => {
7819
7834
  const marker = candidate.isCurrent ? ' *' : '';
7820
- lines.push(`${index + 1}. ${formatAuthQuotaPrefix(locale, candidate.quota)}|${candidate.name}${marker} ${formatCodexAuthCandidateStatus(locale, candidate)}`);
7835
+ lines.push(`${index + 1}. ${formatAuthQuotaPrefix(locale, candidate.quota)}|${formatCodexAuthCandidateDisplayName(candidate.name)}${marker} ${formatCodexAuthCandidateStatus(locale, candidate)}`);
7821
7836
  });
7822
7837
  if (includeWeixinCopyPaste) {
7823
7838
  lines.push('', t(locale, 'weixin_copy_paste_divider'), t(locale, 'weixin_copy_auth_title'), ...page.visible.map(({ index }) => `/auth use ${index + 1}`), ...page.visible.map(({ candidate, index }) => candidate.disabled
@@ -7830,7 +7845,7 @@ function authChoiceKeyboard(locale, record) {
7830
7845
  const page = codexAuthListPage(record.candidates, record);
7831
7846
  const rows = page.visible.map(({ candidate, index }) => [
7832
7847
  {
7833
- text: clipButtonText(`${candidate.isCurrent ? '✅ ' : '🔐 '}${formatAuthQuotaPrefix(locale, candidate.quota)}|${candidate.name}${candidate.disabled ? ' · off' : ''}`),
7848
+ text: clipButtonText(`${candidate.isCurrent ? '✅ ' : '🔐 '}${formatAuthQuotaButtonPrefix(candidate.quota)}|${formatCodexAuthCandidateDisplayName(candidate.name)}${candidate.disabled ? ' · off' : ''}`),
7834
7849
  callback_data: `auth:${record.localId}:${index}`,
7835
7850
  },
7836
7851
  {
@@ -7863,6 +7878,12 @@ function authChoiceKeyboard(locale, record) {
7863
7878
  rows.push([{ text: t(locale, 'button_auth_reload'), callback_data: `auth:${record.localId}:reload` }]);
7864
7879
  return rows;
7865
7880
  }
7881
+ function formatCodexAuthCandidateDisplayName(name) {
7882
+ const prefix = 'auth.json_';
7883
+ return name.length > prefix.length && name.startsWith(prefix)
7884
+ ? name.slice(prefix.length)
7885
+ : name;
7886
+ }
7866
7887
  function authFilterButton(locale, record, filter) {
7867
7888
  const label = t(locale, `button_auth_filter_${filter}`);
7868
7889
  return {
@@ -8566,6 +8587,17 @@ function formatAuthQuotaPrefix(locale, snapshot) {
8566
8587
  .map(([duration, remaining, fallback]) => (`${formatCompactRateLimitWindowLabel(locale, duration, fallback)}:${remaining === null ? '--' : formatUsagePercent(remaining)}`));
8567
8588
  return values.length > 0 ? values.join('|') : '--';
8568
8589
  }
8590
+ function formatAuthQuotaButtonPrefix(snapshot) {
8591
+ return [
8592
+ snapshot?.primaryRemainingPercent ?? null,
8593
+ snapshot?.secondaryRemainingPercent ?? null,
8594
+ ].map(formatAuthQuotaButtonValue).join('|');
8595
+ }
8596
+ function formatAuthQuotaButtonValue(value) {
8597
+ return typeof value === 'number' && Number.isFinite(value)
8598
+ ? formatUsagePercent(value)
8599
+ : '—';
8600
+ }
8569
8601
  function isCodexAuthQuotaSnapshot(value) {
8570
8602
  if (!value || typeof value !== 'object') {
8571
8603
  return false;
package/dist/i18n.d.ts CHANGED
@@ -130,6 +130,7 @@ declare const MESSAGES: {
130
130
  readonly status_last_codex_update: "Last Codex update: {value}";
131
131
  readonly update_started: "FoxClaw update started. I will report here after installation, checks, and service restart complete.";
132
132
  readonly update_succeeded: "FoxClaw updated and restarted: {from} -> {to}.";
133
+ readonly update_codex_result: "Codex CLI: {from} -> {to}.";
133
134
  readonly update_failed: "FoxClaw update failed: {error}\nRun foxclaw update in a terminal for details.";
134
135
  readonly update_unavailable: "Self-update is unavailable in this runtime. Run foxclaw update in a terminal.";
135
136
  readonly update_already_running: "A FoxClaw update is already running. I will report here when it finishes.";
@@ -735,6 +736,7 @@ declare const MESSAGES: {
735
736
  readonly status_last_codex_update: "最近 Codex 升级:{value}";
736
737
  readonly update_started: "已开始升级 FoxClaw。安装、自检和服务重启完成后,我会在这里回报结果。";
737
738
  readonly update_succeeded: "FoxClaw 已升级并重启:{from} -> {to}。";
739
+ readonly update_codex_result: "Codex CLI:{from} -> {to}。";
738
740
  readonly update_failed: "FoxClaw 升级失败:{error}\n请在终端运行 foxclaw update 查看详情。";
739
741
  readonly update_unavailable: "当前运行方式不支持自升级,请在终端运行 foxclaw update。";
740
742
  readonly update_already_running: "FoxClaw 升级已经在进行中,结束后我会在这里回报结果。";
package/dist/i18n.js CHANGED
@@ -128,6 +128,7 @@ const MESSAGES = {
128
128
  status_last_codex_update: 'Last Codex update: {value}',
129
129
  update_started: 'FoxClaw update started. I will report here after installation, checks, and service restart complete.',
130
130
  update_succeeded: 'FoxClaw updated and restarted: {from} -> {to}.',
131
+ update_codex_result: 'Codex CLI: {from} -> {to}.',
131
132
  update_failed: 'FoxClaw update failed: {error}\nRun foxclaw update in a terminal for details.',
132
133
  update_unavailable: 'Self-update is unavailable in this runtime. Run foxclaw update in a terminal.',
133
134
  update_already_running: 'A FoxClaw update is already running. I will report here when it finishes.',
@@ -733,6 +734,7 @@ const MESSAGES = {
733
734
  status_last_codex_update: '最近 Codex 升级:{value}',
734
735
  update_started: '已开始升级 FoxClaw。安装、自检和服务重启完成后,我会在这里回报结果。',
735
736
  update_succeeded: 'FoxClaw 已升级并重启:{from} -> {to}。',
737
+ update_codex_result: 'Codex CLI:{from} -> {to}。',
736
738
  update_failed: 'FoxClaw 升级失败:{error}\n请在终端运行 foxclaw update 查看详情。',
737
739
  update_unavailable: '当前运行方式不支持自升级,请在终端运行 foxclaw update。',
738
740
  update_already_running: 'FoxClaw 升级已经在进行中,结束后我会在这里回报结果。',
package/dist/types.d.ts CHANGED
@@ -365,6 +365,8 @@ export interface RuntimeStatus {
365
365
  fromVersion: string;
366
366
  toVersion: string | null;
367
367
  codexUpdate?: string | null;
368
+ codexFromVersion?: string | null;
369
+ codexToVersion?: string | null;
368
370
  updatedAt: string;
369
371
  } | null;
370
372
  }
package/dist/update.d.ts CHANGED
@@ -7,6 +7,8 @@ export interface SelfUpdateStatus {
7
7
  fromVersion: string;
8
8
  toVersion: string | null;
9
9
  codexUpdate?: string | null;
10
+ codexFromVersion?: string | null;
11
+ codexToVersion?: string | null;
10
12
  error: string | null;
11
13
  updatedAt: string;
12
14
  }
package/dist/update.js CHANGED
@@ -132,6 +132,8 @@ export function readSelfUpdateStatus(statusFile) {
132
132
  fromVersion: parsed.fromVersion,
133
133
  toVersion: typeof parsed.toVersion === 'string' ? parsed.toVersion : null,
134
134
  ...(typeof parsed.codexUpdate === 'string' ? { codexUpdate: parsed.codexUpdate } : {}),
135
+ ...(typeof parsed.codexFromVersion === 'string' ? { codexFromVersion: parsed.codexFromVersion } : {}),
136
+ ...(typeof parsed.codexToVersion === 'string' ? { codexToVersion: parsed.codexToVersion } : {}),
135
137
  error: typeof parsed.error === 'string' ? parsed.error : null,
136
138
  updatedAt: parsed.updatedAt,
137
139
  };
@@ -161,6 +163,8 @@ export function createSelfUpdateRuntime(options) {
161
163
  fromVersion: options.version,
162
164
  toVersion: null,
163
165
  codexUpdate: null,
166
+ codexFromVersion: null,
167
+ codexToVersion: null,
164
168
  error: null,
165
169
  updatedAt: new Date().toISOString(),
166
170
  });
@@ -182,6 +186,8 @@ export function createSelfUpdateRuntime(options) {
182
186
  fromVersion: options.version,
183
187
  toVersion: null,
184
188
  codexUpdate: null,
189
+ codexFromVersion: null,
190
+ codexToVersion: null,
185
191
  error: formatError(error),
186
192
  updatedAt: new Date().toISOString(),
187
193
  });
@@ -205,7 +211,7 @@ export function performSelfUpdate(options) {
205
211
  let codexUpdate = null;
206
212
  try {
207
213
  codexUpdate = updateManagedCodexCli(options.codexCliBin ?? env.CODEX_CLI_BIN ?? '', options.nodePath, env);
208
- console.log(`[UPDATE] ${codexUpdate}`);
214
+ console.log(`[UPDATE] ${codexUpdate.message}`);
209
215
  const installer = resolveSelfUpdateInstaller(options.entryPoint, options.nodePath, fs.existsSync, env);
210
216
  const installerEnv = buildInstallerEnv(options.entryPoint, installer, env);
211
217
  console.log(`[UPDATE] Installing ${PACKAGE_SPEC} with ${installer.manager}...`);
@@ -236,22 +242,52 @@ export function performSelfUpdate(options) {
236
242
  }
237
243
  }
238
244
  function updateManagedCodexCli(codexCliBin, nodePath, env) {
245
+ const fromVersion = readCodexCliVersion(codexCliBin, env);
239
246
  if (!codexCliBin) {
240
- return 'Codex CLI update skipped: CODEX_CLI_BIN is not configured.';
247
+ return {
248
+ message: 'Codex CLI update skipped: CODEX_CLI_BIN is not configured.',
249
+ fromVersion,
250
+ toVersion: fromVersion,
251
+ };
241
252
  }
242
253
  const installer = resolveCodexUpdateInstaller(codexCliBin, nodePath, fs.existsSync, env);
243
254
  if (!installer) {
244
- return 'Codex CLI update skipped: configured installation is not a recognized global npm/pnpm package.';
255
+ return {
256
+ message: 'Codex CLI update skipped: configured installation is not a recognized global npm/pnpm package.',
257
+ fromVersion,
258
+ toVersion: fromVersion,
259
+ };
245
260
  }
246
261
  try {
247
262
  const installerEnv = buildInstallerEnv(fs.realpathSync(codexCliBin), installer, env);
248
263
  runInherited(installer.command, installer.installArgs, installerEnv);
249
- return `Codex CLI updated with ${installer.manager}.`;
264
+ return {
265
+ message: `Codex CLI updated with ${installer.manager}.`,
266
+ fromVersion,
267
+ toVersion: readCodexCliVersion(codexCliBin, installerEnv) ?? fromVersion,
268
+ };
250
269
  }
251
270
  catch (error) {
252
- return `Codex CLI update failed without blocking FoxClaw update: ${formatError(error)}`;
271
+ return {
272
+ message: `Codex CLI update failed without blocking FoxClaw update: ${formatError(error)}`,
273
+ fromVersion,
274
+ toVersion: readCodexCliVersion(codexCliBin, env) ?? fromVersion,
275
+ };
253
276
  }
254
277
  }
278
+ function readCodexCliVersion(codexCliBin, env) {
279
+ if (!codexCliBin) {
280
+ return null;
281
+ }
282
+ const result = spawnSync(codexCliBin, ['--version'], { encoding: 'utf8', env });
283
+ if (result.error || result.status !== 0) {
284
+ return null;
285
+ }
286
+ return parseCodexCliVersion(`${result.stdout}\n${result.stderr}`);
287
+ }
288
+ function parseCodexCliVersion(output) {
289
+ return output.match(/\b\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?\b/)?.[0] ?? null;
290
+ }
255
291
  function executableCandidates(commandName, nodePath, env, preferred = []) {
256
292
  return [
257
293
  ...preferred,
@@ -329,7 +365,9 @@ function completeNotification(notificationFile, state, toVersion, codexUpdate, e
329
365
  ...pending,
330
366
  state,
331
367
  toVersion,
332
- codexUpdate,
368
+ codexUpdate: codexUpdate?.message ?? null,
369
+ codexFromVersion: codexUpdate?.fromVersion ?? null,
370
+ codexToVersion: codexUpdate?.toVersion ?? null,
333
371
  error,
334
372
  updatedAt: new Date().toISOString(),
335
373
  });
@@ -233,7 +233,7 @@ Later commands are sorted by recent usage. Plain text, photos, and files continu
233
233
  - `/status`: FoxClaw, app-server, current thread binding, model, access, and Codex usage summary. In multi-bot mode it also lists every bot's connection, current auth, active turns, and the most recent auth mirror and service/Codex update outcomes. Local session, token, and visible-reply-throughput metrics use a background-generated historical snapshot instead of scanning large logs during the request; throughput is computed end-to-end for completed turns, excluding reasoning tokens while including waiting and tool execution time.
234
234
  - `/account`: current Codex account.
235
235
  - `/quota`: Codex usage and quota window.
236
- - `/update`: upgrade FoxClaw, attempt to update an npm/pnpm-managed Codex CLI, run checks, and restart the service; it refuses while any Telegram bot runtime, an enabled Weixin default runtime, or an auth mirror write is busy, then reports the result through the initiating bot after restart.
236
+ - `/update`: upgrade FoxClaw, attempt to update an npm/pnpm-managed Codex CLI, run checks, and restart the service; it refuses while any Telegram bot runtime, an enabled Weixin default runtime, or an auth mirror write is busy, then reports both FoxClaw and Codex CLI version changes through the initiating bot after restart.
237
237
 
238
238
  ### 3.3 `/config`, `/requirements`, `/provider`
239
239
 
@@ -389,23 +389,23 @@ If the login is cancelled or fails, FoxClaw tries to restore the previous auth t
389
389
 
390
390
  ### 6.3 The `/auth` Panel
391
391
 
392
- `/auth` lists candidate accounts, the current account, and the auth directory. It also provides buttons for switching, disabling, login, and reload. In multi-bot mode the panel names the `@botname` runtime being managed, because private chats, groups, and topics on one bot share that bot's current auth. The panel shows 8 candidates per page and supports paging, `All / Enabled / Attention` filters, and `/auth list <keyword>` filename search for large local inventories. `/auth use <n>` always uses the full-list candidate number, independent of panel paging.
392
+ `/auth` lists candidate accounts, the current account, and the auth directory. It also provides buttons for switching, disabling, login, and reload. In multi-bot mode the panel names the `@botname` runtime being managed, because private chats, groups, and topics on one bot share that bot's current auth. The panel shows 8 candidates per page and supports paging, `All / Enabled / Attention` filters, and `/auth list <keyword>` filename search for large local inventories. Panel text and buttons omit the repeated `auth.json_` prefix from standard candidate filenames, so `auth.json_personal` on disk renders as `personal`; files are not renamed, and search and commands still operate on the original candidates. `/auth use <n>` always uses the full-list candidate number, independent of panel paging.
393
393
 
394
- Each filename is prefixed with observed `window:remaining-percent` values. For example, a Plus account may show `5h:20|7d:25`, while an account with one monthly window may show `30d:97`. The current auth quota is refreshed when the panel opens; other candidates are not switched merely to query quota. When multiple bot runtimes have recently used the same ChatGPT account, FoxClaw combines their cached quota snapshots by verified account ID, so one bot's `/auth` panel can show quota information learned by another bot without mixing different accounts.
394
+ Candidate rows are prefixed with observed `window:remaining-percent` values. For example, a Plus account may show `5h:20|7d:25`, while an account with one monthly window may show `30d:97`. Buttons use a compact two-number `primary|secondary` form such as `20|25`; unknown values render as `—`. The current auth quota is refreshed when the panel opens; other candidates are not switched merely to query quota. When multiple bot runtimes have recently used the same ChatGPT account, FoxClaw combines their cached quota snapshots by verified account ID, so one bot's `/auth` panel can show quota information learned by another bot without mixing different accounts.
395
395
 
396
396
  Approximation:
397
397
 
398
398
  ```text
399
399
  Codex auth
400
- Current: auth.json_personal
400
+ Current: personal
401
401
  Auth dir: /home/alice/.codex
402
402
  Candidates: 2
403
403
  Quota remaining: window:percent|auth
404
- 1. 5h:20|7d:25|auth.json_personal * [Plus · ready · refreshed 2h ago]
405
- 2. --|auth.json_team [quota unknown]
404
+ 1. 5h:20|7d:25|personal * [Plus · ready · refreshed 2h ago]
405
+ 2. --|team [quota unknown]
406
406
 
407
- [✅ 5h:20|7d:25|auth.json_personal] [✅]
408
- [🔐 --|auth.json_team] [✅]
407
+ [✅ 20|25|personal] [✅]
408
+ [🔐 —|—|team] [✅]
409
409
  [☑️ All] [Enabled] [Attention]
410
410
  [🛡️ Access] [🔑 Login]
411
411
  [🔄 Reload auth]
@@ -233,7 +233,7 @@ TG_ALLOWED_TOPIC_ID=42
233
233
  - `/status`:查看 FoxClaw、app-server、当前绑定线程、模型、权限和 Codex 用量摘要。多 bot 模式还会列出所有 bot 的连接、当前 auth、活动 turn,以及最近一次 auth 镜像和服务/Codex 升级结果。本地 session/Token/可见答复吞吐使用后台生成的历史快照,避免状态查询现场扫描大量日志;答复吞吐按完成轮次端到端耗时计算,排除推理 token,但包含等待与工具执行时间。
234
234
  - `/account`:查看当前 Codex 登录账号。
235
235
  - `/quota`:查看 Codex 用量和额度窗口。
236
- - `/update`:升级 FoxClaw,并尝试升级 npm/pnpm 安装的 Codex CLI,然后自检和重启服务;任意 Telegram bot、启用的微信默认 runtime 或 auth 镜像写入不空闲时都会拒绝执行,重启后通过发起命令的 bot 回报结果。
236
+ - `/update`:升级 FoxClaw,并尝试升级 npm/pnpm 安装的 Codex CLI,然后自检和重启服务;任意 Telegram bot、启用的微信默认 runtime 或 auth 镜像写入不空闲时都会拒绝执行,重启后通过发起命令的 bot 回报 FoxClaw 与 Codex CLI 的版本变化。
237
237
 
238
238
  ### 3.3 `/config`、`/requirements`、`/provider`
239
239
 
@@ -389,23 +389,23 @@ cp -L ~/.codex/auth.json ~/.codex/auth.json_personal
389
389
 
390
390
  ### 6.3 `/auth` 面板
391
391
 
392
- `/auth` 会列出候选账号、当前账号和 auth 目录,并提供按钮切换、禁用、登录和重载。多 bot 模式中,面板顶部还会显示当前正在管理的 `@botname`,因为该 bot 内的私聊、群聊和话题共享同一个当前 auth。面板每页显示 8 个候选,支持翻页、`全部 / 已启用 / 需关注` 筛选和 `/auth list <关键词>` 文件名搜索,适合管理较大的本地候选清单。命令 `/auth use <n>` 的编号始终对应完整候选列表,不会因为分页变化。
392
+ `/auth` 会列出候选账号、当前账号和 auth 目录,并提供按钮切换、禁用、登录和重载。多 bot 模式中,面板顶部还会显示当前正在管理的 `@botname`,因为该 bot 内的私聊、群聊和话题共享同一个当前 auth。面板每页显示 8 个候选,支持翻页、`全部 / 已启用 / 需关注` 筛选和 `/auth list <关键词>` 文件名搜索,适合管理较大的本地候选清单。面板文本和按钮会省略标准候选文件名中重复的 `auth.json_` 前缀,例如磁盘上的 `auth.json_personal` 显示为 `personal`;文件本身不会重命名,搜索和命令仍按原候选工作。命令 `/auth use <n>` 的编号始终对应完整候选列表,不会因为分页变化。
393
393
 
394
- 每个候选名前的 `窗口:剩余百分比` 来自最近一次观察到的真实额度窗口,例如 Plus 账号可能显示 `5h:20|7d:25`,只有一个月度窗口的账号可能显示 `30d:97`。当前 auth 会在打开面板时刷新额度;其他候选不会为了查询额度被自动切换。如果多个 bot runtime 最近使用过同一个 ChatGPT 账号,FoxClaw 会按已验证的账号 ID 合并它们缓存到的额度快照,因此一个 bot 的 `/auth` 面板可以显示另一个 bot 掌握到的额度信息,同时不会把不同账号混在一起。
394
+ 文本列表中每个候选名前的 `窗口:剩余百分比` 来自最近一次观察到的真实额度窗口,例如 Plus 账号可能显示 `5h:20|7d:25`,只有一个月度窗口的账号可能显示 `30d:97`。按钮为了适配窄屏,只显示两个剩余百分比数字,例如 `20|25`;未知值显示为 `—`。当前 auth 会在打开面板时刷新额度;其他候选不会为了查询额度被自动切换。如果多个 bot runtime 最近使用过同一个 ChatGPT 账号,FoxClaw 会按已验证的账号 ID 合并它们缓存到的额度快照,因此一个 bot 的 `/auth` 面板可以显示另一个 bot 掌握到的额度信息,同时不会把不同账号混在一起。
395
395
 
396
396
  示意:
397
397
 
398
398
  ```text
399
399
  Codex auth
400
- Current: auth.json_personal
400
+ Current: personal
401
401
  Auth dir: /home/alice/.codex
402
402
  Candidates: 2
403
403
  额度剩余:窗口:百分比|auth
404
- 1. 5h:20|7d:25|auth.json_personal * [Plus · 正常 · 刷新于 2小时前]
405
- 2. --|auth.json_team [额度未知]
404
+ 1. 5h:20|7d:25|personal * [Plus · 正常 · 刷新于 2小时前]
405
+ 2. --|team [额度未知]
406
406
 
407
- [✅ 5h:20|7d:25|auth.json_personal] [✅]
408
- [🔐 --|auth.json_team] [✅]
407
+ [✅ 20|25|personal] [✅]
408
+ [🔐 —|—|team] [✅]
409
409
  [☑️ 全部] [已启用] [需关注]
410
410
  [🛡️ Access] [🔑 设备登录]
411
411
  [🔄 Reload auth]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxden-app/foxclaw",
3
- "version": "0.4.11",
3
+ "version": "0.4.13",
4
4
  "description": "Foxden local execution claw for controlling Codex from trusted chat interfaces.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -67,7 +67,7 @@ Telegram behavior:
67
67
  - when `TG_BOT_TOKENS` contains multiple bots, one FoxClaw service starts independent Codex app-servers and auth selections per bot
68
68
  - isolated Telegram runtimes use independent `CODEX_HOME` directories and file-backed credentials; they do not share Codex sessions
69
69
  - refreshed ChatGPT candidates are mirrored only after online usage-endpoint validation; before auth switch or reload, a runtime restores any newer same-account credential found in another Codex home
70
- - `/auth` paginates large candidate inventories at 8 rows per page, supports all/enabled/attention filters and filename search, and renders the actual observed quota windows rather than assuming every account has 5-hour and 7-day windows
70
+ - `/auth` paginates large candidate inventories at 8 rows per page, supports all/enabled/attention filters and filename search, renders actual observed quota windows in text rows, uses compact two-number remaining quota labels on buttons, and omits the repeated `auth.json_` prefix from panel labels without renaming files
71
71
  - treat the `/auth` `not recently refreshed` state as a maintenance hint only: OpenAI does not publish a fixed ChatGPT refresh-token lifetime or replay grace period, and FoxClaw must not add routine bulk keepalive refreshes
72
72
  - `/auth refresh all` remains a command-only maintenance action, not a 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
73
73
  - 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
@@ -205,7 +205,7 @@ When the user says development is complete, asks to "收尾", or asks to push/pu
205
205
  - For macOS launchd, use the launchd install/start path from this skill and verify with `node dist/main.js status`.
206
206
  - Verify the running service reports the expected FoxClaw version in `status`.
207
207
  - If `doctor` fails only because `DEFAULT_CWD` is missing, report that separately; do not treat it as evidence that the service update failed.
208
- - `/update` now attempts to update globally npm/pnpm-managed Codex CLI installations and refuses to restart while any configured Telegram runtime, enabled Weixin default runtime, or auth mirror write is busy.
208
+ - `/update` now attempts to update globally npm/pnpm-managed Codex CLI installations, reports FoxClaw and Codex CLI version changes, and refuses to restart while any configured Telegram runtime, enabled Weixin default runtime, or auth mirror write is busy.
209
209
  7. Publish to npm when requested:
210
210
  - Prefer GitHub Actions trusted publishing via `.github/workflows/publish.yml`: bump and commit the package version, push `main`, then push a matching `v<version>` tag. The tag version must match `package.json`.
211
211
  - Ensure the matching changelog section exists before pushing the version tag; the publish workflow uses it to create or update the GitHub Release.