Manual acceptance test for #37 — running the
full sandy-ui UX against a sandy on an always-on remote host (a DGX Spark here), via VSCode
Remote-SSH. The extension is a workspace extension (extensionKind: workspace), so
its extension host runs on the remote and drives that machine's sandy/docker/tmux.
Cannot be automated (needs real Mac↔DGX hardware + Twingate transport). Run it by hand when validating the remote workflow, or when #33 (platform-specific VSIX with a remote server-Node target) needs an end-to-end check.
On the DGX, in a plain SSH terminal:
sandy --version # must be >= 1.1.0 (daemon mode)
node --version # NOTE this major (e.g. v20.x) — matters in Part 2
docker ps # docker reachableOn the Mac: VSCode with the Remote-SSH extension, and ssh dgx already working
(Twingate up).
In the DGX SSH terminal:
cd ~/dev/<some-project> # a real workspace under $HOME
sandy --start # detached daemon session; returns when attachable
# confirm daemon:true, attached_clients:0. The `perl` strip guards against a
# shell title-escape leak on stdout (harmless no-op otherwise; see the jq
# callout in Part 3 for what it is):
sandy --print-state light | perl -pe 's/\e\][^\a]*\a//' | jq '.running_containers'Leave it running. Note the workspace path — you'll open that exact folder from the Mac.
On the Mac, in VSCode:
Cmd+Shift+P-> Remote-SSH: Connect to Host -> pick the DGX. New window opens; bottom-left green badge reads SSH: dgx.- File -> Open Folder -> the workspace from Part 1 (e.g.
~/dev/<some-project>on the DGX).
In that window's integrated terminal (now running on the DGX):
git clone https://github.com/rappdw/sandy-ui ~/dev/sandy-ui
cd ~/dev/sandy-ui
npm install # builds node-pty against the DGX's system Node
npm run compile
npm run install-vsix # packages + installs into the SSH:dgx extension hostThen Cmd+Shift+P -> Developer: Reload Window.
If launching sandy fails with
posix_spawnp failed., the DGX's system Node ABI doesn't match the VS Code Server's bundled Node (the remote extension host runs under the server's Node, not Electron). Rebuild node-pty against the server's node:SERVER_NODE=$(find ~/.vscode-server -name node -type f -path '*/server/node' 2>/dev/null | head -1) [ -z "$SERVER_NODE" ] && SERVER_NODE=$(find ~/.vscode-server/bin -maxdepth 2 -name node -type f 2>/dev/null | head -1) "$SERVER_NODE" --version # note its major cd ~/dev/sandy-ui/node_modules/node-pty npx node-gyp rebuild --nodedir="$(dirname "$SERVER_NODE")/.." cd ~/dev/sandy-ui && npm run install-vsixReload window, retry. This friction is exactly what #33 eliminates with a prebuilt remote target. Whether the rebuild was needed is a headline result to report (see Part 5) — it directly informs #33's target matrix.
Your shell is prepending a terminal-title escape (
ESC ] 0 ; <title> BEL) to stdout — a title hook writing to fd 1 instead of/dev/tty, which corrupts any redirected output (found on the original DGX run; #38). Confirm withsandy --print-state light | head -c 40 | od -c(leading033 ] 0 ;= the escape). sandy-ui itself is immune (its poller tolerates this as of #38), so the UI steps below are unaffected — only the manualjqchecks are. Strip it inline:sandy --print-state light | perl -pe 's/\e\][^\a]*\a//' | jq '.running_containers'The durable fix is shell-side (guard the title hook on
[[ -t 1 ]]or write to/dev/tty) — not required to finish this runbook.
| # | Action (Mac VSCode, SSH:dgx window) | Expected | Cross-check (DGX terminal — prepend perl -pe 's/\e\][^\a]*\a//' | before jq if you hit the title-escape leak) |
|---|---|---|---|
| 3.1 | Open the Sandy activity-bar view | Tree lists the DGX daemon session (Part 1), badged persisted | sandy --print-state light | jq '.running_containers[].sandbox' matches |
| 3.2 | Click that session (or status bar -> pick it) | A Sandy tab opens showing the live agent tmux — same screen the DGX CLI sees | ... jq '.running_containers[].attached_clients' went 0 -> 1 |
| 3.3 | Also sandy --attach from a DGX terminal |
Last-attach-wins: one client displaces the other (no split-screen fight) | displaced client detaches cleanly |
| 3.4 | Type in the agent tab | Keystrokes reach the DGX agent (it responds) | — |
| 3.5 | Open a workspace file in the editor, edit + save | The agent (same bind-mounted host files) sees the change | cat <file> on the DGX shows the edit |
| 3.6 | Status-bar Stop (or tree -> Stop sandy) | Session tears down | ... jq '.running_containers' -> container gone; docker ps confirms |
- Re-
sandy --starta session and attach it in the UI (repeat 3.1-3.2). - Bounce the network: Mac Wi-Fi off ~15s, back on (or move to a different network).
- VSCode shows "Disconnected... Reconnecting...", then recovers the SSH: dgx window.
- Expected: the Sandy tree and the attached agent tab come back — the extension host never died on the DGX (it persists across client disconnect) and the daemon container was never touched. The webview repaints; the agent screen is intact.
Per section:
- Happy path or ABI-rebuild? Did Part 2 need the node-pty rebuild? If so, what were
node --version(DGX system) and the server node's version? -> direct input for #33. - Any of 3.1-3.6 that misbehaved, plus the "Sandy" / "Sandy State" output-channel contents if so.
- Part 4 recovery: clean, or did anything need a manual reattach/reload?
Clean run -> close #37. Anything off -> precise follow-ups; the ABI answer feeds #33.
- Most likely snag is Part 2's ABI rebuild. If the DGX runs the same Node major as the
VS Code Server bundles, plain
npm installjust works; otherwise the rebuild box is the path. Either outcome is a useful #37 result — "needed rebuild" is the finding that justifies #33's remote target. - Everything else should just work — the #37 investigation confirmed binary resolution,
reveal (
revealFileInOS+xdg-openfallback), and clipboard (vscode.env.clipboard) are already host-agnostic. Interesting failures would be genuine surprises worth capturing.