Skip to content
Merged
Show file tree
Hide file tree
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
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
*.ts text eol=lf
*.mjs text eol=lf
*.json text eol=lf
*.md text eol=lf
*.sh text eol=lf
*.command text eol=lf
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## 0.6.0 - 2026-07-18

- Added the local macOS Codex Micro launcher and persistent LaunchAgent watcher.
- Added an opt-in authenticated SSH/Tailscale relay for mixed Windows/macOS agent slots and native command routing.
- Added a Windows/Mac target action while keeping agent keys bound to each task's originating host.
- Added host badges and stable `(hostId, threadKey)` routing for the six global agent keys.
- Replaced the one-shot Windows-login launcher with a persistent, single-instance bridge watcher.
- Automatically recovers the bridge after Codex updates, crashes, and normal restarts.
- Detects rapid Codex restarts by main-process generation even when no stopped interval is observed.
- Avoids touching a normal Codex session that was already open when the watcher is first installed.
- Removes stale bridge-port files and records bounded diagnostics in `%LOCALAPPDATA%\CodexDeck\watcher.log`.
- Added independent Windows-only and macOS-only operation from the same Stream Deck plugin package.
- Added host-generic task ownership and global recent-activity ordering for mixed Mac/Windows agent keys.
- Restricted relay listeners and clients to loopback or explicit Tailscale addresses and added hidden token entry on Windows.
- Installed the Windows watcher into a durable per-user location instead of depending on the extracted ZIP folder.
- Added separated release archives, checksums, and an automated audit for private state, personal setup markers, and protected keycap SVGs.

## 0.5.0 - 2026-07-17

- The launcher reuses a healthy existing loopback debug session instead of restarting Codex on every run.
Expand Down
5 changes: 3 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Thanks for helping improve Codex Deck.

## Before opening a pull request

1. Keep the project Windows-only unless the new platform path is tested end to end.
1. Preserve independent Windows-only, macOS-only, and optional multi-host operation. State clearly which paths received automated, live-app, and physical-device testing.
2. Do not commit OpenAI/Elgato proprietary assets, Codex installation files, databases, logs, rollout files, personal paths, or generated release bundles.
3. Do not add hotkey or task-database fallbacks to the native bridge without a separate design discussion.
4. Update compatibility notes when changing renderer integration behavior.
Expand All @@ -15,6 +15,7 @@ npm ci
npm run check
npm test
npm run validate
npm run audit:release
```

Pull requests should explain the tested Codex version, Stream Deck version, hardware model, and manual verification performed.
Pull requests should explain the tested Codex version, Stream Deck version, operating system, hardware model, and manual verification performed. Never describe fixture or build validation as physical-device verification.
179 changes: 78 additions & 101 deletions README.md

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ Do not publish a working exploit, authentication data, Codex databases, rollout

## Important boundary

Codex Deck starts Codex with a Chrome DevTools endpoint bound to `127.0.0.1`. This is intentionally local but remains accessible to processes running on the same Windows account. Do not expose, forward, or rebind that port to a network interface.
Codex Deck starts Codex with a Chrome DevTools endpoint bound to `127.0.0.1`. This is intentionally local but remains accessible to processes running as the same Windows or macOS user. Do not expose, forward, or rebind that port to a network interface.

The optional multi-host relay is a separate authenticated, typed protocol. Use only its loopback SSH tunnel or an explicit Tailscale address. Never forward CDP, use wildcard/public listeners, commit relay state, or share pairing tokens in commands, issues, logs, or screenshots.

Release artifacts are audited for private runtime state, known personal setup markers, and protected Codex keycap SVG files. This reduces accidental packaging risk but does not replace review.
59 changes: 54 additions & 5 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Launcher

`Start-CodexDeck.ps1` finds the installed Microsoft Store Codex package. If a healthy debug-enabled Codex process already exists, it reuses its loopback port. Otherwise it closes the normal Codex processes, chooses an unused loopback port, writes the port number to `%LOCALAPPDATA%\CodexDeck\codex-micro-bridge.json`, and starts `ChatGPT.exe` with:
`Start-CodexDeck.ps1` finds the installed Microsoft Store Codex package. If a healthy debug-enabled Codex process already exists, it reuses its loopback port. Starting an already-running normal session requires an explicit launcher/recovery path; a read-only `-DryRun` never changes it. The launcher chooses an unused loopback port, writes it to `%LOCALAPPDATA%\CodexDeck\codex-micro-bridge.json`, and starts `ChatGPT.exe` with:

```text
--remote-debugging-address=127.0.0.1
Expand All @@ -15,9 +15,34 @@ The bundled runtime helper connects to that renderer and enables the Micro featu

The launcher does not edit the Codex installation, Codex LevelDB, task database, rollout files, or logs.

When startup monitoring is installed, a durable copy under `%LOCALAPPDATA%\CodexDeck\launcher` runs `Watch-CodexDeck.ps1` as
a single hidden PowerShell process. It dynamically resolves the newest Codex
Microsoft Store package on every check, so an app update can change the install
path without invalidating the watcher. A named mutex prevents duplicates.

The watcher follows three safety rules:

1. A healthy debug-enabled Codex session is reused and never restarted.
2. A normal session that was already open when monitoring was installed is left untouched until its next normal restart.
3. A later Codex launch, update restart, or crash recovery without the required loopback port receives at most one recovery restart for that process generation.

Stale port metadata is removed automatically. The bounded watcher log lives at
`%LOCALAPPDATA%\CodexDeck\watcher.log`.

On macOS, the launcher discovers the running or installed app by its signed
bundle metadata, reads `CFBundleExecutable`, and launches the app bundle through
LaunchServices with the same loopback-only debugging arguments. The per-user
LaunchAgent watcher stores a main-process generation (PID, start time, and
executable path), reuses healthy bridges, and performs at most one graceful
recovery restart for a later unbridged generation. The generation and recovery
policy is persisted atomically and guarded by a PID-directory lock.

Both platforms persist a stable `hostId`, `hostName`, and platform identifier.
The relay uses that identity; the CDP port is never a relay endpoint.

### Stream Deck plugin

The plugin discovers the loopback port from the state file or from the command line of a running Codex process. It then uses Chrome DevTools Protocol `Runtime.evaluate` calls to:
The same plugin runs on Windows and macOS. It discovers the local loopback port from the platform state file or from a running Codex process. It then uses Chrome DevTools Protocol `Runtime.evaluate` calls to:

1. discover the current version-hashed Codex renderer modules;
2. announce a connected Micro device state;
Expand All @@ -28,6 +53,25 @@ The plugin discovers the loopback port from the state file or from the command l

The bridge does not emulate a USB HID device and installs no driver.

### Optional multi-host relay

The Mac watcher can host an authenticated WebSocket relay on loopback behind an
SSH tunnel or on one explicitly configured Tailscale address. Wildcard listeners are rejected. The Windows
Stream Deck plugin connects as a client, merges typed Mac and Windows snapshots,
and routes agent presses by stable `(hostId, threadKey)` identity. Other controls
target the host selected by the Windows/Mac toggle.

Host ownership is resolved from exact local rollout filenames, not from a
renderer's mirrored recent list. This distinguishes a Mac desktop task mirrored
through Windows remote SSH from a genuinely Windows-owned task. File contents,
prompts, responses, and project names are never read. The relay never reads or
proxies the remote CLI app-server stream.

The relay protocol has no arbitrary-evaluation, filesystem, shell, or raw-CDP
operation. Payloads are capped at 64 KiB, authentication is required before a
snapshot or command is accepted, and command results use request IDs with
bounded timeouts.

### Rendering

Agent keys are original deterministic SVGs generated in memory from task title and state. The status palette is:
Expand All @@ -43,15 +87,15 @@ Agent keys are original deterministic SVGs generated in memory from task title a

The renderer derives the active Codex appearance from explicit theme tokens when available and falls back to the computed renderer surface luminance. Dark mode uses layered charcoal surfaces rather than pure black, with off-white text and slightly lifted status colors for the Stream Deck display.

Official Codex Micro keycap SVG contents are not part of the source or release. Optional user-local files are loaded from `%LOCALAPPDATA%\CodexDeck\icons` and wrapped in the project's neutral key surface at runtime.
Official Codex Micro keycap SVG contents are not part of the source or release. Optional user-local files are loaded from `%LOCALAPPDATA%\CodexDeck\icons` on Windows or `~/Library/Application Support/CodexDeck/icons` on macOS and wrapped in the project's neutral key surface at runtime.

The controller uses non-overlapping self-scheduled refreshes and caches the last
image sent to each action instance. Unchanged keys therefore produce no repeated
USB image writes. Animated frames are limited to working and approval states.

## Trust boundary

CDP provides privileged access to the Codex renderer. Binding to `127.0.0.1` prevents direct access from another machine, but not from another process running under the local user account. Treat the launcher-started session like any other local debugging session:
CDP provides privileged access to the Codex renderer. Binding to `127.0.0.1` prevents direct access from another machine, but not from another process running as the same local user. Treat the launcher-started session like any other local debugging session:

- do not run untrusted software at the same time;
- do not change the debug address to `0.0.0.0`;
Expand All @@ -60,7 +104,12 @@ CDP provides privileged access to the Codex renderer. Binding to `127.0.0.1` pre

## Data flow

Codex Deck has no server, API key, analytics endpoint, or update service. Runtime data stays between Stream Deck, the local plugin process, and the local Codex renderer.
In single-host mode Codex Deck has no server, API key, analytics endpoint, or
update service. Runtime data stays between Stream Deck, the local plugin
process, and the local Codex renderer. Optional multi-host mode adds one
user-configured Mac listener reachable through SSH or inside the encrypted
tailnet; titles, task IDs, states, ownership metadata, and typed commands pass
between the paired machines and nowhere else.

## Compatibility boundary

Expand Down
13 changes: 7 additions & 6 deletions docs/ICON_SETUP.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Local Codex Micro icon setup

Codex Deck does not distribute the official Codex Micro keycap SVGs. This workflow keeps them local to your Windows account.
Codex Deck does not distribute the official Codex Micro keycap SVGs. This workflow keeps exact local copies outside the repository and release.

## Destination

Create this directory:
Use the directory for the computer running Stream Deck:

```text
%LOCALAPPDATA%\CodexDeck\icons
Windows: %LOCALAPPDATA%\CodexDeck\icons
macOS: ~/Library/Application Support/CodexDeck/icons
```

Each SVG must be named after its Codex keycap ID. The known IDs in the tested Codex build are:
Expand All @@ -29,12 +30,12 @@ The plugin reads matching files for the six synchronized physical action slots
and for the standalone keycap actions. If a file is unavailable, the key still
receives a readable themed label instead of remaining blank.

## Prompt Codex to copy the files locally
## Ask Codex to copy the existing files locally

Run this prompt in a Codex task on the Windows PC where Codex is installed:
Run this on the same computer as the Codex installation. Replace `<ICON_DIRECTORY>` with the platform destination above:

```text
Inspect my locally installed Codex Windows app and locate the official Codex Micro keycap SVG files that are already present on this machine. Do not redraw, modify, download, upload, publish, or commit them. Copy the exact local SVG files into %LOCALAPPDATA%\CodexDeck\icons and rename each copy to its Codex keycap ID, such as FAST.svg, APPR.svg, REJ.svg, SPLIT.svg, MIC.svg, MIND+.svg, and MIND-.svg. Keep the source files unchanged. Verify that every copied file is a valid SVG with a viewBox, confirm that the destination is outside the Git repository, and report only the source location, destination location, filenames, and validation result. If the assets cannot be confirmed as files from my local Codex installation, stop without copying anything.
Inspect my locally installed Codex desktop app and locate the official Codex Micro keycap SVG files that are already present on this machine. Do not redraw, modify, generate, download, upload, publish, or commit them. Copy the exact local SVG files into <ICON_DIRECTORY> and rename each copy to its Codex keycap ID, such as FAST.svg, APPR.svg, REJ.svg, SPLIT.svg, MIC.svg, MIND+.svg, and MIND-.svg. Keep the source files unchanged. Verify that every copied file is a valid SVG with a viewBox, confirm that the destination is outside the Git repository, and report only the source location, destination location, filenames, and validation result. If the assets cannot be confirmed as files from my local Codex installation, stop without copying anything.
```

This prompt intentionally limits the work to files already present in your installation and explicitly prevents them from entering the public repository.
Expand Down
85 changes: 85 additions & 0 deletions docs/MACOS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# macOS-only setup

This mode runs Stream Deck and Codex on the same Mac. It needs no Windows PC, relay, SSH, Tailscale, or host-target key. The same plugin package used on Windows launches new tasks and agent links locally through macOS.

## Install

1. Install `com.simeo.codex-deck.streamDeckPlugin` in Stream Deck for macOS.
2. Extract `codex-deck-launcher-macos-vX.Y.Z.zip`. The official release ZIP is created on macOS so its executable bits are preserved.
3. Install Node.js 20 or newer if `node --version` is unavailable.
4. From Terminal in the extracted launcher directory, run:

```zsh
./start-codex-deck.sh dry-run
./start-codex-deck.sh self-test
./start-codex-deck.sh start
```

**Start Codex Deck.command** is the double-clickable equivalent of `start`.
5. Open **Codex Settings > Codex Micro**, configure the native slots, and add the actions from the [recommended layout](../README.md#recommended-15-key-layout). Leave the Windows/Mac target position empty or replace it with another action.

If an archive tool removed executable permissions, restore only the two launcher files:

```zsh
chmod +x start-codex-deck.sh "Start Codex Deck.command"
```

## Keep the bridge available

```zsh
./start-codex-deck.sh install
```

`install` copies the watcher runtime into Application Support and installs a per-user LaunchAgent. It does not restart a normal Codex session already open during first installation. A later unbridged Codex generation may receive one graceful recovery restart; the same generation is never restarted repeatedly.

Update by extracting the new launcher and running `install` again. The stable host identity, optional relay configuration, and user-owned icons are preserved.

## Commands

```zsh
./start-codex-deck.sh dry-run
./start-codex-deck.sh self-test
./start-codex-deck.sh start
./start-codex-deck.sh install
./start-codex-deck.sh uninstall
```

`start` asks for an explicit `yes` before restarting an already-running normal Codex session. Codex launches through LaunchServices so Input Monitoring/TCC permissions remain attached to the signed app bundle.

## Files

```text
~/Library/Application Support/CodexDeck/
codex-deck-macos.mjs
watcher-launch.sh
codex-micro-bridge.json
host.json
watcher-state.json
watcher.log, watcher.log.1 ...
icons/ # optional user-owned SVG copies

~/Library/LaunchAgents/com.simeo.codex-deck.watcher.plist
```

State writes are atomic, a PID-directory lock prevents duplicate watchers, and logs rotate at approximately 1 MB with three retained generations. Nothing inside the Codex app bundle is modified or re-signed.

## Diagnostics

```zsh
./start-codex-deck.sh dry-run
tail -n 100 "$HOME/Library/Application Support/CodexDeck/watcher.log"
launchctl print "gui/$(id -u)/com.simeo.codex-deck.watcher"
plutil -lint "$HOME/Library/LaunchAgents/com.simeo.codex-deck.watcher.plist"
```

## Optional multi-host mode

Only configure `relay-config` when one Windows-connected Stream Deck should also control this Mac. The relay is disabled in Mac-only mode. See [Windows + Mac multi-host relay](MULTI_HOST.md).

## Uninstall

```zsh
./start-codex-deck.sh uninstall
```

This unloads the LaunchAgent and removes its runtime, bridge state, policy state, lock, and logs. It deliberately preserves `host.json`, optional relay configuration, and `icons/`. No Codex application data is removed and Codex is not restarted.
Loading
Loading