Signed-off-by: 未知时光 <732857315@qq.com>

This commit is contained in:
2026-06-09 16:23:24 +08:00
parent 1bf1f08f9a
commit f7919eca40
13 changed files with 1206 additions and 1 deletions

View File

@@ -0,0 +1,14 @@
[package]
name = "agentsd-plugin-sdk"
version = "0.1.0"
edition = "2021"
[dependencies]
agentsd-proto = { path = "../../core/proto" }
anyhow = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tonic = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }
tracing = { workspace = true }

View File

@@ -0,0 +1,132 @@
use agentsd_proto::agentsd::*;
use anyhow::{anyhow, Context, Result};
use serde::{de::DeserializeOwned, Serialize};
use tonic::metadata::MetadataValue;
use tonic::transport::Channel;
use tonic::Request;
pub const DEFAULT_AGENTSD_ADDR: &str = "http://[::1]:50051";
pub fn agentsd_addr() -> String {
std::env::var("AGENTSD_ADDR").unwrap_or_else(|_| DEFAULT_AGENTSD_ADDR.to_string())
}
pub async fn connect() -> Result<Channel> {
connect_to(&agentsd_addr()).await
}
pub async fn connect_to(addr: &str) -> Result<Channel> {
Channel::from_shared(addr.to_string())
.with_context(|| format!("invalid agentsd address: {addr}"))?
.connect()
.await
.with_context(|| format!("connect agentsd at {addr}"))
}
pub fn request_with_plugin<T>(plugin_id: &str, message: T) -> Result<Request<T>> {
let mut req = Request::new(message);
let value = MetadataValue::try_from(plugin_id)
.map_err(|_| anyhow!("invalid plugin id for metadata: {plugin_id}"))?;
req.metadata_mut().insert("x-plugin-id", value);
Ok(req)
}
pub fn export(name: &str, description: &str) -> ExportDef {
ExportDef {
name: name.to_string(),
description: description.to_string(),
}
}
pub fn plugin_info(
id: &str,
name: &str,
version: &str,
plugin_type: &str,
syscalls: &[&str],
depends: &[&str],
exports: Vec<ExportDef>,
) -> PluginInfo {
PluginInfo {
id: id.to_string(),
name: name.to_string(),
version: version.to_string(),
plugin_type: plugin_type.to_string(),
syscalls: syscalls.iter().map(|s| s.to_string()).collect(),
depends: depends.iter().map(|s| s.to_string()).collect(),
exports,
endpoint: String::new(),
}
}
pub fn plugin_info_with_endpoint(
id: &str,
name: &str,
version: &str,
plugin_type: &str,
syscalls: &[&str],
depends: &[&str],
exports: Vec<ExportDef>,
endpoint: &str,
) -> PluginInfo {
let mut info = plugin_info(id, name, version, plugin_type, syscalls, depends, exports);
info.endpoint = endpoint.to_string();
info
}
pub async fn register(channel: Channel, info: PluginInfo) -> Result<RegisterResponse> {
let mut bridge = plugin_bridge_client::PluginBridgeClient::new(channel);
let resp = bridge.register(info).await?.into_inner();
if !resp.ok {
return Err(anyhow!(resp.error));
}
Ok(resp)
}
pub fn decode_json<T: DeserializeOwned>(bytes: &[u8]) -> Result<T> {
if bytes.is_empty() {
serde_json::from_str("{}").context("decode empty JSON object")
} else {
serde_json::from_slice(bytes).context("decode JSON")
}
}
pub fn encode_json<T: Serialize>(value: &T) -> Result<Vec<u8>> {
serde_json::to_vec(value).context("encode JSON")
}
pub fn call_response_json<T: Serialize>(value: &T, ok: bool, error: impl Into<String>) -> CallResponse {
match encode_json(value) {
Ok(result) => CallResponse {
result,
ok,
error: error.into(),
},
Err(e) => CallResponse {
result: Vec::new(),
ok: false,
error: e.to_string(),
},
}
}
pub fn ok_json<T: Serialize>(value: &T) -> CallResponse {
call_response_json(value, true, "")
}
pub fn err_json(error: impl Into<String>) -> CallResponse {
let error = error.into();
let payload = serde_json::json!({ "error": error });
call_response_json(&payload, false, error)
}
pub fn plugin_endpoint_from_env(default_port: u16) -> String {
if let Ok(endpoint) = std::env::var("PLUGIN_ENDPOINT") {
return endpoint;
}
format!("http://[::1]:{}", default_port)
}
pub fn listen_addr_from_env(default_port: u16) -> String {
std::env::var("PLUGIN_LISTEN_ADDR").unwrap_or_else(|_| format!("[::1]:{}", default_port))
}