重构并拆分项目
This commit is contained in:
Generated
-105
@@ -1,105 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "amap"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
@@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "amap"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "amap"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
@@ -1,18 +0,0 @@
|
||||
name: amap_geocode
|
||||
desc: 将文字地址转换为经纬度坐标。输入地址名称返回坐标。用于"天安门的坐标是什么"等需要将地名转为坐标的场景,通常作为其他地图工具的前置步骤
|
||||
type: http
|
||||
tool: amap
|
||||
command: geocode
|
||||
path: /target/release/amap
|
||||
risk_level: low
|
||||
timeout_secs: 15
|
||||
env:
|
||||
- AMAP_KEY
|
||||
params:
|
||||
- name: address
|
||||
required: true
|
||||
desc: 详细地址名称,如"北京市朝阳区天安门"、"合肥火车站"。越详细越精确
|
||||
- name: city
|
||||
required: false
|
||||
desc: 城市名称,可选。限定地理编码的城市范围
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
name: amap_map_link
|
||||
desc: 生成高德地图可视化链接。输入中心点坐标返回可分享的地图标记链接。用于"把这个位置发给我"等需要可视化展示的场景
|
||||
type: http
|
||||
tool: amap
|
||||
command: map_link
|
||||
path: /target/release/amap
|
||||
risk_level: low
|
||||
timeout_secs: 15
|
||||
env:
|
||||
- AMAP_KEY
|
||||
params:
|
||||
- name: center
|
||||
required: false
|
||||
desc: 地图中心点经纬度坐标,格式"经度,纬度",默认天安门坐标
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
name: amap_poi_search
|
||||
desc: 搜索指定城市内的POI地点(餐饮、酒店、景点、商场、加油站、银行等)。返回名称和地址。用于"附近有什么好吃的""北京有什么景点"等问题
|
||||
type: http
|
||||
tool: amap
|
||||
command: poi_search
|
||||
path: /target/release/amap
|
||||
risk_level: low
|
||||
timeout_secs: 15
|
||||
env:
|
||||
- AMAP_KEY
|
||||
params:
|
||||
- name: keywords
|
||||
required: true
|
||||
desc: 搜索关键词,如"咖啡厅"、"银行"、"加油站"。支持POI类型名称
|
||||
- name: city
|
||||
required: false
|
||||
desc: 城市名称,限定搜索范围。不填则在当前城市搜索
|
||||
- name: radius
|
||||
required: false
|
||||
desc: 搜索半径(米),如 500、1000、3000。默认 1000 米
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
name: amap_reverse_geocode
|
||||
desc: 将经纬度坐标转换为详细文字地址。输入"经度,纬度"返回结构化地址。用于"这个坐标是哪里"等反向查询
|
||||
type: http
|
||||
tool: amap
|
||||
command: reverse_geocode
|
||||
path: /target/release/amap
|
||||
risk_level: low
|
||||
timeout_secs: 15
|
||||
env:
|
||||
- AMAP_KEY
|
||||
params:
|
||||
- name: location
|
||||
required: true
|
||||
desc: 经纬度坐标,格式为"经度,纬度",如"116.397,39.908"
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
name: amap_route_plan
|
||||
desc: 规划两点之间的出行路线。支持驾车(driving)、步行(walking)、骑行(riding)、公交(transit)四种方式。返回距离和预计耗时。用于"从家到公司怎么走""走过去要多久"等问题
|
||||
type: http
|
||||
tool: amap
|
||||
command: route_plan
|
||||
path: /target/release/amap
|
||||
risk_level: low
|
||||
timeout_secs: 15
|
||||
env:
|
||||
- AMAP_KEY
|
||||
params:
|
||||
- name: origin
|
||||
required: true
|
||||
desc: 起点经纬度坐标,格式"经度,纬度"。可先用 amap_geocode 将地名转为坐标
|
||||
- name: destination
|
||||
required: true
|
||||
desc: 终点经纬度坐标,格式"经度,纬度"。可先用 amap_geocode 将地名转为坐标
|
||||
- name: type
|
||||
required: false
|
||||
desc: 出行方式:driving(驾车)、walking(步行)、riding(骑行)、transit(公交)。默认 driving
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
name: amap_travel_plan
|
||||
desc: 为指定城市生成旅游规划。自动搜索城市内的热门景点、美食、住宿等POI。用于"北京三日游怎么安排""合肥有什么好玩的"等旅游咨询
|
||||
type: http
|
||||
tool: amap
|
||||
command: travel_plan
|
||||
path: /target/release/amap
|
||||
risk_level: low
|
||||
timeout_secs: 15
|
||||
env:
|
||||
- AMAP_KEY
|
||||
params:
|
||||
- name: city
|
||||
required: true
|
||||
desc: 目标城市名称,如"北京"、"杭州"、"成都"
|
||||
- name: interests
|
||||
required: false
|
||||
desc: 感兴趣的关键词,逗号分隔,如"景点,美食,酒店"。默认"景点,美食"
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
fn main() {
|
||||
let input: serde_json::Value = serde_json::from_reader(std::io::stdin()).unwrap();
|
||||
let mode = std::env::args().nth(2).unwrap_or_default();
|
||||
let p = &input["params"];
|
||||
let key = match std::env::var("AMAP_KEY") {
|
||||
Ok(k) if !k.is_empty() => k,
|
||||
_ => {
|
||||
println!(r#"{{"type":"result","content":"错误: 缺少环境变量 AMAP_KEY(高德地图 API Key),请在 .env 中设置"}}"#);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if input.get("response").is_none() && input.get("db_response").is_none() {
|
||||
let (url, desc) = match mode.as_str() {
|
||||
"poi_search" => (format!("https://restapi.amap.com/v5/place/text?keywords={}&city={}&key={key}",
|
||||
p["keywords"].as_str().unwrap_or(""), p["city"].as_str().unwrap_or("")), format!("搜索: {}", p["keywords"].as_str().unwrap_or(""))),
|
||||
"geocode" => (format!("https://restapi.amap.com/v3/geocode/geo?address={}&city={}&key={key}",
|
||||
p["address"].as_str().unwrap_or(""), p["city"].as_str().unwrap_or("")), format!("编码: {}", p["address"].as_str().unwrap_or(""))),
|
||||
"reverse_geocode" => (format!("https://restapi.amap.com/v3/geocode/regeo?location={}&key={key}",
|
||||
p["location"].as_str().unwrap_or("")), "逆编码".into()),
|
||||
"route_plan" => (format!("https://restapi.amap.com/v5/direction/{}?origin={}&destination={}&key={key}",
|
||||
p["type"].as_str().unwrap_or("driving"), p["origin"].as_str().unwrap_or(""), p["destination"].as_str().unwrap_or("")), "路径规划".into()),
|
||||
"travel_plan" => (format!("https://restapi.amap.com/v5/place/text?keywords={}&city={}&key={key}",
|
||||
p["interests"].as_str().unwrap_or(""), p["city"].as_str().unwrap_or("")), format!("旅游: {}", p["city"].as_str().unwrap_or(""))),
|
||||
"map_link" => (format!("https://uri.amap.com/marker?position={}",
|
||||
p["center"].as_str().unwrap_or("116.397,39.908")), "地图链接".into()),
|
||||
_ => { println!(r#"{{"type":"result","content":"未知模式"}}"#); return; }
|
||||
};
|
||||
println!("{}", serde_json::json!({"type":"http","method":"GET","url":url,"desc":desc}));
|
||||
} else {
|
||||
let b = &input["response"]["body"];
|
||||
let content = match mode.as_str() {
|
||||
"poi_search"|"travel_plan" => {
|
||||
let n = b["pois"].as_array().map(|a| a.len()).unwrap_or(0);
|
||||
let items: Vec<String> = b["pois"].as_array().unwrap_or(&vec![]).iter().take(5)
|
||||
.map(|x| format!("• {} ({})", x["name"].as_str().unwrap_or(""), x["address"].as_str().unwrap_or(""))).collect();
|
||||
format!("找到 {n} 个:\n{}", items.join("\n"))
|
||||
}
|
||||
"geocode" => {
|
||||
let empty = vec![]; let g = b["geocodes"].as_array().unwrap_or(&empty);
|
||||
g.first().map(|x| format!("{} → {}", x["formatted_address"].as_str().unwrap_or(""), x["location"].as_str().unwrap_or(""))).unwrap_or("未找到".into())
|
||||
}
|
||||
"reverse_geocode" => b["regeocode"]["formatted_address"].as_str().unwrap_or("未找到").into(),
|
||||
"route_plan" => format!("距离: {}m, 耗时: {}s", b["route"]["distance"].as_str().unwrap_or("?"), b["route"]["duration"].as_str().unwrap_or("?")),
|
||||
"map_link" => "地图链接已生成".into(),
|
||||
_ => format!("{b}"),
|
||||
};
|
||||
println!("{}", serde_json::json!({"type":"result","content":content}));
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 构建所有工具,产物统一收集到 bin/ 供长期沙箱容器挂载
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
TOOLS=(datetime weather amap web_search fetch_page memories tool_manager)
|
||||
|
||||
mkdir -p bin
|
||||
|
||||
echo "=== 构建工具 ==="
|
||||
for tool in "${TOOLS[@]}"; do
|
||||
echo " $tool..."
|
||||
(cd "$tool" && cargo build --release --quiet)
|
||||
cp "$tool/target/release/$tool" "bin/$tool"
|
||||
done
|
||||
|
||||
echo "=== 完成 ==="
|
||||
ls -lh bin/*
|
||||
Generated
-382
@@ -1,382 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "datetime"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures-util",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
@@ -1,12 +0,0 @@
|
||||
[package]
|
||||
name = "datetime"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "datetime"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4"
|
||||
serde_json = "1.0"
|
||||
@@ -1,9 +0,0 @@
|
||||
name: get_current_datetime
|
||||
desc: 获取当前系统本地日期时间,返回年月日、星期、时区。用于"现在几点""今天几号""星期几"等问题
|
||||
type: stdio
|
||||
tool: datetime
|
||||
path: /target/release/datetime
|
||||
risk_level: low
|
||||
timeout_secs: 3
|
||||
params: []
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
//! datetime — 本地时间
|
||||
|
||||
use chrono::Local;
|
||||
|
||||
fn main() {
|
||||
let _ = std::io::stdin().read_line(&mut String::new());
|
||||
let now = Local::now();
|
||||
let out = serde_json::json!({
|
||||
"type": "result",
|
||||
"content": format!("当前时间: {}\n星期: {}\n时区: {}",
|
||||
now.format("%Y-%m-%d %H:%M:%S"), now.format("%A"), now.offset())
|
||||
});
|
||||
println!("{out}");
|
||||
}
|
||||
Generated
-105
@@ -1,105 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "fetch_page"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
@@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "fetch_page"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "fetch_page"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
@@ -1,12 +0,0 @@
|
||||
name: fetch_page
|
||||
desc: 抓取指定URL网页并自动提取纯文本正文内容,过滤广告、导航、脚本等无关元素。返回最多30行文本。用于"帮我看看这个网页说了什么"等需要阅读网页内容的场景
|
||||
type: http
|
||||
tool: fetch_page
|
||||
path: /target/release/fetch_page
|
||||
risk_level: low
|
||||
timeout_secs: 30
|
||||
params:
|
||||
- name: url
|
||||
required: true
|
||||
desc: 网页URL
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
fn main() {
|
||||
let input: serde_json::Value = serde_json::from_reader(std::io::stdin()).unwrap();
|
||||
if input.get("response").is_none() && input.get("db_response").is_none() {
|
||||
let url = input["params"]["url"].as_str().unwrap_or("");
|
||||
if url.is_empty() { println!(r#"{{"type":"result","content":"缺少url"}}"#); return; }
|
||||
println!("{}", serde_json::json!({"type":"http","method":"GET","url":url,"desc":format!("抓取 {url}")}));
|
||||
} else {
|
||||
let html = input["response"]["body"].as_str().unwrap_or("");
|
||||
let mut r = String::new(); let mut tag = false;
|
||||
for c in html.chars() { if c=='<' { tag=true; } else if c=='>' { tag=false; } else if !tag { r.push(c); } }
|
||||
let text = r.replace(" "," ").replace("<","<").replace(">",">").replace("&","&");
|
||||
let lines: Vec<&str> = text.lines().filter(|l| !l.trim().is_empty()).take(30).collect();
|
||||
println!("{}", serde_json::json!({"type":"result","content":lines.join("\n")}));
|
||||
}
|
||||
}
|
||||
Generated
-219
@@ -1,219 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||
|
||||
[[package]]
|
||||
name = "memories"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
@@ -1,13 +0,0 @@
|
||||
[package]
|
||||
name = "memories"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "memories"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
dirs = "6.0"
|
||||
@@ -1,15 +0,0 @@
|
||||
name: delete_memory
|
||||
desc: 按ID删除指定的长期记忆。用于"忘记我住哪里""删除那条记录"等清除记忆的请求
|
||||
type: stdio
|
||||
tool: memories
|
||||
command: delete
|
||||
path: /target/release/memories
|
||||
risk_level: high
|
||||
timeout_secs: 5
|
||||
env:
|
||||
- IAS_MEMORY_DIR
|
||||
params:
|
||||
- name: memory_id
|
||||
required: true
|
||||
desc: 记忆ID
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
name: get_memory
|
||||
desc: 按ID查看指定的单条记忆详情。用于需要确认某条具体记忆内容的场景
|
||||
type: stdio
|
||||
tool: memories
|
||||
command: get
|
||||
path: /target/release/memories
|
||||
risk_level: low
|
||||
timeout_secs: 3
|
||||
env:
|
||||
- IAS_MEMORY_DIR
|
||||
params:
|
||||
- name: memory_id
|
||||
required: true
|
||||
desc: 记忆的数字ID,从 read_memories 返回结果中获取
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
name: read_memories
|
||||
desc: 读取用户的所有长期记忆。返回之前记录的个人偏好、习惯、重要信息等。用于"你还记得我住哪吗""我之前说过喜欢什么"等回忆类问题
|
||||
type: stdio
|
||||
tool: memories
|
||||
command: list
|
||||
path: /target/release/memories
|
||||
risk_level: low
|
||||
timeout_secs: 3
|
||||
env:
|
||||
- IAS_MEMORY_DIR
|
||||
params:
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
name: update_memory
|
||||
desc: 按ID更新指定记忆的内容。用于"我搬家了,更新地址""把我名字改成XX"等修改记忆的请求
|
||||
type: stdio
|
||||
tool: memories
|
||||
command: update
|
||||
path: /target/release/memories
|
||||
risk_level: high
|
||||
timeout_secs: 5
|
||||
env:
|
||||
- IAS_MEMORY_DIR
|
||||
params:
|
||||
- name: memory_id
|
||||
required: true
|
||||
desc: 记忆ID
|
||||
- name: content
|
||||
required: true
|
||||
desc: 更新后的新内容,覆盖原记忆
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
name: write_memory
|
||||
desc: 记录一条新的长期记忆。当用户透露个人偏好、习惯、重要信息时使用此工具
|
||||
type: stdio
|
||||
tool: memories
|
||||
command: add
|
||||
path: /target/release/memories
|
||||
risk_level: high
|
||||
timeout_secs: 5
|
||||
env:
|
||||
- IAS_MEMORY_DIR
|
||||
params:
|
||||
- name: content
|
||||
required: true
|
||||
desc: 要记录的记忆内容,用自然语言描述。如"用户住在合肥包河区联投新安里A区"
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
//! memories — 长期记忆管理(通过主进程 DB 代理)
|
||||
|
||||
fn main() {
|
||||
let input: serde_json::Value = serde_json::from_reader(std::io::stdin()).unwrap();
|
||||
let mode = std::env::args().nth(2).unwrap_or_else(|| "list".to_string());
|
||||
let params = &input["params"];
|
||||
|
||||
if input.get("db_response").is_none() {
|
||||
// 发出 DB 请求
|
||||
match mode.as_str() {
|
||||
"list" => println!("{}", serde_json::json!({"type":"db","operation":"read_memories","desc":"读取记忆"})),
|
||||
"add" => {
|
||||
let c = params["content"].as_str().unwrap_or("");
|
||||
if c.is_empty() { println!(r#"{{"type":"result","content":"缺少content"}}"#); return; }
|
||||
println!("{}", serde_json::json!({"type":"db","operation":"write_memory","params":{"content":c},"desc":format!("记录: {c}")}));
|
||||
}
|
||||
"get" => {
|
||||
let id = params["memory_id"].as_u64().unwrap_or(0);
|
||||
println!("{}", serde_json::json!({"type":"db","operation":"read_memories","params":{"memory_id":id},"desc":"读取记忆"}));
|
||||
}
|
||||
"delete" => {
|
||||
let id = params["memory_id"].as_u64().unwrap_or(0);
|
||||
println!("{}", serde_json::json!({"type":"db","operation":"delete_memory","params":{"memory_id":id},"desc":format!("删除记忆 {id}")}));
|
||||
}
|
||||
"update" => {
|
||||
let id = params["memory_id"].as_u64().unwrap_or(0);
|
||||
let c = params["content"].as_str().unwrap_or("");
|
||||
println!("{}", serde_json::json!({"type":"db","operation":"update_memory","params":{"memory_id":id,"content":c},"desc":format!("更新记忆 {id}")}));
|
||||
}
|
||||
_ => println!(r#"{{"type":"result","content":"未知操作"}}"#),
|
||||
}
|
||||
} else {
|
||||
// 处理 DB 响应
|
||||
let result = input["db_response"]["result"].as_str().unwrap_or("");
|
||||
println!("{}", serde_json::json!({"type":"result","content":result}));
|
||||
}
|
||||
}
|
||||
Generated
-105
@@ -1,105 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tool_inspector"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
@@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "tool_inspector"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "tool_inspector"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
@@ -1,17 +0,0 @@
|
||||
name: tool_inspector
|
||||
desc: 只读查看本地工具的源码、Cargo.toml 和 spec 配置。在用 tool_manager 修改某工具前,先用本工具查看其当前实现,避免盲目覆盖丢失逻辑。低风险,无需审批
|
||||
type: stdio
|
||||
tool: tool_inspector
|
||||
path: /target/release/tool_inspector
|
||||
risk_level: low
|
||||
timeout_secs: 10
|
||||
params:
|
||||
- name: action
|
||||
required: true
|
||||
desc: 操作类型:list(列出所有工具及文件构成)、read_tool(读取某工具的全部 Cargo.toml/specs/src 源码)、read_file(读取工具目录下单个文件)
|
||||
- name: tool_name
|
||||
required: false
|
||||
desc: 工具名称(read_tool/read_file 必填),只允许字母、数字、下划线和短横线
|
||||
- name: file
|
||||
required: false
|
||||
desc: read_file 时的相对路径,如 src/main.rs、Cargo.toml、specs/weather.tool.yaml;必须是相对路径,禁止 .. 和 target 目录
|
||||
@@ -1,270 +0,0 @@
|
||||
//! tool_inspector — 只读查看本地工具的源码与配置
|
||||
//!
|
||||
//! 用途:在用 tool_manager 修改工具前,先用本工具查看当前实现,避免盲目覆盖丢失逻辑。
|
||||
//!
|
||||
//! 安全约束:
|
||||
//! - 纯只读:不写文件、不执行命令、不联网
|
||||
//! - tool_name 与相对路径严格校验,禁止路径穿越
|
||||
//! - 跳过 target/ 构建产物目录
|
||||
//! - 低风险(risk_level: low),无需用户审批
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use std::fs;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
fn main() {
|
||||
let input: Value = match serde_json::from_reader(std::io::stdin()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return result(&format!("读取输入失败: {e}")),
|
||||
};
|
||||
let params = &input["params"];
|
||||
let action = params["action"].as_str().unwrap_or("");
|
||||
|
||||
let out = match run(action, params) {
|
||||
Ok(s) => s,
|
||||
Err(e) => format!("查看失败: {e}"),
|
||||
};
|
||||
result(&out);
|
||||
}
|
||||
|
||||
fn run(action: &str, params: &Value) -> Result<String, String> {
|
||||
let tools_root = locate_tools_root()?;
|
||||
match action {
|
||||
"list" => list_tools(&tools_root),
|
||||
"read_tool" => {
|
||||
let name = required_str(params, "tool_name")?;
|
||||
validate_tool_name(name)?;
|
||||
read_tool(&tools_root, name)
|
||||
}
|
||||
"read_file" => {
|
||||
let name = required_str(params, "tool_name")?;
|
||||
validate_tool_name(name)?;
|
||||
let file = required_str(params, "file")?;
|
||||
read_file(&tools_root, name, file)
|
||||
}
|
||||
_ => Err("action 必须是 list、read_tool 或 read_file".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// 列出 tools 目录下所有工具及其文件构成
|
||||
fn list_tools(tools_root: &Path) -> Result<String, String> {
|
||||
let mut names: Vec<String> = Vec::new();
|
||||
for entry in fs::read_dir(tools_root).map_err(|e| e.to_string())? {
|
||||
let entry = entry.map_err(|e| e.to_string())?;
|
||||
let path = entry.path();
|
||||
if !path.is_dir() {
|
||||
continue;
|
||||
}
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
// 跳过产物目录与隐藏目录
|
||||
if name == "bin" || name.starts_with('.') {
|
||||
continue;
|
||||
}
|
||||
// 只有含 specs/src/Cargo.toml 之一的目录才视为工具
|
||||
if path.join("specs").exists()
|
||||
|| path.join("src").exists()
|
||||
|| path.join("Cargo.toml").exists()
|
||||
{
|
||||
names.push(name);
|
||||
}
|
||||
}
|
||||
names.sort();
|
||||
|
||||
if names.is_empty() {
|
||||
return Ok("tools 目录下没有工具".into());
|
||||
}
|
||||
|
||||
let mut out = format!("本地工具列表({} 个):\n", names.len());
|
||||
for n in &names {
|
||||
let dir = tools_root.join(n);
|
||||
let mark = |has: bool| if has { "✓" } else { "✗" };
|
||||
out.push_str(&format!(
|
||||
"• {n} (src:{} specs:{} Cargo.toml:{})\n",
|
||||
mark(dir.join("src").exists()),
|
||||
mark(dir.join("specs").exists()),
|
||||
mark(dir.join("Cargo.toml").exists()),
|
||||
));
|
||||
}
|
||||
out.push_str("\n用 read_tool 查看某工具的全部源码与配置,或用 read_file 读取单个文件。");
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// 一次性读取某工具的 Cargo.toml + 所有 specs + src 下所有 .rs 文件
|
||||
fn read_tool(tools_root: &Path, name: &str) -> Result<String, String> {
|
||||
let tool_dir = tools_root.join(name);
|
||||
if !tool_dir.exists() {
|
||||
return Err(format!("工具 {name} 不存在"));
|
||||
}
|
||||
let mut out = String::new();
|
||||
|
||||
if let Ok(c) = fs::read_to_string(tool_dir.join("Cargo.toml")) {
|
||||
out.push_str(&format!("===== {name}/Cargo.toml =====\n{c}\n\n"));
|
||||
}
|
||||
|
||||
// specs/*.tool.yaml(可能有多个文件),按文件名排序
|
||||
let specs_dir = tool_dir.join("specs");
|
||||
if specs_dir.exists() {
|
||||
let mut specs: Vec<PathBuf> = fs::read_dir(&specs_dir)
|
||||
.map_err(|e| e.to_string())?
|
||||
.filter_map(|e| e.ok())
|
||||
.map(|e| e.path())
|
||||
.filter(|p| matches!(p.extension().and_then(|e| e.to_str()), Some("yaml" | "yml")))
|
||||
.collect();
|
||||
specs.sort();
|
||||
for spec in specs {
|
||||
let rel = spec.strip_prefix(&tool_dir).unwrap_or(&spec).display().to_string();
|
||||
match fs::read_to_string(&spec) {
|
||||
Ok(c) => out.push_str(&format!("===== {name}/{rel} =====\n{c}\n\n")),
|
||||
Err(e) => out.push_str(&format!("===== {name}/{rel} =====\n(读取失败: {e})\n\n")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// src 下所有 .rs 文件(递归,支持多模块工具)
|
||||
let mut src_files: Vec<PathBuf> = Vec::new();
|
||||
collect_rs_files(&tool_dir.join("src"), &mut src_files)?;
|
||||
if src_files.is_empty() {
|
||||
out.push_str(&format!("({name} 无 src/*.rs 文件)\n"));
|
||||
} else {
|
||||
src_files.sort();
|
||||
for f in src_files {
|
||||
let rel = f.strip_prefix(&tool_dir).unwrap_or(&f).display().to_string();
|
||||
match fs::read_to_string(&f) {
|
||||
Ok(c) => out.push_str(&format!("===== {name}/{rel} =====\n{c}\n\n")),
|
||||
Err(e) => out.push_str(&format!("===== {name}/{rel} =====\n(读取失败: {e})\n\n")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if out.is_empty() {
|
||||
return Err(format!("工具 {name} 没有可读的源码或配置"));
|
||||
}
|
||||
Ok(out.trim_end().to_string())
|
||||
}
|
||||
|
||||
/// 读取工具目录下指定的单个文件(严格防路径穿越)
|
||||
fn read_file(tools_root: &Path, name: &str, file: &str) -> Result<String, String> {
|
||||
let tool_dir = tools_root.join(name);
|
||||
if !tool_dir.exists() {
|
||||
return Err(format!("工具 {name} 不存在"));
|
||||
}
|
||||
let rel = sanitize_rel_path(file)?;
|
||||
let target = tool_dir.join(&rel);
|
||||
|
||||
// 规范化后必须仍在 tool_dir 内(双重防穿越)
|
||||
let canonical_tool = tool_dir.canonicalize().map_err(|e| e.to_string())?;
|
||||
let canonical_target = target
|
||||
.canonicalize()
|
||||
.map_err(|_| format!("文件不存在: {file}"))?;
|
||||
if !canonical_target.starts_with(&canonical_tool) {
|
||||
return Err("路径越界,禁止访问工具目录之外的文件".into());
|
||||
}
|
||||
if canonical_target.is_dir() {
|
||||
return Err(format!("{file} 是目录,请用 read_tool 查看或指定具体文件"));
|
||||
}
|
||||
let content = fs::read_to_string(&canonical_target)
|
||||
.map_err(|e| format!("读取 {file} 失败: {e}"))?;
|
||||
Ok(format!("===== {name}/{file} =====\n{content}"))
|
||||
}
|
||||
|
||||
/// 递归收集 .rs 文件,跳过隐藏目录
|
||||
fn collect_rs_files(dir: &Path, out: &mut Vec<PathBuf>) -> Result<(), String> {
|
||||
if !dir.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
for entry in fs::read_dir(dir).map_err(|e| e.to_string())? {
|
||||
let entry = entry.map_err(|e| e.to_string())?;
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
if !entry.file_name().to_string_lossy().starts_with('.') {
|
||||
collect_rs_files(&path, out)?;
|
||||
}
|
||||
} else if path.extension().and_then(|e| e.to_str()) == Some("rs") {
|
||||
out.push(path);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 校验相对路径:禁止绝对路径、禁止 ..、禁止 target 构建产物
|
||||
fn sanitize_rel_path(file: &str) -> Result<PathBuf, String> {
|
||||
if file.is_empty() {
|
||||
return Err("file 不能为空".into());
|
||||
}
|
||||
let p = PathBuf::from(file);
|
||||
for comp in p.components() {
|
||||
match comp {
|
||||
Component::Normal(_) | Component::CurDir => {}
|
||||
Component::ParentDir => return Err("file 禁止包含 ..".into()),
|
||||
Component::RootDir | Component::Prefix(_) => {
|
||||
return Err("file 必须是相对路径".into())
|
||||
}
|
||||
}
|
||||
}
|
||||
if p.starts_with("target") {
|
||||
return Err("禁止读取 target 构建产物目录".into());
|
||||
}
|
||||
Ok(p)
|
||||
}
|
||||
|
||||
fn validate_tool_name(name: &str) -> Result<(), String> {
|
||||
if name.is_empty() {
|
||||
return Err("缺少 tool_name".into());
|
||||
}
|
||||
if !name
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
|
||||
{
|
||||
return Err("tool_name 只允许字母、数字、下划线和短横线".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn required_str<'a>(params: &'a Value, key: &str) -> Result<&'a str, String> {
|
||||
params[key]
|
||||
.as_str()
|
||||
.filter(|s| !s.is_empty())
|
||||
.ok_or_else(|| format!("缺少参数 {key}"))
|
||||
}
|
||||
|
||||
/// 定位 tools 根目录。
|
||||
///
|
||||
/// 产物可能位于两处之一:
|
||||
/// - `tools/bin/tool_inspector`(统一产物目录,registry 优先使用)
|
||||
/// - `tools/tool_inspector/target/release/tool_inspector`(构建产物)
|
||||
///
|
||||
/// 因此不依赖固定向上层数,而是从 exe 向上找第一个“含 bin/ 子目录且至少含一个
|
||||
/// 工具子目录”的祖先,即为 tools 根。对两种位置均正确。
|
||||
fn locate_tools_root() -> Result<PathBuf, String> {
|
||||
let exe = std::env::current_exe().map_err(|e| e.to_string())?;
|
||||
let mut cur = exe.parent();
|
||||
while let Some(dir) = cur {
|
||||
if dir.join("bin").is_dir() && has_tool_subdir(dir) {
|
||||
return Ok(dir.to_path_buf());
|
||||
}
|
||||
cur = dir.parent();
|
||||
}
|
||||
Err("无法定位 tools 根目录".into())
|
||||
}
|
||||
|
||||
/// 判断目录下是否存在至少一个工具子目录(含 specs/Cargo.toml/src 之一)
|
||||
fn has_tool_subdir(dir: &Path) -> bool {
|
||||
let Ok(entries) = fs::read_dir(dir) else {
|
||||
return false;
|
||||
};
|
||||
for entry in entries.flatten() {
|
||||
let p = entry.path();
|
||||
if p.is_dir()
|
||||
&& (p.join("specs").exists()
|
||||
|| p.join("Cargo.toml").exists()
|
||||
|| p.join("src").exists())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn result(content: &str) {
|
||||
println!("{}", json!({"type":"result","content":content}));
|
||||
}
|
||||
Generated
-105
@@ -1,105 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tool_manager"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
@@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "tool_manager"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "tool_manager"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
@@ -1,36 +0,0 @@
|
||||
name: tool_manager
|
||||
desc: 创建、修改和构建本地能力工具。用于给助手新增或更新 tools 目录下的 stdio Rust 工具;高风险操作,执行前需要用户确认。
|
||||
type: stdio
|
||||
tool: tool_manager
|
||||
path: /target/release/tool_manager
|
||||
risk_level: high
|
||||
timeout_secs: 300
|
||||
env:
|
||||
- HOME
|
||||
- CARGO_HOME
|
||||
- RUSTUP_HOME
|
||||
params:
|
||||
- name: action
|
||||
required: true
|
||||
desc: 操作类型:create_tool、update_tool 或 build_tool
|
||||
- name: tool_name
|
||||
required: true
|
||||
desc: 工具名称,只允许字母、数字、下划线和短横线
|
||||
- name: description
|
||||
required: false
|
||||
desc: 工具说明,创建或更新 spec 时使用
|
||||
- name: rust_code
|
||||
required: false
|
||||
desc: 完整的 src/main.rs Rust 源码;省略时 create_tool 会生成默认模板
|
||||
- name: params
|
||||
required: false
|
||||
desc: 参数列表数组,每项包含 name、required、desc
|
||||
- name: risk_level
|
||||
required: false
|
||||
desc: 已弃用,tool_manager 创建/更新的工具一律强制 high risk,传入值将被忽略
|
||||
- name: timeout_secs
|
||||
required: false
|
||||
desc: 新工具超时时间秒数,默认 30
|
||||
- name: overwrite
|
||||
required: false
|
||||
desc: create_tool 目标已存在时是否覆盖,默认 false
|
||||
@@ -1,397 +0,0 @@
|
||||
//! tool_manager — 受控创建、修改、构建本地工具
|
||||
//!
|
||||
//! 安全约束:
|
||||
//! - 不允许操作保留工具名(含 `tool_manager` 自身),防止自改后门
|
||||
//! - 由本工具创建/更新的工具一律 `risk_level: high`,忽略调用方传入值
|
||||
//! - 所有调用追加写入 `tools/.tool_audit.log` 审计日志
|
||||
//! - 构建使用 `timeout` 限制时长,失败时清理残留二进制,避免调用旧版本"假成功"
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
/// 保留工具名 —— 禁止 create/update/build
|
||||
const RESERVED_NAMES: &[&str] = &["target", "specs", "src", ".", "..", "tool_manager"];
|
||||
|
||||
/// 构建超时(秒)。需小于 tool_manager 自身的 timeout_secs。
|
||||
const BUILD_TIMEOUT_SECS: u64 = 180;
|
||||
|
||||
fn main() {
|
||||
let input: Value = match serde_json::from_reader(std::io::stdin()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return result(&format!("读取输入失败: {e}")),
|
||||
};
|
||||
|
||||
let params = &input["params"];
|
||||
let user_id = input["user_id"].as_str().unwrap_or("unknown");
|
||||
let action = params["action"].as_str().unwrap_or("");
|
||||
let tool_name = params["tool_name"].as_str().unwrap_or("");
|
||||
|
||||
let (output, success) = match run(action, tool_name, params) {
|
||||
Ok(msg) => (msg, true),
|
||||
Err(e) => (format!("工具管理失败: {e}"), false),
|
||||
};
|
||||
|
||||
// 审计日志(best effort,不影响主流程)
|
||||
if let Ok(tools_root) = locate_tools_root() {
|
||||
audit(&tools_root, user_id, action, tool_name, success, &output);
|
||||
}
|
||||
|
||||
result(&output);
|
||||
}
|
||||
|
||||
fn run(action: &str, tool_name: &str, params: &Value) -> Result<String, String> {
|
||||
validate_tool_name(tool_name)?;
|
||||
let tools_root = locate_tools_root()?;
|
||||
let tool_dir = tools_root.join(tool_name);
|
||||
|
||||
match action {
|
||||
"create_tool" => {
|
||||
let overwrite = truthy(params.get("overwrite"));
|
||||
if tool_dir.exists() && !overwrite {
|
||||
return Err(format!(
|
||||
"工具 {tool_name} 已存在;如需覆盖,请设置 overwrite=true"
|
||||
));
|
||||
}
|
||||
write_tool_project(&tool_dir, tool_name, params)?;
|
||||
build_tool(&tool_dir, tool_name)?;
|
||||
Ok(format!(
|
||||
"已创建并构建工具 {tool_name}(risk_level 强制为 high)。可通过 query_capabilities 查看并用 call_capability 调用。"
|
||||
))
|
||||
}
|
||||
"update_tool" => {
|
||||
if !tool_dir.exists() {
|
||||
return Err(format!("工具 {tool_name} 不存在,无法修改"));
|
||||
}
|
||||
update_tool_project(&tool_dir, tool_name, params)?;
|
||||
build_tool(&tool_dir, tool_name)?;
|
||||
Ok(format!(
|
||||
"已修改并构建工具 {tool_name}(risk_level 强制为 high)。注册表重载后即可使用最新版本。"
|
||||
))
|
||||
}
|
||||
"build_tool" => {
|
||||
if !tool_dir.exists() {
|
||||
return Err(format!("工具 {tool_name} 不存在,无法构建"));
|
||||
}
|
||||
build_tool(&tool_dir, tool_name)?;
|
||||
Ok(format!("已构建工具 {tool_name}。"))
|
||||
}
|
||||
_ => Err("action 必须是 create_tool、update_tool 或 build_tool".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_tool_project(tool_dir: &Path, tool_name: &str, params: &Value) -> Result<(), String> {
|
||||
fs::create_dir_all(tool_dir.join("src")).map_err(|e| e.to_string())?;
|
||||
fs::create_dir_all(tool_dir.join("specs")).map_err(|e| e.to_string())?;
|
||||
fs::write(tool_dir.join("Cargo.toml"), cargo_toml(tool_name)).map_err(|e| e.to_string())?;
|
||||
fs::write(tool_dir.join("src/main.rs"), rust_code(tool_name, params))
|
||||
.map_err(|e| e.to_string())?;
|
||||
fs::write(
|
||||
tool_dir
|
||||
.join("specs")
|
||||
.join(format!("{tool_name}.tool.yaml")),
|
||||
spec_yaml(tool_name, params),
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_tool_project(tool_dir: &Path, tool_name: &str, params: &Value) -> Result<(), String> {
|
||||
if let Some(code) = params["rust_code"].as_str() {
|
||||
fs::write(tool_dir.join("src/main.rs"), code).map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
// spec 一旦由 tool_manager 管理,risk_level 恒为 high
|
||||
let should_update_spec = params.get("description").is_some()
|
||||
|| params.get("params").is_some()
|
||||
|| params.get("risk_level").is_some()
|
||||
|| params.get("timeout_secs").is_some();
|
||||
if should_update_spec {
|
||||
fs::create_dir_all(tool_dir.join("specs")).map_err(|e| e.to_string())?;
|
||||
fs::write(
|
||||
tool_dir
|
||||
.join("specs")
|
||||
.join(format!("{tool_name}.tool.yaml")),
|
||||
spec_yaml(tool_name, params),
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_tool(tool_dir: &Path, tool_name: &str) -> Result<(), String> {
|
||||
let binary = tool_dir.join("target/release").join(tool_name);
|
||||
let use_builder = std::env::var("IAS_DOCKER_BUILDER")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1");
|
||||
let (uid, gid) = current_uid_gid();
|
||||
|
||||
// 启用 builder 容器时在隔离环境内离线编译,挂载 tool_dir 到 /work,产物写回宿主;
|
||||
// 否则本地 cargo(受 timeout 限制)
|
||||
let output = if use_builder {
|
||||
Command::new("docker")
|
||||
.arg("run").arg("--rm")
|
||||
.arg("--network").arg("none")
|
||||
.arg("--memory").arg("1g")
|
||||
.arg("--cpus").arg("1")
|
||||
.arg("--pids-limit").arg("128")
|
||||
.arg("--cap-drop").arg("ALL")
|
||||
.arg("--security-opt").arg("no-new-privileges")
|
||||
.arg("--user").arg(format!("{}:{}", uid, gid))
|
||||
.arg("-v").arg(format!("{}:/work", tool_dir.display()))
|
||||
.arg("-w").arg("/work")
|
||||
.arg("-e").arg("CARGO_NET_OFFLINE=true")
|
||||
.arg("-e").arg("CARGO_HOME=/tmp/cargo")
|
||||
.arg("ias-tool-builder")
|
||||
.arg("timeout").arg(BUILD_TIMEOUT_SECS.to_string())
|
||||
.arg("cargo").arg("build").arg("--release").arg("--offline")
|
||||
.output()
|
||||
} else {
|
||||
Command::new("timeout")
|
||||
.arg(BUILD_TIMEOUT_SECS.to_string())
|
||||
.arg("cargo")
|
||||
.arg("build")
|
||||
.arg("--release")
|
||||
.current_dir(tool_dir)
|
||||
.output()
|
||||
}
|
||||
.map_err(|e| format!("启动构建失败: {e}"))?;
|
||||
|
||||
if !output.status.success() {
|
||||
// 构建失败:删除可能残留的旧二进制,避免 reload 后仍调用旧版本造成"假成功"
|
||||
let _ = fs::remove_file(&binary);
|
||||
let code = output.status.code().unwrap_or(-1);
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
let reason = if code == 124 {
|
||||
format!("构建超时({BUILD_TIMEOUT_SECS}s)")
|
||||
} else {
|
||||
stderr.trim().to_string()
|
||||
};
|
||||
return Err(format!("cargo build 失败(退出码 {code}): {reason}"));
|
||||
}
|
||||
|
||||
if !binary.exists() {
|
||||
return Err(format!("构建后未找到二进制: {}", binary.display()));
|
||||
}
|
||||
|
||||
// 复制到统一产物目录 tools/bin/,供长期沙箱容器挂载使用
|
||||
let tools_root = tool_dir
|
||||
.parent()
|
||||
.ok_or_else(|| "无法定位 tools 根目录".to_string())?;
|
||||
let bin_dir = tools_root.join("bin");
|
||||
fs::create_dir_all(&bin_dir).map_err(|e| format!("创建产物目录失败: {e}"))?;
|
||||
fs::copy(&binary, bin_dir.join(tool_name))
|
||||
.map_err(|e| format!("复制产物到 bin 失败: {e}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 定位 tools 根目录。
|
||||
///
|
||||
/// 产物可能位于两处之一:
|
||||
/// - `tools/bin/tool_manager`(统一产物目录,registry 优先使用)
|
||||
/// - `tools/tool_manager/target/release/tool_manager`(构建产物)
|
||||
///
|
||||
/// 因此不依赖固定向上层数,而是从 exe 向上找第一个“含 bin/ 子目录且至少含一个
|
||||
/// 工具子目录”的祖先,即为 tools 根。对两种位置均正确。
|
||||
fn locate_tools_root() -> Result<PathBuf, String> {
|
||||
let exe = std::env::current_exe().map_err(|e| e.to_string())?;
|
||||
let mut cur = exe.parent();
|
||||
while let Some(dir) = cur {
|
||||
if dir.join("bin").is_dir() && has_tool_subdir(dir) {
|
||||
return Ok(dir.to_path_buf());
|
||||
}
|
||||
cur = dir.parent();
|
||||
}
|
||||
Err("无法定位 tools 根目录".into())
|
||||
}
|
||||
|
||||
/// 判断目录下是否存在至少一个工具子目录(含 specs/Cargo.toml/src 之一)
|
||||
fn has_tool_subdir(dir: &Path) -> bool {
|
||||
let Ok(entries) = fs::read_dir(dir) else {
|
||||
return false;
|
||||
};
|
||||
for entry in entries.flatten() {
|
||||
let p = entry.path();
|
||||
if p.is_dir()
|
||||
&& (p.join("specs").exists()
|
||||
|| p.join("Cargo.toml").exists()
|
||||
|| p.join("src").exists())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn cargo_toml(tool_name: &str) -> String {
|
||||
format!(
|
||||
r#"[package]
|
||||
name = "{tool_name}"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "{tool_name}"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
fn rust_code(tool_name: &str, params: &Value) -> String {
|
||||
params["rust_code"]
|
||||
.as_str()
|
||||
.map(str::to_string)
|
||||
.unwrap_or_else(|| default_rust_code(tool_name))
|
||||
}
|
||||
|
||||
fn default_rust_code(tool_name: &str) -> String {
|
||||
format!(
|
||||
r#"//! {tool_name} — generated tool
|
||||
|
||||
fn main() {{
|
||||
let input: serde_json::Value = serde_json::from_reader(std::io::stdin()).unwrap_or_default();
|
||||
let content = format!("工具 {tool_name} 已收到参数: {{}}", input["params"]);
|
||||
println!("{{}}", serde_json::json!({{"type":"result","content":content}}));
|
||||
}}
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
fn spec_yaml(tool_name: &str, params: &Value) -> String {
|
||||
// 安全约束:tool_manager 产出的工具一律高风险,忽略调用方传入的 risk_level
|
||||
let risk_level = "high";
|
||||
let timeout_secs = params["timeout_secs"].as_u64().unwrap_or(30).clamp(1, 300);
|
||||
|
||||
let desc = params["description"]
|
||||
.as_str()
|
||||
.unwrap_or("由 tool_manager 创建的本地工具(自动标记 high risk)");
|
||||
|
||||
let mut out = format!(
|
||||
"name: {tool_name}\n\
|
||||
desc: {}\n\
|
||||
type: stdio\n\
|
||||
tool: {tool_name}\n\
|
||||
path: /target/release/{tool_name}\n\
|
||||
risk_level: {risk_level}\n\
|
||||
timeout_secs: {timeout_secs}\n",
|
||||
yaml_scalar(desc)
|
||||
);
|
||||
|
||||
if let Some(items) = params["params"].as_array() {
|
||||
out.push_str("params:\n");
|
||||
for item in items {
|
||||
let Some(name) = item["name"].as_str() else {
|
||||
continue;
|
||||
};
|
||||
if validate_param_name(name).is_err() {
|
||||
continue;
|
||||
}
|
||||
let required = item["required"].as_bool().unwrap_or(false);
|
||||
let desc = item["desc"].as_str().unwrap_or("");
|
||||
out.push_str(&format!(
|
||||
" - name: {name}\n required: {required}\n desc: {}\n",
|
||||
yaml_scalar(desc)
|
||||
));
|
||||
}
|
||||
} else {
|
||||
out.push_str("params: []\n");
|
||||
}
|
||||
|
||||
// 安全约束:tool_manager 产出的工具不受信任(含 LLM 提供的 rust_code),
|
||||
// 默认进 stdio 沙箱容器(无网络、只读根),防止数据外泄/越权文件读
|
||||
out.push_str("sandbox:\n profile: stdio\n");
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
fn validate_tool_name(name: &str) -> Result<(), String> {
|
||||
if name.is_empty() {
|
||||
return Err("缺少 tool_name".to_string());
|
||||
}
|
||||
if RESERVED_NAMES.contains(&name) {
|
||||
return Err(format!(
|
||||
"tool_name '{name}' 是保留名称,禁止 create/update/build"
|
||||
));
|
||||
}
|
||||
if !name
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
|
||||
{
|
||||
return Err("tool_name 只允许字母、数字、下划线和短横线".to_string());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_param_name(name: &str) -> Result<(), String> {
|
||||
if name.is_empty()
|
||||
|| !name
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
|
||||
{
|
||||
return Err("参数名非法".to_string());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn yaml_scalar(value: &str) -> String {
|
||||
serde_json::to_string(value).unwrap_or_else(|_| "\"\"".to_string())
|
||||
}
|
||||
|
||||
fn truthy(value: Option<&Value>) -> bool {
|
||||
match value {
|
||||
Some(Value::Bool(v)) => *v,
|
||||
Some(Value::String(v)) => matches!(v.as_str(), "true" | "1" | "yes" | "是"),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取当前进程的 uid:gid,用于 builder 容器 --user,使容器内进程能写 bind mount 的宿主目录
|
||||
fn current_uid_gid() -> (String, String) {
|
||||
let uid = String::from_utf8_lossy(
|
||||
&Command::new("id").arg("-u").output().map(|o| o.stdout).unwrap_or_default(),
|
||||
)
|
||||
.trim()
|
||||
.to_string();
|
||||
let gid = String::from_utf8_lossy(
|
||||
&Command::new("id").arg("-g").output().map(|o| o.stdout).unwrap_or_default(),
|
||||
)
|
||||
.trim()
|
||||
.to_string();
|
||||
(uid, gid)
|
||||
}
|
||||
|
||||
/// 追加审计日志到 tools 根目录下的 .tool_audit.log
|
||||
fn audit(
|
||||
tools_root: &Path,
|
||||
user_id: &str,
|
||||
action: &str,
|
||||
tool_name: &str,
|
||||
success: bool,
|
||||
detail: &str,
|
||||
) {
|
||||
let ts = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_secs())
|
||||
.unwrap_or(0);
|
||||
// detail 可能很长,截断到 200 字节避免日志膨胀
|
||||
let detail: String = detail.chars().take(200).collect();
|
||||
let line = format!(
|
||||
"{ts}\t{user_id}\t{action}\t{tool_name}\t{}\t{detail}\n",
|
||||
if success { "ok" } else { "err" }
|
||||
);
|
||||
let path = tools_root.join(".tool_audit.log");
|
||||
if let Ok(mut f) = fs::OpenOptions::new().create(true).append(true).open(&path) {
|
||||
use std::io::Write;
|
||||
let _ = f.write_all(line.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
fn result(content: &str) {
|
||||
println!("{}", json!({"type":"result","content":content}));
|
||||
}
|
||||
Generated
-105
@@ -1,105 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "weather"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
@@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "weather"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "weather"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
@@ -1,15 +0,0 @@
|
||||
name: weather_forecast
|
||||
desc: 查询指定城市未来7天的每日天气预报(白天/夜间天气、最高/最低温度)。用于"明天天气""这周天气"等问题
|
||||
type: http
|
||||
tool: weather
|
||||
command: forecast
|
||||
path: /target/release/weather
|
||||
risk_level: low
|
||||
timeout_secs: 10
|
||||
env:
|
||||
- QWEATHER_KEY
|
||||
params:
|
||||
- name: location
|
||||
required: true
|
||||
desc: 城市名称,如"北京"、"上海"、"合肥"。支持地级市和县级市
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
name: weather_hourly
|
||||
desc: 查询指定城市未来24小时逐小时天气(温度、天气状况、风力、降水概率)。用于"今晚会下雨吗""下午几点最热"等精确时间天气问题
|
||||
type: http
|
||||
tool: weather
|
||||
command: hourly
|
||||
path: /target/release/weather
|
||||
risk_level: low
|
||||
timeout_secs: 10
|
||||
env:
|
||||
- QWEATHER_KEY
|
||||
params:
|
||||
- name: location
|
||||
required: true
|
||||
desc: 城市名称,如"北京"、"上海"、"合肥"。支持地级市和县级市
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
name: weather_now
|
||||
desc: 查询指定城市的实时天气(温度、体感温度、风向风力、湿度、气压、能见度)。用于回答"今天天气怎么样""北京多少度"等即时天气问题
|
||||
type: http
|
||||
tool: weather
|
||||
command: now
|
||||
path: /target/release/weather
|
||||
risk_level: low
|
||||
timeout_secs: 10
|
||||
env:
|
||||
- QWEATHER_KEY
|
||||
params:
|
||||
- name: location
|
||||
required: true
|
||||
desc: 城市名称,如"北京"、"上海"、"合肥"。支持地级市和县级市
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
//! weather — 和风天气
|
||||
|
||||
const HOST: &str = "https://devapi.qweather.com/v7";
|
||||
|
||||
fn main() {
|
||||
let input: serde_json::Value = serde_json::from_reader(std::io::stdin()).unwrap();
|
||||
let mode = std::env::args().nth(2).unwrap_or_else(|| "now".to_string());
|
||||
let loc = input["params"]["location"].as_str().unwrap_or("北京");
|
||||
let key = match std::env::var("QWEATHER_KEY") {
|
||||
Ok(k) if !k.is_empty() => k,
|
||||
_ => {
|
||||
println!(r#"{{"type":"result","content":"错误: 缺少环境变量 QWEATHER_KEY(和风天气 API Key),请在 .env 中设置"}}"#);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if input.get("response").is_none() && input.get("db_response").is_none() {
|
||||
let (path, desc) = match mode.as_str() {
|
||||
"now" => ("weather/now", "实时天气"),
|
||||
"forecast" => ("weather/7d", "7天预报"),
|
||||
"hourly" => ("weather/24h", "24h逐时"),
|
||||
_ => { println!(r#"{{"type":"result","content":"未知模式"}}"#); return; }
|
||||
};
|
||||
println!("{}", serde_json::json!({
|
||||
"type":"http","method":"GET",
|
||||
"url":format!("{HOST}/{path}?location={loc}&key={key}"),
|
||||
"desc":desc
|
||||
}));
|
||||
} else {
|
||||
let body = &input["response"]["body"];
|
||||
let ok = body["code"].as_str() == Some("200");
|
||||
let content = if !ok { "查询失败".into() } else { match mode.as_str() {
|
||||
"now" => format!("{} {}°C(体感{}°C) {}风{}级 湿度{}%",
|
||||
body["now"]["text"].as_str().unwrap_or("?"), body["now"]["temp"].as_str().unwrap_or("?"),
|
||||
body["now"]["feelsLike"].as_str().unwrap_or("?"), body["now"]["windDir"].as_str().unwrap_or("?"),
|
||||
body["now"]["windScale"].as_str().unwrap_or("?"), body["now"]["humidity"].as_str().unwrap_or("?")),
|
||||
"forecast" => body["daily"].as_array().unwrap_or(&vec![]).iter().take(7)
|
||||
.map(|d| format!("{}: {}~{}°C", d["fxDate"].as_str().unwrap_or(""),
|
||||
d["tempMin"].as_str().unwrap_or(""), d["tempMax"].as_str().unwrap_or("")))
|
||||
.collect::<Vec<_>>().join("\n"),
|
||||
"hourly" => body["hourly"].as_array().unwrap_or(&vec![]).iter().take(12)
|
||||
.map(|h| format!("{}: {} {}°C", h["fxTime"].as_str().unwrap_or(""),
|
||||
h["text"].as_str().unwrap_or(""), h["temp"].as_str().unwrap_or("")))
|
||||
.collect::<Vec<_>>().join("\n"),
|
||||
_ => String::new(),
|
||||
}};
|
||||
println!("{}", serde_json::json!({"type":"result","content":content}));
|
||||
}
|
||||
}
|
||||
Generated
-105
@@ -1,105 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "web_search"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
@@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "web_search"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "web_search"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
@@ -1,16 +0,0 @@
|
||||
name: web_search
|
||||
desc: 联网搜索互联网最新信息。支持 AI 摘要,返回标题、URL和内容摘要。用于查询实时资讯、最新事实、热点新闻等。需要 TAVILY_API_KEY
|
||||
type: http
|
||||
tool: web_search
|
||||
path: /target/release/web_search
|
||||
risk_level: low
|
||||
timeout_secs: 30
|
||||
env:
|
||||
- TAVILY_API_KEY
|
||||
params:
|
||||
- name: query
|
||||
required: true
|
||||
desc: 搜索关键词,如"Rust 2024 edition 新特性"、"今天比特币价格"
|
||||
- name: max_results
|
||||
desc: 返回的最大搜索结果数量(1-20),默认5条
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
fn main() {
|
||||
let input: serde_json::Value = serde_json::from_reader(std::io::stdin()).unwrap();
|
||||
if input.get("response").is_none() && input.get("db_response").is_none() {
|
||||
let q = input["params"]["query"].as_str().unwrap_or("");
|
||||
if q.is_empty() { println!(r#"{{"type":"result","content":"缺少query"}}"#); return; }
|
||||
let n = input["params"]["max_results"].as_u64().unwrap_or(5);
|
||||
let body = serde_json::json!({"query":q,"max_results":n,"include_answer":true});
|
||||
println!("{}", serde_json::json!({
|
||||
"type":"http","method":"POST","url":"https://api.tavily.com/search",
|
||||
"body":body.to_string(),"desc":format!("搜索: {q}")
|
||||
}));
|
||||
} else {
|
||||
let b = &input["response"]["body"];
|
||||
let mut o = String::new();
|
||||
if let Some(a) = b["answer"].as_str() { o.push_str(&format!("📝 {a}\n\n")); }
|
||||
for (i,r) in b["results"].as_array().unwrap_or(&vec![]).iter().enumerate() {
|
||||
o.push_str(&format!("{}. {} - {}\n", i+1, r["title"].as_str().unwrap_or(""), r["url"].as_str().unwrap_or("")));
|
||||
}
|
||||
if o.is_empty() { o = "未找到结果".into(); }
|
||||
println!("{}", serde_json::json!({"type":"result","content":o}));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user