Skip to content

Windows support: cmd-shim CLI spawning, PowerShell dictation, NSIS packaging - #7

Closed
muneeb-rahmani wants to merge 7 commits into
milind-soni:mainfrom
muneeb-rahmani:feat/windows-support
Closed

Windows support: cmd-shim CLI spawning, PowerShell dictation, NSIS packaging#7
muneeb-rahmani wants to merge 7 commits into
milind-soni:mainfrom
muneeb-rahmani:feat/windows-support

Conversation

@muneeb-rahmani

Copy link
Copy Markdown

Ports OpenMausBot to Windows.

Server: resolve .cmd/.bat CLI shims (claude/codex) and spawn through cmd.exe with proper quoting (server/cli-util.ts); taskkill-based process-tree kill; Windows named-pipe CUA socket.
Electron: platform-safe main process (macOS-only APIs guarded); Windows dictation via a bundled PowerShell System.Speech helper speaking the same NDJSON protocol as the Swift helper.
UI: onboarding/composer/computer-panel perm flows adapt to no-TCC platforms.
Packaging: electron-builder win x64 NSIS + zip targets, icon.ico generation (make-ico.ps1), and platform-guarded native build scripts.
Verified: typecheck, prod build, packaged app launches and serves the UI on Windows.

- server/cli-util.ts (new): resolve .cmd/.bat shims and spawn through
  cmd.exe with CommandLineToArgvW quoting; taskkill-based process-tree
  kill for Windows; typed stdio overloads.
- claude.ts/codex.ts: use spawnCli/execFileCli everywhere, swap unix
  sockets for the Windows named pipe, and use the cross-platform kill.
- index.ts: read the CUA connection descriptor via a platform-safe path.
- dist-server/*: rebuilt output reflecting the above.
- speech.mjs: spawn the PowerShell helper (System.Speech, on-device) on
  Windows; keep the Swift helper for macOS. Same NDJSON protocol.
- resources/speech-helper.ps1 (new): Windows dictation to stdout.
- main.mjs: guard all macOS-only APIs (systemPreferences, app.dock,
  screen-permission helper) behind IS_MAC; Windows mic needs no grant.
- cua.mjs: Windows named-pipe socket path + PATH-based driver probe.
- preload.cjs: surface platform to the renderer.
- Onboarding: Windows has no TCC prompts; collapse permission step to
  not-applicable.
- Composer: pass platform-aware dictation; Windows mic needs no grant.
- ComputerPanel: hide macOS-only screen-permission affordance.
- ogb.d.ts: type the platform field.
- electron-builder.yml: win x64 NSIS + zip targets, icon.ico, bundled
  speech-helper.ps1; macOS config untouched.
- build/make-ico.ps1 (new): generate icon.ico from the app PNG.
- scripts/build-native.mjs (new): guard the Swift-only helpers so
  \pnpm package\ runs on any platform.
- package.json: package:win script; pnpm-workspace.yaml for build approval.
- README: document Windows support + packaging.
- .gitignore: build artifacts.
Detaching the claude/codex CLI processes on Windows gave each a new
console (flashing window) and severed the piped stdout/stderr the
drivers read, so claude exited 1 with no output and codex RPC broke.
detached is only needed on POSIX for -pid group killing; Windows
tree-kill already uses taskkill /T.

Also adds the previously built macro record/replay feature (Win32 low-
level hooks in macro-record.ps1, SendInput replay in macro-replay.ps1,
Electron IPC + ComputerPanel UI) plus its compiled dist-server output.
@milind-soni

Copy link
Copy Markdown
Owner

Thanks for the huge effort here — there's real, careful Win32 work in this PR. After deep-reviewing both this and #5 (which solve the same problem on the same files), here's where we landed:

We're basing the Windows port on #5's spawn strategy (now #10, with fixes). The deciding issue is winQuote in server/cli-util.ts: it implements the CommandLineToArgvW layer but not cmd.exe's own parser, which runs first. Concretely: a token containing &/|/^ but no whitespace is passed through unquoted (--model x&calc executes calc), and %VAR% expands even inside double quotes — and on this codebase, user-influenced strings (--append-system-prompt personas, --mcp-config JSON) ride on argv. That's the CVE-2024-27980 class, and resolving shims to their JS entry (no cmd.exe at all) avoids it structurally rather than by quoting harder.

That said, this PR has valuable pieces #5 doesn't have, and we'd love them as focused follow-up PRs on top of the port once it lands:

  1. speech-helper.ps1 + the speech.mjs Windows branch — I verified it speaks the same NDJSON contract as the Swift helper, and it's spawned correctly (-NoProfile -NonInteractive -ExecutionPolicy Bypass, hidden). Worth shipping with tempered wording (System.Speech is the legacy SAPI recognizer, noticeably below the macOS on-device quality). This is the only Windows dictation implementation anyone has written — please rebase it as its own PR.
  2. build/make-ico.ps1 — clean icon pipeline; just wire it into package:win (right now build/icon.ico is gitignored and nothing generates it during packaging).
  3. Routines + macros are a genuinely interesting feature (~half this PR) but they're new product surface, not Windows porting — as a separate PR, with two fixes: macro-record.ps1/macro-replay.ps1 aren't in win.extraResources (record/replay throws in the packaged build), and the /api/routines/:id/run failure path writes two HTTP responses (ERR_HTTP_HEADERS_SENT).

Also for a future revision: dist-server/** shouldn't be hand-edited (it's pnpm build:server output and gets overwritten at package time), and pnpm-workspace.yaml's allowBuilds: isn't a pnpm field — the working mechanism is pnpm.onlyBuiltDependencies in package.json. Main now has a 3-OS CI matrix + test suite (see CONTRIBUTING.md), which should make all of these follow-ups much easier to validate. Thanks again — hope to see the dictation and macros PRs!

@milind-soni

Copy link
Copy Markdown
Owner

Heads-up: I pushed a merge of current main to this branch (maintainer edit) to clear the README conflict — resolution keeps your Windows requirements note alongside main's grok CLI addition, with the cmd.exe wording trimmed per the review discussion above. No changes to your code beyond the merge. Main's CI (3-OS matrix) and test suite now run on this PR, which should help when splitting out the dictation / icon / routines+macros pieces.

@milind-soni

Copy link
Copy Markdown
Owner

The Windows port this branch introduced has since landed on main through newer, tested implementations, including packaging, dictation gating, named pipes, process-tree cleanup, and desktop capabilities. This older branch would overwrite newer provider and lifecycle work and retains cmd.exe shim execution. Please close it; any remaining shim-resolution work should be rebased as a small no-shell change alongside #41.

@milind-soni

Copy link
Copy Markdown
Owner

Thank you for this — the cmd-shim spawning analysis in particular was correct and is essentially how main works today.

Closing as superseded: Windows support landed in #17 (f3bac3a). server/procs.ts is now the single place that knows Windows process differences — it resolves npm shims via a real lookup before spawning (never building a shell string), reaps process trees with taskkill /T since kill(-pid) is POSIX-only, and sets windowsHide so console CLIs don't flash windows. The permission broker uses a named pipe on win32, and NSIS packaging is in electron-builder.yml with installers now built on a Windows CI runner.

Three Windows ports arrived at once and only one could land; that's a scheduling accident, not a verdict on the code. If something here is still missing on main, a focused PR against current main is very welcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants