mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
feat(skills): refactor skill management functions to use new RPC methods
- Replaced direct Tauri invocations with a unified `callCoreRpc` function for skill management operations, enhancing code consistency and maintainability. - Updated functions for listing, discovering, starting, stopping, and managing skill data to utilize the new RPC schema. - Added new RPC handlers for skill discovery, listing, data reading, writing, enabling, disabling, and checking if a skill is enabled, expanding the skills management capabilities. - Enhanced the SkillManifest struct to support serialization, improving data handling for skill manifests.
This commit is contained in:
@@ -1698,31 +1698,29 @@ export async function openhumanAutocompleteClearHistory(): Promise<
|
||||
}
|
||||
|
||||
export async function runtimeListSkills(): Promise<SkillSnapshot[]> {
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
return await invoke('runtime_list_skills');
|
||||
return await callCoreRpc<SkillSnapshot[]>({
|
||||
method: 'openhuman.skills_list',
|
||||
});
|
||||
}
|
||||
|
||||
export async function runtimeDiscoverSkills(): Promise<RuntimeDiscoveredSkill[]> {
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
return await invoke('runtime_discover_skills');
|
||||
return await callCoreRpc<RuntimeDiscoveredSkill[]>({
|
||||
method: 'openhuman.skills_discover',
|
||||
});
|
||||
}
|
||||
|
||||
export async function runtimeStartSkill(skillId: string): Promise<SkillSnapshot> {
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
return await invoke('runtime_start_skill', { skill_id: skillId });
|
||||
return await callCoreRpc<SkillSnapshot>({
|
||||
method: 'openhuman.skills_start',
|
||||
params: { skill_id: skillId },
|
||||
});
|
||||
}
|
||||
|
||||
export async function runtimeStopSkill(skillId: string): Promise<void> {
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
await invoke('runtime_stop_skill', { skill_id: skillId });
|
||||
await callCoreRpc({
|
||||
method: 'openhuman.skills_stop',
|
||||
params: { skill_id: skillId },
|
||||
});
|
||||
}
|
||||
|
||||
export async function runtimeRpc<T = unknown>(
|
||||
@@ -1730,17 +1728,18 @@ export async function runtimeRpc<T = unknown>(
|
||||
method: string,
|
||||
params: Record<string, unknown> = {}
|
||||
): Promise<T> {
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
return await invoke<T>('runtime_rpc', { skill_id: skillId, method, params });
|
||||
return await callCoreRpc<T>({
|
||||
method: 'openhuman.skills_rpc',
|
||||
params: { skill_id: skillId, method, params },
|
||||
});
|
||||
}
|
||||
|
||||
export async function runtimeSkillDataRead(skillId: string, filename: string): Promise<string> {
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
return await invoke('runtime_skill_data_read', { skill_id: skillId, filename });
|
||||
const result = await callCoreRpc<{ content: string }>({
|
||||
method: 'openhuman.skills_data_read',
|
||||
params: { skill_id: skillId, filename },
|
||||
});
|
||||
return result.content;
|
||||
}
|
||||
|
||||
export async function runtimeSkillDataWrite(
|
||||
@@ -1748,17 +1747,18 @@ export async function runtimeSkillDataWrite(
|
||||
filename: string,
|
||||
content: string
|
||||
): Promise<void> {
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
await invoke('runtime_skill_data_write', { skill_id: skillId, filename, content });
|
||||
await callCoreRpc({
|
||||
method: 'openhuman.skills_data_write',
|
||||
params: { skill_id: skillId, filename, content },
|
||||
});
|
||||
}
|
||||
|
||||
export async function runtimeSkillDataDir(skillId: string): Promise<string> {
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
return await invoke('runtime_skill_data_dir', { skill_id: skillId });
|
||||
const result = await callCoreRpc<{ path: string }>({
|
||||
method: 'openhuman.skills_data_dir',
|
||||
params: { skill_id: skillId },
|
||||
});
|
||||
return result.path;
|
||||
}
|
||||
|
||||
export async function runtimeListSkillOptions(skillId: string): Promise<RuntimeSkillOption[]> {
|
||||
@@ -1785,29 +1785,30 @@ export async function runtimeSetSkillOption(
|
||||
}
|
||||
|
||||
export async function runtimeIsSkillEnabled(skillId: string): Promise<boolean> {
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
return await invoke('runtime_is_skill_enabled', { skill_id: skillId });
|
||||
const result = await callCoreRpc<{ enabled: boolean }>({
|
||||
method: 'openhuman.skills_is_enabled',
|
||||
params: { skill_id: skillId },
|
||||
});
|
||||
return result.enabled;
|
||||
}
|
||||
|
||||
export async function runtimeEnableSkill(skillId: string): Promise<void> {
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
await invoke('runtime_enable_skill', { skill_id: skillId });
|
||||
await callCoreRpc({
|
||||
method: 'openhuman.skills_enable',
|
||||
params: { skill_id: skillId },
|
||||
});
|
||||
}
|
||||
|
||||
export async function runtimeDisableSkill(skillId: string): Promise<void> {
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
await invoke('runtime_disable_skill', { skill_id: skillId });
|
||||
await callCoreRpc({
|
||||
method: 'openhuman.skills_disable',
|
||||
params: { skill_id: skillId },
|
||||
});
|
||||
}
|
||||
|
||||
export async function runtimeSkillDataStats(skillId: string): Promise<RuntimeSkillDataStats> {
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
return await invoke('runtime_skill_data_stats', { skill_id: skillId });
|
||||
return await callCoreRpc<RuntimeSkillDataStats>({
|
||||
method: 'openhuman.skills_status',
|
||||
params: { skill_id: skillId },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ pub struct SkillSetup {
|
||||
}
|
||||
|
||||
/// Raw manifest as it appears on disk.
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, serde::Serialize)]
|
||||
pub struct SkillManifest {
|
||||
/// Unique skill identifier (e.g., "price-tracker").
|
||||
pub id: String,
|
||||
|
||||
@@ -32,6 +32,14 @@ pub fn all_controller_schemas() -> Vec<ControllerSchema> {
|
||||
skills_schema("sync"),
|
||||
skills_schema("call_tool"),
|
||||
skills_schema("rpc"),
|
||||
skills_schema("discover"),
|
||||
skills_schema("list"),
|
||||
skills_schema("data_read"),
|
||||
skills_schema("data_write"),
|
||||
skills_schema("data_dir"),
|
||||
skills_schema("enable"),
|
||||
skills_schema("disable"),
|
||||
skills_schema("is_enabled"),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -112,6 +120,38 @@ pub fn all_registered_controllers() -> Vec<RegisteredController> {
|
||||
schema: skills_schema("rpc"),
|
||||
handler: handle_skills_rpc,
|
||||
},
|
||||
RegisteredController {
|
||||
schema: skills_schema("discover"),
|
||||
handler: handle_skills_discover,
|
||||
},
|
||||
RegisteredController {
|
||||
schema: skills_schema("list"),
|
||||
handler: handle_skills_list,
|
||||
},
|
||||
RegisteredController {
|
||||
schema: skills_schema("data_read"),
|
||||
handler: handle_skills_data_read,
|
||||
},
|
||||
RegisteredController {
|
||||
schema: skills_schema("data_write"),
|
||||
handler: handle_skills_data_write,
|
||||
},
|
||||
RegisteredController {
|
||||
schema: skills_schema("data_dir"),
|
||||
handler: handle_skills_data_dir,
|
||||
},
|
||||
RegisteredController {
|
||||
schema: skills_schema("enable"),
|
||||
handler: handle_skills_enable,
|
||||
},
|
||||
RegisteredController {
|
||||
schema: skills_schema("disable"),
|
||||
handler: handle_skills_disable,
|
||||
},
|
||||
RegisteredController {
|
||||
schema: skills_schema("is_enabled"),
|
||||
handler: handle_skills_is_enabled,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -391,6 +431,124 @@ fn skills_schema(function: &str) -> ControllerSchema {
|
||||
required: true,
|
||||
}],
|
||||
},
|
||||
"discover" => ControllerSchema {
|
||||
namespace: "skills",
|
||||
function: "discover",
|
||||
description: "Discover all available skill manifests from source and workspace directories.",
|
||||
inputs: vec![],
|
||||
outputs: vec![FieldSchema {
|
||||
name: "result",
|
||||
ty: TypeSchema::Json,
|
||||
comment: "Array of skill manifests.",
|
||||
required: true,
|
||||
}],
|
||||
},
|
||||
"list" => ControllerSchema {
|
||||
namespace: "skills",
|
||||
function: "list",
|
||||
description: "List all registered (running or stopped) skill snapshots.",
|
||||
inputs: vec![],
|
||||
outputs: vec![FieldSchema {
|
||||
name: "result",
|
||||
ty: TypeSchema::Json,
|
||||
comment: "Array of skill snapshots.",
|
||||
required: true,
|
||||
}],
|
||||
},
|
||||
"data_read" => ControllerSchema {
|
||||
namespace: "skills",
|
||||
function: "data_read",
|
||||
description: "Read a file from a skill's data directory.",
|
||||
inputs: vec![
|
||||
skill_id_input("The skill ID."),
|
||||
FieldSchema {
|
||||
name: "filename",
|
||||
ty: TypeSchema::String,
|
||||
comment: "Filename to read.",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
outputs: vec![FieldSchema {
|
||||
name: "content",
|
||||
ty: TypeSchema::String,
|
||||
comment: "File content.",
|
||||
required: true,
|
||||
}],
|
||||
},
|
||||
"data_write" => ControllerSchema {
|
||||
namespace: "skills",
|
||||
function: "data_write",
|
||||
description: "Write a file to a skill's data directory.",
|
||||
inputs: vec![
|
||||
skill_id_input("The skill ID."),
|
||||
FieldSchema {
|
||||
name: "filename",
|
||||
ty: TypeSchema::String,
|
||||
comment: "Filename to write.",
|
||||
required: true,
|
||||
},
|
||||
FieldSchema {
|
||||
name: "content",
|
||||
ty: TypeSchema::String,
|
||||
comment: "Content to write.",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
outputs: vec![FieldSchema {
|
||||
name: "result",
|
||||
ty: TypeSchema::Json,
|
||||
comment: "Write acknowledgment.",
|
||||
required: true,
|
||||
}],
|
||||
},
|
||||
"data_dir" => ControllerSchema {
|
||||
namespace: "skills",
|
||||
function: "data_dir",
|
||||
description: "Get the data directory path for a skill.",
|
||||
inputs: vec![skill_id_input("The skill ID.")],
|
||||
outputs: vec![FieldSchema {
|
||||
name: "path",
|
||||
ty: TypeSchema::String,
|
||||
comment: "Absolute path to the skill's data directory.",
|
||||
required: true,
|
||||
}],
|
||||
},
|
||||
"enable" => ControllerSchema {
|
||||
namespace: "skills",
|
||||
function: "enable",
|
||||
description: "Enable a skill (set preference and start it).",
|
||||
inputs: vec![skill_id_input("The skill ID to enable.")],
|
||||
outputs: vec![FieldSchema {
|
||||
name: "result",
|
||||
ty: TypeSchema::Json,
|
||||
comment: "Enable acknowledgment.",
|
||||
required: true,
|
||||
}],
|
||||
},
|
||||
"disable" => ControllerSchema {
|
||||
namespace: "skills",
|
||||
function: "disable",
|
||||
description: "Disable a skill (set preference and stop it).",
|
||||
inputs: vec![skill_id_input("The skill ID to disable.")],
|
||||
outputs: vec![FieldSchema {
|
||||
name: "result",
|
||||
ty: TypeSchema::Json,
|
||||
comment: "Disable acknowledgment.",
|
||||
required: true,
|
||||
}],
|
||||
},
|
||||
"is_enabled" => ControllerSchema {
|
||||
namespace: "skills",
|
||||
function: "is_enabled",
|
||||
description: "Check whether a skill is enabled in user preferences.",
|
||||
inputs: vec![skill_id_input("The skill ID to check.")],
|
||||
outputs: vec![FieldSchema {
|
||||
name: "enabled",
|
||||
ty: TypeSchema::Bool,
|
||||
comment: "Whether the skill is enabled.",
|
||||
required: true,
|
||||
}],
|
||||
},
|
||||
_ => ControllerSchema {
|
||||
namespace: "skills",
|
||||
function: "unknown",
|
||||
@@ -616,6 +774,103 @@ fn handle_skills_rpc(params: Map<String, Value>) -> ControllerFuture {
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_skills_discover(_params: Map<String, Value>) -> ControllerFuture {
|
||||
Box::pin(async move {
|
||||
let engine = require_engine()?;
|
||||
let manifests = engine.discover_skills().await?;
|
||||
serde_json::to_value(&manifests).map_err(|e| e.to_string())
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_skills_list(_params: Map<String, Value>) -> ControllerFuture {
|
||||
Box::pin(async move {
|
||||
let engine = require_engine()?;
|
||||
let skills = engine.list_skills();
|
||||
serde_json::to_value(&skills).map_err(|e| e.to_string())
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_skills_data_read(params: Map<String, Value>) -> ControllerFuture {
|
||||
Box::pin(async move {
|
||||
let skill_id = params
|
||||
.get("skill_id")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or("missing skill_id")?
|
||||
.to_string();
|
||||
let filename = params
|
||||
.get("filename")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or("missing filename")?
|
||||
.to_string();
|
||||
let engine = require_engine()?;
|
||||
let content = engine.data_read(&skill_id, &filename)?;
|
||||
Ok(serde_json::json!({ "content": content }))
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_skills_data_write(params: Map<String, Value>) -> ControllerFuture {
|
||||
Box::pin(async move {
|
||||
let skill_id = params
|
||||
.get("skill_id")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or("missing skill_id")?
|
||||
.to_string();
|
||||
let filename = params
|
||||
.get("filename")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or("missing filename")?
|
||||
.to_string();
|
||||
let content = params
|
||||
.get("content")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or("missing content")?
|
||||
.to_string();
|
||||
let engine = require_engine()?;
|
||||
engine.data_write(&skill_id, &filename, &content)?;
|
||||
Ok(serde_json::json!({ "ok": true }))
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_skills_data_dir(params: Map<String, Value>) -> ControllerFuture {
|
||||
Box::pin(async move {
|
||||
let p: SkillIdParams =
|
||||
serde_json::from_value(Value::Object(params)).map_err(|e| e.to_string())?;
|
||||
let engine = require_engine()?;
|
||||
let path = engine.skill_data_dir(&p.skill_id);
|
||||
Ok(serde_json::json!({ "path": path.display().to_string() }))
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_skills_enable(params: Map<String, Value>) -> ControllerFuture {
|
||||
Box::pin(async move {
|
||||
let p: SkillIdParams =
|
||||
serde_json::from_value(Value::Object(params)).map_err(|e| e.to_string())?;
|
||||
let engine = require_engine()?;
|
||||
engine.enable_skill(&p.skill_id).await?;
|
||||
Ok(serde_json::json!({ "ok": true }))
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_skills_disable(params: Map<String, Value>) -> ControllerFuture {
|
||||
Box::pin(async move {
|
||||
let p: SkillIdParams =
|
||||
serde_json::from_value(Value::Object(params)).map_err(|e| e.to_string())?;
|
||||
let engine = require_engine()?;
|
||||
engine.disable_skill(&p.skill_id).await?;
|
||||
Ok(serde_json::json!({ "ok": true }))
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_skills_is_enabled(params: Map<String, Value>) -> ControllerFuture {
|
||||
Box::pin(async move {
|
||||
let p: SkillIdParams =
|
||||
serde_json::from_value(Value::Object(params)).map_err(|e| e.to_string())?;
|
||||
let engine = require_engine()?;
|
||||
let enabled = engine.is_skill_enabled(&p.skill_id);
|
||||
Ok(serde_json::json!({ "enabled": enabled }))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user