Skip to main content

solari-desktop (Python)

Python SDK for Solari Desktop — create managed, hardware-isolated Linux desktops that boot in well under a second, embed the live VNC stream, and drive them with a computer-use action API (exec, fs, mouse/keyboard, screenshot, clipboard, processes).

This is a faithful port of the TypeScript @solarisdk/sdk package; the class, method, and field names match it one-for-one (snake_case where TS uses camelCase for method/parameter names; wire/response field names are kept as-is).

SDK ──HTTPS──▶ Gateway  (create / get / destroy sessions)
SDK ──WSS────▶ Gateway  (control channel: computer-use JSON-RPC)
            ──WSS────▶  (stream channel: RFB/VNC bytes, embed in a viewer)

Install

pip install solari-desktop

Depends only on httpx (HTTP) and websockets (control channel). Requires Python 3.9+.

Usage

The primary API is async (matching the asyncio WebSocket control channel):

import asyncio
from solari_desktop import DesktopClient


async def main() -> None:
    async with DesktopClient(
        api_key="...",
        base_url="https://api.getsolari.com",
    ) as client:
        # Create a session (assigned from the warm pool — typically sub-second).
        desktop = await client.create(
            template="default",
            resolution="1280x720",
            timeout_ms=1_800_000,   # rolling idle window: auto-pause after 30 min idle
        )

        # Embed this URL in a VNC viewer to watch the live desktop.
        print(desktop.streamUrl)

        # Open the control channel, then drive the desktop.
        await desktop.connect()

        shot = await desktop.screenshot(format="png")          # bytes
        out = await desktop.exec("ls", args=["-la", "/home"])
        print(out.exitCode, out.stdout)

        await desktop.mouse.click(640, 360, humanize=True)
        await desktop.keyboard.press(["ctrl", "c"])
        await desktop.fs.write("/tmp/note.txt", "hello")
        text = await desktop.fs.read_text("/tmp/note.txt")

        # Public preview URL for an in-guest port (requires PREVIEW_DOMAIN).
        preview = await desktop.preview_url(3000)   # {"url": ..., "token"?: ...}
        print(preview["url"])

        # Pause (snapshot RAM+disk, free the slot) and resume later.
        await desktop.pause()
        await desktop.resume()

        await desktop.close()
        await client.destroy(desktop.sessionId)


asyncio.run(main())

A thin synchronous wrapper, SyncDesktopClient, is available for the session-lifecycle calls (create, get, destroy, attach) when you are not running an event loop. The returned Desktop handle remains async.

Client API (DesktopClient)

Method Description
DesktopClient(api_key=, base_url=, http=None, call_timeout_ms=None) Construct a client.
await create(template="default", ttl_seconds=None, timeout_ms=None, resolution=None, metadata=None, record=None, lifecycle=None)Desktop POST /desktops. timeout_ms sets a rolling idle window; lifecycle controls idle behaviour (default auto-pause).
await get(session_id) GET /desktops/:idGetDesktopResponse(sessionId, status, expiresAt).
await destroy(session_id) DELETE /desktops/:idDeleteDesktopResponse(ok=True) (idempotent).
attach(session)Desktop Re-create a handle from a saved CreateDesktopResponse.
await aclose() Close the owned HTTP client. Also usable as an async context manager.

Gateway errors are mapped to typed errors: AuthError (401), PlanError (402), ConcurrencyLimitError (429), NoCapacityError (503), and a generic GatewayError otherwise — all subclasses of SolariError.

Desktop handle (Desktop)

Properties: sessionId, streamUrl, controlUrl, expiresAt, connected.

await connect() opens the control WebSocket; await close() tears it down. Each action sends a { id, method, params } JSON-RPC frame and awaits the matching { id, ok, result } reply, correlated by id, with a per-call timeout (TimeoutError). A failed RPC raises ActionError.

Computer-use actions

Surface Methods
await desktop.exec(cmd, args=None, cwd=None, timeout_ms=None) ExecResult(exitCode, stdout, stderr)
desktop.fs read(path)→bytes, read_text(path)→str, write(path, data, mode=None), list(path)→list[FsEntry]
desktop.mouse move(x,y,humanize=), click(x,y,button=,humanize=), down/up(x,y,button=), scroll(x,y,...)
desktop.keyboard type(text), press(keys), down(keys), up(keys)
await desktop.screenshot(format="png", quality=None) bytes
desktop.display set(w, h)
desktop.clipboard get()→str, set(text)
desktop.process list()→list[ProcessInfo], kill(pid)
await desktop.health() HealthResult(ready, display, vnc)

Lifecycle & preview

Since the 2026-07 desktop/sandbox VM consolidation, a desktop is backed by the same unified session record as a sandbox, so the handle carries the full lifecycle surface (routed to /sandboxes/:id/*):

Method Description
await desktop.pause() / await desktop.resume() Pause (snapshot RAM+disk, free the slot) / resume (re-acquires a slot; may raise ConcurrencyLimitError if the org is at cap).
await desktop.set_timeout(timeout_ms) Extend the rolling idle keep-alive → { expiresAt }.
await desktop.metrics() / await desktop.snapshot(name=None) / await desktop.revert(id) Metrics + in-place snapshot/restore.
await desktop.preview_url(port) Public preview URL { url, token? } for an in-guest port.

See examples/quickstart.py for an end-to-end script.

Deviations from the TypeScript SDK

  • Async-first. The TS SDK is promise-based; this SDK exposes the same surface as async def coroutines, plus SyncDesktopClient for the lifecycle calls. The TS SDK does not split sync/async, so no async/sync split is exposed beyond that convenience wrapper.
  • Naming. Method and parameter names are snake_case (read_text, ttl_seconds, call_timeout_ms); constructor args are keyword-only. Wire and response field names (sessionId, exitCode, streamUrl, …) are kept verbatim so dataclass attributes match the JSON.
  • Bytes vs Uint8Array. fs.read/screenshot return bytes; fs.write accepts bytes | str.
  • HTTP/WS deps. Uses httpx and websockets instead of fetch/ws.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

solari_desktop-0.1.1.tar.gz (54.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

solari_desktop-0.1.1-py3-none-any.whl (45.6 kB view details)

Uploaded Python 3

File details

Details for the file solari_desktop-0.1.1.tar.gz.

File metadata

  • Download URL: solari_desktop-0.1.1.tar.gz
  • Upload date:
  • Size: 54.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for solari_desktop-0.1.1.tar.gz
Algorithm Hash digest
SHA256 03ba3ced7196fa09b2feed0cb35e31fd68eed82260570eef7c30f718f9c6f844
MD5 ae8224cf3ce518b9c6fd601bc868aa58
BLAKE2b-256 24f48619dd860c8789440e0027904614064d5e2f69dc3f31abd0c13ca9b4a736

See more details on using hashes here.

File details

Details for the file solari_desktop-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: solari_desktop-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 45.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for solari_desktop-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6b7387d545e1d0ec0caed5da50747433f3aa2d9a48dcc88e6cdadccee8550e98
MD5 d47a525bc77358a99d7a54b3eaf33abf
BLAKE2b-256 21a75257530d4c0388cf2a7d42640c9816068efdcfbae25a77b01ab8ce18c67b

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.0

2 files

This release

0.1.1 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page