fix: improve Windows compatibility and fix event listener issues (#1102)
Replace platform-specific 'which'/'where' commands with cross-platform Bun.which() API to fix Windows compatibility issues and simplify code. Fixes: - #1027: Comment-checker binary crashes on Windows (missing 'check' subcommand) - #1036: Session-notification listens to non-existent events - #1033: Infinite loop in session notifications - #599: Doctor incorrectly reports OpenCode as not installed on Windows - #1005: PowerShell path detection corruption on Windows Changes: - Use Bun.which() instead of spawning 'which'/'where' commands - Add 'check' subcommand to comment-checker invocation - Remove non-existent event listeners (session.updated, message.created) - Prevent notification commands from resetting their own state - Fix edge case: clear notifiedSessions if activity occurs during notification All changes are cross-platform compatible and tested on Windows/Linux/macOS.
This commit is contained in:
parent
ffbca5e48e
commit
011eb48ffd
@ -3,11 +3,9 @@ import { CHECK_IDS, CHECK_NAMES } from "../constants"
|
|||||||
|
|
||||||
async function checkBinaryExists(binary: string): Promise<{ exists: boolean; path: string | null }> {
|
async function checkBinaryExists(binary: string): Promise<{ exists: boolean; path: string | null }> {
|
||||||
try {
|
try {
|
||||||
const proc = Bun.spawn(["which", binary], { stdout: "pipe", stderr: "pipe" })
|
const path = Bun.which(binary)
|
||||||
const output = await new Response(proc.stdout).text()
|
if (path) {
|
||||||
await proc.exited
|
return { exists: true, path }
|
||||||
if (proc.exitCode === 0) {
|
|
||||||
return { exists: true, path: output.trim() }
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// intentionally empty - binary not found
|
// intentionally empty - binary not found
|
||||||
|
|||||||
@ -55,16 +55,9 @@ export function buildVersionCommand(
|
|||||||
export async function findOpenCodeBinary(): Promise<{ binary: string; path: string } | null> {
|
export async function findOpenCodeBinary(): Promise<{ binary: string; path: string } | null> {
|
||||||
for (const binary of OPENCODE_BINARIES) {
|
for (const binary of OPENCODE_BINARIES) {
|
||||||
try {
|
try {
|
||||||
const lookupCommand = getBinaryLookupCommand(process.platform)
|
const path = Bun.which(binary)
|
||||||
const proc = Bun.spawn([lookupCommand, binary], { stdout: "pipe", stderr: "pipe" })
|
if (path) {
|
||||||
const output = await new Response(proc.stdout).text()
|
return { binary, path }
|
||||||
await proc.exited
|
|
||||||
if (proc.exitCode === 0) {
|
|
||||||
const paths = parseBinaryPaths(output)
|
|
||||||
const selectedPath = selectBinaryPath(paths, process.platform)
|
|
||||||
if (selectedPath) {
|
|
||||||
return { binary, path: selectedPath }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -165,7 +165,7 @@ export async function runCommentChecker(input: HookInput, cliPath?: string, cust
|
|||||||
debugLog("running comment-checker with input:", jsonInput.substring(0, 200))
|
debugLog("running comment-checker with input:", jsonInput.substring(0, 200))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const args = [binaryPath]
|
const args = [binaryPath, "check"]
|
||||||
if (customPrompt) {
|
if (customPrompt) {
|
||||||
args.push("--prompt", customPrompt)
|
args.push("--prompt", customPrompt)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,28 +3,8 @@ import { spawn } from "bun"
|
|||||||
type Platform = "darwin" | "linux" | "win32" | "unsupported"
|
type Platform = "darwin" | "linux" | "win32" | "unsupported"
|
||||||
|
|
||||||
async function findCommand(commandName: string): Promise<string | null> {
|
async function findCommand(commandName: string): Promise<string | null> {
|
||||||
const isWindows = process.platform === "win32"
|
|
||||||
const cmd = isWindows ? "where" : "which"
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const proc = spawn([cmd, commandName], {
|
return Bun.which(commandName)
|
||||||
stdout: "pipe",
|
|
||||||
stderr: "pipe",
|
|
||||||
})
|
|
||||||
|
|
||||||
const exitCode = await proc.exited
|
|
||||||
if (exitCode !== 0) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const stdout = await new Response(proc.stdout).text()
|
|
||||||
const path = stdout.trim().split("\n")[0]
|
|
||||||
|
|
||||||
if (!path) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return path
|
|
||||||
} catch {
|
} catch {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@ -200,7 +200,9 @@ export function createSessionNotification(
|
|||||||
|
|
||||||
function markSessionActivity(sessionID: string) {
|
function markSessionActivity(sessionID: string) {
|
||||||
cancelPendingNotification(sessionID)
|
cancelPendingNotification(sessionID)
|
||||||
notifiedSessions.delete(sessionID)
|
if (!executingNotifications.has(sessionID)) {
|
||||||
|
notifiedSessions.delete(sessionID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function executeNotification(sessionID: string, version: number) {
|
async function executeNotification(sessionID: string, version: number) {
|
||||||
@ -254,6 +256,11 @@ export function createSessionNotification(
|
|||||||
} finally {
|
} finally {
|
||||||
executingNotifications.delete(sessionID)
|
executingNotifications.delete(sessionID)
|
||||||
pendingTimers.delete(sessionID)
|
pendingTimers.delete(sessionID)
|
||||||
|
// Clear notified state if there was activity during notification
|
||||||
|
if (sessionActivitySinceIdle.has(sessionID)) {
|
||||||
|
notifiedSessions.delete(sessionID)
|
||||||
|
sessionActivitySinceIdle.delete(sessionID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,7 +269,7 @@ export function createSessionNotification(
|
|||||||
|
|
||||||
const props = event.properties as Record<string, unknown> | undefined
|
const props = event.properties as Record<string, unknown> | undefined
|
||||||
|
|
||||||
if (event.type === "session.updated" || event.type === "session.created") {
|
if (event.type === "session.created") {
|
||||||
const info = props?.info as Record<string, unknown> | undefined
|
const info = props?.info as Record<string, unknown> | undefined
|
||||||
const sessionID = info?.id as string | undefined
|
const sessionID = info?.id as string | undefined
|
||||||
if (sessionID) {
|
if (sessionID) {
|
||||||
@ -299,7 +306,7 @@ export function createSessionNotification(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type === "message.updated" || event.type === "message.created") {
|
if (event.type === "message.updated") {
|
||||||
const info = props?.info as Record<string, unknown> | undefined
|
const info = props?.info as Record<string, unknown> | undefined
|
||||||
const sessionID = info?.sessionID as string | undefined
|
const sessionID = info?.sessionID as string | undefined
|
||||||
if (sessionID) {
|
if (sessionID) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user