Diffs
diffs es una herramienta de plugin opcional con guía breve integrada en el sistema y un skill complementario que convierte el contenido de cambios en un artefacto de diff de solo lectura para agentes.
Acepta:
- texto
beforeyafter - un
patchunificado
Puede devolver:
- una URL del visor del gateway para presentación en canvas
- una ruta de archivo renderizado (PNG o PDF) para entrega en mensajes
- ambas salidas en una sola llamada
Cuando está habilitado, el plugin antepone una guía de uso concisa en el espacio de prompt del sistema y también expone un skill detallado para casos donde el agente necesita instrucciones más completas.
Inicio rápido
- Habilita el plugin.
- Llama a
diffsconmode: "view"para flujos centrados en canvas. - Llama a
diffsconmode: "file"para flujos de entrega de archivos en chat. - Llama a
diffsconmode: "both"cuando necesites ambos artefactos.
Habilitar el plugin
{
plugins: {
entries: {
diffs: {
enabled: true,
},
},
},
}
Deshabilitar la guía integrada del sistema
Si quieres mantener la herramienta diffs habilitada pero deshabilitar su guía integrada del prompt del sistema, establece plugins.entries.diffs.hooks.allowPromptInjection a false:
{
plugins: {
entries: {
diffs: {
enabled: true,
hooks: {
allowPromptInjection: false,
},
},
},
},
}
Esto bloquea el hook before_prompt_build del plugin diffs mientras mantiene disponibles el plugin, la herramienta y el skill complementario.
Si quieres deshabilitar tanto la guía como la herramienta, deshabilita el plugin en su lugar.
Flujo de trabajo típico del agente
- El agente llama a
diffs. - El agente lee los campos
details. - El agente puede:
- abrir
details.viewerUrlconcanvas present - enviar
details.filePathconmessageusandopathofilePath - hacer ambas cosas
- abrir
Ejemplos de entrada
Before y after:
{
"before": "# Hello\n\nOne",
"after": "# Hello\n\nTwo",
"path": "docs/example.md",
"mode": "view"
}
Patch:
{
"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"
}
Referencia de entrada de la herramienta
Todos los campos son opcionales a menos que se indique:
before(string): texto original. Requerido conaftercuando se omitepatch.after(string): texto actualizado. Requerido conbeforecuando se omitepatch.patch(string): texto de diff unificado. Mutuamente excluyente conbeforeyafter.path(string): nombre de archivo para mostrar en modo before y after.lang(string): pista de sobrecarga de lenguaje para modo before y after.title(string): sobrecarga del título del visor.mode("view" | "file" | "both"): modo de salida. Por defecto usa el valor del plugindefaults.mode.theme("light" | "dark"): tema del visor. Por defecto usa el valor del plugindefaults.theme.layout("unified" | "split"): diseño del diff. Por defecto usa el valor del plugindefaults.layout.expandUnchanged(boolean): expandir secciones sin cambios cuando el contexto completo está disponible. Opción solo por llamada (no es una clave por defecto del plugin).fileFormat("png" | "pdf"): formato del archivo renderizado. Por defecto usa el valor del plugindefaults.fileFormat.fileQuality("standard" | "hq" | "print"): preset de calidad para renderizado PNG o PDF.fileScale(number): sobrecarga de escala de dispositivo (1-4).fileMaxWidth(number): ancho máximo de renderizado en píxeles CSS (640-2400).ttlSeconds(number): TTL del artefacto del visor en segundos. Por defecto 1800, máximo 21600.baseUrl(string): sobrecarga del origen de la URL del visor. Debe serhttpohttps, sin query/hash.
Validación y límites:
beforeyaftermáximo 512 KiB cada uno.patchmáximo 2 MiB.pathmáximo 2048 bytes.langmáximo 128 bytes.titlemáximo 1024 bytes.- Límite de complejidad del patch: máximo 128 archivos y 120000 líneas totales.
patchybeforeoafterjuntos se rechazan.- Límites de seguridad del archivo renderizado (aplican a PNG y PDF):
fileQuality: "standard": máximo 8 MP (8,000,000 píxeles renderizados).fileQuality: "hq": máximo 14 MP (14,000,000 píxeles renderizados).fileQuality: "print": máximo 24 MP (24,000,000 píxeles renderizados).- PDF también tiene un máximo de 50 páginas.
Contrato de detalles de salida
La herramienta devuelve metadatos estructurados bajo details.
Campos compartidos para modos que crean un visor:
artifactIdviewerUrlviewerPathtitleexpiresAtinputKindfileCountmode
Campos de archivo cuando se renderiza PNG o PDF:
filePathpath(mismo valor quefilePath, para compatibilidad con la herramienta message)fileBytesfileFormatfileQualityfileScalefileMaxWidth
Resumen de comportamiento por modo:
mode: "view": solo campos del visor.mode: "file": solo campos de archivo, sin artefacto del visor.mode: "both": campos del visor más campos de archivo. Si el renderizado de archivo falla, el visor aún se devuelve confileError.
Secciones sin cambios colapsadas
- El visor puede mostrar filas como
N unmodified lines. - Los controles de expansión en esas filas son condicionales y no están garantizados para cada tipo de entrada.
- Los controles de expansión aparecen cuando el diff renderizado tiene datos de contexto expandibles, lo cual es típico para entrada de before y after.
- Para muchas entradas de patch unificado, los cuerpos de contexto omitidos no están disponibles en los hunks del patch analizado, así que la fila puede aparecer sin controles de expansión. Este es el comportamiento esperado.
expandUnchangedaplica solo cuando existe contexto expandible.
Valores por defecto del plugin
Establece valores por defecto a nivel de plugin en ~/.openclaw/openclaw.json:
{
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",
},
},
},
},
},
}
Valores por defecto soportados:
fontFamilyfontSizelineSpacinglayoutshowLineNumbersdiffIndicatorswordWrapbackgroundthemefileFormatfileQualityfileScalefileMaxWidthmode
Los parámetros explícitos de la herramienta sobrecargan estos valores por defecto.
Configuración de seguridad
security.allowRemoteViewer(boolean, por defectofalse)false: las solicitudes no-loopback a rutas del visor se deniegan.true: los visores remotos se permiten si la ruta tokenizada es válida.
Ejemplo:
{
plugins: {
entries: {
diffs: {
enabled: true,
config: {
security: {
allowRemoteViewer: false,
},
},
},
},
},
}
Ciclo de vida y almacenamiento de artefactos
- Los artefactos se almacenan bajo la subcarpeta temporal:
$TMPDIR/openclaw-diffs. - Los metadatos del artefacto del visor contienen:
- ID de artefacto aleatorio (20 caracteres hex)
- token aleatorio (48 caracteres hex)
createdAtyexpiresAt- ruta almacenada de
viewer.html
- El TTL por defecto del visor es 30 minutos cuando no se especifica.
- El TTL máximo aceptado del visor es 6 horas.
- La limpieza se ejecuta oportunistamente después de la creación de artefactos.
- Los artefactos expirados se eliminan.
- La limpieza de respaldo elimina carpetas obsoletas con más de 24 horas cuando faltan los metadatos.
URL del visor y comportamiento de red
Ruta del visor:
/plugins/diffs/view/{artifactId}/{token}
Assets del visor:
/plugins/diffs/assets/viewer.js/plugins/diffs/assets/viewer-runtime.js
Comportamiento de construcción de URL:
- Si se proporciona
baseUrl, se usa después de una validación estricta. - Sin
baseUrl, la URL del visor usa por defecto loopback127.0.0.1. - Si el modo de vinculación del gateway es
customygateway.customBindHostestá establecido, se usa ese host.
Reglas de baseUrl:
- Debe ser
http://ohttps://. - Se rechazan query y hash.
- Se permite origen más ruta base opcional.
Modelo de seguridad
Endurecimiento del visor:
- Solo loopback por defecto.
- Rutas del visor tokenizadas con validación estricta de ID y token.
- CSP de la respuesta del visor:
default-src 'none'- scripts y assets solo desde self
- sin
connect-srcde salida
- Throttling de errores remotos cuando el acceso remoto está habilitado:
- 40 fallos por 60 segundos
- 60 segundos de bloqueo (
429 Too Many Requests)
Endurecimiento del renderizado de archivos:
- El enrutamiento de solicitudes del navegador para capturas de pantalla es deny-by-default.
- Solo se permiten assets locales del visor desde
http://127.0.0.1/plugins/diffs/assets/*. - Las solicitudes de red externas se bloquean.
Requisitos del navegador para modo file
mode: "file" y mode: "both" necesitan un navegador compatible con Chromium.
Orden de resolución:
browser.executablePathen la configuración de OpenClaw.- Variables de entorno:
OPENCLAW_BROWSER_EXECUTABLE_PATHBROWSER_EXECUTABLE_PATHPLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
- Respaldo de descubrimiento por comando/ruta de la plataforma.
Texto de error común:
Diff PNG/PDF rendering requires a Chromium-compatible browser...
Solución: instala Chrome, Chromium, Edge o Brave, o establece una de las opciones de ruta ejecutable de arriba.
Solución de problemas
Errores de validación de entrada:
Provide patch or both before and after text.- Incluye tanto
beforecomoafter, o proporcionapatch.
- Incluye tanto
Provide either patch or before/after input, not both.- No mezcles modos de entrada.
Invalid baseUrl: ...- Usa origen
http(s)con ruta opcional, sin query/hash.
- Usa origen
{field} exceeds maximum size (...)- Reduce el tamaño de la carga útil.
- Rechazo de patch grande
- Reduce el conteo de archivos del patch o las líneas totales.
Problemas de accesibilidad del visor:
- La URL del visor resuelve a
127.0.0.1por defecto. - Para escenarios de acceso remoto:
- pasa
baseUrlpor llamada a la herramienta, o - usa
gateway.bind=customygateway.customBindHost
- pasa
- Habilita
security.allowRemoteViewersolo cuando pretendas acceso externo al visor.
La fila de líneas sin modificar no tiene botón de expansión:
- Esto puede ocurrir para entrada de patch cuando el patch no lleva contexto expandible.
- Este es el comportamiento esperado y no indica un fallo del visor.
Artefacto no encontrado:
- El artefacto expiró por TTL.
- El token o la ruta cambió.
- La limpieza eliminó datos obsoletos.
Guía operacional
- Prefiere
mode: "view"para revisiones interactivas locales en canvas. - Prefiere
mode: "file"para canales de chat salientes que necesitan un adjunto. - Mantén
allowRemoteViewerdeshabilitado a menos que tu despliegue requiera URLs de visor remotas. - Establece un
ttlSecondscorto explícito para diffs sensibles. - Evita enviar secretos en la entrada de diffs cuando no sea necesario.
- Si tu canal comprime imágenes agresivamente (por ejemplo Telegram o WhatsApp), prefiere salida PDF (
fileFormat: "pdf").
Motor de renderizado de diffs:
- Impulsado por Diffs.