Skip to content

Diffs 差异查看工具

diffs 是一个可选的插件工具,内置简短的系统提示引导,并附带一个技能(skill),能够将变更内容转化为 Agent 可呈现的只读差异制品。

接受以下输入之一:

  • beforeafter 文本
  • unified patch

可返回:

  • 用于画布展示的 Gateway 查看器 URL
  • 用于消息投递的渲染文件路径(PNG 或 PDF)
  • 一次调用同时返回两者

启用后,插件会将简洁的使用说明注入到系统提示空间,同时也暴露一个详细技能,供 Agent 需要完整指导时使用。

快速开始

  1. 启用插件。
  2. 本地画布展示流程:使用 mode: "view" 调用 diffs
  3. 聊天文件投递流程:使用 mode: "file" 调用 diffs
  4. 同时需要两种输出:使用 mode: "both" 调用 diffs

启用插件

json5
{
  plugins: {
    entries: {
      diffs: {
        enabled: true,
      },
    },
  },
}

禁用内置系统提示引导

如果你想保留 diffs 工具但禁用其内置系统提示引导,设置 plugins.entries.diffs.hooks.allowPromptInjectionfalse

json5
{
  plugins: {
    entries: {
      diffs: {
        enabled: true,
        hooks: {
          allowPromptInjection: false,
        },
      },
    },
  },
}

这会屏蔽 diffs 插件的 before_prompt_build 钩子,同时保持插件、工具和配套技能可用。

若要同时禁用引导和工具,则直接禁用插件。

典型 Agent 工作流

  1. Agent 调用 diffs
  2. Agent 读取 details 字段。
  3. Agent 选择:
    • canvas present 打开 details.viewerUrl
    • 通过 pathfilePathmessage 中发送 details.filePath
    • 两者都做

输入示例

before 和 after:

json
{
  "before": "# Hello\n\nOne",
  "after": "# Hello\n\nTwo",
  "path": "docs/example.md",
  "mode": "view"
}

patch:

json
{
  "patch": "diff --git a/src/example.ts b/src/example.ts\n--- a/src/example.ts\n+++ b/src/example.ts\n@@ -1 +1 @@\n-const x = 1;\n+const x = 2;\n",
  "mode": "both"
}

工具输入参数参考

所有字段均为可选,除非另有说明:

  • beforestring):原始文本。省略 patch 时,需与 after 一起提供。
  • afterstring):更新后文本。省略 patch 时,需与 before 一起提供。
  • patchstring):unified diff 文本。与 before/after 互斥。
  • pathstring):before/after 模式下显示的文件名。
  • langstring):before/after 模式下的语言覆盖提示。
  • titlestring):查看器标题覆盖。
  • mode"view" | "file" | "both"):输出模式,默认为插件默认值 defaults.mode。已废弃别名:"image" 行为与 "file" 相同,仍向后兼容。
  • theme"light" | "dark"):查看器主题,默认为插件默认值 defaults.theme
  • layout"unified" | "split"):差异布局,默认为插件默认值 defaults.layout
  • expandUnchangedboolean):在有完整上下文时展开未改动部分。仅限每次调用设置(不是插件默认 key)。
  • fileFormat"png" | "pdf"):渲染文件格式,默认为插件默认值 defaults.fileFormat
  • fileQuality"standard" | "hq" | "print"):PNG 或 PDF 渲染的质量预设。
  • fileScalenumber):设备缩放倍率覆盖(1-4)。
  • fileMaxWidthnumber):最大渲染宽度(CSS 像素,640-2400)。
  • ttlSecondsnumber):查看器制品 TTL(秒),默认 1800,最大 21600。
  • baseUrlstring):查看器 URL origin 覆盖,必须是 httphttps,不含查询参数和 hash。

验证与限制:

  • beforeafter 各最大 512 KiB。
  • patch 最大 2 MiB。
  • path 最大 2048 字节。
  • lang 最大 128 字节。
  • title 最大 1024 字节。
  • Patch 复杂度上限:最多 128 个文件、120000 行总计。
  • patchbefore/after 同时提供时会被拒绝。
  • 渲染文件安全限制(适用于 PNG 和 PDF):
    • fileQuality: "standard":最大 800 万像素(8,000,000)。
    • fileQuality: "hq":最大 1400 万像素(14,000,000)。
    • fileQuality: "print":最大 2400 万像素(24,000,000)。
    • PDF 另有最大 50 页限制。

输出 details 契约

工具在 details 下返回结构化元数据。

创建查看器模式下的公共字段:

  • artifactId
  • viewerUrl
  • viewerPath
  • title
  • expiresAt
  • inputKind
  • fileCount
  • mode
  • contextagentIdsessionIdmessageChannelagentAccountId,有值时返回)

渲染 PNG 或 PDF 时的文件字段:

  • artifactId
  • expiresAt
  • filePath
  • path(值与 filePath 相同,兼容 message 工具)
  • fileBytes
  • fileFormat
  • fileQuality
  • fileScale
  • fileMaxWidth

模式行为摘要:

  • mode: "view":仅查看器字段。
  • mode: "file":仅文件字段,不创建查看器制品。
  • mode: "both":查看器字段加文件字段。若文件渲染失败,查看器仍返回,并附 fileError

未改动行折叠

  • 查看器可以显示"N 行未修改"之类的折叠行。
  • 折叠行的展开控件是条件性的,不保证对所有输入类型都出现。
  • 展开控件在渲染差异有可展开上下文数据时出现,这在 before/after 输入中很常见。
  • 对于许多 unified patch 输入,已省略的上下文内容在解析后的 patch hunk 中不可用,因此该行可能没有展开控件——这是预期行为,不是查看器故障。
  • expandUnchanged 仅在可展开上下文存在时有效。

插件默认值配置

~/.openclaw/openclaw.json 中设置插件级默认值:

json5
{
  plugins: {
    entries: {
      diffs: {
        enabled: true,
        config: {
          defaults: {
            fontFamily: "Fira Code",
            fontSize: 15,
            lineSpacing: 1.6,
            layout: "unified",
            showLineNumbers: true,
            diffIndicators: "bars",
            wordWrap: true,
            background: true,
            theme: "dark",
            fileFormat: "png",
            fileQuality: "standard",
            fileScale: 2,
            fileMaxWidth: 960,
            mode: "both",
          },
        },
      },
    },
  },
}

支持的默认值字段:

  • fontFamily
  • fontSize
  • lineSpacing
  • layout
  • showLineNumbers
  • diffIndicators
  • wordWrap
  • background
  • theme
  • fileFormat
  • fileQuality
  • fileScale
  • fileMaxWidth
  • mode

工具显式参数会覆盖这些默认值。

安全配置

  • security.allowRemoteViewerboolean,默认 false
    • false:拒绝从非回环地址访问查看器路由。
    • true:若 tokenized 路径有效,允许远程访问查看器。

示例:

json5
{
  plugins: {
    entries: {
      diffs: {
        enabled: true,
        config: {
          security: {
            allowRemoteViewer: false,
          },
        },
      },
    },
  },
}

制品生命周期与存储

  • 制品存储在临时子目录:$TMPDIR/openclaw-diffs
  • 查看器制品元数据包含:
    • 随机制品 ID(20 位十六进制)
    • 随机 token(48 位十六进制)
    • createdAtexpiresAt
    • 存储的 viewer.html 路径
  • 未指定时,默认查看器 TTL 为 30 分钟。
  • 最大接受的查看器 TTL 为 6 小时。
  • 清理在制品创建后机会性地运行。
  • 过期制品会被删除。
  • 元数据缺失时,回退清理会删除 24 小时以上的过期文件夹。

查看器 URL 与网络行为

查看器路由:

  • /plugins/diffs/view/{artifactId}/{token}

查看器资源:

  • /plugins/diffs/assets/viewer.js
  • /plugins/diffs/assets/viewer-runtime.js

URL 构建逻辑:

  • 提供 baseUrl 时,严格验证后使用。
  • 未提供 baseUrl 时,查看器 URL 默认使用回环地址 127.0.0.1
  • 若 Gateway 绑定模式为 custom 且设置了 gateway.customBindHost,则使用该主机。

baseUrl 规则:

  • 必须以 http://https:// 开头。
  • 不允许查询参数和 hash。
  • 允许 origin 加可选基础路径。

安全模型

查看器加固措施:

  • 默认仅限回环地址。
  • Tokenized 查看器路径,严格校验 ID 和 token。
  • 查看器响应 CSP:
    • default-src 'none'
    • 脚本和资源仅来自 self
    • 无出站 connect-src
  • 启用远程访问时对远程未命中进行限流:
    • 每 60 秒 40 次失败
    • 60 秒锁定(429 Too Many Requests

文件渲染加固:

  • 截图浏览器请求路由默认拒绝。
  • 仅允许来自 http://127.0.0.1/plugins/diffs/assets/* 的本地查看器资源。
  • 屏蔽外部网络请求。

file 模式的浏览器依赖

mode: "file"mode: "both" 需要 Chromium 兼容浏览器。

解析顺序:

  1. OpenClaw 配置中的 browser.executablePath
  2. 环境变量:
    • OPENCLAW_BROWSER_EXECUTABLE_PATH
    • BROWSER_EXECUTABLE_PATH
    • PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
  3. 平台命令/路径发现回退。

常见失败提示:

  • Diff PNG/PDF rendering requires a Chromium-compatible browser...

解决:安装 Chrome、Chromium、Edge 或 Brave,或设置上述可执行路径选项之一。

故障排查

输入验证错误:

  • Provide patch or both before and after text.
    • 请同时提供 beforeafter,或提供 patch
  • Provide either patch or before/after input, not both.
    • 不要混用两种输入模式。
  • Invalid baseUrl: ...
    • 使用不含查询参数/hash 的 http(s) origin(加可选路径)。
  • {field} exceeds maximum size (...)
    • 减小 payload 大小。
  • Large patch rejection
    • 减少 patch 文件数或总行数。

查看器访问问题:

  • 查看器 URL 默认解析为 127.0.0.1
  • 远程访问场景下,可以:
    • 每次调用时传 baseUrl,或
    • 使用 gateway.bind=customgateway.customBindHost
  • 仅在需要外部查看器访问时才启用 security.allowRemoteViewer

未改动行无展开按钮:

  • patch 输入不包含可展开上下文时会出现此情况。
  • 这是预期行为,不代表查看器故障。

制品未找到:

  • 制品因 TTL 过期。
  • Token 或路径已变更。
  • 清理程序已删除过期数据。

运维建议

  • 本地交互式审查,优先使用 mode: "view"
  • 需要作为附件发送到外部聊天渠道时,优先使用 mode: "file"
  • 除非部署需要远程查看器 URL,否则保持 allowRemoteViewer 禁用。
  • 对敏感差异设置较短的显式 ttlSeconds
  • 非必要时避免在 diff 输入中包含机密信息。
  • 如果你的渠道会大幅压缩图片(如 Telegram 或 WhatsApp),优先使用 PDF 输出(fileFormat: "pdf")。

差异渲染引擎:

  • Diffs 提供支持。

相关文档