Skip to content

Commit e8b9bca

Browse files
catlog22claude
andcommitted
feat: Add ccw-litellm uninstall button and fix npm install path resolution
- Fix ccw-litellm path lookup for npm distribution by adding PACKAGE_ROOT - Add uninstall button to API Settings page for ccw-litellm - Add /api/litellm-api/ccw-litellm/uninstall endpoint 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 052351a commit e8b9bca

File tree

3 files changed

+95
-1
lines changed

3 files changed

+95
-1
lines changed

ccw/src/core/routes/litellm-api-routes.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44
* Handles LiteLLM provider management, endpoint configuration, and cache management
55
*/
66
import type { IncomingMessage, ServerResponse } from 'http';
7+
import { fileURLToPath } from 'url';
8+
import { dirname, join as pathJoin } from 'path';
9+
10+
// Get current module path for package-relative lookups
11+
const __filename = fileURLToPath(import.meta.url);
12+
const __dirname = dirname(__filename);
13+
// Package root: routes -> core -> src -> ccw -> package root
14+
const PACKAGE_ROOT = pathJoin(__dirname, '..', '..', '..', '..');
15+
716
import {
817
getAllProviders,
918
getProvider,
@@ -813,6 +822,7 @@ export async function handleLiteLLMApiRoutes(ctx: RouteContext): Promise<boolean
813822
path.join(initialPath, 'ccw-litellm'),
814823
path.join(initialPath, '..', 'ccw-litellm'),
815824
path.join(process.cwd(), 'ccw-litellm'),
825+
path.join(PACKAGE_ROOT, 'ccw-litellm'), // npm package internal path
816826
];
817827

818828
let packagePath = '';
@@ -876,5 +886,45 @@ export async function handleLiteLLMApiRoutes(ctx: RouteContext): Promise<boolean
876886
return true;
877887
}
878888

889+
// POST /api/litellm-api/ccw-litellm/uninstall - Uninstall ccw-litellm package
890+
if (pathname === '/api/litellm-api/ccw-litellm/uninstall' && req.method === 'POST') {
891+
handlePostRequest(req, res, async () => {
892+
try {
893+
const { spawn } = await import('child_process');
894+
895+
return new Promise((resolve) => {
896+
const proc = spawn('pip', ['uninstall', '-y', 'ccw-litellm'], { shell: true, timeout: 120000 });
897+
let output = '';
898+
let error = '';
899+
proc.stdout?.on('data', (data) => { output += data.toString(); });
900+
proc.stderr?.on('data', (data) => { error += data.toString(); });
901+
proc.on('close', (code) => {
902+
// Clear status cache after uninstallation attempt
903+
clearCcwLitellmStatusCache();
904+
905+
if (code === 0) {
906+
broadcastToClients({
907+
type: 'CCW_LITELLM_UNINSTALLED',
908+
payload: { timestamp: new Date().toISOString() }
909+
});
910+
resolve({ success: true, message: 'ccw-litellm uninstalled successfully' });
911+
} else {
912+
// Check if package was not installed
913+
if (error.includes('not installed') || output.includes('not installed')) {
914+
resolve({ success: true, message: 'ccw-litellm was not installed' });
915+
} else {
916+
resolve({ success: false, error: error || output || 'Uninstallation failed' });
917+
}
918+
}
919+
});
920+
proc.on('error', (err) => resolve({ success: false, error: err.message }));
921+
});
922+
} catch (err) {
923+
return { success: false, error: (err as Error).message };
924+
}
925+
});
926+
return true;
927+
}
928+
879929
return false;
880930
}

ccw/src/templates/dashboard-js/views/api-settings.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3245,6 +3245,9 @@ function renderCcwLitellmStatusCard() {
32453245
'<i data-lucide="check-circle" class="w-3.5 h-3.5"></i>' +
32463246
'ccw-litellm ' + (status.version || '') +
32473247
'</span>' +
3248+
'<button class="btn-sm btn-outline-danger" onclick="uninstallCcwLitellm()" title="Uninstall ccw-litellm">' +
3249+
'<i data-lucide="trash-2" class="w-3.5 h-3.5"></i>' +
3250+
'</button>' +
32483251
'</div>';
32493252
} else {
32503253
container.innerHTML =
@@ -3299,10 +3302,51 @@ async function installCcwLitellm() {
32993302
}
33003303
}
33013304

3305+
/**
3306+
* Uninstall ccw-litellm package
3307+
*/
3308+
async function uninstallCcwLitellm() {
3309+
if (!confirm('Are you sure you want to uninstall ccw-litellm? This will disable LiteLLM features.')) {
3310+
return;
3311+
}
3312+
3313+
var container = document.getElementById('ccwLitellmStatusContainer');
3314+
if (container) {
3315+
container.innerHTML =
3316+
'<div class="flex items-center gap-2 text-sm text-muted-foreground">' +
3317+
'<div class="animate-spin w-4 h-4 border-2 border-primary border-t-transparent rounded-full"></div>' +
3318+
'Uninstalling ccw-litellm...' +
3319+
'</div>';
3320+
}
3321+
3322+
try {
3323+
var response = await fetch('/api/litellm-api/ccw-litellm/uninstall', {
3324+
method: 'POST',
3325+
headers: { 'Content-Type': 'application/json' },
3326+
body: JSON.stringify({})
3327+
});
3328+
3329+
var result = await response.json();
3330+
3331+
if (result.success) {
3332+
showRefreshToast('ccw-litellm uninstalled successfully!', 'success');
3333+
await checkCcwLitellmStatus(true);
3334+
renderCcwLitellmStatusCard();
3335+
} else {
3336+
showRefreshToast('Failed to uninstall ccw-litellm: ' + result.error, 'error');
3337+
renderCcwLitellmStatusCard();
3338+
}
3339+
} catch (e) {
3340+
showRefreshToast('Uninstall error: ' + e.message, 'error');
3341+
renderCcwLitellmStatusCard();
3342+
}
3343+
}
3344+
33023345
// Make functions globally accessible
33033346
window.checkCcwLitellmStatus = checkCcwLitellmStatus;
33043347
window.renderCcwLitellmStatusCard = renderCcwLitellmStatusCard;
33053348
window.installCcwLitellm = installCcwLitellm;
3349+
window.uninstallCcwLitellm = uninstallCcwLitellm;
33063350

33073351

33083352
// ========== Utility Functions ==========

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "claude-code-workflow",
3-
"version": "6.2.9",
3+
"version": "6.3.0",
44
"description": "JSON-driven multi-agent development framework with intelligent CLI orchestration (Gemini/Qwen/Codex), context-first architecture, and automated workflow execution",
55
"type": "module",
66
"main": "ccw/src/index.js",

0 commit comments

Comments
 (0)