chore: wave-0 baseline (Tauri2+React shell, platform detect, fake catalog)

This commit is contained in:
AgentDock 施工员
2026-08-25 00:21:13 +08:00
commit 296843215f
137 changed files with 6696 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "agentdock-adapter"
version.workspace = true
edition.workspace = true
license.workspace = true
description = "适配器 schema 加载、版本校验、dry-run、执行器接口(架构 §3"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_yaml = "0.9"
+110
View File
@@ -0,0 +1,110 @@
//! 目录索引与占位条目加载(Wave 0)
//!
//! 从 `adapters/catalog.yaml` 读取索引,再逐个读取 `tools/*.yaml`
//! 返回目录条目。仅消费 `id / name / name_zh / vendor / status` 五个字段。
use std::fs;
use std::path::Path;
use serde::{Deserialize, Serialize};
use crate::error::AdapterError;
/// 目录索引(adapters/catalog.yaml
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct Catalog {
#[serde(rename = "catalog_version")]
pub version: u32,
pub tools: Vec<CatalogRef>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct CatalogRef {
pub id: String,
pub file: String,
}
/// 目录条目(占位 schema,Wave 0 仅五字段;完整字段见架构 §3.1)
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct CatalogEntry {
pub id: String,
pub name: String,
#[serde(rename = "name_zh")]
pub name_zh: String,
pub vendor: String,
/// available | watch
pub status: String,
}
/// 加载目录索引与全部工具占位 YAML
pub fn load_catalog<P: AsRef<Path>>(adapters_dir: P) -> Result<Vec<CatalogEntry>, AdapterError> {
let dir = adapters_dir.as_ref();
let catalog_text = fs::read_to_string(dir.join("catalog.yaml"))
.map_err(|e| AdapterError::Io(format!("读取 catalog.yaml 失败: {e}")))?;
let catalog: Catalog = serde_yaml::from_str(&catalog_text)
.map_err(|e| AdapterError::Parse(format!("解析 catalog.yaml 失败: {e}")))?;
let mut entries = Vec::with_capacity(catalog.tools.len());
for r in &catalog.tools {
let text = fs::read_to_string(dir.join(&r.file))
.map_err(|e| AdapterError::Io(format!("读取 {} 失败: {e}", r.file)))?;
let entry: CatalogEntry = serde_yaml::from_str(&text)
.map_err(|e| AdapterError::Parse(format!("解析 {} 失败: {e}", r.file)))?;
if entry.id != r.id {
return Err(AdapterError::Parse(format!(
"索引 id 与文件内 id 不一致: 索引={} 文件={}",
r.id, entry.id
)));
}
if entry.status != "available" && entry.status != "watch" {
return Err(AdapterError::Parse(format!(
"{} 的 status 非法: {}(应为 available | watch",
entry.id, entry.status
)));
}
entries.push(entry);
}
Ok(entries)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_minimal_tool_yaml() {
let yaml = "id: codex\nname: Codex CLI\nname_zh: Codex CLI\nvendor: OpenAI\nstatus: available\n";
let entry: CatalogEntry = serde_yaml::from_str(yaml).expect("应能解析");
assert_eq!(entry.id, "codex");
assert_eq!(entry.name_zh, "Codex CLI");
assert_eq!(entry.vendor, "OpenAI");
assert_eq!(entry.status, "available");
}
#[test]
fn rejects_invalid_status() {
let yaml = "id: x\nname: X\nname_zh: X\nvendor: V\nstatus: unknown\n";
let entry: Result<CatalogEntry, _> = serde_yaml::from_str(yaml);
// 解析本身成功,非法 status 由 load_catalog 校验;此处确认 schema 字段可读
assert!(entry.is_ok());
}
#[test]
fn loads_real_catalog_from_repo() {
// 以真实 adapters/ 目录做集成测试(相对本 crate 位于 ../../adapters
let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../adapters");
let entries = load_catalog(&dir).expect("真实目录应可加载");
assert_eq!(entries.len(), 14, "第一批应为 14 个工具");
for e in &entries {
assert!(!e.id.is_empty());
assert!(!e.name.is_empty());
assert!(!e.name_zh.is_empty());
assert!(!e.vendor.is_empty());
}
let ids: Vec<&str> = entries.iter().map(|e| e.id.as_str()).collect();
for want in ["codex", "claude-code", "gemini", "copilot", "kimi", "qwen", "codebuddy",
"opencode", "crush", "goose", "aider", "cursor", "cline", "warp"] {
assert!(ids.contains(&want), "目录应包含 {want}");
}
}
}
+20
View File
@@ -0,0 +1,20 @@
//! 适配器层错误类型
use std::fmt;
#[derive(Debug)]
pub enum AdapterError {
Io(String),
Parse(String),
}
impl fmt::Display for AdapterError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AdapterError::Io(m) => write!(f, "IO: {m}"),
AdapterError::Parse(m) => write!(f, "Parse: {m}"),
}
}
}
impl std::error::Error for AdapterError {}
+11
View File
@@ -0,0 +1,11 @@
//! agentdock-adapter —— 适配器层
//!
//! 负责适配器 schema 加载、版本校验、dry-run 与执行器接口(架构 §3)。
//! Wave 0:仅落地目录索引与占位条目加载(`catalog` 模块);
//! 完整 schema 校验、执行器接口随 Wave 1 实现。
pub mod catalog;
pub mod error;
pub use catalog::{Catalog, CatalogEntry, CatalogRef, load_catalog};
pub use error::AdapterError;