mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-30 23:14:37 +00:00
* Update Welcome page to integrate OAuth provider and adjust branding - Added OAuthProviderButton to the Welcome component for Google authentication. - Updated the branding from AlphaHuman to OpenHuman in the FeaturesStep component. - Removed unnecessary Lottie animation from the Onboarding page for a cleaner layout. - Changed the backend URL to point to the new API endpoint for TinyHumans. * Refactor routing and sidebar components for improved clarity - Commented out the Mnemonic route and related logic in AppRoutes for future consideration. - Updated MiniSidebar to comment out the Invite Friends section. - Removed unused navigation logic and upgrade call-to-action from the Home page. - Adjusted Onboarding navigation to redirect to Home instead of Mnemonic. * Comment out unused settings menu items for future consideration in SettingsHome component * Add core process and RPC functionality for Alphahuman - Introduced new binaries: `alphahuman-core` and `alphahuman-cli` for core process management and command-line interaction. - Implemented `CoreProcessHandle` for managing the lifecycle of the core process. - Added `core_rpc` module for handling RPC requests and responses. - Created `core_server` module to manage core server logic and routing. - Updated `alphahuman` commands to utilize the new RPC structure for health checks, security policies, and configuration management. - Refactored existing code to streamline interactions with the core process and improve overall architecture. * Update dependencies and enhance CLI functionality - Added `clap` for command-line argument parsing in the `alphahuman-cli`. - Updated `Cargo.lock` and `Cargo.toml` to include `clap` and its features. - Refactored `alphahuman-cli` to utilize structured command handling with subcommands. - Improved core process management and logging in `core_process.rs`. - Enhanced routing and error handling in `core_server.rs` with new root and not found handlers. - Updated various commands in `alphahuman.rs` to ensure core process is running before executing RPC calls. * Enhance service management and CLI functionality - Added new functions for daemon program arguments and command line construction in the service module. - Updated macOS and Linux installation functions to dynamically generate program arguments and command lines. - Introduced a new `Reinstall` command in the CLI for easier service management. - Refactored service command handling to utilize local service functions for improved clarity and maintainability. * Refactor daemon references to agent in components and hooks - Updated terminology from "Daemon" to "Agent" in MiniSidebar, DaemonHealthPanel, and TauriCommandsPanel for consistency. - Modified useDaemonHealth hook to probe agent status and handle agent lifecycle management. - Introduced new agent server status interface and function in Tauri commands for improved status checking. - Adjusted related comments and error handling to reflect the changes in terminology. * Refactor Home component and enhance macOS service management - Updated Home component to navigate to in-app conversations instead of opening a Telegram bot link. - Improved macOS service management by implementing modern lifecycle commands and adding compatibility fallbacks for service control. - Introduced new utility functions for handling macOS GUI domain and service targets. * Refactor Home component to enhance navigation - Removed SkillsGrid component and replaced it with a button that navigates to the Skills page. - Improved layout with additional margin for better spacing in the Home component.
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::alphahuman::config::Config;
|
|
use crate::alphahuman::health;
|
|
use crate::alphahuman::security::{SecretStore, SecurityPolicy};
|
|
use crate::alphahuman::{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_alphahuman_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 })),
|
|
|
|
"alphahuman.health_snapshot" => to_json_value(command_response(
|
|
health::snapshot_json(),
|
|
vec!["health_snapshot requested".to_string()],
|
|
)),
|
|
|
|
"alphahuman.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()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.get_config" => {
|
|
let config = load_alphahuman_config().await?;
|
|
let snapshot = snapshot_config(&config)?;
|
|
to_json_value(command_response(
|
|
snapshot,
|
|
vec![format!(
|
|
"config loaded from {}",
|
|
config.config_path.display()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"alphahuman.update_model_settings" => {
|
|
let update: ModelSettingsUpdate = parse_params(params)?;
|
|
let mut config = load_alphahuman_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()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"alphahuman.update_memory_settings" => {
|
|
let update: MemorySettingsUpdate = parse_params(params)?;
|
|
let mut config = load_alphahuman_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()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"alphahuman.update_gateway_settings" => {
|
|
let update: GatewaySettingsUpdate = parse_params(params)?;
|
|
let mut config = load_alphahuman_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()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"alphahuman.update_tunnel_settings" => {
|
|
let tunnel: crate::alphahuman::config::TunnelConfig = parse_params(params)?;
|
|
let mut config = load_alphahuman_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()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"alphahuman.update_runtime_settings" => {
|
|
let update: RuntimeSettingsUpdate = parse_params(params)?;
|
|
let mut config = load_alphahuman_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()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"alphahuman.update_browser_settings" => {
|
|
let update: BrowserSettingsUpdate = parse_params(params)?;
|
|
let mut config = load_alphahuman_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()
|
|
)],
|
|
))
|
|
}
|
|
|
|
"alphahuman.get_runtime_flags" => {
|
|
let flags = RuntimeFlags {
|
|
browser_allow_all: env_flag_enabled("ALPHAHUMAN_BROWSER_ALLOW_ALL"),
|
|
log_prompts: env_flag_enabled("ALPHAHUMAN_LOG_PROMPTS"),
|
|
};
|
|
to_json_value(command_response(
|
|
flags,
|
|
vec!["runtime flags read".to_string()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.set_browser_allow_all" => {
|
|
let p: SetBrowserAllowAllParams = parse_params(params)?;
|
|
if p.enabled {
|
|
std::env::set_var("ALPHAHUMAN_BROWSER_ALLOW_ALL", "1");
|
|
} else {
|
|
std::env::remove_var("ALPHAHUMAN_BROWSER_ALLOW_ALL");
|
|
}
|
|
let flags = RuntimeFlags {
|
|
browser_allow_all: env_flag_enabled("ALPHAHUMAN_BROWSER_ALLOW_ALL"),
|
|
log_prompts: env_flag_enabled("ALPHAHUMAN_LOG_PROMPTS"),
|
|
};
|
|
to_json_value(command_response(
|
|
flags,
|
|
vec!["browser allow-all flag updated".to_string()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.agent_chat" => {
|
|
let p: AgentChatParams = parse_params(params)?;
|
|
let mut config = load_alphahuman_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::alphahuman::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()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.encrypt_secret" => {
|
|
let p: EncryptSecretParams = parse_params(params)?;
|
|
let config = load_alphahuman_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()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.decrypt_secret" => {
|
|
let p: DecryptSecretParams = parse_params(params)?;
|
|
let config = load_alphahuman_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()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.doctor_report" => {
|
|
let config = load_alphahuman_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()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.doctor_models" => {
|
|
let p: DoctorModelsParams = parse_params(params)?;
|
|
let config = load_alphahuman_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()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.list_integrations" => {
|
|
let config = load_alphahuman_config().await?;
|
|
to_json_value(command_response(
|
|
integrations::list_integrations(&config),
|
|
vec!["integrations listed".to_string()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.get_integration_info" => {
|
|
let p: IntegrationInfoParams = parse_params(params)?;
|
|
let config = load_alphahuman_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)],
|
|
))
|
|
}
|
|
|
|
"alphahuman.models_refresh" => {
|
|
let p: ModelsRefreshParams = parse_params(params)?;
|
|
let config = load_alphahuman_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()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.migrate_openclaw" => {
|
|
let p: MigrateOpenClawParams = parse_params(params)?;
|
|
let config = load_alphahuman_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()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.hardware_discover" => to_json_value(command_response(
|
|
hardware::discover_hardware(),
|
|
vec!["hardware discovery complete".to_string()],
|
|
)),
|
|
|
|
"alphahuman.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)],
|
|
))
|
|
}
|
|
|
|
"alphahuman.service_install" => {
|
|
let config = load_alphahuman_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()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.service_start" => {
|
|
let config = load_alphahuman_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()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.service_stop" => {
|
|
let config = load_alphahuman_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()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.service_status" => {
|
|
let config = load_alphahuman_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()],
|
|
))
|
|
}
|
|
|
|
"alphahuman.service_uninstall" => {
|
|
let config = load_alphahuman_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": "alphahuman-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("ALPHAHUMAN_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))
|
|
}
|