@plexor-dev/claude-code-plugin-localhost 0.1.0-localhost.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 +114 -0
- package/commands/plexor-enabled.js +281 -0
- package/commands/plexor-enabled.md +48 -0
- package/commands/plexor-login.js +283 -0
- package/commands/plexor-login.md +27 -0
- package/commands/plexor-logout.js +143 -0
- package/commands/plexor-logout.md +27 -0
- package/commands/plexor-setup.md +172 -0
- package/commands/plexor-status.js +404 -0
- package/commands/plexor-status.md +21 -0
- package/commands/plexor-uninstall.js +293 -0
- package/commands/plexor-uninstall.md +30 -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 +44 -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 +354 -0
- package/package.json +57 -0
- package/scripts/plexor-cli.sh +48 -0
- package/scripts/postinstall.js +342 -0
- package/scripts/uninstall.js +154 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plexor Login Command
|
|
5
|
+
* Authenticate with Plexor API and auto-configure Claude Code routing
|
|
6
|
+
*
|
|
7
|
+
* KEY FEATURE: After successful login, automatically configures ~/.claude/settings.json
|
|
8
|
+
* to route ALL Claude Code sessions through Plexor gateway.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const https = require('https');
|
|
14
|
+
const http = require('http');
|
|
15
|
+
|
|
16
|
+
// Import centralized constants with HOME directory validation
|
|
17
|
+
const { PLEXOR_DIR, CONFIG_PATH, DEFAULT_API_URL } = require('../lib/constants');
|
|
18
|
+
|
|
19
|
+
// Import settings manager with error handling for missing lib
|
|
20
|
+
let settingsManager;
|
|
21
|
+
try {
|
|
22
|
+
settingsManager = require('../lib/settings-manager').settingsManager;
|
|
23
|
+
} catch (err) {
|
|
24
|
+
if (err.code === 'MODULE_NOT_FOUND') {
|
|
25
|
+
console.error('Error: Plexor plugin files are missing or corrupted.');
|
|
26
|
+
console.error(' Please reinstall: npm install @plexor-dev/claude-code-plugin-localhost');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function loadConfig() {
|
|
33
|
+
try {
|
|
34
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
35
|
+
return { version: 1, auth: {}, settings: {} };
|
|
36
|
+
}
|
|
37
|
+
const data = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
38
|
+
if (!data || data.trim() === '') {
|
|
39
|
+
return { version: 1, auth: {}, settings: {} };
|
|
40
|
+
}
|
|
41
|
+
const config = JSON.parse(data);
|
|
42
|
+
if (typeof config !== 'object' || config === null) {
|
|
43
|
+
return { version: 1, auth: {}, settings: {} };
|
|
44
|
+
}
|
|
45
|
+
return config;
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (err instanceof SyntaxError) {
|
|
48
|
+
console.warn('Warning: Config file is corrupted, will be overwritten');
|
|
49
|
+
}
|
|
50
|
+
return { version: 1, auth: {}, settings: {} };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function saveConfig(config) {
|
|
55
|
+
try {
|
|
56
|
+
if (!fs.existsSync(PLEXOR_DIR)) {
|
|
57
|
+
fs.mkdirSync(PLEXOR_DIR, { recursive: true, mode: 0o700 });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Atomic write: write to temp file, then rename
|
|
61
|
+
const crypto = require('crypto');
|
|
62
|
+
const tempId = crypto.randomBytes(8).toString('hex');
|
|
63
|
+
const tempPath = path.join(PLEXOR_DIR, `.config.${tempId}.tmp`);
|
|
64
|
+
|
|
65
|
+
fs.writeFileSync(tempPath, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
66
|
+
fs.renameSync(tempPath, CONFIG_PATH);
|
|
67
|
+
return true;
|
|
68
|
+
} catch (err) {
|
|
69
|
+
if (err.code === 'EACCES' || err.code === 'EPERM') {
|
|
70
|
+
console.error('Error: Cannot write to ~/.plexor/config.json');
|
|
71
|
+
console.error(' Check file permissions or run with appropriate access.');
|
|
72
|
+
} else {
|
|
73
|
+
console.error('Failed to save config:', err.message);
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function validateApiKey(apiUrl, apiKey) {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const url = new URL(`${apiUrl}/v1/user`);
|
|
82
|
+
const isHttps = url.protocol === 'https:';
|
|
83
|
+
const lib = isHttps ? https : http;
|
|
84
|
+
|
|
85
|
+
const options = {
|
|
86
|
+
hostname: url.hostname,
|
|
87
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
88
|
+
path: url.pathname,
|
|
89
|
+
method: 'GET',
|
|
90
|
+
headers: {
|
|
91
|
+
'X-Plexor-Key': apiKey
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const req = lib.request(options, (res) => {
|
|
96
|
+
let data = '';
|
|
97
|
+
res.on('data', chunk => data += chunk);
|
|
98
|
+
res.on('end', () => {
|
|
99
|
+
if (res.statusCode === 200) {
|
|
100
|
+
try {
|
|
101
|
+
resolve(JSON.parse(data));
|
|
102
|
+
} catch {
|
|
103
|
+
reject(new Error('Invalid response from server'));
|
|
104
|
+
}
|
|
105
|
+
} else if (res.statusCode === 401) {
|
|
106
|
+
reject(new Error('Invalid API key'));
|
|
107
|
+
} else {
|
|
108
|
+
reject(new Error(`Server error: ${res.statusCode}`));
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
req.on('error', (err) => reject(new Error(`Connection failed: ${err.message}`)));
|
|
114
|
+
req.setTimeout(10000, () => {
|
|
115
|
+
req.destroy();
|
|
116
|
+
reject(new Error('Connection timeout'));
|
|
117
|
+
});
|
|
118
|
+
req.end();
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Read API key from stdin (for piped input)
|
|
124
|
+
* @returns {Promise<string>} The API key read from stdin
|
|
125
|
+
*/
|
|
126
|
+
function readFromStdin() {
|
|
127
|
+
return new Promise((resolve, reject) => {
|
|
128
|
+
let data = '';
|
|
129
|
+
const timeout = setTimeout(() => {
|
|
130
|
+
reject(new Error('Timeout reading from stdin'));
|
|
131
|
+
}, 5000);
|
|
132
|
+
|
|
133
|
+
process.stdin.setEncoding('utf8');
|
|
134
|
+
process.stdin.on('data', (chunk) => {
|
|
135
|
+
data += chunk;
|
|
136
|
+
});
|
|
137
|
+
process.stdin.on('end', () => {
|
|
138
|
+
clearTimeout(timeout);
|
|
139
|
+
resolve(data.trim());
|
|
140
|
+
});
|
|
141
|
+
process.stdin.on('error', (err) => {
|
|
142
|
+
clearTimeout(timeout);
|
|
143
|
+
reject(err);
|
|
144
|
+
});
|
|
145
|
+
process.stdin.resume();
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function main() {
|
|
150
|
+
const args = process.argv.slice(2);
|
|
151
|
+
let apiKey = null;
|
|
152
|
+
let keySource = null;
|
|
153
|
+
|
|
154
|
+
// SECURITY FIX: Check for API key in order of preference
|
|
155
|
+
// 1. Environment variable (most secure for scripts/CI)
|
|
156
|
+
// 2. Stdin pipe (secure for interactive use)
|
|
157
|
+
// 3. Command line argument (warns about security risk)
|
|
158
|
+
|
|
159
|
+
// Check environment variable first (most secure)
|
|
160
|
+
if (process.env.PLEXOR_API_KEY) {
|
|
161
|
+
apiKey = process.env.PLEXOR_API_KEY;
|
|
162
|
+
keySource = 'environment';
|
|
163
|
+
}
|
|
164
|
+
// Check if stdin has data (piped input)
|
|
165
|
+
else if (!process.stdin.isTTY && args.length === 0) {
|
|
166
|
+
try {
|
|
167
|
+
apiKey = await readFromStdin();
|
|
168
|
+
keySource = 'stdin';
|
|
169
|
+
} catch {
|
|
170
|
+
// Stdin read failed, continue to check args
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Check command line argument (least secure - shows in ps)
|
|
174
|
+
else if (args[0]) {
|
|
175
|
+
apiKey = args[0];
|
|
176
|
+
keySource = 'argument';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Check for existing login
|
|
180
|
+
const config = loadConfig();
|
|
181
|
+
const existingKey = config.auth?.api_key;
|
|
182
|
+
|
|
183
|
+
if (existingKey && !apiKey) {
|
|
184
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
185
|
+
console.log(`│ Already Logged In │`);
|
|
186
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
187
|
+
console.log(`│ API Key: ${(existingKey.substring(0, 8) + '...').padEnd(33)}│`);
|
|
188
|
+
console.log(`│ To re-login, provide a new key: │`);
|
|
189
|
+
console.log(`│ echo $PLEXOR_API_KEY | /plexor-login │`);
|
|
190
|
+
console.log(`│ To logout: │`);
|
|
191
|
+
console.log(`│ /plexor-logout │`);
|
|
192
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// If no key provided, show secure usage options
|
|
197
|
+
if (!apiKey) {
|
|
198
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
199
|
+
console.log(`│ Plexor Login │`);
|
|
200
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
201
|
+
console.log(`│ Get your API key at: │`);
|
|
202
|
+
console.log(`│ https://plexor.dev/dashboard/api-keys │`);
|
|
203
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
204
|
+
console.log(`│ Secure usage (recommended): │`);
|
|
205
|
+
console.log(`│ echo "plx_..." | /plexor-login │`);
|
|
206
|
+
console.log(`│ PLEXOR_API_KEY=plx_... /plexor-login │`);
|
|
207
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
208
|
+
console.log(`│ Direct usage (visible in ps): │`);
|
|
209
|
+
console.log(`│ /plexor-login <api-key> │`);
|
|
210
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Warn if API key was passed as command line argument
|
|
215
|
+
if (keySource === 'argument') {
|
|
216
|
+
console.log(`⚠ Security note: API key passed as argument is visible in`);
|
|
217
|
+
console.log(` process list (ps aux). For better security, use:`);
|
|
218
|
+
console.log(` echo "plx_..." | /plexor-login`);
|
|
219
|
+
console.log(` PLEXOR_API_KEY=plx_... /plexor-login`);
|
|
220
|
+
console.log('');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Validate key format
|
|
224
|
+
if (!apiKey.startsWith('plx_') || apiKey.length < 20) {
|
|
225
|
+
console.error(`Error: Invalid API key format`);
|
|
226
|
+
console.error(`API keys start with "plx_" and are at least 20 characters`);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const apiUrl = config.settings?.apiUrl || DEFAULT_API_URL;
|
|
231
|
+
|
|
232
|
+
console.log('Validating API key...');
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
const user = await validateApiKey(apiUrl, apiKey);
|
|
236
|
+
|
|
237
|
+
config.auth = config.auth || {};
|
|
238
|
+
config.auth.api_key = apiKey;
|
|
239
|
+
config.settings = config.settings || {};
|
|
240
|
+
config.settings.enabled = true;
|
|
241
|
+
if (!saveConfig(config)) {
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// AUTO-CONFIGURE CLAUDE CODE ROUTING
|
|
246
|
+
// This is the key feature: automatically set ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN
|
|
247
|
+
// in ~/.claude/settings.json so ALL Claude Code sessions route through Plexor
|
|
248
|
+
const useStaging = apiUrl.includes('staging');
|
|
249
|
+
const routingEnabled = settingsManager.enablePlexorRouting(apiKey, { useStaging });
|
|
250
|
+
|
|
251
|
+
const email = user.email || 'Unknown';
|
|
252
|
+
const tier = user.tier?.name || 'Free';
|
|
253
|
+
|
|
254
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
255
|
+
console.log(`│ ✓ Login Successful │`);
|
|
256
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
257
|
+
console.log(`│ Email: ${email.substring(0, 35).padEnd(35)}│`);
|
|
258
|
+
console.log(`│ Tier: ${tier.padEnd(36)}│`);
|
|
259
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
260
|
+
if (routingEnabled) {
|
|
261
|
+
console.log(`│ ✓ Claude Code routing: CONFIGURED │`);
|
|
262
|
+
console.log(`│ All sessions now route through Plexor │`);
|
|
263
|
+
} else {
|
|
264
|
+
console.log(`│ ⚠ Claude Code routing: MANUAL SETUP NEEDED │`);
|
|
265
|
+
console.log(`│ Set ANTHROPIC_BASE_URL in environment │`);
|
|
266
|
+
}
|
|
267
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
268
|
+
console.log(`│ Run /plexor-status to see your stats. │`);
|
|
269
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
270
|
+
} catch (err) {
|
|
271
|
+
console.error(`┌─────────────────────────────────────────────┐`);
|
|
272
|
+
console.error(`│ ✗ Login Failed │`);
|
|
273
|
+
console.error(`├─────────────────────────────────────────────┤`);
|
|
274
|
+
console.error(`│ ${err.message.padEnd(42)}│`);
|
|
275
|
+
console.error(`└─────────────────────────────────────────────┘`);
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
main().catch(err => {
|
|
281
|
+
console.error('Error:', err.message);
|
|
282
|
+
process.exit(1);
|
|
283
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Authenticate with Plexor to enable optimization (user)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Plexor Login
|
|
6
|
+
|
|
7
|
+
Run this command to authenticate with Plexor:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
node ~/.claude/plugins/plexor/commands/plexor-login.js
|
|
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.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plexor Logout Command
|
|
5
|
+
* Clear Plexor credentials and disable proxy
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
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-staging');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function loadConfig() {
|
|
29
|
+
try {
|
|
30
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const data = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
34
|
+
if (!data || data.trim() === '') {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const config = JSON.parse(data);
|
|
38
|
+
if (typeof config !== 'object' || config === null) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return config;
|
|
42
|
+
} catch (err) {
|
|
43
|
+
if (err instanceof SyntaxError) {
|
|
44
|
+
console.warn('Warning: Config file is corrupted');
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function saveConfig(config) {
|
|
51
|
+
try {
|
|
52
|
+
if (!fs.existsSync(PLEXOR_DIR)) {
|
|
53
|
+
fs.mkdirSync(PLEXOR_DIR, { recursive: true, mode: 0o700 });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Atomic write: write to temp file, then rename
|
|
57
|
+
const crypto = require('crypto');
|
|
58
|
+
const tempId = crypto.randomBytes(8).toString('hex');
|
|
59
|
+
const tempPath = path.join(PLEXOR_DIR, `.config.${tempId}.tmp`);
|
|
60
|
+
|
|
61
|
+
fs.writeFileSync(tempPath, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
62
|
+
fs.renameSync(tempPath, CONFIG_PATH);
|
|
63
|
+
return true;
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (err.code === 'EACCES' || err.code === 'EPERM') {
|
|
66
|
+
console.error('Error: Cannot write to ~/.plexor/config.json');
|
|
67
|
+
console.error(' Check file permissions or run with appropriate access.');
|
|
68
|
+
} else {
|
|
69
|
+
console.error('Failed to save config:', err.message);
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function deleteFile(filePath) {
|
|
76
|
+
try {
|
|
77
|
+
if (fs.existsSync(filePath)) {
|
|
78
|
+
fs.unlinkSync(filePath);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
// Ignore errors
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function main() {
|
|
88
|
+
const args = process.argv.slice(2);
|
|
89
|
+
const config = loadConfig();
|
|
90
|
+
|
|
91
|
+
if (!config || !config.auth?.api_key) {
|
|
92
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
93
|
+
console.log(`│ Not Logged In │`);
|
|
94
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
95
|
+
console.log(`│ No active Plexor session found. │`);
|
|
96
|
+
console.log(`│ Run /plexor-login to authenticate. │`);
|
|
97
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const clearCache = args.includes('--clear-cache') || args.includes('-c');
|
|
102
|
+
|
|
103
|
+
// Clear credentials
|
|
104
|
+
delete config.auth.api_key;
|
|
105
|
+
config.settings = config.settings || {};
|
|
106
|
+
config.settings.enabled = false;
|
|
107
|
+
if (!saveConfig(config)) {
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
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
|
+
|
|
116
|
+
// Clear session
|
|
117
|
+
deleteFile(SESSION_PATH);
|
|
118
|
+
|
|
119
|
+
// Optionally clear cache
|
|
120
|
+
let cacheCleared = false;
|
|
121
|
+
if (clearCache) {
|
|
122
|
+
cacheCleared = deleteFile(CACHE_PATH);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
126
|
+
console.log(`│ ✓ Logged Out │`);
|
|
127
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
128
|
+
console.log(`│ ✓ API key removed │`);
|
|
129
|
+
console.log(`│ ${routingDisabled ? '✓' : '○'} Claude Code routing disabled │`);
|
|
130
|
+
console.log(`│ ✓ Session cleared │`);
|
|
131
|
+
if (clearCache) {
|
|
132
|
+
console.log(`│ ${cacheCleared ? '✓' : '○'} Cache cleared │`);
|
|
133
|
+
}
|
|
134
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
135
|
+
console.log(`│ Claude Code now connects directly. │`);
|
|
136
|
+
console.log(`│ Run /plexor-login to re-authenticate. │`);
|
|
137
|
+
if (!clearCache) {
|
|
138
|
+
console.log(`│ Use --clear-cache to also clear cache. │`);
|
|
139
|
+
}
|
|
140
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
main();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Log out from Plexor and clear credentials (user)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Plexor Logout
|
|
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:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
node ~/.claude/plugins/plexor/commands/plexor-logout.js --clear-cache
|
|
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.
|
|
@@ -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": "http://localhost:8000",
|
|
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": "http://localhost:8000/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": "http://localhost:8000",
|
|
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": "http://localhost:8000/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.
|