TypeScript SDK
One package, promigence, the CLI and the SDK together, with two surfaces: a native client, and a compatibility Sandbox class with the familiar sandbox calls, so most existing code keeps working after a one-line import change.
npm i ./promigence-<version>.tgz # the file we sent you; not on npm yet
Language: bashThe native client
Three calls cover a run: quote, fan out, read the report.
import { PromigenceClient } from "promigence";
const client = new PromigenceClient(); // reads PROMIGENCE_API_KEY, or the key signup stored
// 1, know the ceiling first (the snapshot from the quickstart)
const quote = await client.runs.quote({ snapshot: "myapp", count: 20 });
console.log(quote.max_cost); // 0.828: 20 small sandboxes for their whole 15 minutes
// 2, fan out under a hard cap
const run = await client.runs.start({
snapshot: "myapp",
count: 20,
spendCap: 1,
verifyEach: true,
exec: { sh: "cd /work/repo && npm test" },
});
// 3, the number about your environments
await client.runs.waitDone(run.run_id);
const report = await client.runs.report(run.run_id);
console.log(report.totals.env_verified);
console.log(report.totals.env_failed); // ← ours
console.log(report.totals.task_failed); // ← yours
console.log(report.cost?.charged); // what was billed, never more than the cap
Language: typescriptDrop-in compatibility
Reimplemented from published typings. No third-party source, nothing proxied: migrating should be an import change, not a project.
// Familiar sandbox calls keep working: create, commands.run, files and kill
// behave as you expect, so a migration is one import line and an API key,
// not a rewrite.
import { Sandbox } from "promigence";
// A snapshot, not a template: the environment was built and verified once, and
// this is that exact filesystem. `spendCap` sets the ceiling; without it your
// organisation's default applies.
const sandbox = await Sandbox.create("myapp", { spendCap: 1 });
await sandbox.files.write("/tmp/patch.diff", patch);
const res = await sandbox.commands.run("cd /work/repo && git apply /tmp/patch.diff && npm test");
console.log(res.exitCode, res.stdout);
// Still verified? The same integrity probe we use to decide whether a failure
// was ours or yours, asked before you spend the episode, not after.
const { ok, first_bad_path } = await sandbox.probe();
await sandbox.kill(); // the clock stops here: billing is per second
Language: typescriptWhere a method would hand you an unverified box, the SDK throws and names the alternative rather than quietly doing something else.
Errors carry a fix
Every error has a stable code, an HTTP status, an exit code and a fix string. An error an agent cannot act on becomes a retry loop.
import { PromigenceError } from "promigence";
try {
await client.runs.start({ snapshot: "myapp", count: 20, spendCap: 0.5 });
} catch (e) {
if (e instanceof PromigenceError) {
console.error(e.code); // "cap_exceeded"
console.error(e.message); // "quote $0.83 exceeds remaining spend cap $0.50"
console.error(e.fix); // every error carries a fix, it is not optional
console.error(e.exitCode); // 5
}
}
Language: typescript