复刻 @nodeagent/client 的协议行为,供桌面控制台(Tauri 壳)经 Cargo path 依赖: - 模块:error / types / ulid / crypto / tls / discovery / client - 握手:hello → challenge → auth → auth_ok;psk(HMAC-SHA256) 与 ed25519 两种模式 - 证书指纹 sha256(DER) 钉住(E_CERT_MISMATCH);ULID(Crockford Base32) - 断线指数退避重连 1s→30s(±15% 抖动) Rust async Send 三层陷阱(本轮主要成本,解法已写入 client.rs 注释): 1. futures_util::lock::Mutex 无手写 Sync 实现 → 含它的类型整体 !Sync 2. tokio::sync::MutexGuard 非 Send 3. establish → spawn(read_loop) → on_disconnected → establish 构成**类型级递归**, 编译器无法证明 Send。最终采用**监督者模式**:单个 supervise 任务串行 「建立+握手 → 等读循环 Notify 断开 → 退避重连」,读循环只 notify 不回调 establish,从而打破递归;发送半用专用写任务 + mpsc 解耦(同步 send,不跨 await 持锁)。 测试:9 单测(crypto/ulid/Send+Sync 断言)+ 2 E2E(psk 握手→invoke 全链路、连不上端口 返回 NODE_OFFLINE),`cargo clippy --all-targets -- -D warnings` 零告警。 另附 examples/connect.rs(连真实被控端)与 examples/probe.rs(字段契约探针, 输出顶层键+数组首元素键,并按 system.info.os 区分「平台不支持」与「真失败」)。 .gitignore:crates/ 转正后必须显式忽略 /crates/target/ —— 此前是靠整个 /crates/ 被忽略才没让 4GB 级构建产物进库,取消那条保护后会裸奔。
35 lines
1002 B
Rust
35 lines
1002 B
Rust
//! nodeagent 控制端 Rust 客户端。
|
||
//!
|
||
//! 行为对齐 `packages/client`(Node 版),复刻:
|
||
//! - 握手 hello → challenge → auth → auth_ok(psk HMAC / ed25519 签名)
|
||
//! - 泛型能力调用 invoke(带超时与 id 匹配)
|
||
//! - 事件通知(无 id 的 JSON-RPC notification,`event` 方法)
|
||
//! - 断线指数退避重连(1s → 30s,±15% 抖动)
|
||
//! - TLS 自签证书 + 指纹钉住(v21,E_CERT_MISMATCH)
|
||
|
||
pub mod client;
|
||
pub mod crypto;
|
||
pub mod discovery;
|
||
pub mod error;
|
||
pub mod tls;
|
||
pub mod types;
|
||
pub mod ulid;
|
||
|
||
pub use client::{Client, ClientEvent, ClientOptions, ConnState};
|
||
pub use error::{ClientError, ErrorCodes};
|
||
pub use types::{CapabilityDescriptor, InvokeResult};
|
||
|
||
#[cfg(test)]
|
||
mod send_sync_checks {
|
||
use super::*;
|
||
|
||
fn assert_send_sync<T: Send + Sync>() {}
|
||
|
||
#[test]
|
||
fn client_is_send_sync() {
|
||
assert_send_sync::<Client>();
|
||
assert_send_sync::<ClientOptions>();
|
||
assert_send_sync::<ClientError>();
|
||
}
|
||
}
|