* revert: undo PR #543 changes (bun shell GC crash was misdiagnosed) This reverts commit 4a38e70 (PR #543) and 2064568 (follow-up fix). ## Why This Revert The original diagnosis was incorrect. PR #543 assumed Bun's ShellInterpreter GC bug was causing Windows crashes, but further investigation revealed the actual root cause: **The crash occurs when oh-my-opencode's session-notification runs alongside external notification plugins (e.g., @mohak34/opencode-notifier).** Evidence: - User removed opencode-notifier plugin → crashes stopped - Release version (with original ctx.$ code) works fine when used alone - No widespread crash reports from users without external notifiers - Both plugins listen to session.idle and send concurrent notifications The real issue is a conflict between two notification systems: 1. oh-my-opencode: ctx.$ → PowerShell → Windows.UI.Notifications 2. opencode-notifier: node-notifier → SnoreToast.exe A proper fix will detect and handle this conflict gracefully. Refs: #543, oven-sh/bun#23177, oven-sh/bun#24368 See: docs/CRASH_INVESTIGATION_TIMELINE.md (in follow-up commit) * fix(session-notification): detect and avoid conflict with external notification plugins When oh-my-opencode's session-notification runs alongside external notification plugins like opencode-notifier, both listen to session.idle and send concurrent notifications. This can cause crashes on Windows due to resource contention between different notification mechanisms: - oh-my-opencode: ctx.$ → PowerShell → Windows.UI.Notifications - opencode-notifier: node-notifier → SnoreToast.exe This commit adds: 1. External plugin detection (checks opencode.json for known notifiers) 2. Auto-disable of session-notification when conflict detected 3. Console warning explaining the situation 4. Config option 'notification.force_enable' to override Known notification plugins detected: - opencode-notifier - @mohak34/opencode-notifier - mohak34/opencode-notifier This is the actual fix for the Windows crash issue previously misdiagnosed as a Bun.spawn GC bug (PR #543). Refs: #543 * docs: add crash investigation timeline explaining the real root cause Documents the investigation journey from initial misdiagnosis (Bun GC bug) to discovering the actual root cause (notification plugin conflict). Key findings: - PR #543 was based on incorrect assumption - The real issue is concurrent notification plugins - oh-my-opencode + opencode-notifier = crash on Windows - Either plugin alone works fine * fix: address review feedback - add PowerShell escaping and use existing JSONC parser - Add back single-quote escaping for PowerShell soundPath to prevent command failures - Replace custom stripJsonComments with existing parseJsoncSafe from jsonc-parser - All 655 tests pass --------- Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
133 lines
3.8 KiB
TypeScript
133 lines
3.8 KiB
TypeScript
/**
|
|
* Detects external plugins that may conflict with oh-my-opencode features.
|
|
* Used to prevent crashes from concurrent notification plugins.
|
|
*/
|
|
|
|
import * as fs from "node:fs"
|
|
import * as path from "node:path"
|
|
import * as os from "node:os"
|
|
import { log } from "./logger"
|
|
import { parseJsoncSafe } from "./jsonc-parser"
|
|
|
|
interface OpencodeConfig {
|
|
plugin?: string[]
|
|
}
|
|
|
|
/**
|
|
* Known notification plugins that conflict with oh-my-opencode's session-notification.
|
|
* Both plugins listen to session.idle and send notifications simultaneously,
|
|
* which can cause crashes on Windows due to resource contention.
|
|
*/
|
|
const KNOWN_NOTIFICATION_PLUGINS = [
|
|
"opencode-notifier",
|
|
"@mohak34/opencode-notifier",
|
|
"mohak34/opencode-notifier",
|
|
]
|
|
|
|
function getWindowsAppdataDir(): string | null {
|
|
return process.env.APPDATA || null
|
|
}
|
|
|
|
function getConfigPaths(directory: string): string[] {
|
|
const crossPlatformDir = path.join(os.homedir(), ".config")
|
|
const paths = [
|
|
path.join(directory, ".opencode", "opencode.json"),
|
|
path.join(directory, ".opencode", "opencode.jsonc"),
|
|
path.join(crossPlatformDir, "opencode", "opencode.json"),
|
|
path.join(crossPlatformDir, "opencode", "opencode.jsonc"),
|
|
]
|
|
|
|
if (process.platform === "win32") {
|
|
const appdataDir = getWindowsAppdataDir()
|
|
if (appdataDir) {
|
|
paths.push(path.join(appdataDir, "opencode", "opencode.json"))
|
|
paths.push(path.join(appdataDir, "opencode", "opencode.jsonc"))
|
|
}
|
|
}
|
|
|
|
return paths
|
|
}
|
|
|
|
function loadOpencodePlugins(directory: string): string[] {
|
|
for (const configPath of getConfigPaths(directory)) {
|
|
try {
|
|
if (!fs.existsSync(configPath)) continue
|
|
const content = fs.readFileSync(configPath, "utf-8")
|
|
const result = parseJsoncSafe<OpencodeConfig>(content)
|
|
if (result.data) {
|
|
return result.data.plugin ?? []
|
|
}
|
|
} catch {
|
|
continue
|
|
}
|
|
}
|
|
return []
|
|
}
|
|
|
|
/**
|
|
* Check if a plugin entry matches a known notification plugin.
|
|
* Handles various formats: "name", "name@version", "npm:name", "file://path/name"
|
|
*/
|
|
function matchesNotificationPlugin(entry: string): string | null {
|
|
const normalized = entry.toLowerCase()
|
|
for (const known of KNOWN_NOTIFICATION_PLUGINS) {
|
|
if (
|
|
normalized === known ||
|
|
normalized.startsWith(`${known}@`) ||
|
|
normalized.includes(`/${known}`) ||
|
|
normalized.endsWith(`/${known}`)
|
|
) {
|
|
return known
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
export interface ExternalNotifierResult {
|
|
detected: boolean
|
|
pluginName: string | null
|
|
allPlugins: string[]
|
|
}
|
|
|
|
/**
|
|
* Detect if any external notification plugin is configured.
|
|
* Returns information about detected plugins for logging/warning.
|
|
*/
|
|
export function detectExternalNotificationPlugin(directory: string): ExternalNotifierResult {
|
|
const plugins = loadOpencodePlugins(directory)
|
|
|
|
for (const plugin of plugins) {
|
|
const match = matchesNotificationPlugin(plugin)
|
|
if (match) {
|
|
log(`Detected external notification plugin: ${plugin}`)
|
|
return {
|
|
detected: true,
|
|
pluginName: match,
|
|
allPlugins: plugins,
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
detected: false,
|
|
pluginName: null,
|
|
allPlugins: plugins,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate a warning message for users with conflicting notification plugins.
|
|
*/
|
|
export function getNotificationConflictWarning(pluginName: string): string {
|
|
return `[oh-my-opencode] External notification plugin detected: ${pluginName}
|
|
|
|
⚠️ Both oh-my-opencode and ${pluginName} listen to session.idle events.
|
|
Running both simultaneously can cause crashes on Windows.
|
|
|
|
oh-my-opencode's session-notification has been auto-disabled.
|
|
|
|
To use oh-my-opencode's notifications instead, either:
|
|
1. Remove ${pluginName} from your opencode.json plugins
|
|
2. Or set "notification": { "force_enable": true } in oh-my-opencode.json`
|
|
}
|