Computers
Cloud environments that Northstar operates in, or that you control directly.
A computer is an isolated cloud environment that Northstar operates in (via Tasks or the Responses API), or that you control directly with the Computers API. Each one runs Lightcone OS, a minimal desktop runtime built for Northstar.
When you use Tasks, the computer is created and managed for you automatically. You only need this page if you’re building your own computer-use loop or doing direct programmatic control.
Desktop vs. browser
Section titled “Desktop vs. browser”| Desktop | Browser | |
|---|---|---|
kind | "desktop" | "browser" |
| Environment | Full Lightcone OS desktop | Lightcone OS with Chromium in foreground |
| Use cases | Native apps, multi-app workflows, OS-level work | Web automation, scraping, testing |
| Tab management | No | Yes |
| Proxy support | No | Yes (stealth mode, residential proxies) |
| File system | Full Linux filesystem | Isolated per session |
Lifecycle
Section titled “Lifecycle”Create
Section titled “Create”from tzafon import Lightcone
client = Lightcone()
# High-level wrapper with automatic cleanupwith client.computer.create(kind="desktop") as computer: result = computer.screenshot() print(computer.get_screenshot_url(result)) # computer terminates when block exitsimport Lightcone from "@tzafon/lightcone";
const client = new Lightcone();const computer = await client.computers.create({ kind: "desktop" });
const result = await client.computers.screenshot(computer.id!);console.log(result.result?.screenshot_url);
// Always clean up when doneawait client.computers.delete(computer.id!);Interact
Section titled “Interact”Every action returns an ActionResult containing a status, optional result data, and optional page_context with viewport state.
computer.click(100, 200)computer.type("hello world")computer.hotkey("enter")computer.scroll(0, 300, 640, 400) # dx, dy, x, y
result = computer.screenshot()url = computer.get_screenshot_url(result)
html_result = computer.html()content = computer.get_html_content(html_result)const id = computer.id!;
await client.computers.click(id, { x: 100, y: 200 });await client.computers.type(id, { text: "hello world" });await client.computers.hotkey(id, { keys: ["enter"] });await client.computers.scroll(id, { dx: 0, dy: 300, x: 640, y: 400 });
const result = await client.computers.screenshot(id);const url = result.result?.screenshot_url as string;
const htmlResult = await client.computers.html(id);const content = htmlResult.result?.html_content as string;| Action | Result key | Value |
|---|---|---|
screenshot() | screenshot_url | URL of the captured screenshot |
html() | html_content | The page’s HTML as a string |
debug() | debug_response | Shell command output |
Terminate
Section titled “Terminate”# Automatic with context manager (recommended)with client.computer.create(kind="desktop") as computer: pass # ... work with the computer
# Or manual cleanupcomputer = client.computers.create(kind="desktop")result = client.computers.screenshot(computer.id)client.computers.delete(computer.id)await client.computers.delete(computer.id!);ComputerSession (high-level wrapper)
Section titled “ComputerSession (high-level wrapper)”Both SDKs provide a ComputerSession class that binds a computer ID to convenience methods, so you don’t have to pass the ID on every call.
Python. Accessed via client.computer.create() with a context manager:
with client.computer.create(kind="desktop") as computer: computer.click(100, 200) computer.type("hello") result = computer.screenshot() url = computer.get_screenshot_url(result)TypeScript. Imported from the package and created with ComputerSession.create():
import Lightcone, { ComputerSession } from "@tzafon/lightcone";
const client = new Lightcone();const computer = await ComputerSession.create(client, { kind: "desktop" });
try { await computer.click(100, 200); await computer.type("hello"); const result = await computer.screenshot(); const url = ComputerSession.getScreenshotUrl(result);} finally { await computer.terminate();}Actions reference
Section titled “Actions reference”| Action | Description |
|---|---|
click(x, y) | Left-click at pixel coordinates |
double_click(x, y) | Double-click |
right_click(x, y) | Right-click (context menu) |
drag(x1, y1, x2, y2) | Click-and-drag between two points |
mouse_down(x, y) | Press and hold the mouse button |
mouse_up(x, y) | Release the mouse button |
Keyboard
Section titled “Keyboard”| Action | Description |
|---|---|
type(text) | Type text into the focused element |
hotkey(keys) | Press a key combination (e.g., ["ctrl", "c"]) |
key_down(key) | Press and hold a key |
key_up(key) | Release a held key |
Navigation & viewport
Section titled “Navigation & viewport”| Action | Description |
|---|---|
navigate(url) | Go to a URL (browser mode) |
scroll(dx, dy, x, y) | Scroll at position by delta |
viewport(width, height) | Resize the viewport |
screenshot() | Capture the current screen |
html() | Get the page HTML (browser mode) |
Timeouts and keepalive
Section titled “Timeouts and keepalive”| Parameter | Default | Description |
|---|---|---|
max_lifetime_seconds | Plan-dependent | Maximum total lifetime |
inactivity_timeout_seconds | Plan-dependent | Time with no actions before auto-termination: any executed action or keepalive call resets it |
idle_timeout_enabled | true | Whether the inactivity timeout is active (max lifetime always applies) |
computer.keep_alive()await client.computers.keepalive(computer.id!);Persistent state
Section titled “Persistent state”Save environment state and restore it later:
# Save state on terminationwith client.computer.create(kind="desktop", persistent=True) as computer: result = computer.screenshot() # State saved automatically when terminated
# Restore laterwith client.computer.create( kind="desktop", environment_id="previous_computer_id", persistent=True,) as computer: result = computer.screenshot() # Everything is still thereconst computer = await client.computers.create({ kind: "desktop", persistent: true,});await client.computers.delete(computer.id!);
const restored = await client.computers.create({ kind: "desktop", environment_id: computer.id!, persistent: true,});Batch actions
Section titled “Batch actions”Execute multiple actions in a single request:
results = client.computers.batch(computer.id, actions=[ {"type": "click", "x": 100, "y": 200}, {"type": "type", "text": "search query"}, {"type": "hotkey", "keys": ["enter"]}, {"type": "screenshot"},])const results = await client.computers.batch(computer.id!, { actions: [ { type: "click", x: 100, y: 200 }, { type: "type", text: "search query" }, { type: "hotkey", keys: ["enter"] }, { type: "screenshot" }, ],});Fused act-and-observe
Section titled “Fused act-and-observe”Any action endpoint accepts ?screenshot_after=true to return a post-action screenshot in the same response, one round trip instead of two. Combine with settle_ms (how long to let the screen settle first) and base64=true (inline the image instead of returning a URL). See Observability.
Connect with Playwright or Puppeteer (CDP)
Section titled “Connect with Playwright or Puppeteer (CDP)”Browser computers expose their Chrome DevTools Protocol endpoint. Point any CDP client at it and drive the same browser with selectors, while keeping the Lightcone actions API, screenshots, and live view available on the same session:
First discover the browser’s CDP websocket URL from the session’s /cdp/json/version endpoint (passing your key as ?token= embeds it in the returned URL), then connect a CDP client to it:
import httpxfrom playwright.sync_api import sync_playwright
computer = client.computers.create(kind="browser")
# The returned webSocketDebuggerUrl already carries the auth token.info = httpx.get( f"https://api.tzafon.ai/computers/{computer.id}/cdp/json/version", params={"token": "YOUR_API_KEY"},).json()
with sync_playwright() as p: browser = p.chromium.connect_over_cdp(info["webSocketDebuggerUrl"]) page = browser.contexts[0].pages[0] page.goto("https://example.com") print(page.title())import { chromium } from "playwright";
const computer = await client.computers.create({ kind: "browser" });
// The returned webSocketDebuggerUrl already carries the auth token.const info = await fetch( `https://api.tzafon.ai/computers/${computer.id}/cdp/json/version?token=YOUR_API_KEY`,).then((r) => r.json());
const browser = await chromium.connectOverCDP(info.webSocketDebuggerUrl);const page = browser.contexts()[0].pages()[0];await page.goto("https://example.com");console.log(await page.title());This is the recommended pattern for hybrid automation: deterministic Playwright steps for the known path, Northstar for the parts that vary. See the Playwright integration for a full guide.
Proxy support (browser mode)
Section titled “Proxy support (browser mode)”with client.computer.create(kind="browser", use_advanced_proxy=True) as computer: computer.navigate("https://protected-site.com")const computer = await client.computers.create({ kind: "browser", use_advanced_proxy: true,});See also
Section titled “See also”- Lightcone OS: the desktop runtime environments run on
- Operate a computer: step-by-step walkthrough
- Browser tabs: multi-tab workflows (browser mode)
- Shell commands: run terminal commands
- Troubleshooting: common issues and solutions