feat(agents): Add GitHub agent import feature
- Implemented GitHub agent browser in CCAgents component - Created GitHubAgentBrowser component for browsing and importing agents - Added Rust commands for fetching and importing agents from GitHub - Updated API layer with GitHub agent import methods - Updated Cargo.toml with new dependencies - Fixed Tauri configuration and capabilities - Added dropdown menu for import options - Implemented search and preview functionality for GitHub agents
This commit is contained in:
@@ -11,6 +11,7 @@ use std::sync::Mutex;
|
||||
use tauri::{AppHandle, Manager, State, Emitter};
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
use tokio::process::Command;
|
||||
use reqwest;
|
||||
|
||||
/// Finds the full path to the claude binary
|
||||
/// This is necessary because macOS apps have a limited PATH environment
|
||||
@@ -1931,3 +1932,130 @@ pub async fn import_agent_from_file(db: State<'_, AgentDb>, file_path: String) -
|
||||
// Import the agent
|
||||
import_agent(db, json_data).await
|
||||
}
|
||||
|
||||
// GitHub Agent Import functionality
|
||||
|
||||
/// Represents a GitHub agent file from the API
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct GitHubAgentFile {
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub download_url: String,
|
||||
pub size: i64,
|
||||
pub sha: String,
|
||||
}
|
||||
|
||||
/// Represents the GitHub API response for directory contents
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct GitHubApiResponse {
|
||||
name: String,
|
||||
path: String,
|
||||
sha: String,
|
||||
size: i64,
|
||||
url: String,
|
||||
html_url: String,
|
||||
git_url: String,
|
||||
download_url: Option<String>,
|
||||
#[serde(rename = "type")]
|
||||
file_type: String,
|
||||
}
|
||||
|
||||
/// Fetch list of agents from GitHub repository
|
||||
#[tauri::command]
|
||||
pub async fn fetch_github_agents() -> Result<Vec<GitHubAgentFile>, String> {
|
||||
info!("Fetching agents from GitHub repository...");
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let url = "https://api.github.com/repos/getAsterisk/claudia/contents/cc_agents";
|
||||
|
||||
let response = client
|
||||
.get(url)
|
||||
.header("Accept", "application/vnd.github+json")
|
||||
.header("User-Agent", "Claudia-App")
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to fetch from GitHub: {}", e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let status = response.status();
|
||||
let error_text = response.text().await.unwrap_or_default();
|
||||
return Err(format!("GitHub API error ({}): {}", status, error_text));
|
||||
}
|
||||
|
||||
let api_files: Vec<GitHubApiResponse> = response
|
||||
.json()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to parse GitHub response: {}", e))?;
|
||||
|
||||
// Filter only .claudia.json files
|
||||
let agent_files: Vec<GitHubAgentFile> = api_files
|
||||
.into_iter()
|
||||
.filter(|f| f.name.ends_with(".claudia.json") && f.file_type == "file")
|
||||
.filter_map(|f| {
|
||||
f.download_url.map(|download_url| GitHubAgentFile {
|
||||
name: f.name,
|
||||
path: f.path,
|
||||
download_url,
|
||||
size: f.size,
|
||||
sha: f.sha,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
info!("Found {} agents on GitHub", agent_files.len());
|
||||
Ok(agent_files)
|
||||
}
|
||||
|
||||
/// Fetch and preview a specific agent from GitHub
|
||||
#[tauri::command]
|
||||
pub async fn fetch_github_agent_content(download_url: String) -> Result<AgentExport, String> {
|
||||
info!("Fetching agent content from: {}", download_url);
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let response = client
|
||||
.get(&download_url)
|
||||
.header("Accept", "application/json")
|
||||
.header("User-Agent", "Claudia-App")
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to download agent: {}", e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(format!("Failed to download agent: HTTP {}", response.status()));
|
||||
}
|
||||
|
||||
let json_text = response
|
||||
.text()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to read response: {}", e))?;
|
||||
|
||||
// Parse and validate the agent data
|
||||
let export_data: AgentExport = serde_json::from_str(&json_text)
|
||||
.map_err(|e| format!("Invalid agent JSON format: {}", e))?;
|
||||
|
||||
// Validate version
|
||||
if export_data.version != 1 {
|
||||
return Err(format!("Unsupported agent version: {}", export_data.version));
|
||||
}
|
||||
|
||||
Ok(export_data)
|
||||
}
|
||||
|
||||
/// Import an agent directly from GitHub
|
||||
#[tauri::command]
|
||||
pub async fn import_agent_from_github(
|
||||
db: State<'_, AgentDb>,
|
||||
download_url: String,
|
||||
) -> Result<Agent, String> {
|
||||
info!("Importing agent from GitHub: {}", download_url);
|
||||
|
||||
// First, fetch the agent content
|
||||
let export_data = fetch_github_agent_content(download_url).await?;
|
||||
|
||||
// Convert to JSON string and use existing import logic
|
||||
let json_data = serde_json::to_string(&export_data)
|
||||
.map_err(|e| format!("Failed to serialize agent data: {}", e))?;
|
||||
|
||||
// Import using existing function
|
||||
import_agent(db, json_data).await
|
||||
}
|
||||
|
@@ -10,7 +10,6 @@ pub mod claude_binary;
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
@@ -28,7 +28,8 @@ use commands::agents::{
|
||||
get_session_status, cleanup_finished_processes, get_session_output,
|
||||
get_live_session_output, stream_session_output, get_claude_binary_path,
|
||||
set_claude_binary_path, export_agent, export_agent_to_file, import_agent,
|
||||
import_agent_from_file, AgentDb
|
||||
import_agent_from_file, fetch_github_agents, fetch_github_agent_content,
|
||||
import_agent_from_github, AgentDb
|
||||
};
|
||||
use commands::sandbox::{
|
||||
list_sandbox_profiles, create_sandbox_profile, update_sandbox_profile, delete_sandbox_profile,
|
||||
@@ -66,7 +67,6 @@ fn main() {
|
||||
}
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.setup(|app| {
|
||||
// Initialize agents database
|
||||
@@ -143,6 +143,9 @@ fn main() {
|
||||
export_agent_to_file,
|
||||
import_agent,
|
||||
import_agent_from_file,
|
||||
fetch_github_agents,
|
||||
fetch_github_agent_content,
|
||||
import_agent_from_github,
|
||||
execute_agent,
|
||||
list_agent_runs,
|
||||
get_agent_run,
|
||||
|
Reference in New Issue
Block a user