@plexor-dev/claude-code-plugin 0.1.0-beta.31 → 0.1.0-beta.32

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.
@@ -14,6 +14,9 @@
14
14
  const fs = require('fs');
15
15
  const path = require('path');
16
16
 
17
+ // Import centralized constants with HOME directory validation
18
+ const { PLEXOR_DIR, CONFIG_PATH } = require('../lib/constants');
19
+
17
20
  // Import settings manager with error handling for missing lib
18
21
  let settingsManager, PLEXOR_STAGING_URL, PLEXOR_PROD_URL;
19
22
  try {
@@ -30,9 +33,6 @@ try {
30
33
  throw err;
31
34
  }
32
35
 
33
- const CONFIG_PATH = path.join(process.env.HOME, '.plexor', 'config.json');
34
- const PLEXOR_DIR = path.join(process.env.HOME, '.plexor');
35
-
36
36
  function loadConfig() {
37
37
  try {
38
38
  if (!fs.existsSync(CONFIG_PATH)) {
@@ -13,6 +13,9 @@ const path = require('path');
13
13
  const https = require('https');
14
14
  const http = require('http');
15
15
 
16
+ // Import centralized constants with HOME directory validation
17
+ const { PLEXOR_DIR, CONFIG_PATH, DEFAULT_API_URL } = require('../lib/constants');
18
+
16
19
  // Import settings manager with error handling for missing lib
17
20
  let settingsManager;
18
21
  try {
@@ -26,11 +29,6 @@ try {
26
29
  throw err;
27
30
  }
28
31
 
29
- const CONFIG_PATH = path.join(process.env.HOME, '.plexor', 'config.json');
30
- const PLEXOR_DIR = path.join(process.env.HOME, '.plexor');
31
- // PRODUCTION PACKAGE - uses production API
32
- const DEFAULT_API_URL = 'https://api.plexor.dev';
33
-
34
32
  function loadConfig() {
35
33
  try {
36
34
  if (!fs.existsSync(CONFIG_PATH)) {
@@ -121,9 +119,62 @@ function validateApiKey(apiUrl, apiKey) {
121
119
  });
122
120
  }
123
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
+
124
149
  async function main() {
125
150
  const args = process.argv.slice(2);
126
- let apiKey = args[0];
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
+ }
127
178
 
128
179
  // Check for existing login
129
180
  const config = loadConfig();
@@ -135,14 +186,14 @@ async function main() {
135
186
  console.log(`├─────────────────────────────────────────────┤`);
136
187
  console.log(`│ API Key: ${(existingKey.substring(0, 8) + '...').padEnd(33)}│`);
137
188
  console.log(`│ To re-login, provide a new key: │`);
138
- console.log(`│ /plexor-login <api-key> │`);
189
+ console.log(`│ echo $PLEXOR_API_KEY | /plexor-login │`);
139
190
  console.log(`│ To logout: │`);
140
191
  console.log(`│ /plexor-logout │`);
141
192
  console.log(`└─────────────────────────────────────────────┘`);
142
193
  return;
143
194
  }
144
195
 
145
- // If no key provided, prompt for it
196
+ // If no key provided, show secure usage options
146
197
  if (!apiKey) {
147
198
  console.log(`┌─────────────────────────────────────────────┐`);
148
199
  console.log(`│ Plexor Login │`);
@@ -150,11 +201,25 @@ async function main() {
150
201
  console.log(`│ Get your API key at: │`);
151
202
  console.log(`│ https://plexor.dev/dashboard/api-keys │`);
152
203
  console.log(`├─────────────────────────────────────────────┤`);
153
- console.log(`│ Usage: /plexor-login <api-key> │`);
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> │`);
154
210
  console.log(`└─────────────────────────────────────────────┘`);
155
211
  return;
156
212
  }
157
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
+
158
223
  // Validate key format
159
224
  if (!apiKey.startsWith('plx_') || apiKey.length < 20) {
160
225
  console.error(`Error: Invalid API key format`);
@@ -8,10 +8,22 @@
8
8
  const fs = require('fs');
9
9
  const path = require('path');
10
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');
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');
23
+ process.exit(1);
24
+ }
25
+ throw err;
26
+ }
15
27
 
16
28
  function loadConfig() {
17
29
  try {
@@ -96,6 +108,11 @@ function main() {
96
108
  process.exit(1);
97
109
  }
98
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
+
99
116
  // Clear session
100
117
  deleteFile(SESSION_PATH);
101
118
 
@@ -109,12 +126,13 @@ function main() {
109
126
  console.log(`│ ✓ Logged Out │`);
110
127
  console.log(`├─────────────────────────────────────────────┤`);
111
128
  console.log(`│ ✓ API key removed │`);
112
- console.log(`│ ✓ Plexor proxy disabled │`);
129
+ console.log(`│ ${routingDisabled ? '' : '○'} Claude Code routing disabled │`);
113
130
  console.log(`│ ✓ Session cleared │`);
114
131
  if (clearCache) {
115
132
  console.log(`│ ${cacheCleared ? '✓' : '○'} Cache cleared │`);
116
133
  }
117
134
  console.log(`├─────────────────────────────────────────────┤`);
135
+ console.log(`│ Claude Code now connects directly. │`);
118
136
  console.log(`│ Run /plexor-login to re-authenticate. │`);
119
137
  if (!clearCache) {
120
138
  console.log(`│ Use --clear-cache to also clear cache. │`);
@@ -9,10 +9,9 @@ const fs = require('fs');
9
9
  const path = require('path');
10
10
  const https = require('https');
11
11
 
12
- const CONFIG_PATH = path.join(process.env.HOME, '.plexor', 'config.json');
13
- const SESSION_PATH = path.join(process.env.HOME, '.plexor', 'session.json');
14
- const CLAUDE_SETTINGS_PATH = path.join(process.env.HOME, '.claude', 'settings.json');
15
- const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
12
+ // Import centralized constants with HOME directory validation
13
+ const { HOME_DIR, CONFIG_PATH, SESSION_PATH, SESSION_TIMEOUT_MS } = require('../lib/constants');
14
+ const CLAUDE_SETTINGS_PATH = path.join(HOME_DIR, '.claude', 'settings.json');
16
15
 
17
16
  /**
18
17
  * Check if Claude Code is actually routing through Plexor
package/lib/constants.js CHANGED
@@ -4,7 +4,23 @@
4
4
 
5
5
  const path = require('path');
6
6
 
7
- const PLEXOR_DIR = path.join(process.env.HOME || process.env.USERPROFILE || '', '.plexor');
7
+ /**
8
+ * Get the user's home directory with proper validation
9
+ * @returns {string} Home directory path
10
+ * @throws {Error} If HOME is not set or empty
11
+ */
12
+ function getHomeDir() {
13
+ const home = process.env.HOME || process.env.USERPROFILE;
14
+ if (!home || home.trim() === '') {
15
+ console.error('Error: HOME environment variable is not set.');
16
+ console.error(' Please set HOME to your user directory before running Plexor commands.');
17
+ process.exit(1);
18
+ }
19
+ return home;
20
+ }
21
+
22
+ const HOME_DIR = getHomeDir();
23
+ const PLEXOR_DIR = path.join(HOME_DIR, '.plexor');
8
24
  const CONFIG_PATH = path.join(PLEXOR_DIR, 'config.json');
9
25
  const SESSION_PATH = path.join(PLEXOR_DIR, 'session.json');
10
26
  const CACHE_PATH = path.join(PLEXOR_DIR, 'cache.json');
@@ -15,6 +31,8 @@ const DEFAULT_API_URL = 'https://api.plexor.dev';
15
31
  const DEFAULT_TIMEOUT = 5000;
16
32
 
17
33
  module.exports = {
34
+ getHomeDir,
35
+ HOME_DIR,
18
36
  PLEXOR_DIR,
19
37
  CONFIG_PATH,
20
38
  SESSION_PATH,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plexor-dev/claude-code-plugin",
3
- "version": "0.1.0-beta.31",
3
+ "version": "0.1.0-beta.32",
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": {
@@ -11,11 +11,32 @@ const fs = require('fs');
11
11
  const path = require('path');
12
12
  const os = require('os');
13
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 settings manager can't be loaded during uninstall, continue anyway
21
+ settingsManager = null;
22
+ }
23
+
14
24
  const COMMANDS_SOURCE = path.join(__dirname, '..', 'commands');
15
25
  const CLAUDE_COMMANDS_DIR = path.join(os.homedir(), '.claude', 'commands');
16
26
 
17
27
  function main() {
18
28
  try {
29
+ // CRITICAL: Disable Claude Code routing before removing commands
30
+ // This ensures users don't get stuck with Plexor routing after uninstall
31
+ let routingDisabled = false;
32
+ if (settingsManager) {
33
+ try {
34
+ routingDisabled = settingsManager.disablePlexorRouting();
35
+ } catch (e) {
36
+ // Continue with uninstall even if routing cleanup fails
37
+ }
38
+ }
39
+
19
40
  // Get list of our command files
20
41
  const files = fs.readdirSync(COMMANDS_SOURCE)
21
42
  .filter(f => f.endsWith('.md'));
@@ -39,12 +60,21 @@ function main() {
39
60
  }
40
61
  }
41
62
 
42
- if (removed.length > 0) {
63
+ if (removed.length > 0 || routingDisabled) {
43
64
  console.log('');
44
65
  console.log(' Plexor plugin uninstalled');
45
66
  console.log('');
46
- console.log(' Removed commands:');
47
- removed.forEach(cmd => console.log(` /${cmd}`));
67
+
68
+ if (routingDisabled) {
69
+ console.log(' ✓ Claude Code routing disabled');
70
+ console.log(' (Claude Code now connects directly to Anthropic)');
71
+ console.log('');
72
+ }
73
+
74
+ if (removed.length > 0) {
75
+ console.log(' Removed commands:');
76
+ removed.forEach(cmd => console.log(` /${cmd}`));
77
+ }
48
78
 
49
79
  if (restored.length > 0) {
50
80
  console.log('');