diff --git a/.gitignore b/.gitignore index e60004bbc..4c4e2e7d0 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,4 @@ src-tauri/target/ workflow .fastembed_cache +overlay/src-tauri/target/ \ No newline at end of file diff --git a/app/src-tauri/Cargo.lock b/app/src-tauri/Cargo.lock index dcd33fa37..dd882444c 100644 --- a/app/src-tauri/Cargo.lock +++ b/app/src-tauri/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "OpenHuman" -version = "0.52.0" +version = "0.52.2" dependencies = [ "env_logger", "log", diff --git a/app/src-tauri/entitlements.sidecar.plist b/app/src-tauri/entitlements.sidecar.plist index 46f675661..92a9edf6d 100644 --- a/app/src-tauri/entitlements.sidecar.plist +++ b/app/src-tauri/entitlements.sidecar.plist @@ -8,5 +8,15 @@ com.apple.security.cs.disable-library-validation + + com.apple.security.network.client + + + com.apple.security.network.server + diff --git a/src/openhuman/skills/registry_ops.rs b/src/openhuman/skills/registry_ops.rs index 599abf270..a426d6661 100644 --- a/src/openhuman/skills/registry_ops.rs +++ b/src/openhuman/skills/registry_ops.rs @@ -61,8 +61,11 @@ pub async fn registry_fetch( log::info!("[registry] fetching registry from {url}"); + // Use rustls explicitly so we never fall back to native-tls (which can hang + // on macOS under the Hardened Runtime when the system keychain is restricted). let client = reqwest::Client::builder() .timeout(std::time::Duration::from_secs(30)) + .use_rustls_tls() .build() .map_err(|e| format!("failed to create HTTP client: {e}"))?; @@ -150,8 +153,13 @@ async fn fetch_url_bytes(url: &str) -> Result, String> { return read_local_file(url); } + log::debug!("[registry] fetch_url_bytes: connecting to {url}"); + + // Use rustls explicitly so we never fall back to native-tls (which can hang + // on macOS under the Hardened Runtime when the system keychain is restricted). let client = reqwest::Client::builder() .timeout(std::time::Duration::from_secs(30)) + .use_rustls_tls() .build() .map_err(|e| format!("failed to create HTTP client: {e}"))?; @@ -159,16 +167,26 @@ async fn fetch_url_bytes(url: &str) -> Result, String> { .get(url) .send() .await - .map_err(|e| format!("failed to fetch {url}: {e}"))?; + .map_err(|e| format!("[registry] network error fetching {url}: {e}"))?; if !resp.status().is_success() { - return Err(format!("fetch {url} failed with status {}", resp.status())); + return Err(format!( + "[registry] fetch {url} returned HTTP {}", + resp.status() + )); } - resp.bytes() + let bytes = resp + .bytes() .await .map(|b| b.to_vec()) - .map_err(|e| format!("failed to read response from {url}: {e}")) + .map_err(|e| format!("[registry] failed to read response body from {url}: {e}"))?; + + log::debug!( + "[registry] fetch_url_bytes: {} bytes from {url}", + bytes.len() + ); + Ok(bytes) } /// Install a skill from the registry or local directory. @@ -177,6 +195,11 @@ async fn fetch_url_bytes(url: &str) -> Result, String> { /// directory (e.g. `$SKILLS_LOCAL_DIR//`) instead of downloading. /// This also works when registry entry URLs are local file paths. pub async fn skill_install(workspace_dir: &Path, skill_id: &str) -> Result<(), String> { + log::info!( + "[registry] skills_install: starting — skill_id={skill_id} workspace={}", + workspace_dir.display() + ); + // --- Fast path: SKILLS_LOCAL_DIR copies directly from local dev directory --- // This allows developers to work on skills locally and see changes reflected instantly // in the app without having to publish to a registry. @@ -199,7 +222,9 @@ pub async fn skill_install(workspace_dir: &Path, skill_id: &str) -> Result<(), S } // --- Standard path: fetch from registry (remote or local URLs) --- + log::debug!("[registry] skills_install: fetching registry for '{skill_id}'"); let registry = registry_fetch(workspace_dir, false).await?; + log::debug!("[registry] skills_install: registry fetched, looking up '{skill_id}'"); let entry = registry .skills @@ -210,22 +235,37 @@ pub async fn skill_install(workspace_dir: &Path, skill_id: &str) -> Result<(), S .ok_or_else(|| format!("skill '{skill_id}' not found in registry"))? .clone(); + log::debug!( + "[registry] skills_install: found entry '{skill_id}' v{} manifest={} bundle={}", + entry.version, + entry.manifest_url, + entry.download_url + ); + let skill_dir = workspace_dir.join("skills").join(skill_id); std::fs::create_dir_all(&skill_dir).map_err(|e| format!("failed to create skill dir: {e}"))?; // Fetch manifest (local or remote) log::info!( - "[registry] fetching manifest for '{skill_id}' from {}", + "[registry] skills_install: fetching manifest for '{skill_id}' from {}", entry.manifest_url ); let manifest_bytes = fetch_url_bytes(&entry.manifest_url).await?; + log::debug!( + "[registry] skills_install: manifest fetched ({} bytes)", + manifest_bytes.len() + ); // Fetch JS bundle (local or remote) log::info!( - "[registry] fetching JS bundle for '{skill_id}' from {}", + "[registry] skills_install: fetching JS bundle for '{skill_id}' from {}", entry.download_url ); let js_bytes = fetch_url_bytes(&entry.download_url).await?; + log::debug!( + "[registry] skills_install: JS bundle fetched ({} bytes)", + js_bytes.len() + ); // Verify checksum if present to ensure integrity of the downloaded bundle if let Some(expected) = &entry.checksum_sha256 {