Fix web sidecar proxy requests so same-origin browser requests reach the daemon without tripping origin validation, while unrelated origins remain rejected. Fixes#388.
PR #258 standardized agent spawning through `createCommandInvocation`,
which on Windows wraps `.cmd` / `.bat` paths in `cmd.exe /d /s /c <line>`
and quotes each argument with cmd-style doubled quotes. PR #232's
follow-up fix for `shell:true` was lost in that refactor, and the new
shape has its own quoting bug on argv-style spawn:
1. cmd.exe `/s /c` strips exactly one leading and one trailing `"` from
the rest of the command line.
2. Node, with `windowsVerbatimArguments` unset, escapes each argv element
using CommandLineToArgvW rules — so the inner `"path with space"`
ends up surfacing to cmd.exe with an extra layer of `\"` escaping
that cmd doesn't understand.
Together these collapse `"C:\Users\Ethical Byte\...\codex.CMD" --help`
into `C:\Users\Ethical Byte\...\codex.CMD --help` with no quoting
preserved, and cmd.exe parses the first space as a token boundary —
"`Ethical` is not recognized as an internal or external command." See
issue #315 for the full repro.
The fix mirrors what Node's own `child_process.spawn({ shell: true })`
does internally: wrap the entire joined command line in an extra `"…"`
and set `windowsVerbatimArguments: true`. The outer wrap absorbs the
`/s /c` strip, leaving inner per-arg quoting intact, and the verbatim
flag tells Node to pass argv through to CreateProcess unchanged.
Changes:
- `packages/platform/src/index.ts`
- Extend `CommandInvocation` with optional `windowsVerbatimArguments`.
- Extract the cmd.exe shim builder into `buildCmdShimInvocation` and
apply the outer wrap + verbatim flag in both `createCommandInvocation`
and `createPackageManagerInvocation`.
- Forward the flag through `spawnBackgroundProcess` and
`spawnLoggedProcess`.
- `apps/daemon/src/server.ts` — agent spawn forwards
`invocation.windowsVerbatimArguments`. This is the call site that
hit #315 in the wild (Codex CLI `.CMD` shim, user dir with space).
- `tools/pack/src/win.ts` — `runPnpm` and `runNpmInstall` forward the
flag through `execFileAsync`. Affects the Windows packaged-build
pipeline when run from a path with spaces.
- `tools/dev/src/index.ts` — `runLoggedCommand` accepts and forwards the
flag; `buildDesktop` propagates it from
`createPackageManagerInvocation`. Affects local dev on Windows.
Tests:
- 9 new unit tests in `packages/platform/src/index.test.ts` stub
`process.platform` so both Windows and POSIX branches run on every
CI runner. Coverage:
- POSIX pass-through.
- Windows non-shim binary pass-through.
- `.CMD` shim with spaces in the binary path (the #315 repro).
- `.bat` shim parity.
- Argv elements with spaces alongside the shim path.
- Argv elements without whitespace stay unquoted.
- `process.env.ComSpec` fallback.
- `npm_execpath` short-circuit (cross-platform).
- POSIX pnpm pass-through.
- Windows pnpm wrapped through cmd.exe.
Closes#315.
* fix(tools-dev): strip ELECTRON_RUN_AS_NODE before spawning desktop
Parent processes such as Electron-based IDEs may set ELECTRON_RUN_AS_NODE=1
in their environment for sidecar/script reuse. When tools-dev inherits this
env via process.env, the spawned electron.exe runs as plain Node and fails
to inject main-process APIs (app, BrowserWindow, protocol all become
undefined). Explicitly drop the variable before spawning so desktop always
boots in real Electron mode regardless of caller environment.
* fix(desktop): ensure BrowserWindow is visible on initial load
Windows focus-stealing prevention can leave detached-spawned GUI windows
minimized or hidden, even when constructed with show:true. Add a small
ensureWindowVisible helper that restores from minimized state and forces
show+focus after the placeholder URL loads. Cross-platform safe: only
acts when window is actually hidden or minimized, preserving any user
window-state adjustments.
ensureWebDevNodeModules() called fs.symlink(target, path, "dir") to
link apps/web/node_modules into the web runtime root. On Windows,
"dir" symlinks require either Administrator rights or Developer Mode
(SeCreateSymbolicLinkPrivilege). Standard non-elevated user accounts
without Developer Mode get EPERM and tools-dev exits before web ever
starts.
Junctions ("junction") are functionally equivalent for directory-only
links on the same volume, work for any user without elevation or
Developer Mode, and are silently treated as plain symlinks on POSIX.
The existing isSymbolicLink() check on the next launch still matches
junctions on Node.
Reproduced on Windows 11 + Node 24 with a non-elevated PowerShell
session and Developer Mode off.
* feat(tools-dev): add --prod flag and OD_HOST for headless server deployment
- Lazy-load electronBinaryPath so daemon+web can start without Electron
- Add --prod flag to tools-dev start/run that sets NODE_ENV, OD_WEB_PROD,
and OD_WEB_OUTPUT_MODE automatically for production Next.js builds
- Add OD_HOST env var support to daemon and web sidecar bind addresses
(defaults to 127.0.0.1, set to 0.0.0.0 for remote access)
- Skip next.config.ts distDir override when OD_WEB_PROD=1 so production
builds resolve the default .next directory
Closes#221
* fix: address PR review — hardcode daemon bind to 127.0.0.1, cache electron path
- Revert OD_HOST from daemon: daemon is a local privileged process and must
only bind 127.0.0.1. Remote access goes through the web sidecar proxy.
- Web sidecar resolveDaemonOrigin now always uses 127.0.0.1, separated from
the web bind address (OD_HOST) so the daemon proxy works correctly even
when the web listener binds 0.0.0.0.
- Add OD_HOST character validation to reject clearly invalid values early.
- Cache electronBinaryPath getter result to avoid repeated require() calls.
---------
Co-authored-by: yamsfeer <yamsfeer@users.noreply.github.com>
tools-dev generated a temp web tsconfig with Windows backslash relative paths in extends, which Next/TypeScript failed to resolve in some environments. Normalize runtime tsconfig/dist path strings to POSIX separators so dev config resolution works consistently across Windows/Linux/macOS.
Surface daemon log tails when tools-dev waits for daemon status and add targeted guidance for native Node addon ABI mismatches.
Generated-By: looper 0.0.0-dev (runner=worker, agent=gpt-5.5)
* feat(dev): add desktop tools-dev control plane
* refactor(sidecar): split Open Design contracts
Move Open Design-specific sidecar protocol definitions into @open-design/contracts so sidecar and platform can remain descriptor-driven primitives.
* refactor(daemon): organize package sources
Keep daemon app code, tests, and sidecar entrypoints in separate package directories so each layer can be built and verified independently.
* chore(repo): streamline maintenance entrypoints
Centralize agent guidance by directory and reduce root command chains while preserving the existing build scope.
* docs: translate agent guidance to English
* fix(sidecar): tolerate stale IPC sockets
Remove stale Unix socket files only after confirming no listener is active, so tools-dev can restart after unclean shutdowns.