Skip to content

(MOT-4388) feat(shell): sandbox target picker on the page - #767

Open
rohitg00 wants to merge 2 commits into
mainfrom
feat/shell-sandbox-target
Open

(MOT-4388) feat(shell): sandbox target picker on the page#767
rohitg00 wants to merge 2 commits into
mainfrom
feat/shell-sandbox-target

Conversation

@rohitg00

Copy link
Copy Markdown
Contributor

What

Every shell function already takes target: { kind: "sandbox", sandbox_id } — host and sandbox share one wire shape on exec, exec_bg, and all fs::*. The page never exposed it. This adds a target picker to shell's injected page so the same file browser serves the host workspace and every live microVM.

  • Picker: host (default — byte-identical behavior, still on the coder::* twin surface) + one entry per live sandbox. Fleet source: one sandbox::list read on mount, plus {kind:'fleet'} snapshots over the sandbox-code-runner::event trigger when that worker is present (handler iii::shell-ui::fleet::<browserId>); sandbox::list function-not-found hides the picker entirely.
  • Guest mode: the tree/search swap to the target-carrying shell::fs::* twins (ls lazily per expand from /, search via fs::grep with regex-escaped literals). Host-only affordances (git tab, workspace roots) hide; the header shows the sandbox id (truncated, copyable) and a running dot; a sandbox that leaves the fleet degrades to "sandbox gone — reaped or stopped" with a one-click return to host.
  • Guest reads: shell::exec cat — the page cannot consume shell::fs::read's ContentRef channel (the console extension client has no channel API), and the README's read_inline wrapper does not exist in-repo. Truncation is detected by byte-count against fs::stat; binaries by NUL probe; both flip the viewer read-only with the existing notes.
  • Guest writes: none exist for this surface (inline content is S210 on sandbox targets; the mandatory ContentRef form is unproducible from the page) — the editor is explicitly read-only for guests with a visible note, rather than a save that breaks.

Verification

  • shell/ui: tsc clean, vitest 100 passed (32 new: tolerant fleet parsing, host-omits/sandbox-carries payload threading, gone-degrade, guest tree, regex escape), biome clean, build green (bundle unchanged aside from the new module).
  • shell: cargo fmt/clippy -D warnings clean, 1334 tests passed.
  • Live-verified on a running engine: picker listed a live python microVM, its real rootfs browsed from the shell page, read-only note shown, host mode unchanged.

Fixes MOT-4388

Every shell::fs::* and shell::exec* function already accepted
target: { kind: "sandbox", sandbox_id } — one wire shape for host and
guest — but the injected console page only ever spoke to the host. The
page now exposes the selector: a target picker beside the root
affordance lists live microVMs (one sandbox::list read on mount, pushed
{ kind: "fleet" } snapshots over a tab-scoped
sandbox-code-runner::event trigger, manual refresh for when the live
binding is absent; function-not-found on sandbox::list hides the picker
and the page stays pure host).

Host mode is byte-identical to before: coder::* calls, no target field
on the wire (omission is the serde default — an explicit null would be
a deserialize error). With a sandbox selected the same browser/editor
retargets: the tree roots at / and fills in lazily via shell::fs::ls
per expanded directory, search maps onto shell::fs::grep, and every
guest payload carries the target verbatim. Host-coupled chrome drops
out — git tab, workspace root picker, editor writes — and the header
shows the sandbox id (truncated, copyable, full in the title) with a
running/stopped dot. A selected sandbox that leaves the fleet degrades
to the "sandbox gone — reaped or stopped" empty state with a
back-to-host action rather than failing calls silently.

Guest read/write decision: coder::read-file is host-only and
shell::fs::read returns a stream-channel ref the console extension
client cannot consume (host.iii has no channel API), so guest reads
ride shell::exec cat stdout — the same way the git tab already consumes
file bodies via git show — with truncation caught by comparing byte
counts against shell::fs::stat and a NUL probe for binaries. Writes
have no guest path at all (shell::fs::write requires the streamed
ContentRef form on sandbox targets, which the page cannot produce), so
the editor is read-only for guests with a visible note instead of a
broken save.
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workers Ready Ready Preview Aug 11, 2026 12:35pm
workers-tech-spec Ready Ready Preview Aug 11, 2026 12:35pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@rohitg00, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 21 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 548e75be-f33d-4736-bec7-8159e2df471a

📥 Commits

Reviewing files that changed from the base of the PR and between 3b2f204 and f8107b9.

📒 Files selected for processing (8)
  • shell/ui/src/page/EditorPane.tsx
  • shell/ui/src/page/SearchTab.tsx
  • shell/ui/src/page/__tests__/sandbox.test.ts
  • shell/ui/src/page/index.tsx
  • shell/ui/src/page/sandbox.ts
  • shell/ui/src/page/useGuestTree.ts
  • shell/ui/src/page/useSandboxFleet.ts
  • shell/ui/styles.css

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

skill-check — worker

0 verified, 57 skipped (no docs/).

Layer Result
structure
vale
ai
render

Four for four. Nicely done.

- guest reads ride head -c (1 MiB, the editor path's cap) instead of
  bare cat, with truncation flagged whenever the capped bytes fall short
  of the stat size (or fill the cap when stat reports none)
- fleet reads carry an ordering guard: stale sandbox::list responses are
  dropped, and a pushed { kind: "fleet" } snapshot invalidates every
  read still in flight
- switching target snapshots host tabs/expanded and restores them on the
  return to host, so the persistence effect never saves cleared guest
  state over stored host state
- copy-id handles clipboard rejection with a copied/failed flash; the
  target refresh + copy buttons get :focus-visible outlines on
  --color-rule-focus

All five CodeRabbit findings verified valid against the branch; none
skipped.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant