mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-29 22:23:01 +00:00
* chore: update AlphaHuman version to 0.49.3 and configure updater plugin in tauri.conf.json - Bumped the AlphaHuman package version in Cargo.lock to 0.49.3. - Added updater configuration in tauri.conf.json to enable automatic updates with specified endpoints. * refactor: rename AlphaHuman to OpenHuman across the codebase - Updated all instances of "AlphaHuman" to "OpenHuman" in comments, tooltips, and constants to reflect the new branding. - Adjusted relevant documentation and prompts to ensure consistency with the new name. * refactor: update documentation and configurations to reflect OpenHuman branding - Replaced all instances of "AlphaHuman" with "OpenHuman" in documentation, comments, and configuration files to ensure consistency with the new branding. - Updated deep link URLs and related authentication flows to use the new "openhuman://" scheme. - Adjusted paths and references in the skills system and other related files to align with the new project name.te * refactor: standardize OpenHuman references and update configurations - Replaced all instances of "AlphaHuman" with "OpenHuman" across documentation, comments, and configuration files to maintain branding consistency. - Updated URLs and paths to reflect the new "openhuman://" scheme. - Adjusted environment variable names and related settings to align with the new project identity. - Enhanced documentation for clarity and accuracy regarding the OpenHuman framework.r * chore: update subproject commit reference in skills directory * refactor: update backend URL to reflect new service domain - Changed default backend URL from "https://api.openhuman.xyz" to "https://api.tinyhumans.ai" in both JavaScript and Rust configuration files. - Ensured consistency across the codebase regarding the new backend service endpoint. * feat: introduce identity and migration modules for OpenHuman - Added a new identity module to support AIEOS v1.1 JSON format, including structures for identity, psychology, linguistics, motivations, capabilities, physicality, history, and interests. - Implemented a migration module to facilitate data migration from OpenClaw memory, including SQLite and Markdown sources, with detailed reporting on migration statistics and warnings. - Established utility functions for handling multimodal content and image processing within the OpenHuman framework. - Enhanced the agent system with new dispatcher and classifier functionalities to improve tool management and message classification. * chore: remove Android project files and configurations - Deleted various Android project files including .editorconfig, .gitignore, build.gradle.kts, gradle.properties, and others to clean up the project structure. - Removed all related resources, layouts, and source files from the Android app directory to streamline the codebase. - This cleanup is part of a larger effort to refactor and simplify the project structure. * refactor: update login flow and remove Telegram integration - Removed the TelegramLoginButton component and its references from the OAuthLoginSection, streamlining the login options. - Updated the AppRoutes to remove the login route, reflecting changes in the authentication flow. - Enhanced the RotatingTetrahedronCanvas component with improved geometry and lighting effects for better visual presentation. - Adjusted the TypewriterGreeting component's styling for consistency. - Cleaned up the Welcome page to integrate the OAuthLoginSection directly, improving user experience. * chore: update subproject commit reference in skills directory * chore: update test configurations and improve test assertions - Modified test scripts in package.json to use a specific Vitest configuration file for consistency. - Updated assertions in loader tests to ensure loading durations are non-negative. - Enhanced tool loading tests to clarify expected behavior regarding localStorage and cache management. - Adjusted agent tool registry tests to improve error handling and ensure accurate statistics. - Refined device detection tests to reflect updated fallback URLs. * fix: enhance parameter formatting and remove unused components - Updated the `formatParameters` function to handle cases where schema properties are empty, returning a more informative response. - Deleted the `DownloadScreen` component and associated device detection utilities to streamline the codebase and remove unused functionality. - Adjusted tests to reflect changes in the tool loading and agent tool registry, ensuring accuracy in assertions. * chore: simplify Vitest configuration by removing unused include patterns - Updated the Vitest configuration to remove unnecessary test file patterns, streamlining the test setup for better clarity and maintainability. * refactor: update paths and comments for AI configuration and file watching - Modified Vite configuration to ignore only the `src-tauri` directory. - Updated logging messages to reflect the correct path for writing AI configuration files. - Adjusted fetch calls in the file watcher to use the new path for `TOOLS.md`. - Revised comments and logic in Rust code to clarify the handling of AI configuration file paths, including legacy fallback options. * chore: remove unused updater secrets from GitHub Actions workflow - Deleted UPDATER_GIST_URL and UPDATER_GIST_ID environment variables from the package-and-publish workflow, streamlining the configuration. * chore: comment out Vitest thresholds for clarity - Commented out the thresholds section in the Vitest configuration to improve clarity and maintainability, as it is currently not in use. * ran formatter * chore: update updater public key in tauri configuration - Replaced the existing public key in the updater plugin configuration with a new value to ensure proper functionality and security. * chore: update ESLint configuration and refactor components - Added `localStorage` and `sessionStorage` as readonly globals in ESLint configuration for better linting support. - Removed unused imports from `SkillsPanel.tsx` to clean up the code. - Changed the type of `watcherInterval` in `file-watcher.ts` for improved type safety. - Refactored toast management logic in `Intelligence.tsx` to enhance clarity and maintainability. - Simplified import statements in `IntelligenceProvider.tsx` for consistency. - Streamlined object property shorthand in `agentToolRegistry.ts` for cleaner code. * refactor: improve error handling and type safety in Intelligence component - Enhanced toast notification logic to defer state updates, preventing potential issues with setState in effects. - Updated the source filter dispatch to use a more specific type for improved type safety. * refactor: enhance type safety across various components and services - Updated type definitions from `any` to `unknown` in multiple files to improve type safety and prevent potential runtime errors. - Refactored state management in `TauriCommandsPanel` to use more specific types. - Adjusted context and parameters in several interfaces to ensure consistent typing. - Added ESLint directive to `polyfills.ts` for intentional global assignments. - Streamlined type handling in utility functions and API responses for better clarity and maintainability. * refactor: streamline import statements and improve code clarity - Consolidated import statements in `agentToolRegistry.ts` and `intelligenceSlice.ts` for better readability. - Simplified the `createTestStore` function in `test-utils.tsx` to enhance code conciseness. - Cleaned up the `isExecutionStepProgressEvent` function in `intelligence-chat-api.ts` for improved clarity and maintainability.
729 lines
23 KiB
Rust
729 lines
23 KiB
Rust
use anyhow::Result;
|
|
use axum::extract::State;
|
|
use axum::http::StatusCode;
|
|
use axum::response::{IntoResponse, Response};
|
|
use axum::routing::{get, post};
|
|
use axum::{Json, Router};
|
|
use serde::de::DeserializeOwned;
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::json;
|
|
|
|
use crate::openhuman::config::Config;
|
|
use crate::openhuman::health;
|
|
use crate::openhuman::security::{SecretStore, SecurityPolicy};
|
|
use crate::openhuman::{doctor, hardware, integrations, migration, onboard, service};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CommandResponse<T> {
|
|
pub result: T,
|
|
pub logs: Vec<String>,
|
|
}
|
|
|
|
fn command_response<T>(result: T, logs: Vec<String>) -> CommandResponse<T> {
|
|
CommandResponse { result, logs }
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct RpcRequest {
|
|
#[allow(dead_code)]
|
|
jsonrpc: String,
|
|
id: serde_json::Value,
|
|
method: String,
|
|
#[serde(default)]
|
|
params: serde_json::Value,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
struct RpcSuccess {
|
|
jsonrpc: &'static str,
|
|
id: serde_json::Value,
|
|
result: serde_json::Value,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
struct RpcFailure {
|
|
jsonrpc: &'static str,
|
|
id: serde_json::Value,
|
|
error: RpcError,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
struct RpcError {
|
|
code: i64,
|
|
message: String,
|
|
data: Option<serde_json::Value>,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct AppState {
|
|
core_version: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ConfigSnapshot {
|
|
pub config: serde_json::Value,
|
|
pub workspace_dir: String,
|
|
pub config_path: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ModelSettingsUpdate {
|
|
pub api_key: Option<String>,
|
|
pub api_url: Option<String>,
|
|
pub default_provider: Option<String>,
|
|
pub default_model: Option<String>,
|
|
pub default_temperature: Option<f64>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct MemorySettingsUpdate {
|
|
pub backend: Option<String>,
|
|
pub auto_save: Option<bool>,
|
|
pub embedding_provider: Option<String>,
|
|
pub embedding_model: Option<String>,
|
|
pub embedding_dimensions: Option<usize>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct GatewaySettingsUpdate {
|
|
pub host: Option<String>,
|
|
pub port: Option<u16>,
|
|
pub require_pairing: Option<bool>,
|
|
pub allow_public_bind: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct RuntimeSettingsUpdate {
|
|
pub kind: Option<String>,
|
|
pub reasoning_enabled: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct BrowserSettingsUpdate {
|
|
pub enabled: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct RuntimeFlags {
|
|
pub browser_allow_all: bool,
|
|
pub log_prompts: bool,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct AgentChatParams {
|
|
message: String,
|
|
provider_override: Option<String>,
|
|
model_override: Option<String>,
|
|
temperature: Option<f64>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct DoctorModelsParams {
|
|
provider_override: Option<String>,
|
|
use_cache: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct IntegrationInfoParams {
|
|
name: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct ModelsRefreshParams {
|
|
provider_override: Option<String>,
|
|
force: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct MigrateOpenClawParams {
|
|
source_workspace: Option<String>,
|
|
dry_run: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct HardwareIntrospectParams {
|
|
path: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct EncryptSecretParams {
|
|
plaintext: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct DecryptSecretParams {
|
|
ciphertext: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct SetBrowserAllowAllParams {
|
|
enabled: bool,
|
|
}
|
|
|
|
async fn load_openhuman_config() -> Result<Config, String> {
|
|
let timeout_duration = std::time::Duration::from_secs(30);
|
|
match tokio::time::timeout(timeout_duration, Config::load_or_init()).await {
|
|
Ok(Ok(config)) => Ok(config),
|
|
Ok(Err(e)) => Err(e.to_string()),
|
|
Err(_) => Err("Config loading timed out".to_string()),
|
|
}
|
|
}
|
|
|
|
fn snapshot_config(config: &Config) -> Result<ConfigSnapshot, String> {
|
|
let value = serde_json::to_value(config).map_err(|e| e.to_string())?;
|
|
Ok(ConfigSnapshot {
|
|
config: value,
|
|
workspace_dir: config.workspace_dir.display().to_string(),
|
|
config_path: config.config_path.display().to_string(),
|
|
})
|
|
}
|
|
|
|
fn env_flag_enabled(key: &str) -> bool {
|
|
matches!(
|
|
std::env::var(key).ok().as_deref(),
|
|
Some("1") | Some("true") | Some("TRUE") | Some("yes") | Some("YES")
|
|
)
|
|
}
|
|
|
|
fn secret_store_for_config(config: &Config) -> SecretStore {
|
|
let data_dir = config
|
|
.config_path
|
|
.parent()
|
|
.map_or_else(|| std::path::PathBuf::from("."), std::path::PathBuf::from);
|
|
SecretStore::new(&data_dir, true)
|
|
}
|
|
|
|
fn parse_params<T: DeserializeOwned>(params: serde_json::Value) -> Result<T, String> {
|
|
serde_json::from_value(params).map_err(|e| format!("invalid params: {e}"))
|
|
}
|
|
|
|
fn rpc_error_response(id: serde_json::Value, code: i64, message: String) -> Response {
|
|
(
|
|
StatusCode::OK,
|
|
Json(RpcFailure {
|
|
jsonrpc: "2.0",
|
|
id,
|
|
error: RpcError {
|
|
code,
|
|
message,
|
|
data: None,
|
|
},
|
|
}),
|
|
)
|
|
.into_response()
|
|
}
|
|
|
|
fn to_rpc_success(id: serde_json::Value, value: serde_json::Value) -> Response {
|
|
(
|
|
StatusCode::OK,
|
|
Json(RpcSuccess {
|
|
jsonrpc: "2.0",
|
|
id,
|
|
result: value,
|
|
}),
|
|
)
|
|
.into_response()
|
|
}
|
|
|
|
fn to_json_value<T: Serialize>(value: T) -> Result<serde_json::Value, String> {
|
|
serde_json::to_value(value).map_err(|e| e.to_string())
|
|
}
|
|
|
|
async fn rpc_handler(State(state): State<AppState>, Json(req): Json<RpcRequest>) -> Response {
|
|
let id = req.id.clone();
|
|
|
|
let result = dispatch(state, req.method.as_str(), req.params).await;
|
|
|
|
match result {
|
|
Ok(value) => to_rpc_success(id, value),
|
|
Err(message) => rpc_error_response(id, -32000, message),
|
|
}
|
|
}
|
|
|
|
async fn dispatch(
|
|
state: AppState,
|
|
method: &str,
|
|
params: serde_json::Value,
|
|
) -> Result<serde_json::Value, String> {
|
|
match method {
|
|
"core.ping" => to_json_value(json!({ "ok": true })),
|
|
"core.version" => to_json_value(json!({ "version": state.core_version })),
|
|
|
|
"openhuman.health_snapshot" => to_json_value(command_response(
|
|
health::snapshot_json(),
|
|
vec!["health_snapshot requested".to_string()],
|
|
)),
|
|
|
|
"openhuman.security_policy_info" => {
|
|
let policy = SecurityPolicy::default();
|
|
let payload = json!({
|
|
"autonomy": policy.autonomy,
|
|
"workspace_only": policy.workspace_only,
|
|
"allowed_commands": policy.allowed_commands,
|
|
"max_actions_per_hour": policy.max_actions_per_hour,
|
|
"require_approval_for_medium_risk": policy.require_approval_for_medium_risk,
|
|
"block_high_risk_commands": policy.block_high_risk_commands,
|
|
});
|
|
to_json_value(command_response(
|
|
payload,
|
|
vec!["security_policy_info computed".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.get_config" => {
|
|
let config = load_openhuman_config().await?;
|
|
let snapshot = snapshot_config(&config)?;
|
|
to_json_value(command_response(
|
|
snapshot,
|
|
vec![format!(
|
|
"config loaded from {}",
|
|
config.config_path.display()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"openhuman.update_model_settings" => {
|
|
let update: ModelSettingsUpdate = parse_params(params)?;
|
|
let mut config = load_openhuman_config().await?;
|
|
if let Some(api_key) = update.api_key {
|
|
config.api_key = if api_key.trim().is_empty() {
|
|
None
|
|
} else {
|
|
Some(api_key)
|
|
};
|
|
}
|
|
if let Some(api_url) = update.api_url {
|
|
config.api_url = if api_url.trim().is_empty() {
|
|
None
|
|
} else {
|
|
Some(api_url)
|
|
};
|
|
}
|
|
if let Some(provider) = update.default_provider {
|
|
config.default_provider = if provider.trim().is_empty() {
|
|
None
|
|
} else {
|
|
Some(provider)
|
|
};
|
|
}
|
|
if let Some(model) = update.default_model {
|
|
config.default_model = if model.trim().is_empty() {
|
|
None
|
|
} else {
|
|
Some(model)
|
|
};
|
|
}
|
|
if let Some(temp) = update.default_temperature {
|
|
config.default_temperature = temp;
|
|
}
|
|
config.save().await.map_err(|e| e.to_string())?;
|
|
let snapshot = snapshot_config(&config)?;
|
|
to_json_value(command_response(
|
|
snapshot,
|
|
vec![format!(
|
|
"model settings saved to {}",
|
|
config.config_path.display()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"openhuman.update_memory_settings" => {
|
|
let update: MemorySettingsUpdate = parse_params(params)?;
|
|
let mut config = load_openhuman_config().await?;
|
|
if let Some(backend) = update.backend {
|
|
config.memory.backend = backend;
|
|
}
|
|
if let Some(auto_save) = update.auto_save {
|
|
config.memory.auto_save = auto_save;
|
|
}
|
|
if let Some(provider) = update.embedding_provider {
|
|
config.memory.embedding_provider = provider;
|
|
}
|
|
if let Some(model) = update.embedding_model {
|
|
config.memory.embedding_model = model;
|
|
}
|
|
if let Some(dimensions) = update.embedding_dimensions {
|
|
config.memory.embedding_dimensions = dimensions;
|
|
}
|
|
config.save().await.map_err(|e| e.to_string())?;
|
|
let snapshot = snapshot_config(&config)?;
|
|
to_json_value(command_response(
|
|
snapshot,
|
|
vec![format!(
|
|
"memory settings saved to {}",
|
|
config.config_path.display()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"openhuman.update_gateway_settings" => {
|
|
let update: GatewaySettingsUpdate = parse_params(params)?;
|
|
let mut config = load_openhuman_config().await?;
|
|
if let Some(host) = update.host {
|
|
config.gateway.host = host;
|
|
}
|
|
if let Some(port) = update.port {
|
|
config.gateway.port = port;
|
|
}
|
|
if let Some(require_pairing) = update.require_pairing {
|
|
config.gateway.require_pairing = require_pairing;
|
|
}
|
|
if let Some(allow_public_bind) = update.allow_public_bind {
|
|
config.gateway.allow_public_bind = allow_public_bind;
|
|
}
|
|
config.save().await.map_err(|e| e.to_string())?;
|
|
let snapshot = snapshot_config(&config)?;
|
|
to_json_value(command_response(
|
|
snapshot,
|
|
vec![format!(
|
|
"gateway settings saved to {}",
|
|
config.config_path.display()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"openhuman.update_tunnel_settings" => {
|
|
let tunnel: crate::openhuman::config::TunnelConfig = parse_params(params)?;
|
|
let mut config = load_openhuman_config().await?;
|
|
config.tunnel = tunnel;
|
|
config.save().await.map_err(|e| e.to_string())?;
|
|
let snapshot = snapshot_config(&config)?;
|
|
to_json_value(command_response(
|
|
snapshot,
|
|
vec![format!(
|
|
"tunnel settings saved to {}",
|
|
config.config_path.display()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"openhuman.update_runtime_settings" => {
|
|
let update: RuntimeSettingsUpdate = parse_params(params)?;
|
|
let mut config = load_openhuman_config().await?;
|
|
if let Some(kind) = update.kind {
|
|
config.runtime.kind = kind;
|
|
}
|
|
if let Some(reasoning_enabled) = update.reasoning_enabled {
|
|
config.runtime.reasoning_enabled = Some(reasoning_enabled);
|
|
}
|
|
config.save().await.map_err(|e| e.to_string())?;
|
|
let snapshot = snapshot_config(&config)?;
|
|
to_json_value(command_response(
|
|
snapshot,
|
|
vec![format!(
|
|
"runtime settings saved to {}",
|
|
config.config_path.display()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"openhuman.update_browser_settings" => {
|
|
let update: BrowserSettingsUpdate = parse_params(params)?;
|
|
let mut config = load_openhuman_config().await?;
|
|
if let Some(enabled) = update.enabled {
|
|
config.browser.enabled = enabled;
|
|
}
|
|
config.save().await.map_err(|e| e.to_string())?;
|
|
let snapshot = snapshot_config(&config)?;
|
|
to_json_value(command_response(
|
|
snapshot,
|
|
vec![format!(
|
|
"browser settings saved to {}",
|
|
config.config_path.display()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"openhuman.get_runtime_flags" => {
|
|
let flags = RuntimeFlags {
|
|
browser_allow_all: env_flag_enabled("OPENHUMAN_BROWSER_ALLOW_ALL"),
|
|
log_prompts: env_flag_enabled("OPENHUMAN_LOG_PROMPTS"),
|
|
};
|
|
to_json_value(command_response(
|
|
flags,
|
|
vec!["runtime flags read".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.set_browser_allow_all" => {
|
|
let p: SetBrowserAllowAllParams = parse_params(params)?;
|
|
if p.enabled {
|
|
std::env::set_var("OPENHUMAN_BROWSER_ALLOW_ALL", "1");
|
|
} else {
|
|
std::env::remove_var("OPENHUMAN_BROWSER_ALLOW_ALL");
|
|
}
|
|
let flags = RuntimeFlags {
|
|
browser_allow_all: env_flag_enabled("OPENHUMAN_BROWSER_ALLOW_ALL"),
|
|
log_prompts: env_flag_enabled("OPENHUMAN_LOG_PROMPTS"),
|
|
};
|
|
to_json_value(command_response(
|
|
flags,
|
|
vec!["browser allow-all flag updated".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.agent_chat" => {
|
|
let p: AgentChatParams = parse_params(params)?;
|
|
let mut config = load_openhuman_config().await?;
|
|
if let Some(provider) = p.provider_override {
|
|
config.default_provider = Some(provider);
|
|
}
|
|
if let Some(model) = p.model_override {
|
|
config.default_model = Some(model);
|
|
}
|
|
if let Some(temp) = p.temperature {
|
|
config.default_temperature = temp;
|
|
}
|
|
let mut agent =
|
|
crate::openhuman::agent::Agent::from_config(&config).map_err(|e| e.to_string())?;
|
|
let response = agent
|
|
.run_single(&p.message)
|
|
.await
|
|
.map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
response,
|
|
vec!["agent chat completed".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.encrypt_secret" => {
|
|
let p: EncryptSecretParams = parse_params(params)?;
|
|
let config = load_openhuman_config().await?;
|
|
let store = secret_store_for_config(&config);
|
|
let ciphertext = store.encrypt(&p.plaintext).map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
ciphertext,
|
|
vec!["secret encrypted".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.decrypt_secret" => {
|
|
let p: DecryptSecretParams = parse_params(params)?;
|
|
let config = load_openhuman_config().await?;
|
|
let store = secret_store_for_config(&config);
|
|
let plaintext = store.decrypt(&p.ciphertext).map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
plaintext,
|
|
vec!["secret decrypted".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.doctor_report" => {
|
|
let config = load_openhuman_config().await?;
|
|
let report = doctor::run(&config).map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
report,
|
|
vec!["doctor report generated".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.doctor_models" => {
|
|
let p: DoctorModelsParams = parse_params(params)?;
|
|
let config = load_openhuman_config().await?;
|
|
let use_cache = p.use_cache.unwrap_or(true);
|
|
let report = doctor::run_models(&config, p.provider_override.as_deref(), use_cache)
|
|
.map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
report,
|
|
vec!["model probes completed".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.list_integrations" => {
|
|
let config = load_openhuman_config().await?;
|
|
to_json_value(command_response(
|
|
integrations::list_integrations(&config),
|
|
vec!["integrations listed".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.get_integration_info" => {
|
|
let p: IntegrationInfoParams = parse_params(params)?;
|
|
let config = load_openhuman_config().await?;
|
|
let info =
|
|
integrations::get_integration_info(&config, &p.name).map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
info,
|
|
vec![format!("integration loaded: {}", p.name)],
|
|
))
|
|
}
|
|
|
|
"openhuman.models_refresh" => {
|
|
let p: ModelsRefreshParams = parse_params(params)?;
|
|
let config = load_openhuman_config().await?;
|
|
let result = onboard::run_models_refresh(
|
|
&config,
|
|
p.provider_override.as_deref(),
|
|
p.force.unwrap_or(false),
|
|
)
|
|
.map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
result,
|
|
vec!["model refresh completed".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.migrate_openclaw" => {
|
|
let p: MigrateOpenClawParams = parse_params(params)?;
|
|
let config = load_openhuman_config().await?;
|
|
let source = p.source_workspace.map(std::path::PathBuf::from);
|
|
let report =
|
|
migration::migrate_openclaw_memory(&config, source, p.dry_run.unwrap_or(true))
|
|
.await
|
|
.map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
report,
|
|
vec!["migration completed".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.hardware_discover" => to_json_value(command_response(
|
|
hardware::discover_hardware(),
|
|
vec!["hardware discovery complete".to_string()],
|
|
)),
|
|
|
|
"openhuman.hardware_introspect" => {
|
|
let p: HardwareIntrospectParams = parse_params(params)?;
|
|
let info = hardware::introspect_device(&p.path).map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
info,
|
|
vec![format!("introspected {}", p.path)],
|
|
))
|
|
}
|
|
|
|
"openhuman.service_install" => {
|
|
let config = load_openhuman_config().await?;
|
|
let status = service::install(&config).map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
status,
|
|
vec!["service install completed".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.service_start" => {
|
|
let config = load_openhuman_config().await?;
|
|
let status = service::start(&config).map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
status,
|
|
vec!["service start completed".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.service_stop" => {
|
|
let config = load_openhuman_config().await?;
|
|
let status = service::stop(&config).map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
status,
|
|
vec!["service stop completed".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.service_status" => {
|
|
let config = load_openhuman_config().await?;
|
|
let status = service::status(&config).map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
status,
|
|
vec!["service status fetched".to_string()],
|
|
))
|
|
}
|
|
|
|
"openhuman.service_uninstall" => {
|
|
let config = load_openhuman_config().await?;
|
|
let status = service::uninstall(&config).map_err(|e| e.to_string())?;
|
|
to_json_value(command_response(
|
|
status,
|
|
vec!["service uninstall completed".to_string()],
|
|
))
|
|
}
|
|
|
|
_ => Err(format!("unknown method: {method}")),
|
|
}
|
|
}
|
|
|
|
async fn health_handler() -> impl IntoResponse {
|
|
(StatusCode::OK, Json(json!({ "ok": true })))
|
|
}
|
|
|
|
async fn root_handler() -> impl IntoResponse {
|
|
(
|
|
StatusCode::OK,
|
|
Json(json!({
|
|
"name": "openhuman-core",
|
|
"ok": true,
|
|
"endpoints": {
|
|
"health": "/health",
|
|
"rpc": "/rpc"
|
|
},
|
|
"usage": {
|
|
"jsonrpc": {
|
|
"version": "2.0",
|
|
"method": "core.ping",
|
|
"params": {}
|
|
}
|
|
}
|
|
})),
|
|
)
|
|
}
|
|
|
|
async fn not_found_handler() -> impl IntoResponse {
|
|
(
|
|
StatusCode::NOT_FOUND,
|
|
Json(json!({
|
|
"ok": false,
|
|
"error": "not_found",
|
|
"message": "Route not found. Try /, /health, or /rpc."
|
|
})),
|
|
)
|
|
}
|
|
|
|
fn core_port() -> u16 {
|
|
std::env::var("OPENHUMAN_CORE_PORT")
|
|
.ok()
|
|
.and_then(|v| v.parse::<u16>().ok())
|
|
.unwrap_or(7788)
|
|
}
|
|
|
|
pub async fn run_server(port: Option<u16>) -> Result<()> {
|
|
let port = port.unwrap_or_else(core_port);
|
|
let bind_addr = format!("127.0.0.1:{port}");
|
|
let listener = tokio::net::TcpListener::bind(&bind_addr).await?;
|
|
|
|
let app = Router::new()
|
|
.route("/", get(root_handler))
|
|
.route("/health", get(health_handler))
|
|
.route("/rpc", post(rpc_handler))
|
|
.fallback(not_found_handler)
|
|
.with_state(AppState {
|
|
core_version: env!("CARGO_PKG_VERSION").to_string(),
|
|
});
|
|
|
|
log::info!("[core] listening on http://{bind_addr}");
|
|
axum::serve(listener, app).await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn run_from_cli_args(args: &[String]) -> Result<()> {
|
|
let mut port: Option<u16> = None;
|
|
|
|
let mut idx = 0;
|
|
while idx < args.len() {
|
|
match args[idx].as_str() {
|
|
"serve" => {}
|
|
"--port" => {
|
|
let value = args
|
|
.get(idx + 1)
|
|
.ok_or_else(|| anyhow::anyhow!("missing value for --port"))?;
|
|
port = Some(value.parse::<u16>()?);
|
|
idx += 1;
|
|
}
|
|
_ => {}
|
|
}
|
|
idx += 1;
|
|
}
|
|
|
|
let runtime = tokio::runtime::Builder::new_multi_thread()
|
|
.enable_all()
|
|
.build()?;
|
|
runtime.block_on(run_server(port))
|
|
}
|