@plexor-dev/claude-code-plugin 0.1.0-beta.22 → 0.1.0-beta.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plexor-dev/claude-code-plugin",
3
- "version": "0.1.0-beta.22",
3
+ "version": "0.1.0-beta.24",
4
4
  "description": "LLM cost optimization plugin for Claude Code - Save up to 90% on AI costs",
5
5
  "main": "lib/constants.js",
6
6
  "bin": {
@@ -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,14 +31,70 @@ 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');
36
74
  const PLEXOR_PLUGINS_DIR = path.join(HOME_DIR, '.claude', 'plugins', 'plexor', 'commands');
37
75
  const PLEXOR_CONFIG_DIR = path.join(HOME_DIR, '.plexor');
76
+ const PLEXOR_CONFIG_FILE = path.join(PLEXOR_CONFIG_DIR, 'config.json');
77
+
78
+ // Default configuration for new installs
79
+ const DEFAULT_CONFIG = {
80
+ version: 1,
81
+ auth: {
82
+ mode: "pending",
83
+ authenticated_at: null
84
+ },
85
+ settings: {
86
+ enabled: true,
87
+ apiUrl: "https://api.plexor.dev",
88
+ mode: "balanced",
89
+ localCacheEnabled: true
90
+ }
91
+ };
38
92
 
39
93
  function main() {
40
94
  try {
95
+ // Get target user info for chown (if running with sudo)
96
+ const targetUser = getTargetUserIds();
97
+
41
98
  // Create ~/.claude/commands/ if not exists
42
99
  fs.mkdirSync(CLAUDE_COMMANDS_DIR, { recursive: true });
43
100
 
@@ -47,6 +104,17 @@ function main() {
47
104
  // Create ~/.plexor/ with secure permissions (owner only)
48
105
  fs.mkdirSync(PLEXOR_CONFIG_DIR, { recursive: true, mode: 0o700 });
49
106
 
107
+ // Create default config.json if it doesn't exist
108
+ let configCreated = false;
109
+ if (!fs.existsSync(PLEXOR_CONFIG_FILE)) {
110
+ fs.writeFileSync(
111
+ PLEXOR_CONFIG_FILE,
112
+ JSON.stringify(DEFAULT_CONFIG, null, 2),
113
+ { mode: 0o600 }
114
+ );
115
+ configCreated = true;
116
+ }
117
+
50
118
  // Get list of command files (.md for Claude, .js for executors)
51
119
  const mdFiles = fs.readdirSync(COMMANDS_SOURCE)
52
120
  .filter(f => f.endsWith('.md'));
@@ -93,42 +161,77 @@ function main() {
93
161
  jsInstalled.push(file);
94
162
  }
95
163
 
96
- // Print success message
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
+
174
+ // Detect shell type
175
+ const shell = process.env.SHELL || '';
176
+ const isZsh = shell.includes('zsh');
177
+ const shellRc = isZsh ? '~/.zshrc' : '~/.bashrc';
178
+
179
+ // Print success message with clear onboarding steps
97
180
  console.log('');
98
- console.log(' ╔═══════════════════════════════════════════════════════════╗');
99
- console.log(' ║ ║');
100
- console.log(' ║ Plexor Claude Code Plugin installed successfully! ║');
101
- console.log(' ║ ║');
102
- console.log(' ╚═══════════════════════════════════════════════════════════╝');
181
+ console.log(' ╔═══════════════════════════════════════════════════════════════════╗');
182
+ console.log(' ║ ║');
183
+ console.log(' ║ Plexor Claude Code Plugin installed successfully! ║');
184
+ console.log(' ║ ║');
185
+ console.log(' ╚═══════════════════════════════════════════════════════════════════╝');
103
186
  console.log('');
104
- console.log(' Commands installed to ~/.claude/commands/:');
105
- installed.forEach(cmd => {
106
- console.log(` /${cmd}`);
107
- });
108
187
 
109
- if (backed_up.length > 0) {
110
- console.log('');
111
- console.log(' Existing files backed up (.backup):');
112
- backed_up.forEach(f => console.log(` ${f}`));
188
+ if (configCreated) {
189
+ console.log(' ✓ Created ~/.plexor/config.json');
113
190
  }
114
-
191
+ console.log(` ✓ Installed ${installed.length} slash commands to ~/.claude/commands/`);
115
192
  if (jsInstalled.length > 0) {
116
- console.log('');
117
- console.log(' Executors installed to ~/.claude/plugins/plexor/commands/:');
118
- jsInstalled.forEach(f => console.log(` ${f}`));
193
+ console.log(` ✓ Installed ${jsInstalled.length} executors to ~/.claude/plugins/plexor/`);
119
194
  }
195
+ if (targetUser) {
196
+ console.log(` ✓ Set file ownership to ${targetUser.user}`);
197
+ }
198
+ console.log('');
120
199
 
200
+ // CRITICAL: Make the required step VERY obvious
201
+ console.log(' ┌─────────────────────────────────────────────────────────────────┐');
202
+ console.log(' │ REQUIRED: Run this command to enable Plexor routing: │');
203
+ console.log(' └─────────────────────────────────────────────────────────────────┘');
121
204
  console.log('');
122
- console.log(' Next steps:');
123
- console.log(' 1. Open Claude Code');
124
- console.log(' 2. Run /plexor-setup to configure (handles MAX + API key users)');
125
- console.log(' 3. Start saving on LLM costs!');
205
+ console.log(' For Claude MAX users (OAuth):');
126
206
  console.log('');
127
- console.log(' Have Claude MAX? Just set: export ANTHROPIC_BASE_URL="https://api.plexor.dev/gateway/anthropic"');
207
+ console.log(` echo 'export ANTHROPIC_BASE_URL="https://api.plexor.dev/gateway/anthropic"' >> ${shellRc}`);
208
+ console.log(` source ${shellRc}`);
209
+ console.log('');
210
+ console.log(' For API key users (get key at https://plexor.dev/dashboard):');
211
+ console.log('');
212
+ console.log(` echo 'export ANTHROPIC_BASE_URL="https://api.plexor.dev/gateway/anthropic"' >> ${shellRc}`);
213
+ console.log(` echo 'export ANTHROPIC_API_KEY="plx_your_key_here"' >> ${shellRc}`);
214
+ console.log(` source ${shellRc}`);
215
+ console.log('');
216
+ console.log(' ┌─────────────────────────────────────────────────────────────────┐');
217
+ console.log(' │ Then start Claude Code and run: /plexor-status │');
218
+ console.log(' └─────────────────────────────────────────────────────────────────┘');
219
+ console.log('');
220
+ console.log(' Available commands:');
221
+ console.log(' /plexor-status - Check connection and see savings');
222
+ console.log(' /plexor-mode - Switch modes (eco/balanced/quality)');
223
+ console.log(' /plexor-login - Authenticate with API key');
224
+ console.log(' /plexor-settings - View/modify settings');
128
225
  console.log('');
129
226
  console.log(' Documentation: https://plexor.dev/docs');
130
227
  console.log('');
131
228
 
229
+ if (backed_up.length > 0) {
230
+ console.log(' Note: Existing files backed up (.backup):');
231
+ backed_up.forEach(f => console.log(` ${f}`));
232
+ console.log('');
233
+ }
234
+
132
235
  } catch (error) {
133
236
  console.error('');
134
237
  console.error(' Plexor plugin installation failed');