Diffs
diffs는 짧은 내장 시스템 안내와 함께, 에이전트용 변경 내용을 읽기 전용 diff 아티팩트로 바꿔 주는 보조 skill을 제공하는 선택적 플러그인 도구입니다.
다음 중 하나를 입력으로 받을 수 있습니다:
before및after텍스트- unified
patch
- 캔버스 표시를 위한 게이트웨이 뷰어 URL
- 메시지 전달용 렌더링 파일 경로(PNG 또는 PDF)
- 한 번의 호출로 두 출력 모두
빠른 시작
- 플러그인을 활성화합니다.
- 캔버스 우선 흐름에는
mode: "view"로diffs를 호출합니다. - 채팅 파일 전달 흐름에는
mode: "file"로diffs를 호출합니다. - 두 아티팩트가 모두 필요하면
mode: "both"로diffs를 호출합니다.
플러그인 활성화
내장 시스템 안내 비활성화
diffs 도구는 활성화한 상태로 유지하되, 내장 system-prompt 안내만 비활성화하려면 plugins.entries.diffs.hooks.allowPromptInjection을 false로 설정합니다:
before_prompt_build hook은 차단되지만, 플러그인, 도구, 보조 skill은 계속 사용할 수 있습니다.
안내와 도구를 모두 비활성화하려면, 대신 플러그인 자체를 비활성화하면 됩니다.
일반적인 에이전트 워크플로
- 에이전트가
diffs를 호출합니다. - 에이전트가
details필드를 읽습니다. - 에이전트는 다음 중 하나를 수행합니다:
canvas present로details.viewerUrl을 엽니다path또는filePath를 사용해message로details.filePath를 전송합니다- 둘 다 수행합니다
입력 예시
Before and after:도구 입력 참조
별도 표기가 없는 한 모든 필드는 선택 사항입니다:before(string): 원본 텍스트.patch가 생략된 경우after와 함께 필수입니다.after(string): 수정된 텍스트.patch가 생략된 경우before와 함께 필수입니다.patch(string): unified diff 텍스트.before,after와 상호 배타적입니다.path(string): before/after 모드에서 표시할 파일명입니다.lang(string): before/after 모드용 언어 override 힌트입니다.title(string): 뷰어 제목 override입니다.mode("view" | "file" | "both"): 출력 모드입니다. 기본값은 플러그인 기본값defaults.mode입니다.theme("light" | "dark"): 뷰어 테마입니다. 기본값은 플러그인 기본값defaults.theme입니다.layout("unified" | "split"): diff 레이아웃입니다. 기본값은 플러그인 기본값defaults.layout입니다.expandUnchanged(boolean): 전체 컨텍스트를 사용할 수 있을 때 변경되지 않은 구간을 펼칩니다. 호출 단위 옵션만 지원합니다(플러그인 기본값 키는 아님).fileFormat("png" | "pdf"): 렌더링 파일 형식입니다. 기본값은 플러그인 기본값defaults.fileFormat입니다.fileQuality("standard" | "hq" | "print"): PNG 또는 PDF 렌더링용 품질 프리셋입니다.fileScale(number): device scale override입니다(1-4).fileMaxWidth(number): CSS 픽셀 기준 최대 렌더링 너비입니다(640-2400).ttlSeconds(number): 뷰어 아티팩트 TTL(초)입니다. 기본값은 1800, 최대는 21600입니다.baseUrl(string): 뷰어 URL origin override입니다.http또는https여야 하며 query/hash는 허용되지 않습니다.
before,after는 각각 최대 512 KiB입니다.patch는 최대 2 MiB입니다.path는 최대 2048바이트입니다.lang는 최대 128바이트입니다.title은 최대 1024바이트입니다.- Patch complexity 상한: 최대 128개 파일, 총 120000줄입니다.
patch와before또는after를 함께 보내면 거부됩니다.- 렌더링 파일 안전 제한(PNG와 PDF 모두 적용):
fileQuality: "standard": 최대 8 MP(8,000,000 렌더링 픽셀)fileQuality: "hq": 최대 14 MP(14,000,000 렌더링 픽셀)fileQuality: "print": 최대 24 MP(24,000,000 렌더링 픽셀)- PDF는 추가로 최대 50페이지 제한이 있습니다.
출력 details 계약
도구는 구조화된 메타데이터를details 아래에 반환합니다.
뷰어를 생성하는 모드에서 공통으로 제공되는 필드:
artifactIdviewerUrlviewerPathtitleexpiresAtinputKindfileCountmode
filePathpath(filePath와 같은 값이며, message 도구와의 호환성을 위해 제공)fileBytesfileFormatfileQualityfileScalefileMaxWidth
mode: "view": 뷰어 필드만 반환합니다.mode: "file": 파일 필드만 반환하며, 뷰어 아티팩트는 없습니다.mode: "both": 뷰어 필드와 파일 필드를 함께 반환합니다. 파일 렌더링이 실패하면 뷰어는 여전히 반환되고fileError가 포함됩니다.
접힌 변경 없음 구간
- 뷰어는
N unmodified lines같은 행을 표시할 수 있습니다. - 그 행의 펼치기 컨트롤은 조건부이며, 모든 입력 종류에서 항상 보장되지는 않습니다.
- 렌더링된 diff에 펼칠 수 있는 컨텍스트 데이터가 있을 때 펼치기 컨트롤이 나타나며, 이는 보통 before/after 입력에서 흔합니다.
- 많은 unified patch 입력에서는 생략된 컨텍스트 본문이 파싱된 patch hunk에 포함되어 있지 않기 때문에, 펼치기 버튼 없이 해당 행만 표시될 수 있습니다. 이는 예상된 동작입니다.
expandUnchanged는 펼칠 수 있는 컨텍스트가 존재할 때만 적용됩니다.
플러그인 기본값
플러그인 전역 기본값은~/.openclaw/openclaw.json에서 설정합니다:
fontFamilyfontSizelineSpacinglayoutshowLineNumbersdiffIndicatorswordWrapbackgroundthemefileFormatfileQualityfileScalefileMaxWidthmode
보안 설정
security.allowRemoteViewer(boolean, 기본값false)false: 뷰어 라우트에 대한 loopback 이외 요청은 거부됩니다.true: 토큰화된 경로가 유효하면 원격 뷰어 접근이 허용됩니다.
아티팩트 수명 주기와 저장소
- 아티팩트는 임시 하위 폴더
$TMPDIR/openclaw-diffs아래에 저장됩니다. - 뷰어 아티팩트 메타데이터에는 다음이 포함됩니다:
- 무작위 아티팩트 ID(20자리 16진수)
- 무작위 토큰(48자리 16진수)
createdAt및expiresAt- 저장된
viewer.html경로
- 지정하지 않으면 기본 뷰어 TTL은 30분입니다.
- 허용되는 최대 뷰어 TTL은 6시간입니다.
- 아티팩트 생성 후 정리는 기회가 있을 때마다 실행됩니다.
- 만료된 아티팩트는 삭제됩니다.
- 메타데이터가 없을 때는 24시간보다 오래된 폴더를 제거하는 fallback 정리가 수행됩니다.
뷰어 URL 및 네트워크 동작
뷰어 라우트:/plugins/diffs/view/{artifactId}/{token}
/plugins/diffs/assets/viewer.js/plugins/diffs/assets/viewer-runtime.js
baseUrl이 제공되면 엄격한 유효성 검사를 거친 뒤 사용됩니다.baseUrl이 없으면 뷰어 URL은 기본적으로 loopback127.0.0.1을 사용합니다.- 게이트웨이 바인드 모드가
custom이고gateway.customBindHost가 설정되어 있으면 해당 호스트가 사용됩니다.
baseUrl 규칙:
http://또는https://여야 합니다.- Query와 hash는 거부됩니다.
- origin과 선택적 base path만 허용됩니다.
보안 모델
뷰어 하드닝:- 기본적으로 loopback 전용입니다.
- 엄격한 ID 및 토큰 검증이 적용된 토큰화 뷰어 경로를 사용합니다.
- 뷰어 응답 CSP:
default-src 'none'- 스크립트와 에셋은 self에서만 허용
- 외부로 나가는
connect-src없음
- 원격 접근이 활성화되어 있을 때 원격 miss throttling 적용:
- 60초당 실패 40회
- 60초 잠금 (
429 Too Many Requests)
- 스크린샷 브라우저 요청 라우팅은 기본 거부(deny-by-default)입니다.
http://127.0.0.1/plugins/diffs/assets/*의 로컬 뷰어 에셋만 허용됩니다.- 외부 네트워크 요청은 차단됩니다.
파일 모드용 브라우저 요구 사항
mode: "file"과 mode: "both"에는 Chromium 호환 브라우저가 필요합니다.
해결 순서:
- OpenClaw config의
browser.executablePath - 환경 변수:
OPENCLAW_BROWSER_EXECUTABLE_PATHBROWSER_EXECUTABLE_PATHPLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
- 플랫폼 명령/경로 탐색 fallback
Diff PNG/PDF rendering requires a Chromium-compatible browser...
문제 해결
입력 유효성 검사 오류:Provide patch or both before and after text.before와after를 모두 포함하거나patch를 제공합니다.
Provide either patch or before/after input, not both.- 입력 모드를 섞지 마십시오.
Invalid baseUrl: ...- query/hash 없이 선택적 path를 포함한
http(s)origin을 사용합니다.
- query/hash 없이 선택적 path를 포함한
{field} exceeds maximum size (...)- payload 크기를 줄이십시오.
- Large patch rejection
- patch 파일 수 또는 총 줄 수를 줄이십시오.
- 뷰어 URL은 기본적으로
127.0.0.1로 해석됩니다. - 원격 접근 시나리오에서는 다음 중 하나를 사용합니다:
- 각 도구 호출마다
baseUrl을 전달하거나 gateway.bind=custom과gateway.customBindHost를 사용합니다
- 각 도구 호출마다
- 외부 뷰어 접근이 실제로 필요할 때만
security.allowRemoteViewer를 활성화하십시오.
- patch 입력에서, patch가 펼칠 수 있는 컨텍스트를 담고 있지 않으면 이런 일이 발생할 수 있습니다.
- 이는 예상된 동작이며 뷰어 실패를 의미하지 않습니다.
- TTL 만료로 인해 아티팩트가 만료되었습니다.
- 토큰 또는 경로가 변경되었습니다.
- 정리 작업이 오래된 데이터를 제거했습니다.
운영 가이드
- 로컬에서 캔버스로 상호작용형 검토를 할 때는
mode: "view"를 우선 사용하십시오. - 첨부 파일이 필요한 외부 채팅 채널에는
mode: "file"을 우선 사용하십시오. - 배포 환경에서 원격 뷰어 URL이 필요한 경우가 아니면
allowRemoteViewer는 비활성화 상태로 유지하십시오. - 민감한 diff에는 명시적으로 짧은
ttlSeconds를 설정하십시오. - 꼭 필요하지 않다면 diff 입력에 비밀 정보를 넣지 마십시오.
- 채널이 이미지를 과하게 압축하는 경우(예: Telegram, WhatsApp)에는 PDF 출력(
fileFormat: "pdf")을 우선 사용하십시오.
- Diffs 기반입니다.