Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 46 additions & 14 deletions packages/opencode/src/file/watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof ParcelWatcher.subscribe>[2],
): Promise<ParcelWatcher.AsyncSubscription | null> {
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<null>((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 {}
Expand All @@ -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 }
Expand Down