ccus-cli 0.2.11 → 0.2.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/README.md CHANGED
@@ -310,9 +310,9 @@ ccus install --codex --uninstall # 移除 ccus 的 Stop hook
310
310
 
311
311
  - **只支持 Codex CLI**:hooks 与 `codex app-server` 都是 CLI 能力。Codex 桌面版 app(已并入 ChatGPT 桌面 app)不一定触发 CLI 的 hooks、也不一定随附可 spawn 的 `codex` 二进制,不在本期范围。
312
312
  - **代理继承**:ccus 由 Codex 直接 spawn,已继承 Codex 的代理 / `CODEX_HOME`;它再 spawn `codex app-server` 时透传 `process.env`,整条链同环境。主路径必须让 codex 子进程发请求(它读 `HTTP_PROXY` / `HTTPS_PROXY`);wham 回退走 ccus 自己的 Node HTTP,但经统一 env 代理通道(读 `CCUS_PROXY` / `https_proxy` / `http_proxy` / `all_proxy` / `NO_PROXY`,`CCUS_PROXY` 优先、小写优先、对齐 curl),代理环境同样可达。
313
- - **无 codex 命令时回退 wham**:本机没装 `codex` CLI(spawn 返回 `unavailable`)且无新鲜缓存时,ccus 改走 ChatGPT 后端 `wham/usage` HTTP 直连——读 `~/.codex/auth.json` 的 OAuth token(仅 chatgpt 登录模式)、按各窗 `limit_window_seconds` 认 5h / 7d 桶,照常落盘 `source="codex"` 事件。让「装过 codex 留下 hook + auth.json、但 codex 未入 PATH / 已卸载」的用户也能采到额度。仅 `unavailable` 触发,超时 / RPC 错(`error`)不回退。
313
+ - **app-server 失败时回退 wham**:无新鲜缓存时,app-server 返回 `unavailable` / `error` / 空额度或直接抛错,ccus 都会继续走 ChatGPT 后端 `wham/usage` HTTP 直连——读 `~/.codex/auth.json` 的 OAuth token(仅 chatgpt 登录模式)、按各窗 `limit_window_seconds` 认 5h / 7d 桶。HTTP 成功则照常落盘 `source="codex"` 事件;两路都失败则跳过本次事件,不使用过期缓存额度。
314
314
  - **定时同步**:Stop hook 还兜底触发 `ccus sync`(与 Claude statusline 对称)——配过 `ccus sync config --target` 后,Codex 每 turn 结束都会检查 3h 周期,到期自动 export + 复制 bundle(含 Codex token/消息)到目标目录。只用 Codex、不开 Claude Code 时也能自动同步。
315
- - **缓存节流**:Stop 每 turn 触发,额度按 5 分钟 TTL 缓存(`codex-quota-cache.json`),命中秒回不 spawn;过期才拉一次(带 ~10s 超时),失败回退旧缓存。
315
+ - **缓存节流**:Stop 每 turn 触发,额度按 5 分钟 TTL 缓存(`codex-quota-cache.json`),新鲜缓存命中秒回不 spawn;过期后依次尝试 app-server 与 wham,两路失败返回空,不回退过期缓存。
316
316
  - **字段映射**:app-server 返回 `primary`(5h)/ `secondary`(weekly)两窗口,取各自的 `usedPercent`(驼峰,clamp 0–100);ccus 填进 `rate_limits.five_hour` / `seven_day` 的 `used_percentage`,`computeStatuslineEvent` 读时自动算出 usage。
317
317
  - **token / 消息 / 额度都进 export / aggregate**:Codex 的 token、用户消息数、API 请求数合并读取 `<CODEX_HOME>/sessions` 与 `%APPDATA%/orca/codex-runtime-home/home/sessions` 的 rollout;同一相对路径的副本按 JSONL 事件去重,并排除 `source.subagent.other="guardian"` 的 Codex Desktop 内部安全审查会话。统计进入 `ccus export` 的 `weeklySummary.codex` / `dailySummaries[].codex` 段;Stop 落盘的额度快照(`source="codex"` 事件)也进 export/aggregate——export bundle 里 Codex 额度单列到 codex 段、与 Claude 分开看,aggregate daily/weekly CSV 则把 Codex **叠加进 Claude 主字段**合计(累加量相加、额度 peak 取两源 max、latest 两源相加、7d 累计含两源读数),不再单列 `codex*` 列;detail.csv 的 `source` 列区分来源。
318
318
  - **Windows**:`install --codex` 在 Windows 写 `ccus.cmd __codex-hook`(npm 全局装会生成 `ccus.cmd`);Windows 上 Stop hook 偶发收到非法 JSON(已知 bug #23784),ccus 容错按无 payload 处理、仍照常拉额度 + 落盘。
@@ -17,12 +17,12 @@ const node_path_1 = require("node:path");
17
17
  const debug_1 = require("./debug");
18
18
  const api_mode_1 = require("./api-mode");
19
19
  const paths_1 = require("./paths");
20
- // app-server 子进程参数:read-only + untrusted,Codex 官方推荐的最低权限拉额度姿态。
21
- const CODEX_ARGS = ["-s", "read-only", "-a", "untrusted", "app-server"];
20
+ // app-server 子进程参数:read-only + never,避免新版 Codex 拒绝已移除的 untrusted 审批策略。
21
+ const CODEX_ARGS = ["-s", "read-only", "-a", "never", "app-server"];
22
22
  const DEFAULT_TIMEOUT_MS = 10_000;
23
23
  // notify 每 turn 触发,额度缓存 5 分钟,命中秒回避免阻塞 Codex 主流程。
24
24
  const DEFAULT_TTL_MS = 5 * 60 * 1000;
25
- // ChatGPT 后端 wham/usage 回退:仅本机无 codex CLI(spawn unavailable)时走,直连读 auth.json 的 OAuth token。
25
+ // ChatGPT 后端 wham/usage 回退:app-server 未返回可用额度时走,直连读 auth.json 的 OAuth token。
26
26
  const WHAM_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
27
27
  const WHAM_TIMEOUT_MS = 15_000;
28
28
  // wham 各窗口自带的时长(秒):5h 窗 18000、周窗 604800,按它认桶、不假设 primary/secondary 顺序。
@@ -306,7 +306,7 @@ async function fetchCodexQuota(options = {}) {
306
306
  });
307
307
  });
308
308
  }
309
- // --- wham/usage HTTP 直连回退(仅主路径 unavailable 时由 resolveCodexQuota 编排) ---
309
+ // --- wham/usage HTTP 直连回退(主路径未返回可用额度时由 resolveCodexQuota 编排) ---
310
310
  /** 读 wham 窗口使用率:主字段 `used_percent`(下划线,wham 实际返回),驼峰 / `used_percentage` 防御性 fallback;窗口为 null 返回 null。 */
311
311
  function readWhamUsedPercent(window) {
312
312
  if (!window) {
@@ -380,7 +380,7 @@ function emptyWhamOutcome(status) {
380
380
  /**
381
381
  * wham/usage HTTP 直连回退:读 auth.json 的 chatgpt OAuth token → `GET wham/usage` → parseWhamUsage 认桶。
382
382
  *
383
- * 仅在主路径 `unavailable`(本机无 codex CLI)时由 resolveCodexQuota 编排调用。全程不抛错:
383
+ * 主路径未返回可用额度时由 resolveCodexQuota 编排调用。全程不抛错:
384
384
  * 无 token / HTTP 失败 / 非 2xx / JSON 异常 / 解析空 → `{ status: "error", ... }`,成功 → `{ status: "ok", ... }`。
385
385
  * 请求经 api-mode 的 httpRequest(自带统一 env 代理通道,代理环境也能回退采到额度)。
386
386
  */
@@ -465,7 +465,7 @@ function hasAnyQuota(quota) {
465
465
  return quota.fiveHour !== null || quota.sevenDay !== null;
466
466
  }
467
467
  /**
468
- * 缓存优先解析额度:新鲜直接返回;过期拉一次(带超时);拉取失败 / 无有效数据回退旧缓存,全失败返回 null。
468
+ * 缓存优先解析额度:新鲜直接返回;过期先拉 app-server,未得到可用额度再拉 wham;两路均失败或全空返回 null。
469
469
  *
470
470
  * notify 路径用,全程不抛错、不写 stdout。`options.fetcher` 供测试注入。
471
471
  */
@@ -487,30 +487,24 @@ async function resolveCodexQuota(dataDir, options = {}) {
487
487
  if (outcome.status === "ok" && hasAnyQuota(outcome)) {
488
488
  return await persist({ fiveHour: outcome.fiveHour, sevenDay: outcome.sevenDay, resetsAt: outcome.resetsAt });
489
489
  }
490
- // 主路径 unavailable(本机无 codex CLI)→ wham/usage HTTP 直连回退;
491
- // error(超时 / RPC 错 / 进程崩)不触发,避免瞬时故障多扛一次 15s HTTP。
492
- if (outcome.status === "unavailable") {
493
- try {
494
- const whamOutcome = await whamFetcher({
495
- codexHomePath: options.fetchOptions?.codexHomePath,
496
- env: options.fetchOptions?.env,
497
- });
498
- if (whamOutcome.status === "ok" && hasAnyQuota(whamOutcome)) {
499
- return await persist({ fiveHour: whamOutcome.fiveHour, sevenDay: whamOutcome.sevenDay, resetsAt: whamOutcome.resetsAt });
500
- }
501
- (0, debug_1.debugLog)("codex", "wham fallback did not yield usable quota", { status: whamOutcome.status });
502
- }
503
- catch (error) {
504
- (0, debug_1.debugLog)("codex", "wham fallback threw", error instanceof Error ? error.message : String(error));
505
- }
506
- }
507
- else {
508
- (0, debug_1.debugLog)("codex", "fetch did not yield usable quota", { status: outcome.status });
490
+ (0, debug_1.debugLog)("codex", "app-server did not yield usable quota", { status: outcome.status });
491
+ }
492
+ catch (error) {
493
+ (0, debug_1.debugLog)("codex", "app-server fetch threw", error instanceof Error ? error.message : String(error));
494
+ }
495
+ try {
496
+ const whamOutcome = await whamFetcher({
497
+ codexHomePath: options.fetchOptions?.codexHomePath,
498
+ env: options.fetchOptions?.env,
499
+ });
500
+ if (whamOutcome.status === "ok" && hasAnyQuota(whamOutcome)) {
501
+ return await persist({ fiveHour: whamOutcome.fiveHour, sevenDay: whamOutcome.sevenDay, resetsAt: whamOutcome.resetsAt });
509
502
  }
503
+ (0, debug_1.debugLog)("codex", "wham fallback did not yield usable quota", { status: whamOutcome.status });
510
504
  }
511
505
  catch (error) {
512
- (0, debug_1.debugLog)("codex", "fetch threw, fallback to cache", error instanceof Error ? error.message : String(error));
506
+ (0, debug_1.debugLog)("codex", "wham fallback threw", error instanceof Error ? error.message : String(error));
513
507
  }
514
- return cached ? quotaFromCache(cached) : null;
508
+ return null;
515
509
  }
516
510
  //# sourceMappingURL=codex-fetcher.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccus-cli",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "Claude Code statusline usage logger and dashboard CLI",
5
5
  "type": "commonjs",
6
6
  "bin": {