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/:id → GetDesktopResponse(sessionId, status, expiresAt). |
await destroy(session_id) |
DELETE /desktops/:id → DeleteDesktopResponse(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 defcoroutines, plusSyncDesktopClientfor 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/screenshotreturnbytes;fs.writeacceptsbytes | str. - HTTP/WS deps. Uses
httpxandwebsocketsinstead offetch/ws.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
03ba3ced7196fa09b2feed0cb35e31fd68eed82260570eef7c30f718f9c6f844
|
|
| MD5 |
ae8224cf3ce518b9c6fd601bc868aa58
|
|
| BLAKE2b-256 |
24f48619dd860c8789440e0027904614064d5e2f69dc3f31abd0c13ca9b4a736
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b7387d545e1d0ec0caed5da50747433f3aa2d9a48dcc88e6cdadccee8550e98
|
|
| MD5 |
d47a525bc77358a99d7a54b3eaf33abf
|
|
| BLAKE2b-256 |
21a75257530d4c0388cf2a7d42640c9816068efdcfbae25a77b01ab8ce18c67b
|