From 1d85ab86baf16b970a78e28bad00f2785b62cab7 Mon Sep 17 00:00:00 2001 From: pi-bot-01 Date: Fri, 13 Mar 2026 18:19:00 -0700 Subject: [PATCH] feat: auto-detect pi-bot vs openclaw runtime for event delivery - pi-bot: uses ctx.sendUserMessage (persistent session, followUp mode) - openclaw: POSTs to /hooks/agent endpoint (request/response model) - Detection: checks if ctx.sendUserMessage exists on session_start - openclaw env vars: OPENCLAW_HOOKS_URL, OPENCLAW_HOOKS_PATH, OPENCLAW_HOOKS_TOKEN - Same extension, tools, poller, and @mention routing work in both runtimes --- pi-extension/index.ts | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/pi-extension/index.ts b/pi-extension/index.ts index db53fcf..94c4de6 100644 --- a/pi-extension/index.ts +++ b/pi-extension/index.ts @@ -78,11 +78,35 @@ export default function (pi: ExtensionAPI) { pi.on("session_start", async (_event, ctx) => { console.log("[pi-gitea] Session started"); - const sendMessageFn = (msg: string) => { - ctx.sendUserMessage(msg, { deliverAs: "followUp" }); - return Promise.resolve(); - }; - setSendMessage(sendMessageFn); + // Auto-detect runtime: pi-bot (persistent session) vs openclaw (hooks endpoint) + if (ctx.sendUserMessage) { + // pi-bot: inject directly into the running session + console.log("[pi-gitea] Delivery: sendUserMessage (pi-bot mode)"); + setSendMessage((msg: string) => { + ctx.sendUserMessage(msg, { deliverAs: "followUp" }); + return Promise.resolve(); + }); + } else { + // openclaw: POST to the hooks endpoint + const hooksUrl = process.env.OPENCLAW_HOOKS_URL ?? "http://localhost:3001"; + const hooksPath = process.env.OPENCLAW_HOOKS_PATH ?? "/hooks/agent"; + const hooksToken = process.env.OPENCLAW_HOOKS_TOKEN ?? ""; + console.log(`[pi-gitea] Delivery: openclaw hooks (${hooksUrl}${hooksPath})`); + setSendMessage(async (msg: string) => { + const res = await fetch(`${hooksUrl}${hooksPath}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + ...(hooksToken ? { Authorization: `Bearer ${hooksToken}` } : {}), + }, + body: JSON.stringify({ message: msg }), + }); + if (!res.ok) { + const body = await res.text().catch(() => ""); + throw new Error(`Hooks POST failed: ${res.status} ${body}`); + } + }); + } try { await startWebhookServer(pi);