@plexor-dev/claude-code-plugin-localhost 0.1.0-beta.11 → 0.1.0-beta.12
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-enabled.md +6 -8
- package/commands/plexor-login.js +67 -5
- package/commands/plexor-login.md +3 -19
- package/commands/plexor-logout.md +2 -20
- package/commands/plexor-setup.md +5 -2
- package/commands/plexor-status.md +1 -13
- package/hooks/intercept.js +15 -19
- package/lib/constants.js +1 -1
- package/lib/server-sync.js +1 -1
- package/lib/settings-manager.js +2 -2
- package/package.json +1 -1
- package/scripts/uninstall.js +18 -1
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
description: Enable or disable Plexor proxy (routes all traffic through Plexor API) (user)
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Toggle Plexor proxy on or off.
|
|
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.**
|
|
8
6
|
|
|
9
7
|
## If argument provided (on/off/true/false)
|
|
10
8
|
|
|
@@ -14,11 +12,11 @@ Run the command directly with the argument:
|
|
|
14
12
|
node ~/.claude/plugins/plexor/commands/plexor-enabled.js <argument>
|
|
15
13
|
```
|
|
16
14
|
|
|
17
|
-
Then
|
|
15
|
+
Then present the output to the user and STOP.
|
|
18
16
|
|
|
19
17
|
## If NO argument provided
|
|
20
18
|
|
|
21
|
-
**Step 1**:
|
|
19
|
+
**Step 1**: Get current status by running:
|
|
22
20
|
|
|
23
21
|
```bash
|
|
24
22
|
node ~/.claude/plugins/plexor/commands/plexor-enabled.js
|
|
@@ -26,14 +24,14 @@ node ~/.claude/plugins/plexor/commands/plexor-enabled.js
|
|
|
26
24
|
|
|
27
25
|
**Step 2**: Based on the output, use AskUserQuestion to present options:
|
|
28
26
|
|
|
29
|
-
If currently ENABLED (shows "
|
|
27
|
+
If currently ENABLED (shows "Enabled" or "Active"):
|
|
30
28
|
- Question: "Plexor is currently ON. What would you like to do?"
|
|
31
29
|
- Header: "Plexor"
|
|
32
30
|
- Options:
|
|
33
31
|
1. **Turn OFF** - Disable Plexor routing (connect directly to Anthropic)
|
|
34
32
|
2. **Keep ON** - No change
|
|
35
33
|
|
|
36
|
-
If currently DISABLED (shows "
|
|
34
|
+
If currently DISABLED (shows "Disabled" or "Inactive"):
|
|
37
35
|
- Question: "Plexor is currently OFF. What would you like to do?"
|
|
38
36
|
- Header: "Plexor"
|
|
39
37
|
- Options:
|
|
@@ -45,4 +43,4 @@ If currently DISABLED (shows "○ Disabled" or "○ Inactive"):
|
|
|
45
43
|
- If "Turn ON" selected: Run `node ~/.claude/plugins/plexor/commands/plexor-enabled.js true`
|
|
46
44
|
- If "Keep" selected: Do nothing, just confirm current state
|
|
47
45
|
|
|
48
|
-
|
|
46
|
+
After completing all steps, STOP. DO NOT restart the workflow.
|
package/commands/plexor-login.js
CHANGED
|
@@ -76,6 +76,18 @@ function saveConfig(config) {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
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
|
+
|
|
79
91
|
function validateApiKey(apiUrl, apiKey) {
|
|
80
92
|
return new Promise((resolve, reject) => {
|
|
81
93
|
const url = new URL(`${apiUrl}/v1/user`);
|
|
@@ -119,6 +131,56 @@ function validateApiKey(apiUrl, apiKey) {
|
|
|
119
131
|
});
|
|
120
132
|
}
|
|
121
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
|
+
|
|
122
184
|
/**
|
|
123
185
|
* Read API key from stdin (for piped input)
|
|
124
186
|
* @returns {Promise<string>} The API key read from stdin
|
|
@@ -220,10 +282,10 @@ async function main() {
|
|
|
220
282
|
console.log('');
|
|
221
283
|
}
|
|
222
284
|
|
|
223
|
-
// Validate key
|
|
224
|
-
if (!apiKey
|
|
225
|
-
console.error(`Error: Invalid
|
|
226
|
-
console.error(`
|
|
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...")`);
|
|
227
289
|
process.exit(1);
|
|
228
290
|
}
|
|
229
291
|
|
|
@@ -232,7 +294,7 @@ async function main() {
|
|
|
232
294
|
console.log('Validating API key...');
|
|
233
295
|
|
|
234
296
|
try {
|
|
235
|
-
const user = await
|
|
297
|
+
const user = await validateCredential(apiUrl, apiKey);
|
|
236
298
|
|
|
237
299
|
config.auth = config.auth || {};
|
|
238
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.
|
|
@@ -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.
|
|
@@ -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.
|
package/hooks/intercept.js
CHANGED
|
@@ -179,6 +179,17 @@ async function main() {
|
|
|
179
179
|
input = await readStdin();
|
|
180
180
|
request = JSON.parse(input);
|
|
181
181
|
|
|
182
|
+
// Issue #2042: Check for slash commands FIRST, before ANY other processing
|
|
183
|
+
// Slash commands must pass through completely clean — no metadata injection
|
|
184
|
+
// Adding _plexor_client or _plexor to slash command requests adds context noise
|
|
185
|
+
// that causes the model to re-execute commands in a loop
|
|
186
|
+
// Note: session.recordPassthrough() intentionally omitted — slash commands are
|
|
187
|
+
// not API requests and should not pollute session analytics
|
|
188
|
+
if (isSlashCommand(request)) {
|
|
189
|
+
logger.debug('Slash command detected, clean passthrough (no metadata)');
|
|
190
|
+
return output(request); // Completely clean — no metadata added
|
|
191
|
+
}
|
|
192
|
+
|
|
182
193
|
// Phase 3 Hypervisor Mode Detection
|
|
183
194
|
// When ANTHROPIC_BASE_URL points to Plexor, all intelligence is server-side
|
|
184
195
|
// The plugin just passes through - server handles optimization, routing, quality
|
|
@@ -202,25 +213,6 @@ async function main() {
|
|
|
202
213
|
});
|
|
203
214
|
}
|
|
204
215
|
|
|
205
|
-
// CRITICAL: Check for slash commands FIRST (before agentic check)
|
|
206
|
-
// Slash commands like /plexor-status should pass through unchanged
|
|
207
|
-
// Must check before isAgenticRequest since all Claude Code requests have tools
|
|
208
|
-
if (isSlashCommand(request)) {
|
|
209
|
-
logger.debug('Slash command detected, passing through unchanged');
|
|
210
|
-
session.recordPassthrough();
|
|
211
|
-
return output({
|
|
212
|
-
...request,
|
|
213
|
-
plexor_cwd: process.cwd(),
|
|
214
|
-
_plexor: {
|
|
215
|
-
request_id: generateRequestId('slash'), // Issue #701: Add request_id for tracking
|
|
216
|
-
source: 'passthrough_slash_command',
|
|
217
|
-
reason: 'slash_command_detected',
|
|
218
|
-
cwd: process.cwd(),
|
|
219
|
-
latency_ms: Date.now() - startTime
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
|
|
224
216
|
// CRITICAL: Skip optimization for CLI commands requiring tool execution
|
|
225
217
|
// Azure CLI, AWS CLI, kubectl, etc. need tools to be preserved
|
|
226
218
|
if (requiresToolExecution(request)) {
|
|
@@ -578,12 +570,16 @@ function isSlashCommand(request) {
|
|
|
578
570
|
}
|
|
579
571
|
|
|
580
572
|
// Check for system messages with skill instructions
|
|
573
|
+
// Issue #2042: Updated to match new RULE-based .md format (old H1 headers removed)
|
|
581
574
|
for (const msg of messages) {
|
|
582
575
|
if (msg.role === 'system') {
|
|
583
576
|
const content = typeof msg.content === 'string' ? msg.content : '';
|
|
584
577
|
if (/# Plexor (?:Status|Login|Logout|Mode|Provider|Enabled|Settings)/i.test(content)) {
|
|
585
578
|
return true;
|
|
586
579
|
}
|
|
580
|
+
if (/plexor\/commands\/plexor-/i.test(content)) {
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
587
583
|
}
|
|
588
584
|
}
|
|
589
585
|
|
package/lib/constants.js
CHANGED
|
@@ -28,7 +28,7 @@ const CACHE_PATH = path.join(PLEXOR_DIR, 'cache.json');
|
|
|
28
28
|
const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
|
29
29
|
|
|
30
30
|
// STAGING PACKAGE - uses staging API
|
|
31
|
-
const DEFAULT_API_URL = '
|
|
31
|
+
const DEFAULT_API_URL = 'LOCALHOST_TUNNEL_URL';
|
|
32
32
|
const DEFAULT_TIMEOUT = 5000;
|
|
33
33
|
|
|
34
34
|
module.exports = {
|
package/lib/server-sync.js
CHANGED
|
@@ -13,7 +13,7 @@ const http = require('http');
|
|
|
13
13
|
class ServerSync {
|
|
14
14
|
constructor(options = {}) {
|
|
15
15
|
this.apiKey = options.apiKey;
|
|
16
|
-
this.baseUrl = options.baseUrl || '
|
|
16
|
+
this.baseUrl = options.baseUrl || 'https://api.plexor.dev';
|
|
17
17
|
this.timeout = options.timeout || 5000;
|
|
18
18
|
this.enabled = options.enabled !== false;
|
|
19
19
|
this.lastSyncTime = null;
|
package/lib/settings-manager.js
CHANGED
|
@@ -20,8 +20,8 @@ const SETTINGS_PATH = path.join(CLAUDE_DIR, 'settings.json');
|
|
|
20
20
|
const LOCK_TIMEOUT_MS = 5000; // 5 second lock timeout
|
|
21
21
|
|
|
22
22
|
// Plexor gateway endpoints
|
|
23
|
-
const PLEXOR_STAGING_URL = '
|
|
24
|
-
const PLEXOR_PROD_URL = '
|
|
23
|
+
const PLEXOR_STAGING_URL = 'LOCALHOST_TUNNEL_URL/gateway/anthropic';
|
|
24
|
+
const PLEXOR_PROD_URL = 'https://api.plexor.dev/gateway/anthropic';
|
|
25
25
|
|
|
26
26
|
class ClaudeSettingsManager {
|
|
27
27
|
constructor() {
|
package/package.json
CHANGED
package/scripts/uninstall.js
CHANGED
|
@@ -60,7 +60,19 @@ function main() {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
// Remove plugin directory (~/.claude/plugins/plexor/)
|
|
64
|
+
let pluginDirRemoved = false;
|
|
65
|
+
try {
|
|
66
|
+
const pluginDir = path.join(os.homedir(), '.claude', 'plugins', 'plexor');
|
|
67
|
+
if (fs.existsSync(pluginDir)) {
|
|
68
|
+
fs.rmSync(pluginDir, { recursive: true, force: true });
|
|
69
|
+
pluginDirRemoved = true;
|
|
70
|
+
}
|
|
71
|
+
} catch (e) {
|
|
72
|
+
console.warn(` Warning: Could not remove plugin directory: ${e.message}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (removed.length > 0 || routingDisabled || pluginDirRemoved) {
|
|
64
76
|
console.log('');
|
|
65
77
|
console.log(' Plexor plugin uninstalled');
|
|
66
78
|
console.log('');
|
|
@@ -76,6 +88,11 @@ function main() {
|
|
|
76
88
|
removed.forEach(cmd => console.log(` /${cmd}`));
|
|
77
89
|
}
|
|
78
90
|
|
|
91
|
+
if (pluginDirRemoved) {
|
|
92
|
+
console.log('');
|
|
93
|
+
console.log(' ✓ Removed plugin directory (~/.claude/plugins/plexor/)');
|
|
94
|
+
}
|
|
95
|
+
|
|
79
96
|
if (restored.length > 0) {
|
|
80
97
|
console.log('');
|
|
81
98
|
console.log(' Restored from backup:');
|