feat(providers): wire docker dispatch and image cache UX#1352
Draft
mvanhorn wants to merge 6 commits intostablyai:mainfrom
Draft
feat(providers): wire docker dispatch and image cache UX#1352mvanhorn wants to merge 6 commits intostablyai:mainfrom
mvanhorn wants to merge 6 commits intostablyai:mainfrom
Conversation
Adds Docker as a fourth provider triple alongside local and SSH. Pure plumbing: pty / filesystem / git providers + docker engine detection + image build + container lifecycle + bind-mount path resolution. No user-facing UI yet, no dispatch wiring. Mirrors the SSH provider triple's API. Cross-platform: macOS (Docker Desktop / Colima), Linux (Docker Engine, rootless), Windows (Docker Desktop WSL2 named pipe). Tests cover happy and error paths through a docker-engine-fake.ts recorder, so no real Docker daemon is required. Foundation PR for the docker isolation series. Follow-ups will add the user-facing toggle and dispatch wiring.
- Allocate TTY for Docker PTY sessions via tty flag on spawnExec, so isatty() returns true for shells, vim, and ncurses programs. - Return originalContent + modifiedContent for working-tree diffs instead of returning a unified patch as modifiedContent. - Return blob contents for branch diffs and honor options.oldPath for renames, matching the local provider's GitDiffResult shape. - Preserve the new path as path and the old path as oldPath when parsing R100 rename entries (was reversed). - Surface git porcelain v2 u records as unresolved conflict entries so docker worktrees in conflict show their conflicted files. - Resolve repo default branch via origin/HEAD before browseFile, instead of hardcoding 'main' for remote file URLs.
Adds an opt-in "Isolate" toggle per worktree row. When enabled, Orca builds (or reuses cached) a Docker image via the foundation in the docker provider series and persists the choice. Default off. Repo-level default toggle in Repository Settings: "Isolate worktrees from this repo by default." Implementation: - 3 new IPC handlers in src/main/ipc/docker.ts (engine-status, build-image, set-worktree-isolation) with build-progress events - IsolateToggleButton.tsx covers off / building / on / disabled-no-docker states with vitest coverage - RepositoryPane gains the per-repo defaultIsolation toggle - Worktree shape extends with isolation field; defaultIsolation flows through worktree-remote so new worktrees inherit the repo default - vitest.config.ts updated so .test.tsx files are discovered for the new component tests
Building a local Docker image against an SSH repo's remote path would fail or target the wrong machine. Add a guard in both the main process and the renderer: - buildImage IPC returns an explicit error when repo.connectionId is set - IsolateToggleButton disables itself for SSH repos with a clear tooltip Local-only Docker isolation is the v1 surface. SSH-repo container isolation is a separate problem (would require routing the build through the SSH provider) and is out of scope for this series.
Closes the docker isolation series. When a worktree has isolation: 'docker', pty / filesystem / git operations route through the Docker provider triple (built in stablyai#1349, surfaced in stablyai#1350) instead of the local provider. Existing host and SSH paths unchanged. Implementation: - worktree-isolation-lookup.ts: small Store-backed helper - docker-container-registry.ts: per-worktree container cache. Spawn on first attach, terminate on worktree close - pty.ts: getProvider extended to route by isolation - filesystem.ts: third dispatch branch for docker-isolated worktrees alongside the existing local + SSH branches; includes git ops Residuals from stablyai#1349 fixed in this PR: - node-pty wrapping for docker exec (so interactive programs see a real terminal in packaged Electron builds) - search flag honoring (useRegex, wholeWord, include/exclude patterns) in DockerFilesystemProvider.search via rg-in-container - binary blob detection in DockerGitProvider.getDiff and getBranchDiff Adds an "Docker Images" settings pane plus IPC handlers for docker:list-cached-images and docker:prune-image. Prune only touches images carrying the orca label.
Two findings from the dispatch self-review: - DockerPtyProvider.spawn now wraps opts.command in /bin/sh -c instead of treating the whole command string as the docker exec executable. So pty:spawn calls like `bash setup.sh` or `claude "prompt"` start a shell inside the container and run the command, matching LocalPtyProvider semantics. - docker-worktree-paths only rejects true parent-relative paths (exactly `..`, `../`, `..\`, or an absolute path) instead of any string that happens to start with `..`. Filenames like `..cache` or `..tmp` now map into the container as expected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the docker isolation series. Wires the Docker provider triple from #1349 into the inline dispatch in
src/main/ipc/pty.tsandsrc/main/ipc/filesystem.ts. When a worktree carriesisolation: 'docker'(set by the toggle in #1350), pty operations route through the Docker provider triple instead of local. Existing host and SSH paths are unchanged.This PR also addresses the three residuals that PR #1349 disclosed in its AI Review Report:
docker execso interactive programs see a real terminal in packaged Electron buildsDockerFilesystemProvider.searchvia rg-in-containerDockerGitProvider.getDiffandgetBranchDiffAdds a "Docker Images" settings pane plus IPC for
docker:list-cached-imagesanddocker:prune-image. Prune only touches images carrying theorcalabel.This is PR 3 of 3:
Screenshots
UI changes: a new "Docker Images" pane in Settings showing cached images (id, size, last-used, prune button). The toggle UI from #1350 is unchanged here.
The terminal / file-explorer / source-control side has no new visual elements; the change is routing under the hood.
Screenshots will land before merge once the toggle in #1350 lands so the demo can show: enable toggle on a worktree, agent runs in a container, file edit syncs, then prune the image.
Testing
pnpm lintpnpm typecheckpnpm test(full suite, includes 165 tests across 18 docker-related files)pnpm buildAI Review Report
A code review pass was run before submission. Risks checked and findings:
pty.tsandfilesystem.tsare reached by the same call shapes as before. Tests assert no regressions on those paths.docker-container-registry.tsis idempotent ongetOrCreateContainer(multiple panes for one worktree share one container) and terminates cleanly when the worktree closes.tty: truepaths now usenode-pty.spawn('docker', ...)instead ofchild_process.spawnwith piped stdio. MatchesLocalPtyProvider.DockerFilesystemProvider.searchnow shellsrginto the container with the same flag set the local search pipeline uses.getDiffandgetBranchDiffusegit cat-file -tplus a 8KB null-byte sniff, mirroringisBinaryBufferinsrc/main/ipc/filesystem.ts.path.posix.joinfor in-container,path.joinfor host. Windows host->container path translation goes through the helper from feat(providers): docker provider foundation (pty / filesystem / git) #1349 (with cross-platform tests).helpers/utils.Three findings surfaced. Two fixed, one disclosed:
pty:spawnwithargs.command(e.g.,bash setup.sh,claude "prompt") now wraps through/bin/sh -c <command>instead of treating the command string as the docker exec executable. Setup runners and agent launches start correctly inside the container.docker-worktree-paths.tsonly rejects exact..,../,..\\, or absolute paths. Filenames legitimately starting with..(e.g.,..cache/file) map into the container as expected.args.worktreeId. Today, the file explorer, editor, search, and source-control IPC signatures don't all carryworktreeId— they passworktreePathand optionalconnectionId. So Docker filesystem / git routing currently activates only on the subset of handlers that already accept worktreeId. PTY routing works end-to-end. A follow-up PR (3b) will plumbworktreeIdthrough the preload API surfaces and renderer call sites so the routing applies to all worktree-scoped operations. This is structural rather than a bug — the wiring is correct, the renderer just doesn't tell main which worktree it's acting on for non-PTY operations yet.Security Audit
Container processes run with the host user's uid/gid. No host paths leak in beyond the bind-mounted worktree at
/workspace. Path translation (host->container) goes through the dedicated helper with vitest coverage including the parent-relative guard.docker:prune-imageonly acts on images with theorcalabel and a tag matching the conventionorca:<repo>:<dockerfile-hash>. Reject all other ids; confirmed in tests.docker:list-cached-imagesreadsdocker image lswith thelabel=orcafilter so user-managed images are never enumerated.pty:spawnfor docker-isolated worktrees: command strings are wrapped through/bin/sh -c. The shell argv is built as['/bin/sh', '-c', command](separate array entries, no shell-string interpolation in the host process). Container process supervision is handled by thenode-ptywrapper arounddocker exec, same lifecycle semantics as the local PTY.No new outbound network surface from Orca itself. Whatever the agent does inside its container is governed by the user's existing agent trust path.
Notes
This PR builds on #1349 and #1350. Because neither has merged yet, this PR's diff against
mainis cumulative across all three PRs in the series. After #1349 and #1350 merge, this branch will rebase to its true delta.Series:
Recommended merge order: 1, 2, 3 sequentially.
The disclosed
worktreeIdplumbing limitation in the AI Review Report above is a real gap. If the maintainers want a clean end-to-end demo before merging this series, splitting it into PR 3a (this PR, wiring + fixes) and PR 3b (worktreeId plumbing through preload + renderers) keeps each surface narrow. Open to direction.X: @mvanhorn
AI was used for assistance.