fix(#2855): tmux health check fails across module instances in same process

The server-health module used module-level state for inProcessServerRunning,
which doesn't survive when Bun loads separate module instances in the same
process. Fix: use globalThis with Symbol.for key so the flag is truly
process-global.

- server-health.ts: replace module-level boolean with globalThis[Symbol.for()]
- export markServerRunningInProcess from tmux-utils barrel
- test: verify flag skips HTTP fetch, verify globalThis persistence
This commit is contained in:
YeonGyu-Kim
2026-03-27 14:17:06 +09:00
parent 19ab3b5656
commit 7ce7a85768
3 changed files with 50 additions and 4 deletions

View File

@@ -3,6 +3,7 @@ import {
isInsideTmux,
isServerRunning,
resetServerCheck,
markServerRunningInProcess,
spawnTmuxPane,
closeTmuxPane,
applyLayout,
@@ -165,6 +166,46 @@ describe("resetServerCheck", () => {
})
})
describe("markServerRunningInProcess", () => {
const originalFetch = globalThis.fetch
const SERVER_RUNNING_KEY = Symbol.for("oh-my-opencode:server-running-in-process")
beforeEach(() => {
resetServerCheck()
delete (globalThis as Record<symbol, boolean>)[SERVER_RUNNING_KEY]
})
afterEach(() => {
globalThis.fetch = originalFetch
delete (globalThis as Record<symbol, boolean>)[SERVER_RUNNING_KEY]
})
test("skips HTTP fetch when marked as running in-process", async () => {
// given
const fetchMock = mock(async () => ({ ok: true })) as any
globalThis.fetch = fetchMock
markServerRunningInProcess()
// when
const result = await isServerRunning("http://localhost:4096")
// then
expect(result).toBe(true)
expect(fetchMock.mock.calls.length).toBe(0)
})
test("uses globalThis so flag survives across module instances", () => {
// given
markServerRunningInProcess()
// when
const flag = (globalThis as Record<symbol, boolean>)[SERVER_RUNNING_KEY]
// then
expect(flag).toBe(true)
})
})
describe("tmux pane functions", () => {
test("spawnTmuxPane is exported as function", async () => {
// given, #when

View File

@@ -1,7 +1,7 @@
export { isInsideTmux, getCurrentPaneId } from "./tmux-utils/environment"
export type { SplitDirection } from "./tmux-utils/environment"
export { isServerRunning, resetServerCheck } from "./tmux-utils/server-health"
export { isServerRunning, resetServerCheck, markServerRunningInProcess } from "./tmux-utils/server-health"
export { getPaneDimensions } from "./tmux-utils/pane-dimensions"
export type { PaneDimensions } from "./tmux-utils/pane-dimensions"

View File

@@ -1,17 +1,22 @@
let serverAvailable: boolean | null = null
let serverCheckUrl: string | null = null
let inProcessServerRunning = false
const SERVER_RUNNING_KEY = Symbol.for("oh-my-opencode:server-running-in-process")
function delay(milliseconds: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, milliseconds))
}
export function markServerRunningInProcess(): void {
inProcessServerRunning = true
;(globalThis as Record<symbol, boolean>)[SERVER_RUNNING_KEY] = true
}
function isMarkedRunningInProcess(): boolean {
return (globalThis as Record<symbol, boolean>)[SERVER_RUNNING_KEY] === true
}
export async function isServerRunning(serverUrl: string): Promise<boolean> {
if (inProcessServerRunning) {
if (isMarkedRunningInProcess()) {
return true
}