Skip to content

Commit 751f48d

Browse files
committed
Refactor app initialization path to make sure remote server is initialized with the right setup
1 parent 0ca787c commit 751f48d

4 files changed

Lines changed: 142 additions & 60 deletions

File tree

app/src/ai/execution_profiles/profiles.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,13 @@ impl AIExecutionProfilesModel {
181181
id: ClientProfileId::new()
182182
}
183183
}
184+
// Proxy and Daemon don't use AI execution profiles.
185+
// They never reach this code path since they don't go
186+
// through initialize_app, but handle exhaustively.
187+
LaunchMode::Proxy | LaunchMode::Daemon => DefaultProfileState::Unsynced {
188+
id: ClientProfileId::new(),
189+
profile: AIExecutionProfile::create_default_from_legacy_settings(ctx),
190+
},
184191
};
185192
}
186193
}

app/src/lib.rs

Lines changed: 133 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,9 @@ fn determine_agent_source(
330330
LaunchMode::App { .. } | LaunchMode::Test { .. } => {
331331
Some(crate::ai::ambient_agents::AgentSource::CloudMode)
332332
}
333+
// Proxy and Daemon are headless server processes that don't use the
334+
// agent subsystem.
335+
LaunchMode::Proxy | LaunchMode::Daemon => None,
333336
}
334337
}
335338

@@ -359,13 +362,24 @@ pub enum LaunchMode {
359362
driver: Box<Option<TestDriver>>,
360363
is_integration_test: bool,
361364
},
365+
366+
/// Remote server proxy — bridges SSH stdio to the daemon's Unix socket.
367+
/// This is a short-lived process that runs for the lifetime of an SSH session.
368+
Proxy,
369+
370+
/// Remote server daemon — long-lived headless process serving remote
371+
/// connections via a Unix domain socket.
372+
Daemon,
362373
}
363374

364375
impl LaunchMode {
365376
fn args(&self) -> Cow<'_, warp_cli::AppArgs> {
366377
match self {
367378
LaunchMode::App { args, .. } => Cow::Borrowed(args),
368-
_ => Cow::Owned(warp_cli::AppArgs::default()),
379+
LaunchMode::CommandLine { .. }
380+
| LaunchMode::Test { .. }
381+
| LaunchMode::Proxy
382+
| LaunchMode::Daemon => Cow::Owned(warp_cli::AppArgs::default()),
369383
}
370384
}
371385

@@ -376,14 +390,20 @@ impl LaunchMode {
376390
is_integration_test,
377391
..
378392
} => *is_integration_test,
379-
_ => false,
393+
LaunchMode::App { .. }
394+
| LaunchMode::CommandLine { .. }
395+
| LaunchMode::Proxy
396+
| LaunchMode::Daemon => false,
380397
}
381398
}
382399

383400
fn take_test_driver(&mut self) -> Option<TestDriver> {
384401
match self {
385402
LaunchMode::Test { driver, .. } => driver.take(),
386-
_ => None,
403+
LaunchMode::App { .. }
404+
| LaunchMode::CommandLine { .. }
405+
| LaunchMode::Proxy
406+
| LaunchMode::Daemon => None,
387407
}
388408
}
389409

@@ -400,13 +420,19 @@ impl LaunchMode {
400420
LaunchMode::App { .. } => ExecutionMode::App,
401421
LaunchMode::CommandLine { .. } => ExecutionMode::Sdk,
402422
LaunchMode::Test { .. } => ExecutionMode::App,
423+
// Proxy and Daemon don't use execution mode, but Sdk is the
424+
// closest match (headless, no GUI).
425+
LaunchMode::Proxy | LaunchMode::Daemon => ExecutionMode::Sdk,
403426
}
404427
}
405428

406429
fn is_sandboxed(&self) -> bool {
407430
match self {
408431
LaunchMode::CommandLine { is_sandboxed, .. } => *is_sandboxed,
409-
_ => false,
432+
LaunchMode::App { .. }
433+
| LaunchMode::Test { .. }
434+
| LaunchMode::Proxy
435+
| LaunchMode::Daemon => false,
410436
}
411437
}
412438

@@ -417,7 +443,8 @@ impl LaunchMode {
417443
CliCommand::Agent(AgentCommand::Run(args)) => !args.gui,
418444
_ => true,
419445
},
420-
_ => false,
446+
LaunchMode::Proxy | LaunchMode::Daemon => true,
447+
LaunchMode::App { .. } | LaunchMode::Test { .. } => false,
421448
}
422449
}
423450

@@ -427,7 +454,8 @@ impl LaunchMode {
427454
LaunchMode::CommandLine { command, .. } => {
428455
matches!(command, CliCommand::Agent(AgentCommand::Run { .. }))
429456
}
430-
_ => true,
457+
LaunchMode::App { .. } | LaunchMode::Test { .. } => true,
458+
LaunchMode::Proxy | LaunchMode::Daemon => false,
431459
}
432460
}
433461

@@ -436,8 +464,59 @@ impl LaunchMode {
436464
pub(crate) fn crash_recovery_enabled(&self) -> bool {
437465
match self {
438466
LaunchMode::App { .. } => true,
439-
LaunchMode::CommandLine { .. } => false,
440-
LaunchMode::Test { .. } => false,
467+
LaunchMode::CommandLine { .. }
468+
| LaunchMode::Test { .. }
469+
| LaunchMode::Proxy
470+
| LaunchMode::Daemon => false,
471+
}
472+
}
473+
474+
/// Whether Sentry / crash reporting should be initialized in `init_common`.
475+
fn needs_crash_reporting(&self) -> bool {
476+
match self {
477+
LaunchMode::App { .. } => true,
478+
LaunchMode::CommandLine { .. } => true,
479+
LaunchMode::Test { .. } => true,
480+
LaunchMode::Daemon => true,
481+
LaunchMode::Proxy => false,
482+
}
483+
}
484+
485+
/// Whether profiling and tracing should be initialized in `init_common`.
486+
fn needs_profiling(&self) -> bool {
487+
match self {
488+
LaunchMode::App { .. } => true,
489+
LaunchMode::CommandLine { .. } => true,
490+
LaunchMode::Test { .. } => true,
491+
LaunchMode::Daemon => true,
492+
LaunchMode::Proxy => false,
493+
}
494+
}
495+
496+
/// Whether this is a CLI-like mode (logs to file/stderr, not stdout).
497+
fn is_cli_like(&self) -> bool {
498+
match self {
499+
LaunchMode::App { .. } | LaunchMode::Test { .. } => false,
500+
LaunchMode::CommandLine { .. }
501+
| LaunchMode::Proxy
502+
| LaunchMode::Daemon => true,
503+
}
504+
}
505+
506+
/// Log destination for this mode.
507+
fn log_destination(&self) -> Option<LogDestination> {
508+
match self {
509+
LaunchMode::CommandLine { debug, .. } => {
510+
if *debug {
511+
Some(LogDestination::Stderr)
512+
} else {
513+
Some(LogDestination::File)
514+
}
515+
}
516+
// Proxy must log to stderr because stdout is the protocol channel.
517+
LaunchMode::Proxy => Some(LogDestination::Stderr),
518+
LaunchMode::Daemon => Some(LogDestination::File),
519+
LaunchMode::App { .. } | LaunchMode::Test { .. } => None,
441520
}
442521
}
443522

@@ -560,10 +639,12 @@ pub fn run() -> Result<()> {
560639
}
561640
#[cfg(not(target_family = "wasm"))]
562641
warp_cli::Command::Worker(warp_cli::WorkerCommand::RemoteServerProxy) => {
642+
init_common(&LaunchMode::Proxy, None)?;
563643
return crate::remote_server::run_proxy();
564644
}
565645
#[cfg(not(target_family = "wasm"))]
566646
warp_cli::Command::Worker(warp_cli::WorkerCommand::RemoteServerDaemon) => {
647+
init_common(&LaunchMode::Daemon, None)?;
567648
return crate::remote_server::run_daemon();
568649
}
569650
#[cfg(not(target_family = "wasm"))]
@@ -657,43 +738,41 @@ pub fn run_integration_test(driver: TestDriver) -> Result<()> {
657738
run_internal(launch)
658739
}
659740

660-
/// Runs the app.
661-
fn run_internal(mut launch_mode: LaunchMode) -> Result<()> {
741+
/// Shared early initialization for **every** process type (app, CLI, proxy,
742+
/// daemon). Every step in this function runs for all modes, including
743+
/// lightweight ones like Proxy. Think carefully before adding here — if
744+
/// the step is only needed by the full app, add it to `run_internal`
745+
/// instead.
746+
fn init_common(
747+
launch_mode: &LaunchMode,
748+
timer: Option<&mut IntervalTimer>,
749+
) -> Result<()> {
662750
#[cfg(windows)]
663751
dynamic_libraries::configure_library_loading();
664752

665-
profiling::init();
753+
if launch_mode.needs_profiling() {
754+
profiling::init();
755+
}
666756

667757
// The `run` function already initializes feature flags, but ensure they're initialized here
668758
// for other entrypoints.
669759
init_feature_flags();
670760

671-
let mut timer = IntervalTimer::new();
672-
673761
#[cfg(feature = "crash_reporting")]
674-
{
762+
if launch_mode.needs_crash_reporting() {
675763
// Ensure that the main/root Sentry hub is initialized on the main
676764
// thread. PtySpawner creates a background thread to receive logs from
677765
// the terminal server process, and we don't want it to be the host of
678766
// the primary sentry::Hub.
679767
sentry::Hub::main();
680768
}
681769

682-
tracing::init()?;
683-
684-
let is_cli = matches!(launch_mode, LaunchMode::CommandLine { .. });
770+
if launch_mode.needs_profiling() {
771+
tracing::init()?;
772+
}
685773

686-
// Log to a file for CLI commands to not obscure the command output.
687-
let log_destination = match &launch_mode {
688-
LaunchMode::CommandLine { debug, .. } => {
689-
if *debug {
690-
Some(LogDestination::Stderr)
691-
} else {
692-
Some(LogDestination::File)
693-
}
694-
}
695-
_ => None,
696-
};
774+
let is_cli = launch_mode.is_cli_like();
775+
let log_destination = launch_mode.log_destination();
697776

698777
cfg_if::cfg_if! {
699778
if #[cfg(enable_crash_recovery)] {
@@ -707,13 +786,32 @@ fn run_internal(mut launch_mode: LaunchMode) -> Result<()> {
707786
}
708787
}
709788

710-
timer.mark_interval_end("LOG_FILE_SETUP_COMPLETE");
789+
if let Some(timer) = timer {
790+
timer.mark_interval_end("LOG_FILE_SETUP_COMPLETE");
791+
}
711792

712793
// Adjust resource limits early, before doing other work, to ensure that
713794
// any children we spawn (like the terminal server) inherit our adjusted
714795
// rlimits.
715796
resource_limits::adjust_resource_limits();
716797

798+
// Configure rustls to use its default crypto provider. This MUST be called
799+
// before making any network requests that use TLS, otherwise rustls will
800+
// panic.
801+
#[cfg(not(target_family = "wasm"))]
802+
rustls::crypto::aws_lc_rs::default_provider()
803+
.install_default()
804+
.expect("must be able to initialize crypto provider for TLS support");
805+
806+
Ok(())
807+
}
808+
809+
/// Runs the app.
810+
fn run_internal(mut launch_mode: LaunchMode) -> Result<()> {
811+
let mut timer = IntervalTimer::new();
812+
813+
init_common(&launch_mode, Some(&mut timer))?;
814+
717815
// For wasm builds we have this special case to parse out the intent
718816
// from the url that is used to visite the app on web.
719817
#[cfg(target_family = "wasm")]
@@ -785,14 +883,6 @@ fn run_internal(mut launch_mode: LaunchMode) -> Result<()> {
785883
#[cfg(windows)]
786884
command::windows::init();
787885

788-
// Configure rustls to use its default crypto provider. This MUST be called
789-
// before making any network requests that use TLS, otherwise rustls will
790-
// panic.
791-
#[cfg(not(target_family = "wasm"))]
792-
rustls::crypto::aws_lc_rs::default_provider()
793-
.install_default()
794-
.expect("must be able to initialize crypto provider for TLS support");
795-
796886
let private_preferences = settings::init_private_user_preferences();
797887
let (public_preferences, startup_toml_parse_error) = settings::init_public_user_preferences();
798888

@@ -2280,6 +2370,12 @@ fn launch(ctx: &mut warpui::AppContext, app_state: Option<AppState>, launch_mode
22802370
}
22812371
}
22822372
}
2373+
// Proxy and Daemon never go through run_internal / launch; they call
2374+
// init_common directly and then their own entry points.
2375+
LaunchMode::Proxy | LaunchMode::Daemon => {
2376+
log::error!("Proxy/Daemon modes should not use the launch() path");
2377+
std::process::exit(1);
2378+
}
22832379
}
22842380
}
22852381

app/src/remote_server/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub mod unix;
1212
/// Run the `remote-server-proxy` subcommand.
1313
#[cfg(unix)]
1414
pub fn run_proxy() -> anyhow::Result<()> {
15-
unix::run_proxy()
15+
unix::proxy::run()
1616
}
1717

1818
#[cfg(not(unix))]

app/src/remote_server/unix/mod.rs

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,17 @@
1111
//! All platform-specific code is contained here so that the parent `mod.rs`
1212
//! is a thin dispatcher with no Unix assumptions.
1313
14-
mod proxy;
14+
pub(super) mod proxy;
1515

1616
use super::server_model::{ConnectionId, ServerModel};
1717
use warpui::r#async::executor;
1818

19-
/// Run the `remote-server-proxy` subcommand.
20-
///
21-
/// Ensures the daemon is running (starting it if necessary), then bridges
22-
/// this process's stdin/stdout to the daemon's Unix socket for the lifetime
23-
/// of the SSH session.
24-
pub fn run_proxy() -> anyhow::Result<()> {
25-
env_logger::Builder::from_default_env()
26-
.target(env_logger::Target::Stderr)
27-
.init();
28-
proxy::run()
29-
}
30-
3119
/// Run the `remote-server-daemon` subcommand.
3220
///
3321
/// Binds a Unix domain socket and writes a PID file, then delegates the
3422
/// WarpUI app startup to [`super::run_daemon_app`] with the Unix-specific
3523
/// `ServerModel` constructor.
3624
pub fn run_daemon() -> anyhow::Result<()> {
37-
// Log to a rotating file so daemon output is preserved across invocations.
38-
// The file is written to the same directory as client logs (~/Library/Logs
39-
// on macOS, ~/.local/share/warp-terminal on Linux). Since the daemon runs
40-
// on the remote host, there is no conflict with client-side log files.
41-
warp_logging::init(warp_logging::LogConfig {
42-
is_cli: true,
43-
log_destination: Some(warp_logging::LogDestination::File),
44-
})?;
45-
4625
// socket_path: ~/.warp[-channel]/remote-server/server.sock
4726
// The Unix domain socket the daemon binds on. Proxy processes connect
4827
// to it and bridge their SSH stdio channel through it.

0 commit comments

Comments
 (0)