🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@leynier/ccst

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@leynier/ccst - npm Package Compare versions

Comparing version
0.4.0
to
0.5.0
+1
-1
package.json
{
"name": "@leynier/ccst",
"version": "0.4.0",
"version": "0.5.0",
"description": "Claude Code Switch Tools for managing contexts",

@@ -5,0 +5,0 @@ "keywords": [

@@ -10,2 +10,3 @@ import { spawn } from "node:child_process";

isProcessRunning,
killProcessTree,
writePid,

@@ -33,15 +34,11 @@ } from "../../utils/daemon.js";

console.log(pc.dim(`Stopping existing daemon (PID: ${existingPid})...`));
try {
process.kill(existingPid, "SIGTERM");
// Wait for process to terminate
const maxWait = 3000;
const startTime = Date.now();
while (Date.now() - startTime < maxWait) {
if (!isProcessRunning(existingPid)) {
break;
}
await new Promise((resolve) => setTimeout(resolve, 100));
await killProcessTree(existingPid, true);
// Wait for process to terminate
const maxWait = 3000;
const startTime = Date.now();
while (Date.now() - startTime < maxWait) {
if (!isProcessRunning(existingPid)) {
break;
}
} catch {
// Process may have already exited
await new Promise((resolve) => setTimeout(resolve, 100));
}

@@ -58,4 +55,4 @@ }

stdio: ["ignore", logFd, logFd],
// On Windows, need shell: true for proper detachment
...(process.platform === "win32" ? { shell: true } : {}),
// On Windows, need shell: true for proper detachment and windowsHide to hide console
...(process.platform === "win32" ? { shell: true, windowsHide: true } : {}),
});

@@ -62,0 +59,0 @@ if (!child.pid) {

import pc from "picocolors";
import {
CCS_PORTS,
getRunningDaemonPid,
isProcessRunning,
killProcessByPort,
killProcessTree,
removePid,

@@ -14,10 +17,6 @@ } from "../../utils/daemon.js";

const pid = await getRunningDaemonPid();
if (pid === null) {
console.log(pc.yellow("CCS config daemon is not running"));
return;
}
try {
// Send SIGTERM for graceful shutdown, SIGKILL if --force
const signal = options?.force ? "SIGKILL" : "SIGTERM";
process.kill(pid, signal);
let stopped = false;
// Phase 1: Kill by PID if exists
if (pid !== null) {
await killProcessTree(pid, options?.force);
// Wait for process to terminate (with timeout)

@@ -34,16 +33,15 @@ const maxWait = options?.force ? 1000 : 5000;

console.log(pc.green(`CCS config daemon stopped (PID: ${pid})`));
} catch (error) {
const err = error as NodeJS.ErrnoException;
if (err.code === "ESRCH") {
// Process doesn't exist - clean up stale PID file
removePid();
console.log(pc.yellow("Process not found, cleaned up stale PID file"));
} else if (err.code === "EPERM") {
console.log(
pc.red("Permission denied. Try running with elevated privileges."),
);
} else {
console.log(pc.red(`Failed to stop daemon: ${err.message}`));
stopped = true;
}
// Phase 2: Kill processes by port (especially important on Windows)
for (const port of CCS_PORTS) {
const killed = await killProcessByPort(port, options?.force ?? true);
if (killed) {
console.log(pc.dim(`Cleaned up process on port ${port}`));
stopped = true;
}
}
if (!stopped) {
console.log(pc.yellow("CCS config daemon is not running"));
}
};

@@ -84,1 +84,71 @@ import { existsSync, mkdirSync, unlinkSync } from "node:fs";

};
// Known CCS daemon ports
export const CCS_PORTS = [3000, 8317];
// Kill process tree (on Windows, kills all child processes)
export const killProcessTree = async (
pid: number,
force?: boolean,
): Promise<boolean> => {
if (process.platform === "win32") {
const args = ["/PID", String(pid), "/T"];
if (force) args.push("/F");
const proc = Bun.spawn(["taskkill", ...args], {
stdout: "ignore",
stderr: "ignore",
});
await proc.exited;
return proc.exitCode === 0;
}
const signal = force ? "SIGKILL" : "SIGTERM";
try {
process.kill(pid, signal);
return true;
} catch {
return false;
}
};
// Get PID of process listening on a port
export const getProcessByPort = async (
port: number,
): Promise<number | null> => {
if (process.platform === "win32") {
const proc = Bun.spawn(["cmd", "/c", `netstat -ano | findstr :${port}`], {
stdout: "pipe",
stderr: "ignore",
});
const output = await new Response(proc.stdout).text();
await proc.exited;
const lines = output.trim().split("\n");
for (const line of lines) {
if (line.includes("LISTENING") || line.includes("ESTABLISHED")) {
const parts = line.trim().split(/\s+/);
const pid = Number.parseInt(parts[parts.length - 1], 10);
if (Number.isFinite(pid) && pid > 0) {
return pid;
}
}
}
return null;
}
const proc = Bun.spawn(["lsof", "-ti", `:${port}`], {
stdout: "pipe",
stderr: "ignore",
});
const output = await new Response(proc.stdout).text();
await proc.exited;
const pid = Number.parseInt(output.trim(), 10);
return Number.isFinite(pid) ? pid : null;
};
// Kill process by port
export const killProcessByPort = async (
port: number,
force?: boolean,
): Promise<boolean> => {
const pid = await getProcessByPort(port);
if (pid === null) return false;
return killProcessTree(pid, force);
};