Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a2f1afd
docs: add Windows support spec and plan
amanthanvi Feb 3, 2026
0d1c674
fix: improve cross-platform path opening
amanthanvi Feb 3, 2026
3b14fd4
feat(dictation): enable Windows support
amanthanvi Feb 3, 2026
54ea0f6
ci: add Windows build job
amanthanvi Feb 3, 2026
9b533f5
ci(release): add Windows updater artifacts
amanthanvi Feb 3, 2026
30cad9b
docs: clarify Windows updater bundle
amanthanvi Feb 3, 2026
2544941
fix: make default Open in targets platform-aware
amanthanvi Feb 3, 2026
8195845
ci: ensure LLVM available on Windows
amanthanvi Feb 3, 2026
a49d3bc
docs: remove macOS-only framing
amanthanvi Feb 3, 2026
17b6e21
Fix doctor command detection
amanthanvi Feb 3, 2026
125647a
Keep Windows plan/spec current
amanthanvi Feb 3, 2026
1738da8
Fix Codex PATH handling on Windows
amanthanvi Feb 3, 2026
12f17cb
Show shortcuts with Ctrl on Windows
amanthanvi Feb 3, 2026
1d07666
Fix Windows shortcut labels and select theming
amanthanvi Feb 3, 2026
ee26759
Document Windows UX and Codex PATH requirements
amanthanvi Feb 3, 2026
3091919
Run Codex via cmd.exe on Windows
amanthanvi Feb 4, 2026
e3417dd
Make settings copy platform-neutral
amanthanvi Feb 4, 2026
d7a2b30
Use generic file manager icon on Windows
amanthanvi Feb 4, 2026
2856368
Fix platform label in Sentry metrics
amanthanvi Feb 4, 2026
10abdb9
Use platform-neutral default font stacks
amanthanvi Feb 4, 2026
0172c47
Refresh docs copy for Windows support
amanthanvi Feb 4, 2026
6bcf130
Fix Windows open-in commands (.cmd/.bat shims)
amanthanvi Feb 4, 2026
04ebaea
Guard platform detection for non-browser contexts
amanthanvi Feb 4, 2026
353a763
Fix Windows asset globs in release workflow
amanthanvi Feb 5, 2026
a2909b6
Fix Windows Codex process lifecycle
amanthanvi Feb 5, 2026
85c9e2a
Harden Windows cmd spawning for Codex
amanthanvi Feb 5, 2026
4c5b26d
Fix Windows open-in cmd wrappers
amanthanvi Feb 5, 2026
b8b4e94
Make default shortcuts Windows-friendly
amanthanvi Feb 5, 2026
3ba7875
Update Windows plan for shortcut defaults
amanthanvi Feb 5, 2026
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
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,29 @@ jobs:
run: npm ci
- name: Tauri debug build
run: npm run tauri -- build --debug --no-bundle

build-windows:
runs-on: windows-latest
needs:
- lint
- typecheck
- test-js
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- uses: dtolnay/rust-toolchain@stable
- name: Install LLVM (bindgen)
run: choco install llvm -y --no-progress
- name: Configure LLVM (bindgen)
run: |
echo "LIBCLANG_PATH=C:\\Program Files\\LLVM\\bin" >> $env:GITHUB_ENV
echo "C:\\Program Files\\LLVM\\bin" >> $env:GITHUB_PATH
- name: Install dependencies
run: npm ci
- name: Doctor (Windows)
run: npm run doctor:win
- name: Tauri debug build (Windows)
run: npm run tauri -- build --debug --no-bundle --config src-tauri/tauri.windows.conf.json
91 changes: 91 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,74 @@ jobs:
name: appimage-${{ matrix.arch }}
path: src-tauri/target/release/bundle/appimage/*.AppImage*

build_windows:
runs-on: windows-latest
environment: release
env:
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
TAURI_SIGNING_PRIVATE_KEY_B64: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_B64 }}
steps:
- uses: actions/checkout@v4

- name: setup node
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm

- name: install Rust stable
uses: dtolnay/rust-toolchain@stable

- name: Install LLVM (bindgen)
run: choco install llvm -y --no-progress

- name: Configure LLVM (bindgen)
run: |
echo "LIBCLANG_PATH=C:\\Program Files\\LLVM\\bin" >> $env:GITHUB_ENV
echo "C:\\Program Files\\LLVM\\bin" >> $env:GITHUB_PATH

- name: install frontend dependencies
run: npm ci

- name: Write Tauri signing key
shell: bash
run: |
set -euo pipefail
python - <<'PY'
import base64
import os
from pathlib import Path

raw = base64.b64decode(os.environ["TAURI_SIGNING_PRIVATE_KEY_B64"])
home = Path.home()
target = home / ".tauri"
target.mkdir(parents=True, exist_ok=True)
(target / "codexmonitor.key").write_bytes(raw)
PY

- name: build windows bundles
shell: bash
run: |
set -euo pipefail
export TAURI_SIGNING_PRIVATE_KEY
TAURI_SIGNING_PRIVATE_KEY="$(cat "$HOME/.tauri/codexmonitor.key")"
npm run tauri:build:win

- name: Upload Windows artifacts
uses: actions/upload-artifact@v4
with:
name: windows-artifacts
path: |
src-tauri/target/release/bundle/nsis/*.exe*
src-tauri/target/release/bundle/msi/*.msi*

release:
runs-on: ubuntu-latest
environment: release
needs:
- build_macos
- build_linux
- build_windows
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -244,6 +306,12 @@ jobs:
path: release-artifacts
merge-multiple: true

- name: Download Windows artifacts
uses: actions/download-artifact@v4
with:
name: windows-artifacts
path: release-artifacts

- name: Build latest.json
run: |
set -euo pipefail
Expand Down Expand Up @@ -345,6 +413,27 @@ jobs:
"signature": sig_path.read_text().strip(),
}

exe_candidates = sorted(artifacts_dir.rglob("*.exe"), key=lambda p: p.name.lower())
windows_installer = None
for candidate in exe_candidates:
lowered = candidate.name.lower()
if "setup" in lowered or "installer" in lowered:
windows_installer = candidate
break
if windows_installer is None and exe_candidates:
windows_installer = exe_candidates[0]
if windows_installer is None:
raise SystemExit("No Windows installer (.exe) found for latest.json")

Comment on lines +418 to +427
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Windows installer detection logic searches for .exe files containing "setup" or "installer" in lowercase filenames, with a fallback to the first .exe found if no match. This heuristic approach could be fragile if:

  1. Tauri changes its naming convention for Windows installers
  2. Multiple .exe files are present in the release artifacts with ambiguous names
  3. A debug or test .exe is alphabetically first

Consider either:

  1. Using a more specific pattern that matches Tauri's naming convention (e.g., checking for the app name in the filename)
  2. Having Tauri output a manifest file listing the updater bundle filename
  3. Using a glob pattern that's more specific to the actual output (e.g., *_x64-setup.exe or similar)
  4. Adding validation that the selected file has an expected size or signature format

For robustness, at minimum add a log message showing which installer was selected.

Suggested change
for candidate in exe_candidates:
lowered = candidate.name.lower()
if "setup" in lowered or "installer" in lowered:
windows_installer = candidate
break
if windows_installer is None and exe_candidates:
windows_installer = exe_candidates[0]
if windows_installer is None:
raise SystemExit("No Windows installer (.exe) found for latest.json")
# Prefer executables that look like the CodexMonitor installer
preferred_installers = []
for candidate in exe_candidates:
lowered = candidate.name.lower()
if "codexmonitor" in lowered and ("setup" in lowered or "installer" in lowered):
preferred_installers.append(candidate)
if preferred_installers:
windows_installer = preferred_installers[0]
else:
# Fallback to any executable containing "setup" or "installer"
for candidate in exe_candidates:
lowered = candidate.name.lower()
if "setup" in lowered or "installer" in lowered:
windows_installer = candidate
break
if windows_installer is None and exe_candidates:
# Final fallback: first .exe alphabetically (for backward compatibility)
windows_installer = exe_candidates[0]
if windows_installer is None:
raise SystemExit("No Windows installer (.exe) found for latest.json")
print(f"Selected Windows installer for latest.json: {windows_installer.name}")

Copilot uses AI. Check for mistakes.
win_sig_path = windows_installer.with_suffix(windows_installer.suffix + ".sig")
if not win_sig_path.exists():
raise SystemExit(f"Missing signature for {windows_installer.name}")

platforms["windows-x86_64"] = {
"url": f"https://github.com/Dimillian/CodexMonitor/releases/download/v${VERSION}/{windows_installer.name}",
"signature": win_sig_path.read_text().strip(),
}

payload = {
"version": "${VERSION}",
"notes": notes,
Expand Down Expand Up @@ -378,6 +467,8 @@ jobs:
release-artifacts/CodexMonitor.app.tar.gz \
release-artifacts/CodexMonitor.app.tar.gz.sig \
release-artifacts/*.AppImage* \
release-artifacts/nsis/*.exe* \
release-artifacts/msi/*.msi* \
Comment on lines +470 to +471

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reference Windows release assets with stable glob paths

In the release job I checked, Windows artifacts are downloaded from windows-artifacts into release-artifacts, but gh release create only looks under release-artifacts/nsis/*.exe* and release-artifacts/msi/*.msi*; this path assumption is brittle compared with the rest of the workflow (which consumes downloaded artifacts from the root), and when those subfolders are not present the glob is passed as a literal and gh release create fails, blocking publication of the release.

Useful? React with 👍 / 👎.

release-artifacts/latest.json

- name: Bump version and open PR
Expand Down
58 changes: 58 additions & 0 deletions PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Windows Support Execution Plan

Source of truth for requirements: `SPEC.md`.
This plan is **live** and tracks what has landed and what remains for the Windows milestone (auto-updater + dictation).

## Workstream

### 1) Docs

- [x] Add `SPEC.md` (Windows scope, updater + dictation required).
- [x] Keep `PLAN.md` current as work lands.

### 2) Git safety + PR

- [x] Push `feature/windows-support` to a fork remote and set upstream to avoid accidental `main` pushes.
- [x] Open a draft PR early so CI runs on every push.

### 3) Windows UX + path correctness

- [x] Make “Reveal in Finder” platform-aware (Explorer on Windows).
- [x] Fix path joining in the frontend so Windows absolute/relative paths behave.
- [x] Make backend `open_workspace_in` work cross-platform (macOS/Windows/Linux).
- [x] Make default “Open in” targets sensible on Windows (Explorer + command-based editors).
- [x] Make default shortcuts Windows-friendly (Ctrl/Alt; avoid Cmd+Ctrl collapse).

### 4) Dictation on Windows (required)

- [x] Enable Whisper dictation on Windows (`whisper-rs` + `cpal`) by removing the Windows stub.
- [x] Update Windows build checks (`doctor:win`) to require LLVM/Clang + CMake.
- [x] Fix `doctor:win` dependency detection on Unix (no shell builtins).

### 5) CI (required)

- [x] Add a Windows CI job that runs a Tauri debug build with `src-tauri/tauri.windows.conf.json`.

### 6) Release + updater (required)

- [x] Enable Windows updater artifacts in `src-tauri/tauri.windows.conf.json`.
- [x] Add a Windows release build job to `.github/workflows/release.yml`.
- [x] Extend `latest.json` generation to include Windows URL + signature.

## Validation (run after each step)

- `npm run lint`
- `npm run test`
- `npm run typecheck`
- Rust checks are executed in CI for macOS + Windows jobs added by this plan.

## Manual checklist (Windows)

- [ ] `npm run tauri:build:win` succeeds on Windows 10/11.
- [ ] App launches and can open workspaces.
- [ ] Adding a workspace succeeds when `codex --version` works in Windows Terminal.
- [ ] “Reveal in Explorer” opens the right folder.
- [ ] Shortcut hints use Ctrl/Alt labels and work on Windows.
- [ ] Theme dropdown options are readable in Dark/Dim with Reduce Transparency off.
- [ ] Auto-updater finds and applies the latest release.
- [ ] Dictation works end-to-end (download → hold-to-talk → transcript).
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

![CodexMonitor](screenshot.png)

CodexMonitor is a macOS Tauri app for orchestrating multiple Codex agents across local workspaces. It provides a sidebar to manage projects, a home screen for quick actions, and a conversation view backed by the Codex app-server protocol.
CodexMonitor is a Tauri app for orchestrating multiple Codex agents across local workspaces. It provides a sidebar to manage projects, a home screen for quick actions, and a conversation view backed by the Codex app-server protocol.

## Features

Expand Down Expand Up @@ -31,7 +31,7 @@ CodexMonitor is a macOS Tauri app for orchestrating multiple Codex agents across

### Files & Prompts

- File tree with search, file-type icons, and Reveal in Finder.
- File tree with search, file-type icons, and Reveal in Finder/Explorer.
- Prompt library for global/workspace prompts: create/edit/delete/move and run in current or new threads.

### UI & Experience
Expand All @@ -40,13 +40,14 @@ CodexMonitor is a macOS Tauri app for orchestrating multiple Codex agents across
- Responsive layouts (desktop/tablet/phone) with tabbed navigation.
- Sidebar usage and credits meter for account rate limits plus a home usage snapshot.
- Terminal dock with multiple tabs for background commands (experimental).
- In-app updates with toast-driven download/install, debug panel copy/clear, sound notifications, and macOS overlay title bar with vibrancy + reduced transparency toggle.
- In-app updates with toast-driven download/install, debug panel copy/clear, sound notifications, plus platform-specific window effects (macOS overlay title bar + vibrancy) and a reduced transparency toggle.

## Requirements

- Node.js + npm
- Rust toolchain (stable)
- CMake (required for native dependencies; Whisper/dictation uses it on non-Windows)
- CMake (required for native dependencies; dictation/Whisper uses it)
- LLVM/Clang (required on Windows to build dictation dependencies via bindgen)
- Codex installed on your system and available as `codex` in `PATH`
- Git CLI (used for worktree operations)
- GitHub CLI (`gh`) for the Issues panel (optional)
Expand Down Expand Up @@ -74,13 +75,13 @@ npm run tauri dev

## Release Build

Build the production Tauri bundle (app + dmg):
Build the production Tauri bundle:

```bash
npm run tauri build
```

The macOS app bundle will be in `src-tauri/target/release/bundle/macos/`.
Artifacts will be in `src-tauri/target/release/bundle/` (platform-specific subfolders).

### Windows (opt-in)

Expand All @@ -94,8 +95,8 @@ Artifacts will be in:

- `src-tauri/target/release/bundle/nsis/` (installer exe)
- `src-tauri/target/release/bundle/msi/` (msi)

Note: dictation is currently disabled on Windows builds (to avoid requiring LLVM/libclang for `whisper-rs`/bindgen).
Note: building from source on Windows requires LLVM/Clang (for `bindgen` / `libclang`) in addition to CMake.

## Type Checking

Expand Down
92 changes: 92 additions & 0 deletions SPEC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Windows Support Spec

## Goal

Ship a **fully functioning Windows version** of CodexMonitor with:

- **Auto-updater** enabled and wired into the release workflow (`latest.json` includes Windows).
- **Dictation** working on Windows (same UX + model management as other platforms).

This spec is the source of truth for Windows support requirements and acceptance criteria.

## Target Platforms

- Windows 10/11, **x86_64** (GitHub Actions `windows-latest`).

## Non-Goals (for this milestone)

- Authenticode / EV code signing (nice-to-have; not required to be functional).
- Windows ARM64 builds.

## Functional Requirements

### Updater (Required)

- Windows release artifacts must be produced by CI and attached to GitHub Releases.
- `latest.json` must include a **Windows platform entry** with:
- correct asset URL
- signature generated by Tauri updater signing
- In-app updater must be enabled for Windows builds (same endpoints + pubkey as other platforms).
- The updater entry for Windows uses the **NSIS installer** (`.exe` + `.exe.sig`) as the updater bundle.

### Dictation (Required)

Dictation must work on Windows with the same surface area as other platforms:

- Model status: missing/downloading/ready/error
- Model download/cancel/remove
- Session lifecycle: start/listening/stop->processing/transcript/cancel
- Emits the same Tauri events:
- `dictation-download`
- `dictation-event`

Implementation choice for this milestone:

- Use the existing Whisper-based implementation on Windows (via `whisper-rs` + `cpal`).

### Windows UX Correctness (Required)

- “Reveal in Finder” strings must be platform-aware (“Explorer” on Windows).
- Opening paths in an editor or file manager must behave correctly on Windows.
- Shortcut hints and formatting must be platform-aware on Windows (Ctrl/Alt labels; no macOS-only glyphs).
- Settings selects (for example Theme) must remain readable in Dark/Dim with Reduce Transparency off.

## Build & Tooling Requirements

### Windows Build Prereqs (dev + CI)

Required on Windows to build dictation (Whisper + bindgen):

- CMake
- LLVM/Clang (for `bindgen` / `libclang`)

`npm run doctor:win` must fail fast with actionable instructions if missing.
`npm run doctor:win` must correctly detect installed dependencies on Windows/macOS/Linux.

## CI / Release Requirements

### CI (Required)

- Add a Windows job to `.github/workflows/ci.yml` that:
- installs deps
- runs a Windows Tauri debug build (`--no-bundle`) using the Windows config

### Release (Required)

Update `.github/workflows/release.yml` to include Windows:

- Build Windows bundles on `windows-latest`
- Upload `.msi` / `.exe` plus updater `.sig` artifacts
- Include Windows in generated `latest.json`

## Acceptance Criteria

- `npm run tauri:build:win` succeeds on Windows.
- Windows release workflow publishes installers and `latest.json` that enables in-app updates.
- Dictation works on Windows end-to-end (model download → hold-to-talk → transcript).
- Adding a workspace works when `codex --version` runs in Windows Terminal (Codex PATH handling is correct).
- Repo checks pass:
- `npm run lint`
- `npm run test`
- `npm run typecheck`
- Rust checks executed in CI for Windows/macOS as configured
2 changes: 1 addition & 1 deletion docs/changelog.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ <h1>Changelog</h1>
<img class="logo-mark" src="assets/app-icon.png" alt="Codex Monitor app icon" />
Codex Monitor
</div>
<p>macOS Codex agents orchestration, built by and for individuals who ship fast.</p>
<p>Desktop Codex agent orchestration, built by and for individuals who ship fast.</p>
</div>
<div class="footer-links">
<a href="index.html#features">Features</a>
Expand Down
Loading
Loading