@plexor-dev/claude-code-plugin 0.1.0-beta.7 → 0.1.0-beta.9
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/commands/plexor-status.js +192 -0
- package/commands/plexor-status.md +12 -86
- package/package.json +1 -1
- package/lib/constants.js +0 -40
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plexor Status Command
|
|
5
|
+
* Displays formatted status with usage statistics and ANSI colors
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const https = require('https');
|
|
11
|
+
|
|
12
|
+
const CONFIG_PATH = path.join(process.env.HOME, '.plexor', 'config.json');
|
|
13
|
+
|
|
14
|
+
// ANSI color codes
|
|
15
|
+
const c = {
|
|
16
|
+
reset: '\x1b[0m',
|
|
17
|
+
bold: '\x1b[1m',
|
|
18
|
+
dim: '\x1b[2m',
|
|
19
|
+
green: '\x1b[32m',
|
|
20
|
+
yellow: '\x1b[33m',
|
|
21
|
+
blue: '\x1b[34m',
|
|
22
|
+
magenta: '\x1b[35m',
|
|
23
|
+
cyan: '\x1b[36m',
|
|
24
|
+
white: '\x1b[37m',
|
|
25
|
+
gray: '\x1b[90m',
|
|
26
|
+
bgBlue: '\x1b[44m',
|
|
27
|
+
bgGreen: '\x1b[42m',
|
|
28
|
+
bgYellow: '\x1b[43m',
|
|
29
|
+
bgGray: '\x1b[100m',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
async function main() {
|
|
33
|
+
// Read config
|
|
34
|
+
let config;
|
|
35
|
+
try {
|
|
36
|
+
const data = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
37
|
+
config = JSON.parse(data);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.log(`${c.yellow}Not configured.${c.reset} Run ${c.cyan}/plexor-login${c.reset} first.`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const apiKey = config.auth?.api_key;
|
|
44
|
+
const enabled = config.settings?.enabled ?? false;
|
|
45
|
+
const mode = config.settings?.mode || 'balanced';
|
|
46
|
+
const provider = config.settings?.preferred_provider || 'auto';
|
|
47
|
+
const localCache = config.settings?.localCacheEnabled ?? false;
|
|
48
|
+
const apiUrl = config.settings?.apiUrl || 'https://api.plexor.dev';
|
|
49
|
+
|
|
50
|
+
if (!apiKey) {
|
|
51
|
+
console.log(`${c.yellow}Not authenticated.${c.reset} Run ${c.cyan}/plexor-login${c.reset} first.`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Fetch user info and stats
|
|
56
|
+
let user = { email: 'Unknown', tier: { name: 'Free', limits: {} } };
|
|
57
|
+
let stats = { period: {}, summary: {} };
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
[user, stats] = await Promise.all([
|
|
61
|
+
fetchJson(apiUrl, '/v1/user', apiKey),
|
|
62
|
+
fetchJson(apiUrl, '/v1/stats', apiKey)
|
|
63
|
+
]);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
// Continue with defaults if API fails
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Extract data
|
|
69
|
+
const email = user.email || 'Unknown';
|
|
70
|
+
const tierName = (user.tier?.name || 'Free').charAt(0).toUpperCase() + (user.tier?.name || 'free').slice(1);
|
|
71
|
+
const monthlyOpts = user.tier?.limits?.monthly_optimizations || 10000;
|
|
72
|
+
const monthlyComps = user.tier?.limits?.monthly_completions || 100000;
|
|
73
|
+
|
|
74
|
+
const period = stats.period || {};
|
|
75
|
+
const summary = stats.summary || {};
|
|
76
|
+
|
|
77
|
+
const formatDate = (iso) => {
|
|
78
|
+
if (!iso) return '?';
|
|
79
|
+
const d = new Date(iso);
|
|
80
|
+
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
81
|
+
};
|
|
82
|
+
const weekRange = `${formatDate(period.start)} - ${formatDate(period.end)}`;
|
|
83
|
+
|
|
84
|
+
// Format numbers
|
|
85
|
+
const formatNum = (n) => (n || 0).toLocaleString();
|
|
86
|
+
const formatPct = (n) => (n || 0).toFixed(1);
|
|
87
|
+
const formatCost = (n) => (n || 0).toFixed(2);
|
|
88
|
+
|
|
89
|
+
// Calculate usage percentages for progress bars
|
|
90
|
+
const optsUsed = summary.total_optimizations || 0;
|
|
91
|
+
const compsUsed = summary.total_completions || 0;
|
|
92
|
+
const optsPct = Math.min((optsUsed / monthlyOpts) * 100, 100);
|
|
93
|
+
const compsPct = Math.min((compsUsed / monthlyComps) * 100, 100);
|
|
94
|
+
|
|
95
|
+
// Progress bar renderer
|
|
96
|
+
const progressBar = (pct, width = 30) => {
|
|
97
|
+
const filled = Math.round((pct / 100) * width);
|
|
98
|
+
const empty = width - filled;
|
|
99
|
+
const color = pct < 50 ? c.bgBlue : pct < 80 ? c.bgYellow : c.bgGreen;
|
|
100
|
+
return `${color}${' '.repeat(filled)}${c.reset}${c.bgGray}${' '.repeat(empty)}${c.reset}`;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const status = enabled ? `${c.green}● Active${c.reset}` : `${c.gray}○ Inactive${c.reset}`;
|
|
104
|
+
const optStatus = enabled ? `${c.green}Enabled${c.reset}` : `${c.gray}Disabled${c.reset}`;
|
|
105
|
+
const cacheStatus = localCache ? `${c.green}Enabled${c.reset}` : `${c.gray}Disabled${c.reset}`;
|
|
106
|
+
const cacheRate = formatPct((summary.cache_hit_rate || 0) * 100);
|
|
107
|
+
|
|
108
|
+
// Build dashboard URL
|
|
109
|
+
const dashboardUrl = apiUrl.replace('.api.', '.').replace('/api', '') + '/dashboard.html';
|
|
110
|
+
|
|
111
|
+
// Savings highlight
|
|
112
|
+
const savingsPct = summary.cost_saved_percent || 0;
|
|
113
|
+
const savingsColor = savingsPct > 50 ? c.green : savingsPct > 20 ? c.yellow : c.white;
|
|
114
|
+
|
|
115
|
+
console.log(`
|
|
116
|
+
${c.bold}${c.cyan}Plexor Status${c.reset}
|
|
117
|
+
|
|
118
|
+
${c.bold}Account${c.reset}
|
|
119
|
+
${c.gray}Tier:${c.reset} ${c.cyan}${tierName}${c.reset}
|
|
120
|
+
${c.gray}Email:${c.reset} ${email}
|
|
121
|
+
${c.gray}Status:${c.reset} ${status}
|
|
122
|
+
|
|
123
|
+
${c.bold}This Week${c.reset} ${c.dim}(${weekRange})${c.reset}
|
|
124
|
+
${c.gray}Requests:${c.reset} ${formatNum(summary.total_requests)}
|
|
125
|
+
${c.gray}Original tokens:${c.reset} ${formatNum(summary.original_tokens)}
|
|
126
|
+
${c.gray}Optimized tokens:${c.reset} ${formatNum(summary.optimized_tokens)}
|
|
127
|
+
${c.gray}Tokens saved:${c.reset} ${c.green}${formatNum(summary.tokens_saved)}${c.reset} ${c.dim}(${formatPct(summary.tokens_saved_percent)}%)${c.reset}
|
|
128
|
+
${c.gray}Baseline cost:${c.reset} $${formatCost(summary.baseline_cost)}
|
|
129
|
+
${c.gray}Actual cost:${c.reset} $${formatCost(summary.total_cost)}
|
|
130
|
+
${c.gray}Cost saved:${c.reset} ${savingsColor}$${formatCost(summary.cost_saved)}${c.reset} ${c.dim}(${formatPct(savingsPct)}%)${c.reset}
|
|
131
|
+
|
|
132
|
+
${c.bold}Usage Limits${c.reset}
|
|
133
|
+
${c.gray}Optimizations${c.reset}
|
|
134
|
+
${progressBar(optsPct)} ${formatPct(optsPct)}% used
|
|
135
|
+
${c.dim}${formatNum(optsUsed)} / ${formatNum(monthlyOpts)} this month${c.reset}
|
|
136
|
+
|
|
137
|
+
${c.gray}Completions${c.reset}
|
|
138
|
+
${progressBar(compsPct)} ${formatPct(compsPct)}% used
|
|
139
|
+
${c.dim}${formatNum(compsUsed)} / ${formatNum(monthlyComps)} this month${c.reset}
|
|
140
|
+
|
|
141
|
+
${c.bold}Performance${c.reset}
|
|
142
|
+
${c.gray}Cache hit rate:${c.reset} ${cacheRate}%
|
|
143
|
+
|
|
144
|
+
${c.bold}Settings${c.reset}
|
|
145
|
+
${c.gray}Optimization:${c.reset} ${optStatus}
|
|
146
|
+
${c.gray}Local cache:${c.reset} ${cacheStatus}
|
|
147
|
+
${c.gray}Mode:${c.reset} ${c.cyan}${mode}${c.reset}
|
|
148
|
+
${c.gray}Provider routing:${c.reset} ${c.cyan}${provider}${c.reset}
|
|
149
|
+
|
|
150
|
+
${c.dim}Dashboard:${c.reset} ${c.blue}${dashboardUrl}${c.reset}
|
|
151
|
+
`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function fetchJson(apiUrl, endpoint, apiKey) {
|
|
155
|
+
return new Promise((resolve, reject) => {
|
|
156
|
+
const url = new URL(`${apiUrl}${endpoint}`);
|
|
157
|
+
|
|
158
|
+
const options = {
|
|
159
|
+
hostname: url.hostname,
|
|
160
|
+
port: 443,
|
|
161
|
+
path: url.pathname,
|
|
162
|
+
method: 'GET',
|
|
163
|
+
headers: {
|
|
164
|
+
'X-Plexor-Key': apiKey
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const req = https.request(options, (res) => {
|
|
169
|
+
let data = '';
|
|
170
|
+
res.on('data', chunk => data += chunk);
|
|
171
|
+
res.on('end', () => {
|
|
172
|
+
try {
|
|
173
|
+
resolve(JSON.parse(data));
|
|
174
|
+
} catch {
|
|
175
|
+
reject(new Error('Invalid response'));
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
req.on('error', reject);
|
|
181
|
+
req.setTimeout(5000, () => {
|
|
182
|
+
req.destroy();
|
|
183
|
+
reject(new Error('Timeout'));
|
|
184
|
+
});
|
|
185
|
+
req.end();
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
main().catch(err => {
|
|
190
|
+
console.error(`${c.red}Error:${c.reset} ${err.message}`);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
});
|
|
@@ -4,95 +4,21 @@ description: Show Plexor optimization statistics and savings (user)
|
|
|
4
4
|
|
|
5
5
|
# Plexor Status
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Display Plexor optimization statistics with colors and progress bars.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Instructions
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Run the status script and show its output:
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
**Step 2: Parse configuration and check authentication**
|
|
16
|
-
|
|
17
|
-
The config file contains:
|
|
18
|
-
- `enabled`: boolean - whether Plexor proxy is enabled
|
|
19
|
-
- `apiUrl`: string - the API URL (e.g., "https://api.plexor.dev")
|
|
20
|
-
- `apiKey`: string - the user's API key (starts with "plx_")
|
|
21
|
-
- `mode`: string - optimization mode ("eco", "balanced", "quality", "passthrough")
|
|
22
|
-
- `preferredProvider`: string - provider preference ("auto", "claude", "openai", etc.)
|
|
23
|
-
- `localCacheEnabled`: boolean - whether local caching is enabled
|
|
24
|
-
|
|
25
|
-
If `apiKey` is missing or empty, tell the user to run `/plexor-login` first.
|
|
26
|
-
|
|
27
|
-
**Step 3: Call the APIs**
|
|
28
|
-
|
|
29
|
-
First, get user info:
|
|
30
|
-
```
|
|
31
|
-
GET {apiUrl}/v1/user
|
|
32
|
-
X-Plexor-Key: {apiKey}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Then, get usage statistics:
|
|
36
|
-
```
|
|
37
|
-
GET {apiUrl}/v1/stats
|
|
38
|
-
X-Plexor-Key: {apiKey}
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
The stats response contains:
|
|
42
|
-
- `summary.total_requests`: number of requests
|
|
43
|
-
- `summary.original_tokens`: tokens before optimization
|
|
44
|
-
- `summary.optimized_tokens`: tokens after optimization
|
|
45
|
-
- `summary.tokens_saved`: tokens saved
|
|
46
|
-
- `summary.tokens_saved_percent`: percentage saved
|
|
47
|
-
- `summary.baseline_cost`: cost without optimization
|
|
48
|
-
- `summary.total_cost`: actual cost
|
|
49
|
-
- `summary.cost_saved`: money saved
|
|
50
|
-
- `summary.cost_saved_percent`: percentage saved
|
|
51
|
-
- `summary.cache_hit_rate`: cache hit rate
|
|
52
|
-
- `period.start` / `period.end`: date range
|
|
53
|
-
|
|
54
|
-
**Step 4: Display the status**
|
|
55
|
-
|
|
56
|
-
Show the user a formatted status display:
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
┌─────────────────────────────────────────────┐
|
|
60
|
-
│ Plexor Status │
|
|
61
|
-
├─────────────────────────────────────────────┤
|
|
62
|
-
│ Account: [tier from /v1/user] │
|
|
63
|
-
│ Email: [email from /v1/user] │
|
|
64
|
-
│ Status: ● Active │
|
|
65
|
-
├─────────────────────────────────────────────┤
|
|
66
|
-
│ This Week ([period.start] - [period.end]) │
|
|
67
|
-
│ ├── Requests: [total_requests] │
|
|
68
|
-
│ ├── Original tokens: [original_tokens] │
|
|
69
|
-
│ ├── Optimized tokens: [optimized_tokens] │
|
|
70
|
-
│ ├── Tokens saved: [tokens_saved] ([tokens_saved_percent]%) │
|
|
71
|
-
│ ├── Baseline cost: $[baseline_cost] │
|
|
72
|
-
│ ├── Actual cost: $[total_cost] │
|
|
73
|
-
│ └── Cost saved: $[cost_saved] ([cost_saved_percent]%) │
|
|
74
|
-
├─────────────────────────────────────────────┤
|
|
75
|
-
│ Performance │
|
|
76
|
-
│ └── Cache hit rate: [cache_hit_rate]% │
|
|
77
|
-
├─────────────────────────────────────────────┤
|
|
78
|
-
│ Limits │
|
|
79
|
-
│ ├── Monthly optimizations: [from tier] │
|
|
80
|
-
│ └── Monthly completions: [from tier] │
|
|
81
|
-
├─────────────────────────────────────────────┤
|
|
82
|
-
│ Settings │
|
|
83
|
-
│ ├── Optimization: [Enabled/Disabled] │
|
|
84
|
-
│ ├── Local cache: [Enabled/Disabled] │
|
|
85
|
-
│ ├── Mode: [mode] │
|
|
86
|
-
│ └── Provider routing: [preferredProvider] │
|
|
87
|
-
└─────────────────────────────────────────────┘
|
|
88
|
-
|
|
89
|
-
Dashboard: [apiUrl]/dashboard
|
|
13
|
+
```bash
|
|
14
|
+
node ~/.claude/plugins/plexor/commands/plexor-status.js
|
|
90
15
|
```
|
|
91
16
|
|
|
92
|
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
17
|
+
The script outputs colored terminal UI with:
|
|
18
|
+
- Account info and status
|
|
19
|
+
- Weekly usage statistics
|
|
20
|
+
- Progress bars for usage limits
|
|
21
|
+
- Cost savings breakdown
|
|
22
|
+
- Current settings
|
|
97
23
|
|
|
98
|
-
|
|
24
|
+
Do not add commentary - the script output is the complete response.
|
package/package.json
CHANGED
package/lib/constants.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plexor Claude Code Plugin - Constants
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const os = require('os');
|
|
7
|
-
|
|
8
|
-
module.exports = {
|
|
9
|
-
// API endpoints
|
|
10
|
-
PLEXOR_API_URL: process.env.PLEXOR_API_URL || 'https://api.plexor.dev',
|
|
11
|
-
PLEXOR_GATEWAY_URL: process.env.PLEXOR_GATEWAY_URL || 'https://api.plexor.dev/v1',
|
|
12
|
-
PLEXOR_AUTH_URL: 'https://plexor.dev/auth/device',
|
|
13
|
-
|
|
14
|
-
// File paths
|
|
15
|
-
PLEXOR_CONFIG_DIR: process.env.PLEXOR_CONFIG_DIR || path.join(os.homedir(), '.plexor'),
|
|
16
|
-
PLEXOR_CONFIG_FILE: path.join(
|
|
17
|
-
process.env.PLEXOR_CONFIG_DIR || path.join(os.homedir(), '.plexor'),
|
|
18
|
-
'config.json'
|
|
19
|
-
),
|
|
20
|
-
CLAUDE_COMMANDS_DIR: path.join(os.homedir(), '.claude', 'commands'),
|
|
21
|
-
|
|
22
|
-
// Config schema version
|
|
23
|
-
CONFIG_VERSION: 1,
|
|
24
|
-
|
|
25
|
-
// Default settings
|
|
26
|
-
DEFAULTS: {
|
|
27
|
-
enabled: true,
|
|
28
|
-
preferred_provider: 'auto',
|
|
29
|
-
telemetry: true,
|
|
30
|
-
local_cache: false
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
// API key prefix for identification
|
|
34
|
-
API_KEY_PREFIX: 'plx_',
|
|
35
|
-
|
|
36
|
-
// Timeouts (ms)
|
|
37
|
-
DEVICE_CODE_POLL_INTERVAL: 5000,
|
|
38
|
-
DEVICE_CODE_TIMEOUT: 900000, // 15 minutes
|
|
39
|
-
API_TIMEOUT: 30000
|
|
40
|
-
};
|