@plexor-dev/claude-code-plugin 0.1.0-beta.23 → 0.1.0-beta.25
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.js +41 -11
- package/commands/plexor-login.js +21 -2
- package/commands/plexor-setup.md +68 -30
- package/lib/settings-manager.js +238 -0
- package/package.json +1 -1
- package/scripts/postinstall.js +54 -0
|
@@ -3,11 +3,20 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Plexor Enabled Command
|
|
5
5
|
* Enable or disable Plexor optimization proxy
|
|
6
|
+
*
|
|
7
|
+
* KEY FEATURE: When toggling on/off, automatically updates ~/.claude/settings.json
|
|
8
|
+
* to route or un-route ALL Claude Code sessions through Plexor gateway.
|
|
9
|
+
*
|
|
10
|
+
* THE DREAM: User just runs "/plexor-enabled off" to disable, "/plexor-enabled on" to enable.
|
|
11
|
+
* No manual environment variables or config updates required!
|
|
6
12
|
*/
|
|
7
13
|
|
|
8
14
|
const fs = require('fs');
|
|
9
15
|
const path = require('path');
|
|
10
16
|
|
|
17
|
+
// Import settings manager for automatic Claude Code configuration
|
|
18
|
+
const { settingsManager, PLEXOR_STAGING_URL, PLEXOR_PROD_URL } = require('../lib/settings-manager');
|
|
19
|
+
|
|
11
20
|
const CONFIG_PATH = path.join(process.env.HOME, '.plexor', 'config.json');
|
|
12
21
|
const PLEXOR_DIR = path.join(process.env.HOME, '.plexor');
|
|
13
22
|
|
|
@@ -31,14 +40,23 @@ function main() {
|
|
|
31
40
|
const args = process.argv.slice(2);
|
|
32
41
|
const config = loadConfig();
|
|
33
42
|
const currentEnabled = config.settings?.enabled ?? false;
|
|
43
|
+
const apiKey = config.auth?.api_key;
|
|
44
|
+
|
|
45
|
+
// Get current Claude settings.json routing status
|
|
46
|
+
const routingStatus = settingsManager.getRoutingStatus();
|
|
34
47
|
|
|
35
48
|
// No args - show current status
|
|
36
49
|
if (args.length === 0) {
|
|
37
50
|
const status = currentEnabled ? '● Enabled' : '○ Disabled';
|
|
51
|
+
const routingStr = routingStatus.enabled ? '● Active' : '○ Inactive';
|
|
38
52
|
console.log(`┌─────────────────────────────────────────────┐`);
|
|
39
53
|
console.log(`│ Plexor Proxy Status │`);
|
|
40
54
|
console.log(`├─────────────────────────────────────────────┤`);
|
|
41
|
-
console.log(`│
|
|
55
|
+
console.log(`│ Plugin Config: ${status.padEnd(27)}│`);
|
|
56
|
+
console.log(`│ Claude Routing: ${routingStr.padEnd(26)}│`);
|
|
57
|
+
if (routingStatus.enabled) {
|
|
58
|
+
console.log(`│ Endpoint: ${(routingStatus.isStaging ? 'Staging' : 'Production').padEnd(32)}│`);
|
|
59
|
+
}
|
|
42
60
|
console.log(`├─────────────────────────────────────────────┤`);
|
|
43
61
|
console.log(`│ Usage: │`);
|
|
44
62
|
console.log(`│ /plexor-enabled true - Enable proxy │`);
|
|
@@ -62,21 +80,32 @@ function main() {
|
|
|
62
80
|
process.exit(1);
|
|
63
81
|
}
|
|
64
82
|
|
|
65
|
-
|
|
66
|
-
const state = currentEnabled ? 'enabled' : 'disabled';
|
|
67
|
-
console.log(`Plexor proxy is already ${state}`);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
83
|
+
// Update Plexor plugin config
|
|
71
84
|
config.settings = config.settings || {};
|
|
72
85
|
config.settings.enabled = newEnabled;
|
|
73
86
|
saveConfig(config);
|
|
74
87
|
|
|
88
|
+
// THE KEY FEATURE: Update Claude Code settings.json routing
|
|
89
|
+
let routingUpdated = false;
|
|
90
|
+
if (newEnabled) {
|
|
91
|
+
// Enable routing - need API key from config
|
|
92
|
+
if (apiKey) {
|
|
93
|
+
const apiUrl = config.settings?.apiUrl || 'https://api.plexor.dev';
|
|
94
|
+
const useStaging = apiUrl.includes('staging');
|
|
95
|
+
routingUpdated = settingsManager.enablePlexorRouting(apiKey, { useStaging });
|
|
96
|
+
} else {
|
|
97
|
+
console.log('⚠ No API key found. Run /plexor-login first.');
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
// Disable routing - remove env vars from settings.json
|
|
101
|
+
routingUpdated = settingsManager.disablePlexorRouting();
|
|
102
|
+
}
|
|
103
|
+
|
|
75
104
|
const newStatus = newEnabled ? '● Enabled' : '○ Disabled';
|
|
76
105
|
const prevStatus = currentEnabled ? 'Enabled' : 'Disabled';
|
|
77
|
-
const
|
|
78
|
-
? '
|
|
79
|
-
: '
|
|
106
|
+
const routingMsg = routingUpdated
|
|
107
|
+
? (newEnabled ? 'Claude Code now routes through Plexor' : 'Claude Code now connects directly')
|
|
108
|
+
: 'Manual routing update may be needed';
|
|
80
109
|
|
|
81
110
|
console.log(`┌─────────────────────────────────────────────┐`);
|
|
82
111
|
console.log(`│ ✓ Plexor Proxy Updated │`);
|
|
@@ -84,7 +113,8 @@ function main() {
|
|
|
84
113
|
console.log(`│ Previous: ${prevStatus.padEnd(32)}│`);
|
|
85
114
|
console.log(`│ New: ${newStatus.padEnd(37)}│`);
|
|
86
115
|
console.log(`├─────────────────────────────────────────────┤`);
|
|
87
|
-
console.log(`│ ${
|
|
116
|
+
console.log(`│ ${routingMsg.padEnd(42)}│`);
|
|
117
|
+
console.log(`│ Changes take effect immediately. │`);
|
|
88
118
|
console.log(`└─────────────────────────────────────────────┘`);
|
|
89
119
|
}
|
|
90
120
|
|
package/commands/plexor-login.js
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Plexor Login Command
|
|
5
|
-
* Authenticate with Plexor API
|
|
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.
|
|
6
9
|
*/
|
|
7
10
|
|
|
8
11
|
const fs = require('fs');
|
|
@@ -10,6 +13,9 @@ const path = require('path');
|
|
|
10
13
|
const https = require('https');
|
|
11
14
|
const http = require('http');
|
|
12
15
|
|
|
16
|
+
// Import settings manager for automatic Claude Code configuration
|
|
17
|
+
const { settingsManager } = require('../lib/settings-manager');
|
|
18
|
+
|
|
13
19
|
const CONFIG_PATH = path.join(process.env.HOME, '.plexor', 'config.json');
|
|
14
20
|
const PLEXOR_DIR = path.join(process.env.HOME, '.plexor');
|
|
15
21
|
const DEFAULT_API_URL = 'https://api.plexor.dev';
|
|
@@ -141,6 +147,12 @@ async function main() {
|
|
|
141
147
|
config.settings.enabled = true;
|
|
142
148
|
saveConfig(config);
|
|
143
149
|
|
|
150
|
+
// AUTO-CONFIGURE CLAUDE CODE ROUTING
|
|
151
|
+
// This is the key feature: automatically set ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN
|
|
152
|
+
// in ~/.claude/settings.json so ALL Claude Code sessions route through Plexor
|
|
153
|
+
const useStaging = apiUrl.includes('staging');
|
|
154
|
+
const routingEnabled = settingsManager.enablePlexorRouting(apiKey, { useStaging });
|
|
155
|
+
|
|
144
156
|
const email = user.email || 'Unknown';
|
|
145
157
|
const tier = user.tier?.name || 'Free';
|
|
146
158
|
|
|
@@ -150,7 +162,14 @@ async function main() {
|
|
|
150
162
|
console.log(`│ Email: ${email.substring(0, 35).padEnd(35)}│`);
|
|
151
163
|
console.log(`│ Tier: ${tier.padEnd(36)}│`);
|
|
152
164
|
console.log(`├─────────────────────────────────────────────┤`);
|
|
153
|
-
|
|
165
|
+
if (routingEnabled) {
|
|
166
|
+
console.log(`│ ✓ Claude Code routing: CONFIGURED │`);
|
|
167
|
+
console.log(`│ All sessions now route through Plexor │`);
|
|
168
|
+
} else {
|
|
169
|
+
console.log(`│ ⚠ Claude Code routing: MANUAL SETUP NEEDED │`);
|
|
170
|
+
console.log(`│ Set ANTHROPIC_BASE_URL in environment │`);
|
|
171
|
+
}
|
|
172
|
+
console.log(`├─────────────────────────────────────────────┤`);
|
|
154
173
|
console.log(`│ Run /plexor-status to see your stats. │`);
|
|
155
174
|
console.log(`└─────────────────────────────────────────────┘`);
|
|
156
175
|
} catch (err) {
|
package/commands/plexor-setup.md
CHANGED
|
@@ -4,13 +4,16 @@ description: First-time setup wizard for Plexor with Claude Code (user)
|
|
|
4
4
|
|
|
5
5
|
# Plexor Setup Wizard
|
|
6
6
|
|
|
7
|
-
Guide users through first-time Plexor setup
|
|
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.
|
|
8
10
|
|
|
9
11
|
## Steps
|
|
10
12
|
|
|
11
13
|
**Step 1: Check if already configured**
|
|
12
14
|
|
|
13
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.
|
|
14
17
|
|
|
15
18
|
If configured, show:
|
|
16
19
|
```
|
|
@@ -21,9 +24,11 @@ Already configured!
|
|
|
21
24
|
API URL: [apiUrl from config]
|
|
22
25
|
Mode: [mode from config]
|
|
23
26
|
Status: [Enabled/Disabled]
|
|
27
|
+
Claude Routing: [Active/Inactive]
|
|
24
28
|
|
|
25
29
|
Run /plexor-status to see your usage.
|
|
26
30
|
Run /plexor-settings to modify configuration.
|
|
31
|
+
Run /plexor-enabled off to disable routing.
|
|
27
32
|
```
|
|
28
33
|
|
|
29
34
|
**Step 2: Ask about Claude MAX subscription**
|
|
@@ -40,11 +45,17 @@ Options:
|
|
|
40
45
|
|
|
41
46
|
If user selected "Yes, I have Claude MAX":
|
|
42
47
|
|
|
43
|
-
1.
|
|
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`:
|
|
44
54
|
```json
|
|
45
55
|
{
|
|
46
56
|
"version": 1,
|
|
47
57
|
"auth": {
|
|
58
|
+
"api_key": "[user's Plexor key]",
|
|
48
59
|
"mode": "oauth_passthrough",
|
|
49
60
|
"authenticated_at": "[current ISO timestamp]"
|
|
50
61
|
},
|
|
@@ -57,24 +68,39 @@ If user selected "Yes, I have Claude MAX":
|
|
|
57
68
|
}
|
|
58
69
|
```
|
|
59
70
|
|
|
60
|
-
|
|
71
|
+
3. Use the Write tool to update `~/.claude/settings.json` env block:
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"env": {
|
|
75
|
+
"ANTHROPIC_BASE_URL": "https://api.plexor.dev/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:
|
|
61
83
|
```
|
|
62
84
|
Plexor Setup - Claude MAX User
|
|
63
85
|
==============================
|
|
64
|
-
|
|
86
|
+
Setup Complete! No manual configuration needed.
|
|
65
87
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
Then restart your terminal or run: source ~/.bashrc
|
|
88
|
+
What was configured:
|
|
89
|
+
- ~/.plexor/config.json (Plexor plugin settings)
|
|
90
|
+
- ~/.claude/settings.json (automatic Claude Code routing)
|
|
71
91
|
|
|
72
92
|
How it works:
|
|
73
|
-
- Claude Code
|
|
74
|
-
-
|
|
93
|
+
- All Claude Code sessions now route through Plexor
|
|
94
|
+
- Your MAX subscription OAuth token is passed through
|
|
75
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
|
|
76
102
|
|
|
77
|
-
|
|
103
|
+
Changes take effect immediately in all Claude Code sessions!
|
|
78
104
|
```
|
|
79
105
|
|
|
80
106
|
**Step 3B: API Key User Setup**
|
|
@@ -104,31 +130,43 @@ If user selected "No, I'll use a Plexor API key":
|
|
|
104
130
|
}
|
|
105
131
|
```
|
|
106
132
|
|
|
107
|
-
3.
|
|
133
|
+
3. Use the Write tool to update `~/.claude/settings.json` env block:
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"env": {
|
|
137
|
+
"ANTHROPIC_BASE_URL": "https://api.plexor.dev/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:
|
|
108
145
|
```
|
|
109
146
|
Plexor Setup - API Key User
|
|
110
147
|
===========================
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
Add these lines to your shell profile (~/.bashrc or ~/.zshrc):
|
|
148
|
+
Setup Complete! No manual configuration needed.
|
|
114
149
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
Then restart your terminal or run: source ~/.bashrc
|
|
150
|
+
What was configured:
|
|
151
|
+
- ~/.plexor/config.json (Plexor plugin settings)
|
|
152
|
+
- ~/.claude/settings.json (automatic Claude Code routing)
|
|
119
153
|
|
|
120
154
|
How it works:
|
|
121
|
-
-
|
|
155
|
+
- All Claude Code sessions now route through Plexor
|
|
122
156
|
- Plexor picks the best provider (can save up to 90%)
|
|
123
|
-
- Your
|
|
157
|
+
- Your usage is tracked and optimized
|
|
124
158
|
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
127
164
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
Ask: "Would you like me to add this to your shell profile automatically?"
|
|
131
|
-
|
|
132
|
-
If yes, use the Edit tool to append the export lines to `~/.bashrc` (or `~/.zshrc` if it exists).
|
|
165
|
+
Changes take effect immediately in all Claude Code sessions!
|
|
166
|
+
```
|
|
133
167
|
|
|
134
|
-
**IMPORTANT**:
|
|
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.
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code Settings Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages ~/.claude/settings.json to enable automatic Plexor routing.
|
|
5
|
+
*
|
|
6
|
+
* KEY DISCOVERY: Claude Code reads settings.json at runtime and the `env` block
|
|
7
|
+
* overrides environment variables. This allows the plugin to redirect ALL Claude
|
|
8
|
+
* Code sessions to Plexor without users manually setting environment variables.
|
|
9
|
+
*
|
|
10
|
+
* Reference: Claude Code v2.0.1+ behavior - settings.json env takes precedence
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const CLAUDE_DIR = path.join(process.env.HOME || process.env.USERPROFILE || '', '.claude');
|
|
17
|
+
const SETTINGS_PATH = path.join(CLAUDE_DIR, 'settings.json');
|
|
18
|
+
|
|
19
|
+
// Plexor gateway endpoints
|
|
20
|
+
const PLEXOR_STAGING_URL = 'https://staging.api.plexor.dev/gateway/anthropic';
|
|
21
|
+
const PLEXOR_PROD_URL = 'https://api.plexor.dev/gateway/anthropic';
|
|
22
|
+
|
|
23
|
+
class ClaudeSettingsManager {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.settingsPath = SETTINGS_PATH;
|
|
26
|
+
this.claudeDir = CLAUDE_DIR;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Load current Claude settings
|
|
31
|
+
* @returns {Object} settings object or empty object if not found
|
|
32
|
+
*/
|
|
33
|
+
load() {
|
|
34
|
+
try {
|
|
35
|
+
if (!fs.existsSync(this.settingsPath)) {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
const data = fs.readFileSync(this.settingsPath, 'utf8');
|
|
39
|
+
return JSON.parse(data);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
// Return empty object if file doesn't exist or parse error
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Save Claude settings
|
|
48
|
+
* @param {Object} settings - settings object to save
|
|
49
|
+
* @returns {boolean} success status
|
|
50
|
+
*/
|
|
51
|
+
save(settings) {
|
|
52
|
+
try {
|
|
53
|
+
// Ensure .claude directory exists
|
|
54
|
+
if (!fs.existsSync(this.claudeDir)) {
|
|
55
|
+
fs.mkdirSync(this.claudeDir, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fs.writeFileSync(this.settingsPath, JSON.stringify(settings, null, 2));
|
|
59
|
+
return true;
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.error('Failed to save Claude settings:', err.message);
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Enable Plexor routing by setting env vars in settings.json
|
|
68
|
+
*
|
|
69
|
+
* This is the KEY mechanism: setting ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN
|
|
70
|
+
* in the env block redirects ALL Claude Code sessions to Plexor automatically.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} apiKey - Plexor API key (plx_*)
|
|
73
|
+
* @param {Object} options - { useStaging: boolean }
|
|
74
|
+
* @returns {boolean} success status
|
|
75
|
+
*/
|
|
76
|
+
enablePlexorRouting(apiKey, options = {}) {
|
|
77
|
+
const { useStaging = true } = options;
|
|
78
|
+
const apiUrl = useStaging ? PLEXOR_STAGING_URL : PLEXOR_PROD_URL;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const settings = this.load();
|
|
82
|
+
|
|
83
|
+
// Initialize env block if doesn't exist
|
|
84
|
+
if (!settings.env) {
|
|
85
|
+
settings.env = {};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Set the magic environment variables
|
|
89
|
+
// ANTHROPIC_AUTH_TOKEN has higher precedence than ANTHROPIC_API_KEY
|
|
90
|
+
settings.env.ANTHROPIC_BASE_URL = apiUrl;
|
|
91
|
+
settings.env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
92
|
+
|
|
93
|
+
const success = this.save(settings);
|
|
94
|
+
|
|
95
|
+
if (success) {
|
|
96
|
+
console.log(`✓ Plexor routing enabled`);
|
|
97
|
+
console.log(` Base URL: ${apiUrl}`);
|
|
98
|
+
console.log(` API Key: ${apiKey.substring(0, 12)}...`);
|
|
99
|
+
console.log(`\n All Claude Code sessions will now route through Plexor.`);
|
|
100
|
+
console.log(` Changes take effect immediately (no restart needed).`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return success;
|
|
104
|
+
} catch (err) {
|
|
105
|
+
console.error('Failed to enable Plexor routing:', err.message);
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Disable Plexor routing by removing env vars from settings.json
|
|
112
|
+
*
|
|
113
|
+
* This restores direct connection to Anthropic API.
|
|
114
|
+
*
|
|
115
|
+
* @returns {boolean} success status
|
|
116
|
+
*/
|
|
117
|
+
disablePlexorRouting() {
|
|
118
|
+
try {
|
|
119
|
+
const settings = this.load();
|
|
120
|
+
|
|
121
|
+
if (!settings.env) {
|
|
122
|
+
console.log('Plexor routing is not currently enabled.');
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Remove Plexor-specific env vars
|
|
127
|
+
delete settings.env.ANTHROPIC_BASE_URL;
|
|
128
|
+
delete settings.env.ANTHROPIC_AUTH_TOKEN;
|
|
129
|
+
|
|
130
|
+
// Clean up empty env block
|
|
131
|
+
if (Object.keys(settings.env).length === 0) {
|
|
132
|
+
delete settings.env;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const success = this.save(settings);
|
|
136
|
+
|
|
137
|
+
if (success) {
|
|
138
|
+
console.log('✓ Plexor routing disabled');
|
|
139
|
+
console.log(' Claude Code will now connect directly to Anthropic.');
|
|
140
|
+
console.log(' Changes take effect immediately (no restart needed).');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return success;
|
|
144
|
+
} catch (err) {
|
|
145
|
+
console.error('Failed to disable Plexor routing:', err.message);
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get current routing status
|
|
152
|
+
* @returns {Object} { enabled: boolean, baseUrl: string|null, hasToken: boolean }
|
|
153
|
+
*/
|
|
154
|
+
getRoutingStatus() {
|
|
155
|
+
try {
|
|
156
|
+
const settings = this.load();
|
|
157
|
+
|
|
158
|
+
const baseUrl = settings.env?.ANTHROPIC_BASE_URL || null;
|
|
159
|
+
const hasToken = !!settings.env?.ANTHROPIC_AUTH_TOKEN;
|
|
160
|
+
|
|
161
|
+
// Check if routing to Plexor
|
|
162
|
+
const isPlexorRouting = baseUrl && (
|
|
163
|
+
baseUrl.includes('plexor') ||
|
|
164
|
+
baseUrl.includes('staging.api')
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
enabled: isPlexorRouting,
|
|
169
|
+
baseUrl,
|
|
170
|
+
hasToken,
|
|
171
|
+
isStaging: baseUrl?.includes('staging') || false,
|
|
172
|
+
tokenPreview: hasToken ? settings.env.ANTHROPIC_AUTH_TOKEN.substring(0, 12) + '...' : null
|
|
173
|
+
};
|
|
174
|
+
} catch {
|
|
175
|
+
return { enabled: false, baseUrl: null, hasToken: false };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Update just the API key without changing other settings
|
|
181
|
+
* @param {string} apiKey - new Plexor API key
|
|
182
|
+
* @returns {boolean} success status
|
|
183
|
+
*/
|
|
184
|
+
updateApiKey(apiKey) {
|
|
185
|
+
try {
|
|
186
|
+
const settings = this.load();
|
|
187
|
+
|
|
188
|
+
if (!settings.env) {
|
|
189
|
+
settings.env = {};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
settings.env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
193
|
+
return this.save(settings);
|
|
194
|
+
} catch (err) {
|
|
195
|
+
console.error('Failed to update API key:', err.message);
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Switch between staging and production
|
|
202
|
+
* @param {boolean} useStaging - true for staging, false for production
|
|
203
|
+
* @returns {boolean} success status
|
|
204
|
+
*/
|
|
205
|
+
setEnvironment(useStaging) {
|
|
206
|
+
try {
|
|
207
|
+
const settings = this.load();
|
|
208
|
+
|
|
209
|
+
if (!settings.env?.ANTHROPIC_BASE_URL) {
|
|
210
|
+
console.log('Plexor routing is not enabled. Run /plexor-login first.');
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
settings.env.ANTHROPIC_BASE_URL = useStaging ? PLEXOR_STAGING_URL : PLEXOR_PROD_URL;
|
|
215
|
+
|
|
216
|
+
const success = this.save(settings);
|
|
217
|
+
if (success) {
|
|
218
|
+
console.log(`✓ Switched to ${useStaging ? 'staging' : 'production'} environment`);
|
|
219
|
+
}
|
|
220
|
+
return success;
|
|
221
|
+
} catch (err) {
|
|
222
|
+
console.error('Failed to switch environment:', err.message);
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Export singleton instance and class
|
|
229
|
+
const settingsManager = new ClaudeSettingsManager();
|
|
230
|
+
|
|
231
|
+
module.exports = {
|
|
232
|
+
ClaudeSettingsManager,
|
|
233
|
+
settingsManager,
|
|
234
|
+
CLAUDE_DIR,
|
|
235
|
+
SETTINGS_PATH,
|
|
236
|
+
PLEXOR_STAGING_URL,
|
|
237
|
+
PLEXOR_PROD_URL
|
|
238
|
+
};
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
const fs = require('fs');
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const os = require('os');
|
|
13
|
+
const { execSync } = require('child_process');
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Get the correct home directory, accounting for sudo.
|
|
@@ -30,6 +31,43 @@ function getHomeDir() {
|
|
|
30
31
|
return os.homedir();
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Get uid/gid for the target user (handles sudo case).
|
|
36
|
+
* Returns null if not running with sudo or on Windows.
|
|
37
|
+
*/
|
|
38
|
+
function getTargetUserIds() {
|
|
39
|
+
const sudoUser = process.env.SUDO_USER;
|
|
40
|
+
if (!sudoUser || os.platform() === 'win32') {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
// Get uid and gid for the sudo user
|
|
46
|
+
const uid = parseInt(execSync(`id -u ${sudoUser}`, { encoding: 'utf8' }).trim(), 10);
|
|
47
|
+
const gid = parseInt(execSync(`id -g ${sudoUser}`, { encoding: 'utf8' }).trim(), 10);
|
|
48
|
+
return { uid, gid, user: sudoUser };
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Recursively chown a directory and all its contents.
|
|
56
|
+
*/
|
|
57
|
+
function chownRecursive(dirPath, uid, gid) {
|
|
58
|
+
if (!fs.existsSync(dirPath)) return;
|
|
59
|
+
|
|
60
|
+
const stat = fs.statSync(dirPath);
|
|
61
|
+
fs.chownSync(dirPath, uid, gid);
|
|
62
|
+
|
|
63
|
+
if (stat.isDirectory()) {
|
|
64
|
+
const entries = fs.readdirSync(dirPath);
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
chownRecursive(path.join(dirPath, entry), uid, gid);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
33
71
|
const HOME_DIR = getHomeDir();
|
|
34
72
|
const COMMANDS_SOURCE = path.join(__dirname, '..', 'commands');
|
|
35
73
|
const CLAUDE_COMMANDS_DIR = path.join(HOME_DIR, '.claude', 'commands');
|
|
@@ -54,6 +92,9 @@ const DEFAULT_CONFIG = {
|
|
|
54
92
|
|
|
55
93
|
function main() {
|
|
56
94
|
try {
|
|
95
|
+
// Get target user info for chown (if running with sudo)
|
|
96
|
+
const targetUser = getTargetUserIds();
|
|
97
|
+
|
|
57
98
|
// Create ~/.claude/commands/ if not exists
|
|
58
99
|
fs.mkdirSync(CLAUDE_COMMANDS_DIR, { recursive: true });
|
|
59
100
|
|
|
@@ -120,6 +161,16 @@ function main() {
|
|
|
120
161
|
jsInstalled.push(file);
|
|
121
162
|
}
|
|
122
163
|
|
|
164
|
+
// Fix file ownership when running with sudo
|
|
165
|
+
// Files are created as root but should be owned by the original user
|
|
166
|
+
if (targetUser) {
|
|
167
|
+
const { uid, gid } = targetUser;
|
|
168
|
+
// Chown the .claude directory and all contents we created
|
|
169
|
+
chownRecursive(path.join(HOME_DIR, '.claude'), uid, gid);
|
|
170
|
+
// Chown the .plexor directory and all contents
|
|
171
|
+
chownRecursive(PLEXOR_CONFIG_DIR, uid, gid);
|
|
172
|
+
}
|
|
173
|
+
|
|
123
174
|
// Detect shell type
|
|
124
175
|
const shell = process.env.SHELL || '';
|
|
125
176
|
const isZsh = shell.includes('zsh');
|
|
@@ -141,6 +192,9 @@ function main() {
|
|
|
141
192
|
if (jsInstalled.length > 0) {
|
|
142
193
|
console.log(` ✓ Installed ${jsInstalled.length} executors to ~/.claude/plugins/plexor/`);
|
|
143
194
|
}
|
|
195
|
+
if (targetUser) {
|
|
196
|
+
console.log(` ✓ Set file ownership to ${targetUser.user}`);
|
|
197
|
+
}
|
|
144
198
|
console.log('');
|
|
145
199
|
|
|
146
200
|
// CRITICAL: Make the required step VERY obvious
|