Restricted file-operation workspace for AI agents (Python bindings over the Rust core)
Project description
Agent Workspace (ws)
基于 Rust 的受限文件操作 CLI,供 Agent 在指定工作目录内安全地读写文件。所有路径均相对于配置的工作区解析,无法越界访问宿主机其他路径。
特性
- 双后端:本地文件(
file)或 MySQL(mysql),通过config.yaml切换 - 路径隔离(file 后端):读写操作限制在
workspace_dir内,含符号链接逃逸检测 - 元数据:file 后端使用
*.meta.yamlsidecar;mysql 后端将元数据存入数据库 - 并发安全:file 后端使用 advisory 文件锁;mysql 后端使用 InnoDB 行锁
- 按行操作:支持按行区间读取或局部替换写入
- 内容 Hook(可选):
config.yaml可配置 read/write 外部命令,在逻辑内容与物理存储之间转换 - 本地 MCP 服务:
ws mcp通过 stdio 暴露 JSON-RPC(MCP 协议)工具,供 MCP 客户端调用工作区读写列删操作
安装
需要 Rust 工具链。
# 克隆或进入项目目录后
cd rust && cargo build --release
# 安装到 ~/.cargo/bin
cargo install --path rust
构建产物位于 rust/target/release/ws。安装后可直接在终端运行 ws。
初始化
使用 init 命令创建新的工作区(生成 config.yaml,file 后端还会创建 data/ 目录):
# 在当前目录初始化(默认 file 后端)
ws init
# 在指定目录初始化(不存在则自动创建)
ws init ./my-agent-workspace
ws init /path/to/workspace
# 使用 MySQL 后端(生成 mysql 配置模板并自动建库建表)
ws init --backend mysql
ws init ./my-agent-workspace --backend mysql
--backend mysql 会写入 MySQL 连接配置模板,并尝试连接数据库、创建库(若不存在)及 workspace_files 表。请编辑 config.yaml 中的 host、user、password、database 后再使用其他命令。
初始化完成后,进入该目录即可使用其他命令。若目录下已存在 config.yaml,会报错以避免覆盖。
配置
破坏性变更:旧版顶层
workspace_dir/metadata_suffix已移除。请改用backend块,见下方示例。
在项目根目录(或任意启动目录)放置 config.yaml。
File 后端(默认)
backend:
type: file
workspace_dir: ./data # 相对 config 文件所在目录解析
metadata_suffix: ".meta.yaml" # 可省略(默认值)
启动时会校验 workspace_dir 存在且可写。
MySQL 后端
backend:
type: mysql
host: localhost
port: 3306 # 可省略(默认 3306)
user: ws_user
password: change_me
database: agent_workspace
连接时会自动确保数据库和 workspace_files 表存在。元数据(创建者、描述、时间戳、SHA256)与文件内容存储在同一张表中,不再使用 sidecar 文件。
内容 Hook(可选)
在 backend 块之外,可声明顶层 hooks,为当前配置文件绑定一套全局 read / write 外部命令。磁盘/数据库中存的是物理内容;CLI、MCP、Python 默认看到逻辑内容(经 read hook 转换后)。
backend:
type: file
workspace_dir: ./data
hooks:
read:
command: ["python", "hooks/decode.py"]
timeout_ms: 10000 # 可选,默认 30000
write:
command: ["python", "hooks/encode.py"]
命令协议
| 项 | 约定 |
|---|---|
command |
非空 argv 数组(不经 shell) |
| 输入 | 完整内容经 stdin 传入(UTF-8) |
| 输出 | stdout 全文作为结果(不 trim) |
| 工作目录 | config.yaml 所在目录 |
| 环境变量 | WS_HOOK=read|write、WS_PATH=<相对路径> |
| 失败 | 非零退出或超时 → 操作失败,不部分写入 |
未配置 hooks 时行为与无 hook 版本完全一致。仅配置单侧 hook 时启动会打印 warning。
跳过 Hook:read / write 支持 --no-hooks(MCP/Python:skip_hooks: true),直接读写物理存储内容。调试或手工修复时,应对 read/write 一致使用该选项,避免破坏存储格式。
加载顺序
除 init 外,其余命令均通过配置文件加载后端。可按以下优先级指定配置文件:
- 命令行
--config /path/to/config.yaml - 环境变量
AGENT_WORKSPACE_CONFIG - 当前工作目录下的
config.yaml
init 会生成新的 config.yaml,不使用上述加载逻辑;传入的 --config 对其无效。
路径规则
所有命令的路径参数均为工作区相对路径,例如 docs/foo.txt。
路径会按 POSIX 语义归一化,且不能逃出工作区根目录:
| 输入 | 归一化结果 |
|---|---|
docs/foo.txt |
docs/foo.txt |
/docs/foo.txt |
docs/foo.txt |
../docs/foo.txt |
docs/foo.txt |
foo/../bar |
bar |
../etc/passwd |
etc/passwd(访问的是工作区内的 etc/passwd,不是系统 /etc/passwd) |
元数据文件(如 foo.txt.meta.yaml)在 read / remove 中视为不存在,不会泄露其内容。
命令
所有需加载配置的命令均支持全局 --config 选项,例如:
ws --config /path/to/config.yaml read docs/foo.txt
ws read docs/foo.txt --config /path/to/config.yaml
两种写法等价;未指定时按上文「加载顺序」解析。
init — 初始化工作区
ws init
ws init ./my-agent-workspace
ws init --backend mysql
ws init ./my-agent-workspace --backend mysql
| 参数 | 说明 |
|---|---|
[path] |
可选,目标目录;省略则在当前工作目录初始化 |
--backend |
后端类型:file(默认)或 mysql |
read — 读取文件
ws read docs/foo.txt
ws read docs/foo.txt --human
ws read docs/foo.txt --ranges 1-10,20-30
| 选项 | 说明 |
|---|---|
--human |
首行输出路径,每行前加行号(如 12 | content) |
--ranges |
1-indexed 行区间,逗号分隔;human 模式下仍显示真实行号 |
--no-hooks |
跳过配置的 read hook,返回物理存储内容 |
默认输出文件原文(raw)。
write — 写入文件
--content、--created-by、--desc 均为必选参数:
ws write docs/foo.txt --content "hello\n" --created-by agent-x --desc "需求草稿"
按行区间局部替换(删除 START..END 行,在该位置插入新内容):
ws write docs/foo.txt --ranges 2-5 --content "替换内容\n" --created-by agent-x --desc "局部更新"
| 选项 | 说明 |
|---|---|
--content |
写入内容(必选) |
--created-by |
写入元数据,首次创建时记录(必选) |
--desc |
文件描述(必选) |
--ranges START-END |
1-indexed,含端点;省略则整文件覆盖 |
--no-hooks |
跳过配置的 read/write hook,直接读写物理存储内容 |
更新已有文件时,元数据中的 created_by / created_at 会保留,其余字段更新。
list — 列出文件
ws list # 列出工作区全部文件
ws list docs # 只列出 docs/ 下文件
ws list --json
ws list docs --json
默认输出人类可读表格;--json 输出结构化 JSON(含 scope、file_count、total_size_bytes、files)。
remove — 删除文件
ws remove docs/foo.txt
同时删除数据文件及对应元数据 sidecar。
mcp — 本地 MCP 服务
以 Model Context Protocol 服务运行,通过 stdio 收发换行分隔的 JSON-RPC 2.0 消息。MCP 客户端将 ws mcp 作为子进程启动后,即可调用工作区工具。
ws mcp
ws mcp --config /path/to/config.yaml
进程读取 stdin 直到 EOF,所有协议输出写入 stdout(因此该模式下命令本身不向 stdout 打印其他内容)。后端与作用域规则由配置文件决定。
支持的方法:initialize、tools/list、tools/call、ping,以及忽略的 notifications/*。
暴露的工具(每次调用可选 user_id / session_id 进行作用域隔离):
| 工具 | 必选参数 | 可选参数 | 说明 |
|---|---|---|---|
read |
path |
ranges、skip_hooks |
读取文件,可按 1-indexed 行区间过滤 |
write |
path、content、created_by、desc |
ranges(单个 START-END)、skip_hooks |
写入或局部替换 |
list |
— | path |
列出文件,返回 JSON 报告 |
remove |
path |
— | 删除文件及元数据 |
工具执行失败(如路径越界、未找到)按 MCP 约定返回 isError: true 的结果而非 JSON-RPC 协议错误;仅调用格式本身非法(缺少工具名、未知工具)才返回协议错误。
示例(手动喂入请求):
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05"}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
'{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"write","arguments":{"path":"a.txt","content":"hello\n","created_by":"agent","desc":"demo"}}}' \
| ws mcp
在 MCP 客户端中的典型配置(以可执行路径启动):
{
"mcpServers": {
"agent-workspace": {
"command": "/path/to/ws",
"args": ["mcp", "--config", "/path/to/config.yaml"]
}
}
}
也可通过环境变量 AGENT_WORKSPACE_CONFIG 指定配置(省略 --config 时生效):
{
"mcpServers": {
"agent-workspace": {
"command": "/path/to/ws",
"args": ["mcp"],
"env": { "AGENT_WORKSPACE_CONFIG": "/path/to/config.yaml" }
}
}
}
启动目录、
--config或AGENT_WORKSPACE_CONFIG需指向有效的config.yaml,否则服务无法加载后端。
元数据
每个数据文件对应一个 sidecar,命名规则:foo.txt → foo.txt.meta.yaml。
示例:
relative_path: docs/foo.txt
created_by: agent-x
desc: 需求文档草稿
created_at: 2026-06-13T13:00:00+08:00
updated_at: 2026-06-13T13:35:00+08:00
size_bytes: 1024
sha256: "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
退出码
| 码 | 含义 |
|---|---|
| 0 | 成功 |
| 1 | 一般错误 |
| 2 | 路径非法 / 越界 |
| 3 | 未找到(含元数据保护触发) |
| 4 | 锁冲突 |
错误信息输出到 stderr。
并发说明
File 后端
文件锁为进程级 advisory lock,仅对同样使用本工具的进程有效。同一文件的并发 write 会串行化;write 在独占锁下同时更新数据文件和元数据 sidecar,保证一致性。
MySQL 后端
write / remove 在事务内对目标行执行 SELECT ... FOR UPDATE,依赖 InnoDB 行锁串行化并发写入。锁等待超时或死锁时返回退出码 4(LockConflict),与 file 后端的锁冲突行为一致。read 为普通 SELECT,不加行锁。
开发
cd rust
cargo test # 单元测试 + 集成测试(不含需 MySQL 的 ignored 测试)
cargo run -- list # 开发模式运行
Python 绑定(在 python/ 目录):
cd python
uv sync
uv run maturin develop
项目结构
仓库按语言拆分,Rust 核心与 Python 绑定分目录维护:
rust/ Rust crate(CLI、库、MCP、PyO3 绑定)
├── src/
│ ├── main.rs 仅 fn main(),调用 cli::run()
│ ├── lib.rs 模块声明
│ ├── error.rs 错误类型与退出码
│ ├── lock.rs file 后端的 advisory 文件锁
│ ├── cli.rs Cli/Commands 定义、命令分发、按作用域打开后端
│ ├── scoping.rs SessionScope(user/session 作用域解析)
│ ├── ranges.rs 行区间解析、写入替换、过滤
│ ├── metadata.rs FileMetadata、sidecar 读写、SHA256/时间戳
│ ├── paths/ 路径领域
│ │ ├── normalize.rs 工作区相对路径归一化
│ │ ├── resolve.rs 路径解析与越界校验
│ │ ├── metadata_name.rs sidecar 命名与识别
│ │ └── scope_prefix.rs list 作用域前缀匹配
│ ├── config/ 配置领域
│ │ ├── mod.rs Config / BackendConfig
│ │ ├── raw.rs 反序列化 DTO 与默认值
│ │ ├── load.rs 配置发现、加载、校验
│ │ └── templates.rs init 写出的配置模板
│ ├── storage/ 存储领域
│ │ ├── mod.rs WorkspaceBackend trait + ListReport
│ │ ├── handle.rs BackendHandle 枚举与工厂
│ │ ├── file.rs file 后端
│ │ ├── scoped.rs 带作用域的 mysql 后端包装
│ │ └── mysql/ mysql 后端(connection 连接层 + mod CRUD 实现)
│ ├── mcp/ 本地 MCP 服务
│ │ ├── mod.rs 模块入口
│ │ ├── protocol.rs JSON-RPC 2.0 消息类型与错误码
│ │ ├── server.rs stdio 同步循环与方法分发
│ │ └── tools.rs 工具定义与执行(映射到 WorkspaceBackend)
│ └── commands/ 各子命令实现(init/read/write/list/remove)
└── tests/ 集成测试
python/ Python 包与构建配置
├── pyproject.toml maturin/uv(`manifest-path` → ../rust/)
├── uv.lock uv 依赖锁(可选)
└── agent_workspace/
├── __init__.py 公开 API 重导出
└── py.typed PEP 561 类型标记
MySQL 集成测试(可选)
需要本地或 CI 中可访问的 MySQL 实例。设置 MYSQL_TEST_URL 后运行 ignored 测试:
export MYSQL_TEST_URL='mysql://user:pass@localhost:3306/agent_workspace_test'
cd rust && cargo test --test mysql_integration -- --ignored
未设置 MYSQL_TEST_URL 时,cargo test 会跳过这些测试,不影响常规 CI。
快速上手
# 新建工作区
ws init ./my-workspace
cd my-workspace
# 写入、读取、列出、删除
ws write docs/readme.md --content "# 标题\n" --created-by me --desc "示例文件"
ws read docs/readme.md --human
ws list
ws remove docs/readme.md
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agent_workspace-2026.6.16a1.tar.gz.
File metadata
- Download URL: agent_workspace-2026.6.16a1.tar.gz
- Upload date:
- Size: 63.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d0389d6ec86814eef78c120d123d755e94dd44a23e4f035dfd30df4df74753ab
|
|
| MD5 |
b8489705cea18413dcec3e827066009b
|
|
| BLAKE2b-256 |
cd03d8b0317b6ce034f50bcb38ec5200f7e69976709c79c1e0f033c5be30a936
|
File details
Details for the file agent_workspace-2026.6.16a1-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: agent_workspace-2026.6.16a1-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 1.1 MB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b7b5e9c2e198546300fc2e564ca284cc7846793bb50863468a5667b772139ff0
|
|
| MD5 |
04aee18152d4c24b70b12aab805d08f0
|
|
| BLAKE2b-256 |
cb8dff4e63e348a814c5a9d7083b4b37cf5155410f2b1836b613fe1e6f38fc40
|
File details
Details for the file agent_workspace-2026.6.16a1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: agent_workspace-2026.6.16a1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 1.4 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a1b3864b07211a668a7adbddb5c7ee28686f7a3806114c5afb0a047613d715af
|
|
| MD5 |
ff155006bb027142437e1852b758c8c4
|
|
| BLAKE2b-256 |
3b43c8f64c9a0b5f2a2ac1fec7a5b827a9ac719eee767e7a7df299af8ea69b39
|
File details
Details for the file agent_workspace-2026.6.16a1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: agent_workspace-2026.6.16a1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 1.3 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5dce5cfc405a59a2a480733721193dba9eb882b2ab41afc36777af4a286f00d6
|
|
| MD5 |
66b7db798f4253d2bce53145457b2d70
|
|
| BLAKE2b-256 |
a2691faa8a96120bd7da3ef80d6090ff66df8023e8b084eeb345e31b821e5bd2
|
File details
Details for the file agent_workspace-2026.6.16a1-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: agent_workspace-2026.6.16a1-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 1.2 MB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65d6ed56ff5c702576527876da57abb7ec1284ad1b7fd21c9085fec113c2f6c6
|
|
| MD5 |
43a334a9a91b619e320d4e08ca55c956
|
|
| BLAKE2b-256 |
3f314e605bb67cb4ae8dba2614da9881acc9316000d0fd0b8eb9aa272832c106
|
File details
Details for the file agent_workspace-2026.6.16a1-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: agent_workspace-2026.6.16a1-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 1.2 MB
- Tags: CPython 3.9+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8a0a8c6df73be7af6e9dad0c3b83ea9312764fa6cfceb5fa3d4f8fca3d1a6c07
|
|
| MD5 |
7e71d636e6570230b06ee29ef031b6f7
|
|
| BLAKE2b-256 |
fa19804e47e11ff85a96f748fec8a2bdd2c647044e71fe3a5e9e8eb19d837066
|