Put the transport above the drivers instead of inside one
ClaudeDriver::spawn called ssh::command itself, so a translator whose job is a wire format also knew how sessions reach other machines, and every future driver would have had to remember the same. It now emits a `Launch` -- program, arguments, working directory -- and hands it to a `Transport` the manager chose from the session's host. This is the inversion Bryan asked for, and it pays for itself immediately in a place I had reported as a UI bug: "Run on" is offered for every provider but only the Claude driver honoured it, so choosing a host for an echo session silently ran it locally. With the transport above the driver that cannot be written -- EchoDriver builds no Launch, so there is nothing to wrap and nothing to misreport. The picker still needs to stop offering it, but the code no longer lies underneath. crate::ssh keeps the quoting, the forced options and the remote script, with its tests; transport.rs only decides which of the two it is. The two failure messages move with it, since they are transport-specific -- a missing ssh client here is a different thing to check than a program missing from a remote PATH. Noted in transport.rs rather than built, because nothing needs it yet: a remote llama-server is spawned as a process but spoken to over HTTP, so a transport eventually needs "reach this port" as well as "run this". Verified: cargo test (35), clippy, fmt. Nothing outside transport.rs mentions ssh now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xn8nHw1tw1R6PtiY1eEtw
This commit is contained in:
1 parent
ba6c770be3
commit
4cdcbd204a
3 files changed
+121
-25
No files matched your search
@@ -1,8 +1,11 @@
|
||||
//! The Claude Code driver: `claude -p` speaking stream-json on stdio,
|
||||
//! translated into the common event model.
|
||||
//!
|
||||
//! This half owns the process -- spawning it (locally or through `ssh`),
|
||||
//! resuming it after a crash, writing lines to it, and shutting it down.
|
||||
//! This half owns the process -- asking a transport to start it, resuming
|
||||
//! it after a crash, writing lines to it, and shutting it down. Where it
|
||||
//! runs is `session::transport`'s business, not this file's: this one
|
||||
//! emits a `Launch` and never learns whether it became a local child or an
|
||||
//! ssh invocation.
|
||||
//! Turning a line into [`Event`]s is [`translate`], which changes when the
|
||||
//! CLI's wire format does rather than when any of the above does.
|
||||
//!
|
||||
@@ -40,7 +43,8 @@ use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
||||
use super::driver::{Driver, Event, EventSink, ImageRef, SessionStatus};
|
||||
use crate::config::{HostConfig, ProviderConfig, SessionConfig};
|
||||
use super::transport::{Launch, Transport};
|
||||
use crate::config::{ProviderConfig, SessionConfig};
|
||||
use translate::{AnswerOutcome, Translator};
|
||||
|
||||
/// Where the driver remembers its CLI session id between backend runs --
|
||||
@@ -70,7 +74,7 @@ impl ClaudeDriver {
|
||||
pub fn spawn(
|
||||
meta: &SessionConfig,
|
||||
provider: &ProviderConfig,
|
||||
host: Option<&HostConfig>,
|
||||
transport: &Transport,
|
||||
session_dir: &Path,
|
||||
sink: EventSink,
|
||||
) -> Result<Self> {
|
||||
@@ -96,25 +100,13 @@ impl ClaudeDriver {
|
||||
args.push("--include-partial-messages".to_string());
|
||||
|
||||
let program = provider.command.as_deref().unwrap_or("claude");
|
||||
let cwd = meta.cwd.as_deref();
|
||||
let where_it_runs = match host {
|
||||
Some(host) => format!("on {} ({})", host.name, host.address),
|
||||
None => "on this machine".to_string(),
|
||||
};
|
||||
let mut child = crate::ssh::command(host, program, &args, cwd)
|
||||
.spawn()
|
||||
.with_context(|| match host {
|
||||
Some(host) => format!(
|
||||
"couldn't start ssh to run \"{program}\" on {} -- is the ssh client \
|
||||
installed here?",
|
||||
host.name
|
||||
),
|
||||
None => format!(
|
||||
"couldn't run \"{program}\" on this machine -- is it installed and on \
|
||||
PATH? If it lives on another machine, give the session a host to run on.",
|
||||
),
|
||||
})?;
|
||||
tracing::info!("session {} running {program} {where_it_runs}", meta.id);
|
||||
let launch = Launch::new(program, args, meta.cwd.as_deref());
|
||||
let mut child = transport.spawn(&launch)?;
|
||||
tracing::info!(
|
||||
"session {} running {program} {}",
|
||||
meta.id,
|
||||
transport.describe()
|
||||
);
|
||||
|
||||
let stdin = child.stdin.take().expect("piped stdin");
|
||||
let stdout = child.stdout.take().expect("piped stdout");
|
||||
@@ -168,7 +160,7 @@ impl ClaudeDriver {
|
||||
let (kill_tx, kill_rx) = oneshot::channel::<()>();
|
||||
{
|
||||
let sink = sink.clone();
|
||||
let label = format!("{} {where_it_runs}", provider.name);
|
||||
let label = format!("{} {}", provider.name, transport.describe());
|
||||
tokio::spawn(async move {
|
||||
let status = tokio::select! {
|
||||
status = child.wait() => status.ok(),
|
||||
|
||||
Reference in new issue
Block a user