fix(yuanbao): correct AuthBind plugin_version / bot_version mapping (#2804)

This commit is contained in:
linruitao
2026-05-28 20:49:39 -07:00
committed by GitHub
parent 6881183857
commit 1fdb3f76b0
5 changed files with 53 additions and 18 deletions
@@ -9,6 +9,22 @@ use serde::{Deserialize, Serialize};
use super::errors::YuanbaoError;
/// Default value for `DeviceInfo.app_version` (server-side `plugin_version`).
///
/// In `yuanbao-openclaw-plugin`'s wire mapping, `plugin_version` represents
/// the *plugin-layer* (npm package) version while `bot_version` represents
/// the *framework runtime* version. OpenHuman has no separate plugin layer;
/// we reuse this slot for the **yuanbao channel provider's** own iteration
/// version. Bump it when the provider's protocol adapter changes in a way
/// the server might care about. The framework runtime version is reported
/// separately via `CARGO_PKG_VERSION` in `DeviceInfo.bot_version`.
pub(crate) const DEFAULT_PLUGIN_VERSION: &str = "0.1.0";
/// Strip legacy `openhuman/` prefix from version strings in config/TOML.
pub(crate) fn strip_version_prefix(version: &str) -> &str {
version.strip_prefix("openhuman/").unwrap_or(version)
}
/// Production environment endpoints (default).
const PROD_API_DOMAIN: &str = "https://bot.yuanbao.tencent.com";
const PROD_WS_URL: &str = "wss://bot-wss.yuanbao.tencent.com/wss/connection";
@@ -46,8 +62,18 @@ pub struct YuanbaoConfig {
/// `api_domain/api/token/sign` with `(app_key, app_secret)` to fetch one.
#[serde(default)]
pub token: String,
/// Plugin/bot version reported in `AuthBindReq.DeviceInfo.bot_version`.
#[serde(default = "default_bot_version")]
/// Yuanbao channel-provider iteration version, reported in
/// `AuthBindReq.DeviceInfo.app_version` (server-side `plugin_version`).
///
/// **NOTE:** Despite the legacy `bot_version` TOML key name, this value
/// fills the *plugin*-version slot on the wire — see
/// [`DEFAULT_PLUGIN_VERSION`] for the semantic mapping. The framework
/// runtime version (`DeviceInfo.bot_version` / server `bot_version`)
/// is sourced from `CARGO_PKG_VERSION` and is **not** user-configurable;
/// operators who previously set this key intending to override the
/// framework version should drop it. New configs may use the clearer
/// `plugin_version` alias.
#[serde(default = "default_bot_version", alias = "plugin_version")]
pub bot_version: String,
/// Optional bot display name — used by the `@bot` mention guard.
#[serde(default)]
@@ -161,8 +187,11 @@ impl YuanbaoConfig {
}
}
/// Default value for [`YuanbaoConfig::bot_version`] — populates
/// `DeviceInfo.app_version` (server `plugin_version`) when the user config
/// omits the field.
fn default_bot_version() -> String {
"openhuman/0.1.0".into()
DEFAULT_PLUGIN_VERSION.into()
}
fn default_env() -> String {
@@ -401,15 +401,19 @@ impl YuanbaoConnection {
// "bot" when missing (matches the plugin's static-token branch
// and `data.source || "bot"` resolution).
let resolved_source = if source.is_empty() { "bot" } else { source };
// Align with yuanbao-openclaw-plugin: app_version → plugin_version,
// DeviceInfo field 24 → bot_version (OpenHuman framework / CARGO_PKG_VERSION).
let plugin_version = super::config::strip_version_prefix(&cfg.bot_version);
let framework_version = env!("CARGO_PKG_VERSION");
let frame = encode_auth_bind(
"ybBot",
&uid,
resolved_source,
token,
&msg_id,
env!("CARGO_PKG_VERSION"),
plugin_version,
std::env::consts::OS,
&cfg.bot_version,
framework_version,
&cfg.route_env,
);
self.send_frame(frame).await
@@ -599,23 +599,25 @@ mod tests {
assert_eq!(frame.data, b"payload");
}
/// Smoke-test that [`encode_auth_bind`] produces a frame round-trippable
/// via [`decode_conn_msg`] and that the `app_version` / `bot_version`
/// arguments land in the expected `DeviceInfo` fields (regression guard
/// for the plugin_version/bot_version swap).
#[test]
fn auth_bind_smoke() {
let buf = encode_auth_bind(
"biz",
"uid",
"openclaw",
"tok",
"mid",
"1.0",
"linux",
"openhuman/0.1.0",
"",
"biz", "uid", "openclaw", "tok", "mid", "0.1.0", "linux", "1.0", "",
);
let frame = decode_conn_msg(&buf).unwrap();
assert_eq!(frame.cmd, cmd::AUTH_BIND);
assert_eq!(frame.module, module::CONN_ACCESS);
assert!(!frame.data.is_empty());
let req_fields = parse_fields(&frame.data).unwrap();
let dev_buf = get_bytes(&req_fields, 3);
let dev_fields = parse_fields(&dev_buf).unwrap();
assert_eq!(get_string(&dev_fields, 1), "0.1.0", "app_version");
assert_eq!(get_string(&dev_fields, 24), "1.0", "bot_version");
}
#[test]
@@ -65,11 +65,11 @@ pub mod tim {
}
/// Fixed instance id reported in `AuthBindReq.DeviceInfo.instance_id` and
/// the `X-Instance-Id` HTTP header. Mirrors `OPENCLAW_ID = 16` used by
/// the `X-Instance-Id` HTTP header. Mirrors `OPENCLAW_ID = 20` used by
/// `yuanbao-openclaw-plugin` (`src/access/ws/conn-codec.ts`) — the server
/// keys some checks off this value, so it must match the value the sign
/// endpoint sees when the token is minted.
pub const OPENHUMAN_INSTANCE_ID: &str = "16";
pub const OPENHUMAN_INSTANCE_ID: &str = "20";
/// Reconnect backoff schedule (seconds). Mirrors hermes-agent.
pub const RECONNECT_DELAYS: &[u64] = &[1, 2, 5, 10, 30, 60];
@@ -211,13 +211,13 @@ impl SignManager {
.post(&url)
.timeout(Duration::from_secs(HTTP_TIMEOUT_SECS))
.header("Content-Type", "application/json")
.header("X-AppVersion", "openhuman/0.1.0")
.header("X-AppVersion", super::config::DEFAULT_PLUGIN_VERSION)
.header("X-OperationSystem", "linux")
.header(
"X-Instance-Id",
super::proto_constants::OPENHUMAN_INSTANCE_ID,
)
.header("X-Bot-Version", "openhuman/0.1.0");
.header("X-Bot-Version", env!("CARGO_PKG_VERSION"));
if !route_env.is_empty() {
req = req.header("X-Route-Env", route_env);
}