Skip to content

Commit 1b0ca78

Browse files
frenchie4111claude
andauthored
feat: VSCode-style "Connect to remote host" SSH bootstrap (#107)
https://github.com/user-attachments/assets/0a2aa8eb-89a0-48fa-a73f-27041a816acb ## Summary Implements the VSCode-style "Connect to remote host" flow for Harness's existing multi-backend (Tier 1) system. The user picks an SSH host from `~/.ssh/config` (or types a freeform `user@host`), Harness SSHes in, installs `harness-server` if missing, starts it detached so it survives disconnect, sets up an SSH local port forward, and hands the URL+token to the existing `connections:add` flow. This is the implementation of `plans/remote-main.md` §4 (and finishes §3 from the multi-backend Tier-1 work). ## Commits (recommended commit-by-commit review order) 1. `feat(ssh): parse ~/.ssh/config + add ssh-config listing IPC` — `src/main/ssh-config.ts` + IPC handler + tests 2. `feat(ssh): node-ssh dependency + low-level bootstrap orchestrator` — `src/main/ssh-bootstrap.ts` skeleton with progress events (no UI yet) 3. `feat(ssh): ssh-bootstrap slice + tunnel manager` — slice + tunnel lifecycle code 4. `feat(ssh): BackendConnection.ssh field + boot-time reconnect` — extends persistence + boot path 5. `feat(ssh): SSH tab in AddBackendModal` — UI piece 6. `feat(ssh): wire ssh:bootstrap into connections:add + progress log UI` — final integration ## Manual smoke test NOTE: This change required modifying the headless server, so testing manually is kind of a long process. I had to manually build locally, SCP the updated server over to the remote to replace the files already installed from a previous version, and then connect remotely. You might just have to trust me on this one that it works. We won't be able to do a real full test until the new binary is in the GitHub releases anyway --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5db289f commit 1b0ca78

21 files changed

Lines changed: 2440 additions & 128 deletions

package-lock.json

Lines changed: 150 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,15 @@
158158
"lucide-react": "^1.8.0",
159159
"monaco-editor": "^0.55.1",
160160
"node-pty": "^1.1.0",
161+
"node-ssh": "^13.2.1",
161162
"playwright-core": "^1.59.1",
162163
"qrcode.react": "^4.2.0",
163164
"react": "^19.2.5",
164165
"react-dom": "^19.2.5",
165166
"react-markdown": "^10.1.0",
166167
"rehype-highlight": "^7.0.2",
167168
"remark-gfm": "^4.0.1",
169+
"ssh-config": "^5.1.0",
168170
"ws": "^8.20.0"
169171
},
170172
"devDependencies": {

src/main/build-initial-state.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { initialJsonClaude } from '../shared/state/json-claude'
1212
import { initialSnooze } from '../shared/state/snooze'
1313
import { initialAnnouncements } from '../shared/state/announcements'
1414
import { initialScratchpad } from '../shared/state/scratchpad'
15+
import { initialSshBootstrap } from '../shared/state/ssh-bootstrap'
1516
import {
1617
initialSettings,
1718
DEFAULT_LIGHT_THEME,
@@ -70,6 +71,7 @@ export function buildInitialAppState(
7071
snooze: config.snooze ? { byPath: { ...config.snooze } } : initialSnooze,
7172
announcements: initialAnnouncements,
7273
scratchpad: { byWorktreePath: flattenScratchpadNotes(config.scratchpadNotes) },
74+
sshBootstrap: initialSshBootstrap,
7375
settings: {
7476
...initialSettings,
7577
themeMode:

0 commit comments

Comments
 (0)