@plexor-dev/claude-code-plugin-staging 0.1.0-beta.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/LICENSE +21 -0
- package/README.md +117 -0
- package/commands/plexor-config.js +170 -0
- package/commands/plexor-config.md +28 -0
- package/commands/plexor-enabled.js +122 -0
- package/commands/plexor-enabled.md +28 -0
- package/commands/plexor-login.js +189 -0
- package/commands/plexor-login.md +27 -0
- package/commands/plexor-logout.js +92 -0
- package/commands/plexor-logout.md +27 -0
- package/commands/plexor-mode.js +107 -0
- package/commands/plexor-mode.md +27 -0
- package/commands/plexor-provider.js +110 -0
- package/commands/plexor-provider.md +27 -0
- package/commands/plexor-settings.js +155 -0
- package/commands/plexor-settings.md +28 -0
- package/commands/plexor-setup.md +172 -0
- package/commands/plexor-status.js +240 -0
- package/commands/plexor-status.md +21 -0
- package/hooks/intercept.js +634 -0
- package/hooks/track-response.js +376 -0
- package/lib/cache.js +107 -0
- package/lib/config.js +67 -0
- package/lib/constants.js +26 -0
- package/lib/index.js +19 -0
- package/lib/logger.js +36 -0
- package/lib/plexor-client.js +122 -0
- package/lib/server-sync.js +237 -0
- package/lib/session.js +156 -0
- package/lib/settings-manager.js +239 -0
- package/package.json +60 -0
- package/scripts/plexor-cli.sh +48 -0
- package/scripts/postinstall.js +251 -0
- package/scripts/uninstall.js +67 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: First-time setup wizard for Plexor with Claude Code (user)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Plexor Setup Wizard
|
|
6
|
+
|
|
7
|
+
Guide users through first-time Plexor setup. **No manual environment variable configuration required!**
|
|
8
|
+
|
|
9
|
+
The plugin automatically configures `~/.claude/settings.json` to route all Claude Code sessions through Plexor.
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
**Step 1: Check if already configured**
|
|
14
|
+
|
|
15
|
+
Use the Read tool to check if `~/.plexor/config.json` exists and has valid configuration.
|
|
16
|
+
Also check `~/.claude/settings.json` for routing status.
|
|
17
|
+
|
|
18
|
+
If configured, show:
|
|
19
|
+
```
|
|
20
|
+
Plexor Setup
|
|
21
|
+
============
|
|
22
|
+
Already configured!
|
|
23
|
+
|
|
24
|
+
API URL: [apiUrl from config]
|
|
25
|
+
Mode: [mode from config]
|
|
26
|
+
Status: [Enabled/Disabled]
|
|
27
|
+
Claude Routing: [Active/Inactive]
|
|
28
|
+
|
|
29
|
+
Run /plexor-status to see your usage.
|
|
30
|
+
Run /plexor-settings to modify configuration.
|
|
31
|
+
Run /plexor-enabled off to disable routing.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Step 2: Ask about Claude subscription**
|
|
35
|
+
|
|
36
|
+
Use the AskUserQuestion tool:
|
|
37
|
+
|
|
38
|
+
Question: "How do you pay for Claude usage?"
|
|
39
|
+
Header: "Billing"
|
|
40
|
+
Options:
|
|
41
|
+
1. **Claude MAX subscription (Pro/Team/Enterprise)** - I have a subscription and want Plexor for optimization & tracking (you'll still need a Plexor API key)
|
|
42
|
+
2. **Pay-per-use via Plexor** - I want Plexor to handle billing and route to the cheapest provider
|
|
43
|
+
|
|
44
|
+
**Step 3A: Claude MAX User Setup**
|
|
45
|
+
|
|
46
|
+
If user selected "Yes, I have Claude MAX":
|
|
47
|
+
|
|
48
|
+
1. Ask for their Plexor API key:
|
|
49
|
+
"Please provide your Plexor API key (starts with 'plx_')."
|
|
50
|
+
"Get one at: https://plexor.dev/dashboard"
|
|
51
|
+
"Your MAX subscription will be used for Claude - the Plexor key is for tracking/optimization."
|
|
52
|
+
|
|
53
|
+
2. Use the Write tool to create `~/.plexor/config.json`:
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"version": 1,
|
|
57
|
+
"auth": {
|
|
58
|
+
"api_key": "[user's Plexor key]",
|
|
59
|
+
"mode": "oauth_passthrough",
|
|
60
|
+
"authenticated_at": "[current ISO timestamp]"
|
|
61
|
+
},
|
|
62
|
+
"settings": {
|
|
63
|
+
"enabled": true,
|
|
64
|
+
"apiUrl": "https://api.plexor.dev",
|
|
65
|
+
"mode": "balanced",
|
|
66
|
+
"localCacheEnabled": true
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
3. Use the Write tool to update `~/.claude/settings.json` env block:
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"env": {
|
|
75
|
+
"ANTHROPIC_BASE_URL": "https://api.plexor.dev/gateway/anthropic",
|
|
76
|
+
"ANTHROPIC_AUTH_TOKEN": "[user's Plexor key]"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
Note: Preserve any existing settings, just add/update the env block.
|
|
81
|
+
|
|
82
|
+
4. Show the user:
|
|
83
|
+
```
|
|
84
|
+
Plexor Setup - Claude MAX User
|
|
85
|
+
==============================
|
|
86
|
+
Setup Complete! No manual configuration needed.
|
|
87
|
+
|
|
88
|
+
What was configured:
|
|
89
|
+
- ~/.plexor/config.json (Plexor plugin settings)
|
|
90
|
+
- ~/.claude/settings.json (automatic Claude Code routing)
|
|
91
|
+
|
|
92
|
+
How it works:
|
|
93
|
+
- All Claude Code sessions now route through Plexor
|
|
94
|
+
- Your MAX subscription OAuth token is passed through
|
|
95
|
+
- You keep your MAX benefits ($0 cost, 20x rate limits)
|
|
96
|
+
- Plexor optimizes prompts and tracks usage
|
|
97
|
+
|
|
98
|
+
Commands:
|
|
99
|
+
- /plexor-status - See your usage stats
|
|
100
|
+
- /plexor-enabled off - Temporarily disable Plexor
|
|
101
|
+
- /plexor-enabled on - Re-enable Plexor
|
|
102
|
+
|
|
103
|
+
Changes take effect immediately in all Claude Code sessions!
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Step 3B: API Key User Setup**
|
|
107
|
+
|
|
108
|
+
If user selected "No, I'll use a Plexor API key":
|
|
109
|
+
|
|
110
|
+
1. Ask for their Plexor API key:
|
|
111
|
+
"Please provide your Plexor API key (starts with 'plx_')."
|
|
112
|
+
"Get one at: https://plexor.dev/dashboard"
|
|
113
|
+
|
|
114
|
+
2. Once they provide the key, use the Write tool to create `~/.plexor/config.json`:
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"version": 1,
|
|
118
|
+
"auth": {
|
|
119
|
+
"api_key": "[user's API key]",
|
|
120
|
+
"mode": "api_key",
|
|
121
|
+
"authenticated_at": "[current ISO timestamp]"
|
|
122
|
+
},
|
|
123
|
+
"settings": {
|
|
124
|
+
"enabled": true,
|
|
125
|
+
"apiUrl": "https://api.plexor.dev",
|
|
126
|
+
"preferred_provider": "auto",
|
|
127
|
+
"mode": "balanced",
|
|
128
|
+
"localCacheEnabled": true
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
3. Use the Write tool to update `~/.claude/settings.json` env block:
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"env": {
|
|
137
|
+
"ANTHROPIC_BASE_URL": "https://api.plexor.dev/gateway/anthropic",
|
|
138
|
+
"ANTHROPIC_AUTH_TOKEN": "[user's Plexor key]"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
Note: Preserve any existing settings, just add/update the env block.
|
|
143
|
+
|
|
144
|
+
4. Show the user:
|
|
145
|
+
```
|
|
146
|
+
Plexor Setup - API Key User
|
|
147
|
+
===========================
|
|
148
|
+
Setup Complete! No manual configuration needed.
|
|
149
|
+
|
|
150
|
+
What was configured:
|
|
151
|
+
- ~/.plexor/config.json (Plexor plugin settings)
|
|
152
|
+
- ~/.claude/settings.json (automatic Claude Code routing)
|
|
153
|
+
|
|
154
|
+
How it works:
|
|
155
|
+
- All Claude Code sessions now route through Plexor
|
|
156
|
+
- Plexor picks the best provider (can save up to 90%)
|
|
157
|
+
- Your usage is tracked and optimized
|
|
158
|
+
|
|
159
|
+
Commands:
|
|
160
|
+
- /plexor-status - See your usage and savings
|
|
161
|
+
- /plexor-mode eco - Maximize savings
|
|
162
|
+
- /plexor-mode quality - Maximize quality
|
|
163
|
+
- /plexor-enabled off - Temporarily disable Plexor
|
|
164
|
+
|
|
165
|
+
Changes take effect immediately in all Claude Code sessions!
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**IMPORTANT NOTES**:
|
|
169
|
+
- The `~/.claude/settings.json` env block is the KEY mechanism that routes Claude Code through Plexor
|
|
170
|
+
- ANTHROPIC_AUTH_TOKEN takes precedence over ANTHROPIC_API_KEY (use AUTH_TOKEN for the Plexor key)
|
|
171
|
+
- Changes take effect immediately - no shell restart needed
|
|
172
|
+
- After completing setup, STOP. Do not run additional commands.
|
|
@@ -0,0 +1,240 @@
|
|
|
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
|
+
const SESSION_PATH = path.join(process.env.HOME, '.plexor', 'session.json');
|
|
14
|
+
const CLAUDE_SETTINGS_PATH = path.join(process.env.HOME, '.claude', 'settings.json');
|
|
15
|
+
const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if Claude Code is actually routing through Plexor
|
|
19
|
+
* by reading ~/.claude/settings.json
|
|
20
|
+
*/
|
|
21
|
+
function getRoutingStatus() {
|
|
22
|
+
try {
|
|
23
|
+
const data = fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf8');
|
|
24
|
+
const settings = JSON.parse(data);
|
|
25
|
+
const baseUrl = settings.env?.ANTHROPIC_BASE_URL || '';
|
|
26
|
+
const hasToken = !!settings.env?.ANTHROPIC_AUTH_TOKEN;
|
|
27
|
+
const isPlexorRouting = baseUrl.includes('plexor') || baseUrl.includes('staging.api');
|
|
28
|
+
return {
|
|
29
|
+
active: isPlexorRouting && hasToken,
|
|
30
|
+
baseUrl,
|
|
31
|
+
isStaging: baseUrl.includes('staging')
|
|
32
|
+
};
|
|
33
|
+
} catch {
|
|
34
|
+
return { active: false, baseUrl: null, isStaging: false };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function loadSessionStats() {
|
|
39
|
+
try {
|
|
40
|
+
const data = fs.readFileSync(SESSION_PATH, 'utf8');
|
|
41
|
+
const session = JSON.parse(data);
|
|
42
|
+
// Check if session has expired
|
|
43
|
+
if (Date.now() - session.last_activity > SESSION_TIMEOUT_MS) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return session;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function main() {
|
|
53
|
+
// Read config
|
|
54
|
+
let config;
|
|
55
|
+
try {
|
|
56
|
+
const data = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
57
|
+
config = JSON.parse(data);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.log('Not configured. Run /plexor-login first.');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const apiKey = config.auth?.api_key;
|
|
64
|
+
const enabled = config.settings?.enabled ?? false;
|
|
65
|
+
const mode = config.settings?.mode || 'balanced';
|
|
66
|
+
const provider = config.settings?.preferred_provider || 'auto';
|
|
67
|
+
const localCache = config.settings?.localCacheEnabled ?? false;
|
|
68
|
+
const apiUrl = config.settings?.apiUrl || 'https://api.plexor.dev';
|
|
69
|
+
|
|
70
|
+
if (!apiKey) {
|
|
71
|
+
console.log('Not authenticated. Run /plexor-login first.');
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Fetch user info and stats
|
|
76
|
+
let user = { email: 'Unknown', tier: { name: 'Free', limits: {} } };
|
|
77
|
+
let stats = { period: {}, summary: {} };
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
[user, stats] = await Promise.all([
|
|
81
|
+
fetchJson(apiUrl, '/v1/user', apiKey),
|
|
82
|
+
fetchJson(apiUrl, '/v1/stats', apiKey)
|
|
83
|
+
]);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
// Continue with defaults if API fails
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Load session stats
|
|
89
|
+
const session = loadSessionStats();
|
|
90
|
+
|
|
91
|
+
// Extract data
|
|
92
|
+
const email = user.email || 'Unknown';
|
|
93
|
+
const tierName = user.tier?.name || 'Free';
|
|
94
|
+
const monthlyOpts = user.tier?.limits?.monthly_optimizations || '∞';
|
|
95
|
+
const monthlyComps = user.tier?.limits?.monthly_completions || '∞';
|
|
96
|
+
|
|
97
|
+
const period = stats.period || {};
|
|
98
|
+
const summary = stats.summary || {};
|
|
99
|
+
|
|
100
|
+
const formatDate = (iso) => {
|
|
101
|
+
if (!iso) return '?';
|
|
102
|
+
const d = new Date(iso);
|
|
103
|
+
return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
104
|
+
};
|
|
105
|
+
const weekRange = `${formatDate(period.start)} - ${formatDate(period.end)}`;
|
|
106
|
+
|
|
107
|
+
// Format numbers
|
|
108
|
+
const formatNum = (n) => (n || 0).toLocaleString();
|
|
109
|
+
const formatPct = (n) => (n || 0).toFixed(1);
|
|
110
|
+
const formatCost = (n) => (n || 0).toFixed(2);
|
|
111
|
+
|
|
112
|
+
const status = enabled ? '● Active' : '○ Inactive';
|
|
113
|
+
const optEnabled = enabled ? 'Enabled' : 'Disabled';
|
|
114
|
+
const cacheEnabled = localCache ? 'Enabled' : 'Disabled';
|
|
115
|
+
const cacheRate = formatPct((summary.cache_hit_rate || 0) * 100);
|
|
116
|
+
|
|
117
|
+
// Build dashboard URL from API URL
|
|
118
|
+
// API: https://api.plexor.dev or https://staging.api.plexor.dev
|
|
119
|
+
// Dashboard: https://plexor.dev/dashboard or https://staging.plexor.dev/dashboard
|
|
120
|
+
let dashboardUrl = 'https://plexor.dev/dashboard';
|
|
121
|
+
try {
|
|
122
|
+
const url = new URL(apiUrl);
|
|
123
|
+
// Remove 'api.' prefix from hostname if present
|
|
124
|
+
const host = url.hostname.replace(/^api\./, '').replace(/\.api\./, '.');
|
|
125
|
+
dashboardUrl = `${url.protocol}//${host}/dashboard`;
|
|
126
|
+
} catch {
|
|
127
|
+
// If URL parsing fails, use default
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Output formatted status - each line is exactly 43 chars inner width
|
|
131
|
+
const line = (content) => ` │ ${content.padEnd(43)}│`;
|
|
132
|
+
|
|
133
|
+
// Session stats formatting
|
|
134
|
+
const formatDuration = (startedAt) => {
|
|
135
|
+
if (!startedAt) return '0m';
|
|
136
|
+
const elapsed = Date.now() - new Date(startedAt).getTime();
|
|
137
|
+
const minutes = Math.floor(elapsed / 60000);
|
|
138
|
+
if (minutes < 60) return `${minutes}m`;
|
|
139
|
+
const hours = Math.floor(minutes / 60);
|
|
140
|
+
return `${hours}h ${minutes % 60}m`;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const sessionDuration = session ? formatDuration(session.started_at) : '0m';
|
|
144
|
+
const sessionRequests = session ? formatNum(session.requests) : '0';
|
|
145
|
+
const sessionOptimizations = session ? formatNum(session.optimizations) : '0';
|
|
146
|
+
const sessionCacheHits = session ? formatNum(session.cache_hits) : '0';
|
|
147
|
+
const sessionTokensSaved = session ? formatNum(session.tokens_saved) : '0';
|
|
148
|
+
const sessionTokensSavedPct = session && session.original_tokens > 0
|
|
149
|
+
? formatPct((session.tokens_saved / session.original_tokens) * 100)
|
|
150
|
+
: '0.0';
|
|
151
|
+
const sessionCostSaved = session ? formatCost(session.cost_saved) : '0.00';
|
|
152
|
+
|
|
153
|
+
// Build session section (only show if session exists)
|
|
154
|
+
const sessionSection = session ? ` ├─────────────────────────────────────────────┤
|
|
155
|
+
${line(`This Session (${sessionDuration})`)}
|
|
156
|
+
${line(`├── Requests: ${sessionRequests}`)}
|
|
157
|
+
${line(`├── Optimizations: ${sessionOptimizations}`)}
|
|
158
|
+
${line(`├── Cache hits: ${sessionCacheHits}`)}
|
|
159
|
+
${line(`├── Tokens saved: ${sessionTokensSaved} (${sessionTokensSavedPct}%)`)}
|
|
160
|
+
${line(`└── Cost saved: $${sessionCostSaved}`)}
|
|
161
|
+
` : '';
|
|
162
|
+
|
|
163
|
+
// Get routing status from Claude settings.json
|
|
164
|
+
const routing = getRoutingStatus();
|
|
165
|
+
const routingIndicator = routing.active ? '🟢 PLEXOR MODE: ON' : '🔴 PLEXOR MODE: OFF';
|
|
166
|
+
const envLabel = routing.isStaging ? '(staging)' : '(production)';
|
|
167
|
+
|
|
168
|
+
console.log(` ┌─────────────────────────────────────────────┐
|
|
169
|
+
${line(routingIndicator + (routing.active ? ' ' + envLabel : ''))}
|
|
170
|
+
├─────────────────────────────────────────────┤
|
|
171
|
+
${line(`Account: ${tierName}`)}
|
|
172
|
+
${line(`Email: ${email}`)}
|
|
173
|
+
${line(`Status: ${status}`)}
|
|
174
|
+
${sessionSection} ├─────────────────────────────────────────────┤
|
|
175
|
+
${line(`This Week (${weekRange})`)}
|
|
176
|
+
${line(`├── Requests: ${formatNum(summary.total_requests)}`)}
|
|
177
|
+
${line(`├── Original tokens: ${formatNum(summary.original_tokens)}`)}
|
|
178
|
+
${line(`├── Optimized tokens: ${formatNum(summary.optimized_tokens)}`)}
|
|
179
|
+
${line(`├── Tokens saved: ${formatNum(summary.tokens_saved)} (${formatPct(summary.tokens_saved_percent)}%)`)}
|
|
180
|
+
${line(`├── Baseline cost: $${formatCost(summary.baseline_cost)}`)}
|
|
181
|
+
${line(`├── Actual cost: $${formatCost(summary.total_cost)}`)}
|
|
182
|
+
${line(`└── Cost saved: $${formatCost(summary.cost_saved)} (${formatPct(summary.cost_saved_percent)}%)`)}
|
|
183
|
+
├─────────────────────────────────────────────┤
|
|
184
|
+
${line('Performance')}
|
|
185
|
+
${line(`└── Cache hit rate: ${cacheRate}%`)}
|
|
186
|
+
├─────────────────────────────────────────────┤
|
|
187
|
+
${line('Limits')}
|
|
188
|
+
${line(`├── Monthly optimizations: ${formatNum(monthlyOpts)}`)}
|
|
189
|
+
${line(`└── Monthly completions: ${formatNum(monthlyComps)}`)}
|
|
190
|
+
├─────────────────────────────────────────────┤
|
|
191
|
+
${line('Settings')}
|
|
192
|
+
${line(`├── Optimization: ${optEnabled}`)}
|
|
193
|
+
${line(`├── Local cache: ${cacheEnabled}`)}
|
|
194
|
+
${line(`├── Mode: ${mode}`)}
|
|
195
|
+
${line(`└── Provider routing: ${provider}`)}
|
|
196
|
+
└─────────────────────────────────────────────┘
|
|
197
|
+
|
|
198
|
+
Dashboard: ${dashboardUrl}
|
|
199
|
+
`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function fetchJson(apiUrl, endpoint, apiKey) {
|
|
203
|
+
return new Promise((resolve, reject) => {
|
|
204
|
+
const url = new URL(`${apiUrl}${endpoint}`);
|
|
205
|
+
|
|
206
|
+
const options = {
|
|
207
|
+
hostname: url.hostname,
|
|
208
|
+
port: 443,
|
|
209
|
+
path: url.pathname,
|
|
210
|
+
method: 'GET',
|
|
211
|
+
headers: {
|
|
212
|
+
'X-Plexor-Key': apiKey
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const req = https.request(options, (res) => {
|
|
217
|
+
let data = '';
|
|
218
|
+
res.on('data', chunk => data += chunk);
|
|
219
|
+
res.on('end', () => {
|
|
220
|
+
try {
|
|
221
|
+
resolve(JSON.parse(data));
|
|
222
|
+
} catch {
|
|
223
|
+
reject(new Error('Invalid response'));
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
req.on('error', reject);
|
|
229
|
+
req.setTimeout(5000, () => {
|
|
230
|
+
req.destroy();
|
|
231
|
+
reject(new Error('Timeout'));
|
|
232
|
+
});
|
|
233
|
+
req.end();
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
main().catch(err => {
|
|
238
|
+
console.error('Error:', err.message);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Show Plexor optimization statistics and savings (user)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Plexor Status
|
|
6
|
+
|
|
7
|
+
Run this command to display Plexor statistics:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
node ~/.claude/plugins/plexor/commands/plexor-status.js
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Use the Bash tool to execute this single command.
|
|
14
|
+
|
|
15
|
+
**IMPORTANT**: After running this command and displaying the output, STOP. Do not:
|
|
16
|
+
- Read any files
|
|
17
|
+
- Explore the codebase
|
|
18
|
+
- Run additional commands
|
|
19
|
+
- Ask follow-up questions
|
|
20
|
+
|
|
21
|
+
The command output is the complete response. Simply show the output and wait for the user's next input.
|