Repository navigation
Offload: npx @simular-ai/box offload/back/rsh/login, /box:offload and /box:back - #8
Merged
Merged
Conversation
Port upstream cocoonbox's Offload (design §27) to the CLI and the Claude Code plugin. The box side is already in our images. - login/logout: the relay's first-party cocoonbox-cli OAuth client (PKCE, loopback on 127.0.0.1). It gives a device token, kept in ~/.simular-box/credentials.json (0600). BOX_TOKEN wins over it. - rsh: Session.Exec as rsync's and git's remote shell. - offload / back / inbox / status / send / secrets: a faithful port of the Mac packer (preflight, snapshot, rsync + bundle tar stream, merge back against S). - offload install-cli / sign-in: the vendor installer and the vendor login run in the box, from a terminal-only machine. - claude/: box-offload skill, /box:offload and /box:back. - Tests: unit tests, plus offload/back end to end against a fake box over real gRPC, rsync and git. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p POSIX mode checks on Windows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s outside the repo Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hin a minute Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…inals, quiet EPIPE - openBrowser never goes through cmd. Windows uses rundll32 url.dll,FileProtocolHandler, macOS open, Linux xdg-open, and BROWSER is run the same way. Each gets the parsed, normalized URL as one argv element. A box printing https://claude.ai/x?&calc can no longer run a command on a Windows machine, and login's authorize URL (with &) now opens on Windows. - A sign-in link is only RFC 3986 characters, so | ^ quotes and spaces end it. An OSC 8 target containing them is skipped. - rsh -t and offload sign-in: ssh's ~. escape at line start disconnects (exit 255); ~~ sends ~. - gRPC keepalive (30 s, 15 s timeout, only while a call is open), so a half-open stream fails instead of holding the terminal. - EPIPE on stdout or stderr cancels the call and exits 255 without a stack trace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sign-in hint Found in the prod e2e run: the hint printed --kind <K>. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Prod e2e: the first BACK was reported "pasted", but it never reached Claude Code 2.1.317 in the box. There was no UserPromptSubmit hook and no transcript entry, and the inbox showed the session "working" from then on. So `back` waited out its full 10 minutes. A second BACK went through at once. When the first BACK was pasted (not queued behind a real turn), the session is still "working" after 60 s, and no hook ran since, back now sends BACK once more with --interrupt. A queued BACK is never re-sent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mular.ai Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…opping An unconfirmed delivery does not prove the box's Claude stopped, so the message no longer claims it. The pasted-delivery retry is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chenchenSimular
force-pushed
the
chenchen/offload
branch
from
November 4, 2026 13:16
6276303 to
0ca33c1
Compare
…here HEAD went back now polls the box CLI session until it is idle (or blocked) on two polls in a row before it sends BACK. A BACK pasted while Claude Code is ending a turn can vanish and come back as delivery_unconfirmed, which a prod run hit. If the session stays busy past the 10-minute budget, back fails before sending anything and points at back --interrupt, which skips the wait as before. The merged and fast-forward summaries now say when this branch moved to the box's commit and list what is left uncommitted, instead of calling a moved HEAD "applied uncommitted". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Closes #7.
What
This ports upstream cocoonbox's Offload (design §27) to our CLI and the Claude Code plugin. The box side already ships in our images: sbxdesk v7's agent
offloadcode is identical to upstream. Phase 1 evidence is in #7.CLI (
@simular-ai/box0.2.0)login/logout: the relay's first-partycocoonbox-cliOAuth client.127.0.0.1;stateandissare checked.logout.~/.simular-box/credentials.json(0600, in a 0700 directory) behind aTokenStoreinterface, so a keychain backend can replace the file.BOX_TOKENwins over the saved token.--token -reads a personal key from stdin or a hidden prompt.rsh:Session.Execused as an ssh-like remote shell for rsync and git.-tputs the local terminal in raw mode and forwards SIGWINCH and the local terminfo.offload,back, andoffload handover-path|--handover-prompt|inbox|status|send|secrets: a faithful port ofOffloader.swift,OffloadGit.swift,OffloadPreflight.swift,OffloadEnvironment.swiftand the hand-over template. That covers:.gitwithout objects;env.json, the bundle and opted-in secrets;offload install-cliandoffload sign-in: terminal versions of the app's "Install in box" and "Sign in in the box".install-cliruns the vendor installer over Exec.sign-inruns the vendor login on a PTY relayed to your terminal. The first https link on that vendor's own hosts opens once in your browser, and no other link from the box is opened. You paste the code into the terminal. The login stays in the box.cli/src/box/browser.js).URL.hrefas one argv element:rundll32 url.dll,FileProtocolHandleron Windows (nevercmd /c start),openon macOS,xdg-openon Linux, orBROWSERrun the same way.| ^ " ' < >and spaces end it, and an OSC 8 target that contains them is skipped.rsh -t,offload sign-in):~.at the start of a line disconnects (exit 255);~~sends~.rsh box cat big | head) cancels the call and exits 255 quietly, as ssh does, instead of crashing with a stack trace.cli/proto/README.md) and loaded with@grpc/grpc-jsand@grpc/proto-loader. Nothing is generated.Plugin (
claude/0.2.0): thebox-offloadskill,/box:offloadand/box:back. The skill runsnpx -y @simular-ai/box@^0.2.0.Prior art: what we adopted and avoided
The full table is in #7.
--cloud.--uncommitted-changes fail.codex apply).pairclock-skew conflicts).Not changed / limits
rsync. Windows gets a clear block.merge-tree --write-tree), as upstream does.timeoutSec: 3600.offload status,inboxand the printedrsh -t … tmux attachline cover it.Testing
npm test: 54 tests pass. lint is clean.claude plugin validatepasses for the plugin and the marketplace.https://claude.ai/x?a=1&calc|whoami^%PATH%is one element; win32 isrundll32, nevercmd;shell: false.& ; | ^ $()payload ran.&stays in the link (harmless without a shell), and|and^end it.~./~~escape tests.rsh … 'yes | head -c 50M' | head -c 10gives rc 255 and no stack trace.tar, including the pax long path), token store modes, endpoint resolution, the rule that a token is never an argument, rsh host words, preflight pieces, and the OAuth loopback (PKCE verifier/challenge, wrongstate, wrongiss).test/fakebox/): a local gRPC server with Boxes.List/Ensure and a realSession.Exec, plus a fakecocoonbox-agent offloadthat follows the contract. It runs the real CLI, real rsync and git over ourrsh, and the real bundle. It shows:cat, exit 42, stderr kept apart, 255 for a bad token, and the token never printed;.envwarning, then--yesmoves the work tree including uncommitted and untracked files (ignored files stay), and the bundle carries HEAD and S;backfast-forwards the box's commit;cocoonbox/back-*with the work tree untouched;install-cli/sign-incommands.login --token -with a bogus key reachedBoxes.Listonrelay.simular.cloudover native gRPC and gotUNAUTHENTICATED invalid bearer token.main,sbxdesk:v4, agent0.1.0+4fe5895, running before and after). Run from this branch at 91ffc0e:login: the consent page showed "cocoonbox CLI on Chenchens-MacBook-Pro"; after Allow the CLI printedsigned in.credentials.jsonis 0600 in a 0700 directory, and Keys → Devices listed "Chenchens-MacBook-Pro / Command line".rsh: runs asuserin/workspace;git rsync tmux mise cocoonbox-agentare present;cocoonbox-offload-inboxis active and the tmpfs is mounted. Startup took 0.7 s,exit 42came through, and 1 MB of random bytes throughcatwas identical.offload install-cli --kind claudeinstalled Claude Code 2.1.317 into~/.local, whichbash -lcthen finds.offload sign-in --kind claude: the vendor login ran on a PTY in the box. The CLI opened only the claude.com authorize link, once, in the browser. It stopped at Claude's own login page (Google / Apple / email): the test account has no Claude account to use, so I went no further, as instructed. Then~.disconnected (exit 255), and noclaude authprocess was left in the box.offload --kind claudeon a throwaway repo gave exit 3:block box_cli: claude is signed out in the box, with the install-cli / sign-in hint, and nothing changed.rshpushed a repo in 1.4 s (openrsync on the Mac, rsync 3.2.7 in the box), andgit fetchoverrshwithGIT_SSH_VARIANT=simplebrought back a commit made in the box. The scratch directory was removed afterwards.offload statusprinted "no box CLI session";backwith no record exited 1 with "nothing of this repo is in a box".logoutrevoked the device: the old token then gotUNAUTHENTICATED invalid bearer token, and the device was gone from Keys → Devices.loginagain, thenoffload install-cli --kind claude(16 s).offload sign-in --kind claudeopened claude.com in a new tab of the e2e Chrome. You signed in, and the code was pasted into the CLI's terminal.Login successfulandsigned Claude Code in inside main. In the box,claude auth statusshowedloggedIn: true(authMethodclaude.ai).offloaded to main: Claude Code 2.1.317, signed in as …in 5.3 s total: preflight 0.2, begin 0.5, snapshot 0.2, rsync 2.1, send 1.9 s.offload statusshowedworking.inboxshowed#1 progress13 s later, thenidle.dabef43 readme: note the offload.back:backtimed out after 10 min (exit 1, nothing changed). The inbox reported BACK aspasted, but the text never reached Claude Code: no UserPromptSubmit hook, nothing in the transcript, an empty prompt in the tmux pane, and the session stuck atworking.back.backthen took 2.9 s:fast-forward. Localmainis atdabef43with the README line, the tree is clean, andrefs/cocoonbox/{offload,index,back,pre-back}/<id>andrefs/cocoonbox/box/mainare present.stopped, tmux gone).working,backsends BACK once more with--interrupt. A queued BACK is never re-sent. A unit test covers both cases. The root cause is in the box's inbox (deliverinagent/src/offload/inbox.rsdoes not confirm the CLI took the paste) and is a backend ask.claude auth logout; removed the Claude install,/workspace/offload-demo2and the offload state.logoutrevoked the device; only the CI personal key remains under Keys.~/.local/{bin,share,state}/claude,~/.cache/claude,~/.claude*) and left the box running, as it was.--kind claude) instead of<K>.Manual e2e (free test account, prod box)
Review
simular-code-review. The fixes it led to are in the follow-up commits: block offload when rsync is missing, report a failed revoke on logout, and skip the POSIX mode checks on Windows./secure-coding:invariant-reviewfound 2 low findings, both fixed here: the box token reached the preflight's child processes (now only rsync and git over rsh get it), and afile:../..secret entry could leave the repo (now refused).cmd /c start, so a box could inject commands andlogin's&URL broke; raw-terminal sessions had no escape and no keepalive; and EPIPE crashedrsh.state, PKCE andiss; tokens stay out of argv and logs; and only vendor-host https links are opened.🤖 Generated with Claude Code