offstage is a tool for macOS that gives your coding agent its own Mac desktop. It provides Claude Code, Codex and opencode with a second, logged-in macOS account to work in, so that GUI work runs behind your session instead of on top of it. Simulators, Xcode UI tests and the app your agent just built open on that account's desktop, while your windows, keyboard and mouse stay yours. It is free and MIT licensed, built for Macs on Apple Silicon with Node 20 or newer, and the helper account takes a single setup command.
The problem it addresses is focus theft by computer-use agents. Coding agents do more than edit files: they run test suites, boot simulators, drive headed browsers and open the applications they have just built. Every one of those actions normally needs a real display, a real window server and real input, which on a single-user Mac means your screen. Faced with that, you either watch the agent take over your desktop — windows flashing, the pointer moving on its own, focus pulled away from what you were doing — or you avoid running that work as often as you would like. offstage's answer is to stop sharing one desktop: it gives the agent a second macOS account that is logged in at the same time as yours, so both desktops exist simultaneously and the agent's work happens somewhere that is not your screen.
At the centre of offstage is command routing. Before anything runs, offstage reads the command and sends it to the cheapest place that keeps it off your display. Commands that need a real Mac desktop — xcodebuild test, xcrun simctl, XCUITests, open -a, osascript, or a built .app — go to the second macOS account, which has its own desktop, window server and input; this is the reason offstage exists. Headed browser work, signalled by flags such as --headed or headless: false, or by cypress open and WebGL and GPU flags, goes to a Linux container with a virtual display if Docker is installed — a real display, just not yours. Commands that never open a window, such as npm test, vitest, headless Playwright and Puppeteer, run right where they are, because wrapping them would only cost time. And a fourth class — installers, a .pkg, a .dmg or hdiutil — runs nowhere at all: both accounts share one machine, so offstage refuses these and no flag overrides that refusal.
Agents drive offstage through MCP tools. offstage_route tells the agent which lane a command would get, and offstage_run executes it there. For testing an app with a GUI, the agent uses offstage_session_launch, which waits until the app registers and returns its pid; then offstage_session_screenshot, decide, offstage_session_input, and another screenshot to confirm. Coordinates are points, not pixels, so a pixel coordinate is divided by the screenshot's scale. offstage_session_quit closes the app when the agent is done. A status of "skipped" means nothing ran anywhere, and the agent is instructed to surface the fix line from diagnostics and stop rather than re-running the command outside offstage. A "refused" status means offstage will not run an installer on any lane, and the decision to run it belongs to the user.
Isolation is enforced rather than assumed. The daemon posts input to its own session only and never to the global input stream that feeds your screen; if its session is ever the one on the console, it refuses to send input at all. In testing, the window server's own log showed every synthetic event landing in the helper session and none reaching the console. The helper account is an ordinary second user, so it cannot write to your files and can read only what macOS lets any other local account read. Where an agent needs access to a project the helper account cannot otherwise reach, offstage session share grants read-only access to that folder — one folder at a time, reversible with unshare — and each run writes its output to its own artifacts folder.
offstage's approach is deliberately not virtualisation. It is a second user account on the Mac you already have, with no guest OS and nothing to boot. It takes about 3 GB of disk, where the macOS VM image the project measured was a 68.8 GB download. The trade-off is that both accounts share the Mac's CPU, memory and disk. Setting the helper account up happens once, and it needs sudo and one click, so the agent cannot do it: install the CLI with npm i -g @viraatdas/offstage and run offstage doctor to learn which lanes work on this Mac and how to fix the rest; run sudo offstage session setup --create, which creates an ordinary account called computeruse and builds a small Swift daemon for it, granting Screen Recording and Accessibility if your terminal has Full Disk Access and otherwise naming the two toggles to flip, printing the whole root script before running it; switch to the account once using the user menu in the menu bar and then switch back, which starts the daemon and leaves the account logged in behind yours until the Mac restarts; and optionally share a project folder. offstage session status exits 0 when the account is ready. The agent can then connect itself: an MCP install line for Claude Code, a config block for Codex in ~/.codex/config.toml, or an entry in opencode.json. Anything that can run a shell command can use the offstage CLI instead — offstage route -- and offstage run -- are the same code path as the MCP tools.
The benefit is that the agent gets a genuine macOS desktop while you keep yours. GUI test runs no longer steal focus, move your pointer or cover your screen, and you can carry on watching a video or working while an Xcode UI test grinds through a simulator on the other account. Because the second account is a real account rather than an emulation layer, the agent sees the same window server and input model as a normal user. Because input cannot reach the console session, the agent cannot click on your screen even by accident. Because folder sharing is read-only, handing the helper account access to a repository does not give it write access to your work. And because headless commands run in place, the tool does not slow down the parts of the agent's work that never needed a display.
Typical scenarios follow the lanes. A developer asks Claude Code to run an Xcode UI test or boot a simulator: the work lands in the computeruse account, and the app under test opens on that account's desktop. An agent finishes building an app and needs to try it: it launches the app in the helper session, takes a screenshot, issues input, screenshots again to confirm, and quits the app. A Playwright or Cypress run needs a headed browser or WebGL: if Docker is present, it runs in a Linux container with a virtual display. A macOS automation step uses open -a or osascript, again off the developer's screen. Ordinary test suites such as npm test or vitest run in place. Developers who want the behaviour to stick paste the provided instruction into their project's AGENTS.md or CLAUDE.md, so the agent connects offstage itself and keeps GUI work off the screen from then on.
offstage is aimed at developers on Apple Silicon Macs who run coding agents such as Claude Code, Codex and opencode and who want those agents to be able to do GUI work without hijacking a desktop. It works with those three agents through MCP, and Claude Code also as a plugin. Anything else that can run a shell command can use the offstage CLI, which runs the same code path. It requires Node 20 or newer, adds a small Swift daemon for the helper account, and uses a Linux container with a virtual display only for headed browser work and only if Docker is installed. offstage is free, MIT licensed, and open source, with the source published on GitHub.
In short, offstage separates the question of whether an agent may use a desktop from the question of which desktop it may use. It hands the agent a real, logged-in macOS account of its own — one that boots no VM, costs about 3 GB of disk, and cannot post input to your session — so computer-use work keeps happening, and your screen, keyboard and mouse stay exactly where they belong.