@plexor-dev/claude-code-plugin 0.1.0-beta.20 → 0.1.0-beta.22
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-config.js +170 -0
- package/commands/plexor-config.md +15 -31
- package/commands/plexor-enabled.js +91 -0
- package/commands/plexor-enabled.md +14 -42
- package/commands/plexor-login.js +169 -0
- package/commands/plexor-login.md +13 -72
- package/commands/plexor-logout.js +92 -0
- package/commands/plexor-logout.md +13 -30
- package/commands/plexor-mode.js +107 -0
- package/commands/plexor-mode.md +14 -36
- package/commands/plexor-provider.js +110 -0
- package/commands/plexor-provider.md +14 -36
- package/commands/plexor-settings.js +155 -0
- package/commands/plexor-settings.md +14 -46
- package/commands/plexor-status.js +0 -0
- package/lib/plexor-client.js +1 -1
- package/package.json +11 -1
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plexor Config Command
|
|
5
|
+
* Display raw configuration (for debugging)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const CONFIG_PATH = path.join(process.env.HOME, '.plexor', 'config.json');
|
|
12
|
+
const SESSION_PATH = path.join(process.env.HOME, '.plexor', 'session.json');
|
|
13
|
+
const CACHE_PATH = path.join(process.env.HOME, '.plexor', 'cache.json');
|
|
14
|
+
const PLEXOR_DIR = path.join(process.env.HOME, '.plexor');
|
|
15
|
+
|
|
16
|
+
function fileExists(filePath) {
|
|
17
|
+
try {
|
|
18
|
+
return fs.existsSync(filePath);
|
|
19
|
+
} catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getFileSize(filePath) {
|
|
25
|
+
try {
|
|
26
|
+
const stats = fs.statSync(filePath);
|
|
27
|
+
return stats.size;
|
|
28
|
+
} catch {
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function formatBytes(bytes) {
|
|
34
|
+
if (bytes === 0) return '0 B';
|
|
35
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
36
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
37
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function loadConfig() {
|
|
41
|
+
try {
|
|
42
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
43
|
+
return { error: 'not_found', message: 'Config file does not exist' };
|
|
44
|
+
}
|
|
45
|
+
const data = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
46
|
+
return { config: JSON.parse(data) };
|
|
47
|
+
} catch (err) {
|
|
48
|
+
if (err.code === 'ENOENT') {
|
|
49
|
+
return { error: 'not_found', message: 'Config file does not exist' };
|
|
50
|
+
}
|
|
51
|
+
if (err.code === 'EACCES') {
|
|
52
|
+
return { error: 'permission', message: 'Permission denied reading config' };
|
|
53
|
+
}
|
|
54
|
+
if (err instanceof SyntaxError) {
|
|
55
|
+
return { error: 'invalid_json', message: 'Config file contains invalid JSON' };
|
|
56
|
+
}
|
|
57
|
+
return { error: 'unknown', message: err.message };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function maskApiKey(key) {
|
|
62
|
+
if (!key) return 'not set';
|
|
63
|
+
if (key.length <= 12) return '***masked***';
|
|
64
|
+
return key.substring(0, 8) + '...' + key.substring(key.length - 4);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function main() {
|
|
68
|
+
const args = process.argv.slice(2);
|
|
69
|
+
const showRaw = args.includes('--raw') || args.includes('-r');
|
|
70
|
+
const showPaths = args.includes('--paths') || args.includes('-p');
|
|
71
|
+
|
|
72
|
+
// Show paths only
|
|
73
|
+
if (showPaths) {
|
|
74
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
75
|
+
console.log(`│ Plexor File Paths │`);
|
|
76
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
77
|
+
console.log(`│ Directory: ${PLEXOR_DIR.substring(0, 31).padEnd(31)}│`);
|
|
78
|
+
console.log(`│ Config: ${CONFIG_PATH.substring(0, 34).padEnd(34)}│`);
|
|
79
|
+
console.log(`│ Session: ${SESSION_PATH.substring(0, 33).padEnd(33)}│`);
|
|
80
|
+
console.log(`│ Cache: ${CACHE_PATH.substring(0, 35).padEnd(35)}│`);
|
|
81
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const result = loadConfig();
|
|
86
|
+
|
|
87
|
+
if (result.error) {
|
|
88
|
+
const errorMessages = {
|
|
89
|
+
not_found: 'Config file does not exist',
|
|
90
|
+
permission: 'Permission denied reading config file',
|
|
91
|
+
invalid_json: 'Config file contains invalid JSON (corrupted?)',
|
|
92
|
+
unknown: result.message || 'Unknown error'
|
|
93
|
+
};
|
|
94
|
+
const errorMsg = errorMessages[result.error] || result.message;
|
|
95
|
+
const suggestion = result.error === 'invalid_json'
|
|
96
|
+
? 'Try: rm ~/.plexor/config.json && /plexor-login'
|
|
97
|
+
: 'Run /plexor-login to create configuration.';
|
|
98
|
+
|
|
99
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
100
|
+
console.log(`│ Configuration Error │`);
|
|
101
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
102
|
+
console.log(`│ Error: ${result.error.padEnd(35)}│`);
|
|
103
|
+
console.log(`│ ${errorMsg.substring(0, 42).padEnd(42)}│`);
|
|
104
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
105
|
+
console.log(`│ ${suggestion.substring(0, 42).padEnd(42)}│`);
|
|
106
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
107
|
+
console.log(`│ Expected path: │`);
|
|
108
|
+
console.log(`│ ${CONFIG_PATH.substring(0, 42).padEnd(42)}│`);
|
|
109
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const config = result.config;
|
|
114
|
+
|
|
115
|
+
// Show raw JSON
|
|
116
|
+
if (showRaw) {
|
|
117
|
+
// Mask API key in raw output
|
|
118
|
+
const safeConfig = JSON.parse(JSON.stringify(config));
|
|
119
|
+
if (safeConfig.auth?.api_key) {
|
|
120
|
+
safeConfig.auth.api_key = maskApiKey(safeConfig.auth.api_key);
|
|
121
|
+
}
|
|
122
|
+
console.log(JSON.stringify(safeConfig, null, 2));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Show formatted config info
|
|
127
|
+
const configSize = getFileSize(CONFIG_PATH);
|
|
128
|
+
const sessionSize = getFileSize(SESSION_PATH);
|
|
129
|
+
const cacheSize = getFileSize(CACHE_PATH);
|
|
130
|
+
const hasSession = fileExists(SESSION_PATH);
|
|
131
|
+
const hasCache = fileExists(CACHE_PATH);
|
|
132
|
+
|
|
133
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
134
|
+
console.log(`│ Plexor Configuration │`);
|
|
135
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
136
|
+
console.log(`│ Version: ${(config.version || 1).toString().padEnd(33)}│`);
|
|
137
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
138
|
+
console.log(`│ Authentication │`);
|
|
139
|
+
console.log(`│ └── API Key: ${maskApiKey(config.auth?.api_key).padEnd(29)}│`);
|
|
140
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
141
|
+
console.log(`│ Settings │`);
|
|
142
|
+
|
|
143
|
+
const settings = config.settings || {};
|
|
144
|
+
const settingEntries = [
|
|
145
|
+
['enabled', settings.enabled ?? false],
|
|
146
|
+
['mode', settings.mode || 'balanced'],
|
|
147
|
+
['preferred_provider', settings.preferred_provider || 'auto'],
|
|
148
|
+
['apiUrl', settings.apiUrl || 'https://api.plexor.dev'],
|
|
149
|
+
['timeout', (settings.timeout || 5000) + 'ms'],
|
|
150
|
+
['localCacheEnabled', settings.localCacheEnabled ?? false]
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
for (const [key, value] of settingEntries) {
|
|
154
|
+
const displayValue = String(value).substring(0, 25);
|
|
155
|
+
console.log(`│ ├── ${key.padEnd(18)} ${displayValue.padEnd(18)}│`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
159
|
+
console.log(`│ Files │`);
|
|
160
|
+
console.log(`│ ├── config.json: ${formatBytes(configSize).padEnd(24)}│`);
|
|
161
|
+
console.log(`│ ├── session.json: ${(hasSession ? formatBytes(sessionSize) : 'not found').padEnd(23)}│`);
|
|
162
|
+
console.log(`│ └── cache.json: ${(hasCache ? formatBytes(cacheSize) : 'not found').padEnd(25)}│`);
|
|
163
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
164
|
+
console.log(`│ Options: │`);
|
|
165
|
+
console.log(`│ /plexor-config --raw Show raw JSON │`);
|
|
166
|
+
console.log(`│ /plexor-config --paths Show file paths │`);
|
|
167
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
main();
|
|
@@ -4,41 +4,25 @@ description: Configure Plexor settings (user)
|
|
|
4
4
|
|
|
5
5
|
# Plexor Config
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Run this command to view configuration details:
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
```bash
|
|
10
|
+
node ~/.claude/plugins/plexor/commands/plexor-config.js
|
|
11
|
+
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Options:
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
=============
|
|
19
|
-
No configuration found. Run /plexor-login to set up Plexor.
|
|
15
|
+
```bash
|
|
16
|
+
node ~/.claude/plugins/plexor/commands/plexor-config.js --raw # Show raw JSON
|
|
17
|
+
node ~/.claude/plugins/plexor/commands/plexor-config.js --paths # Show file paths
|
|
20
18
|
```
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
Use the Bash tool to execute this command.
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
API Key: [show "configured" if apiKey exists, otherwise "not configured"]
|
|
30
|
-
Mode: [mode]
|
|
31
|
-
Provider: [preferredProvider]
|
|
32
|
-
Local Cache: [localCacheEnabled]
|
|
33
|
-
Timeout: [timeout]ms
|
|
34
|
-
|
|
35
|
-
Configuration file: ~/.plexor/config.json
|
|
36
|
-
|
|
37
|
-
Other commands:
|
|
38
|
-
- /plexor-settings - View/edit all settings
|
|
39
|
-
- /plexor-mode - Change optimization mode
|
|
40
|
-
- /plexor-provider - Change provider
|
|
41
|
-
- /plexor-enabled - Enable/disable proxy
|
|
42
|
-
```
|
|
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
|
|
43
27
|
|
|
44
|
-
|
|
28
|
+
The command output is the complete response. Simply show the output and wait for the user's next input.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plexor Enabled Command
|
|
5
|
+
* Enable or disable Plexor optimization proxy
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const CONFIG_PATH = path.join(process.env.HOME, '.plexor', 'config.json');
|
|
12
|
+
const PLEXOR_DIR = path.join(process.env.HOME, '.plexor');
|
|
13
|
+
|
|
14
|
+
function loadConfig() {
|
|
15
|
+
try {
|
|
16
|
+
const data = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
17
|
+
return JSON.parse(data);
|
|
18
|
+
} catch {
|
|
19
|
+
return { version: 1, auth: {}, settings: {} };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function saveConfig(config) {
|
|
24
|
+
if (!fs.existsSync(PLEXOR_DIR)) {
|
|
25
|
+
fs.mkdirSync(PLEXOR_DIR, { recursive: true, mode: 0o700 });
|
|
26
|
+
}
|
|
27
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function main() {
|
|
31
|
+
const args = process.argv.slice(2);
|
|
32
|
+
const config = loadConfig();
|
|
33
|
+
const currentEnabled = config.settings?.enabled ?? false;
|
|
34
|
+
|
|
35
|
+
// No args - show current status
|
|
36
|
+
if (args.length === 0) {
|
|
37
|
+
const status = currentEnabled ? '● Enabled' : '○ Disabled';
|
|
38
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
39
|
+
console.log(`│ Plexor Proxy Status │`);
|
|
40
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
41
|
+
console.log(`│ Status: ${status.padEnd(34)}│`);
|
|
42
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
43
|
+
console.log(`│ Usage: │`);
|
|
44
|
+
console.log(`│ /plexor-enabled true - Enable proxy │`);
|
|
45
|
+
console.log(`│ /plexor-enabled false - Disable proxy │`);
|
|
46
|
+
console.log(`│ /plexor-enabled on - Enable proxy │`);
|
|
47
|
+
console.log(`│ /plexor-enabled off - Disable proxy │`);
|
|
48
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const arg = args[0].toLowerCase();
|
|
53
|
+
let newEnabled;
|
|
54
|
+
|
|
55
|
+
if (['true', 'on', 'yes', '1', 'enable'].includes(arg)) {
|
|
56
|
+
newEnabled = true;
|
|
57
|
+
} else if (['false', 'off', 'no', '0', 'disable'].includes(arg)) {
|
|
58
|
+
newEnabled = false;
|
|
59
|
+
} else {
|
|
60
|
+
console.error(`Error: Invalid value "${args[0]}"`);
|
|
61
|
+
console.error(`Use: true/false, on/off, yes/no, enable/disable`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (newEnabled === currentEnabled) {
|
|
66
|
+
const state = currentEnabled ? 'enabled' : 'disabled';
|
|
67
|
+
console.log(`Plexor proxy is already ${state}`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
config.settings = config.settings || {};
|
|
72
|
+
config.settings.enabled = newEnabled;
|
|
73
|
+
saveConfig(config);
|
|
74
|
+
|
|
75
|
+
const newStatus = newEnabled ? '● Enabled' : '○ Disabled';
|
|
76
|
+
const prevStatus = currentEnabled ? 'Enabled' : 'Disabled';
|
|
77
|
+
const message = newEnabled
|
|
78
|
+
? 'All requests will be routed through Plexor'
|
|
79
|
+
: 'Requests will go directly to providers';
|
|
80
|
+
|
|
81
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
82
|
+
console.log(`│ ✓ Plexor Proxy Updated │`);
|
|
83
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
84
|
+
console.log(`│ Previous: ${prevStatus.padEnd(32)}│`);
|
|
85
|
+
console.log(`│ New: ${newStatus.padEnd(37)}│`);
|
|
86
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
87
|
+
console.log(`│ ${message.padEnd(42)}│`);
|
|
88
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
main();
|
|
@@ -4,53 +4,25 @@ description: Enable or disable Plexor proxy (routes all traffic through Plexor A
|
|
|
4
4
|
|
|
5
5
|
# Plexor Enabled
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Run this command to view or toggle the Plexor proxy:
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
**Step 1: Read current configuration**
|
|
12
|
-
|
|
13
|
-
Use the Read tool to read `~/.plexor/config.json` and check the current `enabled` status.
|
|
14
|
-
|
|
15
|
-
**Step 2: Ask user what they want to do**
|
|
16
|
-
|
|
17
|
-
Use the `AskUserQuestion` tool to present options:
|
|
18
|
-
|
|
19
|
-
Question: "Enable or disable Plexor proxy?"
|
|
20
|
-
Header: "Proxy"
|
|
21
|
-
Options:
|
|
22
|
-
1. **Enable** - Route traffic through Plexor for optimization (60-90% cost savings)
|
|
23
|
-
2. **Disable** - Direct to Anthropic (no optimization, full price)
|
|
24
|
-
|
|
25
|
-
**Step 3: Update the configuration**
|
|
26
|
-
|
|
27
|
-
Use the Read tool to get the current config, then use the Write tool to update `~/.plexor/config.json`:
|
|
28
|
-
- If **Enable**: Set `"enabled": true`
|
|
29
|
-
- If **Disable**: Set `"enabled": false`
|
|
30
|
-
|
|
31
|
-
Keep all other settings unchanged.
|
|
32
|
-
|
|
33
|
-
**Step 4: Show confirmation**
|
|
34
|
-
|
|
35
|
-
If **Enabled**:
|
|
9
|
+
```bash
|
|
10
|
+
node ~/.claude/plugins/plexor/commands/plexor-enabled.js
|
|
36
11
|
```
|
|
37
|
-
Plexor Proxy: ENABLED
|
|
38
12
|
|
|
39
|
-
|
|
40
|
-
- 60-90% cost reduction via intelligent provider routing
|
|
41
|
-
- Automatic selection of Mistral/DeepSeek/Gemini based on task
|
|
42
|
-
- Usage tracking and savings analytics
|
|
13
|
+
To enable or disable, pass an argument:
|
|
43
14
|
|
|
44
|
-
|
|
45
|
-
|
|
15
|
+
```bash
|
|
16
|
+
node ~/.claude/plugins/plexor/commands/plexor-enabled.js true
|
|
17
|
+
node ~/.claude/plugins/plexor/commands/plexor-enabled.js false
|
|
46
18
|
```
|
|
47
19
|
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
Plexor Proxy: DISABLED
|
|
20
|
+
Use the Bash tool to execute this command.
|
|
51
21
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
55
27
|
|
|
56
|
-
|
|
28
|
+
The command output is the complete response. Simply show the output and wait for the user's next input.
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plexor Login Command
|
|
5
|
+
* Authenticate with Plexor API
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const https = require('https');
|
|
11
|
+
const http = require('http');
|
|
12
|
+
|
|
13
|
+
const CONFIG_PATH = path.join(process.env.HOME, '.plexor', 'config.json');
|
|
14
|
+
const PLEXOR_DIR = path.join(process.env.HOME, '.plexor');
|
|
15
|
+
const DEFAULT_API_URL = 'https://api.plexor.dev';
|
|
16
|
+
|
|
17
|
+
function loadConfig() {
|
|
18
|
+
try {
|
|
19
|
+
const data = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
20
|
+
return JSON.parse(data);
|
|
21
|
+
} catch {
|
|
22
|
+
return { version: 1, auth: {}, settings: {} };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function saveConfig(config) {
|
|
27
|
+
if (!fs.existsSync(PLEXOR_DIR)) {
|
|
28
|
+
fs.mkdirSync(PLEXOR_DIR, { recursive: true, mode: 0o700 });
|
|
29
|
+
}
|
|
30
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function validateApiKey(apiUrl, apiKey) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const url = new URL(`${apiUrl}/v1/user`);
|
|
36
|
+
const isHttps = url.protocol === 'https:';
|
|
37
|
+
const lib = isHttps ? https : http;
|
|
38
|
+
|
|
39
|
+
const options = {
|
|
40
|
+
hostname: url.hostname,
|
|
41
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
42
|
+
path: url.pathname,
|
|
43
|
+
method: 'GET',
|
|
44
|
+
headers: {
|
|
45
|
+
'X-Plexor-Key': apiKey
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const req = lib.request(options, (res) => {
|
|
50
|
+
let data = '';
|
|
51
|
+
res.on('data', chunk => data += chunk);
|
|
52
|
+
res.on('end', () => {
|
|
53
|
+
if (res.statusCode === 200) {
|
|
54
|
+
try {
|
|
55
|
+
resolve(JSON.parse(data));
|
|
56
|
+
} catch {
|
|
57
|
+
reject(new Error('Invalid response from server'));
|
|
58
|
+
}
|
|
59
|
+
} else if (res.statusCode === 401) {
|
|
60
|
+
reject(new Error('Invalid API key'));
|
|
61
|
+
} else {
|
|
62
|
+
reject(new Error(`Server error: ${res.statusCode}`));
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
req.on('error', (err) => reject(new Error(`Connection failed: ${err.message}`)));
|
|
68
|
+
req.setTimeout(10000, () => {
|
|
69
|
+
req.destroy();
|
|
70
|
+
reject(new Error('Connection timeout'));
|
|
71
|
+
});
|
|
72
|
+
req.end();
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function promptForApiKey() {
|
|
77
|
+
const rl = readline.createInterface({
|
|
78
|
+
input: process.stdin,
|
|
79
|
+
output: process.stdout
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
rl.question('Enter your Plexor API key: ', (answer) => {
|
|
84
|
+
rl.close();
|
|
85
|
+
resolve(answer.trim());
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function main() {
|
|
91
|
+
const args = process.argv.slice(2);
|
|
92
|
+
let apiKey = args[0];
|
|
93
|
+
|
|
94
|
+
// Check for existing login
|
|
95
|
+
const config = loadConfig();
|
|
96
|
+
const existingKey = config.auth?.api_key;
|
|
97
|
+
|
|
98
|
+
if (existingKey && !apiKey) {
|
|
99
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
100
|
+
console.log(`│ Already Logged In │`);
|
|
101
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
102
|
+
console.log(`│ API Key: ${(existingKey.substring(0, 8) + '...').padEnd(33)}│`);
|
|
103
|
+
console.log(`│ To re-login, provide a new key: │`);
|
|
104
|
+
console.log(`│ /plexor-login <api-key> │`);
|
|
105
|
+
console.log(`│ To logout: │`);
|
|
106
|
+
console.log(`│ /plexor-logout │`);
|
|
107
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// If no key provided, prompt for it
|
|
112
|
+
if (!apiKey) {
|
|
113
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
114
|
+
console.log(`│ Plexor Login │`);
|
|
115
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
116
|
+
console.log(`│ Get your API key at: │`);
|
|
117
|
+
console.log(`│ https://plexor.dev/dashboard/api-keys │`);
|
|
118
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
119
|
+
console.log(`│ Usage: /plexor-login <api-key> │`);
|
|
120
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Validate key format
|
|
125
|
+
if (!apiKey.startsWith('plx_') || apiKey.length < 20) {
|
|
126
|
+
console.error(`Error: Invalid API key format`);
|
|
127
|
+
console.error(`API keys start with "plx_" and are at least 20 characters`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const apiUrl = config.settings?.apiUrl || DEFAULT_API_URL;
|
|
132
|
+
|
|
133
|
+
console.log('Validating API key...');
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const user = await validateApiKey(apiUrl, apiKey);
|
|
137
|
+
|
|
138
|
+
config.auth = config.auth || {};
|
|
139
|
+
config.auth.api_key = apiKey;
|
|
140
|
+
config.settings = config.settings || {};
|
|
141
|
+
config.settings.enabled = true;
|
|
142
|
+
saveConfig(config);
|
|
143
|
+
|
|
144
|
+
const email = user.email || 'Unknown';
|
|
145
|
+
const tier = user.tier?.name || 'Free';
|
|
146
|
+
|
|
147
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
148
|
+
console.log(`│ ✓ Login Successful │`);
|
|
149
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
150
|
+
console.log(`│ Email: ${email.substring(0, 35).padEnd(35)}│`);
|
|
151
|
+
console.log(`│ Tier: ${tier.padEnd(36)}│`);
|
|
152
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
153
|
+
console.log(`│ Plexor proxy is now enabled. │`);
|
|
154
|
+
console.log(`│ Run /plexor-status to see your stats. │`);
|
|
155
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
156
|
+
} catch (err) {
|
|
157
|
+
console.error(`┌─────────────────────────────────────────────┐`);
|
|
158
|
+
console.error(`│ ✗ Login Failed │`);
|
|
159
|
+
console.error(`├─────────────────────────────────────────────┤`);
|
|
160
|
+
console.error(`│ ${err.message.padEnd(42)}│`);
|
|
161
|
+
console.error(`└─────────────────────────────────────────────┘`);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
main().catch(err => {
|
|
167
|
+
console.error('Error:', err.message);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
});
|
package/commands/plexor-login.md
CHANGED
|
@@ -4,83 +4,24 @@ description: Authenticate with Plexor to enable optimization (user)
|
|
|
4
4
|
|
|
5
5
|
# Plexor Login
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Run this command to authenticate with Plexor:
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
**Step 1: Check existing authentication**
|
|
12
|
-
|
|
13
|
-
Use the Read tool to read `~/.plexor/config.json`.
|
|
14
|
-
|
|
15
|
-
If the file exists and has `auth.api_key` that starts with "plx_", the user is already authenticated. Show them:
|
|
16
|
-
```
|
|
17
|
-
Plexor Login
|
|
18
|
-
============
|
|
19
|
-
Already authenticated!
|
|
20
|
-
API Key: plx_****[last 4 chars]
|
|
21
|
-
API URL: [apiUrl from config]
|
|
22
|
-
Status: [Enabled/Disabled]
|
|
23
|
-
|
|
24
|
-
Run /plexor-status to see your usage statistics.
|
|
25
|
-
Run /plexor-logout to sign out.
|
|
9
|
+
```bash
|
|
10
|
+
node ~/.claude/plugins/plexor/commands/plexor-login.js
|
|
26
11
|
```
|
|
27
12
|
|
|
28
|
-
|
|
13
|
+
To login with an API key:
|
|
29
14
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
- https://plexor.dev/signup (to create an account)
|
|
33
|
-
|
|
34
|
-
Tell them: "Please provide your Plexor API key (starts with 'plx_'):"
|
|
35
|
-
|
|
36
|
-
**Step 3: Save the configuration**
|
|
37
|
-
|
|
38
|
-
Once the user provides an API key (or it was passed as an argument), use the Write tool to create/update `~/.plexor/config.json`:
|
|
39
|
-
|
|
40
|
-
```json
|
|
41
|
-
{
|
|
42
|
-
"version": 1,
|
|
43
|
-
"auth": {
|
|
44
|
-
"api_key": "[user's API key]",
|
|
45
|
-
"authenticated_at": "[current ISO timestamp]"
|
|
46
|
-
},
|
|
47
|
-
"settings": {
|
|
48
|
-
"enabled": true,
|
|
49
|
-
"apiUrl": "https://api.plexor.dev",
|
|
50
|
-
"preferred_provider": "auto",
|
|
51
|
-
"mode": "balanced",
|
|
52
|
-
"localCacheEnabled": true,
|
|
53
|
-
"timeout": 5000
|
|
54
|
-
}
|
|
55
|
-
}
|
|
15
|
+
```bash
|
|
16
|
+
node ~/.claude/plugins/plexor/commands/plexor-login.js plx_your_api_key_here
|
|
56
17
|
```
|
|
57
18
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
**Step 4: Verify the key works**
|
|
61
|
-
|
|
62
|
-
Make a test request to verify authentication:
|
|
63
|
-
```
|
|
64
|
-
GET https://api.plexor.dev/api/users/me
|
|
65
|
-
Authorization: Bearer [apiKey]
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
If successful, show:
|
|
69
|
-
```
|
|
70
|
-
Plexor Login
|
|
71
|
-
============
|
|
72
|
-
Authentication successful!
|
|
73
|
-
|
|
74
|
-
Your API key has been saved to ~/.plexor/config.json
|
|
75
|
-
|
|
76
|
-
Next steps:
|
|
77
|
-
1. Run /plexor-status to see your usage
|
|
78
|
-
2. Run /plexor-enabled true to enable the proxy
|
|
79
|
-
3. Start saving on LLM costs!
|
|
80
|
-
|
|
81
|
-
Dashboard: https://plexor.dev/dashboard
|
|
82
|
-
```
|
|
19
|
+
Use the Bash tool to execute this command.
|
|
83
20
|
|
|
84
|
-
|
|
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
|
|
85
26
|
|
|
86
|
-
|
|
27
|
+
The command output is the complete response. Simply show the output and wait for the user's next input.
|