Files
orca/src/main/ipc/worktree-watcher-removal.ts
T
Neil 03fcfdfb92 feat(orcad): boot the Orca runtime on plain Node (#15968)
* refactor(host): resolve the app root through the port in fork-reachable modules

`parcel-watcher-entry-path.ts` and `session-scanner-service-entry-path.ts` read the
app root via `require('electron').app` inside a try/catch that already returns null
when Electron is absent. They were therefore correct under plain Node at runtime and
only failed the *static* text check — which is real, not pedantic: the comment in
`ports/port-scan-command-client.ts:19` records that the plain-node-entry-guard fails
on that literal text, try/catch or not.

`hasAppEnvironment() ? getAppEnvironment() : null` gives the identical "no app root
here" answer without the text. That restores `hasAppEnvironment`, which an earlier
commit in this stack deleted as unused — it now has the caller it was waiting for.

Ratchet baseline 27 → 25.

Verified: 74 files / 458 tests; `pnpm typecheck` clean; `oxlint` clean.

* feat(orcad): boot the Orca runtime on plain Node

Closes the last two Electron couplings and makes `orcad` a working artifact:
a 4.43 MB Node bundle that boots, pairs, registers a repo, creates a real git
worktree and round-trips a PTY — with zero `require("electron")`.

Ratchet 2 -> 0, so `config/runtime-electron-baseline.txt` is now empty and its
test asserts exactly that: any reachable electron import is a regression.

- speech: inject the service factories, so importing ModelManager for its type
  no longer drags Electron's streaming net.request into the graph
- filesystem-watcher: add a WorktreeWatcherRemoval port. Every entry in those
  maps arrives through an ipcMain handler carrying a renderer sender, so a host
  with no renderer has nothing to close, restore or forget — the inert default
  is what the desktop code does against empty maps, not a stub hiding work
- user-data-path / profile-storage-paths: resolve userData through
  AppEnvironment. These surfaced only once orcad pulled the store in

Both host ports now anchor to a realm-global symbol. `vi.resetModules()` gives
the re-imported graph a fresh module copy, so a binding installed before the
reset silently read back as uninstalled.

The acceptance smoke drives both hosts through one code path (`--target
orcad|electron`) and seeds its own git repo, so it is hermetic and asserts the
same contract of each. Wired into PR CI.

* test(smoke): remove the seeded workspace container, not just the worktree

* test(smoke): surface the server's stderr when it dies before ready

* fix(smoke): build node-pty for Node before booting orcad in CI

* fix(smoke): drive the CLI built from this checkout, not one on PATH

* docs(ratchet): say the baseline must stay empty, not merely shrink

* build(orcad): externalize only the native modules actually in the graph
2026-08-22 21:47:46 -07:00

43 lines
1.7 KiB
TypeScript

import type { WatcherRemovalDeadline } from './watcher-removal-drain'
/**
* Removal-time coordination for the renderer-facing filesystem watchers.
*
* Worktree removal has to close any watcher holding the directory open, then either
* restore it (removal failed) or drop the snapshot (removal succeeded). The desktop
* implementation lives in `./filesystem-watcher`, where that bookkeeping is inseparable
* from the `WebContents` map it suspends and replays.
*
* Why the default is inert rather than a throw: every entry in those maps arrives through
* an `ipcMain` handler carrying a renderer `sender`. A host with no renderer never
* installs one, so there is nothing to close, restore, or forget — the no-op is what the
* desktop code itself would do against empty maps, not a silent stub hiding lost work.
*/
export type WorktreeWatcherRemoval = {
closeLocal(worktreePath: string, deadline?: WatcherRemovalDeadline): Promise<void>
restoreLocal(worktreePath: string): Promise<void>
forgetLocal(worktreePath: string): void
closeRemote(connectionId: string, worktreePath: string): Promise<void>
restoreRemote(connectionId: string, worktreePath: string): Promise<void>
forgetRemote(connectionId: string, worktreePath: string): void
}
const inert: WorktreeWatcherRemoval = {
closeLocal: async () => {},
restoreLocal: async () => {},
forgetLocal: () => {},
closeRemote: async () => {},
restoreRemote: async () => {},
forgetRemote: () => {}
}
let current: WorktreeWatcherRemoval = inert
export function setWorktreeWatcherRemoval(next: WorktreeWatcherRemoval | null): void {
current = next ?? inert
}
export function getWorktreeWatcherRemoval(): WorktreeWatcherRemoval {
return current
}