Commit Graph

4 Commits

Author SHA1 Message Date
José Almeida ed76b7b6f0 refactor(e2e): extract SSH HTTP shapes into api-client-ssh.ts
api-client.ts was already over the 600-line lint cap on main (1528
lines); the SSH additions made it worse. Split the SSH HTTP shape
types into a dedicated module and re-export from api-client.ts so
existing callers keep working. The methods themselves stay on
ApiClient as thin pass-throughs since splitting them out across the
class boundary would change every test call site.

Also document the per-executor bound on the SSH listSessions per-row
GetTaskSession lookup (typical N < 10), and what a batch path would
need (a repo method to fetch task_sessions by id list). Cubic /
Greptile P2.
2026-05-18 00:05:15 +01:00
José Almeida a30833b336 refactor(ssh): drop the unused conn pool + remote curl dependency
CodeRabbit and Greptile both flagged the SSHConnPool: the executor never
took the pool path (CreateInstance always dialDirect'd a fresh client and
parked it on sshSessionState), so the pool's keepalive goroutines and
refcount logic were dead code. Get() also overwrote map entries on every
call, which would have leaked keepalive goroutines if the pool *had* been
used. Delete the entire SSHConnPool + sshConnKey + pooledConn + keepalive
plumbing. Export DialSSH so the SSH test endpoint can dial directly and
close the client when its single-shot probe is done.

While here:
  * pickRemoteAgentctlPort: switch from time.Now().UnixNano()%20000 to
    math/rand/v2.IntN. Two concurrent CreateInstance calls on a fast
    machine were getting the same port (UnixNano cycles in ~20µs through
    the 20k range); randomized picks avoid the collision.
  * createRemoteAgentInstance: POST to the remote control port over a
    direct-tcpip dial via net/http instead of shelling out to `curl`. The
    sshd container no longer needs curl, the kandev test endpoint no
    longer needs to probe for it, and the call inherits Go's standard
    timeout + error handling. Drop `curl` from the e2e Alpine image.
  * maybeUploadCredentials: was probing `printf %s "$HOME"` and
    discarding the output, then allocating an unused sshFileUploader.
    SSH v1 doesn't ship credential upload — just log a warning when a
    user sets remote_credentials so they know it won't take effect, and
    delete the dead uploader file.
2026-05-18 00:05:15 +01:00
José Almeida e7933645fa fix(ssh): resolve session executor_id via TaskSession join
GET /api/v1/ssh/executors/:id/sessions was filtering ExecutorRunning rows by
their ExecutorID column, but the lifecycle manager's buildRunningFromExecution
only sets that column from a prior row — for fresh sessions it's left empty.
The endpoint therefore returned [] for every just-launched SSH session.

Look up the TaskSession by session_id and compare its ExecutorID instead.
Keep the row's own ExecutorID as a fast-path for resume/recovery flows
where it's already populated.
2026-05-17 23:45:07 +01:00
José Almeida 4fded99620 feat(backend): add SSH test + sessions HTTP/WS endpoints
POST /api/v1/ssh/test runs the connection probe used by the settings UI:
dials the host with a permissive host-key callback that records (but
does not pin) the fingerprint, probes uname/arch/git, and reports
whether agentctl is already cached on the remote. The UI gates the
"Trust this host" checkbox on success + fingerprint, and Save persists
the trusted fingerprint into the executor config.

GET /api/v1/ssh/executors/:id/sessions returns the active SSH-runtime
sessions backing the "Active sessions" card on the executor settings
page, mirroring the SpritesInstancesCard / DockerContainersCard shape
(task, session, user@host, remote port, local forward port, uptime).

Both routes also exposed over WS via ssh.test / ssh.sessions.list.
2026-05-17 23:28:50 +01:00