Sophon Docs
Sophon Node

Available Commands

The complete reference for every command a Sophon Node can execute — parameters, responses, scopes, risk levels.

Commands are invoked by the agent via the built-in node.command tool, which the Gateway dispatches to the target Node over SignalR. Every command runs inside the Node's platform abstraction — the same API surface on Windows, macOS, and Linux with OS-specific implementations under the hood.

Required scopes appear in every section. See Permissions & Scopes for scope configuration.

Screen

screen.capture

Captures the full primary display.

Scope: screen.capture · Risk: Low

ParamTypeDefaultNotes
qualityint80JPEG quality (1–100)
maxWidthint?nullOptional max width; aspect ratio preserved

Response: { image: "<base64-jpeg>", sizeBytes, format: "jpeg" }

screen.region

Captures a rectangular region.

Scope: screen.capture · Risk: Low

ParamTypeRequiredNotes
x, y, width, heightintyesRegion bounds
qualityintno (80)JPEG quality

Response: { image, sizeBytes, format, region: { x, y, width, height } }

screen.monitors

Lists the attached displays and their geometry.

Scope: screen.capture · Risk: Low

Response: { count, monitors: [ { id, isPrimary, x, y, width, height, scaleFactor, name } ] }

screen.queryElements

Returns the accessibility tree of the focused (or all) windows — useful for reliable, coordinate-free automation. Backed by UI Automation on Windows, the AX API on macOS, and AT-SPI on Linux.

Scope: screen.capture · Risk: Low

ParamTypeDefaultNotes
includeBackgroundboolfalseInclude unfocused windows
maxDepthint8Maximum tree depth
roleFilterstringOnly return elements of a given role
nameFilterstringOnly return elements whose name matches

Response: { elements: [...], count, focusedWindowTitle, warning? }. On Linux/Wayland the tree may be empty with a warning when the AT-SPI bus is unavailable.

Mouse

input.mouse.move

Move cursor to absolute coordinates.

Scope: input.control · Risk: Medium

ParamTypeRequiredNotes
x, yintyesTarget coordinates

input.mouse.click

Scope: input.control · Risk: Medium

ParamTypeDefaultNotes
x, yintrequiredClick coordinates
buttonstring"left""left", "right", "middle"
doubleClickboolfalseDouble-click

input.mouse.scroll

Scope: input.control · Risk: Medium

ParamTypeDefaultNotes
x, yintrequiredScroll anchor
deltaX, deltaYint0Scroll amount (pixels)

Keyboard

input.keyboard.type

Types a string character by character.

Scope: input.control · Risk: Medium

ParamTypeRequiredNotes
textstringyesText to type

input.keyboard.press

Presses a single named key.

Scope: input.control · Risk: Medium

ParamTypeRequiredNotes
keysstringyesKey name — enter, escape, tab, f5, backspace, delete, home, end, pageup, pagedown, arrows, etc.

input.keyboard.hotkey

Modifier combinations.

Scope: input.control · Risk: Medium

ParamTypeRequiredNotes
keysstringyes"ctrl+c", "alt+f4", "cmd+space", "win+d", "ctrl+shift+t"

Applications

app.launch

Launch an application by name or path.

Scope: app.manage · Risk: Medium

ParamTypeRequiredNotes
namestringyesApp name (chrome, notepad) or full path
argsstring[]noCommand-line args

Response: { pid: 5678, app: "chrome.exe" }

app.close

Close a running application (graceful — sends WM_CLOSE or equivalent, then kills after 5 s).

Scope: app.manage · Risk: Medium

ParamTypeRequiredNotes
namestringeitherApp name
pidinteitherProcess ID

app.focus

Bring an app's window to the foreground.

Scope: app.manage · Risk: Medium

ParamTypeRequiredNotes
namestringeitherApp name
pidinteitherProcess ID

app.list

List apps with visible windows.

Scope: app.manage · Risk: Medium

Response: { count: 5, apps: [ { name, pid, windowTitle } ] }

Windows

window.list

All visible windows with geometry.

Scope: window.manage · Risk: Low

Response:

{
  "count": 3,
  "windows": [
    { "id": "0x00010234", "title": "Untitled - Notepad",
      "x": 100, "y": 100, "width": 800, "height": 600,
      "processName": "notepad.exe" }
  ]
}

window.move

Move and optionally resize.

Scope: window.manage · Risk: Low

ParamTypeRequiredNotes
windowIdstringyesFrom window.list
x, yintyesNew position
width, heightintnoKeeps current if omitted

window.minimize, window.maximize

Scope: window.manage · Risk: Low

ParamTypeRequiredNotes
windowIdstringyesFrom window.list

Clipboard

clipboard.read

Scope: clipboard.access · Risk: Low

Response: { text: "...", length: 42 }

clipboard.write

Scope: clipboard.access · Risk: Low

ParamTypeRequiredNotes
textstringyesText to write

System

system.info

Machine info.

Scope: system.info · Risk: None

Response:

{
  "osDescription": "Microsoft Windows 11.0.26200",
  "machineName": "DESKTOP-ABC",
  "processorCount": 8,
  "totalMemoryMb": 16384,
  "dotnetVersion": ".NET 10.0.0",
  "uptime": 3600,
  "platform": "windows",
  "architecture": "x64"
}

system.processes

All running processes.

Scope: system.info · Risk: None

Response: { count: 42, processes: [ { pid, name, cpuPercent, memoryMb } ] }

system.execute

Run shell command. Always approval-gated regardless of scope config.

Scope: system.execute · Risk: Critical

ParamTypeRequiredNotes
commandstringyesThe full command line, as a single string
timeoutMsintnoExecution timeout (ms)

The whole command is passed as one string (not a separate args array) so it can be audited and matched against the denylist consistently. It runs via the platform shell:

  • Windows: cmd.exe /c "<command>"
  • macOS: /bin/zsh -c "<command>"
  • Linux: /bin/bash -c "<command>"

Response: { exitCode, stdout, stderr, durationMs }

Output truncated to 1 MB per stream.

Notifications

notify.show

Show an OS notification.

Scope: notify.send · Risk: None

ParamTypeDefaultNotes
titlestring"Notification"Notification title
bodystring""Notification body
iconstringOptional icon URL or path

Implementation:

  • Windows: WinRT / shell toast
  • macOS: osascript -e 'display notification ...'
  • Linux: notify-send

Canvas

canvas.present

Open the live Canvas for the current session on the Node's own screen.

Scope: canvas.control · Risk: Medium · consent-gated on the device

ParamTypeRequiredNotes
sessionIdstringyes*Session whose Canvas to show (*unless url is given)
urlstringnoExplicit http/https URL override

The Node opens the Dashboard Canvas page in the default browser. See Canvas on a Node for the full flow.

Reserved capabilities

Some scopes are reserved for future releases and have no handlers yet — granting them does nothing until the commands ship: filesystem access (filesystem.*), browser automation (browser.*), voice runtime (voice.*), camera (camera.*), and location (location.*).

Calling conventions

From an agent

node.command({
  nodeId: "office-pc",
  command: "screen.capture",
  params: { quality: 80, maxWidth: 1280 }
})

The agent selects the target Node by ID. For users with multiple Nodes, agents can call node.list first and ask which to use.

From the Dashboard

The Settings → Devices page lists each paired Node with its status and granted scopes, and surfaces recent command activity — handy for verifying scope configuration.

Listing nodes from the CLI

sophon nodes list      # paired nodes and their connection status

Commands themselves are issued by the agent through node.command, not typed as CLI verbs.

Timeouts

Every command has a default 30-second execution timeout. Long-running commands (large screen captures, shell commands) should pass timeoutMs explicitly.

If the Node doesn't respond within the timeout, the Gateway returns { error: "timeout" } but doesn't cancel the in-flight work on the Node. For commands that can't be interrupted cleanly, the side effect may still complete.

Error responses

{
  "ok": false,
  "error": "not_authorized",
  "message": "Node does not have required scope: input.control"
}

Common error codes:

  • not_authorized — scope missing
  • approval_rejected — approval gate rejected (Critical commands only)
  • timeout — command exceeded its execution budget
  • node_offline — Node isn't connected to the Gateway
  • platform_unsupported — command not available on this OS
  • invalid_params — params failed validation
  • internal_error — unexpected failure on the Node (check Node logs)

Where to go next