forward plugin install failures to manager from sub-agents
install_configured now takes an optional notify recipient. on a non-zero or spawn-failed 'claude plugin install', sub-agents send the spec + stderr to manager via the hyperhive socket; manager passes None so it doesn't message itself. boot still proceeds either way — notification is best-effort.
This commit is contained in:
parent
3e040d5b16
commit
772fdd8320
3 changed files with 47 additions and 4 deletions
|
|
@ -71,7 +71,7 @@ async fn main() -> Result<()> {
|
||||||
let login_state = Arc::new(Mutex::new(initial));
|
let login_state = Arc::new(Mutex::new(initial));
|
||||||
let bus = Bus::new();
|
let bus = Bus::new();
|
||||||
let files = turn::TurnFiles::prepare(&cli.socket, &label, mcp::Flavor::Agent).await?;
|
let files = turn::TurnFiles::prepare(&cli.socket, &label, mcp::Flavor::Agent).await?;
|
||||||
plugins::install_configured().await;
|
plugins::install_configured(&cli.socket, Some("manager")).await;
|
||||||
tokio::spawn(web_ui::serve(
|
tokio::spawn(web_ui::serve(
|
||||||
label,
|
label,
|
||||||
port,
|
port,
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ async fn main() -> Result<()> {
|
||||||
let login_state = Arc::new(Mutex::new(initial));
|
let login_state = Arc::new(Mutex::new(initial));
|
||||||
let bus = Bus::new();
|
let bus = Bus::new();
|
||||||
let files = turn::TurnFiles::prepare(&cli.socket, &label, mcp::Flavor::Manager).await?;
|
let files = turn::TurnFiles::prepare(&cli.socket, &label, mcp::Flavor::Manager).await?;
|
||||||
plugins::install_configured().await;
|
plugins::install_configured(&cli.socket, None).await;
|
||||||
tokio::spawn(web_ui::serve(
|
tokio::spawn(web_ui::serve(
|
||||||
label,
|
label,
|
||||||
port,
|
port,
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,21 @@
|
||||||
//! recreate is fine. Failures log a warning but do not abort boot —
|
//! recreate is fine. Failures log a warning but do not abort boot —
|
||||||
//! we'd rather start without a plugin than refuse to serve.
|
//! we'd rather start without a plugin than refuse to serve.
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
use crate::client;
|
||||||
|
|
||||||
const PLUGINS_PATH: &str = "/etc/hyperhive/claude-plugins.json";
|
const PLUGINS_PATH: &str = "/etc/hyperhive/claude-plugins.json";
|
||||||
|
|
||||||
pub async fn install_configured() {
|
/// Install every plugin in `/etc/hyperhive/claude-plugins.json`. When
|
||||||
|
/// `notify_recipient` is `Some(name)`, install failures also get sent
|
||||||
|
/// as a hyperhive message to that recipient (typically `"manager"` for
|
||||||
|
/// sub-agents) so it surfaces in the inbox rather than being buried in
|
||||||
|
/// journald. The manager itself passes `None` — there's nobody above
|
||||||
|
/// it to notify.
|
||||||
|
pub async fn install_configured(socket: &Path, notify_recipient: Option<&str>) {
|
||||||
let raw = match tokio::fs::read_to_string(PLUGINS_PATH).await {
|
let raw = match tokio::fs::read_to_string(PLUGINS_PATH).await {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(_) => return,
|
Err(_) => return,
|
||||||
|
|
@ -33,16 +43,49 @@ pub async fn install_configured() {
|
||||||
tracing::info!(spec = %spec, "claude plugin install ok");
|
tracing::info!(spec = %spec, "claude plugin install ok");
|
||||||
}
|
}
|
||||||
Ok(out) => {
|
Ok(out) => {
|
||||||
|
let stderr = String::from_utf8_lossy(&out.stderr).into_owned();
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
spec = %spec,
|
spec = %spec,
|
||||||
status = ?out.status,
|
status = ?out.status,
|
||||||
stderr = %String::from_utf8_lossy(&out.stderr),
|
stderr = %stderr,
|
||||||
"claude plugin install failed",
|
"claude plugin install failed",
|
||||||
);
|
);
|
||||||
|
if let Some(to) = notify_recipient {
|
||||||
|
notify(
|
||||||
|
socket,
|
||||||
|
to,
|
||||||
|
format!(
|
||||||
|
"claude plugin install failed for `{spec}`:\n{}",
|
||||||
|
stderr.trim()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::warn!(spec = %spec, error = ?e, "claude plugin install spawn failed");
|
tracing::warn!(spec = %spec, error = ?e, "claude plugin install spawn failed");
|
||||||
|
if let Some(to) = notify_recipient {
|
||||||
|
notify(
|
||||||
|
socket,
|
||||||
|
to,
|
||||||
|
format!("claude plugin install spawn failed for `{spec}`: {e}"),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Best-effort hyperhive send. Swallows transport errors — the warn log
|
||||||
|
/// is already in journald and the harness boot must not stall waiting
|
||||||
|
/// for the broker to be reachable.
|
||||||
|
async fn notify(socket: &Path, to: &str, body: String) {
|
||||||
|
let req = hive_sh4re::AgentRequest::Send {
|
||||||
|
to: to.to_owned(),
|
||||||
|
body,
|
||||||
|
};
|
||||||
|
if let Err(e) = client::request::<_, hive_sh4re::AgentResponse>(socket, &req).await {
|
||||||
|
tracing::warn!(error = ?e, "failed to notify {to} of plugin install failure");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue