minimax-status 1.1.13 → 1.2.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/README.md CHANGED
@@ -134,10 +134,10 @@ npm run package
134
134
  集成成功后,底部状态栏将显示:
135
135
 
136
136
  ```
137
- my-app main * MiniMax-M2 205K │ Usage ██████░░░░ 60% (2700/4500) 1h20m 到期 5天
137
+ my-app main * MiniMax-M2 60% (2700/4500) 1h20m 5天
138
138
  ```
139
139
 
140
- 显示格式:`目录 分支 模型 上下文 │ Usage 进度条 百分比(剩余/总数) 倒计时 到期 天数`
140
+ 显示格式:`目录 分支 模型 百分比(剩余/总数) 倒计时 到期天数`
141
141
 
142
142
  **颜色说明**:
143
143
 
@@ -219,18 +219,10 @@ my-app │ main * │ ...
219
219
  集成成功后,底部状态栏将显示:
220
220
 
221
221
  ```
222
- minimax-status main * Usage █░░░░░░░░ 10% (4047/4500) 12m 到期 21天
222
+ minimax-status main * 10% (4047/4500) 12m 21天
223
223
  ```
224
224
 
225
- 显示格式:`目录 分支 Usage 进度条 百分比(剩余/总数) 倒计时 到期 天数`
226
-
227
- ### 进度条风格
228
-
229
- 使用 `█` 和 `░` 字符显示进度条:
230
-
231
- - `█░░░░░░░░` - 10% 使用量
232
- - `█████░░░░` - 50% 使用量
233
- - `██████████` - 100% 使用量
225
+ 显示格式:`目录 分支 百分比(剩余/总数) 倒计时 到期天数`
234
226
 
235
227
  **颜色说明**:
236
228
 
@@ -277,6 +269,16 @@ minimax-status │ main * │ Usage █░░░░░░░░ 10% (4047/4500)
277
269
  [● MiniMax-M2 27% • 3307/4500 • 1h26m ⚡
278
270
  ```
279
271
 
272
+ ## 截图演示
273
+
274
+ ### Claude Code 集成
275
+
276
+ ![Claude Code StatusBar](./images/claude%20code.png)
277
+
278
+ ### Droid 集成
279
+
280
+ ![Droid StatusBar](./images/droid.png)
281
+
280
282
  ## 命令说明
281
283
 
282
284
  | 命令 | 描述 | 示例 |
@@ -297,18 +299,10 @@ minimax-status │ main * │ Usage █░░░░░░░░ 10% (4047/4500)
297
299
  | 分支 | Git 分支名称 |
298
300
  | 模型 | MiniMax 模型名称 |
299
301
  | 上下文 | 上下文窗口使用率 |
300
- | Usage | 使用量进度条和百分比(剩余/总数) |
302
+ | Usage | 使用量百分比(剩余/总数) |
301
303
  | ⏱ | 额度重置倒计时 |
302
304
  | 到期 | 订阅到期时间(颜色动态变化) |
303
305
 
304
- ### 进度条
305
-
306
- 使用 `█` 和 `░` 字符显示进度条:
307
-
308
- - `█░░░░░░░░` - 10% 使用量
309
- - `█████░░░░░` - 50% 使用量
310
- - `██████████` - 100% 使用量
311
-
312
306
  ### 颜色规则
313
307
 
314
308
  | 场景 | 颜色 | 说明 |
@@ -407,6 +401,16 @@ MIT License - 详见 [LICENSE](LICENSE) 文件
407
401
 
408
402
  欢迎提交 Issue 和 Pull Request!
409
403
 
404
+ ## 导航
405
+
406
+ | 客户端 | 路径 | 说明 |
407
+ |--------|------|------|
408
+ | **CLI** | [`cli/`](cli/) | 命令行工具,npm 全局包 |
409
+ | **VSCode** | [`vscode-extension/`](vscode-extension/) | VSCode 状态栏集成 |
410
+ | **OpenClaw** | [`openclaw/`](openclaw/) | OpenClaw 集成 |
411
+
412
+ ---
413
+
410
414
  ## 相关链接
411
415
 
412
416
  - [MiniMax 开放平台](https://platform.minimaxi.com/)
package/cli/api.js CHANGED
@@ -80,7 +80,7 @@ class MinimaxAPI {
80
80
 
81
81
  try {
82
82
  const response = await axios.get(
83
- `https://www.minimaxi.com/v1/api/openplatform/coding_plan/remains`,
83
+ `https://www.minimaxi.com/v1/token_plan/remains`,
84
84
  {
85
85
  headers: {
86
86
  Authorization: `Bearer ${this.token}`,
@@ -295,9 +295,9 @@ class MinimaxAPI {
295
295
  const endTime = new Date(modelData.end_time);
296
296
 
297
297
  // Calculate counts
298
- // 注意:current_interval_usage_count 实际是剩余次数,不是已用次数
299
- const remainingCount = modelData.current_interval_usage_count;
300
- const usedCount = modelData.current_interval_total_count - remainingCount;
298
+ // 新接口 usage_count 是已使用次数(正确值)
299
+ const usedCount = modelData.current_interval_usage_count;
300
+ const remainingCount = modelData.current_interval_total_count - usedCount;
301
301
 
302
302
  // Calculate percentage - 基于已使用次数的百分比
303
303
  const usedPercentage = Math.round(
@@ -310,7 +310,7 @@ class MinimaxAPI {
310
310
  const minutes = Math.floor((remainingMs % (1000 * 60 * 60)) / (1000 * 60));
311
311
 
312
312
  // Calculate weekly usage data
313
- const weeklyUsed = modelData.current_weekly_total_count - modelData.current_weekly_usage_count;
313
+ const weeklyUsed = modelData.current_weekly_usage_count;
314
314
  const weeklyTotal = modelData.current_weekly_total_count;
315
315
  const weeklyPercentage = weeklyTotal > 0 ? Math.floor((weeklyUsed / weeklyTotal) * 100) : 0;
316
316
  const weeklyRemainingMs = modelData.weekly_remains_time;
@@ -382,8 +382,8 @@ class MinimaxAPI {
382
382
  : `${minutes} 分钟后重置`,
383
383
  },
384
384
  usage: {
385
- used: usedCount, // 修复:显示已使用次数,不是剩余次数
386
- remaining: remainingCount, // 新增:剩余次数
385
+ used: usedCount,
386
+ remaining: remainingCount,
387
387
  total: modelData.current_interval_total_count,
388
388
  percentage: usedPercentage,
389
389
  },
@@ -415,13 +415,15 @@ class MinimaxAPI {
415
415
 
416
416
  return apiData.model_remains.map(modelData => {
417
417
  const totalCount = modelData.current_interval_total_count;
418
- const remainingCount = modelData.current_interval_usage_count;
419
- const usedCount = totalCount - remainingCount;
418
+ // 新接口 usage_count 是已使用次数(正确值)
419
+ const usedCount = modelData.current_interval_usage_count;
420
+ const remainingCount = totalCount - usedCount;
420
421
  const usedPercentage = totalCount > 0 ? Math.round((usedCount / totalCount) * 100) : 0;
421
422
 
422
423
  // Weekly data
423
424
  const weeklyTotal = modelData.current_weekly_total_count || 0;
424
- const weeklyUsed = weeklyTotal > 0 ? (modelData.current_weekly_total_count - modelData.current_weekly_usage_count) : 0;
425
+ const weeklyUsed = modelData.current_weekly_usage_count || 0;
426
+ const weeklyRemainingCount = weeklyTotal - weeklyUsed;
425
427
  const weeklyPercentage = weeklyTotal > 0 ? Math.floor((weeklyUsed / weeklyTotal) * 100) : 0;
426
428
 
427
429
  return {
@@ -433,7 +435,7 @@ class MinimaxAPI {
433
435
  unlimited: weeklyTotal === 0,
434
436
  weeklyPercentage,
435
437
  weeklyTotal,
436
- weeklyRemainingCount: modelData.current_weekly_usage_count || 0,
438
+ weeklyRemainingCount,
437
439
  };
438
440
  });
439
441
  }
package/cli/index.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  // Force color output even in non-TTY environments (e.g., Claude Code statusline)
4
4
  process.env.FORCE_COLOR = "1";
@@ -350,13 +350,17 @@ program
350
350
  }
351
351
  }
352
352
 
353
- // 使用 Claude Code 提供的 context_window(最准确)
354
- let contextUsageValue = contextUsageTokens;
353
+ // 优先使用 Claude Code 提供的实时 tokens_used,如果没有则回退到 transcript 解析
354
+ let contextUsageValue = 0;
355
355
  let contextSizeValue = contextSize;
356
356
 
357
357
  if (stdinData?.context_window) {
358
358
  const cw = stdinData.context_window;
359
359
  contextSizeValue = cw.context_window_size || contextSize;
360
+ // 关键点:优先取这里,这是最实时的
361
+ contextUsageValue = cw.tokens_used || contextUsageTokens || 0;
362
+ } else {
363
+ contextUsageValue = contextUsageTokens || 0;
360
364
  }
361
365
 
362
366
  const context = {
@@ -634,66 +638,85 @@ program
634
638
  // ignore errors
635
639
  }
636
640
 
637
- // 进度条渲染函数
638
- function getBarColor(p) {
639
- if (p >= 85) return chalk.red;
640
- if (p >= 60) return chalk.yellow;
641
- return chalk.green;
642
- }
643
- const coloredBar = (percent, width = 10) => {
644
- const filled = Math.round((percent / 100) * width);
645
- const empty = width - filled;
646
- const barColor = getBarColor(percent);
647
- return barColor('█'.repeat(filled) + '\x1b[2m' + '░'.repeat(empty) + '\x1b[0m');
648
- };
641
+ const blocks = [];
649
642
 
650
- // 简化输出:目录 | git分支 | 使用量(进度条) | 倒计时
651
- const parts = [];
652
-
653
- // 目录
643
+ // 高对比度徽章配色,纯 Powerline 渲染
654
644
  if (currentDir) {
655
- parts.push(`${chalk.cyan(currentDir)}`);
645
+ blocks.push({ text: ` ${currentDir} `, bg: '#1D4ED8' }); // 皇家蓝
656
646
  }
657
647
 
658
- // Git 分支
648
+ const useNerdFonts = !process.env.MINIMAX_PLAIN_UI && !process.env.NO_NERD_FONTS;
649
+ const arrow = useNerdFonts ? '\uE0B0' : '>';
650
+ const branchIcon = useNerdFonts ? '\uE0A0' : '*';
651
+
659
652
  if (gitBranch && gitBranch.name) {
660
- const isMainBranch = gitBranch.name === 'main' || gitBranch.name === 'master';
661
- const branchColor = isMainBranch ? chalk.green : chalk.white;
662
- let branchStr = branchColor(gitBranch.name);
653
+ let branchStr = gitBranch.name;
654
+ if (branchStr.length > 20) branchStr = branchStr.substring(0, 10) + '…' + branchStr.substring(branchStr.length - 7);
663
655
  if (gitBranch.hasChanges) {
664
- branchStr += chalk.red(' *');
656
+ branchStr += ' *';
665
657
  }
666
- parts.push(branchStr);
658
+ blocks.push({ text: ` ${branchIcon} ${branchStr} `, bg: '#7E22CE' }); // 紫色回归
667
659
  }
668
660
 
669
- // 使用量 - 进度条风格 (显示次数)
670
- const usageBar = coloredBar(usage.percentage);
671
- const usageColor = usage.percentage >= 85 ? chalk.red : usage.percentage >= 60 ? chalk.yellow : chalk.green;
672
- let usageLine = `${usageBar} ${usageColor(usage.percentage + '%')} (${usage.remaining}/${usage.total})`;
673
- // 周用量紧跟在 usage 后面
674
- if (weekly) {
675
- if (weekly.unlimited) {
676
- usageLine += ` ${chalk.gray('·')} ${chalk.blue('W')} ♾️`;
677
- } else {
678
- const weeklyColor = weekly.percentage >= 85 ? chalk.red : weekly.percentage >= 60 ? chalk.yellow : chalk.green;
679
- usageLine += ` ${chalk.gray('·')} ${chalk.blue('W')} ${weeklyColor(weekly.percentage + '%')}`;
661
+ if (usage && usage.total > 0) {
662
+ let bg = '#065F46'; // 回归稳健的深翠绿 (Emerald 800)
663
+ if (usage.percentage >= 95) bg = '#991B1B'; // danger (Red 800)
664
+ else if (usage.percentage >= 75) bg = '#9A3412'; // warn (Orange 800)
665
+
666
+ let usageText = ` ${usage.percentage}% (${usage.remaining}/${usage.total}) `;
667
+ if (weekly) {
668
+ if (weekly.unlimited) {
669
+ usageText += `· W ∞ `;
670
+ } else {
671
+ usageText += W ${weekly.percentage}% `;
672
+ }
680
673
  }
674
+ blocks.push({ text: usageText, bg: bg });
681
675
  }
682
- parts.push(usageLine);
683
676
 
684
- // 倒计时
685
- const remainingText = remaining.hours > 0
686
- ? `${remaining.hours}h${remaining.minutes}m`
687
- : `${remaining.minutes}m`;
688
- parts.push(`${chalk.yellow('⏱')} ${remainingText}`);
677
+ if (remaining) {
678
+ const remainingText = remaining.hours > 0
679
+ ? `${remaining.hours}h${remaining.minutes}m`
680
+ : `${remaining.minutes}m`;
681
+ blocks.push({ text: ` ${remainingText} `, bg: '#92400E' });
682
+ }
689
683
 
690
- // 到期
691
684
  if (expiry) {
692
- const expiryColor = expiry.daysRemaining <= 3 ? chalk.red : expiry.daysRemaining <= 7 ? chalk.yellow : chalk.green;
693
- parts.push(`${expiryColor('到期 ' + expiry.daysRemaining + '')}`);
685
+ let bg = '#374151'; // Gray 700
686
+ if (expiry.daysRemaining <= 7) bg = '#9A3412';
687
+ if (expiry.daysRemaining <= 3) bg = '#991B1B';
688
+ blocks.push({ text: ` 剩${expiry.daysRemaining}天 `, bg: bg });
694
689
  }
695
-
696
- console.log(parts.join(' '));
690
+
691
+ let out = '';
692
+ const leftArrow = useNerdFonts ? '\uE0B0' : '>';
693
+
694
+ for (let i = 0; i < blocks.length; i++) {
695
+ const b = blocks[i];
696
+
697
+ // 磁贴开启:顺行式起点,利用黑色箭头实现内凹镂空感
698
+ if (i === 0) {
699
+ out += '\u001b[0m' + chalk.bgHex(b.bg).black(leftArrow);
700
+ }
701
+
702
+ // 磁贴文字内容
703
+ out += '\u001b[0m' + chalk.bgHex(b.bg).bold.whiteBright(b.text);
704
+
705
+ if (i < blocks.length - 1) {
706
+ const nextB = blocks[i + 1];
707
+ if (useNerdFonts) {
708
+ // 衔接尖部
709
+ out += '\u001b[0m' + chalk.bgHex(nextB.bg).hex(b.bg)(arrow);
710
+ } else {
711
+ out += '\u001b[0m' + chalk.bgHex(b.bg).bold.whiteBright(arrow);
712
+ }
713
+ } else {
714
+ // 最后一块磁贴:顺行式终点
715
+ out += '\u001b[0m' + chalk.hex(b.bg)(arrow);
716
+ }
717
+ }
718
+
719
+ console.log(out);
697
720
  });
698
721
 
699
722
  // 模型上下文窗口大小(仅MiniMax模型)
package/cli/renderer.js CHANGED
@@ -5,6 +5,15 @@ const chalk = require('chalk').default;
5
5
  class Renderer {
6
6
  constructor() {
7
7
  this.RESET = '\x1b[0m';
8
+ // 自动检测:如果环境变量设置了 MINIMAX_PLAIN_UI 或者是 NO_NERD_FONTS,则停用特殊图标
9
+ this.useNerdFonts = !process.env.MINIMAX_PLAIN_UI && !process.env.NO_NERD_FONTS;
10
+
11
+ // 图标配置
12
+ this.icons = {
13
+ arrow: this.useNerdFonts ? '\uE0B0' : '>',
14
+ leftArrow: this.useNerdFonts ? '\uE0B0' : '>', // 改为顺向箭头,实现顺行感
15
+ branch: this.useNerdFonts ? '\uE0A0' : '*'
16
+ };
8
17
  }
9
18
 
10
19
  formatTokens(tokens) {
@@ -60,94 +69,91 @@ class Renderer {
60
69
 
61
70
  renderSessionLine(data) {
62
71
  const {
63
- modelName,
64
- currentDir,
65
- usagePercentage,
66
- usage,
67
- remaining,
68
- expiry,
69
- contextUsage,
70
- contextSize,
71
- configCounts,
72
- sessionDuration,
73
- weekly,
72
+ modelName, currentDir, usagePercentage, usage,
73
+ remaining, expiry, contextUsage, contextSize, weekly, gitBranch
74
74
  } = data;
75
75
 
76
- const parts = [];
76
+ const blocks = [];
77
77
 
78
+ // Pure Powerline Data Blocks
78
79
  if (currentDir) {
79
- parts.push(`${chalk.cyan(currentDir)}`);
80
+ blocks.push({ text: ` ${currentDir} `, bg: '#2563EB', fg: '#FFFFFF' });
80
81
  }
81
82
 
82
- // Git 分支显示
83
- if (data.gitBranch && data.gitBranch.name) {
84
- const { name, ahead, behind, hasChanges } = data.gitBranch;
83
+ if (gitBranch && gitBranch.name) {
84
+ let name = gitBranch.name;
85
+ if (name.length > 20) name = name.substring(0, 10) + '…' + name.substring(name.length - 7);
86
+ const star = gitBranch.hasChanges ? ' *' : '';
87
+ blocks.push({ text: ` ${this.icons.branch} ${name}${star} `, bg: '#9333EA', fg: '#FFFFFF' });
88
+ }
85
89
 
86
- // 主分支(main/master)用绿色,其他分支用白色
87
- const isMainBranch = name === 'main' || name === 'master';
88
- const branchColor = isMainBranch ? chalk.green : chalk.white;
90
+ if (modelName) {
91
+ blocks.push({ text: ` ${modelName} `, bg: '#4C1D95', fg: '#FFFFFF' });
92
+ }
89
93
 
90
- // 分支名截断处理:超过20字符时截断并省略中间部分
91
- let displayBranchName = name;
92
- if (name.length > 20) {
93
- const prefixLength = 10;
94
- const suffixLength = 7;
95
- displayBranchName = name.substring(0, prefixLength) + '' + name.substring(name.length - suffixLength);
94
+ if (contextSize) {
95
+ if (contextUsage) {
96
+ const pct = Math.round((contextUsage / contextSize) * 100);
97
+ blocks.push({ text: ` ${pct}% · ${this.formatTokens(contextUsage)} `, bg: '#0369A1', fg: '#FFFFFF' });
98
+ } else {
99
+ blocks.push({ text: ` ${this.formatContextSize(contextSize)} `, bg: '#0369A1', fg: '#FFFFFF' });
96
100
  }
101
+ }
97
102
 
98
- // 构建分支显示字符串
99
- let branchStr = branchColor(displayBranchName);
100
-
101
- // 未提交更改用 * 标记
102
- if (hasChanges) {
103
- branchStr += chalk.red(' *');
103
+ if (usage && usage.total > 0) {
104
+ let bg = '#065F46'; // safe (Emerald 800 - dark enough for white text)
105
+ if (usagePercentage >= 95) bg = '#991B1B'; // danger (Red 800)
106
+ else if (usagePercentage >= 75) bg = '#9A3412'; // warn (Orange 800)
107
+
108
+ let usageText = ` ${usagePercentage}% (${usage.remaining}/${usage.total}) `;
109
+
110
+ if (weekly) {
111
+ if (weekly.unlimited) {
112
+ usageText += `· W ∞ `;
113
+ } else {
114
+ usageText += `· W ${weekly.percentage}% `;
115
+ }
104
116
  }
105
-
106
- parts.push(branchStr);
117
+ blocks.push({ text: usageText, bg: bg, fg: '#FFFFFF' });
107
118
  }
108
119
 
109
- // 模型
110
- parts.push(`${chalk.magenta(modelName)}`);
120
+ if (remaining) {
121
+ const remainingText = remaining.hours > 0 ? `${remaining.hours}h${remaining.minutes}m` : `${remaining.minutes}m`;
122
+ blocks.push({ text: ` ${remainingText} `, bg: '#92400E', fg: '#FFFFFF' });
123
+ }
111
124
 
112
- // 上下文窗口
113
- if (contextUsage !== null && contextUsage !== undefined) {
114
- const contextPercent = Math.round((contextUsage / contextSize) * 100);
115
- const contextColor = this.getStatusColor(contextPercent);
116
- parts.push(`${contextColor(contextPercent + '%')} ${contextColor('·')} ${contextColor(this.formatTokens(contextUsage) + ' tokens')}`);
117
- } else {
118
- parts.push(chalk.cyan(this.formatContextSize(contextSize)));
125
+ if (expiry) {
126
+ let bg = '#374151'; // Gray 700
127
+ if (expiry.daysRemaining <= 7) bg = '#9A3412';
128
+ if (expiry.daysRemaining <= 3) bg = '#991B1B';
129
+ blocks.push({ text: ` 剩${expiry.daysRemaining} `, bg: bg, fg: '#FFFFFF' });
119
130
  }
120
131
 
121
- // 使用量 - 进度条风格
122
- const usageColor = this.getStatusColor(usagePercentage);
123
- const filled = Math.round((usagePercentage / 100) * 10);
124
- const empty = 10 - filled;
125
- const usageBar = usageColor('█'.repeat(filled) + '\x1b[2m' + '░'.repeat(empty) + '\x1b[0m');
126
- let usageLine = `${usageBar} ${usageColor(usagePercentage + '%')} (${usage.remaining}/${usage.total})`;
127
- // 周用量紧跟在 usage 后面
128
- if (weekly) {
129
- if (weekly.unlimited) {
130
- usageLine += ` ${chalk.gray('·')} ${chalk.blue('W')} ♾️`;
132
+ // Powerline arrow seamless integration
133
+ let out = '';
134
+ const arrow = this.icons.arrow;
135
+
136
+ for (let i = 0; i < blocks.length; i++) {
137
+ const b = blocks[i];
138
+ // 顺行式起点:使用正向箭头 + 黑色前景色模拟内凹效果
139
+ if (i === 0) {
140
+ out += this.RESET + chalk.bgHex(b.bg).black(this.icons.leftArrow);
141
+ }
142
+ // 磁贴文字
143
+ out += chalk.bgHex(b.bg).whiteBright(b.text);
144
+ if (i < blocks.length - 1) {
145
+ const nextB = blocks[i + 1];
146
+ if (this.useNerdFonts) {
147
+ out += chalk.bgHex(nextB.bg).hex(b.bg)(arrow);
148
+ } else {
149
+ out += chalk.bgHex(b.bg).whiteBright(arrow);
150
+ }
131
151
  } else {
132
- const weeklyColor = this.getStatusColor(weekly.percentage);
133
- usageLine += ` ${chalk.gray('·')} ${chalk.blue('W')} ${weeklyColor(weekly.percentage + '%')}`;
152
+ out += chalk.hex(b.bg)(arrow);
134
153
  }
135
154
  }
136
- parts.push(usageLine);
137
-
138
- // 倒计时 - 保留图标
139
- const remainingText = remaining.hours > 0
140
- ? `${remaining.hours}h${remaining.minutes}m`
141
- : `${remaining.minutes}m`;
142
- parts.push(`${chalk.yellow('⏱')} ${remainingText}`);
143
-
144
- // 到期 - 保留图标
145
- if (expiry) {
146
- const expiryColor = expiry.daysRemaining <= 3 ? chalk.red : expiry.daysRemaining <= 7 ? chalk.yellow : chalk.green;
147
- parts.push(`${expiryColor('到期 ' + expiry.daysRemaining + '天')}`);
148
- }
149
155
 
150
- return parts.join(' │ ');
156
+ return out;
151
157
  }
152
158
 
153
159
  renderToolsLine(tools) {
package/cli/status.js CHANGED
@@ -79,14 +79,14 @@ class StatusBar {
79
79
 
80
80
  // 获取状态颜色
81
81
  const getStatusColor = (percentage) => {
82
- if (percentage >= 85) return chalk.red;
83
- if (percentage >= 60) return chalk.yellow;
84
- return chalk.green;
82
+ if (percentage >= 85) return chalk.hex('#EF4444');
83
+ if (percentage >= 60) return chalk.hex('#F59E0B');
84
+ return chalk.hex('#10B981');
85
85
  };
86
86
 
87
87
  // 显示状态
88
88
  const getStatusText = (percentage) => {
89
- if (percentage >= 85) return '';
89
+ if (percentage >= 85) return '';
90
90
  if (percentage >= 60) return '⚡';
91
91
  return '✓';
92
92
  };
@@ -166,11 +166,11 @@ class StatusBar {
166
166
  contentLines.push('');
167
167
  if (weekly.unlimited) {
168
168
  // 不受限制
169
- contentLines.push(`${chalk.cyan('周限额:')} ${chalk.green('不受限制')}`);
169
+ contentLines.push(`${chalk.cyan('周限额:')} ${chalk.hex('#10B981')('不受限制')}`);
170
170
  } else {
171
171
  // 有限制,显示具体数据
172
172
  const weeklyPercent = weekly.percentage;
173
- const weeklyColor = weeklyPercent >= 85 ? chalk.red : weeklyPercent >= 60 ? chalk.yellow : chalk.green;
173
+ const weeklyColor = weeklyPercent >= 85 ? chalk.hex('#EF4444') : weeklyPercent >= 60 ? chalk.hex('#F59E0B') : chalk.hex('#10B981');
174
174
  const weeklyProgress = this.createProgressBar(
175
175
  Math.floor((weeklyPercent / 100) * 15),
176
176
  15 - Math.floor((weeklyPercent / 100) * 15),
@@ -222,11 +222,11 @@ class StatusBar {
222
222
 
223
223
  // 进度条颜色基于已使用百分比:使用越多越危险(红色)
224
224
  if (percentage >= 85) {
225
- return chalk.red(bar);
225
+ return chalk.hex('#EF4444')(bar);
226
226
  } else if (percentage >= 60) {
227
- return chalk.yellow(bar);
227
+ return chalk.hex('#F59E0B')(bar);
228
228
  } else {
229
- return chalk.green(bar);
229
+ return chalk.hex('#10B981')(bar);
230
230
  }
231
231
  }
232
232
 
@@ -240,18 +240,18 @@ class StatusBar {
240
240
 
241
241
  getStatusColor(status) {
242
242
  if (status === '⚡ 注意使用') {
243
- return chalk.yellow(status);
244
- } else if (status === ' 即将用完') {
245
- return chalk.red(status);
243
+ return chalk.hex('#F59E0B')(status);
244
+ } else if (status === ' 即将用完') {
245
+ return chalk.hex('#EF4444')(status);
246
246
  } else {
247
- return chalk.green(status);
247
+ return chalk.hex('#10B981')(status);
248
248
  }
249
249
  }
250
250
 
251
251
  getStatus(percentage) {
252
252
  // 基于已使用百分比
253
253
  if (percentage >= 85) {
254
- return ' 即将用完';
254
+ return ' 即将用完';
255
255
  } else if (percentage >= 60) {
256
256
  return '⚡ 注意使用';
257
257
  } else {
@@ -266,11 +266,11 @@ class StatusBar {
266
266
  // 颜色基于已使用百分比:使用越多越危险
267
267
  let color;
268
268
  if (usage.percentage >= 85) {
269
- color = chalk.red;
269
+ color = chalk.hex('#EF4444');
270
270
  } else if (usage.percentage >= 60) {
271
- color = chalk.yellow;
271
+ color = chalk.hex('#F59E0B');
272
272
  } else {
273
- color = chalk.green;
273
+ color = chalk.hex('#10B981');
274
274
  }
275
275
 
276
276
  // 添加到期信息(如果可用)
@@ -298,14 +298,14 @@ class StatusBar {
298
298
  // 颜色基于百分比
299
299
  let color;
300
300
  if (model.percentage >= 85) {
301
- color = chalk.red;
301
+ color = chalk.hex('#EF4444');
302
302
  } else if (model.percentage >= 60) {
303
- color = chalk.yellow;
303
+ color = chalk.hex('#F59E0B');
304
304
  } else {
305
- color = chalk.green;
305
+ color = chalk.hex('#10B981');
306
306
  }
307
307
 
308
- const status = model.percentage >= 85 ? ' 即将用完' : model.percentage >= 60 ? '⚡ 注意' : '✓ 正常';
308
+ const status = model.percentage >= 85 ? ' 即将用完' : model.percentage >= 60 ? '⚡ 注意' : '✓ 正常';
309
309
  const name = model.name.length > 28 ? model.name.substring(0, 25) + '...' : model.name;
310
310
 
311
311
  lines.push(`│ ${name.padEnd(30)} ${color(`${model.used}/${model.total} (${model.percentage}%)`).padEnd(15)} ${color(status)}`);
@@ -147,36 +147,43 @@ class TranscriptParser {
147
147
  }
148
148
 
149
149
  try {
150
- const fileContent = fs.readFileSync(transcriptPath, 'utf8');
151
- const lines = fileContent.trim().split('\n').filter(line => line.trim());
150
+ const stats = fs.statSync(transcriptPath);
151
+ const fileSize = stats.size;
152
+ const bufferSize = Math.min(fileSize, 64 * 1024); // 读取最后 64KB
153
+ const buffer = Buffer.alloc(bufferSize);
154
+
155
+ const fd = fs.openSync(transcriptPath, 'r');
156
+ fs.readSync(fd, buffer, 0, bufferSize, Math.max(0, fileSize - bufferSize));
157
+ fs.closeSync(fd);
152
158
 
153
- if (lines.length === 0) {
154
- return null;
155
- }
159
+ const content = buffer.toString('utf8');
160
+ const lines = content.split('\n').filter(line => line.trim());
156
161
 
157
- const lastLine = lines[lines.length - 1].trim();
158
- const lastEntry = JSON.parse(lastLine);
162
+ if (lines.length === 0) return null;
159
163
 
160
- if (lastEntry.type === 'summary' && lastEntry.leafUuid) {
161
- return this.findUsageByUuid(transcriptPath, lastEntry.leafUuid);
162
- }
164
+ // 检查最后一行是否是 summary
165
+ const lastLine = lines[lines.length - 1].trim();
166
+ try {
167
+ const lastEntry = JSON.parse(lastLine);
168
+ if (lastEntry.type === 'summary' && lastEntry.leafUuid) {
169
+ return this.findUsageByUuid(transcriptPath, lastEntry.leafUuid);
170
+ }
171
+ } catch (e) {}
163
172
 
173
+ // 从后往前找最近的 assistant usage
164
174
  for (let i = lines.length - 1; i >= 0; i--) {
165
- const line = lines[i].trim();
166
- if (!line) continue;
167
-
168
175
  try {
169
- const entry = JSON.parse(line);
176
+ const entry = JSON.parse(lines[i]);
170
177
  if (entry.type === 'assistant' && entry.message?.usage) {
171
178
  return this.calculateContextTokens(entry.message.usage);
172
179
  }
173
- } catch {
180
+ } catch (e) {
174
181
  continue;
175
182
  }
176
183
  }
177
184
 
178
185
  return null;
179
- } catch {
186
+ } catch (error) {
180
187
  return null;
181
188
  }
182
189
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minimax-status",
3
- "version": "1.1.13",
3
+ "version": "1.2.0",
4
4
  "description": "MiniMax Claude Code 使用状态监控工具",
5
5
  "bin": {
6
6
  "minimax-status": "cli/index.js",