mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-11 00:47:52 +00:00
fix: lint errors and deleting plugin error on windows
This commit is contained in:
@@ -249,7 +249,7 @@ router.all('/:name/rpc/*', async (req, res) => {
|
||||
});
|
||||
|
||||
// DELETE /:name — Uninstall plugin (stops server first)
|
||||
router.delete('/:name', (req, res) => {
|
||||
router.delete('/:name', async (req, res) => {
|
||||
try {
|
||||
const pluginName = req.params.name;
|
||||
|
||||
@@ -258,12 +258,12 @@ router.delete('/:name', (req, res) => {
|
||||
return res.status(400).json({ error: 'Invalid plugin name' });
|
||||
}
|
||||
|
||||
// Stop server if running
|
||||
// Stop server and wait for the process to fully exit before deleting files
|
||||
if (isPluginRunning(pluginName)) {
|
||||
stopPluginServer(pluginName);
|
||||
await stopPluginServer(pluginName);
|
||||
}
|
||||
|
||||
uninstallPlugin(pluginName);
|
||||
await uninstallPlugin(pluginName);
|
||||
res.json({ success: true, name: pluginName });
|
||||
} catch (err) {
|
||||
res.status(400).json({ error: 'Failed to uninstall plugin', details: err.message });
|
||||
|
||||
@@ -326,13 +326,28 @@ export function updatePluginFromGit(name) {
|
||||
});
|
||||
}
|
||||
|
||||
export function uninstallPlugin(name) {
|
||||
export async function uninstallPlugin(name) {
|
||||
const pluginDir = getPluginDir(name);
|
||||
if (!pluginDir) {
|
||||
throw new Error(`Plugin "${name}" not found`);
|
||||
}
|
||||
|
||||
fs.rmSync(pluginDir, { recursive: true, force: true });
|
||||
// On Windows, file handles may be released slightly after process exit.
|
||||
// Retry a few times with a short delay before giving up.
|
||||
const MAX_RETRIES = 5;
|
||||
const RETRY_DELAY_MS = 500;
|
||||
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||
try {
|
||||
fs.rmSync(pluginDir, { recursive: true, force: true });
|
||||
break;
|
||||
} catch (err) {
|
||||
if (err.code === 'EBUSY' && attempt < MAX_RETRIES) {
|
||||
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from config
|
||||
const config = getPluginsConfig();
|
||||
|
||||
@@ -93,26 +93,33 @@ export function startPluginServer(name, pluginDir, serverEntry) {
|
||||
|
||||
/**
|
||||
* Stop a plugin's server subprocess.
|
||||
* Returns a Promise that resolves when the process has fully exited.
|
||||
*/
|
||||
export function stopPluginServer(name) {
|
||||
const entry = runningPlugins.get(name);
|
||||
if (!entry) return;
|
||||
if (!entry) return Promise.resolve();
|
||||
|
||||
entry.process.kill('SIGTERM');
|
||||
|
||||
// Force kill after 5 seconds if still running
|
||||
const forceKillTimer = setTimeout(() => {
|
||||
if (runningPlugins.has(name)) {
|
||||
entry.process.kill('SIGKILL');
|
||||
return new Promise((resolve) => {
|
||||
const cleanup = () => {
|
||||
clearTimeout(forceKillTimer);
|
||||
runningPlugins.delete(name);
|
||||
}
|
||||
}, 5000);
|
||||
resolve();
|
||||
};
|
||||
|
||||
entry.process.on('exit', () => {
|
||||
clearTimeout(forceKillTimer);
|
||||
entry.process.once('exit', cleanup);
|
||||
|
||||
entry.process.kill('SIGTERM');
|
||||
|
||||
// Force kill after 5 seconds if still running
|
||||
const forceKillTimer = setTimeout(() => {
|
||||
if (runningPlugins.has(name)) {
|
||||
entry.process.kill('SIGKILL');
|
||||
cleanup();
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
console.log(`[Plugins] Server stopped for "${name}"`);
|
||||
});
|
||||
|
||||
console.log(`[Plugins] Server stopped for "${name}"`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,9 +140,11 @@ export function isPluginRunning(name) {
|
||||
* Stop all running plugin servers (called on host shutdown).
|
||||
*/
|
||||
export function stopAllPlugins() {
|
||||
const stops = [];
|
||||
for (const [name] of runningPlugins) {
|
||||
stopPluginServer(name);
|
||||
stops.push(stopPluginServer(name));
|
||||
}
|
||||
return Promise.all(stops);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user