Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ yarn.lock
node_modules
dist
dist-ssr
/skills/
*.local

# Environment variables
Expand Down
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

1 change: 1 addition & 0 deletions docs/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ todo
[] - add a local model that can read through the screen and also go through voice using an API like whisper
[] - add a screener recorder that goes through the intefaces in the screen and locally summarizes what is happening and brings more assitance to the user
[] clean up the core so that we can run it as a binary on a server or as docker
[] we need to get reverse tunneling solutions added
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dev:web": "vite",
"dev:app": "source scripts/load-dotenv.sh && tauri dev",
"build": "tsc && vite build",
"build:app": "yarn skills:build && tsc && vite build",
"build:app": "tsc && vite build",
"compile": "tsc --noEmit",
"preview": "vite preview",
"tauri": "tauri",
Expand Down Expand Up @@ -37,8 +37,8 @@
"format:check": "prettier --check .",
"lint": "eslint . --ext .ts,.tsx",
"lint:fix": "eslint . --ext .ts,.tsx --fix",
"skills:build": "cd skills && yarn build",
"skills:watch": "cd skills && yarn build:watch",
"skills:build": "echo \"skills are runtime-installed from registry\"",
"skills:watch": "echo \"skills are runtime-installed from registry\"",
"prepare": "husky"
},
"dependencies": {
Expand Down
1 change: 0 additions & 1 deletion skills
Submodule skills deleted from 0685a6
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 72 additions & 1 deletion src-tauri/src/commands/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use crate::models::socket::SocketState;
use crate::runtime::socket_manager::SocketManager;
use crate::utils::config::get_backend_url;
use std::sync::Arc;
use std::collections::HashMap;
use tauri::State;
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -75,6 +74,10 @@ pub struct ZeroClawToolResult {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
mod desktop {
use super::*;
use crate::runtime::registry::{
install_or_update_skill, list_catalog, sync_core_skills, uninstall_skill,
RegistryCatalogEntry, RegistrySyncResult,
};

/// List all skills discovered from the skills directory (including not-yet-started).
#[tauri::command]
Expand Down Expand Up @@ -111,6 +114,44 @@ mod desktop {
Ok(result)
}

#[tauri::command]
pub async fn registry_sync_core(
engine: State<'_, Arc<RuntimeEngine>>,
) -> Result<RegistrySyncResult, String> {
sync_core_skills(&engine)
}

#[tauri::command]
pub async fn registry_list_catalog(
engine: State<'_, Arc<RuntimeEngine>>,
) -> Result<Vec<RegistryCatalogEntry>, String> {
list_catalog(&engine)
}

#[tauri::command]
pub async fn registry_install_skill(
engine: State<'_, Arc<RuntimeEngine>>,
skill_id: String,
) -> Result<(), String> {
install_or_update_skill(&engine, &skill_id)
}

#[tauri::command]
pub async fn registry_update_skill(
engine: State<'_, Arc<RuntimeEngine>>,
skill_id: String,
) -> Result<(), String> {
install_or_update_skill(&engine, &skill_id)
}

#[tauri::command]
pub async fn registry_uninstall_skill(
engine: State<'_, Arc<RuntimeEngine>>,
skill_id: String,
) -> Result<(), String> {
uninstall_skill(&engine, &skill_id)
}

/// List all currently registered (running/stopped/error) skill instances.
#[tauri::command]
pub async fn runtime_list_skills(
Expand Down Expand Up @@ -498,6 +539,36 @@ mod mobile {
Ok(vec![])
}

#[tauri::command]
pub async fn registry_sync_core() -> Result<serde_json::Value, String> {
Ok(serde_json::json!({
"repo_path": "",
"updated_core": [],
"skipped_core": [],
"errors": []
}))
}

#[tauri::command]
pub async fn registry_list_catalog() -> Result<Vec<serde_json::Value>, String> {
Ok(vec![])
}

#[tauri::command]
pub async fn registry_install_skill(_skill_id: String) -> Result<(), String> {
Err(MOBILE_ERROR.to_string())
}

#[tauri::command]
pub async fn registry_update_skill(_skill_id: String) -> Result<(), String> {
Err(MOBILE_ERROR.to_string())
}

#[tauri::command]
pub async fn registry_uninstall_skill(_skill_id: String) -> Result<(), String> {
Err(MOBILE_ERROR.to_string())
}

#[tauri::command]
pub async fn runtime_list_skills() -> Result<Vec<SkillSnapshot>, String> {
Ok(vec![])
Expand Down
27 changes: 27 additions & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,7 @@ pub fn run() {
match runtime::qjs_engine::RuntimeEngine::new(skills_data_dir) {
Ok(engine) => {
engine.set_app_handle(app.handle().clone());
engine.set_skills_source_dir(data_dir.join("skills").join("installed"));

// Set resource directory for bundled skills (production builds)
if let Ok(resource_dir) = app.path().resource_dir() {
Expand Down Expand Up @@ -797,6 +798,22 @@ pub fn run() {
// lightweight contexts don't have V8's memory reservation issue)
let engine_clone = engine.clone();
tauri::async_runtime::spawn(async move {
match runtime::registry::sync_core_skills(&engine_clone) {
Ok(sync) => {
log::info!(
"[registry] core sync complete: updated={}, skipped={}, errors={}",
sync.updated_core.len(),
sync.skipped_core.len(),
sync.errors.len()
);
for error in sync.errors {
log::error!("[registry] core sync error: {}", error);
}
}
Err(e) => {
log::error!("[registry] core sync failed: {}", e);
}
}
engine_clone.auto_start_skills().await;
});

Expand Down Expand Up @@ -1013,6 +1030,11 @@ pub fn run() {
ai_write_memory_file,
ai_list_memory_files,
// Runtime commands
registry_sync_core,
registry_list_catalog,
registry_install_skill,
registry_update_skill,
registry_uninstall_skill,
runtime_discover_skills,
runtime_list_skills,
runtime_start_skill,
Expand Down Expand Up @@ -1144,6 +1166,11 @@ pub fn run() {
ai_write_memory_file,
ai_list_memory_files,
// Runtime commands
registry_sync_core,
registry_list_catalog,
registry_install_skill,
registry_update_skill,
registry_uninstall_skill,
runtime_discover_skills,
runtime_list_skills,
runtime_start_skill,
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub mod cron_scheduler;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub mod ping_scheduler;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub mod registry;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub mod skill_registry;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub mod qjs_engine;
Expand Down
61 changes: 8 additions & 53 deletions src-tauri/src/runtime/qjs_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::runtime::ping_scheduler::PingScheduler;
use crate::runtime::preferences::PreferencesStore;
use crate::runtime::skill_registry::SkillRegistry;
use crate::runtime::socket_manager::SocketManager;
use crate::runtime::types::{events, SkillMessage, SkillSnapshot, SkillStatus, ToolResult};
use crate::runtime::types::{events, SkillSnapshot, SkillStatus, ToolResult};
use crate::runtime::qjs_skill_instance::{BridgeDeps, QjsSkillInstance};
// IdbStorage removed during runtime cleanup

Expand Down Expand Up @@ -119,60 +119,15 @@ impl RuntimeEngine {
log::info!("[runtime] Using explicit skills source dir: {:?}", dir);
return Ok(dir.clone());
}

let current =
std::env::current_dir().map_err(|e| format!("Failed to get current dir: {e}"))?;

// 2. Dev: cwd/skills/skills
let dev_skills = current.join("skills").join("skills");
if dev_skills.exists() {
log::info!("[runtime] Using dev skills dir: {:?}", dev_skills);
return Ok(dev_skills);
}

// 3. Dev: ../skills/skills
if let Some(parent) = current.parent() {
let parent_skills = parent.join("skills").join("skills");
if parent_skills.exists() {
log::info!("[runtime] Using parent dev skills dir: {:?}", parent_skills);
return Ok(parent_skills);
}
}

// 4. Production: bundled resources
if let Some(resource_dir) = self.resource_dir.read().as_ref() {
let bundled_skills = resource_dir.join("_up_").join("skills").join("skills");
if bundled_skills.exists() {
log::info!(
"[runtime] Using bundled skills from resources: {:?}",
bundled_skills
);
return Ok(bundled_skills);
}

let bundled_skills_alt = resource_dir.join("skills");
if bundled_skills_alt.exists() {
log::info!(
"[runtime] Using bundled skills from resources (alt): {:?}",
bundled_skills_alt
);
return Ok(bundled_skills_alt);
}

log::warn!(
"[runtime] Resource dir set but skills not found. Checked: {:?} and {:?}",
bundled_skills,
bundled_skills_alt
);
}

// 5. Final fallback: app data dir
let prod_dir = self.skills_data_dir.clone();
// Runtime-managed install directory fallback.
let install_dir = self.skills_data_dir.join("installed");
std::fs::create_dir_all(&install_dir)
.map_err(|e| format!("Failed to create install dir {}: {e}", install_dir.display()))?;
log::info!(
"[runtime] Skills source dir (data dir fallback): {:?}",
prod_dir
"[runtime] Skills source dir (runtime install fallback): {:?}",
install_dir
);
Ok(prod_dir)
Ok(install_dir)
}

/// Expose the resolved skills source directory (for external callers like unified registry).
Expand Down
Loading
Loading