Diffs
diffs는 간단한 기본 제공 시스템 가이드와 함께 변경 내용을 에이전트용 읽기 전용 diff 아티팩트로 변환하는 컴패니언 스킬을 갖춘 선택적 플러그인 도구입니다.
다음 중 하나를 입력으로 받습니다:
before와after텍스트- 통합
patch
반환 가능한 출력:
- 캔버스 프레젠테이션을 위한 게이트웨이 뷰어 URL
- 메시지 전달을 위한 렌더링된 파일 경로 (PNG 또는 PDF)
- 한 번의 호출로 두 출력 모두
활성화되면, 플러그인은 시스템 프롬프트에 간결한 사용 가이드를 삽입하고, 에이전트가 더 자세한 지시가 필요한 경우를 위한 상세 스킬도 노출합니다.
빠른 시작
- 플러그인 활성화.
- 캔버스 우선 흐름에는
mode: "view"로diffs호출. - 채팅 파일 전달 흐름에는
mode: "file"로diffs호출. - 두 아티팩트가 모두 필요하면
mode: "both"로diffs호출.
플러그인 활성화
{
plugins: {
entries: {
diffs: {
enabled: true,
},
},
},
}
기본 제공 시스템 가이드 비활성화
diffs 도구는 활성화하되 기본 제공 시스템 프롬프트 가이드를 비활성화하려면, plugins.entries.diffs.hooks.allowPromptInjection을 false로 설정하세요:
{
plugins: {
entries: {
diffs: {
enabled: true,
hooks: {
allowPromptInjection: false,
},
},
},
},
}
이렇게 하면 플러그인, 도구 및 컴패니언 스킬은 유지하면서 diffs 플러그인의 before_prompt_build 후크를 차단합니다.
가이드와 도구를 모두 비활성화하려면 대신 플러그인을 비활성화하세요.
일반적인 에이전트 워크플로
- 에이전트가
diffs를 호출합니다. - 에이전트가
details필드를 읽습니다. - 에이전트가 다음 중 하나를 수행합니다:
details.viewerUrl을canvas present로 열기details.filePath를message에path또는filePath를 사용하여 전송- 두 가지 모두 수행
입력 예시
Before와 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"
}
도구 입력 참조
별도 표시가 없으면 모든 필드는 선택적입니다:
before(string): 원본 텍스트.patch가 생략된 경우after와 함께 필수.after(string): 수정된 텍스트.patch가 생략된 경우before와 함께 필수.patch(string): 통합 diff 텍스트.before/after와 상호 배타적.path(string): before/after 모드의 표시 파일명.lang(string): before/after 모드의 언어 오버라이드 힌트.title(string): 뷰어 제목 오버라이드.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): 장치 스케일 오버라이드 (1-4).fileMaxWidth(number): CSS 픽셀 기준 최대 렌더 너비 (640-2400).ttlSeconds(number): 뷰어 아티팩트 TTL(초). 기본값 1800, 최대 21600.baseUrl(string): 뷰어 URL 출처 오버라이드.http또는https여야 하며, 쿼리/해시 불가.
유효성 검사 및 제한:
before와after는 각각 최대 512 KiB.patch는 최대 2 MiB.path는 최대 2048 바이트.lang은 최대 128 바이트.title은 최대 1024 바이트.- 패치 복잡성 상한: 최대 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
PNG 또는 PDF가 렌더링된 경우의 파일 필드:
filePathpath(filePath와 같은 값, message 도구 호환성용)fileBytesfileFormatfileQualityfileScalefileMaxWidth
모드별 동작 요약:
mode: "view": 뷰어 필드만.mode: "file": 파일 필드만, 뷰어 아티팩트 없음.mode: "both": 뷰어 필드 + 파일 필드. 파일 렌더링이 실패하면 뷰어는fileError와 함께 반환.
축소된 변경되지 않은 섹션
- 뷰어는
N unmodified lines과 같은 행을 표시할 수 있습니다. - 해당 행의 확장 컨트롤은 조건부이며 모든 입력 종류에서 보장되지 않습니다.
- 렌더링된 diff에 확장 가능한 컨텍스트 데이터가 있을 때 확장 컨트롤이 나타나며, before/after 입력에서 일반적입니다.
- 많은 통합 패치 입력에서 생략된 컨텍스트 본문은 파싱된 패치 헝크에서 사용할 수 없으므로, 확장 컨트롤 없이 행이 나타날 수 있습니다. 이는 예상된 동작입니다.
expandUnchanged는 확장 가능한 컨텍스트가 존재할 때만 적용됩니다.
플러그인 기본값
~/.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",
},
},
},
},
},
}
지원되는 기본값:
fontFamilyfontSizelineSpacinglayoutshowLineNumbersdiffIndicatorswordWrapbackgroundthemefileFormatfileQualityfileScalefileMaxWidthmode
명시적 도구 파라미터가 이 기본값을 오버라이드합니다.
보안 설정
security.allowRemoteViewer(boolean, 기본값false)false: 비루프백 요청이 뷰어 라우트에서 거부됩니다.true: 토큰화된 경로가 유효하면 원격 뷰어가 허용됩니다.
예시:
{
plugins: {
entries: {
diffs: {
enabled: true,
config: {
security: {
allowRemoteViewer: false,
},
},
},
},
},
}
아티팩트 생명주기 및 저장소
- 아티팩트는 임시 하위 폴더에 저장됩니다:
$TMPDIR/openclaw-diffs. - 뷰어 아티팩트 메타데이터에 포함되는 것:
- 랜덤 아티팩트 ID (20 hex 문자)
- 랜덤 토큰 (48 hex 문자)
createdAt및expiresAt- 저장된
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로 설정.- 게이트웨이 바인드 모드가
custom이고gateway.customBindHost가 설정된 경우, 해당 호스트가 사용됩니다.
baseUrl 규칙:
http://또는https://여야 합니다.- 쿼리와 해시는 거부됩니다.
- 출처와 선택적 기본 경로가 허용됩니다.
보안 모델
뷰어 보안 강화:
- 기본적으로 루프백 전용.
- 엄격한 ID 및 토큰 검증을 갖춘 토큰화된 뷰어 경로.
- 뷰어 응답 CSP:
default-src 'none'- self에서만 스크립트 및 에셋
- 아웃바운드
connect-src없음
- 원격 접근 활성화 시 원격 미스 스로틀링:
- 60초당 40번 실패
- 60초 잠금 (
429 Too Many Requests)
파일 렌더링 보안 강화:
- 스크린샷 브라우저 요청 라우팅은 기본 거부.
http://127.0.0.1/plugins/diffs/assets/*의 로컬 뷰어 에셋만 허용.- 외부 네트워크 요청은 차단됩니다.
파일 모드의 브라우저 요구 사항
mode: "file" 및 mode: "both"에는 Chromium 호환 브라우저가 필요합니다.
탐색 순서:
- OpenClaw 설정의
browser.executablePath. - 환경 변수:
OPENCLAW_BROWSER_EXECUTABLE_PATHBROWSER_EXECUTABLE_PATHPLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
- 플랫폼 명령/경로 탐색 폴백.
일반적인 오류 텍스트:
Diff PNG/PDF rendering requires a Chromium-compatible browser...
Chrome, Chromium, Edge 또는 Brave를 설치하거나 위의 실행 파일 경로 옵션 중 하나를 설정하여 해결합니다.
문제 해결
입력 유효성 검사 오류:
Provide patch or both before and after text.before와after를 모두 포함하거나patch를 제공하세요.
Provide either patch or before/after input, not both.- 입력 모드를 혼합하지 마세요.
Invalid baseUrl: ...- 선택적 경로가 있는
http(s)출처를 사용하되, 쿼리/해시는 불가.
- 선택적 경로가 있는
{field} exceeds maximum size (...)- 페이로드 크기를 줄이세요.
- 대규모 패치 거부
- 패치 파일 수 또는 총 줄 수를 줄이세요.
뷰어 접근성 문제:
- 뷰어 URL은 기본적으로
127.0.0.1로 해석됩니다. - 원격 접근 시나리오의 경우:
- 도구 호출마다
baseUrl을 전달하거나, gateway.bind=custom및gateway.customBindHost를 사용
- 도구 호출마다
- 외부 뷰어 접근을 의도하는 경우에만
security.allowRemoteViewer를 활성화하세요.
변경되지 않은 줄 행에 확장 버튼이 없는 경우:
- 패치 입력이 확장 가능한 컨텍스트를 포함하지 않는 경우 발생할 수 있습니다.
- 이는 예상된 동작이며 뷰어 오류를 의미하지 않습니다.
아티팩트를 찾을 수 없는 경우:
- TTL에 의해 아티팩트가 만료됨.
- 토큰 또는 경로가 변경됨.
- 정리가 오래된 데이터를 제거함.
운영 가이드
- 캔버스에서의 로컬 대화형 검토에는
mode: "view"를 선호하세요. - 첨부 파일이 필요한 아웃바운드 채팅 채널에는
mode: "file"을 선호하세요. - 배포에 원격 뷰어 URL이 필요한 경우가 아니면
allowRemoteViewer를 비활성화 상태로 유지하세요. - 민감한 diff에는 명시적으로 짧은
ttlSeconds를 설정하세요. - 필요하지 않은 경우 diff 입력에 시크릿을 보내지 마세요.
- 채널이 이미지를 과도하게 압축하는 경우(예: Telegram 또는 WhatsApp), PDF 출력(
fileFormat: "pdf")을 선호하세요.
Diff 렌더링 엔진:
- Diffs에 의해 구동됩니다.