minimax-status 1.1.12 → 1.1.14

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
@@ -407,6 +407,16 @@ MIT License - 详见 [LICENSE](LICENSE) 文件
407
407
 
408
408
  欢迎提交 Issue 和 Pull Request!
409
409
 
410
+ ## 导航
411
+
412
+ | 客户端 | 路径 | 说明 |
413
+ |--------|------|------|
414
+ | **CLI** | [`cli/`](cli/) | 命令行工具,npm 全局包 |
415
+ | **VSCode** | [`vscode-extension/`](vscode-extension/) | VSCode 状态栏集成 |
416
+ | **OpenClaw** | [`openclaw/`](openclaw/) | OpenClaw 集成 |
417
+
418
+ ---
419
+
410
420
  ## 相关链接
411
421
 
412
422
  - [MiniMax 开放平台](https://platform.minimaxi.com/)
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";
@@ -138,8 +138,9 @@ program
138
138
  if (options.compact) {
139
139
  console.log(statusBar.renderCompact());
140
140
  } else {
141
- console.log("\n" + statusBar.render() + "\n");
142
- console.log(StatusBar.renderAllModels(allModels) + "\n");
141
+ // allModels 传入 StatusBar 内部渲染
142
+ const statusBarWithModels = new StatusBar(usageData, usageStats, api, allModels);
143
+ console.log("\n" + statusBarWithModels.render() + "\n");
143
144
  }
144
145
 
145
146
  if (options.watch) {
@@ -167,11 +168,10 @@ program
167
168
  ]);
168
169
  const usageData = api.parseUsageData(apiData, subscriptionData);
169
170
  const allModels = api.parseAllModels(apiData);
170
- const statusBar = new StatusBar(usageData);
171
171
 
172
172
  spinner.succeed("状态获取成功");
173
- console.log("\n" + statusBar.render() + "\n");
174
- console.log(StatusBar.renderAllModels(allModels) + "\n");
173
+ const statusBarWithModels = new StatusBar(usageData, null, null, allModels);
174
+ console.log("\n" + statusBarWithModels.render() + "\n");
175
175
  } catch (error) {
176
176
  spinner.fail(chalk.red("获取状态失败"));
177
177
  console.error(chalk.red(`错误: ${error.message}`));
@@ -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
@@ -4,10 +4,11 @@ const { default: boxen } = require('boxen');
4
4
  const { default: stringWidth } = require('string-width');
5
5
 
6
6
  class StatusBar {
7
- constructor(data, usageStats = null, api = null) {
7
+ constructor(data, usageStats = null, api = null, allModels = []) {
8
8
  this.data = data;
9
9
  this.usageStats = usageStats;
10
10
  this.api = api;
11
+ this.allModels = allModels;
11
12
  this.totalWidth = 63; // 总宽度包括边框
12
13
  this.borderWidth = 4; // '│ ' (2) + ' │' (2) = 4
13
14
  }
@@ -54,6 +55,56 @@ class StatusBar {
54
55
  return lines.join('\n');
55
56
  }
56
57
 
58
+ // 渲染所有模型额度区块
59
+ renderAllModelsSection() {
60
+ if (!this.allModels || this.allModels.length === 0) {
61
+ return '';
62
+ }
63
+
64
+ const lines = [];
65
+ lines.push('');
66
+ lines.push(chalk.bold('📋 所有模型额度'));
67
+
68
+ // 简化模型名称映射
69
+ const shortName = (name) => {
70
+ if (name.includes('MiniMax-M')) return 'MiniMax-M*';
71
+ if (name.includes('speech')) return 'speech-hd';
72
+ if (name.includes('Hailuo-2.3-Fast')) return 'Hailuo';
73
+ if (name.includes('Hailuo-2.3')) return 'Hailuo-2.3';
74
+ if (name.includes('Hailuo')) return 'Hailuo';
75
+ if (name.includes('music')) return 'music';
76
+ if (name.includes('image')) return 'image';
77
+ return name.length > 15 ? name.substring(0, 12) + '...' : name;
78
+ };
79
+
80
+ // 获取状态颜色
81
+ const getStatusColor = (percentage) => {
82
+ if (percentage >= 85) return chalk.hex('#EF4444');
83
+ if (percentage >= 60) return chalk.hex('#F59E0B');
84
+ return chalk.hex('#10B981');
85
+ };
86
+
87
+ // 显示状态
88
+ const getStatusText = (percentage) => {
89
+ if (percentage >= 85) return '⛔';
90
+ if (percentage >= 60) return '⚡';
91
+ return '✓';
92
+ };
93
+
94
+ // 每行显示一个模型
95
+ for (const model of this.allModels) {
96
+ const short = shortName(model.name);
97
+ const color = getStatusColor(model.percentage);
98
+ const status = getStatusText(model.percentage);
99
+ const pct = `${model.percentage}%`;
100
+ const usedTotal = `${model.used}/${model.total}`;
101
+
102
+ lines.push(` ${color(short.padEnd(15))} ${color(pct.padEnd(5))} ${color(usedTotal.padEnd(12))} ${color(status)}`);
103
+ }
104
+
105
+ return lines.join('\n');
106
+ }
107
+
57
108
  // 辅助函数:填充内容到正确长度,处理 chalk 代码和中文字符
58
109
  padLine(leftContent, rightContent) {
59
110
  // 移除 chalk 代码以便计算
@@ -115,11 +166,11 @@ class StatusBar {
115
166
  contentLines.push('');
116
167
  if (weekly.unlimited) {
117
168
  // 不受限制
118
- contentLines.push(`${chalk.cyan('周限额:')} ${chalk.green('不受限制')}`);
169
+ contentLines.push(`${chalk.cyan('周限额:')} ${chalk.hex('#10B981')('不受限制')}`);
119
170
  } else {
120
171
  // 有限制,显示具体数据
121
172
  const weeklyPercent = weekly.percentage;
122
- 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');
123
174
  const weeklyProgress = this.createProgressBar(
124
175
  Math.floor((weeklyPercent / 100) * 15),
125
176
  15 - Math.floor((weeklyPercent / 100) * 15),
@@ -141,6 +192,11 @@ class StatusBar {
141
192
  contentLines.push(this.renderConsumptionStats());
142
193
  }
143
194
 
195
+ // 添加所有模型额度(如果有数据)
196
+ if (this.allModels && this.allModels.length > 0) {
197
+ contentLines.push(this.renderAllModelsSection());
198
+ }
199
+
144
200
  contentLines.push('');
145
201
 
146
202
  // 状态行
@@ -166,11 +222,11 @@ class StatusBar {
166
222
 
167
223
  // 进度条颜色基于已使用百分比:使用越多越危险(红色)
168
224
  if (percentage >= 85) {
169
- return chalk.red(bar);
225
+ return chalk.hex('#EF4444')(bar);
170
226
  } else if (percentage >= 60) {
171
- return chalk.yellow(bar);
227
+ return chalk.hex('#F59E0B')(bar);
172
228
  } else {
173
- return chalk.green(bar);
229
+ return chalk.hex('#10B981')(bar);
174
230
  }
175
231
  }
176
232
 
@@ -184,18 +240,18 @@ class StatusBar {
184
240
 
185
241
  getStatusColor(status) {
186
242
  if (status === '⚡ 注意使用') {
187
- return chalk.yellow(status);
188
- } else if (status === ' 即将用完') {
189
- return chalk.red(status);
243
+ return chalk.hex('#F59E0B')(status);
244
+ } else if (status === ' 即将用完') {
245
+ return chalk.hex('#EF4444')(status);
190
246
  } else {
191
- return chalk.green(status);
247
+ return chalk.hex('#10B981')(status);
192
248
  }
193
249
  }
194
250
 
195
251
  getStatus(percentage) {
196
252
  // 基于已使用百分比
197
253
  if (percentage >= 85) {
198
- return ' 即将用完';
254
+ return ' 即将用完';
199
255
  } else if (percentage >= 60) {
200
256
  return '⚡ 注意使用';
201
257
  } else {
@@ -210,11 +266,11 @@ class StatusBar {
210
266
  // 颜色基于已使用百分比:使用越多越危险
211
267
  let color;
212
268
  if (usage.percentage >= 85) {
213
- color = chalk.red;
269
+ color = chalk.hex('#EF4444');
214
270
  } else if (usage.percentage >= 60) {
215
- color = chalk.yellow;
271
+ color = chalk.hex('#F59E0B');
216
272
  } else {
217
- color = chalk.green;
273
+ color = chalk.hex('#10B981');
218
274
  }
219
275
 
220
276
  // 添加到期信息(如果可用)
@@ -242,14 +298,14 @@ class StatusBar {
242
298
  // 颜色基于百分比
243
299
  let color;
244
300
  if (model.percentage >= 85) {
245
- color = chalk.red;
301
+ color = chalk.hex('#EF4444');
246
302
  } else if (model.percentage >= 60) {
247
- color = chalk.yellow;
303
+ color = chalk.hex('#F59E0B');
248
304
  } else {
249
- color = chalk.green;
305
+ color = chalk.hex('#10B981');
250
306
  }
251
307
 
252
- const status = model.percentage >= 85 ? ' 即将用完' : model.percentage >= 60 ? '⚡ 注意' : '✓ 正常';
308
+ const status = model.percentage >= 85 ? ' 即将用完' : model.percentage >= 60 ? '⚡ 注意' : '✓ 正常';
253
309
  const name = model.name.length > 28 ? model.name.substring(0, 25) + '...' : model.name;
254
310
 
255
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.12",
3
+ "version": "1.1.14",
4
4
  "description": "MiniMax Claude Code 使用状态监控工具",
5
5
  "bin": {
6
6
  "minimax-status": "cli/index.js",