diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts index 80e524349af..ef2d00ab683 100644 --- a/packages/opencode/src/file/watcher.ts +++ b/packages/opencode/src/file/watcher.ts @@ -33,6 +33,40 @@ export namespace FileWatcher { return createWrapper(binding) as typeof import("@parcel/watcher") }) + // WORKAROUND: Timeout for watcher subscribe - hangs indefinitely on x86 emulation via Rosetta (parcel-bundler/watcher#159) + // On timeout: no auto-detection of branch switches or external file changes + const SUBSCRIBE_TIMEOUT_MS = 5000 + + async function subscribeWithTimeout( + dir: string, + callback: ParcelWatcher.SubscribeCallback, + options: Parameters[2], + ): Promise { + let timeoutId: Timer | undefined + let resolved = false + const result = await Promise.race([ + watcher() + .subscribe(dir, callback, options) + .then((sub) => { + resolved = true + return sub + }), + new Promise((resolve) => { + timeoutId = setTimeout(() => { + if (!resolved) { + log.error("watcher subscribe timeout", { dir }) + resolve(null) + } + }, SUBSCRIBE_TIMEOUT_MS) + }), + ]).catch((e) => { + log.error("watcher subscribe failed", { dir, error: e }) + return null + }) + if (timeoutId) clearTimeout(timeoutId) + return result + } + const state = Instance.state( async () => { if (Instance.project.vcs !== "git") return {} @@ -57,24 +91,22 @@ export namespace FileWatcher { } } - const subs = [] + const subs: (ParcelWatcher.AsyncSubscription | null)[] = [] const cfgIgnores = cfg.watcher?.ignore ?? [] - subs.push( - await watcher().subscribe(Instance.directory, subscribe, { - ignore: [...FileIgnore.PATTERNS, ...cfgIgnores], - backend, - }), - ) + const subDir = await subscribeWithTimeout(Instance.directory, subscribe, { + ignore: [...FileIgnore.PATTERNS, ...cfgIgnores], + backend, + }) + if (subDir) subs.push(subDir) - const vcsDir = await $`git rev-parse --git-dir`.quiet().nothrow().cwd(Instance.worktree).text() + const vcsDir = (await $`git rev-parse --git-dir`.quiet().nothrow().cwd(Instance.worktree).text()).trim() if (vcsDir && !cfgIgnores.includes(".git") && !cfgIgnores.includes(vcsDir)) { - subs.push( - await watcher().subscribe(vcsDir, subscribe, { - ignore: ["hooks", "info", "logs", "objects", "refs", "worktrees", "modules", "lfs"], - backend, - }), - ) + const subGit = await subscribeWithTimeout(vcsDir, subscribe, { + ignore: ["hooks", "info", "logs", "objects", "refs", "worktrees", "modules", "lfs"], + backend, + }) + if (subGit) subs.push(subGit) } return { subs }