Drop the in-repo state fallback
Nothing has run against the host's backend yet, so there is no old config or transcript to keep working -- the XDG paths are simply where state lives. Removing the fallback takes repo_root() with it, since finding the repo from the running executable existed only to locate that legacy state. AGENTS.md gets the arrangement that replaced it: the host builds and runs from its own clone outside the shared mount, and code reaches it by push to gitea, which this VM's key is not authorized for. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xn8nHw1tw1R6PtiY1eEtw
This commit is contained in:
1 parent
d2d2832ec8
commit
ce349af414
3 files changed
+19
-106
No files matched your search
+4
-92
@@ -21,7 +21,7 @@ mod ssh;
|
||||
mod usage;
|
||||
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
@@ -55,59 +55,6 @@ fn xdg_dir(var: &str, fallback: &str) -> PathBuf {
|
||||
.join("ai-app")
|
||||
}
|
||||
|
||||
/// Picks `preferred`, unless only the older in-repo `legacy` path exists --
|
||||
/// in which case it is used, loudly.
|
||||
///
|
||||
/// State used to live in the repo, which is wrong for two reasons: a
|
||||
/// `git clean -xdf` would take the enrollment and every transcript with
|
||||
/// it, and this repo is a virtiofs mount shared with a machine that is not
|
||||
/// trusted with them (see PLAN.md's security section). Moving is left to a
|
||||
/// person rather than done silently, because relocating a live config out
|
||||
/// from under a running backend is not the kind of thing a program should
|
||||
/// decide to do on its own.
|
||||
fn prefer_xdg(preferred: PathBuf, legacy: PathBuf, what: &str) -> PathBuf {
|
||||
if !preferred.exists() && legacy.exists() {
|
||||
tracing::warn!(
|
||||
"using {} from {} -- move it to {} when convenient; the repo is shared with the VM \
|
||||
and is not a good home for {what}",
|
||||
what,
|
||||
legacy.display(),
|
||||
preferred.display(),
|
||||
);
|
||||
return legacy;
|
||||
}
|
||||
preferred
|
||||
}
|
||||
|
||||
/// The repo root, one level above this crate. Only used now to find the
|
||||
/// legacy in-repo state above; everything the server reads by default
|
||||
/// resolves from the XDG directories instead.
|
||||
///
|
||||
/// Found from the running executable first, and only then from the path
|
||||
/// compiled in. This repo is shared between a VM and its host over
|
||||
/// virtiofs at *different* absolute paths (`~/host/repos/ai-app` vs
|
||||
/// `~/stuff/vm/ai/repos/ai-app`), and `target/` is shared along with it --
|
||||
/// so a binary built on one side and run on the other would otherwise look
|
||||
/// for its config under a path that doesn't exist there, which is exactly
|
||||
/// what happened once. Where both agree (the ordinary case) the answer is
|
||||
/// identical either way; the flags below override it regardless.
|
||||
fn repo_root() -> &'static Path {
|
||||
static ROOT: std::sync::OnceLock<PathBuf> = std::sync::OnceLock::new();
|
||||
ROOT.get_or_init(|| {
|
||||
// target/{debug,release}/ai-server -> four levels up is the root.
|
||||
let from_exe = std::env::current_exe().ok().and_then(|exe| {
|
||||
let root = exe.ancestors().nth(4)?.to_path_buf();
|
||||
root.join("server/Cargo.toml").is_file().then_some(root)
|
||||
});
|
||||
from_exe.unwrap_or_else(|| {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.expect("CARGO_MANIFEST_DIR has a repo-root parent")
|
||||
.to_path_buf()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Serves AI coding sessions (Claude Code, llama.cpp) to the phone app.
|
||||
#[derive(Parser)]
|
||||
struct Args {
|
||||
@@ -193,16 +140,8 @@ async fn main() -> Result<()> {
|
||||
tracing_subscriber::fmt().with_env_filter("info").init();
|
||||
let args = Args::parse();
|
||||
|
||||
let config_path = args.config.unwrap_or_else(|| {
|
||||
prefer_xdg(
|
||||
config_home().join("config.json"),
|
||||
repo_root().join("config.json"),
|
||||
"the config",
|
||||
)
|
||||
});
|
||||
let data_dir = args.data_dir.unwrap_or_else(|| {
|
||||
prefer_xdg(data_home().join("sessions"), repo_root().join("sessions"), "transcripts")
|
||||
});
|
||||
let config_path = args.config.unwrap_or_else(|| config_home().join("config.json"));
|
||||
let data_dir = args.data_dir.unwrap_or_else(|| data_home().join("sessions"));
|
||||
let manager = Arc::new(
|
||||
SessionManager::new(config_path.clone(), data_dir)
|
||||
.with_context(|| format!("failed to load {}", config_path.display()))?,
|
||||
@@ -244,9 +183,7 @@ async fn main() -> Result<()> {
|
||||
print_enrollment(bind_ip, args.port, &token)?;
|
||||
}
|
||||
|
||||
let certs_dir = args
|
||||
.certs
|
||||
.unwrap_or_else(|| prefer_xdg(config_home().join("certs"), repo_root().join("certs"), "certificates"));
|
||||
let certs_dir = args.certs.unwrap_or_else(|| config_home().join("certs"));
|
||||
let leaf_cert = certs_dir.join("leaf.pem");
|
||||
let leaf_key = certs_dir.join("leaf-key.pem");
|
||||
if !leaf_cert.is_file() || !leaf_key.is_file() {
|
||||
@@ -292,31 +229,6 @@ async fn main() -> Result<()> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Choosing the wrong file here is silent -- the server would come up
|
||||
/// happily on the wrong tokens and the wrong session list -- so the
|
||||
/// precedence is worth pinning down.
|
||||
#[test]
|
||||
fn state_prefers_xdg_and_falls_back_to_the_repo_only_when_it_must() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let xdg = dir.path().join("xdg/config.json");
|
||||
let legacy = dir.path().join("repo/config.json");
|
||||
std::fs::create_dir_all(xdg.parent().unwrap()).expect("mkdir");
|
||||
std::fs::create_dir_all(legacy.parent().unwrap()).expect("mkdir");
|
||||
|
||||
// Neither exists: the XDG path, which is where a first run writes.
|
||||
assert_eq!(prefer_xdg(xdg.clone(), legacy.clone(), "the config"), xdg);
|
||||
|
||||
// Only the old in-repo one exists: keep using it rather than
|
||||
// silently starting empty and losing the enrollment.
|
||||
std::fs::write(&legacy, "{}").expect("write");
|
||||
assert_eq!(prefer_xdg(xdg.clone(), legacy.clone(), "the config"), legacy);
|
||||
|
||||
// Both exist: the moved one wins, so finishing a migration takes
|
||||
// effect even if the old file was left behind.
|
||||
std::fs::write(&xdg, "{}").expect("write");
|
||||
assert_eq!(prefer_xdg(xdg.clone(), legacy, "the config"), xdg);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xdg_dirs_respect_the_environment_and_are_namespaced() {
|
||||
// Relative values are ignored per the spec, rather than resolving
|
||||
|
||||
Reference in new issue
Block a user