@plexor-dev/claude-code-plugin-localhost 0.1.0-localhost.1
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/LICENSE +21 -0
- package/README.md +114 -0
- package/commands/plexor-enabled.js +281 -0
- package/commands/plexor-enabled.md +48 -0
- package/commands/plexor-login.js +283 -0
- package/commands/plexor-login.md +27 -0
- package/commands/plexor-logout.js +143 -0
- package/commands/plexor-logout.md +27 -0
- package/commands/plexor-setup.md +172 -0
- package/commands/plexor-status.js +404 -0
- package/commands/plexor-status.md +21 -0
- package/commands/plexor-uninstall.js +293 -0
- package/commands/plexor-uninstall.md +30 -0
- 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 +44 -0
- 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/lib/settings-manager.js +354 -0
- package/package.json +57 -0
- package/scripts/plexor-cli.sh +48 -0
- package/scripts/postinstall.js +342 -0
- package/scripts/uninstall.js +154 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plexor Claude Code Plugin - Postinstall Script
|
|
5
|
+
*
|
|
6
|
+
* Copies slash commands to ~/.claude/commands/ and creates
|
|
7
|
+
* the ~/.plexor/ configuration directory.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const { execSync } = require('child_process');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get the correct home directory, accounting for sudo.
|
|
17
|
+
* When running with sudo, os.homedir() returns /root, but we want
|
|
18
|
+
* the actual user's home directory.
|
|
19
|
+
*/
|
|
20
|
+
function getHomeDir() {
|
|
21
|
+
// Check if running with sudo - SUDO_USER contains the original username
|
|
22
|
+
if (process.env.SUDO_USER) {
|
|
23
|
+
// On Linux/Mac, home directories are typically /home/<user> or /Users/<user>
|
|
24
|
+
const platform = os.platform();
|
|
25
|
+
if (platform === 'darwin') {
|
|
26
|
+
return path.join('/Users', process.env.SUDO_USER);
|
|
27
|
+
} else if (platform === 'linux') {
|
|
28
|
+
return path.join('/home', process.env.SUDO_USER);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return os.homedir();
|
|
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
|
+
|
|
71
|
+
const HOME_DIR = getHomeDir();
|
|
72
|
+
const COMMANDS_SOURCE = path.join(__dirname, '..', 'commands');
|
|
73
|
+
const LIB_SOURCE = path.join(__dirname, '..', 'lib');
|
|
74
|
+
const CLAUDE_COMMANDS_DIR = path.join(HOME_DIR, '.claude', 'commands');
|
|
75
|
+
const PLEXOR_PLUGINS_DIR = path.join(HOME_DIR, '.claude', 'plugins', 'plexor', 'commands');
|
|
76
|
+
const PLEXOR_LIB_DIR = path.join(HOME_DIR, '.claude', 'plugins', 'plexor', 'lib');
|
|
77
|
+
const PLEXOR_CONFIG_DIR = path.join(HOME_DIR, '.plexor');
|
|
78
|
+
const PLEXOR_CONFIG_FILE = path.join(PLEXOR_CONFIG_DIR, 'config.json');
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check for orphaned Plexor routing in settings.json without valid config.
|
|
82
|
+
* This can happen if a previous uninstall was incomplete.
|
|
83
|
+
*/
|
|
84
|
+
function checkOrphanedRouting() {
|
|
85
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
86
|
+
if (!home) return;
|
|
87
|
+
|
|
88
|
+
const settingsPath = path.join(home, '.claude', 'settings.json');
|
|
89
|
+
const configPath = path.join(home, '.plexor', 'config.json');
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
if (!fs.existsSync(settingsPath)) return;
|
|
93
|
+
|
|
94
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
95
|
+
const env = settings.env || {};
|
|
96
|
+
|
|
97
|
+
const hasPlexorUrl = env.ANTHROPIC_BASE_URL &&
|
|
98
|
+
env.ANTHROPIC_BASE_URL.includes('plexor');
|
|
99
|
+
|
|
100
|
+
if (hasPlexorUrl) {
|
|
101
|
+
// Check if there's a valid Plexor config
|
|
102
|
+
let hasValidConfig = false;
|
|
103
|
+
try {
|
|
104
|
+
if (fs.existsSync(configPath)) {
|
|
105
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
106
|
+
hasValidConfig = config.apiKey && config.apiKey.startsWith('plx_');
|
|
107
|
+
}
|
|
108
|
+
} catch (e) {}
|
|
109
|
+
|
|
110
|
+
if (!hasValidConfig) {
|
|
111
|
+
console.log('\n Warning: Detected orphaned Plexor routing in Claude settings');
|
|
112
|
+
console.log(' This may be from a previous installation.\n');
|
|
113
|
+
console.log(' Run /plexor-login to reconfigure, or');
|
|
114
|
+
console.log(' Run /plexor-uninstall to clean up\n');
|
|
115
|
+
} else {
|
|
116
|
+
console.log('\n Existing Plexor configuration detected');
|
|
117
|
+
console.log(' Your previous settings have been preserved.\n');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} catch (e) {
|
|
121
|
+
// Ignore errors in detection - don't break install
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Default configuration for new installs
|
|
126
|
+
// LOCALHOST PACKAGE
|
|
127
|
+
const DEFAULT_CONFIG = {
|
|
128
|
+
version: 1,
|
|
129
|
+
auth: {
|
|
130
|
+
mode: "pending",
|
|
131
|
+
authenticated_at: null
|
|
132
|
+
},
|
|
133
|
+
settings: {
|
|
134
|
+
enabled: true,
|
|
135
|
+
apiUrl: "http://localhost:8000",
|
|
136
|
+
mode: "balanced",
|
|
137
|
+
localCacheEnabled: true
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
function main() {
|
|
142
|
+
// Check for orphaned routing at start of postinstall
|
|
143
|
+
checkOrphanedRouting();
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
// Get target user info for chown (if running with sudo)
|
|
147
|
+
const targetUser = getTargetUserIds();
|
|
148
|
+
|
|
149
|
+
// Create ~/.claude/commands/ if not exists
|
|
150
|
+
fs.mkdirSync(CLAUDE_COMMANDS_DIR, { recursive: true });
|
|
151
|
+
|
|
152
|
+
// Create ~/.claude/plugins/plexor/commands/ for JS executors
|
|
153
|
+
fs.mkdirSync(PLEXOR_PLUGINS_DIR, { recursive: true });
|
|
154
|
+
|
|
155
|
+
// Create ~/.claude/plugins/plexor/lib/ for shared modules
|
|
156
|
+
fs.mkdirSync(PLEXOR_LIB_DIR, { recursive: true });
|
|
157
|
+
|
|
158
|
+
// Create ~/.plexor/ with secure permissions (owner only)
|
|
159
|
+
fs.mkdirSync(PLEXOR_CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
160
|
+
|
|
161
|
+
// Create default config.json if it doesn't exist
|
|
162
|
+
let configCreated = false;
|
|
163
|
+
if (!fs.existsSync(PLEXOR_CONFIG_FILE)) {
|
|
164
|
+
fs.writeFileSync(
|
|
165
|
+
PLEXOR_CONFIG_FILE,
|
|
166
|
+
JSON.stringify(DEFAULT_CONFIG, null, 2),
|
|
167
|
+
{ mode: 0o600 }
|
|
168
|
+
);
|
|
169
|
+
configCreated = true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Get list of command files (.md for Claude, .js for executors)
|
|
173
|
+
const mdFiles = fs.readdirSync(COMMANDS_SOURCE)
|
|
174
|
+
.filter(f => f.endsWith('.md'));
|
|
175
|
+
const jsFiles = fs.readdirSync(COMMANDS_SOURCE)
|
|
176
|
+
.filter(f => f.endsWith('.js'));
|
|
177
|
+
|
|
178
|
+
if (mdFiles.length === 0) {
|
|
179
|
+
console.error('No command files found in package. Installation may be corrupt.');
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const installed = [];
|
|
184
|
+
const backed_up = [];
|
|
185
|
+
|
|
186
|
+
// Copy .md command files to ~/.claude/commands/
|
|
187
|
+
for (const file of mdFiles) {
|
|
188
|
+
const src = path.join(COMMANDS_SOURCE, file);
|
|
189
|
+
const dest = path.join(CLAUDE_COMMANDS_DIR, file);
|
|
190
|
+
|
|
191
|
+
// Backup existing file if present and different
|
|
192
|
+
if (fs.existsSync(dest)) {
|
|
193
|
+
const existingContent = fs.readFileSync(dest, 'utf8');
|
|
194
|
+
const newContent = fs.readFileSync(src, 'utf8');
|
|
195
|
+
|
|
196
|
+
if (existingContent !== newContent) {
|
|
197
|
+
const backupPath = dest + '.backup';
|
|
198
|
+
fs.copyFileSync(dest, backupPath);
|
|
199
|
+
backed_up.push(file);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
fs.copyFileSync(src, dest);
|
|
204
|
+
installed.push(file.replace('.md', ''));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Copy .js executor files to ~/.claude/plugins/plexor/commands/
|
|
208
|
+
const jsInstalled = [];
|
|
209
|
+
for (const file of jsFiles) {
|
|
210
|
+
const src = path.join(COMMANDS_SOURCE, file);
|
|
211
|
+
const dest = path.join(PLEXOR_PLUGINS_DIR, file);
|
|
212
|
+
fs.copyFileSync(src, dest);
|
|
213
|
+
// Make executable
|
|
214
|
+
fs.chmodSync(dest, 0o755);
|
|
215
|
+
jsInstalled.push(file);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Copy lib files to ~/.claude/plugins/plexor/lib/
|
|
219
|
+
// CRITICAL: These are required for commands to work
|
|
220
|
+
const libInstalled = [];
|
|
221
|
+
if (fs.existsSync(LIB_SOURCE)) {
|
|
222
|
+
const libFiles = fs.readdirSync(LIB_SOURCE).filter(f => f.endsWith('.js'));
|
|
223
|
+
if (libFiles.length === 0) {
|
|
224
|
+
console.warn(' ⚠ Warning: No lib files found in package. Commands may not work.');
|
|
225
|
+
}
|
|
226
|
+
for (const file of libFiles) {
|
|
227
|
+
try {
|
|
228
|
+
const src = path.join(LIB_SOURCE, file);
|
|
229
|
+
const dest = path.join(PLEXOR_LIB_DIR, file);
|
|
230
|
+
fs.copyFileSync(src, dest);
|
|
231
|
+
libInstalled.push(file);
|
|
232
|
+
} catch (err) {
|
|
233
|
+
console.error(` ✗ Failed to copy lib/${file}: ${err.message}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
console.error(' ✗ CRITICAL: lib/ directory not found in package.');
|
|
238
|
+
console.error(' Commands will fail. Please reinstall the package.');
|
|
239
|
+
console.error(` Expected location: ${LIB_SOURCE}`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Verify critical lib file exists
|
|
243
|
+
const criticalLibFile = path.join(PLEXOR_LIB_DIR, 'settings-manager.js');
|
|
244
|
+
if (!fs.existsSync(criticalLibFile)) {
|
|
245
|
+
console.error('');
|
|
246
|
+
console.error(' ✗ CRITICAL: settings-manager.js was not installed.');
|
|
247
|
+
console.error(' This file is required for commands to work.');
|
|
248
|
+
console.error(' Try reinstalling: npm install @plexor-dev/claude-code-plugin-localhost');
|
|
249
|
+
console.error('');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Fix file ownership when running with sudo
|
|
253
|
+
// Files are created as root but should be owned by the original user
|
|
254
|
+
if (targetUser) {
|
|
255
|
+
const { uid, gid } = targetUser;
|
|
256
|
+
// Chown the .claude directory and all contents we created
|
|
257
|
+
chownRecursive(path.join(HOME_DIR, '.claude'), uid, gid);
|
|
258
|
+
// Chown the .plexor directory and all contents
|
|
259
|
+
chownRecursive(PLEXOR_CONFIG_DIR, uid, gid);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Detect shell type
|
|
263
|
+
const shell = process.env.SHELL || '';
|
|
264
|
+
const isZsh = shell.includes('zsh');
|
|
265
|
+
const shellRc = isZsh ? '~/.zshrc' : '~/.bashrc';
|
|
266
|
+
|
|
267
|
+
// Print success message with clear onboarding steps
|
|
268
|
+
console.log('');
|
|
269
|
+
console.log(' ╔═══════════════════════════════════════════════════════════════════╗');
|
|
270
|
+
console.log(' ║ ║');
|
|
271
|
+
console.log(' ║ Plexor Claude Code Plugin installed successfully! ║');
|
|
272
|
+
console.log(' ║ ║');
|
|
273
|
+
console.log(' ╚═══════════════════════════════════════════════════════════════════╝');
|
|
274
|
+
console.log('');
|
|
275
|
+
|
|
276
|
+
if (configCreated) {
|
|
277
|
+
console.log(' ✓ Created ~/.plexor/config.json');
|
|
278
|
+
}
|
|
279
|
+
console.log(` ✓ Installed ${installed.length} slash commands to ~/.claude/commands/`);
|
|
280
|
+
if (jsInstalled.length > 0) {
|
|
281
|
+
console.log(` ✓ Installed ${jsInstalled.length} executors to ~/.claude/plugins/plexor/commands/`);
|
|
282
|
+
}
|
|
283
|
+
if (libInstalled.length > 0) {
|
|
284
|
+
console.log(` ✓ Installed ${libInstalled.length} lib modules to ~/.claude/plugins/plexor/lib/`);
|
|
285
|
+
}
|
|
286
|
+
if (targetUser) {
|
|
287
|
+
console.log(` ✓ Set file ownership to ${targetUser.user}`);
|
|
288
|
+
}
|
|
289
|
+
console.log('');
|
|
290
|
+
|
|
291
|
+
// CRITICAL: Make the required step VERY obvious
|
|
292
|
+
console.log(' ┌─────────────────────────────────────────────────────────────────┐');
|
|
293
|
+
console.log(' │ REQUIRED: Run this command to enable Plexor routing: │');
|
|
294
|
+
console.log(' └─────────────────────────────────────────────────────────────────┘');
|
|
295
|
+
console.log('');
|
|
296
|
+
console.log(' For Claude MAX users (OAuth):');
|
|
297
|
+
console.log('');
|
|
298
|
+
console.log(` echo 'export ANTHROPIC_BASE_URL="http://localhost:8000/gateway/anthropic"' >> ${shellRc}`);
|
|
299
|
+
console.log(` source ${shellRc}`);
|
|
300
|
+
console.log('');
|
|
301
|
+
console.log(' For API key users (get key at https://plexor.dev/dashboard):');
|
|
302
|
+
console.log('');
|
|
303
|
+
console.log(` echo 'export ANTHROPIC_BASE_URL="http://localhost:8000/gateway/anthropic"' >> ${shellRc}`);
|
|
304
|
+
console.log(` echo 'export ANTHROPIC_API_KEY="plx_your_key_here"' >> ${shellRc}`);
|
|
305
|
+
console.log(` source ${shellRc}`);
|
|
306
|
+
console.log('');
|
|
307
|
+
console.log(' ┌─────────────────────────────────────────────────────────────────┐');
|
|
308
|
+
console.log(' │ Then start Claude Code and run: /plexor-status │');
|
|
309
|
+
console.log(' └─────────────────────────────────────────────────────────────────┘');
|
|
310
|
+
console.log('');
|
|
311
|
+
console.log(' Available commands:');
|
|
312
|
+
console.log(' /plexor-setup - First-time setup wizard');
|
|
313
|
+
console.log(' /plexor-login - Authenticate with API key');
|
|
314
|
+
console.log(' /plexor-status - Check connection and see savings');
|
|
315
|
+
console.log(' /plexor-enabled - Enable/disable Plexor routing');
|
|
316
|
+
console.log('');
|
|
317
|
+
console.log(' Documentation: https://plexor.dev/docs');
|
|
318
|
+
console.log('');
|
|
319
|
+
|
|
320
|
+
if (backed_up.length > 0) {
|
|
321
|
+
console.log(' Note: Existing files backed up (.backup):');
|
|
322
|
+
backed_up.forEach(f => console.log(` ${f}`));
|
|
323
|
+
console.log('');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.error('');
|
|
328
|
+
console.error(' Plexor plugin installation failed');
|
|
329
|
+
console.error('');
|
|
330
|
+
console.error(` Error: ${error.message}`);
|
|
331
|
+
console.error('');
|
|
332
|
+
console.error(' Troubleshooting:');
|
|
333
|
+
console.error(' - Ensure you have write access to ~/.claude/commands/');
|
|
334
|
+
console.error(' - Try running with sudo if permission denied');
|
|
335
|
+
console.error('');
|
|
336
|
+
console.error(' Report issues: https://github.com/plexor-ai/claude-code-plugin/issues');
|
|
337
|
+
console.error('');
|
|
338
|
+
process.exit(1);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
main();
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plexor Claude Code Plugin (Staging) - Comprehensive Uninstall Script
|
|
5
|
+
*
|
|
6
|
+
* Runs on npm uninstall (when npm actually calls it).
|
|
7
|
+
* Also callable directly: node scripts/uninstall.js
|
|
8
|
+
*
|
|
9
|
+
* Performs complete cleanup:
|
|
10
|
+
* 1. Removes Plexor routing from ~/.claude/settings.json
|
|
11
|
+
* 2. Removes slash command files from ~/.claude/commands/
|
|
12
|
+
* 3. Removes plugin directory from ~/.claude/plugins/plexor/
|
|
13
|
+
* 4. Restores any backups if they exist
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
// Get home directory - support both Unix and Windows
|
|
20
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
21
|
+
if (!home) {
|
|
22
|
+
console.log('Warning: HOME not set, skipping cleanup');
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log('');
|
|
27
|
+
console.log(' Plexor plugin cleanup...');
|
|
28
|
+
console.log('');
|
|
29
|
+
|
|
30
|
+
const results = {
|
|
31
|
+
routing: false,
|
|
32
|
+
commands: [],
|
|
33
|
+
restored: [],
|
|
34
|
+
pluginDir: false
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// 1. Remove routing from settings.json
|
|
38
|
+
// This is CRITICAL - do NOT depend on settings-manager module since it may not load during uninstall
|
|
39
|
+
try {
|
|
40
|
+
const settingsPath = path.join(home, '.claude', 'settings.json');
|
|
41
|
+
if (fs.existsSync(settingsPath)) {
|
|
42
|
+
const data = fs.readFileSync(settingsPath, 'utf8');
|
|
43
|
+
if (data && data.trim()) {
|
|
44
|
+
const settings = JSON.parse(data);
|
|
45
|
+
if (settings.env) {
|
|
46
|
+
const hadBaseUrl = !!settings.env.ANTHROPIC_BASE_URL;
|
|
47
|
+
const hadAuthToken = !!settings.env.ANTHROPIC_AUTH_TOKEN;
|
|
48
|
+
|
|
49
|
+
delete settings.env.ANTHROPIC_BASE_URL;
|
|
50
|
+
delete settings.env.ANTHROPIC_AUTH_TOKEN;
|
|
51
|
+
|
|
52
|
+
// Clean up empty env block
|
|
53
|
+
if (Object.keys(settings.env).length === 0) {
|
|
54
|
+
delete settings.env;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (hadBaseUrl || hadAuthToken) {
|
|
58
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), { mode: 0o600 });
|
|
59
|
+
results.routing = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.log(` Warning: Could not clean settings.json: ${e.message}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 2. Remove slash command files
|
|
69
|
+
// These are the Plexor-specific command files that get installed to ~/.claude/commands/
|
|
70
|
+
const plexorCommands = [
|
|
71
|
+
'plexor-config.md',
|
|
72
|
+
'plexor-enabled.md',
|
|
73
|
+
'plexor-login.md',
|
|
74
|
+
'plexor-logout.md',
|
|
75
|
+
'plexor-mode.md',
|
|
76
|
+
'plexor-provider.md',
|
|
77
|
+
'plexor-settings.md',
|
|
78
|
+
'plexor-setup.md',
|
|
79
|
+
'plexor-status.md',
|
|
80
|
+
'plexor-uninstall.md'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const commandsDir = path.join(home, '.claude', 'commands');
|
|
85
|
+
if (fs.existsSync(commandsDir)) {
|
|
86
|
+
for (const cmd of plexorCommands) {
|
|
87
|
+
const cmdPath = path.join(commandsDir, cmd);
|
|
88
|
+
const backupPath = cmdPath + '.backup';
|
|
89
|
+
|
|
90
|
+
if (fs.existsSync(cmdPath)) {
|
|
91
|
+
fs.unlinkSync(cmdPath);
|
|
92
|
+
results.commands.push(cmd.replace('.md', ''));
|
|
93
|
+
|
|
94
|
+
// Restore backup if it exists
|
|
95
|
+
if (fs.existsSync(backupPath)) {
|
|
96
|
+
fs.renameSync(backupPath, cmdPath);
|
|
97
|
+
results.restored.push(cmd);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} catch (e) {
|
|
103
|
+
console.log(` Warning: Could not clean commands: ${e.message}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 3. Remove plugin directory
|
|
107
|
+
try {
|
|
108
|
+
const pluginDir = path.join(home, '.claude', 'plugins', 'plexor');
|
|
109
|
+
if (fs.existsSync(pluginDir)) {
|
|
110
|
+
fs.rmSync(pluginDir, { recursive: true, force: true });
|
|
111
|
+
results.pluginDir = true;
|
|
112
|
+
}
|
|
113
|
+
} catch (e) {
|
|
114
|
+
console.log(` Warning: Could not remove plugin directory: ${e.message}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Output results
|
|
118
|
+
if (results.routing || results.commands.length > 0 || results.pluginDir) {
|
|
119
|
+
console.log(' Plexor plugin uninstalled');
|
|
120
|
+
console.log('');
|
|
121
|
+
|
|
122
|
+
if (results.routing) {
|
|
123
|
+
console.log(' Removed Plexor routing from Claude settings');
|
|
124
|
+
console.log(' (Claude Code now connects directly to Anthropic)');
|
|
125
|
+
console.log('');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (results.commands.length > 0) {
|
|
129
|
+
console.log(' Removed commands:');
|
|
130
|
+
results.commands.forEach(cmd => console.log(` /${cmd}`));
|
|
131
|
+
console.log('');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (results.restored.length > 0) {
|
|
135
|
+
console.log(' Restored from backup:');
|
|
136
|
+
results.restored.forEach(f => console.log(` ${f}`));
|
|
137
|
+
console.log('');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (results.pluginDir) {
|
|
141
|
+
console.log(' Removed plugin directory');
|
|
142
|
+
console.log('');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log(' Note: ~/.plexor/ config directory was preserved.');
|
|
146
|
+
console.log(' To remove it: rm -rf ~/.plexor');
|
|
147
|
+
console.log('');
|
|
148
|
+
} else {
|
|
149
|
+
console.log(' No Plexor components found to clean up.');
|
|
150
|
+
console.log('');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
console.log(' Cleanup complete');
|
|
154
|
+
console.log('');
|