Files
openhuman/src-tauri/src/core_process.rs
T
Steven EnamakelandGitHub 5ecc7cbc75 refactor: consolidate CLI binaries and streamline CI workflows (#44)
* chore: update dependencies and remove unused packages

- Removed several unused dependencies from package.json, including `@tauri-apps/plugin-shell`, `@types/react-router-dom`, `immer`, `qrcode.react`, and `@testing-library/user-event`.
- Updated the Vite configuration to exclude `telegram` from the optimizeDeps include list.
- Cleaned up yarn.lock by removing references to deleted packages.
- Deleted outdated GitHub Actions workflows for macOS ARM64 build, package and publish, and update changelog.

* refactor: consolidate CLI binaries and update configurations

- Renamed the main binary from `openhuman-core` to `openhuman` for consistency across the project.
- Removed the `openhuman-cli` and `openhuman-core` binaries, consolidating functionality into the new `openhuman` binary.
- Updated build configurations in GitHub Actions and Tauri to reflect the new binary name and paths.
- Adjusted the Tailwind and Vite configurations to point to the correct HTML and source directories.
- Introduced a new method in the Rust core server for handling JSON-RPC calls, enhancing the API structure.

* chore: update subproject commit reference in skills

* chore: update Rust test workflow and improve CLI command formatting

- Modified the GitHub Actions workflow to include a new job for running Rust tests in the workspace.
- Updated the `test:rust` command in `package.json` to run tests for the entire workspace.
- Refactored CLI command definitions in `openhuman.rs` for improved readability by consolidating struct fields into single lines.

* chore: enhance E2E testing scripts and workflows

- Updated `.prettierignore` to exclude the `rust-core` directory.
- Added new E2E testing commands in `package.json` for running all flows and improved formatting commands to include `cargo fmt`.
- Enhanced GitHub Actions workflows to streamline E2E testing, including new scripts for running individual specs and all flows sequentially.
- Refactored existing E2E scripts to utilize a common function for running tests, improving maintainability and readability.
- Introduced a new script for resolving Node.js and Appium dependencies, ensuring compatibility for E2E tests.
- Cleaned up and simplified existing E2E scripts by removing redundant code and consolidating functionality.

* chore: update GitHub Actions workflows for Tauri build process

- Modified build and release workflows to reference the correct target directory for Rust binaries, ensuring compatibility with the Cargo workspace structure.
- Adjusted caching configuration in the test workflow for consistency and improved readability.
- Standardized environment variable formatting in the test workflow to enhance clarity.

* fix: standardize string formatting in GitHub Actions workflows

- Updated the test workflow to use single quotes for cache configuration and environment variables, enhancing consistency across the file.
2026-03-27 13:58:29 -07:00

175 lines
5.2 KiB
Rust

use std::path::PathBuf;
use std::sync::Arc;
use tokio::process::{Child, Command};
use tokio::sync::Mutex;
#[derive(Clone)]
pub struct CoreProcessHandle {
child: Arc<Mutex<Option<Child>>>,
port: u16,
core_bin: Option<PathBuf>,
}
impl CoreProcessHandle {
pub fn new(port: u16, core_bin: Option<PathBuf>) -> Self {
Self {
child: Arc::new(Mutex::new(None)),
port,
core_bin,
}
}
pub fn rpc_url(&self) -> String {
format!("http://127.0.0.1:{}/rpc", self.port)
}
pub async fn ensure_running(&self) -> Result<(), String> {
if crate::core_rpc::ping().await {
log::info!(
"[core] found existing core rpc endpoint at {}",
self.rpc_url()
);
return Ok(());
}
let mut guard = self.child.lock().await;
if guard.is_none() {
let mut cmd = if let Some(core_bin) = &self.core_bin {
let mut cmd = Command::new(core_bin);
cmd.arg("serve").arg("--port").arg(self.port.to_string());
log::info!(
"[core] spawning dedicated core binary: {:?} serve --port {}",
cmd.as_std().get_program(),
self.port
);
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("serve")
.arg("--port")
.arg(self.port.to_string());
log::warn!(
"[core] dedicated core binary not found; falling back to self subcommand"
);
cmd
};
let child = cmd
.spawn()
.map_err(|e| format!("failed to spawn core process: {e}"))?;
*guard = Some(child);
}
drop(guard);
for _ in 0..40 {
if crate::core_rpc::ping().await {
log::info!("[core] core rpc became ready at {}", self.rpc_url());
return Ok(());
}
let mut guard = self.child.lock().await;
if let Some(child) = guard.as_mut() {
match child.try_wait() {
Ok(Some(status)) => {
return Err(format!("core process exited before ready: {status}"));
}
Ok(None) => {}
Err(e) => return Err(format!("failed checking core process status: {e}")),
}
}
drop(guard);
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
}
Err("core process did not become ready".to_string())
}
pub async fn shutdown(&self) {
let mut guard = self.child.lock().await;
if let Some(child) = guard.as_mut() {
let _ = child.kill().await;
}
*guard = None;
}
}
pub fn default_core_port() -> u16 {
std::env::var("OPENHUMAN_CORE_PORT")
.ok()
.and_then(|v| v.parse::<u16>().ok())
.unwrap_or(7788)
}
pub fn default_core_bin() -> Option<PathBuf> {
if let Ok(path) = std::env::var("OPENHUMAN_CORE_BIN") {
let candidate = PathBuf::from(path);
if candidate.exists() {
return Some(candidate);
}
}
let exe = std::env::current_exe().ok()?;
let exe_dir = exe.parent()?;
#[cfg(windows)]
let standalone = exe_dir.join("openhuman.exe");
#[cfg(not(windows))]
let standalone = exe_dir.join("openhuman");
if standalone.exists() {
return Some(standalone);
}
#[cfg(windows)]
let legacy_standalone = exe_dir.join("openhuman-core.exe");
#[cfg(not(windows))]
let legacy_standalone = exe_dir.join("openhuman-core");
if legacy_standalone.exists() {
return Some(legacy_standalone);
}
// Sidecar layout: bundle.externalBin("binaries/openhuman") is emitted as
// openhuman-<target-triple>(.exe) under app resources.
let mut search_dirs = vec![exe_dir.to_path_buf()];
#[cfg(target_os = "macos")]
{
if let Some(resources_dir) = exe_dir.parent().map(|p| p.join("Resources")) {
search_dirs.push(resources_dir);
}
}
for dir in search_dirs {
let Ok(entries) = std::fs::read_dir(&dir) else {
continue;
};
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"))
|| (file_name.starts_with("openhuman-core-") && file_name.ends_with(".exe"));
#[cfg(not(windows))]
let matches =
file_name.starts_with("openhuman-") || file_name.starts_with("openhuman-core-");
if matches {
return Some(path);
}
}
}
None
}