fix: prevent double Matrix bot startup (server + client)

OpenCode V2 loads plugins in both the background server and the client.
Add a file-based mutex (/tmp/opencode-matrix-plugin.lock) so only the
first instance (server) starts the Matrix bot. The second instance
(client) detects the lock and returns a no-op cleanup.
This commit is contained in:
Бородин Роман 2026-09-26 00:09:28 +03:00
parent 51b98e02a5
commit f981998ab8
1 changed files with 50 additions and 3 deletions

View File

@ -6,11 +6,58 @@ import { log } from "./logger.js"
log("Plugin module loaded")
// =============================================================================
// Global mutex — prevents double startup (server + client both load plugins)
// =============================================================================
const LOCK_FILE = "/tmp/opencode-matrix-plugin.lock"
function tryAcquireLock(): boolean {
try {
// Check if an existing lock is stale (process no longer running)
if (fs.existsSync(LOCK_FILE)) {
const existingPid = parseInt(fs.readFileSync(LOCK_FILE, "utf-8").trim(), 10)
if (existingPid && existingPid !== process.pid) {
try {
process.kill(existingPid, 0) // throws if process doesn't exist
} catch {
// Process dead, remove stale lock
fs.unlinkSync(LOCK_FILE)
}
} else if (existingPid === process.pid) {
// Same process re-entering (shouldn't happen, but be safe)
return false
} else {
return false // Valid lock held by another process
}
}
fs.writeFileSync(LOCK_FILE, String(process.pid), { mode: 0o644 })
return true
} catch {
return false
}
}
function releaseLock(): void {
try {
fs.unlinkSync(LOCK_FILE)
} catch {
// ignore
}
}
// =============================================================================
// Plugin Setup
// =============================================================================
async function runPlugin(ctx: any, options: any) {
// Acquire global lock — only one instance (server or client) starts the bot
const locked = tryAcquireLock()
if (!locked) {
log("Another instance already running (lock held), skipping bot startup")
return () => {}
}
const eventController = new AbortController()
let cleanup: (() => void) | undefined
@ -213,15 +260,15 @@ async function runPlugin(ctx: any, options: any) {
log("Plugin cleaning up...")
eventController.abort()
matrix.stop()
releaseLock()
}
return cleanup
} catch (err) {
fs.appendFileSync("/tmp/opencode-matrix-plugin/setup-error.log", `[${new Date().toISOString()}] ${String(err)}\n`)
log(`Setup error: ${String(err)}`)
return () => {
cleanup?.()
}
releaseLock()
return () => {}
}
}