Rename setups and add provider reauthentication
This commit is contained in:
1 parent
e9a0f1b9da
commit
7d9df5d572
36 files changed
+1866
-684
No files matched your search
+117
-115
@@ -30,8 +30,8 @@ use serde::Serialize;
|
||||
use tokio::sync::{broadcast, mpsc};
|
||||
|
||||
use crate::config::{
|
||||
Config, DEFAULT_RESUME_MESSAGE, DriverKind, ProviderConfig, ScheduledResume, SessionConfig,
|
||||
SetupConfig, SshConfig, TokenEntry,
|
||||
Config, DEFAULT_RESUME_MESSAGE, DriverKind, MachineConfig, ProviderConfig, ScheduledResume,
|
||||
SessionConfig, SshConfig, TokenEntry,
|
||||
};
|
||||
use claude::ClaudeDriver;
|
||||
use codex::CodexDriver;
|
||||
@@ -100,7 +100,7 @@ pub fn now() -> f64 {
|
||||
}
|
||||
|
||||
pub struct SpawnSpec {
|
||||
pub setup: String,
|
||||
pub machine: String,
|
||||
pub provider: String,
|
||||
pub title: Option<String>,
|
||||
pub model: Option<String>,
|
||||
@@ -172,7 +172,7 @@ impl AutoResumeView {
|
||||
pub struct OwedResume {
|
||||
pub session_id: String,
|
||||
/// The machine whose account ran out, which is the one to ask.
|
||||
pub setup: String,
|
||||
pub machine: String,
|
||||
/// Which meter reports on it -- a `crate::usage::UsageProvider::name`, the
|
||||
/// same pairing `SessionInfo::usage_provider` uses.
|
||||
pub provider: &'static str,
|
||||
@@ -193,11 +193,11 @@ fn resume_message(meta: &SessionConfig) -> String {
|
||||
pub struct SessionInfo {
|
||||
pub id: String,
|
||||
pub provider: String,
|
||||
pub setup: String,
|
||||
pub machine: String,
|
||||
/// That machine's current label, resolved when this row is built, so
|
||||
/// renaming a setup renames it everywhere rather than leaving old
|
||||
/// renaming a machine renames it everywhere rather than leaving old
|
||||
/// sessions showing the old name.
|
||||
pub setup_name: String,
|
||||
pub machine_name: String,
|
||||
pub title: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub model: Option<String>,
|
||||
@@ -562,7 +562,7 @@ impl LiveSession {
|
||||
Ok((name, path))
|
||||
}
|
||||
|
||||
/// `setup_name` and `cwd` are passed in rather than read from the
|
||||
/// `machine_name` and `cwd` are passed in rather than read from the
|
||||
/// snapshot this session launched with: only the manager holds the
|
||||
/// config, and both can change under a running session. Passed rather
|
||||
/// than mirrored into `Shared`, so there is one answer, read where the
|
||||
@@ -574,7 +574,7 @@ impl LiveSession {
|
||||
/// cautious one.
|
||||
fn info(
|
||||
&self,
|
||||
setup_name: &str,
|
||||
machine_name: &str,
|
||||
cwd: Option<&Path>,
|
||||
effort: Option<&str>,
|
||||
imported: bool,
|
||||
@@ -584,8 +584,8 @@ impl LiveSession {
|
||||
SessionInfo {
|
||||
id: self.meta.id.clone(),
|
||||
provider: self.meta.provider.clone(),
|
||||
setup: self.meta.setup.clone(),
|
||||
setup_name: setup_name.to_string(),
|
||||
machine: self.meta.machine.clone(),
|
||||
machine_name: machine_name.to_string(),
|
||||
title: self.shared.title.lock().unwrap().clone(),
|
||||
model: self.shared.model.lock().unwrap().clone(),
|
||||
permission_mode: self.shared.permission_mode.lock().unwrap().clone(),
|
||||
@@ -646,7 +646,7 @@ pub struct SessionManager {
|
||||
/// The CLI-owned copy optionally removed with an ai-app session.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ForeignTranscript {
|
||||
pub setup: String,
|
||||
pub machine: String,
|
||||
pub id: String,
|
||||
kind: DriverKind,
|
||||
}
|
||||
@@ -702,10 +702,10 @@ impl SessionManager {
|
||||
// One unlaunchable session -- a corrupt transcript, an
|
||||
// unreachable host, a provider edited away -- shows as exited
|
||||
// rather than taking the server down, and can still be deleted.
|
||||
match resolve(&config, meta).and_then(|(setup, provider)| {
|
||||
match resolve(&config, meta).and_then(|(machine, provider)| {
|
||||
launch(
|
||||
meta.clone(),
|
||||
&setup,
|
||||
&machine,
|
||||
&provider,
|
||||
Env {
|
||||
data_dir: &data_dir,
|
||||
@@ -777,7 +777,7 @@ impl SessionManager {
|
||||
self
|
||||
}
|
||||
|
||||
/// Writes this machine into a config that has no setups, with the
|
||||
/// Writes this machine into a config that has no machines, with the
|
||||
/// providers actually found on it.
|
||||
///
|
||||
/// Discovered rather than assumed. This used to write a `claude-cli`
|
||||
@@ -789,16 +789,16 @@ impl SessionManager {
|
||||
/// this server runs, and says so in the log. Seeding the hardcoded list
|
||||
/// would be the original bug with an extra step, and seeding nothing
|
||||
/// leaves a fresh install with nothing to prove the pipe with.
|
||||
pub async fn seed_setup(&self) -> Result<()> {
|
||||
if !self.inner.read().unwrap().config.setups.is_empty() {
|
||||
pub async fn seed_machine(&self) -> Result<()> {
|
||||
if !self.inner.read().unwrap().config.machines.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let providers = match crate::setups::discover(&transport::Transport::Here).await {
|
||||
let providers = match crate::machines::discover(&transport::Transport::Here).await {
|
||||
Ok(found) => found,
|
||||
Err(err) => {
|
||||
tracing::warn!(
|
||||
"couldn't ask this machine what it has ({err}); seeding {} only -- \
|
||||
re-probe the setup from the app once that is fixed",
|
||||
re-probe the machine from the app once that is fixed",
|
||||
crate::config::ECHO_PROVIDER
|
||||
);
|
||||
vec![Config::echo_provider()]
|
||||
@@ -807,16 +807,16 @@ impl SessionManager {
|
||||
let names: Vec<&str> = providers.iter().map(|p| p.name.as_str()).collect();
|
||||
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
if !inner.config.setups.is_empty() {
|
||||
if !inner.config.machines.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut candidate = inner.config.clone();
|
||||
candidate.setups.push(Config::seed(providers.clone()));
|
||||
candidate.machines.push(Config::seed(providers.clone()));
|
||||
candidate.save(&self.config_path)?;
|
||||
inner.config = candidate;
|
||||
tracing::info!(
|
||||
"no setups configured -- added \"{}\" with: {}",
|
||||
crate::config::LOCAL_SETUP,
|
||||
"no machines configured -- added \"{}\" with: {}",
|
||||
crate::config::LOCAL_MACHINE,
|
||||
names.join(", ")
|
||||
);
|
||||
Ok(())
|
||||
@@ -839,80 +839,80 @@ impl SessionManager {
|
||||
///
|
||||
/// `providers` comes from probing rather than from the caller: the
|
||||
/// probe is async and this is not, so the route asks and this writes.
|
||||
pub fn add_setup(
|
||||
pub fn add_machine(
|
||||
&self,
|
||||
name: &str,
|
||||
ssh: Option<SshConfig>,
|
||||
providers: Vec<ProviderConfig>,
|
||||
) -> Result<SetupConfig> {
|
||||
) -> Result<MachineConfig> {
|
||||
let name = name.trim().to_string();
|
||||
if name.is_empty() {
|
||||
bail!("a setup needs a name");
|
||||
bail!("a machine needs a name");
|
||||
}
|
||||
self.update(|config| {
|
||||
if config.setup_named(&name).is_some() {
|
||||
bail!("there is already a setup called \"{name}\"");
|
||||
if config.machine_named(&name).is_some() {
|
||||
bail!("there is already a machine called \"{name}\"");
|
||||
}
|
||||
// Ids are derived once and then fixed, so a label can be
|
||||
// edited later without orphaning the sessions that named it.
|
||||
let mut id = crate::setups::id_from(&name);
|
||||
while config.setup(&id).is_some() {
|
||||
let mut id = crate::machines::id_from(&name);
|
||||
while config.machine(&id).is_some() {
|
||||
id = format!("{id}-{}", &random_hex()[..4]);
|
||||
}
|
||||
let setup = SetupConfig {
|
||||
let machine = MachineConfig {
|
||||
id,
|
||||
name: name.clone(),
|
||||
ssh,
|
||||
providers,
|
||||
};
|
||||
config.setups.push(setup.clone());
|
||||
Ok(setup)
|
||||
config.machines.push(machine.clone());
|
||||
Ok(machine)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_setup(
|
||||
pub fn update_machine(
|
||||
&self,
|
||||
id: &str,
|
||||
name: Option<&str>,
|
||||
providers: Option<Vec<ProviderConfig>>,
|
||||
) -> Result<SetupConfig> {
|
||||
) -> Result<MachineConfig> {
|
||||
self.update(|config| {
|
||||
if let Some(name) = name {
|
||||
let name = name.trim();
|
||||
if name.is_empty() {
|
||||
bail!("a setup needs a name");
|
||||
bail!("a machine needs a name");
|
||||
}
|
||||
if config.setups.iter().any(|s| s.name == name && s.id != id) {
|
||||
bail!("there is already a setup called \"{name}\"");
|
||||
if config.machines.iter().any(|s| s.name == name && s.id != id) {
|
||||
bail!("there is already a machine called \"{name}\"");
|
||||
}
|
||||
}
|
||||
let setup = config
|
||||
.setups
|
||||
let machine = config
|
||||
.machines
|
||||
.iter_mut()
|
||||
.find(|setup| setup.id == id)
|
||||
.with_context(|| format!("no setup with id \"{id}\""))?;
|
||||
.find(|machine| machine.id == id)
|
||||
.with_context(|| format!("no machine with id \"{id}\""))?;
|
||||
if let Some(name) = name {
|
||||
setup.name = name.trim().to_string();
|
||||
machine.name = name.trim().to_string();
|
||||
}
|
||||
if let Some(providers) = providers {
|
||||
setup.providers = providers;
|
||||
machine.providers = providers;
|
||||
}
|
||||
Ok(setup.clone())
|
||||
Ok(machine.clone())
|
||||
})
|
||||
}
|
||||
|
||||
/// Removes a machine, provided nothing is still running on it.
|
||||
/// Refused rather than cascaded: the person asking is better placed to
|
||||
/// decide which of those sessions they still want.
|
||||
pub fn delete_setup(&self, id: &str) -> Result<()> {
|
||||
pub fn delete_machine(&self, id: &str) -> Result<()> {
|
||||
self.update(|config| {
|
||||
if config.setup(id).is_none() {
|
||||
bail!("no setup with id \"{id}\"");
|
||||
if config.machine(id).is_none() {
|
||||
bail!("no machine with id \"{id}\"");
|
||||
}
|
||||
let using: Vec<&str> = config
|
||||
.sessions
|
||||
.iter()
|
||||
.filter(|session| session.setup == id)
|
||||
.filter(|session| session.machine == id)
|
||||
.map(|session| session.title.as_str())
|
||||
.collect();
|
||||
if !using.is_empty() {
|
||||
@@ -922,7 +922,7 @@ impl SessionManager {
|
||||
using.join(", "),
|
||||
);
|
||||
}
|
||||
config.setups.retain(|setup| setup.id != id);
|
||||
config.machines.retain(|machine| machine.id != id);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
@@ -1061,18 +1061,18 @@ impl SessionManager {
|
||||
pub fn remote_of(&self, id: &str) -> Option<(crate::config::SshConfig, Option<PathBuf>)> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
let meta = inner.config.sessions.iter().find(|meta| meta.id == id)?;
|
||||
let setup = inner
|
||||
let machine = inner
|
||||
.config
|
||||
.setups
|
||||
.machines
|
||||
.iter()
|
||||
.find(|setup| setup.id == meta.setup)?;
|
||||
Some((setup.ssh.clone()?, meta.cwd.clone()))
|
||||
.find(|machine| machine.id == meta.machine)?;
|
||||
Some((machine.ssh.clone()?, meta.cwd.clone()))
|
||||
}
|
||||
|
||||
pub fn foreign_transcript(&self, id: &str) -> Option<ForeignTranscript> {
|
||||
let inner = self.inner.read().unwrap();
|
||||
let meta = inner.config.sessions.iter().find(|meta| meta.id == id)?;
|
||||
let kind = kind_of(&inner.config, &meta.setup, &meta.provider)?;
|
||||
let kind = kind_of(&inner.config, &meta.machine, &meta.provider)?;
|
||||
let session_dir = self.data_dir.join(&meta.id);
|
||||
let foreign = match kind {
|
||||
DriverKind::ClaudeCli => {
|
||||
@@ -1085,7 +1085,7 @@ impl SessionManager {
|
||||
DriverKind::Echo | DriverKind::LlamaCpp => None,
|
||||
}?;
|
||||
Some(ForeignTranscript {
|
||||
setup: meta.setup.clone(),
|
||||
machine: meta.machine.clone(),
|
||||
id: foreign,
|
||||
kind,
|
||||
})
|
||||
@@ -1101,28 +1101,28 @@ impl SessionManager {
|
||||
.iter()
|
||||
.map(|meta| match inner.live.get(&meta.id) {
|
||||
Some(session) => session.info(
|
||||
label_of(&inner.config, &meta.setup),
|
||||
label_of(&inner.config, &meta.machine),
|
||||
meta.cwd.as_deref(),
|
||||
meta.effort.as_deref(),
|
||||
import::read_cursor(&self.data_dir.join(&meta.id)).is_some(),
|
||||
kind_of(&inner.config, &meta.setup, &meta.provider),
|
||||
kind_of(&inner.config, &meta.machine, &meta.provider),
|
||||
AutoResumeView::of(meta),
|
||||
),
|
||||
None => SessionInfo {
|
||||
id: meta.id.clone(),
|
||||
setup: meta.setup.clone(),
|
||||
setup_name: label_of(&inner.config, &meta.setup).to_string(),
|
||||
machine: meta.machine.clone(),
|
||||
machine_name: label_of(&inner.config, &meta.machine).to_string(),
|
||||
provider: meta.provider.clone(),
|
||||
title: meta.title.clone(),
|
||||
model: meta.model.clone(),
|
||||
permission_mode: meta.permission_mode.clone(),
|
||||
effort: meta.effort.clone(),
|
||||
takes_effort: kind_of(&inner.config, &meta.setup, &meta.provider)
|
||||
takes_effort: kind_of(&inner.config, &meta.machine, &meta.provider)
|
||||
.is_some_and(DriverKind::takes_effort),
|
||||
context_tokens: None,
|
||||
max_image_edge: kind_of(&inner.config, &meta.setup, &meta.provider)
|
||||
max_image_edge: kind_of(&inner.config, &meta.machine, &meta.provider)
|
||||
.and_then(DriverKind::max_image_edge),
|
||||
usage_provider: kind_of(&inner.config, &meta.setup, &meta.provider)
|
||||
usage_provider: kind_of(&inner.config, &meta.machine, &meta.provider)
|
||||
.and_then(DriverKind::usage_provider),
|
||||
notify: meta.notify,
|
||||
auto_resume: meta.auto_resume,
|
||||
@@ -1131,10 +1131,10 @@ impl SessionManager {
|
||||
imported: import::read_cursor(&self.data_dir.join(&meta.id)).is_some(),
|
||||
keeps_own_transcript: keeps_own_transcript(
|
||||
&inner.config,
|
||||
&meta.setup,
|
||||
&meta.machine,
|
||||
&meta.provider,
|
||||
),
|
||||
own_transcript_name: kind_of(&inner.config, &meta.setup, &meta.provider)
|
||||
own_transcript_name: kind_of(&inner.config, &meta.machine, &meta.provider)
|
||||
.and_then(DriverKind::own_transcript_name),
|
||||
cwd: meta.cwd.clone(),
|
||||
status: status_of_unlaunched(&self.data_dir.join(&meta.id)),
|
||||
@@ -1172,8 +1172,8 @@ impl SessionManager {
|
||||
|
||||
/// Every machine this server can run something on, each with what it
|
||||
/// can run. One list rather than two, because the pair is the choice.
|
||||
pub fn setups(&self) -> Vec<SetupConfig> {
|
||||
self.inner.read().unwrap().config.setups.clone()
|
||||
pub fn machines(&self) -> Vec<MachineConfig> {
|
||||
self.inner.read().unwrap().config.machines.clone()
|
||||
}
|
||||
|
||||
pub fn spawn_session(&self, spec: SpawnSpec) -> Result<SessionInfo> {
|
||||
@@ -1191,28 +1191,28 @@ impl SessionManager {
|
||||
|
||||
fn spawn_seeded(&self, spec: SpawnSpec, seed: Option<Seed>) -> Result<SessionInfo> {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
let setup = inner
|
||||
let machine = inner
|
||||
.config
|
||||
.setup(&spec.setup)
|
||||
.machine(&spec.machine)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"no setup with id \"{}\" -- configured: {}",
|
||||
spec.setup,
|
||||
"no machine with id \"{}\" -- configured: {}",
|
||||
spec.machine,
|
||||
// Ids, since that is what was looked up. Labels made
|
||||
// the failure read as a contradiction: "no setup named
|
||||
// the failure read as a contradiction: "no machine named
|
||||
// X -- configured: X".
|
||||
names(inner.config.setups.iter().map(|s| s.id.as_str())),
|
||||
names(inner.config.machines.iter().map(|s| s.id.as_str())),
|
||||
)
|
||||
})?
|
||||
.clone();
|
||||
let provider = setup
|
||||
let provider = machine
|
||||
.provider(&spec.provider)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"setup \"{}\" has no provider named \"{}\" -- it offers: {}",
|
||||
spec.setup,
|
||||
"machine \"{}\" has no provider named \"{}\" -- it offers: {}",
|
||||
spec.machine,
|
||||
spec.provider,
|
||||
names(setup.providers.iter().map(|p| p.name.as_str())),
|
||||
names(machine.providers.iter().map(|p| p.name.as_str())),
|
||||
)
|
||||
})?
|
||||
.clone();
|
||||
@@ -1223,7 +1223,7 @@ impl SessionManager {
|
||||
.unwrap_or_else(|| format!("{} session", provider.name));
|
||||
let meta = SessionConfig {
|
||||
id: id.clone(),
|
||||
setup: setup.id.clone(),
|
||||
machine: machine.id.clone(),
|
||||
provider: provider.name.clone(),
|
||||
title,
|
||||
// No model unless one was chosen. This used to fall back to the
|
||||
@@ -1267,7 +1267,7 @@ impl SessionManager {
|
||||
|
||||
let session = launch(
|
||||
meta.clone(),
|
||||
&setup,
|
||||
&machine,
|
||||
&provider,
|
||||
self.env(),
|
||||
self.announce.clone(),
|
||||
@@ -1286,7 +1286,7 @@ impl SessionManager {
|
||||
// Whether this one was seeded, the same question the listing asks
|
||||
// of the directory a moment later.
|
||||
let info = session.info(
|
||||
&setup.name,
|
||||
&machine.name,
|
||||
session.meta.cwd.as_deref(),
|
||||
session.meta.effort.as_deref(),
|
||||
import::read_cursor(&self.data_dir.join(&id)).is_some(),
|
||||
@@ -1442,8 +1442,8 @@ impl SessionManager {
|
||||
let scheduled = meta.resume?;
|
||||
Some(OwedResume {
|
||||
session_id: meta.id.clone(),
|
||||
setup: meta.setup.clone(),
|
||||
provider: kind_of(&inner.config, &meta.setup, &meta.provider)?
|
||||
machine: meta.machine.clone(),
|
||||
provider: kind_of(&inner.config, &meta.machine, &meta.provider)?
|
||||
.usage_provider()?,
|
||||
scheduled,
|
||||
})
|
||||
@@ -1628,7 +1628,7 @@ impl SessionManager {
|
||||
/// it entirely.
|
||||
///
|
||||
/// Whether the directory exists is the caller's question, because
|
||||
/// asking it is an ssh round trip on a remote setup; see the route.
|
||||
/// asking it is an ssh round trip on a remote machine; see the route.
|
||||
pub fn set_session_cwd(&self, id: &str, cwd: PathBuf) -> Result<()> {
|
||||
{
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
@@ -1889,7 +1889,7 @@ impl SessionManager {
|
||||
// Fresh from the config, like every other launch: a model or a
|
||||
// permission mode changed while the session was stopped is what it
|
||||
// starts with.
|
||||
let (setup, provider) = resolve(&inner.config, &meta)?;
|
||||
let (machine, provider) = resolve(&inner.config, &meta)?;
|
||||
match existing {
|
||||
Some(session) => {
|
||||
// Replacing the value a driver lives in does not end the
|
||||
@@ -1901,7 +1901,7 @@ impl SessionManager {
|
||||
}
|
||||
*session.driver.lock().unwrap() = Some(make_driver(
|
||||
&meta,
|
||||
&setup,
|
||||
&machine,
|
||||
&provider,
|
||||
self.env(),
|
||||
session.dir(),
|
||||
@@ -1915,7 +1915,7 @@ impl SessionManager {
|
||||
None => {
|
||||
let session = launch(
|
||||
meta,
|
||||
&setup,
|
||||
&machine,
|
||||
&provider,
|
||||
self.env(),
|
||||
self.announce.clone(),
|
||||
@@ -2050,24 +2050,26 @@ fn adoptable(session_dir: &Path) -> bool {
|
||||
/// The provider and host a session's config names, or a message saying
|
||||
/// which one is missing. Both are looked up fresh at every launch, so
|
||||
/// editing either takes effect on the next respawn.
|
||||
fn resolve(config: &Config, meta: &SessionConfig) -> Result<(SetupConfig, ProviderConfig)> {
|
||||
let setup = config
|
||||
.setup(&meta.setup)
|
||||
.with_context(|| format!("no setup named \"{}\"", meta.setup))?;
|
||||
let provider = setup.provider(&meta.provider).with_context(|| {
|
||||
fn resolve(config: &Config, meta: &SessionConfig) -> Result<(MachineConfig, ProviderConfig)> {
|
||||
let machine = config
|
||||
.machine(&meta.machine)
|
||||
.with_context(|| format!("no machine named \"{}\"", meta.machine))?;
|
||||
let provider = machine.provider(&meta.provider).with_context(|| {
|
||||
format!(
|
||||
"setup \"{}\" has no provider named \"{}\"",
|
||||
meta.setup, meta.provider
|
||||
"machine \"{}\" has no provider named \"{}\"",
|
||||
meta.machine, meta.provider
|
||||
)
|
||||
})?;
|
||||
Ok((setup.clone(), provider.clone()))
|
||||
Ok((machine.clone(), provider.clone()))
|
||||
}
|
||||
|
||||
/// A setup's current label, or its id when the setup has been deleted --
|
||||
/// A machine's current label, or its id when the machine has been deleted --
|
||||
/// which is what a session left behind by a removed machine shows, and is
|
||||
/// better than an empty column or a guess at what it used to be called.
|
||||
fn label_of<'a>(config: &'a Config, id: &'a str) -> &'a str {
|
||||
config.setup(id).map_or(id, |setup| setup.name.as_str())
|
||||
config
|
||||
.machine(id)
|
||||
.map_or(id, |machine| machine.name.as_str())
|
||||
}
|
||||
|
||||
/// The two ways a session directory can name a Claude Code conversation:
|
||||
@@ -2094,21 +2096,21 @@ fn foreign_ids(dir: &Path) -> (Option<String>, Option<String>) {
|
||||
/// app's delete cannot reach.
|
||||
///
|
||||
/// False when the provider can't be found, which is the safe way round: a
|
||||
/// setup or provider removed from the config leaves sessions naming one
|
||||
/// machine or provider removed from the config leaves sessions naming one
|
||||
/// that is gone, and the warning that then shows is the strong one. Saying
|
||||
/// "this can be brought back" on no evidence is the answer that loses
|
||||
/// somebody's conversation.
|
||||
fn keeps_own_transcript(config: &Config, setup: &str, provider: &str) -> bool {
|
||||
kind_of(config, setup, provider).is_some_and(DriverKind::keeps_own_transcript)
|
||||
fn keeps_own_transcript(config: &Config, machine: &str, provider: &str) -> bool {
|
||||
kind_of(config, machine, provider).is_some_and(DriverKind::keeps_own_transcript)
|
||||
}
|
||||
|
||||
/// What a session's provider is, for the questions answered by its *kind*
|
||||
/// rather than by its name. `None` for a provider that has been edited away,
|
||||
/// which is a session that cannot run at all.
|
||||
fn kind_of(config: &Config, setup: &str, provider: &str) -> Option<DriverKind> {
|
||||
fn kind_of(config: &Config, machine: &str, provider: &str) -> Option<DriverKind> {
|
||||
config
|
||||
.setup(setup)
|
||||
.and_then(|setup| setup.providers.iter().find(|it| it.name == provider))
|
||||
.machine(machine)
|
||||
.and_then(|machine| machine.providers.iter().find(|it| it.name == provider))
|
||||
.map(|provider| provider.kind)
|
||||
}
|
||||
|
||||
@@ -2310,7 +2312,7 @@ struct Env<'a> {
|
||||
/// process for it to speak to. See [`Launching`] for when that is.
|
||||
fn launch(
|
||||
meta: SessionConfig,
|
||||
setup: &SetupConfig,
|
||||
machine: &MachineConfig,
|
||||
provider: &ProviderConfig,
|
||||
env: Env<'_>,
|
||||
announce: Announcements,
|
||||
@@ -2405,7 +2407,7 @@ fn launch(
|
||||
&& shared.context_tokens.lock().unwrap().is_none()
|
||||
&& let Some(session_id) = claude::read_resume_token(&dir)
|
||||
{
|
||||
let transport = Transport::for_setup(setup);
|
||||
let transport = Transport::for_machine(machine);
|
||||
let shared = Arc::clone(&shared);
|
||||
tokio::spawn(async move {
|
||||
if let Some(context) = import::context_of(&transport, &session_id).await {
|
||||
@@ -2423,7 +2425,7 @@ fn launch(
|
||||
&& shared.context_tokens.lock().unwrap().is_none()
|
||||
&& let Some(thread_id) = codex::read_thread(&dir)
|
||||
{
|
||||
let transport = Transport::for_setup(setup);
|
||||
let transport = Transport::for_machine(machine);
|
||||
let shared = Arc::clone(&shared);
|
||||
tokio::spawn(async move {
|
||||
if let Some(context) = codex::context_of(&transport, &thread_id).await {
|
||||
@@ -2441,7 +2443,7 @@ fn launch(
|
||||
// anybody pressing anything.
|
||||
if let Some(cursor) = import::read_cursor(&dir) {
|
||||
spawn_import_sync(
|
||||
Transport::for_setup(setup),
|
||||
Transport::for_machine(machine),
|
||||
dir.clone(),
|
||||
cursor,
|
||||
sink.clone(),
|
||||
@@ -2454,7 +2456,7 @@ fn launch(
|
||||
.then(|| {
|
||||
make_driver(
|
||||
&meta,
|
||||
setup,
|
||||
machine,
|
||||
provider,
|
||||
env,
|
||||
&dir,
|
||||
@@ -2505,7 +2507,7 @@ fn launch(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn make_driver(
|
||||
meta: &SessionConfig,
|
||||
setup: &SetupConfig,
|
||||
machine: &MachineConfig,
|
||||
provider: &ProviderConfig,
|
||||
env: Env<'_>,
|
||||
dir: &Path,
|
||||
@@ -2525,7 +2527,7 @@ fn make_driver(
|
||||
DriverKind::LlamaCpp => Arc::new(LlamaDriver::launch(
|
||||
meta,
|
||||
provider,
|
||||
&Transport::for_setup(setup),
|
||||
&Transport::for_machine(machine),
|
||||
env.models_dir,
|
||||
transcript_path,
|
||||
dir,
|
||||
@@ -2535,7 +2537,7 @@ fn make_driver(
|
||||
DriverKind::ClaudeCli => Arc::new(ClaudeDriver::launch(
|
||||
meta,
|
||||
provider,
|
||||
&Transport::for_setup(setup),
|
||||
&Transport::for_machine(machine),
|
||||
dir,
|
||||
sink.clone(),
|
||||
Arc::clone(subagents),
|
||||
@@ -2543,7 +2545,7 @@ fn make_driver(
|
||||
DriverKind::CodexCli => Arc::new(CodexDriver::launch(
|
||||
meta,
|
||||
provider,
|
||||
Transport::for_setup(setup),
|
||||
Transport::for_machine(machine),
|
||||
dir,
|
||||
sink.clone(),
|
||||
)?),
|
||||
@@ -2799,7 +2801,7 @@ mod tests {
|
||||
fn echo_spec() -> SpawnSpec {
|
||||
SpawnSpec {
|
||||
params: Default::default(),
|
||||
setup: crate::config::LOCAL_SETUP_ID.to_string(),
|
||||
machine: crate::config::LOCAL_MACHINE_ID.to_string(),
|
||||
provider: crate::config::ECHO_PROVIDER.to_string(),
|
||||
title: None,
|
||||
model: None,
|
||||
@@ -2857,7 +2859,7 @@ mod tests {
|
||||
/// depending on whether `claude` happens to be installed.
|
||||
fn seed_echo_only(config_path: &std::path::Path) {
|
||||
Config {
|
||||
setups: vec![Config::seed(vec![Config::echo_provider()])],
|
||||
machines: vec![Config::seed(vec![Config::echo_provider()])],
|
||||
..Config::default()
|
||||
}
|
||||
.save(config_path)
|
||||
@@ -3356,7 +3358,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
manager.foreign_transcript(&info.id),
|
||||
Some(ForeignTranscript {
|
||||
setup: info.setup.clone(),
|
||||
machine: info.machine.clone(),
|
||||
id: "5ecf21da-d53f".to_string(),
|
||||
kind: DriverKind::ClaudeCli,
|
||||
})
|
||||
@@ -3979,7 +3981,7 @@ mod tests {
|
||||
std::fs::write(&command, "#!/bin/sh\ncat > /dev/null\n").expect("write stand-in");
|
||||
std::fs::set_permissions(&command, std::fs::Permissions::from_mode(0o755)).expect("chmod");
|
||||
Config {
|
||||
setups: vec![Config::seed(vec![
|
||||
machines: vec![Config::seed(vec![
|
||||
Config::echo_provider(),
|
||||
ProviderConfig {
|
||||
name: "stand-in".to_string(),
|
||||
|
||||
Reference in new issue
Block a user