harness: declarative claude plugin marketplaces
new `hyperhive.claudeMarketplaces` option (list of strings — URL, path, or github:owner/repo). harness boot adds each via `claude plugin marketplace add` before updating + installing the configured plugins, so specs like `foo@some-marketplace` resolve on a fresh container. idempotent: 'already exists' stderr is treated as success.
This commit is contained in:
parent
608de57924
commit
597351ca4e
2 changed files with 66 additions and 0 deletions
|
|
@ -18,6 +18,53 @@ use tokio::process::Command;
|
|||
use crate::client;
|
||||
|
||||
const PLUGINS_PATH: &str = "/etc/hyperhive/claude-plugins.json";
|
||||
const MARKETPLACES_PATH: &str = "/etc/hyperhive/claude-marketplaces.json";
|
||||
|
||||
/// Add every marketplace from `/etc/hyperhive/claude-marketplaces.json`
|
||||
/// via `claude plugin marketplace add <source>`. Idempotent: re-add of
|
||||
/// an existing marketplace is treated as success (claude prints an
|
||||
/// "already exists" message and exits non-zero on some versions).
|
||||
/// Required before any `<plugin>@<marketplace>` install can resolve.
|
||||
async fn add_marketplaces() {
|
||||
let raw = match tokio::fs::read_to_string(MARKETPLACES_PATH).await {
|
||||
Ok(s) => s,
|
||||
Err(_) => return,
|
||||
};
|
||||
let sources: Vec<String> = match serde_json::from_str(&raw) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
tracing::warn!(path = MARKETPLACES_PATH, error = ?e, "claude-marketplaces spec parse failed; skipping");
|
||||
return;
|
||||
}
|
||||
};
|
||||
for source in sources {
|
||||
match Command::new("claude")
|
||||
.args(["plugin", "marketplace", "add", &source])
|
||||
.output()
|
||||
.await
|
||||
{
|
||||
Ok(out) if out.status.success() => {
|
||||
tracing::info!(source = %source, "claude plugin marketplace add ok");
|
||||
}
|
||||
Ok(out) => {
|
||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
||||
if stderr.contains("already") {
|
||||
tracing::debug!(source = %source, "marketplace already added");
|
||||
} else {
|
||||
tracing::warn!(
|
||||
source = %source,
|
||||
status = ?out.status,
|
||||
stderr = %stderr,
|
||||
"claude plugin marketplace add failed (non-fatal)",
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(source = %source, error = ?e, "claude plugin marketplace add spawn failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Update all configured plugin marketplaces. Non-fatal — logs a warning
|
||||
/// on failure but does not abort the install sequence.
|
||||
|
|
@ -64,6 +111,7 @@ pub async fn install_configured(socket: &Path, notify_recipient: Option<&str>) {
|
|||
if specs.is_empty() {
|
||||
return;
|
||||
}
|
||||
add_marketplaces().await;
|
||||
update_marketplaces().await;
|
||||
for spec in specs {
|
||||
match Command::new("claude")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue