Migrate test plugins to Rust and async-ify syscall modules
- Replace Python test plugins (echo, ai_test, claudecode) with Rust implementations driven by the plugin SDK - Convert fs/network/process/timer syscall modules to async/await - Change Network.OpenConn to return stream ConnEvent (conn_id handed to client in first "open" frame for Send/Close routing) - Bind timer callbacks to plugin identity; stream_call subscriptions are identity-checked - Update docs and regenerate Python SDK protobuf stubs Verified: cargo build/test/clippy clean; echo + ai_test full syscall suites pass; hermes & claudecode register and heartbeat; cross-plugin bridge.call routing and auth denial verified end-to-end. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -78,15 +78,18 @@ memory.search(query) -> results
|
||||
**管**:对外连接、监听 | **不管**:本地 IPC(内核 gRPC 总线)
|
||||
|
||||
```
|
||||
net.request(url, method, body?) -> response
|
||||
net.connect(addr, protocol) -> conn
|
||||
net.listen(port, protocol) -> listener
|
||||
net.send(conn, data)
|
||||
net.recv(conn) -> data
|
||||
net.close(conn)
|
||||
net.available() -> bool
|
||||
network.request(url, method, headers?, body?) -> response # HTTP 请求
|
||||
network.open_conn(addr, protocol) -> stream ConnEvent # TCP 建连,流式推送入站数据
|
||||
network.send(conn_id, data) # 向已有连接发数据
|
||||
network.close(conn_id) # 关闭连接
|
||||
network.listen(port, protocol) -> stream ConnEvent # 监听端口,每 accept 推送 ConnEvent
|
||||
network.available() -> bool # 网络连通性探测
|
||||
```
|
||||
|
||||
gRPC 流式语义:OpenConn/Listen 返回 `stream ConnEvent{conn_id, data, kind}`。
|
||||
- 首帧 kind="open"/"accept" 携带 conn_id,后续 kind="data" 携带入站字节,kind="closed" 表示对端关闭。
|
||||
- 发送用单独的 `Send` RPC(需传 conn_id),关闭用 `Close` RPC。
|
||||
|
||||
---
|
||||
|
||||
## 6. Process
|
||||
@@ -115,7 +118,7 @@ proc.list() -> [pid]
|
||||
|
||||
```
|
||||
timer.once(delay, callback) -> id # 到期后通过 PluginBridge.StreamCall 投递事件
|
||||
timer.cron(expr, callback) -> id
|
||||
timer.cron(expr, callback) -> id # cron 表达式格式(7段): sec min hour day month weekday year
|
||||
timer.cancel(id)
|
||||
timer.now() -> timestamp
|
||||
```
|
||||
|
||||
@@ -177,3 +177,25 @@ message CallRequest {
|
||||
- 系统插件:内核信任,无限制
|
||||
- 基础插件:按 `syscalls` 白名单精确到方法(如 `fs.list`)校验,也支持 `fs` / `fs.*` / `*` 授权(= seccomp filter)
|
||||
- 标准插件:syscalls + depends 白名单,未声明的一律拒绝
|
||||
|
||||
### 身份认证
|
||||
|
||||
所有 syscall 调用**必须**携带 `x-plugin-id` gRPC metadata,否则返回 `UNAUTHENTICATED`。
|
||||
这是第一道安全边界——即使 server 监听 TCP,未注册的进程也无法调用任何 syscall。
|
||||
|
||||
认证流程:
|
||||
1. 插件通过 `PluginBridge.Register` 注册,获得 `session_id`
|
||||
2. 后续所有 syscall 请求须在 gRPC metadata 中带 `x-plugin-id: <plugin_id>`
|
||||
3. 内核校验:plugin_id 存在 → status=Running → syscall 在该插件的白名单内 → 放行
|
||||
|
||||
### stream_call 身份绑定
|
||||
|
||||
`PluginBridge.StreamCall` 用于订阅 Timer 等回调事件。安全策略:
|
||||
- 必须携带 `x-plugin-id`(否则 UNAUTHENTICATED)
|
||||
- 只能订阅**自己**的事件通道(target 为空或等于自身 plugin_id)
|
||||
- 尝试订阅其他插件的事件返回 PERMISSION_DENIED
|
||||
|
||||
### 死插件路由保护
|
||||
|
||||
`PluginBridge.Call` 在路由到目标插件前,校验其 status 为 Running。
|
||||
Stopped/Failed 的插件会立即返回错误,而不是等 5 秒连接超时。
|
||||
|
||||
@@ -8,24 +8,27 @@
|
||||
- [x] 实现 Memory syscall(SQLite KV + FTS5 trigram search,默认写入 data/memory.db)
|
||||
- [x] 实现 Input syscall(stdin)
|
||||
- [x] 实现 Display syscall(stdout)
|
||||
- [x] 实现 Process syscall(spawn/kill/wait/stdin/stdout)
|
||||
- [x] 实现 Process syscall(spawn/kill/wait/stdin/stdout + watch channel 无竞态 wait)
|
||||
- [x] 实现 Timer syscall(once/cancel/now + 回调事件通道)
|
||||
- [x] 实现 Audio syscall(stub,待系统插件)
|
||||
- [x] 实现 HID syscall(stub,待系统插件)
|
||||
- [x] 实现 Network syscall(HTTP via curl,待替换)
|
||||
- [x] 实现 Network syscall(HTTP via reqwest)
|
||||
- [x] Plugin Bridge(注册/调用路由/心跳,注册状态写入 data/plugins.json)
|
||||
- [x] 插件加载:gRPC 握手 + 注册
|
||||
- [x] echo 测试插件(Python)验证基础链路
|
||||
- [x] ai_test 手动验证插件(Python,默认 disabled/autoload false)覆盖 Display/Memory/FS/Process/Timer/权限拒绝
|
||||
- [x] echo 测试插件(Rust)验证基础链路
|
||||
- [x] ai_test 手动验证插件(Rust,默认 disabled/autoload false)覆盖 Display/Memory/FS/Process/Timer/权限拒绝
|
||||
- [x] proto 定义:完整 9 个 service + PluginBridge
|
||||
- [x] 权限安全:无 x-plugin-id 的请求一律拒绝(UNAUTHENTICATED)
|
||||
- [x] 死插件路由保护:Bridge.call 校验 PluginStatus::Running
|
||||
|
||||
**验证结果**:agentsd 启动 → Python 插件通过 gRPC 注册 → ai_test 调用 Display/Memory/FS/Process/Timer syscall 并验证权限拒绝。
|
||||
**验证结果**:agentsd 启动 → Rust 插件通过 gRPC 注册 → ai_test 调用 Display/Memory/FS/Process/Timer syscall 并验证权限拒绝。
|
||||
|
||||
## 阶段 2:完善核心 syscall
|
||||
## 阶段 2:完善核心 syscall ✅ 部分完成
|
||||
|
||||
- [ ] Network:替换 curl 为 hyper/reqwest 原生 HTTP client
|
||||
- [ ] FS.Watch:接入 notify-rs 文件监听
|
||||
- [ ] Timer.Cron:接入 cron 表达式解析
|
||||
- [x] Network:reqwest 原生 HTTP client(阶段 1 已完成)
|
||||
- [x] Network TCP 流:open_conn/send/close/listen 双向 TCP 通信
|
||||
- [x] FS.Watch:接入 notify-rs v7 文件监听(递归)
|
||||
- [x] Timer.Cron:接入 cron 表达式定时(7 段格式)
|
||||
- [ ] Audio:接入系统音频后端(wasapi/alsa)
|
||||
- [ ] HID:接入屏幕截取(Windows: win32 API)
|
||||
- [ ] Unix socket / Named pipe 传输(替代 TCP)
|
||||
|
||||
@@ -51,22 +51,21 @@ Agentsd/
|
||||
│ ├── agentsd_pb2.py
|
||||
│ └── agentsd_pb2_grpc.py
|
||||
│
|
||||
├── echo/ # 测试插件
|
||||
├── echo/ # 测试插件(Rust)
|
||||
│ ├── plugin.yaml
|
||||
│ └── echo_plugin.py
|
||||
│ └── src/main.rs
|
||||
│
|
||||
├── ai_test/ # AI 手动验证插件(enabled=false,autoload=false)
|
||||
├── ai_test/ # AI 手动验证插件(Rust,enabled=false,autoload=false)
|
||||
│ ├── plugin.yaml
|
||||
│ └── ai_test_plugin.py
|
||||
│ └── src/main.rs
|
||||
│
|
||||
├── hermes/ # Hermes Agent 桥接(63 tool)
|
||||
├── hermes/ # Hermes Agent 桥接(Python,Rust 版仅 stub)
|
||||
│ ├── plugin.yaml
|
||||
│ └── hermes_plugin.py
|
||||
│
|
||||
└── claudecode/ # Claude Code CLI 桥接
|
||||
└── claudecode/ # Claude Code CLI 桥接(Rust)
|
||||
├── plugin.yaml
|
||||
├── claudecode_plugin.py
|
||||
└── test_chat.py
|
||||
└── src/main.rs
|
||||
```
|
||||
|
||||
## 布局原则
|
||||
@@ -94,7 +93,7 @@ cargo build --release # 约 6.1MB 单二进制
|
||||
|
||||
# 启动插件(各开一个终端)
|
||||
python plugins/hermes/hermes_plugin.py
|
||||
python plugins/claudecode/claudecode_plugin.py
|
||||
cargo run -p agentsd-plugin-claudecode
|
||||
```
|
||||
|
||||
## 手动验证插件
|
||||
@@ -103,7 +102,7 @@ python plugins/claudecode/claudecode_plugin.py
|
||||
|
||||
```bash
|
||||
RUST_LOG=agentsd=info ./target/release/agentsd.exe
|
||||
python plugins/ai_test/ai_test_plugin.py
|
||||
cargo run -p agentsd-plugin-ai-test
|
||||
```
|
||||
|
||||
## 重新生成 SDK
|
||||
|
||||
Reference in New Issue
Block a user