@promigence/sdk 0.1.0-beta.5 → 0.1.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -253,6 +253,11 @@ export interface Totals {
253
253
  grader_failed?: number;
254
254
  snapshot_failed: number;
255
255
  aborted_by_client: number;
256
+ /**
257
+ * Sandboxes Promigence support stopped (outcome `stopped_by_support`), each with its reason in its
258
+ * failure's message. Never counted as your own kill (`aborted_by_client`). Absent from an older API.
259
+ */
260
+ stopped_by_support?: number;
256
261
  expired: number;
257
262
  cap_killed: number;
258
263
  not_started: number;
@@ -349,7 +354,9 @@ export interface SecretRecord {
349
354
  name: string;
350
355
  /** Length of the stored value, so a caller can sanity-check without reading it. */
351
356
  bytes: number;
357
+ /** On `secrets.set` only: `false` when an existing secret of that name was overwritten. */
352
358
  created?: boolean;
359
+ /** On `secrets.list` only. */
353
360
  created_at?: string;
354
361
  updated_at?: string;
355
362
  /**
@@ -370,7 +377,7 @@ export interface SecretRecord {
370
377
  * `api.anthropic.com`. Absent when there is none. A variable the caller sets itself wins.
371
378
  */
372
379
  env?: Record<string, string>;
373
- /** The request headers the secret authenticates (present when `hosts` is). */
380
+ /** The request headers the secret authenticates: on `secrets.list`, when `hosts` is present. */
374
381
  headers?: string[];
375
382
  /** What the variable in a sandbox that names it holds instead of the value (on `set` of a bound secret). */
376
383
  placeholder?: string;
@@ -467,13 +474,17 @@ export interface SnapshotRecord {
467
474
  source?: SnapshotSource;
468
475
  /** `false` for an image snapshot built without `verify` — stored, but nothing was verified. */
469
476
  verified?: boolean;
470
- start?: string | string[];
471
477
  created_at?: string;
472
478
  /**
473
479
  * Set when the snapshot can no longer be started, saying why (it was lost before it could be
474
480
  * kept); a fork of it is refused `snapshot_unavailable`. Absent while it can be started.
475
481
  */
476
482
  unavailable?: string;
483
+ /**
484
+ * The user commands, terminals and SSH sessions in its sandboxes run as when they name none:
485
+ * its image's own `USER`, or the `user` it was created with. Absent: root.
486
+ */
487
+ user?: string | null;
477
488
  [extra: string]: unknown;
478
489
  }
479
490
  /** `GET /snapshots/:id/manifest`: the snapshot's manifest, sealed (`buildBundle` turns it into a `.promigence` bundle). */
@@ -510,8 +521,6 @@ export interface ExecResponse {
510
521
  stdout: string;
511
522
  stderr: string;
512
523
  truncated?: boolean;
513
- stdout_ref?: string;
514
- stderr_ref?: string;
515
524
  /** True when the command was killed at its own timeout. */
516
525
  timed_out?: boolean;
517
526
  /** True when the command was killed for running out of memory. */
@@ -539,7 +548,13 @@ export interface SandboxRecord extends QueuedInfo {
539
548
  */
540
549
  pause_reason?: "spend_cap" | "credits_exhausted";
541
550
  run_id?: string;
551
+ /**
552
+ * The snapshot it was started from, as its run names it (`runs.get(runId).snapshot_id`): the
553
+ * snapshot's id, or `promigence/base` for the built-in base.
554
+ */
542
555
  snapshot_id?: string;
556
+ /** When the sandbox was created, RFC 3339. */
557
+ created_at?: string;
543
558
  outcome?: Outcome;
544
559
  /**
545
560
  * The labels this sandbox inherited from its run — the same map `runs.start({ labels })` took.
@@ -627,7 +642,7 @@ export interface Page<T> {
627
642
  /** What the server actually used, which is not always what was asked for (it caps at 500). */
628
643
  limit: number;
629
644
  }
630
- /** `GET /health` — unauthenticated: `ok`, `version`, `build_sha` and `serving`. */
645
+ /** `GET /health` — unauthenticated: `ok`, `version`, `serving` and `location`; `build_sha` to a caller with a key. */
631
646
  export interface HealthResponse {
632
647
  ok?: boolean;
633
648
  version?: string;
@@ -1184,9 +1199,15 @@ export interface SearchOpts {
1184
1199
  * `POST /sandboxes/:id/files/archive` — where the tar landed **inside the sandbox** and how big
1185
1200
  * it is. The bytes stay in the sandbox on purpose; see `files.downloadDir`.
1186
1201
  */
1187
- export interface ArchiveResult extends FileInfo {
1202
+ export interface ArchiveResult {
1203
+ sandbox_id: string;
1204
+ /** Where the tar landed inside the sandbox. */
1188
1205
  archive_path: string;
1206
+ /** Its size in bytes. */
1207
+ size: number;
1208
+ /** The directory that was packed. */
1189
1209
  source: string;
1210
+ served_by?: ServedBy;
1190
1211
  }
1191
1212
  /** `POST /sandboxes/:id/files/extract`. `entries` is `null` on the fallback path, which cannot
1192
1213
  * count them without a second pass — a number invented there would be worse than none. */
@@ -1312,8 +1333,6 @@ export interface MeResponse {
1312
1333
  email?: string;
1313
1334
  provider?: string;
1314
1335
  } | null;
1315
- /** @deprecated Never sent: the key is `key.key_id`. */
1316
- key_id?: string;
1317
1336
  /**
1318
1337
  * The plan the organisation's limits and prices come from: `hobby` for a trial organisation
1319
1338
  * with no plan assigned. The name `tier_over_plan` and the other refusals use.
@@ -1349,7 +1368,6 @@ export interface MeResponse {
1349
1368
  /** The cap a run gets when it names none, in USD; `0` names none (the run is capped at its quote). */
1350
1369
  default_spend_cap?: number;
1351
1370
  card_on_file?: boolean;
1352
- default_class?: string;
1353
1371
  /** The free trial: credits, data out and the limits that apply until a card is on file. */
1354
1372
  trial?: TrialStatus;
1355
1373
  /** Something about the account to act on (the card-required switch), or null. */
@@ -1661,6 +1679,18 @@ export interface SnapshotCreateOpts {
1661
1679
  tier?: Tier;
1662
1680
  /** Image forms: the name of a stored secret holding the registry credentials. */
1663
1681
  registrySecret?: string;
1682
+ /**
1683
+ * Image and Dockerfile forms: the user commands, terminals and SSH sessions in its sandboxes
1684
+ * run as when they name none. A user name or uid, with an optional `:group` or `:gid`, as an
1685
+ * image's `USER` takes it; `root` for root. Default: the image's own `USER`, as `docker run`
1686
+ * does (root when it sets none).
1687
+ */
1688
+ user?: string;
1689
+ /**
1690
+ * Image and Dockerfile forms: the user its `start`, `setup` and `verify` run as (for example
1691
+ * the snapshot's own `user`). Default: `root`.
1692
+ */
1693
+ setupUser?: string;
1664
1694
  alias?: string;
1665
1695
  /**
1666
1696
  * A DEFAULT grader for this snapshot: a run on it with no `grade` of its own uses this one.
@@ -1951,12 +1981,15 @@ export declare class PromigenceClient {
1951
1981
  timeoutMs?: number;
1952
1982
  }): Promise<StatusResponse>;
1953
1983
  /**
1954
- * `GET /pricing` — the price list, live: the per-second rates, every sandbox shape on sale and
1955
- * its price, the build price, the limits, the free credit and the plan they come from, read on
1956
- * every call from what billing charges. With a key: what this key's organization pays. Without
1957
- * one, or with `newAccount: true` (no key is sent): what a new account gets.
1984
+ * `GET /pricing` — your organization's price list, live: the per-second rates, every sandbox
1985
+ * shape on sale and its price, the build price, the limits, the free credit and the plan they
1986
+ * come from, read on every call from what billing charges. Needs a key: without one this throws
1987
+ * `AuthError` and sends nothing. The published list prices are on the pricing page.
1958
1988
  *
1959
1989
  * Prices change: read them when you need them rather than keeping a copy.
1990
+ *
1991
+ * @param opts.newAccount No longer supported: there is no key-less price list. Passing `true`
1992
+ * throws `InvalidArgumentError` and sends nothing.
1960
1993
  */
1961
1994
  pricing(opts?: {
1962
1995
  newAccount?: boolean;
@@ -2475,6 +2508,12 @@ export declare class PromigenceClient {
2475
2508
  /**
2476
2509
  * `POST /sandboxes/:id/probe` — is this environment still the one that was verified?
2477
2510
  *
2511
+ * Compares the sandbox's files under the paths its snapshot's verification recorded with what
2512
+ * was recorded. It is the same check a failed run gets, which decides whether the failure was
2513
+ * the environment's (`integrity_mismatch`, not billed) or a path the snapshot's own `verify`
2514
+ * never covered (`verify_did_not_cover`, billed). Asking for it up front is cheaper than
2515
+ * spending a run to find out.
2516
+ *
2478
2517
  * `first_bad_path` names the **first differing path**, which is the only part of a failed
2479
2518
  * probe anyone can act on.
2480
2519
  */
@@ -2485,8 +2524,7 @@ export declare class PromigenceClient {
2485
2524
  ok: boolean;
2486
2525
  ms: number;
2487
2526
  checked: string | string[];
2488
- first_bad_path: string | null;
2489
- probe: Record<string, unknown>;
2527
+ first_bad_path?: string | null;
2490
2528
  }>;
2491
2529
  /** `DELETE /sandboxes/:id`. */
2492
2530
  kill: (sandboxId: string) => Promise<unknown>;
@@ -2623,9 +2661,11 @@ export declare class PromigenceClient {
2623
2661
  /**
2624
2662
  * Write a file of **any size**, in [`FILE_PUT_CHUNK_BYTES`] requests.
2625
2663
  *
2626
- * A failure mid-loop leaves a **prefix** in the sandbox, not a corrupt file, and the
2627
- * thrown error carries how far it got (`written` in the error's evidence).
2628
- * Re-running the same `put` is safe: chunk 0 truncates.
2664
+ * Chunk 0 goes without `?offset=`, which means "this is the file" (create, truncate);
2665
+ * every later chunk carries one, which appends, so a file has no size limit here.
2666
+ *
2667
+ * A failure mid-loop leaves a **prefix** in the sandbox, not a corrupt file. Re-running the
2668
+ * same `put` is safe: chunk 0 truncates.
2629
2669
  *
2630
2670
  * `mode` (`0o755`) sets the file's permission bits. Without it a file that already exists
2631
2671
  * keeps its mode, as a shell `>` does, and a new one is `0o644`.
@@ -2735,8 +2775,7 @@ export declare class PromigenceClient {
2735
2775
  ok: boolean;
2736
2776
  ms: number;
2737
2777
  checked: string | string[];
2738
- first_bad_path: string | null;
2739
- probe: Record<string, unknown>;
2778
+ first_bad_path?: string | null;
2740
2779
  }>;
2741
2780
  };
2742
2781
  /**
@@ -2883,15 +2922,14 @@ export declare class PromigenceClient {
2883
2922
  manifest: (idOrAlias: string) => Promise<SnapshotManifestResponse>;
2884
2923
  /**
2885
2924
  * The `.promigence` bundle for a snapshot: the sealed form, which names exactly one
2886
- * environment and verifies offline (`verifyBundle`). `served_by` says whether the API wrote it
2887
- * (`control`) or this client composed it from the snapshot's manifest (`client`); both are the
2888
- * same document.
2925
+ * environment and verifies offline (`verifyBundle`). This client composes it from the
2926
+ * snapshot's sealed manifest (`served_by: "client"`); the API serves no bundle of its own.
2889
2927
  */
2890
2928
  bundle: (idOrAlias: string, opts?: {
2891
2929
  alias?: string;
2892
2930
  }) => Promise<{
2893
2931
  bundle: Bundle;
2894
- served_by: "control" | "client";
2932
+ served_by: "client";
2895
2933
  record?: SnapshotRecord;
2896
2934
  }>;
2897
2935
  };
@@ -540,6 +540,12 @@ function snapshotCreateBody(opts) {
540
540
  if (o.env !== undefined && Object.keys(o.env).length && forms[0] === "repo") {
541
541
  throw new core_js_1.InvalidArgumentError("snapshots.create env is for an image or a Dockerfile", "A repository snapshot sets its environment in its setup steps, or pass env on each command.");
542
542
  }
543
+ if ((o.user !== undefined || o.setupUser !== undefined) && forms[0] === "repo") {
544
+ throw new core_js_1.InvalidArgumentError("snapshots.create user and setupUser are for an image or a Dockerfile", "A repository snapshot runs as root; name a user on each command instead ({ user: 'app' }).");
545
+ }
546
+ if (o.setupUser !== undefined && o.user === undefined) {
547
+ throw new core_js_1.InvalidArgumentError("snapshots.create setupUser needs user", "Pass { user: 'app', setupUser: 'app' }: start, setup and verify as app too (without setupUser they run as root), every later command as app.");
548
+ }
543
549
  if (!github && (o.ref !== undefined || o.from !== undefined || o.track)) {
544
550
  throw new core_js_1.InvalidArgumentError("snapshots.create ref, from and track are for a connected repository (repo: 'github:owner/name' or 'gitlab:group/project')", "Pass { repo: 'github:your-org/app', ref: 'main' }, or drop them for this form.");
545
551
  }
@@ -549,7 +555,7 @@ function snapshotCreateBody(opts) {
549
555
  setup: o.setup, verify, tier: o.tier, alias: o.alias,
550
556
  others: [
551
557
  ["sources", Boolean(o.sources?.length)], ["start", o.start !== undefined],
552
- ["registrySecret", Boolean(o.registrySecret)], ["cpuClass", Boolean(o.cpuClass)],
558
+ ["registrySecret", Boolean(o.registrySecret)],
553
559
  ],
554
560
  });
555
561
  }
@@ -597,11 +603,13 @@ function snapshotCreateBody(opts) {
597
603
  body["registry_secret"] = o.registrySecret;
598
604
  if (o.env !== undefined && Object.keys(o.env).length)
599
605
  body["env"] = { ...o.env };
606
+ if (o.user !== undefined)
607
+ body["user"] = o.user;
608
+ if (o.setupUser !== undefined)
609
+ body["setup_user"] = o.setupUser;
600
610
  }
601
611
  if (o.alias)
602
612
  body["alias"] = o.alias;
603
- if (o.cpuClass)
604
- body["cpu_class"] = o.cpuClass;
605
613
  if (o.grade) {
606
614
  if (!o.grade.cmd)
607
615
  throw new core_js_1.InvalidArgumentError("snapshots.create grade needs a cmd", "Pass { grade: { cmd: 'pytest -q tests/' } }.");
@@ -1240,12 +1248,14 @@ class PromigenceClient {
1240
1248
  ...(opts.timeoutMs === undefined ? {} : { timeoutMs: opts.timeoutMs }),
1241
1249
  });
1242
1250
  }
1243
- pricing(opts = {}) {
1244
- const anonymous = opts.newAccount === true || !this.authenticated;
1251
+ async pricing(opts = {}) {
1252
+ if (opts.newAccount === true) {
1253
+ throw new core_js_1.InvalidArgumentError("pricing({ newAccount: true }) is no longer supported: the price list needs a key", "Call pricing() with your API key for your organization's prices. The published list prices are on the pricing page, and `promigence pricing` prints them without a key.");
1254
+ }
1255
+ this.requireKey();
1245
1256
  return this.request({
1246
1257
  method: "GET",
1247
1258
  path: "/pricing",
1248
- ...(anonymous ? { anonymous: true } : {}),
1249
1259
  ...(opts.signal ? { signal: opts.signal } : {}),
1250
1260
  ...(opts.timeoutMs === undefined ? {} : { timeoutMs: opts.timeoutMs }),
1251
1261
  });
@@ -2447,19 +2457,6 @@ class PromigenceClient {
2447
2457
  },
2448
2458
  manifest: (idOrAlias) => this.request({ method: "GET", path: `/snapshots/${seg(idOrAlias)}/manifest` }),
2449
2459
  bundle: async (idOrAlias, opts = {}) => {
2450
- try {
2451
- const r = await this.request({
2452
- method: "GET", path: `/snapshots/${seg(idOrAlias)}/bundle`,
2453
- });
2454
- if (r?.bundle?.schema === bundle_js_1.BUNDLE_SCHEMA) {
2455
- return { bundle: r.bundle, served_by: "control" };
2456
- }
2457
- }
2458
- catch (e) {
2459
- const code = e?.code;
2460
- if (code !== "not_found" && code !== "enoent" && e?.status !== 405)
2461
- throw e;
2462
- }
2463
2460
  const [record, m] = await Promise.all([
2464
2461
  this.snapshots.get(idOrAlias).catch(() => undefined),
2465
2462
  this.snapshots.manifest(idOrAlias),
@@ -1,5 +1,5 @@
1
1
  /** Promigence SDK — constants, error hierarchy and shared types. */
2
- export declare const VERSION = "0.1.0-beta.5";
2
+ export declare const VERSION = "0.1.0-beta.7";
3
3
  export declare const DOCS_URL = "https://promigence.ai";
4
4
  /** The error reference; each code is an anchor on it (the API's `docs` link has the same form). */
5
5
  export declare const ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
package/dist/cjs/core.js CHANGED
@@ -7,7 +7,7 @@ exports.fixBeyondMessage = fixBeyondMessage;
7
7
  exports.previewsOff = previewsOff;
8
8
  exports.exitForCode = exitForCode;
9
9
  const hint_js_1 = require("./hint.js");
10
- exports.VERSION = "0.1.0-beta.5";
10
+ exports.VERSION = "0.1.0-beta.7";
11
11
  exports.DOCS_URL = "https://promigence.ai";
12
12
  exports.ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
13
13
  exports.DEFAULT_API_URL = "https://api.promigence.ai/v1";
@@ -249,7 +249,7 @@ exports.EXIT_BY_CODE = {
249
249
  nonzero_exit: 4, grade_not_passed: 4, task_timeout: 4, oom_over_tier: 4, disk_over_tier: 4,
250
250
  shutdown_by_task: 4, modified_by_task: 4,
251
251
  cap_exceeded: 5, cap_killed: 12, cap_paused: 5, credits_paused: 5,
252
- aborted_by_client: 130, expired: 12, not_started: 12, superseded: 0,
252
+ aborted_by_client: 130, stopped_by_support: 12, expired: 12, not_started: 12, superseded: 0,
253
253
  usage: 2, internal: 1, auth: 7, not_found: 8, unreachable: 9, rate_limited: 10,
254
254
  timeout: 11, not_verified: 6, enoent: 8,
255
255
  snapshot_incompatible: 6, storage_limit_reached: 5,
@@ -274,6 +274,8 @@ exports.EXIT_BY_CODE = {
274
274
  invite_invalid: 7,
275
275
  invite_required: 7,
276
276
  key_expired: 7,
277
+ account_blocked: 7,
278
+ setup_required: 7,
277
279
  };
278
280
  function exitForCode(code) {
279
281
  return Object.prototype.hasOwnProperty.call(exports.EXIT_BY_CODE, code) ? exports.EXIT_BY_CODE[code] : 1;
@@ -72,7 +72,9 @@ export interface AuthConfig {
72
72
  */
73
73
  console_authorize_url?: string | null;
74
74
  /**
75
- * The sign-in buttons to offer, as name -> label.
75
+ * The sign-in methods to offer, as name -> label (`email`, `google`, `github`). A client that
76
+ * asks the sign-in pages for one method passes the label as their `identity_provider`, except
77
+ * `email`, which is the pages' own form and needs none.
76
78
  */
77
79
  identity_providers: Record<string, string>;
78
80
  /**
@@ -81,7 +83,6 @@ export interface AuthConfig {
81
83
  * account signs in as before either way.
82
84
  */
83
85
  invite_mode?: boolean;
84
- issuer: string;
85
86
  /**
86
87
  * The redirect URIs a command-line client may listen on.
87
88
  */
@@ -90,10 +91,6 @@ export interface AuthConfig {
90
91
  * Where a headless sign-in lands instead, to show a code to paste back.
91
92
  */
92
93
  paste_redirect_uri: string;
93
- /**
94
- * Which identity service is in front of the API.
95
- */
96
- provider: string;
97
94
  scopes: Array<string>;
98
95
  token_url: string;
99
96
  }
@@ -660,7 +657,15 @@ export interface CaptureView {
660
657
  * The card on file, enough to recognise it.
661
658
  */
662
659
  export interface CardOnFile {
660
+ /**
661
+ * The card's brand (`visa`, `mastercard`, ...); for a card held in a wallet, the wallet
662
+ * (`apple_pay`, `google_pay`); for another payment method, its kind (`link`, `cashapp`,
663
+ * `amazon_pay`).
664
+ */
663
665
  brand?: string | null;
666
+ /**
667
+ * The card's last four digits; `null` for a payment method without a card number.
668
+ */
664
669
  last4?: string | null;
665
670
  }
666
671
  /**
@@ -825,7 +830,7 @@ export interface CliTokenCreate {
825
830
  /**
826
831
  * Every error and failure code the API returns.
827
832
  */
828
- export type Code = "fork_failed" | "start_timeout" | "readiness_failed" | "verify_too_slow" | "verify_each_failed" | "sandbox_unresponsive" | "oom_within_tier" | "disk_full_within_tier" | "sandbox_interrupted" | "integrity_mismatch" | "egress_unavailable" | "service_timeout" | "capacity_busy" | "verify_did_not_cover" | "nonzero_exit" | "task_timeout" | "oom_over_tier" | "disk_over_tier" | "cap_killed" | "cap_exceeded" | "aborted_by_client" | "expired" | "not_started" | "superseded" | "usage" | "internal" | "auth" | "not_found" | "unreachable" | "rate_limited" | "timeout" | "not_verified" | "enoent" | "trial_credits_exhausted" | "card_required" | "concurrency_limit" | "egress_cap_reached" | "network_policy_unsupported" | "network_host_refused" | "toolchain_mismatch" | "resume_unavailable" | "sandbox_unreachable" | "region_at_capacity" | "tier_unavailable" | "snapshot_unavailable" | "capacity_limit" | "build_timeout" | "builder_unavailable" | "builder_busy" | "overloaded" | "preempted" | "grader_failed" | "grade_not_passed" | "shutdown_by_task" | "modified_by_task" | "cap_paused" | "credits_paused" | "forbidden" | "snapshot_incompatible" | "storage_limit_reached" | "dockerfile_build_failed" | "image_unresolvable" | "registry_unavailable" | "credit_unclaimed" | "billing_unavailable" | "project_cap_reached" | "spend_limit_reached" | "tier_over_plan" | "secrets_in_snapshot" | "secrets_unavailable" | "secret_binding_unsupported" | "idempotency_key_reused" | "idempotency_key_in_use" | "repo_credentials_not_supported" | "account_exists" | "invite_invalid" | "invite_required" | "key_expired";
833
+ export type Code = "fork_failed" | "start_timeout" | "readiness_failed" | "verify_too_slow" | "verify_each_failed" | "sandbox_unresponsive" | "oom_within_tier" | "disk_full_within_tier" | "sandbox_interrupted" | "integrity_mismatch" | "egress_unavailable" | "service_timeout" | "capacity_busy" | "verify_did_not_cover" | "nonzero_exit" | "task_timeout" | "oom_over_tier" | "disk_over_tier" | "cap_killed" | "cap_exceeded" | "aborted_by_client" | "expired" | "not_started" | "superseded" | "usage" | "internal" | "auth" | "not_found" | "unreachable" | "rate_limited" | "timeout" | "not_verified" | "enoent" | "trial_credits_exhausted" | "card_required" | "concurrency_limit" | "egress_cap_reached" | "network_policy_unsupported" | "network_host_refused" | "toolchain_mismatch" | "resume_unavailable" | "sandbox_unreachable" | "region_at_capacity" | "tier_unavailable" | "snapshot_unavailable" | "capacity_limit" | "build_timeout" | "builder_unavailable" | "builder_busy" | "overloaded" | "preempted" | "grader_failed" | "grade_not_passed" | "shutdown_by_task" | "modified_by_task" | "cap_paused" | "credits_paused" | "stopped_by_support" | "forbidden" | "snapshot_incompatible" | "storage_limit_reached" | "dockerfile_build_failed" | "image_unresolvable" | "registry_unavailable" | "credit_unclaimed" | "billing_unavailable" | "project_cap_reached" | "spend_limit_reached" | "tier_over_plan" | "secrets_in_snapshot" | "secrets_unavailable" | "secret_binding_unsupported" | "idempotency_key_reused" | "idempotency_key_in_use" | "repo_credentials_not_supported" | "account_exists" | "invite_invalid" | "invite_required" | "key_expired" | "account_blocked" | "setup_required";
829
834
  export interface Concurrency {
830
835
  }
831
836
  /**
@@ -1070,7 +1075,7 @@ export interface ErrorBody {
1070
1075
  /**
1071
1076
  * Every error code the API returns. `category` decides whether the failure is billed: `env` is ours (not billed before hand-over; after it, the time since the sandbox's last snapshot is credited back, up to 24 hours, when our side shows the cause), `task` is your command and is billed. The full table is `x-promigence-error-codes` at the document root.
1072
1077
  */
1073
- export type ErrorCode = "fork_failed" | "start_timeout" | "readiness_failed" | "verify_too_slow" | "verify_each_failed" | "toolchain_mismatch" | "sandbox_unresponsive" | "oom_within_tier" | "disk_full_within_tier" | "sandbox_interrupted" | "integrity_mismatch" | "egress_unavailable" | "service_timeout" | "preempted" | "verify_did_not_cover" | "grader_failed" | "nonzero_exit" | "grade_not_passed" | "task_timeout" | "oom_over_tier" | "disk_over_tier" | "shutdown_by_task" | "modified_by_task" | "cap_killed" | "cap_exceeded" | "cap_paused" | "credits_paused" | "aborted_by_client" | "expired" | "not_started" | "superseded" | "usage" | "internal" | "auth" | "forbidden" | "not_found" | "unreachable" | "rate_limited" | "timeout" | "not_verified" | "snapshot_incompatible" | "storage_limit_reached" | "resume_unavailable" | "sandbox_unreachable" | "capacity_busy" | "region_at_capacity" | "builder_unavailable" | "builder_busy" | "overloaded" | "tier_unavailable" | "snapshot_unavailable" | "capacity_limit" | "build_timeout" | "dockerfile_build_failed" | "image_unresolvable" | "registry_unavailable" | "enoent" | "trial_credits_exhausted" | "credit_unclaimed" | "card_required" | "billing_unavailable" | "concurrency_limit" | "egress_cap_reached" | "project_cap_reached" | "spend_limit_reached" | "tier_over_plan" | "network_host_refused" | "network_policy_unsupported" | "secrets_in_snapshot" | "secrets_unavailable" | "secret_binding_unsupported" | "idempotency_key_reused" | "idempotency_key_in_use" | "repo_credentials_not_supported" | "account_exists" | "invite_invalid" | "invite_required" | "key_expired";
1078
+ export type ErrorCode = "fork_failed" | "start_timeout" | "readiness_failed" | "verify_too_slow" | "verify_each_failed" | "toolchain_mismatch" | "sandbox_unresponsive" | "oom_within_tier" | "disk_full_within_tier" | "sandbox_interrupted" | "integrity_mismatch" | "egress_unavailable" | "service_timeout" | "preempted" | "verify_did_not_cover" | "grader_failed" | "nonzero_exit" | "grade_not_passed" | "task_timeout" | "oom_over_tier" | "disk_over_tier" | "shutdown_by_task" | "modified_by_task" | "cap_killed" | "cap_exceeded" | "cap_paused" | "credits_paused" | "aborted_by_client" | "stopped_by_support" | "expired" | "not_started" | "superseded" | "usage" | "internal" | "auth" | "forbidden" | "not_found" | "unreachable" | "rate_limited" | "timeout" | "not_verified" | "snapshot_incompatible" | "storage_limit_reached" | "resume_unavailable" | "sandbox_unreachable" | "capacity_busy" | "region_at_capacity" | "builder_unavailable" | "builder_busy" | "overloaded" | "tier_unavailable" | "snapshot_unavailable" | "capacity_limit" | "build_timeout" | "dockerfile_build_failed" | "image_unresolvable" | "registry_unavailable" | "enoent" | "trial_credits_exhausted" | "credit_unclaimed" | "setup_required" | "card_required" | "billing_unavailable" | "concurrency_limit" | "egress_cap_reached" | "project_cap_reached" | "spend_limit_reached" | "tier_over_plan" | "network_host_refused" | "network_policy_unsupported" | "secrets_in_snapshot" | "secrets_unavailable" | "secret_binding_unsupported" | "idempotency_key_reused" | "idempotency_key_in_use" | "repo_credentials_not_supported" | "account_exists" | "invite_invalid" | "invite_required" | "key_expired" | "account_blocked";
1074
1079
  /**
1075
1080
  * `POST /v1/sandboxes/{id}/execs`, and the `exec` of `POST /v1/runs`: one command. `argv` (a
1076
1081
  * non-empty list) or `sh` (a shell line) is required.
@@ -1989,9 +1994,10 @@ export interface Guard {
1989
1994
  */
1990
1995
  export interface Health {
1991
1996
  /**
1992
- * The commit this build came from; empty for a local build.
1997
+ * The commit this build came from; empty for a local build. Answered to a caller with a key;
1998
+ * absent for one without.
1993
1999
  */
1994
- build_sha: string;
2000
+ build_sha?: string;
1995
2001
  /**
1996
2002
  * Where sandboxes run, in plain words (for example "US East Coast"). Absent when the service
1997
2003
  * does not say.
@@ -2737,7 +2743,7 @@ export interface OtherUsage {
2737
2743
  amount: number;
2738
2744
  entries: number;
2739
2745
  }
2740
- export type Outcome = "completed" | "task_failed" | "env_failed" | "snapshot_failed" | "aborted_by_client" | "expired" | "cap_killed" | "not_started" | "superseded" | "grader_failed" | "preempted";
2746
+ export type Outcome = "completed" | "task_failed" | "env_failed" | "snapshot_failed" | "aborted_by_client" | "expired" | "cap_killed" | "not_started" | "superseded" | "grader_failed" | "preempted" | "stopped_by_support";
2741
2747
  /**
2742
2748
  * `GET /v1/me/outcomes?period=month` — what the organization (with a project's key: that project)
2743
2749
  * got this period, measured from its own runs and sandboxes. A figure with nothing behind it is
@@ -3075,10 +3081,10 @@ export interface PricingTier {
3075
3081
  vcpus: number;
3076
3082
  }
3077
3083
  /**
3078
- * `GET /v1/pricing` — the prices, sandbox shapes, limits and free credit in force, read on every
3079
- * call from the same place billing charges from. Without a key: what a new account gets. With a
3080
- * key: what that key's organisation pays. A run is billed at the rates in force when it was
3081
- * created, so a later change to these values never reprices a run already started.
3084
+ * `GET /v1/pricing` — the prices, sandbox shapes, limits and free credit in force for the key's
3085
+ * organisation, read on every call from the same place billing charges from. A key is required.
3086
+ * A run is billed at the rates in force when it was created, so a later change to these values
3087
+ * never reprices a run already started.
3082
3088
  */
3083
3089
  export interface PricingView {
3084
3090
  /**
@@ -3118,9 +3124,9 @@ export interface PricingView {
3118
3124
  */
3119
3125
  monthly_minimum_usd: number;
3120
3126
  /**
3121
- * The organisation these values are for; `null` for `new_account`.
3127
+ * The organisation these values are for.
3122
3128
  */
3123
- org_id?: string | null;
3129
+ org_id: string;
3124
3130
  plan: PricingPlan;
3125
3131
  rates: PricingRates;
3126
3132
  /**
@@ -3128,8 +3134,7 @@ export interface PricingView {
3128
3134
  */
3129
3135
  schema: string;
3130
3136
  /**
3131
- * `new_account` (asked without a key): what a new account gets. `organization` (asked with a
3132
- * key): what that key's organisation pays.
3137
+ * `organization`: what the key's organisation pays.
3133
3138
  */
3134
3139
  scope: string;
3135
3140
  /**
@@ -4434,6 +4439,11 @@ export interface SandboxView {
4434
4439
  * once it has started; absent from list rows.
4435
4440
  */
4436
4441
  cost_usd?: number | null;
4442
+ /**
4443
+ * When the sandbox was created, RFC 3339: when its run was started, or when the retry or
4444
+ * replay that made it was asked for.
4445
+ */
4446
+ created_at: string;
4437
4447
  /**
4438
4448
  * Milliseconds from the start request, after any time queued, to the sandbox being handed
4439
4449
  * over: it does not include `queued_ms`. Add `queued_ms` for the whole wait.
@@ -4491,6 +4501,13 @@ export interface SandboxView {
4491
4501
  * Why it ended; `null` while it is still alive.
4492
4502
  */
4493
4503
  outcome?: string | null;
4504
+ /**
4505
+ * While it is paused: when it will be deleted, with its memory and files, unless it is
4506
+ * resumed before then (RFC 3339). A paused sandbox is kept for 30 days, and resuming it
4507
+ * starts the 30 days again. Absent while it is not paused, and for a paused sandbox that is
4508
+ * not due to be deleted.
4509
+ */
4510
+ pause_expires_at?: string | null;
4494
4511
  /**
4495
4512
  * Why it is paused, when a limit paused it rather than a request: `spend_cap` (its run
4496
4513
  * reached its spend cap) or `credits_exhausted` (the organization's credits ran out). Its
@@ -4528,6 +4545,11 @@ export interface SandboxView {
4528
4545
  */
4529
4546
  run_id: string;
4530
4547
  sandbox_id: string;
4548
+ /**
4549
+ * The snapshot it was started from, as its run names it (`GET /v1/runs/{id}`): the
4550
+ * snapshot's id, or `promigence/base` for the built-in base.
4551
+ */
4552
+ snapshot_id: string;
4531
4553
  /**
4532
4554
  * `queued` | `ready` | `running` | `paused` | `ended`.
4533
4555
  */
@@ -4729,6 +4751,11 @@ export interface SecretStored {
4729
4751
  * otherwise. Present when `hosts` is.
4730
4752
  */
4731
4753
  raw?: string | null;
4754
+ /**
4755
+ * Whether the value is masked out of responses from the bound hosts. Present when `hosts`
4756
+ * is.
4757
+ */
4758
+ scan_responses?: boolean | null;
4732
4759
  /**
4733
4760
  * RFC 3339.
4734
4761
  */
@@ -4990,6 +5017,11 @@ export interface SnapshotCreate {
4990
5017
  * Commands run once, in order, before `verify`.
4991
5018
  */
4992
5019
  setup?: OneOrMany | null;
5020
+ /**
5021
+ * Image and Dockerfile forms: the user its `start`, `setup` and `verify` run as (for example
5022
+ * the snapshot's own `user`). Default: `root`.
5023
+ */
5024
+ setup_user?: string | null;
4993
5025
  /**
4994
5026
  * Extra repositories cloned before `setup` (repository form only, at most 8).
4995
5027
  */
@@ -5008,6 +5040,13 @@ export interface SnapshotCreate {
5008
5040
  * charged to the organization.
5009
5041
  */
5010
5042
  track?: boolean;
5043
+ /**
5044
+ * Image and Dockerfile forms: the user commands, terminals and SSH sessions in its sandboxes
5045
+ * run as when they name none. A user name or uid, with an optional `:group` or `:gid`, as an
5046
+ * image's `USER` takes it; `root` for root. Default: the image's own `USER`, as `docker run`
5047
+ * does (root when it sets none).
5048
+ */
5049
+ user?: string | null;
5011
5050
  /**
5012
5051
  * The command that must exit 0 for the snapshot to be stored. Required in the repository
5013
5052
  * form.
@@ -5103,6 +5142,11 @@ export interface SnapshotDeduped {
5103
5142
  * while it can be started.
5104
5143
  */
5105
5144
  unavailable?: string | null;
5145
+ /**
5146
+ * The user commands, terminals and SSH sessions in its sandboxes run as when they name
5147
+ * none: its image's own `USER`, or the `user` it was created with. Absent: root.
5148
+ */
5149
+ user?: string | null;
5106
5150
  /**
5107
5151
  * What the verification command did when it last ran.
5108
5152
  */
@@ -5314,6 +5358,11 @@ export interface SnapshotView {
5314
5358
  * while it can be started.
5315
5359
  */
5316
5360
  unavailable?: string | null;
5361
+ /**
5362
+ * The user commands, terminals and SSH sessions in its sandboxes run as when they name
5363
+ * none: its image's own `USER`, or the `user` it was created with. Absent: root.
5364
+ */
5365
+ user?: string | null;
5317
5366
  /**
5318
5367
  * What the verification command did when it last ran.
5319
5368
  */
@@ -5650,6 +5699,11 @@ export interface Totals {
5650
5699
  */
5651
5700
  preempted?: number;
5652
5701
  snapshot_failed: number;
5702
+ /**
5703
+ * Sandboxes Promigence support stopped (`stopped_by_support`), each with its reason. Its own
5704
+ * counter: not the customer's own kills (`aborted_by_client`) and not a failure.
5705
+ */
5706
+ stopped_by_support?: number;
5653
5707
  task_failed: number;
5654
5708
  }
5655
5709
  /**
@@ -134,7 +134,6 @@ export interface SandboxCreateOpts extends SandboxOpts {
134
134
  alias?: string;
135
135
  /** Name of a stored secret holding the registry credentials. */
136
136
  registrySecret?: string;
137
- cpuClass?: string;
138
137
  /** Bound on the first build of an image (default 15 min). Expiry cancels nothing. */
139
138
  buildTimeoutMs?: number;
140
139
  /**
@@ -165,7 +164,7 @@ export interface SandboxCreateOpts extends SandboxOpts {
165
164
  * inputs) was built before, or 202 with a `build_id`. The **body**, not the status, decides: an
166
165
  * idempotency replay answers 202 with a document that may already be `verified`.
167
166
  */
168
- export declare function imageSnapshot(client: PromigenceClient, opts: Pick<SandboxCreateOpts, "image" | "setup" | "verify" | "start" | "tier" | "alias" | "registrySecret" | "cpuClass" | "buildTimeoutMs">, hooks?: {
167
+ export declare function imageSnapshot(client: PromigenceClient, opts: Pick<SandboxCreateOpts, "image" | "setup" | "verify" | "start" | "tier" | "alias" | "registrySecret" | "buildTimeoutMs">, hooks?: {
169
168
  /**
170
169
  * The `POST /snapshots` answer, before any wait: `build_price` and whether it was `deduped`
171
170
  * or `joined` (nothing new charged). An implicit build costs money, so a caller
@@ -224,9 +223,10 @@ interface SandboxInit {
224
223
  timeoutMs?: number;
225
224
  }
226
225
  /**
227
- * The snapshot `Sandbox.create()` forks when it is given none: Debian 12 + Python 3.12, node/npm,
228
- * git, curl, jq, gcc/make, `small` tier (built in, not verified). It is always ready to start. The template
229
- * name `'base'` means the same thing.
226
+ * The snapshot `Sandbox.create()` forks when it is given none: Debian 12 + Python 3.12 (pip, venv, uv),
227
+ * Node 24 (npm), Bun, Go 1.27, Docker, git, Git LFS, the GitHub CLI, curl, jq, ripgrep, rsync, gcc/make,
228
+ * and the Claude Code, Codex, Gemini CLI and OpenCode agents, `small` tier (built in, not verified). It
229
+ * is always ready to start. The template name `'base'` means the same thing.
230
230
  */
231
231
  export declare const DEFAULT_SNAPSHOT = "promigence/base";
232
232
  /**
@@ -421,8 +421,7 @@ export declare class Sandbox {
421
421
  ok: boolean;
422
422
  ms: number;
423
423
  checked: string | string[];
424
- first_bad_path: string | null;
425
- probe: Record<string, unknown>;
424
+ first_bad_path?: string | null;
426
425
  }>;
427
426
  isRunning(): Promise<boolean>;
428
427
  /**
@@ -97,8 +97,6 @@ async function imageSnapshot(client, opts, hooks = {}) {
97
97
  create.alias = opts.alias;
98
98
  if (opts.registrySecret !== undefined)
99
99
  create.registrySecret = opts.registrySecret;
100
- if (opts.cpuClass !== undefined)
101
- create.cpuClass = opts.cpuClass;
102
100
  let build = (await client.snapshots.create(create)) ?? {};
103
101
  if (hooks.onBuild)
104
102
  await hooks.onBuild(build);
package/dist/client.d.ts CHANGED
@@ -253,6 +253,11 @@ export interface Totals {
253
253
  grader_failed?: number;
254
254
  snapshot_failed: number;
255
255
  aborted_by_client: number;
256
+ /**
257
+ * Sandboxes Promigence support stopped (outcome `stopped_by_support`), each with its reason in its
258
+ * failure's message. Never counted as your own kill (`aborted_by_client`). Absent from an older API.
259
+ */
260
+ stopped_by_support?: number;
256
261
  expired: number;
257
262
  cap_killed: number;
258
263
  not_started: number;
@@ -349,7 +354,9 @@ export interface SecretRecord {
349
354
  name: string;
350
355
  /** Length of the stored value, so a caller can sanity-check without reading it. */
351
356
  bytes: number;
357
+ /** On `secrets.set` only: `false` when an existing secret of that name was overwritten. */
352
358
  created?: boolean;
359
+ /** On `secrets.list` only. */
353
360
  created_at?: string;
354
361
  updated_at?: string;
355
362
  /**
@@ -370,7 +377,7 @@ export interface SecretRecord {
370
377
  * `api.anthropic.com`. Absent when there is none. A variable the caller sets itself wins.
371
378
  */
372
379
  env?: Record<string, string>;
373
- /** The request headers the secret authenticates (present when `hosts` is). */
380
+ /** The request headers the secret authenticates: on `secrets.list`, when `hosts` is present. */
374
381
  headers?: string[];
375
382
  /** What the variable in a sandbox that names it holds instead of the value (on `set` of a bound secret). */
376
383
  placeholder?: string;
@@ -467,13 +474,17 @@ export interface SnapshotRecord {
467
474
  source?: SnapshotSource;
468
475
  /** `false` for an image snapshot built without `verify` — stored, but nothing was verified. */
469
476
  verified?: boolean;
470
- start?: string | string[];
471
477
  created_at?: string;
472
478
  /**
473
479
  * Set when the snapshot can no longer be started, saying why (it was lost before it could be
474
480
  * kept); a fork of it is refused `snapshot_unavailable`. Absent while it can be started.
475
481
  */
476
482
  unavailable?: string;
483
+ /**
484
+ * The user commands, terminals and SSH sessions in its sandboxes run as when they name none:
485
+ * its image's own `USER`, or the `user` it was created with. Absent: root.
486
+ */
487
+ user?: string | null;
477
488
  [extra: string]: unknown;
478
489
  }
479
490
  /** `GET /snapshots/:id/manifest`: the snapshot's manifest, sealed (`buildBundle` turns it into a `.promigence` bundle). */
@@ -510,8 +521,6 @@ export interface ExecResponse {
510
521
  stdout: string;
511
522
  stderr: string;
512
523
  truncated?: boolean;
513
- stdout_ref?: string;
514
- stderr_ref?: string;
515
524
  /** True when the command was killed at its own timeout. */
516
525
  timed_out?: boolean;
517
526
  /** True when the command was killed for running out of memory. */
@@ -539,7 +548,13 @@ export interface SandboxRecord extends QueuedInfo {
539
548
  */
540
549
  pause_reason?: "spend_cap" | "credits_exhausted";
541
550
  run_id?: string;
551
+ /**
552
+ * The snapshot it was started from, as its run names it (`runs.get(runId).snapshot_id`): the
553
+ * snapshot's id, or `promigence/base` for the built-in base.
554
+ */
542
555
  snapshot_id?: string;
556
+ /** When the sandbox was created, RFC 3339. */
557
+ created_at?: string;
543
558
  outcome?: Outcome;
544
559
  /**
545
560
  * The labels this sandbox inherited from its run — the same map `runs.start({ labels })` took.
@@ -627,7 +642,7 @@ export interface Page<T> {
627
642
  /** What the server actually used, which is not always what was asked for (it caps at 500). */
628
643
  limit: number;
629
644
  }
630
- /** `GET /health` — unauthenticated: `ok`, `version`, `build_sha` and `serving`. */
645
+ /** `GET /health` — unauthenticated: `ok`, `version`, `serving` and `location`; `build_sha` to a caller with a key. */
631
646
  export interface HealthResponse {
632
647
  ok?: boolean;
633
648
  version?: string;
@@ -1184,9 +1199,15 @@ export interface SearchOpts {
1184
1199
  * `POST /sandboxes/:id/files/archive` — where the tar landed **inside the sandbox** and how big
1185
1200
  * it is. The bytes stay in the sandbox on purpose; see `files.downloadDir`.
1186
1201
  */
1187
- export interface ArchiveResult extends FileInfo {
1202
+ export interface ArchiveResult {
1203
+ sandbox_id: string;
1204
+ /** Where the tar landed inside the sandbox. */
1188
1205
  archive_path: string;
1206
+ /** Its size in bytes. */
1207
+ size: number;
1208
+ /** The directory that was packed. */
1189
1209
  source: string;
1210
+ served_by?: ServedBy;
1190
1211
  }
1191
1212
  /** `POST /sandboxes/:id/files/extract`. `entries` is `null` on the fallback path, which cannot
1192
1213
  * count them without a second pass — a number invented there would be worse than none. */
@@ -1312,8 +1333,6 @@ export interface MeResponse {
1312
1333
  email?: string;
1313
1334
  provider?: string;
1314
1335
  } | null;
1315
- /** @deprecated Never sent: the key is `key.key_id`. */
1316
- key_id?: string;
1317
1336
  /**
1318
1337
  * The plan the organisation's limits and prices come from: `hobby` for a trial organisation
1319
1338
  * with no plan assigned. The name `tier_over_plan` and the other refusals use.
@@ -1349,7 +1368,6 @@ export interface MeResponse {
1349
1368
  /** The cap a run gets when it names none, in USD; `0` names none (the run is capped at its quote). */
1350
1369
  default_spend_cap?: number;
1351
1370
  card_on_file?: boolean;
1352
- default_class?: string;
1353
1371
  /** The free trial: credits, data out and the limits that apply until a card is on file. */
1354
1372
  trial?: TrialStatus;
1355
1373
  /** Something about the account to act on (the card-required switch), or null. */
@@ -1661,6 +1679,18 @@ export interface SnapshotCreateOpts {
1661
1679
  tier?: Tier;
1662
1680
  /** Image forms: the name of a stored secret holding the registry credentials. */
1663
1681
  registrySecret?: string;
1682
+ /**
1683
+ * Image and Dockerfile forms: the user commands, terminals and SSH sessions in its sandboxes
1684
+ * run as when they name none. A user name or uid, with an optional `:group` or `:gid`, as an
1685
+ * image's `USER` takes it; `root` for root. Default: the image's own `USER`, as `docker run`
1686
+ * does (root when it sets none).
1687
+ */
1688
+ user?: string;
1689
+ /**
1690
+ * Image and Dockerfile forms: the user its `start`, `setup` and `verify` run as (for example
1691
+ * the snapshot's own `user`). Default: `root`.
1692
+ */
1693
+ setupUser?: string;
1664
1694
  alias?: string;
1665
1695
  /**
1666
1696
  * A DEFAULT grader for this snapshot: a run on it with no `grade` of its own uses this one.
@@ -1951,12 +1981,15 @@ export declare class PromigenceClient {
1951
1981
  timeoutMs?: number;
1952
1982
  }): Promise<StatusResponse>;
1953
1983
  /**
1954
- * `GET /pricing` — the price list, live: the per-second rates, every sandbox shape on sale and
1955
- * its price, the build price, the limits, the free credit and the plan they come from, read on
1956
- * every call from what billing charges. With a key: what this key's organization pays. Without
1957
- * one, or with `newAccount: true` (no key is sent): what a new account gets.
1984
+ * `GET /pricing` — your organization's price list, live: the per-second rates, every sandbox
1985
+ * shape on sale and its price, the build price, the limits, the free credit and the plan they
1986
+ * come from, read on every call from what billing charges. Needs a key: without one this throws
1987
+ * `AuthError` and sends nothing. The published list prices are on the pricing page.
1958
1988
  *
1959
1989
  * Prices change: read them when you need them rather than keeping a copy.
1990
+ *
1991
+ * @param opts.newAccount No longer supported: there is no key-less price list. Passing `true`
1992
+ * throws `InvalidArgumentError` and sends nothing.
1960
1993
  */
1961
1994
  pricing(opts?: {
1962
1995
  newAccount?: boolean;
@@ -2475,6 +2508,12 @@ export declare class PromigenceClient {
2475
2508
  /**
2476
2509
  * `POST /sandboxes/:id/probe` — is this environment still the one that was verified?
2477
2510
  *
2511
+ * Compares the sandbox's files under the paths its snapshot's verification recorded with what
2512
+ * was recorded. It is the same check a failed run gets, which decides whether the failure was
2513
+ * the environment's (`integrity_mismatch`, not billed) or a path the snapshot's own `verify`
2514
+ * never covered (`verify_did_not_cover`, billed). Asking for it up front is cheaper than
2515
+ * spending a run to find out.
2516
+ *
2478
2517
  * `first_bad_path` names the **first differing path**, which is the only part of a failed
2479
2518
  * probe anyone can act on.
2480
2519
  */
@@ -2485,8 +2524,7 @@ export declare class PromigenceClient {
2485
2524
  ok: boolean;
2486
2525
  ms: number;
2487
2526
  checked: string | string[];
2488
- first_bad_path: string | null;
2489
- probe: Record<string, unknown>;
2527
+ first_bad_path?: string | null;
2490
2528
  }>;
2491
2529
  /** `DELETE /sandboxes/:id`. */
2492
2530
  kill: (sandboxId: string) => Promise<unknown>;
@@ -2623,9 +2661,11 @@ export declare class PromigenceClient {
2623
2661
  /**
2624
2662
  * Write a file of **any size**, in [`FILE_PUT_CHUNK_BYTES`] requests.
2625
2663
  *
2626
- * A failure mid-loop leaves a **prefix** in the sandbox, not a corrupt file, and the
2627
- * thrown error carries how far it got (`written` in the error's evidence).
2628
- * Re-running the same `put` is safe: chunk 0 truncates.
2664
+ * Chunk 0 goes without `?offset=`, which means "this is the file" (create, truncate);
2665
+ * every later chunk carries one, which appends, so a file has no size limit here.
2666
+ *
2667
+ * A failure mid-loop leaves a **prefix** in the sandbox, not a corrupt file. Re-running the
2668
+ * same `put` is safe: chunk 0 truncates.
2629
2669
  *
2630
2670
  * `mode` (`0o755`) sets the file's permission bits. Without it a file that already exists
2631
2671
  * keeps its mode, as a shell `>` does, and a new one is `0o644`.
@@ -2735,8 +2775,7 @@ export declare class PromigenceClient {
2735
2775
  ok: boolean;
2736
2776
  ms: number;
2737
2777
  checked: string | string[];
2738
- first_bad_path: string | null;
2739
- probe: Record<string, unknown>;
2778
+ first_bad_path?: string | null;
2740
2779
  }>;
2741
2780
  };
2742
2781
  /**
@@ -2883,15 +2922,14 @@ export declare class PromigenceClient {
2883
2922
  manifest: (idOrAlias: string) => Promise<SnapshotManifestResponse>;
2884
2923
  /**
2885
2924
  * The `.promigence` bundle for a snapshot: the sealed form, which names exactly one
2886
- * environment and verifies offline (`verifyBundle`). `served_by` says whether the API wrote it
2887
- * (`control`) or this client composed it from the snapshot's manifest (`client`); both are the
2888
- * same document.
2925
+ * environment and verifies offline (`verifyBundle`). This client composes it from the
2926
+ * snapshot's sealed manifest (`served_by: "client"`); the API serves no bundle of its own.
2889
2927
  */
2890
2928
  bundle: (idOrAlias: string, opts?: {
2891
2929
  alias?: string;
2892
2930
  }) => Promise<{
2893
2931
  bundle: Bundle;
2894
- served_by: "control" | "client";
2932
+ served_by: "client";
2895
2933
  record?: SnapshotRecord;
2896
2934
  }>;
2897
2935
  };
package/dist/client.js CHANGED
@@ -3,7 +3,7 @@ import { homedir } from "node:os";
3
3
  import { inspect } from "node:util";
4
4
  import { dirname, join } from "node:path";
5
5
  import { ApiError, AuthError, EnvFailedError, PromigenceError, InvalidArgumentError, NotFoundError, NotReleasedError, NotVerifiedError, RateLimitError, SpendCapError, TimeoutError, UnreachableError, DEFAULT_API_URL, ERRORS_DOCS_URL, FILE_PUT_CHUNK_BYTES, MAX_OUTPUT_BYTES_DEFAULT, VERSION, WAIT_TIMEOUT_MS, PreviewsUnavailableError, exitForCode, previewsOff, withPreviewsFix, } from "./core.js";
6
- import { BUNDLE_SCHEMA, buildBundle } from "./bundle.js";
6
+ import { buildBundle } from "./bundle.js";
7
7
  import { keychainReadCached, keychainStore, storeLabel } from "./credstore.js";
8
8
  import { CALLER_HEADER, resolveCaller, userAgentWithCaller } from "./caller.js";
9
9
  import { connectPty } from "./pty.js";
@@ -507,6 +507,12 @@ export function snapshotCreateBody(opts) {
507
507
  if (o.env !== undefined && Object.keys(o.env).length && forms[0] === "repo") {
508
508
  throw new InvalidArgumentError("snapshots.create env is for an image or a Dockerfile", "A repository snapshot sets its environment in its setup steps, or pass env on each command.");
509
509
  }
510
+ if ((o.user !== undefined || o.setupUser !== undefined) && forms[0] === "repo") {
511
+ throw new InvalidArgumentError("snapshots.create user and setupUser are for an image or a Dockerfile", "A repository snapshot runs as root; name a user on each command instead ({ user: 'app' }).");
512
+ }
513
+ if (o.setupUser !== undefined && o.user === undefined) {
514
+ throw new InvalidArgumentError("snapshots.create setupUser needs user", "Pass { user: 'app', setupUser: 'app' }: start, setup and verify as app too (without setupUser they run as root), every later command as app.");
515
+ }
510
516
  if (!github && (o.ref !== undefined || o.from !== undefined || o.track)) {
511
517
  throw new InvalidArgumentError("snapshots.create ref, from and track are for a connected repository (repo: 'github:owner/name' or 'gitlab:group/project')", "Pass { repo: 'github:your-org/app', ref: 'main' }, or drop them for this form.");
512
518
  }
@@ -516,7 +522,7 @@ export function snapshotCreateBody(opts) {
516
522
  setup: o.setup, verify, tier: o.tier, alias: o.alias,
517
523
  others: [
518
524
  ["sources", Boolean(o.sources?.length)], ["start", o.start !== undefined],
519
- ["registrySecret", Boolean(o.registrySecret)], ["cpuClass", Boolean(o.cpuClass)],
525
+ ["registrySecret", Boolean(o.registrySecret)],
520
526
  ],
521
527
  });
522
528
  }
@@ -564,11 +570,13 @@ export function snapshotCreateBody(opts) {
564
570
  body["registry_secret"] = o.registrySecret;
565
571
  if (o.env !== undefined && Object.keys(o.env).length)
566
572
  body["env"] = { ...o.env };
573
+ if (o.user !== undefined)
574
+ body["user"] = o.user;
575
+ if (o.setupUser !== undefined)
576
+ body["setup_user"] = o.setupUser;
567
577
  }
568
578
  if (o.alias)
569
579
  body["alias"] = o.alias;
570
- if (o.cpuClass)
571
- body["cpu_class"] = o.cpuClass;
572
580
  if (o.grade) {
573
581
  if (!o.grade.cmd)
574
582
  throw new InvalidArgumentError("snapshots.create grade needs a cmd", "Pass { grade: { cmd: 'pytest -q tests/' } }.");
@@ -1207,12 +1215,14 @@ export class PromigenceClient {
1207
1215
  ...(opts.timeoutMs === undefined ? {} : { timeoutMs: opts.timeoutMs }),
1208
1216
  });
1209
1217
  }
1210
- pricing(opts = {}) {
1211
- const anonymous = opts.newAccount === true || !this.authenticated;
1218
+ async pricing(opts = {}) {
1219
+ if (opts.newAccount === true) {
1220
+ throw new InvalidArgumentError("pricing({ newAccount: true }) is no longer supported: the price list needs a key", "Call pricing() with your API key for your organization's prices. The published list prices are on the pricing page, and `promigence pricing` prints them without a key.");
1221
+ }
1222
+ this.requireKey();
1212
1223
  return this.request({
1213
1224
  method: "GET",
1214
1225
  path: "/pricing",
1215
- ...(anonymous ? { anonymous: true } : {}),
1216
1226
  ...(opts.signal ? { signal: opts.signal } : {}),
1217
1227
  ...(opts.timeoutMs === undefined ? {} : { timeoutMs: opts.timeoutMs }),
1218
1228
  });
@@ -2414,19 +2424,6 @@ export class PromigenceClient {
2414
2424
  },
2415
2425
  manifest: (idOrAlias) => this.request({ method: "GET", path: `/snapshots/${seg(idOrAlias)}/manifest` }),
2416
2426
  bundle: async (idOrAlias, opts = {}) => {
2417
- try {
2418
- const r = await this.request({
2419
- method: "GET", path: `/snapshots/${seg(idOrAlias)}/bundle`,
2420
- });
2421
- if (r?.bundle?.schema === BUNDLE_SCHEMA) {
2422
- return { bundle: r.bundle, served_by: "control" };
2423
- }
2424
- }
2425
- catch (e) {
2426
- const code = e?.code;
2427
- if (code !== "not_found" && code !== "enoent" && e?.status !== 405)
2428
- throw e;
2429
- }
2430
2427
  const [record, m] = await Promise.all([
2431
2428
  this.snapshots.get(idOrAlias).catch(() => undefined),
2432
2429
  this.snapshots.manifest(idOrAlias),
package/dist/core.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Promigence SDK — constants, error hierarchy and shared types. */
2
- export declare const VERSION = "0.1.0-beta.5";
2
+ export declare const VERSION = "0.1.0-beta.7";
3
3
  export declare const DOCS_URL = "https://promigence.ai";
4
4
  /** The error reference; each code is an anchor on it (the API's `docs` link has the same form). */
5
5
  export declare const ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
package/dist/core.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { hintArg } from "./hint.js";
2
- export const VERSION = "0.1.0-beta.5";
2
+ export const VERSION = "0.1.0-beta.7";
3
3
  export const DOCS_URL = "https://promigence.ai";
4
4
  export const ERRORS_DOCS_URL = "https://www.promigence.ai/docs/errors";
5
5
  export const DEFAULT_API_URL = "https://api.promigence.ai/v1";
@@ -226,7 +226,7 @@ export const EXIT_BY_CODE = {
226
226
  nonzero_exit: 4, grade_not_passed: 4, task_timeout: 4, oom_over_tier: 4, disk_over_tier: 4,
227
227
  shutdown_by_task: 4, modified_by_task: 4,
228
228
  cap_exceeded: 5, cap_killed: 12, cap_paused: 5, credits_paused: 5,
229
- aborted_by_client: 130, expired: 12, not_started: 12, superseded: 0,
229
+ aborted_by_client: 130, stopped_by_support: 12, expired: 12, not_started: 12, superseded: 0,
230
230
  usage: 2, internal: 1, auth: 7, not_found: 8, unreachable: 9, rate_limited: 10,
231
231
  timeout: 11, not_verified: 6, enoent: 8,
232
232
  snapshot_incompatible: 6, storage_limit_reached: 5,
@@ -251,6 +251,8 @@ export const EXIT_BY_CODE = {
251
251
  invite_invalid: 7,
252
252
  invite_required: 7,
253
253
  key_expired: 7,
254
+ account_blocked: 7,
255
+ setup_required: 7,
254
256
  };
255
257
  export function exitForCode(code) {
256
258
  return Object.prototype.hasOwnProperty.call(EXIT_BY_CODE, code) ? EXIT_BY_CODE[code] : 1;
@@ -72,7 +72,9 @@ export interface AuthConfig {
72
72
  */
73
73
  console_authorize_url?: string | null;
74
74
  /**
75
- * The sign-in buttons to offer, as name -> label.
75
+ * The sign-in methods to offer, as name -> label (`email`, `google`, `github`). A client that
76
+ * asks the sign-in pages for one method passes the label as their `identity_provider`, except
77
+ * `email`, which is the pages' own form and needs none.
76
78
  */
77
79
  identity_providers: Record<string, string>;
78
80
  /**
@@ -81,7 +83,6 @@ export interface AuthConfig {
81
83
  * account signs in as before either way.
82
84
  */
83
85
  invite_mode?: boolean;
84
- issuer: string;
85
86
  /**
86
87
  * The redirect URIs a command-line client may listen on.
87
88
  */
@@ -90,10 +91,6 @@ export interface AuthConfig {
90
91
  * Where a headless sign-in lands instead, to show a code to paste back.
91
92
  */
92
93
  paste_redirect_uri: string;
93
- /**
94
- * Which identity service is in front of the API.
95
- */
96
- provider: string;
97
94
  scopes: Array<string>;
98
95
  token_url: string;
99
96
  }
@@ -660,7 +657,15 @@ export interface CaptureView {
660
657
  * The card on file, enough to recognise it.
661
658
  */
662
659
  export interface CardOnFile {
660
+ /**
661
+ * The card's brand (`visa`, `mastercard`, ...); for a card held in a wallet, the wallet
662
+ * (`apple_pay`, `google_pay`); for another payment method, its kind (`link`, `cashapp`,
663
+ * `amazon_pay`).
664
+ */
663
665
  brand?: string | null;
666
+ /**
667
+ * The card's last four digits; `null` for a payment method without a card number.
668
+ */
664
669
  last4?: string | null;
665
670
  }
666
671
  /**
@@ -825,7 +830,7 @@ export interface CliTokenCreate {
825
830
  /**
826
831
  * Every error and failure code the API returns.
827
832
  */
828
- export type Code = "fork_failed" | "start_timeout" | "readiness_failed" | "verify_too_slow" | "verify_each_failed" | "sandbox_unresponsive" | "oom_within_tier" | "disk_full_within_tier" | "sandbox_interrupted" | "integrity_mismatch" | "egress_unavailable" | "service_timeout" | "capacity_busy" | "verify_did_not_cover" | "nonzero_exit" | "task_timeout" | "oom_over_tier" | "disk_over_tier" | "cap_killed" | "cap_exceeded" | "aborted_by_client" | "expired" | "not_started" | "superseded" | "usage" | "internal" | "auth" | "not_found" | "unreachable" | "rate_limited" | "timeout" | "not_verified" | "enoent" | "trial_credits_exhausted" | "card_required" | "concurrency_limit" | "egress_cap_reached" | "network_policy_unsupported" | "network_host_refused" | "toolchain_mismatch" | "resume_unavailable" | "sandbox_unreachable" | "region_at_capacity" | "tier_unavailable" | "snapshot_unavailable" | "capacity_limit" | "build_timeout" | "builder_unavailable" | "builder_busy" | "overloaded" | "preempted" | "grader_failed" | "grade_not_passed" | "shutdown_by_task" | "modified_by_task" | "cap_paused" | "credits_paused" | "forbidden" | "snapshot_incompatible" | "storage_limit_reached" | "dockerfile_build_failed" | "image_unresolvable" | "registry_unavailable" | "credit_unclaimed" | "billing_unavailable" | "project_cap_reached" | "spend_limit_reached" | "tier_over_plan" | "secrets_in_snapshot" | "secrets_unavailable" | "secret_binding_unsupported" | "idempotency_key_reused" | "idempotency_key_in_use" | "repo_credentials_not_supported" | "account_exists" | "invite_invalid" | "invite_required" | "key_expired";
833
+ export type Code = "fork_failed" | "start_timeout" | "readiness_failed" | "verify_too_slow" | "verify_each_failed" | "sandbox_unresponsive" | "oom_within_tier" | "disk_full_within_tier" | "sandbox_interrupted" | "integrity_mismatch" | "egress_unavailable" | "service_timeout" | "capacity_busy" | "verify_did_not_cover" | "nonzero_exit" | "task_timeout" | "oom_over_tier" | "disk_over_tier" | "cap_killed" | "cap_exceeded" | "aborted_by_client" | "expired" | "not_started" | "superseded" | "usage" | "internal" | "auth" | "not_found" | "unreachable" | "rate_limited" | "timeout" | "not_verified" | "enoent" | "trial_credits_exhausted" | "card_required" | "concurrency_limit" | "egress_cap_reached" | "network_policy_unsupported" | "network_host_refused" | "toolchain_mismatch" | "resume_unavailable" | "sandbox_unreachable" | "region_at_capacity" | "tier_unavailable" | "snapshot_unavailable" | "capacity_limit" | "build_timeout" | "builder_unavailable" | "builder_busy" | "overloaded" | "preempted" | "grader_failed" | "grade_not_passed" | "shutdown_by_task" | "modified_by_task" | "cap_paused" | "credits_paused" | "stopped_by_support" | "forbidden" | "snapshot_incompatible" | "storage_limit_reached" | "dockerfile_build_failed" | "image_unresolvable" | "registry_unavailable" | "credit_unclaimed" | "billing_unavailable" | "project_cap_reached" | "spend_limit_reached" | "tier_over_plan" | "secrets_in_snapshot" | "secrets_unavailable" | "secret_binding_unsupported" | "idempotency_key_reused" | "idempotency_key_in_use" | "repo_credentials_not_supported" | "account_exists" | "invite_invalid" | "invite_required" | "key_expired" | "account_blocked" | "setup_required";
829
834
  export interface Concurrency {
830
835
  }
831
836
  /**
@@ -1070,7 +1075,7 @@ export interface ErrorBody {
1070
1075
  /**
1071
1076
  * Every error code the API returns. `category` decides whether the failure is billed: `env` is ours (not billed before hand-over; after it, the time since the sandbox's last snapshot is credited back, up to 24 hours, when our side shows the cause), `task` is your command and is billed. The full table is `x-promigence-error-codes` at the document root.
1072
1077
  */
1073
- export type ErrorCode = "fork_failed" | "start_timeout" | "readiness_failed" | "verify_too_slow" | "verify_each_failed" | "toolchain_mismatch" | "sandbox_unresponsive" | "oom_within_tier" | "disk_full_within_tier" | "sandbox_interrupted" | "integrity_mismatch" | "egress_unavailable" | "service_timeout" | "preempted" | "verify_did_not_cover" | "grader_failed" | "nonzero_exit" | "grade_not_passed" | "task_timeout" | "oom_over_tier" | "disk_over_tier" | "shutdown_by_task" | "modified_by_task" | "cap_killed" | "cap_exceeded" | "cap_paused" | "credits_paused" | "aborted_by_client" | "expired" | "not_started" | "superseded" | "usage" | "internal" | "auth" | "forbidden" | "not_found" | "unreachable" | "rate_limited" | "timeout" | "not_verified" | "snapshot_incompatible" | "storage_limit_reached" | "resume_unavailable" | "sandbox_unreachable" | "capacity_busy" | "region_at_capacity" | "builder_unavailable" | "builder_busy" | "overloaded" | "tier_unavailable" | "snapshot_unavailable" | "capacity_limit" | "build_timeout" | "dockerfile_build_failed" | "image_unresolvable" | "registry_unavailable" | "enoent" | "trial_credits_exhausted" | "credit_unclaimed" | "card_required" | "billing_unavailable" | "concurrency_limit" | "egress_cap_reached" | "project_cap_reached" | "spend_limit_reached" | "tier_over_plan" | "network_host_refused" | "network_policy_unsupported" | "secrets_in_snapshot" | "secrets_unavailable" | "secret_binding_unsupported" | "idempotency_key_reused" | "idempotency_key_in_use" | "repo_credentials_not_supported" | "account_exists" | "invite_invalid" | "invite_required" | "key_expired";
1078
+ export type ErrorCode = "fork_failed" | "start_timeout" | "readiness_failed" | "verify_too_slow" | "verify_each_failed" | "toolchain_mismatch" | "sandbox_unresponsive" | "oom_within_tier" | "disk_full_within_tier" | "sandbox_interrupted" | "integrity_mismatch" | "egress_unavailable" | "service_timeout" | "preempted" | "verify_did_not_cover" | "grader_failed" | "nonzero_exit" | "grade_not_passed" | "task_timeout" | "oom_over_tier" | "disk_over_tier" | "shutdown_by_task" | "modified_by_task" | "cap_killed" | "cap_exceeded" | "cap_paused" | "credits_paused" | "aborted_by_client" | "stopped_by_support" | "expired" | "not_started" | "superseded" | "usage" | "internal" | "auth" | "forbidden" | "not_found" | "unreachable" | "rate_limited" | "timeout" | "not_verified" | "snapshot_incompatible" | "storage_limit_reached" | "resume_unavailable" | "sandbox_unreachable" | "capacity_busy" | "region_at_capacity" | "builder_unavailable" | "builder_busy" | "overloaded" | "tier_unavailable" | "snapshot_unavailable" | "capacity_limit" | "build_timeout" | "dockerfile_build_failed" | "image_unresolvable" | "registry_unavailable" | "enoent" | "trial_credits_exhausted" | "credit_unclaimed" | "setup_required" | "card_required" | "billing_unavailable" | "concurrency_limit" | "egress_cap_reached" | "project_cap_reached" | "spend_limit_reached" | "tier_over_plan" | "network_host_refused" | "network_policy_unsupported" | "secrets_in_snapshot" | "secrets_unavailable" | "secret_binding_unsupported" | "idempotency_key_reused" | "idempotency_key_in_use" | "repo_credentials_not_supported" | "account_exists" | "invite_invalid" | "invite_required" | "key_expired" | "account_blocked";
1074
1079
  /**
1075
1080
  * `POST /v1/sandboxes/{id}/execs`, and the `exec` of `POST /v1/runs`: one command. `argv` (a
1076
1081
  * non-empty list) or `sh` (a shell line) is required.
@@ -1989,9 +1994,10 @@ export interface Guard {
1989
1994
  */
1990
1995
  export interface Health {
1991
1996
  /**
1992
- * The commit this build came from; empty for a local build.
1997
+ * The commit this build came from; empty for a local build. Answered to a caller with a key;
1998
+ * absent for one without.
1993
1999
  */
1994
- build_sha: string;
2000
+ build_sha?: string;
1995
2001
  /**
1996
2002
  * Where sandboxes run, in plain words (for example "US East Coast"). Absent when the service
1997
2003
  * does not say.
@@ -2737,7 +2743,7 @@ export interface OtherUsage {
2737
2743
  amount: number;
2738
2744
  entries: number;
2739
2745
  }
2740
- export type Outcome = "completed" | "task_failed" | "env_failed" | "snapshot_failed" | "aborted_by_client" | "expired" | "cap_killed" | "not_started" | "superseded" | "grader_failed" | "preempted";
2746
+ export type Outcome = "completed" | "task_failed" | "env_failed" | "snapshot_failed" | "aborted_by_client" | "expired" | "cap_killed" | "not_started" | "superseded" | "grader_failed" | "preempted" | "stopped_by_support";
2741
2747
  /**
2742
2748
  * `GET /v1/me/outcomes?period=month` — what the organization (with a project's key: that project)
2743
2749
  * got this period, measured from its own runs and sandboxes. A figure with nothing behind it is
@@ -3075,10 +3081,10 @@ export interface PricingTier {
3075
3081
  vcpus: number;
3076
3082
  }
3077
3083
  /**
3078
- * `GET /v1/pricing` — the prices, sandbox shapes, limits and free credit in force, read on every
3079
- * call from the same place billing charges from. Without a key: what a new account gets. With a
3080
- * key: what that key's organisation pays. A run is billed at the rates in force when it was
3081
- * created, so a later change to these values never reprices a run already started.
3084
+ * `GET /v1/pricing` — the prices, sandbox shapes, limits and free credit in force for the key's
3085
+ * organisation, read on every call from the same place billing charges from. A key is required.
3086
+ * A run is billed at the rates in force when it was created, so a later change to these values
3087
+ * never reprices a run already started.
3082
3088
  */
3083
3089
  export interface PricingView {
3084
3090
  /**
@@ -3118,9 +3124,9 @@ export interface PricingView {
3118
3124
  */
3119
3125
  monthly_minimum_usd: number;
3120
3126
  /**
3121
- * The organisation these values are for; `null` for `new_account`.
3127
+ * The organisation these values are for.
3122
3128
  */
3123
- org_id?: string | null;
3129
+ org_id: string;
3124
3130
  plan: PricingPlan;
3125
3131
  rates: PricingRates;
3126
3132
  /**
@@ -3128,8 +3134,7 @@ export interface PricingView {
3128
3134
  */
3129
3135
  schema: string;
3130
3136
  /**
3131
- * `new_account` (asked without a key): what a new account gets. `organization` (asked with a
3132
- * key): what that key's organisation pays.
3137
+ * `organization`: what the key's organisation pays.
3133
3138
  */
3134
3139
  scope: string;
3135
3140
  /**
@@ -4434,6 +4439,11 @@ export interface SandboxView {
4434
4439
  * once it has started; absent from list rows.
4435
4440
  */
4436
4441
  cost_usd?: number | null;
4442
+ /**
4443
+ * When the sandbox was created, RFC 3339: when its run was started, or when the retry or
4444
+ * replay that made it was asked for.
4445
+ */
4446
+ created_at: string;
4437
4447
  /**
4438
4448
  * Milliseconds from the start request, after any time queued, to the sandbox being handed
4439
4449
  * over: it does not include `queued_ms`. Add `queued_ms` for the whole wait.
@@ -4491,6 +4501,13 @@ export interface SandboxView {
4491
4501
  * Why it ended; `null` while it is still alive.
4492
4502
  */
4493
4503
  outcome?: string | null;
4504
+ /**
4505
+ * While it is paused: when it will be deleted, with its memory and files, unless it is
4506
+ * resumed before then (RFC 3339). A paused sandbox is kept for 30 days, and resuming it
4507
+ * starts the 30 days again. Absent while it is not paused, and for a paused sandbox that is
4508
+ * not due to be deleted.
4509
+ */
4510
+ pause_expires_at?: string | null;
4494
4511
  /**
4495
4512
  * Why it is paused, when a limit paused it rather than a request: `spend_cap` (its run
4496
4513
  * reached its spend cap) or `credits_exhausted` (the organization's credits ran out). Its
@@ -4528,6 +4545,11 @@ export interface SandboxView {
4528
4545
  */
4529
4546
  run_id: string;
4530
4547
  sandbox_id: string;
4548
+ /**
4549
+ * The snapshot it was started from, as its run names it (`GET /v1/runs/{id}`): the
4550
+ * snapshot's id, or `promigence/base` for the built-in base.
4551
+ */
4552
+ snapshot_id: string;
4531
4553
  /**
4532
4554
  * `queued` | `ready` | `running` | `paused` | `ended`.
4533
4555
  */
@@ -4729,6 +4751,11 @@ export interface SecretStored {
4729
4751
  * otherwise. Present when `hosts` is.
4730
4752
  */
4731
4753
  raw?: string | null;
4754
+ /**
4755
+ * Whether the value is masked out of responses from the bound hosts. Present when `hosts`
4756
+ * is.
4757
+ */
4758
+ scan_responses?: boolean | null;
4732
4759
  /**
4733
4760
  * RFC 3339.
4734
4761
  */
@@ -4990,6 +5017,11 @@ export interface SnapshotCreate {
4990
5017
  * Commands run once, in order, before `verify`.
4991
5018
  */
4992
5019
  setup?: OneOrMany | null;
5020
+ /**
5021
+ * Image and Dockerfile forms: the user its `start`, `setup` and `verify` run as (for example
5022
+ * the snapshot's own `user`). Default: `root`.
5023
+ */
5024
+ setup_user?: string | null;
4993
5025
  /**
4994
5026
  * Extra repositories cloned before `setup` (repository form only, at most 8).
4995
5027
  */
@@ -5008,6 +5040,13 @@ export interface SnapshotCreate {
5008
5040
  * charged to the organization.
5009
5041
  */
5010
5042
  track?: boolean;
5043
+ /**
5044
+ * Image and Dockerfile forms: the user commands, terminals and SSH sessions in its sandboxes
5045
+ * run as when they name none. A user name or uid, with an optional `:group` or `:gid`, as an
5046
+ * image's `USER` takes it; `root` for root. Default: the image's own `USER`, as `docker run`
5047
+ * does (root when it sets none).
5048
+ */
5049
+ user?: string | null;
5011
5050
  /**
5012
5051
  * The command that must exit 0 for the snapshot to be stored. Required in the repository
5013
5052
  * form.
@@ -5103,6 +5142,11 @@ export interface SnapshotDeduped {
5103
5142
  * while it can be started.
5104
5143
  */
5105
5144
  unavailable?: string | null;
5145
+ /**
5146
+ * The user commands, terminals and SSH sessions in its sandboxes run as when they name
5147
+ * none: its image's own `USER`, or the `user` it was created with. Absent: root.
5148
+ */
5149
+ user?: string | null;
5106
5150
  /**
5107
5151
  * What the verification command did when it last ran.
5108
5152
  */
@@ -5314,6 +5358,11 @@ export interface SnapshotView {
5314
5358
  * while it can be started.
5315
5359
  */
5316
5360
  unavailable?: string | null;
5361
+ /**
5362
+ * The user commands, terminals and SSH sessions in its sandboxes run as when they name
5363
+ * none: its image's own `USER`, or the `user` it was created with. Absent: root.
5364
+ */
5365
+ user?: string | null;
5317
5366
  /**
5318
5367
  * What the verification command did when it last ran.
5319
5368
  */
@@ -5650,6 +5699,11 @@ export interface Totals {
5650
5699
  */
5651
5700
  preempted?: number;
5652
5701
  snapshot_failed: number;
5702
+ /**
5703
+ * Sandboxes Promigence support stopped (`stopped_by_support`), each with its reason. Its own
5704
+ * counter: not the customer's own kills (`aborted_by_client`) and not a failure.
5705
+ */
5706
+ stopped_by_support?: number;
5653
5707
  task_failed: number;
5654
5708
  }
5655
5709
  /**
package/dist/sandbox.d.ts CHANGED
@@ -134,7 +134,6 @@ export interface SandboxCreateOpts extends SandboxOpts {
134
134
  alias?: string;
135
135
  /** Name of a stored secret holding the registry credentials. */
136
136
  registrySecret?: string;
137
- cpuClass?: string;
138
137
  /** Bound on the first build of an image (default 15 min). Expiry cancels nothing. */
139
138
  buildTimeoutMs?: number;
140
139
  /**
@@ -165,7 +164,7 @@ export interface SandboxCreateOpts extends SandboxOpts {
165
164
  * inputs) was built before, or 202 with a `build_id`. The **body**, not the status, decides: an
166
165
  * idempotency replay answers 202 with a document that may already be `verified`.
167
166
  */
168
- export declare function imageSnapshot(client: PromigenceClient, opts: Pick<SandboxCreateOpts, "image" | "setup" | "verify" | "start" | "tier" | "alias" | "registrySecret" | "cpuClass" | "buildTimeoutMs">, hooks?: {
167
+ export declare function imageSnapshot(client: PromigenceClient, opts: Pick<SandboxCreateOpts, "image" | "setup" | "verify" | "start" | "tier" | "alias" | "registrySecret" | "buildTimeoutMs">, hooks?: {
169
168
  /**
170
169
  * The `POST /snapshots` answer, before any wait: `build_price` and whether it was `deduped`
171
170
  * or `joined` (nothing new charged). An implicit build costs money, so a caller
@@ -224,9 +223,10 @@ interface SandboxInit {
224
223
  timeoutMs?: number;
225
224
  }
226
225
  /**
227
- * The snapshot `Sandbox.create()` forks when it is given none: Debian 12 + Python 3.12, node/npm,
228
- * git, curl, jq, gcc/make, `small` tier (built in, not verified). It is always ready to start. The template
229
- * name `'base'` means the same thing.
226
+ * The snapshot `Sandbox.create()` forks when it is given none: Debian 12 + Python 3.12 (pip, venv, uv),
227
+ * Node 24 (npm), Bun, Go 1.27, Docker, git, Git LFS, the GitHub CLI, curl, jq, ripgrep, rsync, gcc/make,
228
+ * and the Claude Code, Codex, Gemini CLI and OpenCode agents, `small` tier (built in, not verified). It
229
+ * is always ready to start. The template name `'base'` means the same thing.
230
230
  */
231
231
  export declare const DEFAULT_SNAPSHOT = "promigence/base";
232
232
  /**
@@ -421,8 +421,7 @@ export declare class Sandbox {
421
421
  ok: boolean;
422
422
  ms: number;
423
423
  checked: string | string[];
424
- first_bad_path: string | null;
425
- probe: Record<string, unknown>;
424
+ first_bad_path?: string | null;
426
425
  }>;
427
426
  isRunning(): Promise<boolean>;
428
427
  /**
package/dist/sandbox.js CHANGED
@@ -87,8 +87,6 @@ export async function imageSnapshot(client, opts, hooks = {}) {
87
87
  create.alias = opts.alias;
88
88
  if (opts.registrySecret !== undefined)
89
89
  create.registrySecret = opts.registrySecret;
90
- if (opts.cpuClass !== undefined)
91
- create.cpuClass = opts.cpuClass;
92
90
  let build = (await client.snapshots.create(create)) ?? {};
93
91
  if (hooks.onBuild)
94
92
  await hooks.onBuild(build);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promigence/sdk",
3
- "version": "0.1.0-beta.5",
3
+ "version": "0.1.0-beta.7",
4
4
  "description": "The fastest sandboxes for AI agents and code. The Promigence TypeScript SDK: snapshot an environment once, fork it as often as you need, run commands in every copy. A simple Sandbox class and the full PromigenceClient, with zero runtime dependencies.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://www.promigence.ai",