glm-quota-line 0.5.0 → 0.7.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 +15 -0
- package/README.en.md +117 -0
- package/README.md +49 -94
- package/package.json +2 -2
- package/src/cli/args.js +5 -1
- package/src/cli/commands.js +30 -1
- package/src/cli/index.js +14 -0
- package/src/cli/update.js +120 -0
- package/src/core/quota/cache.js +55 -7
- package/src/core/quota/parse.js +141 -46
- package/src/core/status/format.js +92 -39
- package/src/core/status/viewModel.js +73 -52
- package/src/shared/config.js +12 -5
- package/src/shared/constants.js +2 -0
- package/src/shared/packageInfo.js +29 -0
- package/README.zh-CN.md +0 -166
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
- Added `--version` and `version` so the installed CLI version is visible directly from the command line
|
|
6
|
+
- Added `check-update` to compare the installed version with npm and print a suggested upgrade command without auto-updating
|
|
7
|
+
- Improved CLI help and README docs with upgrade and version-check examples
|
|
8
|
+
- Corrected published package metadata to include `README.en.md` in the npm file list
|
|
9
|
+
|
|
10
|
+
## 0.6.0
|
|
11
|
+
|
|
12
|
+
- Added official international GLM quota endpoint support for `api.z.ai` while preserving domestic `open.bigmodel.cn` detection
|
|
13
|
+
- Added dual-token status rendering for the new package shape, showing both the 5h quota and weekly quota in `text`, `compact`, and `bar` styles
|
|
14
|
+
- Kept old packages compatible by continuing to read the legacy `TOKENS_LIMIT(number=5)` quota and ignoring `TIME_LIMIT` / MCP usage
|
|
15
|
+
- Updated cache compatibility so existing cached percent-based results still render after upgrading
|
|
16
|
+
- Refreshed English and Chinese README examples to document international setup and the new token-only display behavior
|
|
17
|
+
|
|
3
18
|
## 0.5.0
|
|
4
19
|
|
|
5
20
|
- Changed bar characters from `■□` (discrete squares) to `█░` (continuous blocks) to match Claude Code official statusline style
|
package/README.en.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<h1 align="center">glm-quota-line</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
One-command install, zero dependencies. Shows accurate GLM quota balance and reset time in the Claude Code status line — data from the official API, not estimated.
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/glm-quota-line"><img alt="npm version" src="https://img.shields.io/npm/v/glm-quota-line?logo=npm&color=cb3837"></a>
|
|
9
|
+
<a href="https://www.npmjs.com/package/glm-quota-line"><img alt="node version" src="https://img.shields.io/node/v/glm-quota-line?logo=node.js&color=339933"></a>
|
|
10
|
+
<a href="./LICENSE"><img alt="license" src="https://img.shields.io/npm/l/glm-quota-line"></a>
|
|
11
|
+
<img alt="dependencies" src="https://img.shields.io/badge/dependencies-0-2ea44f">
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="./README.md">简体中文</a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
`glm-quota-line` reads the GLM 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
|
+
## Features
|
|
21
|
+
|
|
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
|
|
24
|
+
- **Domestic + international endpoints** — auto-detects `open.bigmodel.cn` and `api.z.ai`
|
|
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
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install -g glm-quota-line
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Global install is recommended because `install` writes stable executable paths into `statusLine.command` and `SessionStart` hooks. `npx` works for one-off previews.
|
|
35
|
+
|
|
36
|
+
Upgrade:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install -g glm-quota-line
|
|
40
|
+
glm-quota-line check-update
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
glm-quota-line install
|
|
47
|
+
glm-quota-line config set style bar
|
|
48
|
+
glm-quota-line config set theme ansi
|
|
49
|
+
glm-quota-line config set palette dark
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Output
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
GLM Lite | 5h 91% | week 47% | reset 14:47
|
|
56
|
+
|
|
57
|
+
GLM 5h 91% W 47% | 14:47
|
|
58
|
+
|
|
59
|
+
GLM Lite █░░░░░░░░░ 91% | W 47% | 14:47
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Both the 5-hour quota (primary) and weekly quota (secondary) are shown with automatic reset time.
|
|
63
|
+
|
|
64
|
+
## Styles & Colors
|
|
65
|
+
|
|
66
|
+
| Use case | Config |
|
|
67
|
+
| -------------- | --------------------------------------------- |
|
|
68
|
+
| Dark terminal | `style=bar`, `theme=ansi`, `palette=dark` |
|
|
69
|
+
| Light terminal | `style=compact`, `theme=ansi`, `palette=mono` |
|
|
70
|
+
| Plain fallback | any `style`, `theme=plain` |
|
|
71
|
+
|
|
72
|
+
`style` controls layout (`text` / `compact` / `bar`). `theme` enables ANSI color. `palette` only applies when `theme=ansi`.
|
|
73
|
+
|
|
74
|
+
Set `NO_COLOR=1` to force plain output.
|
|
75
|
+
|
|
76
|
+
## Custom Auth
|
|
77
|
+
|
|
78
|
+
If Claude Code runs behind a gateway or proxy and the injected `ANTHROPIC_AUTH_TOKEN` / `ANTHROPIC_BASE_URL` are not the real values:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
glm-quota-line config set auth-token <your-real-token>
|
|
82
|
+
glm-quota-line config set base-url https://open.bigmodel.cn/api/anthropic
|
|
83
|
+
# or
|
|
84
|
+
glm-quota-line config set base-url https://api.z.ai/api/anthropic
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Clear with `glm-quota-line config unset auth-token` / `base-url`. Stored overrides take precedence over Claude-injected environment variables.
|
|
88
|
+
|
|
89
|
+
## Commands
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
glm-quota-line [--style text|compact|bar] [--display left|used|both]
|
|
93
|
+
[--theme plain|ansi] [--palette dark|mono]
|
|
94
|
+
|
|
95
|
+
glm-quota-line install [--force]
|
|
96
|
+
glm-quota-line uninstall
|
|
97
|
+
glm-quota-line version
|
|
98
|
+
glm-quota-line check-update
|
|
99
|
+
glm-quota-line config show
|
|
100
|
+
glm-quota-line config set <style|display|theme|palette|auth-token|base-url> <value>
|
|
101
|
+
glm-quota-line config unset <key>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Run `glm-quota-line --help` for full descriptions.
|
|
105
|
+
|
|
106
|
+
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.
|
|
107
|
+
|
|
108
|
+
## Notes
|
|
109
|
+
|
|
110
|
+
- Only `TOKENS_LIMIT` quotas are shown; `TIME_LIMIT` / MCP usage is ignored
|
|
111
|
+
- Missing auth returns `GLM | auth expired`; API failures return `GLM | quota unavailable`
|
|
112
|
+
- `install` does not replace an unmanaged status line unless `--force` is used
|
|
113
|
+
- `install --force` backs up the previous entry; `uninstall` restores it when possible
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
[MIT](./LICENSE)
|
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 配额余额与重置时间。数据来自官方接口,精准非估算。
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
@@ -12,50 +12,35 @@
|
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
14
|
<p align="center">
|
|
15
|
-
<a href="./README.
|
|
15
|
+
<a href="./README.en.md">English</a>
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
`glm-quota-line` 读取 GLM 配额接口、缓存结果,并输出一行适合 Claude Code `statusLine.command` 的状态文本。通过 `SessionStart` hook 预刷新缓存,新会话不会显示旧配额。
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
## 功能
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
- **一行命令安装** — `glm-quota-line install` 自动接入 Claude Code 状态栏和 `SessionStart` hook
|
|
23
|
+
- **精准配额显示** — 数据来自官方配额接口,非估算;同时展示 5 小时和周 token 配额
|
|
24
|
+
- **国内 + 国际端点** — 自动识别 `open.bigmodel.cn` 和 `api.z.ai`
|
|
25
|
+
- **零依赖** — 无运行时依赖,单一用途
|
|
26
|
+
- **智能缓存** — 按会话、TTL 和 token 用量刷新;支持代理/网关手动覆盖鉴权
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
## Why
|
|
27
|
-
|
|
28
|
-
- Built for a single purpose: show GLM quota clearly in Claude Code
|
|
29
|
-
- Small and predictable: no runtime dependencies, no unnecessary abstraction
|
|
30
|
-
- Practical for real usage: cache-aware, session-aware, and easy to install
|
|
31
|
-
|
|
32
|
-
## Features
|
|
33
|
-
|
|
34
|
-
- Built for Claude Code `statusLine.command`
|
|
35
|
-
- No runtime dependencies
|
|
36
|
-
- Optional `text`, `compact`, and `bar` layouts
|
|
37
|
-
- Optional ANSI color themes for dark and light terminals
|
|
38
|
-
- Manual `auth-token` and `base-url` overrides for proxy or gateway setups
|
|
39
|
-
- Automatic install and uninstall for Claude Code status line integration
|
|
40
|
-
- SessionStart pre-refresh so a new Claude session does not briefly show stale quota
|
|
41
|
-
|
|
42
|
-
## Installation
|
|
43
|
-
|
|
44
|
-
Recommended:
|
|
28
|
+
## 安装
|
|
45
29
|
|
|
46
30
|
```bash
|
|
47
31
|
npm install -g glm-quota-line
|
|
48
32
|
```
|
|
49
33
|
|
|
50
|
-
|
|
34
|
+
推荐全局安装,`install` 会将稳定的可执行路径写入 `statusLine.command` 和 `SessionStart` hooks。`npx` 适合一次性试跑。
|
|
35
|
+
|
|
36
|
+
升级:
|
|
51
37
|
|
|
52
38
|
```bash
|
|
53
|
-
npm install -g
|
|
39
|
+
npm install -g glm-quota-line
|
|
40
|
+
glm-quota-line check-update
|
|
54
41
|
```
|
|
55
42
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
## Quick Start
|
|
43
|
+
## 快速开始
|
|
59
44
|
|
|
60
45
|
```bash
|
|
61
46
|
glm-quota-line install
|
|
@@ -64,71 +49,44 @@ glm-quota-line config set theme ansi
|
|
|
64
49
|
glm-quota-line config set palette dark
|
|
65
50
|
```
|
|
66
51
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
glm-quota-line --style compact
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Output
|
|
52
|
+
## 输出示例
|
|
74
53
|
|
|
75
54
|
```text
|
|
76
|
-
GLM Lite | 5h
|
|
77
|
-
GLM 91% | 14:47
|
|
78
|
-
GLM Lite █░░░░░░░░░ 91% | 14:47
|
|
79
|
-
```
|
|
55
|
+
GLM Lite | 5h 91% | week 47% | reset 14:47
|
|
80
56
|
|
|
81
|
-
|
|
57
|
+
GLM 5h 91% W 47% | 14:47
|
|
82
58
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
| Dark terminal | `style=bar`, `theme=ansi`, `palette=dark` |
|
|
86
|
-
| Light terminal | `style=compact`, `theme=ansi`, `palette=mono` |
|
|
87
|
-
| Plain fallback | any `style`, `theme=plain` |
|
|
88
|
-
|
|
89
|
-
## Color Presets
|
|
90
|
-
|
|
91
|
-
`style` controls layout. `theme` enables or disables ANSI color. `palette` only applies when `theme=ansi`.
|
|
92
|
-
|
|
93
|
-
- Dark terminal: `theme=ansi` + `palette=dark`
|
|
94
|
-
- Light terminal: `theme=ansi` + `palette=mono`
|
|
95
|
-
- Maximum compatibility: `theme=plain`
|
|
59
|
+
GLM Lite █░░░░░░░░░ 91% | W 47% | 14:47
|
|
60
|
+
```
|
|
96
61
|
|
|
97
|
-
|
|
62
|
+
同时显示 5 小时配额(主要)和周配额(次要),并自动展示重置时间。
|
|
98
63
|
|
|
99
|
-
|
|
100
|
-
glm-quota-line config set style text
|
|
101
|
-
glm-quota-line config set theme ansi
|
|
102
|
-
glm-quota-line config set palette dark
|
|
64
|
+
## 样式与配色
|
|
103
65
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
66
|
+
| 使用场景 | 配置 |
|
|
67
|
+
| ---------- | --------------------------------------------- |
|
|
68
|
+
| 深色终端 | `style=bar`, `theme=ansi`, `palette=dark` |
|
|
69
|
+
| 浅色终端 | `style=compact`, `theme=ansi`, `palette=mono` |
|
|
70
|
+
| 纯文本回退 | 任意 `style`,`theme=plain` |
|
|
107
71
|
|
|
108
|
-
|
|
109
|
-
```
|
|
72
|
+
`style` 控制布局(`text` / `compact` / `bar`),`theme` 控制是否启用 ANSI 颜色,`palette` 仅在 `theme=ansi` 时生效。
|
|
110
73
|
|
|
111
|
-
|
|
74
|
+
设置 `NO_COLOR=1` 可强制关闭颜色。
|
|
112
75
|
|
|
113
|
-
##
|
|
76
|
+
## 自定义鉴权
|
|
114
77
|
|
|
115
|
-
|
|
78
|
+
如果 Claude Code 运行在代理或网关后面,注入的 `ANTHROPIC_AUTH_TOKEN` / `ANTHROPIC_BASE_URL` 不是实际值:
|
|
116
79
|
|
|
117
80
|
```bash
|
|
118
81
|
glm-quota-line config set auth-token <your-real-token>
|
|
119
82
|
glm-quota-line config set base-url https://open.bigmodel.cn/api/anthropic
|
|
83
|
+
# 或
|
|
84
|
+
glm-quota-line config set base-url https://api.z.ai/api/anthropic
|
|
120
85
|
```
|
|
121
86
|
|
|
122
|
-
|
|
87
|
+
清除:`glm-quota-line config unset auth-token` / `base-url`。手动保存的值优先于 Claude 注入的环境变量。
|
|
123
88
|
|
|
124
|
-
|
|
125
|
-
glm-quota-line config unset auth-token
|
|
126
|
-
glm-quota-line config unset base-url
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
Stored overrides take precedence over Claude-injected environment variables. `config show` redacts the token, but the local config file still stores the real value in plain text.
|
|
130
|
-
|
|
131
|
-
## Commands
|
|
89
|
+
## 命令
|
|
132
90
|
|
|
133
91
|
```bash
|
|
134
92
|
glm-quota-line [--style text|compact|bar] [--display left|used|both]
|
|
@@ -136,27 +94,24 @@ glm-quota-line [--style text|compact|bar] [--display left|used|both]
|
|
|
136
94
|
|
|
137
95
|
glm-quota-line install [--force]
|
|
138
96
|
glm-quota-line uninstall
|
|
97
|
+
glm-quota-line version
|
|
98
|
+
glm-quota-line check-update
|
|
139
99
|
glm-quota-line config show
|
|
140
|
-
glm-quota-line config set style
|
|
141
|
-
glm-quota-line config
|
|
142
|
-
glm-quota-line config set theme <plain|ansi>
|
|
143
|
-
glm-quota-line config set palette <dark|mono>
|
|
144
|
-
glm-quota-line config set auth-token <token>
|
|
145
|
-
glm-quota-line config set base-url <url>
|
|
146
|
-
glm-quota-line config unset <style|display|theme|palette|auth-token|base-url>
|
|
100
|
+
glm-quota-line config set <style|display|theme|palette|auth-token|base-url> <value>
|
|
101
|
+
glm-quota-line config unset <key>
|
|
147
102
|
```
|
|
148
103
|
|
|
149
|
-
|
|
104
|
+
完整说明运行 `glm-quota-line --help`。
|
|
105
|
+
|
|
106
|
+
`glm-quota-line --version` 可直接查看当前安装版本;`check-update` 只检查并提示升级命令,不会自动修改环境。
|
|
150
107
|
|
|
151
|
-
##
|
|
108
|
+
## 说明
|
|
152
109
|
|
|
153
|
-
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
- `install`
|
|
157
|
-
- `install` does not replace an unmanaged Claude status line unless `--force` is used
|
|
158
|
-
- `install --force` backs up the previous entry and `uninstall` restores it when possible
|
|
110
|
+
- 只展示 `TOKENS_LIMIT` 配额,忽略 `TIME_LIMIT` / MCP 使用量
|
|
111
|
+
- 鉴权缺失返回 `GLM | auth expired`;接口异常返回 `GLM | quota unavailable`
|
|
112
|
+
- `install` 默认不会覆盖非本工具管理的状态栏,除非使用 `--force`
|
|
113
|
+
- `install --force` 会备份旧配置,`uninstall` 会在可能时恢复
|
|
159
114
|
|
|
160
|
-
##
|
|
115
|
+
## 许可证
|
|
161
116
|
|
|
162
117
|
[MIT](./LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glm-quota-line",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Claude Code status line helper for GLM Coding Plan quota.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"files": [
|
|
28
28
|
"src",
|
|
29
29
|
"README.md",
|
|
30
|
-
"README.
|
|
30
|
+
"README.en.md",
|
|
31
31
|
"LICENSE",
|
|
32
32
|
"CHANGELOG.md"
|
|
33
33
|
],
|
package/src/cli/args.js
CHANGED
|
@@ -26,6 +26,11 @@ export function parseArgs(argv = process.argv.slice(2)) {
|
|
|
26
26
|
continue;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
if (arg === "--version" || arg === "-v") {
|
|
30
|
+
options.version = true;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
|
|
29
34
|
if (arg === "--force") {
|
|
30
35
|
options.force = true;
|
|
31
36
|
continue;
|
|
@@ -67,4 +72,3 @@ export function parseArgs(argv = process.argv.slice(2)) {
|
|
|
67
72
|
options.positionals = positionals;
|
|
68
73
|
return options;
|
|
69
74
|
}
|
|
70
|
-
|
package/src/cli/commands.js
CHANGED
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
} from "../claude/settings.js";
|
|
14
14
|
import { installClaudeStatusLine, uninstallClaudeStatusLine } from "../claude/install.js";
|
|
15
15
|
import { refreshQuotaOnSessionStart } from "../claude/sessionStart.js";
|
|
16
|
+
import { checkForUpdates } from "./update.js";
|
|
17
|
+
import { getPackageVersion } from "../shared/packageInfo.js";
|
|
16
18
|
|
|
17
19
|
function isNonEmptyString(value) {
|
|
18
20
|
return typeof value === "string" && value.trim().length > 0;
|
|
@@ -66,9 +68,36 @@ const CONFIG_KEYS = {
|
|
|
66
68
|
}
|
|
67
69
|
};
|
|
68
70
|
|
|
69
|
-
export async function handleCommand(args, output = process.stdout) {
|
|
71
|
+
export async function handleCommand(args, output = process.stdout, dependencies = {}) {
|
|
72
|
+
const getVersion = dependencies.getVersion || getPackageVersion;
|
|
73
|
+
const runUpdateCheck = dependencies.runUpdateCheck || checkForUpdates;
|
|
70
74
|
const [command, subcommand, key, value] = args.positionals;
|
|
71
75
|
|
|
76
|
+
if (command === "version") {
|
|
77
|
+
output.write(`glm-quota-line ${await getVersion()}\n`);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (command === "check-update") {
|
|
82
|
+
const result = await runUpdateCheck();
|
|
83
|
+
output.write(`glm-quota-line ${result.currentVersion}\n`);
|
|
84
|
+
|
|
85
|
+
if (result.status === "up-to-date") {
|
|
86
|
+
output.write(`latest: ${result.latestVersion}\nstatus: up to date\n`);
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (result.status === "update-available") {
|
|
91
|
+
output.write(
|
|
92
|
+
`latest: ${result.latestVersion}\nstatus: update available\nupgrade: ${result.upgradeCommand}\n`
|
|
93
|
+
);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
output.write(`status: unable to check updates\nreason: ${result.errorMessage}\n`);
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
72
101
|
if (command === "install") {
|
|
73
102
|
const result = await installClaudeStatusLine(undefined, undefined, undefined, {
|
|
74
103
|
force: Boolean(args.force)
|
package/src/cli/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { formatStatus } from "../core/status/format.js";
|
|
|
7
7
|
import { readStatusLineInput } from "../claude/input.js";
|
|
8
8
|
import { readToolConfig } from "../claude/settings.js";
|
|
9
9
|
import { resolveQuotaStatus } from "../core/quota/service.js";
|
|
10
|
+
import { getPackageVersion } from "../shared/packageInfo.js";
|
|
10
11
|
import {
|
|
11
12
|
isValidDisplayMode,
|
|
12
13
|
isValidPalette,
|
|
@@ -20,8 +21,11 @@ function printHelp() {
|
|
|
20
21
|
Usage:
|
|
21
22
|
glm-quota-line [--style text|compact|bar] [--display left|used|both]
|
|
22
23
|
[--theme plain|ansi] [--palette dark|mono]
|
|
24
|
+
glm-quota-line --version
|
|
23
25
|
glm-quota-line install [--force]
|
|
24
26
|
glm-quota-line uninstall
|
|
27
|
+
glm-quota-line version
|
|
28
|
+
glm-quota-line check-update
|
|
25
29
|
glm-quota-line config set style <text|compact|bar>
|
|
26
30
|
glm-quota-line config set display <left|used|both>
|
|
27
31
|
glm-quota-line config set theme <plain|ansi>
|
|
@@ -35,6 +39,8 @@ Commands:
|
|
|
35
39
|
install Install glm-quota-line into Claude Code statusLine.command and SessionStart hooks.
|
|
36
40
|
install --force Replace an existing unmanaged status line and back it up.
|
|
37
41
|
uninstall Remove the managed status line and SessionStart hooks, and restore a backup if one exists.
|
|
42
|
+
version Print the installed glm-quota-line version.
|
|
43
|
+
check-update Check npm for a newer version and print the upgrade command.
|
|
38
44
|
config show Print the current persisted config. Stored tokens are redacted.
|
|
39
45
|
config set ... Persist a display option or manual credential override.
|
|
40
46
|
config unset ... Remove one persisted config key.
|
|
@@ -45,11 +51,14 @@ Options:
|
|
|
45
51
|
--theme Color mode: plain or ansi.
|
|
46
52
|
--palette ANSI palette: dark for dark terminals, mono for light terminals.
|
|
47
53
|
--force Allow install to replace an unmanaged Claude status line.
|
|
54
|
+
-v, --version Show the installed version.
|
|
48
55
|
-h, --help Show this help text.
|
|
49
56
|
|
|
50
57
|
Examples:
|
|
51
58
|
glm-quota-line
|
|
59
|
+
glm-quota-line --version
|
|
52
60
|
glm-quota-line --style bar --theme ansi --palette dark
|
|
61
|
+
glm-quota-line check-update
|
|
53
62
|
glm-quota-line config set style compact
|
|
54
63
|
glm-quota-line config set theme ansi
|
|
55
64
|
glm-quota-line config set auth-token <your-real-token>
|
|
@@ -101,6 +110,11 @@ export async function main() {
|
|
|
101
110
|
return;
|
|
102
111
|
}
|
|
103
112
|
|
|
113
|
+
if (args.version) {
|
|
114
|
+
process.stdout.write(`glm-quota-line ${await getPackageVersion()}\n`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
104
118
|
if (await handleCommand(args)) {
|
|
105
119
|
return;
|
|
106
120
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { readPackageInfo } from "../shared/packageInfo.js";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
4
|
+
const DEFAULT_REGISTRY_URL = "https://registry.npmjs.org";
|
|
5
|
+
|
|
6
|
+
function parseVersion(version) {
|
|
7
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(version);
|
|
8
|
+
if (!match) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
major: Number.parseInt(match[1], 10),
|
|
14
|
+
minor: Number.parseInt(match[2], 10),
|
|
15
|
+
patch: Number.parseInt(match[3], 10),
|
|
16
|
+
prerelease: match[4] || ""
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function compareVersions(left, right) {
|
|
21
|
+
const parsedLeft = parseVersion(left);
|
|
22
|
+
const parsedRight = parseVersion(right);
|
|
23
|
+
|
|
24
|
+
if (!parsedLeft || !parsedRight) {
|
|
25
|
+
return left === right ? 0 : left.localeCompare(right);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
for (const key of ["major", "minor", "patch"]) {
|
|
29
|
+
if (parsedLeft[key] !== parsedRight[key]) {
|
|
30
|
+
return parsedLeft[key] - parsedRight[key];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (parsedLeft.prerelease === parsedRight.prerelease) {
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!parsedLeft.prerelease) {
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!parsedRight.prerelease) {
|
|
43
|
+
return -1;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return parsedLeft.prerelease.localeCompare(parsedRight.prerelease);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeRegistryUrl(value) {
|
|
50
|
+
return value.replace(/\/+$/u, "");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function toErrorMessage(error) {
|
|
54
|
+
if (error?.name === "AbortError") {
|
|
55
|
+
return "npm registry request timed out";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (error?.name === "TypeError" && error?.message === "fetch failed") {
|
|
59
|
+
return "npm registry request failed";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof error?.message === "string" && error.message.trim()) {
|
|
63
|
+
return error.message.trim();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return "npm registry request failed";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function checkForUpdates(options = {}) {
|
|
70
|
+
const fetchImpl = options.fetchImpl || globalThis.fetch;
|
|
71
|
+
if (typeof fetchImpl !== "function") {
|
|
72
|
+
throw new Error("fetch is not available in this Node.js runtime");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const packageInfo = options.packageInfo || (await readPackageInfo());
|
|
76
|
+
const packageName = packageInfo.name || "glm-quota-line";
|
|
77
|
+
const currentVersion = packageInfo.version || "0.0.0";
|
|
78
|
+
const registryUrl = normalizeRegistryUrl(options.registryUrl || DEFAULT_REGISTRY_URL);
|
|
79
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
80
|
+
|
|
81
|
+
const controller = new AbortController();
|
|
82
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetchImpl(`${registryUrl}/${encodeURIComponent(packageName)}/latest`, {
|
|
86
|
+
headers: {
|
|
87
|
+
accept: "application/json"
|
|
88
|
+
},
|
|
89
|
+
signal: controller.signal
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
throw new Error(`npm registry returned HTTP ${response.status}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const payload = await response.json();
|
|
97
|
+
const latestVersion = typeof payload?.version === "string" ? payload.version.trim() : "";
|
|
98
|
+
if (!latestVersion) {
|
|
99
|
+
throw new Error("npm registry response did not include a version");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
packageName,
|
|
104
|
+
currentVersion,
|
|
105
|
+
latestVersion,
|
|
106
|
+
status: compareVersions(currentVersion, latestVersion) < 0 ? "update-available" : "up-to-date",
|
|
107
|
+
upgradeCommand: `npm install -g ${packageName}`
|
|
108
|
+
};
|
|
109
|
+
} catch (error) {
|
|
110
|
+
return {
|
|
111
|
+
packageName,
|
|
112
|
+
currentVersion,
|
|
113
|
+
latestVersion: null,
|
|
114
|
+
status: "error",
|
|
115
|
+
errorMessage: toErrorMessage(error)
|
|
116
|
+
};
|
|
117
|
+
} finally {
|
|
118
|
+
clearTimeout(timeoutId);
|
|
119
|
+
}
|
|
120
|
+
}
|
package/src/core/quota/cache.js
CHANGED
|
@@ -1,24 +1,72 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
-
function
|
|
4
|
+
function isValidQuotaShape(value) {
|
|
5
|
+
if (!value || typeof value.key !== "string") {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
if (!Number.isFinite(value.leftPercent)) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!Number.isFinite(value.usedPercent)) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if ("nextResetTime" in value && !Number.isFinite(value.nextResetTime)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isLegacyPercentSuccessCacheShape(value) {
|
|
25
|
+
if (!value || value.kind !== "success" || typeof value.level !== "string") {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (value.display !== "percent") {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!Number.isFinite(value.leftPercent)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if ("usedPercent" in value && !Number.isFinite(value.usedPercent)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if ("nextResetTime" in value && !Number.isFinite(value.nextResetTime)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isMultiQuotaSuccessCacheShape(value) {
|
|
5
49
|
if (!value || value.kind !== "success" || typeof value.level !== "string") {
|
|
6
50
|
return false;
|
|
7
51
|
}
|
|
8
52
|
|
|
9
|
-
if (!
|
|
53
|
+
if (!Array.isArray(value.quotas) || value.quotas.length === 0) {
|
|
10
54
|
return false;
|
|
11
55
|
}
|
|
12
56
|
|
|
13
|
-
if (value.
|
|
14
|
-
return
|
|
57
|
+
if (!value.quotas.every(isValidQuotaShape)) {
|
|
58
|
+
return false;
|
|
15
59
|
}
|
|
16
60
|
|
|
17
|
-
if (value.
|
|
18
|
-
return
|
|
61
|
+
if ("primaryQuotaKey" in value && typeof value.primaryQuotaKey !== "string") {
|
|
62
|
+
return false;
|
|
19
63
|
}
|
|
20
64
|
|
|
21
|
-
return
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isSuccessCacheShape(value) {
|
|
69
|
+
return isMultiQuotaSuccessCacheShape(value) || isLegacyPercentSuccessCacheShape(value);
|
|
22
70
|
}
|
|
23
71
|
|
|
24
72
|
function asFiniteNumber(value) {
|