glm-quota-line 0.9.0 → 1.0.0

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
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0
4
+
5
+ - Replaced `theme`/`palette` two-config system with unified `theme` presets: `dark`, `light`, `mono`
6
+ - Removed `palette` config and CLI flag entirely
7
+ - Removed `display=both` option; display now supports only `left` (default) and `used`
8
+ - Removed `bar-width` config option; bar width is fixed at 10
9
+ - Changed default style from `text` to `bar`
10
+ - Bar fill now represents remaining quota by default (was used); `display=used` fills by usage
11
+ - Added `light` theme with blue accents for light terminals
12
+ - `loadConfig` now reads `~/.claude/settings.json` env as a fallback auth source
13
+ - `loadConfig` changed from sync to async; all callers updated
14
+ - Updated README with complete config reference, display docs, and terminal quick-check usage
15
+
3
16
  ## 0.9.0
4
17
 
5
18
  - Replaced progressive tier-based backoff with diamond-shaped refresh strategy: high quota (80–100%) refreshes every 2 min, medium (30–79%) every 5 min, low (0–29%) every 2 min — frequent updates when usage is active or quota is near exhaustion, relaxed in between
package/README.en.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <h1 align="center">glm-quota-line</h1>
2
2
 
3
3
  <p align="center">
4
- One-command install, zero dependencies. Shows accurate Zhipu GLM Coding Plan quota balance and reset time in the Claude Code status line — data from the official API, not estimated.
4
+ A Zhipu GLM Coding Plan quota monitor built for Claude Code. Accurate, real-time data from the official site — no window switching, just glance and go.
5
5
  </p>
6
6
 
7
7
  <p align="center">
@@ -15,15 +15,14 @@
15
15
  <a href="./README.md">简体中文</a>
16
16
  </p>
17
17
 
18
- `glm-quota-line` reads the Zhipu GLM Coding Plan quota API, caches results, and prints a short status line for Claude Code's `statusLine.command`. A `SessionStart` hook pre-refreshes the cache so new sessions never show stale data.
19
-
20
18
  ## Features
21
19
 
22
- - **One-command install** — `glm-quota-line install` wires status line and `SessionStart` hooks into Claude Code automatically
23
- - **Accurate quota display** — reads directly from the official GLM quota API, not estimated; shows both 5-hour and weekly token quotas
20
+ - **Terminal quick check** — run `glm-quota-line` in any terminal to view your quota without launching Claude Code
21
+ - **Claude Code status line** — auto-embeds in the status bar after install, shows quota balance and reset time in real time
22
+ - **Bar visualization** — default bar style shows remaining quota at a glance
23
+ - **Smart caching** — tiered refresh by session, TTL, and token usage; `SessionStart` hook pre-refreshes so new sessions never show stale data
24
24
  - **Domestic + international endpoints** — auto-detects `open.bigmodel.cn` and `api.z.ai`
25
25
  - **Zero dependencies** — no runtime deps, single-purpose CLI
26
- - **Smart caching** — refreshes by session, TTL, and token usage; supports manual auth overrides for proxy/gateway setups
27
26
 
28
27
  ## Quick Start
29
28
 
@@ -32,13 +31,13 @@ npm install -g glm-quota-line
32
31
  glm-quota-line install
33
32
  ```
34
33
 
35
- That's it — works out of the box. Default output (`style=text`, `theme=ansi`, `palette=dark`):
34
+ Done. Your Claude Code status bar will now show the quota:
36
35
 
37
- ```text
38
- GLM Lite | 5h 91% | week 47% | reset 14:47
36
+ ```
37
+ GLM Lite █████████░ 91% | W 47% | 14:47
39
38
  ```
40
39
 
41
- Both the 5-hour quota (primary) and weekly quota (secondary) are shown with automatic reset time.
40
+ You can also run `glm-quota-line` directly in the terminal to check your quota without launching Claude Code.
42
41
 
43
42
  Upgrade:
44
43
 
@@ -47,38 +46,52 @@ npm install -g glm-quota-line
47
46
  glm-quota-line check-update
48
47
  ```
49
48
 
50
- ## Custom Styles
49
+ ## Configuration
50
+
51
+ All options are optional. Persist with `glm-quota-line config set`, or override per-invocation with CLI flags.
51
52
 
52
- All style options are optional — adjust only if needed:
53
+ ### style — Output layout
54
+
55
+ | Value | Description | Example |
56
+ |---|---|---|
57
+ | `bar` (default) | Progress bar | `GLM Lite █████████░ 91% \| W 47% \| 14:47` |
58
+ | `text` | Full text | `GLM Lite \| 5h 91% \| week 47% \| reset 14:47` |
59
+ | `compact` | Compact mode | `GLM 5h 91% W 47% \| 14:47` |
53
60
 
54
61
  ```bash
55
- glm-quota-line config set style bar # layout: text / compact / bar
56
- glm-quota-line config set theme ansi # color: plain / ansi
57
- glm-quota-line config set palette dark # palette: dark / mono (only when theme=ansi)
62
+ glm-quota-line config set style compact
58
63
  ```
59
64
 
60
- Different `style` outputs:
61
-
62
- ```text
63
- # style=text (default)
64
- GLM Lite | 5h 91% | week 47% | reset 14:47
65
+ ### theme — Color theme
65
66
 
66
- # style=compact
67
- GLM 5h 91% W 47% | 14:47
67
+ | Value | Description |
68
+ |---|---|
69
+ | `dark` (default) | Dark terminal, strongest color contrast |
70
+ | `light` | White/light terminal, blue-tinted accents |
71
+ | `mono` | Grayscale, minimal distraction |
68
72
 
69
- # style=bar
70
- GLM Lite █░░░░░░░░░ 91% | W 47% | 14:47
73
+ ```bash
74
+ glm-quota-line config set theme light
71
75
  ```
72
76
 
73
- Recommended combinations:
77
+ Quota percentage colors change automatically based on remaining amount:
78
+
79
+ - Green — remaining >= 60%
80
+ - Yellow — remaining 30%–60%
81
+ - Red — remaining < 30%
74
82
 
75
- | Use case | Config |
76
- | -------------- | --------------------------------------------- |
77
- | Dark terminal | `style=bar`, `theme=ansi`, `palette=dark` |
78
- | Light terminal | `style=compact`, `theme=ansi`, `palette=mono` |
79
- | Plain fallback | any `style`, `theme=plain` |
83
+ ### display — Quota metric
80
84
 
81
- ## Custom Auth
85
+ | Value | Description |
86
+ |---|---|
87
+ | `left` (default) | Show remaining quota (bar fills with remaining) |
88
+ | `used` | Show used quota (bar fills with used) |
89
+
90
+ ```bash
91
+ glm-quota-line config set display used
92
+ ```
93
+
94
+ ### auth-token / base-url — Custom auth
82
95
 
83
96
  If Claude Code runs behind a gateway or proxy and the injected `ANTHROPIC_AUTH_TOKEN` / `ANTHROPIC_BASE_URL` are not the real values:
84
97
 
@@ -89,27 +102,38 @@ glm-quota-line config set base-url https://open.bigmodel.cn/api/anthropic
89
102
  glm-quota-line config set base-url https://api.z.ai/api/anthropic
90
103
  ```
91
104
 
92
- Clear with `glm-quota-line config unset auth-token` / `base-url`. Stored overrides take precedence over Claude-injected environment variables.
105
+ Clear with `glm-quota-line config unset auth-token` / `base-url`.
93
106
 
94
- ## Commands
107
+ Auth source priority (highest to lowest):
95
108
 
96
- ```bash
97
- glm-quota-line [--style text|compact|bar] [--display left|used|both]
98
- [--theme plain|ansi] [--palette dark|mono]
109
+ 1. Values persisted via `config set`
110
+ 2. Environment variables `ANTHROPIC_AUTH_TOKEN` / `ANTHROPIC_BASE_URL`
111
+ 3. `env` field in `~/.claude/settings.json`
112
+
113
+ ## Recommended Combinations
99
114
 
115
+ | Use case | Config |
116
+ |---|---|
117
+ | Dark terminal (default) | `style=bar`, `theme=dark` |
118
+ | Light terminal | `style=bar`, `theme=light` |
119
+ | Minimal setup | `style=compact`, `theme=mono` |
120
+ | Track usage | `style=bar`, `display=used` |
121
+
122
+ ## Command Reference
123
+
124
+ ```bash
125
+ glm-quota-line [--style text|compact|bar] [--display left|used] [--theme dark|light|mono]
100
126
  glm-quota-line install [--force]
101
127
  glm-quota-line uninstall
102
128
  glm-quota-line version
103
129
  glm-quota-line check-update
104
130
  glm-quota-line config show
105
- glm-quota-line config set <style|display|theme|palette|auth-token|base-url> <value>
131
+ glm-quota-line config set <style|display|theme|auth-token|base-url> <value>
106
132
  glm-quota-line config unset <key>
107
133
  ```
108
134
 
109
135
  Run `glm-quota-line --help` for full descriptions.
110
136
 
111
- Use `glm-quota-line --version` to print the installed version. `check-update` only checks and prints the suggested upgrade command; it does not modify your environment.
112
-
113
137
  ## Notes
114
138
 
115
139
  - Only `TOKENS_LIMIT` quotas are shown; `TIME_LIMIT` / MCP usage is ignored
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <h1 align="center">glm-quota-line</h1>
2
2
 
3
3
  <p align="center">
4
- 一行命令安装、零依赖,在 Claude Code 状态栏实时显示智谱(Zhipu)GLM Coding Plan 配额余额与重置时间。数据来自官方接口,精准非估算。
4
+ 为 Claude Code 打造的智谱 GLM Coding Plan 配额监控工具。数据精准同步官网,无需切换窗口,随时掌握用量。
5
5
  </p>
6
6
 
7
7
  <p align="center">
@@ -15,15 +15,14 @@
15
15
  <a href="./README.en.md">English</a>
16
16
  </p>
17
17
 
18
- `glm-quota-line` 读取智谱(Zhipu)GLM Coding Plan 配额接口、缓存结果,并输出一行适合 Claude Code `statusLine.command` 的状态文本。通过 `SessionStart` hook 预刷新缓存,新会话不会显示旧配额。
19
-
20
18
  ## 功能
21
19
 
22
- - **一行命令安装** — `glm-quota-line install` 自动接入 Claude Code 状态栏和 `SessionStart` hook
23
- - **精准配额显示** — 数据来自官方配额接口,非估算;同时展示 5 小时和周 token 配额
20
+ - **终端快速查询** — 直接运行 `glm-quota-line` 即可在终端查看配额,无需启动 Claude Code
21
+ - **Claude Code 状态栏** — 安装后自动嵌入状态栏,实时显示配额余额和重置时间
22
+ - **进度条可视化** — 默认 bar 风格,一眼看清剩余比例
23
+ - **智能缓存** — 按会话、TTL 和 token 用量分级刷新;`SessionStart` hook 预刷新,新会话不显示旧数据
24
24
  - **国内 + 国际端点** — 自动识别 `open.bigmodel.cn` 和 `api.z.ai`
25
25
  - **零依赖** — 无运行时依赖,单一用途
26
- - **智能缓存** — 按会话、TTL 和 token 用量刷新;支持代理/网关手动覆盖鉴权
27
26
 
28
27
  ## 快速开始
29
28
 
@@ -32,13 +31,13 @@ npm install -g glm-quota-line
32
31
  glm-quota-line install
33
32
  ```
34
33
 
35
- 两步完成,开箱即用。默认输出(`style=text`,`theme=ansi`,`palette=dark`):
34
+ 安装完成。Claude Code 底部状态栏会自动显示配额:
36
35
 
37
- ```text
38
- GLM Lite | 5h 91% | week 47% | reset 14:47
36
+ ```
37
+ GLM Lite █████████░ 91% | W 47% | 14:47
39
38
  ```
40
39
 
41
- 同时显示 5 小时配额(主要)和周配额(次要),并自动展示重置时间。
40
+ 也可以直接在终端运行 `glm-quota-line` 快速查看用量,无需启动 Claude Code。
42
41
 
43
42
  升级版本:
44
43
 
@@ -47,40 +46,54 @@ npm install -g glm-quota-line
47
46
  glm-quota-line check-update
48
47
  ```
49
48
 
50
- ## 自定义样式
49
+ ## 配置项
50
+
51
+ 所有配置均为可选,按需调整。通过 `glm-quota-line config set` 持久化,或用 CLI 参数临时覆盖。
51
52
 
52
- 所有样式配置均为可选,按需调整:
53
+ ### style — 输出布局
54
+
55
+ | 值 | 说明 | 示例 |
56
+ |---|---|---|
57
+ | `bar`(默认) | 进度条可视化 | `GLM Lite █████████░ 91% \| W 47% \| 14:47` |
58
+ | `text` | 完整文本 | `GLM Lite \| 5h 91% \| week 47% \| reset 14:47` |
59
+ | `compact` | 紧凑模式 | `GLM 5h 91% W 47% \| 14:47` |
53
60
 
54
61
  ```bash
55
- glm-quota-line config set style bar # 布局: text / compact / bar
56
- glm-quota-line config set theme ansi # 配色: plain / ansi
57
- glm-quota-line config set palette dark # 色板: dark / mono(仅 theme=ansi 时生效)
62
+ glm-quota-line config set style compact
58
63
  ```
59
64
 
60
- 不同 style 效果:
61
-
62
- ```text
63
- # style=text(默认)
64
- GLM Lite | 5h 91% | week 47% | reset 14:47
65
+ ### theme — 主题配色
65
66
 
66
- # style=compact
67
- GLM 5h 91% W 47% | 14:47
67
+ | 值 | 说明 |
68
+ |---|---|
69
+ | `dark`(默认) | 深色终端,彩色对比最强 |
70
+ | `light` | 浅色/白色终端,蓝调配色 |
71
+ | `mono` | 灰阶,极简低干扰 |
68
72
 
69
- # style=bar
70
- GLM Lite █░░░░░░░░░ 91% | W 47% | 14:47
73
+ ```bash
74
+ glm-quota-line config set theme light
71
75
  ```
72
76
 
73
- 推荐搭配:
77
+ 配额百分比会根据剩余量自动变色:
74
78
 
75
- | 使用场景 | 配置 |
76
- | ---------- | --------------------------------------------- |
77
- | 深色终端 | `style=bar`, `theme=ansi`, `palette=dark` |
78
- | 浅色终端 | `style=compact`, `theme=ansi`, `palette=mono` |
79
- | 纯文本回退 | 任意 `style`,`theme=plain` |
79
+ - 绿色 — 剩余 >= 60%
80
+ - 黄色 — 剩余 30%–60%
81
+ - 红色 — 剩余 < 30%
80
82
 
81
- ## 自定义鉴权
83
+ ### display — 显示指标
82
84
 
83
- 如果 Claude Code 运行在代理或网关后面,注入的 `ANTHROPIC_AUTH_TOKEN` / `ANTHROPIC_BASE_URL` 不是实际值:
85
+ | 值 | 说明 |
86
+ |---|---|
87
+ | `left`(默认) | 显示剩余量(进度条填充 = 剩余比例) |
88
+ | `used` | 显示已用量(进度条填充 = 已用比例) |
89
+
90
+ ```bash
91
+ glm-quota-line config set display used
92
+ ```
93
+
94
+ ### auth-token / base-url — 自定义鉴权
95
+
96
+ 当 Claude Code 运行在代理或网关后面时,注入的 `ANTHROPIC_AUTH_TOKEN` / `ANTHROPIC_BASE_URL` 可能不是实际值,可手动覆盖:
84
97
 
85
98
  ```bash
86
99
  glm-quota-line config set auth-token <your-real-token>
@@ -89,27 +102,38 @@ glm-quota-line config set base-url https://open.bigmodel.cn/api/anthropic
89
102
  glm-quota-line config set base-url https://api.z.ai/api/anthropic
90
103
  ```
91
104
 
92
- 清除:`glm-quota-line config unset auth-token` / `base-url`。手动保存的值优先于 Claude 注入的环境变量。
105
+ 清除:`glm-quota-line config unset auth-token` / `base-url`。
93
106
 
94
- ## 命令
107
+ 鉴权来源优先级(从高到低):
95
108
 
96
- ```bash
97
- glm-quota-line [--style text|compact|bar] [--display left|used|both]
98
- [--theme plain|ansi] [--palette dark|mono]
109
+ 1. `config set` 持久化的值
110
+ 2. 环境变量 `ANTHROPIC_AUTH_TOKEN` / `ANTHROPIC_BASE_URL`
111
+ 3. `~/.claude/settings.json` 中的 `env` 字段
112
+
113
+ ## 推荐搭配
99
114
 
115
+ | 使用场景 | 配置 |
116
+ |---|---|
117
+ | 深色终端(默认) | `style=bar`, `theme=dark` |
118
+ | 浅色终端 | `style=bar`, `theme=light` |
119
+ | 极简工作流 | `style=compact`, `theme=mono` |
120
+ | 关注已用量 | `style=bar`, `display=used` |
121
+
122
+ ## 命令参考
123
+
124
+ ```bash
125
+ glm-quota-line [--style text|compact|bar] [--display left|used] [--theme dark|light|mono]
100
126
  glm-quota-line install [--force]
101
127
  glm-quota-line uninstall
102
128
  glm-quota-line version
103
129
  glm-quota-line check-update
104
130
  glm-quota-line config show
105
- glm-quota-line config set <style|display|theme|palette|auth-token|base-url> <value>
131
+ glm-quota-line config set <style|display|theme|auth-token|base-url> <value>
106
132
  glm-quota-line config unset <key>
107
133
  ```
108
134
 
109
135
  运行 `glm-quota-line --help` 查看完整说明。
110
136
 
111
- `glm-quota-line --version` 可直接查看当前安装版本;`check-update` 只检查并提示升级命令,不会自动修改环境。
112
-
113
137
  ## 说明
114
138
 
115
139
  - 只展示 `TOKENS_LIMIT` 配额,忽略 `TIME_LIMIT` / MCP 使用量
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glm-quota-line",
3
- "version": "0.9.0",
3
+ "version": "1.0.0",
4
4
  "description": "Zhipu GLM Coding Plan quota monitor for Claude Code status line.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -11,7 +11,7 @@ export async function refreshQuotaOnSessionStart(options = {}) {
11
11
  const loadConfigFn = options.loadConfigFn ?? loadConfig;
12
12
  const resolveQuotaStatusFn = options.resolveQuotaStatusFn ?? resolveQuotaStatus;
13
13
  const config = {
14
- ...loadConfigFn(options.env ?? process.env, userConfig),
14
+ ...(await loadConfigFn(options.env ?? process.env, userConfig)),
15
15
  sessionId: hookInput?.session_id || ""
16
16
  };
17
17
 
@@ -5,7 +5,6 @@ import {
5
5
  TOOL_CONFIG_MANAGED_BY,
6
6
  TOOL_CONFIG_SCHEMA_VERSION,
7
7
  isValidDisplayMode,
8
- isValidPalette,
9
8
  isValidStatusStyle,
10
9
  isValidTheme
11
10
  } from "../shared/constants.js";
@@ -56,10 +55,6 @@ function normalizeToolConfig(config) {
56
55
  normalized.theme = base.theme;
57
56
  }
58
57
 
59
- if (isValidPalette(base.palette)) {
60
- normalized.palette = base.palette;
61
- }
62
-
63
58
  const authToken = normalizeOptionalString(base.authToken);
64
59
  if (authToken) {
65
60
  normalized.authToken = authToken;
package/src/cli/args.js CHANGED
@@ -59,14 +59,6 @@ export function parseArgs(argv = process.argv.slice(2)) {
59
59
  }
60
60
  continue;
61
61
  }
62
-
63
- if (arg === "--palette" || arg.startsWith("--palette=")) {
64
- options.palette = takeValue(argv, index);
65
- if (!arg.includes("=")) {
66
- index += 1;
67
- }
68
- continue;
69
- }
70
62
  }
71
63
 
72
64
  options.positionals = positionals;
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  isValidDisplayMode,
3
- isValidPalette,
4
3
  isValidStatusStyle,
5
4
  isValidTheme
6
5
  } from "../shared/constants.js";
@@ -42,17 +41,12 @@ const CONFIG_KEYS = {
42
41
  display: {
43
42
  property: "displayMode",
44
43
  validate: isValidDisplayMode,
45
- invalidMessage: "Invalid display. Use: left, used, or both."
44
+ invalidMessage: "Invalid display. Use: left or used."
46
45
  },
47
46
  theme: {
48
47
  property: "theme",
49
48
  validate: isValidTheme,
50
- invalidMessage: "Invalid theme. Use: plain or ansi."
51
- },
52
- palette: {
53
- property: "palette",
54
- validate: isValidPalette,
55
- invalidMessage: "Invalid palette. Use: dark or mono."
49
+ invalidMessage: "Invalid theme. Use: dark, light, or mono."
56
50
  },
57
51
  "auth-token": {
58
52
  property: "authToken",
@@ -150,7 +144,7 @@ export async function handleCommand(args, output = process.stdout, dependencies
150
144
  const configKey = CONFIG_KEYS[key];
151
145
  if (!configKey) {
152
146
  process.exitCode = 1;
153
- output.write("Supported config keys: style, display, theme, palette, auth-token, base-url\n");
147
+ output.write("Supported config keys: style, display, theme, auth-token, base-url\n");
154
148
  return true;
155
149
  }
156
150
 
@@ -171,7 +165,7 @@ export async function handleCommand(args, output = process.stdout, dependencies
171
165
  const configKey = CONFIG_KEYS[key];
172
166
  if (!configKey) {
173
167
  process.exitCode = 1;
174
- output.write("Supported config keys: style, display, theme, palette, auth-token, base-url\n");
168
+ output.write("Supported config keys: style, display, theme, auth-token, base-url\n");
175
169
  return true;
176
170
  }
177
171
 
package/src/cli/index.js CHANGED
@@ -10,7 +10,6 @@ import { resolveQuotaStatus } from "../core/quota/service.js";
10
10
  import { getPackageVersion } from "../shared/packageInfo.js";
11
11
  import {
12
12
  isValidDisplayMode,
13
- isValidPalette,
14
13
  isValidStatusStyle,
15
14
  isValidTheme
16
15
  } from "../shared/constants.js";
@@ -19,20 +18,19 @@ function printHelp() {
19
18
  process.stdout.write(`glm-quota-line
20
19
 
21
20
  Usage:
22
- glm-quota-line [--style text|compact|bar] [--display left|used|both]
23
- [--theme plain|ansi] [--palette dark|mono]
21
+ glm-quota-line [--style text|compact|bar] [--display left|used]
22
+ [--theme dark|light|mono]
24
23
  glm-quota-line --version
25
24
  glm-quota-line install [--force]
26
25
  glm-quota-line uninstall
27
26
  glm-quota-line version
28
27
  glm-quota-line check-update
29
28
  glm-quota-line config set style <text|compact|bar>
30
- glm-quota-line config set display <left|used|both>
31
- glm-quota-line config set theme <plain|ansi>
32
- glm-quota-line config set palette <dark|mono>
29
+ glm-quota-line config set display <left|used>
30
+ glm-quota-line config set theme <dark|light|mono>
33
31
  glm-quota-line config set auth-token <token>
34
32
  glm-quota-line config set base-url <url>
35
- glm-quota-line config unset <style|display|theme|palette|auth-token|base-url>
33
+ glm-quota-line config unset <style|display|theme|auth-token|base-url>
36
34
  glm-quota-line config show
37
35
 
38
36
  Commands:
@@ -47,9 +45,8 @@ Commands:
47
45
 
48
46
  Options:
49
47
  --style Output layout: text, compact, or bar.
50
- --display Quota metric: left, used, or both.
51
- --theme Color mode: plain or ansi.
52
- --palette ANSI palette: dark for dark terminals, mono for light terminals.
48
+ --display Quota metric: left or used.
49
+ --theme Theme preset: dark, light, or mono.
53
50
  --force Allow install to replace an unmanaged Claude status line.
54
51
  -v, --version Show the installed version.
55
52
  -h, --help Show this help text.
@@ -57,10 +54,10 @@ Options:
57
54
  Examples:
58
55
  glm-quota-line
59
56
  glm-quota-line --version
60
- glm-quota-line --style bar --theme ansi --palette dark
57
+ glm-quota-line --style bar --theme dark
61
58
  glm-quota-line check-update
62
59
  glm-quota-line config set style compact
63
- glm-quota-line config set theme ansi
60
+ glm-quota-line config set theme light
64
61
  glm-quota-line config set auth-token <your-real-token>
65
62
  glm-quota-line install
66
63
 
@@ -74,8 +71,7 @@ function getStoredDisplayOverrides(userConfig) {
74
71
  return {
75
72
  ...(isValidStatusStyle(userConfig.style) ? { style: userConfig.style } : {}),
76
73
  ...(isValidDisplayMode(userConfig.displayMode) ? { displayMode: userConfig.displayMode } : {}),
77
- ...(isValidTheme(userConfig.theme) ? { theme: userConfig.theme } : {}),
78
- ...(isValidPalette(userConfig.palette) ? { palette: userConfig.palette } : {})
74
+ ...(isValidTheme(userConfig.theme) ? { theme: userConfig.theme } : {})
79
75
  };
80
76
  }
81
77
 
@@ -101,7 +97,7 @@ export async function main() {
101
97
  const config = {
102
98
  // Stored auth/base-url must override Claude's injected env so users can
103
99
  // bypass gateway/proxy credentials when necessary.
104
- ...loadConfig(process.env, userConfig),
100
+ ...(await loadConfig(process.env, userConfig)),
105
101
  // Display config precedence is env defaults -> persisted config -> CLI flags.
106
102
  ...getStoredDisplayOverrides(userConfig),
107
103
  ...args,
@@ -113,9 +109,7 @@ export async function main() {
113
109
  `${formatStatus(quotaStatus, {
114
110
  displayMode: config.displayMode,
115
111
  style: config.style,
116
- theme: config.theme,
117
- palette: config.palette,
118
- barWidth: config.barWidth
112
+ theme: config.theme
119
113
  })}\n`
120
114
  );
121
115
  } catch {
@@ -1,16 +1,8 @@
1
- import { DEFAULT_BAR_WIDTH, STATUS_BAR_CHARACTERS } from "../../shared/constants.js";
1
+ import { STATUS_BAR_CHARACTERS } from "../../shared/constants.js";
2
2
 
3
- function normalizeBarWidth(barWidth) {
4
- if (!Number.isFinite(barWidth)) {
5
- return DEFAULT_BAR_WIDTH;
6
- }
7
-
8
- return Math.min(20, Math.max(5, Math.round(barWidth)));
9
- }
10
-
11
- export function buildBar(usedPercent, barWidth, characters = STATUS_BAR_CHARACTERS) {
12
- const safePercent = Math.min(100, Math.max(0, usedPercent));
13
- const width = normalizeBarWidth(barWidth);
3
+ export function buildBar(percent, characters = STATUS_BAR_CHARACTERS) {
4
+ const safePercent = Math.min(100, Math.max(0, percent));
5
+ const width = 10;
14
6
  let filledUnits;
15
7
 
16
8
  if (safePercent <= 0) {
@@ -29,4 +21,3 @@ export function buildBar(usedPercent, barWidth, characters = STATUS_BAR_CHARACTE
29
21
  emptyText: characters.empty.repeat(width - filledUnits)
30
22
  };
31
23
  }
32
-
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  normalizeDisplayMode,
3
- normalizePalette,
4
3
  normalizeStatusStyle,
5
4
  normalizeTheme
6
5
  } from "../../shared/constants.js";
@@ -30,15 +29,6 @@ function createQuotaTextSegments(quota, displayMode, tone) {
30
29
  ];
31
30
  }
32
31
 
33
- if (mode === "both") {
34
- return [
35
- { text: `${quota.label} left `, tone: "muted" },
36
- { text: quota.leftText, tone },
37
- { text: " used ", tone: "muted" },
38
- { text: quota.usedText, tone }
39
- ];
40
- }
41
-
42
32
  return [
43
33
  { text: `${quota.label} `, tone: "muted" },
44
34
  { text: quota.leftText, tone }
@@ -113,7 +103,21 @@ function createCompactSegments(model) {
113
103
  return segments;
114
104
  }
115
105
 
116
- function createBarSegments(model, barWidth) {
106
+ function createBarMetric(quota, displayMode) {
107
+ if (normalizeDisplayMode(displayMode) === "used") {
108
+ return {
109
+ percent: quota.usedPercent,
110
+ text: quota.usedText
111
+ };
112
+ }
113
+
114
+ return {
115
+ percent: quota.leftPercent,
116
+ text: quota.leftText
117
+ };
118
+ }
119
+
120
+ function createBarSegments(model, displayMode) {
117
121
  if (
118
122
  !Number.isFinite(model.primaryQuota?.leftPercent) ||
119
123
  !Number.isFinite(model.primaryQuota?.usedPercent)
@@ -121,7 +125,8 @@ function createBarSegments(model, barWidth) {
121
125
  return createErrorSegments({ kind: "unavailable" });
122
126
  }
123
127
 
124
- const bar = buildBar(model.primaryQuota.usedPercent, barWidth);
128
+ const metric = createBarMetric(model.primaryQuota, displayMode);
129
+ const bar = buildBar(metric.percent);
125
130
  const severityTone = model.severity;
126
131
  const segments = [
127
132
  { text: model.levelLabel, tone: "label" },
@@ -129,7 +134,7 @@ function createBarSegments(model, barWidth) {
129
134
  { text: bar.filledText, tone: severityTone },
130
135
  { text: bar.emptyText, tone: "barEmpty" },
131
136
  { text: " ", tone: "plain" },
132
- { text: model.primaryQuota.leftText, tone: severityTone }
137
+ { text: metric.text, tone: severityTone }
133
138
  ];
134
139
 
135
140
  if (model.secondaryQuota) {
@@ -152,8 +157,7 @@ export function formatStatus(result, options = {}) {
152
157
 
153
158
  if (model.kind !== "success") {
154
159
  return applyTheme(createErrorSegments(model), {
155
- theme: normalizeTheme(options.theme),
156
- palette: normalizePalette(options.palette)
160
+ theme: normalizeTheme(options.theme)
157
161
  });
158
162
  }
159
163
 
@@ -163,13 +167,12 @@ export function formatStatus(result, options = {}) {
163
167
  if (style === "compact") {
164
168
  segments = createCompactSegments(model);
165
169
  } else if (style === "bar") {
166
- segments = createBarSegments(model, options.barWidth);
170
+ segments = createBarSegments(model, options.displayMode);
167
171
  } else {
168
172
  segments = createTextSegments(model, options.displayMode);
169
173
  }
170
174
 
171
175
  return applyTheme(segments, {
172
- theme: normalizeTheme(options.theme),
173
- palette: normalizePalette(options.palette)
176
+ theme: normalizeTheme(options.theme)
174
177
  });
175
178
  }
@@ -1,7 +1,5 @@
1
1
  import {
2
- DEFAULT_PALETTE,
3
2
  DEFAULT_THEME,
4
- normalizePalette,
5
3
  normalizeTheme
6
4
  } from "../../shared/constants.js";
7
5
 
@@ -9,8 +7,11 @@ const ANSI = {
9
7
  reset: "\u001b[0m",
10
8
  bold: "\u001b[1m",
11
9
  dim: "\u001b[2m",
10
+ underline: "\u001b[4m",
11
+ black: "\u001b[30m",
12
12
  gray: "\u001b[90m",
13
13
  white: "\u001b[37m",
14
+ blue: "\u001b[34m",
14
15
  cyan: "\u001b[36m",
15
16
  green: "\u001b[32m",
16
17
  yellow: "\u001b[33m",
@@ -50,9 +51,11 @@ function getDarkCodes(tone) {
50
51
  function getMonoCodes(tone) {
51
52
  switch (tone) {
52
53
  case "muted":
54
+ return [ANSI.gray];
53
55
  case "barEmpty":
54
- return [ANSI.dim];
56
+ return [ANSI.dim, ANSI.gray];
55
57
  case "reset":
58
+ return [ANSI.underline];
56
59
  case "label":
57
60
  case "good":
58
61
  case "warn":
@@ -64,17 +67,39 @@ function getMonoCodes(tone) {
64
67
  }
65
68
  }
66
69
 
70
+ function getLightCodes(tone) {
71
+ switch (tone) {
72
+ case "label":
73
+ case "reset":
74
+ return [ANSI.blue];
75
+ case "muted":
76
+ return [ANSI.gray];
77
+ case "barEmpty":
78
+ return [ANSI.dim, ANSI.gray];
79
+ case "good":
80
+ return [ANSI.green];
81
+ case "warn":
82
+ return [ANSI.yellow];
83
+ case "danger":
84
+ return [ANSI.red];
85
+ case "neutral":
86
+ return [ANSI.black];
87
+ default:
88
+ return [];
89
+ }
90
+ }
91
+
67
92
  export function applyTheme(segments, options = {}) {
68
93
  const theme = normalizeTheme(options.theme || DEFAULT_THEME);
69
- const palette = normalizePalette(options.palette || DEFAULT_PALETTE);
70
-
71
- if (theme !== "ansi") {
72
- return segments.map((segment) => segment.text).join("");
73
- }
74
94
 
75
95
  return segments
76
96
  .map((segment) => {
77
- const codes = palette === "mono" ? getMonoCodes(segment.tone) : getDarkCodes(segment.tone);
97
+ const codes =
98
+ theme === "mono"
99
+ ? getMonoCodes(segment.tone)
100
+ : theme === "light"
101
+ ? getLightCodes(segment.tone)
102
+ : getDarkCodes(segment.tone);
78
103
  return applyCodes(segment.text, codes);
79
104
  })
80
105
  .join("");
@@ -1,20 +1,34 @@
1
1
  import crypto from "node:crypto";
2
+ import fs from "node:fs/promises";
2
3
  import os from "node:os";
3
4
  import path from "node:path";
4
5
 
5
6
  import {
6
- DEFAULT_BAR_WIDTH,
7
7
  DEFAULT_CACHE_TTL_MS,
8
8
  DEFAULT_CN_BASE_URL,
9
9
  DEFAULT_INTL_BASE_URL,
10
10
  DEFAULT_DISPLAY_MODE,
11
- DEFAULT_PALETTE,
12
11
  DEFAULT_QUOTA_URL,
13
12
  DEFAULT_STYLE,
14
13
  DEFAULT_THEME,
15
14
  DEFAULT_TIMEOUT_MS
16
15
  } from "./constants.js";
17
16
 
17
+ function getClaudeSettingsPath() {
18
+ return path.join(os.homedir(), ".claude", "settings.json");
19
+ }
20
+
21
+ async function readClaudeEnv(claudeSettingsPath) {
22
+ try {
23
+ const filePath = claudeSettingsPath || getClaudeSettingsPath();
24
+ const raw = await fs.readFile(filePath, "utf8");
25
+ const settings = JSON.parse(raw);
26
+ return settings?.env && typeof settings.env === "object" ? settings.env : null;
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
18
32
  function getCacheRoot() {
19
33
  if (process.platform === "darwin") {
20
34
  return path.join(os.homedir(), "Library", "Caches");
@@ -63,13 +77,17 @@ export function normalizeOptionalString(value) {
63
77
  return value.trim();
64
78
  }
65
79
 
66
- export function loadConfig(env = process.env, overrides = {}) {
80
+ export async function loadConfig(env = process.env, overrides = {}, options = {}) {
81
+ const claudeEnv = await readClaudeEnv(options.claudeSettingsPath);
67
82
  const anthropicBaseUrl =
68
- normalizeOptionalString(overrides.baseUrl) || normalizeOptionalString(env.ANTHROPIC_BASE_URL);
83
+ normalizeOptionalString(overrides.baseUrl) ||
84
+ normalizeOptionalString(env.ANTHROPIC_BASE_URL) ||
85
+ normalizeOptionalString(claudeEnv?.ANTHROPIC_BASE_URL);
69
86
  const derivedQuotaUrl = deriveQuotaUrl(anthropicBaseUrl);
70
87
  const authorization =
71
88
  normalizeOptionalString(overrides.authToken) ||
72
- normalizeOptionalString(env.ANTHROPIC_AUTH_TOKEN);
89
+ normalizeOptionalString(env.ANTHROPIC_AUTH_TOKEN) ||
90
+ normalizeOptionalString(claudeEnv?.ANTHROPIC_AUTH_TOKEN);
73
91
  const tokenHash = authorization
74
92
  ? crypto.createHash("sha256").update(authorization).digest("hex").slice(0, 12)
75
93
  : "anonymous";
@@ -84,8 +102,6 @@ export function loadConfig(env = process.env, overrides = {}) {
84
102
  displayMode: DEFAULT_DISPLAY_MODE,
85
103
  style: DEFAULT_STYLE,
86
104
  theme: DEFAULT_THEME,
87
- palette: DEFAULT_PALETTE,
88
- barWidth: DEFAULT_BAR_WIDTH,
89
105
  cacheFilePath: path.join(getCacheRoot(), "glm-quota-line", cacheFileName)
90
106
  };
91
107
  }
@@ -14,10 +14,8 @@ export const LOW_QUOTA_THRESHOLD = 30;
14
14
  export const RATE_LIMIT_RETRY_TTL_MS = 180_000;
15
15
  export const UNAVAILABLE_RETRY_TTL_MS = 120_000;
16
16
  export const DEFAULT_DISPLAY_MODE = "left";
17
- export const DEFAULT_STYLE = "text";
18
- export const DEFAULT_BAR_WIDTH = 10;
19
- export const DEFAULT_THEME = "ansi";
20
- export const DEFAULT_PALETTE = "dark";
17
+ export const DEFAULT_STYLE = "bar";
18
+ export const DEFAULT_THEME = "dark";
21
19
 
22
20
  export const STATUS_BAR_CHARACTERS = {
23
21
  filled: "█",
@@ -32,15 +30,11 @@ export function isValidStatusStyle(value) {
32
30
  }
33
31
 
34
32
  export function isValidDisplayMode(value) {
35
- return value === "left" || value === "used" || value === "both";
33
+ return value === "left" || value === "used";
36
34
  }
37
35
 
38
36
  export function isValidTheme(value) {
39
- return value === "plain" || value === "ansi";
40
- }
41
-
42
- export function isValidPalette(value) {
43
- return value === "dark" || value === "mono";
37
+ return value === "dark" || value === "light" || value === "mono";
44
38
  }
45
39
 
46
40
  export function normalizeStatusStyle(value) {
@@ -54,7 +48,3 @@ export function normalizeDisplayMode(value) {
54
48
  export function normalizeTheme(value) {
55
49
  return isValidTheme(value) ? value : DEFAULT_THEME;
56
50
  }
57
-
58
- export function normalizePalette(value) {
59
- return isValidPalette(value) ? value : DEFAULT_PALETTE;
60
- }