Merge fix: GUI spaceshd discovery + non-fatal connect
This commit is contained in:
@@ -179,6 +179,7 @@ rm -rf ~/.spacesh # сбрасывает сокет, лок, state.json,
|
||||
- **`daemon unavailable` / CLI висит:** сокет битый. `pkill spaceshd; rm -f ~/.spacesh/sock` (или свой `$SPACESH_SOCK`), повтори.
|
||||
- **«another spaceshd is already running»:** уже есть живой демон на этом сокете — это норма; используй его или `spacesh shutdown`.
|
||||
- **GUI не видит демон / пусто:** проверь, что GUI и демон на одном сокете (если задавал `SPACESH_SOCK` для демона — задай тот же для GUI: `SPACESH_SOCK=… npm run tauri dev`).
|
||||
- **GUI пишет «could not connect to spaceshd» (lazy-start не нашёл бинарь):** в `tauri dev` app-бинарь лежит в `app/src-tauri/target/` и демон ему не «сосед» — GUI ищет его по dev-пути в корневом `target/debug/spaceshd` (убедись, что сделан `cargo build -p spaceshd`). Можно явно указать: `SPACESHD_BIN=$PWD/target/debug/spaceshd npm run tauri dev`, либо просто подними демон сам перед GUI: `./target/debug/spaceshd &`. Окно теперь открывается даже без демона (не падает) — команды заработают, как только демон поднимется (перезапусти GUI).
|
||||
- **Статусы у claude не меняются:** проверь, что `claude` в PATH, и что в `~/.spacesh/hooks/<sid>/settings.json` абсолютный путь к `spacesh` верный. Имена/формат хуков Claude Code дрейфуют по версиям — при несовпадении правится только `crates/spaceshd/src/hooks.rs`.
|
||||
- **Нет уведомлений:** проверь разрешение macOS (Системные настройки → Уведомления → spacesh) и что окно действительно не в фокусе.
|
||||
- **`npm run tauri dev` падает на компиляции:** прогони `cargo build -p spaceshd` отдельно, посмотри ошибку; затем `cd app && npm install`.
|
||||
|
||||
@@ -27,24 +27,62 @@ pub struct Bridge {
|
||||
}
|
||||
|
||||
fn socket_path() -> Result<PathBuf> {
|
||||
// Honor SPACESH_SOCK so the GUI matches a daemon/CLI started with the same
|
||||
// override (mirrors the daemon's lifecycle::socket_path and the CLI client).
|
||||
if let Ok(p) = std::env::var("SPACESH_SOCK") {
|
||||
if !p.is_empty() {
|
||||
return Ok(PathBuf::from(p));
|
||||
}
|
||||
}
|
||||
Ok(dirs::home_dir().context("no home")?.join(".spacesh").join("sock"))
|
||||
}
|
||||
|
||||
/// Locate the `spaceshd` binary. The Tauri app is its own cargo workspace, so in
|
||||
/// `tauri dev` the app binary lives in `app/src-tauri/target/debug/` while the
|
||||
/// daemon is built into the repo-root `target/debug/` — they are NOT siblings.
|
||||
/// Try, in order: SPACESHD_BIN override, a sibling (release/bundled layout),
|
||||
/// the repo-root dev/release target relative to the app binary, then PATH.
|
||||
fn find_daemon() -> PathBuf {
|
||||
if let Ok(p) = std::env::var("SPACESHD_BIN") {
|
||||
if !p.is_empty() {
|
||||
return PathBuf::from(p);
|
||||
}
|
||||
}
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
let sibling = exe.with_file_name("spaceshd");
|
||||
if sibling.exists() {
|
||||
return sibling;
|
||||
}
|
||||
if let Some(dir) = exe.parent() {
|
||||
// app/src-tauri/target/{debug,release} → repo-root target/{debug,release}
|
||||
for rel in ["../../../../target/debug/spaceshd", "../../../../target/release/spaceshd"] {
|
||||
let cand = dir.join(rel);
|
||||
if cand.exists() {
|
||||
return cand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PathBuf::from("spaceshd") // last resort: rely on PATH
|
||||
}
|
||||
|
||||
async fn ensure_daemon(sock: &PathBuf) -> Result<UnixStream> {
|
||||
if let Ok(s) = UnixStream::connect(sock).await {
|
||||
return Ok(s);
|
||||
}
|
||||
// Lazy start: spawn the daemon binary, then poll for the socket.
|
||||
let exe = std::env::current_exe()?;
|
||||
let daemon = exe.with_file_name("spaceshd");
|
||||
let _ = std::process::Command::new(daemon).spawn();
|
||||
let daemon = find_daemon();
|
||||
match std::process::Command::new(&daemon).spawn() {
|
||||
Ok(_) => {}
|
||||
Err(e) => anyhow::bail!("could not spawn daemon at {}: {e}", daemon.display()),
|
||||
}
|
||||
for _ in 0..100 {
|
||||
if let Ok(s) = UnixStream::connect(sock).await {
|
||||
return Ok(s);
|
||||
}
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(30)).await;
|
||||
}
|
||||
anyhow::bail!("daemon did not come up")
|
||||
anyhow::bail!("daemon spawned ({}) but did not bind {} in time", daemon.display(), sock.display())
|
||||
}
|
||||
|
||||
impl Bridge {
|
||||
|
||||
@@ -10,10 +10,20 @@ pub fn run() {
|
||||
let handle = app.handle().clone();
|
||||
// Connect the bridge on a tokio runtime, then manage it.
|
||||
tauri::async_runtime::block_on(async move {
|
||||
let bridge = bridge::Bridge::connect(handle.clone())
|
||||
.await
|
||||
.expect("failed to connect to spaceshd");
|
||||
match bridge::Bridge::connect(handle.clone()).await {
|
||||
Ok(bridge) => {
|
||||
handle.manage(bridge);
|
||||
}
|
||||
// Don't crash the app — open the window and log. Commands will
|
||||
// error until a daemon is reachable; start it manually
|
||||
// (./target/debug/spaceshd) or set SPACESHD_BIN / SPACESH_SOCK.
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"spacesh: could not connect to spaceshd: {e} — \
|
||||
start it manually or set SPACESHD_BIN/SPACESH_SOCK"
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user