Make dor / dor ab work on Windows#188
Conversation
`dor ab` failed with "agent-browser was not found" on Windows even when agent-browser ran fine in the same shell. Two Windows-only spawn failures were behind it, both reproduced: - ENOENT: Node's spawn ignores PATHEXT, so a bare `agent-browser` never resolves the `.cmd` PATH shim that npm/vfox installs. - EINVAL: Node >=22 refuses to spawn a `.cmd`/`.bat` even by absolute path (CVE-2024-27980 hardening). Route every external/user-binary spawn through cross-spawn, which resolves via PATH/PATHEXT, runs `.cmd` through cmd.exe with correct escaping, and is a no-op on POSIX: - dor CLI (execAgentBrowserProcess) - agent-browser host (spawnAgentBrowser) — hits this even for the absolute binaryPath dor ab resolves, since GUI hosts don't share the shell PATH - standalone dev harness (pnpm + agent-browser) dor.js is ESM and cross-spawn is CommonJS, so add an esbuild createRequire banner to its build (the per-host CJS bundles need no shim). Resolve the binary path once per invocation and reuse it for both the missing-install pre-check and the host request, dropping a redundant PATH walk. Also set ELECTRON_RUN_AS_NODE in the bin launchers: when DORMOUSE_NODE is the editor's Electron binary, without it Electron launches its GUI, ignores the script, and exits 0 — so `dor` would silently do nothing. Document the rule in docs/specs/dor-cli.md ("Spawning External Binaries") with a pointer from dor-browser.md. Make two POSIX-only tests platform-aware (create a `.cmd` shim and join PATH with the platform delimiter on Windows). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts: # pnpm-lock.yaml
… paths Tauri's resource_dir() returns \?\ verbatim paths in the bundled/dev layout. Those flowed into DORMOUSE_CLI_BIN (prepended to PATH) and DORMOUSE_CLI_JS unchanged, so cmd.exe could not execute dor.cmd via the verbatim path and `dor` failed with "The system cannot find the path specified." Strip the prefix in dor_cli_paths_from_root, mirroring sidecar_script_arg_path, and add a Windows regression test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… close agent-browser's `open` launches a detached per-session daemon that on Windows inherits the parent's stdout/stderr pipes. Those pipes never reach EOF while the daemon lives, so the child's 'close' event never fires and the spawn helpers — which awaited 'close' — hung forever. POSIX dodges this because the daemon double-forks and detaches from the inherited fds, which is why it only ever reproduced on Windows. Both spawn helpers (dor/src/commands/agent-browser.ts and the shared agent-browser host) now resolve on 'exit', which fires when the foreground process ends regardless of lingering pipe holders, giving 'close' a short grace (CLOSE_GRACE_MS) to win first so a normal command's full output still flushes. Add 'exit' to dor's ambient ChildProcess type and document the rule in docs/specs/dor-cli.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The cross-spawn deps were added to dor/lib/standalone package.json but the lockfile was never regenerated alongside them. Add the cross-spawn entry (and its which/isexe/path-key/shebang transitive deps) so the lockfile matches the manifests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On Windows an interrupted `tauri dev` leaves two strays that break the next
run: an orphaned vite squatting on the dev port ("Port 1420 is already in
use") and an orphaned sidecar node.exe running out of target\debug that
locks the binary, so the next Rust rebuild fails copying it with "Access is
denied." Add scripts/free-dev-port.mjs to kill both (Windows-only no-op
otherwise) and run it from the root `dev:standalone` script before Tauri.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Spell the intra-package script calls as `pnpm run stage` rather than the `pnpm stage` shorthand, so they always resolve as scripts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cross-spawn runs the agent-browser `.cmd` shim through cmd.exe. Without windowsHide, Node shows a console window for each spawn — and the panel's screenshot loop spawns one per stream-frame pulse on a live page, so a console window flashed and stole focus several times a second, making the app unusable. Pass windowsHide: true at both spawn sites (no-op off Windows) and add it to dor's ambient spawn option types. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`dor ab open` printed a burst of blank lines on Windows. The forwarded agent-browser process produces clean output over a pipe (it detects the non-TTY and skips its animation), but `open` leaves a daemon alive that on Windows inherits our stdout/stderr pipes and keeps scribbling into them. Because we resolve the spawn on 'exit' + a short grace (the daemon keeps 'close' from ever firing), that post-command daemon noise was captured during the grace window and replayed to the terminal. macOS never saw it because its daemon detaches from the inherited fds. Snapshot the captured output at 'exit' — when the foreground command is done — and resolve the grace path with that snapshot, so only the command's own output is returned. The 'close' path still uses the live buffers (no lingering daemon, so nothing extra to drop). Applied to both spawn helpers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Strip the `\?\` verbatim prefix once in resolve_sidecar_path (the entry boundary) instead of re-stripping it at each consumer. Deletes the sidecar_script_arg_path helper and the sidecar_arg_path indirection in start_sidecar, and drops the strip from dor_cli_paths_from_root — every derived path is now plain for free. Retarget the regression test to the boundary and update the dor-cli spec reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deploying mouseterm with
|
| Latest commit: |
9cb1afd
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://26ddd150.mouseterm.pages.dev |
| Branch Preview URL: | https://ab-install.mouseterm.pages.dev |
dormouse-bot
left a comment
There was a problem hiding this comment.
The Windows fixes themselves read well — routing spawns through cross-spawn, normalizing the \\?\ verbatim path once at the resolve_sidecar_path boundary, the exit/close grace for the lingering daemon, and setting ELECTRON_RUN_AS_NODE in the launchers are all clearly reasoned.
One blocking issue: Build & Test is red because dor/test/snapshots/ensure-json.snap no longer matches. The ensure json output test runs dor ensure --cwd /Users/me/projects/site, and callerWorkingDirectory resolves that absolute --cwd via resolvePath, which returns it verbatim on Linux. The committed snapshot was regenerated on Windows (C:\\Users\\me\\projects\\site), so on the Linux CI runner actual=/Users/me/projects/site ≠ expected. Inline fix below restores it.
Root cause worth a note: this snapshot is platform-dependent — on win32 resolvePath('/Users/me/projects/site') folds to the current drive, so a future Windows CI leg would re-break it the other direction. If Windows CI is on the roadmap, consider feeding a --cwd that resolves identically on both platforms (or normalizing the path in the snapshot harness) so the fixture stays deterministic.
…m committed The `ensure json output` snapshot resolves `--cwd /Users/me/projects/site` through node:path, which on win32 folds it to the current drive (`C:\Users\me\projects\site`) and JSON-escapes the separators. Committing the Windows form turned Linux CI red (and vice versa). Keep the snapshot in its Unix form and smudge the produced output back to that form as it's captured — only on win32, only for a `X:\...` token so it can't touch escapes like `\n`. A snapshot regenerated on Windows now writes the Unix form, and the comparison stays byte-exact. Apply the same smudge to the one argv deepEqual test that resolved a POSIX PWD. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Gets the
dorCLI anddor ab(agent-browser surfaces) working on Windows. Each layer of breakage was only reachable once the previous one was fixed, so this is a stack of related fixes.What was broken, in order
spawnignoresPATHEXT(ENOENT on a bareagent-browser) and Node ≥22 refuses to spawn a.cmdeven by absolute path (EINVAL). Route all external spawns through cross-spawn. (8270828)dorlauncher — the\?\verbatim path Tauri'sresource_dir()returns flowed intoDORMOUSE_CLI_BIN/DORMOUSE_CLI_JS; cmd.exe can't execute a batch file via a verbatim path ("The system cannot find the path specified."). Normalize once at the boundary (resolve_sidecar_path). (1c95bb2,3188b92)dor abhang —agent-browser openleaves a daemon that on Windows inherits our stdout/stderr pipes, so'close'never fires. Resolve on'exit'with a short grace. (3d7b9cd).cmdthrough cmd.exe; the screenshot loop spawned one per frame.windowsHide: true. (9d30c27)'exit'. (aa252a6)Dev ergonomics
scripts/free-dev-port.mjsnow also reaps an orphaned sidecarnode.exelockingtarget\debugbeforetauri dev(its JobObject leash can leak on a force-quit/crash). (4c875ea)Notes
docs/specs/dor-cli.md→ "Spawning External Binaries".dor-lib-common).🤖 Generated with Claude Code