Plugin SDK reference

ตัวช่วยรันไทม์ของ Plugin

ข้อมูลอ้างอิงสำหรับอ็อบเจ็กต์ api.runtime ที่ฉีดเข้าไปในทุก Plugin ระหว่างการลงทะเบียน ใช้ตัวช่วยเหล่านี้แทนการนำเข้าอินเทอร์นัลของโฮสต์โดยตรง

typescript
register(api) {  const runtime = api.runtime;}

การโหลดและการเขียนการกำหนดค่า

ควรใช้การกำหนดค่าที่ถูกส่งเข้ามาในเส้นทางการเรียกที่ใช้งานอยู่แล้ว เช่น api.config ระหว่างการลงทะเบียน หรืออาร์กิวเมนต์ cfg ในคอลแบ็กของช่องทาง/ผู้ให้บริการ วิธีนี้ทำให้สแนปช็อตของกระบวนการหนึ่งไหลผ่านงาน แทนที่จะพาร์สการกำหนดค่าซ้ำในเส้นทางทำงานที่มีการเรียกบ่อย

ใช้ api.runtime.config.current() เฉพาะเมื่อแฮนด์เลอร์ที่มีอายุยาวต้องการสแนปช็อตของกระบวนการปัจจุบัน และไม่มีการส่งการกำหนดค่าเข้ามาในฟังก์ชันนั้น ค่าที่คืนมาเป็นแบบอ่านอย่างเดียว ให้โคลนหรือใช้ตัวช่วยการแก้ไขก่อนแก้ไข

แฟกทอรีเครื่องมือได้รับ ctx.runtimeConfig พร้อมกับ ctx.getRuntimeConfig() ใช้ getter ภายในคอลแบ็ก execute ของเครื่องมือที่มีอายุยาวเมื่อการกำหนดค่าอาจเปลี่ยนหลังจากสร้างนิยามเครื่องมือแล้ว

คงการเปลี่ยนแปลงไว้ด้วย api.runtime.config.mutateConfigFile(...) หรือ api.runtime.config.replaceConfigFile(...) การเขียนแต่ละครั้งต้องเลือกนโยบาย afterWrite อย่างชัดเจน:

  • afterWrite: { mode: "auto" } ให้ตัวตัดสินใจโหลด Gateway ซ้ำเป็นผู้ตัดสินใจ
  • afterWrite: { mode: "restart", reason: "..." } บังคับรีสตาร์ตแบบสะอาดเมื่อผู้เขียนรู้ว่าการโหลดซ้ำแบบร้อนไม่ปลอดภัย
  • afterWrite: { mode: "none", reason: "..." } ระงับการโหลดซ้ำ/รีสตาร์ตอัตโนมัติเฉพาะเมื่อผู้เรียกเป็นเจ้าของงานติดตามผล

ตัวช่วยการแก้ไขคืนค่า afterWrite พร้อมสรุป followUp แบบมีชนิด เพื่อให้ผู้เรียกสามารถบันทึกล็อกหรือทดสอบว่าตนร้องขอการรีสตาร์ตหรือไม่ Gateway ยังคงเป็นเจ้าของว่ารีสตาร์ตนั้นจะเกิดขึ้นจริงเมื่อใด

api.runtime.config.loadConfig() และ api.runtime.config.writeConfigFile(...) เป็นตัวช่วยความเข้ากันได้ที่เลิกใช้แล้วภายใต้ runtime-config-load-write ตัวช่วยเหล่านี้เตือนหนึ่งครั้งที่รันไทม์ และยังคงพร้อมใช้งานสำหรับ Plugin ภายนอกเก่าระหว่างช่วงย้ายระบบ Plugin ที่บันเดิลไว้ต้องไม่ใช้ตัวช่วยเหล่านี้ การ์ดขอบเขตการกำหนดค่าจะล้มเหลวหากโค้ด Plugin เรียกใช้หรือนำเข้าตัวช่วยเหล่านั้นจากพาธย่อยของ Plugin SDK

สำหรับการนำเข้า SDK โดยตรง ให้ใช้พาธย่อยการกำหนดค่าที่เจาะจงแทน barrel ความเข้ากันได้แบบกว้าง openclaw/plugin-sdk/config-runtime: ใช้ config-contracts สำหรับ ชนิด, plugin-config-runtime สำหรับการยืนยันการกำหนดค่าที่โหลดแล้วและการค้นหารายการเข้า Plugin, runtime-config-snapshot สำหรับสแนปช็อตกระบวนการปัจจุบัน และ config-mutation สำหรับการเขียน การทดสอบ Plugin ที่บันเดิลไว้ควร mock พาธย่อยที่เจาะจงเหล่านี้โดยตรง แทนที่จะ mock barrel ความเข้ากันได้แบบกว้าง

โค้ดรันไทม์ภายในของ OpenClaw มีทิศทางเดียวกัน: โหลดการกำหนดค่าหนึ่งครั้งที่ CLI, Gateway หรือขอบเขตกระบวนการ แล้วส่งค่านั้นต่อไป การเขียนการแก้ไขที่สำเร็จจะรีเฟรชสแนปช็อตรันไทม์ของกระบวนการและเลื่อน revision ภายใน แคชที่มีอายุยาวควรผูกกับคีย์แคชที่รันไทม์เป็นเจ้าของ แทนการซีเรียลไลซ์การกำหนดค่าในเครื่อง โมดูลรันไทม์ที่มีอายุยาวมีสแกนเนอร์แบบไม่ยอมรับการเรียก loadConfig() จากบริบทรอบข้างเลย ให้ใช้ cfg ที่ถูกส่งเข้ามา, context.getRuntimeConfig() ของคำขอ หรือ getRuntimeConfig() ที่ขอบเขตกระบวนการอย่างชัดเจน

เส้นทางการทำงานของผู้ให้บริการและช่องทางต้องใช้สแนปช็อตการกำหนดค่ารันไทม์ที่ใช้งานอยู่ ไม่ใช่สแนปช็อตไฟล์ที่คืนมาสำหรับอ่านกลับหรือแก้ไขการกำหนดค่า สแนปช็อตไฟล์เก็บค่าต้นทาง เช่น เครื่องหมาย SecretRef สำหรับ UI และการเขียน ส่วนคอลแบ็กผู้ให้บริการต้องการมุมมองรันไทม์ที่ resolve แล้ว เมื่อ helper อาจถูกเรียกด้วยสแนปช็อตต้นทางที่ใช้งานอยู่หรือสแนปช็อตรันไทม์ที่ใช้งานอยู่ ให้ส่งผ่าน selectApplicableRuntimeConfig() ก่อนอ่านข้อมูลรับรอง

ยูทิลิตีรันไทม์ที่ใช้ซ้ำได้

ใช้ข้อเท็จจริง botLoopProtection ขาเข้าสำหรับข้อความขาเข้าที่บอทเป็นผู้เขียน Core ใช้ guard แบบ sliding-window ในหน่วยความจำร่วมก่อนบันทึกเซสชันและ dispatch โดยไม่ผูกนโยบายกับช่องทางใดช่องทางหนึ่ง guard ติดตามคีย์ (scopeId, conversationId, participant pair), นับทั้งสองทิศทางของคู่ร่วมกัน, ใช้ cooldown เมื่อเกินงบของหน้าต่างเวลา และตัดรายการที่ไม่ใช้งานออกตามโอกาส

Plugin ช่องทางที่เปิดเผยพฤติกรรมนี้ให้ผู้ปฏิบัติงานควรใช้รูปทรง channels.defaults.botLoopProtection ร่วมสำหรับงบพื้นฐานก่อน แล้วค่อยวาง override เฉพาะช่องทาง/ผู้ให้บริการไว้ด้านบน การกำหนดค่าร่วมใช้หน่วยวินาทีเพราะเป็นส่วนที่ผู้ใช้เห็น:

typescript
type ChannelBotLoopProtectionConfig = {  enabled?: boolean;  maxEventsPerWindow?: number;  windowSeconds?: number;  cooldownSeconds?: number;};

ส่งข้อเท็จจริงคู่บอทที่ทำให้เป็นมาตรฐานแล้วพร้อม turn ที่ resolve แล้ว Core จะ resolve ค่าเริ่มต้น, การแปลงหน่วย และความหมายของ enabled:

typescript
return {  channel: "example",  routeSessionKey,  storePath,  ctxPayload,  recordInboundSession,  runDispatch,  botLoopProtection: {    scopeId: "account-1",    conversationId: "channel-1",    senderId: "bot-a",    receiverId: "bot-b",    config: channelConfig.botLoopProtection,    defaultsConfig: runtimeConfig.channels?.defaults?.botLoopProtection,    defaultEnabled: allowBotsMode !== "off",  },};

ใช้ openclaw/plugin-sdk/pair-loop-guard-runtime โดยตรงเฉพาะสำหรับลูปเหตุการณ์สองฝ่ายแบบกำหนดเองที่ไม่ได้ผ่าน runner การตอบกลับขาเข้าร่วม

เนมสเปซรันไทม์

api.runtime.agent

ตัวตน Agent, ไดเรกทอรี และการจัดการเซสชัน

typescript
// Resolve the agent's working directoryconst agentDir = api.runtime.agent.resolveAgentDir(cfg); // Resolve agent workspaceconst workspaceDir = api.runtime.agent.resolveAgentWorkspaceDir(cfg); // Get agent identityconst identity = api.runtime.agent.resolveAgentIdentity(cfg); // Get default thinking levelconst thinking = api.runtime.agent.resolveThinkingDefault({  cfg,  provider,  model,}); // Validate a user-provided thinking level against the active provider profileconst policy = api.runtime.agent.resolveThinkingPolicy({ provider, model });const level = api.runtime.agent.normalizeThinkingLevel("extra high");if (level && policy.levels.some((entry) => entry.id === level)) {  // pass level to an embedded run} // Get agent timeoutconst timeoutMs = api.runtime.agent.resolveAgentTimeoutMs(cfg); // Ensure workspace existsawait api.runtime.agent.ensureAgentWorkspace(cfg); // Run an embedded agent turnconst result = await api.runtime.agent.runEmbeddedAgent({  sessionId: "my-plugin:task-1",  runId: crypto.randomUUID(),  workspaceDir: api.runtime.agent.resolveAgentWorkspaceDir(cfg),  prompt: "Summarize the latest changes",  timeoutMs: api.runtime.agent.resolveAgentTimeoutMs(cfg),});

runEmbeddedAgent(...) เป็น helper กลางสำหรับเริ่ม turn ของ Agent OpenClaw ปกติจากโค้ด Plugin โดยใช้การ resolve ผู้ให้บริการ/โมเดลและการเลือก agent-harness แบบเดียวกับการตอบกลับที่ทริกเกอร์จากช่องทาง

runEmbeddedPiAgent(...) ยังคงอยู่เป็น alias ความเข้ากันได้ที่เลิกใช้แล้วสำหรับ Plugin ที่มีอยู่ โค้ดใหม่ควรใช้ runEmbeddedAgent(...)

resolveThinkingPolicy(...) คืนระดับ thinking ที่ผู้ให้บริการ/โมเดลรองรับและค่าเริ่มต้นที่เป็นตัวเลือก Plugin ผู้ให้บริการเป็นเจ้าของโปรไฟล์เฉพาะโมเดลผ่าน hook thinking ของตน ดังนั้น Plugin เครื่องมือควรเรียก helper รันไทม์นี้แทนการนำเข้าหรือทำซ้ำรายการผู้ให้บริการ

normalizeThinkingLevel(...) แปลงข้อความผู้ใช้ เช่น on, x-high หรือ extra high เป็นระดับที่จัดเก็บตามรูปแบบ canonical ก่อนตรวจสอบกับนโยบายที่ resolve แล้ว

ตัวช่วย session store อยู่ภายใต้ api.runtime.agent.session:

typescript
const entry = api.runtime.agent.session.getSessionEntry({ agentId, sessionKey });for (const { sessionKey, entry } of api.runtime.agent.session.listSessionEntries({ agentId })) {  // Iterate session rows without depending on the legacy sessions.json shape.}await api.runtime.agent.session.patchSessionEntry({  agentId,  sessionKey,  update: (entry) => ({ thinkingLevel: "high" }),}); const storePath = api.runtime.agent.session.resolveStorePath(cfg.session?.store, { agentId });await api.runtime.agent.session.runWithWorkAdmission(  { storePath, sessionKey },  async (signal) => {    // Create or update the session, then pass signal to the admitted agent run.  },);

ควรใช้ getSessionEntry(...), listSessionEntries(...), patchSessionEntry(...) หรือ upsertSessionEntry(...) สำหรับเวิร์กโฟลว์เซสชัน helper เหล่านี้อ้างอิงเซสชันด้วยตัวตน agent/session เพื่อให้ Plugin ไม่ต้องพึ่งพารูปทรง storage sessions.json แบบเดิม ใช้ preserveActivity: true สำหรับแพตช์เฉพาะเมทาดาทาที่ไม่ควรรีเฟรชกิจกรรมเซสชัน และใช้ replaceEntry: true เฉพาะเมื่อคอลแบ็กคืน entry ที่สมบูรณ์และฟิลด์ที่ถูกลบต้องยังคงถูกลบ

ใช้ runWithWorkAdmission(...) เมื่อ Plugin เริ่มงานบนเซสชันที่คงอยู่ คอลแบ็กจะปฏิเสธเซสชันที่ถูกเก็บถาวรหรือถูกแทนที่พร้อมกัน ทำให้การกลายพันธุ์ archive/reset/delete ประสานกับการเสร็จสิ้น และได้รับ AbortSignal ที่ต้องส่งต่อไปยังการรัน Agent

สำหรับการอ่านและเขียน transcript ให้นำเข้า openclaw/plugin-sdk/session-transcript-runtime และใช้ resolveSessionTranscriptIdentity(...), resolveSessionTranscriptTarget(...), readSessionTranscriptEvents(...), appendSessionTranscriptMessageByIdentity(...), publishSessionTranscriptUpdateByIdentity(...) หรือ withSessionTranscriptWriteLock(...) พร้อม { agentId, sessionKey, sessionId } API เหล่านี้ให้ Plugin ระบุ transcript, อ่านเหตุการณ์, เพิ่มข้อความ, เผยแพร่การอัปเดต และรันการดำเนินการที่เกี่ยวข้องภายใต้ write lock ของ transcript เดียวกัน การส่ง sessionFile, การใช้ resolveSessionTranscriptLegacyFileTarget(...) หรือการนำเข้า appendSessionTranscriptMessage(...) / emitSessionTranscriptUpdate(...) ระดับต่ำจาก openclaw/plugin-sdk/agent-harness-runtime เลิกใช้แล้ว พาธเหล่านั้นมีไว้เฉพาะสำหรับโค้ดเก่าที่ได้รับ artifact transcript ที่ใช้งานอยู่แล้ว

loadSessionStore(...), saveSessionStore(...), updateSessionStore(...), resolveSessionFilePath(...) และ resolveAndPersistSessionFile(...) เป็น helper ความเข้ากันได้ที่เลิกใช้แล้วสำหรับ Plugin ที่ยังตั้งใจพึ่งพารูปทรง whole-store หรือ transcript-file แบบเดิม โค้ด Plugin ใหม่ต้องไม่ใช้ helper เหล่านั้น และผู้เรียกที่มีอยู่ควรย้ายไปยัง entry helper และ transcript identity helper

api.runtime.agent.defaults

ค่าคงที่โมเดลและผู้ให้บริการเริ่มต้น:

typescript
const model = api.runtime.agent.defaults.model; // e.g. "anthropic/claude-sonnet-4-6"const provider = api.runtime.agent.defaults.provider; // e.g. "anthropic"
api.runtime.llm

รัน text completion ที่โฮสต์เป็นเจ้าของโดยไม่ต้องนำเข้าอินเทอร์นัลของผู้ให้บริการหรือทำซ้ำการเตรียมโมเดล/การยืนยันตัวตน/base URL ของ OpenClaw

typescript
const result = await api.runtime.llm.complete({  messages: [{ role: "user", content: "Summarize this transcript." }],  purpose: "my-plugin.summary",  maxTokens: 512,  temperature: 0.2,});

helper ใช้เส้นทางการเตรียม simple-completion แบบเดียวกับรันไทม์ในตัวของ OpenClaw และสแนปช็อตการกำหนดค่ารันไทม์ที่โฮสต์เป็นเจ้าของ เอนจินบริบทได้รับ capability llm.complete ที่ผูกกับเซสชัน ดังนั้นการเรียกโมเดลจึงใช้ Agent ของเซสชันที่ใช้งานอยู่และไม่ fallback ไปยัง Agent เริ่มต้นอย่างเงียบ ๆ ผลลัพธ์มี attribution ของผู้ให้บริการ/โมเดล/Agent พร้อม token, cache และการใช้งานค่าใช้จ่ายโดยประมาณที่ทำให้เป็นมาตรฐานแล้วเมื่อมีให้ใช้งาน

api.runtime.subagent

เปิดใช้งานและจัดการการรัน subagent เบื้องหลัง

typescript
// Start a subagent runconst { runId } = await api.runtime.subagent.run({  sessionKey: "agent:main:subagent:search-helper",  message: "Expand this query into focused follow-up searches.",  provider: "openai", // optional override  model: "gpt-4.1-mini", // optional override  deliver: false,}); // Wait for completionconst result = await api.runtime.subagent.waitForRun({ runId, timeoutMs: 30000 }); // Read session messagesconst { messages } = await api.runtime.subagent.getSessionMessages({  sessionKey: "agent:main:subagent:search-helper",  limit: 10,}); // Delete a sessionawait api.runtime.subagent.deleteSession({  sessionKey: "agent:main:subagent:search-helper",});

deleteSession(...) สามารถลบเซสชันที่ Plugin เดียวกันสร้างผ่าน api.runtime.subagent.run(...) ได้ การลบเซสชันของผู้ใช้หรือผู้ปฏิบัติการใดๆ ยังต้องใช้คำขอ Gateway ที่มีขอบเขตผู้ดูแลระบบ

api.runtime.nodes

แสดงรายการโหนดที่เชื่อมต่ออยู่ และเรียกใช้คำสั่งโฮสต์โหนดจากโค้ด Plugin ที่ Gateway โหลด หรือจากคำสั่ง CLI ของ Plugin ใช้สิ่งนี้เมื่อ Plugin เป็นเจ้าของงานในเครื่องบนอุปกรณ์ที่จับคู่ไว้ เช่นบริดจ์เบราว์เซอร์หรือเสียงบน Mac เครื่องอื่น

typescript
const { nodes } = await api.runtime.nodes.list({ connected: true }); const result = await api.runtime.nodes.invoke({  nodeId: "mac-studio",  command: "my-plugin.command",  params: { action: "start" },  timeoutMs: 30000,});

ภายใน Gateway รันไทม์นี้อยู่ในกระบวนการเดียวกัน ในคำสั่ง CLI ของ Plugin รันไทม์นี้จะเรียก Gateway ที่กำหนดค่าไว้ผ่าน RPC ดังนั้นคำสั่งอย่าง openclaw googlemeet recover-tab จึงตรวจสอบโหนดที่จับคู่จากเทอร์มินัลได้ คำสั่ง Node ยังคงผ่านการจับคู่โหนด Gateway ตามปกติ รายการอนุญาตคำสั่ง นโยบาย node-invoke ของ Plugin และการจัดการคำสั่งภายในโหนด

Plugin ที่เปิดเผยคำสั่งโฮสต์โหนดที่อันตรายควรลงทะเบียนนโยบาย node-invoke ด้วย api.registerNodeInvokePolicy(...) นโยบายจะทำงานใน Gateway หลังจากตรวจสอบรายการอนุญาตคำสั่งและก่อนส่งต่อคำสั่งไปยังโหนด ดังนั้นการเรียก node.invoke โดยตรงและเครื่องมือ Plugin ระดับสูงกว่าจะใช้เส้นทางบังคับใช้นโยบายเดียวกัน

api.runtime.tasks.managedFlows

ผูกรันไทม์ Task Flow กับคีย์เซสชัน OpenClaw ที่มีอยู่หรือบริบทเครื่องมือที่เชื่อถือได้ จากนั้นสร้างและจัดการ Task Flow โดยไม่ต้องส่งเจ้าของในทุกการเรียก

Task Flow ติดตามสถานะเวิร์กโฟลว์หลายขั้นตอนที่คงทนถาวร มันไม่ใช่ตัวจัดตารางเวลา: ใช้ Cron หรือ api.session.workflow.scheduleSessionTurn(...) สำหรับการปลุกในอนาคต จากนั้นใช้ managedFlows จากเทิร์นที่จัดตารางไว้เมื่องานนั้น ต้องการสถานะโฟลว์ งานลูก การรอ หรือการยกเลิก

typescript
const taskFlow = api.runtime.tasks.managedFlows.fromToolContext(ctx); const created = taskFlow.createManaged({  controllerId: "my-plugin/review-batch",  goal: "Review new pull requests",}); const child = taskFlow.runTask({  flowId: created.flowId,  runtime: "acp",  childSessionKey: "agent:main:subagent:reviewer",  task: "Review PR #123",  status: "running",  startedAt: Date.now(),}); const waiting = taskFlow.setWaiting({  flowId: created.flowId,  expectedRevision: created.revision,  currentStep: "await-human-reply",  waitJson: { kind: "reply", channel: "telegram" },});

ใช้ bindSession({ sessionKey, requesterOrigin }) เมื่อคุณมีคีย์เซสชัน OpenClaw ที่เชื่อถือได้จากชั้นการผูกของคุณเองอยู่แล้ว อย่าผูกจากอินพุตผู้ใช้ดิบ

api.runtime.tts

การสังเคราะห์ข้อความเป็นเสียงพูด

typescript
// Standard TTSconst clip = await api.runtime.tts.textToSpeech({  text: "Hello from OpenClaw",  cfg: api.config,}); // Telephony-optimized TTSconst telephonyClip = await api.runtime.tts.textToSpeechTelephony({  text: "Hello from OpenClaw",  cfg: api.config,}); // List available voicesconst voices = await api.runtime.tts.listVoices({  provider: "elevenlabs",  cfg: api.config,});

ใช้การกำหนดค่า messages.tts ของแกนหลักและการเลือกผู้ให้บริการ ส่งคืนบัฟเฟอร์เสียง PCM + อัตราตัวอย่าง

api.runtime.mediaUnderstanding

การวิเคราะห์รูปภาพ เสียง และวิดีโอ

typescript
// Describe an imageconst image = await api.runtime.mediaUnderstanding.describeImageFile({  filePath: "/tmp/inbound-photo.jpg",  cfg: api.config,  agentDir: "/tmp/agent",}); // Transcribe audioconst { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({  filePath: "/tmp/inbound-audio.ogg",  cfg: api.config,  mime: "audio/ogg", // optional, for when MIME cannot be inferred}); // Describe a videoconst video = await api.runtime.mediaUnderstanding.describeVideoFile({  filePath: "/tmp/inbound-video.mp4",  cfg: api.config,}); // Generic file analysisconst result = await api.runtime.mediaUnderstanding.runFile({  filePath: "/tmp/inbound-file.pdf",  cfg: api.config,}); // Structured image extraction through a specific provider/model.// Include at least one image; text inputs are supplemental context.const evidence = await api.runtime.mediaUnderstanding.extractStructuredWithModel({  provider: "codex",  model: "gpt-5.5",  input: [    {      type: "image",      buffer: receiptImageBuffer,      fileName: "receipt.png",      mime: "image/png",    },    { type: "text", text: "Prefer the printed total over handwritten notes." },  ],  instructions: "Extract vendor, total, and searchable tags.",  schemaName: "receipt.evidence",  jsonSchema: {    type: "object",    properties: {      vendor: { type: "string" },      total: { type: "number" },      tags: { type: "array", items: { type: "string" } },    },    required: ["vendor", "total"],  },  cfg: api.config,});

ส่งคืน { text: undefined } เมื่อไม่มีเอาต์พุตที่สร้างขึ้น (เช่น อินพุตที่ถูกข้าม)

api.runtime.imageGeneration

การสร้างรูปภาพ

typescript
const result = await api.runtime.imageGeneration.generate({  prompt: "A robot painting a sunset",  cfg: api.config,}); const providers = api.runtime.imageGeneration.listProviders({ cfg: api.config });
api.runtime.webSearch

การค้นหาเว็บ

typescript
const providers = api.runtime.webSearch.listProviders({ config: api.config }); const result = await api.runtime.webSearch.search({  config: api.config,  args: { query: "OpenClaw plugin SDK", count: 5 },});
api.runtime.media

ยูทิลิตีสื่อระดับต่ำ

typescript
const webMedia = await api.runtime.media.loadWebMedia(url);const mime = await api.runtime.media.detectMime(buffer);const kind = api.runtime.media.mediaKindFromMime("image/jpeg"); // "image"const isVoice = api.runtime.media.isVoiceCompatibleAudio(filePath);const metadata = await api.runtime.media.getImageMetadata(filePath);const resized = await api.runtime.media.resizeToJpeg(buffer, { maxWidth: 800 });const terminalQr = await api.runtime.media.renderQrTerminal("https://openclaw.ai");const pngQr = await api.runtime.media.renderQrPngBase64("https://openclaw.ai", {  scale: 6, // 1-12  marginModules: 4, // 0-16});const pngQrDataUrl = await api.runtime.media.renderQrPngDataUrl("https://openclaw.ai");const tmpRoot = resolvePreferredOpenClawTmpDir();const pngQrFile = await api.runtime.media.writeQrPngTempFile("https://openclaw.ai", {  tmpRoot,  dirPrefix: "my-plugin-qr-",  fileName: "qr.png",});
api.runtime.config

สแนปช็อตการกำหนดค่ารันไทม์ปัจจุบันและการเขียนการกำหนดค่าแบบทรานแซกชัน ควรใช้ การกำหนดค่าที่ส่งเข้าเส้นทางการเรียกที่ใช้งานอยู่แล้ว ใช้ current() เฉพาะเมื่อแฮนด์เลอร์ต้องการสแนปช็อตของกระบวนการโดยตรง

typescript
const cfg = api.runtime.config.current();await api.runtime.config.mutateConfigFile({  afterWrite: { mode: "auto" },  mutate(draft) {    draft.plugins ??= {};  },});

mutateConfigFile(...) และ replaceConfigFile(...) ส่งคืนค่า followUp เช่น { mode: "restart", requiresRestart: true, reason } ซึ่งบันทึกเจตนาของตัวเขียนโดยไม่ดึงการควบคุมการรีสตาร์ตออกจาก gateway

api.runtime.system

ยูทิลิตีระดับระบบ

typescript
await api.runtime.system.enqueueSystemEvent(event);api.runtime.system.requestHeartbeat({  source: "other",  intent: "event",  reason: "plugin-event",});api.runtime.system.requestHeartbeatNow({ reason: "plugin-event" }); // Deprecated compatibility alias.const output = await api.runtime.system.runCommandWithTimeout(cmd, args, opts);const hint = api.runtime.system.formatNativeDependencyHint(pkg);

runCommandWithTimeout(...) ส่งคืน stdout และ stderr ที่จับไว้ จำนวน การตัดทอนที่เป็นทางเลือก, code, signal, killed, termination และ noOutputTimedOut ผลลัพธ์ timeout และ no-output-timeout จะรายงาน code: 124 เมื่อกระบวนการลูกไม่ได้ให้รหัสออกที่ไม่เป็นศูนย์ การออกด้วยสัญญาณ ที่ไม่ใช่ timeout ยังสามารถส่งคืน code: null ได้ ดังนั้นให้ใช้ termination และ noOutputTimedOut เพื่อแยกเหตุผลของ timeout

api.runtime.events

การสมัครรับเหตุการณ์

typescript
api.runtime.events.onAgentEvent((event) => {  /* ... */});api.runtime.events.onSessionTranscriptUpdate((update) => {  /* ... */});
api.runtime.logging

การบันทึกล็อก

typescript
const verbose = api.runtime.logging.shouldLogVerbose();const childLogger = api.runtime.logging.getChildLogger({ plugin: "my-plugin" }, { level: "debug" });
api.runtime.modelAuth

การแก้ไขสิทธิ์ยืนยันตัวตนของโมเดลและผู้ให้บริการ

typescript
const auth = await api.runtime.modelAuth.getApiKeyForModel({ model, cfg });const providerAuth = await api.runtime.modelAuth.resolveApiKeyForProvider({  provider: "openai",  cfg,});
api.runtime.state

การระบุไดเรกทอรีสถานะและพื้นที่จัดเก็บแบบคีย์ที่รองรับด้วย SQLite

typescript
const stateDir = api.runtime.state.resolveStateDir(process.env);const store = api.runtime.state.openKeyedStore<MyRecord>({  namespace: "my-feature",  maxEntries: 200,  defaultTtlMs: 15 * 60_000,}); await store.register("key-1", { value: "hello" });const claimed = await store.registerIfAbsent("dedupe-key", { value: "first" });const value = await store.lookup("key-1");await store.consume("key-1");await store.clear();

พื้นที่จัดเก็บแบบคีย์ยังคงอยู่หลังรีสตาร์ตและถูกแยกตามรหัส Plugin ที่ผูกกับรันไทม์ ใช้ registerIfAbsent(...) สำหรับการอ้างสิทธิ์ dedupe แบบอะตอมิก: จะคืนค่า true เมื่อคีย์ไม่มีอยู่หรือหมดอายุแล้วและถูกลงทะเบียน หรือ false เมื่อมีค่าที่ยังใช้งานอยู่แล้วโดยไม่เขียนทับค่า เวลาในการสร้าง หรือ TTL ของค่านั้น ข้อจำกัด: maxEntries ต่อ namespace, แถวที่ใช้งานอยู่ 6,000 แถวต่อ Plugin, ค่า JSON ต่ำกว่า 64KB และการหมดอายุ TTL แบบไม่บังคับ เมื่อการเขียนจะทำให้เกินเพดานจำนวนแถวของ Plugin รันไทม์อาจขับแถวที่ใช้งานอยู่ที่เก่าที่สุดออกจาก namespace ที่กำลังเขียนอยู่; namespace พี่น้องจะไม่ถูกขับออกสำหรับการเขียนนั้น และการเขียนยังคงล้มเหลวหาก namespace ไม่สามารถคืนพื้นที่แถวได้เพียงพอ

api.runtime.tools

แฟกทอรีเครื่องมือหน่วยความจำและ CLI

typescript
const getTool = api.runtime.tools.createMemoryGetTool(/* ... */);const searchTool = api.runtime.tools.createMemorySearchTool(/* ... */);api.runtime.tools.registerMemoryCli(/* ... */);
api.runtime.channel

ตัวช่วยรันไทม์เฉพาะช่องทาง (พร้อมใช้งานเมื่อโหลด Plugin ช่องทางแล้ว)

api.runtime.channel.media คือพื้นผิวที่แนะนำสำหรับการดาวน์โหลดและจัดเก็บสื่อของช่องทาง:

typescript
const saved = await api.runtime.channel.media.saveRemoteMedia({  url,  subdir: "inbound",  maxBytes,  filePathHint: fileName,});

ใช้ saveRemoteMedia(...) เมื่อ URL ระยะไกลควรถูกแปลงเป็นสื่อของ OpenClaw ใช้ saveResponseMedia(...) เมื่อ Plugin ดึง Response มาแล้วด้วยการจัดการ auth, redirect หรือ allowlist ที่ Plugin เป็นเจ้าของ ใช้ readRemoteMediaBuffer(...) เฉพาะเมื่อ Plugin ต้องการไบต์ดิบสำหรับการตรวจสอบ การแปลง การถอดรหัส หรือการอัปโหลดซ้ำ fetchRemoteMedia(...) ยังคงเป็น alias ความเข้ากันได้ที่เลิกใช้แล้วสำหรับ readRemoteMediaBuffer(...)

api.runtime.channel.mentions คือพื้นผิวนโยบายการ mention ขาเข้าที่ใช้ร่วมกันสำหรับ Plugin ช่องทางที่รวมมาและใช้การฉีดรันไทม์:

typescript
const mentionMatch = api.runtime.channel.mentions.matchesMentionWithExplicit(text, {  mentionRegexes,  mentionPatterns,}); const decision = api.runtime.channel.mentions.resolveInboundMentionDecision({  facts: {    canDetectMention: true,    wasMentioned: mentionMatch.matched,    implicitMentionKinds: api.runtime.channel.mentions.implicitMentionKindWhen(      "reply_to_bot",      isReplyToBot,    ),  },  policy: {    isGroup,    requireMention,    allowTextCommands,    hasControlCommand,    commandAuthorized,  },});

ตัวช่วย mention ที่พร้อมใช้งาน:

  • buildMentionRegexes
  • matchesMentionPatterns
  • matchesMentionWithExplicit
  • implicitMentionKindWhen
  • resolveInboundMentionDecision

api.runtime.channel.mentions ตั้งใจไม่เปิดเผยตัวช่วยความเข้ากันได้ resolveMentionGating* รุ่นเก่า แนะนำให้ใช้เส้นทาง { facts, policy } ที่ทำให้อยู่ในรูปแบบมาตรฐานแล้ว

การจัดเก็บการอ้างอิงรันไทม์

ใช้ createPluginRuntimeStore เพื่อจัดเก็บการอ้างอิงรันไทม์สำหรับใช้นอก callback register:

  • Create the store

    typescript
    import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";import type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store"; const store = createPluginRuntimeStore<PluginRuntime>({  pluginId: "my-plugin",  errorMessage: "my-plugin runtime not initialized",});
  • Wire into the entry point

    typescript
    export default defineChannelPluginEntry({  id: "my-plugin",  name: "My Plugin",  description: "Example",  plugin: myPlugin,  setRuntime: store.setRuntime,});
  • Access from other files

    typescript
    export function getRuntime() {  return store.getRuntime(); // throws if not initialized} export function tryGetRuntime() {  return store.tryGetRuntime(); // returns null if not initialized}
  • ฟิลด์ api ระดับบนอื่น ๆ

    นอกเหนือจาก api.runtime แล้ว ออบเจ็กต์ API ยังมีสิ่งต่อไปนี้ด้วย:

    api.idstring

    รหัส Plugin

    api.namestring

    ชื่อที่แสดงของ Plugin

    api.configOpenClawConfig

    สแนปชอต config ปัจจุบัน (สแนปชอตรันไทม์ในหน่วยความจำที่ใช้งานอยู่เมื่อพร้อมใช้งาน)

    OPENCLAW_DOCS_MARKER:paramOpen:IHBhdGg9ImFwaS5wbHVnaW5Db25maWciIHR5cGU9IlJlY29yZDxzdHJpbmcsIHVua25vd24 "> config เฉพาะ Plugin จาก plugins.entries.<id>.config

    api.loggerPluginLogger

    logger แบบจำกัดขอบเขต (debug, info, warn, error)

    api.registrationModePluginRegistrationMode

    โหมดการโหลดปัจจุบัน; "setup-runtime" คือช่วงเริ่มต้น/ตั้งค่าแบบเบาก่อนเข้า entry เต็มรูปแบบ

    api.resolvePath(input)"(string)

    ที่เกี่ยวข้อง

    Was this useful?
    On this page

    On this page