fix(rpc): register openhuman.system_info and add legacy alias (Sentry CORE-RUST-G0, closes #2871) (#2877)

Co-authored-by: cyrus@tinyhumans.ai <cyrus@tinyhumans.ai>
This commit is contained in:
Mega Mind
2026-05-29 04:04:24 +05:30
committed by GitHub
co-authored by cyrus@tinyhumans.ai <cyrus@tinyhumans.ai>
parent 972e327381
commit 32dd0be19a
5 changed files with 199 additions and 10 deletions
@@ -95,6 +95,24 @@ describe('rpcMethods catalog', () => {
});
});
describe('health legacy alias resolution (Sentry CORE-RUST-FG / CORE-RUST-G0)', () => {
test('health_snapshot resolves to openhuman.health_snapshot', () => {
expect(normalizeRpcMethod('health_snapshot')).toBe(CORE_RPC_METHODS.healthSnapshot);
});
test('openhuman.system_info resolves to openhuman.health_system_info (Sentry CORE-RUST-G0)', () => {
// Older clients called openhuman.system_info before the method was
// namespaced under health as openhuman.health_system_info.
expect(normalizeRpcMethod('openhuman.system_info')).toBe(CORE_RPC_METHODS.healthSystemInfo);
});
test('canonical health_system_info passes through unchanged', () => {
expect(normalizeRpcMethod('openhuman.health_system_info')).toBe(
'openhuman.health_system_info'
);
});
});
test('catalog canonical methods exist in core schema registry (drift guard)', () => {
const schemaSources = [
fs.readFileSync(
+5
View File
@@ -38,6 +38,7 @@ export const CORE_RPC_METHODS = {
mcpClientsInstalledList: 'openhuman.mcp_clients_installed_list',
mcpClientsToolCall: 'openhuman.mcp_clients_tool_call',
healthSnapshot: 'openhuman.health_snapshot',
healthSystemInfo: 'openhuman.health_system_info',
} as const;
export type CoreRpcMethod = (typeof CORE_RPC_METHODS)[keyof typeof CORE_RPC_METHODS];
@@ -77,6 +78,10 @@ export const LEGACY_METHOD_ALIASES: Record<string, CoreRpcMethod> = {
'openhuman.providers_list_models': CORE_RPC_METHODS.inferenceListModels,
'openhuman.inference_embed': CORE_RPC_METHODS.embeddingsEmbed,
health_snapshot: CORE_RPC_METHODS.healthSnapshot,
// `openhuman.system_info` was used by older clients / SDK callers before the
// method was namespaced as `openhuman.health_system_info`.
// Sentry CORE-RUST-G0 — https://sentry.tinyhumans.ai/organizations/tinyhumans/issues/6340/
'openhuman.system_info': CORE_RPC_METHODS.healthSystemInfo,
};
export function normalizeRpcMethod(method: string): string {
+16
View File
@@ -118,6 +118,10 @@ const LEGACY_ALIASES: &[(&str, &str)] = &[
// bare `health_snapshot` (no namespace prefix) was used by older clients
// before the canonical `openhuman.health_snapshot` form was established.
("health_snapshot", "openhuman.health_snapshot"),
// `openhuman.system_info` was used by older clients / SDK callers before
// the method was namespaced under `health` as `openhuman.health_system_info`.
// Sentry CORE-RUST-G0 — https://sentry.tinyhumans.ai/organizations/tinyhumans/issues/6340/
("openhuman.system_info", "openhuman.health_system_info"),
("openhuman.inference_embed", "openhuman.embeddings_embed"),
("openhuman.local_ai_presets", "openhuman.inference_presets"),
(
@@ -422,6 +426,18 @@ mod tests {
);
}
#[test]
fn resolve_legacy_rewrites_system_info() {
// Sentry CORE-RUST-G0: older clients called `openhuman.system_info`
// before the method was namespaced under `health` as
// `openhuman.health_system_info`. The alias table must rewrite it so
// the call resolves against the registered controller.
assert_eq!(
resolve_legacy("openhuman.system_info"),
"openhuman.health_system_info",
);
}
#[test]
fn resolve_legacy_passes_through_unknown_methods() {
assert_eq!(
+82
View File
@@ -1,8 +1,90 @@
//! JSON-RPC / CLI controller surface for the process health registry.
use serde::Serialize;
use crate::openhuman::health;
use crate::rpc::RpcOutcome;
pub fn health_snapshot() -> RpcOutcome<serde_json::Value> {
RpcOutcome::single_log(health::snapshot_json(), "health_snapshot requested")
}
/// Static system information returned by `openhuman.health_system_info`.
#[derive(Debug, Serialize)]
pub struct SystemInfo {
/// Cargo package version of the running core binary.
pub version: &'static str,
/// Target operating system name (`linux`, `macos`, `windows`, …).
pub os: &'static str,
/// Target CPU architecture (`x86_64`, `aarch64`, …).
pub arch: &'static str,
/// Current process ID.
pub pid: u32,
}
/// Returns static system information: version, OS, architecture, and PID.
///
/// This is the handler backing the `openhuman.health_system_info` RPC method
/// (legacy callers may send `openhuman.system_info`, which the alias table
/// rewrites before dispatch).
pub fn system_info() -> RpcOutcome<SystemInfo> {
let info = SystemInfo {
version: env!("CARGO_PKG_VERSION"),
os: std::env::consts::OS,
arch: std::env::consts::ARCH,
pid: std::process::id(),
};
tracing::debug!(
version = info.version,
os = info.os,
arch = info.arch,
pid = info.pid,
"[health] system_info requested"
);
RpcOutcome::new(info, vec![])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn system_info_returns_non_empty_version() {
let outcome = system_info();
let json = outcome
.into_cli_compatible_json()
.expect("serialization ok");
let version = json["version"].as_str().expect("version is a string");
assert!(!version.is_empty(), "version must be non-empty");
}
#[test]
fn system_info_returns_known_os() {
let outcome = system_info();
let json = outcome
.into_cli_compatible_json()
.expect("serialization ok");
let os = json["os"].as_str().expect("os is a string");
// std::env::consts::OS is always one of the compile-time Rust target OS names.
assert!(!os.is_empty(), "os must be non-empty");
}
#[test]
fn system_info_returns_non_zero_pid() {
let outcome = system_info();
let json = outcome
.into_cli_compatible_json()
.expect("serialization ok");
let pid = json["pid"].as_u64().expect("pid is a u64");
assert!(pid > 0, "pid must be greater than zero");
}
#[test]
fn health_snapshot_returns_serializable_value() {
let outcome = health_snapshot();
let json = outcome
.into_cli_compatible_json()
.expect("serialization ok");
assert!(json.is_object(), "snapshot must be a JSON object");
}
}
+78 -10
View File
@@ -5,14 +5,20 @@ use crate::core::{ControllerSchema, FieldSchema, TypeSchema};
use crate::rpc::RpcOutcome;
pub fn all_controller_schemas() -> Vec<ControllerSchema> {
vec![schemas("snapshot")]
vec![schemas("snapshot"), schemas("system_info")]
}
pub fn all_registered_controllers() -> Vec<RegisteredController> {
vec![RegisteredController {
schema: schemas("snapshot"),
handler: handle_snapshot,
}]
vec![
RegisteredController {
schema: schemas("snapshot"),
handler: handle_snapshot,
},
RegisteredController {
schema: schemas("system_info"),
handler: handle_system_info,
},
]
}
pub fn schemas(function: &str) -> ControllerSchema {
@@ -29,6 +35,39 @@ pub fn schemas(function: &str) -> ControllerSchema {
required: true,
}],
},
"system_info" => ControllerSchema {
namespace: "health",
function: "system_info",
description:
"Return static system information: app version, OS, architecture, and PID.",
inputs: vec![],
outputs: vec![
FieldSchema {
name: "version",
ty: TypeSchema::String,
comment: "Running core binary version (CARGO_PKG_VERSION).",
required: true,
},
FieldSchema {
name: "os",
ty: TypeSchema::String,
comment: "Host operating system name (linux, macos, windows, …).",
required: true,
},
FieldSchema {
name: "arch",
ty: TypeSchema::String,
comment: "CPU architecture (x86_64, aarch64, …).",
required: true,
},
FieldSchema {
name: "pid",
ty: TypeSchema::String,
comment: "Current process ID.",
required: true,
},
],
},
_ => ControllerSchema {
namespace: "health",
function: "unknown",
@@ -48,6 +87,10 @@ fn handle_snapshot(_params: Map<String, Value>) -> ControllerFuture {
Box::pin(async { to_json(crate::openhuman::health::rpc::health_snapshot()) })
}
fn handle_system_info(_params: Map<String, Value>) -> ControllerFuture {
Box::pin(async { to_json(crate::openhuman::health::rpc::system_info()) })
}
fn to_json<T: serde::Serialize>(outcome: RpcOutcome<T>) -> Result<Value, String> {
outcome.into_cli_compatible_json()
}
@@ -57,13 +100,13 @@ mod tests {
use super::*;
#[test]
fn all_schemas_returns_one() {
assert_eq!(all_controller_schemas().len(), 1);
fn all_schemas_returns_two() {
assert_eq!(all_controller_schemas().len(), 2);
}
#[test]
fn all_controllers_returns_one() {
assert_eq!(all_registered_controllers().len(), 1);
fn all_controllers_returns_two() {
assert_eq!(all_registered_controllers().len(), 2);
}
#[test]
@@ -75,6 +118,16 @@ mod tests {
assert!(!s.outputs.is_empty());
}
#[test]
fn system_info_schema() {
let s = schemas("system_info");
assert_eq!(s.namespace, "health");
assert_eq!(s.function, "system_info");
assert!(s.inputs.is_empty());
// version, os, arch, pid
assert_eq!(s.outputs.len(), 4);
}
#[test]
fn unknown_function_returns_unknown() {
let s = schemas("bad");
@@ -86,7 +139,10 @@ mod tests {
fn schemas_and_controllers_match() {
let s = all_controller_schemas();
let c = all_registered_controllers();
assert_eq!(s[0].function, c[0].schema.function);
assert_eq!(s.len(), c.len());
for (schema, controller) in s.iter().zip(c.iter()) {
assert_eq!(schema.function, controller.schema.function);
}
}
#[tokio::test]
@@ -96,6 +152,18 @@ mod tests {
assert!(result.unwrap().is_object());
}
#[tokio::test]
async fn handle_system_info_returns_json_object() {
let result = handle_system_info(Map::new()).await;
assert!(result.is_ok());
let json = result.unwrap();
assert!(json.is_object());
assert!(json["version"].as_str().is_some());
assert!(json["os"].as_str().is_some());
assert!(json["arch"].as_str().is_some());
assert!(json["pid"].as_u64().is_some());
}
#[test]
fn to_json_helper() {
let outcome = RpcOutcome::single_log(serde_json::json!({"ok": true}), "log");