minimax-status 1.0.0 → 1.0.1

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/cli/api.js ADDED
@@ -0,0 +1,125 @@
1
+ const axios = require('axios');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ class MinimaxAPI {
6
+ constructor() {
7
+ this.token = null;
8
+ this.groupId = null;
9
+ this.configPath = path.join(process.env.HOME || process.env.USERPROFILE, '.minimax-config.json');
10
+ this.loadConfig();
11
+ }
12
+
13
+ loadConfig() {
14
+ try {
15
+ // 只从独立的 config 文件读取
16
+ if (fs.existsSync(this.configPath)) {
17
+ const config = JSON.parse(fs.readFileSync(this.configPath, 'utf8'));
18
+ this.token = config.token;
19
+ this.groupId = config.groupId;
20
+ }
21
+ } catch (error) {
22
+ console.error('Failed to load config:', error.message);
23
+ }
24
+ }
25
+
26
+ saveConfig() {
27
+ try {
28
+ // 保存到独立的 config 文件
29
+ const config = {
30
+ token: this.token,
31
+ groupId: this.groupId
32
+ };
33
+ fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2));
34
+ } catch (error) {
35
+ console.error('Failed to save config:', error.message);
36
+ }
37
+ }
38
+
39
+ setCredentials(token, groupId) {
40
+ this.token = token;
41
+ this.groupId = groupId;
42
+ this.saveConfig();
43
+ }
44
+
45
+ async getUsageStatus() {
46
+ if (!this.token || !this.groupId) {
47
+ throw new Error('Missing credentials. Please run "minimax-status auth <token> <groupId>" first');
48
+ }
49
+
50
+ try {
51
+ const response = await axios.get(
52
+ `https://www.minimaxi.com/v1/api/openplatform/coding_plan/remains`,
53
+ {
54
+ params: { GroupId: this.groupId },
55
+ headers: {
56
+ 'Authorization': `Bearer ${this.token}`,
57
+ 'Accept': 'application/json'
58
+ }
59
+ }
60
+ );
61
+
62
+ return response.data;
63
+ } catch (error) {
64
+ if (error.response?.status === 401) {
65
+ throw new Error('Invalid token or unauthorized. Please check your credentials.');
66
+ }
67
+ throw new Error(`API request failed: ${error.message}`);
68
+ }
69
+ }
70
+
71
+ parseUsageData(apiData) {
72
+ if (!apiData.model_remains || apiData.model_remains.length === 0) {
73
+ throw new Error('No usage data available');
74
+ }
75
+
76
+ const modelData = apiData.model_remains[0];
77
+ const startTime = new Date(modelData.start_time);
78
+ const endTime = new Date(modelData.end_time);
79
+ const now = new Date();
80
+
81
+ // Calculate counts
82
+ // 注意:current_interval_usage_count 实际是剩余次数,不是已用次数
83
+ const remainingCount = modelData.current_interval_usage_count;
84
+ const usedCount = modelData.current_interval_total_count - remainingCount;
85
+
86
+ // Calculate percentage - 基于已使用次数的百分比
87
+ const usedPercentage = Math.round((usedCount / modelData.current_interval_total_count) * 100);
88
+
89
+ // Calculate remaining time in human-readable format
90
+ const remainingMs = modelData.remains_time;
91
+ const hours = Math.floor(remainingMs / (1000 * 60 * 60));
92
+ const minutes = Math.floor((remainingMs % (1000 * 60 * 60)) / (1000 * 60));
93
+
94
+ return {
95
+ modelName: modelData.model_name,
96
+ timeWindow: {
97
+ start: startTime.toLocaleTimeString('zh-CN', {
98
+ hour: '2-digit',
99
+ minute: '2-digit',
100
+ timeZone: 'Asia/Shanghai',
101
+ hour12: false
102
+ }),
103
+ end: endTime.toLocaleTimeString('zh-CN', {
104
+ hour: '2-digit',
105
+ minute: '2-digit',
106
+ timeZone: 'Asia/Shanghai',
107
+ hour12: false
108
+ }),
109
+ timezone: 'UTC+8'
110
+ },
111
+ remaining: {
112
+ hours,
113
+ minutes,
114
+ text: hours > 0 ? `${hours} 小时 ${minutes} 分钟后重置` : `${minutes} 分钟后重置`
115
+ },
116
+ usage: {
117
+ used: remainingCount,
118
+ total: modelData.current_interval_total_count,
119
+ percentage: usedPercentage
120
+ }
121
+ };
122
+ }
123
+ }
124
+
125
+ module.exports = MinimaxAPI;
package/cli/prompts.js ADDED
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+
3
+ const MinimaxAPI = require('./api');
4
+ const chalk = require('chalk').default;
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ class PromptStatus {
9
+ constructor() {
10
+ this.api = new MinimaxAPI();
11
+ }
12
+
13
+ async loadSettings() {
14
+ // Try to load from settings.json in various locations
15
+ const possiblePaths = [
16
+ path.join(process.env.HOME || process.env.USERPROFILE, '.claude', 'settings.json'),
17
+ path.join(process.env.HOME || process.env.USERPROFILE, '.config', 'claude', 'settings.json'),
18
+ path.join(process.cwd(), '.claude-settings.json'),
19
+ path.join(process.cwd(), '.claude', 'settings.json'),
20
+ ];
21
+
22
+ for (const configPath of possiblePaths) {
23
+ if (fs.existsSync(configPath)) {
24
+ try {
25
+ const settings = JSON.parse(fs.readFileSync(configPath, 'utf8'));
26
+ if (settings.minimaxToken && settings.minimaxGroupId) {
27
+ this.api.setCredentials(settings.minimaxToken, settings.minimaxGroupId);
28
+ return settings.minimaxStatus || {};
29
+ }
30
+ } catch (error) {
31
+ console.error(`Failed to read settings from ${configPath}:`, error.message);
32
+ }
33
+ }
34
+ }
35
+
36
+ return {};
37
+ }
38
+
39
+ async getPromptStatus(mode = 'compact') {
40
+ try {
41
+ await this.loadSettings();
42
+ const apiData = await this.api.getUsageStatus();
43
+ const usageData = this.api.parseUsageData(apiData);
44
+
45
+ if (mode === 'compact') {
46
+ return this.renderCompact(usageData);
47
+ } else if (mode === 'minimal') {
48
+ return this.renderMinimal(usageData);
49
+ }
50
+ return null;
51
+ } catch (error) {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ renderMinimal(data) {
57
+ const { usage } = data;
58
+ const percentage = usage.percentage;
59
+
60
+ let color = chalk.green;
61
+ if (percentage >= 85) {
62
+ color = chalk.red;
63
+ } else if (percentage >= 60) {
64
+ color = chalk.yellow;
65
+ }
66
+
67
+ return color(`[MM:${percentage}%]`);
68
+ }
69
+
70
+ renderCompact(data) {
71
+ const { usage, remaining, modelName } = data;
72
+ const percentage = usage.percentage;
73
+
74
+ let color = chalk.green;
75
+ if (percentage >= 85) {
76
+ color = chalk.red;
77
+ } else if (percentage >= 60) {
78
+ color = chalk.yellow;
79
+ }
80
+
81
+ const status = percentage >= 85 ? '⚠' : percentage >= 60 ? '⚡' : '✓';
82
+ const remainingText = remaining.hours > 0
83
+ ? `${remaining.hours}h${remaining.minutes}m`
84
+ : `${remaining.minutes}m`;
85
+
86
+ return `${color('●')} ${modelName} ${color(percentage + '%')} ${remainingText} ${status}`;
87
+ }
88
+ }
89
+
90
+ // CLI usage
91
+ async function main() {
92
+ const args = process.argv.slice(2);
93
+ const mode = args.includes('--minimal') ? 'minimal' : 'compact';
94
+
95
+ const prompt = new PromptStatus();
96
+ const output = await prompt.getPromptStatus(mode);
97
+
98
+ if (output) {
99
+ console.log(output);
100
+ } else {
101
+ // Silent mode - no output if not configured
102
+ }
103
+ }
104
+
105
+ if (require.main === module) {
106
+ main().catch(error => {
107
+ // Silent fail for prompt integration
108
+ process.exit(0);
109
+ });
110
+ }
111
+
112
+ module.exports = PromptStatus;
package/cli/status.js ADDED
@@ -0,0 +1,107 @@
1
+ const chalk = require('chalk').default;
2
+ const dayjs = require('dayjs');
3
+
4
+ class StatusBar {
5
+ constructor(data) {
6
+ this.data = data;
7
+ }
8
+
9
+ render() {
10
+ const { modelName, timeWindow, remaining, usage } = this.data;
11
+
12
+ // Calculate progress bar width
13
+ const width = 30;
14
+ const filled = Math.floor((usage.percentage / 100) * width);
15
+ const empty = width - filled;
16
+
17
+ // Create progress bar with colors based on usage percentage
18
+ const progressBar = this.createProgressBar(filled, empty, usage.percentage);
19
+
20
+ // Format output
21
+ const lines = [
22
+ chalk.bold.blue('┌─────────────────────────────────────────────────────────────┐'),
23
+ `│ ${chalk.bold('MiniMax Claude Code 使用状态')} ${chalk.dim('│')}`,
24
+ `│ ${chalk.dim('│')}`,
25
+ `│ ${chalk.cyan('当前模型:')} ${modelName.padEnd(35)} ${chalk.dim('│')}`,
26
+ `│ ${chalk.cyan('时间窗口:')} ${timeWindow.start}-${timeWindow.end}(${timeWindow.timezone})${' '.repeat(25)} ${chalk.dim('│')}`,
27
+ `│ ${chalk.cyan('剩余时间:')} ${remaining.text}${' '.repeat(30 - remaining.text.length)} ${chalk.dim('│')}`,
28
+ `│ ${chalk.dim('│')}`,
29
+ `│ ${chalk.cyan('已用额度:')} ${progressBar} ${usage.percentage}% ${chalk.dim('│')}`,
30
+ `│ ${chalk.dim(' 剩余:')} ${usage.used}/${usage.total} 次调用`.padEnd(43) + chalk.dim('│'),
31
+ `│ ${chalk.dim('│')}`,
32
+ this.getStatusLine(usage.percentage),
33
+ chalk.bold.blue('└─────────────────────────────────────────────────────────────┘')
34
+ ];
35
+
36
+ return lines.join('\n');
37
+ }
38
+
39
+ createProgressBar(filled, empty, percentage) {
40
+ // filled 是已使用部分,empty 是剩余部分
41
+ const usedBar = '█'.repeat(filled);
42
+ const remainingBar = '░'.repeat(empty);
43
+ const bar = `${usedBar}${remainingBar}`;
44
+
45
+ // 进度条颜色基于已使用百分比:使用越多越危险(红色)
46
+ if (percentage >= 85) {
47
+ return chalk.red(bar);
48
+ } else if (percentage >= 60) {
49
+ return chalk.yellow(bar);
50
+ } else {
51
+ return chalk.green(bar);
52
+ }
53
+ }
54
+
55
+ getStatusLine(percentage) {
56
+ const status = this.getStatus(percentage);
57
+ const leftPadding = '│ ';
58
+ const rightPadding = ' '.repeat(41 - status.length) + '│';
59
+
60
+ let statusColor;
61
+ switch (status) {
62
+ case '✓ 正常':
63
+ statusColor = chalk.green(status);
64
+ break;
65
+ case '⚠ 接近限制':
66
+ statusColor = chalk.yellow(status);
67
+ break;
68
+ case '⚠ 即将到期':
69
+ statusColor = chalk.red(status);
70
+ break;
71
+ default:
72
+ statusColor = chalk.gray(status);
73
+ }
74
+
75
+ return `${leftPadding}${chalk.cyan('状态:')} ${statusColor}${rightPadding}`;
76
+ }
77
+
78
+ getStatus(percentage) {
79
+ // 基于已使用百分比
80
+ if (percentage >= 85) {
81
+ return '⚠ 即将用完';
82
+ } else if (percentage >= 60) {
83
+ return '⚡ 注意使用';
84
+ } else {
85
+ return '✓ 正常使用';
86
+ }
87
+ }
88
+
89
+ renderCompact() {
90
+ const { usage, remaining, modelName } = this.data;
91
+ const status = this.getStatus(usage.percentage);
92
+
93
+ // 颜色基于已使用百分比:使用越多越危险
94
+ let color;
95
+ if (usage.percentage >= 85) {
96
+ color = chalk.red;
97
+ } else if (usage.percentage >= 60) {
98
+ color = chalk.yellow;
99
+ } else {
100
+ color = chalk.green;
101
+ }
102
+
103
+ return `${color('●')} ${modelName} ${usage.percentage}% ${chalk.dim(`(${usage.used}/${usage.total})`)} ${chalk.gray('•')} ${remaining.text} ${chalk.gray('•')} ${status}`;
104
+ }
105
+ }
106
+
107
+ module.exports = StatusBar;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minimax-status",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "MiniMax Claude Code 使用状态监控工具",
5
5
  "bin": {
6
6
  "minimax-status": "./cli/index.js",
@@ -0,0 +1,97 @@
1
+ const axios = require('axios');
2
+
3
+ class MinimaxAPI {
4
+ constructor(context) {
5
+ this.context = context;
6
+ this.token = null;
7
+ this.groupId = null;
8
+ this.loadConfig();
9
+ }
10
+
11
+ loadConfig() {
12
+ const config = vscode.workspace.getConfiguration('minimaxStatus');
13
+ this.token = config.get('token');
14
+ this.groupId = config.get('groupId');
15
+ }
16
+
17
+ async getUsageStatus() {
18
+ if (!this.token || !this.groupId) {
19
+ throw new Error('请在设置中配置 MiniMax 访问令牌和组 ID');
20
+ }
21
+
22
+ try {
23
+ const response = await axios.get(
24
+ `https://www.minimaxi.com/v1/api/openplatform/coding_plan/remains`,
25
+ {
26
+ params: { GroupId: this.groupId },
27
+ headers: {
28
+ 'Authorization': `Bearer ${this.token}`,
29
+ 'Accept': 'application/json'
30
+ }
31
+ }
32
+ );
33
+
34
+ return response.data;
35
+ } catch (error) {
36
+ if (error.response?.status === 401) {
37
+ throw new Error('无效的令牌或未授权。请检查您的凭据。');
38
+ }
39
+ throw new Error(`API 请求失败: ${error.message}`);
40
+ }
41
+ }
42
+
43
+ parseUsageData(apiData) {
44
+ if (!apiData.model_remains || apiData.model_remains.length === 0) {
45
+ throw new Error('没有可用的使用数据');
46
+ }
47
+
48
+ const modelData = apiData.model_remains[0];
49
+ const startTime = new Date(modelData.start_time);
50
+ const endTime = new Date(modelData.end_time);
51
+
52
+ // Calculate used percentage
53
+ const totalTime = modelData.end_time - modelData.start_time;
54
+ const usedTime = totalTime - modelData.remains_time;
55
+ const usedPercentage = Math.round((usedTime / totalTime) * 100);
56
+
57
+ // Calculate remaining time
58
+ const remainingMs = modelData.remains_time;
59
+ const hours = Math.floor(remainingMs / (1000 * 60 * 60));
60
+ const minutes = Math.floor((remainingMs % (1000 * 60 * 60)) / (1000 * 60));
61
+
62
+ return {
63
+ modelName: modelData.model_name,
64
+ timeWindow: {
65
+ start: startTime.toLocaleTimeString('zh-CN', {
66
+ hour: '2-digit',
67
+ minute: '2-digit',
68
+ timeZone: 'Asia/Shanghai',
69
+ hour12: false
70
+ }),
71
+ end: endTime.toLocaleTimeString('zh-CN', {
72
+ hour: '2-digit',
73
+ minute: '2-digit',
74
+ timeZone: 'Asia/Shanghai',
75
+ hour12: false
76
+ }),
77
+ timezone: 'UTC+8'
78
+ },
79
+ remaining: {
80
+ hours,
81
+ minutes,
82
+ text: hours > 0 ? `${hours} 小时 ${minutes} 分钟后重置` : `${minutes} 分钟后重置`
83
+ },
84
+ usage: {
85
+ used: modelData.current_interval_usage_count,
86
+ total: modelData.current_interval_total_count,
87
+ percentage: usedPercentage
88
+ }
89
+ };
90
+ }
91
+
92
+ refreshConfig() {
93
+ this.loadConfig();
94
+ }
95
+ }
96
+
97
+ module.exports = MinimaxAPI;
@@ -0,0 +1,136 @@
1
+ const vscode = require('vscode');
2
+ const MinimaxAPI = require('./api');
3
+
4
+ /**
5
+ * @param {vscode.ExtensionContext} context
6
+ */
7
+ function activate(context) {
8
+ console.log('MiniMax Status 扩展已激活');
9
+
10
+ const api = new MinimaxAPI(context);
11
+ const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
12
+ statusBarItem.command = 'minimaxStatus.refresh';
13
+ statusBarItem.show();
14
+
15
+ let intervalId;
16
+
17
+ const updateStatus = async () => {
18
+ try {
19
+ const apiData = await api.getUsageStatus();
20
+ const usageData = api.parseUsageData(apiData);
21
+ updateStatusBar(statusBarItem, usageData);
22
+ } catch (error) {
23
+ console.error('获取状态失败:', error.message);
24
+ statusBarItem.text = '$(warning) MiniMax';
25
+ statusBarItem.tooltip = `错误: ${error.message}\n点击配置`;
26
+ statusBarItem.color = new vscode.ThemeColor('errorForeground');
27
+ }
28
+ };
29
+
30
+ const config = vscode.workspace.getConfiguration('minimaxStatus');
31
+ const interval = config.get('refreshInterval', 30) * 1000;
32
+
33
+ // Initial update
34
+ updateStatus();
35
+
36
+ // Set up interval
37
+ intervalId = setInterval(updateStatus, interval);
38
+
39
+ // Subscribe to configuration changes
40
+ const configChangeDisposable = vscode.workspace.onDidChangeConfiguration((e) => {
41
+ if (e.affectsConfiguration('minimaxStatus')) {
42
+ api.refreshConfig();
43
+ const newInterval = config.get('refreshInterval', 30) * 1000;
44
+ clearInterval(intervalId);
45
+ intervalId = setInterval(updateStatus, newInterval);
46
+ updateStatus();
47
+ }
48
+ });
49
+
50
+ // Subscribe to refresh command
51
+ const refreshDisposable = vscode.commands.registerCommand('minimaxStatus.refresh', updateStatus);
52
+
53
+ // Subscribe to setup command
54
+ const setupDisposable = vscode.commands.registerCommand('minimaxStatus.setup', async () => {
55
+ const token = await vscode.window.showInputBox({
56
+ prompt: '请输入您的 MiniMax 访问令牌',
57
+ password: true,
58
+ ignoreFocusOut: true
59
+ });
60
+
61
+ if (!token) return;
62
+
63
+ const groupId = await vscode.window.showInputBox({
64
+ prompt: '请输入您的 MiniMax 组 ID',
65
+ ignoreFocusOut: true
66
+ });
67
+
68
+ if (!groupId) return;
69
+
70
+ await config.update('token', token, vscode.ConfigurationTarget.Global);
71
+ await config.update('groupId', groupId, vscode.ConfigurationTarget.Global);
72
+
73
+ vscode.window.showInformationMessage('MiniMax 凭据已保存');
74
+ api.refreshConfig();
75
+ updateStatus();
76
+ });
77
+
78
+ // Add to subscriptions
79
+ context.subscriptions.push(
80
+ statusBarItem,
81
+ configChangeDisposable,
82
+ refreshDisposable,
83
+ setupDisposable
84
+ );
85
+
86
+ // Show setup message if credentials are missing
87
+ if (!api.token || !api.groupId) {
88
+ setTimeout(() => {
89
+ vscode.window.showInformationMessage(
90
+ 'MiniMax Status 扩展需要配置令牌和组 ID',
91
+ '立即配置'
92
+ ).then((selection) => {
93
+ if (selection === '立即配置') {
94
+ vscode.commands.executeCommand('minimaxStatus.setup');
95
+ }
96
+ });
97
+ }, 2000);
98
+ }
99
+ }
100
+
101
+ function updateStatusBar(statusBarItem, data) {
102
+ const { usage, modelName, remaining } = data;
103
+
104
+ // Set status bar text with color
105
+ const percentage = usage.percentage;
106
+ if (percentage < 60) {
107
+ statusBarItem.color = new vscode.ThemeColor('statusBar.foreground');
108
+ } else if (percentage < 85) {
109
+ statusBarItem.color = new vscode.ThemeColor('problemsWarningIcon.foreground');
110
+ } else {
111
+ statusBarItem.color = new vscode.ThemeColor('errorForeground');
112
+ }
113
+
114
+ statusBarItem.text = `$(clock) ${modelName} ${percentage}%`;
115
+
116
+ // Build tooltip
117
+ const tooltip = [
118
+ `模型: ${modelName}`,
119
+ `使用进度: ${usage.percentage}% (${usage.used}/${usage.total})`,
120
+ `剩余时间: ${remaining.text}`,
121
+ `时间窗口: ${data.timeWindow.start}-${data.timeWindow.end}(${data.timeWindow.timezone})`,
122
+ '',
123
+ '点击刷新状态'
124
+ ].join('\n');
125
+
126
+ statusBarItem.tooltip = tooltip;
127
+ }
128
+
129
+ function deactivate() {
130
+ console.log('MiniMax Status 扩展已停用');
131
+ }
132
+
133
+ module.exports = {
134
+ activate,
135
+ deactivate
136
+ };