OpenClawoc-path插件:用oc://统一寻址 markdown / JSONC / JSONL / YAML 工作区文件
【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 🦞项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw
oc-path是 OpenClaw 内置(bundled)但默认不激活(opt-in)的 CLI 插件,它为oc://工作区文件寻址方案提供openclaw path命令,让你在终端里用一套稳定、与文件类型无关(kind-agnostic)的地址语法,精确读取或改写 markdown、JSONC、JSONL、YAML/.lobster文件中的单个叶子节点。读完本文,你将掌握oc://地址的完整语法、五个子命令(resolve/find/set/validate/emit)的用法与退出码约定、逐文件类型的寻址与改写语义,以及插件背后的源码实现原理(解析器、哨兵守卫、字节保真契约)。
插件定位:窄寻址层,不做高层语义
oc-path插件的元数据声明在extensions/oc-path/openclaw.plugin.json,它只做一件事:把openclaw pathCLI 接入 CLI 体系。
{ "id": "oc-path", "name": "OC Path", "description": "Adds the openclaw path CLI for oc:// workspace file addressing.", "cliCommands": [ { "name": "path", "description": "Inspect and edit workspace files via oc:// paths", "hasSubcommands": true } ], "activation": { "onStartup": false, "onCommands": ["path"] }, "commandAliases": [ { "name": "path", "kind": "cli" } ], "configSchema": { "type": "object", "additionalProperties": false, "properties": {} } }插件入口extensions/oc-path/index.ts通过definePluginEntry注册,register(api)中调用registerOcPathCli(api)完成 CLI 接线。registerOcPathCli位于extensions/oc-path/cli-registration.ts,它做两件关键事:
- 懒加载:真正的子命令实现通过动态
import("./src/cli.js")引入,CLI 在首次执行openclaw path …时才加载插件代码; - 机器输出判定:
isPathMachineOutput决定默认输出模式——显式传--json时输出 JSON;否则当 stdout 不是 TTY(被管道/重定向)时也输出 JSON;只有交互式终端且未传--json/--human时才默认人类可读输出。
onStartup: false保证插件不进 Gateway 启动路径,commandAliases与activation.onCommands配合让 CLI 在第一次运行path动词时按需加载——从未用过该动词的安装不付出任何加载成本。
为什么启用它
脚本、hooks 或本地 Agent 工具需要指向工作区状态的精确片段、又不愿为每种文件形状维护一套专属解析器时,就该启用oc-path。一个oc://地址可以命名 markdown frontmatter 键、章节条目、JSONC 配置叶子、JSONL 事件字段或 YAML 工作流步骤。常见动机:
- 本地自动化:shell 脚本用
openclaw path … --json解析或更新一个工作区值,免去分别维护 markdown、JSONC、JSONL、YAML 解析代码; - Agent 可见的编辑:Agent 写入前先对一个被寻址的叶子展示 dry-run diff,比自由格式的文件重写更容易审查;
- 编辑器集成:编辑器把
oc://AGENTS.md/tools/gh映射到精确的 markdown 节点和行号,无需靠标题文本猜测; - 诊断:
emit把文件完整走一遍解析器 + 发射器,在依赖自动化编辑前先确认某类文件是否字节稳定(byte-stable)。
# 该配置中 GitHub 插件是否启用? openclaw path resolve 'oc://config.jsonc/plugins/github/enabled' --json # 这段会话日志里出现了哪些工具调用名? openclaw path find 'oc://session.jsonl/[event=tool_call]/name' --json # 这次微小的配置编辑会写出什么字节? openclaw path set 'oc://config.jsonc/plugins/github/enabled' 'true' --dry-run边界声明:oc-path有意不拥有更高层语义。内存写入归 memory 插件,完整配置管理归 config 命令,last-known-good(LKG)配置恢复归其自身的恢复/提升流程。oc-path是那些高层工具可以构建其上的窄寻址 + 字节保真文件操作层。
它运行在哪里
插件在调用命令的宿主机上、openclawCLI 进程内运行。它不需要正在运行的 Gateway,不打开任何网络 socket——每个动词都是对指向文件的纯变换(pure transform)。
启用与停用
openclaw plugins enable oc-path启用后,如果运行 Gateway,重启它以便 manifest 快照拾取新状态。裸的openclaw path调用在同一宿主机上立即生效——CLI 按需加载插件。停用:
openclaw plugins disable oc-path启用oc-path不会给核心运行时拉入新包:所有解析依赖都是插件局部的(见extensions/oc-path/package.json)。依赖清单如下:
| 依赖 | 用途 |
|---|---|
commander | resolve、find、set、validate、emit的子命令接线。 |
jsonc-parser | JSONC 解析与叶子编辑,保留注释与尾逗号。 |
markdown-it | markdown 分词,支撑 section / item / field 模型。 |
yaml | YAMLDocument的 parse / emit / edit,保留注释与 flow 风格。 |
diff | set --dry-run --diff的统一 diff 输出(源码cli.ts引用structuredPatch/formatPatch)。 |
JSONL 保持手写实现:面向行的解析比任何依赖都简单,且逐行解析本身已经过jsonc-parser。
插件提供的能力清单
| 表面 | 提供方 |
|---|---|
openclaw pathCLI | extensions/oc-path/cli-registration.ts(注册)、extensions/oc-path/src/cli.ts(子命令实现) |
oc://解析器 / 格式化器 | extensions/oc-path/src/oc-path/oc-path.ts |
| 逐类型 parse / emit / edit | extensions/oc-path/src/oc-path/{md,jsonc,jsonl,yaml} |
| 通用 resolve / find / set | extensions/oc-path/src/oc-path/{resolve,find,edit,universal}.ts |
| 红action 哨兵守卫 | extensions/oc-path/src/oc-path/sentinel.ts |
目前 CLI 是唯一公开表面。底层的 substrate 动词(parseOcPath、resolveOcPath、setOcPath、findOcPaths、各类emit*等)对插件私有,消费者通过 CLI 使用,或基于 SDK 构建自己的插件。extensions/oc-path/src/oc-path/index.ts这个 barrel 文件明确注释:“Keep this barrel limited to CLI imports”,印证了内部面仅为 CLI 服务的定位。
oc://地址语法
oc://FILE/SECTION/ITEM/FIELD?session=SCOPE槽位规则:field依赖item,item依赖section。跨全部四个槽位的语法要素:
- 引号段(Quoted segments):
"a/b.c"内的/和.作为字面内容保留。引号内按字节原样解释,且不允许包含"与\。文件槽也感知引号:oc://"skills/email-drafter"/Tools/$last把skills/email-drafter当作单一文件路径。 - 谓词(Predicates):
[k=v]、[k!=v]、[k<v]、[k<=v]、[k>v]、[k>=v]。数值运算符要求两侧都能转成有限数值。 - 联合(Unions):
{a,b,c}匹配任意一个候选项。 - 通配符(Wildcards):
*匹配单个子段,**匹配零个或多个(递归)。find接受它们;resolve与set拒绝并把它们视为有歧义。 - 位置(Positional):
$first/$last解析为第一个 / 最后一个索引或声明键。 - 序数(Ordinal):
#N按文档顺序取第 N 个匹配。 - 插入标记(Insertion markers):
+、+key、+nnn分别用于键控 / 索引插入(配合set使用)。 - 会话作用域(Session scope):
?session=cron-daily等,与槽位嵌套正交。会话值按原始文本处理、不做百分号解码,且不得包含控制字符或保留查询分隔符(?、&、%)。
保留字符(?、&、%)在引号、谓词或联合段之外会被拒绝。控制字符(U+0000–U+001F、U+007F)在任何位置都被拒绝,包括session查询值。
规范化往返契约:formatOcPath(parseOcPath(path)) === path对规范化路径成立。非规范的查询参数会被忽略,只取第一个非空session=值。
硬上限(源码oc-path.ts中的常量):路径上限 4096 字节;最多 4 个槽位(file/section/item/field);每个槽位最多 64 个点分(dotted)子段;深层 JSON 路径最多 256 层嵌套遍历。另外,任何会加载文件的动词,超过 16 MiB 的输入文件在解析前一律拒绝——JSONC/JSON 报OC_JSONC_INPUT_TOO_LARGE,其他文件类型报OC_PATH_INPUT_TOO_LARGE(CLI 侧cli.ts中MAX_OC_PATH_INPUT_BYTES = MAX_JSONC_INPUT_BYTES,即所有解析器共用同一输入上限)。
源码中的安全校验
parseOcPath在入口处层层设防(validateFileSlot):
- 拒绝绝对路径(
/、\开头或盘符:前缀),错误码OC_PATH_ABSOLUTE_FILE——oc://路径是工作区相对的; - 拒绝
..父目录逃逸段,错误码OC_PATH_PARENT_TRAVERSAL; - 拒绝控制字符,错误码
OC_PATH_CONTROL_CHAR。
路径先做 NFC 规范化(兼容 macOS HFS+ 的 NFD 与 Unix/Windows 的 NFC),并容忍 BOM 前缀;因为 NFC 可能使字符串变长,规范化后会重新检查 4096 字节上限。formatOcPath还会做防御性检查:格式化结果若包含红action 哨兵则直接抛OcEmitSentinelError(见下文安全节),防止哨兵经遥测/审计/错误消息流出。
子命令一览
| 子命令 | 用途 |
|---|---|
resolve <oc-path> | 打印路径处的具体匹配(或 “not found”)。 |
find <pattern> | 枚举通配符 / 联合 / 谓词路径的所有匹配。 |
set <oc-path> <value> | 在具体路径写入一个叶子或插入目标,支持--dry-run。 |
validate <oc-path> | 仅解析;打印结构拆解(file / section / item / field)。 |
emit <file> | 把文件完整跑一遍 parse + emit(字节保真诊断)。 |
全局标志
| 标志 | 适用命令 | 用途 |
|---|---|---|
--cwd <dir> | resolve、find、set、emit | 相对该目录解析文件槽(默认:process.cwd())。 |
--file <path> | resolve、find、set、emit | 覆盖文件槽解析出的路径(绝对访问)。 |
--json | 全部 | 强制 JSON 输出(stdout 非 TTY 时默认)。 |
--human | 全部 | 强制人类可读输出(stdout 为 TTY 时默认)。 |
--value-json | set | 对 JSON/JSONC/JSONL 叶子替换,把<value>当作 JSON 解析。 |
--dry-run | set | 只打印将要写入的字节,不实际写入。 |
--diff | set(需配合--dry-run) | 打印统一 diff 而非完整字节。 |
validate只接受--json/--human;它不做任何文件系统访问,因此--cwd与--file不适用。
退出码约定
| 码 | 含义 |
|---|---|
0 | 成功(resolve/find:至少一个匹配;set:写入成功)。 |
1 | 无匹配,或set被 substrate 拒绝(如哨兵守卫命中,非系统级错误)。 |
2 | 参数或解析错误。 |
输出模式
openclaw path是 TTY 感知的:终端上输出人类可读文本,stdout 被管道或重定向时输出 JSON。--json与--human覆盖自动检测。此外 CLI 在输出前会把哨兵字面量替换为[REDACTED](cli.ts的scrubSentinel),保证终端捕获与管道不会泄漏标记。
逐文件类型的寻址模型
| 类型 | 文件扩展名 | 寻址模型 |
|---|---|---|
| Markdown | .md | 按 slug 定位 H2 章节,按 slug 或#N定位条目,通过[frontmatter]访问 frontmatter。 |
| JSONC/JSON | .jsonc、.json | 对象键与数组索引;点号拆分嵌套子段(引号内除外)。 |
| JSONL | .jsonl、.ndjson | 顶层行地址(L1、L2、$first、$last),行内再按 JSONC 风格向下走。 |
| YAML/.lobster | .yaml、.yml、.lobster | 映射键与序列索引;注释与 flow 风格由 YAML document API 处理。 |
resolve返回结构化匹配:root、node、leaf或insertion-point,附 1 起始行号。叶子值以文本 +leafType形式呈现,让插件作者无需依赖各类型 AST 形状即可渲染预览。
改写契约(Mutation contract)
set只写一个具体目标:
- Markdown:frontmatter 值与
- key: value条目字段是字符串叶子。值按字面写入(包括$1、$&、$$)。Markdown 插入会追加章节、frontmatter 键或章节条目,并为变更后的文件渲染规范的 Markdown 形状。章节正文不能整体通过set写入。 - JSONC:叶子写入把字符串值强制转换为既有叶子类型(
string、有限number、true/false或null)。当 JSONC/JSON/JSONL 叶子替换需要把<value>当 JSON 解析、可能改变形状时(例如把字符串密钥引用简写替换为对象),使用--value-json。JSONC 对象与数组插入把<value>当 JSON 解析,普通叶子写入走jsonc-parser编辑路径,保留注释与周边格式。 - JSONL:行内叶子写入的强制转换规则与 JSONC 一致。整行替换与追加把
<value>当 JSON 解析。渲染时保留文件的主导 LF/CRLF 换行约定(对文件内换行做多数投票,所以以 CRLF 为主的文件即便混有零星 LF 也保持 CRLF)。 - YAML:叶子写入强制转换到既有标量类型(
string、有限number、true/false或null)。YAML 插入使用内置yaml包的 document API 做 map/sequence 更新。带解析错误的畸形 YAML 文档在变更前即以parse-error拒绝。
在字节精确重要的场景,先跑--dry-run。JSONC 与 YAML 编辑是修补既有文档(经jsonc-parser或 YAML document API),未触及的字节通常原样存活;markdown 则在任何编辑时都会按解析出的结构重建文件,可能规范化变更叶子之外的偶然格式。想要聚焦的前后补丁而非完整渲染文件时,加--diff——补丁会记录换行变化与缺失的结尾换行,应用它产生的字节与真实写入完全一致。
逐类型实战示例
Markdown(--human输出):
<!-- frontmatter.md --> --- name: drafter description: email drafting agent tier: core --- ## Tools - gh: GitHub CLI - curl: HTTP client - send_email: enabled$ openclaw path resolve 'oc://x.md/[frontmatter]/tier' --file frontmatter.md --human leaf @ L4: "core" (string) $ openclaw path resolve 'oc://x.md/tools/gh/gh' --file frontmatter.md --human leaf @ L9: "GitHub CLI" (string) $ openclaw path find 'oc://x.md/tools/*' --file frontmatter.md --human 3 matches for oc://x.md/tools/*: oc://x.md/tools/gh → node @ L9 [md-item] oc://x.md/tools/curl → node @ L10 [md-item] oc://x.md/tools/send-email → node @ L11 [md-item][frontmatter]谓词寻址 YAML frontmatter 块;tools通过 slug 匹配## Tools标题;条目叶子保持 slug 形式,即使源文件用下划线(send_email变成send-email)。
JSONC:
// config.jsonc { "plugins": { "github": {"enabled": true, "role": "vcs"}, "slack": {"enabled": false, "role": "chat"} } }$ openclaw path resolve 'oc://config.jsonc/plugins/github/enabled' --file config.jsonc --human leaf @ L4: "true" (boolean) $ openclaw path set 'oc://config.jsonc/plugins/slack/enabled' 'true' --file config.jsonc --dry-run --dry-run: would write 142 bytes to /…/config.jsonc { "plugins": { "github": {"enabled": true, "role": "vcs"}, "slack": {"enabled": true, "role": "chat"} } }JSONC 编辑走jsonc-parser,注释与空白在set后存活。.json文件使用与.jsonc相同的适配器与编辑路径。
JSONL:
{"event":"start","userId":"u1","ts":1} {"event":"action","userId":"u1","ts":2} {"event":"end","userId":"u1","ts":3}$ openclaw path find 'oc://session.jsonl/[event=action]/userId' --file session.jsonl --human 1 match for oc://session.jsonl/[event=action]/userId: oc://session.jsonl/L2/userId → leaf @ L2: "u1" (string) $ openclaw path resolve 'oc://session.jsonl/L2/ts' --file session.jsonl --human leaf @ L2: "2" (number)每行是一条记录。不知道行号时用谓词([event=action])寻址,知道行号时用规范化的LN段。.ndjson文件与.jsonl使用同一适配器。
YAML:
# workflow.yaml name: inbox-triage steps: - id: fetch command: gmail.search - id: classify command: openclaw.invoke$ openclaw path resolve 'oc://workflow.yaml/steps/0/id' --file workflow.yaml --human leaf @ L3: "fetch" (string) $ openclaw path set 'oc://workflow.yaml/steps/$last/id' 'classify-renamed' --file workflow.yaml --dry-run --dry-run: would write 99 bytes to /…/workflow.yaml name: inbox-triage steps: - id: fetch command: gmail.search - id: classify-renamed command: openclaw.invokeYAML 用yaml包的DocumentAPI 而非手写解析器,普通 parse/emit 往返保留注释与编写形状,而解析出的路径与 JSONC 使用同一套 map-key / sequence-index 模型。同一适配器处理.yaml、.yml与.lobster。
子命令参考
resolve <oc-path>
读取单个叶子或节点。通配符被拒绝——这类查询用find。匹配时退出0,干净未命中退出1,解析错误或模式被拒退出2。
openclaw path resolve 'oc://AGENTS.md/tools/gh/risk' --human openclaw path resolve 'oc://gateway.jsonc/server/port' --jsonfind <pattern>
枚举通配符 / 谓词 / 联合模式的每一个匹配。至少一个匹配时退出0,零匹配退出1。文件槽的通配符以OC_PATH_FILE_WILDCARD_UNSUPPORTED拒绝——必须传具体文件(多文件 glob 属于后续功能)。
openclaw path find 'oc://AGENTS.md/tools/**/risk' openclaw path find 'oc://session.jsonl/[event=action]/userId' openclaw path find 'oc://config.jsonc/plugins/{github,slack}/enabled'set <oc-path> <value>
写入一个叶子。配合--dry-run预览将要写入的字节而不碰文件;加--diff得到统一 diff 预览。写入成功退出0,substrate 拒绝(如哨兵守卫命中)退出1,解析错误退出2。
openclaw path set 'oc://gateway.jsonc/version' '2.0' --dry-run openclaw path set 'oc://gateway.jsonc/version' '2.0' --dry-run --diff openclaw path set 'oc://gateway.jsonc/version' '2.0' openclaw path set 'oc://AGENTS.md/Tools/+gh/risk' 'low'+key插入标记会在命名子节点不存在时创建它;+nnn与裸+分别用于索引插入与追加插入。
validate <oc-path>
纯解析检查,不做文件系统访问。适合在替换变量前确认模板路径格式正确,或调试时查看结构拆解:
$ openclaw path validate 'oc://AGENTS.md/tools/gh' --human valid: oc://AGENTS.md/tools/gh file: AGENTS.md section: tools item: gh合法退出0,非法退出1(带结构化code与message),参数错误退出2。
emit <file>
把文件完整走一遍对应类型的解析器与发射器。对健全文件输出应与输入逐字节一致;出现差异说明解析器 bug 或哨兵命中。适合在真实输入上调试 substrate 行为。
openclaw path emit ./AGENTS.md openclaw path emit ./gateway.jsonc --json安全:红action 哨兵守卫
set通过 substrate 的 emit 路径写原始字节,emit 路径自动应用红action 哨兵守卫(extensions/oc-path/src/oc-path/sentinel.ts)。携带__OPENCLAW_REDACTED__(逐字或作为子串出现)的叶子在写入时以OC_EMIT_SENTINEL拒绝——注意是子串匹配而非相等匹配,prefix__OPENCLAW_REDACTED__suffix仍然泄漏标记,所以同样拦截。哨兵检测对所有字符串值生效,包括 markdown frontmatter、条目与标题的插入值;既有的无关标记文本在普通编辑中原样保留。
守卫采用**失败关闭(fail-closed)**策略:与其静默剥离标记(那会悄然损坏文件),不如拒绝写入。guardSentinel在 emit 边界抛出OcEmitSentinelError,让每一条写入路径都被覆盖,而不只是被审计的消费者。CLI 侧(cli.ts的emit/emitError/scrubSentinel)还会在打印任何人类可读或 JSON 输出前把哨兵字面量替换成[REDACTED],终端捕获与管道永远不会泄漏该标记。
与其他插件的关系
memory-*:内存写入走 memory 插件,不经过oc-path。oc-path是通用文件 substrate;memory 插件在其上叠加自己的语义。- LKG:
path不感知 last-known-good 配置恢复。如果你通过path编辑的文件同时被 LKG 跟踪,下一次配置 observe 周期会决定提升还是恢复它——把path编辑视同对该文件的任何其他直接写入。
实用速查:组合语法示例
# 校验一个路径(不做文件系统访问) openclaw path validate 'oc://AGENTS.md/Tools/$last/risk' # 读取一个叶子 openclaw path resolve 'oc://gateway.jsonc/version' # 通配符搜索 openclaw path find 'oc://session.jsonl/*/event' --file ./logs/session.jsonl # 干跑一次写入 openclaw path set 'oc://gateway.jsonc/version' '2.0' --dry-run # 干跑并以统一 diff 预览 openclaw path set 'oc://gateway.jsonc/version' '2.0' --dry-run --diff # 应用写入 openclaw path set 'oc://gateway.jsonc/version' '2.0' # 字节保真往返(诊断) openclaw path emit ./AGENTS.md更完整的语法示例:
# 引号键,容纳包含 / 或 . 的键 openclaw path resolve 'oc://config.jsonc/agents.defaults.models/"anthropic/claude-opus-4-7"/alias' # 深层 JSON/JSONC 路径可用斜杠分段;它们归一化为点分子段 openclaw path set 'oc://openclaw.json/agents/list/0/tools/exec/security' 'allowlist' --dry-run # 把 JSONC 叶子替换为解析后的对象 openclaw path set 'oc://openclaw.json/gateway/auth/token' '{"source":"file","provider":"secrets","id":"/test"}' --value-json --dry-run # 在 JSONC 子节点上做谓词搜索 openclaw path find 'oc://config.jsonc/plugins/[enabled=true]/id' # 插入 JSONC 数组元素 openclaw path set 'oc://config.jsonc/items/+1' '{"id":"new","enabled":true}' --dry-run # 插入 JSONC 对象键 openclaw path set 'oc://config.jsonc/plugins/+github' '{"enabled":true}' --dry-run # 追加 JSONL 事件 openclaw path set 'oc://session.jsonl/+' '{"event":"checkpoint","ok":true}' --file ./logs/session.jsonl # 解析最后一行 JSONL 值 openclaw path resolve 'oc://session.jsonl/$last/event' --file ./logs/session.jsonl # 解析 YAML 工作流步骤 openclaw path resolve 'oc://workflow.yaml/steps/0/id' # 更新 YAML 标量 openclaw path set 'oc://workflow.yaml/steps/$last/id' 'classify-renamed' --dry-run # 寻址 markdown frontmatter openclaw path resolve 'oc://AGENTS.md/[frontmatter]/name' # 插入 markdown frontmatter openclaw path set 'oc://AGENTS.md/[frontmatter]/+description' 'Agent instructions' --dry-run # 查找 markdown 条目字段 openclaw path find 'oc://SKILL.md/Tools/*/send_email' # 校验一个带会话作用域的路径 openclaw path validate 'oc://AGENTS.md/Tools/$last/risk?session=cron-daily'使用边界与注意事项
set在写入或干跑预览前,拒绝包含__OPENCLAW_REDACTED__(逐字或子串)的字符串叶子值。- JSONC 解析与叶子编辑使用插件局部的
jsonc-parser依赖,普通叶子写入保留注释与格式,而不是走手写解析/重渲染路径。 path不感知 LKG 配置跟踪或恢复——该生命周期由其他模块所有。通过path编辑的 LKG 跟踪文件,下一次配置读取决定提升或恢复,视同任何其他直接写入。resolve/set只接受具体路径;通配符、联合、谓词等模式在写入前被拒绝,探索性匹配请用find。
进一步阅读
openclaw pathCLI 参考(完整语法、逐动词标志清单与逐文件类型工作示例)- 管理插件
- 构建插件
- 插件源码与测试:
extensions/oc-path/,其中src/oc-path/tests/scenarios/下的byte-fidelity、sentinel-guard、roundtrip-property等测试用例覆盖了本文所述的关键契约
【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 🦞项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考