Skip to main content

SDK Reference

Every method on the NanoTermClient class. All methods are async and return typed results. Errors follow the shared error envelope.

Workspaces

createWorkspace(config?)

Create a new workspace.

const ws = await client.createWorkspace({
name: 'my-agent', // optional
image: 'ubuntu:22.04' // optional, default: ubuntu:22.04
})

console.log(ws)
// {
// id: 'ws_a1b2c3d4',
// name: 'my-agent',
// image: 'ubuntu:22.04',
// status: 'running',
// createdAt: '2026-04-06T...'
// }

Returns: Promise<WorkspaceInfo>

listWorkspaces()

List all workspaces in the current org.

const workspaces = await client.listWorkspaces()
// WorkspaceInfo[]

getWorkspace(id)

Get details for a specific workspace.

const ws = await client.getWorkspace('ws_a1b2c3d4')

terminateWorkspace(id)

Stop and remove a workspace.

await client.terminateWorkspace('ws_a1b2c3d4')

Execution

exec(workspaceId, command, opts?)

Execute a command and return the result.

const result = await client.exec('ws_a1b2c3d4', ['npm', 'test'])

console.log(result)
// {
// stdout: 'PASS src/index.test.ts\nTests: 12 passed',
// stderr: '',
// exitCode: 0
// }

// Run in a subdirectory
await client.exec('ws_a1b2c3d4', ['pnpm', 'build'], {
cwd: '/workspace/api',
})

Parameters:

NameTypeDescription
workspaceIdstringTarget workspace ID
commandstring[]Command and arguments
opts.cwdstringWorking directory inside the workspace (default /workspace)

Returns: Promise<ExecResult>

interface ExecResult {
stdout: string
stderr: string
exitCode: number
}

getTerminalUrl(workspaceId)

Get the WebSocket URL for interactive terminal access.

const url = client.getTerminalUrl('ws_a1b2c3d4')
// wss://api.nanoterm.dev/api/workspaces/ws_a1b2c3d4/terminal

Use this with a WebSocket client to establish a PTY session. The WebSocket upgrade handshake is gated by the same auth as HTTP — pass getAuthHeaders() to your client constructor:

import WebSocket from 'ws'
const ws = new WebSocket(client.getTerminalUrl(id), {
headers: client.getAuthHeaders(),
})

Messages are JSON-framed:

Client → Server:

// Send input
{ type: 'stdin', data: '<base64>' }

// Resize terminal
{ type: 'resize', cols: 80, rows: 24 }

Server → Client:

// Terminal output
{ type: 'stdout', data: '<base64>' }

Types


Files

writeFile(workspaceId, filePath, content)

Write a file inside the workspace. The server stats the file after writing and returns the actual bytes and mtime — verify these against what you sent before claiming success.

const content = new TextEncoder().encode('hello\n').buffer
const result = await client.writeFile('ws_a1b2c3d4', '/workspace/hello.txt', content)
// { bytes: 6, mtime: '2026-05-06T08:01:23.000Z' }

if (result.bytes !== 6) {
throw new Error('write was truncated upstream')
}

A 4xx/5xx response throws a NanoTermError with the standard error envelope. Common error codes:

CodeMeaning
FILE_WRITE_BYTE_MISMATCHThe bytes on disk don't match what you uploaded — usually a streaming/proxy issue. Retry.

readFile(workspaceId, filePath)

Read a file from the workspace as an ArrayBuffer. Throws on non-2xx responses (including the standard error envelope).

const buf = await client.readFile('ws_a1b2c3d4', '/workspace/result.json')
const text = new TextDecoder().decode(buf)

Auth helpers

getAuthHeaders()

Headers the SDK would attach to a regular HTTP request — Authorization, X-Org-Id, X-Client-Name, X-Client-Version. Use this when opening raw WebSocket / fetch connections (terminal, proxy) so the upgrade handshake authenticates correctly:

const ws = new WebSocket(client.getTerminalUrl(id), {
headers: client.getAuthHeaders(),
})

Error handling

Errors thrown by the SDK are either NanoTermError (HTTP envelope from the server) or TransientNetworkError (network / non-JSON 5xx). Both carry agent-actionable fields:

FieldWhen setWhat it means
codeNanoTermErrorStable error identifier (e.g. WORKSPACE_NOT_FOUND)
retryablebothtrue if the same call may succeed later
requestSentbothfalse = server never saw the request (safe to retry non-idempotent ops); true = effects may have been applied
attemptNanoTermErrorRetry attempt that produced this error (1-based)
statusbothHTTP status if applicable
retryAfterSecNanoTermError 429Seconds to wait before retry

The CLI's --json mode emits exactly this shape on stderr so an agent can decide how to recover without parsing prose.


Types

All types are available from @nanoterm/types:

import type {
WorkspaceInfo,
WorkspaceStatus,
CreateWorkspaceRequest,
ExecRequest,
ExecResult,
TerminalInputMessage,
TerminalOutputMessage,
OrgInfo,
ApiKeyInfo,
} from '@nanoterm/types'