windows
This commit is contained in:
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
Instructions for AI coding agents working with this codebase.
|
Instructions for AI coding agents working with this codebase.
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
- Do not use emojis in code, output, or documentation. Unicode symbols (✓, ✗, →, ⚠) are acceptable.
|
||||||
|
|
||||||
<!-- opensrc:start -->
|
<!-- opensrc:start -->
|
||||||
|
|
||||||
## Source Code Reference
|
## Source Code Reference
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ license = "Apache-2.0"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|||||||
+155
-20
@@ -2,14 +2,19 @@ use serde::{Deserialize, Serialize};
|
|||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::{BufRead, BufReader, Write};
|
use std::io::{BufRead, BufReader, Read, Write};
|
||||||
use std::os::unix::net::UnixStream;
|
use std::net::TcpStream;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::{exit, Command, Stdio};
|
use std::process::{exit, Command, Stdio};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
// Unix socket support (Unix only)
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::net::UnixStream;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
#[allow(dead_code)]
|
||||||
struct Request {
|
struct Request {
|
||||||
id: String,
|
id: String,
|
||||||
action: String,
|
action: String,
|
||||||
@@ -24,6 +29,61 @@ struct Response {
|
|||||||
error: Option<String>,
|
error: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connection type abstraction
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum Connection {
|
||||||
|
#[cfg(unix)]
|
||||||
|
Unix(UnixStream),
|
||||||
|
Tcp(TcpStream),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Connection {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
match self {
|
||||||
|
#[cfg(unix)]
|
||||||
|
Connection::Unix(s) => s.read(buf),
|
||||||
|
Connection::Tcp(s) => s.read(buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Connection {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
match self {
|
||||||
|
#[cfg(unix)]
|
||||||
|
Connection::Unix(s) => s.write(buf),
|
||||||
|
Connection::Tcp(s) => s.write(buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
match self {
|
||||||
|
#[cfg(unix)]
|
||||||
|
Connection::Unix(s) => s.flush(),
|
||||||
|
Connection::Tcp(s) => s.flush(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Connection {
|
||||||
|
fn set_read_timeout(&self, dur: Option<Duration>) -> std::io::Result<()> {
|
||||||
|
match self {
|
||||||
|
#[cfg(unix)]
|
||||||
|
Connection::Unix(s) => s.set_read_timeout(dur),
|
||||||
|
Connection::Tcp(s) => s.set_read_timeout(dur),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_write_timeout(&self, dur: Option<Duration>) -> std::io::Result<()> {
|
||||||
|
match self {
|
||||||
|
#[cfg(unix)]
|
||||||
|
Connection::Unix(s) => s.set_write_timeout(dur),
|
||||||
|
Connection::Tcp(s) => s.set_write_timeout(dur),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
fn get_socket_path(session: &str) -> PathBuf {
|
fn get_socket_path(session: &str) -> PathBuf {
|
||||||
let tmp = env::temp_dir();
|
let tmp = env::temp_dir();
|
||||||
tmp.join(format!("agent-browser-{}.sock", session))
|
tmp.join(format!("agent-browser-{}.sock", session))
|
||||||
@@ -34,6 +94,25 @@ fn get_pid_path(session: &str) -> PathBuf {
|
|||||||
tmp.join(format!("agent-browser-{}.pid", session))
|
tmp.join(format!("agent-browser-{}.pid", session))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn get_port_path(session: &str) -> PathBuf {
|
||||||
|
let tmp = env::temp_dir();
|
||||||
|
tmp.join(format!("agent-browser-{}.port", session))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get port number for TCP mode (Windows)
|
||||||
|
/// Uses a hash of the session name to get a consistent port
|
||||||
|
#[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);
|
||||||
|
}
|
||||||
|
// Port range 49152-65535 (dynamic/private ports)
|
||||||
|
49152 + ((hash.abs() as u16) % 16383)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
fn is_daemon_running(session: &str) -> bool {
|
fn is_daemon_running(session: &str) -> bool {
|
||||||
let pid_path = get_pid_path(session);
|
let pid_path = get_pid_path(session);
|
||||||
if !pid_path.exists() {
|
if !pid_path.exists() {
|
||||||
@@ -49,10 +128,38 @@ fn is_daemon_running(session: &str) -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_daemon(session: &str, headed: bool) -> Result<(), String> {
|
#[cfg(windows)]
|
||||||
let socket_path = get_socket_path(session);
|
fn is_daemon_running(session: &str) -> bool {
|
||||||
|
let pid_path = get_pid_path(session);
|
||||||
|
if !pid_path.exists() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// On Windows, try to connect to the port to check if daemon is running
|
||||||
|
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 is_daemon_running(session) && socket_path.exists() {
|
fn daemon_ready(session: &str) -> bool {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
get_socket_path(session).exists()
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
// On Windows, try to connect to verify daemon is ready
|
||||||
|
let port = get_port_for_session(session);
|
||||||
|
TcpStream::connect_timeout(
|
||||||
|
&format!("127.0.0.1:{}", port).parse().unwrap(),
|
||||||
|
Duration::from_millis(50)
|
||||||
|
).is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_daemon(session: &str, headed: bool) -> Result<(), String> {
|
||||||
|
if is_daemon_running(session) && daemon_ready(session) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +193,7 @@ fn ensure_daemon(session: &str, headed: bool) -> Result<(), String> {
|
|||||||
.map_err(|e| format!("Failed to start daemon: {}", e))?;
|
.map_err(|e| format!("Failed to start daemon: {}", e))?;
|
||||||
|
|
||||||
for _ in 0..50 {
|
for _ in 0..50 {
|
||||||
if socket_path.exists() {
|
if daemon_ready(session) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
thread::sleep(Duration::from_millis(100));
|
thread::sleep(Duration::from_millis(100));
|
||||||
@@ -95,10 +202,25 @@ fn ensure_daemon(session: &str, headed: bool) -> Result<(), String> {
|
|||||||
Err("Daemon failed to start".to_string())
|
Err("Daemon failed to start".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn connect(session: &str) -> Result<Connection, String> {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
let socket_path = get_socket_path(session);
|
||||||
|
UnixStream::connect(&socket_path)
|
||||||
|
.map(Connection::Unix)
|
||||||
|
.map_err(|e| format!("Failed to connect: {}", e))
|
||||||
|
}
|
||||||
|
#[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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn send_command(cmd: Value, session: &str) -> Result<Response, String> {
|
fn send_command(cmd: Value, session: &str) -> Result<Response, String> {
|
||||||
let socket_path = get_socket_path(session);
|
let mut stream = connect(session)?;
|
||||||
let mut stream = UnixStream::connect(&socket_path)
|
|
||||||
.map_err(|e| format!("Failed to connect: {}", e))?;
|
|
||||||
|
|
||||||
stream.set_read_timeout(Some(Duration::from_secs(30))).ok();
|
stream.set_read_timeout(Some(Duration::from_secs(30))).ok();
|
||||||
stream.set_write_timeout(Some(Duration::from_secs(5))).ok();
|
stream.set_write_timeout(Some(Duration::from_secs(5))).ok();
|
||||||
@@ -166,7 +288,7 @@ fn clean_args(args: &[String]) -> Vec<String> {
|
|||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
let mut skip_next = false;
|
let mut skip_next = false;
|
||||||
|
|
||||||
for (i, arg) in args.iter().enumerate() {
|
for (_i, arg) in args.iter().enumerate() {
|
||||||
if skip_next {
|
if skip_next {
|
||||||
skip_next = false;
|
skip_next = false;
|
||||||
continue;
|
continue;
|
||||||
@@ -491,7 +613,7 @@ fn print_response(resp: &Response, json_mode: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !resp.success {
|
if !resp.success {
|
||||||
eprintln!("\x1b[31m✗ Error:\x1b[0m {}", resp.error.as_deref().unwrap_or("Unknown error"));
|
eprintln!("\x1b[31m✗\x1b[0m {}", resp.error.as_deref().unwrap_or("Unknown error"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -804,13 +926,26 @@ fn run_install(with_deps: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn which_exists(cmd: &str) -> bool {
|
fn which_exists(cmd: &str) -> bool {
|
||||||
Command::new("which")
|
#[cfg(unix)]
|
||||||
.arg(cmd)
|
{
|
||||||
.stdout(Stdio::null())
|
Command::new("which")
|
||||||
.stderr(Stdio::null())
|
.arg(cmd)
|
||||||
.status()
|
.stdout(Stdio::null())
|
||||||
.map(|s| s.success())
|
.stderr(Stdio::null())
|
||||||
.unwrap_or(false)
|
.status()
|
||||||
|
.map(|s| s.success())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
Command::new("where")
|
||||||
|
.arg(cmd)
|
||||||
|
.stdout(Stdio::null())
|
||||||
|
.stderr(Stdio::null())
|
||||||
|
.status()
|
||||||
|
.map(|s| s.success())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -843,7 +978,7 @@ fn main() {
|
|||||||
if flags.json {
|
if flags.json {
|
||||||
println!(r#"{{"success":false,"error":"{}"}}"#, e);
|
println!(r#"{{"success":false,"error":"{}"}}"#, e);
|
||||||
} else {
|
} else {
|
||||||
eprintln!("\x1b[31m✗ Error:\x1b[0m {}", e);
|
eprintln!("\x1b[31m✗\x1b[0m {}", e);
|
||||||
}
|
}
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -870,7 +1005,7 @@ fn main() {
|
|||||||
if flags.json {
|
if flags.json {
|
||||||
println!(r#"{{"success":false,"error":"{}"}}"#, e);
|
println!(r#"{{"success":false,"error":"{}"}}"#, e);
|
||||||
} else {
|
} else {
|
||||||
eprintln!("\x1b[31m✗ Error:\x1b[0m {}", e);
|
eprintln!("\x1b[31m✗\x1b[0m {}", e);
|
||||||
}
|
}
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,13 @@ RUN apt-get update && apt-get install -y \
|
|||||||
mingw-w64 \
|
mingw-w64 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Add Rust targets (Unix only - Windows uses Node.js fallback due to Unix socket dependency)
|
# Add Rust targets for all platforms
|
||||||
RUN rustup target add \
|
RUN rustup target add \
|
||||||
x86_64-unknown-linux-gnu \
|
x86_64-unknown-linux-gnu \
|
||||||
aarch64-unknown-linux-gnu \
|
aarch64-unknown-linux-gnu \
|
||||||
x86_64-apple-darwin \
|
x86_64-apple-darwin \
|
||||||
aarch64-apple-darwin
|
aarch64-apple-darwin \
|
||||||
|
x86_64-pc-windows-gnu
|
||||||
|
|
||||||
# Install cargo-zigbuild for easier cross-compilation (especially macOS)
|
# Install cargo-zigbuild for easier cross-compilation (especially macOS)
|
||||||
RUN curl -sSL https://ziglang.org/download/0.13.0/zig-linux-x86_64-0.13.0.tar.xz | tar -xJ -C /opt \
|
RUN curl -sSL https://ziglang.org/download/0.13.0/zig-linux-x86_64-0.13.0.tar.xz | tar -xJ -C /opt \
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Docker Compose for building agent-browser
|
# Docker Compose for building agent-browser
|
||||||
# Usage: docker compose -f docker/docker-compose.yml run build-linux
|
# Usage: docker compose -f docker/docker-compose.yml run build-linux
|
||||||
|
# docker compose -f docker/docker-compose.yml run build-windows
|
||||||
#
|
#
|
||||||
# Note: macOS builds should be done natively on macOS for compatibility.
|
# Note: macOS builds should be done natively on macOS for compatibility.
|
||||||
# Windows uses Node.js fallback (Unix sockets not supported).
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# Build for Linux platforms only (macOS should be built natively)
|
# Build for Linux platforms
|
||||||
build-linux:
|
build-linux:
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: ..
|
||||||
@@ -35,6 +35,27 @@ services:
|
|||||||
ls -la /output/agent-browser-linux-*
|
ls -la /output/agent-browser-linux-*
|
||||||
'
|
'
|
||||||
|
|
||||||
|
# Build for Windows
|
||||||
|
build-windows:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: docker/Dockerfile.build
|
||||||
|
volumes:
|
||||||
|
- ../cli:/build
|
||||||
|
- ../bin:/output
|
||||||
|
command: |
|
||||||
|
-c '
|
||||||
|
set -e
|
||||||
|
echo "Building for Windows x64..."
|
||||||
|
|
||||||
|
cargo build --release --target x86_64-pc-windows-gnu
|
||||||
|
cp /build/target/x86_64-pc-windows-gnu/release/agent-browser.exe /output/agent-browser-win32-x64.exe
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✓ Windows build completed!"
|
||||||
|
ls -la /output/agent-browser-win32-*
|
||||||
|
'
|
||||||
|
|
||||||
# Build for a single target (override with TARGET env var)
|
# Build for a single target (override with TARGET env var)
|
||||||
build-single:
|
build-single:
|
||||||
build:
|
build:
|
||||||
|
|||||||
+2
-1
@@ -17,7 +17,8 @@
|
|||||||
"build:native": "cargo build --release --manifest-path cli/Cargo.toml && node scripts/copy-native.js",
|
"build:native": "cargo build --release --manifest-path cli/Cargo.toml && node scripts/copy-native.js",
|
||||||
"build:linux": "docker compose -f docker/docker-compose.yml run --rm build-linux",
|
"build:linux": "docker compose -f docker/docker-compose.yml run --rm build-linux",
|
||||||
"build:macos": "cargo build --release --manifest-path cli/Cargo.toml --target aarch64-apple-darwin && cargo build --release --manifest-path cli/Cargo.toml --target x86_64-apple-darwin && cp cli/target/aarch64-apple-darwin/release/agent-browser bin/agent-browser-darwin-arm64 && cp cli/target/x86_64-apple-darwin/release/agent-browser bin/agent-browser-darwin-x64",
|
"build:macos": "cargo build --release --manifest-path cli/Cargo.toml --target aarch64-apple-darwin && cargo build --release --manifest-path cli/Cargo.toml --target x86_64-apple-darwin && cp cli/target/aarch64-apple-darwin/release/agent-browser bin/agent-browser-darwin-arm64 && cp cli/target/x86_64-apple-darwin/release/agent-browser bin/agent-browser-darwin-x64",
|
||||||
"build:all-platforms": "npm run build:linux && npm run build:macos",
|
"build:windows": "docker compose -f docker/docker-compose.yml run --rm build-windows",
|
||||||
|
"build:all-platforms": "npm run build:linux && npm run build:windows && npm run build:macos",
|
||||||
"build:docker": "docker build -t agent-browser-builder -f docker/Dockerfile.build .",
|
"build:docker": "docker build -t agent-browser-builder -f docker/Dockerfile.build .",
|
||||||
"start": "node dist/index.js",
|
"start": "node dist/index.js",
|
||||||
"dev": "tsx src/index.ts",
|
"dev": "tsx src/index.ts",
|
||||||
|
|||||||
+64
-8
@@ -6,6 +6,9 @@ import { BrowserManager } from './browser.js';
|
|||||||
import { parseCommand, serializeResponse, errorResponse } from './protocol.js';
|
import { parseCommand, serializeResponse, errorResponse } from './protocol.js';
|
||||||
import { executeCommand } from './actions.js';
|
import { executeCommand } from './actions.js';
|
||||||
|
|
||||||
|
// Platform detection
|
||||||
|
const isWindows = process.platform === 'win32';
|
||||||
|
|
||||||
// Session support - each session gets its own socket/pid
|
// Session support - each session gets its own socket/pid
|
||||||
let currentSession = process.env.AGENT_BROWSER_SESSION || 'default';
|
let currentSession = process.env.AGENT_BROWSER_SESSION || 'default';
|
||||||
|
|
||||||
@@ -24,13 +27,38 @@ export function getSession(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the socket path for the current session
|
* Get port number for TCP mode (Windows)
|
||||||
|
* Uses a hash of the session name to get a consistent port
|
||||||
|
*/
|
||||||
|
function getPortForSession(session: string): number {
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < session.length; i++) {
|
||||||
|
hash = ((hash << 5) - hash) + session.charCodeAt(i);
|
||||||
|
hash |= 0;
|
||||||
|
}
|
||||||
|
// Port range 49152-65535 (dynamic/private ports)
|
||||||
|
return 49152 + (Math.abs(hash) % 16383);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the socket path for the current session (Unix) or port (Windows)
|
||||||
*/
|
*/
|
||||||
export function getSocketPath(session?: string): string {
|
export function getSocketPath(session?: string): string {
|
||||||
const sess = session ?? currentSession;
|
const sess = session ?? currentSession;
|
||||||
|
if (isWindows) {
|
||||||
|
return String(getPortForSession(sess));
|
||||||
|
}
|
||||||
return path.join(os.tmpdir(), `agent-browser-${sess}.sock`);
|
return path.join(os.tmpdir(), `agent-browser-${sess}.sock`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the port file path for Windows (stores the port number)
|
||||||
|
*/
|
||||||
|
export function getPortFile(session?: string): string {
|
||||||
|
const sess = session ?? currentSession;
|
||||||
|
return path.join(os.tmpdir(), `agent-browser-${sess}.port`);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the PID file path for the current session
|
* Get the PID file path for the current session
|
||||||
*/
|
*/
|
||||||
@@ -48,7 +76,7 @@ export function isDaemonRunning(session?: string): boolean {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
|
const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
|
||||||
// Check if process exists
|
// Check if process exists (works on both Unix and Windows)
|
||||||
process.kill(pid, 0);
|
process.kill(pid, 0);
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
@@ -58,15 +86,32 @@ export function isDaemonRunning(session?: string): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get connection info for the current session
|
||||||
|
* Returns { type: 'unix', path: string } or { type: 'tcp', port: number }
|
||||||
|
*/
|
||||||
|
export function getConnectionInfo(session?: string): { type: 'unix'; path: string } | { type: 'tcp'; port: number } {
|
||||||
|
const sess = session ?? currentSession;
|
||||||
|
if (isWindows) {
|
||||||
|
return { type: 'tcp', port: getPortForSession(sess) };
|
||||||
|
}
|
||||||
|
return { type: 'unix', path: path.join(os.tmpdir(), `agent-browser-${sess}.sock`) };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean up socket and PID file for the current session
|
* Clean up socket and PID file for the current session
|
||||||
*/
|
*/
|
||||||
export function cleanupSocket(session?: string): void {
|
export function cleanupSocket(session?: string): void {
|
||||||
const socketPath = getSocketPath(session);
|
|
||||||
const pidFile = getPidFile(session);
|
const pidFile = getPidFile(session);
|
||||||
try {
|
try {
|
||||||
if (fs.existsSync(socketPath)) fs.unlinkSync(socketPath);
|
|
||||||
if (fs.existsSync(pidFile)) fs.unlinkSync(pidFile);
|
if (fs.existsSync(pidFile)) fs.unlinkSync(pidFile);
|
||||||
|
if (isWindows) {
|
||||||
|
const portFile = getPortFile(session);
|
||||||
|
if (fs.existsSync(portFile)) fs.unlinkSync(portFile);
|
||||||
|
} else {
|
||||||
|
const socketPath = getSocketPath(session);
|
||||||
|
if (fs.existsSync(socketPath)) fs.unlinkSync(socketPath);
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore cleanup errors
|
// Ignore cleanup errors
|
||||||
}
|
}
|
||||||
@@ -144,15 +189,26 @@ export async function startDaemon(): Promise<void> {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const socketPath = getSocketPath();
|
|
||||||
const pidFile = getPidFile();
|
const pidFile = getPidFile();
|
||||||
|
|
||||||
// Write PID file before listening
|
// Write PID file before listening
|
||||||
fs.writeFileSync(pidFile, process.pid.toString());
|
fs.writeFileSync(pidFile, process.pid.toString());
|
||||||
|
|
||||||
server.listen(socketPath, () => {
|
if (isWindows) {
|
||||||
// Daemon is ready
|
// 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
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Unix: use Unix domain socket
|
||||||
|
const socketPath = getSocketPath();
|
||||||
|
server.listen(socketPath, () => {
|
||||||
|
// Daemon is ready
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
server.on('error', (err) => {
|
server.on('error', (err) => {
|
||||||
console.error('Server error:', err);
|
console.error('Server error:', err);
|
||||||
|
|||||||
Reference in New Issue
Block a user