Skip to content

TypeScript SDK

The TypeScript SDK gives Node programs and agent frameworks the Wisps API. It needs Node 20 or later and depends on ws, since a browser-style WebSocket cannot send the Authorization header. Browsers are not supported. Field names follow the API (snake case), and its types are generated from the API’s OpenAPI document. See the API reference for request and response fields../api/wisps.md).

  1. Install the SDK artifact supplied by your provider. The SDK is not published on npm. Replace the path with the package you received.

    Terminal window
    npm install /path/to/wisps-0.1.0.tgz
  2. Create a sandbox, use it and delete it. Without a name or mode you get an ephemeral wisp with a generated name.

    import { Client } from "wisps";
    const c = new Client(process.env.WISPS_URL!, process.env.WISPS_TOKEN!);
    const w = await c.create({ labels: { agent_run: "r-123" } });
    await w.writeFile("app/main.py", "print(sum(range(10)))\n", { mkdirs: true });
    const res = await w.run({ cmd: ["python3", "main.py"], dir: "app" });
    console.log(res.exit_code, res.stdout);
    await w.delete();

The program prints 0 45.

Task Call
Create an ephemeral sandbox c.create(); options: labels, idle_timeout_seconds, max_lifetime_seconds, delete_after_stopped_seconds, region, near, size, no_start
Create a workspace that persists (person token) c.create({ name: "dev", mode: MODE_ON_DEMAND })
Run to completion w.run({ cmd: [...] }) or w.run({ script }); env, dir (relative to home), stdin, timeout_seconds, encoding: "base64" for bytes
Output as it arrives w.runStream({ script }, (stream, data) => ...)
Interactive or long command const s = await w.exec(["sh"], { tty: true }), then s.send(), s.closeStdin(), for await (const ev of s), or await s.wait()
Background work w.exec(cmd, { detachable: true }), later w.sessions(), w.attach(id), w.holdSession(id), w.killSession(id)
Files w.writeFile, w.readFile (bytes), w.readText, w.listDir (home by default), w.mkdir, w.remove
Find wisps c.list({ labels: { agent_run: "r-123" } })
Change mode, clocks or labels w.update({...}); w.keep() switches an ephemeral wisp to on-demand
Checkpoints w.checkpoint("note", { key }), w.listCheckpoints(), w.restore(id), w.operation(key); resend with the same key after a disconnect

w.info holds what the API last said about the wisp (status, mode, labels, lifetime_ends_at, delete_at). run is not cut off by the client’s request timeout; the command’s own timeout_seconds bounds it.

Every error is a WispsError. API refusals are APIError subclasses with status and, for 410, reason:

Class When
NotFound No such wisp, or another team’s
Gone Deleted automatically; create a new one
Conflict The name is taken, or an ephemeral wisp’s lifetime is over (keep() to use it again)
Forbidden A quota, or an API token asking for on-demand or always
BadRequest Invalid options, or a program that does not exist
Unavailable No capacity, or the node is unreachable

A command that fails is not an error: check exit_code, timed_out and stdout_truncated on the result.