@foxden-app/foxclaw 0.5.3 → 0.5.5

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.5.5 - 2026-06-04
6
+
7
+ ### 中文
8
+ - 在 `/login_device`、`/auth add <name>` 和 `/auth` 面板设备登录返回内容中加入 ChatGPT 设备代码授权前置条件:在 ChatGPT 左下角用户名菜单进入“设置 > 安全”,启用“为 Codex 启用设备代码授权”。
9
+ - README、安装指南、用户手册和故障排查补充同一指引,并强调 workspace 账号可能需要管理员允许设备码登录,以及设备代码不要分享给他人或粘贴到不可信页面。
10
+
11
+ ### English
12
+ - Added a device-code authorization prerequisite to `/login_device`, `/auth add <name>`, and the `/auth` panel Login response: in ChatGPT, open the lower-left username menu, then Settings > Security, and enable device code authorization for Codex.
13
+ - Added the same guidance to the README, install guide, user manual, and troubleshooting docs, including workspace-admin requirements and the warning not to share device codes or paste them into untrusted pages.
14
+
15
+ ## 0.5.4 - 2026-06-04
16
+
17
+ ### 中文
18
+ - 修复 `/update` 遗留 `self-update.json` pending 后永久显示“升级已经在进行中”的问题:pending 超过 15 分钟会自动转为失败状态,Telegram 会回报失败,下一次 `/update` 可以重新发起。
19
+ - 这个补丁专门兜底 0.5.2 在 `KillMode=control-group` 下被杀掉的旧 updater,以及任何未来被外部中断、没有写完成状态的升级进程。
20
+
21
+ ### English
22
+ - Fixed `/update` getting permanently stuck on "update already running" after a leftover `self-update.json` pending state: pending updates now expire as failed after 15 minutes, Telegram reports the failure, and the next `/update` can start again.
23
+ - This specifically recovers from the 0.5.2 updater killed by `KillMode=control-group`, and from any future updater interruption that fails to write a terminal status.
24
+
5
25
  ## 0.5.3 - 2026-06-04
6
26
 
7
27
  ### 中文
package/README.md CHANGED
@@ -50,6 +50,7 @@ FoxClaw(狸爪)的目标很直接:让你用手机控制本机的 Codex,
50
50
 
51
51
  - macOS 或 Linux,`codex` CLI 可用
52
52
  - Codex 已完成登录认证
53
+ - 如果要通过 `/auth add` 或 `/login_device` 从 Telegram 发起设备码登录,需要先在 ChatGPT 左下角用户名菜单进入“设置 > 安全”,启用“为 Codex 启用设备代码授权”
53
54
  - Node.js 24+
54
55
  - 一个 `@BotFather` 创建的 Telegram bot token
55
56
  - 你的 Telegram 数字用户 ID
@@ -129,6 +130,8 @@ FoxClaw 的一大特色是自动多账号切换。当一个账号触发用量限
129
130
  3. 用 `/auth` 查看所有候选账号状态。
130
131
  4. 用 `/auth enable <n>` / `/auth disable <n>` 控制哪些账号参与自动轮换。
131
132
 
133
+ 通过 `/auth add <name>` 或 `/login_device` 进行设备码登录前,需要在 ChatGPT 网页左下角点用户名,进入“设置 > 安全”,启用“为 Codex 启用设备代码授权”。设备代码等同于一次登录授权,切勿转发给他人或粘贴到不可信页面。
134
+
132
135
  候选较多时,`/auth` 每页显示 8 个账号,并提供翻页、`全部 / 已启用 / 需关注` 筛选。也可以用 `/auth list <关键词>` 搜索文件名,或用 `/auth page <页码>` 直接跳页。文本列表的额度窗口按 Codex 实际返回值展示,例如 `5h:20|7d:25` 或单月窗口 `30d:97`;按钮只显示紧凑的两个剩余数字,例如 `20|25`,未知值显示为 `—`。面板会省略候选名中重复的 `auth.json_` 前缀,磁盘文件名保持不变。
133
136
 
134
137
  当 Codex 报告用量限制错误时,FoxClaw 会自动:
package/README_EN.md CHANGED
@@ -50,6 +50,7 @@ The minimum install needs one or more Telegram bot tokens, your numeric Telegram
50
50
 
51
51
  - macOS or Linux with a working `codex` CLI
52
52
  - Codex authenticated on the host machine
53
+ - If you want to start device-code login from Telegram with `/auth add` or `/login_device`, first open the username menu in the lower-left corner of ChatGPT, then Settings > Security, and enable device code authorization for Codex
53
54
  - Node.js 24+
54
55
  - A Telegram bot token from `@BotFather`
55
56
  - Your Telegram numeric user id
@@ -129,6 +130,8 @@ Setup:
129
130
  3. Use `/auth` to view all candidate account statuses.
130
131
  4. Use `/auth enable <n>` / `/auth disable <n>` to control which accounts participate in auto-rotation.
131
132
 
133
+ Before using `/auth add <name>` or `/login_device` for device-code login, open ChatGPT, click the username menu in the lower-left corner, go to Settings > Security, and enable device code authorization for Codex. Treat the device code as a login grant: never forward it or paste it into an untrusted page.
134
+
132
135
  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.
133
136
 
134
137
  When Codex reports a usage-limit error, FoxClaw automatically:
@@ -4175,6 +4175,7 @@ export class BridgeSessionCore {
4175
4175
  });
4176
4176
  await this.sendMessage(scopeId, [
4177
4177
  t(locale, 'auth_add_started', { value: candidateName }),
4178
+ t(locale, 'login_device_prereq'),
4178
4179
  t(locale, 'login_url', { value: login.verificationUrl }),
4179
4180
  t(locale, 'login_code', { value: login.userCode }),
4180
4181
  t(locale, 'login_id', { value: login.loginId }),
@@ -4227,6 +4228,7 @@ export class BridgeSessionCore {
4227
4228
  this.pendingLoginScopesById.set(login.loginId, scopeId);
4228
4229
  await this.sendMessage(scopeId, [
4229
4230
  t(locale, 'login_device_started'),
4231
+ t(locale, 'login_device_prereq'),
4230
4232
  t(locale, 'login_url', { value: login.verificationUrl }),
4231
4233
  t(locale, 'login_code', { value: login.userCode }),
4232
4234
  t(locale, 'login_id', { value: login.loginId }),
package/dist/i18n.d.ts CHANGED
@@ -474,6 +474,7 @@ declare const MESSAGES: {
474
474
  readonly usage_quota_nudge: "Usage: /quota_nudge <credits|usage_limit> confirm";
475
475
  readonly quota_nudge_sent: "Nudge email requested.";
476
476
  readonly login_device_started: "Device login started.";
477
+ readonly login_device_prereq: "Prerequisite: in ChatGPT, open your username menu at the lower left, then Settings > Security, and enable device code authorization for Codex. For workspace accounts, an admin may need to allow device code login. Never share the device code.";
477
478
  readonly login_url: "URL: {value}";
478
479
  readonly login_code: "Code: {value}";
479
480
  readonly login_id: "Login id: {value}";
@@ -1104,6 +1105,7 @@ declare const MESSAGES: {
1104
1105
  readonly usage_quota_nudge: "用法:/quota_nudge <credits|usage_limit> confirm";
1105
1106
  readonly quota_nudge_sent: "已请求发送额度提醒邮件。";
1106
1107
  readonly login_device_started: "已开始设备码登录。";
1108
+ readonly login_device_prereq: "前置条件:在 ChatGPT 左下角点用户名,进入“设置 > 安全”,启用“为 Codex 启用设备代码授权”。Workspace 账号可能需要管理员允许设备码登录。切勿分享设备代码。";
1107
1109
  readonly login_url: "地址:{value}";
1108
1110
  readonly login_code: "验证码:{value}";
1109
1111
  readonly login_id: "登录 ID:{value}";
package/dist/i18n.js CHANGED
@@ -472,6 +472,7 @@ const MESSAGES = {
472
472
  usage_quota_nudge: 'Usage: /quota_nudge <credits|usage_limit> confirm',
473
473
  quota_nudge_sent: 'Nudge email requested.',
474
474
  login_device_started: 'Device login started.',
475
+ login_device_prereq: 'Prerequisite: in ChatGPT, open your username menu at the lower left, then Settings > Security, and enable device code authorization for Codex. For workspace accounts, an admin may need to allow device code login. Never share the device code.',
475
476
  login_url: 'URL: {value}',
476
477
  login_code: 'Code: {value}',
477
478
  login_id: 'Login id: {value}',
@@ -1102,6 +1103,7 @@ const MESSAGES = {
1102
1103
  usage_quota_nudge: '用法:/quota_nudge <credits|usage_limit> confirm',
1103
1104
  quota_nudge_sent: '已请求发送额度提醒邮件。',
1104
1105
  login_device_started: '已开始设备码登录。',
1106
+ login_device_prereq: '前置条件:在 ChatGPT 左下角点用户名,进入“设置 > 安全”,启用“为 Codex 启用设备代码授权”。Workspace 账号可能需要管理员允许设备码登录。切勿分享设备代码。',
1105
1107
  login_url: '地址:{value}',
1106
1108
  login_code: '验证码:{value}',
1107
1109
  login_id: '登录 ID:{value}',
package/dist/update.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { AppLocale } from './types.js';
2
+ export declare const SELF_UPDATE_PENDING_TIMEOUT_MS: number;
2
3
  export type SelfUpdateState = 'pending' | 'succeeded' | 'failed';
3
4
  export interface SelfUpdateStatus {
4
5
  state: SelfUpdateState;
@@ -31,6 +32,8 @@ interface CreateSelfUpdateRuntimeOptions {
31
32
  statusPath: string;
32
33
  logPath: string;
33
34
  codexCliBin?: string;
35
+ pendingTimeoutMs?: number;
36
+ now?: () => Date;
34
37
  }
35
38
  interface PerformSelfUpdateOptions {
36
39
  entryPoint: string;
package/dist/update.js CHANGED
@@ -5,6 +5,7 @@ import { spawn, spawnSync } from 'node:child_process';
5
5
  const PACKAGE_SPEC = '@foxden-app/foxclaw@latest';
6
6
  const CODEX_PACKAGE_SPEC = '@openai/codex@latest';
7
7
  const UPDATE_STATUS_FILENAME = 'self-update.json';
8
+ export const SELF_UPDATE_PENDING_TIMEOUT_MS = 15 * 60_000;
8
9
  export function selfUpdateStatusPath(statusPath) {
9
10
  return path.join(path.dirname(statusPath), UPDATE_STATUS_FILENAME);
10
11
  }
@@ -148,11 +149,33 @@ export function writeSelfUpdateStatus(statusFile, status) {
148
149
  fs.writeFileSync(temporaryFile, `${JSON.stringify(status, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
149
150
  fs.renameSync(temporaryFile, statusFile);
150
151
  }
152
+ function readNormalizedSelfUpdateStatus(statusFile, pendingTimeoutMs, now) {
153
+ const status = readSelfUpdateStatus(statusFile);
154
+ if (!status || status.state !== 'pending' || pendingTimeoutMs <= 0) {
155
+ return status;
156
+ }
157
+ const updatedAtMs = Date.parse(status.updatedAt);
158
+ const nowMs = now().getTime();
159
+ const isStale = Number.isNaN(updatedAtMs) || nowMs - updatedAtMs > pendingTimeoutMs;
160
+ if (!isStale) {
161
+ return status;
162
+ }
163
+ const failed = {
164
+ ...status,
165
+ state: 'failed',
166
+ error: `self-update timed out after ${Math.round(pendingTimeoutMs / 60_000)} minute(s) without writing a completion status; the previous updater may have been interrupted`,
167
+ updatedAt: new Date(nowMs).toISOString(),
168
+ };
169
+ writeSelfUpdateStatus(statusFile, failed);
170
+ return failed;
171
+ }
151
172
  export function createSelfUpdateRuntime(options) {
152
173
  const statusFile = selfUpdateStatusPath(options.statusPath);
174
+ const pendingTimeoutMs = options.pendingTimeoutMs ?? SELF_UPDATE_PENDING_TIMEOUT_MS;
175
+ const now = options.now ?? (() => new Date());
153
176
  return {
154
177
  async launch(scopeId, locale) {
155
- const current = readSelfUpdateStatus(statusFile);
178
+ const current = readNormalizedSelfUpdateStatus(statusFile, pendingTimeoutMs, now);
156
179
  if (current?.state === 'pending') {
157
180
  throw new Error('A FoxClaw update is already running.');
158
181
  }
@@ -219,7 +242,7 @@ export function createSelfUpdateRuntime(options) {
219
242
  }
220
243
  },
221
244
  async readStatus() {
222
- return readSelfUpdateStatus(statusFile);
245
+ return readNormalizedSelfUpdateStatus(statusFile, pendingTimeoutMs, now);
223
246
  },
224
247
  async clearStatus() {
225
248
  fs.rmSync(statusFile, { force: true });
@@ -71,6 +71,8 @@ Log in:
71
71
  codex login
72
72
  ```
73
73
 
74
+ If you are logging in from a headless host, remote SSH session, WSL setup, or any environment where browser callback login is unreliable, you may use Codex device-code login later through FoxClaw's `/login_device`, `/auth add <name>`, or the Login button in the `/auth` panel. First open `https://chatgpt.com/`, click your username in the lower-left corner, go to Settings > Security, and enable device code authorization for Codex. Workspace accounts may require an admin to allow device-code login. Never share the device code or paste it into an untrusted page.
75
+
74
76
  Check the CLI exists:
75
77
 
76
78
  ```bash
@@ -156,6 +156,8 @@ If Codex is not logged in:
156
156
  codex login
157
157
  ```
158
158
 
159
+ If FoxClaw's `/login_device`, `/auth add <name>`, or `/auth` panel Login button gives you a device code but the login page says it is not allowed, falls back to the regular browser flow, or cannot complete on a headless host, check ChatGPT security settings first: open `https://chatgpt.com/`, click your username in the lower-left corner, go to Settings > Security, and enable device code authorization for Codex. Workspace accounts may require an admin to allow device-code login. Treat the device code as a login grant and never share it.
160
+
159
161
  `codex --version` only verifies the command exists. To verify auth, run:
160
162
 
161
163
  ```bash
@@ -376,6 +376,10 @@ The safer path is adding candidates from the phone with `/auth add <name>`.
376
376
  - `/logout confirm`: logs out the current Codex account.
377
377
  - `/auth add <name>`: adds a candidate account. For example, `/auth add work` creates `auth.json_work` and starts device login.
378
378
 
379
+ Device-code login is the Codex login path for headless hosts. Your ChatGPT account may not allow it by default. Before using `/login_device`, `/auth add <name>`, or the Login button in the `/auth` panel, open `https://chatgpt.com/`, click your username in the lower-left corner, go to Settings > Security, and enable device code authorization for Codex. Workspace or enterprise accounts may require a workspace admin to allow device-code login.
380
+
381
+ Use this carefully: a device code can be stolen by phishing and acts as a login grant. Enter the code only on the ChatGPT/Codex login page you trust. Never send the device code to another person or paste it into an untrusted page.
382
+
379
383
  `/auth add <name>` flow:
380
384
 
381
385
  1. FoxClaw prepares `auth.json_<name>`.
@@ -69,6 +69,8 @@ npm install -g @openai/codex
69
69
  codex login
70
70
  ```
71
71
 
72
+ 如果你是在无头主机、远程 SSH、WSL 或没有可用浏览器回调的环境里登录,后续可能会使用 Codex 的设备码登录,包括 FoxClaw 的 `/login_device`、`/auth add <name>` 和 `/auth` 面板里的“设备登录”。先在浏览器打开 `https://chatgpt.com/`,左下角点用户名,进入“设置 > 安全”,启用“为 Codex 启用设备代码授权”。Workspace 账号可能需要管理员允许设备码登录。设备代码不要分享给任何人,也不要粘贴到不可信页面。
73
+
72
74
  检查命令是否存在:
73
75
 
74
76
  ```bash
@@ -157,6 +157,8 @@ codex --version
157
157
  codex login
158
158
  ```
159
159
 
160
+ 如果你通过 FoxClaw 的 `/login_device`、`/auth add <name>` 或 `/auth` 面板“设备登录”拿到了设备码,但登录页面提示没有权限、退回普通浏览器流程,或无头环境无法完成登录,先检查 ChatGPT 安全设置:打开 `https://chatgpt.com/`,左下角点用户名,进入“设置 > 安全”,启用“为 Codex 启用设备代码授权”。Workspace 账号可能需要管理员在 workspace 权限里允许设备码登录。设备代码等同于登录授权,切勿分享给他人。
161
+
160
162
  `codex --version` 只证明命令存在。要验证认证真的可用,运行:
161
163
 
162
164
  ```bash
@@ -376,6 +376,10 @@ cp -L ~/.codex/auth.json ~/.codex/auth.json_personal
376
376
  - `/logout confirm`:退出当前 Codex 登录。
377
377
  - `/auth add <name>`:新增一个候选账号,例如 `/auth add work` 会创建 `auth.json_work` 并发起设备码登录。
378
378
 
379
+ 设备码登录是无头主机上的 Codex 登录流程。默认情况下 ChatGPT 账号可能没有打开这个权限。使用 `/login_device`、`/auth add <name>` 或 `/auth` 面板里的“设备登录”按钮前,先在浏览器打开 `https://chatgpt.com/`,左下角点用户名,进入“设置 > 安全”,启用“为 Codex 启用设备代码授权”。如果是 workspace/企业账号,可能需要管理员在 workspace 权限里允许设备码登录。
380
+
381
+ 开启后仍要谨慎:设备代码可以被钓鱼页面窃取,等同于一次登录授权。只在你确认的 ChatGPT/Codex 登录页面输入设备代码,不要把设备代码发给别人,也不要粘贴到不可信网页。
382
+
379
383
  `/auth add <name>` 的流程:
380
384
 
381
385
  1. FoxClaw 准备 `auth.json_<name>`。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxden-app/foxclaw",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "Foxden local execution claw for controlling Codex from trusted chat interfaces.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",