feat(cli,mcp,sdk,cloud): workspaces default to the cloud; --local/--host for a host; environment-only create

Merged
SA
2mo ago
+1774-2598

Do not merge yet. Satya asked for a PR to review, not a ship.

The rule

Workspace commands default to the cloud only for accounts that can use cloud workspaces. That's decided by the new cloudWorkspace.available query. Everyone else keeps this machine as the default, exactly as before, so non-cloud flows don't change.

  • --local or --host <id> always picks a host.
  • Nothing is guessed from an ID or from where the command runs.
  • The CLI caches the availability answer per organization for an hour, in ~/.superset/cloud-availability.json.
  • An API that can't answer counts as unavailable, whether it's offline or older than this query. Version skew can therefore only fall back to local, never to the cloud.
  • The MCP keeps hostId required for accounts without cloud access.

Verified against production, which lacks the new query: ws list, terminals list --workspace <local id> and ws create --project …, all run without --local, took the this-machine path.

What changed

CLI

  • workspaces list/create/get/open/update/delete, terminals list/read/send/create/close and agents create follow the rule above. --cloud is gone.
  • Host-only flags without a location are refused with a hint, before any API call: --project, --session, --checkout, --pr, --task, --tag and so on. An old ws create --project … fails loudly instead of starting a billed sandbox.
  • Accounts without cloud access get the same --local / --host hint when the API refuses them.
  • ws update on a cloud workspace renames it. Tasks and tags stay host-only.
  • Fast path to a running box. A cloud command reaches host-service through the gate with a ticket, reusing the ticket from the previous call. Tickets are cached in ~/.superset/cloud-workspace-tickets.json with mode 0600 and last 12 hours. If there's no usable ticket, it asks the new cloudWorkspace.hostTicket endpoint for one. That endpoint signs a ticket for the address stored on the workspace row, with no Vercel lookup. The CLI wakes the sandbox only when nothing answers there. A stopped box returns a non-tRPC 502, SANDBOX_NOT_LISTENING, in about 130ms. The MCP tools and the SDK use the same host ticket and wake fallback. The MCP retries a call after a wake only when host-service never answered, so a mutation that reached the box is never repeated. A wake refreshes the stored address.
  • Timings on a running production sandbox: 2.1s for the first call, 0.4s once the ticket is cached (local is 0.35s), and 0.9s to recover from a stale ticket. Before this change every call took about 3.7s, because the API's wake re-ran the whole open sequence.
  • No box needed for details. ws get and ws open read the API row and never touch the sandbox.
  • Reads no longer extend a sandbox's session. A cloud box that only CLI reads touch now stops at its session limit.
  • Automations keep defaulting to this machine, as on main. --local is accepted as an explicit spelling of that default.
  • Machine-only commands are unchanged: projects, agents list, browser, hosts, start/stop/status, settings.

MCP

  • workspaces_list/create/update/delete, terminals_* and agents_create treat a missing hostId as the cloud.
  • cloud_workspaces_list/create/delete are folded into the workspace tools and removed.

Environment-only creation

  • cloudWorkspace.create no longer takes repositoryIds, and an environment without repositories is refused. That includes "Default", which is the base you build environments from.
  • The desktop create screen drops the repository picker and offers only environments that have repositories.

SDK (cloud-only)

  • workspaces.list/retrieve/create/update/delete work on cloud workspaces.
  • terminals.* and agents.create go through the workspace's sandbox gate, using a cached cloudWorkspace.access ticket. The API key and organization header are stripped from requests to the gate.
  • Hosts, projects, automations, agents.list, the relay option and host routing are removed.
  • The SDK docs and README are rewritten.
  • Typecheck is clean and tests pass, 3 of 3. Tests used a mocked fetch only, not a live workspace.

Docs and skills

  • Public docs are unchanged from main. They describe today's behavior and mention no cloud defaults or flags while cloud workspaces stay flagged.
  • AGENTS.md and the Superset skills match main, except for --local on workspaces create/list/delete. The released CLI already accepts --local there, and it behaves the same before and after this change.
  • The standup sweep lists --local workspaces by default, so it never fans out over cloud workspaces.
  • Follow-up. The SDK package README and api.md describe the cloud-only SDK. They only become public when a new SDK version is published.

Verified

Against production, with a CLI binary built from this branch:

  • ws list returns the cloud workspaces, including "Hi".
  • ws list --local returns this machine's workspaces.
  • terminals list --workspace <Hi> reaches the agent terminal through the gate.
  • terminals read returns the agent's screen.
  • ws get <Hi> returns the name "Hi", the branch and /workspace.

These refusals behave, each with a hint: ws list --project x, ws create --project … without a location, ws create --environment Default, --environment with --local, terminals list --local on a cloud ID, ws update <cloud> --tag x, and automations create without a host.

In the dev app over CDP, with Cloud selected, the create row reads Cloud › "GitHub as me" › main, with no repository pill. The environment picker lists only environments that have repositories. cloudWorkspace.create on "Default" returns BAD_REQUEST with the new message.

Checks: typecheck is clean for cli, mcp, trpc, shared and desktop, apart from a stale host-service type unrelated to this change. Tests pass: cli 260, mcp 6, shared 1058, trpc 284, desktop create flow 159. check:i18n passes, with the new server message translated in all 16 locales.

Not verified

  • Mutating cloud commands. terminals send/create/close and agents create weren't run against the live box. They use the same client as list and read.
  • The MCP cloud path. Not run live, because the MCP server runs in the API and this branch isn't deployed.
  • The stored-address fallback, verified on a dev sandbox. A host ticket plus the gate call took about 350ms, against 500ms for a no-wake access and 1.75s for a wake. After I stopped the box, the stored-address call failed fast with a 502. The wake took 9.7s, and both ticket kinds answered afterwards. The address stayed the same across the resume. I deleted the dev workspace afterwards.
  • Rollout order. The CLI, MCP and SDK call cloudWorkspace.hostTicket, so the API has to deploy before a CLI release. An old API would answer that call with NOT_FOUND.
  • The new cloud default for ws create. Not exercised end to end, to avoid starting a sandbox.

Not in this PR

  • Automations targeting cloud. Designed, pending decisions on concurrency, cleanup, per-run branches and prompt size.
  • The create-flow redesign. Tracked in SUPER-2332.

https://claude.ai/code/session_01FK6rLzBEUiHHXZWNmpQ2BP