Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 32 additions & 24 deletions cli/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,13 @@ fn get_port_path(session: &str) -> PathBuf {
}

#[cfg(windows)]
fn get_port_for_session(session: &str) -> u16 {
let mut hash: i32 = 0;
for c in session.chars() {
hash = ((hash << 5).wrapping_sub(hash)).wrapping_add(c as i32);
}
// Correct logic: first take absolute modulo, then cast to u16
// Using unsigned_abs() to safely handle i32::MIN
49152 + ((hash.unsigned_abs() as u32 % 16383) as u16)
fn get_port_for_session(session: &str) -> Option<u16> {
let port_path = get_port_path(session);
if let Ok(content) = fs::read_to_string(&port_path) {
content.trim().parse::<u16>().ok()
} else {
None
}
}

#[cfg(unix)]
Expand All @@ -172,12 +171,15 @@ fn is_daemon_running(session: &str) -> bool {
if !pid_path.exists() {
return false;
}
let port = get_port_for_session(session);
TcpStream::connect_timeout(
&format!("127.0.0.1:{}", port).parse().unwrap(),
Duration::from_millis(100),
)
.is_ok()
if let Some(port) = get_port_for_session(session) {
TcpStream::connect_timeout(
&format!("127.0.0.1:{}", port).parse().unwrap(),
Duration::from_millis(250),
)
.is_ok()
} else {
false
}
}

fn daemon_ready(session: &str) -> bool {
Expand All @@ -188,12 +190,15 @@ fn daemon_ready(session: &str) -> bool {
}
#[cfg(windows)]
{
let port = get_port_for_session(session);
TcpStream::connect_timeout(
&format!("127.0.0.1:{}", port).parse().unwrap(),
Duration::from_millis(50),
)
.is_ok()
if let Some(port) = get_port_for_session(session) {
TcpStream::connect_timeout(
&format!("127.0.0.1:{}", port).parse().unwrap(),
Duration::from_millis(250),
)
.is_ok()
} else {
false
}
}
}

Expand Down Expand Up @@ -465,10 +470,13 @@ fn connect(session: &str) -> Result<Connection, String> {
}
#[cfg(windows)]
{
let port = get_port_for_session(session);
TcpStream::connect(format!("127.0.0.1:{}", port))
.map(Connection::Tcp)
.map_err(|e| format!("Failed to connect: {}", e))
if let Some(port) = get_port_for_session(session) {
TcpStream::connect(format!("127.0.0.1:{}", port))
.map(Connection::Tcp)
.map_err(|e| format!("Failed to connect: {}", e))
} else {
Err("Port file not found (daemon not running?)".to_string())
}
}
}

Expand Down
34 changes: 28 additions & 6 deletions src/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ export function getSocketDir(): string {
export function getSocketPath(session?: string): string {
const sess = session ?? currentSession;
if (isWindows) {
try {
const portFile = getPortFile(sess);
if (fs.existsSync(portFile)) {
return fs.readFileSync(portFile, 'utf8').trim();
}
} catch {
// Ignore errors
}
return String(getPortForSession(sess));
}
return path.join(getSocketDir(), `${sess}.sock`);
Expand Down Expand Up @@ -135,6 +143,17 @@ export function getConnectionInfo(
): { type: 'unix'; path: string } | { type: 'tcp'; port: number } {
const sess = session ?? currentSession;
if (isWindows) {
try {
const portFile = getPortFile(sess);
if (fs.existsSync(portFile)) {
const port = parseInt(fs.readFileSync(portFile, 'utf8').trim(), 10);
if (!isNaN(port)) {
return { type: 'tcp', port };
}
}
} catch {
// Ignore errors
}
return { type: 'tcp', port: getPortForSession(sess) };
}
return { type: 'unix', path: path.join(getSocketDir(), `${sess}.sock`) };
Expand Down Expand Up @@ -370,12 +389,15 @@ export async function startDaemon(options?: {
fs.writeFileSync(pidFile, process.pid.toString());

if (isWindows) {
// Windows: use TCP socket on localhost
const port = getPortForSession(currentSession);
const portFile = getPortFile();
fs.writeFileSync(portFile, port.toString());
server.listen(port, '127.0.0.1', () => {
// Daemon is ready on TCP port
// Windows: use TCP socket on localhost with random port
// We bind to 0 to let the OS assign a free port, avoiding collisions
server.listen(0, '127.0.0.1', () => {
const address = server.address();
if (address && typeof address === 'object') {
const port = address.port;
const portFile = getPortFile();
fs.writeFileSync(portFile, port.toString());
}
});
} else {
// Unix: use Unix domain socket
Expand Down