SDK

SDK

Install

bun add @indexable/sdk

Each package reads IX_TOKEN, then the file that ix login writes.

Example

import { Client, Machine } from "@indexable/sdk"

// A machine for this agent, removed
// when this scope ends.
await using machine = await Machine.create({
  image: "ix/debian:12",
  name: "agent-7",
  lifetime: "ephemeral",
})
await machine.writeFile("/task.md", prompt)
const run = await machine.exec(["./agent", "/task.md"], { check: true })
console.log(run.stdout)

// Every restore is a new machine.
const snap = await machine.snapshot()
const snapshots = new Client().snapshots
const forks = await Promise.all(
  ["a", "b", "c"].map((n) =>
    snapshots.restore(snap.snapshotId, `agent-7-${n}`),
  ),
)

Ephemeral and persistent

A machine is persistent by default. lifetime: "ephemeral" removes it when the scope ends. A ttl makes the platform remove it at the deadline. Leaving the scope of a persistent machine never removes it.

const ix = new Client()

// Ephemeral: removed when the scope ends.
await using scratch = await ix.machines.create({ lifetime: "ephemeral" })

// A deadline: the platform removes it after 20 minutes,
// even if this process dies first.
const ci = await ix.machines.create({
  ttl: "20m",
  name: "ci-run-418",
})

// Persistent, the default: scope exit only releases the handle.
await using dev = await ix.machines.create({ name: "dev" })
const again = await ix.machines.connect("dev")

Live output

machine.exec starts the command and returns a Process at once. Await it for the result, or read stdout and stderr while it runs. Dropping the Process stops the command. The SDK reference lists every member.

// exec returns at once. Read the output while it runs.
const proc = machine.exec(["npm", "test"])
for await (const chunk of proc.stdout) process.stdout.write(chunk)
const { exitCode } = await proc

// Write to stdin.
const sort = machine.exec(["sort"], { stdin: true })
await sort.stdin.write("b\na\n")
await sort.stdin.close()
console.log((await sort).stdout)

// Stop a command.
const slow = machine.exec(["sleep", "600"])
setTimeout(() => slow.kill(), 5000)
await slow

Reach a machine

In TypeScript, Machine has static functions that need no client. Each one uses your ix login or IX_TOKEN.

FunctionWhat it does
Machine.create(options)Boot a machine. image picks the OCI image it boots and lifetime how long it lives. The machine answers when the call returns.
Machine.attach(nameOrId)Adopt a machine that exists. Releasing the handle does not remove it.
Machine.list()List the machines in your account.

Every method on a machine

MethodWhat it does
exec, shellRun a command given as an argument list, or a script. Both return a Process at once. Await it for the result. A non-zero exit is a result unless you pass check: true.
spawnStart a command that outlives the call. It returns the guest pid.
readFile, writeFile, listDirMove files in and out.
snapshot, waitSnapshotReady, forkCapture the disk, wait until the snapshot can be restored, or do both and restore into a new machine.
start, stop, restart, rename, deleteLifecycle.
logs, tailLogs, watchRecorded output, live output, and status changes.
forwardPort, connectPortA local port that forwards into the machine, or a byte stream to a port inside it.

Account namespaces

Everything that belongs to the account and not to one machine hangs off new Client() in TypeScript or ix_sdk.Client() in Python, as properties. Rust calls them as methods, for example ix.machines(). Python calls are asynchronous.

CallWhat it does
client.machines.create(options)Boot a machine from an image or from a snapshot.
client.snapshots.restore(id, name)Restore a snapshot into a new machine.
client.keys.create(name, limitUsd)Create an API key with a spending ceiling.
client.secrets.set(name, value)Store a secret.
client.groups.create(slug), addMemberCreate a private network group and add machines to it.

Python uses the same names in snake case. Rust names follow the crate's conventions.

esc
  • Loading