stepfun-status 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +190 -174
- package/cli/api.js +408 -345
- package/package.json +1 -1
- package/ARCHITECTURE.md +0 -175
- package/stepfun-status-1.0.1.tgz +0 -0
package/README.md
CHANGED
|
@@ -1,174 +1,190 @@
|
|
|
1
|
-
# stepfun-status
|
|
2
|
-
|
|
3
|
-
> StepFun Token-Plan 使用状态监控工具,支持 Claude Code 状态栏常驻显示。
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/stepfun-status)
|
|
6
|
-
[](https://www.npmjs.com/package/stepfun-status)
|
|
6
|
+
[](https://www.npmjs.com/package/stepfun-status?activeTab=downloads)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
## 效果预览
|
|
11
|
+
|
|
12
|
+
Claude Code 底部状态栏:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
my-app │ main │ step-3.5-flash │ ██████░░░░ 60% (40/100) │ ⏱ 1h30m │ W ███░░ 58% │ 到期 12天
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
终端详细视图:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
┌──────────────────────────────────────────────┐
|
|
22
|
+
│ StepFun Claude Code 使用状态 │
|
|
23
|
+
│ │
|
|
24
|
+
│ 当前模型: step-3.5-flash │
|
|
25
|
+
│ 套餐: Mini │
|
|
26
|
+
│ │
|
|
27
|
+
│ 5小时用量: ██████████████████░░░░░░░░░░ 60% │
|
|
28
|
+
│ 剩余: 40% │
|
|
29
|
+
│ 重置: 1 小时 30 分钟后重置 │
|
|
30
|
+
│ │
|
|
31
|
+
│ 每周用量: ████████░░░░░░░ 58% │
|
|
32
|
+
│ 剩余: 42% │
|
|
33
|
+
│ 重置: 3 小时 12 分钟后重置 │
|
|
34
|
+
│ │
|
|
35
|
+
│ 套餐到期: 2025/6/1 00:00:00 (还剩 12 天) │
|
|
36
|
+
│ │
|
|
37
|
+
│ 状态: ⚡ 注意使用 │
|
|
38
|
+
└──────────────────────────────────────────────┘
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 安装
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install -g stepfun-status
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 快速开始
|
|
48
|
+
|
|
49
|
+
### 1. 获取 Cookie 并认证
|
|
50
|
+
|
|
51
|
+
运行下方命令,会自动打印获取步骤:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
stepfun auth
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
按提示操作:
|
|
58
|
+
1. 登录 [https://platform.stepfun.com/plan-subscribe](https://platform.stepfun.com/plan-subscribe)
|
|
59
|
+
2. F12 → Network 标签 → 刷新页面
|
|
60
|
+
3. 点击任意 API 请求(如 `GetStepPlanStatus`)
|
|
61
|
+
4. 滚动到 Request Headers → 找到 `Cookie` 行
|
|
62
|
+
5. **右键 → Copy value**(必须用此方式,直接框选会截断)
|
|
63
|
+
6. 粘贴到终端并回车
|
|
64
|
+
|
|
65
|
+
> **提示**:Cookie 较长,建议使用交互式 `stepfun auth`(无参数),避免命令行参数截断问题。
|
|
66
|
+
|
|
67
|
+
### 2. 验证连接
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
stepfun health
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 3. 查看状态
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
stepfun status
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Claude Code 状态栏集成
|
|
80
|
+
|
|
81
|
+
### 配置
|
|
82
|
+
|
|
83
|
+
编辑 `~/.claude/settings.json`,添加:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"statusLine": {
|
|
88
|
+
"type": "command",
|
|
89
|
+
"command": "stepfun statusline"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
重启 Claude Code 后生效。
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
### 状态栏格式
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
目录 │ 分支 │ 模型 │ 5小时用量进度条(剩余) │ ⏱ 倒计时 │ W 周用量 │ 到期天数
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**颜色说明**:
|
|
104
|
+
|
|
105
|
+
| 指标 | 绿色 | 黄色 | 红色 |
|
|
106
|
+
|------|------|------|------|
|
|
107
|
+
| 用量 | < 60% | 60–85% | ≥ 85% |
|
|
108
|
+
| 到期 | > 7 天 | ≤ 7 天 | ≤ 3 天 |
|
|
109
|
+
| 分支 | main/master | — | 有未提交改动(`*`) |
|
|
110
|
+
|
|
111
|
+
## 命令参考
|
|
112
|
+
|
|
113
|
+
| 命令 | 选项 | 说明 |
|
|
114
|
+
|------|------|------|
|
|
115
|
+
| `stepfun auth [cookie]` | — | 设置认证 Cookie(推荐无参数交互式输入) |
|
|
116
|
+
| `stepfun health` | — | 检查配置文件、Cookie 和 API 连通性 |
|
|
117
|
+
| `stepfun status` | — | 详细模式显示当前使用状态 |
|
|
118
|
+
| `stepfun status` | `-c, --compact` | 紧凑单行模式 |
|
|
119
|
+
| `stepfun status` | `-w, --watch` | 实时监控,每 10 秒刷新 |
|
|
120
|
+
| `stepfun status` | `-c -w` | 紧凑模式 + 实时监控 |
|
|
121
|
+
| `stepfun statusline` | — | 输出状态行(供 Claude Code 调用) |
|
|
122
|
+
| `stepfun statusline` | `-w, --watch` | 持续刷新状态行输出 |
|
|
123
|
+
|
|
124
|
+
## 卸载 / 更新
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# 一键更新到最新版
|
|
128
|
+
npm update -g stepfun-status
|
|
129
|
+
|
|
130
|
+
# 完全卸载
|
|
131
|
+
npm uninstall -g stepfun-status
|
|
132
|
+
# 可选:同时删除配置文件
|
|
133
|
+
# macOS / Linux
|
|
134
|
+
rm -f ~/.stepfun-config.json
|
|
135
|
+
# Windows (PowerShell)
|
|
136
|
+
Remove-Item ~\.stepfun-config.json -ErrorAction SilentlyContinue
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## 配置文件
|
|
140
|
+
|
|
141
|
+
| 项目 | 说明 |
|
|
142
|
+
|------|------|
|
|
143
|
+
| 路径 | `~/.stepfun-config.json` |
|
|
144
|
+
| 内容 | `{ "cookie": "..." }` |
|
|
145
|
+
| 权限 | Unix/Mac 下自动设置为 `0600`(仅所有者可读写) |
|
|
146
|
+
|
|
147
|
+
> **Windows 注意**:不支持 Unix 文件权限位,Cookie 以明文存储,请妥善保管配置文件,避免他人访问。
|
|
148
|
+
|
|
149
|
+
> **macOS / Linux**:配置文件权限已自动设为 `0600`,只有当前用户可读写,无需额外操作。
|
|
150
|
+
|
|
151
|
+
## 常见问题
|
|
152
|
+
|
|
153
|
+
**Q: 状态栏显示 ❌**
|
|
154
|
+
A: Cookie 未配置或已过期,运行 `stepfun health` 诊断,再用 `stepfun auth` 重新设置。
|
|
155
|
+
|
|
156
|
+
**Q: Cookie 粘贴后提示"不能为空"**
|
|
157
|
+
A: 可能复制到了空内容,请确保在 Network 面板使用"右键 → Copy value"而非框选复制。
|
|
158
|
+
|
|
159
|
+
**Q: 状态栏不显示**
|
|
160
|
+
A: 确认 `~/.claude/settings.json` 中 `statusLine.command` 为 `stepfun statusline`,且 `stepfun-status` 已全局安装(`npm install -g stepfun-status`)。
|
|
161
|
+
|
|
162
|
+
**Q: macOS / Linux 上 `stepfun` 命令找不到**
|
|
163
|
+
A: 确认 npm 全局 bin 目录已加入 `PATH`。可运行 `npm bin -g` 查看路径,并将其添加到 `~/.zshrc` 或 `~/.bashrc`:
|
|
164
|
+
```bash
|
|
165
|
+
export PATH="$(npm bin -g):$PATH"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 开发
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
git clone https://github.com/Daiyimo/stepfun-status.git
|
|
172
|
+
cd stepfun-status
|
|
173
|
+
npm install
|
|
174
|
+
npm link # 本地全局链接,方便调试
|
|
175
|
+
|
|
176
|
+
npm start # 查看当前状态
|
|
177
|
+
npm test # 检查连通性
|
|
178
|
+
npm run lint # 语法检查
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## 许可证
|
|
182
|
+
|
|
183
|
+
[MIT](LICENSE)
|
|
184
|
+
|
|
185
|
+
## 相关链接
|
|
186
|
+
|
|
187
|
+
- [StepFun 开放平台](https://platform.stepfun.com)
|
|
188
|
+
- [StepFun 接口密钥](https://platform.stepfun.com/interface-key)
|
|
189
|
+
- [StepFun 订阅情况](https://platform.stepfun.com/plan-subscribe)
|
|
190
|
+
- [StepFun 接入指南](https://platform.stepfun.com/docs/zh/step-plan/integrations)
|
package/cli/api.js
CHANGED
|
@@ -1,345 +1,408 @@
|
|
|
1
|
-
const https = require("https");
|
|
2
|
-
const fs = require("fs");
|
|
3
|
-
const path = require("path");
|
|
4
|
-
const os = require("os");
|
|
5
|
-
|
|
6
|
-
// ─── 常量 ─────────────────────────────────────────────────────────────────────
|
|
7
|
-
|
|
8
|
-
const STEPFUN_HOST = "platform.stepfun.com";
|
|
9
|
-
const STEPFUN_ORIGIN = `https://${STEPFUN_HOST}`;
|
|
10
|
-
|
|
11
|
-
const CONFIG = {
|
|
12
|
-
CACHE_TIMEOUT: 30000, // 缓存超时:30 秒
|
|
13
|
-
REQUEST_TIMEOUT: 8000, // 网络请求超时:8 秒
|
|
14
|
-
CONFIG_FILENAME: ".stepfun-config.json",
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// ─── 跨平台 User-Agent ────────────────────────────────────────────────────────
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* 根据当前操作系统返回对应的浏览器 User-Agent 字符串,
|
|
21
|
-
* 避免 macOS/Linux 下使用 Windows UA 被服务端检测。
|
|
22
|
-
*/
|
|
23
|
-
function getPlatformUserAgent() {
|
|
24
|
-
const platform = os.platform();
|
|
25
|
-
if (platform === "darwin") {
|
|
26
|
-
return "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
|
|
27
|
-
}
|
|
28
|
-
if (platform === "linux") {
|
|
29
|
-
return "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
|
|
30
|
-
}
|
|
31
|
-
// Windows(默认)
|
|
32
|
-
return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// 配置文件路径(模块级常量,供外部共享,避免各处重复计算)
|
|
36
|
-
const CONFIG_PATH = path.join(
|
|
37
|
-
process.env.HOME || process.env.USERPROFILE,
|
|
38
|
-
CONFIG.CONFIG_FILENAME
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
// ─── 模块级纯函数(提升出 parseUsageData,避免每次调用重新创建) ──────────────
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 将 Unix 时间戳转换为剩余时间对象
|
|
45
|
-
* @param {string|number|null} timestamp Unix 秒级时间戳
|
|
46
|
-
* @returns {{ ts: number, date: string, hoursUntil: number, minutesUntil: number } | null}
|
|
47
|
-
*/
|
|
48
|
-
function formatResetTime(timestamp) {
|
|
49
|
-
if (!timestamp) return null;
|
|
50
|
-
const ts = parseInt(timestamp, 10);
|
|
51
|
-
const date = new Date(ts * 1000);
|
|
52
|
-
const diffMs = date.getTime() - Date.now();
|
|
53
|
-
return {
|
|
54
|
-
ts,
|
|
55
|
-
date: date.toLocaleString(),
|
|
56
|
-
hoursUntil: Math.max(0, Math.floor(diffMs / (1000 * 60 * 60))),
|
|
57
|
-
minutesUntil: Math.max(0, Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60))),
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* 将剩余时间对象格式化为可读文本
|
|
63
|
-
* @param {{ hoursUntil: number, minutesUntil: number } | null} resetInfo
|
|
64
|
-
* @returns {string}
|
|
65
|
-
*/
|
|
66
|
-
function calcRemainingText(resetInfo) {
|
|
67
|
-
if (!resetInfo) return "未知";
|
|
68
|
-
const { hoursUntil, minutesUntil } = resetInfo;
|
|
69
|
-
return hoursUntil > 0
|
|
70
|
-
? `${hoursUntil} 小时 ${minutesUntil} 分钟后重置`
|
|
71
|
-
: `${minutesUntil} 分钟后重置`;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// ───
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
1
|
+
const https = require("https");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const os = require("os");
|
|
5
|
+
|
|
6
|
+
// ─── 常量 ─────────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
const STEPFUN_HOST = "platform.stepfun.com";
|
|
9
|
+
const STEPFUN_ORIGIN = `https://${STEPFUN_HOST}`;
|
|
10
|
+
|
|
11
|
+
const CONFIG = {
|
|
12
|
+
CACHE_TIMEOUT: 30000, // 缓存超时:30 秒
|
|
13
|
+
REQUEST_TIMEOUT: 8000, // 网络请求超时:8 秒
|
|
14
|
+
CONFIG_FILENAME: ".stepfun-config.json",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// ─── 跨平台 User-Agent ────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 根据当前操作系统返回对应的浏览器 User-Agent 字符串,
|
|
21
|
+
* 避免 macOS/Linux 下使用 Windows UA 被服务端检测。
|
|
22
|
+
*/
|
|
23
|
+
function getPlatformUserAgent() {
|
|
24
|
+
const platform = os.platform();
|
|
25
|
+
if (platform === "darwin") {
|
|
26
|
+
return "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
|
|
27
|
+
}
|
|
28
|
+
if (platform === "linux") {
|
|
29
|
+
return "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
|
|
30
|
+
}
|
|
31
|
+
// Windows(默认)
|
|
32
|
+
return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 配置文件路径(模块级常量,供外部共享,避免各处重复计算)
|
|
36
|
+
const CONFIG_PATH = path.join(
|
|
37
|
+
process.env.HOME || process.env.USERPROFILE,
|
|
38
|
+
CONFIG.CONFIG_FILENAME
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// ─── 模块级纯函数(提升出 parseUsageData,避免每次调用重新创建) ──────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 将 Unix 时间戳转换为剩余时间对象
|
|
45
|
+
* @param {string|number|null} timestamp Unix 秒级时间戳
|
|
46
|
+
* @returns {{ ts: number, date: string, hoursUntil: number, minutesUntil: number } | null}
|
|
47
|
+
*/
|
|
48
|
+
function formatResetTime(timestamp) {
|
|
49
|
+
if (!timestamp) return null;
|
|
50
|
+
const ts = parseInt(timestamp, 10);
|
|
51
|
+
const date = new Date(ts * 1000);
|
|
52
|
+
const diffMs = date.getTime() - Date.now();
|
|
53
|
+
return {
|
|
54
|
+
ts,
|
|
55
|
+
date: date.toLocaleString(),
|
|
56
|
+
hoursUntil: Math.max(0, Math.floor(diffMs / (1000 * 60 * 60))),
|
|
57
|
+
minutesUntil: Math.max(0, Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60))),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 将剩余时间对象格式化为可读文本
|
|
63
|
+
* @param {{ hoursUntil: number, minutesUntil: number } | null} resetInfo
|
|
64
|
+
* @returns {string}
|
|
65
|
+
*/
|
|
66
|
+
function calcRemainingText(resetInfo) {
|
|
67
|
+
if (!resetInfo) return "未知";
|
|
68
|
+
const { hoursUntil, minutesUntil } = resetInfo;
|
|
69
|
+
return hoursUntil > 0
|
|
70
|
+
? `${hoursUntil} 小时 ${minutesUntil} 分钟后重置`
|
|
71
|
+
: `${minutesUntil} 分钟后重置`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ─── Cookie 合并工具函数 ──────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 将 Set-Cookie 响应头合并到现有 Cookie 字符串中(模拟浏览器行为)。
|
|
78
|
+
* 新值覆盖同名旧值,从而自动延长会话有效期。
|
|
79
|
+
*
|
|
80
|
+
* @param {string} existingCookie 当前 Cookie 字符串("k1=v1; k2=v2" 格式)
|
|
81
|
+
* @param {string[]} setCookieHeaders 响应头中的 Set-Cookie 数组
|
|
82
|
+
* @returns {string} 合并后的 Cookie 字符串
|
|
83
|
+
*/
|
|
84
|
+
function mergeCookies(existingCookie, setCookieHeaders) {
|
|
85
|
+
// 1. 解析现有 cookie 为 Map(保持插入顺序)
|
|
86
|
+
const cookieMap = new Map();
|
|
87
|
+
if (existingCookie) {
|
|
88
|
+
existingCookie.split(/;\s*/).forEach((c) => {
|
|
89
|
+
const idx = c.indexOf("=");
|
|
90
|
+
if (idx === -1) return;
|
|
91
|
+
const key = c.slice(0, idx).trim();
|
|
92
|
+
if (key) cookieMap.set(key, c.slice(idx + 1));
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 2. 从 Set-Cookie 头中提取 name=value(忽略 Path/Domain/Expires 等属性)
|
|
97
|
+
let updated = false;
|
|
98
|
+
for (const header of setCookieHeaders) {
|
|
99
|
+
// Set-Cookie 格式: "name=value; Path=/; Domain=...; Expires=..."
|
|
100
|
+
const firstPart = header.split(";")[0].trim();
|
|
101
|
+
const idx = firstPart.indexOf("=");
|
|
102
|
+
if (idx === -1) continue;
|
|
103
|
+
const key = firstPart.slice(0, idx).trim();
|
|
104
|
+
const val = firstPart.slice(idx + 1);
|
|
105
|
+
if (!key) continue;
|
|
106
|
+
const oldVal = cookieMap.get(key);
|
|
107
|
+
if (oldVal !== val) {
|
|
108
|
+
cookieMap.set(key, val);
|
|
109
|
+
updated = true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!updated) return existingCookie;
|
|
114
|
+
|
|
115
|
+
// 3. 重新拼接
|
|
116
|
+
return Array.from(cookieMap.entries())
|
|
117
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
118
|
+
.join("; ");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ─── StepFunAPI 类 ────────────────────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
class StepFunAPI {
|
|
124
|
+
constructor() {
|
|
125
|
+
this.cookie = null;
|
|
126
|
+
this.cache = {
|
|
127
|
+
statusData: null,
|
|
128
|
+
quotaData: null,
|
|
129
|
+
statusTimestamp: 0,
|
|
130
|
+
quotaTimestamp: 0,
|
|
131
|
+
};
|
|
132
|
+
this.loadConfig();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** 是否已配置凭据 */
|
|
136
|
+
isAuthenticated() {
|
|
137
|
+
return Boolean(this.cookie);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
loadConfig() {
|
|
141
|
+
try {
|
|
142
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
143
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
|
|
144
|
+
this.cookie = config.cookie || null;
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error("Failed to load config:", error.message);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
saveConfig() {
|
|
152
|
+
try {
|
|
153
|
+
const config = { cookie: this.cookie };
|
|
154
|
+
// 0o600:仅所有者可读写,在 Unix/Mac 上防止其他用户读取 Cookie
|
|
155
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.error("Failed to save config:", error.message);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
setCredentials(cookie) {
|
|
162
|
+
if (!cookie || typeof cookie !== "string" || !cookie.trim()) {
|
|
163
|
+
throw new Error("Cookie 不能为空");
|
|
164
|
+
}
|
|
165
|
+
this.cookie = cookie.trim();
|
|
166
|
+
this.saveConfig();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** 从 Cookie 字符串中提取指定字段 */
|
|
170
|
+
extractFromCookie() {
|
|
171
|
+
if (!this.cookie) return { oasisWebid: null };
|
|
172
|
+
|
|
173
|
+
const cookies = {};
|
|
174
|
+
this.cookie.split(/;\s*/).forEach((c) => {
|
|
175
|
+
const idx = c.indexOf("=");
|
|
176
|
+
if (idx === -1) return;
|
|
177
|
+
const key = c.slice(0, idx).trim();
|
|
178
|
+
const val = c.slice(idx + 1);
|
|
179
|
+
if (!key) return;
|
|
180
|
+
try {
|
|
181
|
+
cookies[key] = decodeURIComponent(val);
|
|
182
|
+
} catch {
|
|
183
|
+
cookies[key] = val;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return { oasisWebid: cookies["Oasis-Webid"] || null };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async makePlatformRequest(endpoint, data = {}) {
|
|
191
|
+
if (!this.cookie) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
'Missing cookie. Please run "stepfun auth <cookie>" first.\n' +
|
|
194
|
+
"获取方式:\n" +
|
|
195
|
+
"1. 登录 https://platform.stepfun.com/plan-subscribe\n" +
|
|
196
|
+
"2. F12 -> Network -> 刷新页面\n" +
|
|
197
|
+
"3. 点击任意 API 请求 -> Copy -> Copy request headers -> 复制 Cookie 行"
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const { oasisWebid } = this.extractFromCookie();
|
|
202
|
+
const postData = JSON.stringify(data);
|
|
203
|
+
|
|
204
|
+
const headers = {
|
|
205
|
+
accept: "*/*",
|
|
206
|
+
"content-type": "application/json",
|
|
207
|
+
"oasis-appid": "10300",
|
|
208
|
+
"oasis-platform": "web",
|
|
209
|
+
"oasis-webid": oasisWebid || "",
|
|
210
|
+
origin: STEPFUN_ORIGIN,
|
|
211
|
+
referer: `${STEPFUN_ORIGIN}/plan-subscribe`,
|
|
212
|
+
"user-agent": getPlatformUserAgent(),
|
|
213
|
+
"content-length": Buffer.byteLength(postData),
|
|
214
|
+
Cookie: this.cookie,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
return new Promise((resolve, reject) => {
|
|
218
|
+
const req = https.request(
|
|
219
|
+
{
|
|
220
|
+
hostname: STEPFUN_HOST, // 与 STEPFUN_ORIGIN 保持同一数据源
|
|
221
|
+
port: 443,
|
|
222
|
+
path: endpoint,
|
|
223
|
+
method: "POST",
|
|
224
|
+
headers,
|
|
225
|
+
rejectUnauthorized: true, // 显式启用 SSL 证书验证
|
|
226
|
+
},
|
|
227
|
+
(res) => {
|
|
228
|
+
let body = "";
|
|
229
|
+
res.on("data", (chunk) => { body += chunk; });
|
|
230
|
+
res.on("end", () => {
|
|
231
|
+
// 自动捕获 Set-Cookie 响应头,合并更新本地 Cookie(模拟浏览器保活)
|
|
232
|
+
const setCookieHeaders = res.headers["set-cookie"];
|
|
233
|
+
if (setCookieHeaders && setCookieHeaders.length > 0 && this.cookie) {
|
|
234
|
+
const merged = mergeCookies(this.cookie, setCookieHeaders);
|
|
235
|
+
if (merged !== this.cookie) {
|
|
236
|
+
this.cookie = merged;
|
|
237
|
+
this.saveConfig();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (res.statusCode === 200) {
|
|
242
|
+
try {
|
|
243
|
+
resolve(JSON.parse(body));
|
|
244
|
+
} catch {
|
|
245
|
+
reject(new Error("Failed to parse response: invalid JSON"));
|
|
246
|
+
}
|
|
247
|
+
} else if (res.statusCode === 401) {
|
|
248
|
+
this.clearCache();
|
|
249
|
+
reject(
|
|
250
|
+
new Error(
|
|
251
|
+
"Cookie 已失效或过期,请重新运行 auth 命令\n" +
|
|
252
|
+
"获取方式: 登录 https://platform.stepfun.com/plan-subscribe → F12 → Network → 复制 Cookie"
|
|
253
|
+
)
|
|
254
|
+
);
|
|
255
|
+
} else {
|
|
256
|
+
reject(new Error(`API error: HTTP ${res.statusCode}`));
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
// 超时保护
|
|
263
|
+
req.setTimeout(CONFIG.REQUEST_TIMEOUT, () => {
|
|
264
|
+
req.destroy();
|
|
265
|
+
reject(new Error(`Request timeout after ${CONFIG.REQUEST_TIMEOUT / 1000}s`));
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
req.on("error", (e) => {
|
|
269
|
+
reject(new Error(`Network error: ${e.message}`));
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
req.write(postData);
|
|
273
|
+
req.end();
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* 带缓存的请求封装,消除 getSubscriptionStatus / getQuotaInfo 的重复逻辑
|
|
279
|
+
* @param {'statusData'|'quotaData'} dataKey 缓存数据字段名
|
|
280
|
+
* @param {'statusTimestamp'|'quotaTimestamp'} tsKey 缓存时间戳字段名
|
|
281
|
+
* @param {string} endpoint API 路径
|
|
282
|
+
* @param {boolean} forceRefresh 是否强制跳过缓存
|
|
283
|
+
*/
|
|
284
|
+
async _cachedRequest(dataKey, tsKey, endpoint, forceRefresh) {
|
|
285
|
+
const now = Date.now();
|
|
286
|
+
if (
|
|
287
|
+
!forceRefresh &&
|
|
288
|
+
this.cache[dataKey] &&
|
|
289
|
+
now - this.cache[tsKey] < CONFIG.CACHE_TIMEOUT
|
|
290
|
+
) {
|
|
291
|
+
return this.cache[dataKey];
|
|
292
|
+
}
|
|
293
|
+
const data = await this.makePlatformRequest(endpoint);
|
|
294
|
+
this.cache[dataKey] = data;
|
|
295
|
+
this.cache[tsKey] = now;
|
|
296
|
+
return data;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async getSubscriptionStatus(forceRefresh = false) {
|
|
300
|
+
return this._cachedRequest(
|
|
301
|
+
"statusData",
|
|
302
|
+
"statusTimestamp",
|
|
303
|
+
"/api/step.openapi.devcenter.Dashboard/GetStepPlanStatus",
|
|
304
|
+
forceRefresh
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async getQuotaInfo(forceRefresh = false) {
|
|
309
|
+
return this._cachedRequest(
|
|
310
|
+
"quotaData",
|
|
311
|
+
"quotaTimestamp",
|
|
312
|
+
"/api/step.openapi.devcenter.Dashboard/QueryStepPlanRateLimit",
|
|
313
|
+
forceRefresh
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ── 预留扩展区 ──────────────────────────────────────────────────────────────
|
|
318
|
+
// 后续新增接口时,只需:
|
|
319
|
+
// 1. 在 this.cache 里加对应的 data/timestamp 字段
|
|
320
|
+
// 2. 仿照下方模板写一个 get 方法
|
|
321
|
+
// 3. 在 getUsageStatus / parseUsageData 里消费新数据
|
|
322
|
+
//
|
|
323
|
+
// 模板(等 Token 用量明细接口开放后接入):
|
|
324
|
+
//
|
|
325
|
+
// async getTokenUsage(forceRefresh = false) {
|
|
326
|
+
// return this._cachedRequest(
|
|
327
|
+
// "tokenData",
|
|
328
|
+
// "tokenTimestamp",
|
|
329
|
+
// "/api/step.openapi.devcenter.Dashboard/QueryTokenUsage", // 待确认
|
|
330
|
+
// forceRefresh
|
|
331
|
+
// );
|
|
332
|
+
// }
|
|
333
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
334
|
+
|
|
335
|
+
async getUsageStatus(forceRefresh = false) {
|
|
336
|
+
const [statusData, quotaData] = await Promise.all([
|
|
337
|
+
this.getSubscriptionStatus(forceRefresh),
|
|
338
|
+
this.getQuotaInfo(forceRefresh),
|
|
339
|
+
]);
|
|
340
|
+
return this.parseUsageData(statusData, quotaData);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
parseUsageData(statusData, quotaData) {
|
|
344
|
+
const subscription = statusData.subscription || {};
|
|
345
|
+
const planDefinition = statusData.plan_definition || {};
|
|
346
|
+
|
|
347
|
+
const fiveHourLeftRate = quotaData.five_hour_usage_left_rate || 0;
|
|
348
|
+
const weeklyLeftRate = quotaData.weekly_usage_left_rate || 0;
|
|
349
|
+
|
|
350
|
+
const fiveHourUsedPercent = Math.round((1 - fiveHourLeftRate) * 100);
|
|
351
|
+
const weeklyUsedPercent = Math.round((1 - weeklyLeftRate) * 100);
|
|
352
|
+
|
|
353
|
+
const fiveHourReset = formatResetTime(quotaData.five_hour_usage_reset_time);
|
|
354
|
+
const weeklyReset = formatResetTime(quotaData.weekly_usage_reset_time);
|
|
355
|
+
|
|
356
|
+
let expiry = null;
|
|
357
|
+
if (subscription.expired_at) {
|
|
358
|
+
const expiryDate = new Date(parseInt(subscription.expired_at, 10) * 1000);
|
|
359
|
+
const daysRemaining = Math.ceil(
|
|
360
|
+
(expiryDate.getTime() - Date.now()) / (1000 * 3600 * 24)
|
|
361
|
+
);
|
|
362
|
+
expiry = {
|
|
363
|
+
date: expiryDate.toLocaleString(),
|
|
364
|
+
daysRemaining,
|
|
365
|
+
text:
|
|
366
|
+
daysRemaining > 0
|
|
367
|
+
? `还剩 ${daysRemaining} 天`
|
|
368
|
+
: daysRemaining === 0
|
|
369
|
+
? "今天到期"
|
|
370
|
+
: `已过期 ${Math.abs(daysRemaining)} 天`,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return {
|
|
375
|
+
modelName: planDefinition.support_models?.[0] || "step-3.5-flash",
|
|
376
|
+
planName: subscription.name || "Mini",
|
|
377
|
+
remaining: {
|
|
378
|
+
hours: fiveHourReset?.hoursUntil || 0,
|
|
379
|
+
minutes: fiveHourReset?.minutesUntil || 0,
|
|
380
|
+
text: calcRemainingText(fiveHourReset),
|
|
381
|
+
},
|
|
382
|
+
usage: {
|
|
383
|
+
percentage: fiveHourUsedPercent,
|
|
384
|
+
remaining: Math.round(fiveHourLeftRate * 100),
|
|
385
|
+
},
|
|
386
|
+
weekly: {
|
|
387
|
+
percentage: weeklyUsedPercent,
|
|
388
|
+
remaining: Math.round(weeklyLeftRate * 100),
|
|
389
|
+
text: calcRemainingText(weeklyReset),
|
|
390
|
+
daysUntilReset: weeklyReset?.hoursUntil
|
|
391
|
+
? Math.floor(weeklyReset.hoursUntil / 24)
|
|
392
|
+
: 0,
|
|
393
|
+
},
|
|
394
|
+
expiry,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
clearCache() {
|
|
399
|
+
this.cache = {
|
|
400
|
+
statusData: null,
|
|
401
|
+
quotaData: null,
|
|
402
|
+
statusTimestamp: 0,
|
|
403
|
+
quotaTimestamp: 0,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
module.exports = { StepFunAPI, CONFIG_PATH };
|
package/package.json
CHANGED
package/ARCHITECTURE.md
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
# StepFun Status 项目架构文档
|
|
2
|
-
|
|
3
|
-
## 概述
|
|
4
|
-
|
|
5
|
-
StepFun Status 是一个 StepFun Token-Plan 使用状态监控工具,支持 CLI 命令和 Claude Code 状态栏集成。
|
|
6
|
-
|
|
7
|
-
**注意**: 此工具需要 StepFun 官方开放用量查询 API 后才能显示完整额度信息。
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## API 接口汇总
|
|
12
|
-
|
|
13
|
-
### 1. Platform API (需要浏览器 Cookie)
|
|
14
|
-
|
|
15
|
-
**Base URL**: `https://platform.stepfun.com`
|
|
16
|
-
|
|
17
|
-
**认证方式**: 完整 Cookie 字符串(包含 `Oasis-Token` 和 `Oasis-Webid`)
|
|
18
|
-
|
|
19
|
-
**获取方式**:
|
|
20
|
-
1. 登录 https://platform.stepfun.com/plan-subscribe
|
|
21
|
-
2. F12 → Network 标签
|
|
22
|
-
3. 刷新页面
|
|
23
|
-
4. 点击任意 API 请求(如 `GetStepPlanStatus`)
|
|
24
|
-
5. 滚动到 Request Headers → 找到 `Cookie` 行
|
|
25
|
-
6. 右键 → Copy value
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
#### 1.1 GetStepPlanStatus - 获取订阅信息
|
|
30
|
-
|
|
31
|
-
**Endpoint**: `POST /api/step.openapi.devcenter.Dashboard/GetStepPlanStatus`
|
|
32
|
-
|
|
33
|
-
**请求体**: `{}`
|
|
34
|
-
|
|
35
|
-
**响应格式**:
|
|
36
|
-
```json
|
|
37
|
-
{
|
|
38
|
-
"status": 1,
|
|
39
|
-
"desc": "",
|
|
40
|
-
"subscription": {
|
|
41
|
-
"plan_type": 4,
|
|
42
|
-
"name": "Mini",
|
|
43
|
-
"status": 1,
|
|
44
|
-
"pay_channel": 1,
|
|
45
|
-
"activated_at": "1774255868",
|
|
46
|
-
"expired_at": "1776847868",
|
|
47
|
-
"auto_renew": false,
|
|
48
|
-
"plan_id": "5"
|
|
49
|
-
},
|
|
50
|
-
"plan_definition": {
|
|
51
|
-
"type": 4,
|
|
52
|
-
"price": "2500",
|
|
53
|
-
"support_models": ["step-3.5-flash"],
|
|
54
|
-
"available": true,
|
|
55
|
-
"original_price": "4900",
|
|
56
|
-
"plan_id": "5"
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
**字段说明**:
|
|
62
|
-
- `subscription.name`: 套餐名称 (Mini/Pro等)
|
|
63
|
-
- `subscription.activated_at`: 激活时间戳(秒)
|
|
64
|
-
- `subscription.expired_at`: 到期时间戳(秒)
|
|
65
|
-
- `plan_definition.support_models`: 支持的模型列表
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
#### 1.2 QueryStepPlanRateLimit - 获取用量信息
|
|
70
|
-
|
|
71
|
-
**Endpoint**: `POST /api/step.openapi.devcenter.Dashboard/QueryStepPlanRateLimit`
|
|
72
|
-
|
|
73
|
-
**请求体**: `{}`
|
|
74
|
-
|
|
75
|
-
**响应格式**:
|
|
76
|
-
```json
|
|
77
|
-
{
|
|
78
|
-
"status": 1,
|
|
79
|
-
"desc": "",
|
|
80
|
-
"five_hour_usage_left_rate": 0.9999867,
|
|
81
|
-
"five_hour_usage_reset_time": "1774324800",
|
|
82
|
-
"weekly_usage_left_rate": 0.9103905,
|
|
83
|
-
"weekly_usage_reset_time": "1774857600"
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
**字段说明**:
|
|
88
|
-
- `five_hour_usage_left_rate`: 5小时窗口剩余比例 (0-1)
|
|
89
|
-
- `five_hour_usage_reset_time`: 5小时窗口重置时间戳(秒)
|
|
90
|
-
- `weekly_usage_left_rate`: 每周剩余比例 (0-1)
|
|
91
|
-
- `weekly_usage_reset_time`: 每周重置时间戳(秒)
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
### 2. Developer API (API Key 方式) - [待开放]
|
|
96
|
-
|
|
97
|
-
**Base URL**: `https://api.stepfun.com`
|
|
98
|
-
|
|
99
|
-
**认证方式**: `Authorization: Bearer <API_KEY>`
|
|
100
|
-
|
|
101
|
-
**API Key 获取**: https://platform.stepfun.com/interface-key
|
|
102
|
-
|
|
103
|
-
#### 2.1 Models - 模型列表
|
|
104
|
-
|
|
105
|
-
**Endpoint**: `GET /v1/models`
|
|
106
|
-
|
|
107
|
-
**响应格式**:
|
|
108
|
-
```json
|
|
109
|
-
{
|
|
110
|
-
"data": [
|
|
111
|
-
{
|
|
112
|
-
"id": "step-3.5-flash",
|
|
113
|
-
"object": "model",
|
|
114
|
-
"created": 1769580037,
|
|
115
|
-
"owned_by": "stepai"
|
|
116
|
-
}
|
|
117
|
-
],
|
|
118
|
-
"object": "list"
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
#### 2.2 Chat Completions - 对话
|
|
123
|
-
|
|
124
|
-
**Endpoint**: `POST /v1/chat/completions`
|
|
125
|
-
|
|
126
|
-
**请求示例**:
|
|
127
|
-
```json
|
|
128
|
-
{
|
|
129
|
-
"model": "step-3.5-flash",
|
|
130
|
-
"messages": [{"role": "user", "content": "hi"}]
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
#### 2.3 Usage API - [待 StepFun 官方开放]
|
|
135
|
-
|
|
136
|
-
**Endpoint**: `GET /v1/usage` (待确认)
|
|
137
|
-
|
|
138
|
-
此接口将允许使用 API Key 查询账户用量,无需浏览器 Cookie。
|
|
139
|
-
|
|
140
|
-
---
|
|
141
|
-
|
|
142
|
-
## 配置文件
|
|
143
|
-
|
|
144
|
-
**路径**: `~/.stepfun-config.json`
|
|
145
|
-
|
|
146
|
-
**格式**:
|
|
147
|
-
```json
|
|
148
|
-
{
|
|
149
|
-
"cookie": "Oasis-Webid=xxx; Oasis-Token=xxx; ..."
|
|
150
|
-
}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
---
|
|
154
|
-
|
|
155
|
-
## 与 MiniMax 的对比
|
|
156
|
-
|
|
157
|
-
| 功能 | MiniMax | StepFun |
|
|
158
|
-
|------|---------|---------|
|
|
159
|
-
| AI API | ✅ OpenAI Compatible | ✅ OpenAI Compatible |
|
|
160
|
-
| 用量查询 API | ✅ 有 | ⏳ 待开放 |
|
|
161
|
-
| 认证方式 | API Key | Cookie (当前) / API Key (待开放) |
|
|
162
|
-
| 配置文件 | `~/.minimax-config.json` | `~/.stepfun-config.json` |
|
|
163
|
-
|
|
164
|
-
---
|
|
165
|
-
|
|
166
|
-
## 页面显示数据来源
|
|
167
|
-
|
|
168
|
-
页面 https://platform.stepfun.com/plan-subscribe 显示的数据:
|
|
169
|
-
|
|
170
|
-
```
|
|
171
|
-
5小时用量: 剩余 100%, 重置时间: 2026-03-24 12:00:00
|
|
172
|
-
每周用量: 剩余 91%, 重置时间: 2026-03-30 16:00:00
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
这些数据来自 `QueryStepPlanRateLimit` 接口。
|
package/stepfun-status-1.0.1.tgz
DELETED
|
Binary file
|