Files
plan-usage/dist/index.js

213 lines
5.5 KiB
JavaScript

#!/usr/bin/env node
/**
* Plan Usage - GLM & MiniMax Coding Plan Statusline
* Shows real-time usage for Claude Code
*
* Based on glm-plan-usage by jukanntenn
* Modified to support both GLM and MiniMax
*
* Usage:
* plan-usage # Show statusline
* plan-usage --json # Show JSON output
* plan-usage --glm # Show only GLM
* plan-usage --minimax # Show only MiniMax
*/
const https = require('https');
// Colors
const colors = {
reset: '\x1b[0m',
green: '\x1b[32m',
yellow: '\x1b[33m',
red: '\x1b[31m',
blue: '\x1b[36m',
orange: '\x1b[38;5;208m',
purple: '\x1b[35m'
};
// API Configuration
const CONFIG = {
glm: {
apiKey: process.env.GLM_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN,
baseUrl: 'api.z.ai',
endpoint: '/api/monitor/usage/quota/limit'
},
minimax: {
apiKey: process.env.MINIMAX_API_KEY || 'sk-cp-XC8cbgbVBuv1g8mMcao0ABeZu_rGEN_S22EhBUqo4lJbY_UJVqUVO5XF8hVobp8gE_39JbgQggr00TQwNdV9vP458Y_MBC_8GstvzmwhuukEGY4a2I5_L6A',
baseUrl: 'api.minimax.io',
endpoint: '/v1/api/openplatform/coding_plan/remains'
}
};
// Make API request
function makeRequest(options) {
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
resolve(JSON.parse(data));
} catch (e) {
reject(e);
}
});
});
req.on('error', reject);
req.setTimeout(5000, () => { req.destroy(); reject(new Error('Timeout')); });
req.end();
});
}
// Fetch GLM usage
async function getGlmUsage() {
if (!CONFIG.glm.apiKey) {
return { error: 'No GLM API key configured' };
}
try {
const response = await makeRequest({
hostname: CONFIG.glm.baseUrl,
path: CONFIG.glm.endpoint,
method: 'GET',
headers: {
'Authorization': `Bearer ${CONFIG.glm.apiKey}`,
'Content-Type': 'application/json'
}
});
const limits = response?.data?.limits || [];
let tokenPct = 0;
let timeRemaining = 0;
for (const l of limits) {
if (l.quota_type === 'TOKENS_LIMIT') tokenPct = l.percentage || 0;
if (l.quota_type === 'TIME_LIMIT') timeRemaining = (l.remaining || 0) / 1000 / 60;
}
return { tokenPct, timeRemaining, error: null };
} catch (e) {
return { tokenPct: 0, timeRemaining: 0, error: e.message };
}
}
// Fetch MiniMax usage
async function getMinimaxUsage() {
if (!CONFIG.minimax.apiKey) {
return { error: 'No MiniMax API key configured' };
}
try {
const response = await makeRequest({
hostname: CONFIG.minimax.baseUrl,
path: CONFIG.minimax.endpoint,
method: 'GET',
headers: {
'Authorization': `Bearer ${CONFIG.minimax.apiKey}`,
'Content-Type': 'application/json'
}
});
const m = response?.model_remains?.[0];
if (!m) {
return { pct: 0, remainingMinutes: 0, model: '?', error: 'No data' };
}
const remainingMinutes = m.remains_time / 1000 / 60;
const used = m.current_interval_usage_count;
const total = m.current_interval_total_count;
const pct = (used / total) * 100;
return { pct, remainingMinutes, model: m.model_name, error: null };
} catch (e) {
return { pct: 0, remainingMinutes: 0, model: '?', error: e.message };
}
}
// Get color based on percentage
function getColor(pct) {
if (pct > 90) return colors.red;
if (pct > 70) return colors.yellow;
return colors.green;
}
// Format time
function formatTime(minutes) {
if (minutes < 60) return `${minutes.toFixed(0)}m`;
return `${(minutes/60).toFixed(1)}h`;
}
// Format GLM status
function formatGlmStatus(glm) {
if (glm.error) {
return `${colors.blue}GLM${colors.reset} ${colors.red}offline${colors.reset}`;
}
let status = `${colors.blue}GLM${colors.reset} ${getColor(glm.tokenPct)}${glm.tokenPct.toFixed(0)}%${colors.reset}`;
if (glm.timeRemaining > 0) {
status += ` ${formatTime(glm.timeRemaining)}`;
}
return status;
}
// Format MiniMax status
function formatMinimaxStatus(minimax) {
if (minimax.error) {
return `${colors.purple}MiniMax${colors.reset} ${colors.red}offline${colors.reset}`;
}
let status = `${colors.purple}MiniMax${colors.reset} ${getColor(minimax.pct)}${minimax.pct.toFixed(0)}%${colors.reset}`;
if (minimax.remainingMinutes > 0) {
status += ` ${formatTime(minimax.remainingMinutes)}`;
}
return status;
}
// Main function
async function main() {
const args = process.argv.slice(2);
const showJson = args.includes('--json');
const showOnlyGlm = args.includes('--glm');
const showOnlyMinimax = args.includes('--minimax');
try {
const [glm, minimax] = await Promise.all([
showOnlyMinimax ? Promise.resolve({ error: 'disabled' }) : getGlmUsage(),
showOnlyGlm ? Promise.resolve({ error: 'disabled' }) : getMinimaxUsage()
]);
if (showJson) {
console.log(JSON.stringify({ glm, minimax }, null, 2));
return;
}
// Statusline output
let parts = [];
if (!showOnlyMinimax && !glm.error) {
parts.push(formatGlmStatus(glm));
}
if (!showOnlyGlm && !minimax.error) {
parts.push(formatMinimaxStatus(minimax));
}
if (parts.length === 0) {
console.log(`${colors.red}No API configured${colors.reset}`);
return;
}
console.log(parts.join(' | '));
} catch (e) {
if (showJson) {
console.log(JSON.stringify({ error: e.message }, null, 2));
} else {
console.log(`${colors.red}Error: ${e.message}${colors.reset}`);
}
}
}
main();