feat(wave-1): 适配器框架与安全基座(schema校验/dry-run/exec沙箱/密钥库/日志脱敏/字段拆分)

This commit is contained in:
AgentDock 施工员
2026-08-25 00:38:10 +08:00
parent 296843215f
commit 7208586850
39 changed files with 2323 additions and 81 deletions
+61 -3
View File
@@ -15,10 +15,14 @@ pub fn detect_env() -> PlatformEnv {
#[cfg(not(windows))]
let (os, os_version) = (String::from("linux"), linux_os_version());
let (distro, distro_version) = distro_info();
PlatformEnv {
os,
os_version,
arch: std::env::consts::ARCH.to_string(),
distro,
distro_version,
shells: detect_shells(),
runtimes: detect_runtimes(),
path_entries: path_entries(),
@@ -26,6 +30,31 @@ pub fn detect_env() -> PlatformEnv {
}
}
#[cfg(windows)]
fn distro_info() -> (Option<String>, Option<String>) {
(None, None)
}
#[cfg(not(windows))]
fn distro_info() -> (Option<String>, Option<String>) {
parse_os_release(&std::fs::read_to_string("/etc/os-release").unwrap_or_default())
}
/// 解析 os-release 内容,返回 (发行版名, 版本号)。
/// 独立成纯函数以便跨平台单元测试。
pub fn parse_os_release(content: &str) -> (Option<String>, Option<String>) {
let mut id = None;
let mut ver = None;
for line in content.lines() {
if let Some(v) = line.strip_prefix("ID=") {
id = Some(v.trim().trim_matches('"').to_string());
} else if let Some(v) = line.strip_prefix("VERSION_ID=") {
ver = Some(v.trim().trim_matches('"').to_string());
}
}
(id, ver)
}
/// 从命令输出中提取首个版本号(如 "v24.18.0" -> "24.18.0")。
/// 规则:取第一段以数字开头、由数字与点组成的子串,尾部点剔除。
pub fn extract_version(output: &str) -> Option<String> {
@@ -95,7 +124,7 @@ fn resolve_program(program: &str) -> Option<PathBuf> {
}
/// 探测一个可执行文件:返回状态 / 版本 / 路径。
/// 结果分类:installed / not_in_path / exec_failed / version_unparseable。
/// 结果分类:installed / not_in_path / permission_denied / exec_failed / version_unparseable。
fn probe(program: &str, args: &[&str]) -> Option<RuntimeInfo> {
let path = resolve_program(program);
let path_str = path.as_ref().map(|p| p.to_string_lossy().to_string());
@@ -119,9 +148,15 @@ fn probe(program: &str, args: &[&str]) -> Option<RuntimeInfo> {
let output = match cmd.output() {
Ok(o) => o,
Err(_) => {
Err(e) => {
// 架构 §5:把「权限不足」与一般执行失败拆分开(总工 Wave 0 🟡)
let status = if e.kind() == std::io::ErrorKind::PermissionDenied {
"permission_denied"
} else {
"exec_failed"
};
return Some(RuntimeInfo {
status: "exec_failed".into(),
status: status.into(),
version: None,
path: path_str,
});
@@ -296,6 +331,29 @@ mod tests {
assert_eq!(extract_version("v1.2.3."), Some("1.2.3".into()));
}
#[test]
fn parses_os_release_distro_and_version() {
let content = "NAME=\"Ubuntu\"\nVERSION=\"24.04.1 LTS (Noble Numbat)\"\nID=ubuntu\nID_LIKE=debian\nVERSION_ID=\"24.04\"\n";
let (distro, ver) = parse_os_release(content);
assert_eq!(distro.as_deref(), Some("ubuntu"));
assert_eq!(ver.as_deref(), Some("24.04"));
}
#[test]
fn parses_os_release_missing_fields() {
let (distro, ver) = parse_os_release("NAME=\"Other\"\n");
assert_eq!(distro, None);
assert_eq!(ver, None);
}
#[cfg(windows)]
#[test]
fn windows_distro_is_none() {
let env = detect_env();
assert_eq!(env.distro, None);
assert_eq!(env.distro_version, None);
}
#[test]
fn env_has_path_on_windows() {
// PATH 变量在任何真实 Windows/Linux 上都存在
+7 -1
View File
@@ -11,6 +11,12 @@ pub struct PlatformEnv {
pub os_version: String,
/// 架构(std::env::consts::ARCH,如 x86_64
pub arch: String,
/// Linux 发行版名称(如 "ubuntu");Windows 为 None。
/// 架构 §5 原 `distro?: ubuntu + version` 合并字段,本波拆分为
/// `distro` + `distro_version` 两个字段(总工 Wave 0 🟡)。
pub distro: Option<String>,
/// Linux 发行版版本(如 "22.04");Windows 为 None。
pub distro_version: Option<String>,
pub shells: Shells,
pub runtimes: Runtimes,
/// PATH 条目(Windows 用 ';' 分割,Linux 用 ':'
@@ -41,7 +47,7 @@ pub struct Runtimes {
/// 单个运行时探测结果
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct RuntimeInfo {
/// installed | not_installed | not_in_path | exec_failed | version_unparseable
/// installed | not_in_path | permission_denied | exec_failed | version_unparseable
pub status: String,
pub version: Option<String>,
/// 解析到的可执行文件绝对路径(PATH 搜索)