@plexor-dev/claude-code-plugin 0.1.0-beta.2 → 0.1.0-beta.21
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 +31 -30
- package/commands/plexor-enabled.js +91 -0
- package/commands/plexor-enabled.md +48 -28
- package/commands/plexor-login.js +169 -0
- package/commands/plexor-login.md +51 -52
- package/commands/plexor-logout.js +92 -0
- package/commands/plexor-logout.md +21 -27
- package/commands/plexor-mode.js +107 -0
- package/commands/plexor-mode.md +41 -17
- package/commands/plexor-provider.js +110 -0
- package/commands/plexor-provider.md +42 -18
- package/commands/plexor-settings.js +155 -0
- package/commands/plexor-settings.md +39 -72
- package/commands/plexor-setup.md +134 -0
- package/commands/plexor-status.js +213 -0
- package/commands/plexor-status.md +12 -37
- 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 +16 -31
- 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/package.json +12 -1
- package/scripts/plexor-cli.sh +48 -0
- package/scripts/postinstall.js +53 -7
package/commands/plexor-login.md
CHANGED
|
@@ -1,87 +1,86 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Authenticate with Plexor to enable optimization
|
|
2
|
+
description: Authenticate with Plexor to enable optimization (user)
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Plexor Login
|
|
6
6
|
|
|
7
7
|
Authenticate with your Plexor account to enable LLM cost optimization.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Steps
|
|
10
10
|
|
|
11
|
-
1
|
|
12
|
-
2. If already authenticated and valid, show current account info and status
|
|
13
|
-
3. If not authenticated, initiate device flow:
|
|
14
|
-
a. Call POST https://api.plexor.dev/v1/auth/device-code to get device_code and user_code
|
|
15
|
-
b. Display the user code and verification URL to the user
|
|
16
|
-
c. Open https://plexor.dev/auth/device in the user's browser
|
|
17
|
-
d. Poll POST https://api.plexor.dev/v1/auth/device-token every 5 seconds
|
|
18
|
-
e. On success, save API key to ~/.plexor/config.json
|
|
19
|
-
f. Verify the key works by calling GET https://api.plexor.dev/v1/user
|
|
11
|
+
**Step 1: Check existing authentication**
|
|
20
12
|
|
|
21
|
-
|
|
22
|
-
a. Set ANTHROPIC_BASE_URL to route traffic through Plexor gateway
|
|
23
|
-
b. Inform user they need to restart Claude Code
|
|
13
|
+
Use the Read tool to read `~/.plexor/config.json`.
|
|
24
14
|
|
|
25
|
-
|
|
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.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Step 2: If not authenticated, ask for API key**
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
Ask the user to provide their Plexor API key. They can get one from:
|
|
31
|
+
- https://plexor.dev/dashboard (if they have an account)
|
|
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`:
|
|
28
39
|
|
|
29
40
|
```json
|
|
30
41
|
{
|
|
31
42
|
"version": 1,
|
|
32
43
|
"auth": {
|
|
33
|
-
"api_key": "
|
|
34
|
-
"
|
|
35
|
-
"tier": "beta",
|
|
36
|
-
"authenticated_at": "2025-12-16T20:00:00Z"
|
|
44
|
+
"api_key": "[user's API key]",
|
|
45
|
+
"authenticated_at": "[current ISO timestamp]"
|
|
37
46
|
},
|
|
38
47
|
"settings": {
|
|
39
48
|
"enabled": true,
|
|
49
|
+
"apiUrl": "https://api.plexor.dev",
|
|
40
50
|
"preferred_provider": "auto",
|
|
41
|
-
"mode": "balanced"
|
|
51
|
+
"mode": "balanced",
|
|
52
|
+
"localCacheEnabled": true,
|
|
53
|
+
"timeout": 5000
|
|
42
54
|
}
|
|
43
55
|
}
|
|
44
56
|
```
|
|
45
57
|
|
|
46
|
-
|
|
58
|
+
**IMPORTANT**: If the user provided the API key as an argument (e.g., `/plexor-login plx_abc123`), use that key directly instead of asking for it.
|
|
47
59
|
|
|
48
|
-
|
|
49
|
-
Plexor Login
|
|
50
|
-
============
|
|
60
|
+
**Step 4: Verify the key works**
|
|
51
61
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
ABCD-1234
|
|
57
|
-
|
|
58
|
-
Waiting for authentication... ✓
|
|
59
|
-
|
|
60
|
-
✓ Authenticated as user@example.com
|
|
61
|
-
✓ Plan: Beta ($1/month)
|
|
62
|
-
✓ Plexor gateway configured
|
|
63
|
-
|
|
64
|
-
To activate, run this in your terminal:
|
|
65
|
-
|
|
66
|
-
export ANTHROPIC_BASE_URL="https://api.plexor.dev/v1/gateway/anthropic"
|
|
67
|
-
|
|
68
|
-
Then restart Claude Code. All prompts will be optimized automatically.
|
|
69
|
-
|
|
70
|
-
Run /plexor-status to see your savings.
|
|
62
|
+
Make a test request to verify authentication:
|
|
63
|
+
```
|
|
64
|
+
GET https://api.plexor.dev/api/users/me
|
|
65
|
+
Authorization: Bearer [apiKey]
|
|
71
66
|
```
|
|
72
67
|
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
If successful, show:
|
|
75
69
|
```
|
|
76
70
|
Plexor Login
|
|
77
71
|
============
|
|
72
|
+
Authentication successful!
|
|
78
73
|
|
|
79
|
-
|
|
80
|
-
Plan: Beta ($1/month)
|
|
81
|
-
Status: Active
|
|
74
|
+
Your API key has been saved to ~/.plexor/config.json
|
|
82
75
|
|
|
83
|
-
|
|
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!
|
|
84
80
|
|
|
85
|
-
|
|
86
|
-
Run /plexor-logout to sign out.
|
|
81
|
+
Dashboard: https://plexor.dev/dashboard
|
|
87
82
|
```
|
|
83
|
+
|
|
84
|
+
If the API call fails, tell the user the key may be invalid and ask them to check it.
|
|
85
|
+
|
|
86
|
+
**IMPORTANT**: After completing the task, STOP. Do not run additional commands or explore the codebase.
|
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
const PLEXOR_DIR = path.join(process.env.HOME, '.plexor');
|
|
12
|
+
const CONFIG_PATH = path.join(PLEXOR_DIR, 'config.json');
|
|
13
|
+
const SESSION_PATH = path.join(PLEXOR_DIR, 'session.json');
|
|
14
|
+
const CACHE_PATH = path.join(PLEXOR_DIR, 'cache.json');
|
|
15
|
+
|
|
16
|
+
function loadConfig() {
|
|
17
|
+
try {
|
|
18
|
+
const data = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
19
|
+
return JSON.parse(data);
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function saveConfig(config) {
|
|
26
|
+
if (!fs.existsSync(PLEXOR_DIR)) {
|
|
27
|
+
fs.mkdirSync(PLEXOR_DIR, { recursive: true, mode: 0o700 });
|
|
28
|
+
}
|
|
29
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function deleteFile(filePath) {
|
|
33
|
+
try {
|
|
34
|
+
if (fs.existsSync(filePath)) {
|
|
35
|
+
fs.unlinkSync(filePath);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
// Ignore errors
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function main() {
|
|
45
|
+
const args = process.argv.slice(2);
|
|
46
|
+
const config = loadConfig();
|
|
47
|
+
|
|
48
|
+
if (!config || !config.auth?.api_key) {
|
|
49
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
50
|
+
console.log(`│ Not Logged In │`);
|
|
51
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
52
|
+
console.log(`│ No active Plexor session found. │`);
|
|
53
|
+
console.log(`│ Run /plexor-login to authenticate. │`);
|
|
54
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const clearCache = args.includes('--clear-cache') || args.includes('-c');
|
|
59
|
+
|
|
60
|
+
// Clear credentials
|
|
61
|
+
delete config.auth.api_key;
|
|
62
|
+
config.settings = config.settings || {};
|
|
63
|
+
config.settings.enabled = false;
|
|
64
|
+
saveConfig(config);
|
|
65
|
+
|
|
66
|
+
// Clear session
|
|
67
|
+
deleteFile(SESSION_PATH);
|
|
68
|
+
|
|
69
|
+
// Optionally clear cache
|
|
70
|
+
let cacheCleared = false;
|
|
71
|
+
if (clearCache) {
|
|
72
|
+
cacheCleared = deleteFile(CACHE_PATH);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
76
|
+
console.log(`│ ✓ Logged Out │`);
|
|
77
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
78
|
+
console.log(`│ ✓ API key removed │`);
|
|
79
|
+
console.log(`│ ✓ Plexor proxy disabled │`);
|
|
80
|
+
console.log(`│ ✓ Session cleared │`);
|
|
81
|
+
if (clearCache) {
|
|
82
|
+
console.log(`│ ${cacheCleared ? '✓' : '○'} Cache cleared │`);
|
|
83
|
+
}
|
|
84
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
85
|
+
console.log(`│ Run /plexor-login to re-authenticate. │`);
|
|
86
|
+
if (!clearCache) {
|
|
87
|
+
console.log(`│ Use --clear-cache to also clear cache. │`);
|
|
88
|
+
}
|
|
89
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
main();
|
|
@@ -1,50 +1,44 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Log out from Plexor and clear credentials
|
|
2
|
+
description: Log out from Plexor and clear credentials (user)
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Plexor Logout
|
|
6
6
|
|
|
7
7
|
Log out from your Plexor account and clear stored credentials.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Steps
|
|
10
10
|
|
|
11
|
-
1
|
|
12
|
-
2. If not authenticated, inform the user they are not logged in
|
|
13
|
-
3. If authenticated:
|
|
14
|
-
a. Clear the API key from the configuration
|
|
15
|
-
b. Preserve non-sensitive settings (mode, provider preferences)
|
|
16
|
-
c. Save the updated configuration
|
|
17
|
-
4. Remind user to restore original Anthropic URL
|
|
18
|
-
5. Display confirmation message
|
|
11
|
+
**Step 1: Read current configuration**
|
|
19
12
|
|
|
20
|
-
|
|
13
|
+
Use the Read tool to read `~/.plexor/config.json`.
|
|
21
14
|
|
|
15
|
+
If the file doesn't exist or has no `apiKey`, show:
|
|
22
16
|
```
|
|
23
17
|
Plexor Logout
|
|
24
18
|
=============
|
|
19
|
+
You are not currently logged in.
|
|
20
|
+
Run /plexor-login to authenticate.
|
|
21
|
+
```
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
✓ API key cleared from ~/.plexor/config.json
|
|
28
|
-
|
|
29
|
-
To restore direct Anthropic access, run:
|
|
30
|
-
|
|
31
|
-
unset ANTHROPIC_BASE_URL
|
|
32
|
-
|
|
33
|
-
Then restart Claude Code.
|
|
34
|
-
|
|
35
|
-
Note: Your Plexor subscription is still active.
|
|
36
|
-
To manage billing: https://plexor.dev/billing
|
|
23
|
+
**Step 2: Clear the API key**
|
|
37
24
|
|
|
38
|
-
|
|
39
|
-
|
|
25
|
+
If authenticated, use the Write tool to update `~/.plexor/config.json`:
|
|
26
|
+
- Remove or clear the `apiKey` field (set to empty string "")
|
|
27
|
+
- Keep all other settings (mode, preferredProvider, etc.)
|
|
40
28
|
|
|
41
|
-
|
|
29
|
+
**Step 3: Show confirmation**
|
|
42
30
|
|
|
43
31
|
```
|
|
44
32
|
Plexor Logout
|
|
45
33
|
=============
|
|
34
|
+
Successfully logged out!
|
|
46
35
|
|
|
47
|
-
|
|
36
|
+
- API key cleared from ~/.plexor/config.json
|
|
37
|
+
- Settings preserved (mode, provider preferences)
|
|
48
38
|
|
|
49
|
-
|
|
39
|
+
To use Plexor again, run /plexor-login
|
|
40
|
+
|
|
41
|
+
Dashboard: https://plexor.dev/dashboard
|
|
50
42
|
```
|
|
43
|
+
|
|
44
|
+
**IMPORTANT**: After completing the task, STOP. Do not run additional commands or explore the codebase.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plexor Mode Command
|
|
5
|
+
* Set optimization mode: eco, balanced, quality, passthrough
|
|
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
|
+
const VALID_MODES = ['eco', 'balanced', 'quality', 'passthrough'];
|
|
15
|
+
|
|
16
|
+
const MODE_INFO = {
|
|
17
|
+
eco: {
|
|
18
|
+
description: 'Maximum savings (~$0.04/1M tokens)',
|
|
19
|
+
models: 'Ministral 3B, small models'
|
|
20
|
+
},
|
|
21
|
+
balanced: {
|
|
22
|
+
description: 'Good balance (~$0.15/1M tokens)',
|
|
23
|
+
models: 'DeepSeek, Gemini Flash, Mistral Small'
|
|
24
|
+
},
|
|
25
|
+
quality: {
|
|
26
|
+
description: 'Premium models (~$5/1M tokens)',
|
|
27
|
+
models: 'Claude Opus, Gemini Pro, Mistral Large'
|
|
28
|
+
},
|
|
29
|
+
passthrough: {
|
|
30
|
+
description: 'No optimization',
|
|
31
|
+
models: 'Direct to requested model'
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function loadConfig() {
|
|
36
|
+
try {
|
|
37
|
+
const data = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
38
|
+
return JSON.parse(data);
|
|
39
|
+
} catch {
|
|
40
|
+
return { version: 1, auth: {}, settings: {} };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function saveConfig(config) {
|
|
45
|
+
if (!fs.existsSync(PLEXOR_DIR)) {
|
|
46
|
+
fs.mkdirSync(PLEXOR_DIR, { recursive: true, mode: 0o700 });
|
|
47
|
+
}
|
|
48
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function main() {
|
|
52
|
+
const args = process.argv.slice(2);
|
|
53
|
+
const config = loadConfig();
|
|
54
|
+
const currentMode = config.settings?.mode || 'balanced';
|
|
55
|
+
|
|
56
|
+
// No args - show current mode and options
|
|
57
|
+
if (args.length === 0) {
|
|
58
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
59
|
+
console.log(`│ Plexor Optimization Mode │`);
|
|
60
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
61
|
+
console.log(`│ Current: ${currentMode.padEnd(33)}│`);
|
|
62
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
63
|
+
console.log(`│ Available modes: │`);
|
|
64
|
+
|
|
65
|
+
for (const mode of VALID_MODES) {
|
|
66
|
+
const marker = mode === currentMode ? '●' : '○';
|
|
67
|
+
const info = MODE_INFO[mode];
|
|
68
|
+
console.log(`│ ${marker} ${mode.padEnd(12)} ${info.description.substring(0, 26).padEnd(26)}│`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
72
|
+
console.log(`│ Usage: /plexor-mode <mode> │`);
|
|
73
|
+
console.log(`│ Example: /plexor-mode eco │`);
|
|
74
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const newMode = args[0].toLowerCase();
|
|
79
|
+
|
|
80
|
+
if (!VALID_MODES.includes(newMode)) {
|
|
81
|
+
console.error(`Error: Invalid mode "${newMode}"`);
|
|
82
|
+
console.error(`Valid modes: ${VALID_MODES.join(', ')}`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (newMode === currentMode) {
|
|
87
|
+
console.log(`Mode is already set to "${currentMode}"`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
config.settings = config.settings || {};
|
|
92
|
+
config.settings.mode = newMode;
|
|
93
|
+
saveConfig(config);
|
|
94
|
+
|
|
95
|
+
const info = MODE_INFO[newMode];
|
|
96
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
97
|
+
console.log(`│ ✓ Mode Updated │`);
|
|
98
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
99
|
+
console.log(`│ Previous: ${currentMode.padEnd(32)}│`);
|
|
100
|
+
console.log(`│ New: ${newMode.padEnd(37)}│`);
|
|
101
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
102
|
+
console.log(`│ ${info.description.padEnd(42)}│`);
|
|
103
|
+
console.log(`│ Models: ${info.models.substring(0, 34).padEnd(34)}│`);
|
|
104
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
main();
|
package/commands/plexor-mode.md
CHANGED
|
@@ -1,25 +1,49 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Set Plexor optimization mode (eco/balanced/quality/passthrough)
|
|
2
|
+
description: Set Plexor optimization mode (eco/balanced/quality/passthrough) (user)
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# Plexor Mode
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Set the Plexor optimization mode to control cost vs quality trade-offs.
|
|
8
|
+
|
|
9
|
+
## Steps
|
|
10
|
+
|
|
11
|
+
**Step 1: Read current configuration**
|
|
12
|
+
|
|
13
|
+
Use the Read tool to read `~/.plexor/config.json` and check the current `mode` setting.
|
|
14
|
+
|
|
15
|
+
**Step 2: Ask user which mode they want**
|
|
16
|
+
|
|
17
|
+
Use the `AskUserQuestion` tool:
|
|
8
18
|
|
|
9
19
|
Question: "Which optimization mode would you like to use?"
|
|
10
20
|
Header: "Mode"
|
|
11
21
|
Options:
|
|
12
|
-
1. **eco** - Maximum
|
|
13
|
-
2. **balanced** -
|
|
14
|
-
3. **quality** - Premium models (Claude Opus
|
|
15
|
-
4. **passthrough** - No optimization
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
1. **eco** - Maximum savings (~$0.04/1M tokens) - Uses Ministral 3B
|
|
23
|
+
2. **balanced** - Good balance (~$0.15/1M) - DeepSeek, Gemini Flash, Mistral Small
|
|
24
|
+
3. **quality** - Premium models (~$5/1M) - Claude Opus, Gemini Pro, Mistral Large
|
|
25
|
+
4. **passthrough** - No optimization - Direct to requested model
|
|
26
|
+
|
|
27
|
+
**Step 3: Update the configuration**
|
|
28
|
+
|
|
29
|
+
Use the Read tool to get the current config, then use the Write tool to update `~/.plexor/config.json`:
|
|
30
|
+
- Set the `mode` field to the selected value: "eco", "balanced", "quality", or "passthrough"
|
|
31
|
+
|
|
32
|
+
Keep all other settings unchanged.
|
|
33
|
+
|
|
34
|
+
**Step 4: Show confirmation**
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
Plexor Mode: [SELECTED MODE]
|
|
38
|
+
|
|
39
|
+
Mode Details:
|
|
40
|
+
- eco: Maximum cost savings using Ministral 3B ($0.04/$0.04 per 1M tokens)
|
|
41
|
+
- balanced: Cost-first with quality fallbacks (DeepSeek → Gemini Flash → Mistral)
|
|
42
|
+
- quality: Premium models for complex tasks (Claude Opus, Gemini Pro)
|
|
43
|
+
- passthrough: No optimization, direct to Anthropic
|
|
44
|
+
|
|
45
|
+
Current mode: [mode]
|
|
46
|
+
Run /plexor-status to see your savings.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**IMPORTANT**: After completing the task, STOP. Do not run additional commands or explore the codebase.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plexor Provider Command
|
|
5
|
+
* Force a specific LLM provider
|
|
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
|
+
const VALID_PROVIDERS = ['auto', 'claude', 'anthropic', 'openai', 'deepseek', 'mistral', 'gemini', 'google'];
|
|
15
|
+
|
|
16
|
+
const PROVIDER_INFO = {
|
|
17
|
+
auto: 'Automatic routing based on task and mode',
|
|
18
|
+
claude: 'Anthropic Claude models (alias for anthropic)',
|
|
19
|
+
anthropic: 'Anthropic Claude models',
|
|
20
|
+
openai: 'OpenAI GPT models',
|
|
21
|
+
deepseek: 'DeepSeek models (excellent for code)',
|
|
22
|
+
mistral: 'Mistral AI models',
|
|
23
|
+
gemini: 'Google Gemini models',
|
|
24
|
+
google: 'Google Gemini models (alias for gemini)'
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function loadConfig() {
|
|
28
|
+
try {
|
|
29
|
+
const data = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
30
|
+
return JSON.parse(data);
|
|
31
|
+
} catch {
|
|
32
|
+
return { version: 1, auth: {}, settings: {} };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function saveConfig(config) {
|
|
37
|
+
if (!fs.existsSync(PLEXOR_DIR)) {
|
|
38
|
+
fs.mkdirSync(PLEXOR_DIR, { recursive: true, mode: 0o700 });
|
|
39
|
+
}
|
|
40
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function normalizeProvider(provider) {
|
|
44
|
+
const normalized = provider.toLowerCase();
|
|
45
|
+
if (normalized === 'claude') return 'anthropic';
|
|
46
|
+
if (normalized === 'google') return 'gemini';
|
|
47
|
+
return normalized;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function main() {
|
|
51
|
+
const args = process.argv.slice(2);
|
|
52
|
+
const config = loadConfig();
|
|
53
|
+
const currentProvider = config.settings?.preferred_provider || 'auto';
|
|
54
|
+
|
|
55
|
+
// No args - show current provider and options
|
|
56
|
+
if (args.length === 0) {
|
|
57
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
58
|
+
console.log(`│ Plexor Provider Routing │`);
|
|
59
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
60
|
+
console.log(`│ Current: ${currentProvider.padEnd(33)}│`);
|
|
61
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
62
|
+
console.log(`│ Available providers: │`);
|
|
63
|
+
|
|
64
|
+
const displayProviders = ['auto', 'anthropic', 'openai', 'deepseek', 'mistral', 'gemini'];
|
|
65
|
+
for (const provider of displayProviders) {
|
|
66
|
+
const marker = provider === currentProvider ? '●' : '○';
|
|
67
|
+
const info = PROVIDER_INFO[provider];
|
|
68
|
+
console.log(`│ ${marker} ${provider.padEnd(10)} ${info.substring(0, 29).padEnd(29)}│`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
72
|
+
console.log(`│ Usage: /plexor-provider <provider> │`);
|
|
73
|
+
console.log(`│ Example: /plexor-provider deepseek │`);
|
|
74
|
+
console.log(`│ Aliases: claude→anthropic, google→gemini │`);
|
|
75
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const inputProvider = args[0].toLowerCase();
|
|
80
|
+
|
|
81
|
+
if (!VALID_PROVIDERS.includes(inputProvider)) {
|
|
82
|
+
console.error(`Error: Invalid provider "${args[0]}"`);
|
|
83
|
+
console.error(`Valid providers: auto, anthropic, openai, deepseek, mistral, gemini`);
|
|
84
|
+
console.error(`Aliases: claude (→anthropic), google (→gemini)`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const newProvider = normalizeProvider(inputProvider);
|
|
89
|
+
|
|
90
|
+
if (newProvider === currentProvider) {
|
|
91
|
+
console.log(`Provider is already set to "${currentProvider}"`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
config.settings = config.settings || {};
|
|
96
|
+
config.settings.preferred_provider = newProvider;
|
|
97
|
+
saveConfig(config);
|
|
98
|
+
|
|
99
|
+
const info = PROVIDER_INFO[newProvider];
|
|
100
|
+
console.log(`┌─────────────────────────────────────────────┐`);
|
|
101
|
+
console.log(`│ ✓ Provider Updated │`);
|
|
102
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
103
|
+
console.log(`│ Previous: ${currentProvider.padEnd(32)}│`);
|
|
104
|
+
console.log(`│ New: ${newProvider.padEnd(37)}│`);
|
|
105
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
106
|
+
console.log(`│ ${info.padEnd(42)}│`);
|
|
107
|
+
console.log(`└─────────────────────────────────────────────┘`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main();
|
|
@@ -1,25 +1,49 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Force a specific LLM provider (claude/openai/deepseek/mistral/gemini/auto)
|
|
2
|
+
description: Force a specific LLM provider (claude/openai/deepseek/mistral/gemini/auto) (user)
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# Plexor Provider
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Set your preferred LLM provider for Plexor routing.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Steps
|
|
10
|
+
|
|
11
|
+
**Step 1: Read current configuration**
|
|
12
|
+
|
|
13
|
+
Use the Read tool to read `~/.plexor/config.json` and check the current `preferredProvider` setting.
|
|
14
|
+
|
|
15
|
+
**Step 2: Ask user which provider they prefer**
|
|
16
|
+
|
|
17
|
+
Use the `AskUserQuestion` tool:
|
|
18
|
+
|
|
19
|
+
Question: "Which LLM provider would you prefer?"
|
|
10
20
|
Header: "Provider"
|
|
11
21
|
Options:
|
|
12
|
-
1. **auto** -
|
|
13
|
-
2. **
|
|
14
|
-
3. **
|
|
15
|
-
4. **gemini** -
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
1. **auto** - Let Plexor choose the best provider based on task (Recommended)
|
|
23
|
+
2. **deepseek** - Use DeepSeek models (cheapest, good for code)
|
|
24
|
+
3. **mistral** - Use Mistral models (fast, good balance)
|
|
25
|
+
4. **gemini** - Use Google Gemini models (good for analysis)
|
|
26
|
+
|
|
27
|
+
**Step 3: Update the configuration**
|
|
28
|
+
|
|
29
|
+
Use the Read tool to get the current config, then use the Write tool to update `~/.plexor/config.json`:
|
|
30
|
+
- Set `preferredProvider` to: "auto", "deepseek", "mistral", or "gemini"
|
|
31
|
+
|
|
32
|
+
Keep all other settings unchanged.
|
|
33
|
+
|
|
34
|
+
**Step 4: Show confirmation**
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
Plexor Provider: [SELECTED PROVIDER]
|
|
38
|
+
|
|
39
|
+
Provider options:
|
|
40
|
+
- auto: Plexor automatically selects the best provider for each task
|
|
41
|
+
- deepseek: DeepSeek models ($0.14/$0.28 per 1M tokens) - Great for code
|
|
42
|
+
- mistral: Mistral models ($0.10/$0.30 per 1M tokens) - Fast and versatile
|
|
43
|
+
- gemini: Google Gemini ($0.075/$0.30 per 1M tokens) - Good for analysis
|
|
44
|
+
|
|
45
|
+
Current provider: [preferredProvider]
|
|
46
|
+
Run /plexor-status to see your usage.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**IMPORTANT**: After completing the task, STOP. Do not run additional commands or explore the codebase.
|