diff --git a/Cargo.lock b/Cargo.lock index 14bb9a1f0..6029a1472 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5120,7 +5120,7 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openhuman" -version = "0.51.19" +version = "0.52.0" dependencies = [ "aes-gcm", "anyhow", diff --git a/app/package.json b/app/package.json index 988e4ea33..b89ad3c1c 100644 --- a/app/package.json +++ b/app/package.json @@ -43,7 +43,8 @@ "format": "prettier --write . && yarn rust:format", "format:check": "prettier --check . && yarn rust:format:check", "lint": "eslint . --ext .ts,.tsx", - "lint:fix": "eslint . --ext .ts,.tsx --fix" + "lint:fix": "eslint . --ext .ts,.tsx --fix", + "knip": "knip" }, "dependencies": { "@heroicons/react": "^2.2.0", @@ -105,6 +106,7 @@ "eslint-plugin-react-hooks": "^7.0.1", "husky": "^9.1.7", "jsdom": "^28.0.0", + "knip": "^6.3.1", "postcss": "^8.5.6", "prettier": "^3.8.1", "tailwindcss": "^3.4.19", diff --git a/src/api/rest.rs b/src/api/rest.rs index fcafdc61e..62539446e 100644 --- a/src/api/rest.rs +++ b/src/api/rest.rs @@ -64,10 +64,10 @@ fn user_id_from_object(obj: &serde_json::Map) -> Option { None } -/// Best-effort user id from an authenticated profile payload. +/// Best-effort extraction of a user ID from an authenticated profile payload. /// -/// Accepts a raw user object or an envelope that nests the user under `data` -/// or `user`. +/// This function handles various envelope formats, including raw user objects +/// or those nested under `data` or `user` keys. pub fn user_id_from_profile_payload(payload: &Value) -> Option { let obj = payload.as_object()?; if let Some(data) = obj.get("data").and_then(|v| v.as_object()) { @@ -85,14 +85,17 @@ pub fn user_id_from_profile_payload(payload: &Value) -> Option { }) } +/// Alias for [`user_id_from_profile_payload`] for semantic clarity in auth flows. pub fn user_id_from_auth_me_payload(payload: &Value) -> Option { user_id_from_profile_payload(payload) } -/// JSON body returned by the backend after OAuth connect starts. +/// JSON body returned by the backend when an OAuth connection process is initiated. #[derive(Debug, Clone, Deserialize)] pub struct ConnectResponse { + /// The URL to redirect the user to for OAuth authorization. pub oauth_url: String, + /// The state parameter used to prevent CSRF and correlate the callback. pub state: String, } @@ -117,12 +120,15 @@ struct IntegrationsData { integrations: Vec, } -/// Integration row from `GET /auth/integrations` (no tokens). +/// A summary of an active integration, as returned by the backend. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct IntegrationSummary { + /// Unique identifier for the integration. pub id: String, + /// The name of the integration provider (e.g., "google", "slack"). pub provider: String, + /// RFC3339 timestamp of when the integration was created. pub created_at: String, } @@ -149,16 +155,20 @@ struct LoginTokenConsumeData { jwt_token: String, } -/// Decrypted OAuth token payload from `POST /auth/integrations/:id/tokens`. +/// Decrypted OAuth token payload for handing off tokens to a local service or skill. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct IntegrationTokensHandoff { + /// The OAuth access token. pub access_token: String, + /// The optional OAuth refresh token. #[serde(default)] pub refresh_token: Option, + /// RFC3339 timestamp of when the access token expires. pub expires_at: String, } +/// A client for interacting with the TinyHumans / AlphaHuman backend API. #[derive(Clone)] pub struct BackendOAuthClient { client: Client, @@ -166,13 +176,14 @@ pub struct BackendOAuthClient { } impl BackendOAuthClient { + /// Creates a new `BackendOAuthClient` with the given API base URL. pub fn new(api_base: &str) -> Result { let base = Url::parse(api_base.trim()).context("Invalid API base URL")?; let client = build_backend_reqwest_client()?; Ok(Self { client, base }) } - /// `GET /auth/{provider}/login` — open in browser; Origin/Referer must be allowlisted on the server. + /// Returns the URL for initiating a login flow for a specific provider. pub fn login_url(&self, provider: &str) -> Result { let p = provider.trim().trim_matches('/'); anyhow::ensure!(!p.is_empty(), "provider is required"); @@ -181,7 +192,7 @@ impl BackendOAuthClient { .context("build login URL") } - /// `GET /auth/{provider}/connect` with Bearer JWT. + /// Initiates an OAuth connection flow for the current user and a specific provider. pub async fn connect( &self, provider: &str, @@ -236,7 +247,7 @@ impl BackendOAuthClient { Ok(ConnectResponse { oauth_url, state }) } - /// `GET /auth/me` — current authenticated user profile for the Bearer session JWT. + /// Fetches the current authenticated user profile using the provided JWT. pub async fn fetch_current_user(&self, bearer_jwt: &str) -> Result { let url = self.base.join("auth/me").context("build /auth/me URL")?; let resp = self @@ -255,7 +266,7 @@ impl BackendOAuthClient { parse_api_response_json(&text) } - /// `POST /telegram/login-tokens/:token/consume` — exchange a one-time login token for a JWT. + /// Exchanges a one-time login token (e.g. from Telegram) for a long-lived JWT. pub async fn consume_login_token(&self, login_token: &str) -> Result { let token = login_token.trim(); anyhow::ensure!(!token.is_empty(), "login token is required"); @@ -295,13 +306,13 @@ impl BackendOAuthClient { Ok(jwt) } - /// Confirms the JWT is accepted by the API using `GET /auth/me`. + /// Validates that the provided session token is still active and accepted. pub async fn validate_session_token(&self, bearer_jwt: &str) -> Result<()> { let _ = self.fetch_current_user(bearer_jwt).await?; Ok(()) } - /// `POST /auth/channels/:channel/link-token` — create a short-lived channel link token. + /// Creates a short-lived link token for connecting a specific communication channel. pub async fn create_channel_link_token( &self, channel: &str, @@ -333,8 +344,7 @@ impl BackendOAuthClient { parse_api_response_json(&text) } - /// Generic authenticated JSON request helper for backend API routes that - /// follow the standard `{ success, data, message }` envelope. + /// Generic authenticated JSON request helper for backend API routes. pub async fn authed_json( &self, bearer_jwt: &str, @@ -374,7 +384,7 @@ impl BackendOAuthClient { parse_api_response_json(&text) } - /// `GET /auth/integrations` + /// Lists all active integrations for the current user. pub async fn list_integrations(&self, bearer_jwt: &str) -> Result> { let url = self .base @@ -401,7 +411,10 @@ impl BackendOAuthClient { Ok(env.data.integrations) } - /// `POST /auth/integrations/:id/tokens` — one-time handoff; decrypt with same key format as backend. + /// Fetches the decrypted OAuth tokens for a specific integration. + /// + /// This is a one-time handoff process. The encryption key must match the + /// one used by the backend to encrypt the tokens. pub async fn fetch_integration_tokens_handoff( &self, integration_id: &str, @@ -441,7 +454,10 @@ impl BackendOAuthClient { serde_json::from_str(&plaintext).context("parse decrypted token JSON") } - /// `POST /auth/integrations/:id/client-key` — one-time handoff of client key share (deleted from Redis after retrieval). + /// Fetches the client key share for a specific integration. + /// + /// This is a one-time handoff; the key is deleted from the backend's + /// temporary storage (Redis) after retrieval. pub async fn fetch_client_key(&self, integration_id: &str, bearer_jwt: &str) -> Result { let id = integration_id.trim(); anyhow::ensure!( @@ -487,7 +503,7 @@ impl BackendOAuthClient { Ok(client_key.to_string()) } - /// `POST /channels/:channel/messages` — Send a rich message to a channel. + /// Sends a message to a communication channel. pub async fn send_channel_message( &self, channel: &str, @@ -506,7 +522,7 @@ impl BackendOAuthClient { .await } - /// `POST /channels/:channel/reactions` — React to a message in a channel. + /// Sends a reaction (e.g. emoji) to a message in a channel. pub async fn send_channel_reaction( &self, channel: &str, @@ -525,7 +541,7 @@ impl BackendOAuthClient { .await } - /// `POST /agent-integrations/tenor/search` — Search for GIFs via Tenor. + /// Searches for GIFs using the Tenor integration. pub async fn search_tenor_gifs( &self, bearer_jwt: &str, @@ -547,7 +563,7 @@ impl BackendOAuthClient { .await } - /// `POST /channels/:channel/threads` — Create a thread in a channel. + /// Creates a new thread in a communication channel. pub async fn create_channel_thread( &self, channel: &str, @@ -568,7 +584,7 @@ impl BackendOAuthClient { .await } - /// `PATCH /channels/:channel/threads/:thread_id` — Close or reopen a thread. + /// Updates an existing thread (e.g., closing or reopening it). pub async fn update_channel_thread( &self, channel: &str, @@ -595,7 +611,7 @@ impl BackendOAuthClient { .await } - /// `GET /channels/:channel/threads` — List threads, optionally filtered by active status. + /// Lists threads in a communication channel, optionally filtering by status. pub async fn list_channel_threads( &self, channel: &str, @@ -616,7 +632,7 @@ impl BackendOAuthClient { self.authed_json(bearer_jwt, Method::GET, &path, None).await } - /// `DELETE /auth/integrations/:id` + /// Revokes (deletes) an active integration. pub async fn revoke_integration(&self, integration_id: &str, bearer_jwt: &str) -> Result<()> { let id = integration_id.trim(); anyhow::ensure!(!id.is_empty(), "integration id is required"); diff --git a/src/core/all.rs b/src/core/all.rs index 1846a0f4a..1d525612f 100644 --- a/src/core/all.rs +++ b/src/core/all.rs @@ -1,3 +1,9 @@ +//! Registry and dispatch logic for all OpenHuman controllers. +//! +//! This module serves as the central hub for registering domain-specific +//! controllers (e.g., memory, skills, config) and providing a unified +//! interface for both the CLI and RPC layers to invoke them. + use std::future::Future; use std::pin::Pin; use std::sync::OnceLock; @@ -6,23 +12,37 @@ use serde_json::{Map, Value}; use crate::core::ControllerSchema; +/// A pinned, boxed future returned by a controller handler. pub type ControllerFuture = Pin> + Send + 'static>>; + +/// A function pointer type for controller handlers. +/// +/// Handlers take a map of parameters and return a [`ControllerFuture`]. pub type ControllerHandler = fn(Map) -> ControllerFuture; +/// A registered controller combining its schema and handler function. #[derive(Clone)] pub struct RegisteredController { + /// The schema defining the controller's identity and parameters. pub schema: ControllerSchema, + /// The actual function that executes the controller's logic. pub handler: ControllerHandler, } impl RegisteredController { + /// Returns the canonical RPC method name for this controller (e.g., `openhuman.memory_doc_put`). pub fn rpc_method_name(&self) -> String { rpc_method_name(&self.schema) } } +/// The global static registry of all controllers, initialized once on first access. static REGISTRY: OnceLock> = OnceLock::new(); +/// Returns a reference to the global controller registry. +/// +/// This function initializes the registry if it hasn't been already, +/// performing validation to ensure no duplicates or missing handlers exist. fn registry() -> &'static [RegisteredController] { REGISTRY .get_or_init(|| { @@ -36,6 +56,7 @@ fn registry() -> &'static [RegisteredController] { .as_slice() } +/// Aggregates all controller implementations from across the codebase. fn build_registered_controllers() -> Vec { let mut controllers = Vec::new(); controllers.extend(crate::openhuman::about_app::all_about_app_registered_controllers()); @@ -79,6 +100,7 @@ fn build_registered_controllers() -> Vec { controllers } +/// Aggregates all controller schemas from across the codebase. fn build_declared_controller_schemas() -> Vec { let mut schemas = Vec::new(); schemas.extend(crate::openhuman::about_app::all_about_app_controller_schemas()); @@ -119,19 +141,25 @@ fn build_declared_controller_schemas() -> Vec { schemas } +/// Returns a vector of all currently registered controllers. pub fn all_registered_controllers() -> Vec { registry().to_vec() } +/// Returns a vector of all currently declared controller schemas. pub fn all_controller_schemas() -> Vec { let _ = registry(); build_declared_controller_schemas() } +/// Generates a standardized RPC method name from a controller schema. pub fn rpc_method_name(schema: &ControllerSchema) -> String { format!("openhuman.{}_{}", schema.namespace, schema.function) } +/// Returns a human-readable description for a given namespace. +/// +/// This is used for CLI help output. pub fn namespace_description(namespace: &str) -> Option<&'static str> { match namespace { "about_app" => Some("Catalog the app's user-facing capabilities and where to find them."), @@ -171,6 +199,7 @@ pub fn namespace_description(namespace: &str) -> Option<&'static str> { } } +/// Looks up an RPC method name based on namespace and function. pub fn rpc_method_from_parts(namespace: &str, function: &str) -> Option { registry() .iter() @@ -178,6 +207,7 @@ pub fn rpc_method_from_parts(namespace: &str, function: &str) -> Option .map(|r| r.rpc_method_name()) } +/// Retrieves the schema for a specific RPC method. pub fn schema_for_rpc_method(method: &str) -> Option { registry() .iter() @@ -185,6 +215,11 @@ pub fn schema_for_rpc_method(method: &str) -> Option { .map(|r| r.schema.clone()) } +/// Validates that the provided parameters match the requirements of the controller schema. +/// +/// # Errors +/// +/// Returns an error message if required parameters are missing or if unknown parameters are provided. pub fn validate_params( schema: &ControllerSchema, params: &Map, @@ -210,6 +245,9 @@ pub fn validate_params( Ok(()) } +/// Attempts to invoke a registered RPC method by name. +/// +/// Returns `None` if the method is not found in the registry. pub async fn try_invoke_registered_rpc( method: &str, params: Map, @@ -222,6 +260,14 @@ pub async fn try_invoke_registered_rpc( None } +/// Validates the consistency of the controller registry. +/// +/// Ensures that: +/// - There are no duplicate controllers or RPC methods. +/// - Every declared schema has a registered handler. +/// - Every registered handler has a declared schema. +/// - Namespaces and functions are not empty. +/// - Required input names are unique within a controller. fn validate_registry( registered: &[RegisteredController], declared: &[ControllerSchema], diff --git a/src/core/dispatch.rs b/src/core/dispatch.rs index 649448aa3..aceae3bbe 100644 --- a/src/core/dispatch.rs +++ b/src/core/dispatch.rs @@ -1,7 +1,26 @@ +//! Central dispatcher for RPC requests. +//! +//! This module coordinates the routing of incoming requests to either the +//! core subsystem or the OpenHuman domain-specific handlers. + use crate::core::rpc_log; use crate::core::types::{AppState, InvocationResult}; use serde_json::json; +/// Dispatches an RPC method call to the appropriate subsystem. +/// +/// It first attempts to route the request to the core subsystem (e.g., `core.ping`). +/// If not found, it delegates to the `openhuman` domain-specific dispatcher. +/// +/// # Arguments +/// +/// * `state` - The current application state. +/// * `method` - The name of the RPC method to invoke. +/// * `params` - The parameters for the method call as a JSON value. +/// +/// # Returns +/// +/// A `Result` containing the JSON-formatted response or an error message. pub async fn dispatch( state: AppState, method: &str, @@ -13,10 +32,13 @@ pub async fn dispatch( rpc_log::redact_params_for_log(¶ms) ); + // Try routing to internal core methods first. if let Some(result) = try_core_dispatch(&state, method, params.clone()) { log::debug!("[rpc:dispatch] routed method={} subsystem=core", method); return result.map(crate::core::types::invocation_to_rpc_json); } + + // Delegate to the domain-specific dispatcher. if let Some(result) = crate::rpc::try_dispatch(method, params).await { log::debug!( "[rpc:dispatch] routed method={} subsystem=openhuman", @@ -29,6 +51,11 @@ pub async fn dispatch( Err(format!("unknown method: {method}")) } +/// Handles internal core-level RPC methods. +/// +/// Currently supports: +/// - `core.ping`: Returns `{ "ok": true }`. +/// - `core.version`: Returns the current core version. fn try_core_dispatch( state: &AppState, method: &str, diff --git a/src/core/types.rs b/src/core/types.rs index 4fcc4bf3c..4e49dbcc7 100644 --- a/src/core/types.rs +++ b/src/core/types.rs @@ -1,20 +1,34 @@ +//! Shared core-level type definitions and response formats. +//! +//! This module contains structs and methods for handling RPC requests and +//! responses, as well as maintaining application state across subsystems. + use serde::{Deserialize, Serialize}; use serde_json::json; +/// Standard response structure for commands that include execution logs. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CommandResponse { + /// The primary data returned by the command. pub result: T, + /// A list of log messages generated during command execution. pub logs: Vec, } /// Success payload from a core RPC handler before JSON-RPC wrapping. +/// +/// This internal type allows handlers to return a generic JSON value along +/// with optional logs. #[derive(Debug, Clone)] pub struct InvocationResult { + /// The value returned by the RPC function call. pub value: serde_json::Value, + /// A list of execution logs. pub logs: Vec, } impl InvocationResult { + /// Creates a success result from any serializable value with no logs. pub fn ok(v: T) -> Result { Ok(Self { value: serde_json::to_value(v).map_err(|e| e.to_string())?, @@ -22,6 +36,7 @@ impl InvocationResult { }) } + /// Creates a success result from a serializable value with accompanying logs. pub fn with_logs(v: T, logs: Vec) -> Result { Ok(Self { value: serde_json::to_value(v).map_err(|e| e.to_string())?, @@ -30,6 +45,10 @@ impl InvocationResult { } } +/// Formats an [`InvocationResult`] into its standard JSON-RPC format. +/// +/// If there are no logs, returns the value directly. Otherwise, returns an +/// object containing both `result` and `logs` keys. pub fn invocation_to_rpc_json(inv: InvocationResult) -> serde_json::Value { if inv.logs.is_empty() { inv.value @@ -38,38 +57,57 @@ pub fn invocation_to_rpc_json(inv: InvocationResult) -> serde_json::Value { } } +/// Standard JSON-RPC request format. #[derive(Debug, Deserialize)] pub struct RpcRequest { + /// The JSON-RPC version (e.g., `2.0`). #[allow(dead_code)] pub jsonrpc: String, + /// Unique identifier for the request, to be mirrored in the response. pub id: serde_json::Value, + /// The name of the method to be invoked. pub method: String, + /// Parameters for the method call. Defaults to null if not provided. #[serde(default)] pub params: serde_json::Value, } +/// Standard JSON-RPC success response format. #[derive(Debug, Serialize)] pub struct RpcSuccess { + /// The JSON-RPC version (always `2.0`). pub jsonrpc: &'static str, + /// The identifier mirrored from the original request. pub id: serde_json::Value, + /// The result of the successful method invocation. pub result: serde_json::Value, } +/// Standard JSON-RPC error response format. #[derive(Debug, Serialize)] pub struct RpcFailure { + /// The JSON-RPC version (always `2.0`). pub jsonrpc: &'static str, + /// The identifier mirrored from the original request. pub id: serde_json::Value, + /// Information about the error that occurred. pub error: RpcError, } +/// Detail about an RPC invocation error. #[derive(Debug, Serialize)] pub struct RpcError { + /// Standardized error code (e.g., -32601 for Method not found). pub code: i64, + /// A short, human-readable error message. pub message: String, + /// Optional additional diagnostic data. pub data: Option, } +/// Global core-level application state. #[derive(Clone)] pub struct AppState { + /// The current version of the OpenHuman core binary. pub core_version: String, } diff --git a/src/openhuman/agent/agent.rs b/src/openhuman/agent/agent.rs index ce28f6aa4..4f96e8dc4 100644 --- a/src/openhuman/agent/agent.rs +++ b/src/openhuman/agent/agent.rs @@ -1,3 +1,10 @@ +//! Core agent implementation for the OpenHuman platform. +//! +//! This module provides the `Agent` struct, which orchestrates the interaction +//! between the AI provider, available tools, memory systems, and the user. +//! It handles the agent's "turn" logic, including tool execution and history +//! management. + use super::dispatcher::{ NativeToolDispatcher, ParsedToolCall, ToolDispatcher, ToolExecutionResult, XmlToolDispatcher, }; @@ -17,6 +24,11 @@ use anyhow::Result; use std::io::Write as IoWrite; use std::sync::Arc; +/// An autonomous or semi-autonomous AI agent. +/// +/// The `Agent` is the central component that manages conversation state, +/// executes tools based on model requests, and interacts with the memory system +/// to maintain context across turns. pub struct Agent { provider: Box, tools: Vec>, @@ -39,6 +51,7 @@ pub struct Agent { learning_enabled: bool, } +/// A builder for creating `Agent` instances with custom configuration. pub struct AgentBuilder { provider: Option>, tools: Option>>, @@ -66,6 +79,7 @@ impl Default for AgentBuilder { } impl AgentBuilder { + /// Creates a new `AgentBuilder` with default values. pub fn new() -> Self { Self { provider: None, @@ -88,56 +102,67 @@ impl AgentBuilder { } } + /// Sets the AI provider for the agent. pub fn provider(mut self, provider: Box) -> Self { self.provider = Some(provider); self } + /// Sets the available tools for the agent. pub fn tools(mut self, tools: Vec>) -> Self { self.tools = Some(tools); self } + /// Sets the memory system for the agent. pub fn memory(mut self, memory: Arc) -> Self { self.memory = Some(memory); self } + /// Sets the system prompt builder for the agent. pub fn prompt_builder(mut self, prompt_builder: SystemPromptBuilder) -> Self { self.prompt_builder = Some(prompt_builder); self } + /// Sets the tool dispatcher for the agent. pub fn tool_dispatcher(mut self, tool_dispatcher: Box) -> Self { self.tool_dispatcher = Some(tool_dispatcher); self } + /// Sets the memory loader for the agent. pub fn memory_loader(mut self, memory_loader: Box) -> Self { self.memory_loader = Some(memory_loader); self } + /// Sets the agent configuration. pub fn config(mut self, config: crate::openhuman::config::AgentConfig) -> Self { self.config = Some(config); self } + /// Sets the model name to use for chat requests. pub fn model_name(mut self, model_name: String) -> Self { self.model_name = Some(model_name); self } + /// Sets the temperature for chat requests. pub fn temperature(mut self, temperature: f64) -> Self { self.temperature = Some(temperature); self } + /// Sets the workspace directory for the agent. pub fn workspace_dir(mut self, workspace_dir: std::path::PathBuf) -> Self { self.workspace_dir = Some(workspace_dir); self } + /// Sets the identity configuration for the agent. pub fn identity_config( mut self, identity_config: crate::openhuman::config::IdentityConfig, @@ -146,16 +171,19 @@ impl AgentBuilder { self } + /// Sets the skills available to the agent. pub fn skills(mut self, skills: Vec) -> Self { self.skills = Some(skills); self } + /// Enables or disables automatic saving of conversation history to memory. pub fn auto_save(mut self, auto_save: bool) -> Self { self.auto_save = Some(auto_save); self } + /// Sets the query classification configuration. pub fn classification_config( mut self, classification_config: crate::openhuman::config::QueryClassificationConfig, @@ -164,21 +192,25 @@ impl AgentBuilder { self } + /// Sets the available model hints for auto-classification. pub fn available_hints(mut self, available_hints: Vec) -> Self { self.available_hints = Some(available_hints); self } + /// Sets the post-turn hooks to be executed after each turn. pub fn post_turn_hooks(mut self, hooks: Vec>) -> Self { self.post_turn_hooks = hooks; self } + /// Enables or disables learning features. pub fn learning_enabled(mut self, enabled: bool) -> Self { self.learning_enabled = enabled; self } + /// Validates the configuration and builds the `Agent` instance. pub fn build(self) -> Result { let tools = self .tools @@ -224,6 +256,10 @@ impl AgentBuilder { } impl Agent { + /// Injects unique IDs into tool calls that are missing them. + /// + /// This is necessary for some tool dispatchers to correctly track and + /// associate results. fn with_fallback_tool_call_ids( mut parsed_calls: Vec, iteration: usize, @@ -236,6 +272,10 @@ impl Agent { parsed_calls } + /// Converts parsed tool calls into the provider-standard `ToolCall` format. + /// + /// If the provider response already contains native tool calls, they are + /// returned as-is. fn persisted_tool_calls_for_history( response: &crate::openhuman::providers::ChatResponse, parsed_calls: &[ParsedToolCall], @@ -259,18 +299,25 @@ impl Agent { .collect() } + /// Returns a new `AgentBuilder`. pub fn builder() -> AgentBuilder { AgentBuilder::new() } + /// Returns the current conversation history. pub fn history(&self) -> &[ConversationMessage] { &self.history } + /// Clears the agent's conversation history. pub fn clear_history(&mut self) { self.history.clear(); } + /// Creates an `Agent` instance from a global configuration. + /// + /// This is the primary way to initialize an agent with all system + /// integrations (memory, tools, skills, etc.) configured. pub fn from_config(config: &Config) -> Result { let runtime: Arc = Arc::from(host_runtime::create_runtime(&config.runtime)?); @@ -445,6 +492,10 @@ impl Agent { .build() } + /// Truncates the conversation history to the configured maximum message count. + /// + /// System messages are always preserved. Older non-system messages are + /// dropped first. fn trim_history(&mut self) { let max = self.config.max_history_messages; if self.history.len() <= max { @@ -472,7 +523,10 @@ impl Agent { self.history.extend(other_messages); } - /// Pre-fetch learned context data from memory (async, non-blocking). + /// Pre-fetches learned context data from memory (observations, patterns, user profile). + /// + /// This is an async, non-blocking operation that populates the context + /// for the system prompt. async fn fetch_learned_context(&self) -> crate::openhuman::agent::prompt::LearnedContextData { use crate::openhuman::agent::prompt::LearnedContextData; @@ -524,6 +578,8 @@ impl Agent { } } + /// Builds the system prompt for the current turn, including tool + /// instructions and learned context. fn build_system_prompt( &self, learned: crate::openhuman::agent::prompt::LearnedContextData, @@ -571,6 +627,7 @@ impl Agent { } } + /// Executes a single tool call and returns the result and execution record. async fn execute_tool_call( &self, call: &ParsedToolCall, @@ -621,6 +678,7 @@ impl Agent { (exec_result, record) } + /// Executes multiple tool calls in sequence. async fn execute_tools( &self, calls: &[ParsedToolCall], @@ -635,6 +693,7 @@ impl Agent { (results, records) } + /// Classifies the user message to determine if a specific model hint should be used. fn classify_model(&self, user_message: &str) -> String { if let Some(hint) = super::classifier::classify(&self.classification_config, user_message) { if self.available_hints.contains(&hint) { @@ -645,6 +704,11 @@ impl Agent { self.model_name.clone() } + /// Performs a single interaction "turn" with the agent. + /// + /// This is the core logic that takes user input, manages the history, + /// calls the LLM, handles tool calls (up to `max_tool_iterations`), + /// and returns the final assistant response. pub async fn turn(&mut self, user_message: &str) -> Result { let turn_started = std::time::Instant::now(); log::info!( @@ -864,10 +928,12 @@ impl Agent { ) } + /// Runs a single turn with the given message and returns the response. pub async fn run_single(&mut self, message: &str) -> Result { self.turn(message).await } + /// Runs an interactive CLI loop, reading from standard input and printing to standard output. pub async fn run_interactive(&mut self) -> Result<()> { println!("🦀 OpenHuman Interactive Mode"); println!("Type /quit to exit.\n"); @@ -895,6 +961,7 @@ impl Agent { } } +/// Convenience entry point to run an agent with the given configuration and message. pub async fn run( config: Config, message: Option, diff --git a/src/openhuman/config/ops.rs b/src/openhuman/config/ops.rs index f5c259540..048312acc 100644 --- a/src/openhuman/config/ops.rs +++ b/src/openhuman/config/ops.rs @@ -9,6 +9,7 @@ use crate::openhuman::config::Config; use crate::openhuman::screen_intelligence; use crate::rpc::RpcOutcome; +/// Checks if an environment variable flag is enabled (e.g., "1", "true", "yes"). fn env_flag_enabled(key: &str) -> bool { matches!( std::env::var(key).ok().as_deref(), @@ -16,6 +17,7 @@ fn env_flag_enabled(key: &str) -> bool { ) } +/// Returns the core RPC URL from environment variables or a default value. pub fn core_rpc_url_from_env() -> String { std::env::var("OPENHUMAN_CORE_RPC_URL") .unwrap_or_else(|_| "http://127.0.0.1:7788/rpc".to_string()) @@ -23,7 +25,10 @@ pub fn core_rpc_url_from_env() -> String { const CONFIG_LOAD_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30); -/// Loads persisted config with the same 30s timeout used by JSON-RPC and the core CLI. +/// Loads persisted config with a 30s timeout. +/// +/// This is used by JSON-RPC and CLI handlers to ensure they don't hang +/// indefinitely if disk I/O is blocked. pub async fn load_config_with_timeout() -> Result { match tokio::time::timeout(CONFIG_LOAD_TIMEOUT, Config::load_or_init()).await { Ok(Ok(config)) => Ok(config), @@ -32,6 +37,7 @@ pub async fn load_config_with_timeout() -> Result { } } +/// Returns the default workspace directory fallback (~/.openhuman/workspace). fn fallback_workspace_dir() -> PathBuf { dirs::home_dir() .unwrap_or_else(|| PathBuf::from(".")) @@ -39,16 +45,19 @@ fn fallback_workspace_dir() -> PathBuf { .join("workspace") } +/// Returns the default OpenHuman configuration directory (~/.openhuman). fn default_openhuman_dir() -> PathBuf { dirs::home_dir() .unwrap_or_else(|| PathBuf::from(".")) .join(".openhuman") } +/// Returns the path to the active workspace marker file. fn active_workspace_marker_path(default_openhuman_dir: &Path) -> PathBuf { default_openhuman_dir.join("active_workspace.toml") } +/// Returns the parent directory of the config file. fn config_openhuman_dir(config: &Config) -> PathBuf { config .config_path @@ -56,6 +65,7 @@ fn config_openhuman_dir(config: &Config) -> PathBuf { .map_or_else(|| PathBuf::from("."), PathBuf::from) } +/// Internal helper to reset local data by removing specific directories and markers. async fn reset_local_data_for_paths( current_openhuman_dir: &Path, default_openhuman_dir: &Path, @@ -119,6 +129,7 @@ async fn reset_local_data_for_paths( )) } +/// Serializes the current configuration into a JSON snapshot for the UI. pub fn snapshot_config_json(config: &Config) -> Result { let value = serde_json::to_value(config).map_err(|e| e.to_string())?; Ok(json!({ @@ -181,6 +192,7 @@ pub struct RuntimeFlagsOut { pub log_prompts: bool, } +/// Returns a full configuration snapshot for the UI. pub async fn get_config_snapshot(config: &Config) -> Result, String> { let snapshot = snapshot_config_json(config)?; Ok(RpcOutcome::new( @@ -192,6 +204,7 @@ pub async fn get_config_snapshot(config: &Config) -> Result Result, String> { let config = load_config_with_timeout().await?; get_config_snapshot(&config).await } +/// Loads the configuration, applies model settings updates, and saves it. pub async fn load_and_apply_model_settings( update: ModelSettingsPatch, ) -> Result, String> { @@ -362,6 +381,7 @@ pub async fn load_and_apply_model_settings( apply_model_settings(&mut config, update).await } +/// Loads the configuration, applies memory settings updates, and saves it. pub async fn load_and_apply_memory_settings( update: MemorySettingsPatch, ) -> Result, String> { @@ -369,6 +389,7 @@ pub async fn load_and_apply_memory_settings( apply_memory_settings(&mut config, update).await } +/// Loads the configuration, applies screen intelligence settings updates, and saves it. pub async fn load_and_apply_screen_intelligence_settings( update: ScreenIntelligenceSettingsPatch, ) -> Result, String> { @@ -376,6 +397,7 @@ pub async fn load_and_apply_screen_intelligence_settings( apply_screen_intelligence_settings(&mut config, update).await } +/// Loads the configuration, applies runtime settings updates, and saves it. pub async fn load_and_apply_runtime_settings( update: RuntimeSettingsPatch, ) -> Result, String> { @@ -383,6 +405,7 @@ pub async fn load_and_apply_runtime_settings( apply_runtime_settings(&mut config, update).await } +/// Updates the analytics-related settings in the configuration. pub async fn apply_analytics_settings( config: &mut Config, update: AnalyticsSettingsPatch, @@ -401,6 +424,7 @@ pub async fn apply_analytics_settings( )) } +/// Loads the configuration, applies analytics settings updates, and saves it. pub async fn load_and_apply_analytics_settings( update: AnalyticsSettingsPatch, ) -> Result, String> { @@ -408,6 +432,7 @@ pub async fn load_and_apply_analytics_settings( apply_analytics_settings(&mut config, update).await } +/// Loads the configuration, applies browser settings updates, and saves it. pub async fn load_and_apply_browser_settings( update: BrowserSettingsPatch, ) -> Result, String> { @@ -415,13 +440,14 @@ pub async fn load_and_apply_browser_settings( apply_browser_settings(&mut config, update).await } +/// Resolves the effective API URL from configuration or defaults. pub async fn load_and_resolve_api_url() -> Result, String> { let config = load_config_with_timeout().await?; let resolved = crate::api::config::effective_api_url(&config.api_url); Ok(RpcOutcome::new(json!({ "api_url": resolved }), Vec::new())) } -/// Resolves workspace (load config or fallback), validates flag name, returns whether the flag file exists. +/// Resolves a workspace onboarding flag, creating or checking its existence. pub async fn workspace_onboarding_flag_resolve( flag_name: Option, default_name: &str, @@ -442,6 +468,7 @@ pub async fn workspace_onboarding_flag_resolve( workspace_onboarding_flag_exists(workspace_dir, trimmed) } +/// Returns the current state of runtime-only flags. pub fn get_runtime_flags() -> RpcOutcome { RpcOutcome::single_log( RuntimeFlagsOut { @@ -452,6 +479,7 @@ pub fn get_runtime_flags() -> RpcOutcome { ) } +/// Updates the `OPENHUMAN_BROWSER_ALLOW_ALL` environment flag. pub fn set_browser_allow_all(enabled: bool) -> RpcOutcome { if enabled { std::env::set_var("OPENHUMAN_BROWSER_ALLOW_ALL", "1"); @@ -465,6 +493,7 @@ pub fn set_browser_allow_all(enabled: bool) -> RpcOutcome { RpcOutcome::single_log(flags, "browser allow-all flag updated") } +/// Checks if a specific onboarding flag file exists in the workspace. pub fn workspace_onboarding_flag_exists( workspace_dir: PathBuf, flag_name: &str, @@ -483,7 +512,7 @@ pub fn workspace_onboarding_flag_exists( )) } -/// Creates or removes the workspace onboarding flag file. +/// Creates or removes an onboarding flag file in the workspace. pub async fn workspace_onboarding_flag_set( flag_name: Option, default_name: &str, @@ -520,6 +549,7 @@ pub async fn workspace_onboarding_flag_set( )) } +/// Returns whether the onboarding process has been marked as completed. pub async fn get_onboarding_completed() -> Result, String> { let config = load_config_with_timeout().await?; Ok(RpcOutcome::single_log( @@ -528,6 +558,7 @@ pub async fn get_onboarding_completed() -> Result, String> { )) } +/// Updates and persists the onboarding completion status. pub async fn set_onboarding_completed(value: bool) -> Result, String> { let mut config = load_config_with_timeout().await?; config.onboarding_completed = value; @@ -540,6 +571,7 @@ pub async fn set_onboarding_completed(value: bool) -> Result, S // ── Dictation settings ─────────────────────────────────────────────── +/// Represents a partial update to dictation-related settings. pub struct DictationSettingsPatch { pub enabled: Option, pub hotkey: Option, @@ -549,6 +581,7 @@ pub struct DictationSettingsPatch { pub streaming_interval_ms: Option, } +/// Returns the current dictation settings as a JSON object. pub async fn get_dictation_settings() -> Result, String> { let config = load_config_with_timeout().await?; let result = json!({ @@ -565,6 +598,7 @@ pub async fn get_dictation_settings() -> Result, S )) } +/// Loads configuration, applies dictation settings updates, and saves it. pub async fn load_and_apply_dictation_settings( update: DictationSettingsPatch, ) -> Result, String> { @@ -614,6 +648,7 @@ pub async fn load_and_apply_dictation_settings( // ── Voice server settings ─────────────────────────────────────────── +/// Represents a partial update to voice server related settings. pub struct VoiceServerSettingsPatch { pub auto_start: Option, pub hotkey: Option, @@ -624,6 +659,7 @@ pub struct VoiceServerSettingsPatch { pub custom_dictionary: Option>, } +/// Returns the current voice server settings as a JSON object. pub async fn get_voice_server_settings() -> Result, String> { let config = load_config_with_timeout().await?; let result = json!({ @@ -641,6 +677,7 @@ pub async fn get_voice_server_settings() -> Result )) } +/// Loads configuration, applies voice server settings updates, and saves it. pub async fn load_and_apply_voice_server_settings( update: VoiceServerSettingsPatch, ) -> Result, String> { @@ -691,6 +728,7 @@ pub async fn load_and_apply_voice_server_settings( )) } +/// Returns the operational status of the agent server. pub fn agent_server_status() -> RpcOutcome { let running = crate::openhuman::service::mock::mock_agent_running().unwrap_or(true); log::info!("[config] agent_server_status requested: running={running}"); @@ -701,6 +739,7 @@ pub fn agent_server_status() -> RpcOutcome { RpcOutcome::single_log(payload, "agent server status checked") } +/// Deletes all local data directories and workspace markers. pub async fn reset_local_data() -> Result, String> { let config = load_config_with_timeout().await?; let current_openhuman_dir = config_openhuman_dir(&config); diff --git a/src/openhuman/local_ai/ops.rs b/src/openhuman/local_ai/ops.rs index e42ace176..43fe335de 100644 --- a/src/openhuman/local_ai/ops.rs +++ b/src/openhuman/local_ai/ops.rs @@ -1,4 +1,8 @@ //! JSON-RPC / CLI controller surface for the bundled local AI stack. +//! +//! This module provides high-level functions for interacting with local AI +//! services such as agent chat, model downloads, summarization, and +//! transcription. These functions are typically invoked via RPC or CLI. use chrono::Utc; use once_cell::sync::Lazy; @@ -16,9 +20,21 @@ use crate::openhuman::local_ai::{ use crate::openhuman::providers::{self, ProviderRuntimeOptions}; use crate::rpc::RpcOutcome; +/// A static registry of active agent sessions for the REPL. static REPL_AGENT_SESSIONS: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); +/// Executes a single chat turn with an AI agent. +/// +/// This function initializes an agent from the provided configuration and +/// processes the input message. +/// +/// # Arguments +/// +/// * `config` - The configuration used to build the agent. May be updated with model/temp overrides. +/// * `message` - The user message to process. +/// * `model_override` - Optional model name to use for this call. +/// * `temperature` - Optional sampling temperature override. pub async fn agent_chat( config: &mut Config, message: &str, @@ -36,6 +52,7 @@ pub async fn agent_chat( Ok(RpcOutcome::single_log(response, "agent chat completed")) } +/// A simplified chat interface that does not update the base configuration. pub async fn agent_chat_simple( config: &Config, message: &str, @@ -88,6 +105,7 @@ pub async fn agent_chat_simple( )) } +/// Starts a persistent agent session for REPL interactions. pub async fn agent_repl_session_start( config: &Config, session_id: Option, @@ -122,6 +140,7 @@ pub async fn agent_repl_session_start( )) } +/// Resets the history of an active REPL session. pub async fn agent_repl_session_reset( session_id: &str, ) -> Result, String> { @@ -144,6 +163,7 @@ pub async fn agent_repl_session_reset( )) } +/// Terminates an active REPL session. pub async fn agent_repl_session_end( session_id: &str, ) -> Result, String> { @@ -163,6 +183,7 @@ pub async fn agent_repl_session_end( )) } +/// Returns the current operational status of the local AI stack. pub async fn local_ai_status( config: &Config, ) -> Result, String> { @@ -181,6 +202,7 @@ pub async fn local_ai_status( )) } +/// Triggers a full download of all required local AI models. pub async fn local_ai_download( config: &Config, force: bool, @@ -202,6 +224,7 @@ pub async fn local_ai_download( )) } +/// Triggers a download of all local AI assets and returns progress information. pub async fn local_ai_download_all_assets( config: &Config, force: bool, @@ -227,6 +250,7 @@ pub async fn local_ai_download_all_assets( )) } +/// Generates a summary of the provided text using local AI models. pub async fn local_ai_summarize( config: &Config, text: &str, @@ -247,6 +271,7 @@ pub async fn local_ai_summarize( )) } +/// Suggests relevant follow-up questions based on the provided context. pub async fn local_ai_suggest_questions( config: &Config, context: Option, @@ -273,6 +298,7 @@ pub async fn local_ai_suggest_questions( )) } +/// Executes a raw prompt directly against the local AI model. pub async fn local_ai_prompt( config: &Config, prompt: &str, @@ -291,6 +317,7 @@ pub async fn local_ai_prompt( Ok(RpcOutcome::single_log(output, "local ai prompt completed")) } +/// Executes a multimodal (vision) prompt with associated images. pub async fn local_ai_vision_prompt( config: &Config, prompt: &str, @@ -308,6 +335,7 @@ pub async fn local_ai_vision_prompt( )) } +/// Generates semantic embeddings for the provided input strings. pub async fn local_ai_embed( config: &Config, inputs: &[String], @@ -323,6 +351,7 @@ pub async fn local_ai_embed( )) } +/// Transcribes the audio file at the specified path. pub async fn local_ai_transcribe( config: &Config, audio_path: &str, @@ -338,6 +367,7 @@ pub async fn local_ai_transcribe( )) } +/// Transcribes raw audio bytes by first saving them to a temporary file. pub async fn local_ai_transcribe_bytes( config: &Config, audio_bytes: &[u8], @@ -382,6 +412,7 @@ pub async fn local_ai_transcribe_bytes( )) } +/// Performs text-to-speech synthesis and optionally saves the result to a file. pub async fn local_ai_tts( config: &Config, text: &str, @@ -395,6 +426,7 @@ pub async fn local_ai_tts( Ok(RpcOutcome::single_log(output, "local ai tts completed")) } +/// Returns the status of all local AI assets (models and support files). pub async fn local_ai_assets_status( config: &Config, ) -> Result, String> { @@ -409,6 +441,7 @@ pub async fn local_ai_assets_status( )) } +/// Returns progress for any ongoing asset downloads. pub async fn local_ai_downloads_progress( config: &Config, ) -> Result, String> { @@ -423,6 +456,7 @@ pub async fn local_ai_downloads_progress( )) } +/// Triggers the download of a specific AI asset based on capability name. pub async fn local_ai_download_asset( config: &Config, capability: &str, @@ -438,12 +472,16 @@ pub async fn local_ai_download_asset( )) } +/// A single message in a local AI chat conversation. #[derive(Debug, serde::Deserialize)] pub struct LocalAiChatMessage { + /// The role of the message sender (e.g., "user", "assistant"). pub role: String, + /// The text content of the message. pub content: String, } +/// Executes a multi-turn chat conversation using the local model. pub async fn local_ai_chat( config: &Config, messages: Vec, @@ -489,9 +527,10 @@ pub struct ReactionDecision { pub emoji: Option, } -/// Ask the local model whether the assistant should add an emoji reaction to -/// the user's message, based on channel type and message content. -/// Designed to be called fire-and-forget — fast, lightweight, no cloud cost. +/// Evaluates whether the assistant should add an emoji reaction to a user message. +/// +/// This uses the local model to make a quick decision based on the message +/// content and the channel context. pub async fn local_ai_should_react( config: &Config, message: &str, diff --git a/src/openhuman/memory/ops.rs b/src/openhuman/memory/ops.rs index d240c7986..80c32ff32 100644 --- a/src/openhuman/memory/ops.rs +++ b/src/openhuman/memory/ops.rs @@ -30,11 +30,15 @@ use std::path::{Component, Path, PathBuf}; // All access goes through `active_memory_client()` below. /// Generates a unique request ID for memory operations. +/// +/// This ID is used for tracing and logging purposes in the API response metadata. fn memory_request_id() -> String { uuid::Uuid::new_v4().to_string() } /// Converts an iterator of memory counts into a BTreeMap. +/// +/// This is a convenience helper for populating the `counts` field in the API metadata. fn memory_counts( entries: impl IntoIterator, ) -> BTreeMap { @@ -45,6 +49,8 @@ fn memory_counts( } /// Wraps data in an RPC API envelope. +/// +/// This standardises the response format for memory-related RPC methods. fn envelope( data: T, counts: Option>, @@ -67,6 +73,8 @@ fn envelope( } /// Wraps an error in an RPC API envelope. +/// +/// This provides a consistent error reporting format for the memory system. fn error_envelope(code: &str, message: String) -> RpcOutcome> { RpcOutcome::new( ApiEnvelope { @@ -89,6 +97,8 @@ fn error_envelope(code: &str, message: String) -> RpcOutcome Option { if !timestamp.is_finite() || timestamp < 0.0 { return None; @@ -113,6 +123,8 @@ fn memory_kind_label(kind: &MemoryItemKind) -> &'static str { } /// Generates a unique string identity for a graph relation. +/// +/// The identity is composed of the namespace, subject, predicate, and object. fn relation_identity(relation: &GraphRelationRecord) -> String { format!( "{}|{}|{}|{}", @@ -482,127 +494,188 @@ async fn query_limit_for_request( Ok(requested.max(total_documents)) } +/// Parameters for the `doc_put` RPC method. #[derive(Debug, Deserialize)] pub struct PutDocParams { + /// Namespace to store the document in. pub namespace: String, + /// Unique key for the document within the namespace. pub key: String, + /// Human-readable title for the document. pub title: String, + /// The raw text content of the document. pub content: String, + /// The source type of the document (e.g., "doc", "web"). #[serde(default = "default_source_type")] pub source_type: String, + /// Priority level for retrieval (e.g., "high", "medium", "low"). #[serde(default = "default_priority")] pub priority: String, + /// Optional tags for categorization and filtering. #[serde(default)] pub tags: Vec, + /// Additional unstructured metadata. #[serde(default)] pub metadata: serde_json::Value, + /// Core category for the document (e.g., "core", "user"). #[serde(default = "default_category")] pub category: String, + /// Optional session ID associated with the document. #[serde(default)] pub session_id: Option, + /// Optional explicit document ID. #[serde(default)] pub document_id: Option, } +/// Parameters for the `doc_ingest` RPC method. #[derive(Debug, Deserialize)] pub struct IngestDocParams { + /// Namespace to store the document in. pub namespace: String, + /// Unique key for the document within the namespace. pub key: String, + /// Human-readable title for the document. pub title: String, + /// The raw text content of the document. pub content: String, + /// The source type of the document. #[serde(default = "default_source_type")] pub source_type: String, + /// Priority level for retrieval. #[serde(default = "default_priority")] pub priority: String, + /// Optional tags for the document. #[serde(default)] pub tags: Vec, + /// Additional unstructured metadata. #[serde(default)] pub metadata: serde_json::Value, + /// Core category for the document. #[serde(default = "default_category")] pub category: String, + /// Optional session ID. #[serde(default)] pub session_id: Option, + /// Optional explicit document ID. #[serde(default)] pub document_id: Option, + /// Configuration for the ingestion process (chunking, etc.). #[serde(default)] pub config: Option, } +/// Parameters for RPC methods that only require a namespace. #[derive(Debug, Deserialize)] pub struct NamespaceOnlyParams { + /// The target namespace. pub namespace: String, } +/// Parameters for the `clear_namespace` RPC method. #[derive(Debug, Deserialize)] pub struct ClearNamespaceParams { + /// The namespace to clear. pub namespace: String, } +/// Result returned by the `clear_namespace` RPC method. #[derive(Debug, Serialize)] pub struct ClearNamespaceResult { + /// Whether the namespace was successfully cleared. pub cleared: bool, + /// The namespace that was cleared. pub namespace: String, } +/// Parameters for the `doc_delete` RPC method. #[derive(Debug, Deserialize)] pub struct DeleteDocParams { + /// The namespace containing the document. pub namespace: String, + /// The unique ID of the document to delete. pub document_id: String, } +/// Parameters for the `context_query` RPC method. #[derive(Debug, Deserialize)] pub struct QueryNamespaceParams { + /// The namespace to query. pub namespace: String, + /// The natural language query string. pub query: String, + /// Maximum number of results to return. #[serde(default)] pub limit: Option, } +/// Parameters for the `context_recall` RPC method. #[derive(Debug, Deserialize)] pub struct RecallNamespaceParams { + /// The namespace to recall from. pub namespace: String, + /// Maximum number of results to return. #[serde(default)] pub limit: Option, } +/// Parameters for the `kv_set` RPC method. #[derive(Debug, Deserialize)] pub struct KvSetParams { + /// The namespace for the key-value pair. #[serde(default)] pub namespace: Option, + /// The unique key. pub key: String, + /// The value to store. pub value: serde_json::Value, } +/// Parameters for `kv_get` and `kv_delete` RPC methods. #[derive(Debug, Deserialize)] pub struct KvGetDeleteParams { + /// The namespace containing the key. #[serde(default)] pub namespace: Option, + /// The unique key. pub key: String, } +/// Parameters for the `graph_upsert` RPC method. #[derive(Debug, Deserialize)] pub struct GraphUpsertParams { + /// The namespace for the relation. #[serde(default)] pub namespace: Option, + /// The subject of the relation triple. pub subject: String, + /// The predicate (relationship) of the triple. pub predicate: String, + /// The object of the triple. pub object: String, + /// Additional attributes for the relation. #[serde(default)] pub attrs: serde_json::Value, } +/// Parameters for the `graph_query` RPC method. #[derive(Debug, Deserialize)] pub struct GraphQueryParams { + /// The namespace to query. #[serde(default)] pub namespace: Option, + /// Optional subject filter. #[serde(default)] pub subject: Option, + /// Optional predicate filter. #[serde(default)] pub predicate: Option, } +/// Result returned by the `doc_put` RPC method. #[derive(Debug, Serialize)] pub struct PutDocResult { + /// The unique ID of the upserted document. pub document_id: String, } diff --git a/src/openhuman/service/ops.rs b/src/openhuman/service/ops.rs index f66dc3388..e66ec9abd 100644 --- a/src/openhuman/service/ops.rs +++ b/src/openhuman/service/ops.rs @@ -5,26 +5,31 @@ use crate::openhuman::service::daemon_host::DaemonHostConfig; use crate::openhuman::service::{self, daemon_host, ServiceStatus}; use crate::rpc::RpcOutcome; +/// Installs the OpenHuman daemon as a system service. pub async fn service_install(config: &Config) -> Result, String> { let status = service::install(config).map_err(|e| e.to_string())?; Ok(RpcOutcome::single_log(status, "service install completed")) } +/// Starts the installed OpenHuman daemon service. pub async fn service_start(config: &Config) -> Result, String> { let status = service::start(config).map_err(|e| e.to_string())?; Ok(RpcOutcome::single_log(status, "service start completed")) } +/// Stops the running OpenHuman daemon service. pub async fn service_stop(config: &Config) -> Result, String> { let status = service::stop(config).map_err(|e| e.to_string())?; Ok(RpcOutcome::single_log(status, "service stop completed")) } +/// Returns the current status of the OpenHuman daemon service. pub async fn service_status(config: &Config) -> Result, String> { let status = service::status(config).map_err(|e| e.to_string())?; Ok(RpcOutcome::single_log(status, "service status fetched")) } +/// Requests an asynchronous restart of the core process. pub async fn service_restart( source: Option, reason: Option, @@ -32,6 +37,7 @@ pub async fn service_restart( service::restart::service_restart(source, reason).await } +/// Uninstalls the OpenHuman daemon system service. pub async fn service_uninstall(config: &Config) -> Result, String> { let status = service::uninstall(config).map_err(|e| e.to_string())?; Ok(RpcOutcome::single_log( @@ -40,6 +46,7 @@ pub async fn service_uninstall(config: &Config) -> Result Result, String> { let config_dir = config .config_path @@ -49,6 +56,7 @@ pub async fn daemon_host_get(config: &Config) -> Result Vec { vec![ schemas("install"), @@ -19,6 +29,10 @@ pub fn all_controller_schemas() -> Vec { ] } +/// Returns a collection of all registered controllers for the service domain. +/// +/// Each `RegisteredController` pairs a `ControllerSchema` with its corresponding +/// async handler function. pub fn all_registered_controllers() -> Vec { vec![ RegisteredController { @@ -56,6 +70,11 @@ pub fn all_registered_controllers() -> Vec { ] } +/// Returns the specific `ControllerSchema` for a given service function. +/// +/// # Arguments +/// +/// * `function` - The name of the service function (e.g., "install", "restart"). pub fn schemas(function: &str) -> ControllerSchema { match function { "install" | "restart" | "start" | "stop" | "status" | "uninstall" => ControllerSchema { @@ -153,6 +172,7 @@ fn handle_install(_params: Map) -> ControllerFuture { }) } +/// Service controller for `service.start`. fn handle_start(_params: Map) -> ControllerFuture { Box::pin(async move { let config = config_rpc::load_config_with_timeout().await?; @@ -166,6 +186,8 @@ struct ServiceRestartParams { reason: Option, } +/// Service controller for `service.restart`. +/// /// Service restart is intentionally config-free. /// /// Unlike install/start/stop/status, the restart action targets the currently @@ -181,6 +203,7 @@ fn handle_restart(params: Map) -> ControllerFuture { }) } +/// Service controller for `service.stop`. fn handle_stop(_params: Map) -> ControllerFuture { Box::pin(async move { let config = config_rpc::load_config_with_timeout().await?; @@ -188,6 +211,7 @@ fn handle_stop(_params: Map) -> ControllerFuture { }) } +/// Service controller for `service.status`. fn handle_status(_params: Map) -> ControllerFuture { Box::pin(async move { let config = config_rpc::load_config_with_timeout().await?; @@ -195,6 +219,7 @@ fn handle_status(_params: Map) -> ControllerFuture { }) } +/// Service controller for `service.uninstall`. fn handle_uninstall(_params: Map) -> ControllerFuture { Box::pin(async move { let config = config_rpc::load_config_with_timeout().await?; @@ -207,6 +232,7 @@ struct DaemonHostSetParams { show_tray: bool, } +/// Service controller for `service.daemon_host_get`. fn handle_daemon_host_get(_params: Map) -> ControllerFuture { Box::pin(async move { let config = config_rpc::load_config_with_timeout().await?; @@ -214,6 +240,7 @@ fn handle_daemon_host_get(_params: Map) -> ControllerFuture { }) } +/// Service controller for `service.daemon_host_set`. fn handle_daemon_host_set(params: Map) -> ControllerFuture { Box::pin(async move { let payload: DaemonHostSetParams = @@ -223,6 +250,7 @@ fn handle_daemon_host_set(params: Map) -> ControllerFuture { }) } +/// Formats the RpcOutcome as an OpenHuman-standard JSON result. fn to_json(outcome: RpcOutcome) -> Result { outcome.into_cli_compatible_json() } diff --git a/src/rpc/dispatch.rs b/src/rpc/dispatch.rs index 91a431e10..40f2d4461 100644 --- a/src/rpc/dispatch.rs +++ b/src/rpc/dispatch.rs @@ -1,16 +1,38 @@ +//! Main dispatcher for domain-specific RPC methods. +//! +//! This module routes RPC calls to their respective domain handlers (e.g., +//! security, memory, local AI). It serves as an extension point for +//! domain-level functionality in the OpenHuman platform. + use serde::Serialize; use crate::rpc::RpcOutcome; +/// Helper to convert an [`RpcOutcome`] into a JSON value compatible with the CLI. fn rpc_json(outcome: RpcOutcome) -> Result { outcome.into_cli_compatible_json() } +/// Dispatches an RPC method to its domain-specific handler. +/// +/// If the method is recognized, it executes the handler and returns the +/// result wrapped in a `Some(Result)`. If not recognized, it returns `None`. +/// +/// # Arguments +/// +/// * `method` - The name of the RPC method to invoke. +/// * `params` - The parameters for the call as a JSON value. +/// +/// # Returns +/// +/// `Some(Ok(Value))` if successful, `Some(Err(String))` if the handler failed, +/// or `None` if the method was not found in this dispatcher. pub async fn try_dispatch( method: &str, _params: serde_json::Value, ) -> Option> { match method { + // Core security policy information. "openhuman.security_policy_info" => Some(rpc_json( crate::openhuman::security::rpc::security_policy_info(), )), diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 6a97d1505..65bbf60f6 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -1,7 +1,11 @@ -//! Shared types for JSON-RPC / CLI controller surfaces (`*/rpc.rs` in each domain). +//! Shared types for JSON-RPC / CLI controller surfaces. //! -//! Domain `rpc` modules must not import `crate::core_server`; map to -//! [`crate::core_server::types::InvocationResult`] only in `core_server::dispatch`. +//! This module provides the foundational types and utilities for handling +//! RPC outcomes across different domain modules. It ensures a consistent +//! response format for both internal consumption and external presentation. +//! +//! Domain `rpc` modules should use [`RpcOutcome`] to wrap their results, +//! which facilitates consistent logging and error handling. use serde::Serialize; use serde_json::json; @@ -11,19 +15,26 @@ mod dispatch; pub use dispatch::try_dispatch; /// Successful RPC handler result: serialized JSON value plus optional log lines. +/// +/// This type represents the result of a domain-specific RPC call, including +/// any log messages generated during execution. #[derive(Debug)] pub struct RpcOutcome { + /// The actual data returned by the RPC call. pub value: T, + /// A collection of log messages for auditing or debugging. pub logs: Vec, } impl RpcOutcome { + /// Creates a new `RpcOutcome` with a value and a list of logs. pub fn new(value: T, logs: Vec) -> Self { Self { value, logs } } } impl RpcOutcome { + /// Creates a new `RpcOutcome` with a value and a single log message. pub fn single_log(value: T, log: impl Into) -> Self { Self { value, @@ -31,7 +42,15 @@ impl RpcOutcome { } } - /// JSON shape matches the core CLI / `invocation_to_rpc_json` wrapper (`result` + `logs`). + /// Converts the outcome into a CLI-compatible JSON value. + /// + /// The resulting JSON shape matches the core CLI expectations: + /// - If no logs are present, the value is returned directly. + /// - If logs are present, an object with `result` and `logs` keys is returned. + /// + /// # Errors + /// + /// Returns an error if serialization to JSON fails. pub fn into_cli_compatible_json(self) -> Result { let RpcOutcome { value, logs } = self; let value = serde_json::to_value(value).map_err(|e| e.to_string())?; diff --git a/yarn.lock b/yarn.lock index b4c99bf8b..a2a1b7ad5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -916,6 +916,13 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@napi-rs/wasm-runtime@^1.1.1": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz#1eeb8699770481306e5fcd84471f20fcb6177336" + integrity sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ== + dependencies: + "@tybys/wasm-util" "^0.10.1" + "@noble/curves@2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-2.0.1.tgz#64ba8bd5e8564a02942655602515646df1cdb3ad" @@ -954,6 +961,215 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@oxc-parser/binding-android-arm-eabi@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.121.0.tgz#a9440638713cdd6541f954a006558c85cc4f9b7c" + integrity sha512-n07FQcySwOlzap424/PLMtOkbS7xOu8nsJduKL8P3COGHKgKoDYXwoAHCbChfgFpHnviehrLWIPX0lKGtbEk/A== + +"@oxc-parser/binding-android-arm64@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.121.0.tgz#e58da1d3983d9292d6d47598a6808f2c92a56ef4" + integrity sha512-/Dd1xIXboYAicw+twT2utxPD7bL8qh7d3ej0qvaYIMj3/EgIrGR+tSnjCUkiCT6g6uTC0neSS4JY8LxhdSU/sA== + +"@oxc-parser/binding-darwin-arm64@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.121.0.tgz#0fb029403980e2f4470ff07e8385e3f2b01c7b01" + integrity sha512-A0jNEvv7QMtCO1yk205t3DWU9sWUjQ2KNF0hSVO5W9R9r/R1BIvzG01UQAfmtC0dQm7sCrs5puixurKSfr2bRQ== + +"@oxc-parser/binding-darwin-x64@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.121.0.tgz#037cc978673883264ded01c912dbcefbd0302ca3" + integrity sha512-SsHzipdxTKUs3I9EOAPmnIimEeJOemqRlRDOp9LIj+96wtxZejF51gNibmoGq8KoqbT1ssAI5po/E3J+vEtXGA== + +"@oxc-parser/binding-freebsd-x64@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.121.0.tgz#6dc183f7dc869a2475cb7e79af58967b3c6c4b64" + integrity sha512-v1APOTkCp+RWOIDAHRoaeW/UoaHF15a60E8eUL6kUQXh+i4K7PBwq2Wi7jm8p0ymID5/m/oC1w3W31Z/+r7HQw== + +"@oxc-parser/binding-linux-arm-gnueabihf@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.121.0.tgz#a3108e318ce2e1654849c4ea7cfa87e66f22e8a3" + integrity sha512-PmqPQuqHZyFVWA4ycr0eu4VnTMmq9laOHZd+8R359w6kzuNZPvmmunmNJ8ybkm769A0nCoVp3TJ6dUz7B3FYIQ== + +"@oxc-parser/binding-linux-arm-musleabihf@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.121.0.tgz#190df3a09f183055182c2f1635c8cc7296deeffb" + integrity sha512-vF24htj+MOH+Q7y9A8NuC6pUZu8t/C2Fr/kDOi2OcNf28oogr2xadBPXAbml802E8wRAVfbta6YLDQTearz+jw== + +"@oxc-parser/binding-linux-arm64-gnu@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.121.0.tgz#9bba1a976350714f39f38a0f7fd01e2d2d090e88" + integrity sha512-wjH8cIG2Lu/3d64iZpbYr73hREMgKAfu7fqpXjgM2S16y2zhTfDIp8EQjxO8vlDtKP5Rc7waZW72lh8nZtWrpA== + +"@oxc-parser/binding-linux-arm64-musl@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.121.0.tgz#7eb768a8551a78cf5388f0e24647b6f88914fb7c" + integrity sha512-qT663J/W8yQFw3dtscbEi9LKJevr20V7uWs2MPGTnvNZ3rm8anhhE16gXGpxDOHeg9raySaSHKhd4IGa3YZvuw== + +"@oxc-parser/binding-linux-ppc64-gnu@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.121.0.tgz#f2fb9187417fab075d830098c4c87b48f8115365" + integrity sha512-mYNe4NhVvDBbPkAP8JaVS8lC1dsoJZWH5WCjpw5E+sjhk1R08wt3NnXYUzum7tIiWPfgQxbCMcoxgeemFASbRw== + +"@oxc-parser/binding-linux-riscv64-gnu@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.121.0.tgz#534fe6c5054573fd06773b803e43777da70b96ca" + integrity sha512-+QiFoGxhAbaI/amqX567784cDyyuZIpinBrJNxUzb+/L2aBRX67mN6Jv40pqduHf15yYByI+K5gUEygCuv0z9w== + +"@oxc-parser/binding-linux-riscv64-musl@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.121.0.tgz#a557ef48dce6bb6da1d0035d9fe203b21ff6fd51" + integrity sha512-9ykEgyTa5JD/Uhv2sttbKnCfl2PieUfOjyxJC/oDL2UO0qtXOtjPLl7H8Kaj5G7p3hIvFgu3YWvAxvE0sqY+hQ== + +"@oxc-parser/binding-linux-s390x-gnu@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.121.0.tgz#a4779c98e0858af55a48a391e14fec6cc80941dd" + integrity sha512-DB1EW5VHZdc1lIRjOI3bW/wV6R6y0xlfvdVrqj6kKi7Ayu2U3UqUBdq9KviVkcUGd5Oq+dROqvUEEFRXGAM7EQ== + +"@oxc-parser/binding-linux-x64-gnu@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.121.0.tgz#5c5ce9f1d4c37dd85943cd29089b87a79a3373f6" + integrity sha512-s4lfobX9p4kPTclvMiH3gcQUd88VlnkMTF6n2MTMDAyX5FPNRhhRSFZK05Ykhf8Zy5NibV4PbGR6DnK7FGNN6A== + +"@oxc-parser/binding-linux-x64-musl@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.121.0.tgz#7c096e2ec6c53718f38acf4a0947efcfc30db565" + integrity sha512-P9KlyTpuBuMi3NRGpJO8MicuGZfOoqZVRP1WjOecwx8yk4L/+mrCRNc5egSi0byhuReblBF2oVoDSMgV9Bj4Hw== + +"@oxc-parser/binding-openharmony-arm64@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.121.0.tgz#cb9aaa37e238648a0313016d6f7929b352ca7403" + integrity sha512-R+4jrWOfF2OAPPhj3Eb3U5CaKNAH9/btMveMULIrcNW/hjfysFQlF8wE0GaVBr81dWz8JLgQlsxwctoL78JwXw== + +"@oxc-parser/binding-wasm32-wasi@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.121.0.tgz#e439fec1ea805979a2f75c8167a395568f20d6d6" + integrity sha512-5TFISkPTymKvsmIlKasPVTPuWxzCcrT8pM+p77+mtQbIZDd1UC8zww4CJcRI46kolmgrEX6QpKO8AvWMVZ+ifw== + dependencies: + "@napi-rs/wasm-runtime" "^1.1.1" + +"@oxc-parser/binding-win32-arm64-msvc@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.121.0.tgz#9cbc2116b6f62387a2a5c205b08734c6f67d0104" + integrity sha512-V0pxh4mql4XTt3aiEtRNUeBAUFOw5jzZNxPABLaOKAWrVzSr9+XUaB095lY7jqMf5t8vkfh8NManGB28zanYKw== + +"@oxc-parser/binding-win32-ia32-msvc@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.121.0.tgz#ac0ef57500be7e1af61241e0040d49cd6be1e597" + integrity sha512-4Ob1qvYMPnlF2N9rdmKdkQFdrq16QVcQwBsO8yiPZXof0fHKFF+LmQV501XFbi7lHyrKm8rlJRfQ/M8bZZPVLw== + +"@oxc-parser/binding-win32-x64-msvc@0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.121.0.tgz#142dadc748813ee0445a3a12003214ae28bf2c3f" + integrity sha512-BOp1KCzdboB1tPqoCPXgntgFs0jjeSyOXHzgxVFR7B/qfr3F8r4YDacHkTOUNXtDgM8YwKnkf3rE5gwALYX7NA== + +"@oxc-project/types@^0.121.0": + version "0.121.0" + resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.121.0.tgz#85c497d5dea608212ac041d52c8dd69a0343359c" + integrity sha512-CGtOARQb9tyv7ECgdAlFxi0Fv7lmzvmlm2rpD/RdijOO9rfk/JvB1CjT8EnoD+tjna/IYgKKw3IV7objRb+aYw== + +"@oxc-resolver/binding-android-arm-eabi@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.19.1.tgz#c44120aa5104e991e4a9969bb0b816263a6f4bc1" + integrity sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg== + +"@oxc-resolver/binding-android-arm64@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.19.1.tgz#bac86a9f2afda9cd6181ea1d1546448df579b740" + integrity sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA== + +"@oxc-resolver/binding-darwin-arm64@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.19.1.tgz#6bddb5b779cde0003dae0d703bf734e1536968f1" + integrity sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ== + +"@oxc-resolver/binding-darwin-x64@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.19.1.tgz#055331438a73b21d357fdef947b9c06ec9046104" + integrity sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ== + +"@oxc-resolver/binding-freebsd-x64@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.19.1.tgz#735ebe53bad7e935255a0eb072f74a43ffb032c4" + integrity sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw== + +"@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.19.1.tgz#2072f679e5a8485f3c0fe98f3bd57dfd4647a19d" + integrity sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A== + +"@oxc-resolver/binding-linux-arm-musleabihf@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.19.1.tgz#d74d0aa4738cc9f2af9e2015cbded0a5d34610dc" + integrity sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ== + +"@oxc-resolver/binding-linux-arm64-gnu@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.19.1.tgz#06aa9a330ecda461be6938969c1c4b67e969419e" + integrity sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig== + +"@oxc-resolver/binding-linux-arm64-musl@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.19.1.tgz#e951cb96e7f72a8b83d6379d19d4e2624fca8d6e" + integrity sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew== + +"@oxc-resolver/binding-linux-ppc64-gnu@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.19.1.tgz#b73a3d95b9b7a74d8aa95ddefc06b6ab73d8afc6" + integrity sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ== + +"@oxc-resolver/binding-linux-riscv64-gnu@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.19.1.tgz#a9d29b7ddc351824ca25c4aa9de6cd45cffb5d1f" + integrity sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w== + +"@oxc-resolver/binding-linux-riscv64-musl@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.19.1.tgz#0fcbb8ff0a09e1c61cbcd2a0803946aec0cb9b3b" + integrity sha512-YlRdeWb9j42p29ROh+h4eg/OQ3dTJlpHSa+84pUM9+p6i3djtPz1q55yLJhgW9XfDch7FN1pQ/Vd6YP+xfRIuw== + +"@oxc-resolver/binding-linux-s390x-gnu@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.19.1.tgz#1b7e32df63bf323e5ec765f536d2be14fba51787" + integrity sha512-EDpafVOQWF8/MJynsjOGFThcqhRHy417sRyLfQmeiamJ8qVhSKAn2Dn2VVKUGCjVB9C46VGjhNo7nOPUi1x6uA== + +"@oxc-resolver/binding-linux-x64-gnu@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.19.1.tgz#4ab2754f1b9521a3d0f00cb251d8b6603222f2f4" + integrity sha512-NxjZe+rqWhr+RT8/Ik+5ptA3oz7tUw361Wa5RWQXKnfqwSSHdHyrw6IdcTfYuml9dM856AlKWZIUXDmA9kkiBQ== + +"@oxc-resolver/binding-linux-x64-musl@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.19.1.tgz#91c3ae986004131726c78ba9aef6154daa9c238e" + integrity sha512-cM/hQwsO3ReJg5kR+SpI69DMfvNCp+A/eVR4b4YClE5bVZwz8rh2Nh05InhwI5HR/9cArbEkzMjcKgTHS6UaNw== + +"@oxc-resolver/binding-openharmony-arm64@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.19.1.tgz#305fe66843f4ba2499fdacf1aac00341b5968198" + integrity sha512-QF080IowFB0+9Rh6RcD19bdgh49BpQHUW5TajG1qvWHvmrQznTZZjYlgE2ltLXyKY+qs4F/v5xuX1XS7Is+3qA== + +"@oxc-resolver/binding-wasm32-wasi@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.19.1.tgz#0e885eb1fd6e80582cf7dfb756647fbc427cce1b" + integrity sha512-w8UCKhX826cP/ZLokXDS6+milN8y4X7zidsAttEdWlVoamTNf6lhBJldaWr3ukTDiye7s4HRcuPEPOXNC432Vg== + dependencies: + "@napi-rs/wasm-runtime" "^1.1.1" + +"@oxc-resolver/binding-win32-arm64-msvc@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.19.1.tgz#2e14871c6075520ebfd312eb9c6a15e753237b90" + integrity sha512-nJ4AsUVZrVKwnU/QRdzPCCrO0TrabBqgJ8pJhXITdZGYOV28TIYystV1VFLbQ7DtAcaBHpocT5/ZJnF78YJPtQ== + +"@oxc-resolver/binding-win32-ia32-msvc@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.19.1.tgz#b684992dd0fc6e8f88053bfa86327b932e4cc486" + integrity sha512-EW+ND5q2Tl+a3pH81l1QbfgbF3HmqgwLfDfVithRFheac8OTcnbXt/JxqD2GbDkb7xYEqy1zNaVFRr3oeG8npA== + +"@oxc-resolver/binding-win32-x64-msvc@11.19.1": + version "11.19.1" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.19.1.tgz#a48dccdcb0833da4957579b0e314666407990364" + integrity sha512-6hIU3RQu45B+VNTY4Ru8ppFwjVS/S5qwYyGhBotmjxfEKk41I2DlGtRfGJndZ5+6lneE2pwloqunlOyZuX/XAw== + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" @@ -1419,6 +1635,13 @@ resolved "https://registry.yarnpkg.com/@tweenjs/tween.js/-/tween.js-23.1.3.tgz#eff0245735c04a928bb19c026b58c2a56460539d" integrity sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA== +"@tybys/wasm-util@^0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz#ecddd3205cf1e2d5274649ff0eedd2991ed7f414" + integrity sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg== + dependencies: + tslib "^2.4.0" + "@types/aria-query@^5.0.1": version "5.0.4" resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz" @@ -3923,7 +4146,7 @@ fast-fifo@^1.2.0, fast-fifo@^1.3.2: resolved "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz" integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== -fast-glob@^3.3.2: +fast-glob@^3.3.2, fast-glob@^3.3.3: version "3.3.3" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== @@ -3958,6 +4181,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fd-package-json@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fd-package-json/-/fd-package-json-2.0.0.tgz#03f53ce5a0af552c2f4faf703a24e526310a2411" + integrity sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ== + dependencies: + walk-up-path "^4.0.0" + fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz" @@ -4052,6 +4282,13 @@ foreground-child@^3.1.0: cross-spawn "^7.0.6" signal-exit "^4.0.1" +formatly@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/formatly/-/formatly-0.3.0.tgz#5bb3b4e692f5a8c74ad8fe26154dd0a74aac6819" + integrity sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w== + dependencies: + fd-package-json "^2.0.0" + fraction.js@^5.3.4: version "5.3.4" resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz" @@ -4169,6 +4406,13 @@ get-symbol-description@^1.1.0: es-errors "^1.3.0" get-intrinsic "^1.2.6" +get-tsconfig@4.13.7: + version "4.13.7" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.7.tgz#b9d8b199b06033ceeea1a93df7ea5765415089bc" + integrity sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q== + dependencies: + resolve-pkg-maps "^1.0.0" + get-tsconfig@^4.7.5: version "4.13.6" resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz" @@ -4979,7 +5223,7 @@ jiti@^1.21.7: resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz" integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A== -jiti@^2.6.1: +jiti@^2.6.0, jiti@^2.6.1: version "2.6.1" resolved "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz" integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ== @@ -5091,6 +5335,27 @@ keyv@^4.5.4: dependencies: json-buffer "3.0.1" +knip@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/knip/-/knip-6.3.1.tgz#3cccdee10fe6a682171f0023d39ac1ed3edc0abc" + integrity sha512-22kLJloVcOVOAudCxlFOC0ICAMme7dKsS7pVTEnrmyKGpswb8ieznvAiSKUeFVDJhb01ect6dkDc1Ha1g1sPpg== + dependencies: + "@nodelib/fs.walk" "^1.2.3" + fast-glob "^3.3.3" + formatly "^0.3.0" + get-tsconfig "4.13.7" + jiti "^2.6.0" + minimist "^1.2.8" + oxc-parser "^0.121.0" + oxc-resolver "^11.19.1" + picocolors "^1.1.1" + picomatch "^4.0.1" + smol-toml "^1.6.1" + strip-json-comments "5.0.3" + unbash "^2.2.0" + yaml "^2.8.2" + zod "^4.1.11" + lazystream@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz" @@ -5668,7 +5933,7 @@ minimatch@^9.0.0, minimatch@^9.0.4, minimatch@^9.0.5: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.8: version "1.2.8" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -5941,6 +6206,60 @@ own-keys@^1.0.1: object-keys "^1.1.1" safe-push-apply "^1.0.0" +oxc-parser@^0.121.0: + version "0.121.0" + resolved "https://registry.yarnpkg.com/oxc-parser/-/oxc-parser-0.121.0.tgz#da2670453eecf84863b48eccad353f365b7518bf" + integrity sha512-ek9o58+SCv6AV7nchiAcUJy1DNE2CC5WRdBcO0mF+W4oRjNQfPO7b3pLjTHSFECpHkKGOZSQxx3hk8viIL5YCg== + dependencies: + "@oxc-project/types" "^0.121.0" + optionalDependencies: + "@oxc-parser/binding-android-arm-eabi" "0.121.0" + "@oxc-parser/binding-android-arm64" "0.121.0" + "@oxc-parser/binding-darwin-arm64" "0.121.0" + "@oxc-parser/binding-darwin-x64" "0.121.0" + "@oxc-parser/binding-freebsd-x64" "0.121.0" + "@oxc-parser/binding-linux-arm-gnueabihf" "0.121.0" + "@oxc-parser/binding-linux-arm-musleabihf" "0.121.0" + "@oxc-parser/binding-linux-arm64-gnu" "0.121.0" + "@oxc-parser/binding-linux-arm64-musl" "0.121.0" + "@oxc-parser/binding-linux-ppc64-gnu" "0.121.0" + "@oxc-parser/binding-linux-riscv64-gnu" "0.121.0" + "@oxc-parser/binding-linux-riscv64-musl" "0.121.0" + "@oxc-parser/binding-linux-s390x-gnu" "0.121.0" + "@oxc-parser/binding-linux-x64-gnu" "0.121.0" + "@oxc-parser/binding-linux-x64-musl" "0.121.0" + "@oxc-parser/binding-openharmony-arm64" "0.121.0" + "@oxc-parser/binding-wasm32-wasi" "0.121.0" + "@oxc-parser/binding-win32-arm64-msvc" "0.121.0" + "@oxc-parser/binding-win32-ia32-msvc" "0.121.0" + "@oxc-parser/binding-win32-x64-msvc" "0.121.0" + +oxc-resolver@^11.19.1: + version "11.19.1" + resolved "https://registry.yarnpkg.com/oxc-resolver/-/oxc-resolver-11.19.1.tgz#1b6b49812ae3469a2dcd10314444c2cb73a8d6a1" + integrity sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg== + optionalDependencies: + "@oxc-resolver/binding-android-arm-eabi" "11.19.1" + "@oxc-resolver/binding-android-arm64" "11.19.1" + "@oxc-resolver/binding-darwin-arm64" "11.19.1" + "@oxc-resolver/binding-darwin-x64" "11.19.1" + "@oxc-resolver/binding-freebsd-x64" "11.19.1" + "@oxc-resolver/binding-linux-arm-gnueabihf" "11.19.1" + "@oxc-resolver/binding-linux-arm-musleabihf" "11.19.1" + "@oxc-resolver/binding-linux-arm64-gnu" "11.19.1" + "@oxc-resolver/binding-linux-arm64-musl" "11.19.1" + "@oxc-resolver/binding-linux-ppc64-gnu" "11.19.1" + "@oxc-resolver/binding-linux-riscv64-gnu" "11.19.1" + "@oxc-resolver/binding-linux-riscv64-musl" "11.19.1" + "@oxc-resolver/binding-linux-s390x-gnu" "11.19.1" + "@oxc-resolver/binding-linux-x64-gnu" "11.19.1" + "@oxc-resolver/binding-linux-x64-musl" "11.19.1" + "@oxc-resolver/binding-openharmony-arm64" "11.19.1" + "@oxc-resolver/binding-wasm32-wasi" "11.19.1" + "@oxc-resolver/binding-win32-arm64-msvc" "11.19.1" + "@oxc-resolver/binding-win32-ia32-msvc" "11.19.1" + "@oxc-resolver/binding-win32-x64-msvc" "11.19.1" + p-limit@^3.0.2: version "3.1.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" @@ -6164,6 +6483,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +picomatch@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" + integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== + picomatch@^4.0.2, picomatch@^4.0.3: version "4.0.3" resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz" @@ -6979,6 +7303,11 @@ smart-buffer@^4.2.0: resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== +smol-toml@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/smol-toml/-/smol-toml-1.6.1.tgz#4fceb5f7c4b86c2544024ef686e12ff0983465be" + integrity sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg== + socket.io-client@^4.8.3: version "4.8.3" resolved "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz" @@ -7268,6 +7597,11 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" +strip-json-comments@5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-5.0.3.tgz#b7304249dd402ee67fd518ada993ab3593458bcf" + integrity sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw== + strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" @@ -7515,7 +7849,7 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.0.1, tslib@^2.1.0: +tslib@^2.0.1, tslib@^2.1.0, tslib@^2.4.0: version "2.8.1" resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -7607,6 +7941,11 @@ typescript@~5.8.3: resolved "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz" integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== +unbash@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/unbash/-/unbash-2.2.0.tgz#ab65fcb3903aa5f2bc5ff8f057129ac2dd5bb2db" + integrity sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w== + unbox-primitive@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz" @@ -7840,6 +8179,11 @@ wait-port@^1.1.0: commander "^9.3.0" debug "^4.3.4" +walk-up-path@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-4.0.0.tgz#590666dcf8146e2d72318164f1f2ac6ef51d4198" + integrity sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A== + wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" @@ -8097,6 +8441,11 @@ yallist@^3.0.2: resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yaml@^2.8.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.3.tgz#a0d6bd2efb3dd03c59370223701834e60409bd7d" + integrity sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg== + yargs-parser@^20.2.2, yargs-parser@^20.2.9: version "20.2.9" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" @@ -8185,7 +8534,7 @@ zip-stream@^6.0.1: resolved "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz" integrity sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ== -"zod@^3.25.0 || ^4.0.0": +"zod@^3.25.0 || ^4.0.0", zod@^4.1.11: version "4.3.6" resolved "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz" integrity sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==