P2 slice 8: the viewer's menu, setup, no confinement (D-32)
No Landlock, no seccomp, no helper: isolation is "same_user (advisory)". The lease socket keeps two intents (close, format-upgrade consent). The viewer holds the books open while it runs and gains a Books menu: New books, Back up now, Restore (preview, then confirm), Close, Connect Claude Code / Codex. New commands `wanderbooks books back-up` and `wanderbooks connect claude|codex`. The scaffold (`new`/`init` and its templates) is gone; install.sh needs no sudo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
1 parent
019d8d76a3
commit
3cf359a0cd
28 files changed
+1976
-1931
No files matched your search
@@ -12,7 +12,8 @@
|
||||
//! INVARIANTS:
|
||||
//! 1. Before each try the holder waits while a restart holds
|
||||
//! run/update.lock.
|
||||
//! 2. No host answering: it starts one (its Starter) and keeps trying;
|
||||
//! 2. No host answering: it starts one (cleanly, start.rs) and keeps
|
||||
//! trying;
|
||||
//! a host that is closing or restarting turns it away and it tries
|
||||
//! again until a host takes the lease.
|
||||
//! 3. A start that brings no lease within START_WAIT (or whose host
|
||||
@@ -87,20 +88,11 @@ impl From<&str> for HoldError {
|
||||
}
|
||||
}
|
||||
|
||||
/// How a holder starts a host when none answers.
|
||||
pub enum Starter {
|
||||
/// Start it itself (a holder that isn't confined).
|
||||
Direct,
|
||||
/// Ask its helper to (a confined holder).
|
||||
Helper(std::sync::Arc<crate::helper::Client>),
|
||||
}
|
||||
|
||||
/// Takes leases for one holder.
|
||||
pub struct Opener {
|
||||
root: Option<PathBuf>,
|
||||
run: PathBuf,
|
||||
knobs: Knobs,
|
||||
starter: Starter,
|
||||
kind: Kind,
|
||||
book: Option<String>,
|
||||
failures: u32,
|
||||
@@ -140,7 +132,6 @@ impl Opener {
|
||||
pub fn new(
|
||||
root: Option<&Path>,
|
||||
knobs: Knobs,
|
||||
starter: Starter,
|
||||
kind: Kind,
|
||||
book: Option<String>,
|
||||
) -> Result<Self, String> {
|
||||
@@ -150,7 +141,6 @@ impl Opener {
|
||||
root: root.map(|_| found.path().to_path_buf()),
|
||||
run,
|
||||
knobs,
|
||||
starter,
|
||||
kind,
|
||||
book,
|
||||
failures: 0,
|
||||
@@ -192,6 +182,12 @@ impl Opener {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start counting failures over (a holder that waited a while after
|
||||
/// giving up tries again); what it has held is kept (INVARIANT 5).
|
||||
pub fn forget_failures(&mut self) {
|
||||
self.failures = 0;
|
||||
}
|
||||
|
||||
/// Take a lease, starting a host if none answers (INVARIANTS 1-4).
|
||||
pub async fn hold(&mut self) -> Result<Lease, HoldError> {
|
||||
let mut started: Option<Instant> = None;
|
||||
@@ -230,8 +226,8 @@ impl Opener {
|
||||
// for it rather than start another.
|
||||
let held = host_lock_held(&self.run);
|
||||
match started {
|
||||
// A start that fails (a spawn error, the helper
|
||||
// gone) is a failure like any other (INVARIANT 3).
|
||||
// A start that fails (a spawn error) is a
|
||||
// failure like any other (INVARIANT 3).
|
||||
None if !held => match self.start().await {
|
||||
Ok(()) => started = Some(Instant::now()),
|
||||
Err(_) => self.failed().await?,
|
||||
@@ -256,25 +252,13 @@ impl Opener {
|
||||
}
|
||||
}
|
||||
|
||||
/// Start a host, through this holder's starter.
|
||||
/// Start a host, cleanly (start.rs).
|
||||
async fn start(&self) -> Result<(), String> {
|
||||
match &self.starter {
|
||||
Starter::Direct => {
|
||||
let root = self.root.clone();
|
||||
let knobs = self.knobs;
|
||||
tokio::task::spawn_blocking(move || {
|
||||
crate::start::start_host(root.as_deref(), &knobs)
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
Starter::Helper(helper) => {
|
||||
let helper = helper.clone();
|
||||
tokio::task::spawn_blocking(move || helper.start_host())
|
||||
.await
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
}
|
||||
let root = self.root.clone();
|
||||
let knobs = self.knobs;
|
||||
tokio::task::spawn_blocking(move || crate::start::start_host(root.as_deref(), &knobs))
|
||||
.await
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
|
||||
/// Say hello on a connection and read the host's first word.
|
||||
@@ -468,7 +452,6 @@ mod tests {
|
||||
backoff: Duration::from_millis(1),
|
||||
..Knobs::default()
|
||||
},
|
||||
starter: Starter::Direct,
|
||||
kind: Kind::Cli,
|
||||
book: None,
|
||||
failures: 0,
|
||||
|
||||
Reference in new issue
Block a user