Skip to content

Fix attach_session URL to match sprites-go and sprites-py#10

Open
jhgaylor wants to merge 1 commit intosuperfly:mainfrom
ravi-hq:fix/attach-session-url
Open

Fix attach_session URL to match sprites-go and sprites-py#10
jhgaylor wants to merge 1 commit intosuperfly:mainfrom
ravi-hq:fix/attach-session-url

Conversation

@jhgaylor
Copy link
Copy Markdown

@jhgaylor jhgaylor commented May 1, 2026

Summary

Sprites.attach_session/3 was building URLs as /v1/sprites/:name/exec?session_id=X, but the server expects the path-based form /v1/sprites/:name/exec/:session_id — the same shape both sprites-go and sprites-py use.

Symptom

attach_session would return {:ok, command} because the WebSocket upgrade succeeded — the server saw the bad URL as a fresh exec with empty command. The originally detached process kept running on the sprite, but no output ever flowed to the new connection. End result: it was impossible to recover an in-flight detachable: true command after a client-side crash.

Fix

When :session_id is set in opts:

  • Build the attach URL with the session id in the path, matching the other SDKs.
  • Skip spawn-only query params (path/cmd/env/dir/detachable) — the server ignores them on attach since they belong to the original spawn.
  • stdin/tty are still forwarded since they're meaningful for the attached side.

Test plan

  • Existing test suite still passes (24 tests, 1 unrelated boilerplate failure).
  • Verified end-to-end with a probe:
    1. Spawn bash -c "echo BEFORE; sleep 20; echo AFTER" with detachable: true.
    2. Hard-kill the Command GenServer mid-sleep.
    3. list_sessions shows the detached session with is_active: false.
    4. attach_session to that id.
    5. Observed: BEFORE replayed from the session buffer, AFTER streamed live post-disconnect, clean exit 0 reported.
  • Reviewer to confirm against the server-side URL routing in sprites.dev.

🤖 Generated with Claude Code

The attach_session function was building URLs of the form
`/v1/sprites/:name/exec?session_id=X`, but the server expects the
path-based form `/v1/sprites/:name/exec/:session_id` (also used by the
Go and Python SDKs).

Symptom: `Sprites.attach_session/3` would succeed the WebSocket upgrade
because the server treated the request as a fresh exec with empty
command. The original detached process would keep running on the
sprite, but no output ever flowed to the new connection — making it
impossible to recover an in-flight `detachable: true` command after a
client-side crash.

Fix: when `:session_id` is set in opts, build the attach URL with the
session id in the path. Also skip the spawn-only query params
(path/cmd/env/dir/detachable), since the server ignores them on attach.

Verified end-to-end with a probe that:
1. spawns `bash -c "echo BEFORE; sleep 20; echo AFTER"` detachably
2. hard-kills the Command GenServer mid-sleep
3. lists sessions and reattaches via attach_session
4. observes BEFORE replayed from the session buffer, AFTER streamed
   live post-disconnect, and a clean exit code 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant