claude-cache-keepalive 0.1.13 → 0.1.14

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
@@ -64,6 +64,19 @@ cwarm setup --remove # restores your previous statusline
64
64
 
65
65
  (Written in Node — no Python dependency, no Windows codepage issues.)
66
66
 
67
+ ### Quota segments
68
+
69
+ Once you run `cwarm setup`, the statusline also surfaces two quota warnings sourced straight from Claude Code's own `rate_limits` payload — no extra config needed:
70
+
71
+ ```
72
+ ⚠️ 96%、離reset還2h,建議暫停 │ 🔴 週超前15.0%,剩3.5d均10.0%/天,休息21.8h回綠
73
+ ```
74
+
75
+ - **5-hour window** — once usage crosses 95% *and* the reset is still more than an hour away, an `⚠️ NN%、離reset還Nh,建議暫停` warning appears suggesting you pause. Nothing shows below 95%, or once you're within an hour of reset (the tail is going to burn either way).
76
+ - **7-day window** — a colored ball tracks how far ahead of a straight-line pace budget (100%/7 ≈ 14.3%/day) you're running: 🟢 under 2 points ahead (or behind), a quiet 2-3 point gap with no ball, 🟡 3-5, 🟠 6-8, 🩷 9-11 (no pink circle emoji exists, so it borrows the pink heart), 🔴 12+. Next to the ball: how many days are left in the window, the average %/day you can still spend if usage stays flat for the rest of it, and — once you're more than 2 points ahead — how long you'd need to rest (no further usage) before the pace line catches up and the ball turns green again.
77
+
78
+ Both segments are pure display; they never stop the keepalive from firing. `--ai` mode reads the same underlying numbers to actually throttle itself (see below).
79
+
67
80
  ## Unattended AI mode (opt-in, off by default)
68
81
 
69
82
  Plain keepalive only sends `hi` — harmless, but it doesn't make idle time *useful*. `--ai` mode does: once cwarm decides you've genuinely stepped away, instead of `hi` it starts feeding Claude a repeating checklist of safe, self-verifying work (review the session, sweep for TODOs, add missing tests, sync docs, distill lessons into memory, …), so idle windows turn into progress instead of dead air.
@@ -158,6 +171,19 @@ Environment variables (mostly for testing / advanced use):
158
171
  - **選配 statusline**(顯示 `♻️ cache 58m12s` 倒數;會先備份、包裝既有 statusline、可一鍵還原):`cwarm setup` / `cwarm setup --remove`
159
172
  - **限制**:不能 detach(關視窗=結束,但縮小/背景照常保溫)。
160
173
 
174
+ ### 額度段位(statusline)
175
+
176
+ 裝了 `cwarm setup` 之後,statusline 會直接從 Claude Code 自帶的 `rate_limits` payload 秀出兩段額度提醒,不用額外設定:
177
+
178
+ ```
179
+ ⚠️ 96%、離reset還2h,建議暫停 │ 🔴 週超前15.0%,剩3.5d均10.0%/天,休息21.8h回綠
180
+ ```
181
+
182
+ - **5 小時視窗**:用量超過 95% 且離 reset 還超過 1 小時,才會出現 `⚠️ NN%、離reset還Nh,建議暫停`;不到 95%、或已經進入最後一小時(尾巴額度反正燒到撞牆),都不顯示。
183
+ - **7 天視窗**:用一顆彩色球表示「超前按時間比例均攤的日均進度線(100%/7 ≈ 14.3%/天)多少個百分點」:不到 2 個百分點(含落後)是 🟢,2~3 之間留白不顯示,3~5 是 🟡,6~8 是 🟠,9~11 是 🩷(Unicode 沒有粉紅圓形,借粉紅愛心最接近),12 以上是 🔴。球旁邊接著顯示視窗還剩幾天、照這剩餘天數均攤接下來每天還能燒多少 %,以及——只要超前 2 個百分點以上——要「休息」(不再新增用量)多久,進度線才會爬上來讓球回綠。
184
+
185
+ 這兩段都只是顯示,不會擋掉保溫注入;`--ai` 模式才是拿同一組數字真的去踩剎車(見下方)。
186
+
161
187
  ### 運作原理
162
188
 
163
189
  - **PTY host**:`cwarm` 把 `claude` spawn 在一個它自己擁有的 pseudo-terminal 裡,透明地把你的鍵盤 ↔ claude ↔ 畫面(含視窗 resize)接起來。這跟 tmux/expect/VS Code 終端的做法相同,也是唯一穩健、能把輸入注入終端程式的方式。
@@ -241,6 +267,10 @@ CWARM_AI=1 cwarm # 效果相同,走環境變數
241
267
 
242
268
  ## Changelog
243
269
 
270
+ ### 0.1.14
271
+ - **Feature:** the statusline's 7-day quota warning is now a real pace gauge, not just a raw number. A colored ball shows how far ahead of a straight-line pace budget (100%/7 ≈ 14.3%/day) you're running — 🟢 under 2pt ahead, 🟡 3-5pt, 🟠 6-8pt, 🩷 9-11pt, 🔴 12pt+ — alongside how many days are left in the window, the average %/day you can still spend at that pace, and (once you're 2pt+ ahead) how long you'd need to rest before the ball turns green again. See the new "Quota segments" section above.
272
+ - **功能:** statusline 的 7 天額度提醒從一個原始數字變成真正的配速計。彩色球顯示超前「按時間比例均攤」的日均進度線(100%/7 ≈ 14.3%/天)多少個百分點——🟢 不到 2pt、🟡 3~5pt、🟠 6~8pt、🩷 9~11pt、🔴 12pt+——旁邊接著顯示視窗剩幾天、照這個配速接下來每天還能燒多少 %,以及(超前 2pt 以上時)要休息多久球才會回綠。詳見上方新增的「額度段位(statusline)」章節。
273
+
244
274
  ### 0.1.13
245
275
  - **Fix:** running multiple Claude Code sessions in tabs **in the same folder** no longer corrupts each other's keepalive timing. The host used to measure idle from the *newest* transcript in the project folder — with a busier sibling session next door, its own idle never crossed the threshold, the keepalive never fired, and the cache countdown ran cold (🔴). Now the statusline lands each session's exact `transcript_path` into a per‑session bridge file (`~/.claude/cwarm-session-<id>.json`, keyed by a `CWARM_HOST_ID` the host passes through the PTY environment), and the host pins idle *and* TTL‑regime detection to its own transcript. Bridge files are deleted on exit; stale orphans (crashes) are swept on startup. Without the statusline installed, behavior falls back to the previous folder‑newest heuristic.
246
276
  - **修正:** 用分頁在**同一個資料夾**開多個 Claude Code session 時,保溫計時不再互相干擾。過去 host 是拿專案資料夾裡 *mtime 最新* 的 transcript 判閒置——隔壁分頁還在活動時,自己的閒置永遠算不滿門檻、keepalive 不發、cache 倒數一路走到冷掉(🔴)。現在 statusline 會把每個 session 精確的 `transcript_path` 落地成 per-session 橋接檔(`~/.claude/cwarm-session-<id>.json`,以 host 經 PTY 環境變數傳入的 `CWARM_HOST_ID` 為鍵),host 的閒置判定**與 TTL 檔位偵測**都針定自己的 transcript。橋接檔退出時自動刪除;crash 留下的孤兒檔啟動時順手清掉。沒裝 statusline 時回退原本「資料夾最新」的猜法。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-cache-keepalive",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "Keep Claude Code's prompt cache warm while idle, by running claude inside a PTY host and injecting a tiny keepalive when you step away. Cross-platform, no tmux required.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/keepalive.mjs CHANGED
@@ -313,6 +313,40 @@ export function weeklyGate({ usedPct, resetsAt, nowSec, graceDays = 1 } = {}) {
313
313
  return 'ok';
314
314
  }
315
315
 
316
+ // ---- 週配速具現化(給人看的球號 + 剩餘天數均可用量)----
317
+ // weeklyGate 只吐三檔給 aiPacing 悄悄降速,人完全看不到;這裡把「超前進度線多少」
318
+ // 換算成球色:
319
+ // <2% 🟢(配速正常)/3~5% 🟡/6~8% 🟠/9~11% 🩷(Unicode 無粉紅圓形,借粉紅
320
+ // 愛心最接近)/≥12% 🔴。2~3% 之間留白不顯示球(維持安靜,避免在正常/警示邊界
321
+ // 上抖動)。
322
+ // avgPerDayRemaining:把剩餘額度 (100-usedPct) 均攤到視窗剩餘天數,讓使用者知道
323
+ // 「接下來每天還能燒多少」而不是只看到一個抽象的超前百分點。
324
+ // recoverySec:不再新增用量的前提下,進度線每秒自然爬升 100/WEEK% ——excess 會跟著
325
+ // 等速下降,算出降到 <2%(回綠球門檻)還要「休息」幾秒;已經是綠球則為 0。算出來若
326
+ // 超過視窗剩餘時間,代表撐到 reset 都回不了綠,直接鉗在剩餘時間(reset 一到整組歸零,
327
+ // 保證回綠)。
328
+ export function weeklyPaceInfo({ usedPct, resetsAt, nowSec } = {}) {
329
+ if (usedPct == null || resetsAt == null) return null;
330
+ const WEEK = 7 * 86400;
331
+ const elapsed = Math.min(Math.max(nowSec - (resetsAt - WEEK), 0), WEEK);
332
+ const expectedPct = (elapsed / WEEK) * 100;
333
+ const excess = usedPct - expectedPct;
334
+ let ball = null;
335
+ if (excess >= 12) ball = '\u{1F534}'; // 🔴
336
+ else if (excess >= 9) ball = '\u{1FA77}'; // 🩷
337
+ else if (excess >= 6) ball = '\u{1F7E0}'; // 🟠
338
+ else if (excess >= 3) ball = '\u{1F7E1}'; // 🟡
339
+ else if (excess < 2) ball = '\u{1F7E2}'; // 🟢
340
+ if (!ball) return null;
341
+ const daysRemaining = Math.max(WEEK - elapsed, 0) / 86400;
342
+ const remainingPct = Math.max(100 - usedPct, 0);
343
+ const avgPerDayRemaining = daysRemaining > 0 ? remainingPct / daysRemaining : 0;
344
+ const remainingSec = Math.max(WEEK - elapsed, 0);
345
+ const rate = 100 / WEEK; // %/秒,進度線自然爬升速度
346
+ const recoverySec = excess > 2 ? Math.min((excess - 2) / rate, remainingSec) : 0;
347
+ return { ball, excess, daysRemaining, avgPerDayRemaining, recoverySec };
348
+ }
349
+
316
350
  // ---- 無人值守 AI 模式 ----
317
351
  // 連續兩次注入之間完全沒有人為鍵盤輸入 → 判定無人值守,第三發起改敲「能與 AI 互動」
318
352
  // 的訊息(續推任務/回報進度),而不是傻傻的 "hi";使用者一敲鍵就歸零回到 "hi"。
@@ -4,7 +4,7 @@
4
4
  import fs from 'node:fs';
5
5
  import path from 'node:path';
6
6
  import { spawnSync } from 'node:child_process';
7
- import { defaultClaudeDir, readTtlRegime, regimeParams, billingModeFromSources, accountInfoFromSources, readAuthSources, usageState, usageBridgePath, readAiState, sessionBridgePath } from '../keepalive.mjs';
7
+ import { defaultClaudeDir, readTtlRegime, regimeParams, billingModeFromSources, accountInfoFromSources, readAuthSources, usageState, usageBridgePath, readAiState, sessionBridgePath, weeklyPaceInfo } from '../keepalive.mjs';
8
8
 
9
9
  const claudeDir = defaultClaudeDir();
10
10
  const ORIG = path.join(claudeDir, 'cwarm-statusline-orig.json');
@@ -51,8 +51,11 @@ function payloadCwd(payload) {
51
51
  // 額度橋接 + 提醒段:host(保溫迴圈)收不到 statusline payload,這裡把 rate_limits.five_hour
52
52
  // 落地成 per-cwd 橋接檔給 host 讀(撞牆偵測與 reset 後改敲 "go on" 都靠它)——不同帳號/
53
53
  // 專案的並行 session 各寫各的,不會互相覆蓋彼此的額度視窗。
54
- // 回傳提醒文字:用量 ≥95% 且離 reset 還超過 1 小時 → 建議使用者暫時中斷(接在 cache 倒數之後);
55
- // 不滿 1 小時就不提醒,尾巴額度照常燒到撞牆。
54
+ // 回傳提醒文字(可能兩段用 接起來):
55
+ // 5h — 用量 ≥95% 且離 reset 還超過 1 小時 → 建議使用者暫時中斷;不滿 1 小時就不提醒,
56
+ // 尾巴額度照常燒到撞牆。
57
+ // 週 — weeklyPaceInfo 依超前「按時間比例均攤」進度線幾個百分點顯示球號(<2🟢/3~5🟡/
58
+ // 6~8🟠/9~11🩷/≥12🔴,2~3% 留白),並附上剩餘天數的日均可用量。
56
59
  function usageBridgeAndWarn(payload, cwd) {
57
60
  const fh = payload?.rate_limits?.five_hour;
58
61
  if (!fh || typeof fh !== 'object') return '';
@@ -69,9 +72,25 @@ function usageBridgeAndWarn(payload, cwd) {
69
72
  }));
70
73
  } catch { /* 落地失敗不影響顯示 */ }
71
74
  }
72
- if (usageState({ usedPct, resetsAt, nowSec: Date.now() / 1000 }) !== 'warn') return '';
73
- const hLeft = Math.round((resetsAt - Date.now() / 1000) / 3600);
74
- return `⚠️ ${Math.round(usedPct)}%、離reset還${hLeft}h,建議暫停`; // ⚠️ NN%、離reset還Nh,建議暫停
75
+ const nowSec = Date.now() / 1000;
76
+ const parts = [];
77
+ if (usageState({ usedPct, resetsAt, nowSec }) === 'warn') {
78
+ const hLeft = Math.round((resetsAt - nowSec) / 3600);
79
+ parts.push(`⚠️ ${Math.round(usedPct)}%、離reset還${hLeft}h,建議暫停`); // ⚠️ NN%、離reset還Nh,建議暫停
80
+ }
81
+ const wp = weeklyPaceInfo({ usedPct: sd?.used_percentage ?? null, resetsAt: sd?.resets_at ?? null, nowSec });
82
+ if (wp) {
83
+ // 球 週超前X.X%,剩N.Nd均M.M%/天(,休息H.Hh回綠):X 是超出「按時間比例均攤」
84
+ // 進度線的百分點,均M.M%/天是把剩餘額度攤到視窗剩餘天數後接下來每天還能燒多少,
85
+ // 休息段只在非綠球時出現——不再新增用量、單純等進度線爬上來要等多久才回綠球。
86
+ let text = `${wp.ball} 週超前${wp.excess.toFixed(1)}%,剩${wp.daysRemaining.toFixed(1)}d均${wp.avgPerDayRemaining.toFixed(1)}%/天`;
87
+ if (wp.recoverySec > 0) {
88
+ const h = wp.recoverySec / 3600;
89
+ text += h >= 24 ? `,休息${(h / 24).toFixed(1)}d回綠` : `,休息${h.toFixed(1)}h回綠`;
90
+ }
91
+ parts.push(text);
92
+ }
93
+ return parts.join(' │ ');
75
94
  }
76
95
 
77
96
  // AI 模式開關段:顯示無人值守 AI 模式 on/off 與切換熱鍵。狀態檔以 cwd 編碼命名