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 memory and disk (restorable when the call returns), wait on a snapshot by id, or snapshot 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