Feat/refactor UI code (#52)

* Enhance autocomplete functionality and settings panel

- Added a new AutocompletePanel component for managing inline autocomplete settings, including options for enabling/disabling, debounce timing, and style configurations.
- Integrated autocomplete status tracking and logging within the panel to provide real-time feedback on the autocomplete engine's state.
- Updated settings navigation to include the new autocomplete settings route, improving user accessibility to autocomplete features.
- Introduced new Tauri commands for managing autocomplete operations, including start, stop, and current status retrieval, enhancing interaction with the autocomplete engine.
- Refactored existing code to streamline autocomplete-related functionalities and improve overall maintainability.

* Update TypeScript configuration and add new assets

- Modified `tsconfig.json` to adjust path aliases and include directories for improved module resolution.
- Added new SVG and image assets to the public directory, enhancing the application's visual resources.
- Introduced multiple Lottie animation JSON files for dynamic UI elements, expanding the application's animation capabilities.

* Update project structure and paths for Tauri integration

- Adjusted paths in the pull request template and various workflow files to reflect the new project structure, moving Tauri-related files under the `app` directory.
- Updated commands in the build and release workflows to ensure compatibility with the new file locations.
- Enhanced the test workflow to create the necessary `.env` file in the correct directory for end-to-end testing.
- Added new markdown files for agent prompts and configuration, establishing a foundation for OpenHuman's AI capabilities.

* Refactor project paths and update configurations for Tauri integration

- Adjusted script paths in package.json to reflect the new project structure, ensuring compatibility with the updated directory layout.
- Modified tsconfig.json to correct path aliases and include directories for improved module resolution.
- Introduced a new utility for resolving development paths, enhancing the ability to locate the `rust-core/ai` directory across different project structures.
- Updated Cargo.toml and tauri.conf.json to align with the new directory structure, ensuring proper resource and dependency management.
- Added a new dev_paths module to streamline path resolution logic, improving maintainability and clarity in the codebase.

* Refactor project structure and update configurations for Tauri integration

- Adjusted paths in .gitignore, Cargo.toml, and various scripts to reflect the new directory layout, moving Tauri-related files under the `app` directory.
- Introduced a new package.json file to manage workspace scripts and dependencies effectively.
- Updated end-to-end build and run scripts to ensure compatibility with the new project structure.
- Enhanced documentation in CONTRIBUTING.md to guide contributors on the updated project organization and Tauri command usage.

* Refactor Tauri command invocations to use dedicated utility functions

- Replaced direct `invoke` calls with utility functions from `tauriCommands` for improved readability and maintainability across multiple components.
- Updated `SkillsGrid`, `Skills`, `SkillProvider`, and `SkillManager` to utilize the new command structure, enhancing consistency in Tauri command handling.
- Introduced a new `coreRpcClient` for managing core RPC relay requests, streamlining error handling and request processing.
- Added a new `core_rpc_relay` command in the Tauri backend to facilitate communication with the core service, ensuring better service management and error reporting.

* Refactor Tauri command invocations in intelligence stats and memory manager

- Replaced direct `invoke` calls with utility functions from `tauriCommands` in `useIntelligenceStats` and `MemoryManager` for improved readability and maintainability.
- Updated the `aiListMemoryFiles`, `aiReadMemoryFile`, and `aiWriteMemoryFile` functions to utilize the new command structure, enhancing consistency in Tauri command handling.
- Introduced new command handling in the Rust backend for `ai.list_memory_files`, `ai.read_memory_file`, and `ai.write_memory_file`, streamlining communication with the core service.

* Refactor SkillsGrid and remove SelfEvolveModal component

- Removed the SelfEvolveModal component to streamline the SkillsGrid functionality.
- Updated the SkillsGrid to utilize the runtimeDiscoverSkills function for loading skills, replacing the previous invoke method.
- Simplified the skill entry normalization process by integrating it directly into the skills loading logic.
- Enhanced error handling during skill loading to improve robustness and user feedback.

* Refactor Tauri command invocations to use coreRpcClient

- Replaced direct `invoke` calls with `callCoreRpc` in various components, including `useIntelligenceStats`, `MemoryManager`, `SessionManager`, and `transcript` functions, enhancing code readability and maintainability.
- Updated the Rust backend to handle new command structures for memory and session management, streamlining communication with the core service.
- Improved consistency in handling Tauri commands across the application.

* Refactor Tauri command invocations to utilize coreRpcClient

- Replaced direct `invoke` calls with `callCoreRpc` in `tauriCommands.ts` and `tauriSocket.ts`, enhancing code readability and maintainability.
- Updated the Rust backend to support new command structures for authentication and session management, streamlining communication with the core service.
- Removed legacy socket reporting methods in `tauriSocket.ts`, reflecting a shift towards event-driven socket state management.
- Improved consistency in handling Tauri commands across the application, aligning with recent refactoring efforts.

* Remove pre-commit hook and update TODO list with completed tasks and new objectives. This includes separating the binary from the Tauri codebase, integrating accessibility service installation, and removing Android/iOS support from the codebase.

* Add core server functionality with dispatch and RPC handling

- Introduced new modules for core server operations, including dispatching RPC requests and handling various AI and memory-related commands.
- Implemented a robust structure for managing authentication, configuration, and session states through the `openhuman` namespace.
- Added helper functions for loading configurations, managing memory files, and processing authentication profiles.
- Established a new routing system using Axum for handling HTTP requests, including health checks and RPC endpoints.
- Enhanced error handling and logging throughout the new functionalities to improve maintainability and user feedback.

* Implement core server CLI and modular structure

- Introduced a new CLI module for the core server, enabling various commands for server management, health checks, and configuration settings.
- Established a modular structure for core server functionalities, including dispatching RPC requests and managing settings for models, memory, and runtime.
- Added comprehensive tests to validate the functionality of accessibility and autocomplete commands, ensuring robust error handling and schema compliance.
- Enhanced the overall organization of the core server codebase, improving maintainability and readability.

* Implement AI RPC dispatch functionality

- Introduced a new `ai_rpc` module for handling various AI-related commands, including memory file operations and session management.
- Enhanced the `try_dispatch` function to support commands such as listing, reading, writing memory files, and managing session states.
- Updated the core server dispatch module to integrate the new AI RPC functionality, improving modularity and maintainability.
- Refactored existing code to ensure consistent parameter parsing and error handling across AI commands.

* Update TODO list and refactor Rust core server files

- Added new tasks to the TODO list for documentation updates and feature flag cleanup.
- Introduced `Arc` import in `cli.rs` for improved concurrency handling.
- Cleaned up imports in `helpers.rs` and added conditional compilation for `tauri-host`.
- Removed unused `value_only` function in `types.rs` and added `#[allow(dead_code)]` to `SocketConnectParams` and `SocketEmitParams`.
- Enhanced `try_dispatch` function in `dispatch/mod.rs` for non-tauri-host scenarios.
- Updated `try_dispatch` in `openhuman/platform.rs` to correctly handle session parameters.
- Modified `screen_intelligence` configuration in tests to include new properties for better session management.

* Refactor import statements and enhance code readability

- Cleaned up import statements across multiple files for improved organization and consistency.
- Reformatted code in `cli.rs`, `helpers.rs`, and various dispatch modules to enhance readability.
- Ensured consistent parameter handling in `try_dispatch` functions, improving maintainability.
- Removed unnecessary whitespace and adjusted formatting for better code clarity.

* Refactor project structure and enhance AI memory management

- Consolidated the `openhuman-core` package into a single `Cargo.toml` file, removing the previous `rust-core` directory.
- Introduced new modules for AI memory management, including filesystem-based storage and encryption functionalities.
- Added Tauri commands for initializing memory and session management, enhancing user interaction with memory files.
- Implemented JSON-based storage for memory chunks and session transcripts, improving data accessibility and organization.
- Updated dependencies and features in `Cargo.toml` to support new functionalities and ensure compatibility.

* Update build and release workflows to reflect project structure changes

- Adjusted paths in GitHub Actions workflows to accommodate the consolidation of the `openhuman-core` package into a single `Cargo.toml`.
- Updated import statements in various files to point to the new locations of markdown resources.
- Modified the Tauri configuration to reflect the new resource paths, ensuring proper access to AI prompts.
- Enhanced the staging script to build the standalone binary from the updated project structure.

* Refactor AI directory resolution and update documentation

- Updated the logic for resolving AI directory paths to reflect the new project structure, replacing references to `rust-core/ai` with `src/ai/prompts`.
- Enhanced the `find_ai_directory` function across multiple modules to utilize the new path resolution methods.
- Updated documentation comments to clarify the new directory structure and fallback mechanisms for loading AI prompts.

* Rename `openhuman-core` to `openhuman` across the project

- Updated package names in `Cargo.toml` and `Cargo.lock` to reflect the new naming convention.
- Adjusted references in GitHub Actions workflows and scripts to use the new package name.
- Modified CLI command names and error messages to align with the updated naming.
- Ensured consistency in executable file names and paths throughout the codebase.

* Refactor project commands and update package scripts

- Updated package.json to change workspace references from `openhuman` to `app` for build, compile, dev, format, lint, and test scripts.
- Removed outdated memory and chat command files to streamline the codebase and improve maintainability.
- Adjusted the `lib.rs` file to reflect changes in memory command handling, transitioning to use `callCoreRpc` for Neocortex memory operations.
- Cleaned up the commands module by removing unused imports and consolidating functionality.

* Add Tauri host support and new daemon configuration

- Introduced new modules for Tauri host functionality, including `desktop` and `daemon_host`.
- Added static variables and initialization functions for managing the desktop app handle and resource directory.
- Updated import paths for `HeartbeatEngine` to improve clarity and organization.
- Implemented configuration loading and saving for daemon UI preferences, enhancing user experience.

* Update package names in project configuration

- Changed workspace references in package.json from `app` to `openhuman-app` for consistency.
- Updated the name field in the app's package.json to reflect the new naming convention.

* Remove Tauri host feature flags from core server modules

- Eliminated conditional compilation for Tauri host in `lib.rs`, `helpers.rs`, and `dispatch` modules.
- Streamlined socket management functions and dispatch logic by removing unused code related to Tauri host.
- Improved code clarity and maintainability by consolidating socket-related functionality.

* Enhance Tauri host feature integration and update dependencies

- Added `tauri-host` as a default feature in `Cargo.toml` to streamline feature management.
- Removed explicit feature flag from `openhuman` dependency in `app/src-tauri/Cargo.toml` for cleaner configuration.
- Updated Tauri command attributes in various modules to conditionally compile with the `tauri-host` feature, improving modularity.
- Expanded TODO list to include migration support from OpenClaw, indicating future development focus.

* Refactor authentication and credential management in OpenHuman

- Introduced new modules for handling authentication profiles and tokens, including `anthropic_token`, `openai_oauth`, and `profiles`.
- Removed unused Tauri host-related code from core server modules, enhancing clarity and maintainability.
- Updated `Cargo.toml` and `Cargo.lock` to reflect the removal of the `rquickjs` dependency and other package adjustments.
- Streamlined memory client initialization in dispatch logic to utilize the new `local_memory` module.
- Enhanced code organization by consolidating credential management functionalities and improving the overall structure of the OpenHuman module.

* Enhance Rust core RPC structure and streamline helper functions

- Introduced a dedicated `rpc.rs` file for each domain in the Rust core to manage JSON-RPC and CLI behavior, improving code organization and clarity.
- Refactored helper functions to utilize `rpc_invocation_from_outcome` for consistent handling of RPC responses across various modules.
- Removed unused authentication and credential management functions from `helpers.rs`, consolidating relevant logic into the new RPC structure.
- Updated dispatch logic in multiple modules to leverage the new RPC functions, enhancing maintainability and reducing code duplication.

* Enhance Rust core RPC structure and streamline helper functions

- Introduced a dedicated `rpc.rs` file for each domain in the Rust core to manage JSON-RPC and CLI behavior, improving code organization and clarity.
- Refactored helper functions to utilize `rpc_invocation_from_outcome` for consistent handling of RPC responses across various modules.
- Removed unused authentication and credential management functions from `helpers.rs`, consolidating relevant logic into the new RPC structure.
- Updated dispatch logic in multiple modules to leverage the new RPC functions, enhancing maintainability and reducing code duplication.

* Refactor OpenHuman configuration loading and enhance onboarding RPC

- Replaced the `load_openhuman_config` function with a new `load_config_with_timeout` method to improve timeout handling during configuration loading.
- Consolidated configuration loading logic across various modules, reducing redundancy and enhancing maintainability.
- Introduced new RPC functions for applying settings related to models, memory, screen intelligence, gateway, tunnel, runtime, and browser, streamlining the update process.
- Added onboarding helpers in a new `onboard` module, including a JSON-RPC controller for model refresh operations, improving onboarding flow management.

* Refactor CLI and configuration management in OpenHuman

- Consolidated CLI-related functionality by introducing new modules for settings and credentials management, enhancing code organization.
- Removed redundant functions and streamlined the configuration loading process, improving maintainability.
- Added new CLI helpers for screenshot tools and workspace initialization, facilitating better user experience and onboarding.
- Enhanced JSON-RPC responses to be more compatible with CLI requirements, ensuring consistent output across various commands.

* Remove gateway settings and related functionality from OpenHuman

- Eliminated the GatewaySettingsUpdate interface and associated functions from the codebase, streamlining configuration management.
- Removed references to gateway settings in the CLI and configuration modules, enhancing clarity and maintainability.
- Deleted the gateway module and its related components, including rate limiting and client handling, to simplify the architecture.
- Updated Cargo.toml and Cargo.lock to reflect the removal of dependencies related to gateway functionality.

* Update documentation and improve clarity in OpenHuman

- Revised comments in the `mod.rs`, `traits.rs`, and `pairing.rs` files to enhance clarity and accuracy.
- Updated descriptions related to security policy, long-running processes, and pairing functionality for better understanding.

* Refactor loading prop in TauriCommandsPanel for cleaner code

- Simplified the loading prop assignment in the TauriCommandsPanel component by removing unnecessary line breaks, enhancing readability and maintainability.

* Add OpenSSL dependency and implement OAuth authentication features

- Added OpenSSL as a dependency in `Cargo.toml` to support cryptographic operations.
- Introduced new OAuth-related structures and parameters in `types.rs` for handling authentication flows.
- Implemented OAuth connection and integration token fetching in `auth_socket.rs`, enhancing the authentication capabilities of the OpenHuman module.
- Created new modules for managing authentication profiles and responses, improving the organization of authentication-related code.
- Removed deprecated `anthropic_token` and `openai_oauth` modules to streamline credential management.
- Updated `Cargo.lock` to reflect the addition of the OpenSSL dependency.

* Refactor OpenHuman module and update dependencies

- Added OpenHuman integration entry in the registry for improved backend inference handling.
- Updated various files to enhance code clarity and organization, including adjustments to OAuth client methods and integration tests.
- Refactored import statements and removed unnecessary line breaks for better readability.
- Updated `Cargo.lock` to reflect changes in dependencies and ensure consistency across the project.

* Implement desktop host features and refactor runtime handling

- Introduced new modules for memory management, socket handling, and command definitions to support desktop host functionality.
- Refactored QuickJS runtime initialization to log errors when the engine is not linked, improving clarity on runtime status.
- Added placeholder commands for chat and model interactions, indicating unavailability in the desktop build while maintaining structure for future integration.
- Enhanced organization of the codebase by creating dedicated files for runtime and utility functions, streamlining the development process.
- Updated documentation to reflect new modules and their purposes, ensuring better understanding for future contributors.

* Add CLI banner and print function to enhance user experience

- Introduced a new CLI banner with branding and GitHub link for user engagement.
- Implemented a `print_cli_banner` function to display the banner when running the CLI, improving visibility and user interaction.
- Updated the CLI entry point to call the new banner function, ensuring it appears at startup.

* Add API integration and update dependencies

- Introduced new API modules for handling HTTP requests and WebSocket connections to the TinyHumans backend.
- Added `ureq` dependency for simplified HTTP client functionality, updating `Cargo.toml` and `Cargo.lock` accordingly.
- Implemented configuration and JWT handling in the new `api` module, enhancing session management and API interactions.
- Refactored existing code to utilize the new API helpers, improving code organization and maintainability.
- Updated documentation to reflect new API functionalities and usage guidelines.

* Refactor settings fetching and update dependencies

- Removed the `ureq` dependency and associated functions for fetching settings, streamlining the codebase.
- Updated the `fetch_settings` method to utilize `reqwest` for HTTP requests, enhancing consistency and reliability in API interactions.
- Adjusted the `Cargo.toml` to reflect the removal of `ureq`, ensuring dependencies are up to date.

* Update `ureq` dependency to version 3.3.0 in `Cargo.lock`

- Removed the specific version constraint for `ureq`, allowing for more flexibility in dependency resolution.
- Updated the `Cargo.lock` to reflect the new version of `ureq`, ensuring compatibility with recent changes in the codebase.

* Enhance JSON-RPC logging and CLI initialization

- Introduced a new `rpc_log` module for structured logging of JSON-RPC requests and responses, including redaction of sensitive parameters.
- Updated `execute_core_cli` to initialize logging with a default level and timestamp format.
- Enhanced logging in `rpc_handler` and `dispatch` functions to provide detailed insights into method calls and their execution times.
- Improved error handling logging to capture method failures with context, aiding in debugging and monitoring.

* Refactor HTTP server setup and add integration tests

- Introduced a new `build_core_http_router` function to encapsulate the HTTP routing logic, improving code organization and readability.
- Updated the `run_server` function to utilize the new router function, streamlining server initialization.
- Added comprehensive integration tests for the JSON-RPC API, ensuring robust functionality and error handling in real-world scenarios.

* Enhance OpenHuman backend integration and refactor provider handling

- Added support for the OpenHuman backend in the TauriCommandsPanel, including default configurations and validation for API keys.
- Introduced a new REPL command in the CLI for interactive RPC communication, allowing for dynamic mode switching and message handling.
- Refactored provider creation logic to streamline the integration of the OpenHuman backend, removing deprecated provider overrides and ensuring consistent usage across the codebase.
- Updated various components to improve error handling and user feedback related to provider selection and API interactions.

* Refactor provider handling and update default model settings

- Removed provider override states from the AgentChatPanel and TauriCommandsPanel components, simplifying state management.
- Updated local storage handling to exclude provider overrides, ensuring cleaner data storage.
- Changed default model settings across various components and backend configurations to use "neocortex-mk1" as the new default model.
- Enhanced error handling and validation logic in the TauriCommandsPanel, focusing on model and temperature settings.
- Streamlined integration tests and removed deprecated provider validation logic to improve code clarity and maintainability.

* Refactor code for improved readability and consistency

- Adjusted formatting in several files to enhance code clarity, including consistent parameter passing and alignment.
- Simplified match statement syntax in the `run_models` function for better readability.
- Streamlined assertions in tests to maintain consistency in error handling checks.
- Updated default model name handling in the `AgentBuilder` for cleaner initialization.

* Refactor API URL handling and enhance error reporting

- Updated the `effective_api_url` function to improve clarity in resolving the API base URL, incorporating environment variable checks.
- Enhanced diagnostics in the configuration check to provide clearer messages regarding the API URL status.
- Introduced new error formatting functions to improve the clarity of error messages related to API transport issues.
- Refactored error handling in the OpenAiCompatibleProvider to utilize the new error formatting, ensuring consistent and informative error reporting.

* Refactor API client initialization for consistency

- Updated the instantiation of `BackendOAuthClient` to consistently pass the API URL by reference across multiple functions.
- Simplified the match statement in the `run_models_refresh` function for improved readability.

* Enhance REPL command handling and add fallback mechanisms

- Improved error handling in the REPL command processing, providing clearer feedback for command execution failures.
- Introduced a fallback mechanism for the `agent_chat` RPC call, allowing for graceful degradation to a simpler chat method or a direct backend curl transport if the primary call fails.
- Added a new `backend_chat_via_curl` function to handle chat requests using curl as a last resort, ensuring continued functionality in case of RPC issues.
- Updated the `agent_chat_simple` function to support model overrides and temperature settings, enhancing flexibility in chat interactions.

* Update default Ollama model settings for consistency

- Changed the default Ollama model and vision model to "gemma3:4b-it-qat" for improved alignment across configurations.
- Ensured consistent model naming to enhance clarity in model usage within the local AI module.

* Implement login token consumption and enhance error handling

- Added functionality to consume login tokens via a new API endpoint, returning a JWT for authenticated sessions.
- Improved error handling in the Conversations component, introducing a fallback mechanism for chat interactions when the primary method is unavailable.
- Updated UserProvider to restore session tokens automatically, enhancing user experience during authentication.
- Refactored thread API to support the new login token consumption logic, ensuring seamless integration with the backend.

* Update HTTP client configuration to use Rustls TLS

- Replaced the HTTP/1.1 only setting with Rustls TLS in the OpenAiCompatibleProvider's client builder for enhanced security.
- Ensured consistent application of the new TLS setting across multiple client instances.

* Add Local AI command support and enhance error handling

- Introduced a new `LocalAi` command in the CLI for managing local AI runtime operations, including status checks, asset downloads, and prompt handling.
- Added detailed argument structures for various local AI functionalities, improving command usability.
- Enhanced error reporting in the `LocalAiService` by including response details in error messages for better debugging and user feedback.
- Refactored existing error handling to provide clearer context on failures during API interactions.

* Add local AI module with Ollama integration and model management

- Introduced a new local AI module that includes functionality for automatic installation of the Ollama runtime across different operating systems (Windows, macOS, Linux).
- Implemented model ID resolution and management, providing default settings for various AI models and ensuring compatibility with user configurations.
- Added HTTP API structures and request handling for Ollama, enabling interaction with the local AI service for generating responses and managing assets.
- Developed utility functions for parsing model outputs and managing workspace paths, enhancing the overall structure and usability of the local AI service.
- Established a comprehensive service layer for managing local AI operations, including status tracking and error handling for improved user experience.

* Refactor local AI service structure and enhance asset management

- Simplified the local AI module by reorganizing the service structure, introducing new modules for model IDs, paths, and asset management.
- Added comprehensive asset status tracking for various AI models, including chat, vision, embedding, STT, and TTS, with improved error handling.
- Implemented methods for downloading models and assets, ensuring better management of local AI resources.
- Updated visibility of service methods to enhance encapsulation and maintainability within the local AI service.

* Enhance local AI module with new download progress tracking and unit tests

- Added new structures for tracking download progress of various AI models, including detailed status and metrics.
- Implemented unit tests for model ID resolution, parsing suggestions, and asset path resolution to ensure robust functionality.
- Refactored service methods to improve encapsulation and maintainability, enhancing the overall structure of the local AI service.
- Updated existing tests to cover new functionalities and ensure consistent behavior across the module.

* Implement new local AI download functionalities and refactor model management

- Added support for downloading all local AI assets and tracking download progress, enhancing user experience and resource management.
- Introduced new RPC methods for fetching download progress and managing asset states, improving the overall functionality of the local AI module.
- Refactored existing model management code to utilize the new model catalog, ensuring better organization and maintainability.
- Updated relevant tests to cover new functionalities and ensure consistent behavior across the local AI service.

* Add new interfaces and functions for local AI download progress tracking

- Introduced `LocalAiDownloadProgressItem` and `LocalAiDownloadsProgress` interfaces to structure download progress data for various AI models.
- Implemented `openhumanLocalAiDownloadAllAssets` and `openhumanLocalAiDownloadsProgress` functions to facilitate downloading all assets and tracking their progress.
- Enhanced error handling for Tauri environment checks in new functions, ensuring robust operation within the local AI module.

* Refactor agent loop structure and introduce modular components

- Deleted the `loop_.rs` file and reorganized the agent loop into multiple modules for better maintainability and clarity.
- Introduced new files for handling credentials, history management, tool instructions, memory context, and parsing logic.
- Implemented functions for scrubbing sensitive credentials, managing conversation history, and building tool instructions.
- Enhanced the overall structure of the agent loop to facilitate easier testing and future development.

* Refactor authentication structure and migrate to credentials module

- Moved authentication-related functionality from `auth_profiles` to a new `credentials` module for better organization and clarity.
- Updated references in the API and core server to reflect the new module structure.
- Introduced new data structures and methods for managing authentication profiles, including session support and response handling.
- Removed the obsolete `auth_profiles` module to streamline the codebase and enhance maintainability.

* Add screen intelligence module with capture and context management

- Introduced new modules for screen capture and context management, specifically targeting macOS.
- Implemented functionality to capture screen images and retrieve foreground application context.
- Added data structures for managing application context and window bounds.
- Established limits for screenshot sizes and context character counts to ensure efficient resource management.
- Enhanced helper functions for input action validation and vision summary processing.
- Set up a modular structure for better maintainability and future enhancements.

* Refactor screen intelligence module and remove obsolete components

- Deleted unused files related to screen intelligence, including context and permissions management, to streamline the codebase.
- Refactored the capture functionality to improve organization and maintainability.
- Updated function signatures for better clarity and consistency.
- Enhanced the overall structure of the screen intelligence module for future development and testing.

* Enhance autocomplete CLI functionality and refactor related code

- Added new options for the autocomplete command in the CLI, allowing users to run the autocomplete loop in the current process or spawn a detached process.
- Introduced `AutocompleteStartCliOptions` struct to encapsulate the new command-line arguments.
- Refactored the `autocomplete_start_cli` function to handle the new options and improve process management for the autocomplete service.
- Updated documentation in `CLAUDE.md` to clarify the separation of concerns between routing and controller logic in the codebase.

* Enhance autocomplete error handling and improve focused text context retrieval

- Added a new function to identify "no text candidate" errors, improving error management in the autocomplete engine.
- Refactored the `focused_text_context` and `focused_text_context_verbose` functions to enhance clarity and reliability in retrieving application context.
- Updated the return format of the `focused_text_context_verbose` function to use a separator for better data parsing.
- Added a new TODO item for allowing users to select LLM model versions based on their CPU capabilities.

* Remove Docker, Native, and WASM runtime implementations along with related traits and tests

- Deleted the DockerRuntime, NativeRuntime, and WasmRuntime implementations to streamline the codebase.
- Removed associated traits and factory functions for runtime creation.
- Eliminated all related tests to ensure a clean removal of unused components.
- This refactor aims to simplify the runtime management and prepare for future enhancements.

* Add quickjs-runtime feature and introduce runtime module

- Added a new feature flag for `quickjs-runtime` in `Cargo.toml` to enable its usage.
- Created a new `runtime.rs` module to implement `NativeRuntime` and `DockerRuntime` with associated traits for runtime management.
- Updated the `skills` module to reference the correct path for `SkillConfig`.
- Removed the obsolete `skillforge` module from the `openhuman` namespace to streamline the codebase.
- Enhanced the `skills` module with new structures and functions for managing skills, including initialization and loading logic.

* Refactor autocomplete configuration to remove legacy disabled apps

- Updated the default configuration for `AutocompleteConfig` to remove the legacy disabled apps ('terminal' and 'code'), allowing for broader usage of Codex/CLI.
- Introduced a migration function to handle legacy disabled apps during configuration loading, ensuring custom user preferences remain intact.
- This change enhances the flexibility of the autocomplete feature by preventing unnecessary restrictions on application usage.

* Update rquickjs dependencies in Cargo.lock

- Updated the rquickjs and rquickjs-core dependencies to versions 0.11.0 and 0.9.0 respectively, ensuring compatibility with the latest features and fixes.
- Added new entries for rquickjs-sys and its corresponding version 0.9.0 to the dependency list, enhancing the project's runtime capabilities.
- This update improves the overall stability and performance of the application by leveraging the latest improvements in the rquickjs ecosystem.

* Add terminal application detection to autocomplete logic

- Introduced a new function `is_terminal_app` to identify terminal applications based on their names, enhancing the autocomplete feature's context awareness.
- Updated the `focused_text_context_verbose` function to allow terminal applications to bypass text role checks when the input value is not empty, improving user experience in terminal environments.
- This change aims to provide better support for terminal-based applications in the autocomplete system.

* Add terminal input context extraction and noise line detection

- Introduced functions to identify terminal-like buffers and filter out noise lines in terminal input, enhancing the autocomplete engine's context awareness.
- Updated the `focused_text_context` logic to utilize the new terminal context extraction, improving the handling of text in terminal applications.
- Enhanced the `focused_text_context_verbose` function to better retrieve static text values from UI elements, ensuring accurate context representation in terminal environments.
- These changes aim to improve user experience and functionality for terminal-based applications in the autocomplete system.

* Enhance autocomplete engine state management and error handling

- Added new fields `last_escape_down` and `last_overlay_signature` to `EngineState` for improved state tracking.
- Implemented `try_reject_via_escape` method to handle escape key interactions, allowing users to reject suggestions more intuitively.
- Updated error handling to display notifications for different states (ready, accepted, rejected, error) using `show_overflow_badge`.
- Refactored state updates to ensure consistent management of suggestion and phase transitions, enhancing overall user experience in the autocomplete system.

* Implement periodic status logging in autocomplete service

- Added a polling mechanism to log the status of the autocomplete engine at regular intervals.
- Enhanced logging to capture changes in phase, application name, suggestions, and errors, improving visibility during service execution.
- Refactored the `autocomplete_start_cli` function to integrate the new logging functionality, ensuring a more informative user experience while the service is running.

* Refactor memory dispatch logic and remove local memory implementation

- Updated the memory dispatch functions to utilize the new `memory_rpc` module, enhancing the handling of memory operations such as document management and namespace queries.
- Removed the local memory implementation, including database interactions and related functions, to streamline the codebase and improve maintainability.
- Introduced new RPC calls for document operations (put, list, delete) and context queries, ensuring a more efficient and consistent approach to memory management.
- This refactor aims to enhance the overall architecture and performance of the memory handling system.

* Remove macOS-specific overflow badge functionality and related helper functions

- Deleted the `show_overflow_badge` and `escape_applescript_string` functions, which were specific to macOS, to streamline the codebase.
- Refactored the `show_overflow_badge` function to provide a no-op implementation for non-macOS platforms, enhancing cross-platform compatibility.
- This change simplifies the autocomplete module by removing platform-dependent code, improving maintainability and clarity.

* Enhance text application logic in autocomplete module

- Updated the `apply_text_to_focused_field` function to improve interaction with focused UI elements on macOS.
- The new implementation retrieves the current value of the focused element and appends the provided text, ensuring better handling of text input.
- Enhanced error reporting to include stderr output when applying suggestions fails, improving debugging capabilities.
- These changes aim to provide a more robust and user-friendly experience in the autocomplete functionality.

* Refactor Landlock feature configuration for Linux support

- Moved the `landlock` and `rppal` dependencies under a conditional target configuration for Linux in both `Cargo.toml` files, ensuring they are only included when building for Linux.
- Updated the `landlock.rs` module to check for both the `sandbox-landlock` feature and the Linux target OS, improving the conditional compilation logic.
- This change enhances cross-platform compatibility and ensures that Landlock functionality is only available on supported systems.

* Update dependencies and enhance Tauri integration

- Updated the Tauri dependency in `Cargo.toml` to include the `tray-icon` feature, enabling system tray support.
- Introduced a new `rust-toolchain.toml` file to pin the Rust version to 1.93.0, ensuring compatibility with the matrix-sdk.
- Modified GitHub workflows to use the specified Rust version from `rust-toolchain.toml` instead of the stable version, improving build consistency.
- Refactored Tauri commands to utilize a new `wrapCommandResult` function for better response handling.
- Added a new `tray` module in `openhuman` for managing system tray functionality, enhancing the desktop experience.
- Updated various command implementations to streamline service management and improve error handling.

* Refactor core process handling and enhance encryption features

- Removed the `openhuman` dependency from `Cargo.lock` and `Cargo.toml`, streamlining the project structure.
- Updated the core process handling to fall back to a child process when in-process execution is unavailable, improving error handling and logging.
- Introduced new encryption commands (`ai_init_encryption`, `ai_encrypt`, `ai_decrypt`) to enhance security features, utilizing AES-GCM for data protection.
- Added a new `tray` module for managing system tray functionality, improving user experience on desktop platforms.
- Refactored various command implementations to improve service management and error handling, ensuring a more robust application architecture.

* Remove unused modules and refactor daemon host configuration

- Deleted the `daemon_host_config`, `memory`, `models`, `openhuman_daemon`, `tray`, `chat`, `conscious_loop`, and `runtime` modules to streamline the codebase.
- Refactored the daemon host configuration logic into the `openhuman` module, consolidating related functionality.
- Updated command implementations to utilize the new configuration methods, ensuring consistent handling of daemon host settings.
- This cleanup enhances maintainability and reduces complexity in the project structure.

* Refactor memory management and update Tauri dependencies

- Removed the `tray-icon` feature from the Tauri dependency in `Cargo.toml` to streamline the configuration.
- Deleted the `core:tray:default` capability from the default capabilities JSON, simplifying the capabilities structure.
- Refactored memory handling in tests to utilize `UnifiedMemory` instead of `SqliteMemory`, enhancing consistency across memory operations.
- Updated memory store, recall, and forget functionalities to support a global namespace, improving memory management and retrieval processes.
- Enhanced error handling and logging in memory operations to provide clearer feedback during execution.

* Remove AI encryption commands and related functionality

- Deleted the `ai_init_encryption`, `ai_encrypt`, and `ai_decrypt` functions to streamline the codebase and remove unused features.
- Updated the command registration in the `run` function to reflect the removal of these encryption commands, enhancing maintainability and reducing complexity.

* Add OAuth integration token handling and channel connection management

- Introduced functions to fetch and encrypt integration tokens using OAuth, enhancing security for token management.
- Updated the channel connections API to support OAuth integration, including listing, connecting, and disconnecting channels.
- Implemented checks for supported channels and authentication modes, improving the robustness of channel connection handling.
- Enhanced error handling for integration token retrieval to ensure required fields are present before proceeding.

* Refactor project structure and update documentation

- Renamed the project from "Outsourced" to "OpenHuman" and revised the project summary to reflect its focus on AI-powered assistance for crypto communities.
- Restructured the repository layout, detailing the purpose of each directory and its contents.
- Updated runtime scope to clarify platform support and Tauri's desktop-only focus.
- Enhanced documentation across various files, including architecture, services, and routing, to improve clarity and usability for contributors.
- Removed outdated sections and streamlined commands for development and production builds, ensuring consistency in the documentation.

* Implement REPL session management and multimodal support

- Introduced a new REPL session management system, allowing for session-specific interactions with agents.
- Added functions for starting, chatting, resetting, and ending REPL sessions, enhancing user experience and control.
- Implemented multimodal message handling, enabling the processing of images alongside text in user messages.
- Updated the project structure to include new modules for identity and multimodal functionalities, improving organization and maintainability.
- Enhanced error handling and logging for session operations, providing clearer feedback during execution.

* Refactor memory store implementation and introduce unified memory management

- Removed the legacy memory store implementation and replaced it with a new unified memory management system.
- Introduced a `MemoryClient` for handling document storage, retrieval, and namespace management.
- Added support for key-value storage and graph data structures within the unified memory framework.
- Enhanced the `UnifiedMemory` struct with methods for document upsertion, querying, and namespace operations.
- Updated the project structure to include new modules for memory types, factories, and traits, improving organization and maintainability.
- Improved error handling and logging across memory operations for clearer feedback during execution.

* Implement QuickJS skill instance management

- Removed the previous QjsSkillInstance implementation and replaced it with a new modular structure.
- Introduced separate modules for event loop management, instance handling, JavaScript handlers, and utility functions.
- Enhanced the event loop to efficiently manage QuickJS runtime tasks, including timer callbacks and message processing.
- Added support for asynchronous tool calls and lifecycle management within the QuickJS context.
- Improved error handling and logging throughout the new implementation for better debugging and user feedback.
- Updated documentation to reflect the new structure and functionality of the QuickJS skill instance.
This commit is contained in:
Steven Enamakel
2026-03-29 10:30:18 -07:00
committed by GitHub
parent c03b1dc2d5
commit 244702d349
851 changed files with 32695 additions and 52534 deletions
+1 -1
View File
@@ -16,7 +16,7 @@
## Testing
- [ ] `yarn -s compile`
- [ ] `cargo check --manifest-path src-tauri/Cargo.toml`
- [ ] `cargo check --manifest-path app/src-tauri/Cargo.toml`
- [ ] Other checks run (list commands)
- [ ] Manual validation completed (list scenarios)
+8 -7
View File
@@ -30,7 +30,7 @@ jobs:
cache: 'yarn'
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@1.93.0
with:
targets: x86_64-unknown-linux-gnu
@@ -44,7 +44,7 @@ jobs:
id: cargo-lock-fingerprint
shell: bash
run: |
echo "hash=$(tail -n +8 src-tauri/Cargo.lock | openssl dgst -sha256 | awk '{print $2}')" >> "$GITHUB_OUTPUT"
echo "hash=$(tail -n +8 Cargo.lock | openssl dgst -sha256 | awk '{print $2}')" >> "$GITHUB_OUTPUT"
- name: Cache Cargo registry and git sources
uses: actions/cache@v4
@@ -73,16 +73,17 @@ jobs:
run: cd skills && yarn install --frozen-lockfile
- name: Build sidecar core binary
run: cargo build --manifest-path rust-core/Cargo.toml --release --target x86_64-unknown-linux-gnu --bin openhuman
run: cargo build --manifest-path Cargo.toml --release --target x86_64-unknown-linux-gnu --bin openhuman
- name: Stage sidecar for Tauri bundler
run: |
mkdir -p src-tauri/binaries
# Workspace root target/ (not rust-core/target/) — see root Cargo.toml [workspace]
cp target/x86_64-unknown-linux-gnu/release/openhuman src-tauri/binaries/openhuman-x86_64-unknown-linux-gnu
chmod +x src-tauri/binaries/openhuman-x86_64-unknown-linux-gnu
mkdir -p app/src-tauri/binaries
# Release artifacts for the root package land in repo root target/
cp target/x86_64-unknown-linux-gnu/release/openhuman app/src-tauri/binaries/openhuman-x86_64-unknown-linux-gnu
chmod +x app/src-tauri/binaries/openhuman-x86_64-unknown-linux-gnu
- name: Build Tauri app
working-directory: app
run: |
TAURI_CONFIG_OVERRIDE='{"bundle":{"createUpdaterArtifacts":false}}'
yarn tauri build -c "$TAURI_CONFIG_OVERRIDE" --bundles none -- --target x86_64-unknown-linux-gnu
+18 -15
View File
@@ -79,9 +79,9 @@ jobs:
throw new Error(`Invalid release_type: ${releaseType}`);
}
const packagePath = 'package.json';
const tauriPath = 'src-tauri/tauri.conf.json';
const cargoPath = 'src-tauri/Cargo.toml';
const packagePath = 'app/package.json';
const tauriPath = 'app/src-tauri/tauri.conf.json';
const cargoPath = 'app/src-tauri/Cargo.toml';
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
const match = String(pkg.version || '').match(/^(\d+)\.(\d+)\.(\d+)$/);
@@ -119,7 +119,7 @@ jobs:
`$1${nextVersion}$3`,
);
if (updatedCargo === cargo) {
throw new Error('Failed to update [package].version in src-tauri/Cargo.toml');
throw new Error('Failed to update [package].version in app/src-tauri/Cargo.toml');
}
fs.writeFileSync(cargoPath, updatedCargo);
@@ -147,7 +147,7 @@ jobs:
VERSION: ${{ steps.bump.outputs.version }}
TAG: ${{ steps.bump.outputs.tag }}
run: |
git add package.json src-tauri/tauri.conf.json src-tauri/Cargo.toml
git add app/package.json app/src-tauri/tauri.conf.json app/src-tauri/Cargo.toml
git commit -m "chore(release): v${VERSION}"
git push origin main
@@ -244,8 +244,8 @@ jobs:
node-version: 24.x
cache: yarn
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Install Rust (rust-toolchain.toml)
uses: dtolnay/rust-toolchain@1.93.0
with:
targets: ${{ matrix.settings.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
@@ -260,7 +260,7 @@ jobs:
id: cargo-lock-fingerprint
shell: bash
run: |
echo "hash=$(tail -n +8 src-tauri/Cargo.lock | openssl dgst -sha256 | awk '{print $2}')" >> "$GITHUB_OUTPUT"
echo "hash=$(tail -n +8 Cargo.lock | openssl dgst -sha256 | awk '{print $2}')" >> "$GITHUB_OUTPUT"
- name: Cache Cargo registry and git sources
uses: actions/cache@v4
@@ -287,7 +287,7 @@ jobs:
env:
BASE_URL: ${{ vars.BASE_URL }}
UPDATER_PUBLIC_KEY: ${{ secrets.UPDATER_PUBLIC_KEY }}
WITH_UPDATER: 'true'
WITH_UPDATER: "true"
with:
script: |
const workspacePath = process.env.GITHUB_WORKSPACE.replace(/\\/g, '/');
@@ -313,12 +313,14 @@ jobs:
CORE_DIR="openhuman_core"
elif [ -f "rust-core/Cargo.toml" ]; then
CORE_DIR="rust-core"
elif [ -f "Cargo.toml" ] && grep -q '^name = "openhuman"' Cargo.toml; then
CORE_DIR="."
else
echo "No core Cargo manifest found (expected openhuman_core/Cargo.toml or rust-core/Cargo.toml)"
echo "No core Cargo manifest found (expected root Cargo.toml with openhuman, openhuman_core/Cargo.toml, or rust-core/Cargo.toml)"
exit 1
fi
SIDE_CAR_BASE="$(node -e "const fs=require('fs');const c=JSON.parse(fs.readFileSync('src-tauri/tauri.conf.json','utf8'));const b=(c.bundle&&Array.isArray(c.bundle.externalBin)&&c.bundle.externalBin[0])||'binaries/openhuman';process.stdout.write(String(b).split('/').pop());")"
SIDE_CAR_BASE="$(node -e "const fs=require('fs');const c=JSON.parse(fs.readFileSync('app/src-tauri/tauri.conf.json','utf8'));const b=(c.bundle&&Array.isArray(c.bundle.externalBin)&&c.bundle.externalBin[0])||'binaries/openhuman';process.stdout.write(String(b).split('/').pop());")"
CORE_BIN_NAME="${SIDE_CAR_BASE}"
echo "core_dir=$CORE_DIR" >> "$GITHUB_OUTPUT"
@@ -346,9 +348,9 @@ jobs:
- name: Stage sidecar for Tauri bundler
shell: bash
run: |
mkdir -p src-tauri/binaries
mkdir -p app/src-tauri/binaries
SOURCE="$CORE_TARGET_DIR/$CORE_BIN_NAME"
DEST="src-tauri/binaries/$SIDECAR_BASE-$MATRIX_TARGET"
DEST="app/src-tauri/binaries/$SIDECAR_BASE-$MATRIX_TARGET"
cp "$SOURCE" "$DEST"
chmod +x "$DEST"
env:
@@ -370,9 +372,10 @@ jobs:
BASE_URL: ${{ vars.BASE_URL }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY || secrets.UPDATER_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD || secrets.UPDATER_PRIVATE_KEY_PASSWORD }}
WITH_UPDATER: 'true'
WITH_UPDATER: "true"
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.settings.platform == 'macos-latest' && '10.15' || '' }}
with:
projectPath: app
# Tools discovery now uses a JS mock registry (no Rust discovery
# binary target), so there is no extra helper executable for Tauri
# to copy/sign inside release app bundles.
@@ -391,7 +394,7 @@ jobs:
if: matrix.settings.platform == 'macos-latest'
shell: bash
run: |
APP_PATH="src-tauri/target/${{ matrix.settings.target }}/release/bundle/macos/OpenHuman.app"
APP_PATH="target/${{ matrix.settings.target }}/release/bundle/macos/OpenHuman.app"
echo "Inspecting bundle at: $APP_PATH"
ls -la "$APP_PATH/Contents/MacOS"
ls -la "$APP_PATH/Contents/Resources" | grep openhuman || true
+11 -11
View File
@@ -26,7 +26,7 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 24.x
cache: 'yarn'
cache: "yarn"
- name: Cache node modules
id: yarn-cache
@@ -64,8 +64,8 @@ jobs:
with:
fetch-depth: 1
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Install Rust (rust-toolchain.toml)
uses: dtolnay/rust-toolchain@1.93.0
with:
targets: x86_64-unknown-linux-gnu
@@ -91,11 +91,11 @@ jobs:
restore-keys: |
${{ runner.os }}-rust-test-cargo-
- name: Test rust-core (openhuman-core)
run: cargo test -p openhuman-core
- name: Test rust-core (openhuman)
run: cargo test -p openhuman
- name: Test src-tauri (OpenHuman)
run: cargo test -p OpenHuman
run: cargo test --manifest-path app/src-tauri/Cargo.toml
e2e-macos:
name: E2E (macOS / Appium)
@@ -112,10 +112,10 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 24.x
cache: 'yarn'
cache: "yarn"
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Install Rust (rust-toolchain.toml)
uses: dtolnay/rust-toolchain@1.93.0
- name: Install dependencies
run: yarn install --frozen-lockfile
@@ -124,7 +124,7 @@ jobs:
run: cd skills && yarn install --frozen-lockfile
- name: Ensure .env exists for E2E build
run: touch .env
run: touch app/.env
- name: Install Appium and mac2 driver
run: |
@@ -134,7 +134,7 @@ jobs:
- name: Build E2E app bundle
run: yarn test:e2e:build
env:
E2E_SKIP_CARGO_CLEAN: '1'
E2E_SKIP_CARGO_CLEAN: "1"
- name: Run all E2E flows
run: yarn test:e2e:all:flows
+1 -1
View File
@@ -44,7 +44,7 @@ jobs:
run: yarn install --frozen-lockfile
- name: Type check TypeScript files
run: npx tsc
run: yarn typecheck
env:
NODE_ENV: test
+1 -2
View File
@@ -8,7 +8,6 @@ pnpm-debug.log*
lerna-debug.log*
package-lock.json
yarn.lock
node_modules
dist
@@ -35,7 +34,7 @@ scripts/ci-secrets.local.json
*.sln
*.sw?
references/
src-tauri/runtime-skill-*
app/src-tauri/runtime-skill-*
.mypy_cache
.ruff_cache
.kotlin
View File
+99 -452
View File
@@ -1,520 +1,167 @@
## Project Summary
# OpenHuman
Cross-platform crypto community communication platform built with **Tauri v2** (React 19 + Rust). Targets desktop (Windows, macOS) and mobile (Android, iOS). Features deep Telegram integration via MTProto, real-time Socket.io communication, V8-based skill execution engine, and an MCP (Model Context Protocol) tool system for AI-driven Telegram interactions.
**AI-powered assistant for crypto communities — React + Tauri v2 desktop app with a Rust core (JSON-RPC / CLI) and sandboxed QuickJS skills.**
## Runtime Scope
This file orients contributors and coding agents. Authoritative narrative architecture: [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md). Frontend layout: [`docs/src/README.md`](docs/src/README.md). Tauri shell: [`docs/src-tauri/README.md`](docs/src-tauri/README.md).
- Tauri host/runtime is desktop-only.
- Always run and validate Tauri codepaths as desktop (`windows`, `macOS`, `linux`).
- Do not add or maintain Android/iOS/web runtime branches inside `src-tauri`.
---
## App Theme & Design System
## Repository layout
**Design Philosophy**: Premium, sophisticated crypto platform with calm, trustworthy aesthetic.
| Path | Role |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`app/`** | Yarn workspace **`openhuman-app`**: Vite + React (`app/src/`), Tauri desktop host (`app/src-tauri/`), Vitest tests |
| **Repo root `src/`** | Rust library **`openhuman_core`** and **`openhuman`** CLI binary (`src/bin/openhuman.rs`) — `core_server`, `openhuman::*` domains, skills runtime (QuickJS / `rquickjs`), MCP routing in the core process |
| **`skills/`** | Skill packages (built into `skills/skills` for bundling) |
| **`Cargo.toml`** (root) | Core crate; `cargo build --bin openhuman` produces the sidecar the UI stages via `app`s `core:stage` |
| **`docs/`** | Architecture and module guides (numbered pages under `docs/src/`, `docs/src-tauri/`) |
### Color Palette
Commands in documentation assume the **repo root** unless noted: `yarn dev` runs the `app` workspace.
- **Primary**: Ocean blue (`#4A83DD`) optimized for dark backgrounds
- **Sage**: Success green (`#4DC46F`) for growth indicators
- **Amber**: Warning (`#E8A838`) for attention states
- **Coral**: Error (`#F56565`) soft professional red
- **Canvas**: Background layers (`#FAFAF9` to `#D4D4D1`) with subtle warmth
- **Market Colors**: Bullish green, bearish red, Bitcoin orange, Ethereum purple
---
### Typography
## Runtime scope
- **Primary**: Inter (premium font stack)
- **Display**: Cabinet Grotesk for headings
- **Mono**: JetBrains Mono for code
- **Scale**: Sophisticated sizing with negative letter spacing for elegance
- **Shipped product**: desktop — Windows, macOS, Linux (see [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) “Platform reach”).
- **Tauri host** (`app/src-tauri`): **desktop-only** (`compile_error!` for non-desktop targets). Do not add Android/iOS branches inside `app/src-tauri`.
- **Core binary** (`openhuman`): spawned/staged as a **sidecar**; the Web UI talks to it over HTTP (`core_rpc_relay` + `core_rpc` client), not by re-implementing domain logic in the shell.
### Component System
---
- **Shadows**: Glow effects, subtle to float depth levels
- **Animations**: Fade-in, slide-in, scale-in with cubic-bezier easing
- **Border Radius**: Smooth system from `xs` (0.25rem) to `5xl` (2rem)
- **Spacing**: Extended scale including custom values (4.5, 13, 15, etc.)
### Current UI State
- Uses HashRouter (not BrowserRouter) as seen in `App.tsx:1`
- 153 TypeScript files total in src/
- Sophisticated Tailwind config with custom color system and animations
## Commands
## Commands (from repository root)
```bash
# Frontend dev server only (port 1420)
# Frontend + Tauri dev (workspace delegates to app/)
yarn dev
# Desktop dev with hot-reload (starts Vite + Tauri)
# Desktop with Tauri (loads env via scripts/load-dotenv.sh)
yarn tauri dev
# Desktop dev with enhanced debugging (RUST_BACKTRACE and RUST_LOG enabled)
yarn dev:app
# Production UI build (app workspace)
yarn build
# Production build (TypeScript compile + Vite build + Tauri bundle)
yarn tauri build
# Typecheck / lint / format (app workspace)
yarn typecheck
yarn lint
yarn format
yarn format:check
# Debug build with .app bundle (required for deep link testing on macOS)
# On macOS, openhuman:// only works when running the .app, not `tauri dev`
yarn tauri build --debug --bundles app
yarn macos:dev
# Stage openhuman core binary next to Tauri resources (required for core RPC)
cd app && yarn core:stage
# Android
yarn tauri android dev
yarn tauri android build
# Skills (under skills/)
yarn workspace openhuman-app skills:build
yarn workspace openhuman-app skills:watch
# iOS
yarn tauri ios dev
yarn tauri ios build
# Rust — core library + CLI (repo root)
cargo check --manifest-path Cargo.toml
cargo build --manifest-path Cargo.toml --bin openhuman
# Skills development
yarn skills:build # Build skills in development mode
yarn skills:watch # Watch skills for changes
# AI Configuration
yarn tools:generate # Discover tools from V8 runtime and generate TOOLS.md
# Rust checks
cargo check --manifest-path src-tauri/Cargo.toml
cargo clippy --manifest-path src-tauri/Cargo.toml
# Rust — Tauri shell only
cargo check --manifest-path app/src-tauri/Cargo.toml
```
No test framework is currently configured. **ESLint and Prettier are configured** with Husky pre-commit/pre-push hooks for code quality enforcement.
**Tests**: Vitest in `app/` (`yarn test`, `yarn test:coverage`). Rust tests via `cargo test` at repo root as wired in `app/package.json`.
## Architecture
**Quality**: ESLint + Prettier + Husky in the `app` workspace.
### Provider Chain (App.tsx)
---
The app wraps in this order: `Redux Provider``PersistGate``SocketProvider``TelegramProvider``HashRouter``AppRoutes`. **Note**: Now uses HashRouter instead of BrowserRouter. This ordering matters because Socket.io and Telegram providers depend on Redux auth state.
## Frontend (`app/src/`)
### State Management (Redux Toolkit + Persist)
### Provider chain (`app/src/App.tsx`)
State lives in `src/store/` using Redux Toolkit slices:
Order matters for auth and realtime:
- **authSlice** — JWT token, onboarding completion flag (persisted)
- **userSlice** — user profile
- **socketSlice** — connection status, socket ID
- **telegramSlice** — connection/auth status, chats, messages, threads (selectively persisted; loading/error states excluded)
- **aiSlice** — AI system state, memory management, session tracking
- **skillsSlice** — skills catalog, setup status, management state, V8 runtime integration
- **teamSlice** — team management, member invites, permissions
`Redux Provider``PersistGate`**`UserProvider`** → **`SocketProvider`** → **`AIProvider`** → **`SkillProvider`** → **`HashRouter`** → `AppRoutes`.
Redux Persist stores auth and telegram state (storage backend is configurable; default uses localStorage). The telegram slice has a complex nested structure in `src/store/telegram/` with separate files for types, reducers, extraReducers, and thunks.
There is **no** `TelegramProvider` in the current tree; Telegram may appear in UI copy or legacy settings, but MTProto is not an active provider here.
### LocalStorage
### State (`app/src/store/`)
- **Do not use `localStorage` (or `sessionStorage`) for app state or feature logic.** Use Redux (and Redux Persist where needed) instead.
- **Remove any existing `localStorage` usage** when touching related code. User-scoped data (auth, onboarding, Telegram session, socket state) lives in Redux, keyed by user id where applicable. Telegram session is in `telegram.byUser[userId].sessionString`, not localStorage.
- **Exceptions**: Redux-persist may use a localStorage-backed storage adapter by default; that is the persistence layer, not app logic. Any other remaining usage (e.g. deep-link `deepLinkHandled` flag) should be migrated to Redux or similar when that code is modified.
- **General rule**: Avoid adding new `localStorage` or `sessionStorage` usage; prefer Redux and remove existing usage when you work on affected areas.
Redux Toolkit slices include **auth**, **user**, **socket**, **ai**, **skills**, **team**, and related modules. Prefer Redux (and persist where configured) over ad hoc `localStorage` for app state; see project rules for exceptions.
### Service Layer (Singletons)
### Services (`app/src/services/`)
- **mtprotoService** (`src/services/mtprotoService.ts`) — Telegram MTProto client via `telegram` npm package. Session stored in Redux (`telegram.byUser[userId].sessionString`), not localStorage. Auto-retries FLOOD_WAIT up to 60s.
- **socketService** (`src/services/socketService.ts`) — Socket.io client. Auth token passed in socket `auth` object (not query string). Transports: polling first, then WebSocket. Enhanced with Rust-native Socket.io client for persistent connections.
- **apiClient** (`src/services/apiClient.ts`) — HTTP client for REST backend.
Singleton-style modules include **`apiClient`**, **`socketService`**, **`coreRpcClient`** (HTTP bridge to the core process), and domain **`api/*`** clients. There is **no** `mtprotoService` in this tree.
### MCP System (`src/lib/mcp/`)
### MCP (`app/src/lib/mcp/`)
Model Context Protocol implementation for AI tool execution over Socket.io:
Transport, validation, and types for JSON-RPC-style messaging over Socket.io — **not** a large Telegram tool pack. Tooling for agents is driven by the **skills** system and backend; see `agentToolRegistry.ts` and core RPC.
- `transport.ts` — Socket.io JSON-RPC 2.0 transport with 30s timeout
- `telegram/server.ts` — TelegramMCPServer manages 99 tool definitions
- `telegram/tools/` — Individual tool files (one per Telegram API operation)
- Tools use `big-integer` library for Telegram's large integer IDs
### Routing (`app/src/AppRoutes.tsx`)
### Routing (`src/AppRoutes.tsx`)
Hash routes include `/`, `/onboarding`, `/mnemonic`, `/home`, `/intelligence`, `/skills`, `/conversations`, `/invites`, `/agents`, `/settings/*`, plus `DefaultRedirect`. **No** dedicated `/login` route in `AppRoutes` (auth flows use the welcome/onboarding paths).
```
/ → Welcome (public)
/login → Login (public)
/onboarding → Onboarding (protected, requires auth, not yet onboarded)
/home → Home (protected, requires auth + onboarded)
* → DefaultRedirect (routes based on auth state)
```
### AI configuration
`PublicRoute` redirects authenticated users away. `ProtectedRoute` enforces auth and optionally onboarding status.
Bundled prompts live under **`src/ai/prompts/`** at the **repository root** (also bundled via `app/src-tauri/tauri.conf.json` `resources`). Loaders under `app/src/lib/ai/` use `?raw` imports, optional remote fetch, and in Tauri **`ai_get_config` / `ai_refresh_config`** for packaged content.
### Deep Link Auth Flow
---
Web-to-desktop handoff using `openhuman://` URL scheme:
## Tauri shell (`app/src-tauri/`)
1. User authenticates in browser
2. Browser redirects to `openhuman://auth?token=<loginToken>`
3. Tauri catches the deep link, Rust `exchange_token` command calls backend via `reqwest` (bypasses CORS)
4. Backend returns `sessionToken` + user object
5. App stores session in Redux, navigates to onboarding/home
Thin desktop host: window management, daemon health bridging, **core process lifecycle** (`core_process`, `CoreProcessHandle`), and **JSON-RPC relay** to the **`openhuman`** sidecar (`core_rpc_relay`, `core_rpc`).
Key file: `src/utils/desktopDeepLinkListener.ts` (lazy-loaded in `main.tsx`). Uses a `deepLinkHandled` flag to prevent infinite reload loops. Deep links do NOT work in `tauri dev` on macOS — must use built `.app` bundle.
Registered IPC commands (see [`docs/src-tauri/02-commands.md`](docs/src-tauri/02-commands.md)) include **`greet`**, **`write_ai_config_file`**, **`ai_get_config`**, **`ai_refresh_config`**, **`core_rpc_relay`**, **window** commands, and **OpenHuman service / daemon host** helpers (`openhuman_*`).
### Rust Backend (`src-tauri/src/lib.rs`)
Deep link plugin is registered where supported; behavior is platform-specific (see platform notes below).
Enhanced Rust backend with comprehensive skill execution and runtime management:
---
**Core Commands:**
## Rust core (repo root `src/`)
- `greet` — demo command
- `exchange_token` — CORS-free HTTP POST to backend for token exchange (desktop only)
- **`openhuman/`** — Domain logic (skills, memory, channels, config, …). RPC controllers live in **`rpc.rs`** files per domain; use **`RpcOutcome<T>`** pattern per [`AGENTS.md`](AGENTS.md) / internal rules.
- **`src/openhuman/` module layout**: **New** functionality must live in a **dedicated subdirectory** (its own folder/module, e.g. `openhuman/my_domain/mod.rs` plus related files, or a new subfolder under an existing domain). Do **not** add new standalone `*.rs` files directly at `src/openhuman/` root; place new code in a module directory and declare it from `mod.rs` (or merge into an existing domain folder).
- **`core_server/`** — Transport only: Axum/HTTP, JSON-RPC envelope, CLI parsing, **dispatch** (`core_server::dispatch`) — **no** heavy business logic here.
- **Layering**: Implementation in `openhuman::<domain>/`, controllers in `openhuman::<domain>/rpc.rs`, routes in `core_server/`.
**Runtime Management:**
Skills runtime uses **QuickJS** (`rquickjs`) in **`src/openhuman/skills/`** (e.g. `qjs_skill_instance.rs`, `qjs_engine.rs`), not V8/deno_core in this repository.
- `discover_skills` — V8 skill discovery and manifest parsing
- `enable_skill` / `disable_skill` — skill lifecycle management
- `get_skill_preferences` / `set_skill_preferences` — skill configuration
- `connect_to_socket` — Rust-native Socket.io connection
- `get_socket_status` — connection status monitoring
---
**Android Support:**
## App theme & design system
- `RuntimeService` — background service for skill execution
- Notification permissions and foreground service management
- Android logging integration with logcat
**Design intent**: Premium, calm crypto UI — ocean primary (`#4A83DD`), sage / amber / coral semantic colors, Inter + Cabinet Grotesk + JetBrains Mono, Tailwind with custom radii/spacing/shadows. Details: [`docs/DESIGN_GUIDELINES.md`](docs/DESIGN_GUIDELINES.md).
Deep link plugin registered at setup. `register_all()` called only on Windows/Linux (panics on macOS).
---
### V8 Runtime System (`src-tauri/src/runtime/`)
## Environment variables (Vite)
Advanced JavaScript execution engine for skills using V8 (via deno_core):
Set in `.env` for the **`app`** workspace (`VITE_*` exposed to the client):
**Core Components:**
| Variable | Purpose |
| ------------------------- | ------------------------------------------------------------------------- |
| `VITE_BACKEND_URL` | API base (default in code: production API; see `app/src/utils/config.ts`) |
| `VITE_SENTRY_DSN` | Optional Sentry DSN |
| `VITE_SKILLS_GITHUB_REPO` | Skills catalog GitHub repo slug |
- `v8_engine.rs` — V8 JavaScript runtime initialization and management
- `v8_skill_instance.rs` — Individual skill execution contexts and lifecycle
- `skill_registry.rs` — Skill discovery, registration, and state management
- `manifest.rs` — Skill manifest parsing with platform compatibility checks
- `socket_manager.rs` — Persistent Socket.io connections with reconnection logic
- `cron_scheduler.rs` — Scheduled task execution for time-based skills
- `preferences.rs` — Skill configuration and settings persistence
---
**Bridge System (`src-tauri/src/runtime/bridge/`):**
## Git workflow
- `skills_bridge.rs` — Skill-to-skill communication and state sharing
- `tauri_bridge.rs` — Frontend-backend IPC and environment access
- `net.rs` — HTTP/fetch operations for skills
- `db.rs` — Database operations and storage management
- `store.rs` — Key-value storage for skill data
- `log_bridge.rs` — Structured logging from skills
- `cron_bridge.rs` — Cron job scheduling and management
- **Public repo**; push to your working branch; PRs target **`main`**.
- Use [`.github/pull_request_template.md`](.github/pull_request_template.md); AI-generated PR text should follow its sections and checklist.
**Quickjs Integration (`src-tauri/src/services/quickjs/`):**
---
- `service.rs` — High-level client management with V8 integration
- `bootstrap.js` — V8 JavaScript bootstrap environment
- `ops/mod.rs` — Native operations for WebSocket, timers, and async handling
- `storage.rs` — Persistent storage for sessions and data
## Key patterns (concise)
**Platform Support:**
- **`src/openhuman/`**: New features go in a **folder/module**, not new root-level `src/openhuman/*.rs` files (see Rust core section).
- **File size**: Prefer ≤ ~500 lines per source file; split modules when growing.
- **Pre-merge checks** (when touching code): Prettier, ESLint, `tsc --noEmit` in `app/`; `cargo fmt` + `cargo check` for changed Rust (`Cargo.toml` at root and/or `app/src-tauri/Cargo.toml` as appropriate).
- **No dynamic imports** in app code (static `import` only); use try/catch around Tauri APIs where needed.
- **Type-only imports**: `import type` where appropriate.
- **Dual socket / tool sync**: If you change realtime protocol, keep **frontend** (`socketService` / MCP transport) and **core** socket behavior aligned (see [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) dual-socket section).
- Desktop platforms: Full V8 runtime with all features
- Mobile platforms: Error handling with feature availability checks
- Platform-specific skill filtering based on manifest declarations
---
## Environment Variables
## Platform notes
Set in `.env` (Vite exposes `VITE_*` prefixed vars):
- **macOS deep links**: Often require a built **`.app`** bundle; not only `tauri dev`. See [`docs/telegram-login-desktop.md`](docs/telegram-login-desktop.md) if applicable.
- **`window.__TAURI__`**: Not assumed at module load; guard Tauri usage accordingly.
- **Core sidecar**: Must be staged/built so `core_rpc` can reach the `openhuman` binary (see `scripts/stage-core-sidecar.mjs`).
| Variable | Purpose |
| --------------------------- | ------------------------------------------------------------------- |
| `VITE_BACKEND_URL` | Backend API URL (default: `http://localhost:5005`) |
| `VITE_SENTRY_DSN` | Sentry DSN for error reporting (optional) |
| `VITE_DEBUG` | Debug mode flag |
| `OPENHUMAN_DAEMON_INTERNAL` | Force internal daemon mode (default: false, uses external services) |
---
Production defaults are in `src/utils/config.ts`.
## AI Configuration System
OpenHumanuses an OpenClaw-compliant AI configuration system that automatically injects persona and tool context into every user message for consistent AI behavior.
### Configuration Files
All AI configuration lives in the `/ai/` directory:
- **`/ai/SOUL.md`** - AI personality, voice, tone, and behavior patterns
- **`/ai/TOOLS.md`** - Auto-generated documentation of all available tools (generated via `yarn tools:generate`)
- **`/ai/IDENTITY.md`** - Core identity and values (TODO)
- **`/ai/AGENTS.md`** - Agent roles and specializations (TODO)
- **`/ai/USER.md`** - User adaptation strategies (TODO)
- **`/ai/BOOTSTRAP.md`** - Initialization procedures (TODO)
- **`/ai/MEMORY.md`** - Long-term knowledge and patterns (TODO)
### Modular Loader System
```typescript
// Individual loaders with multi-layer caching
loadSoul() SoulConfig // Personality, voice, behavior
loadTools() ToolsConfig // Available tools and capabilities
// Unified loader
loadAIConfig() AIConfig // Combined SOUL + TOOLS configuration
```
**Caching Strategy:**
- Memory cache (immediate)
- localStorage cache (30min TTL)
- GitHub remote (latest)
- Bundled fallback (reliable)
**TODO**: Set up public AI configuration repository to eliminate 404 fallback errors
- Current: AI config loaders try GitHub URLs first (fail with 404), then fallback to bundled files
- Console shows: "Failed to load resource: the server responded with a status of 404"
- Affected: Settings → AI Configuration "Refresh Soul/Tools" buttons
- Files: `src/lib/ai/soul/loader.ts`, `src/lib/ai/tools/loader.ts`
### Unified Injection System
Every user message automatically gets AI context injected:
```typescript
// Unified injection (recommended)
import { injectAll } from '../lib/ai/injector';
// Individual injections (for specific needs)
import { injectSoul, injectTools } from '../lib/ai/injector';
const injectedMessage = await injectAll(userMessage);
const soulMessage = await injectSoul(userMessage);
const toolsMessage = await injectTools(userMessage);
```
**Message Format:**
```
[PERSONA_CONTEXT]
I am OpenHuman that incredibly smart, funny friend who loves helping people get stuff done
Personality: Curious & Enthusiastic, Witty & Engaging, Empathetic
Voice: Conversational, Use humor naturally but don't force it
[/PERSONA_CONTEXT]
[TOOLS_CONTEXT]
4 tools across 3 skills
Categories: Communication (2), Productivity (1), Email (1)
Key skills: telegram, notion, gmail
[/TOOLS_CONTEXT]
User message: Hello!
```
### Dynamic TOOLS.md Generation
TOOLS.md is automatically generated from the V8 skills runtime:
```bash
# Discover tools and generate documentation
yarn tools:generate
# Integration in build pipeline
yarn skills:build && yarn tools:generate && tsc && vite build
```
**Process:**
1. **Discovery**: Spawns Tauri runtime to call `runtime_all_tools()`
2. **Parsing**: Extracts tool definitions with JSON Schema
3. **Formatting**: Generates OpenClaw-compliant markdown
4. **Bundling**: Includes in app for AI context injection
**Generated Output:**
- Professional documentation with usage examples
- Environment-specific configurations
- Tool categorization by skill
- Statistics and metadata
### Integration Points
AI context injection happens in 4 places:
1. **`src/pages/Conversations.tsx`** - Main chat interface
2. **`src/store/threadSlice.ts`** - Redux sendMessage thunk
3. **`src/services/api/threadApi.ts`** - API layer
4. **`src/utils/tauriCommands.ts`** - Tauri agent chat
All use the unified `injectAll()` function for consistency.
### Settings UI
View and manage AI configuration in **Settings → AI Configuration**:
- Live SOUL personality preview
- TOOLS statistics and categories
- Individual refresh buttons
- Source indicators (GitHub vs bundled)
- Combined "Refresh All" functionality
## Recent Changes
Key updates from recent commits (cd9ebcd to current):
### Major Runtime Transition
- **V8 Runtime Migration** (`99c20ea`, `0f6a092`): Complete transition from QuickJS to V8
- Replaced QuickJS with V8 (via deno_core) for improved JavaScript execution and WASM support
- Enhanced skill management with V8 runtime including improved performance and compatibility
- New V8 skill instance handling with advanced execution contexts
- Updated dependencies and Cargo.toml to reflect V8 integration
- Platform compatibility checks and enhanced manifest handling
### Android Platform Support
- **Full Android Integration** (`ce06cfc`, `a2578b9`): Production-ready mobile platform support
- Complete Android project generation with MainActivity and RuntimeService
- Background service for persistent skill execution on Android
- Notification permission handling and foreground service management
- Android logging integration with logcat for better debugging
- Deep link support configuration in AndroidManifest.xml
### Enhanced Socket & Runtime Management
- **Rust-Native Socket.io Client** (`68d397e`): Persistent connection infrastructure
- Native Rust Socket.io implementation for improved reliability
- Enhanced socket connection handling with reconnection logic
- Dynamic backend URL configuration support
- Improved error handling and connection status monitoring
### Skills System Improvements
- **Advanced Skill Management** (`e841c86`, `719e6e5`): Enhanced skill lifecycle and configuration
- Skill setup pipeline with contextual Enable/Setup/Configure/Retry buttons
- Platform filtering for skills with manifest-based compatibility checks
- Enhanced skill status derivation and connection indicators
- Environment variable exposure to skills (whitelisted values)
- Improved skill discovery and manifest processing with logging
### Major Additions
- **ESLint & Prettier Integration** (`5896966`): Complete code quality toolchain
- ES module syntax for ESLint configuration with enhanced TypeScript support
- Husky pre-commit/pre-push hooks for automatic formatting and linting
- Type-only imports standardization across codebase
- Consolidated import statements and improved code organization
- GitHub workflows updated with Prettier and ESLint checks
- **Advanced Skills System** (`10ec1b3`): Comprehensive skill management platform
- Dynamic skills loading from local directory via Rust integration
- SkillSetupModal with conditional rendering (wizard vs management panel)
- Background GitHub sync for skills catalog updates
- Skills table with setup status indicators and management controls
- Enhanced skill metadata with setup hooks and descriptions
- **Team Management Features** (`10ec1b3`): Multi-user collaboration system
- TeamPanel, TeamMembersPanel, and TeamInvitesPanel components
- Redux state management for teams, members, and invites
- Team API integration with CRUD operations
- Settings modal routing for team management paths
- Role-based permissions and invitation system
- **AI System Enhancements**: Advanced memory and session management
- Hybrid search with encryption for AI memory
- Constitution-based AI behavior with GitHub integration
- Entity graph migration to Neo4j backend
- Session capture and transcript management
- Memory chunking and context formatting
- **Enhanced CI/CD Pipeline** (`b1d7bce`): Production-ready deployment
- XGH_TOKEN authentication for openhumanxyz/openhuman releases
- Python sidecar setup and caching for cross-platform builds
- Tauri configuration updates (com.openhuman.app identifier)
- GitHub Pages deployment with optimized workflows
- Version tagging and environment variable management
- **Device Detection & Download System** (`9d74721`, `b5bccd2`): Enhanced multi-architecture download support
- Optimized asset parsing using Maps for unique architecture links per platform
- Enhanced DownloadScreen.tsx with architecture-specific download options
- Improved device detection for Windows, macOS, Linux, and Android platforms
- Added preference logic for more specific filenames in asset parsing
- Support for multiple architectures (x64, aarch64) with intelligent sorting
- **Version Bump**: Project updated to v0.20.0 (`891517c`)
### Design System Updates
- **Settings Modal UI**: Clean 520px white modal contrasting with glass morphism theme
- **Animations**: 200ms entry animations, 250ms panel transitions, chevron hover effects
- **Lottie Animations**: Integrated into onboarding flow (`334673e`)
- **Connection Components**: Added Telegram and Gmail connection indicators
- **Routing**: Switched to HashRouter for better desktop app compatibility
- **Theme**: Implemented sophisticated color system with premium crypto aesthetic
### Component Structure
- **200+ TypeScript files** across `src/` directory with comprehensive tooling
- **AI System Architecture** (`src/lib/ai/`): Advanced artificial intelligence platform
- Memory management with encryption, chunking, and hybrid search
- Constitution-based behavior with GitHub integration
- Entity graph with Neo4j backend integration
- Session capture, transcript management, and tool compression
- Provider system with OpenAI integration and custom providers
- **Skills Management System**: Dynamic skill platform with Rust integration
- SkillsGrid.tsx - Skills catalog with setup status and management
- SkillSetupModal.tsx - Conditional wizard/management panel rendering
- SkillProvider.tsx - GitHub sync and local directory integration
- Skills submodule integration with background updates
- **Team Collaboration Features**: Multi-user workspace management
- TeamPanel.tsx - Team overview with member management
- TeamMembersPanel.tsx - Member roles and permissions
- TeamInvitesPanel.tsx - Invitation system with role assignment
- Team API integration with Redux state management
- **Settings Modal System**: Comprehensive configuration interface
- SettingsModal.tsx - Main container with URL routing
- SettingsLayout.tsx - Modal wrapper with createPortal
- Enhanced panels: Billing, Team, Connections, Privacy, Profile
- Hooks: useSettingsNavigation.ts, useSettingsAnimation.ts
- **Download System**: Enhanced multi-platform distribution
- DownloadScreen.tsx - Platform detection with architecture support
- deviceDetection.ts - Comprehensive device/architecture utilities
- GitHub API integration for real-time release assets
- **Code Quality Infrastructure**: ESLint, Prettier, and Husky integration
- Pre-commit/pre-push hooks with TypeScript compilation checks
- Standardized type-only imports and consolidated statements
- GitHub workflow integration with automated quality checks
## Git Workflow
- **Repository visibility**: The project is public.
- **Push target**: Pushes should go to your working branch in the public repository (or your fork if your access model requires it).
- **PR target**: Open pull requests against the public upstream repository, targeting the **`main`** branch.
- **PR template required**: Use `.github/pull_request_template.md` for all pull requests.
- **AI tooling rule**: Any AI-generated PR description must follow the template sections in order and keep all checklist items.
## Key Patterns
- **MANDATORY: Pre-completion checks**: Before considering ANY task complete, ALWAYS run these checks and fix all errors:
1. `npx prettier --check .` (formatting)
2. `npx eslint .` (lint)
3. `npx tsc --noEmit` (TypeScript)
4. `cargo fmt --manifest-path src-tauri/Cargo.toml` (Rust formatting, if Rust files were changed)
5. `cargo check --manifest-path src-tauri/Cargo.toml` (Rust compilation, if Rust files were changed)
- **Code Quality**: ESLint and Prettier enforce code standards with Husky hooks. Use type-only imports (`import type`) and consolidate imports from same modules.
- **Frontend scope**: The frontend (`src/`) is primarily a REST API client to `rust-core/`; Rust code in `src-tauri/` manages the core process lifecycle and bridge surface.
- **Core feature exposure**: Any new feature added in `rust-core/` must be exposed through both the CLI and a REST API so it can be integrated into the UI without backend rewrites.
- **No dynamic imports**: All imports must be static `import` statements at the top of the file. Do not use `await import()` or `import().then()` inside functions or code blocks. Use try/catch around Tauri API calls for non-Tauri environments instead.
- **No localStorage**: Avoid `localStorage` and `sessionStorage`; use Redux (and persist) for app state. Remove any direct usage when working on affected code.
- **AI System Integration**: Use `src/lib/ai/` for memory management, constitution loading, entity queries, and session capture. AI providers abstracted through interface pattern.
- **AI Configuration System**: OpenClaw-compliant AI configuration with dynamic TOOLS.md generation. Use `loadSoul()`, `loadTools()`, `loadAIConfig()` for configuration loading, and `injectAll()` for unified SOUL + TOOLS injection into user messages.
- **V8 Skills Runtime**: Skills execute in V8 JavaScript engine on desktop platforms. Use `SkillProvider` for GitHub sync, `SkillsGrid` for management interface, and Rust runtime commands for lifecycle management. Platform filtering ensures skills only run on supported platforms.
- **Team Collaboration**: Team features in `src/components/settings/panels/Team*`. Use Redux `teamSlice` for state management and `teamApi` for backend operations.
- **Device Detection**: Use `deviceDetection.ts` utilities for platform/architecture detection. Support multiple architectures per platform (x64, aarch64) with intelligent preference logic.
- **GitHub Integration**: Fetch release assets via GitHub API (`fetchLatestRelease()`) and parse by architecture (`parseReleaseAssetsByArchitecture()`). Use Maps for efficient unique architecture tracking.
- **Download System**: Platform-specific file type support (.exe/.msi for Windows, .dmg for macOS, .AppImage/.deb/.rpm for Linux, .apk for Android) with fallback links.
- **Modal System**: Settings modal uses `createPortal` pattern with URL-based routing. Clean white design (not glass morphism) for system settings. Navigate with `/settings` paths for different panels.
- **Component Reuse**: Connection management reuses `connectOptions` array and components from onboarding flow. Maintains consistent UX patterns across features.
- **Redux Integration**: Multiple slices (auth, user, telegram, ai, skills, team) with Redux Persist. Use typed hooks and selectors. State functions accept optional `userId` param.
- **Node polyfills**: Vite config (`vite.config.ts`) polyfills `buffer`, `process`, `util`, `os`, `crypto`, `stream` for the `telegram` package which requires Node APIs.
- **Telegram IDs**: Use `big-integer` library, not native JS numbers (Telegram IDs exceed `Number.MAX_SAFE_INTEGER`).
- **MCP tool files**: Each tool in `src/lib/mcp/telegram/tools/` exports a handler conforming to `TelegramMCPToolHandler` interface. Tool names are typed in `src/lib/mcp/telegram/types.ts`.
- **Tauri IPC**: Frontend calls Rust via `invoke()` from `@tauri-apps/api/core`. Rust commands are registered in `generate_handler![]` macro. Enhanced with runtime management commands for V8 skill execution and Socket.io integration.
- **CORS workaround**: External HTTP requests from the WebView hit CORS. Use Rust `reqwest` via Tauri commands instead of browser `fetch()`.
- **Hash Routing**: Uses HashRouter for desktop app compatibility and deep link handling.
- **Integration Libraries**: Each integration (Telegram, future Gmail, etc.) lives under `src/lib/<integration>/` with its own `state/`, `services/`, `api/` subdirectories. Domain-specific services belong in the integration folder, not in `src/services/` (which holds only cross-cutting services like socketService, apiClient).
- **Unit Tests**: All unit tests live in `__tests__/` folders co-located with the code they test. Use Jest with TypeScript support.
- **Runtime Platform Differences**: V8 runtime is desktop-only. Mobile platforms use feature detection and graceful degradation. Skills with platform restrictions are filtered during discovery.
- **Socket Management**: Rust-native Socket.io client provides persistent connections with automatic reconnection. Use `connect_to_socket` command instead of frontend-only socket connections for reliability.
- **Dual Socket Codebase**: Socket event handling exists in **both** the TypeScript frontend (`src/services/socketService.ts`, `src/utils/tauriSocket.ts`) and the Rust backend (`src-tauri/src/runtime/socket_manager.rs`). **Any new socket event or protocol change must be implemented in both codebases.** The web frontend handles events directly via Socket.io; the Rust backend handles them over raw WebSocket with Engine.IO/Socket.IO framing. Example: `tool:sync` is emitted from both `src/lib/skills/sync.ts` (web mode) and `socket_manager.rs` (Rust mode, on connect + skill lifecycle changes).
## Platform Gotchas
- **macOS deep links**: Require `.app` bundle (not `tauri dev`). Clear WebKit caches when debugging stale content: `rm -rf ~/Library/WebKit/com.openhuman.app ~/Library/Caches/com.openhuman.app`
- **Cargo caching**: May serve stale frontend assets on incremental builds. Run `cargo clean --manifest-path src-tauri/Cargo.toml` if the app shows outdated UI.
- **`window.__TAURI__`**: Not available at module load time. Use static imports and try/catch around Tauri API calls (not around imports).
- **Android background services**: RuntimeService requires notification permissions (API 33+) and foreground service type specification (API 34+). Use Android logging (`android_logger`) for debug output in logcat.
- **V8 runtime limitations**: V8 engine is desktop-only. Android skills should use lightweight alternatives or server-side execution patterns.
- **Socket connections**: Persistent Socket.io connections via Rust backend work better than WebView-based connections on mobile platforms.
_Last aligned with monorepo layout (`app/` + root `src/`), QuickJS skills in `openhuman_core`, and Tauri shell IPC as of repo state._
+1 -1
View File
@@ -98,7 +98,7 @@ Maintainers will review and may request changes. Once approved, your PR will be
- **Imports**: Use static `import`/`import type` at the top of the file. No dynamic `import()` for app code; use try/catch around Tauri API calls in non-Tauri environments instead.
- **Code style**: ESLint and Prettier are authoritative. Use type-only imports where appropriate and consolidate imports from the same module.
- **Telegram IDs**: Use the `big-integer` library; do not rely on native JavaScript numbers for Telegram IDs.
- **Tauri**: Commands are in Rust under `src-tauri`; frontend uses `invoke()` from `@tauri-apps/api/core`. Handle missing `window.__TAURI__` where the app can run outside Tauri.
- **Tauri**: Commands are in Rust under `app/src-tauri`; frontend uses `invoke()` from `@tauri-apps/api/core`. Handle missing `window.__TAURI__` where the app can run outside Tauri. Install JS deps from the repo root (`yarn install`) so the `app` workspace is linked; most scripts are also available as `yarn <script>` from the root.
- **Socket events**: Behavior exists in both the TypeScript frontend and the Rust backend. Any new socket event or protocol change must be implemented in both places.
- **Skills**: Follow the V8 runtime and skill manifest rules; respect platform compatibility and the documented bridge/API surface.
Generated
+11 -815
View File
File diff suppressed because it is too large Load Diff
+110 -3
View File
@@ -1,3 +1,110 @@
[workspace]
members = ["rust-core", "src-tauri"]
resolver = "2"
[package]
name = "openhuman"
version = "0.49.16"
edition = "2021"
description = "OpenHuman core business logic and RPC server"
autobins = true
[lib]
name = "openhuman_core"
crate-type = ["rlib"]
[dependencies]
tauri = { version = "2.10", features = ["tray-icon"] }
rquickjs = { version = "0.9", features = ["futures", "parallel"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
reqwest = { version = "0.12", default-features = false, features = ["json", "blocking", "rustls-tls", "native-tls", "stream", "http2", "multipart", "socks"] }
tokio = { version = "1", features = ["full", "sync"] }
once_cell = "1.19"
parking_lot = "0.12"
log = "0.4"
env_logger = "0.11"
base64 = "0.22"
aes-gcm = "0.10"
argon2 = "0.5"
rand = "0.9"
dirs = "5"
sha2 = "0.10"
hmac = "0.12"
uuid = { version = "1", features = ["v4"] }
anyhow = "1.0"
async-trait = "0.1"
chacha20poly1305 = "0.10"
hex = "0.4"
tokio-util = { version = "0.7", features = ["rt"] }
tokio-tungstenite = { version = "0.24", features = ["rustls-tls-webpki-roots"] }
futures = "0.3"
rusqlite = { version = "0.37", features = ["bundled"] }
chrono = { version = "0.4", features = ["serde"] }
cron = "0.12"
futures-util = "0.3"
directories = "6"
toml = "1.0"
shellexpand = "3.1"
schemars = "1.2"
tracing = { version = "0.1", default-features = false }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter"] }
prometheus = { version = "0.14", default-features = false }
urlencoding = "2.1"
thiserror = "2.0"
ring = "0.17"
prost = { version = "0.14", default-features = false }
postgres = { version = "0.19", features = ["with-chrono-0_4"] }
chrono-tz = "0.10"
dialoguer = { version = "0.12", features = ["fuzzy-select"] }
console = "0.16"
glob = "0.3"
regex = "1.10"
hostname = "0.4.2"
rustls = { version = "0.23", features = ["ring"] }
rustls-pki-types = "1.14.0"
tokio-rustls = "0.26.4"
webpki-roots = "1.0.6"
clap = { version = "4.5", features = ["derive"] }
clap_complete = "4.5"
lettre = { version = "0.11.19", default-features = false, features = ["builder", "smtp-transport", "rustls-tls"] }
mail-parser = "0.11.2"
async-imap = { version = "0.11", features = ["runtime-tokio"], default-features = false }
axum = { version = "0.8", default-features = false, features = ["http1", "json", "tokio", "query", "ws", "macros"] }
tower = { version = "0.5", default-features = false }
opentelemetry = { version = "0.31", default-features = false, features = ["trace", "metrics"] }
opentelemetry_sdk = { version = "0.31", default-features = false, features = ["trace", "metrics"] }
opentelemetry-otlp = { version = "0.31", default-features = false, features = ["trace", "metrics", "http-proto", "reqwest-client", "reqwest-rustls-webpki-roots"] }
tokio-stream = { version = "0.1.18", features = ["full"] }
url = "2"
openssl = "0.10"
matrix-sdk = { version = "0.16", optional = true, default-features = false, features = ["e2e-encryption", "rustls-tls", "markdown"] }
fantoccini = { version = "0.22.0", optional = true, default-features = false, features = ["rustls-tls"] }
serde-big-array = { version = "0.5", optional = true }
nusb = { version = "0.2", default-features = false, optional = true }
tokio-serial = { version = "5", default-features = false, optional = true }
probe-rs = { version = "0.30", optional = true }
pdf-extract = { version = "0.10", optional = true }
wa-rs = { version = "0.2", optional = true, default-features = false }
wa-rs-core = { version = "0.2", optional = true, default-features = false }
wa-rs-binary = { version = "0.2", optional = true, default-features = false }
wa-rs-proto = { version = "0.2", optional = true, default-features = false }
wa-rs-ureq-http = { version = "0.2", optional = true }
wa-rs-tokio-transport = { version = "0.2", optional = true, default-features = false }
[target.'cfg(target_os = "linux")'.dependencies]
landlock = { version = "0.4", optional = true }
rppal = { version = "0.22", optional = true }
[dev-dependencies]
tempfile = "3"
[features]
sandbox-landlock = ["dep:landlock"]
sandbox-bubblewrap = []
hardware = ["dep:nusb", "dep:tokio-serial"]
channel-matrix = ["dep:matrix-sdk"]
peripheral-rpi = ["dep:rppal"]
browser-native = ["dep:fantoccini"]
fantoccini = ["browser-native"]
landlock = ["sandbox-landlock"]
probe = ["dep:probe-rs"]
rag-pdf = ["dep:pdf-extract"]
whatsapp-web = ["dep:wa-rs", "dep:wa-rs-core", "dep:wa-rs-binary", "dep:wa-rs-proto", "dep:wa-rs-ureq-http", "dep:wa-rs-tokio-transport", "serde-big-array"]
View File
+95 -7
View File
@@ -1,26 +1,114 @@
{
"name": "openhuman",
"private": true,
"version": "0.1.0",
"name": "openhuman-app",
"version": "0.49.16",
"type": "module",
"scripts": {
"dev": "vite",
"dev:web": "vite",
"dev:app": "source ../scripts/load-dotenv.sh && tauri dev",
"core:stage": "node ../scripts/stage-core-sidecar.mjs",
"build": "tsc && vite build",
"build:app": "yarn skills:build && tsc && vite build",
"compile": "tsc --noEmit",
"preview": "vite preview",
"tauri": "tauri"
"tauri": "tauri",
"tauri:build:ui": "tauri build -- --bin OpenHuman",
"macos:build:intel": "source ../scripts/load-dotenv.sh && tauri build --bundles app dmg --target x86_64-apple-darwin -- --bin OpenHuman",
"macos:build:intel:debug": "yarn macos:build:intel --debug",
"macos:build:debug": "yarn macos:build:release --debug",
"macos:build:release": "source ../scripts/load-dotenv.sh && tauri build --bundles app dmg -- --bin OpenHuman",
"macos:build:release:signed": "source ../scripts/load-env.sh && tauri build --bundles app dmg -- --bin OpenHuman",
"macos:build:sign:release": "yarn macos:build:release:signed",
"macos:run": "open '../target/debug/bundle/macos/OpenHuman.app'",
"macos:dev": "yarn macos:build:debug && open '../target/debug/bundle/macos/OpenHuman.app'",
"test": "vitest run --config test/vitest.config.ts",
"test:unit": "vitest run --config test/vitest.config.ts",
"test:unit:watch": "vitest --config test/vitest.config.ts",
"test:watch": "vitest --config test/vitest.config.ts",
"test:coverage": "vitest run --config test/vitest.config.ts --coverage",
"test:rust": "source $HOME/.cargo/env 2>/dev/null; cargo test --manifest-path ../Cargo.toml --workspace",
"test:e2e:build": "bash ../scripts/e2e-build.sh",
"test:e2e:login": "bash ../scripts/e2e-login.sh",
"test:e2e:auth": "bash ../scripts/e2e-auth.sh",
"test:e2e": "yarn test:e2e:build && yarn test:e2e:login && yarn test:e2e:auth",
"test:e2e:all:flows": "bash ../scripts/e2e-run-all-flows.sh",
"test:e2e:all": "yarn test:e2e:build && yarn test:e2e:all:flows",
"test:all": "yarn test:coverage && yarn test:rust && yarn test:e2e",
"format": "prettier --write . && cargo fmt --manifest-path ../Cargo.toml --all",
"format:check": "prettier --check . && cargo fmt --manifest-path ../Cargo.toml --all --check",
"lint": "eslint . --ext .ts,.tsx",
"lint:fix": "eslint . --ext .ts,.tsx --fix",
"skills:build": "cd skills && yarn build",
"skills:watch": "cd skills && yarn build:watch",
"prepare": "husky"
},
"dependencies": {
"@heroicons/react": "^2.2.0",
"@noble/hashes": "^2.0.1",
"@noble/secp256k1": "^3.0.0",
"@reduxjs/toolkit": "^2.11.2",
"@scure/bip32": "^2.0.1",
"@scure/bip39": "^2.0.1",
"@sentry/react": "^10.38.0",
"@tauri-apps/api": "^2.10.0",
"@tauri-apps/plugin-deep-link": "^2",
"@tauri-apps/plugin-opener": "^2",
"@tauri-apps/plugin-os": "^2.3.2",
"@types/three": "^0.183.1",
"buffer": "^6.0.3",
"debug": "^4.4.3",
"lottie-react": "^2.4.1",
"os-browserify": "^0.3.0",
"process": "^0.11.10",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-opener": "^2"
"react-markdown": "^10.1.0",
"react-redux": "^9.2.0",
"react-router-dom": "^7.13.0",
"redux-logger": "^3.0.6",
"redux-persist": "^6.0.0",
"socket.io-client": "^4.8.3",
"three": "^0.183.2",
"util": "^0.12.5"
},
"devDependencies": {
"@eslint/js": "^9.39.2",
"@tailwindcss/forms": "^0.5.11",
"@tailwindcss/typography": "^0.5.19",
"@tauri-apps/cli": "2.10.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/debug": "^4.1.12",
"@types/node": "^25.0.10",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@types/redux-logger": "^3.0.13",
"@typescript-eslint/eslint-plugin": "^8.54.0",
"@typescript-eslint/parser": "^8.54.0",
"@vitejs/plugin-react": "^4.6.0",
"@vitest/coverage-v8": "^4.0.18",
"@wdio/appium-service": "^9.24.0",
"@wdio/cli": "^9.24.0",
"@wdio/local-runner": "^9.24.0",
"@wdio/mocha-framework": "^9.24.0",
"@wdio/spec-reporter": "^9.24.0",
"autoprefixer": "^10.4.23",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"husky": "^9.1.7",
"jsdom": "^28.0.0",
"msw": "^2.12.10",
"postcss": "^8.5.6",
"prettier": "^3.8.1",
"tailwindcss": "^3.4.19",
"typescript": "~5.8.3",
"vite": "^7.0.4",
"@tauri-apps/cli": "^2"
"vite-plugin-node-polyfills": "^0.25.0",
"vitest": "^4.0.18"
}
}

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Before

Width:  |  Height:  |  Size: 201 KiB

After

Width:  |  Height:  |  Size: 201 KiB

View File

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

View File

Before

Width:  |  Height:  |  Size: 266 KiB

After

Width:  |  Height:  |  Size: 266 KiB

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

+1
View File
@@ -1,6 +1,7 @@
# Generated by Cargo
# will have compiled files and executables
/target/
/binaries/
# Generated by Tauri
# will have schema files for capabilities auto-completion
File diff suppressed because it is too large Load Diff
+158 -9
View File
@@ -1,9 +1,11 @@
[package]
name = "openhuman"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
name = "OpenHuman"
version = "0.49.16"
description = "OpenHuman - AI-powered Super Assistant"
authors = ["OpenHuman"]
edition = "2021"
default-run = "OpenHuman"
autobins = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -11,15 +13,162 @@ edition = "2021"
# The `_lib` suffix may seem redundant but it is necessary
# to make the lib name unique and wouldn't conflict with the bin name.
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
name = "openhuman_lib"
name = "openhuman"
crate-type = ["staticlib", "cdylib", "rlib"]
[[bin]]
name = "OpenHuman"
path = "src/main.rs"
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
tauri = { version = "2", features = [] }
tauri-plugin-opener = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
[dependencies]
# Tauri core and plugins
tauri = { version = "2.10", features = ["macos-private-api"] }
tauri-plugin-opener = "2"
tauri-plugin-deep-link = "2.0.0"
tauri-plugin-os = "2"
# Desktop host plugins
# Serialization
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# HTTP client: rustls + native-tls for desktop skill HTTP compatibility
# (some APIs/CDNs use TLS configs that rustls can't negotiate — native-tls uses OS TLS stack)
reqwest = { version = "0.12", default-features = false, features = ["json", "blocking", "rustls-tls", "native-tls", "stream", "http2", "multipart", "socks"] }
# Async runtime
tokio = { version = "1", features = ["full", "sync"] }
# Keyring is used for desktop credential storage.
# Concurrency utilities
once_cell = "1.19"
parking_lot = "0.12"
# Logging
log = "0.4"
env_logger = "0.11"
# AI module dependencies
base64 = "0.22"
aes-gcm = "0.10"
argon2 = "0.5"
rand = "0.9"
dirs = "5"
sha2 = "0.10"
hmac = "0.12"
uuid = { version = "1", features = ["v4"] }
# OpenHuman agent runtime dependencies
anyhow = "1.0"
async-trait = "0.1"
chacha20poly1305 = "0.10"
hex = "0.4"
tokio-util = { version = "0.7", features = ["rt"] }
# V8 JavaScript runtime is desktop-only in this host.
tokio-tungstenite = { version = "0.24", features = ["rustls-tls-webpki-roots"] }
# HTTP streaming support for fetch polyfill
futures = "0.3"
# Embedded SQLite for skill databases
rusqlite = { version = "0.37", features = ["bundled"] }
# Date/time utilities
chrono = { version = "0.4", features = ["serde"] }
# Cron expression parsing for scheduled skill tasks
cron = "0.12"
# Stream/Sink utilities for WebSocket and HTTP streaming
futures-util = "0.3"
# Socket manager is persistent in the desktop host.
# OpenHuman config + observability
directories = "6"
toml = "1.0"
shellexpand = "3.1"
schemars = "1.2"
tracing = { version = "0.1", default-features = false }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter"] }
prometheus = { version = "0.14", default-features = false }
urlencoding = "2.1"
thiserror = "2.0"
ring = "0.17"
prost = { version = "0.14", default-features = false }
postgres = { version = "0.19", features = ["with-chrono-0_4"] }
chrono-tz = "0.10"
dialoguer = { version = "0.12", features = ["fuzzy-select"] }
console = "0.16"
glob = "0.3"
regex = "1.10"
hostname = "0.4.2"
rustls = { version = "0.23", features = ["ring"] }
rustls-pki-types = "1.14.0"
tokio-rustls = "0.26.4"
webpki-roots = "1.0.6"
clap = { version = "4.5", features = ["derive"] }
lettre = { version = "0.11.19", default-features = false, features = ["builder", "smtp-transport", "rustls-tls"] }
mail-parser = "0.11.2"
async-imap = { version = "0.11", features = ["runtime-tokio"], default-features = false }
axum = { version = "0.8", default-features = false, features = ["http1", "json", "tokio", "query", "ws", "macros"] }
tower = { version = "0.5", default-features = false }
tower-http = { version = "0.6", default-features = false, features = ["limit", "timeout"] }
http-body-util = "0.1"
opentelemetry = { version = "0.31", default-features = false, features = ["trace", "metrics"] }
opentelemetry_sdk = { version = "0.31", default-features = false, features = ["trace", "metrics"] }
opentelemetry-otlp = { version = "0.31", default-features = false, features = ["trace", "metrics", "http-proto", "reqwest-client", "reqwest-rustls-webpki-roots"] }
tokio-stream = { version = "0.1.18", features = ["full"] }
url = "2"
# Optional integrations
matrix-sdk = { version = "0.16", optional = true, default-features = false, features = ["e2e-encryption", "rustls-tls", "markdown"] }
fantoccini = { version = "0.22.0", optional = true, default-features = false, features = ["rustls-tls"] }
serde-big-array = { version = "0.5", optional = true }
nusb = { version = "0.2", default-features = false, optional = true }
tokio-serial = { version = "5", default-features = false, optional = true }
probe-rs = { version = "0.30", optional = true }
pdf-extract = { version = "0.10", optional = true }
wa-rs = { version = "0.2", optional = true, default-features = false }
wa-rs-core = { version = "0.2", optional = true, default-features = false }
wa-rs-binary = { version = "0.2", optional = true, default-features = false }
wa-rs-proto = { version = "0.2", optional = true, default-features = false }
wa-rs-ureq-http = { version = "0.2", optional = true }
wa-rs-tokio-transport = { version = "0.2", optional = true, default-features = false }
keyring = "3"
tauri-plugin-autostart = "2"
tauri-plugin-notification = "2"
# QuickJS JavaScript runtime (desktop-only host)
rquickjs = { version = "0.11", features = ["futures", "macro", "loader", "parallel"] }
[target.'cfg(target_os = "linux")'.dependencies]
landlock = { version = "0.4", optional = true }
rppal = { version = "0.22", optional = true }
[dev-dependencies]
tempfile = "3"
[features]
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
custom-protocol = ["tauri/custom-protocol"]
sandbox-landlock = ["dep:landlock"]
sandbox-bubblewrap = []
# OpenHuman feature flags
hardware = ["dep:nusb", "dep:tokio-serial"]
channel-matrix = ["dep:matrix-sdk"]
peripheral-rpi = ["dep:rppal"]
browser-native = ["dep:fantoccini"]
fantoccini = ["browser-native"]
landlock = ["sandbox-landlock"]
probe = ["dep:probe-rs"]
rag-pdf = ["dep:pdf-extract"]
whatsapp-web = ["dep:wa-rs", "dep:wa-rs-core", "dep:wa-rs-binary", "dep:wa-rs-proto", "dep:wa-rs-ureq-http", "dep:wa-rs-tokio-transport", "serde-big-array"]
+32 -1
View File
@@ -1,3 +1,34 @@
use std::env;
fn main() {
tauri_build::build()
maybe_override_tauri_config_for_local_builds();
tauri_build::build();
}
fn maybe_override_tauri_config_for_local_builds() {
let profile = env::var("PROFILE").unwrap_or_default();
let skip_resources = env::var("TAURI_SKIP_RESOURCES").is_ok() || profile != "release";
if !skip_resources {
return;
}
let mut merge_config = serde_json::json!({});
if skip_resources {
merge_config["bundle"]["resources"] = serde_json::json!([]);
// Keep sidecars enabled for local/debug builds so the desktop host can
// exercise the same core process launch path as packaged builds.
}
match serde_json::to_string(&merge_config) {
Ok(json) => {
env::set_var("TAURI_CONFIG", json);
if skip_resources {
println!("cargo:warning=TAURI resources disabled for local build");
}
}
Err(err) => {
println!("cargo:warning=Failed to serialize TAURI_CONFIG override: {err}");
}
}
}
+13 -2
View File
@@ -1,10 +1,21 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"description": "Capability for the main window (desktop only)",
"platforms": ["linux", "macOS", "windows"],
"windows": ["main"],
"permissions": [
"core:default",
"opener:default"
"opener:default",
"deep-link:default",
"os:default",
"autostart:default",
"autostart:allow-enable",
"autostart:allow-disable",
"autostart:allow-is-enabled",
"notification:default",
"notification:allow-notify",
"notification:allow-request-permission",
"notification:allow-is-permission-granted"
]
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 974 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 903 B

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before

Width:  |  Height:  |  Size: 275 KiB

After

Width:  |  Height:  |  Size: 275 KiB

+81
View File
@@ -0,0 +1,81 @@
use crate::core_process::CoreProcessHandle;
use serde::Deserialize;
use serde_json::Value;
use tauri::Manager;
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CoreRpcRelayRequest {
pub method: String,
#[serde(default)]
pub params: Value,
#[serde(default)]
pub service_managed: bool,
}
async fn invoke_core_cli_call(method: &str, params: Value) -> Result<(), String> {
let core_bin = crate::core_process::default_core_bin()
.ok_or_else(|| "openhuman core binary not found".to_string())?;
let params_json =
serde_json::to_string(&params).map_err(|e| format!("failed to serialize params: {e}"))?;
let output = tokio::process::Command::new(core_bin)
.arg("call")
.arg("--method")
.arg(method)
.arg("--params")
.arg(params_json)
.output()
.await
.map_err(|e| format!("failed to run core cli call {method}: {e}"))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string();
return Err(format!(
"core cli call {method} failed with status {}. stdout: {} stderr: {}",
output.status, stdout, stderr
));
}
Ok(())
}
pub(crate) async fn ensure_service_managed_core_running() -> Result<(), String> {
if crate::core_rpc::ping().await {
return Ok(());
}
let _ = invoke_core_cli_call("openhuman.service_install", serde_json::json!({})).await;
let _ = invoke_core_cli_call("openhuman.service_start", serde_json::json!({})).await;
for _ in 0..40 {
if crate::core_rpc::ping().await {
return Ok(());
}
tokio::time::sleep(std::time::Duration::from_millis(250)).await;
}
Err(
"OpenHuman Core daemon did not become ready. Confirm the background service is running."
.to_string(),
)
}
#[tauri::command]
pub async fn core_rpc_relay(
app: tauri::AppHandle,
request: CoreRpcRelayRequest,
) -> Result<Value, String> {
if request.service_managed {
ensure_service_managed_core_running().await?;
} else {
let core = app
.try_state::<CoreProcessHandle>()
.ok_or_else(|| "core process handle is not available".to_string())?;
let handle: CoreProcessHandle = (*core).clone();
handle.ensure_running().await?;
}
crate::core_rpc::call::<Value>(&request.method, request.params).await
}
+11
View File
@@ -0,0 +1,11 @@
pub mod core_relay;
pub mod openhuman;
#[cfg(desktop)]
pub mod window;
pub use core_relay::*;
pub use openhuman::*;
#[cfg(desktop)]
pub use window::*;
+129
View File
@@ -0,0 +1,129 @@
use crate::core_process::CoreProcessHandle;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use tauri::{AppHandle, Manager};
const DAEMON_HOST_CONFIG_FILE: &str = "daemon_host_config.json";
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DaemonHostConfig {
pub show_tray: bool,
}
impl Default for DaemonHostConfig {
fn default() -> Self {
Self { show_tray: true }
}
}
fn daemon_host_config_path(app: &AppHandle) -> PathBuf {
app.path()
.app_data_dir()
.unwrap_or_else(|_| {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".openhuman")
})
.join(DAEMON_HOST_CONFIG_FILE)
}
async fn load_daemon_host_config(app: &AppHandle) -> DaemonHostConfig {
let path = daemon_host_config_path(app);
let Ok(contents) = tokio::fs::read_to_string(path).await else {
return DaemonHostConfig::default();
};
serde_json::from_str::<DaemonHostConfig>(&contents).unwrap_or_default()
}
async fn save_daemon_host_config(app: &AppHandle, config: &DaemonHostConfig) -> Result<(), String> {
let path = daemon_host_config_path(app);
if let Some(parent) = path.parent() {
tokio::fs::create_dir_all(parent)
.await
.map_err(|e| format!("failed to create daemon host config directory: {e}"))?;
}
let bytes = serde_json::to_vec_pretty(config)
.map_err(|e| format!("failed to serialize daemon host config: {e}"))?;
tokio::fs::write(path, bytes)
.await
.map_err(|e| format!("failed to write daemon host config: {e}"))
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ServiceState {
Running,
Stopped,
NotInstalled,
Unknown(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceStatus {
pub state: ServiceState,
pub unit_path: Option<std::path::PathBuf>,
pub label: String,
pub details: Option<String>,
}
#[derive(Debug, Deserialize)]
struct RpcCommandResponse<T> {
result: T,
}
async fn ensure_core_running(app: &AppHandle) -> Result<(), String> {
let core = app
.try_state::<CoreProcessHandle>()
.ok_or_else(|| "core process handle is not available".to_string())?;
let handle: CoreProcessHandle = (*core).clone();
handle.ensure_running().await
}
async fn call_service_method(app: &AppHandle, method: &str) -> Result<ServiceStatus, String> {
ensure_core_running(app).await?;
let response =
crate::core_rpc::call::<RpcCommandResponse<ServiceStatus>>(method, serde_json::json!({}))
.await?;
Ok(response.result)
}
#[tauri::command]
pub async fn openhuman_get_daemon_host_config(app: AppHandle) -> Result<DaemonHostConfig, String> {
Ok(load_daemon_host_config(&app).await)
}
#[tauri::command]
pub async fn openhuman_set_daemon_host_config(
app: AppHandle,
show_tray: bool,
) -> Result<DaemonHostConfig, String> {
let mut cfg = load_daemon_host_config(&app).await;
cfg.show_tray = show_tray;
save_daemon_host_config(&app, &cfg).await?;
Ok(cfg)
}
#[tauri::command]
pub async fn openhuman_service_install(app: AppHandle) -> Result<ServiceStatus, String> {
call_service_method(&app, "openhuman.service_install").await
}
#[tauri::command]
pub async fn openhuman_service_start(app: AppHandle) -> Result<ServiceStatus, String> {
call_service_method(&app, "openhuman.service_start").await
}
#[tauri::command]
pub async fn openhuman_service_stop(app: AppHandle) -> Result<ServiceStatus, String> {
call_service_method(&app, "openhuman.service_stop").await
}
#[tauri::command]
pub async fn openhuman_service_status(app: AppHandle) -> Result<ServiceStatus, String> {
call_service_method(&app, "openhuman.service_status").await
}
#[tauri::command]
pub async fn openhuman_service_uninstall(app: AppHandle) -> Result<ServiceStatus, String> {
call_service_method(&app, "openhuman.service_uninstall").await
}
@@ -46,19 +46,32 @@ impl CoreProcessHandle {
match self.run_mode {
CoreRunMode::InProcess => {
let mut guard = self.task.lock().await;
log::warn!(
"[core] in-process core mode is unavailable in host-only build; falling back to child process"
);
let mut guard = self.child.lock().await;
if guard.is_none() {
let port = self.port;
log::info!("[core] launching in-process core server on port {}", port);
let task = tokio::spawn(async move {
if let Err(err) = openhuman_core::core_server::run_server(Some(port)).await
{
log::error!("[core] in-process core server exited with error: {err}");
} else {
log::warn!("[core] in-process core server exited");
let mut cmd = if let Some(core_bin) = &self.core_bin {
let mut cmd = Command::new(core_bin);
if is_current_exe_path(core_bin) {
cmd.arg("core");
}
});
*guard = Some(task);
cmd.arg("run").arg("--port").arg(self.port.to_string());
cmd
} else {
let exe = std::env::current_exe()
.map_err(|e| format!("failed to resolve current executable: {e}"))?;
let mut cmd = Command::new(exe);
cmd.arg("core")
.arg("run")
.arg("--port")
.arg(self.port.to_string());
cmd
};
let child = cmd
.spawn()
.map_err(|e| format!("failed to spawn core process: {e}"))?;
*guard = Some(child);
}
}
CoreRunMode::ChildProcess => {
@@ -150,21 +163,6 @@ impl CoreProcessHandle {
Err("core process did not become ready".to_string())
}
pub async fn shutdown(&self) {
let mut child_guard = self.child.lock().await;
if let Some(child) = child_guard.as_mut() {
let _ = child.kill().await;
}
*child_guard = None;
drop(child_guard);
let mut task_guard = self.task.lock().await;
if let Some(task) = task_guard.take() {
task.abort();
let _ = task.await;
}
}
}
fn is_current_exe_path(candidate: &std::path::Path) -> bool {
@@ -217,10 +215,29 @@ pub fn default_core_bin() -> Option<PathBuf> {
}
}
// Dev ergonomics: in debug builds, prefer spawning this same executable with
// `core run` so Cargo recompiles core logic changes as part of tauri dev.
// Sidecar discovery remains enabled for packaged/release builds.
// Dev ergonomics: allow an explicit staged sidecar from src-tauri/binaries in
// debug builds before falling back to self-subcommand spawning.
if cfg!(debug_assertions) {
let binaries_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("binaries");
if let Ok(entries) = std::fs::read_dir(&binaries_dir) {
for entry in entries.flatten() {
let path = entry.path();
if !path.is_file() {
continue;
}
let Some(file_name) = path.file_name().and_then(|n| n.to_str()) else {
continue;
};
#[cfg(windows)]
let matches = file_name.starts_with("openhuman-") && file_name.ends_with(".exe");
#[cfg(not(windows))]
let matches = file_name.starts_with("openhuman-");
if matches {
return Some(path);
}
}
}
return None;
}
@@ -237,9 +254,9 @@ pub fn default_core_bin() -> Option<PathBuf> {
}
#[cfg(windows)]
let legacy_standalone = exe_dir.join("openhuman-core.exe");
let legacy_standalone = exe_dir.join("openhuman.exe");
#[cfg(not(windows))]
let legacy_standalone = exe_dir.join("openhuman-core");
let legacy_standalone = exe_dir.join("openhuman");
if legacy_standalone.exists() && !same_executable_path(&legacy_standalone, &exe) {
return Some(legacy_standalone);
@@ -270,10 +287,10 @@ pub fn default_core_bin() -> Option<PathBuf> {
#[cfg(windows)]
let matches = (file_name.starts_with("openhuman-") && file_name.ends_with(".exe"))
|| (file_name.starts_with("openhuman-core-") && file_name.ends_with(".exe"));
|| (file_name.starts_with("openhuman-") && file_name.ends_with(".exe"));
#[cfg(not(windows))]
let matches =
file_name.starts_with("openhuman-") || file_name.starts_with("openhuman-core-");
file_name.starts_with("openhuman-") || file_name.starts_with("openhuman-");
if matches && !same_executable_path(&path, &exe) {
return Some(path);
+648 -6
View File
@@ -1,14 +1,656 @@
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
//! OpenHuman Desktop Application
//!
//! This is the Rust backend for the cross-platform crypto community platform.
//! It provides deep link handling, core process RPC relay, window management,
//! and AI configuration helpers.
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
compile_error!("src-tauri host is desktop-only. Non-desktop targets are not supported.");
mod commands;
mod core_process;
mod core_rpc;
mod utils;
use aes_gcm::aead::{Aead, KeyInit};
use aes_gcm::{Aes256Gcm, Key, Nonce};
use base64::{engine::general_purpose::STANDARD as B64, Engine as _};
use commands::*;
use rand::TryRngCore;
use serde::Serialize;
use std::collections::HashMap;
use std::path::PathBuf;
use tauri::{AppHandle, Emitter, Manager, RunEvent};
use tokio::{
fs,
time::{interval, Duration},
};
#[cfg(any(windows, target_os = "linux"))]
use tauri_plugin_deep_link::DeepLinkExt;
/// Demo command - can be removed in production
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
fn derive_key(password: &str) -> [u8; 32] {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(password.as_bytes());
let hash = hasher.finalize();
let mut key = [0u8; 32];
key.copy_from_slice(&hash[..32]);
key
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct AIPreview {
soul: AIPreviewSoul,
tools: AIPreviewTools,
metadata: AIPreviewMetadata,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct AIPreviewSoul {
raw: String,
name: String,
description: String,
personality_preview: Vec<String>,
safety_rules_preview: Vec<String>,
loaded_at: i64,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct AIPreviewTools {
raw: String,
total_tools: usize,
active_skills: usize,
skills_preview: Vec<String>,
loaded_at: i64,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct AIPreviewMetadata {
loaded_at: i64,
loading_duration: i64,
has_fallbacks: bool,
sources: AIPreviewSources,
errors: Vec<String>,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct AIPreviewSources {
soul: String,
tools: String,
}
fn now_ms() -> i64 {
chrono::Utc::now().timestamp_millis()
}
fn extract_section(raw: &str, heading: &str) -> String {
let marker = format!("## {heading}");
let Some(start) = raw.find(&marker) else {
return String::new();
};
let body = &raw[start + marker.len()..];
if let Some(next_idx) = body.find("\n## ") {
body[..next_idx].trim().to_string()
} else {
body.trim().to_string()
}
}
fn parse_soul_preview(raw: String, loaded_at: i64) -> AIPreviewSoul {
let name = raw
.lines()
.find_map(|line| line.strip_prefix("# ").map(|s| s.trim().to_string()))
.unwrap_or_else(|| "OpenHuman".to_string());
let description = raw
.lines()
.map(str::trim)
.find(|line| !line.is_empty() && !line.starts_with('#'))
.unwrap_or("AI assistant")
.to_string();
let personality_preview = extract_section(&raw, "Personality")
.lines()
.filter_map(|line| line.trim().strip_prefix("- **"))
.filter_map(|line| {
let mut parts = line.splitn(2, "**:");
let trait_name = parts.next()?.trim();
let detail = parts.next().unwrap_or("").trim();
Some(format!("{trait_name}: {detail}"))
})
.take(3)
.collect::<Vec<_>>();
let safety_rules_preview = extract_section(&raw, "Safety Rules")
.lines()
.filter_map(|line| {
let trimmed = line.trim();
let dot_idx = trimmed.find('.')?;
let (prefix, rest) = trimmed.split_at(dot_idx);
if prefix.chars().all(|c| c.is_ascii_digit()) {
Some(rest.trim_start_matches('.').trim().to_string())
} else {
None
}
})
.take(3)
.collect::<Vec<_>>();
AIPreviewSoul {
raw,
name,
description,
personality_preview,
safety_rules_preview,
loaded_at,
}
}
fn parse_tools_preview(raw: String, loaded_at: i64) -> AIPreviewTools {
let mut current_skill = "General".to_string();
let mut skill_counts: HashMap<String, usize> = HashMap::new();
let mut total_tools = 0usize;
for line in raw.lines() {
let trimmed = line.trim();
if let Some(title) = trimmed.strip_prefix("### ") {
if let Some(skill_title) = title.strip_suffix(" Tools") {
current_skill = skill_title.trim().to_string();
skill_counts.entry(current_skill.clone()).or_insert(0);
}
continue;
}
if trimmed.starts_with("#### ") {
total_tools += 1;
*skill_counts.entry(current_skill.clone()).or_insert(0) += 1;
}
}
let mut skills = skill_counts.into_iter().collect::<Vec<_>>();
let active_skills = skills.len();
skills.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0)));
let skills_preview = skills
.into_iter()
.take(6)
.map(|(name, count)| format!("{name} ({count})"))
.collect::<Vec<_>>();
AIPreviewTools {
raw,
total_tools,
active_skills,
skills_preview,
loaded_at,
}
}
fn resolve_ai_directory(app: &tauri::AppHandle) -> Option<(PathBuf, &'static str)> {
if let Ok(resource_dir) = app.path().resource_dir() {
if let Some(ai_dir) = utils::dev_paths::bundled_openclaw_prompts_dir(&resource_dir) {
return Some((ai_dir, "bundled"));
}
}
if let Ok(cwd) = std::env::current_dir() {
if let Some(path) = utils::dev_paths::repo_ai_prompts_dir(&cwd) {
return Some((path, "bundled"));
}
let fallback = cwd.join("ai");
if fallback.is_dir() {
return Some((fallback, "bundled"));
}
}
None
}
fn build_ai_preview(app: &tauri::AppHandle) -> AIPreview {
let started = now_ms();
let loaded_at = now_ms();
let mut errors = Vec::new();
let mut soul_raw = String::new();
let mut tools_raw = String::new();
let mut source = "bundled".to_string();
if let Some((ai_dir, resolved_source)) = resolve_ai_directory(app) {
source = resolved_source.to_string();
let soul_path = ai_dir.join("SOUL.md");
let tools_path = ai_dir.join("TOOLS.md");
soul_raw = std::fs::read_to_string(&soul_path).unwrap_or_else(|e| {
errors.push(format!("Failed to read SOUL.md: {e}"));
String::new()
});
tools_raw = std::fs::read_to_string(&tools_path).unwrap_or_else(|e| {
errors.push(format!("Failed to read TOOLS.md: {e}"));
String::new()
});
} else {
errors.push("AI config directory not found".to_string());
}
let soul = parse_soul_preview(soul_raw, loaded_at);
let tools = parse_tools_preview(tools_raw, loaded_at);
let done = now_ms();
AIPreview {
soul,
tools,
metadata: AIPreviewMetadata {
loaded_at: done,
loading_duration: done - started,
has_fallbacks: false,
sources: AIPreviewSources {
soul: source.clone(),
tools: source,
},
errors,
},
}
}
#[tauri::command]
async fn ai_get_config(app: tauri::AppHandle) -> Result<AIPreview, String> {
Ok(build_ai_preview(&app))
}
#[tauri::command]
async fn ai_refresh_config(app: tauri::AppHandle) -> Result<AIPreview, String> {
Ok(build_ai_preview(&app))
}
/// Write AI configuration files to `src/ai/prompts` in the repo (dev resolution from cwd).
#[tauri::command]
async fn write_ai_config_file(filename: String, content: String) -> Result<bool, String> {
use std::env;
// Determine runtime working directory
let current_dir =
env::current_dir().map_err(|e| format!("Failed to get current directory: {e}"))?;
// Ensure filename is safe (only allow .md files)
if !filename.ends_with(".md") {
return Err("Only .md files are allowed".to_string());
}
// Prevent path traversal by checking for dangerous characters
if filename.contains("..") || filename.contains("/") || filename.contains("\\") {
return Err("Invalid filename: path traversal not allowed".to_string());
}
let ai_dir = utils::dev_paths::repo_ai_prompts_dir(&current_dir)
.unwrap_or_else(|| current_dir.join("src").join("ai").join("prompts"));
let file_path = ai_dir.join(&filename);
// Ensure ai directory exists
std::fs::create_dir_all(&ai_dir).map_err(|e| format!("Failed to create ai directory: {e}"))?;
// Write the file
std::fs::write(&file_path, content)
.map_err(|e| format!("Failed to write file {}: {e}", filename))?;
Ok(true)
}
fn is_daemon_mode() -> bool {
std::env::args().any(|arg| arg == "daemon" || arg == "--daemon")
}
/// Watch daemon health file and bridge changes to frontend Tauri events
async fn watch_daemon_health_file(app_handle: AppHandle, data_dir: PathBuf) {
let state_file = data_dir.join("daemon_state.json");
let mut interval = interval(Duration::from_secs(2));
let mut last_modified: Option<std::time::SystemTime> = None;
log::info!(
"[openhuman] Watching daemon health file: {}",
state_file.display()
);
loop {
interval.tick().await;
// Check if file exists and was modified
if let Ok(metadata) = fs::metadata(&state_file).await {
if let Ok(modified) = metadata.modified() {
if last_modified.map_or(true, |last| modified > last) {
last_modified = Some(modified);
// Read and parse health data
if let Ok(content) = fs::read_to_string(&state_file).await {
if let Ok(json_value) = serde_json::from_str::<serde_json::Value>(&content)
{
log::debug!(
"[openhuman] Broadcasting health event from file: {:?}",
json_value
);
// Emit Tauri event to frontend (same as internal daemon)
if let Err(e) = app_handle.emit("openhuman:health", &json_value) {
log::error!(
"[openhuman] Failed to emit health event from file: {}",
e
);
} else {
log::debug!(
"[openhuman] Health event emitted successfully from file"
);
}
} else {
log::debug!(
"[openhuman] Failed to parse health file as JSON: {}",
state_file.display()
);
}
} else {
log::debug!(
"[openhuman] Failed to read health file: {}",
state_file.display()
);
}
}
}
} else {
// File doesn't exist yet - external daemon may not be writing yet
log::debug!(
"[openhuman] Health file not found yet: {}",
state_file.display()
);
}
}
}
pub fn run() {
tauri::Builder::default()
if let Err(err) = rustls::crypto::ring::default_provider().install_default() {
log::warn!(
"[app] rustls crypto provider not installed (already set?): {:?}",
err
);
} else {
log::info!("[app] rustls crypto provider installed (ring)");
}
let daemon_mode = is_daemon_mode();
// Initialize logger
{
use env_logger::fmt::style::{AnsiColor, Style};
use std::io::Write;
let default_filter = std::env::var("RUST_LOG")
.unwrap_or_else(|_| "info,tungstenite=warn,tokio_tungstenite=warn,reqwest=warn,rusqlite=warn,hyper=warn,h2=warn".to_string());
let write_style = std::env::var("RUST_LOG_STYLE")
.map(|v| match v.as_str() {
"never" => env_logger::fmt::WriteStyle::Never,
_ => env_logger::fmt::WriteStyle::Always,
})
.unwrap_or(env_logger::fmt::WriteStyle::Always);
let _ = env_logger::Builder::new()
.parse_filters(&default_filter)
.write_style(write_style)
.format(|buf, record| {
let timestamp = buf.timestamp_millis()
.to_string();
// Strip the date prefix, keep only HH:MM:SS.mmm
let time_only = timestamp.split('T')
.nth(1)
.and_then(|t| t.strip_suffix('Z'))
.unwrap_or(&timestamp);
let level = record.level();
// Level colors
let level_style = match level {
log::Level::Error => Style::new().fg_color(Some(AnsiColor::Red.into())).bold(),
log::Level::Warn => Style::new().fg_color(Some(AnsiColor::Yellow.into())).bold(),
log::Level::Info => Style::new().fg_color(Some(AnsiColor::Green.into())),
log::Level::Debug => Style::new().fg_color(Some(AnsiColor::BrightBlack.into())),
log::Level::Trace => Style::new().fg_color(Some(AnsiColor::BrightBlack.into())),
};
let msg = format!("{}", record.args());
// Extract tag from message (e.g. "[socket-mgr]", "[skill:x]")
let (tag, rest) = if msg.starts_with('[') {
if let Some(end) = msg.find(']') {
let tag = &msg[..=end];
let rest = msg[end + 1..].trim_start();
(Some(tag.to_string()), rest.to_string())
} else {
(None, msg)
}
} else {
(None, msg)
};
// Tag-based colors
let tag_style = if let Some(ref t) = tag {
let t_lower = t.to_lowercase();
if t_lower.contains("socket") {
Style::new().fg_color(Some(AnsiColor::Blue.into())).bold()
} else if t_lower.contains("runtime") {
Style::new().fg_color(Some(AnsiColor::Cyan.into())).bold()
} else if t_lower.contains("skill") {
Style::new().fg_color(Some(AnsiColor::Green.into())).bold()
} else if t_lower.contains("ping") || t_lower.contains("cron") {
Style::new().fg_color(Some(AnsiColor::Yellow.into())).bold()
} else if t_lower.contains("app") {
Style::new().fg_color(Some(AnsiColor::White.into())).bold()
} else if t_lower.contains("ai") {
Style::new().fg_color(Some(AnsiColor::BrightMagenta.into())).bold()
} else {
Style::new().fg_color(Some(AnsiColor::BrightBlack.into()))
}
} else {
Style::new()
};
let dim = Style::new().fg_color(Some(AnsiColor::BrightBlack.into()));
if let Some(ref t) = tag {
writeln!(
buf,
"{dim}{time_only}{dim:#} {level_style}{level:<5}{level_style:#} {tag_style}{t}{tag_style:#} {rest}"
)
} else {
writeln!(
buf,
"{dim}{time_only}{dim:#} {level_style}{level:<5}{level_style:#} {rest}"
)
}
})
.try_init();
}
let mut builder = tauri::Builder::default()
// Plugins
.plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
.plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_os::init());
// Add desktop-only plugins (autostart, notification)
#[cfg(desktop)]
{
builder = builder
.plugin(tauri_plugin_autostart::init(
tauri_plugin_autostart::MacosLauncher::LaunchAgent,
Some(vec!["--daemon"]),
))
.plugin(tauri_plugin_notification::init());
}
builder
// Setup
.setup(move |app| {
// Register deep link handlers (Windows/Linux)
#[cfg(any(windows, target_os = "linux"))]
{
app.deep_link().register_all()?;
}
// macOS-specific: Handle window close event to minimize to tray
#[cfg(target_os = "macos")]
{
if let Some(window) = app.get_webview_window("main") {
let app_handle = app.handle().clone();
window.on_window_event(move |event| {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
// Prevent the window from closing, hide it instead
api.prevent_close();
if let Some(win) = app_handle.get_webview_window("main") {
let _ = win.hide();
}
}
});
}
}
// Bridge external daemon health file and ensure core background service.
{
let data_dir = app.path().app_data_dir().unwrap_or_else(|_| {
dirs::home_dir()
.unwrap_or_else(|| std::path::PathBuf::from("."))
.join(".openhuman")
});
let app_handle_for_watcher = app.handle().clone();
let data_dir_clone = data_dir.clone();
tauri::async_runtime::spawn(async move {
watch_daemon_health_file(app_handle_for_watcher, data_dir_clone).await;
});
tauri::async_runtime::spawn(async move {
match commands::core_relay::ensure_service_managed_core_running().await {
Ok(()) => {
log::info!("[openhuman] Core background service ensured via core RPC");
}
Err(e) => {
log::error!(
"[openhuman] Failed to ensure core background service: {e}"
);
}
}
});
}
// Start/ensure standalone core process for business logic RPC.
{
let core_run_mode = core_process::default_core_run_mode(daemon_mode);
let core_bin = if matches!(core_run_mode, core_process::CoreRunMode::ChildProcess) {
core_process::default_core_bin()
} else {
None
};
let core_handle = core_process::CoreProcessHandle::new(
core_process::default_core_port(),
core_bin,
core_run_mode,
);
std::env::set_var("OPENHUMAN_CORE_RPC_URL", core_handle.rpc_url());
app.manage(core_handle.clone());
tauri::async_runtime::spawn(async move {
if let Err(err) = core_handle.ensure_running().await {
log::error!("[core] failed to start core process: {err}");
} else {
log::info!("[core] core process ready");
}
});
}
if daemon_mode {
if let Some(window) = app.get_webview_window("main") {
let _ = window.hide();
}
}
Ok(())
})
// Register all commands (desktop build lists handlers explicitly below).
.invoke_handler({
#[cfg(desktop)]
{
tauri::generate_handler![
greet,
// AI config file writing
write_ai_config_file,
ai_get_config,
ai_refresh_config,
core_rpc_relay,
show_window,
hide_window,
toggle_window,
is_window_visible,
minimize_window,
maximize_window,
close_window,
set_window_title,
// OpenHuman local host commands (core RPC uses core_rpc_relay)
openhuman_get_daemon_host_config,
openhuman_set_daemon_host_config,
openhuman_service_install,
openhuman_service_start,
openhuman_service_stop,
openhuman_service_status,
openhuman_service_uninstall,
]
}
})
.build({
let mut context = tauri::generate_context!();
if daemon_mode {
context.config_mut().app.windows.clear();
}
context
})
.expect("error while building tauri application")
.run(move |app_handle, event| {
match event {
// Handle macOS Dock icon click (reopen event)
#[cfg(target_os = "macos")]
RunEvent::Reopen { .. } => {
if !daemon_mode {
if let Some(window) = app_handle.get_webview_window("main") {
let _ = window.show();
let _ = window.unminimize();
let _ = window.set_focus();
}
}
}
// Gracefully shut down background services before process exit.
RunEvent::Exit => {
log::info!("[app] Exit event received, shutting down");
let _ = app_handle;
}
_ => {
let _ = app_handle;
}
}
});
}
pub fn run_core_from_args(args: &[String]) -> anyhow::Result<()> {
let core_bin = crate::core_process::default_core_bin()
.ok_or_else(|| anyhow::anyhow!("openhuman core binary not found"))?;
let status = std::process::Command::new(core_bin)
.args(args)
.status()
.map_err(|e| anyhow::anyhow!("failed to execute core binary: {e}"))?;
if !status.success() {
anyhow::bail!("core binary exited with status {status}");
}
Ok(())
}
+10 -1
View File
@@ -2,5 +2,14 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
openhuman_lib::run()
let args: Vec<String> = std::env::args().collect();
if args.get(1).map(String::as_str) == Some("core") {
if let Err(err) = openhuman::run_core_from_args(&args[2..]) {
eprintln!("core process failed: {err}");
std::process::exit(1);
}
return;
}
openhuman::run()
}
+30
View File
@@ -0,0 +1,30 @@
//! Dev-time path resolution for bundled / repo AI prompts (desktop host).
use std::path::{Path, PathBuf};
/// Best-effort path to repo `src/ai/prompts` when running from a checkout.
pub fn repo_ai_prompts_dir(cwd: &Path) -> Option<PathBuf> {
let prompts = cwd.join("src").join("ai").join("prompts");
if prompts.is_dir() {
return Some(prompts);
}
let app_prompts = cwd.join("app").join("src").join("ai").join("prompts");
if app_prompts.is_dir() {
return Some(app_prompts);
}
None
}
/// Bundled OpenClaw-style prompts inside the packaged app resource dir.
pub fn bundled_openclaw_prompts_dir(resource_dir: &Path) -> Option<PathBuf> {
let candidates = [
resource_dir.join("ai").join("prompts"),
resource_dir.join("_up_").join("ai").join("prompts"),
];
for p in candidates {
if p.is_dir() {
return Some(p);
}
}
None
}
+1
View File
@@ -0,0 +1 @@
pub mod dev_paths;
+31 -11
View File
@@ -1,25 +1,29 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "openhuman",
"version": "0.1.0",
"identifier": "com.tinyhumansai.openhuman",
"productName": "OpenHuman",
"version": "0.49.16",
"identifier": "com.openhuman.app",
"build": {
"beforeDevCommand": "yarn dev",
"beforeDevCommand": "npm run core:stage && npm run dev",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "yarn build",
"beforeBuildCommand": "npm run build:app && npm run core:stage",
"frontendDist": "../dist"
},
"app": {
"windows": [
{
"title": "openhuman",
"label": "main",
"title": "OpenHuman",
"width": 800,
"height": 600
"height": 720,
"visible": false,
"decorations": true,
"resizable": true,
"center": true
}
],
"security": {
"csp": null
}
"security": { "csp": null },
"macOSPrivateApi": true
},
"bundle": {
"active": true,
@@ -30,6 +34,22 @@
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
],
"resources": ["../../skills/skills", "../../src/ai/prompts"],
"externalBin": ["binaries/openhuman"],
"createUpdaterArtifacts": true,
"macOS": {
"minimumSystemVersion": "10.15",
"dmg": { "background": "./images/background-dmg.png" }
}
},
"plugins": {
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDA1NkI1NEQzNjVDMjA0NDgKUldSSUJNSmwwMVJyQmJCTnQrQWxPSmhZQmVyWU1NMXdwRlRCSHR0ZEdlbkRncCtpQnhaZU5rUEQK",
"endpoints": [
"https://github.com/tinyhumansai/openhuman/releases/latest/download/latest.json"
]
},
"deep-link": { "desktop": { "schemes": ["openhuman"] } }
}
}
+2 -2
View File
@@ -11,8 +11,8 @@
line-height: 24px;
font-weight: 400;
color: #0f0f0f;
background-color: #f6f6f6;
color: #fff;
background-color: #000;
font-synthesis: none;
text-rendering: optimizeLegibility;
+59 -44
View File
@@ -1,50 +1,65 @@
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import { invoke } from "@tauri-apps/api/core";
import "./App.css";
import * as Sentry from '@sentry/react';
import { Provider } from 'react-redux';
import { HashRouter as Router } from 'react-router-dom';
import { PersistGate } from 'redux-persist/integration/react';
import AppRoutes from './AppRoutes';
import ErrorFallbackScreen from './components/ErrorFallbackScreen';
import MiniSidebar from './components/MiniSidebar';
import AIProvider from './providers/AIProvider';
import SkillProvider from './providers/SkillProvider';
import SocketProvider from './providers/SocketProvider';
import UserProvider from './providers/UserProvider';
import { tagErrorSource } from './services/errorReportQueue';
import { persistor, store } from './store';
import { syncMemoryClientToken } from './utils/tauriCommands';
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
setGreetMsg(await invoke("greet", { name }));
}
return (
<main className="container">
<h1>Welcome to Tauri + React</h1>
<div className="row">
<a href="https://vite.dev" target="_blank">
<img src="/vite.svg" className="logo vite" alt="Vite logo" />
</a>
<a href="https://tauri.app" target="_blank">
<img src="/tauri.svg" className="logo tauri" alt="Tauri logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<p>Click on the Tauri, Vite, and React logos to learn more.</p>
<form
className="row"
onSubmit={(e) => {
e.preventDefault();
greet();
}}
>
<input
id="greet-input"
onChange={(e) => setName(e.currentTarget.value)}
placeholder="Enter a name..."
/>
<button type="submit">Greet</button>
</form>
<p>{greetMsg}</p>
</main>
<Sentry.ErrorBoundary
fallback={({ error, componentStack, resetError }) => (
<ErrorFallbackScreen error={error} componentStack={componentStack} onReset={resetError} />
)}
onError={(_error, componentStack, eventId) => {
tagErrorSource(eventId, 'react', componentStack);
}}>
<Provider store={store}>
<PersistGate
loading={null}
persistor={persistor}
onBeforeLift={async () => {
const token = store.getState().auth.token;
console.info('[memory] PersistGate onBeforeLift: token_present=%s', !!token);
if (token) await syncMemoryClientToken(token);
}}>
<UserProvider>
<SocketProvider>
<AIProvider>
<SkillProvider>
<Router>
<div className="relative h-screen flex flex-col overflow-hidden">
<div className="flex-1 flex overflow-hidden">
<MiniSidebar />
<div className="flex flex-col flex-1 relative overflow-hidden">
<div className="flex-1 overflow-y-auto">
<AppRoutes />
</div>
<div className="pointer-events-none flex-shrink-0 flex justify-center z-50">
<div className="w-full px-3 py-1.5 text-[9px] uppercase tracking-[0.18em] text-white/40 text-center bg-[#000]">
OpenHuman is in early beta
</div>
</div>
</div>
</div>
</div>
</Router>
</SkillProvider>
</AIProvider>
</SocketProvider>
</UserProvider>
</PersistGate>
</Provider>
</Sentry.ErrorBoundary>
);
}
View File

Before

Width:  |  Height:  |  Size: 644 B

After

Width:  |  Height:  |  Size: 644 B

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -1,4 +1,3 @@
import { invoke } from '@tauri-apps/api/core';
import { useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
@@ -6,7 +5,7 @@ import { deriveConnectionStatus, useSkillConnectionStatus } from '../lib/skills/
import { deriveSkillSyncSummaryText } from '../pages/skillsSyncUi';
import { useAppSelector } from '../store/hooks';
import { IS_DEV } from '../utils/config';
import SelfEvolveModal from './skills/SelfEvolveModal';
import { runtimeDiscoverSkills } from '../utils/tauriCommands';
import {
DefaultIcon,
SKILL_ICONS,
@@ -16,30 +15,6 @@ import {
} from './skills/shared';
import SkillSetupModal from './skills/SkillSetupModal';
/** Normalize a raw unified registry entry into a SkillListEntry for display. */
function normalizeUnifiedEntry(e: Record<string, unknown>): SkillListEntry {
const setup = e.setup as { required?: boolean; oauth?: unknown } | undefined;
// Treat both interactive setup steps and OAuth-only flows as "has setup"
// so that clicking a skill (e.g. Gmail) opens the connection/setup wizard
// instead of jumping straight to the management panel.
const hasSetup =
!!setup &&
(setup.required === true ||
// OAuth config means we still need a connection step in the wizard
!!setup.oauth);
return {
id: e.id as string,
name:
(e.name as string) || (e.id as string).charAt(0).toUpperCase() + (e.id as string).slice(1),
description: (e.description as string) || '',
icon: SKILL_ICONS[e.id as string],
ignoreInProduction: (e.ignoreInProduction as boolean) ?? false,
hasSetup,
skill_type: (e.skill_type as 'openhuman' | 'openclaw') ?? 'openhuman',
};
}
interface SkillRowProps {
skillId: string;
name: string;
@@ -115,8 +90,6 @@ export default function SkillsGrid() {
const navigate = useNavigate();
const [skillsList, setSkillsList] = useState<SkillListEntry[]>([]);
const [loading, setLoading] = useState(true);
const [generating, setGenerating] = useState(false);
const [selfEvolveOpen, setSelfEvolveOpen] = useState(false);
const [setupModalOpen, setSetupModalOpen] = useState(false);
const [activeSkillId, setActiveSkillId] = useState<string | null>(null);
const [activeSkillName, setActiveSkillName] = useState<string>('');
@@ -124,21 +97,16 @@ export default function SkillsGrid() {
const [activeSkillHasSetup, setActiveSkillHasSetup] = useState(false);
const [activeSkillType, setActiveSkillType] = useState<'openhuman' | 'openclaw'>('openhuman');
// Get Redux state for sorting
const skillsState = useAppSelector(state => state.skills.skills);
const skillStates = useAppSelector(state => state.skills.skillStates);
const syncStatsBySkill = useAppSelector(state => state.skills.syncStatsBySkill);
// Load skills from the unified registry (covers both openhuman and openclaw types).
// Extracted so it can be called after skill creation (e.g. from SelfEvolveModal).
const refreshSkills = async () => {
try {
// Try unified registry first — it merges both skill types.
const entries = await invoke<Array<Record<string, unknown>>>('unified_list_skills');
const processed: SkillListEntry[] = entries
.filter(e => {
const id = e.id as string;
const manifests = await runtimeDiscoverSkills();
const processed: SkillListEntry[] = manifests
.filter(m => {
const id = m.id as string;
if (id.includes('_')) {
console.warn(
`Skill "${id}" contains underscore and will be skipped. Skill IDs cannot contain underscores.`
@@ -147,38 +115,27 @@ export default function SkillsGrid() {
}
return true;
})
.map(normalizeUnifiedEntry)
.map(m => {
const setup = m.setup as { required?: boolean; oauth?: unknown } | undefined;
const hasSetup =
!!setup &&
(setup.required === true ||
// OAuth-only skills still need a setup/connect flow
!!setup.oauth);
return {
id: m.id as string,
name: (m.name as string) || (m.id as string),
description: (m.description as string) || '',
icon: SKILL_ICONS[m.id as string],
ignoreInProduction: (m.ignoreInProduction as boolean) ?? false,
hasSetup,
skill_type: 'openhuman' as const,
};
})
.filter(s => IS_DEV || !s.ignoreInProduction);
setSkillsList(processed);
} catch {
// Fallback to legacy runtime_discover_skills if unified registry isn't available.
try {
const manifests = await invoke<Array<Record<string, unknown>>>('runtime_discover_skills');
const processed: SkillListEntry[] = manifests
.filter(m => !(m.id as string).includes('_'))
.map(m => {
const setup = m.setup as { required?: boolean; oauth?: unknown } | undefined;
const hasSetup =
!!setup &&
(setup.required === true ||
// OAuth-only skills still need a setup/connect flow
!!setup.oauth);
return {
id: m.id as string,
name: (m.name as string) || (m.id as string),
description: (m.description as string) || '',
icon: SKILL_ICONS[m.id as string],
ignoreInProduction: (m.ignoreInProduction as boolean) ?? false,
hasSetup,
skill_type: 'openhuman' as const,
};
})
.filter(s => IS_DEV || !s.ignoreInProduction);
setSkillsList(processed);
} catch (err) {
console.warn('Could not load skills:', err);
}
} catch (err) {
console.warn('Could not load skills:', err);
} finally {
setLoading(false);
}
@@ -188,7 +145,6 @@ export default function SkillsGrid() {
refreshSkills();
}, []);
// Sort skills by connection status (connected first)
const sortedSkillsList = useMemo(() => {
return [...skillsList]
.sort((a, b) => {
@@ -203,7 +159,6 @@ export default function SkillsGrid() {
const priorityA = STATUS_PRIORITY[statusA] ?? 999;
const priorityB = STATUS_PRIORITY[statusB] ?? 999;
// If same priority, sort alphabetically by name
if (priorityA === priorityB) {
return a.name.localeCompare(b.name);
}
@@ -212,7 +167,7 @@ export default function SkillsGrid() {
})
.filter(s => IS_DEV || !s.ignoreInProduction);
}, [skillsList, skillsState, skillStates]);
// If loading or no skills on desktop, don't render
if (loading || skillsList.length === 0) {
return null;
}
@@ -231,70 +186,6 @@ export default function SkillsGrid() {
<div className="animate-fade-up mt-4 mb-8 relative">
<div className="flex items-center justify-between mb-3 px-1">
<h3 className="text-sm font-semibold text-white opacity-80">Available Skills</h3>
<div className="flex items-center gap-3">
{/* Auto-Generate button — opens the self-evolving skill modal */}
<button
onClick={e => {
e.stopPropagation();
setSelfEvolveOpen(true);
}}
className="text-xs text-primary-400 hover:text-primary-300 transition-colors flex items-center gap-1">
{/* Sparkle / robot icon */}
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M5 3l1.5 4.5L11 9l-4.5 1.5L5 15l-1.5-4.5L-1 9l4.5-1.5L5 3zM19 11l1 3 3 1-3 1-1 3-1-3-3-1 3-1 1-3z"
/>
</svg>
Auto-Generate
</button>
{/* Generate button — quick scaffold */}
<button
onClick={async e => {
e.stopPropagation();
setGenerating(true);
try {
await invoke('unified_generate_skill', {
spec: {
name: `generated-demo-${Date.now()}`,
description: 'Auto-generated skill demonstrating the unified registry',
skill_type: 'openhuman',
tool_code:
'return { message: `Hello from generated skill! args=${JSON.stringify(args)}` };',
},
});
await refreshSkills();
} catch (err) {
console.warn('Failed to generate skill:', err);
} finally {
setGenerating(false);
}
}}
className="text-xs text-primary-400 hover:text-primary-300 transition-colors flex items-center gap-1 disabled:opacity-50"
disabled={generating}>
{generating ? (
<span className="opacity-60">Generating</span>
) : (
<>
<svg
className="w-3.5 h-3.5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 4v16m8-8H4"
/>
</svg>
Generate
</>
)}
</button>
</div>
</div>
<div
className="glass rounded-xl overflow-hidden skills-table-container relative cursor-pointer"
@@ -345,14 +236,12 @@ export default function SkillsGrid() {
</tbody>
</table>
</div>
{/* Hover overlay */}
<div className="skills-table-overlay absolute inset-0 bg-black/80 flex items-center justify-center rounded-xl opacity-0 transition-opacity duration-200 pointer-events-none">
<span className="text-sm font-medium text-white">View all skills</span>
</div>
</div>
</div>
{/* Setup modal */}
{setupModalOpen && activeSkillId && (
<SkillSetupModal
skillId={activeSkillId}
@@ -366,11 +255,6 @@ export default function SkillsGrid() {
}}
/>
)}
{/* Self-Evolve modal */}
{selfEvolveOpen && (
<SelfEvolveModal onClose={() => setSelfEvolveOpen(false)} onSkillCreated={refreshSkills} />
)}
</>
);
}

Some files were not shown because too many files have changed in this diff Show More