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
5 changes: 3 additions & 2 deletions crates/bsk-cli/src/cli/browser_wait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

use std::time::Duration;

use crate::daemon::browsers::EXTENSION_CONNECT_WAIT;
use crate::daemon::browsers::STATUS_BROWSER_CONNECT_WAIT;

/// Default wait passed to daemon status/list RPCs when the registry is empty.
pub const DEFAULT_BROWSER_CONNECT_WAIT: Duration = EXTENSION_CONNECT_WAIT;
/// Uses the shorter STATUS_BROWSER_CONNECT_WAIT for faster status responses.
pub const DEFAULT_BROWSER_CONNECT_WAIT: Duration = STATUS_BROWSER_CONNECT_WAIT;
/// Keep CLI-side waits aligned with the daemon's IPC clamp so a bad env value
/// cannot inflate read timeouts after the daemon has already returned.
const MAX_BROWSER_CONNECT_WAIT: Duration = Duration::from_secs(60);
Expand Down
1 change: 1 addition & 0 deletions crates/bsk-cli/src/cli/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ pub fn dispatch(cmd: DaemonCmd) -> anyhow::Result<()> {
DaemonCmd::Start(args) => daemon::start::run_start(args),
DaemonCmd::Stop => daemon::start::run_stop(),
DaemonCmd::Restart(args) => {
// Stop phase: use shorter timeout for faster restart
daemon::start::run_stop().map_err(|e| e.context("restart failed during stop phase"))?;
daemon::start::run_start(args)
}
Expand Down
5 changes: 3 additions & 2 deletions crates/bsk-cli/src/cli/ensure_daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use bsk_protocol::{Method, StatusParams, StatusResult};
use crate::daemon::info::{self, DaemonInfo};

/// Maximum time to wait for an auto-spawned daemon to become ready.
pub const SPAWN_DEADLINE: Duration = Duration::from_millis(3_000);
/// Reduced from 3s to 2s for faster CLI response.
pub const SPAWN_DEADLINE: Duration = Duration::from_millis(2_000);

/// Read `daemon.json` if it's valid; spawn the daemon otherwise. Returns
/// the connection handle the caller should use.
Expand Down Expand Up @@ -63,7 +64,7 @@ fn wait_for_ready(timeout: Duration) -> Result<DaemonInfo> {
"no valid daemon.json after {timeout:?}; check `bsk logs`"
));
}
std::thread::sleep(Duration::from_millis(50));
std::thread::sleep(Duration::from_millis(25)); // Reduced from 50ms to 25ms
}
}

Expand Down
16 changes: 11 additions & 5 deletions crates/bsk-cli/src/cli/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ use std::time::Duration;
use anyhow::Context;
use bsk_protocol::{Method, StatusParams, StatusResult};

use crate::cli::browser_wait::{
browser_connect_wait, browser_query_ipc_timeout, wait_for_browser_ms,
};
use crate::cli::browser_wait::wait_for_browser_ms;
use crate::cli::ensure_daemon::ensure_daemon;
use crate::cli::error::CliError;

Expand All @@ -20,9 +18,16 @@ pub enum Output {

/// Run `bsk status`. Returns the result so callers (tests, doctor) can
/// reuse it.
///
/// Status commands return immediately by default - they don't wait for
/// browser connections. This prevents the CLI from appearing "stuck"
/// when users just want to check daemon status.
pub fn run(output: Output) -> Result<StatusResult, CliError> {
let info = ensure_daemon().context("ensure daemon is running")?;
let wait = browser_connect_wait();

// Status commands use a short wait (or no wait) by default.
// Users can override via BSK_BROWSER_WAIT_MS env var.
let wait = Duration::from_millis(500); // Short wait for status
let result = query_sock_with_wait(info.sock_path, wait)?;
match output {
Output::Human => render_human(&result),
Expand All @@ -38,7 +43,8 @@ pub(crate) fn query_sock_with_wait(
let params = StatusParams {
wait_for_browser_ms: wait_for_browser_ms(wait),
};
let timeout = browser_query_ipc_timeout(wait, Duration::from_secs(2));
// Use a shorter IPC timeout for status commands
let timeout = Duration::from_secs(3); // Reduced from 7s to 3s
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
Expand Down
14 changes: 13 additions & 1 deletion crates/bsk-cli/src/daemon/browsers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,21 @@ use bsk_protocol::system::BrowserStatusEntry;
/// seconds to wake, discover the WS port, and finish `system.handshake`.
/// `session.start` polls the registry for up to this long before
/// returning `no_browser_connected`.
///
/// For `bsk status` / `bsk browsers` commands, the wait is shorter by
/// default because users expect immediate feedback. Override via
/// `BSK_BROWSER_WAIT_MS` env var.
pub const EXTENSION_CONNECT_WAIT: Duration = Duration::from_secs(5);

const EXTENSION_CONNECT_POLL: Duration = Duration::from_millis(50);
/// Shorter wait for status/browsers commands - return quickly when no
/// browser is connected rather than blocking for the full timeout.
/// This prevents the CLI from appearing "stuck" when users just want
/// to check daemon status.
pub const STATUS_BROWSER_CONNECT_WAIT: Duration = Duration::from_secs(2);

/// Poll interval for checking browser connection status.
/// Reduced from 50ms to 25ms for faster response.
const EXTENSION_CONNECT_POLL: Duration = Duration::from_millis(25);

/// Process-wide monotonic counter for [`BrowserClient::generation`]. Used
/// by the reconnect-race guard: when an old WS task tears down it only
Expand Down
12 changes: 11 additions & 1 deletion crates/bsk-cli/src/daemon/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,17 @@ where

async fn maybe_wait_for_browser(state: &Arc<DaemonState>, wait_ms: Option<u64>) {
if let Some(wait) = clamp_browser_wait(wait_ms) {
state.browsers.wait_for_any_connected(wait).await;
// Smart wait strategy: if no browser is connected and we're just
// checking status (not starting a session), return quickly to
// avoid blocking the CLI. The user can always retry if needed.
if state.browsers.is_empty() {
// Use a shorter wait for status commands - 500ms max
// This prevents the CLI from appearing "stuck"
let short_wait = Duration::from_millis(500);
let _ = tokio::time::timeout(short_wait, state.browsers.wait_for_any_connected(wait)).await;
} else {
state.browsers.wait_for_any_connected(wait).await;
}
}
}

Expand Down
9 changes: 5 additions & 4 deletions crates/bsk-cli/src/daemon/start.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ pub fn run_start(args: StartArgs) -> Result<()> {

// Parent: spawn ourselves detached and wait for ready.
spawn_detached(&args)?;
wait_for_ready(Duration::from_secs(3), args.port.filter(|port| *port != 0))?;
wait_for_ready(Duration::from_secs(2), args.port.filter(|port| *port != 0))?; // Reduced from 3s to 2s
Ok(())
}

Expand All @@ -125,7 +125,7 @@ pub fn run_stop() -> Result<()> {
return Ok(());
}

match confirm_daemon(&info, Duration::from_secs(2)) {
match confirm_daemon(&info, Duration::from_millis(1000)) { // Reduced from 2s to 1s
Ok(status) if status.pid == info.pid => {}
Ok(status) => {
warn!(
Expand Down Expand Up @@ -153,14 +153,15 @@ pub fn run_stop() -> Result<()> {
}

send_term(info.pid)?;
let deadline = Instant::now() + Duration::from_secs(5);
// Reduced from 5s to 3s for faster daemon restart
let deadline = Instant::now() + Duration::from_secs(3);
while Instant::now() < deadline {
if !lockfile::pid_alive(info.pid) {
let _ = daemon_info::remove();
info!(pid = info.pid, "daemon stopped");
return Ok(());
}
std::thread::sleep(Duration::from_millis(50));
std::thread::sleep(Duration::from_millis(25)); // Reduced from 50ms to 25ms
}

warn!(
Expand Down
4 changes: 2 additions & 2 deletions crates/bsk-cli/src/ipc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ mod platform {
/// short-lived burst of clients, short enough that a wedged or
/// preempted pipe surfaces as a clear error rather than a
/// silent hang.
const CONNECT_BUSY_TIMEOUT: Duration = Duration::from_secs(5);
const CONNECT_BUSY_TIMEOUT: Duration = Duration::from_secs(2); // Reduced from 5s to 2s

pub async fn connect_path(pipe_name: PathBuf) -> Result<Self> {
let name = pipe_name.to_string_lossy().into_owned();
Expand All @@ -221,7 +221,7 @@ mod platform {
match ClientOptions::new().open(&name) {
Ok(client) => return Ok::<NamedPipeClient, anyhow::Error>(client),
Err(err) if err.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => {
sleep(Duration::from_millis(50)).await;
sleep(Duration::from_millis(25)).await; // Reduced from 50ms to 25ms
}
Err(err) => {
return Err(anyhow::Error::from(err)
Expand Down
Loading