Plugin maintainer reference
รายละเอียดภายในสถาปัตยกรรม Plugin
สำหรับโมเดลความสามารถสาธารณะ รูปแบบ Plugin และสัญญาด้านความเป็นเจ้าของ/การดำเนินการ โปรดดู สถาปัตยกรรม Plugin หน้านี้ครอบคลุมกลไกภายใน ได้แก่ ไปป์ไลน์การโหลด รีจิสทรี ฮุกขณะรัน เส้นทาง HTTP ของ Gateway พาธการนำเข้า และตารางสคีมา
ไปป์ไลน์การโหลด
เมื่อเริ่มต้น OpenClaw จะดำเนินการโดยคร่าว ๆ ดังนี้:
- ค้นหารูทของ Plugin ที่เป็นตัวเลือก
- อ่านไฟล์กำกับบันเดิลแบบเนทีฟหรือแบบที่เข้ากันได้ รวมถึงเมทาดาทาของแพ็กเกจ
- ปฏิเสธตัวเลือกที่ไม่ปลอดภัย
- ปรับการกำหนดค่า Plugin ให้เป็นรูปแบบมาตรฐาน (
plugins.enabled,allow,deny,entries,slots,load.paths) - ตัดสินใจว่าจะเปิดใช้งานตัวเลือกแต่ละรายการหรือไม่
- โหลดโมดูลเนทีฟที่เปิดใช้งาน: โมดูลบันเดิลที่สร้างแล้วใช้ตัวโหลดเนทีฟ ส่วนซอร์ส TypeScript ภายในเครื่องจากบุคคลที่สามใช้ Jiti สำรองสำหรับกรณีฉุกเฉิน
- เรียกฮุกเนทีฟ
register(api)และรวบรวมการลงทะเบียนไว้ในรีจิสทรี Plugin - เปิดให้คำสั่งและพื้นผิวขณะรันเข้าถึงรีจิสทรี
ด่านตรวจสอบความปลอดภัยทำงาน ก่อน การดำเนินการขณะรัน การค้นหาจะบล็อกตัวเลือกเมื่อ:
- จุดเริ่มต้นที่แปลงพาธแล้วออกนอกเหนือรูทของ Plugin
- พาธ (หรือไดเรกทอรีรูท) เปิดให้ผู้ใช้ทุกคนเขียนได้
- สำหรับ Plugin ที่ไม่ได้รวมมาให้ ความเป็นเจ้าของพาธไม่ตรงกับ uid ปัจจุบัน (หรือ root)
สำหรับไดเรกทอรีที่รวมมาให้และเปิดให้ผู้ใช้ทุกคนเขียนได้ ระบบจะพยายามซ่อมแซมด้วย chmod ในตำแหน่งเดิมก่อน
(การติดตั้ง npm/แบบโกลบอลอาจส่งมอบไดเรกทอรีแพ็กเกจด้วยสิทธิ์ 0777) แล้วจึงตรวจสอบด่านอีกครั้ง;
ระบบจะข้ามการตรวจสอบความเป็นเจ้าของทั้งหมดสำหรับแหล่งที่มาซึ่งรวมมาให้
ตัวเลือกที่ถูกบล็อกยังคงมีรหัส Plugin อยู่ในการวินิจฉัยที่ส่งออกเมื่อทราบรหัสดังกล่าว (รวมถึงรหัสที่อ่านได้จากไฟล์กำกับภายในไดเรกทอรีที่ถูกปฏิเสธด้วยเหตุผลอื่น) ดังนั้นการกำหนดค่าที่อ้างถึงรหัสดังกล่าวจะเห็น Plugin ที่ถูกบล็อกพร้อมคำเตือนด้านความปลอดภัยของพาธ แทนข้อผิดพลาด "ไม่รู้จัก Plugin" ที่ไม่เกี่ยวข้อง
ลักษณะการทำงานที่ให้ไฟล์กำกับมาก่อน
ไฟล์กำกับเป็นแหล่งข้อมูลจริงของระนาบควบคุม OpenClaw ใช้ไฟล์นี้เพื่อ:
- ระบุ Plugin
- ค้นหาช่องทาง/Skills/สคีมาการกำหนดค่าหรือความสามารถของบันเดิลที่ประกาศไว้
- ตรวจสอบความถูกต้องของ
plugins.entries.<id>.config - เพิ่มป้ายกำกับ/ข้อความตัวอย่างของ Control UI
- แสดงเมทาดาทาการติดตั้ง/แค็ตตาล็อก
- เก็บตัวบรรยายการเปิดใช้งานและการตั้งค่าที่ประมวลผลได้อย่างประหยัดโดยไม่ต้องโหลดส่วนขณะรันของ Plugin
สำหรับ Plugin เนทีฟ โมดูลขณะรันคือส่วนระนาบข้อมูล โมดูลนี้ลงทะเบียนลักษณะการทำงานจริง เช่น ฮุก เครื่องมือ คำสั่ง หรือโฟลว์ของผู้ให้บริการ
บล็อก activation และ setup ที่เป็นตัวเลือกในไฟล์กำกับจะยังคงอยู่ในระนาบควบคุม
บล็อกเหล่านี้เป็นเพียงตัวบรรยายเมทาดาทาสำหรับการวางแผนเปิดใช้งานและการค้นหาการตั้งค่า;
ไม่ได้แทนที่การลงทะเบียนขณะรัน register(...) หรือ setupEntry
ผู้ใช้ข้อมูลการเปิดใช้งานแบบสดจะใช้คำใบ้เกี่ยวกับคำสั่ง ช่องทาง และผู้ให้บริการจากไฟล์กำกับ
เพื่อจำกัดการโหลด Plugin ให้แคบลงก่อนสร้างรีจิสทรีในขอบเขตที่กว้างขึ้น:
- การโหลด CLI จำกัดเฉพาะ Plugin ที่เป็นเจ้าของคำสั่งหลักที่ร้องขอ
- การตั้งค่าช่องทาง/การแก้หา Plugin จำกัดเฉพาะ Plugin ที่เป็นเจ้าของรหัสช่องทางที่ร้องขอ
- การตั้งค่า/การแก้หาขณะรันของผู้ให้บริการแบบระบุชัดเจน จำกัดเฉพาะ Plugin ที่เป็นเจ้าของรหัสผู้ให้บริการที่ร้องขอ
- การวางแผนเริ่มต้น Gateway ใช้
activation.onStartupสำหรับการนำเข้าเมื่อเริ่มต้นที่ระบุชัดเจน; Plugin ที่ไม่มีเมทาดาทาการเริ่มต้นจะโหลดผ่านทริกเกอร์การเปิดใช้งานที่มีขอบเขตแคบกว่าเท่านั้น
ตัววางแผนการเปิดใช้งานมีทั้ง API ที่ส่งคืนเฉพาะรหัสสำหรับผู้เรียกเดิม
และ API แผนสำหรับการวินิจฉัย รายการในแผนรายงานเหตุผลที่เลือก Plugin
โดยแยกคำใบ้ activation.* ที่ระบุชัดเจนออกจากการสำรองตามความเป็นเจ้าของในไฟล์กำกับ:
เหตุผล (จากคำใบ้ activation.*) |
เหตุผล (จากความเป็นเจ้าของในไฟล์กำกับ) |
|---|---|
activation-agent-harness-hint |
— |
activation-capability-hint |
— |
activation-channel-hint |
manifest-channel-owner (channels) |
activation-command-hint |
manifest-command-alias (commandAliases) |
activation-provider-hint |
manifest-provider-owner (providers), manifest-setup-provider-owner (setup.providers) |
activation-route-hint |
— |
| — (ทริกเกอร์ฮุกไม่มีรูปแบบคำใบ้) | manifest-hook-owner (hooks), manifest-tool-contract (contracts.tools) |
การแยกเหตุผลนี้คือขอบเขตความเข้ากันได้: เมทาดาทา Plugin ที่มีอยู่ยังคงทำงานต่อไป ขณะที่โค้ดใหม่สามารถตรวจพบคำใบ้แบบกว้างหรือลักษณะการทำงานสำรอง โดยไม่เปลี่ยนความหมายของการโหลดขณะรัน
การโหลดล่วงหน้าขณะรันในช่วงประมวลผลคำขอที่ขอขอบเขตแบบกว้าง all
ยังคงสร้างชุดรหัส Plugin ที่มีผลแบบระบุชัดเจนจากการกำหนดค่า การวางแผนเริ่มต้น
ช่องทางที่กำหนดค่าไว้ สล็อต และกฎการเปิดใช้งานอัตโนมัติ
(resolveEffectivePluginIds ใน src/plugins/effective-plugin-ids.ts) หากชุดที่สร้างขึ้นนั้นว่างเปล่า
OpenClaw จะคงขอบเขตให้ว่างไว้แทนการขยายไปยัง Plugin ทุกตัวที่ค้นพบได้
การค้นหาการตั้งค่าจะให้ความสำคัญกับรหัสที่ตัวบรรยายเป็นเจ้าของ เช่น setup.providers และ
setup.cliBackends เพื่อจำกัด Plugin ที่เป็นตัวเลือก ก่อนสำรองไปใช้
setup-api สำหรับ Plugin ที่ยังต้องใช้ฮุกขณะรันในช่วงตั้งค่า รายการตั้งค่าผู้ให้บริการ
ใช้ providerAuthChoices จากไฟล์กำกับ ตัวเลือกการตั้งค่าที่ได้จากตัวบรรยาย
และเมทาดาทาแค็ตตาล็อกการติดตั้งโดยไม่โหลดส่วนขณะรันของผู้ให้บริการ
การระบุ setup.requiresRuntime: false อย่างชัดเจนเป็นจุดตัดที่ใช้เฉพาะตัวบรรยาย;
การไม่ระบุ requiresRuntime จะคงการสำรองไปใช้ setup-api แบบเดิมไว้เพื่อความเข้ากันได้
หาก Plugin ที่ค้นพบมากกว่าหนึ่งตัวอ้างสิทธิ์ในรหัสผู้ให้บริการตั้งค่าหรือแบ็กเอนด์ CLI
ที่ปรับเป็นรูปแบบมาตรฐานเดียวกัน การค้นหาการตั้งค่าจะปฏิเสธเจ้าของที่กำกวม
แทนการอาศัยลำดับการค้นพบ เมื่อส่วนขณะรันของการตั้งค่าทำงานจริง
การวินิจฉัยของรีจิสทรีจะรายงานความคลาดเคลื่อนระหว่าง setup.providers / setup.cliBackends
กับผู้ให้บริการหรือแบ็กเอนด์ CLI ที่ setup-api ลงทะเบียนจริง โดยไม่บล็อก Plugin แบบเดิม
ขอบเขตแคชของ Plugin
OpenClaw ไม่แคชผลการค้นหา Plugin หรือข้อมูลรีจิสทรีจากไฟล์กำกับโดยตรง ภายใต้ช่วงเวลาตามนาฬิกา การติดตั้ง การแก้ไขไฟล์กำกับ และการเปลี่ยนแปลงพาธการโหลด ต้องมองเห็นได้ในการอ่านเมทาดาทาอย่างชัดเจนหรือการสร้างสแนปช็อตครั้งถัดไป ตัวแยกวิเคราะห์ไฟล์กำกับจะเก็บแคชลายเซ็นไฟล์แบบมีขอบเขต โดยใช้พาธไฟล์กำกับที่เปิด ร่วมกับอุปกรณ์/inode ขนาด และ mtime/ctime เป็นคีย์; แคชดังกล่าวมีไว้เพียงเพื่อหลีกเลี่ยง การแยกวิเคราะห์ไบต์เดิมที่ไม่เปลี่ยนแปลงซ้ำ และต้องไม่แคชคำตอบเกี่ยวกับการค้นหา รีจิสทรี เจ้าของ หรือนโยบาย
เส้นทางด่วนของเมทาดาทาที่ปลอดภัยคือความเป็นเจ้าของออบเจ็กต์อย่างชัดเจน ไม่ใช่แคชที่ซ่อนอยู่
เส้นทางที่ใช้งานหนักระหว่างการเริ่มต้น Gateway ควรส่ง PluginMetadataSnapshot ปัจจุบัน
PluginLookUpTable ที่สร้างขึ้น หรือรีจิสทรีไฟล์กำกับที่ระบุชัดเจนไปตามสายการเรียก
การตรวจสอบความถูกต้องของการกำหนดค่า การเปิดใช้งานอัตโนมัติเมื่อเริ่มต้น
การบูตสแตรป Plugin และการเลือกผู้ให้บริการสามารถใช้ออบเจ็กต์เหล่านี้ซ้ำได้
ตราบใดที่ออบเจ็กต์ยังแทนการกำหนดค่าและรายการ Plugin ปัจจุบัน
การค้นหาการตั้งค่ายังคงสร้างเมทาดาทาไฟล์กำกับใหม่ตามต้องการ
เว้นแต่เส้นทางการตั้งค่านั้นได้รับรีจิสทรีไฟล์กำกับอย่างชัดเจน;
ให้คงลักษณะนี้เป็นการสำรองสำหรับเส้นทางที่เรียกใช้น้อย แทนการเพิ่มแคชการค้นหาที่ซ่อนอยู่
เมื่ออินพุตเปลี่ยน ให้สร้างสแนปช็อตใหม่และแทนที่ของเดิม แทนการแก้ไขออบเจ็กต์เดิม
หรือเก็บสำเนาในอดีต มุมมองของรีจิสทรี Plugin ที่ใช้งานอยู่และตัวช่วยบูตสแตรปช่องทาง
ที่รวมมาให้ควรคำนวณใหม่จากรีจิสทรี/รูทปัจจุบัน แผนที่อายุสั้นใช้ได้ภายในหนึ่งการเรียก
เพื่อขจัดงานซ้ำหรือป้องกันการกลับเข้าซ้ำ แต่ต้องไม่กลายเป็นแคชเมทาดาทาระดับโพรเซส
สำหรับการโหลด Plugin ชั้นแคชแบบคงอยู่คือการโหลดขณะรัน โดยอาจใช้สถานะตัวโหลดซ้ำ เมื่อมีการโหลดโค้ดหรืออาร์ติแฟกต์ที่ติดตั้งจริง เช่น:
PluginLoaderCacheStateและรีจิสทรีขณะรันที่ใช้งานอยู่ซึ่งเข้ากันได้- แคช jiti/โมดูล และแคชตัวโหลดพื้นผิวสาธารณะที่ใช้เพื่อหลีกเลี่ยงการนำเข้าพื้นผิวขณะรันเดียวกันซ้ำ
- แคชระบบไฟล์สำหรับอาร์ติแฟกต์ Plugin ที่ติดตั้ง
- แผนที่อายุสั้นต่อการเรียกสำหรับการปรับพาธเป็นรูปแบบมาตรฐานหรือการแก้รายการซ้ำ
แคชเหล่านั้นเป็นรายละเอียดการทำงานของระนาบข้อมูล และต้องไม่ตอบคำถามของระนาบควบคุม เช่น "Plugin ใดเป็นเจ้าของผู้ให้บริการนี้?" เว้นแต่ผู้เรียกจะขอให้โหลดขณะรันโดยเจตนา
อย่าเพิ่มแคชแบบคงอยู่หรือแคชตามช่วงเวลาสำหรับ:
- ผลการค้นหา
- รีจิสทรีไฟล์กำกับโดยตรง
- รีจิสทรีไฟล์กำกับที่สร้างใหม่จากดัชนี Plugin ที่ติดตั้ง
- การค้นหาเจ้าของผู้ให้บริการ การระงับโมเดล นโยบายผู้ให้บริการ หรือเมทาดาทาอาร์ติแฟกต์สาธารณะ
- คำตอบอื่นใดที่ได้จากไฟล์กำกับ ซึ่งไฟล์กำกับ ดัชนีที่ติดตั้ง หรือพาธการโหลดที่เปลี่ยนแปลง ควรมองเห็นได้ในการอ่านเมทาดาทาครั้งถัดไป
ผู้เรียกที่สร้างเมทาดาทาไฟล์กำกับใหม่จากดัชนี Plugin ที่ติดตั้งและเก็บไว้อย่างถาวร จะสร้างรีจิสทรีดังกล่าวใหม่ตามต้องการ ดัชนีที่ติดตั้งคือสถานะระนาบแหล่งข้อมูลที่คงทน; ไม่ใช่แคชเมทาดาทาในโพรเซสที่ซ่อนอยู่
โมเดลรีจิสทรี
Plugin ที่โหลดแล้วจะไม่แก้ไขตัวแปรโกลบอลของแกนหลักแบบสุ่มโดยตรง
แต่จะลงทะเบียนเข้าสู่รีจิสทรี Plugin ส่วนกลาง (PluginRegistry ใน src/plugins/registry-types.ts)
ซึ่งติดตามระเบียน Plugin (อัตลักษณ์ แหล่งที่มา ต้นทาง สถานะ การวินิจฉัย)
รวมถึงอาร์เรย์สำหรับความสามารถทุกประเภท ได้แก่ เครื่องมือ ฮุกแบบเดิมและฮุกแบบมีชนิด
ช่องทาง ผู้ให้บริการ ตัวจัดการ RPC ของ Gateway เส้นทาง HTTP ตัวลงทะเบียน CLI
บริการเบื้องหลัง คำสั่งที่ Plugin เป็นเจ้าของ และกลุ่มผู้ให้บริการแบบมีชนิดอีกหลายสิบประเภท
(เสียงพูด เอ็มเบดดิง การสร้างรูปภาพ/วิดีโอ/เพลง การดึงข้อมูล/ค้นหาเว็บ
ชุดควบคุมเอเจนต์ การดำเนินการกับเซสชัน และอื่น ๆ)
จากนั้นฟีเจอร์แกนหลักจะอ่านจากรีจิสทรีดังกล่าวแทนการติดต่อโมดูล Plugin โดยตรง ทำให้การโหลดเป็นแบบทิศทางเดียว:
- โมดูล Plugin -> การลงทะเบียนรีจิสทรี
- ส่วนขณะรันแกนหลัก -> การใช้รีจิสทรี
การแยกส่วนนี้สำคัญต่อการบำรุงรักษา เพราะพื้นผิวแกนหลักส่วนใหญ่ต้องการจุดเชื่อมต่อเพียงจุดเดียว: "อ่านรีจิสทรี" ไม่ใช่ "จัดการโมดูล Plugin ทุกตัวเป็นกรณีพิเศษ"
คอลแบ็กการผูกการสนทนา
Plugin ที่ผูกการสนทนาสามารถตอบสนองเมื่อการอนุมัติได้รับการตัดสินแล้ว
ใช้ api.onConversationBindingResolved(...) เพื่อรับคอลแบ็กหลังคำขอผูกได้รับการอนุมัติหรือปฏิเสธ:
export default { id: "my-plugin", register(api) { api.onConversationBindingResolved(async (event) => { if (event.status === "approved") { // A binding now exists for this plugin + conversation. console.log(event.binding?.conversationId); return; } // The request was denied; clear any local pending state. console.log(event.request.conversation.conversationId); }); },};ฟิลด์เพย์โหลดของคอลแบ็ก:
status:"approved"หรือ"denied"decision:"allow-once","allow-always"หรือ"deny"binding: การผูกที่ได้รับการตัดสินแล้วสำหรับคำขอที่อนุมัติrequest: สรุปคำขอเดิม คำใบ้การยกเลิกการเชื่อมโยง รหัสผู้ส่ง และเมทาดาทาการสนทนา
คอลแบ็กนี้ใช้เพื่อแจ้งเตือนเท่านั้น ไม่ได้เปลี่ยนว่าใครได้รับอนุญาตให้ผูกการสนทนา และจะทำงานหลังการประมวลผลการอนุมัติของแกนหลักเสร็จสิ้น
ฮุกขณะรันของผู้ให้บริการ
Plugin ผู้ให้บริการมีสามชั้น:
- เมทาดาทาไฟล์กำกับ สำหรับการค้นหาก่อนรันที่ประหยัด:
setup.providers[].envVars,providerAuthEnvVarsสำหรับความเข้ากันได้ซึ่งเลิกแนะนำแล้ว,providerAuthAliases,providerAuthChoicesและchannelEnvVars - ฮุกช่วงกำหนดค่า:
catalog(discoveryแบบเดิม) รวมถึงapplyConfigDefaults - ฮุกขณะรัน: ฮุกตัวเลือกมากกว่า 40 รายการ ครอบคลุมการยืนยันตัวตน การแก้หาโมเดล การห่อสตรีม ระดับการคิด นโยบายการเล่นซ้ำ และปลายทางข้อมูลการใช้งาน โปรดดู ลำดับและการใช้งานฮุก
OpenClaw ยังคงเป็นเจ้าของลูปเอเจนต์ทั่วไป การสลับไปใช้ระบบสำรอง การจัดการบทสนทนาที่บันทึกไว้ และนโยบายเครื่องมือ ฮุกเหล่านี้เป็นพื้นผิวส่วนขยายสำหรับลักษณะการทำงานเฉพาะผู้ให้บริการ โดยไม่ต้องมีระบบขนส่งการอนุมานแบบกำหนดเองทั้งชุด
ใช้ setup.providers[].envVars ในไฟล์ manifest เมื่อผู้ให้บริการมีข้อมูลรับรองที่อิงตัวแปรสภาพแวดล้อม ซึ่งเส้นทางทั่วไปสำหรับการยืนยันตัวตน/สถานะ/ตัวเลือกโมเดลควรมองเห็นได้โดยไม่ต้องโหลดรันไทม์ของ Plugin โดยอะแดปเตอร์ความเข้ากันได้ยังคงอ่าน providerAuthEnvVars ที่เลิกใช้แล้วในช่วงเวลาการเลิกใช้ และ Plugin ที่ไม่ได้รวมมาในชุดซึ่งใช้งานฟิลด์นี้จะได้รับการวินิจฉัย manifest ใช้ providerAuthAliases ในไฟล์ manifest เมื่อรหัสผู้ให้บริการหนึ่งควรใช้ตัวแปรสภาพแวดล้อม โปรไฟล์การยืนยันตัวตน การยืนยันตัวตนที่อิงการกำหนดค่า และตัวเลือกการเริ่มต้นใช้งานด้วยคีย์ API ของรหัสผู้ให้บริการอีกรหัสหนึ่งร่วมกัน ใช้ providerAuthChoices ในไฟล์ manifest เมื่อพื้นผิว CLI สำหรับการเริ่มต้นใช้งาน/การเลือกวิธียืนยันตัวตนควรทราบรหัสตัวเลือก ป้ายกำกับกลุ่ม และการเชื่อมโยงการยืนยันตัวตนแบบง่ายด้วยแฟล็กเดียวของผู้ให้บริการ โดยไม่ต้องโหลดรันไทม์ของผู้ให้บริการ เก็บ envVars ของรันไทม์ผู้ให้บริการไว้สำหรับคำแนะนำที่แสดงต่อผู้ปฏิบัติงาน เช่น ป้ายกำกับการเริ่มต้นใช้งาน หรือตัวแปรสำหรับตั้งค่า OAuth client-id/client-secret
ใช้ channelEnvVars ในไฟล์ manifest เมื่อช่องทางมีการยืนยันตัวตนหรือการตั้งค่าที่ขับเคลื่อนด้วยตัวแปรสภาพแวดล้อม ซึ่งกลไกสำรองทั่วไปจากตัวแปรสภาพแวดล้อมของเชลล์ การตรวจสอบการกำหนดค่า/สถานะ หรือพรอมต์การตั้งค่าควรมองเห็นได้โดยไม่ต้องโหลดรันไทม์ของช่องทาง
ลำดับและการใช้งาน Hook
สำหรับ Plugin ของโมเดล/ผู้ให้บริการ OpenClaw จะเรียก Hook ตามลำดับคร่าว ๆ ดังนี้
คอลัมน์ "ควรใช้เมื่อใด" เป็นคู่มือสำหรับตัดสินใจอย่างรวดเร็ว
ฟิลด์ของผู้ให้บริการที่มีไว้เพื่อความเข้ากันได้เท่านั้นและ OpenClaw ไม่ได้เรียกใช้อีกต่อไป เช่น ProviderPlugin.capabilities และ suppressBuiltInModel จะไม่แสดงไว้ที่นี่โดยเจตนา
| Hook | ทำหน้าที่อะไร | ควรใช้เมื่อ |
|---|---|---|
catalog |
เผยแพร่การกำหนดค่าผู้ให้บริการไปยัง models.providers ระหว่างการสร้าง models.json |
ผู้ให้บริการเป็นเจ้าของแค็ตตาล็อกหรือค่าเริ่มต้นของ URL ฐาน |
applyConfigDefaults |
ใช้ค่าเริ่มต้นการกำหนดค่าส่วนกลางที่ผู้ให้บริการเป็นเจ้าของระหว่างการสร้างการกำหนดค่าให้สมบูรณ์ | ค่าเริ่มต้นขึ้นอยู่กับโหมดการยืนยันตัวตน สภาพแวดล้อม หรือความหมายของตระกูลโมเดลของผู้ให้บริการ |
| (การค้นหาโมเดลในตัว) | OpenClaw ลองใช้เส้นทางรีจิสทรี/แค็ตตาล็อกปกติก่อน | (ไม่ใช่ฮุกของ Plugin) |
normalizeModelId |
ปรับนามแฝงรหัสโมเดลแบบเก่าหรือรุ่นตัวอย่างให้เป็นมาตรฐานก่อนค้นหา | ผู้ให้บริการเป็นเจ้าของการล้างนามแฝงก่อนการแปลงโมเดลเป็นรูปแบบมาตรฐาน |
normalizeTransport |
ปรับ api / baseUrl ของตระกูลผู้ให้บริการให้เป็นมาตรฐานก่อนประกอบโมเดลทั่วไป |
ผู้ให้บริการเป็นเจ้าของการล้างการขนส่งสำหรับรหัสผู้ให้บริการแบบกำหนดเองในตระกูลการขนส่งเดียวกัน |
normalizeConfig |
ปรับ models.providers.<id> ให้เป็นมาตรฐานก่อนการแปลงค่ารันไทม์/ผู้ให้บริการ |
ผู้ให้บริการต้องการล้างการกำหนดค่าที่ควรอยู่กับ Plugin; ตัวช่วยตระกูล Google ที่รวมมาในตัวช่วยรองรับรายการกำหนดค่า Google ที่รองรับด้วย |
applyNativeStreamingUsageCompat |
ใช้การปรับแก้ความเข้ากันได้ของข้อมูลการใช้งานแบบสตรีมมิงเนทีฟกับผู้ให้บริการในการกำหนดค่า | ผู้ให้บริการต้องการแก้ไขข้อมูลเมตาการใช้งานแบบสตรีมมิงเนทีฟตามเอนด์พอยต์ |
resolveConfigApiKey |
แปลงการยืนยันตัวตนด้วยเครื่องหมายสภาพแวดล้อมสำหรับผู้ให้บริการในการกำหนดค่าก่อนโหลดการยืนยันตัวตนของรันไทม์ | ผู้ให้บริการเปิดเผยฮุกของตนเองสำหรับแปลงคีย์ API จากเครื่องหมายสภาพแวดล้อม |
resolveSyntheticAuth |
เปิดเผยการยืนยันตัวตนแบบภายในเครื่อง/โฮสต์เองหรือแบบอิงการกำหนดค่าโดยไม่จัดเก็บข้อความธรรมดา | ผู้ให้บริการสามารถทำงานด้วยเครื่องหมายข้อมูลประจำตัวสังเคราะห์/ภายในเครื่อง |
resolveExternalAuthProfiles |
ซ้อนทับโปรไฟล์การยืนยันตัวตนภายนอกที่ผู้ให้บริการเป็นเจ้าของ; ค่าเริ่มต้นของ persistence คือ runtime-only สำหรับข้อมูลประจำตัวที่ CLI/แอปเป็นเจ้าของ |
ผู้ให้บริการนำข้อมูลประจำตัวการยืนยันตัวตนภายนอกมาใช้ซ้ำโดยไม่จัดเก็บโทเค็นรีเฟรชที่คัดลอกมา; ประกาศ contracts.externalAuthProviders ในแมนิเฟสต์ |
shouldDeferSyntheticProfileAuth |
ลดลำดับความสำคัญของตัวแทนโปรไฟล์สังเคราะห์ที่จัดเก็บไว้ให้อยู่หลังการยืนยันตัวตนที่อิงสภาพแวดล้อม/การกำหนดค่า | ผู้ให้บริการจัดเก็บโปรไฟล์ตัวแทนสังเคราะห์ที่ไม่ควรมีลำดับความสำคัญเหนือกว่า |
resolveDynamicModel |
ทางเลือกสำรองแบบซิงก์สำหรับรหัสโมเดลที่ผู้ให้บริการเป็นเจ้าของซึ่งยังไม่มีในรีจิสทรีภายในเครื่อง | ผู้ให้บริการยอมรับรหัสโมเดลต้นทางใดก็ได้ |
prepareDynamicModel |
เตรียมพร้อมแบบอะซิงก์ แล้วเรียก resolveDynamicModel อีกครั้ง |
ผู้ให้บริการต้องการข้อมูลเมตาจากเครือข่ายก่อนแปลงรหัสที่ไม่รู้จัก |
normalizeResolvedModel |
ปรับแก้ครั้งสุดท้ายก่อนตัวรันที่ฝังอยู่ใช้โมเดลที่แปลงค่าแล้ว | ผู้ให้บริการต้องการปรับแก้การขนส่งแต่ยังคงใช้การขนส่งหลัก |
normalizeToolSchemas |
ปรับสคีมาของเครื่องมือให้เป็นมาตรฐานก่อนที่ตัวรันแบบฝังตัวจะเห็น | ผู้ให้บริการต้องการล้างสคีมาของตระกูลการขนส่ง |
inspectToolSchemas |
แสดงการวินิจฉัยสคีมาที่ผู้ให้บริการเป็นเจ้าของหลังการปรับให้เป็นมาตรฐาน | ผู้ให้บริการต้องการคำเตือนเกี่ยวกับคีย์เวิร์ดโดยไม่ต้องสอนกฎเฉพาะผู้ให้บริการให้แกนหลัก |
resolveReasoningOutputMode |
เลือกสัญญาเอาต์พุตการให้เหตุผลแบบเนทีฟหรือแบบมีแท็ก | ผู้ให้บริการต้องการเอาต์พุตการให้เหตุผล/เอาต์พุตสุดท้ายแบบมีแท็กแทนฟิลด์เนทีฟ |
prepareExtraParams |
ปรับพารามิเตอร์คำขอให้เป็นมาตรฐานก่อนตัวห่อหุ้มตัวเลือกสตรีมทั่วไป | ผู้ให้บริการต้องการพารามิเตอร์คำขอเริ่มต้นหรือการล้างพารามิเตอร์รายผู้ให้บริการ |
createStreamFn |
แทนที่เส้นทางสตรีมปกติทั้งหมดด้วยการขนส่งแบบกำหนดเอง | ผู้ให้บริการต้องการโพรโทคอลระดับสายแบบกำหนดเอง ไม่ใช่เพียงตัวห่อหุ้ม |
wrapStreamFn |
ตัวห่อหุ้มสตรีมหลังจากใช้ตัวห่อหุ้มทั่วไปแล้ว | ผู้ให้บริการต้องการตัวห่อหุ้มความเข้ากันได้ของส่วนหัวคำขอ/เนื้อหา/โมเดลโดยไม่ใช้การขนส่งแบบกำหนดเอง |
resolveTransportTurnState |
แนบส่วนหัวหรือข้อมูลเมตาการขนส่งเนทีฟประจำแต่ละรอบ | ผู้ให้บริการต้องการให้การขนส่งทั่วไปส่งข้อมูลระบุรอบแบบเนทีฟของผู้ให้บริการ |
resolveWebSocketSessionPolicy |
แนบส่วนหัว WebSocket เนทีฟหรือนโยบายพักเซสชัน | ผู้ให้บริการต้องการให้การขนส่ง WS ทั่วไปปรับแต่งส่วนหัวเซสชันหรือนโยบายทางเลือกสำรอง |
formatApiKey |
ตัวจัดรูปแบบโปรไฟล์การยืนยันตัวตน: โปรไฟล์ที่จัดเก็บไว้จะกลายเป็นสตริง apiKey ของรันไทม์ |
ผู้ให้บริการจัดเก็บข้อมูลเมตาการยืนยันตัวตนเพิ่มเติมและต้องการรูปแบบโทเค็นรันไทม์แบบกำหนดเอง |
refreshOAuth |
แทนที่การรีเฟรช OAuth สำหรับเอนด์พอยต์รีเฟรชแบบกำหนดเองหรือนโยบายเมื่อรีเฟรชล้มเหลว | ผู้ให้บริการไม่สอดคล้องกับตัวรีเฟรชร่วมของ OpenClaw |
buildAuthDoctorHint |
คำแนะนำการซ่อมแซมที่เพิ่มต่อท้ายเมื่อการรีเฟรช OAuth ล้มเหลว | ผู้ให้บริการต้องการคำแนะนำการซ่อมแซมการยืนยันตัวตนที่ผู้ให้บริการเป็นเจ้าของหลังการรีเฟรชล้มเหลว |
matchesContextOverflowError |
ตัวตรวจจับบัฟเฟอร์ล้นของหน้าต่างบริบทที่ผู้ให้บริการเป็นเจ้าของ | ผู้ให้บริการมีข้อผิดพลาดบัฟเฟอร์ล้นดิบที่ฮิวริสติกทั่วไปอาจตรวจไม่พบ |
classifyFailoverReason |
การจำแนกเหตุผลของการสลับระบบสำรองที่ผู้ให้บริการเป็นเจ้าของ | ผู้ให้บริการสามารถจับคู่ข้อผิดพลาด API/การขนส่งดิบกับการจำกัดอัตรา/โหลดเกิน/อื่น ๆ |
isCacheTtlEligible |
นโยบายแคชพรอมป์สำหรับผู้ให้บริการพร็อกซี/แบ็กฮอล | ผู้ให้บริการต้องการควบคุมสิทธิ์ TTL ของแคชเฉพาะพร็อกซี |
buildMissingAuthMessage |
ข้อความทดแทนสำหรับการกู้คืนกรณีขาดการยืนยันตัวตนแบบทั่วไป | ผู้ให้บริการต้องการคำแนะนำการกู้คืนกรณีขาดการยืนยันตัวตนเฉพาะผู้ให้บริการ |
augmentModelCatalog |
แถวแค็ตตาล็อกสังเคราะห์/สุดท้ายที่เพิ่มหลังการค้นพบ (เลิกใช้แล้ว โปรดดูด้านล่าง) | ผู้ให้บริการต้องการแถวสังเคราะห์สำหรับความเข้ากันได้ในอนาคตใน models list และตัวเลือก |
resolveThinkingProfile |
ชุดระดับ /think เฉพาะโมเดล ป้ายกำกับที่แสดง และค่าเริ่มต้น |
ผู้ให้บริการเปิดเผยลำดับขั้นการคิดแบบกำหนดเองหรือป้ายกำกับแบบไบนารีสำหรับโมเดลที่เลือก |
isBinaryThinking |
ฮุกความเข้ากันได้สำหรับสลับเปิด/ปิดการให้เหตุผล | ผู้ให้บริการเปิดเผยเพียงการเปิด/ปิดการคิดแบบไบนารี |
supportsXHighThinking |
ฮุกความเข้ากันได้สำหรับการรองรับการให้เหตุผล xhigh |
ผู้ให้บริการต้องการใช้ xhigh เฉพาะกับโมเดลบางส่วน |
resolveDefaultThinkingLevel |
ฮุกความเข้ากันได้สำหรับระดับ /think เริ่มต้น |
ผู้ให้บริการเป็นเจ้าของนโยบาย /think เริ่มต้นสำหรับตระกูลโมเดล |
isModernModelRef |
ตัวตรวจจับโมเดลสมัยใหม่สำหรับตัวกรองโปรไฟล์แบบสดและการเลือกทดสอบเบื้องต้น | ผู้ให้บริการเป็นเจ้าของการจับคู่โมเดลที่ต้องการสำหรับการทดสอบแบบสด/เบื้องต้น |
prepareRuntimeAuth |
แลกเปลี่ยนข้อมูลประจำตัวที่กำหนดค่าไว้เป็นโทเค็น/คีย์รันไทม์จริงก่อนการอนุมาน | ผู้ให้บริการต้องแลกเปลี่ยนโทเค็นหรือใช้ข้อมูลประจำตัวคำขอที่มีอายุสั้น |
resolveUsageAuth |
แปลงข้อมูลประจำตัวการใช้งาน/การเรียกเก็บเงินสำหรับ /usage และพื้นผิวสถานะที่เกี่ยวข้อง |
ผู้ให้บริการต้องการการแยกวิเคราะห์โทเค็นการใช้งาน/โควตาแบบกำหนดเองหรือข้อมูลประจำตัวการใช้งานประเภทอื่น |
fetchUsageSnapshot |
ดึงและปรับสแนปช็อตการใช้งาน/โควตาเฉพาะผู้ให้บริการให้เป็นมาตรฐานหลังแปลงการยืนยันตัวตนแล้ว | ผู้ให้บริการต้องการเอนด์พอยต์การใช้งานหรือตัวแยกวิเคราะห์เพย์โหลดเฉพาะผู้ให้บริการ |
createEmbeddingProvider |
สร้างอะแดปเตอร์การฝังเวกเตอร์ที่ผู้ให้บริการเป็นเจ้าของสำหรับหน่วยความจำ/การค้นหา | พฤติกรรมการฝังเวกเตอร์ของหน่วยความจำเป็นความรับผิดชอบของ Plugin ผู้ให้บริการ |
buildReplayPolicy |
ส่งคืนนโยบายการเล่นซ้ำที่ควบคุมการจัดการทรานสคริปต์สำหรับผู้ให้บริการ | ผู้ให้บริการต้องใช้นโยบายทรานสคริปต์แบบกำหนดเอง (เช่น การตัดบล็อกการคิดออก) |
sanitizeReplayHistory |
เขียนประวัติการเล่นซ้ำใหม่หลังการล้างทรานสคริปต์ทั่วไป | ผู้ให้บริการต้องเขียนข้อมูลการเล่นซ้ำใหม่ในแบบเฉพาะผู้ให้บริการ ซึ่งเกินขอบเขตของตัวช่วย Compaction ที่ใช้ร่วมกัน |
validateReplayTurns |
ตรวจสอบความถูกต้องหรือปรับรูปแบบเทิร์นการเล่นซ้ำขั้นสุดท้ายก่อนส่งไปยังตัวรันแบบฝังตัว | การขนส่งของผู้ให้บริการต้องตรวจสอบความถูกต้องของเทิร์นอย่างเข้มงวดยิ่งขึ้นหลังการล้างข้อมูลทั่วไป |
onModelSelected |
เรียกใช้ผลข้างเคียงหลังการเลือกที่ผู้ให้บริการเป็นเจ้าของ | ผู้ให้บริการต้องใช้ข้อมูลการวัดและติดตาม หรือสถานะที่ผู้ให้บริการเป็นเจ้าของเมื่อโมเดลเริ่มทำงาน |
normalizeModelId, normalizeTransport และ normalizeConfig จะตรวจสอบ Plugin ของผู้ให้บริการที่ตรงกันก่อน จากนั้นจึงไล่ตรวจสอบ Plugin ของผู้ให้บริการอื่นที่รองรับฮุก จนกว่าจะพบ Plugin ที่เปลี่ยนรหัสโมเดลหรือการขนส่ง/การกำหนดค่าจริง วิธีนี้ช่วยให้ชิมของผู้ให้บริการสำหรับนามแฝง/ความเข้ากันได้ยังคงทำงาน โดยผู้เรียกไม่จำเป็นต้องทราบว่า Plugin ที่รวมมาในชุดใดเป็นเจ้าของการเขียนค่าใหม่ หากไม่มีฮุกของผู้ให้บริการใดเขียนรายการการกำหนดค่าตระกูล Google ที่รองรับใหม่ ตัวปรับการกำหนดค่า Google ที่รวมมาในชุดจะยังคงดำเนินการล้างค่าด้านความเข้ากันได้นั้น
หากผู้ให้บริการต้องใช้โพรโทคอลระดับสายสัญญาณแบบกำหนดเองทั้งหมดหรือตัวดำเนินการคำขอแบบกำหนดเอง กรณีนั้นถือเป็นส่วนขยายคนละประเภท ฮุกเหล่านี้มีไว้สำหรับพฤติกรรมของผู้ให้บริการที่ยังคงทำงานบนลูปการอนุมานปกติของ OpenClaw
resolveUsageAuth ตัดสินใจว่า OpenClaw ควรเรียก fetchUsageSnapshot หรือย้อนกลับไปใช้การแก้ไขข้อมูลประจำตัวแบบทั่วไปสำหรับพื้นผิวการใช้งาน/สถานะ ให้คืนค่า { token, accountId?, subscriptionType?, rateLimitTier? } เมื่อผู้ให้บริการมีข้อมูลประจำตัวสำหรับการใช้งาน (ข้อมูลเมตาของแผนที่ไม่บังคับจะถูกส่งต่อไปยัง fetchUsageSnapshot) คืนค่า { handled: true } เมื่อการตรวจสอบสิทธิ์การใช้งานที่ผู้ให้บริการเป็นเจ้าของได้จัดการคำขอแล้วและต้องระงับการย้อนกลับไปใช้คีย์ API/OAuth แบบทั่วไป และคืนค่า null หรือ undefined เมื่อผู้ให้บริการไม่ได้จัดการการตรวจสอบสิทธิ์การใช้งาน
ประกาศข้อมูลประจำตัวขององค์กรหรือการเรียกเก็บเงินใน providerUsageAuthEnvVars ของไฟล์กำกับ วิธีนี้ช่วยให้พื้นผิวการค้นพบทั่วไปและการล้างข้อมูลลับสามารถจดจำข้อมูลเหล่านี้ได้ โดยไม่ทำให้ข้อมูลเหล่านี้กลายเป็นตัวเลือกสำหรับการตรวจสอบสิทธิ์การอนุมาน
ตัวอย่างผู้ให้บริการ
api.registerProvider({ id: "example-proxy", label: "Example Proxy", auth: [], catalog: { order: "simple", run: async (ctx) => { const apiKey = ctx.resolveProviderApiKey("example-proxy").apiKey; if (!apiKey) { return null; } return { provider: { baseUrl: "https://proxy.example.com/v1", apiKey, api: "openai-completions", models: [{ id: "auto", name: "Auto" }], }, }; }, }, resolveDynamicModel: (ctx) => ({ id: ctx.modelId, name: ctx.modelId, provider: "example-proxy", api: "openai-completions", baseUrl: "https://proxy.example.com/v1", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128000, maxTokens: 8192, }), prepareRuntimeAuth: async (ctx) => { const exchanged = await exchangeToken(ctx.apiKey); return { apiKey: exchanged.token, baseUrl: exchanged.baseUrl, expiresAt: exchanged.expiresAt, }; }, resolveUsageAuth: async (ctx) => { const auth = await ctx.resolveOAuthToken(); return auth ? { token: auth.token } : null; }, fetchUsageSnapshot: async (ctx) => { return await fetchExampleProxyUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn); },});ตัวอย่างที่มีมาให้ในตัว
Plugin ของผู้ให้บริการที่รวมมาในชุดจะผสานฮุกข้างต้นให้เหมาะกับแค็ตตาล็อก การตรวจสอบสิทธิ์ การคิด การเล่นซ้ำ และความต้องการด้านการใช้งานของผู้จำหน่ายแต่ละราย ชุดฮุกที่เป็นแหล่งอ้างอิงหลักอยู่กับแต่ละ Plugin ภายใต้ extensions/ หน้านี้แสดงรูปแบบต่าง ๆ แทนการทำสำเนารายการดังกล่าว
ผู้ให้บริการแค็ตตาล็อกแบบส่งผ่าน
OpenRouter, Kilocode, Z.AI และ xAI ลงทะเบียน catalog ร่วมกับ
resolveDynamicModel / prepareDynamicModel เพื่อให้สามารถแสดงรหัสโมเดล
ต้นทางก่อนแค็ตตาล็อกแบบคงที่ของ OpenClaw
ผู้ให้บริการปลายทาง OAuth และการใช้งาน
GitHub Copilot, Gemini CLI, ChatGPT Codex, MiniMax, Xiaomi และ z.ai จับคู่
prepareRuntimeAuth หรือ formatApiKey กับ resolveUsageAuth +
fetchUsageSnapshot เพื่อเป็นเจ้าของการแลกเปลี่ยนโทเค็นและการผสานรวม /usage
ตระกูลการเล่นซ้ำและการล้างบทสนทนา
ตระกูลที่ใช้ชื่อร่วมกัน (google-gemini, passthrough-gemini,
anthropic-by-model, hybrid-anthropic-openai) ช่วยให้ผู้ให้บริการเลือกใช้
นโยบายบทสนทนาผ่าน buildReplayPolicy แทนที่แต่ละ Plugin
จะนำกระบวนการล้างไปสร้างใหม่เอง
ผู้ให้บริการเฉพาะแค็ตตาล็อก
byteplus, cloudflare-ai-gateway, huggingface, kimi-coding, nvidia,
qianfan, synthetic, together, venice, vercel-ai-gateway และ
volcengine ลงทะเบียนเฉพาะ catalog และใช้ลูปการอนุมานที่ใช้ร่วมกัน
ตัวช่วยสตรีมเฉพาะ Anthropic
ส่วนหัวเบตา, /fast / serviceTier และ context1m อยู่ภายในรอยต่อ
api.ts / contract-api.ts สาธารณะของ Plugin Anthropic
(wrapAnthropicProviderStream, resolveAnthropicBetas,
resolveAnthropicFastMode, resolveAnthropicServiceTier) แทนที่จะอยู่ใน
SDK แบบทั่วไป
ตัวช่วยรันไทม์
Plugin สามารถเข้าถึงตัวช่วยหลักที่เลือกไว้ผ่าน api.runtime สำหรับ TTS:
const clip = await api.runtime.tts.textToSpeech({ text: "Hello from OpenClaw", cfg: api.config,}); const result = await api.runtime.tts.textToSpeechTelephony({ text: "Hello from OpenClaw", cfg: api.config,}); const voices = await api.runtime.tts.listVoices({ provider: "elevenlabs", cfg: api.config,});หมายเหตุ:
textToSpeechคืนเพย์โหลดเอาต์พุต TTS หลักตามปกติสำหรับพื้นผิวไฟล์/ข้อความเสียง- ใช้การกำหนดค่า
messages.ttsหลักและการเลือกผู้ให้บริการ - คืนบัฟเฟอร์เสียง PCM พร้อมอัตราการสุ่มตัวอย่าง Plugin ต้องปรับอัตราการสุ่มตัวอย่าง/เข้ารหัสให้เหมาะกับผู้ให้บริการ
listVoicesเป็นตัวเลือกที่ไม่บังคับสำหรับผู้ให้บริการแต่ละราย ใช้สำหรับตัวเลือกเสียงหรือขั้นตอนการตั้งค่าที่ผู้จำหน่ายเป็นเจ้าของ- ส่วนหลักจะส่งกำหนดเวลาสิ้นสุดคำขอที่แก้ไขแล้วไปยังฮุก
listVoicesของผู้ให้บริการ การตั้งค่าระยะหมดเวลาเฉพาะผู้ให้บริการอาจเขียนทับค่านี้ - รายการเสียงสามารถมีข้อมูลเมตาที่ละเอียดขึ้น เช่น โลแคล เพศ และแท็กบุคลิกภาพ สำหรับตัวเลือกที่รับรู้ผู้ให้บริการ
- ปัจจุบัน OpenAI และ ElevenLabs รองรับโทรศัพท์ แต่ Microsoft ไม่รองรับ
Plugin ยังสามารถลงทะเบียนผู้ให้บริการเสียงพูดผ่าน api.registerSpeechProvider(...) ได้ด้วย
api.registerSpeechProvider({ id: "acme-speech", label: "Acme Speech", isConfigured: ({ config }) => Boolean(config.messages?.tts), synthesize: async (req) => { return { audioBuffer: Buffer.from([]), outputFormat: "mp3", fileExtension: ".mp3", voiceCompatible: false, }; },});หมายเหตุ:
- เก็บนโยบาย TTS การย้อนกลับ และการส่งคำตอบไว้ในส่วนหลัก
- ใช้ผู้ให้บริการเสียงพูดสำหรับพฤติกรรมการสังเคราะห์ที่ผู้จำหน่ายเป็นเจ้าของ
- อินพุต Microsoft แบบเดิม
edgeจะถูกปรับให้เป็นรหัสผู้ให้บริการmicrosoft - รูปแบบความเป็นเจ้าของที่แนะนำเน้นตามบริษัท: Plugin ของผู้จำหน่ายหนึ่งรายสามารถเป็นเจ้าของผู้ให้บริการข้อความ เสียงพูด รูปภาพ และสื่อในอนาคต เมื่อ OpenClaw เพิ่มสัญญาความสามารถเหล่านั้น
สำหรับการทำความเข้าใจรูปภาพ/เสียง/วิดีโอ Plugin จะลงทะเบียนผู้ให้บริการการทำความเข้าใจสื่อแบบมีชนิดหนึ่งราย แทนการใช้ชุดคีย์/ค่าแบบทั่วไป:
api.registerMediaUnderstandingProvider({ id: "google", capabilities: ["image", "audio", "video"], describeImage: async (req) => ({ text: "..." }), transcribeAudio: async (req) => ({ text: "..." }), describeVideo: async (req) => ({ text: "..." }),});หมายเหตุ:
- เก็บการประสานงาน การย้อนกลับ การกำหนดค่า และการเชื่อมต่อช่องทางไว้ในส่วนหลัก
- เก็บพฤติกรรมของผู้จำหน่ายไว้ใน Plugin ของผู้ให้บริการ
- การขยายแบบเพิ่มเติมควรคงการกำหนดชนิดไว้: เมธอดที่ไม่บังคับใหม่ ฟิลด์ผลลัพธ์ที่ไม่บังคับใหม่ และความสามารถที่ไม่บังคับใหม่
- การสร้างวิดีโอใช้รูปแบบเดียวกันอยู่แล้ว:
- ส่วนหลักเป็นเจ้าของสัญญาความสามารถและตัวช่วยรันไทม์
- Plugin ของผู้จำหน่ายลงทะเบียน
api.registerVideoGenerationProvider(...) - Plugin ของฟีเจอร์/ช่องทางใช้
api.runtime.videoGeneration.*
สำหรับตัวช่วยรันไทม์การทำความเข้าใจสื่อ Plugin สามารถเรียกใช้:
const image = await api.runtime.mediaUnderstanding.describeImageFile({ filePath: "/tmp/inbound-photo.jpg", cfg: api.config, agentDir: "/tmp/agent",}); const video = await api.runtime.mediaUnderstanding.describeVideoFile({ filePath: "/tmp/inbound-video.mp4", cfg: api.config,}); const extraction = await api.runtime.mediaUnderstanding.extractStructuredWithModel({ provider: "codex", model: "gpt-5.6-sol", input: [ { type: "image", buffer: receiptImageBuffer, fileName: "receipt.png", mime: "image/png", }, { type: "text", text: "Use the printed fields as the source of truth." }, ], instructions: "Return entities and searchable tags.", schemaName: "example.evidence", jsonSchema: { type: "object", properties: { entities: { type: "array", items: { type: "string" } }, tags: { type: "array", items: { type: "string" } }, }, }, cfg: api.config,});สำหรับการถอดเสียง Plugin สามารถใช้รันไทม์การทำความเข้าใจสื่อหรือนามแฝง STT รุ่นเก่าก็ได้:
const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({ filePath: "/tmp/inbound-audio.ogg", cfg: api.config, // Optional when MIME cannot be inferred reliably: mime: "audio/ogg",});หมายเหตุ:
api.runtime.mediaUnderstanding.*เป็นพื้นผิวร่วมที่แนะนำสำหรับการทำความเข้าใจรูปภาพ/เสียง/วิดีโอextractStructuredWithModel(...)เป็นรอยต่อสำหรับ Plugin เพื่อใช้ในการแยกข้อมูลแบบมีขอบเขตที่เน้นรูปภาพเป็นหลักและผู้ให้บริการเป็นเจ้าของ ต้องมีอินพุตรูปภาพอย่างน้อยหนึ่งรายการ ส่วนอินพุตข้อความเป็นบริบทเสริม Plugin ของผลิตภัณฑ์เป็นเจ้าของเส้นทางและสคีมา ขณะที่ OpenClaw เป็นเจ้าของขอบเขตผู้ให้บริการ/รันไทม์- ใช้การกำหนดค่าเสียงสำหรับการทำความเข้าใจสื่อของส่วนหลัก (
tools.media.audio) และลำดับการย้อนกลับของผู้ให้บริการ - คืนค่า
{ text: undefined }เมื่อไม่มีเอาต์พุตการถอดเสียงเกิดขึ้น (เช่น อินพุตถูกข้าม/ไม่รองรับ) api.runtime.stt.transcribeAudioFile(...)ยังคงอยู่ในฐานะนามแฝงเพื่อความเข้ากันได้
Plugin ยังสามารถเริ่มการทำงานของเอเจนต์ย่อยเบื้องหลังผ่าน api.runtime.subagent ได้ด้วย:
const result = await api.runtime.subagent.run({ sessionKey: "agent:main:subagent:search-helper", message: "Expand this query into focused follow-up searches.", provider: "openai", model: "gpt-4.1-mini", deliver: false,});หมายเหตุ:
providerและmodelเป็นค่าที่เขียนทับเฉพาะการทำงานแต่ละครั้งซึ่งไม่บังคับ ไม่ใช่การเปลี่ยนแปลงเซสชันแบบถาวร- OpenClaw ยอมรับฟิลด์เขียนทับเหล่านั้นเฉพาะจากผู้เรียกที่เชื่อถือได้
- สำหรับการทำงานแบบย้อนกลับที่ Plugin เป็นเจ้าของ ผู้ดำเนินการต้องเลือกเข้าร่วมด้วย
plugins.entries.<id>.subagent.allowModelOverride: true - ใช้
plugins.entries.<id>.subagent.allowedModelsเพื่อจำกัด Plugin ที่เชื่อถือได้ให้ใช้เป้าหมายprovider/modelมาตรฐานที่กำหนด หรือใช้"*"เพื่ออนุญาตทุกเป้าหมายอย่างชัดเจน - การทำงานของเอเจนต์ย่อยจาก Plugin ที่ไม่น่าเชื่อถือยังคงทำงานได้ แต่คำขอเขียนทับจะถูกปฏิเสธแทนการย้อนกลับโดยไม่แจ้ง
- เซสชันเอเจนต์ย่อยที่ Plugin สร้างขึ้นจะถูกติดแท็กด้วยรหัส Plugin ที่สร้างเซสชันนั้น
api.runtime.subagent.deleteSession(...)แบบย้อนกลับสามารถลบได้เฉพาะเซสชันที่เป็นเจ้าของเหล่านั้น การลบเซสชันใด ๆ ยังคงต้องใช้คำขอ Gateway ที่มีขอบเขตผู้ดูแลระบบ
สำหรับการค้นหาเว็บ Plugin สามารถใช้ตัวช่วยรันไทม์ร่วมแทนการเข้าถึงการเชื่อมต่อเครื่องมือเอเจนต์โดยตรง:
const providers = api.runtime.webSearch.listProviders({ config: api.config,}); const result = await api.runtime.webSearch.search({ config: api.config, args: { query: "OpenClaw plugin runtime helpers", count: 5, },});Plugin ยังสามารถลงทะเบียนผู้ให้บริการค้นหาเว็บผ่าน api.registerWebSearchProvider(...) ได้ด้วย
หมายเหตุ:
- เก็บการเลือกผู้ให้บริการ การแก้ไขข้อมูลประจำตัว และความหมายของคำขอร่วมไว้ในส่วนหลัก
- ใช้ผู้ให้บริการค้นหาเว็บสำหรับการขนส่งการค้นหาเฉพาะผู้จำหน่าย
api.runtime.webSearch.*เป็นพื้นผิวร่วมที่แนะนำสำหรับ Plugin ของฟีเจอร์/ช่องทางที่ต้องใช้พฤติกรรมการค้นหาโดยไม่ขึ้นกับตัวห่อเครื่องมือเอเจนต์
api.runtime.imageGeneration
const result = await api.runtime.imageGeneration.generate({ config: api.config, args: { prompt: "A friendly lobster mascot", size: "1024x1024" },}); const providers = api.runtime.imageGeneration.listProviders({ config: api.config,});generate(...): สร้างรูปภาพโดยใช้สายโซ่ผู้ให้บริการสร้างรูปภาพที่กำหนดค่าไว้listProviders(...): แสดงรายการผู้ให้บริการสร้างรูปภาพที่พร้อมใช้งานและความสามารถของแต่ละราย
เส้นทาง HTTP ของ Gateway
Plugin สามารถเปิดเผยปลายทาง HTTP ด้วย api.registerHttpRoute(...)
api.registerHttpRoute({ path: "/acme/webhook", auth: "plugin", match: "exact", handler: async (_req, res) => { res.statusCode = 200; res.end("ok"); return true; },});ฟิลด์ของเส้นทาง:
path: พาธของเส้นทางภายใต้เซิร์ฟเวอร์ HTTP ของ Gatewayauth: จำเป็นต้องระบุ โดยใช้"gateway"หรือ"plugin"ใช้"gateway"เพื่อบังคับใช้การยืนยันตัวตนตามปกติของ Gateway หรือใช้"plugin"สำหรับการยืนยันตัวตนหรือการตรวจสอบ Webhook ที่ Plugin จัดการmatch: ไม่บังคับ ใช้"exact"(ค่าเริ่มต้น) หรือ"prefix"handleUpgrade: ตัวจัดการที่ไม่บังคับสำหรับคำขออัปเกรด WebSocket บนเส้นทางเดียวกันreplaceExisting: ไม่บังคับ อนุญาตให้ Plugin เดิมแทนที่การลงทะเบียนเส้นทางที่มีอยู่ของตนเองhandler: คืนค่าtrueเมื่อเส้นทางจัดการคำขอแล้ว
หมายเหตุ:
api.registerHttpHandler(...)ถูกนำออกแล้วและจะทำให้เกิดข้อผิดพลาดขณะโหลด Plugin ให้ใช้api.registerHttpRoute(...)แทน- เส้นทางของ Plugin ต้องประกาศ
authอย่างชัดเจน - ระบบจะปฏิเสธความขัดแย้งของ
path + matchที่ตรงกันทั้งหมด เว้นแต่กำหนดreplaceExisting: trueและ Plugin หนึ่งไม่สามารถแทนที่เส้นทางของ Plugin อื่นได้ - ระบบจะปฏิเสธเส้นทางที่ทับซ้อนกันและมีระดับ
authต่างกัน ให้ใช้สายการส่งต่อexact/prefixเฉพาะภายในระดับการยืนยันตัวตนเดียวกัน - เส้นทางที่ใช้
auth: "plugin"จะ ไม่ได้รับ ขอบเขตรันไทม์ของผู้ดำเนินการโดยอัตโนมัติ เส้นทางเหล่านี้มีไว้สำหรับ Webhook หรือการตรวจสอบลายเซ็นที่ Plugin จัดการ ไม่ใช่สำหรับการเรียกตัวช่วยของ Gateway ที่มีสิทธิ์ระดับสูง - เส้นทางที่ใช้
auth: "gateway"ทำงานภายในขอบเขตรันไทม์ของคำขอ Gateway พื้นผิวเริ่มต้น (gatewayRuntimeScopeSurface: "write-default") ตั้งใจออกแบบให้จำกัดสิทธิ์อย่างระมัดระวัง:- การยืนยันตัวตนแบบ bearer ด้วยความลับร่วม (
gateway.auth.mode = "token"/"password") และวิธีการยืนยันตัวตนใด ๆ ที่ไม่ใช่พร็อกซีที่เชื่อถือได้ จะได้รับขอบเขตoperator.writeเพียงขอบเขตเดียว แม้ผู้เรียกจะส่งx-openclaw-scopes - ผู้เรียกผ่าน
trusted-proxyที่ไม่มีส่วนหัวx-openclaw-scopesอย่างชัดเจนจะยังคงได้รับเฉพาะพื้นผิวแบบเดิมที่มีเพียงoperator.write - ผู้เรียกผ่าน
trusted-proxyที่ส่งx-openclaw-scopesจะได้รับขอบเขตที่ประกาศไว้แทน - เส้นทางสามารถเลือกใช้
gatewayRuntimeScopeSurface: "trusted-operator"เพื่อให้เคารพx-openclaw-scopesเสมอสำหรับโหมดการยืนยันตัวตนที่มีข้อมูลอัตลักษณ์ (และใช้ชุดขอบเขตเริ่มต้นทั้งหมดของ CLI เป็นค่าทดแทนเมื่อไม่มีส่วนหัว)
- การยืนยันตัวตนแบบ bearer ด้วยความลับร่วม (
- กฎเชิงปฏิบัติ: อย่าถือว่าเส้นทาง Plugin ที่ยืนยันตัวตนผ่าน Gateway เป็นพื้นผิวผู้ดูแลระบบโดยปริยาย หากเส้นทางของคุณต้องใช้พฤติกรรมเฉพาะผู้ดูแลระบบ ให้เลือกใช้พื้นผิวขอบเขต
trusted-operatorบังคับใช้โหมดการยืนยันตัวตนที่มีข้อมูลอัตลักษณ์ และจัดทำเอกสารสัญญาของส่วนหัวx-openclaw-scopesอย่างชัดเจน - หลังจากจับคู่เส้นทางและยืนยันตัวตนแล้ว ตัวจัดการทั่วไปจะอยู่ภายใต้การควบคุมการรับงานระดับรากของ Gateway โดย Gateway ที่อยู่ในสถานะเตรียมพร้อมหรือกำลังเริ่มใหม่จะคืนค่า
503ก่อนเรียกใช้ตัวจัดการ ข้อยกเว้นที่จำกัดคือเส้นทางauth: "gateway"ที่ได้รับสิทธิ์จากไฟล์ manifest และเลือกใช้พื้นผิวtrusted-operatorเฉพาะเส้นทางด้วย เส้นทางนี้ยังคงเข้าถึงได้เพื่อไม่ให้การส่งคำสั่งควบคุมการระงับติดค้าง ขณะที่เส้นทางพี่น้องทั่วไปจาก Plugin เดียวกันยังคงอยู่หลังขอบเขตการรับงาน การเป็นเจ้าของhandleUpgradeของ WebSocket ใช้ขอบเขตการรับงานแบบอะตอมมิกเดียวกัน เมื่อใดที่ตัวจัดการยอมรับซ็อกเก็ตแล้ว วงจรชีวิตหลังจากนั้นของซ็อกเก็ตจะอยู่ภายใต้การดูแลของ Plugin และขอบเขตนี้จะไม่ติดตามอีกต่อไป
พาธการนำเข้าของ Plugin SDK
เมื่อสร้าง Plugin ใหม่ ให้ใช้พาธย่อย SDK ที่เฉพาะเจาะจงแทน barrel รากแบบรวมศูนย์ openclaw/plugin-sdk
พาธย่อยหลัก:
| พาธย่อย | วัตถุประสงค์ |
|---|---|
openclaw/plugin-sdk/plugin-entry |
องค์ประกอบพื้นฐานสำหรับการลงทะเบียน Plugin |
openclaw/plugin-sdk/channel-core |
ตัวช่วยสำหรับจุดเข้าและการสร้างช่องทาง |
openclaw/plugin-sdk/core |
ตัวช่วยทั่วไปร่วมกันและสัญญารวม |
openclaw/plugin-sdk/config-schema |
สคีมา Zod ของ openclaw.json ระดับราก (OpenClawSchema) |
Plugin ช่องทางเลือกใช้จากชุดรอยต่อที่เฉพาะเจาะจง ได้แก่ channel-setup,
setup-runtime, setup-tools, channel-pairing,
channel-contract, channel-feedback, channel-inbound, channel-outbound,
command-auth, secret-input, webhook-ingress,
channel-targets และ channel-actions พฤติกรรมการอนุมัติควรรวมไว้ในสัญญา
approvalCapability เดียว แทนการผสมข้ามฟิลด์ Plugin ที่ไม่เกี่ยวข้อง
ดู Plugin ช่องทาง
ตัวช่วยรันไทม์และการกำหนดค่าอยู่ภายใต้พาธย่อย *-runtime ที่มีขอบเขตตรงกัน
(approval-runtime, agent-runtime, lazy-runtime, directory-runtime,
text-runtime, runtime-store, system-event-runtime, heartbeat-runtime,
channel-activity-runtime เป็นต้น) ควรใช้ config-contracts,
plugin-config-runtime, runtime-config-snapshot และ config-mutation
แทน barrel ความเข้ากันได้ config-runtime แบบกว้าง
จุดเข้าภายในรีโพ (แยกตามรากแพ็กเกจของ Plugin ที่รวมมากับระบบ):
index.js— จุดเข้าของ Plugin ที่รวมมากับระบบapi.js— barrel ของตัวช่วยและชนิดข้อมูลruntime-api.js— barrel สำหรับรันไทม์เท่านั้นsetup-entry.js— จุดเข้าของ Plugin สำหรับการตั้งค่า
Plugin ภายนอกควรนำเข้าเฉพาะพาธย่อย openclaw/plugin-sdk/* เท่านั้น ห้าม
นำเข้า src/* ของแพ็กเกจ Plugin อื่นจากแกนหลักหรือจาก Plugin อื่น
จุดเข้าที่โหลดผ่าน facade จะเลือกใช้สแนปช็อตการกำหนดค่ารันไทม์ที่ใช้งานอยู่ก่อนเมื่อมี
จากนั้นจึงใช้ไฟล์การกำหนดค่าที่แก้ไขพาธแล้วบนดิสก์เป็นทางเลือกสำรอง
พาธย่อยเฉพาะความสามารถ เช่น image-generation, media-understanding
และ speech มีอยู่เนื่องจาก Plugin ที่รวมมากับระบบใช้งานอยู่ในปัจจุบัน พาธเหล่านี้
ไม่ได้เป็นสัญญาภายนอกที่ถูกตรึงไว้ระยะยาวโดยอัตโนมัติ โปรดตรวจสอบหน้าอ้างอิง SDK
ที่เกี่ยวข้องเมื่อต้องพึ่งพาพาธเหล่านี้
สคีมาของเครื่องมือข้อความ
Plugin ควรเป็นเจ้าของส่วนเพิ่มเติมของสคีมา describeMessageTool(...)
เฉพาะช่องทางสำหรับองค์ประกอบพื้นฐานที่ไม่ใช่ข้อความ เช่น การแสดงปฏิกิริยา การอ่าน และโพล
การนำเสนอร่วมสำหรับการส่งควรใช้สัญญาทั่วไป MessagePresentation
แทนฟิลด์ปุ่ม คอมโพเนนต์ บล็อก หรือการ์ดแบบเฉพาะผู้ให้บริการ
ดูสัญญา กฎการใช้ทางเลือกสำรอง การแมปผู้ให้บริการ และรายการตรวจสอบสำหรับผู้สร้าง Plugin
ได้ที่ การนำเสนอข้อความ
Plugin ที่สามารถส่งข้อความได้จะประกาศสิ่งที่ตนแสดงผลได้ผ่านความสามารถด้านข้อความ:
presentationสำหรับบล็อกการนำเสนอเชิงความหมาย (text,context,divider,chart,table,buttons,select)delivery-pinสำหรับคำขอส่งแบบปักหมุด
แกนหลักเป็นผู้ตัดสินใจว่าจะแสดงผลการนำเสนอแบบเนทีฟหรือลดรูปเป็นข้อความ อย่าเปิดช่องทางหลบออกไปใช้ UI แบบเนทีฟของผู้ให้บริการจากเครื่องมือข้อความทั่วไป ตัวช่วย SDK ที่เลิกแนะนำให้ใช้แล้วสำหรับสคีมาเนทีฟแบบเดิมยังคงส่งออกไว้สำหรับ Plugin ของบุคคลที่สามที่มีอยู่ แต่ Plugin ใหม่ไม่ควรใช้ตัวช่วยเหล่านี้
การแก้ไขเป้าหมายของช่องทาง
Plugin ช่องทางควรเป็นเจ้าของความหมายเชิงพฤติกรรมของเป้าหมายเฉพาะช่องทาง ให้โฮสต์ ขาออกร่วมคงความเป็นทั่วไป และใช้พื้นผิวอะแดปเตอร์การส่งข้อความสำหรับกฎของผู้ให้บริการ:
messaging.inferTargetChatType({ to })ตัดสินใจว่าควรปฏิบัติต่อเป้าหมาย ที่ปรับให้อยู่ในรูปมาตรฐานแล้วเป็นdirect,groupหรือchannelก่อนค้นหาไดเรกทอรีmessaging.targetResolver.looksLikeId(raw, normalized)บอกแกนหลักว่าอินพุต ควรข้ามไปยังการแก้ไขแบบรหัสโดยตรงแทนการค้นหาไดเรกทอรีหรือไม่messaging.targetResolver.reservedLiteralsแสดงรายการคำเดี่ยวที่เป็น การอ้างอิงช่องทางหรือเซสชันสำหรับผู้ให้บริการนั้น การแก้ไขจะรักษารายการไดเรกทอรี ที่กำหนดค่าไว้ก่อนปฏิเสธคำสงวน จากนั้นจะหยุดอย่างปลอดภัยเมื่อค้นหาในไดเรกทอรีไม่พบmessaging.targetResolver.resolveTarget(...)เป็นทางเลือกสำรองของ Plugin เมื่อ แกนหลักต้องการการแก้ไขขั้นสุดท้ายที่ผู้ให้บริการเป็นเจ้าของ หลังการปรับให้อยู่ในรูปมาตรฐาน หรือหลังค้นหาในไดเรกทอรีไม่พบmessaging.resolveOutboundSessionRoute(...)เป็นเจ้าของการสร้างเส้นทางเซสชัน เฉพาะผู้ให้บริการเมื่อแก้ไขเป้าหมายแล้ว
การแบ่งหน้าที่ที่แนะนำ:
- ใช้
inferTargetChatTypeสำหรับการตัดสินใจประเภทที่ควรเกิดขึ้นก่อน ค้นหาเพียร์หรือกลุ่ม - ใช้
looksLikeIdสำหรับการตรวจสอบว่า “ให้ปฏิบัติต่อค่านี้เป็นรหัสเป้าหมายแบบชัดเจนหรือเนทีฟ” - ใช้
resolveTargetเป็นทางเลือกสำรองสำหรับการปรับให้อยู่ในรูปมาตรฐานเฉพาะผู้ให้บริการ ไม่ใช่สำหรับการค้นหาไดเรกทอรีแบบกว้าง - เก็บรหัสเนทีฟของผู้ให้บริการ เช่น รหัสแชต รหัสเธรด JID ชื่ออ้างอิง และรหัสห้อง
ไว้ภายในค่า
targetหรือพารามิเตอร์เฉพาะผู้ให้บริการ ไม่ใช่ในฟิลด์ SDK ทั่วไป
ไดเรกทอรีที่อิงการกำหนดค่า
Plugin ที่สร้างรายการไดเรกทอรีจากการกำหนดค่าควรเก็บตรรกะนั้นไว้ใน
Plugin และนำตัวช่วยร่วมจาก
openclaw/plugin-sdk/directory-runtime มาใช้ซ้ำ
ใช้รูปแบบนี้เมื่อช่องทางต้องการเพียร์หรือกลุ่มที่อิงการกำหนดค่า เช่น:
- เพียร์ DM ที่ควบคุมด้วยรายการอนุญาต
- แมปช่องทางหรือกลุ่มที่กำหนดค่าไว้
- ทางเลือกสำรองของไดเรกทอรีแบบคงที่ที่จำกัดขอบเขตตามบัญชี
ตัวช่วยร่วมใน directory-runtime จัดการเฉพาะการดำเนินการทั่วไป:
- การกรองคำค้น
- การใช้ขีดจำกัด
- ตัวช่วยกำจัดรายการซ้ำและปรับให้อยู่ในรูปมาตรฐาน
- การสร้าง
ChannelDirectoryEntry[]
การตรวจสอบบัญชีเฉพาะช่องทางและการปรับรหัสให้อยู่ในรูปมาตรฐานควรอยู่ใน การติดตั้งใช้งานของ Plugin
แค็ตตาล็อกผู้ให้บริการ
Plugin ผู้ให้บริการสามารถกำหนดแค็ตตาล็อกโมเดลสำหรับการอนุมานด้วย
registerProvider({ catalog: { run(...) { ... } } })
catalog.run(...) คืนค่ารูปแบบเดียวกับที่ OpenClaw เขียนลงใน
models.providers:
{ provider }สำหรับรายการผู้ให้บริการหนึ่งรายการ{ providers }สำหรับรายการผู้ให้บริการหลายรายการ
ใช้ catalog เมื่อ Plugin เป็นเจ้าของรหัสโมเดลเฉพาะผู้ให้บริการ ค่าเริ่มต้น
ของ URL ฐาน หรือข้อมูลเมตาของโมเดลที่จำกัดด้วยการยืนยันตัวตน
catalog.order ควบคุมลำดับเวลาที่แค็ตตาล็อกของ Plugin ผสานเข้ากับ
ผู้ให้บริการโดยปริยายที่ติดตั้งมากับ OpenClaw:
simple: ผู้ให้บริการที่ใช้คีย์ API แบบทั่วไปหรือขับเคลื่อนด้วยตัวแปรสภาพแวดล้อมprofile: ผู้ให้บริการที่ปรากฏเมื่อมีโปรไฟล์การยืนยันตัวตนpaired: ผู้ให้บริการที่สังเคราะห์รายการผู้ให้บริการที่เกี่ยวข้องกันหลายรายการlate: รอบสุดท้าย หลังจากผู้ให้บริการโดยปริยายอื่น ๆ
ผู้ให้บริการที่มาภายหลังจะชนะเมื่อคีย์ชนกัน ดังนั้น Plugin จึงสามารถตั้งใจแทนที่ รายการผู้ให้บริการที่ติดตั้งมากับระบบซึ่งมีรหัสผู้ให้บริการเดียวกันได้
Plugin ยังสามารถเผยแพร่แถวโมเดลแบบอ่านอย่างเดียวผ่าน
api.registerModelCatalogProvider({ provider, kinds, staticCatalog, liveCatalog }) ได้ด้วย นี่คือแนวทางต่อไปสำหรับพื้นผิวรายการ วิธีใช้ และตัวเลือก โดยรองรับแถว
text, voice, image_generation, video_generation และ music_generation
Plugin ผู้ให้บริการยังคงเป็นเจ้าของการเรียกปลายทางแบบสด การแลกเปลี่ยนโทเค็น และ
การแมปการตอบกลับจากผู้จำหน่าย ส่วนแกนหลักเป็นเจ้าของรูปแบบแถวร่วม ป้ายกำกับแหล่งที่มา และ
การจัดรูปแบบวิธีใช้เครื่องมือสื่อ การลงทะเบียนผู้ให้บริการสร้างสื่อจะสังเคราะห์
แถวแค็ตตาล็อกแบบคงที่โดยอัตโนมัติจาก defaultModel, models และ
capabilities
ความเข้ากันได้:
discoveryยังคงทำงานเป็นนามแฝงแบบเดิม แต่จะแสดงคำเตือนการเลิกใช้งาน- หากลงทะเบียนทั้ง
catalogและdiscoveryOpenClaw จะใช้catalogและแสดงคำเตือน augmentModelCatalogเลิกแนะนำให้ใช้แล้ว ผู้ให้บริการที่รวมมากับระบบควรเผยแพร่ แถวเพิ่มเติมผ่านregisterModelCatalogProvider
การตรวจสอบช่องทางแบบอ่านอย่างเดียว
หาก Plugin ของคุณลงทะเบียนช่องทาง ควรติดตั้งใช้งาน
plugin.config.inspectAccount(cfg, accountId) ควบคู่กับ resolveAccount(...)
เหตุผล:
resolveAccount(...)เป็นพาธรันไทม์ โดยสามารถถือว่าข้อมูลรับรอง ถูกจัดเตรียมไว้อย่างสมบูรณ์แล้ว และสามารถหยุดทันทีเมื่อไม่มีข้อมูลลับที่จำเป็น- พาธคำสั่งแบบอ่านอย่างเดียว เช่น
openclaw status,openclaw status --all,openclaw channels status,openclaw channels resolveและกระบวนการ ซ่อมแซมของ doctor หรือการกำหนดค่า ไม่ควรต้องจัดเตรียมข้อมูลรับรองรันไทม์เพียงเพื่อ อธิบายการกำหนดค่า
พฤติกรรม inspectAccount(...) ที่แนะนำ:
- ส่งคืนเฉพาะสถานะบัญชีเชิงบรรยาย
- คง
enabledและconfiguredไว้ - ระบุฟิลด์แหล่งที่มา/สถานะของข้อมูลรับรองเมื่อเกี่ยวข้อง เช่น:
tokenSource,tokenStatusbotTokenSource,botTokenStatusappTokenSource,appTokenStatussigningSecretSource,signingSecretStatus
- คุณไม่จำเป็นต้องส่งคืนค่าโทเค็นดิบเพียงเพื่อรายงานความพร้อมใช้งานแบบอ่านอย่างเดียว การส่งคืน
tokenStatus: "available"(พร้อมฟิลด์แหล่งที่มาที่ตรงกัน) ก็เพียงพอสำหรับคำสั่งประเภทแสดงสถานะ - ใช้
configured_unavailableเมื่อกำหนดค่าข้อมูลรับรองผ่าน SecretRef แล้ว แต่ข้อมูลนั้นไม่พร้อมใช้งานในเส้นทางคำสั่งปัจจุบัน
วิธีนี้ช่วยให้คำสั่งแบบอ่านอย่างเดียวรายงานว่า "กำหนดค่าแล้วแต่ไม่พร้อมใช้งานในเส้นทางคำสั่งนี้" แทนที่จะหยุดทำงานหรือรายงานผิดว่าบัญชียังไม่ได้กำหนดค่า
แพ็กเกจรวม
ไดเรกทอรี Plugin อาจมี package.json ที่ระบุ openclaw.extensions:
{ "name": "my-pack", "openclaw": { "extensions": ["./src/safety.ts", "./src/tools.ts"], "setupEntry": "./src/setup-entry.ts" }}แต่ละรายการจะกลายเป็น Plugin หากแพ็กรวมระบุส่วนขยายหลายรายการ รหัส Plugin จะเป็น <manifestOrPackageName>/<fileBase> (รหัสในแมนิเฟสต์มีลำดับความสำคัญหากมีอยู่ มิฉะนั้นจะใช้ชื่อ package.json ที่ไม่มีสโคป)
หาก Plugin ของคุณนำเข้าดีเพนเดนซี npm ให้ติดตั้งไว้ในไดเรกทอรีนั้นเพื่อให้มี node_modules พร้อมใช้งาน (npm install / pnpm install)
ข้อจำกัดด้านความปลอดภัย: ทุกรายการใน openclaw.extensions ต้องยังคงอยู่ภายในไดเรกทอรี Plugin หลังจากแก้ไข symlink แล้ว รายการที่ออกนอกไดเรกทอรีแพ็กเกจจะถูกปฏิเสธ
หมายเหตุด้านความปลอดภัย: openclaw plugins install ติดตั้งดีเพนเดนซีของ Plugin ด้วย npm install --omit=dev --ignore-scripts ภายในโปรเจกต์ (ไม่เรียกใช้สคริปต์วงจรชีวิตและไม่มีดีเพนเดนซีสำหรับการพัฒนาในขณะรันไทม์) โดยไม่ใช้การตั้งค่าการติดตั้ง npm ส่วนกลางที่สืบทอดมา ควรรักษาโครงสร้างดีเพนเดนซีของ Plugin ให้เป็น "JS/TS ล้วน" และหลีกเลี่ยงแพ็กเกจที่ต้องสร้างผ่าน postinstall
ทางเลือก: openclaw.setupEntry สามารถชี้ไปยังโมดูลขนาดเบาที่ใช้สำหรับการตั้งค่าเท่านั้น เมื่อ OpenClaw ต้องการส่วนติดต่อการตั้งค่าสำหรับ Plugin ช่องทางที่ปิดใช้งาน หรือเมื่อเปิดใช้งาน Plugin ช่องทางแล้วแต่ยังไม่ได้กำหนดค่า ระบบจะโหลด setupEntry แทนจุดเข้าหลักของ Plugin วิธีนี้ช่วยให้การเริ่มต้นและการตั้งค่าเบาลง เมื่อจุดเข้าหลักของ Plugin เชื่อมต่อเครื่องมือ ฮุก หรือโค้ดอื่นที่ใช้เฉพาะขณะรันไทม์ด้วย
ทางเลือก: openclaw.startup.deferConfiguredChannelFullLoadUntilAfterListen ช่วยให้ Plugin ช่องทางเลือกใช้เส้นทาง setupEntry เดียวกันในช่วงเริ่มต้นก่อนที่ Gateway จะเริ่มรับการเชื่อมต่อ แม้ว่าช่องทางนั้นจะกำหนดค่าไว้แล้วก็ตาม
ใช้ตัวเลือกนี้เฉพาะเมื่อ setupEntry ครอบคลุมส่วนติดต่อการเริ่มต้นทั้งหมดที่ต้องมีอยู่ก่อน Gateway เริ่มรับการเชื่อมต่อ ในทางปฏิบัติ หมายความว่าจุดเข้าสำหรับการตั้งค่าต้องลงทะเบียนความสามารถทั้งหมดที่ช่องทางเป็นเจ้าของและการเริ่มต้นต้องพึ่งพา เช่น:
- การลงทะเบียนช่องทางเอง
- เส้นทาง HTTP ใดๆ ที่ต้องพร้อมใช้งานก่อน Gateway เริ่มรับการเชื่อมต่อ
- เมธอด เครื่องมือ หรือบริการใดๆ ของ Gateway ที่ต้องมีอยู่ในช่วงเวลาเดียวกันนั้น
หากจุดเข้าหลักของคุณยังคงเป็นเจ้าของความสามารถที่จำเป็นต่อการเริ่มต้น อย่าเปิดใช้แฟล็กนี้ ให้ Plugin ใช้พฤติกรรมเริ่มต้นต่อไปและปล่อยให้ OpenClaw โหลดจุดเข้าหลักทั้งหมดระหว่างการเริ่มต้น
ช่องทางที่รวมมาในชุดยังสามารถเผยแพร่ตัวช่วยส่วนติดต่อสัญญาที่ใช้เฉพาะการตั้งค่า ซึ่งแกนหลักสามารถเรียกใช้ก่อนโหลดรันไทม์ของช่องทางทั้งหมดได้ ส่วนติดต่อการเลื่อนระดับการตั้งค่าปัจจุบันคือ:
singleAccountKeysToMovenamedAccountPromotionKeysresolveSingleAccountPromotionTarget(...)
แกนหลักใช้ส่วนติดต่อนี้เมื่อต้องเลื่อนระดับการกำหนดค่าช่องทางแบบบัญชีเดียวรุ่นเก่าไปยัง channels.<id>.accounts.* โดยไม่โหลดจุดเข้าหลักทั้งหมดของ Plugin ปัจจุบัน Matrix คือตัวอย่างที่รวมมาในชุด: เมื่อมีบัญชีแบบระบุชื่ออยู่แล้ว ระบบจะย้ายเฉพาะคีย์การยืนยันตัวตน/บูตสแตรปไปยังบัญชีแบบระบุชื่อที่เลื่อนระดับ และสามารถคงคีย์บัญชีเริ่มต้นที่กำหนดค่าไว้แต่ไม่ใช่คีย์มาตรฐาน แทนที่จะสร้าง accounts.default เสมอ
อะแดปเตอร์แพตช์สำหรับการตั้งค่าเหล่านี้ช่วยให้การค้นพบส่วนติดต่อสัญญาที่รวมมาในชุดยังคงเป็นแบบโหลดเมื่อจำเป็น เวลาในการนำเข้าจึงยังคงสั้น โดยจะโหลดส่วนติดต่อการเลื่อนระดับเฉพาะเมื่อใช้งานครั้งแรก แทนที่จะกลับเข้าสู่การเริ่มต้นช่องทางที่รวมมาในชุดอีกครั้งระหว่างการนำเข้าโมดูล
เมื่อส่วนติดต่อการเริ่มต้นเหล่านั้นมีเมธอด RPC ของ Gateway ให้ใช้คำนำหน้าที่เฉพาะกับ Plugin เนมสเปซผู้ดูแลระบบของแกนหลัก (config.*, exec.approvals.*, wizard.*, update.*) ยังคงสงวนไว้และจะถูกกำหนดเป็น operator.admin เสมอ แม้ว่า Plugin จะร้องขอขอบเขตที่แคบกว่าก็ตาม
ตัวอย่าง:
{ "name": "@scope/my-channel", "openclaw": { "extensions": ["./index.ts"], "setupEntry": "./setup-entry.ts", "startup": { "deferConfiguredChannelFullLoadUntilAfterListen": true } }}เมทาดาทาแค็ตตาล็อกช่องทาง
Plugin ช่องทางสามารถประกาศเมทาดาทาสำหรับการตั้งค่า/การค้นพบผ่าน openclaw.channel และคำแนะนำในการติดตั้งผ่าน openclaw.install วิธีนี้ช่วยให้แค็ตตาล็อกแกนหลักไม่ต้องเก็บข้อมูล
ตัวอย่าง:
{ "name": "@openclaw/nextcloud-talk", "openclaw": { "extensions": ["./index.ts"], "channel": { "id": "nextcloud-talk", "label": "Nextcloud Talk", "selectionLabel": "Nextcloud Talk (self-hosted)", "docsPath": "/channels/nextcloud-talk", "docsLabel": "nextcloud-talk", "blurb": "Self-hosted chat via Nextcloud Talk webhook bots.", "order": 65, "aliases": ["nc-talk", "nc"] }, "install": { "npmSpec": "@openclaw/nextcloud-talk", "localPath": "<bundled-plugin-local-path>", "defaultChoice": "npm" } }}ฟิลด์ openclaw.channel ที่มีประโยชน์นอกเหนือจากตัวอย่างขั้นต่ำ:
detailLabel: ป้ายกำกับรองสำหรับส่วนติดต่อแค็ตตาล็อก/สถานะที่มีรายละเอียดมากขึ้นdocsLabel: แทนที่ข้อความลิงก์สำหรับลิงก์เอกสารpreferOver: รหัส Plugin/ช่องทางที่มีลำดับความสำคัญต่ำกว่า ซึ่งรายการแค็ตตาล็อกนี้ควรมีอันดับเหนือกว่าselectionDocsPrefix,selectionDocsOmitLabel,selectionExtras: การควบคุมข้อความบนส่วนติดต่อการเลือกmarkdownCapable: ระบุว่าช่องทางรองรับ Markdown เพื่อใช้ตัดสินใจเรื่องการจัดรูปแบบขาออกexposure.configured: ซ่อนช่องทางจากส่วนติดต่อรายการช่องทางที่กำหนดค่าแล้วเมื่อตั้งค่าเป็นfalseexposure.setup: ซ่อนช่องทางจากตัวเลือกการตั้งค่า/กำหนดค่าแบบโต้ตอบเมื่อตั้งค่าเป็นfalseexposure.docs: ระบุว่าช่องทางเป็นช่องทางภายใน/ส่วนตัวสำหรับส่วนติดต่อการนำทางเอกสารshowConfigured/showInSetup: ชื่อแทนรุ่นเก่าที่ยังคงรองรับเพื่อความเข้ากันได้ แนะนำให้ใช้exposurequickstartAllowFrom: ให้ช่องทางเข้าร่วมโฟลว์เริ่มต้นด่วนมาตรฐานallowFromforceAccountBinding: กำหนดให้ผูกบัญชีอย่างชัดเจน แม้ว่าจะมีบัญชีเพียงบัญชีเดียวpreferSessionLookupForAnnounceTarget: ให้ความสำคัญกับการค้นหาเซสชันเมื่อแก้ไขเป้าหมายการประกาศ
OpenClaw ยังสามารถผสาน แค็ตตาล็อกช่องทางภายนอก (ตัวอย่างเช่น ข้อมูลส่งออกจากรีจิสทรี MPM) ได้อีกด้วย วางไฟล์ JSON ไว้ที่ตำแหน่งใดตำแหน่งหนึ่งต่อไปนี้:
~/.openclaw/mpm/plugins.json~/.openclaw/mpm/catalog.json~/.openclaw/plugins/catalog.json
หรือกำหนดให้ OPENCLAW_PLUGIN_CATALOG_PATHS (หรือ OPENCLAW_MPM_CATALOG_PATHS) ชี้ไปยังไฟล์ JSON หนึ่งไฟล์ขึ้นไป (คั่นด้วยจุลภาค/เซมิโคลอน/PATH) แต่ละไฟล์ควรมี { "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] } ตัวแยกวิเคราะห์ยังรองรับ "packages" หรือ "plugins" เป็นชื่อแทนรุ่นเก่าของคีย์ "entries"
รายการแค็ตตาล็อกช่องทางและรายการแค็ตตาล็อกการติดตั้งผู้ให้บริการที่สร้างขึ้น จะแสดงข้อเท็จจริงเกี่ยวกับแหล่งที่มาการติดตั้งที่ปรับเป็นมาตรฐานแล้ว ควบคู่กับบล็อก openclaw.install ดิบ ข้อเท็จจริงที่ปรับเป็นมาตรฐานจะระบุว่าสเปก npm เป็นเวอร์ชันที่แน่นอนหรือตัวเลือกแบบลอยตัว มีเมทาดาทาความสมบูรณ์ที่คาดหวังหรือไม่ และมีเส้นทางแหล่งที่มาในเครื่องด้วยหรือไม่ เมื่อทราบข้อมูลประจำตัวของแค็ตตาล็อก/แพ็กเกจ ข้อเท็จจริงที่ปรับเป็นมาตรฐานจะแจ้งเตือนหากชื่อแพ็กเกจ npm ที่แยกวิเคราะห์ได้เบี่ยงเบนจากข้อมูลประจำตัวนั้น นอกจากนี้ยังแจ้งเตือนเมื่อ defaultChoice ไม่ถูกต้องหรือชี้ไปยังแหล่งที่มาที่ไม่พร้อมใช้งาน และเมื่อมีเมทาดาทาความสมบูรณ์ของ npm แต่ไม่มีแหล่งที่มา npm ที่ถูกต้อง ผู้ใช้ข้อมูลควรถือว่า installSource เป็นฟิลด์ทางเลือกแบบเพิ่มเติม เพื่อให้รายการที่สร้างด้วยตนเองและชิมของแค็ตตาล็อกไม่จำเป็นต้องสังเคราะห์ฟิลด์นี้
วิธีนี้ช่วยให้การเริ่มต้นใช้งานและการวินิจฉัยอธิบายสถานะในระดับแหล่งที่มาได้โดยไม่ต้องนำเข้ารันไทม์ของ Plugin
รายการ npm ภายนอกอย่างเป็นทางการควรใช้ npmSpec ที่ระบุเวอร์ชันแน่นอนร่วมกับ expectedIntegrity ชื่อแพ็กเกจเปล่าและ dist-tag ยังคงใช้งานได้เพื่อความเข้ากันได้ แต่จะแสดงคำเตือนในระดับแหล่งที่มา เพื่อให้แค็ตตาล็อกสามารถเปลี่ยนไปสู่การติดตั้งที่ตรึงเวอร์ชันและตรวจสอบความสมบูรณ์ได้โดยไม่ทำให้ Plugin ที่มีอยู่เสียหาย
เมื่อกระบวนการเริ่มต้นใช้งานติดตั้งจากเส้นทางแค็ตตาล็อกในเครื่อง ระบบจะบันทึกรายการดัชนี Plugin ที่จัดการ โดยมี source: "path" และ sourcePath ที่สัมพันธ์กับเวิร์กสเปซเมื่อทำได้ เส้นทางโหลดสำหรับการปฏิบัติงานแบบสัมบูรณ์ยังคงอยู่ใน plugins.load.paths; ระเบียนการติดตั้งจะหลีกเลี่ยงการทำซ้ำเส้นทางของเวิร์กสเตชันภายในเครื่องลงในการกำหนดค่าระยะยาว วิธีนี้ทำให้การติดตั้งสำหรับการพัฒนาในเครื่องยังคงปรากฏในการวินิจฉัยระดับแหล่งที่มา โดยไม่เพิ่มส่วนติดต่อที่สองสำหรับเปิดเผยเส้นทางระบบไฟล์ดิบ ตาราง SQLite installed_plugin_index ที่คงอยู่เป็นแหล่งข้อมูลจริงสำหรับการติดตั้ง และสามารถรีเฟรชได้โดยไม่โหลดโมดูลรันไทม์ของ Plugin แมป installRecords จะคงทนแม้ว่าแมนิเฟสต์ของ Plugin จะหายไปหรือไม่ถูกต้อง ส่วนเพย์โหลด plugins เป็นมุมมองแมนิเฟสต์ที่สร้างใหม่ได้
Plugin กลไกบริบท
Plugin กลไกบริบทเป็นเจ้าของการประสานบริบทของเซสชันสำหรับการรับข้อมูล การประกอบ และ Compaction ลงทะเบียนจาก Plugin ของคุณด้วย api.registerContextEngine(id, factory) แล้วเลือกกลไกที่ใช้งานด้วย plugins.slots.contextEngine
ใช้วิธีนี้เมื่อ Plugin ของคุณต้องแทนที่หรือขยายไปป์ไลน์บริบทเริ่มต้น แทนที่จะเพียงเพิ่มการค้นหาหน่วยความจำหรือฮุก
export default function (api) { api.registerContextEngine("lossless-claw", (ctx) => ({ info: { id: "lossless-claw", name: "Lossless Claw", ownsCompaction: true }, async ingest() { return { ingested: true }; }, async assemble({ messages, sessionKey, availableTools, citationsMode }) { return { messages, estimatedTokens: 0, systemPromptAddition: buildMemorySystemPromptAddition({ availableTools: availableTools ?? new Set(), citationsMode, agentId: resolveSessionAgentId({ config: ctx.config, sessionKey }), agentSessionKey: sessionKey, }), }; }, async compact() { return { ok: true, compacted: false }; }, }));}แฟกทอรี ctx เปิดเผยค่า config, agentDir และ workspaceDir แบบทางเลือกสำหรับการเริ่มต้นค่าในเวลาสร้าง
assemble() อาจส่งคืน contextProjection เมื่อชุดควบคุมที่ใช้งานมีเธรดแบ็กเอนด์แบบคงอยู่ ให้ละเว้นค่านี้สำหรับการฉายภาพต่อรอบแบบเดิม ส่งคืน { mode: "thread_bootstrap", epoch } เมื่อควรฉีดบริบทที่ประกอบแล้วเข้าสู่เธรดแบ็กเอนด์หนึ่งครั้งและนำกลับมาใช้ซ้ำจนกว่ายุคจะเปลี่ยนแปลง เปลี่ยนยุคหลังจากบริบทเชิงความหมายของกลไกเปลี่ยนไป เช่น หลังจากรอบ Compaction ที่กลไกเป็นเจ้าของ โฮสต์อาจเก็บรักษาเมทาดาทาการเรียกใช้เครื่องมือ รูปแบบอินพุต และผลลัพธ์เครื่องมือที่ปกปิดข้อมูลไว้ในการฉายภาพแบบบูตสแตรปเธรด เพื่อให้เธรดแบ็กเอนด์ใหม่คงความต่อเนื่องของเครื่องมือไว้ได้โดยไม่คัดลอกเพย์โหลดดิบที่มีข้อมูลลับ
หากกลไกของคุณ ไม่ได้ เป็นเจ้าของอัลกอริทึม Compaction ให้คงการติดตั้งใช้งาน compact() ไว้และมอบหมายอย่างชัดเจน:
buildMemorySystemPromptAddition, delegateCompactionToRuntime,} from "openclaw/plugin-sdk/core"; export default function (api) { api.registerContextEngine("my-memory-engine", (ctx) => ({ info: { id: "my-memory-engine", name: "My Memory Engine", ownsCompaction: false, }, async ingest() { return { ingested: true }; }, async assemble({ messages, sessionKey, availableTools, citationsMode }) { return { messages, estimatedTokens: 0, systemPromptAddition: buildMemorySystemPromptAddition({ availableTools: availableTools ?? new Set(), citationsMode, agentId: resolveSessionAgentId({ config: ctx.config, sessionKey }), agentSessionKey: sessionKey, }), }; }, async compact(params) { return await delegateCompactionToRuntime(params); }, }));}การเพิ่มความสามารถใหม่
เมื่อ Plugin ต้องการพฤติกรรมที่ไม่สอดคล้องกับ API ปัจจุบัน อย่าข้ามระบบ Plugin ด้วยการเข้าถึงภายในแบบส่วนตัว แต่ให้เพิ่มความสามารถที่ขาดหายไป
ลำดับที่แนะนำ:
- กำหนดสัญญาของแกนหลัก ตัดสินใจว่าแกนหลักควรเป็นเจ้าของพฤติกรรมร่วมใดบ้าง ได้แก่ นโยบาย กลไกสำรอง การผสานการกำหนดค่า วงจรชีวิต ความหมายเชิงพฤติกรรมสำหรับช่องทาง และรูปแบบตัวช่วยรันไทม์
- เพิ่มพื้นผิวการลงทะเบียน/รันไทม์ของ Plugin ที่มีชนิดข้อมูล ขยาย
OpenClawPluginApiและ/หรือapi.runtimeด้วยพื้นผิวความสามารถที่มีชนิดข้อมูลซึ่งเล็กที่สุดแต่ใช้งานได้จริง - เชื่อมต่อแกนหลักกับผู้ใช้ในช่องทาง/ฟีเจอร์ ช่องทางและ Plugin ฟีเจอร์ควรใช้ความสามารถใหม่ผ่านแกนหลัก ไม่ใช่นำเข้าการติดตั้งใช้งานของผู้จำหน่ายโดยตรง
- ลงทะเบียนการติดตั้งใช้งานของผู้จำหน่าย จากนั้น Plugin ของผู้จำหน่ายจึงลงทะเบียนแบ็กเอนด์ของตนกับความสามารถดังกล่าว
- เพิ่มการครอบคลุมสัญญา เพิ่มการทดสอบเพื่อให้รูปแบบความเป็นเจ้าของและการลงทะเบียนยังคงชัดเจนตลอดเวลา
นี่คือวิธีที่ OpenClaw รักษาแนวทางที่ชัดเจนโดยไม่ผูกโค้ดตายตัวกับมุมมองของผู้ให้บริการรายใดรายหนึ่ง ดูรายการตรวจสอบไฟล์ที่เป็นรูปธรรมและตัวอย่างที่ดำเนินการครบถ้วนได้ใน คู่มือความสามารถ
รายการตรวจสอบความสามารถ
เมื่อเพิ่มความสามารถใหม่ โดยทั่วไปการติดตั้งใช้งานควรปรับพื้นผิวเหล่านี้ร่วมกัน:
- ชนิดสัญญาของแกนหลักใน
src/<capability>/types.ts - ตัวรันแกนหลัก/ตัวช่วยรันไทม์ใน
src/<capability>/runtime.ts - พื้นผิวการลงทะเบียน API ของ Plugin ใน
src/plugins/types.ts - การเชื่อมต่อรีจิสทรี Plugin ใน
src/plugins/registry.ts - การเปิดเผยรันไทม์ของ Plugin ใน
src/plugins/runtime/*เมื่อ Plugin ฟีเจอร์/ช่องทางจำเป็นต้องใช้งาน - ตัวช่วยจับข้อมูล/ทดสอบใน
src/test-utils/plugin-registration.ts - การยืนยันความเป็นเจ้าของ/สัญญาใน
src/plugins/contracts/registry.ts - เอกสารสำหรับผู้ดำเนินการ/Plugin ใน
docs/
หากพื้นผิวใดพื้นผิวหนึ่งขาดหายไป โดยทั่วไปเป็นสัญญาณว่าความสามารถดังกล่าวยังไม่ได้รับการผสานรวมอย่างสมบูรณ์
แม่แบบความสามารถ
รูปแบบขั้นต่ำ:
// core contractexport type VideoGenerationProviderPlugin = { id: string; label: string; generateVideo: (req: VideoGenerationRequest) => Promise<VideoGenerationResult>;}; // plugin APIapi.registerVideoGenerationProvider({ id: "openai", label: "OpenAI", async generateVideo(req) { return await generateOpenAiVideo(req); },}); // shared runtime helper for feature/channel pluginsconst clip = await api.runtime.videoGeneration.generate({ prompt: "Show the robot walking through the lab.", cfg,});รูปแบบการทดสอบสัญญา (src/plugins/contracts/registry.ts เปิดเผยการค้นหาความเป็นเจ้าของ เช่น providerContractPluginIds; การทดสอบจะยืนยันว่ารายการ contracts.videoGenerationProviders ของ Plugin ตรงกับสิ่งที่ Plugin ลงทะเบียนจริง):
expect(pluginManifest.contracts?.videoGenerationProviders).toEqual(["openai"]);วิธีนี้ทำให้กฎเรียบง่าย:
- แกนหลักเป็นเจ้าของสัญญาความสามารถและการประสานงาน
- Plugin ของผู้จำหน่ายเป็นเจ้าของการติดตั้งใช้งานเฉพาะผู้จำหน่าย
- Plugin ฟีเจอร์/ช่องทางใช้ตัวช่วยรันไทม์
- การทดสอบสัญญาทำให้ความเป็นเจ้าของชัดเจนอยู่เสมอ
ที่เกี่ยวข้อง
- สถาปัตยกรรม Plugin — โมเดลและรูปแบบความสามารถสาธารณะ
- เส้นทางย่อยของ Plugin SDK
- การตั้งค่า Plugin SDK
- การสร้าง Plugin