@fyeeme/pi-statusline 1.2.3 → 1.2.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
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.2.5] - 2026-09-18
11
+
12
+ ### Changed
13
+
14
+ - Compact token segment: `tokens 78.9%(↑in ↓out Rcache)` replaces `tokens total(in, out, cache,hit%)`. Cache hit rate moves to the front; the total and cache-write numbers are no longer displayed.
15
+ - ZAI quota segments made symmetric and labeled: `5h 42%(1.2M,1h23m) · wk 35%(12M,3d4h)` replaces `Usage 42%(1h23m) · W:35%(1.2M,3d4h)`. Both windows now show in-window token usage (the 5h window is summed via the model-usage API over `[resetAt - 5h, now]`); the natural-week fallback stays `wk 1.2M`. The context segment gains a `Ctx` prefix (`Ctx 45.2%/64k`).
16
+
17
+ ## [1.2.4] - 2026-09-10
18
+
19
+ ### Changed
20
+
21
+ - Follow DeepSeek's DeepSeek-V4.1-Flash price revision: the Flash column is now ¥0.02/0.04 cache-hit input, ¥1/2 cache-miss input, ¥4/8 output per million tokens (peak values; off-peak is half). `deepseek-flash` is the canonical id for these rates, and the retired `deepseek-v4-flash` / `deepseek-v4-flash-vision-exp` ids still work and are billed at the same Flash rates, so all three share one price entry. V4 Pro (`deepseek-v4-pro`, DeepSeek-V4-Pro-0813) is unchanged.
22
+
10
23
  ## [1.2.3] - 2026-09-09
11
24
 
12
25
  ### Added
package/README.md CHANGED
@@ -9,7 +9,7 @@ A rich custom status bar for [pi](https://pi.dev) that replaces the default foot
9
9
 
10
10
  - **Provider-aware usage** — special support for DeepSeek and GLM/ZAI (see [Provider Support](#provider-support))
11
11
  - **DeepSeek peak/off-peak pricing** — session cost follows DeepSeek's 2026-08-17 peak/off-peak billing (peak 9:00-12:00 / 14:00-18:00 Beijing time on weekdays; weekends are off-peak all day since 2026-08-23), priced per-message by timestamp, with a live `peak`/`off-peak` indicator in the footer (see [DeepSeek](#deepseek))
12
- - **Token usage**: input, output, cache read/write, total per session, with cache hit rate
12
+ - **Token usage**: compact `tokens 78.9%(↑in ↓out Rcache)` segment with cache hit rate
13
13
  - **Cost**: cumulative session cost with currency auto-detection (¥ for DeepSeek/CNY, $ otherwise)
14
14
  - **Context window**: usage percentage and size
15
15
  - **Timing**: elapsed time + tokens/sec for last response
@@ -29,9 +29,11 @@ DeepSeek now bills in two periods — **peak hours are 9:00-12:00 and 14:00-18:0
29
29
 
30
30
  | Model | Period | Cache-hit input | Cache-miss input | Output |
31
31
  |---|---|---|---|---|
32
- | deepseek-v4-flash / deepseek-v4-flash-vision-exp | off-peak / peak | ¥0.05 / ¥0.10 | ¥1.5 / ¥3.0 | ¥4.5 / ¥9.0 |
32
+ | deepseek-flash | off-peak / peak | ¥0.02 / ¥0.04 | ¥1 / ¥2 | ¥4 / ¥8 |
33
33
  | deepseek-v4-pro | off-peak / peak | ¥0.15 / ¥0.30 | ¥4.5 / ¥9.0 | ¥13.5 / ¥27.0 |
34
34
 
35
+ - **Legacy model ids** — `deepseek-v4-flash` and `deepseek-v4-flash-vision-exp` are retired but still callable; requests are served by DeepSeek-V4.1-Flash and billed at the Flash rates, so they share the `deepseek-flash` prices.
36
+
35
37
  - **Per-message pricing by timestamp** — each message is priced at the rate of its own time (`AssistantMessage.timestamp`), so a long session spanning a period boundary (9:00/12:00/14:00/18:00) accounts every message correctly instead of using a single fixed rate.
36
38
  - **Peak/off-peak indicator** — the footer appends the current period so you always know which rate is in effect: `peak` during weekday 9:00-12:00 / 14:00-18:00 Beijing time, `off-peak` otherwise (including all day on weekends since 2026-08-23):
37
39
 
@@ -60,12 +62,12 @@ Shows **5-hour rolling quota** and **weekly quota** (or natural-week usage as fa
60
62
 
61
63
  | Segment | Example | Source |
62
64
  |---------|---------|--------|
63
- | 5h quota | `Usage 42%(1h23m)` | `GET /api/monitor/usage/quota/limit` (`unit:3`) |
64
- | Weekly quota | `W:35%(1.2M,3d4h)` | `quota/limit` (`unit:6`) + `model-usage` API |
65
- | Natural week | `W:1.2M` | `model-usage` API (Mon 00:00 local → now) |
65
+ | 5h quota | `5h 42%(1.2M,1h23m)` | `quota/limit` (`unit:3`) + `model-usage` API |
66
+ | Weekly quota | `wk 35%(12M,3d4h)` | `quota/limit` (`unit:6`) + `model-usage` API |
67
+ | Natural week | `wk 1.2M` | `model-usage` API (Mon 00:00 local → now) |
66
68
 
67
- - **5-hour rolling window**: percentage used + countdown to reset
68
- - **Weekly quota**: if the plan exposes a `unit:6` weekly limit, shows percentage, tokens, and reset countdown; otherwise falls back to real usage for the current natural week (host local timezone)
69
+ - **5-hour rolling window**: percentage used, in-window token usage, countdown to reset
70
+ - **Weekly quota**: if the plan exposes a `unit:6` weekly limit, shows percentage, in-week token usage, and reset countdown; otherwise falls back to real usage for the current natural week (host local timezone)
69
71
  - **Account level**: also fetched (visible in `/status-debug`)
70
72
  - Supports both `zai` (`api.z.ai`) and `zai-coding-cn` (`open.bigmodel.cn`) endpoints
71
73
 
@@ -119,7 +121,7 @@ See the Pi Packages guide on [pi.dev](https://pi.dev) for the full list of sourc
119
121
 
120
122
  ```
121
123
  ~/projects/my-repo (main) deepseek-v4-pro · xhigh
122
- tokens 65k(in 12k, out 8k, cache 45k) · ¥0.12/50.00 · 7d:1.2M · off-peak · 45.2%/64k · 2m30s 38.2tok/s
124
+ tokens 78.9%(↑12k ↓8k R45k) · ¥0.12/50.00 · 7d:1.2M · off-peak · Ctx 45.2%/64k · 2m30s 38.2tok/s
123
125
  ```
124
126
 
125
127
  **Line 1**: cwd + git branch (left) | model + thinking level (right)
@@ -130,7 +132,7 @@ tokens 65k(in 12k, out 8k, cache 45k) · ¥0.12/50.00 · 7d:1.2M · off-peak ·
130
132
 
131
133
  ```
132
134
  ~/projects/my-repo (main) glm-4.6 · high
133
- tokens 65k(in 12k, out 8k, cache 45k) · Usage 42%(1h23m) · W:35%(1.2M,3d4h) · 45.2%/64k · 2m30s 38.2tok/s
135
+ tokens 78.9%(↑12k ↓8k R45k) · 5h 42%(1.2M,1h23m) · wk 35%(12M,3d4h) · Ctx 45.2%/64k · 2m30s 38.2tok/s
134
136
  ```
135
137
 
136
138
  ## Environment Variables
package/footer.ts CHANGED
@@ -78,22 +78,17 @@ export function buildStatLine(
78
78
  ): string {
79
79
  const mods: string[] = [];
80
80
 
81
- // tokens 566k(in 29k, out 22k, cache 515k,45.2%)
81
+ // tokens 45.2%(↑29k ↓22k R515k)
82
82
  {
83
83
  const tok: string[] = [];
84
- if (stats.input) tok.push(`in ${fmt(stats.input)}`);
85
- if (stats.output) tok.push(`out ${fmt(stats.output)}`);
86
- const cache = stats.cacheRead + stats.cacheWrite;
87
- if (cache) {
88
- const cacheStr = stats.hitRate > 0
89
- ? `cache ${fmt(cache)},${(stats.hitRate * 100).toFixed(1)}%`
90
- : `cache ${fmt(cache)}`;
91
- tok.push(cacheStr);
92
- }
93
- if (tok.length > 0 && stats.total) {
94
- mods.push(`tokens ${fmt(stats.total)}(${tok.join(", ")})`);
95
- } else if (tok.length > 0) {
96
- mods.push(tok.join(", "));
84
+ if (stats.input) tok.push(`↑${fmt(stats.input)}`);
85
+ if (stats.output) tok.push(`↓${fmt(stats.output)}`);
86
+ if (stats.cacheRead) tok.push(`R${fmt(stats.cacheRead)}`);
87
+ if (tok.length > 0) {
88
+ const pct = stats.hitRate > 0 ? ` ${(stats.hitRate * 100).toFixed(1)}%` : "";
89
+ mods.push(`tokens${pct}(${tok.join(" ")})`);
90
+ } else if (stats.total) {
91
+ mods.push(`tokens ${fmt(stats.total)}`);
97
92
  }
98
93
  }
99
94
 
@@ -111,13 +106,13 @@ export function buildStatLine(
111
106
  }
112
107
  }
113
108
 
114
- // context: 45.2%/16k or ?/16k
109
+ // Ctx: 45.2%/16k or ?/16k
115
110
  if (contextUsage) {
116
111
  const w = fmt(contextUsage.contextWindow);
117
112
  mods.push(
118
113
  contextUsage.percent !== null
119
- ? `${contextUsage.percent.toFixed(1)}%/${w}`
120
- : `?/${w}`,
114
+ ? `Ctx ${contextUsage.percent.toFixed(1)}%/${w}`
115
+ : `Ctx ?/${w}`,
121
116
  );
122
117
  }
123
118
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fyeeme/pi-statusline",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "Rich custom status bar for pi with token usage, cost, DeepSeek balance, context window, and timing.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -31,19 +31,23 @@ export interface DeepSeekCnyPrice {
31
31
  }
32
32
 
33
33
  /**
34
- * DeepSeek 官方 CNY 单价(元 / 百万 tokens),2026-08-17 峰谷计价起生效(见官方定价页):
35
- * 缓存命中输入 / 未命中输入 / 输出;cacheWrite 官方未单列(按 0 计)。
34
+ * DeepSeek 官方 CNY 单价(元 / 百万 tokens):2026-08-17 起峰谷计价生效;flash 单价随后按官方定价页的
35
+ * DeepSeek-V4.1-Flash 档位下调(高峰 ¥2 输入 / ¥8 输出 / ¥0.04 缓存命中,空闲为其一半),pro
36
+ * (DeepSeek-V4-Pro-0813)维持原价。字段为缓存命中输入 / 未命中输入 / 输出;cacheWrite 官方未单列(按 0 计)。
36
37
  */
38
+
39
+ /** Flash 档单价:`deepseek-flash` 与其两个已下线旧 id 均由 DeepSeek-V4.1-Flash 提供服务,价格一致。 */
40
+ const DEEPSEEK_FLASH_PRICES: DeepSeekCnyPrice = {
41
+ peak: { input: 2.0, output: 8.0, cacheRead: 0.04, cacheWrite: 0 },
42
+ offPeak: { input: 1.0, output: 4.0, cacheRead: 0.02, cacheWrite: 0 },
43
+ };
44
+
37
45
  export const DEEPSEEK_CNY_PRICES: Record<string, DeepSeekCnyPrice> = {
38
- "deepseek-v4-flash": {
39
- peak: { input: 3.0, output: 9.0, cacheRead: 0.1, cacheWrite: 0 },
40
- offPeak: { input: 1.5, output: 4.5, cacheRead: 0.05, cacheWrite: 0 },
41
- },
42
- // 视觉变体:官方定价与 V4-Flash 完全相同(图片按 token 计费,单张最高 384 tokens,无视觉专项费用)
43
- "deepseek-v4-flash-vision-exp": {
44
- peak: { input: 3.0, output: 9.0, cacheRead: 0.1, cacheWrite: 0 },
45
- offPeak: { input: 1.5, output: 4.5, cacheRead: 0.05, cacheWrite: 0 },
46
- },
46
+ "deepseek-flash": DEEPSEEK_FLASH_PRICES,
47
+ // 旧 id:模型已下线,但请求仍可发出,由 DeepSeek-V4.1-Flash 提供服务并按 Flash 价计费;
48
+ // vision 变体亦然(官方未单列视觉费用,图片按 token 计费,单张最高 384 tokens)。
49
+ "deepseek-v4-flash": DEEPSEEK_FLASH_PRICES,
50
+ "deepseek-v4-flash-vision-exp": DEEPSEEK_FLASH_PRICES,
47
51
  "deepseek-v4-pro": {
48
52
  peak: { input: 9.0, output: 27.0, cacheRead: 0.3, cacheWrite: 0 },
49
53
  offPeak: { input: 4.5, output: 13.5, cacheRead: 0.15, cacheWrite: 0 },
package/providers/zai.ts CHANGED
@@ -60,6 +60,7 @@ export class ZaiUsageProvider implements UsageProvider {
60
60
  const level = quotaData.data?.level ?? "";
61
61
 
62
62
  const now = Date.now();
63
+ let fiveHourTokens = 0;
63
64
  let weeklyTokens = 0;
64
65
  let weeklyResetAt = 0;
65
66
  let weeklyPct = 0;
@@ -83,10 +84,23 @@ export class ZaiUsageProvider implements UsageProvider {
83
84
  weeklyTokens = await this.fetchCycleUsage(origin, headers, cycleStart, now);
84
85
  }
85
86
 
87
+ // 5h rolling window: GLM starts the 5h window at the first request, so
88
+ // the current window spans [resetAt - 5h, now]. Sum model-usage over it.
89
+ const tokensResetAt = fiveHourLimit?.nextResetTime ?? 0;
90
+ if (tokensResetAt > 0) {
91
+ fiveHourTokens = await this.fetchCycleUsage(
92
+ origin,
93
+ headers,
94
+ tokensResetAt - 5 * 60 * 60 * 1000,
95
+ now,
96
+ );
97
+ }
98
+
86
99
  return {
87
100
  provider: model.provider,
88
101
  tokensLimitPct: fiveHourLimit?.percentage ?? 0,
89
- tokensResetAt: fiveHourLimit?.nextResetTime ?? 0,
102
+ tokensResetAt,
103
+ fiveHourTokens,
90
104
  level,
91
105
  weeklyTokens,
92
106
  weeklyResetAt,
@@ -103,18 +117,18 @@ export class ZaiUsageProvider implements UsageProvider {
103
117
  const zai = result as ZaiResult;
104
118
  const parts: string[] = [];
105
119
 
106
- // 5-hour rolling quota
107
- const countdown = formatCountdown(zai.tokensResetAt);
108
- parts.push(`Usage ${zai.tokensLimitPct}%(${countdown})`);
120
+ // 5h 42%(1.2M,1h23m) — 5-hour rolling window: pct(in-window tokens,reset countdown)
121
+ if (zai.tokensResetAt > 0) {
122
+ parts.push(`5h ${zai.tokensLimitPct}%(${fmt(zai.fiveHourTokens)},${formatCountdown(zai.tokensResetAt)})`);
123
+ }
109
124
 
110
- // Weekly quota
125
+ // wk 35%(12M,3d4h) — weekly quota; natural-week fallback has no pct → wk 1.2M
111
126
  if (zai.isNaturalWeek) {
112
127
  if (zai.weeklyTokens > 0) {
113
- parts.push(`W:${fmt(zai.weeklyTokens)}`);
128
+ parts.push(`wk ${fmt(zai.weeklyTokens)}`);
114
129
  }
115
- } else if (zai.weeklyTokens > 0 || zai.weeklyPct > 0) {
116
- const weeklyCountdown = formatWeeklyCountdown(zai.weeklyResetAt);
117
- parts.push(`W:${zai.weeklyPct}%(${fmt(zai.weeklyTokens)},${weeklyCountdown})`);
130
+ } else if (zai.weeklyResetAt > 0) {
131
+ parts.push(`wk ${zai.weeklyPct}%(${fmt(zai.weeklyTokens)},${formatWeeklyCountdown(zai.weeklyResetAt)})`);
118
132
  }
119
133
 
120
134
  return parts.join(" · ");
@@ -125,6 +139,7 @@ export class ZaiUsageProvider implements UsageProvider {
125
139
  const zai = result as ZaiResult;
126
140
  w(` tokensLimitPct: ${zai.tokensLimitPct}%`);
127
141
  w(` tokensResetAt: ${new Date(zai.tokensResetAt).toISOString()}`);
142
+ w(` fiveHourTokens: ${zai.fiveHourTokens}`);
128
143
  w(` level: ${zai.level}`);
129
144
  w(` weeklyTokens: ${zai.weeklyTokens}`);
130
145
  w(` weeklyPct: ${zai.weeklyPct}%`);
package/types.ts CHANGED
@@ -33,6 +33,8 @@ export type ZaiResult = {
33
33
  provider: "zai" | "zai-coding-cn";
34
34
  tokensLimitPct: number;
35
35
  tokensResetAt: number;
36
+ /** tokens used within the current 5-hour window (0 when unknown) */
37
+ fiveHourTokens: number;
36
38
  level: string;
37
39
  weeklyTokens: number;
38
40
  weeklyResetAt: number;