From 3cf3b137cb919c083c22bbc0da34ab5a40a3e808 Mon Sep 17 00:00:00 2001 From: Peeyush Aggarwal Date: Tue, 15 Sep 2026 18:22:02 -0700 Subject: [PATCH] feat: support OpenCode v2 --- README.md | 13 +++++++- index.ts | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + tsconfig.json | 1 + 4 files changed, 100 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f734beb..d7fc126 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,18 @@ systemd-inhibit --list ## Install -Add to your `opencode.json`: +The package supports both OpenCode v1 and v2. Use the configuration key for +the OpenCode version you run. + +OpenCode v2: + +```json +{ + "plugins": ["opencode-wakelock"] +} +``` + +OpenCode v1: ```json { diff --git a/index.ts b/index.ts index 880a597..6915051 100644 --- a/index.ts +++ b/index.ts @@ -1,4 +1,8 @@ import type { Plugin } from "@opencode-ai/plugin"; +import type { + Context as PluginContextV2, + Plugin as PluginV2, +} from "@opencode/plugin/promise/plugin"; import { existsSync, mkdirSync, @@ -305,4 +309,85 @@ const wakelockPlugin: Plugin = async ({ client }) => { }; }; -export default { id: "opencode-wakelock", server: wakelockPlugin }; +const wakelockPluginV2: PluginV2 = { + id: "opencode-wakelock", + async setup(ctx: PluginContextV2) { + const platform = detectPlatform(); + if (platform === null) return; + + startupCleanup(platform); + + const log: LogFn = async (level, message, extra) => { + const details = extra ? ` ${JSON.stringify(extra)}` : ""; + const line = `[opencode-wakelock] ${message}${details}`; + if (level === "warn") console.warn(line); + else if (level === "debug") console.debug(line); + else console.info(line); + }; + const waitingSessions = new Set(); + + const watchUntilIdle = (sessionID: string) => { + if (waitingSessions.has(sessionID)) return; + waitingSessions.add(sessionID); + void ctx.session.wait({ sessionID }) + .catch(() => undefined) + .finally(() => { + waitingSessions.delete(sessionID); + release(sessionID, platform); + }); + }; + + // The context hook is the reliable point at which a v2 model dispatch is + // active. session.wait() follows it through every terminal path, including + // cases where the public lifecycle stream does not emit an idle event. + await ctx.session.hook("context", (request) => { + acquire(request.sessionID, platform, log); + watchUntilIdle(request.sessionID); + }); + + const controller = new AbortController(); + const watcher = (async () => { + try { + for await (const event of ctx.event.subscribe({ signal: controller.signal })) { + const data = "data" in event && event.data && typeof event.data === "object" + ? event.data as { sessionID?: string; status?: { type?: string } } + : undefined; + const sessionID = data?.sessionID; + if (!sessionID) continue; + if (event.type === "session.status" && data.status?.type === "busy") { + acquire(sessionID, platform, log); + watchUntilIdle(sessionID); + } else if ( + event.type === "session.idle" || + event.type === "session.execution.failed" || + event.type === "session.execution.interrupted" + ) { + waitingSessions.delete(sessionID); + release(sessionID, platform); + } + } + } catch (error) { + if (!controller.signal.aborted) { + await log("warn", "Event subscription stopped", { + error: error instanceof Error ? error.message : String(error), + }); + } + } + })(); + + return async () => { + controller.abort(); + await watcher; + for (const sessionID of getActiveSessions()) { + const filePath = `${SESSIONS_DIR}/${sessionID}`; + try { + if (Number(readFileSync(filePath, "utf8").trim()) === process.pid) { + release(sessionID, platform); + } + } catch {} + } + }; + }, +}; + +export default { ...wakelockPluginV2, server: wakelockPlugin }; diff --git a/package.json b/package.json index 170d987..88a0ae0 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "prepack": "npm run build" }, "devDependencies": { + "@opencode/plugin": "^2.0.3", "@opencode-ai/plugin": "latest", "bun-types": "latest", "typescript": "7.0.2" diff --git a/tsconfig.json b/tsconfig.json index 1dc7ace..e1d4f76 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "moduleResolution": "bundler", "outDir": "dist", "strict": true, + "skipLibCheck": true, "types": ["bun-types"] } }