Tools
ส่วนต่าง
diffs เป็นเครื่องมือ Plugin แบบไม่บังคับที่มีคำแนะนำระบบในตัวแบบสั้น และมี Skill คู่กันที่แปลงเนื้อหาการเปลี่ยนแปลงเป็นอาร์ติแฟกต์ diff แบบอ่านอย่างเดียวสำหรับเอเจนต์
รองรับอย่างใดอย่างหนึ่งต่อไปนี้:
- ข้อความ
beforeและafter patchแบบ unified
สามารถส่งคืน:
- URL ตัวแสดงผลของ Gateway สำหรับการนำเสนอแบบแคนวาส
- พาธไฟล์ที่เรนเดอร์แล้ว (PNG หรือ PDF) สำหรับการส่งผ่านข้อความ
- เอาต์พุตทั้งสองแบบในการเรียกครั้งเดียว
เมื่อเปิดใช้งาน Plugin นี้จะเติมคำแนะนำการใช้งานแบบกระชับไว้ในพื้นที่ system-prompt และยังเปิดเผย Skill แบบละเอียดสำหรับกรณีที่เอเจนต์ต้องการคำแนะนำที่ครบถ้วนกว่า
เริ่มต้นอย่างรวดเร็ว
ติดตั้ง Plugin
openclaw plugins install diffsเปิดใช้งาน Plugin
{ plugins: { entries: { diffs: { enabled: true, }, }, },}เลือกโหมด
view
โฟลว์ที่ให้ความสำคัญกับแคนวาสก่อน: เอเจนต์เรียก diffs ด้วย mode: "view" และเปิด details.viewerUrl ด้วย canvas present
file
การส่งไฟล์ผ่านแชต: เอเจนต์เรียก diffs ด้วย mode: "file" และส่ง details.filePath ด้วย message โดยใช้ path หรือ filePath
both
แบบรวม: เอเจนต์เรียก diffs ด้วย mode: "both" เพื่อรับอาร์ติแฟกต์ทั้งสองแบบในการเรียกครั้งเดียว
ปิดใช้งานคำแนะนำระบบในตัว
หากคุณต้องการเปิดใช้เครื่องมือ diffs ต่อไปแต่ปิดใช้งานคำแนะนำ system-prompt ในตัว ให้ตั้งค่า plugins.entries.diffs.hooks.allowPromptInjection เป็น false:
{ plugins: { entries: { diffs: { enabled: true, hooks: { allowPromptInjection: false, }, }, }, },}การตั้งค่านี้จะบล็อกฮุก before_prompt_build ของ Plugin diffs โดยยังคงให้ Plugin เครื่องมือ และ Skill คู่กันพร้อมใช้งาน
หากคุณต้องการปิดใช้งานทั้งคำแนะนำและเครื่องมือ ให้ปิดใช้งาน Plugin แทน
เวิร์กโฟลว์เอเจนต์ทั่วไป
เรียก diffs
เอเจนต์เรียกเครื่องมือ diffs พร้อมอินพุต
อ่านรายละเอียด
เอเจนต์อ่านฟิลด์ details จากการตอบกลับ
นำเสนอ
เอเจนต์เปิด details.viewerUrl ด้วย canvas present หรือส่ง details.filePath ด้วย message โดยใช้ path หรือ filePath หรือทำทั้งสองอย่าง
ตัวอย่างอินพุต
ก่อนและหลัง
{ "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"}เอกสารอ้างอิงอินพุตของเครื่องมือ
ทุกฟิลด์เป็นแบบไม่บังคับ เว้นแต่จะระบุไว้
beforestringข้อความต้นฉบับ จำเป็นต้องใช้ร่วมกับ after เมื่อไม่ระบุ patch
afterstringข้อความที่อัปเดตแล้ว จำเป็นต้องใช้ร่วมกับ before เมื่อไม่ระบุ patch
patchstringข้อความ unified diff ใช้ร่วมกับ before และ after ไม่ได้
pathstringชื่อไฟล์ที่แสดงสำหรับโหมดก่อนและหลัง
langstringคำใบ้สำหรับแทนที่ภาษาในโหมดก่อนและหลัง ค่าที่ไม่รู้จักจะย้อนกลับไปใช้ข้อความธรรมดา
titlestringค่าแทนที่ชื่อของตัวแสดงผล
mode"view" | "file" | "both"โหมดเอาต์พุต ค่าเริ่มต้นเป็นค่าเริ่มต้นของ Plugin defaults.mode นามแฝงที่เลิกแนะนำแล้ว: "image" ทำงานเหมือน "file" และยังคงยอมรับเพื่อความเข้ากันได้ย้อนหลัง
theme"light" | "dark"ธีมของตัวแสดงผล ค่าเริ่มต้นเป็นค่าเริ่มต้นของ Plugin defaults.theme
layout"unified" | "split"เลย์เอาต์ diff ค่าเริ่มต้นเป็นค่าเริ่มต้นของ Plugin defaults.layout
expandUnchangedbooleanขยายส่วนที่ไม่เปลี่ยนแปลงเมื่อมีบริบทเต็มพร้อมใช้งาน เป็นตัวเลือกต่อการเรียกเท่านั้น (ไม่ใช่คีย์ค่าเริ่มต้นของ Plugin)
fileFormat"png" | "pdf"รูปแบบไฟล์ที่เรนเดอร์ ค่าเริ่มต้นเป็นค่าเริ่มต้นของ Plugin defaults.fileFormat
fileQuality"standard" | "hq" | "print"พรีเซ็ตคุณภาพสำหรับการเรนเดอร์ PNG หรือ PDF
fileScalenumberค่าแทนที่สเกลอุปกรณ์ (1-4)
fileMaxWidthnumberความกว้างเรนเดอร์สูงสุดในพิกเซล CSS (640-2400)
ttlSecondsnumberdefault: 1800TTL ของอาร์ติแฟกต์เป็นวินาทีสำหรับเอาต์พุตตัวแสดงผลและไฟล์แบบสแตนด์อโลน สูงสุด 21600
baseUrlstringค่าแทนที่ origin ของ URL ตัวแสดงผล แทนที่ viewerBaseUrl ของ Plugin ต้องเป็น http หรือ https และไม่มี query/hash
นามแฝงอินพุตเดิม
ยังคงยอมรับเพื่อความเข้ากันได้ย้อนหลัง:
format->fileFormatimageFormat->fileFormatimageQuality->fileQualityimageScale->fileScaleimageMaxWidth->fileMaxWidth
การตรวจสอบความถูกต้องและขีดจำกัด
beforeและafterแต่ละรายการสูงสุด 512 KiBpatchสูงสุด 2 MiBpathสูงสุด 2048 ไบต์langสูงสุด 128 ไบต์titleสูงสุด 1024 ไบต์- เพดานความซับซ้อนของ patch: สูงสุด 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
ฟิลด์ตัวแสดงผล
ฟิลด์ร่วมสำหรับโหมดที่สร้างตัวแสดงผล:
artifactIdviewerUrlviewerPathtitleexpiresAtinputKindfileCountmodecontext(agentId,sessionId,messageChannel,agentAccountIdเมื่อมี)
ฟิลด์ไฟล์
ฟิลด์ไฟล์เมื่อมีการเรนเดอร์ PNG หรือ PDF:
artifactIdexpiresAtfilePathpath(ค่าเดียวกับfilePathเพื่อความเข้ากันได้กับเครื่องมือข้อความ)fileBytesfileFormatfileQualityfileScalefileMaxWidth
นามแฝงเพื่อความเข้ากันได้
ส่งคืนด้วยสำหรับผู้เรียกเดิม:
format(ค่าเดียวกับfileFormat)imagePath(ค่าเดียวกับfilePath)imageBytes(ค่าเดียวกับfileBytes)imageQuality(ค่าเดียวกับfileQuality)imageScale(ค่าเดียวกับfileScale)imageMaxWidth(ค่าเดียวกับfileMaxWidth)
สรุปพฤติกรรมของโหมด:
| โหมด | สิ่งที่ส่งคืน |
|---|---|
"view" |
ฟิลด์ตัวแสดงผลเท่านั้น |
"file" |
ฟิลด์ไฟล์เท่านั้น ไม่มีอาร์ติแฟกต์ตัวแสดงผล |
"both" |
ฟิลด์ตัวแสดงผลพร้อมฟิลด์ไฟล์ หากการเรนเดอร์ไฟล์ล้มเหลว ตัวแสดงผลยังคงส่งคืนพร้อม fileError และนามแฝง imageError |
ส่วนที่ไม่เปลี่ยนแปลงซึ่งถูกยุบไว้
- ตัวแสดงผลสามารถแสดงแถวเช่น
N unmodified lines - ตัวควบคุมขยายบนแถวเหล่านั้นมีเงื่อนไข และไม่รับประกันว่าจะมีสำหรับอินพุตทุกชนิด
- ตัวควบคุมขยายจะปรากฏเมื่อ diff ที่เรนเดอร์มีข้อมูลบริบทที่ขยายได้ ซึ่งเป็นเรื่องปกติสำหรับอินพุตก่อนและหลัง
- สำหรับอินพุต unified patch จำนวนมาก เนื้อหาบริบทที่ละไว้จะไม่มีอยู่ใน hunk ของ patch ที่แยกวิเคราะห์แล้ว ดังนั้นแถวอาจปรากฏโดยไม่มีตัวควบคุมขยาย นี่เป็นพฤติกรรมที่คาดไว้
expandUnchangedใช้เฉพาะเมื่อมีบริบทที่ขยายได้อยู่เท่านั้น
ค่าเริ่มต้นของ Plugin
ตั้งค่าเริ่มต้นทั้ง Plugin ใน ~/.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", ttlSeconds: 21600, }, }, }, }, },}ค่าเริ่มต้นที่รองรับ:
fontFamilyfontSizelineSpacinglayoutshowLineNumbersdiffIndicatorswordWrapbackgroundthemefileFormatfileQualityfileScalefileMaxWidthmodettlSeconds
พารามิเตอร์เครื่องมือที่ระบุอย่างชัดเจนจะแทนที่ค่าเริ่มต้นเหล่านี้
การกำหนดค่า URL ตัวแสดงผลแบบถาวร
viewerBaseUrlstringค่าทางเลือกสำรองที่ Plugin เป็นเจ้าของสำหรับลิงก์ตัวแสดงผลที่ส่งคืนเมื่อการเรียกเครื่องมือไม่ส่ง baseUrl ต้องเป็น http หรือ https และไม่มี query/hash
{ plugins: { entries: { diffs: { enabled: true, config: { viewerBaseUrl: "https://gateway.example.com/openclaw", }, }, }, },}การกำหนดค่าความปลอดภัย
security.allowRemoteViewerbooleandefault: falsefalse: คำขอที่ไม่ใช่ loopback ไปยัง route ของตัวแสดงผลจะถูกปฏิเสธ true: อนุญาตตัวแสดงผลระยะไกลหากพาธที่มีโทเค็นถูกต้อง
{ plugins: { entries: { diffs: { enabled: true, config: { security: { allowRemoteViewer: false, }, }, }, }, },}วงจรชีวิตและที่จัดเก็บอาร์ติแฟกต์
- อาร์ติแฟกต์ถูกจัดเก็บไว้ใต้โฟลเดอร์ย่อยชั่วคราว:
$TMPDIR/openclaw-diffs - เมทาดาทาของอาร์ติแฟกต์ตัวแสดงผลประกอบด้วย:
- ID อาร์ติแฟกต์แบบสุ่ม (อักขระ hex 20 ตัว)
- โทเค็นแบบสุ่ม (อักขระ hex 48 ตัว)
createdAtและexpiresAt- พาธ
viewer.htmlที่จัดเก็บไว้
- TTL เริ่มต้นของอาร์ติแฟกต์คือ 30 นาทีเมื่อไม่ได้ระบุ
- TTL ของตัวแสดงผลสูงสุดที่ยอมรับคือ 6 ชั่วโมง
- การล้างข้อมูลทำงานตามโอกาสหลังจากสร้างอาร์ติแฟกต์
- อาร์ติแฟกต์ที่หมดอายุจะถูกลบ
- การล้างข้อมูลสำรองจะลบโฟลเดอร์เก่าที่เก่ากว่า 24 ชั่วโมงเมื่อไม่มีเมทาดาทา
URL ตัวแสดงผลและพฤติกรรมเครือข่าย
Route ของตัวแสดงผล:
/plugins/diffs/view/{artifactId}/{token}
แอสเซ็ตของตัวแสดงผล:
/plugins/diffs/assets/viewer.js/plugins/diffs/assets/viewer-runtime.js
เอกสารตัวแสดงผล resolve แอสเซ็ตเหล่านั้นโดยอิงกับ URL ของตัวแสดงผล ดังนั้น prefix พาธ baseUrl แบบไม่บังคับจะถูกคงไว้สำหรับคำขอแอสเซ็ตทั้งสองรายการด้วย
พฤติกรรมการสร้าง URL:
- หากระบุ
baseUrlในการเรียกเครื่องมือ ระบบจะใช้ค่านั้นหลังจากตรวจสอบอย่างเข้มงวด - มิฉะนั้น หากกำหนดค่า
viewerBaseUrlของ Plugin ไว้ ระบบจะใช้ค่านั้น - หากไม่มีค่าแทนที่ทั้งสองแบบ URL ตัวแสดงผลจะมีค่าเริ่มต้นเป็น loopback
127.0.0.1 - หากโหมด bind ของ Gateway เป็น
customและตั้งค่าgateway.customBindHostไว้ ระบบจะใช้ host นั้น
กฎของ baseUrl:
- ต้องเป็น
http://หรือhttps:// - Query และ hash จะถูกปฏิเสธ
- อนุญาตให้ใช้ origin พร้อมพาธฐานแบบไม่บังคับ
โมเดลความปลอดภัย
การเสริมความปลอดภัยของ Viewer
- จำกัดเฉพาะ loopback โดยค่าเริ่มต้น
- เส้นทาง Viewer แบบมีโทเค็น พร้อมการตรวจสอบ ID และโทเค็นอย่างเข้มงวด
- CSP ของการตอบกลับจาก Viewer:
default-src 'none'- สคริปต์และแอสเซ็ตมาจาก self เท่านั้น
- ไม่มี
connect-srcขาออก
- จำกัดความถี่ของ remote miss เมื่อเปิดใช้การเข้าถึงระยะไกล:
- ล้มเหลว 40 ครั้งต่อ 60 วินาที
- ล็อกเอาต์ 60 วินาที (
429 Too Many Requests)
การเสริมความปลอดภัยของการเรนเดอร์ไฟล์
- การกำหนดเส้นทางคำขอเบราว์เซอร์สำหรับสกรีนช็อตเป็นแบบปฏิเสธโดยค่าเริ่มต้น
- อนุญาตเฉพาะแอสเซ็ต Viewer ภายในเครื่องจาก
http://127.0.0.1/plugins/diffs/assets/* - คำขอเครือข่ายภายนอกถูกบล็อก
ข้อกำหนดของเบราว์เซอร์สำหรับโหมดไฟล์
mode: "file" และ mode: "both" ต้องใช้เบราว์เซอร์ที่เข้ากันได้กับ Chromium
ลำดับการแก้ไข:
Config
browser.executablePath ในการกำหนดค่า OpenClaw
ตัวแปรสภาพแวดล้อม
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หรือระบุpatchProvide either patch or before/after input, not both.— อย่าผสมโหมดอินพุตInvalid baseUrl: ...— ใช้ origin แบบhttp(s)พร้อม path ที่ไม่บังคับได้ ไม่มี query/hash{field} exceeds maximum size (...)— ลดขนาด payload- การปฏิเสธแพตช์ขนาดใหญ่ — ลดจำนวนไฟล์แพตช์หรือจำนวนบรรทัดทั้งหมด
การเข้าถึง Viewer
- URL ของ Viewer จะ resolve เป็น
127.0.0.1โดยค่าเริ่มต้น - สำหรับสถานการณ์การเข้าถึงระยะไกล ให้ทำอย่างใดอย่างหนึ่ง:
- ตั้งค่า Plugin
viewerBaseUrlหรือ - ส่ง
baseUrlต่อการเรียกเครื่องมือแต่ละครั้ง หรือ - ใช้
gateway.bind=customและgateway.customBindHost
- ตั้งค่า Plugin
- หาก
gateway.trustedProxiesมี loopback สำหรับพร็อกซีโฮสต์เดียวกัน (ตัวอย่างเช่น Tailscale Serve) คำขอ Viewer แบบ raw loopback ที่ไม่มีส่วนหัว client-IP ที่ถูกส่งต่อมาจะล้มเหลวแบบ fail closed ตามการออกแบบ - สำหรับ topology ของพร็อกซีนั้น:
- ควรใช้
mode: "file"หรือmode: "both"เมื่อคุณต้องการเพียงไฟล์แนบ หรือ - ตั้งใจเปิดใช้
security.allowRemoteViewerและตั้งค่า PluginviewerBaseUrlหรือส่งbaseUrlของพร็อกซี/สาธารณะ เมื่อคุณต้องการ URL ของ Viewer ที่แชร์ได้
- ควรใช้
- เปิดใช้
security.allowRemoteViewerเฉพาะเมื่อคุณตั้งใจให้เข้าถึง Viewer จากภายนอก
แถวบรรทัดที่ไม่ได้แก้ไขไม่มีปุ่มขยาย
สิ่งนี้อาจเกิดขึ้นกับอินพุตแพตช์เมื่อแพตช์ไม่มีบริบทที่ขยายได้ นี่เป็นพฤติกรรมที่คาดไว้และไม่ได้บ่งชี้ว่า Viewer ล้มเหลว
ไม่พบอาร์ติแฟกต์
- อาร์ติแฟกต์หมดอายุเนื่องจาก TTL
- โทเค็นหรือเส้นทางเปลี่ยนไป
- การล้างข้อมูลนำข้อมูลเก่าออกแล้ว
คำแนะนำการดำเนินงาน
- ควรใช้
mode: "view"สำหรับการรีวิวแบบโต้ตอบภายในเครื่องในแคนวาส - ควรใช้
mode: "file"สำหรับช่องแชตขาออกที่ต้องใช้ไฟล์แนบ - ปิดใช้
allowRemoteViewerไว้ เว้นแต่การปรับใช้ของคุณต้องใช้ URL ของ Viewer ระยะไกล - ตั้งค่า
ttlSecondsแบบสั้นอย่างชัดเจนสำหรับ diff ที่ละเอียดอ่อน - หลีกเลี่ยงการส่งข้อมูลลับในอินพุต diff เมื่อไม่จำเป็น
- หากช่องของคุณบีบอัดรูปภาพอย่างรุนแรง (ตัวอย่างเช่น Telegram หรือ WhatsApp) ควรใช้เอาต์พุต PDF (
fileFormat: "pdf")