@arker-ai/sdk
v0.6.3
Published
TypeScript SDK for the Arker virtual computer platform — spawn sandboxed VMs, run code, sync files.
Readme
Arker TypeScript SDK
A small, typed wrapper around the Arker VM API: fork a machine, run commands, sync files.
Install
npm install @arker-ai/sdkNode 18+. The client reads your key from ARKER_API_KEY — get one in the console.
Quickstart
import { Arker } from "@arker-ai/sdk";
const ar = new Arker({ region: "us-west-2" });
// Fork a public golden, run a command, read/write a file.
const vm = await ar.fork("ubuntu-full"); // public golden — org inferred
const run = await vm.run("python3 -c 'print(2 + 2)'");
if (run.type === "completed") console.log(new TextDecoder().decode(run.stdout));
await vm.sync("/tmp/data.txt", "hello\n"); // write
const data = await vm.sync("/tmp/data.txt"); // read -> Uint8Array
await vm.delete();Interactive PTY
arker shell opens a native PTY session over WebSocket. It does not use SSH and
does not call /runs for each line:
arker shell vm_123
arker shell vm_123 --session-id sess_123The SDK exposes the same transport:
const pty = await vm.connectPty({ cols: 120, rows: 32 });
pty.onData((chunk) => process.stdout.write(chunk));
await pty.ready;
pty.send("echo hello\n");
pty.resize(100, 30);
pty.close(); // detach; the session is not deletedCore API
const ar = new Arker({ region, apiKey?, baseUrl?, retry? });
// VMs
await ar.fork("ubuntu-full"); // public golden by name (org inferred)
await ar.fork(vm, { name: "child" }); // an existing VM (uses its id)
await ar.fork({ sourceVmName, sourceOrgId, name?, durable? });
await ar.listVms({ state? });
ar.vm(vmId); // bare handle
await ar.vm(vmId).run(command, options?);
await ar.vm(vmId).connectPty({ sessionId?, cols?, rows?, command?, persist? });
await ar.vm(vmId).resize({ vcpu_count, memory_mib });
await ar.vm(vmId).delete();
// Files inside a VM
await vm.sync(path); // read -> Uint8Array
await vm.sync(path, data); // write
// Filesystems — standalone, persistent volumes
await ar.createFilesystem({ name });
await ar.listFilesystems();
await ar.deleteFilesystem(filesystemId);
// Syncs — mount a filesystem into a VM at a path
await vm.createSync({ filesystemId, path });
await vm.listSyncs();
await vm.deleteSync(syncId);apiKey falls back to ARKER_API_KEY; region to ARKER_REGION. Pass baseUrl for dev targets. Configure retries with retry: { attempts, baseDelayMs, maxDelayMs }, or retry: false to disable.
Interactive terminal (PTY)
Open a real pseudo-terminal in a VM and drive it interactively — stream raw
terminal bytes out, send keystrokes in (incl. control chars like Ctrl-C),
resize, and kill. isatty() is true inside, so an interactive shell, vim,
htop, a language REPL, and claude all work. Transport is a TLS WebSocket;
a key can only attach to its own org's VMs.
const vm = await ar.fork("ubuntu-full");
const pty = await vm.createPty({
cols: 80,
rows: 24,
// command defaults to the login shell. It is a single executable path —
// the guest does not shell-split, so launch a shell and `sendInput` it.
onData: (bytes) => process.stdout.write(bytes), // raw output (ANSI/colors)
});
await pty.sendInput(new TextEncoder().encode("ls -la\n"));
await pty.resize({ cols: 120, rows: 40 }); // a full-screen app reflows
await pty.kill(); // tears down the shellWire it into xterm.js in a browser (term.onData → pty.sendInput,
pty onData → term.write, term.onResize → pty.resize), or pipe it to a local
TTY in a script. Node needs the optional ws package (installed by default).
The CLI exposes the same thing — arker pty <vm> (and arker shell on a TTY)
drop you into a live terminal you can run claude in:
arker pty <vm_id> # login shell in a fresh/!existing VM
arker pty --command /usr/bin/htop # launch a program directlyDurability
For long-running or non-idempotent work, fork with durable: true and pass an idempotency key when retrying a run:
const vm = await ar.fork("ubuntu-full", { durable: true });
await vm.run("python3 train.py", { background: true, idempotencyKey: crypto.randomUUID() });If the host fails mid-run, the run resumes on a healthy host with the VM's filesystem state preserved. Backends without durability return ArkerError code unsupported_operation.
Compatibility imports
The SDK includes limited compatibility layers for common Daytona, E2B, and Modal sandbox workflows. These entrypoints keep the original SDK-shaped calls, route through ComputeSDK, use Arker as the first provider, and fall back to the original provider when resolving an existing non-Arker sandbox ID.
For the supported surface below, migration is a one-line import change:
| SDK | Replace | With |
| --- | --- | --- |
| Daytona | import { Daytona } from "@daytonaio/sdk"; | import { Daytona } from "@arker-ai/sdk/daytona"; |
| E2B | import { Sandbox } from "e2b"; | import { Sandbox } from "@arker-ai/sdk/e2b"; |
| Modal | import { ModalClient } from "modal"; | import { ModalClient } from "@arker-ai/sdk/modal"; |
Daytona
import { Daytona } from "@arker-ai/sdk/daytona";
const daytona = new Daytona({ apiKey: process.env.DAYTONA_API_KEY });
const sandbox = await daytona.create();
const result = await sandbox.process.exec("echo hello");
console.log(result.result);
await daytona.delete(sandbox);Supported Daytona surface:
new Daytona({ apiKey?, arker? })daytona.create()daytona.get(id)daytona.delete(idOrSandbox)sandbox.idsandbox.process.exec(command)sandbox.process.executeCommand(command)sandbox.delete()
E2B
import { Sandbox } from "@arker-ai/sdk/e2b";
const sandbox = await Sandbox.create();
const result = await sandbox.commands.run("echo hello");
console.log(result.stdout);
await sandbox.kill();Supported E2B surface:
Sandbox.create()Sandbox.create(templateId, { timeoutMs? })Sandbox.connect(id)sandbox.sandboxIdsandbox.commands.run(command)sandbox.files.read/write/makeDir/list/exists/removesandbox.kill()
Modal
import { ModalClient } from "@arker-ai/sdk/modal";
const client = new ModalClient({
tokenId: process.env.MODAL_TOKEN_ID,
tokenSecret: process.env.MODAL_TOKEN_SECRET,
});
const sandbox = await client.sandboxes.create();
const proc = await sandbox.exec(["sh", "-c", "echo hello"]);
console.log(await proc.stdout.readText());
await sandbox.terminate();Supported Modal surface:
new ModalClient({ tokenId?, tokenSecret?, arker? })client.sandboxes.create()client.sandboxes.fromId(id)sandbox.sandboxIdsandbox.exec(commandOrArgv, { workdir?, env?, timeoutMs?, stdout?: "pipe", stderr?: "pipe", mode?: "text" })process.stdout.readText()process.stderr.readText()process.wait()sandbox.terminate()
Unsupported provider-specific methods and options throw explicit errors instead of being silently ignored. Arker credentials come from ARKER_API_KEY and optional ARKER_REGION / ARKER_BASE_URL; original provider credentials are only used for fallback.
Compatibility test commands:
npm run test:compat
ARKER_API_KEY=... npm run test:compat-live
ARKER_API_KEY=... DAYTONA_API_KEY=... E2B_API_KEY=... MODAL_TOKEN_ID=... MODAL_TOKEN_SECRET=... npm run test:compat-fallback-liveLicense
Apache-2.0
