Skip to content

Feat/islo sdk provider#24

Closed
AdamGold wants to merge 2 commits into
openclaw:mainfrom
islo-labs:feat/islo-sdk-provider
Closed

Feat/islo sdk provider#24
AdamGold wants to merge 2 commits into
openclaw:mainfrom
islo-labs:feat/islo-sdk-provider

Conversation

@AdamGold

@AdamGold AdamGold commented May 4, 2026

Copy link
Copy Markdown

replacing #16 for adding islo.dev support with Go SDK

Summary

Adds provider: islo to crabbox, driven by the islo Go SDK. No islo CLI required. Auth via ISLO_API_KEY; the SDK's NewIslo handles API-key → JWT exchange and auto-refresh.

export ISLO_API_KEY=ak_...
crabbox warmup --provider islo
crabbox run    --provider islo --id <sandbox> -- pnpm test
crabbox status --provider islo --id <sandbox> --wait
crabbox list   --provider islo --json
crabbox stop   --provider islo <sandbox>

Mapping

crabbox op SDK call
warmup Sandboxes.CreateSandbox
run Sandboxes.CreateSandbox (idempotent) + direct SSE consumer on POST /sandboxes/{name}/exec/stream
status Sandboxes.GetSandbox; --wait polls every 2s
list Sandboxes.ListSandboxes
stop Sandboxes.DeleteSandbox

Why a custom SSE consumer

The SDK's Sandboxes.ExecInSandboxStream returns interface{} via the standard JSON caller.Call — it buffers the SSE body and decodes, so callers don't see live deltas. Until the SDK exposes a streaming iterator, internal/cli/islo.go issues the streaming request directly. Auth (customauth.Provider), base URL, and token refresh still come from the SDK, so behavior stays in lockstep with NewIslo. The wire format observed against api.islo.dev is plain SSE (event: stdout|stderr|exit, one or more data: lines, blank-line terminator); parseIsloSSE follows the spec including multi-line data: joining and the leading-space strip.

Mirrors the blacksmith pattern

  • IsloConfig struct + islo: YAML block + CRABBOX_ISLO_* env overrides
  • --islo-image, --islo-workdir, --islo-gateway-profile flags on warmup/run
  • Lease IDs of the form isb_<sandbox-name> in the existing claim store; reuses claimLeaseForRepoProvider, removeLeaseClaim, newLeaseSlug, resolveLeaseClaim unchanged
  • serverTypeForConfig / serverTypeForProviderClass return "" for islo
  • Dispatch sites in run.go, pool.go, status.go, capabilities.go, target.go, screenshot.go, vnc.go add isIsloProvider next to existing isBlacksmithProvider checks

Tests

Hand-rolled fake IsloClient (interface seam) drives unit tests for warmup, run, streaming output, exit-code propagation, list (text + JSON), status wait, status timeout, stop, auth-failure surfacing, lease-ID resolution, and SSE parsing. config_test.go extended with islo YAML + CRABBOX_ISLO_* env assertions.

Live verification (against api.islo.dev)

  • go vet ./...
  • gofmt -l $(git ls-files '*.go') clean
  • go test -race ./...
  • go build -trimpath -o bin/crabbox ./cmd/crabbox
  • node scripts/check-docs-links.mjs and node scripts/check-command-docs.mjs pass
  • Core coverage gate: 86.1% (above 85%)
  • crabbox warmup --provider islo — sandbox created, lease + slug printed
  • crabbox status --provider islo --id <sandbox> --wait — polled to running
  • crabbox run --provider islo --id <sandbox> -- echo hello — streamed stdout
  • crabbox run --provider islo --id <sandbox> -- bash -lc '...; sleep 1; ...; exit 7' — interleaved stdout/stderr arriving live; exit code 7 propagates as ExitError{Code: 7}
  • crabbox list --provider islo --json — sandbox visible with running status
  • crabbox stop --provider islo <sandbox> — sandbox released, claim removed
  • Negative: missing ISLO_API_KEY → exit 2 with actionable message
  • Negative: --desktop → exit 2 (islo sandboxes are headless)
  • Negative: --sync-only → exit 2 (sync is delegated)

Follow-ups (not part of this PR)

  • File against go-sdk: expose Sandboxes.ExecStream(ctx, ...) <-chan ExecLogLine (or similar) so the custom SSE consumer here can be deleted.
  • The islo runner emits a trailing ANSI clear-screen sequence at the end of each exec; harmless but visible. Worth raising upstream.

🤖 Generated with Claude Code

AdamGold and others added 2 commits May 4, 2026 12:48
Adds `provider: islo` (alias-free) backed by github.com/islo-labs/go-sdk.
Auth comes from ISLO_API_KEY (the SDK's NewIslo handles token exchange and
refresh). Operations map to SDK calls: warmup -> CreateSandbox, status ->
GetSandbox (with --wait polling), list -> ListSandboxes, stop -> DeleteSandbox.
Run streams stdout/stderr live by consuming the islo SSE exec endpoint
directly (the SDK currently buffers SSE through its JSON wrapper); auth and
base URL still come from the SDK so retry/refresh stay consistent.

Mirrors the existing blacksmith provider's dispatch and lease/claim model:
- IsloConfig + islo: YAML block + CRABBOX_ISLO_* env overrides
- --islo-image, --islo-workdir, --islo-gateway-profile flags on warmup/run
- isb_<sandbox-name> lease IDs in the local claim store
- Rejects --desktop, --browser, --sync-only, --checksum, --force-sync-large,
  --actions-runner for islo (all delegated or unsupported)
- target.go skips coordinator routing; screenshot/vnc reject islo

Tests: hand-rolled fake IsloClient (interface seam) covers warmup, run,
streaming output, exit-code propagation, list, status wait + timeout, stop,
auth-failure surfacing, lease-ID resolution, and SSE parsing. Existing config
tests extended with islo YAML and CRABBOX_ISLO_* assertions.

Live smoke against api.islo.dev confirmed warmup, status --wait, run with
streamed stdout/stderr and nonzero exit propagation, list --json, and stop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The islo API has three exec entrypoints; the doc now records why we picked
POST /exec/stream (batch, no stdin) over WebSocket /exec (interactive PTY)
and over POST /exec + GetExecResult polling. Adds the SSE wire format
(event: stdout|stderr|exit + data: lines) so a future maintainer can debug
without reading parseIsloSSE.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@steipete

steipete commented May 4, 2026

Copy link
Copy Markdown
Contributor

I tested this, seems not yet to be working and codex prefers the cli approach. I first need to see if your service makes sense, tbh the only reason we added blacksmith is because they sponsor us.

@steipete steipete mentioned this pull request May 4, 2026
7 tasks
@steipete

steipete commented May 4, 2026

Copy link
Copy Markdown
Contributor

Thanks for the SDK-based follow-up and the detailed write-up. Closing for now because Crabbox is not taking an Islo provider integration at this point; providers are staying focused on the current ownership/provisioning backends. If we revisit Islo later after the service/product fit is clearer, I would start from a fresh, smaller design rather than keep this broad provider PR open.

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.

2 participants