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 +13 -0
- package/README.en.md +63 -39
- package/README.md +64 -40
- package/package.json +1 -1
- package/src/claude/sessionStart.js +1 -1
- package/src/claude/settings.js +0 -5
- package/src/cli/args.js +0 -8
- package/src/cli/commands.js +4 -10
- package/src/cli/index.js +12 -18
- package/src/core/status/bar.js +4 -13
- package/src/core/status/format.js +21 -18
- package/src/core/status/theme.js +34 -9
- package/src/shared/config.js +23 -7
- package/src/shared/constants.js +4 -14
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
|
-
|
|
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
|
-
- **
|
|
23
|
-
- **
|
|
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
|
-
|
|
34
|
+
Done. Your Claude Code status bar will now show the quota:
|
|
36
35
|
|
|
37
|
-
```
|
|
38
|
-
GLM Lite
|
|
36
|
+
```
|
|
37
|
+
GLM Lite █████████░ 91% | W 47% | 14:47
|
|
39
38
|
```
|
|
40
39
|
|
|
41
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
```text
|
|
63
|
-
# style=text (default)
|
|
64
|
-
GLM Lite | 5h 91% | week 47% | reset 14:47
|
|
65
|
+
### theme — Color theme
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
70
|
-
|
|
73
|
+
```bash
|
|
74
|
+
glm-quota-line config set theme light
|
|
71
75
|
```
|
|
72
76
|
|
|
73
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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`.
|
|
105
|
+
Clear with `glm-quota-line config unset auth-token` / `base-url`.
|
|
93
106
|
|
|
94
|
-
|
|
107
|
+
Auth source priority (highest to lowest):
|
|
95
108
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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|
|
|
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
|
-
|
|
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
|
-
-
|
|
23
|
-
-
|
|
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
|
-
|
|
34
|
+
安装完成。Claude Code 底部状态栏会自动显示配额:
|
|
36
35
|
|
|
37
|
-
```
|
|
38
|
-
GLM Lite
|
|
36
|
+
```
|
|
37
|
+
GLM Lite █████████░ 91% | W 47% | 14:47
|
|
39
38
|
```
|
|
40
39
|
|
|
41
|
-
|
|
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
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
```text
|
|
63
|
-
# style=text(默认)
|
|
64
|
-
GLM Lite | 5h 91% | week 47% | reset 14:47
|
|
65
|
+
### theme — 主题配色
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
| 值 | 说明 |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `dark`(默认) | 深色终端,彩色对比最强 |
|
|
70
|
+
| `light` | 浅色/白色终端,蓝调配色 |
|
|
71
|
+
| `mono` | 灰阶,极简低干扰 |
|
|
68
72
|
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
```bash
|
|
74
|
+
glm-quota-line config set theme light
|
|
71
75
|
```
|
|
72
76
|
|
|
73
|
-
|
|
77
|
+
配额百分比会根据剩余量自动变色:
|
|
74
78
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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
|
|
105
|
+
清除:`glm-quota-line config unset auth-token` / `base-url`。
|
|
93
106
|
|
|
94
|
-
|
|
107
|
+
鉴权来源优先级(从高到低):
|
|
95
108
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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|
|
|
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
|
@@ -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
|
|
package/src/claude/settings.js
CHANGED
|
@@ -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;
|
package/src/cli/commands.js
CHANGED
|
@@ -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
|
|
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:
|
|
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,
|
|
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,
|
|
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
|
|
23
|
-
[--theme
|
|
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
|
|
31
|
-
glm-quota-line config set theme <
|
|
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|
|
|
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
|
|
51
|
-
--theme
|
|
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
|
|
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
|
|
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 {
|
package/src/core/status/bar.js
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { STATUS_BAR_CHARACTERS } from "../../shared/constants.js";
|
|
2
2
|
|
|
3
|
-
function
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
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
|
|
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:
|
|
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.
|
|
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
|
}
|
package/src/core/status/theme.js
CHANGED
|
@@ -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 =
|
|
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("");
|
package/src/shared/config.js
CHANGED
|
@@ -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) ||
|
|
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
|
}
|
package/src/shared/constants.js
CHANGED
|
@@ -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 = "
|
|
18
|
-
export const
|
|
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"
|
|
33
|
+
return value === "left" || value === "used";
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
export function isValidTheme(value) {
|
|
39
|
-
return value === "
|
|
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
|
-
}
|