Skip to content

cmux Cloud: cmux-tui only machines, working create/attach/type/desktop, sized disks, provisioned images (master dogfood PR) - #10831

Closed
austinywang wants to merge 59 commits into
mainfrom
cloud-dogfood-integration-3
Closed

austinywang wants to merge 59 commits into
mainfrom
cloud-dogfood-integration-3

Conversation

@austinywang

@austinywang austinywang commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

Master PR for cmux Cloud dogfood: machines are cmux-tui only (cmuxd-remote removed from the Blaxel path), the create → attach → type → desktop flow works end to end, and the right sidebar gets a Finder-like Cloud tree (machines → workspaces → terminals / desktop / ports) that people drag into the main view and agents drive through cmux vm tree/open/route/agent. Folds in the design doc from #10800 and the ancestry of #10812 / #10793 / #10758.

What was broken (as dogfooded)

  • New VM failed (502): the cmux-tui install script used sha256sum -c -s; -s is BusyBox-only and GNU coreutils (the xfce-vnc desktop image) rejects it.
  • Terminal never attached: vm new / vm base open / fork / restore / the sidebar button / the Machines panel all went through the legacy cmuxd websocket PTY path; only vm shell tried cmux-tui.
  • Typing into the cmux-tui pane was intermittent: the client was spawned in its own process group and moved to the tty foreground afterwards, racing its raw-mode setup.
  • noVNC rendered unstyled with a dead Connect button: the desktop wrapper iframed the gateway page; the gateway's bl_preview_token cookie is third-party inside a cross-site frame and WebKit drops it, so every asset and the websockify upgrade 401'd.
  • 24 GB machines had a 5 GB disk; the provision step was Alpine-only (a no-op on the Ubuntu desktop image); nothing was preinstalled.
  • Raw *.preview.bl.run routes for the daemon: the branded <machine>.vm.cmux.sh ingress refuses WebSocket upgrades without a User-Agent.

Changes

Backend (web/)

  • Blaxel driver is cmux-tui only: no daemon injection, no CMUX_VM_BLAXEL_DAEMON_*, no legacy websocket attach, no kill switch. The bare branded host <machine>.vm.cmux.sh belongs to the cmux-tui preview; clients advertising direct-ws-user-agent get it, everyone else the raw preview. openAttach on Blaxel → 409 vm_attach_transport_unsupported pointing at transport: "cmux-remote" (/attach-endpoint default branch and /sessions).
  • Desktop wrapper: validated top-level redirect to the noVNC page (no iframe), instant = false, no nested html/body.
  • Home volume sized from memory, clamped to Blaxel's measured 16 GB ceiling (≤4 GB → 8 GB, else 16 GB); CMUX_VM_BLAXEL_HOME_VOLUME_MB still pins.
  • Background provisioning script (Ubuntu + Alpine): ripgrep/fd/jq/tmux/git/curl/gh/xdotool, node 22, bun, uv, Claude Code / Codex / OpenCode / Pi into the persistent /root, and the CUA driver (cua-computer-server).
  • Free-plan list payload carries freeAccessExpiresAt (per machine + earliest).

macOS app

  • Cloud tree in the right sidebar (NSOutlineView, Files-sidebar styling): machine → Workspaces (cmux-tui) → terminals (lifecycle, title, cwd, agent badge, open marker) → Desktop → Ports; expansion persisted; keyboard navigation; per-node context menus; com.cmux.cloud-surface.transfer drag that drops a terminal, desktop, or port as a pane at the drop position through the same pane-drop routing every other drag uses.
  • One headless cmux-tui remote connect --headless link per awake machine (never wakes a sleeping one), session current snapshot + session current events feed the tree; a terminal opens locally as a plain terminal pane (attach --terminal <id>, no TUI chrome). Surface↔terminal bindings make reopen focus the existing pane.
  • Socket methods vm.tree, vm.terminal_open, vm.terminal_new, vm.desktop_open, vm.port_open, vm.link_socket — one shared path for the sidebar and the CLI.
  • Every Cloud VM entrypoint (vm new/base open/fork/restore/shell, sidebar button, Machines panel) goes through one open path; the pane is a plain terminal on the machine; vm tui <id> is the explicit full-client attach.
  • remote-probe --json capabilities forwarded as clientCapabilities; workspaces carry a persisted cloud VM binding (workspace.cloud_vm_bind, saved in the session snapshot).
  • Machines panel / vm ls: "N of 1 machine" on free plans and a "Free cloud access expires in …" indicator (7-day window) that opens the upgrade flow.
  • Deleting a machine closes its cmux-tui-bound workspace.

CLI + agent skill

  • cmux vm tree [<machine>] [--json], cmux vm open <m>/<ws>/<term> | <m>:desktop | <m>:port/<n> (the <id> <port> form is unchanged), cmux vm route (the machine chooser vm run already uses, exposed), cmux vm agent --agent claude|codex|opencode|pi -- <prompt> (runs the agent inside the chosen machine's cmux-tui session as a new terminal that shows up in the tree).
  • skills/cmux-cloud-vm rewritten: when to run in the cloud, how to pick a machine, run/watch/report back, what a machine contains, and how CodeRouter composes (credentials, not compute).

cmux-tui (Rust)

  • remote-probe --json advertises capabilities: ["direct-ws-user-agent"].

Dogfood

Tagged build cloud-dogfood-integration-3 (fleet-built, bundled cmux-tui client from the branch's artifacts) against the local web stack on :3777. Verified live: create (16 GB volume, provisioning log), branded wss://<machine>.vm.cmux.sh routes, vm tree across three machines (workspaces, a running Claude Code terminal, desktop, port 8000), a terminal opened from the tree as a plain vivid-gecko:~# pane, keystrokes delivered, noVNC desktop connected.

Summary by CodeRabbit

  • New Features

    • Added a Finder-like Cloud Machines tree for browsing machines, workspaces, terminals, desktops, and ports.
    • Added Cloud VM commands for routing, running agents, waiting for readiness, and transferring files.
    • Added cmux-tui remote connection, enrollment approval, workspace opening, and drag-and-drop support.
    • Added persistent Cloud VM workspace bindings and session restoration.
    • Added free-access countdowns, expiration messaging, and upgrade prompts.
    • Improved desktop access by redirecting directly to the upstream viewer.
  • Bug Fixes

    • Improved VM transport validation, connection errors, and expired-access responses.
    • Added universal cmux-tui client bundling and release validation.

cmux reload-cloud and others added 30 commits August 25, 2026 15:11
Plan shape: the free plan now includes one full-size machine (24 GB
default and cap — the free machine demos the product; the paywall is
the window and the count, not the machine's usefulness) and Pro includes
five machines (24 GB default, 32 GB cap). 24576 joins the memory picker
options. All numbers stay env-overridable per plan.

Free access window: a free-plan machine older than 5 days
(CMUX_VM_FREE_ACCESS_WINDOW_DAYS, 0 disables) is preserved but
unreachable — attach, ssh, exec, ports, and sessions fail with a 402
vm_access_requires_pro upgrade prompt, while list/status/rename/delete
keep working so the machine stays visible and disposable. The gate keys
on the caller's CURRENT plan, so upgrading unlocks existing machines
immediately. Enforced in one place (requireAccessibleUserVm) that every
access workflow shares; the five REST routes thread the caller's plan
and map the typed error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* machines: surface the free access window — countdown rows, locked rows, upgrade routing

The list payload now carries freeAccessWindowDays (0 for paid plans) so
clients render policy from the wire instead of hardcoding it. The
Machines panel mirrors the backend's window math per row: free-plan
machines show a days-left countdown in the subtitle, and a machine past
the window renders locked — lock glyph in place of the activity dot,
Locked in the subtitle, and double-click/context-menu routing to the
shared Pro upgrade presenter instead of a doomed connect (the backend
still enforces with 402s; the UI just stops walking into them).
Rename/Status/Delete stay available on locked rows so the machine
remains manageable and disposable. Strings localized en+ja; snapshot
window math unit-tested against the backend's boundary behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* machines: flip free-window rows at the boundary itself, not on a poll tick

Review follow-up: the countdown/lock facet was only as fresh as the 45s
list poll. Expiry is a known future timestamp the client can compute
(createdAt + window), so the panel now arms a one-shot timer at exactly
the next transition across the fleet — each day-boundary where the label
decrements, and finally the expiry — and recomputes the facet locally
with no network, re-arming for the next boundary. Rows flip at the
moment the state changes; the slow poll is left covering only what
genuinely needs the server (machines created or deleted elsewhere). The
recompute happens above the lazy-list snapshot boundary, so the panel's
snapshot rule (cmux#2586) holds. Boundary math unit-tested.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: cmux reload-cloud <cmux-reload-cloud@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…main

After #10781, worktreeDeviceID/worktreeFileID were both defaulted at the
declaration and assigned in the explicit init, which the current toolchain
rejects ("immutable value may only be initialized once"). The init's
parameter defaults keep the same call-site contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
vm run routes a command to a cloud machine without naming one: sticky
per-directory binding, then an idle agent-pool machine, then a sleeper,
then a freshly provisioned pool machine. push/pull move files over the
exec channel (base64 chunks, SHA-256 verified, directories as tarballs);
wait blocks until ready and optionally wakes the machine. The skill lets
any coding agent drive machines from plain CLI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… progress

Live dogfood on Blaxel: a 512 KiB chunk base64-encodes past Linux's 128 KiB
per-argument limit ("argument list too long"), macOS tar shipped ._* files
onto the machine, and chunk progress ran together when stderr was captured.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…el; review fixes

- The router now only drafts machines it provisioned itself (ids recorded in
  ~/.cmuxterm/vm-run-pool.json at create time, pruned when machines vanish); a
  user machine renamed agent-pool is never used. Test covers the impostor.
- Staging tarball is removed if reading it throws before the defer is armed.
- Push/pull chunk progress is localized (cli.vm.push.progress, cli.vm.pull.progress).
- vm --help, the usage contract, and the contract doc list open/ports/tools/
  handoff/promote-template, which the dispatcher already handled.
- Sticky-binding fixture uses a fixed instant, not the host clock.
- Skill recipes: --sync runs inside the synced dir (no remote $PWD), port
  readiness poll instead of sleep, eligibility filter instead of .vms[0],
  background test exit status captured to a status file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…recipes

- updateVMRunPool does the read-modify-write under flock on a sibling lock
  file, so two routers provisioning at once both land in the store; covered by
  a two-process test against two mock sockets.
- Dev-server recipe reuses a live server or starts one with a workspace pidfile
  and log; test recipe uses per-run log/status paths written atomically.
- Document that --sync is additive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r and use unique run ids

- updateVMRunPool/saveVMRunPool throw on lock or write failure and createPoolVM
  reports the machine it provisioned but could not record, instead of a silent
  unlocked update.
- The dev-server recipe reuses a server only when the recorded pid is alive and
  owns :3000 (netstat -p), refuses to start a second server on a port someone
  else owns, and clears stale metadata.
- Test-run ids come from uuidgen, not the epoch second.
- Skill docs: cmux vm shell is a cmux-tui session now that machines run the
  cmux-tui remote daemon; agents keep working through vm run/exec.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-remote (Phase 1)

Implements the next open item of docs/cloud-cmux-tui-daemon.md (#10800): every
Blaxel machine gets the pinned static-musl cmux-tui (CMUX_VM_BLAXEL_TUI_URL +
_SHA256, verified in-VM, installed on the persistent home volume) running
`server start --remote-ws` under the sandbox supervisor, with its own private
preview. attach-endpoint accepts transport:"cmux-remote" and returns the
tokenized /v1/link route plus a single-use enrollment invitation when the
caller's device is not enrolled; a new cmux-remote/approve route approves the
pending claim the control plane invited. Opt-in per deployment; no change
for clients that do not ask.

Measured on Blaxel: a WebSocket upgrade with the preview token as a query
parameter completes on the raw <hash>.preview.bl.run host but is refused
through the vm.cmux.sh custom domain, so the daemon preview is created
unbranded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- `cmux vm tui <id>` and, by default, `cmux vm shell <id>` (which the Machines
  panel launches) open a workspace whose pane runs the local cmux-tui client
  against the machine's authenticated /v1/link route; the hidden
  vm-tui-connect helper hands the terminal to the client and approves the
  device enrollment through the app socket, remembering the device
  fingerprint per machine. The websocket attach remains only for
  deployments without a cmux-tui pin.
- Socket methods vm.cmux_remote_info / vm.cmux_remote_approve and the
  VMClient calls behind them; capabilities list updated.
- With the pin configured, new Blaxel machines get cmux-tui only: no
  cmuxd-remote install or process; the sleep watcher counts cmux-tui's
  terminal children as work.
- Client discovery probes candidates with `remote-probe --json` so the
  SSH-remote bootstrap's shell wrapper at ~/.cmux/bin/cmux is skipped.
- Localized en+ja strings; help, usage contract and docs updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d installs curl

Found by creating a machine under the pin: a just-created sandbox 404s its
API for a few seconds (the cmuxd path only survived because encoding the Go
binary took that long), and a stock blaxel/base-image has no curl until the
background provisioning adds it. The first write now retries until the API
answers, and the installer adds curl via apk or falls back to busybox wget.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…es the client

- The Blaxel driver resolves the daemon build from the artifacts manifest
  (rolling latest by default, CMUX_VM_CMUX_TUI_MANIFEST_URL to pin a commit,
  CMUX_VM_CMUX_TUI_ENABLED=0 as the kill switch); the sha256 comes from the
  manifest, never from env. An installed daemon that no longer matches the
  manifest is reinstalled on attach. The endpoint reports the daemon's build
  identity and remote protocol.
- scripts/install-cmux-tui-client.sh bundles a universal, sha256-verified
  cmux-tui client into Contents/Resources/bin like the Ghostty helper;
  reload.sh, ci.yml and release.yml run it. The CLI looks there first and
  checks the client/daemon remote protocol before opening a pane, naming the
  stale side.
- cmux-tui-artifacts.yml publishes on main pushes again so latest/ tracks main.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hosted ingress in front of cmux Cloud machines (CloudFront on the branded
vm.cmux.sh domain) refuses upgrades that omit User-Agent, and tungstenite
sends none by default, so the daemon route had to fall back to the raw
preview host. Direct dials now carry cmux-tui/<version>; no Origin is set
because the daemon rejects browser-style upgrades. Unit test covers the
header and that the endpoint query (route token, lane) survives.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oud-dogfood-integration-3

# Conflicts:
#	web/app/api/vm/[id]/attach-endpoint/route.ts
…nto cloud-dogfood-integration-3

# Conflicts:
#	Resources/Localizable.xcstrings
#	cmux.xcodeproj/project.pbxproj
#	docs/cli-contract.md
Plan shape: the free plan now includes one full-size machine (24 GB
default and cap — the free machine demos the product; the paywall is
the window and the count, not the machine's usefulness) and Pro includes
five machines (24 GB default, 32 GB cap). 24576 joins the memory picker
options. All numbers stay env-overridable per plan.

Free access window: a free-plan machine older than 5 days
(CMUX_VM_FREE_ACCESS_WINDOW_DAYS, 0 disables) is preserved but
unreachable — attach, ssh, exec, ports, and sessions fail with a 402
vm_access_requires_pro upgrade prompt, while list/status/rename/delete
keep working so the machine stays visible and disposable. The gate keys
on the caller's CURRENT plan, so upgrading unlocks existing machines
immediately. Enforced in one place (requireAccessibleUserVm) that every
access workflow shares; the five REST routes thread the caller's plan
and map the typed error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* machines: surface the free access window — countdown rows, locked rows, upgrade routing

The list payload now carries freeAccessWindowDays (0 for paid plans) so
clients render policy from the wire instead of hardcoding it. The
Machines panel mirrors the backend's window math per row: free-plan
machines show a days-left countdown in the subtitle, and a machine past
the window renders locked — lock glyph in place of the activity dot,
Locked in the subtitle, and double-click/context-menu routing to the
shared Pro upgrade presenter instead of a doomed connect (the backend
still enforces with 402s; the UI just stops walking into them).
Rename/Status/Delete stay available on locked rows so the machine
remains manageable and disposable. Strings localized en+ja; snapshot
window math unit-tested against the backend's boundary behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* machines: flip free-window rows at the boundary itself, not on a poll tick

Review follow-up: the countdown/lock facet was only as fresh as the 45s
list poll. Expiry is a known future timestamp the client can compute
(createdAt + window), so the panel now arms a one-shot timer at exactly
the next transition across the fleet — each day-boundary where the label
decrements, and finally the expiry — and recomputes the facet locally
with no network, re-arming for the next boundary. Rows flip at the
moment the state changes; the slow poll is left covering only what
genuinely needs the server (machines created or deleted elsewhere). The
recompute happens above the lazy-list snapshot boundary, so the panel's
snapshot rule (cmux#2586) holds. Boundary math unit-tested.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: cmux reload-cloud <cmux-reload-cloud@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-remote (Phase 1)

Implements the next open item of docs/cloud-cmux-tui-daemon.md (#10800): every
Blaxel machine gets the pinned static-musl cmux-tui (CMUX_VM_BLAXEL_TUI_URL +
_SHA256, verified in-VM, installed on the persistent home volume) running
`server start --remote-ws` under the sandbox supervisor, with its own private
preview. attach-endpoint accepts transport:"cmux-remote" and returns the
tokenized /v1/link route plus a single-use enrollment invitation when the
caller's device is not enrolled; a new cmux-remote/approve route approves the
pending claim the control plane invited. Opt-in per deployment; no change
for clients that do not ask.

Measured on Blaxel: a WebSocket upgrade with the preview token as a query
parameter completes on the raw <hash>.preview.bl.run host but is refused
through the vm.cmux.sh custom domain, so the daemon preview is created
unbranded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- `cmux vm tui <id>` and, by default, `cmux vm shell <id>` (which the Machines
  panel launches) open a workspace whose pane runs the local cmux-tui client
  against the machine's authenticated /v1/link route; the hidden
  vm-tui-connect helper hands the terminal to the client and approves the
  device enrollment through the app socket, remembering the device
  fingerprint per machine. The websocket attach remains only for
  deployments without a cmux-tui pin.
- Socket methods vm.cmux_remote_info / vm.cmux_remote_approve and the
  VMClient calls behind them; capabilities list updated.
- With the pin configured, new Blaxel machines get cmux-tui only: no
  cmuxd-remote install or process; the sleep watcher counts cmux-tui's
  terminal children as work.
- Client discovery probes candidates with `remote-probe --json` so the
  SSH-remote bootstrap's shell wrapper at ~/.cmux/bin/cmux is skipped.
- Localized en+ja strings; help, usage contract and docs updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d installs curl

Found by creating a machine under the pin: a just-created sandbox 404s its
API for a few seconds (the cmuxd path only survived because encoding the Go
binary took that long), and a stock blaxel/base-image has no curl until the
background provisioning adds it. The first write now retries until the API
answers, and the installer adds curl via apk or falls back to busybox wget.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…es the client

- The Blaxel driver resolves the daemon build from the artifacts manifest
  (rolling latest by default, CMUX_VM_CMUX_TUI_MANIFEST_URL to pin a commit,
  CMUX_VM_CMUX_TUI_ENABLED=0 as the kill switch); the sha256 comes from the
  manifest, never from env. An installed daemon that no longer matches the
  manifest is reinstalled on attach. The endpoint reports the daemon's build
  identity and remote protocol.
- scripts/install-cmux-tui-client.sh bundles a universal, sha256-verified
  cmux-tui client into Contents/Resources/bin like the Ghostty helper;
  reload.sh, ci.yml and release.yml run it. The CLI looks there first and
  checks the client/daemon remote protocol before opening a pane, naming the
  stale side.
- cmux-tui-artifacts.yml publishes on main pushes again so latest/ tracks main.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hosted ingress in front of cmux Cloud machines (CloudFront on the branded
vm.cmux.sh domain) refuses upgrades that omit User-Agent, and tungstenite
sends none by default, so the daemon route had to fall back to the raw
preview host. Direct dials now carry cmux-tui/<version>; no Origin is set
because the daemon rejects browser-style upgrades. Unit test covers the
header and that the endpoint query (route token, lane) survives.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… in the rebase

The pre-rebase toolchain-fix commit had absorbed these cmux.swift hunks when
it was amended, so dropping it in favor of main's #10820/#10822 dropped
them too: the vm-tui-connect dispatch, tui in every vm usage string and the
help block, and the internal access on applyWindowOrCallerContext /
setTerminalForegroundProcessGroup that CMUXCLI+VMTui.swift needs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
openVmCmuxRemote and approveVmCmuxRemoteEnrollment resolve the machine through
requireAccessibleUserVm with the caller's current plan, and both routes map
VmFreeAccessExpiredError to the same 402 upgrade prompt the websocket attach
uses, so a free machine past its window is locked on every transport.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 26, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

Next included review available in 2 minutes.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

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

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ec999c78-0987-4c4d-ba80-eb232da2f1dd

📥 Commits

Reviewing files that changed from the base of the PR and between c6102fb and 701b926.

📒 Files selected for processing (89)
  • .claude/skills/cmux-cloud-vm
  • .github/workflows/ci.yml
  • .github/workflows/cmux-tui-artifacts.yml
  • .github/workflows/nightly.yml
  • .github/workflows/release.yml
  • CLAUDE.md
  • CLI/CMUXCLI+VMTransfer.swift
  • CLI/CMUXCLI+VMTui.swift
  • CLI/cmux.swift
  • Resources/Info.plist
  • Resources/Localizable.xcstrings
  • Sources/AppDelegate.swift
  • Sources/Cloud/CloudMachineLink.swift
  • Sources/Cloud/CloudMachineLinkManager.swift
  • Sources/Cloud/CloudTreeCellView.swift
  • Sources/Cloud/CloudTreeDragPayload.swift
  • Sources/Cloud/CloudTreeDragRegistry.swift
  • Sources/Cloud/CloudTreeExpansionStore.swift
  • Sources/Cloud/CloudTreeModel.swift
  • Sources/Cloud/CloudTreeNSOutlineView.swift
  • Sources/Cloud/CloudTreeNode.swift
  • Sources/Cloud/CloudTreeNodeActions.swift
  • Sources/Cloud/CloudTreeOutlineView.swift
  • Sources/Cloud/CloudTreeRowContentView.swift
  • Sources/Cloud/CloudTreeService.swift
  • Sources/Cloud/CloudTreeServiceAccess.swift
  • Sources/Cloud/CloudTreeSnapshotParser.swift
  • Sources/Cloud/CloudTuiClientPaths.swift
  • Sources/Cloud/CloudTuiCommandLine.swift
  • Sources/Cloud/MachinesPanelView.swift
  • Sources/Cloud/MachinesPanelViewModel.swift
  • Sources/Cloud/VMClient.swift
  • Sources/Cloud/VMClientSocketCommands.swift
  • Sources/DragOverlayRoutingPolicy.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/PaneDropContainer.swift
  • Sources/PaneTransferSourceResolver.swift
  • Sources/SessionPersistence.swift
  • Sources/TerminalController+MobileWorkspaceList.swift
  • Sources/TerminalController+WorkspaceCreate.swift
  • Sources/TerminalController.swift
  • Sources/Workspace.swift
  • Sources/WorkspaceCloudSurfaceDrop.swift
  • cmux-tui/crates/cmux-remote/src/provider/websocket.rs
  • cmux-tui/crates/cmux-tui/src/remote_cli.rs
  • cmux-tui/scripts/test_check_resource_api_boundary.py
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/CLIVMTransferTests.swift
  • cmuxTests/CloudVMMenuItemMetricsTests.swift
  • cmuxTests/MachinesPanelModelTests.swift
  • cmuxTests/TabManagerSessionSnapshotTests.swift
  • docs/cli-contract.md
  • docs/cloud-cmux-tui-daemon.md
  • docs/internal/machine-router.md
  • scripts/install-cmux-tui-client.sh
  • scripts/reload.sh
  • skills/cmux-cloud-vm/SKILL.md
  • skills/cmux-cloud-vm/agents/openai.yaml
  • skills/cmux-cloud-vm/references/agent-workflows.md
  • skills/cmux-cloud-vm/references/commands.md
  • web/.env.example
  • web/app/api/vm/[id]/attach-endpoint/route.ts
  • web/app/api/vm/[id]/cmux-remote/approve/route.ts
  • web/app/api/vm/[id]/exec/route.ts
  • web/app/api/vm/[id]/open-port/route.ts
  • web/app/api/vm/[id]/sessions/route.ts
  • web/app/api/vm/[id]/ssh-endpoint/route.ts
  • web/app/api/vm/route.ts
  • web/app/vm/desktop/[id]/layout.tsx
  • web/app/vm/desktop/[id]/page.tsx
  • web/scripts/cloud-vm/smoke-vm-api.mjs
  • web/scripts/cloud-vm/stress-vm-api.mjs
  • web/scripts/test-blaxel-vm-poc.ts
  • web/services/vms/README.md
  • web/services/vms/desktopWrapper.ts
  • web/services/vms/drivers/blaxel.ts
  • web/services/vms/drivers/types.ts
  • web/services/vms/entitlements.ts
  • web/services/vms/errors.ts
  • web/services/vms/images/manifest.json
  • web/services/vms/providerGateway.ts
  • web/services/vms/routeHelpers.ts
  • web/services/vms/workflows.ts
  • web/tests/vm-billing-limit-paywall.test.ts
  • web/tests/vm-blaxel-cmux-tui.test.ts
  • web/tests/vm-blaxel-provider.test.ts
  • web/tests/vm-desktop-wrapper.test.ts
  • web/tests/vm-route-auth.test.ts
  • web/tests/vm-workflows.test.ts
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cloud-dogfood-integration-3

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.

austinywang and others added 10 commits August 26, 2026 02:46
…ss expires

The list payload carries a server-authoritative freeAccessExpiresAt per machine (and the
earliest across them). The Machines panel meter uses singular/plural forms, and free plans get a
banner under the control bar counting down the 7-day window (expires in 6d 23h / expires today /
expired) that opens the existing Pro upgrade flow. cmux vm ls prints the same footer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…inals, desktop, ports)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…kill teaches agents to route work to machines

vm tree shows machines → cmux-tui workspaces → terminals (title, cwd, agent badge, open marker),
desktop and ports; vm open addresses any node (<m>/<ws>/<term>, <m>:desktop, <m>:port/<n>) and
keeps the <id> <port> form; vm route exposes the machine chooser vm run already uses; vm agent runs
Claude Code / Codex / OpenCode / Pi inside the chosen machine's cmux-tui session as a new terminal
that shows up in the tree. vm desktop and the shell's desktop split share one path (vm.desktop_open).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Cloud tab is an NSOutlineView: machine → Workspaces (cmux-tui) → terminals (lifecycle,
title, cwd, agent badge, open marker) → Desktop → Ports, with asleep/connecting/error
placeholders, persisted expansion, keyboard navigation, per-node context menus, and a
com.cmux.cloud-surface.transfer drag that drops a terminal, desktop or port as a pane at the
drop position through the same CloudTreeServicing path the CLI uses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…/terminal_open/terminal_new/desktop_open/port_open/link_socket

The app keeps one headless 'remote connect --headless --json' link per awake machine (never waking a
sleeping one), reads 'session current snapshot' and follows 'session current events' to build the
Cloud tree, and opens a remote terminal locally as a pane running 'attach --terminal <id>'. Bindings
between local surfaces and remote terminals make terminal_open reuse an open pane. Deleting a
machine now closes its cmux-tui-bound workspace too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…main-actor isolated

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…p await in the link manager

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@blacksmith-sh

This comment has been minimized.

austinywang and others added 4 commits August 26, 2026 14:11
Restored vm:<id> workspaces keep their WorkspaceCloudVMBinding so
workspace(forCloudVMID:), the sidebar cloud button's Base reuse, and
vm.terminal_open find the machine's workspace again after relaunch.
Only the binding is persisted; the pane's one-shot link is not replayed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three never-mutated vars and an unused optional binding in CLI/cmux.swift and
TerminalController+MobileWorkspaceList.swift, plus the occlusion observer in
GhosttyTerminalView calling a main-actor method from its main-queue closure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…licit full-client attach

vm new / base open / shell / fork / restore, the sidebar cloud button and the Machines
panel now create a terminal in the machine's cmux-tui session through vm.terminal_new and
show it as a single-terminal pane (attach --terminal), like an ssh session — no cmux-tui
sidebar or tabs in the pane. vm tree renders link state (connecting / asleep / error)
instead of hiding it behind '(none yet)'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@austinywang

Copy link
Copy Markdown
Contributor Author

CI baseline notes (so reviewers can tell this PR's failures from inherited ones):

swift-package-tests — SidebarDropPlannerPackageTests.rootDropBeforeEmptyHeaderProducesPlan and SSHForegroundAuthenticationRetryPolicyTests.processTreeTerminationUsesOneOverallDeadline fail identically on a pristine origin/main worktree (cdac873f08), same 5 issues, run locally with
swift test --package-path Packages/macOS/CmuxFoundation --filter "SidebarDropPlanner|SSHForegroundAuthenticationRetryPolicy". git diff origin/main -- Packages/macOS/CmuxFoundation is empty on this branch. The retry-policy one is a wall-clock assertion (elapsed 3.8s < 3s).

app-host unit tests — the four shards were red on every recent main run where they actually executed (2026-08-09 … 2026-08-19: at least two shards failing each time). The suites failing on this branch's runs (File search workspace scope timeout, RestorableAgentSessionIndexTests, ShellStartupMatrixTests, SSHForegroundAuthenticationMarkerCleanupTests, TerminalFontZoomSessionPersistenceTests) are not touched by this PR.

Everything this PR introduced that CI caught (missing ProcessRunResult rename, main-actor isolation in the drop handler and menu item, Int64 literals in MachinesPanelModelTests, and five Swift warnings over budget — three from merged branches, two inherited from main's #10815/#10710 merges) has been fixed on the branch.

A drag from the Cloud tree now carries the real Bonsplit destination (pane, orientation,
insert-first → left/right/up/down, or tab index) through CloudTreeOpenTarget into the same
surface.split / surface.create path every other pane drag uses, so dropping on the left edge
splits left instead of always splitting right. vm.terminal_open/desktop_open/port_open accept
pane_id/surface_id/direction/tab_index.

Rows follow the Files sidebar: secondary/tertiary labels and template symbols, one status
dot per machine, a single dim 'CPU · Mem · Disk' line instead of colored gauges.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@austinywang
austinywang marked this pull request as ready for review August 26, 2026 21:17
austinywang and others added 4 commits August 26, 2026 14:19
#10855 added terminal.output_read to spec/resource-operations-v2.json
without bumping the frozen count in test_check_resource_api_boundary,
so the cmux-tui SDKs and cmux-tui spec checks fail on every merge with
main (125 != 124).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n slot, chevron on the name line

Every row lays out as (level+1)×16pt indent → 6pt → 16pt icon slot → 8pt → title, so glyphs form a
column and rows without a chevron reserve its slot. Machine rows put the status dot in its own 10pt
slot and top-align the disclosure with the name line instead of letting it float between the
subtitle lines next to the dot. Trailing markers get a 10pt gap and an 8pt edge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ts raw id

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@austinywang

Copy link
Copy Markdown
Contributor Author

Superseded by #10887 (cloud-surfaces), which contains every commit from this branch plus the surface catalog (terminals/screens/browsers as resources, panes as projections). Keeping this open only until #10887 passes dogfood; it will be closed then.

@austinywang

Copy link
Copy Markdown
Contributor Author

Merged as part of #10887 (cloud-surfaces).

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