@plexor-dev/claude-code-plugin 0.1.0-beta.6 → 0.1.0-beta.8
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 +155 -0
- package/commands/plexor-status.md +5 -60
- package/package.json +1 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plexor Status Command
|
|
5
|
+
* Displays formatted status with usage statistics
|
|
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
|
+
async function main() {
|
|
15
|
+
// Read config
|
|
16
|
+
let config;
|
|
17
|
+
try {
|
|
18
|
+
const data = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
19
|
+
config = JSON.parse(data);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.log('Not configured. Run /plexor-login first.');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const apiKey = config.auth?.api_key;
|
|
26
|
+
const enabled = config.settings?.enabled ?? false;
|
|
27
|
+
const mode = config.settings?.mode || 'balanced';
|
|
28
|
+
const provider = config.settings?.preferred_provider || 'auto';
|
|
29
|
+
const localCache = config.settings?.localCacheEnabled ?? false;
|
|
30
|
+
const apiUrl = config.settings?.apiUrl || 'https://api.plexor.dev';
|
|
31
|
+
|
|
32
|
+
if (!apiKey) {
|
|
33
|
+
console.log('Not authenticated. Run /plexor-login first.');
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Fetch user info and stats
|
|
38
|
+
let user = { email: 'Unknown', tier: { name: 'Free', limits: {} } };
|
|
39
|
+
let stats = { period: {}, summary: {} };
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
[user, stats] = await Promise.all([
|
|
43
|
+
fetchJson(apiUrl, '/v1/user', apiKey),
|
|
44
|
+
fetchJson(apiUrl, '/v1/stats', apiKey)
|
|
45
|
+
]);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
// Continue with defaults if API fails
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Extract data
|
|
51
|
+
const email = user.email || 'Unknown';
|
|
52
|
+
const tierName = user.tier?.name || 'Free';
|
|
53
|
+
const monthlyOpts = user.tier?.limits?.monthly_optimizations || '∞';
|
|
54
|
+
const monthlyComps = user.tier?.limits?.monthly_completions || '∞';
|
|
55
|
+
|
|
56
|
+
const period = stats.period || {};
|
|
57
|
+
const summary = stats.summary || {};
|
|
58
|
+
|
|
59
|
+
const formatDate = (iso) => {
|
|
60
|
+
if (!iso) return '?';
|
|
61
|
+
const d = new Date(iso);
|
|
62
|
+
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
63
|
+
};
|
|
64
|
+
const weekRange = `${formatDate(period.start)} - ${formatDate(period.end)}`;
|
|
65
|
+
|
|
66
|
+
// Format numbers
|
|
67
|
+
const formatNum = (n) => (n || 0).toLocaleString();
|
|
68
|
+
const formatPct = (n) => (n || 0).toFixed(1);
|
|
69
|
+
const formatCost = (n) => (n || 0).toFixed(2);
|
|
70
|
+
|
|
71
|
+
const status = enabled ? '● Active' : '○ Inactive';
|
|
72
|
+
const optEnabled = enabled ? 'Enabled' : 'Disabled';
|
|
73
|
+
const cacheEnabled = localCache ? 'Enabled' : 'Disabled';
|
|
74
|
+
const cacheRate = formatPct((summary.cache_hit_rate || 0) * 100);
|
|
75
|
+
|
|
76
|
+
// Build dashboard URL
|
|
77
|
+
const dashboardUrl = apiUrl.replace('.api.', '.').replace('/api', '') + '/dashboard.html';
|
|
78
|
+
|
|
79
|
+
// Output formatted status - each line is exactly 43 chars inner width
|
|
80
|
+
const line = (content) => ` │ ${content.padEnd(43)}│`;
|
|
81
|
+
|
|
82
|
+
console.log(`
|
|
83
|
+
┌─────────────────────────────────────────────┐
|
|
84
|
+
${line('Plexor Status')}
|
|
85
|
+
├─────────────────────────────────────────────┤
|
|
86
|
+
${line(`Account: ${tierName}`)}
|
|
87
|
+
${line(`Email: ${email}`)}
|
|
88
|
+
${line(`Status: ${status}`)}
|
|
89
|
+
├─────────────────────────────────────────────┤
|
|
90
|
+
${line(`This Week (${weekRange})`)}
|
|
91
|
+
${line(`├── Requests: ${formatNum(summary.total_requests)}`)}
|
|
92
|
+
${line(`├── Original tokens: ${formatNum(summary.original_tokens)}`)}
|
|
93
|
+
${line(`├── Optimized tokens: ${formatNum(summary.optimized_tokens)}`)}
|
|
94
|
+
${line(`├── Tokens saved: ${formatNum(summary.tokens_saved)} (${formatPct(summary.tokens_saved_percent)}%)`)}
|
|
95
|
+
${line(`├── Baseline cost: $${formatCost(summary.baseline_cost)}`)}
|
|
96
|
+
${line(`├── Actual cost: $${formatCost(summary.total_cost)}`)}
|
|
97
|
+
${line(`└── Cost saved: $${formatCost(summary.cost_saved)} (${formatPct(summary.cost_saved_percent)}%)`)}
|
|
98
|
+
├─────────────────────────────────────────────┤
|
|
99
|
+
${line('Performance')}
|
|
100
|
+
${line(`└── Cache hit rate: ${cacheRate}%`)}
|
|
101
|
+
├─────────────────────────────────────────────┤
|
|
102
|
+
${line('Limits')}
|
|
103
|
+
${line(`├── Monthly optimizations: ${formatNum(monthlyOpts)}`)}
|
|
104
|
+
${line(`└── Monthly completions: ${formatNum(monthlyComps)}`)}
|
|
105
|
+
├─────────────────────────────────────────────┤
|
|
106
|
+
${line('Settings')}
|
|
107
|
+
${line(`├── Optimization: ${optEnabled}`)}
|
|
108
|
+
${line(`├── Local cache: ${cacheEnabled}`)}
|
|
109
|
+
${line(`├── Mode: ${mode}`)}
|
|
110
|
+
${line(`└── Provider routing: ${provider}`)}
|
|
111
|
+
└─────────────────────────────────────────────┘
|
|
112
|
+
|
|
113
|
+
Dashboard: ${dashboardUrl}
|
|
114
|
+
`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function fetchJson(apiUrl, endpoint, apiKey) {
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
const url = new URL(`${apiUrl}${endpoint}`);
|
|
120
|
+
|
|
121
|
+
const options = {
|
|
122
|
+
hostname: url.hostname,
|
|
123
|
+
port: 443,
|
|
124
|
+
path: url.pathname,
|
|
125
|
+
method: 'GET',
|
|
126
|
+
headers: {
|
|
127
|
+
'X-Plexor-Key': apiKey
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const req = https.request(options, (res) => {
|
|
132
|
+
let data = '';
|
|
133
|
+
res.on('data', chunk => data += chunk);
|
|
134
|
+
res.on('end', () => {
|
|
135
|
+
try {
|
|
136
|
+
resolve(JSON.parse(data));
|
|
137
|
+
} catch {
|
|
138
|
+
reject(new Error('Invalid response'));
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
req.on('error', reject);
|
|
144
|
+
req.setTimeout(5000, () => {
|
|
145
|
+
req.destroy();
|
|
146
|
+
reject(new Error('Timeout'));
|
|
147
|
+
});
|
|
148
|
+
req.end();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
main().catch(err => {
|
|
153
|
+
console.error('Error:', err.message);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
});
|
|
@@ -6,67 +6,12 @@ description: Show Plexor optimization statistics and savings (user)
|
|
|
6
6
|
|
|
7
7
|
Show current Plexor proxy status, configuration, and usage statistics.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Instructions
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Use the Read tool to read the file `~/.plexor/config.json`. If the file doesn't exist, tell the user to run `/plexor-login` first.
|
|
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 stats API**
|
|
28
|
-
|
|
29
|
-
Make a request to get usage statistics:
|
|
30
|
-
```
|
|
31
|
-
GET {apiUrl}/api/users/me/usage
|
|
32
|
-
X-Plexor-Key: {apiKey}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Note: Use the X-Plexor-Key header with the API key (not Authorization Bearer).
|
|
36
|
-
|
|
37
|
-
**Step 4: Display the status**
|
|
38
|
-
|
|
39
|
-
Show the user a formatted status display using this box-style format. Calculate weekly date range (Monday to Sunday of current week):
|
|
11
|
+
Run the plexor-status script and display its output directly to the user:
|
|
40
12
|
|
|
13
|
+
```bash
|
|
14
|
+
node ~/.claude/plugins/plexor/commands/plexor-status.js
|
|
41
15
|
```
|
|
42
|
-
● ┌─────────────────────────────────────────────┐
|
|
43
|
-
│ Plexor Status │
|
|
44
|
-
├─────────────────────────────────────────────┤
|
|
45
|
-
│ Account: [tier, e.g., "Pro" or "beta"] │
|
|
46
|
-
│ Email: [email from config] │
|
|
47
|
-
│ Status: ● Active │
|
|
48
|
-
├─────────────────────────────────────────────┤
|
|
49
|
-
│ This Week ([start date] - [end date]) │
|
|
50
|
-
│ ├── Requests: [totalRequests] │
|
|
51
|
-
│ ├── Tokens saved: [tokensUsed] ([optimizationPercent]%) │
|
|
52
|
-
│ ├── Avg latency: [avgLatency]ms │
|
|
53
|
-
│ └── Savings: $[costSavings] │
|
|
54
|
-
├─────────────────────────────────────────────┤
|
|
55
|
-
│ Settings │
|
|
56
|
-
│ ├── Optimization: [Enabled/Disabled] │
|
|
57
|
-
│ ├── Local cache: [Enabled/Disabled] │
|
|
58
|
-
│ ├── Mode: [mode] │
|
|
59
|
-
│ └── Provider routing: [preferredProvider] │
|
|
60
|
-
└─────────────────────────────────────────────┘
|
|
61
|
-
|
|
62
|
-
Dashboard: [apiUrl base]/dashboard.html
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Notes:
|
|
66
|
-
- Use "● Active" (green dot) if enabled, "○ Inactive" if disabled
|
|
67
|
-
- Format token counts with commas (e.g., 46,700)
|
|
68
|
-
- Format costs with 2 decimal places (e.g., $8.02)
|
|
69
|
-
- Calculate current week dates dynamically
|
|
70
|
-
- Use the apiUrl from config to construct the dashboard link (replace /api with empty string)
|
|
71
16
|
|
|
72
|
-
|
|
17
|
+
Do not add any additional commentary - just show the script output.
|
package/package.json
CHANGED