mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-14 02:42:04 +08:00
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 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,15 @@
|
|||||||
* Handles LiteLLM provider management, endpoint configuration, and cache management
|
* Handles LiteLLM provider management, endpoint configuration, and cache management
|
||||||
*/
|
*/
|
||||||
import type { IncomingMessage, ServerResponse } from 'http';
|
import type { IncomingMessage, ServerResponse } from 'http';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname, join as pathJoin } from 'path';
|
||||||
|
|
||||||
|
// Get current module path for package-relative lookups
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
// Package root: routes -> core -> src -> ccw -> package root
|
||||||
|
const PACKAGE_ROOT = pathJoin(__dirname, '..', '..', '..', '..');
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getAllProviders,
|
getAllProviders,
|
||||||
getProvider,
|
getProvider,
|
||||||
@@ -813,6 +822,7 @@ export async function handleLiteLLMApiRoutes(ctx: RouteContext): Promise<boolean
|
|||||||
path.join(initialPath, 'ccw-litellm'),
|
path.join(initialPath, 'ccw-litellm'),
|
||||||
path.join(initialPath, '..', 'ccw-litellm'),
|
path.join(initialPath, '..', 'ccw-litellm'),
|
||||||
path.join(process.cwd(), 'ccw-litellm'),
|
path.join(process.cwd(), 'ccw-litellm'),
|
||||||
|
path.join(PACKAGE_ROOT, 'ccw-litellm'), // npm package internal path
|
||||||
];
|
];
|
||||||
|
|
||||||
let packagePath = '';
|
let packagePath = '';
|
||||||
@@ -876,5 +886,45 @@ export async function handleLiteLLMApiRoutes(ctx: RouteContext): Promise<boolean
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// POST /api/litellm-api/ccw-litellm/uninstall - Uninstall ccw-litellm package
|
||||||
|
if (pathname === '/api/litellm-api/ccw-litellm/uninstall' && req.method === 'POST') {
|
||||||
|
handlePostRequest(req, res, async () => {
|
||||||
|
try {
|
||||||
|
const { spawn } = await import('child_process');
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const proc = spawn('pip', ['uninstall', '-y', 'ccw-litellm'], { shell: true, timeout: 120000 });
|
||||||
|
let output = '';
|
||||||
|
let error = '';
|
||||||
|
proc.stdout?.on('data', (data) => { output += data.toString(); });
|
||||||
|
proc.stderr?.on('data', (data) => { error += data.toString(); });
|
||||||
|
proc.on('close', (code) => {
|
||||||
|
// Clear status cache after uninstallation attempt
|
||||||
|
clearCcwLitellmStatusCache();
|
||||||
|
|
||||||
|
if (code === 0) {
|
||||||
|
broadcastToClients({
|
||||||
|
type: 'CCW_LITELLM_UNINSTALLED',
|
||||||
|
payload: { timestamp: new Date().toISOString() }
|
||||||
|
});
|
||||||
|
resolve({ success: true, message: 'ccw-litellm uninstalled successfully' });
|
||||||
|
} else {
|
||||||
|
// Check if package was not installed
|
||||||
|
if (error.includes('not installed') || output.includes('not installed')) {
|
||||||
|
resolve({ success: true, message: 'ccw-litellm was not installed' });
|
||||||
|
} else {
|
||||||
|
resolve({ success: false, error: error || output || 'Uninstallation failed' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
proc.on('error', (err) => resolve({ success: false, error: err.message }));
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
return { success: false, error: (err as Error).message };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3245,6 +3245,9 @@ function renderCcwLitellmStatusCard() {
|
|||||||
'<i data-lucide="check-circle" class="w-3.5 h-3.5"></i>' +
|
'<i data-lucide="check-circle" class="w-3.5 h-3.5"></i>' +
|
||||||
'ccw-litellm ' + (status.version || '') +
|
'ccw-litellm ' + (status.version || '') +
|
||||||
'</span>' +
|
'</span>' +
|
||||||
|
'<button class="btn-sm btn-outline-danger" onclick="uninstallCcwLitellm()" title="Uninstall ccw-litellm">' +
|
||||||
|
'<i data-lucide="trash-2" class="w-3.5 h-3.5"></i>' +
|
||||||
|
'</button>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
} else {
|
} else {
|
||||||
container.innerHTML =
|
container.innerHTML =
|
||||||
@@ -3299,10 +3302,51 @@ async function installCcwLitellm() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uninstall ccw-litellm package
|
||||||
|
*/
|
||||||
|
async function uninstallCcwLitellm() {
|
||||||
|
if (!confirm('Are you sure you want to uninstall ccw-litellm? This will disable LiteLLM features.')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var container = document.getElementById('ccwLitellmStatusContainer');
|
||||||
|
if (container) {
|
||||||
|
container.innerHTML =
|
||||||
|
'<div class="flex items-center gap-2 text-sm text-muted-foreground">' +
|
||||||
|
'<div class="animate-spin w-4 h-4 border-2 border-primary border-t-transparent rounded-full"></div>' +
|
||||||
|
'Uninstalling ccw-litellm...' +
|
||||||
|
'</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var response = await fetch('/api/litellm-api/ccw-litellm/uninstall', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({})
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
showRefreshToast('ccw-litellm uninstalled successfully!', 'success');
|
||||||
|
await checkCcwLitellmStatus(true);
|
||||||
|
renderCcwLitellmStatusCard();
|
||||||
|
} else {
|
||||||
|
showRefreshToast('Failed to uninstall ccw-litellm: ' + result.error, 'error');
|
||||||
|
renderCcwLitellmStatusCard();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showRefreshToast('Uninstall error: ' + e.message, 'error');
|
||||||
|
renderCcwLitellmStatusCard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Make functions globally accessible
|
// Make functions globally accessible
|
||||||
window.checkCcwLitellmStatus = checkCcwLitellmStatus;
|
window.checkCcwLitellmStatus = checkCcwLitellmStatus;
|
||||||
window.renderCcwLitellmStatusCard = renderCcwLitellmStatusCard;
|
window.renderCcwLitellmStatusCard = renderCcwLitellmStatusCard;
|
||||||
window.installCcwLitellm = installCcwLitellm;
|
window.installCcwLitellm = installCcwLitellm;
|
||||||
|
window.uninstallCcwLitellm = uninstallCcwLitellm;
|
||||||
|
|
||||||
|
|
||||||
// ========== Utility Functions ==========
|
// ========== Utility Functions ==========
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claude-code-workflow",
|
"name": "claude-code-workflow",
|
||||||
"version": "6.2.9",
|
"version": "6.3.0",
|
||||||
"description": "JSON-driven multi-agent development framework with intelligent CLI orchestration (Gemini/Qwen/Codex), context-first architecture, and automated workflow execution",
|
"description": "JSON-driven multi-agent development framework with intelligent CLI orchestration (Gemini/Qwen/Codex), context-first architecture, and automated workflow execution",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "ccw/src/index.js",
|
"main": "ccw/src/index.js",
|
||||||
|
|||||||
Reference in New Issue
Block a user