随附的第一方 source
Plexus 随附一组第一方 capability source——网关一启动,agent 就有真实的东西可发现。本页逐一交代:capability id、所需授权、如何启用 / 配置、前置条件,以及如实的只读 vs 可写暴露面。
这些 source:
| Source | 访问 | 前置条件 |
|---|---|---|
Obsidian(obsidian-fs) | read | 磁盘上的一个 vault 文件夹 |
Obsidian(obsidian-rest) | read + write | Obsidian Local REST API plugin |
| Apple Calendar | read | macOS + Calendar TCC |
| Apple Reminders | read + write | macOS + Reminders TCC |
| Apple Notes | read + 仅限创建的 write | macOS + Automation TCC |
| Apple Mail | 只读 | macOS + Automation TCC |
| Apple Contacts | 只读 | macOS + Automation TCC |
| Apple Photos | read(export 向受限目录写出一个文件) | macOS + Automation TCC |
Shortcuts(shortcuts) | read + execute(默认记录模式) | macOS shortcuts CLI |
Browser(browser) | 只读(Safari + Chrome) | macOS(Safari 历史需要完全磁盘访问权限) |
Browser control(browser-control) | read + execute(驱动一个真实的 Chrome) | Google Chrome;在你授权一个域名之前是惰性的 |
Workspace(workspace) | read + write | 磁盘上一个已授权的工作目录 |
Claude Code(claudecode) | execute(受沙箱约束) | PATH 上有 claude + macOS sandbox-exec |
Codex(codex) | execute(受沙箱约束) | PATH 上有 codex CLI + macOS sandbox-exec |
两种启用形态
Apple source(Calendar、Reminders、Notes、Mail、Contacts、Photos)、Shortcuts、Browser、Browser control,加上三个受沙箱约束的演示 / agent source(Workspace、Claude Code、Codex)都是编译进网关的,自动注册,没有添加步骤。Obsidian 适配器则是受管 source,在运行时添加(CLI 或 /admin)。两类下面都会讲到。
安全姿态(对它们全都适用)
默认拒绝,且以你的授权为界:连接 agent 时,你为它勾选可触达的 capability 授权子集,子集之外的授权请求直接拒绝——绝不挂起。子集之内,连接时勾选的 read 成为常驻授权;勾选的带副作用的 capability(write / execute)保持逐次——每次调用都挂起等待人类批准(即 grant_pending_user 那套动作——见连接一个 agent),除非你在连接时为那一项 capability 显式 opt-in 常驻,或之后在批准它的请求时选一个真实的信任窗口。agent 永远无法给自己授予变更性调用。信任模型见项目 README和看信任闭环。
Obsidian
Obsidian vault 说到底就是一个装 .md 文件的文件夹。Plexus 提供两种暴露方式——按你是否需要写入来选。
obsidian-fs——直接、只读、路径受限
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
obsidian.vault.read | capability | read | 构造上只读 |
obsidian.vault.search | capability | read | 大小写不敏感的子串搜索,覆盖笔记路径 + 内容(默认 20 条命中,上限 100) |
obsidian.vault.how-to-cite | skill | — | 使用指引(当上下文读) |
构造上只读——代码里根本没有写入或执行路径——并且路径受限:../ 穿越、绝对路径、逃出 vault 的符号链接,一律拒绝,绝不读出。
**前置条件:**磁盘上有一个 vault 文件夹即可。不需要 Obsidian 应用,不需要 plugin,不需要密钥。
启用(受管 source——添加后持久化到 ~/.plexus/sources.json,热加载,无需重启)。在仓库根目录:
# via the plexus CLI
bun run packages/cli/src/bin/plexus source add obsidian-fs --vault-path ~/Documents/MyVault
# or the launcher shortcut (persists the same managed source)
bun run start --vault ~/Documents/MyVault也可以在 /admin 的 What I expose 标签页添加。确认它已经上线:
bun run packages/cli/src/bin/plexus source list
# → … obsidian-fs … enabled · live … capabilities:…同一个 source 会出现在 /admin 的 What I expose 树里;你为其授权过的 agent,在它自己的 list 里能看到 obsidian.vault.read。
obsidian-rest——经由 Local REST API plugin 的读 + 写
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
obsidian-rest.vault.list | capability | read | 列出 vault 条目 |
obsidian-rest.vault.read | capability | read | 读一条笔记 |
obsidian-rest.vault.search | capability | read | 全文搜索 vault(POST /search/simple/) |
obsidian-rest.vault.write | capability | write | 创建/覆盖一条笔记——替换整条笔记 → 挂起 |
obsidian-rest.vault.append | capability | write | 追加到笔记末尾(不存在则创建) → 挂起 |
obsidian-rest.vault.how-to-use | skill | — | 使用指引 |
**前置条件:**在同一台 Mac 的 Obsidian 应用里安装并运行 Obsidian Local REST API plugin。该 plugin 在回环上提供 HTTPS(默认 https://127.0.0.1:27124),用其设置里的 Bearer API key 认证。Plexus 接受它的自签名证书,仅仅因为主机解析到回环;transport 每次调用前都会重新核验回环。
启用(API key 只从 STDIN 读取——绝不走 argv,那会经 ps 泄漏——按名字存进 ~/.plexus/secrets/,绝不回显):
printf %s "$OBSIDIAN_KEY" | bun run packages/cli/src/bin/plexus source add obsidian-rest \
--base-url https://127.0.0.1:27124 --secret-name obsidian-local-rest-api-key --api-key-stdin一条值得当真的写入警告:obsidian-rest.vault.write 会替换整条笔记(PUT /vault/{path},请求体是完整的 markdown 全文)——先读出笔记,再把想保留的内容全部重发。做增量编辑——日志、跟进、随手记——优先用 obsidian-rest.vault.append:它追加到笔记末尾,保留已有内容(笔记不存在时会创建)。
两项 write(vault.write / vault.append)都带 write 授权,授予时会挂起等人——agent 收到 grant_pending_user,你在 Approvals 标签页批准。三项 read 你在连接时勾选即为常驻授权——调用直接过。(重新配置 source 的 --base-url 或密钥会清除它的授权——先前的批准带不到新端点上。)完整的 source 管理见 docs/sources/MANAGING-SOURCES.md。
Apple Calendar——只读
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
apple-calendar.calendars.list | capability | read | 列出日历 |
apple-calendar.events.list | capability | read | 列出某时间窗口内的事件 |
apple-calendar.how-to-use | skill | — | 使用指引 |
构造上只读——provider 只暴露 listCalendars() / listEvents(),没有写入路径。自动注册(编译进来的第一方 source),没有添加步骤。
**前置条件(真实 macOS):**Calendar 应用,加一次性的 macOS TCC 授权。第一次实时调用会 shell 出 osascript -l JavaScript(JXA),触发 macOS 授权对话框——系统设置 ▸ 隐私与安全性 ▸ 自动化(以及日历)。拒绝之后,调用会失败并给出准确的"到系统设置里启用"提示;Plexus 无法替你再次弹窗——你要自己去系统设置重新授予。
封闭模式(无 macOS、无 TCC):设 PLEXUS_FAKE_APPLE=1,source 会解析到假 provider,带确定性的内存夹具(示例日历 Home / Work / Birthdays 和示例事件)。验收剧本和测试关卡就是这样跑的。
PLEXUS_FAKE_APPLE=1 bun run start # fake providers — no TCC, deterministic fixturesApple Reminders——读 + 写
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
apple-reminders.lists.list | capability | read | 列出提醒列表 |
apple-reminders.reminders.list | capability | read | 列出提醒 |
apple-reminders.reminders.create | capability | write | 创建一条提醒 → 挂起 |
apple-reminders.reminders.complete | capability | write | 把提醒标为完成 → 挂起 |
apple-reminders.skill.how-to-use | skill | — | 使用指引 |
两项 write capability 会实实在在改动用户的 Reminders——它们的 describe 也是这么写的——都带 write 授权,因此都挂起等待批准。两项 read 你在连接时勾选即为常驻授权——调用直接过。自动注册(编译进来的第一方 source)。
**前置条件(真实 macOS):**Reminders 应用,加一次性 TCC 授权(系统设置 ▸ 隐私与安全性 ▸ 自动化 + 提醒事项)。真实 provider 用 osascript 执行 tell application "Reminders"(AppleScript);首次实时使用会弹授权。封闭模式:PLEXUS_FAKE_APPLE=1(种子列表 Reminders / Groceries;create/complete 改动内存存储)。
Apple Notes——读 + 仅限创建的写
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
apple-notes.folders.list | capability | read | 列出文件夹(按账户) |
apple-notes.notes.search | capability | read | 有界的标题/正文搜索(默认 20 条命中,硬上限 50) |
apple-notes.notes.read | capability | read | 按 id 或精确标题读一条笔记(text + 原始 html) |
apple-notes.notes.create | capability | write | 创建一条新笔记 → 挂起 |
apple-notes.skill.how-to-use | skill | — | 使用指引 |
构造上仅限创建的写入面:唯一的写入就是创建一条新笔记——没有 update、没有 delete、没有 move、没有 rename 条目,整个 source 里也根本不存在(provider seam 没有这类方法,bridge 没有这类 handler)。已有笔记无法经 Plexus 修改或删除。apple-notes.notes.create 仍带 write 授权,挂起等待批准;三项 read 你在连接时勾选即为常驻授权——调用直接过。搜索返回命中摘要(id、标题、文件夹、修改日期、短摘录——绝不返回全文);把命中的 id 传给 notes.read 拿实际内容。自动注册(编译进来的第一方 source)。
**前置条件(真实 macOS):**Notes 应用,加一次性 TCC 授权(系统设置 ▸ 隐私与安全性 ▸ 自动化)——provider 驱动 osascript/JXA。封闭模式:PLEXUS_FAKE_APPLE=1(确定性内存夹具;create 改动内存存储)。
Apple Mail——严格只读
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
apple-mail.mailboxes.list | capability | read | 账户 + 各邮箱及未读数 |
apple-mail.messages.search | capability | read | 在单个邮箱内的有界搜索(默认 20,硬上限 50) |
apple-mail.message.read | capability | read | 按 id 读一封邮件的纯文本(正文上限 20,000 字符) |
apple-mail.how-to-use | skill | — | 使用指引 |
构造上严格只读——每项 capability 都带 read,provider seam 没有起草/发送/移动/删除方法:起草或发送 capability 在这个 source 里根本不存在,而不是仅仅被拒绝。搜索一次只在一个邮箱内进行(默认 INBOX,即统一收件箱),按发件人/主题子串和/或收件日期范围过滤,结果最新在前,带约 200 字符的摘录和 truncated 标志;大邮箱上优先用日期范围或发件人过滤。自动注册(编译进来的第一方 source)。
**前置条件(真实 macOS):**Mail 应用,加一次性 TCC 授权(系统设置 ▸ 隐私与安全性 ▸ 自动化)。封闭模式:PLEXUS_FAKE_APPLE=1(确定性内存夹具)。
Apple Contacts——只读
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
apple-contacts.contacts.search | capability | read | 有界的姓名/邮箱/电话子串搜索(默认 20,硬上限 50) |
apple-contacts.contacts.read | capability | read | 按联系人 id 读完整名片 |
apple-contacts.how-to-use | skill | — | 使用指引 |
构造上只读——provider seam 没有 create/update/delete 方法;这个 source 里不存在任何写入 capability。搜索匹配姓名、邮箱地址或电话号码的大小写不敏感子串(电话匹配按数字比较——查询需要 ≥ 3 位数字才会匹配电话);contacts.read 返回完整名片(姓名、组织、生日,以及带标签的邮箱/电话/邮政地址)。自动注册(编译进来的第一方 source)。
**前置条件(真实 macOS):**Contacts 应用,加一次性 TCC 授权(系统设置 ▸ 隐私与安全性 ▸ 自动化)。封闭模式:PLEXUS_FAKE_APPLE=1(确定性内存夹具)。
Apple Photos——read 姿态、牢笼化导出
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
apple-photos.albums.list | capability | read | 相册 + 文件夹及条目数(每层最多 200) |
apple-photos.search | capability | read | 仅元数据的媒体搜索(默认 20,最多 100) |
apple-photos.export | capability | read | 把一个条目导出到 ~/.plexus/exports/photos/ 牢笼 |
apple-photos.how-to-use | skill | — | 使用指引 |
三项都带 read——provider seam 没有任何改动照片库的方法。apple-photos.search 仅搜元数据(相册、拍摄日期范围、文件名/关键词子串——没有内容/ML 搜索,找不到"狗的照片");对超过 5000 个条目的无范围搜索会被拒绝——用 album 限定。apple-photos.export 有一个如实声明的磁盘副作用:它恰好写出一个文件,且只写进网关所有的牢笼目录 ~/.plexus/exports/photos/(缺失则创建;每次导出一个全新子目录)。它永远写不到磁盘上任何别处,也绝不改动照片库本身——所以它如实地保持 read 授权,副作用在其 describe 文本里逐字写明。自动注册(编译进来的第一方 source)。
**前置条件(真实 macOS):**Photos 应用,加一次性 TCC 授权(系统设置 ▸ 隐私与安全性 ▸ 自动化 ▸ 照片)。封闭模式:PLEXUS_FAKE_APPLE=1(确定性内存夹具)。
可注入 provider / TCC 的来龙去脉(全部 Apple source 都适用)
每个 source 通过一次 env 检查选择 provider——process.env.PLEXUS_FAKE_APPLE === "1" → 带夹具的假 provider;否则是真实 macOS provider(驱动 osascript/JXA,首次使用受 macOS TCC 管控)。这个选择在单元测试里也可注入。所以 PLEXUS_FAKE_APPLE=1 就是封闭、免 TCC 运行的单一开关——bash run-tests.sh、 tests/harnesses/acceptance-apple 剧本和 CI 用的都是它。(Shortcuts 和 Browser 以各自的开关沿用同一模式:PLEXUS_FAKE_SHORTCUTS=1 与 PLEXUS_FAKE_BROWSER=1。)
osascript 的性能,实话实说
Apple provider 靠 osascript 驱动各自的应用,在超大存储上很慢——列出或搜索成百上千条要花好几秒。把查询限定到时间窗口、具体列表/邮箱或相册,别一次索要全部。
Shortcuts——read + execute(默认记录模式)
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
shortcuts.list | capability | read | 列出 shortcut 名称 + 文件夹名 |
shortcuts.run | capability | execute | 按名字运行一个 shortcut → 挂起;默认记录模式 |
shortcuts.how-to-use | skill | — | 使用指引 |
shortcut 是用户自定义的自动化——拥有者把它造成什么样,它就能干什么(发消息、移文件、控制应用)——所以 shortcuts.run 被拥有者双重把关:它带 execute 授权,挂起等拥有者;即便调用获批,默认也处于记录模式——返回 launched: false,外加本来会执行的那条 shortcuts run 命令原文,已记录、已审计,但没有执行——直到拥有者在 Plexus 控制台为这个 source 启用真实启动(What I expose ▸ Shortcuts ▸ Real launch)。shortcuts.list 是只读发现(绝不运行任何东西)——连接时勾选即为常驻授权,调用直接过;先 list 再 run——run 按原文接收 shortcut 名字。
**前置条件(真实 macOS):**macOS 的 shortcuts CLI(现代 macOS 自带)。自动注册(编译进来的第一方 source);CLI 是否在场经 health 如实上报,不靠隐藏条目。封闭模式:PLEXUS_FAKE_SHORTCUTS=1。
Browser——只读(Safari + Chrome)
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
browser.tabs.list | capability | read | Safari + Chrome 当前打开的标签页 |
browser.bookmarks.search | capability | read | 按标题/URL 子串搜书签,有界(默认 20,硬上限 200) |
browser.history.search | capability | read | 按子串 + 可选日期范围搜历史,最新在前,有界 |
browser.how-to-use | skill | — | 使用指引 |
构造上只读——provider seam 任何地方都没有导航/打开/关闭/写入/删除方法;书签/历史的 sqlite 文件只会被拷贝到临时路径再读取(所以运行中的 Chrome 也不会挡住读取)。结果合并 Safari + Chrome,并按浏览器优雅降级:每个结果都带 browsers.safari / browsers.chrome 状态段;未安装、未运行或不可读的浏览器只贡献空列表加一条说明——绝不影响另一个浏览器的行。自动注册(编译进来的第一方 source)。
前置条件(真实 macOS):列出标签页需要对每个浏览器各一次的自动化 TCC 授权;Safari 历史(和书签)需要完全磁盘访问权限——没有它,Safari 这一半降级为 unavailable,Chrome 的结果照常返回。封闭模式:PLEXUS_FAKE_BROWSER=1(确定性内存夹具)。
Browser control——驱动一个真实的 Chrome(读 + execute)
browser-control 与上面那个只读的 browser 是两个 source,这是刻意的:那一个是构造上只读——它的 provider seam 里根本不存在变更方法——把页面控制折进去,会让那条保证悄悄变成假话。
它直接说 Chrome DevTools Protocol。不需要 Puppeteer,不需要 Playwright,也不下载浏览器:CDP 就是 WebSocket 上的 JSON,运行时本来就两样都有。
真正定分量的那个决定:给哪个浏览器
三种模式的 capability 暴露面完全一样。不同的只是调试端点从哪来——而这决定了爆炸半径:
| 模式 | agent 拿到的浏览器 | 它能触达什么 |
|---|---|---|
launch(默认) | Plexus 自己拉起的 Chrome,用自己的 profile | 一个干净的浏览器——没有 cookie,没有已登录会话 |
attach(所有者 opt-in) | 你正在用的 Chrome,经 chrome://inspect/#remote-debugging | 那个浏览器登录过的每一个会话 |
extension(所有者 opt-in) | 同样是你正在用的 Chrome,经 Plexus 扩展 | 同上——但同意只在安装时给一次 |
launch 覆盖日常的「去把这页读了」,是安全默认值。另外两个够到的是你已登录的 web,因此是一个明确的所有者决定——和 exec source 上的 Real launch 一样。
Chrome 自己的同意是全有或全无的——它的权限对话框授权的是那个浏览器,不是一组站点。所以你真正想要的那条边界(「这个 agent 可以碰 GitHub,别的不行」)不可能来自 Chrome。它来自 Plexus。
Capability
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
browser-control.tabs.list | capability | read | 哪些标签页可被驱动——只列已授权域名的 |
browser-control.page.read | capability | read | 页面的标题、url 与渲染后的文本 |
browser-control.page.elements | capability | read | 可交互元素及可用的选择器;密码框只报长度 |
browser-control.page.screenshot | capability | read | 视口截图,或用 fullPage 截整页 |
browser-control.page.scroll | capability | read | 移动视口;回报 atBottom |
browser-control.page.wait | capability | read | 等一个选择器、一段字符串,或等加载结束 |
browser-control.frames.list | capability | read | 内嵌 frame,按它自己的域名判定 |
browser-control.page.navigate | capability | execute | 跳转到某个 URL → 挂起——allowlist 的首要对象 |
browser-control.page.click | capability | execute | 在选择器上走一遍真实指针序列 → 挂起 |
browser-control.page.type | capability | execute | 填一个字段或一个编辑器 → 挂起 |
browser-control.page.press | capability | execute | 一个真实按键事件 → 挂起(Enter 可以提交) |
browser-control.page.upload | capability | execute | 附加一个文件 → 挂起,只能来自你指定的上传目录 |
browser-control.page.evaluate | capability | execute | 以该页面的身份执行 JavaScript → 挂起 |
browser-control.page.cdp | capability | execute | 任意页面级 CDP 命令,原样透传 → 挂起 |
browser-control.how-to-use | skill | — | 使用指引 |
页面暴露面是刻意开放的。在一个 agent 本来就被允许触碰的页面里,click + type 已经等同于完整的用户能动性——下单、发送、删除、改设置都做得到。在这之上再扣住 evaluate,挡不住任何真实伤害,只会让这项能力比所有者转而会去用的替代品更差。扣住的是 CDP 里属于浏览器全局的那一半——不属于任何页面的那部分——正是它让域名 allowlist 是真边界,而不是装饰。
scroll 和 wait 算 read,因为两者都不代表站点做事:它们改变的是可见范围、或我们看多久,提交不了、跟不进、也激活不了任何东西。
边界——域名 allowlist
每次调用都会解析出一个目标 URL,source 用你设定的名单去校验这个 URL 的 origin——从真实目标在服务端解析出来,绝不采信 agent 自报的字段。三条规则让它成立:
- 空名单 = 拒绝,对那个有东西可失去的浏览器而言。面对你自己的浏览器(
attach/extension),没设就是惰性的,不是开放的。面对 Plexus 在空 profile 上拉起的浏览器,没有会话可隔离,所以没设就意味着开放 web——给一个「谁也不是」的浏览器砌墙,什么都保护不了。http/https的 scheme 规则两种情况都适用,所以「整个 web」永远不包括本地磁盘或 Chrome 自己的设置页。 - 一条条目授权它那个域名,含子域。
deepseek.com覆盖www.deepseek.com。匹配发生在解析出的 host 上、且按点边界,所以deepseek.com.evil.com和evildeepseek.com都在界外;IP 条目精确匹配;scheme 必须相同,所以授权一个站点绝不隐含授权它的明文形式。 - **每次动作之前,重新校验该标签页的当前 origin。**一个在
github.com上时被放行的标签页,导航到mail.google.com之后就不再被放行——复用已持有调试 socket 的调用同样如此。复用是传输层的优化,它从不把判定结果带到下一次。
跨站 <iframe> 跑在自己的渲染进程里,判定方式与标签页完全一致:按它自己的域名。一个被授权的页面并不授权它内嵌的东西——正是这条,挡住了一个你放行的页面把已登录的 accounts.google.com frame 一起带进射程。
它与按 agent 的 scope 机制是叠加而非替代:source 级 allowlist 是地板,授权约束只能从中做减法。
上传是一条外泄通道
page.upload 把你机器上的一个文件递给一个网站。那个牢笼不是围着这项功能的便利设施,它就是这项功能本身:路径相对于你指定的一个目录,用与文件类 source 相同的「词法 + realpath」双重校验封住,且没设就拒绝一切上传——与空 allowlist 同样的 fail-closed 默认。审计记录完整路径与大小;线上只给文件名。
怎么配
在控制台的 What I expose → Browser control 下,三项设置:
- 模式——
launch/attach/extension。 - 已授权域名——一行一个。对一个你已登录的浏览器,空名单拒绝一切。
- 上传目录——没设就拒绝一切上传。
改动即时生效,不必重启。启动时的兜底是 PLEXUS_BROWSER_CONTROL_MODE、PLEXUS_BROWSER_CONTROL_ORIGINS(逗号分隔)与 PLEXUS_BROWSER_CONTROL_UPLOAD_DIR;控制台里保存过的设置优先于环境变量。
用 attach:在 chrome://inspect/#remote-debugging 处一次性打开远程调试(Chrome 144+)。这不是图方便——自 Chrome 136 起,二进制在默认 profile 上拒绝 --remote-debugging-port,所以那个开关是进入你真正登录着的那个浏览器的唯一路径。之后 Chrome 会逐连接询问权限,并挂出它那条「正受自动化测试软件控制」的横幅。
**用 extension:**先注册一次本地消息宿主,再加载扩展:
bun run packages/runtime/src/sources/browser-control/install-native-host.ts然后 chrome://extensions → 开发者模式 → 加载已解压的扩展程序 → 选 extension/plexus-browser。有网关连上时,徽标是绿色。
这个扩展只是一条传输通道——它不持有 allowlist,也没有任何批准逻辑。本地消息宿主由 Chrome 自己拉起,而且它只会拉起「清单里写明了这个扩展 id」的那一个,所以绑定由 Chrome 强制,你不需要在两个窗口之间复制任何配对 token。它相对开关那条路的好处是:同意只在安装时给一次,而不是每次连接都给。
前置条件:Google Chrome。自动注册(编译进来的第一方 source),且在你授权一个域名之前是惰性的;Chrome 是否在场经 health 如实上报,不靠隐藏条目。Plexus 拿走什么就放回什么——它持有的调试 socket 和它开的标签页会在关停时关掉,所以 agent 的浏览不会在你的 Chrome 里越堆越多窗口。
Workspace——沙箱化工作目录(读 + 写)
workspace 把磁盘上一个已授权的工作目录暴露为路径受限的文件系统——也就是演示流程里 agent 的草稿 / 产出文件夹。它是下面两个沙箱化 runner 的配套读写面:agent 在这里 list/read 文件,让 Claude Code 或 Codex 在同一个牢笼里构建,再把产物读回来。
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
workspace.list | capability | read | 列出目录(只读) |
workspace.read | capability | read | 读文件(只读) |
workspace.write | capability | write | 创建/覆盖文件 → 挂起 |
workspace.how-to-use | skill | — | 使用指引 |
和 Obsidian 的 vault 读取器一样路径受限:所有路径都在 workspace 根之下解析,逃逸(..、绝对路径、向外的符号链接)一律拒绝。两项 read(list/read)你在连接时勾选即为常驻授权——调用直接过;workspace.write 在第一方 source 上带 write 授权,因此挂起等拥有者。自动注册(编译进来的第一方 source);可用性(已授权目录是否存在)经 health 报告,绝不靠隐藏条目。
Claude Code——无头、受沙箱约束(execute)
claudecode 把 Claude Code CLI 暴露为一项敏感 capability:启动无头 Claude Code 做真实编码工作,由 macOS sandbox-exec 约束在已授权目录内。agent 看不到 shell,也看不到启动命令——只有 { prompt }。牢笼之外的读写在内核处失败。
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
claudecode.run | capability | execute | 在牢笼里启动无头 Claude Code → 挂起 |
claudecode.how-to-use | skill | — | 使用指引 |
claudecode.run 是第一方 source 上的 execute,属于敏感度升级,挂起等拥有者——发出调用后等待批准。两次调用之间用 workspace.read 验证产物。自动注册(编译进来的第一方 source);claude + sandbox-exec 是否在场经 health 如实上报,不靠隐藏条目。
Codex——无头、受沙箱约束(execute)
codex 是 claudecode 的镜像:无头运行本地 Codex CLI(codex exec)做真实编码工作,由 macOS sandbox-exec 约束在已授权目录内。姿态相同——只有 { prompt }(外加可选的、牢笼内的 cwd);牢笼之外的读写在内核处失败。
| Capability id | 类别 | 授权 | 暴露面 |
|---|---|---|---|
codex.run | capability | execute | 在牢笼里启动无头 codex exec → 挂起 |
codex.how-to-use | skill | — | 使用指引 |
codex.run 是第一方 source 上的 execute,因此挂起等拥有者——发出调用后等待。本地 codex CLI 缺席时,调用返回 source_unavailable,不会让会话失败。自动注册(编译进来的第一方 source);codex + sandbox-exec 是否在场经 health 如实上报。
接下来去哪
- 连接一个 agent——端到端驱动这些 capability(原始 HTTP,以及一个真实的 Codex agent),含 pending → approve 动作。
- 编写一个扩展——添加网关未随附的 capability。
docs/sources/MANAGING-SOURCES.md——完整的受管 source 生命周期(添加 / 启用 / 禁用 / 重新配置 / 移除)。