@plexor-dev/claude-code-plugin 0.1.0-beta.31 → 0.1.0-beta.33
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/README.md +1 -1
- package/commands/plexor-enabled.js +63 -3
- package/commands/plexor-enabled.md +2 -21
- package/commands/plexor-login.js +141 -14
- package/commands/plexor-login.md +3 -19
- package/commands/plexor-logout.js +23 -5
- package/commands/plexor-logout.md +2 -20
- package/commands/plexor-setup.md +5 -2
- package/commands/plexor-status.js +67 -11
- package/commands/plexor-status.md +1 -13
- package/commands/plexor-uninstall.js +293 -0
- package/commands/plexor-uninstall.md +12 -0
- package/hooks/intercept.js +17 -21
- package/hooks/track-response.js +2 -2
- package/lib/config.js +1 -1
- package/lib/constants.js +19 -1
- package/lib/settings-manager.js +56 -8
- package/package.json +3 -2
- package/scripts/postinstall.js +48 -0
- package/scripts/uninstall.js +129 -42
package/README.md
CHANGED
|
@@ -106,7 +106,7 @@ rm -rf ~/.plexor
|
|
|
106
106
|
## Support
|
|
107
107
|
|
|
108
108
|
- **Documentation**: https://plexor.dev/docs
|
|
109
|
-
- **Issues**: https://github.com/
|
|
109
|
+
- **Issues**: https://github.com/micoverde/plex-vc-fund-platform/issues
|
|
110
110
|
- **Email**: hello@plexor.dev
|
|
111
111
|
|
|
112
112
|
## License
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
const fs = require('fs');
|
|
15
15
|
const path = require('path');
|
|
16
16
|
|
|
17
|
+
// Import centralized constants with HOME directory validation
|
|
18
|
+
const { PLEXOR_DIR, CONFIG_PATH } = require('../lib/constants');
|
|
19
|
+
|
|
17
20
|
// Import settings manager with error handling for missing lib
|
|
18
21
|
let settingsManager, PLEXOR_STAGING_URL, PLEXOR_PROD_URL;
|
|
19
22
|
try {
|
|
@@ -30,9 +33,6 @@ try {
|
|
|
30
33
|
throw err;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
const CONFIG_PATH = path.join(process.env.HOME, '.plexor', 'config.json');
|
|
34
|
-
const PLEXOR_DIR = path.join(process.env.HOME, '.plexor');
|
|
35
|
-
|
|
36
36
|
function loadConfig() {
|
|
37
37
|
try {
|
|
38
38
|
if (!fs.existsSync(CONFIG_PATH)) {
|
|
@@ -94,6 +94,30 @@ function isValidApiKeyFormat(key) {
|
|
|
94
94
|
return key && typeof key === 'string' && key.startsWith('plx_') && key.length >= 20;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Check for state mismatch between config.json enabled flag and settings.json routing
|
|
99
|
+
* @param {boolean} configEnabled - enabled flag from config.json
|
|
100
|
+
* @param {boolean} routingActive - whether settings.json has Plexor routing configured
|
|
101
|
+
* @returns {Object|null} mismatch details or null if states are consistent
|
|
102
|
+
*/
|
|
103
|
+
function checkStateMismatch(configEnabled, routingActive) {
|
|
104
|
+
if (configEnabled && !routingActive) {
|
|
105
|
+
return {
|
|
106
|
+
type: 'config-enabled-routing-inactive',
|
|
107
|
+
message: 'Config shows enabled but Claude routing is not configured',
|
|
108
|
+
suggestion: 'Run /plexor-enabled true to sync and configure routing'
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (!configEnabled && routingActive) {
|
|
112
|
+
return {
|
|
113
|
+
type: 'config-disabled-routing-active',
|
|
114
|
+
message: 'Config shows disabled but Claude routing is active',
|
|
115
|
+
suggestion: 'Run /plexor-enabled false to sync and disable routing'
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
97
121
|
function main() {
|
|
98
122
|
const args = process.argv.slice(2);
|
|
99
123
|
const config = loadConfig();
|
|
@@ -107,6 +131,10 @@ function main() {
|
|
|
107
131
|
if (args.length === 0) {
|
|
108
132
|
const status = currentEnabled ? '● Enabled' : '○ Disabled';
|
|
109
133
|
const routingStr = routingStatus.enabled ? '● Active' : '○ Inactive';
|
|
134
|
+
|
|
135
|
+
// Check for state mismatch between config enabled flag and routing status
|
|
136
|
+
const stateMismatch = checkStateMismatch(currentEnabled, routingStatus.enabled);
|
|
137
|
+
|
|
110
138
|
console.log(`┌─────────────────────────────────────────────┐`);
|
|
111
139
|
console.log(`│ Plexor Proxy Status │`);
|
|
112
140
|
console.log(`├─────────────────────────────────────────────┤`);
|
|
@@ -115,6 +143,12 @@ function main() {
|
|
|
115
143
|
if (routingStatus.enabled) {
|
|
116
144
|
console.log(`│ Endpoint: ${(routingStatus.isStaging ? 'Staging' : 'Production').padEnd(32)}│`);
|
|
117
145
|
}
|
|
146
|
+
if (stateMismatch) {
|
|
147
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
148
|
+
console.log(`│ ⚠ State mismatch detected: │`);
|
|
149
|
+
console.log(`│ ${stateMismatch.message.padEnd(42)}│`);
|
|
150
|
+
console.log(`│ ${stateMismatch.suggestion.padEnd(42)}│`);
|
|
151
|
+
}
|
|
118
152
|
console.log(`├─────────────────────────────────────────────┤`);
|
|
119
153
|
console.log(`│ Usage: │`);
|
|
120
154
|
console.log(`│ /plexor-enabled true - Enable proxy │`);
|
|
@@ -161,6 +195,19 @@ function main() {
|
|
|
161
195
|
const apiUrl = config.settings?.apiUrl || 'https://api.plexor.dev';
|
|
162
196
|
const useStaging = apiUrl.includes('staging');
|
|
163
197
|
routingUpdated = settingsManager.enablePlexorRouting(apiKey, { useStaging });
|
|
198
|
+
|
|
199
|
+
// Check if settings.json update failed
|
|
200
|
+
if (!routingUpdated) {
|
|
201
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
202
|
+
console.log(`│ ✗ Failed to Enable Plexor Routing │`);
|
|
203
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
204
|
+
console.log(`│ Could not update ~/.claude/settings.json │`);
|
|
205
|
+
console.log(`│ Config.json was updated but routing failed.│`);
|
|
206
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
207
|
+
console.log(`│ Check file permissions and try again. │`);
|
|
208
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
164
211
|
}
|
|
165
212
|
} else {
|
|
166
213
|
// Update Plexor plugin config
|
|
@@ -172,6 +219,19 @@ function main() {
|
|
|
172
219
|
|
|
173
220
|
// Disable routing - remove env vars from settings.json
|
|
174
221
|
routingUpdated = settingsManager.disablePlexorRouting();
|
|
222
|
+
|
|
223
|
+
// Check if settings.json update failed
|
|
224
|
+
if (!routingUpdated) {
|
|
225
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
226
|
+
console.log(`│ ✗ Failed to Disable Plexor Routing │`);
|
|
227
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
228
|
+
console.log(`│ Could not update ~/.claude/settings.json │`);
|
|
229
|
+
console.log(`│ Config.json was updated but routing failed.│`);
|
|
230
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
231
|
+
console.log(`│ Check file permissions and try again. │`);
|
|
232
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
175
235
|
}
|
|
176
236
|
|
|
177
237
|
// Show error if no API key when enabling
|
|
@@ -2,27 +2,8 @@
|
|
|
2
2
|
description: Enable or disable Plexor proxy (routes all traffic through Plexor API) (user)
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Run this command to view or toggle the Plexor proxy:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
node ~/.claude/plugins/plexor/commands/plexor-enabled.js
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
To enable or disable, pass an argument:
|
|
5
|
+
**RULE: Execute the bash command below EXACTLY ONCE. After the Bash tool returns output, your ONLY action is to present that output to the user. DO NOT re-execute the command. DO NOT call any other tools.**
|
|
14
6
|
|
|
15
7
|
```bash
|
|
16
|
-
node ~/.claude/plugins/plexor/commands/plexor-enabled.js
|
|
17
|
-
node ~/.claude/plugins/plexor/commands/plexor-enabled.js false
|
|
8
|
+
node ~/.claude/plugins/plexor/commands/plexor-enabled.js $ARGUMENTS
|
|
18
9
|
```
|
|
19
|
-
|
|
20
|
-
Use the Bash tool to execute this command.
|
|
21
|
-
|
|
22
|
-
**IMPORTANT**: After running this command and displaying the output, STOP. Do not:
|
|
23
|
-
- Read any files
|
|
24
|
-
- Explore the codebase
|
|
25
|
-
- Run additional commands
|
|
26
|
-
- Ask follow-up questions
|
|
27
|
-
|
|
28
|
-
The command output is the complete response. Simply show the output and wait for the user's next input.
|
package/commands/plexor-login.js
CHANGED
|
@@ -13,6 +13,9 @@ const path = require('path');
|
|
|
13
13
|
const https = require('https');
|
|
14
14
|
const http = require('http');
|
|
15
15
|
|
|
16
|
+
// Import centralized constants with HOME directory validation
|
|
17
|
+
const { PLEXOR_DIR, CONFIG_PATH, DEFAULT_API_URL } = require('../lib/constants');
|
|
18
|
+
|
|
16
19
|
// Import settings manager with error handling for missing lib
|
|
17
20
|
let settingsManager;
|
|
18
21
|
try {
|
|
@@ -26,11 +29,6 @@ try {
|
|
|
26
29
|
throw err;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
const CONFIG_PATH = path.join(process.env.HOME, '.plexor', 'config.json');
|
|
30
|
-
const PLEXOR_DIR = path.join(process.env.HOME, '.plexor');
|
|
31
|
-
// PRODUCTION PACKAGE - uses production API
|
|
32
|
-
const DEFAULT_API_URL = 'https://api.plexor.dev';
|
|
33
|
-
|
|
34
32
|
function loadConfig() {
|
|
35
33
|
try {
|
|
36
34
|
if (!fs.existsSync(CONFIG_PATH)) {
|
|
@@ -78,6 +76,18 @@ function saveConfig(config) {
|
|
|
78
76
|
}
|
|
79
77
|
}
|
|
80
78
|
|
|
79
|
+
function isPlexorApiKey(token) {
|
|
80
|
+
return typeof token === 'string' && token.startsWith('plx_') && token.length >= 20;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function isJwtToken(token) {
|
|
84
|
+
if (typeof token !== 'string' || !token.startsWith('eyJ')) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const parts = token.split('.');
|
|
88
|
+
return parts.length === 3 && parts.every(part => part.length > 0);
|
|
89
|
+
}
|
|
90
|
+
|
|
81
91
|
function validateApiKey(apiUrl, apiKey) {
|
|
82
92
|
return new Promise((resolve, reject) => {
|
|
83
93
|
const url = new URL(`${apiUrl}/v1/user`);
|
|
@@ -121,9 +131,112 @@ function validateApiKey(apiUrl, apiKey) {
|
|
|
121
131
|
});
|
|
122
132
|
}
|
|
123
133
|
|
|
134
|
+
function validateJwtToken(apiUrl, jwtToken) {
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
const url = new URL(`${apiUrl}/v1/auth/me`);
|
|
137
|
+
const isHttps = url.protocol === 'https:';
|
|
138
|
+
const lib = isHttps ? https : http;
|
|
139
|
+
|
|
140
|
+
const options = {
|
|
141
|
+
hostname: url.hostname,
|
|
142
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
143
|
+
path: url.pathname,
|
|
144
|
+
method: 'GET',
|
|
145
|
+
headers: {
|
|
146
|
+
'Authorization': `Bearer ${jwtToken}`
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const req = lib.request(options, (res) => {
|
|
151
|
+
let data = '';
|
|
152
|
+
res.on('data', chunk => data += chunk);
|
|
153
|
+
res.on('end', () => {
|
|
154
|
+
if (res.statusCode === 200) {
|
|
155
|
+
try {
|
|
156
|
+
resolve(JSON.parse(data));
|
|
157
|
+
} catch {
|
|
158
|
+
reject(new Error('Invalid response from server'));
|
|
159
|
+
}
|
|
160
|
+
} else if (res.statusCode === 401) {
|
|
161
|
+
reject(new Error('Invalid login token'));
|
|
162
|
+
} else {
|
|
163
|
+
reject(new Error(`Server error: ${res.statusCode}`));
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
req.on('error', (err) => reject(new Error(`Connection failed: ${err.message}`)));
|
|
169
|
+
req.setTimeout(10000, () => {
|
|
170
|
+
req.destroy();
|
|
171
|
+
reject(new Error('Connection timeout'));
|
|
172
|
+
});
|
|
173
|
+
req.end();
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function validateCredential(apiUrl, credential) {
|
|
178
|
+
if (isJwtToken(credential)) {
|
|
179
|
+
return validateJwtToken(apiUrl, credential);
|
|
180
|
+
}
|
|
181
|
+
return validateApiKey(apiUrl, credential);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Read API key from stdin (for piped input)
|
|
186
|
+
* @returns {Promise<string>} The API key read from stdin
|
|
187
|
+
*/
|
|
188
|
+
function readFromStdin() {
|
|
189
|
+
return new Promise((resolve, reject) => {
|
|
190
|
+
let data = '';
|
|
191
|
+
const timeout = setTimeout(() => {
|
|
192
|
+
reject(new Error('Timeout reading from stdin'));
|
|
193
|
+
}, 5000);
|
|
194
|
+
|
|
195
|
+
process.stdin.setEncoding('utf8');
|
|
196
|
+
process.stdin.on('data', (chunk) => {
|
|
197
|
+
data += chunk;
|
|
198
|
+
});
|
|
199
|
+
process.stdin.on('end', () => {
|
|
200
|
+
clearTimeout(timeout);
|
|
201
|
+
resolve(data.trim());
|
|
202
|
+
});
|
|
203
|
+
process.stdin.on('error', (err) => {
|
|
204
|
+
clearTimeout(timeout);
|
|
205
|
+
reject(err);
|
|
206
|
+
});
|
|
207
|
+
process.stdin.resume();
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
124
211
|
async function main() {
|
|
125
212
|
const args = process.argv.slice(2);
|
|
126
|
-
let apiKey =
|
|
213
|
+
let apiKey = null;
|
|
214
|
+
let keySource = null;
|
|
215
|
+
|
|
216
|
+
// SECURITY FIX: Check for API key in order of preference
|
|
217
|
+
// 1. Environment variable (most secure for scripts/CI)
|
|
218
|
+
// 2. Stdin pipe (secure for interactive use)
|
|
219
|
+
// 3. Command line argument (warns about security risk)
|
|
220
|
+
|
|
221
|
+
// Check environment variable first (most secure)
|
|
222
|
+
if (process.env.PLEXOR_API_KEY) {
|
|
223
|
+
apiKey = process.env.PLEXOR_API_KEY;
|
|
224
|
+
keySource = 'environment';
|
|
225
|
+
}
|
|
226
|
+
// Check if stdin has data (piped input)
|
|
227
|
+
else if (!process.stdin.isTTY && args.length === 0) {
|
|
228
|
+
try {
|
|
229
|
+
apiKey = await readFromStdin();
|
|
230
|
+
keySource = 'stdin';
|
|
231
|
+
} catch {
|
|
232
|
+
// Stdin read failed, continue to check args
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Check command line argument (least secure - shows in ps)
|
|
236
|
+
else if (args[0]) {
|
|
237
|
+
apiKey = args[0];
|
|
238
|
+
keySource = 'argument';
|
|
239
|
+
}
|
|
127
240
|
|
|
128
241
|
// Check for existing login
|
|
129
242
|
const config = loadConfig();
|
|
@@ -135,14 +248,14 @@ async function main() {
|
|
|
135
248
|
console.log(`├─────────────────────────────────────────────┤`);
|
|
136
249
|
console.log(`│ API Key: ${(existingKey.substring(0, 8) + '...').padEnd(33)}│`);
|
|
137
250
|
console.log(`│ To re-login, provide a new key: │`);
|
|
138
|
-
console.log(`│ /plexor-login
|
|
251
|
+
console.log(`│ echo $PLEXOR_API_KEY | /plexor-login │`);
|
|
139
252
|
console.log(`│ To logout: │`);
|
|
140
253
|
console.log(`│ /plexor-logout │`);
|
|
141
254
|
console.log(`└─────────────────────────────────────────────┘`);
|
|
142
255
|
return;
|
|
143
256
|
}
|
|
144
257
|
|
|
145
|
-
// If no key provided,
|
|
258
|
+
// If no key provided, show secure usage options
|
|
146
259
|
if (!apiKey) {
|
|
147
260
|
console.log(`┌─────────────────────────────────────────────┐`);
|
|
148
261
|
console.log(`│ Plexor Login │`);
|
|
@@ -150,15 +263,29 @@ async function main() {
|
|
|
150
263
|
console.log(`│ Get your API key at: │`);
|
|
151
264
|
console.log(`│ https://plexor.dev/dashboard/api-keys │`);
|
|
152
265
|
console.log(`├─────────────────────────────────────────────┤`);
|
|
153
|
-
console.log(`│
|
|
266
|
+
console.log(`│ Secure usage (recommended): │`);
|
|
267
|
+
console.log(`│ echo "plx_..." | /plexor-login │`);
|
|
268
|
+
console.log(`│ PLEXOR_API_KEY=plx_... /plexor-login │`);
|
|
269
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
270
|
+
console.log(`│ Direct usage (visible in ps): │`);
|
|
271
|
+
console.log(`│ /plexor-login <api-key> │`);
|
|
154
272
|
console.log(`└─────────────────────────────────────────────┘`);
|
|
155
273
|
return;
|
|
156
274
|
}
|
|
157
275
|
|
|
158
|
-
//
|
|
159
|
-
if (
|
|
160
|
-
console.
|
|
161
|
-
console.
|
|
276
|
+
// Warn if API key was passed as command line argument
|
|
277
|
+
if (keySource === 'argument') {
|
|
278
|
+
console.log(`⚠ Security note: API key passed as argument is visible in`);
|
|
279
|
+
console.log(` process list (ps aux). For better security, use:`);
|
|
280
|
+
console.log(` echo "plx_..." | /plexor-login`);
|
|
281
|
+
console.log(` PLEXOR_API_KEY=plx_... /plexor-login`);
|
|
282
|
+
console.log('');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Validate credential format (Plexor API key OR login JWT)
|
|
286
|
+
if (!isPlexorApiKey(apiKey) && !isJwtToken(apiKey)) {
|
|
287
|
+
console.error(`Error: Invalid credential format`);
|
|
288
|
+
console.error(`Expected Plexor API key ("plx_...") or login token ("eyJ...")`);
|
|
162
289
|
process.exit(1);
|
|
163
290
|
}
|
|
164
291
|
|
|
@@ -167,7 +294,7 @@ async function main() {
|
|
|
167
294
|
console.log('Validating API key...');
|
|
168
295
|
|
|
169
296
|
try {
|
|
170
|
-
const user = await
|
|
297
|
+
const user = await validateCredential(apiUrl, apiKey);
|
|
171
298
|
|
|
172
299
|
config.auth = config.auth || {};
|
|
173
300
|
config.auth.api_key = apiKey;
|
package/commands/plexor-login.md
CHANGED
|
@@ -2,26 +2,10 @@
|
|
|
2
2
|
description: Authenticate with Plexor to enable optimization (user)
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**RULE: Execute the bash command below EXACTLY ONCE. After the Bash tool returns output, your ONLY action is to present that output to the user. DO NOT re-execute the command. DO NOT call any other tools.**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Pass the API key as argument (format: `plx_your_api_key_here`).
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
node ~/.claude/plugins/plexor/commands/plexor-login.js
|
|
10
|
+
node ~/.claude/plugins/plexor/commands/plexor-login.js $ARGUMENTS
|
|
11
11
|
```
|
|
12
|
-
|
|
13
|
-
To login with an API key:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
node ~/.claude/plugins/plexor/commands/plexor-login.js plx_your_api_key_here
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Use the Bash tool to execute this command.
|
|
20
|
-
|
|
21
|
-
**IMPORTANT**: After running this command and displaying the output, STOP. Do not:
|
|
22
|
-
- Read any files
|
|
23
|
-
- Explore the codebase
|
|
24
|
-
- Run additional commands
|
|
25
|
-
- Ask follow-up questions
|
|
26
|
-
|
|
27
|
-
The command output is the complete response. Simply show the output and wait for the user's next input.
|
|
@@ -8,10 +8,22 @@
|
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
const CONFIG_PATH =
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
// Import centralized constants with HOME directory validation
|
|
12
|
+
const { PLEXOR_DIR, CONFIG_PATH, SESSION_PATH, CACHE_PATH } = require('../lib/constants');
|
|
13
|
+
|
|
14
|
+
// Import settings manager for Claude Code routing cleanup
|
|
15
|
+
let settingsManager;
|
|
16
|
+
try {
|
|
17
|
+
const lib = require('../lib/settings-manager');
|
|
18
|
+
settingsManager = lib.settingsManager;
|
|
19
|
+
} catch (err) {
|
|
20
|
+
if (err.code === 'MODULE_NOT_FOUND') {
|
|
21
|
+
console.error('Error: Plexor plugin files are missing or corrupted.');
|
|
22
|
+
console.error(' Please reinstall: npm install @plexor-dev/claude-code-plugin');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
15
27
|
|
|
16
28
|
function loadConfig() {
|
|
17
29
|
try {
|
|
@@ -96,6 +108,11 @@ function main() {
|
|
|
96
108
|
process.exit(1);
|
|
97
109
|
}
|
|
98
110
|
|
|
111
|
+
// CRITICAL FIX: Disable Claude Code routing in ~/.claude/settings.json
|
|
112
|
+
// This removes ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN so Claude Code
|
|
113
|
+
// no longer tries to route through Plexor with removed credentials
|
|
114
|
+
const routingDisabled = settingsManager.disablePlexorRouting();
|
|
115
|
+
|
|
99
116
|
// Clear session
|
|
100
117
|
deleteFile(SESSION_PATH);
|
|
101
118
|
|
|
@@ -109,12 +126,13 @@ function main() {
|
|
|
109
126
|
console.log(`│ ✓ Logged Out │`);
|
|
110
127
|
console.log(`├─────────────────────────────────────────────┤`);
|
|
111
128
|
console.log(`│ ✓ API key removed │`);
|
|
112
|
-
console.log(`│ ✓
|
|
129
|
+
console.log(`│ ${routingDisabled ? '✓' : '○'} Claude Code routing disabled │`);
|
|
113
130
|
console.log(`│ ✓ Session cleared │`);
|
|
114
131
|
if (clearCache) {
|
|
115
132
|
console.log(`│ ${cacheCleared ? '✓' : '○'} Cache cleared │`);
|
|
116
133
|
}
|
|
117
134
|
console.log(`├─────────────────────────────────────────────┤`);
|
|
135
|
+
console.log(`│ Claude Code now connects directly. │`);
|
|
118
136
|
console.log(`│ Run /plexor-login to re-authenticate. │`);
|
|
119
137
|
if (!clearCache) {
|
|
120
138
|
console.log(`│ Use --clear-cache to also clear cache. │`);
|
|
@@ -2,26 +2,8 @@
|
|
|
2
2
|
description: Log out from Plexor and clear credentials (user)
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Run this command to log out and clear credentials:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
node ~/.claude/plugins/plexor/commands/plexor-logout.js
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
To also clear the cache:
|
|
5
|
+
**RULE: Execute the bash command below EXACTLY ONCE. After the Bash tool returns output, your ONLY action is to present that output to the user. DO NOT re-execute the command. DO NOT call any other tools.**
|
|
14
6
|
|
|
15
7
|
```bash
|
|
16
|
-
node ~/.claude/plugins/plexor/commands/plexor-logout.js
|
|
8
|
+
node ~/.claude/plugins/plexor/commands/plexor-logout.js $ARGUMENTS
|
|
17
9
|
```
|
|
18
|
-
|
|
19
|
-
Use the Bash tool to execute this command.
|
|
20
|
-
|
|
21
|
-
**IMPORTANT**: After running this command and displaying the output, STOP. Do not:
|
|
22
|
-
- Read any files
|
|
23
|
-
- Explore the codebase
|
|
24
|
-
- Run additional commands
|
|
25
|
-
- Ask follow-up questions
|
|
26
|
-
|
|
27
|
-
The command output is the complete response. Simply show the output and wait for the user's next input.
|
package/commands/plexor-setup.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
description: First-time setup wizard for Plexor with Claude Code (user)
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
**RULE: Execute this workflow EXACTLY ONCE. After completing all steps, STOP. DO NOT restart the workflow. DO NOT re-execute any commands. DO NOT call any other tools.**
|
|
6
|
+
|
|
5
7
|
# Plexor Setup Wizard
|
|
6
8
|
|
|
7
9
|
Guide users through first-time Plexor setup. **No manual environment variable configuration required!**
|
|
@@ -165,8 +167,9 @@ Commands:
|
|
|
165
167
|
Changes take effect immediately in all Claude Code sessions!
|
|
166
168
|
```
|
|
167
169
|
|
|
168
|
-
**
|
|
170
|
+
**NOTES**:
|
|
169
171
|
- The `~/.claude/settings.json` env block is the KEY mechanism that routes Claude Code through Plexor
|
|
170
172
|
- ANTHROPIC_AUTH_TOKEN takes precedence over ANTHROPIC_API_KEY (use AUTH_TOKEN for the Plexor key)
|
|
171
173
|
- Changes take effect immediately - no shell restart needed
|
|
172
|
-
|
|
174
|
+
|
|
175
|
+
After completing all steps, STOP. DO NOT restart the workflow. DO NOT re-execute any commands.
|
|
@@ -9,10 +9,9 @@ const fs = require('fs');
|
|
|
9
9
|
const path = require('path');
|
|
10
10
|
const https = require('https');
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const CLAUDE_SETTINGS_PATH = path.join(
|
|
15
|
-
const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
|
12
|
+
// Import centralized constants with HOME directory validation
|
|
13
|
+
const { HOME_DIR, CONFIG_PATH, SESSION_PATH, SESSION_TIMEOUT_MS } = require('../lib/constants');
|
|
14
|
+
const CLAUDE_SETTINGS_PATH = path.join(HOME_DIR, '.claude', 'settings.json');
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
17
|
* Check if Claude Code is actually routing through Plexor
|
|
@@ -35,6 +34,31 @@ function getRoutingStatus() {
|
|
|
35
34
|
}
|
|
36
35
|
}
|
|
37
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Detect partial routing state where URL points to Plexor but auth is missing/invalid
|
|
39
|
+
* This can cause confusing auth errors for users
|
|
40
|
+
* @returns {Object} { partial: boolean, issue: string|null }
|
|
41
|
+
*/
|
|
42
|
+
function detectPartialState() {
|
|
43
|
+
try {
|
|
44
|
+
const data = fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf8');
|
|
45
|
+
const settings = JSON.parse(data);
|
|
46
|
+
const baseUrl = settings.env?.ANTHROPIC_BASE_URL || '';
|
|
47
|
+
const authToken = settings.env?.ANTHROPIC_AUTH_TOKEN || '';
|
|
48
|
+
const isPlexorUrl = baseUrl.includes('plexor') || baseUrl.includes('staging.api');
|
|
49
|
+
|
|
50
|
+
if (isPlexorUrl && !authToken) {
|
|
51
|
+
return { partial: true, issue: 'Plexor URL set but no auth token' };
|
|
52
|
+
}
|
|
53
|
+
if (isPlexorUrl && !authToken.startsWith('plx_')) {
|
|
54
|
+
return { partial: true, issue: 'Plexor URL set but auth token is not a Plexor key' };
|
|
55
|
+
}
|
|
56
|
+
return { partial: false, issue: null };
|
|
57
|
+
} catch {
|
|
58
|
+
return { partial: false, issue: null };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
38
62
|
function loadSessionStats() {
|
|
39
63
|
try {
|
|
40
64
|
const data = fs.readFileSync(SESSION_PATH, 'utf8');
|
|
@@ -102,6 +126,30 @@ function checkEnvironmentMismatch(configApiUrl, routingBaseUrl) {
|
|
|
102
126
|
return null;
|
|
103
127
|
}
|
|
104
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Check for state mismatch between config.json enabled flag and settings.json routing
|
|
131
|
+
* @param {boolean} configEnabled - enabled flag from config.json
|
|
132
|
+
* @param {boolean} routingActive - whether settings.json has Plexor routing configured
|
|
133
|
+
* @returns {Object|null} mismatch details or null if states are consistent
|
|
134
|
+
*/
|
|
135
|
+
function checkStateMismatch(configEnabled, routingActive) {
|
|
136
|
+
if (configEnabled && !routingActive) {
|
|
137
|
+
return {
|
|
138
|
+
type: 'config-enabled-routing-inactive',
|
|
139
|
+
message: 'Config shows enabled but Claude routing is not configured',
|
|
140
|
+
suggestion: 'Run /plexor-enabled true to sync and configure routing'
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (!configEnabled && routingActive) {
|
|
144
|
+
return {
|
|
145
|
+
type: 'config-disabled-routing-active',
|
|
146
|
+
message: 'Config shows disabled but Claude routing is active',
|
|
147
|
+
suggestion: 'Run /plexor-enabled false to sync and disable routing'
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
105
153
|
async function main() {
|
|
106
154
|
// Read config with integrity checking
|
|
107
155
|
const config = loadConfig();
|
|
@@ -222,14 +270,22 @@ ${line(`└── Cost saved: $${sessionCostSaved}`)}
|
|
|
222
270
|
const routingIndicator = routing.active ? '🟢 PLEXOR MODE: ON' : '🔴 PLEXOR MODE: OFF';
|
|
223
271
|
const envLabel = routing.isStaging ? '(staging)' : '(production)';
|
|
224
272
|
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
273
|
+
// Note: Environment mismatch warning removed - it caused false positives during
|
|
274
|
+
// concurrent operations and transient states. The partial state and config/routing
|
|
275
|
+
// mismatch warnings below provide more actionable feedback.
|
|
276
|
+
|
|
277
|
+
// Check for partial routing state (Plexor URL without valid auth)
|
|
278
|
+
const partialState = detectPartialState();
|
|
279
|
+
if (partialState.partial) {
|
|
280
|
+
console.log(` ⚠ PARTIAL STATE DETECTED: ${partialState.issue}`);
|
|
281
|
+
console.log(` Run /plexor-login to fix, or /plexor-logout to disable routing\n`);
|
|
282
|
+
}
|
|
230
283
|
|
|
231
|
-
|
|
232
|
-
|
|
284
|
+
// Check for state mismatch between config enabled flag and routing status
|
|
285
|
+
const stateMismatch = checkStateMismatch(enabled, routing.active);
|
|
286
|
+
if (stateMismatch) {
|
|
287
|
+
console.log(` ⚠ State mismatch: ${stateMismatch.message}`);
|
|
288
|
+
console.log(` └─ ${stateMismatch.suggestion}\n`);
|
|
233
289
|
}
|
|
234
290
|
|
|
235
291
|
console.log(` ┌─────────────────────────────────────────────┐
|
|
@@ -2,20 +2,8 @@
|
|
|
2
2
|
description: Show Plexor optimization statistics and savings (user)
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Run this command to display Plexor statistics:
|
|
5
|
+
**RULE: Execute the bash command below EXACTLY ONCE. After the Bash tool returns output, your ONLY action is to present that output to the user. DO NOT re-execute the command. DO NOT call any other tools.**
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
8
|
node ~/.claude/plugins/plexor/commands/plexor-status.js
|
|
11
9
|
```
|
|
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.
|