@promigence/sdk 0.1.0-beta.13 → 0.1.0-beta.15

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.
@@ -727,6 +727,10 @@ export interface OrgRecord {
727
727
  projects: number;
728
728
  members: number;
729
729
  created_at: string;
730
+ /** Days command output is kept after a sandbox ends; `null` = the default, 7. */
731
+ output_retention_days?: number | null;
732
+ /** Keep a failed sandbox to reopen where it broke, when a run does not say; absent or `null` = the default. */
733
+ failure_captures?: boolean | null;
730
734
  }
731
735
  export interface ProjectRecord {
732
736
  project_id: string;
@@ -736,8 +740,14 @@ export interface ProjectRecord {
736
740
  keys: number;
737
741
  /** The most the project may bill in a calendar month (UTC); `null` = no cap. */
738
742
  monthly_cap_usd?: number | null;
743
+ /** Days this project's command output is kept after a sandbox ends; `null` = the organization's. */
744
+ output_retention_days?: number | null;
745
+ /** Keep this project's failed sandboxes to reopen, when a run does not say; absent or `null` = the organization's. */
746
+ failure_captures?: boolean | null;
739
747
  created_at: string;
740
748
  }
749
+ /** The longest command output may be kept after a sandbox ends, in days. */
750
+ export declare const OUTPUT_RETENTION_MAX_DAYS = 30;
741
751
  export interface MemberRecord {
742
752
  user_id: string;
743
753
  email: string;
@@ -905,8 +915,10 @@ export interface PricingView {
905
915
  invite_required: boolean;
906
916
  };
907
917
  monthly_fee_usd: number;
908
- /** Usage the monthly fee includes each month, USD. */
918
+ /** Credits the plan adds each paid month, USD. */
909
919
  included_credits_usd: number;
920
+ /** For how many paid months the plan adds `included_credits_usd`; `null` when it adds them every paid month. */
921
+ included_credits_promo_months?: number | null;
910
922
  monthly_minimum_usd: number;
911
923
  }
912
924
  export interface BillingUsage {
@@ -957,6 +969,20 @@ export interface BillingUsage {
957
969
  period_end?: string | null;
958
970
  } | null;
959
971
  upcoming_invoice_error?: string | null;
972
+ /** What the organization's credits can pay for: balance, held, available, the next to expire. */
973
+ credits?: M.BillingCredits;
974
+ /** Auto-reload: when available credits fall below `threshold_usd`, `amount_usd` is added from `card`. */
975
+ auto_reload?: M.AutoReload;
976
+ /** The plan in force, and a renewal payment in trouble. */
977
+ plan_status?: M.PlanStatus;
978
+ /** `credits` (paid from credits bought in advance) or `postpaid` (billed after use, until `switch.at`). */
979
+ model?: string;
980
+ /** For a `postpaid` organization: when it moves to credits, and the credits ready then. */
981
+ switch?: M.BillingSwitch | null;
982
+ /** What this organization can buy now. */
983
+ trust?: M.TrustLimits;
984
+ /** A hold that refuses new work, and the page that lifts it; `null` when there is none. */
985
+ hold?: M.BillingHold | null;
960
986
  [extra: string]: unknown;
961
987
  }
962
988
  /** One row of `GET /billing/history`: a credit the organization received, or a payment. */
@@ -1150,7 +1176,7 @@ export interface SnapshotVerifyResponse {
1150
1176
  state: string;
1151
1177
  }>;
1152
1178
  }
1153
- /** `GET /sandboxes/:id/files/stat`. */
1179
+ /** `POST /sandboxes/:id/files/stat`. */
1154
1180
  export interface FileStat {
1155
1181
  path: string;
1156
1182
  size: number;
@@ -1178,7 +1204,7 @@ export interface DirEntry {
1178
1204
  }
1179
1205
  /** An opaque label of how a file request was answered, passed through for support. */
1180
1206
  export type ServedBy = string;
1181
- /** `GET /sandboxes/:id/files/list?path=`. */
1207
+ /** `POST /sandboxes/:id/files/list`. */
1182
1208
  export interface DirListing {
1183
1209
  sandbox_id: string;
1184
1210
  path: string;
@@ -1187,7 +1213,7 @@ export interface DirListing {
1187
1213
  truncated: boolean;
1188
1214
  served_by?: ServedBy;
1189
1215
  }
1190
- /** `?recursive=&depth=&limit=&hidden=` on `files/list`. */
1216
+ /** `recursive`, `depth`, `limit` and `hidden` on `files/list` (sent in the request body). */
1191
1217
  export interface DirListOpts {
1192
1218
  recursive?: boolean;
1193
1219
  /** With `recursive`: how many levels below `path`. Unset = no bound. */
@@ -1207,7 +1233,7 @@ export interface FileInfo {
1207
1233
  is_symlink: boolean;
1208
1234
  served_by?: ServedBy;
1209
1235
  }
1210
- /** One hit of `GET /sandboxes/:id/files/search`. `line`/`text` only for a `contains` search. */
1236
+ /** One hit of `POST /sandboxes/:id/files/search`. `line`/`text` only for a `contains` search. */
1211
1237
  export interface FileMatch {
1212
1238
  path: string;
1213
1239
  line?: number | null;
@@ -1906,6 +1932,8 @@ interface RequestOpts {
1906
1932
  onHeaders?: (headers: Headers) => void;
1907
1933
  /** Extra request headers (e.g. `promigence-replaced-by`). Never the authorization. */
1908
1934
  headers?: Record<string, string>;
1935
+ /** A `POST` that changes nothing (a file read): safe to send twice, so it is retried as a `GET` is. */
1936
+ replayable?: boolean;
1909
1937
  }
1910
1938
  /** One answer's timing ([`PromigenceClient.lastTiming`]). */
1911
1939
  export interface Timing {
@@ -2011,13 +2039,16 @@ export declare class PromigenceClient {
2011
2039
  * artefact must not go through: it would corrupt binary and cost a second copy of a file that
2012
2040
  * can be gigabytes. This reads the body as an `ArrayBuffer` and hands it back untouched.
2013
2041
  * Errors still arrive as JSON, so a non-2xx answer is decoded and mapped the same way.
2042
+ *
2043
+ * `body` is sent as given with its content type (a JSON question, or a `files put` form).
2044
+ * `read`: the request changes nothing, so a refusal is sent again as a `GET` would be.
2014
2045
  */
2015
2046
  private bytes;
2016
2047
  /**
2017
- * `GET` a byte body **without holding it**: each chunk goes to `onChunk` as it arrives, and
2048
+ * Read a byte body **without holding it**: each chunk goes to `onChunk` as it arrives, and
2018
2049
  * the next is not read until `onChunk` has finished with it, so a 10 GiB artefact costs one
2019
- * chunk of memory. Errors arrive
2020
- * as JSON and are mapped exactly as [`bytes`] maps them; a door refusal is sent again.
2050
+ * chunk of memory. A read only (a `POST` carries its question as `body`), so a door refusal
2051
+ * is sent again. Errors arrive as JSON and are mapped exactly as [`bytes`] maps them.
2021
2052
  */
2022
2053
  private stream;
2023
2054
  /**
@@ -2075,7 +2106,7 @@ export declare class PromigenceClient {
2075
2106
  signal?: AbortSignal;
2076
2107
  }): Promise<Outcomes>;
2077
2108
  readonly billing: {
2078
- /** `GET /billing` — plan, card, period, usage, trial, upcoming invoice. */
2109
+ /** `GET /billing` — plan, credits, auto-reload, card, period, usage, trial, the next plan invoice. */
2079
2110
  usage: () => Promise<BillingUsage>;
2080
2111
  /** `GET /me/ledger?limit=N` — the metered rows the month's total is the sum of (one page). */
2081
2112
  ledger: (limit?: number) => Promise<LedgerEntry[]>;
@@ -2097,6 +2128,29 @@ export declare class PromigenceClient {
2097
2128
  limit?: number;
2098
2129
  cursor?: string;
2099
2130
  }) => Promise<Page<BillingHistoryEntry>>;
2131
+ /** `GET /billing/holds` — what each running or paused sandbox holds of the organization's credits. */
2132
+ holds: () => Promise<M.CreditHolds>;
2133
+ /**
2134
+ * `POST /billing/credits` — a payment page to buy credits, from $5, in whole dollars. The credits are
2135
+ * added when the payment goes through: `settleCredits(session_id)` checks it. An owner's full-access key.
2136
+ */
2137
+ buyCredits: (amountUsd: number) => Promise<M.PaymentPage>;
2138
+ /** `POST /billing/credits/:session_id/settle` — check a credits payment and add its credits if it went through. */
2139
+ settleCredits: (sessionId: string) => Promise<M.CreditsSettled>;
2140
+ /** `GET /billing/auto-reload` — the auto-reload settings, and the authorization turning it on asks for. */
2141
+ autoReload: () => Promise<M.AutoReload>;
2142
+ /**
2143
+ * `PUT /billing/auto-reload` — turn auto-reload on or off, or change it. Turning it on, or changing the level or
2144
+ * the amount, needs `authorizationVersion`: the version of the authorization the owner accepted.
2145
+ */
2146
+ setAutoReload: (opts: {
2147
+ on: boolean;
2148
+ thresholdUsd?: number;
2149
+ amountUsd?: number;
2150
+ authorizationVersion?: string;
2151
+ }) => Promise<M.AutoReload>;
2152
+ /** `POST /billing/disputes/:dispute_id/repay` — a payment page to repay a disputed payment. */
2153
+ repayDispute: (disputeId: string) => Promise<M.PaymentPage>;
2100
2154
  /** `POST /billing/checkout` — the hosted page that puts a card on file. */
2101
2155
  checkout: () => Promise<{
2102
2156
  url: string;
@@ -2109,7 +2163,7 @@ export declare class PromigenceClient {
2109
2163
  }>;
2110
2164
  /** `GET /referrals` — your code and its offer, your referrals by status, what they earned. */
2111
2165
  referrals: () => Promise<ReferralStatus>;
2112
- /** `POST /referrals/apply` — apply a referral code (an owner's key, before the first paid invoice). */
2166
+ /** `POST /referrals/apply` — apply a referral code (an owner's key, before the first payment of at least the reward). */
2113
2167
  applyReferral: (code: string) => Promise<ReferralApplied>;
2114
2168
  };
2115
2169
  private group_billing;
@@ -2173,6 +2227,15 @@ export declare class PromigenceClient {
2173
2227
  create: (org: string, name: string) => Promise<ProjectRecord>;
2174
2228
  /** `PATCH /orgs/:org/projects/:project {name}` — rename it (the `default` project keeps its name). */
2175
2229
  rename: (org: string, project: string, name: string) => Promise<ProjectRecord>;
2230
+ /**
2231
+ * `PATCH /orgs/:org/projects/:project` — rename it, set how long its command output is kept, or
2232
+ * whether its failed sandboxes are kept to reopen (that one on its own). The settings need an owner signed in.
2233
+ */
2234
+ update: (org: string, project: string, update: {
2235
+ name?: string;
2236
+ outputRetentionDays?: number | null;
2237
+ failureCaptures?: boolean | null;
2238
+ }) => Promise<ProjectRecord>;
2176
2239
  };
2177
2240
  };
2178
2241
  private group_orgs;
@@ -2499,7 +2562,7 @@ export declare class PromigenceClient {
2499
2562
  rows?: number;
2500
2563
  cmd?: string;
2501
2564
  /** Stored secret names, or `{name, as}` bindings, put into the terminal's environment
2502
- * by the server. Names travel in a header; values never leave the service. */
2565
+ * by the server. Names travel inside the terminal's connection; values never leave the service. */
2503
2566
  secrets?: SecretBinding[];
2504
2567
  onData?: (data: Uint8Array) => void;
2505
2568
  onExit?: (exit: PtyExit) => void;
@@ -2544,6 +2607,12 @@ export declare class PromigenceClient {
2544
2607
  optimizeFor?: OptimizeFor;
2545
2608
  spendCap?: number;
2546
2609
  }) => Promise<ResumeResult>;
2610
+ /**
2611
+ * `POST /sandboxes/resume {paused_for: "credits"}` — resume every sandbox paused because credits
2612
+ * ran out. Each resume is checked like new work: the answer lists the sandboxes that resumed and
2613
+ * those that still need credits.
2614
+ */
2615
+ resumePausedForCredits: () => Promise<M.SandboxesResumed>;
2547
2616
  /**
2548
2617
  * `POST /sandboxes/:id/snapshot` — photograph a running sandbox and keep it running.
2549
2618
  *
@@ -2656,8 +2725,9 @@ export declare class PromigenceClient {
2656
2725
  }) => Promise<SandboxPatchResponse>;
2657
2726
  /**
2658
2727
  * `get` streams: the body is handed back as bytes and never parsed, because an eval
2659
- * artefact, a trace or a run log is exactly what this is for. `put` sends the file as the
2660
- * body rather than base64 inside JSON.
2728
+ * artefact, a trace or a run log is exactly what this is for. `put` sends the file's bytes
2729
+ * as they are rather than base64 inside JSON. A file's path, a name pattern and a search
2730
+ * text travel in the request body, never in the URL.
2661
2731
  */
2662
2732
  files: {
2663
2733
  /**
@@ -2744,7 +2814,7 @@ export declare class PromigenceClient {
2744
2814
  /**
2745
2815
  * Write a file of **any size**, in [`FILE_PUT_CHUNK_BYTES`] requests.
2746
2816
  *
2747
- * Chunk 0 goes without `?offset=`, which means "this is the file" (create, truncate);
2817
+ * Chunk 0 goes without an `offset`, which means "this is the file" (create, truncate);
2748
2818
  * every later chunk carries one, which appends, so a file has no size limit here.
2749
2819
  *
2750
2820
  * A failure mid-loop leaves a **prefix** in the sandbox, not a corrupt file. Re-running the
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PromigenceClient = exports.LONG_POLL_MS = exports.isKeyedCreate = exports.REPO_CREDENTIALS_FIX = exports.CONTEXT_GIT_RE = exports.IMAGE_DIGEST_RE = exports.MAX_SOURCES = exports.NOTICE_HEADER = exports.isWaiting = exports.FILE_TIMEOUT_MS = void 0;
3
+ exports.PromigenceClient = exports.LONG_POLL_MS = exports.isKeyedCreate = exports.REPO_CREDENTIALS_FIX = exports.CONTEXT_GIT_RE = exports.IMAGE_DIGEST_RE = exports.MAX_SOURCES = exports.NOTICE_HEADER = exports.OUTPUT_RETENTION_MAX_DAYS = exports.isWaiting = exports.FILE_TIMEOUT_MS = void 0;
4
4
  exports.execOutputBytes = execOutputBytes;
5
5
  exports.configDir = configDir;
6
6
  exports.credentialsPath = credentialsPath;
@@ -75,6 +75,26 @@ function bytesToB64(b) {
75
75
  function modeParam(mode) {
76
76
  return typeof mode === "number" ? mode.toString(8) : mode;
77
77
  }
78
+ function jsonBody(value) {
79
+ return { data: new TextEncoder().encode(JSON.stringify(value)), contentType: "application/json" };
80
+ }
81
+ function fileForm(fields, data) {
82
+ const boundary = `promigence-${globalThis.crypto.randomUUID().replace(/-/g, "")}`;
83
+ let head = "";
84
+ for (const [name, value] of fields) {
85
+ head += `--${boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`;
86
+ }
87
+ head += `--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="blob"\r\n`
88
+ + "Content-Type: application/octet-stream\r\n\r\n";
89
+ const enc = new TextEncoder();
90
+ const pre = enc.encode(head);
91
+ const post = enc.encode(`\r\n--${boundary}--\r\n`);
92
+ const out = new Uint8Array(pre.length + data.length + post.length);
93
+ out.set(pre, 0);
94
+ out.set(data, pre.length);
95
+ out.set(post, pre.length + data.length);
96
+ return { data: out, contentType: `multipart/form-data; boundary=${boundary}` };
97
+ }
78
98
  function execOutputBytes(r, stream = "stdout") {
79
99
  const b64 = r[`${stream}_b64`];
80
100
  if (typeof b64 === "string")
@@ -104,6 +124,32 @@ function pageQuery(limit, max, cursor) {
104
124
  q.set("cursor", cursor);
105
125
  return q.toString();
106
126
  }
127
+ exports.OUTPUT_RETENTION_MAX_DAYS = 30;
128
+ function projectUpdateBody(u) {
129
+ const body = {};
130
+ if (u.name !== undefined)
131
+ body["name"] = u.name;
132
+ if (u.outputRetentionDays !== undefined) {
133
+ const d = u.outputRetentionDays;
134
+ if (d !== null && (!Number.isInteger(d) || d < 1 || d > exports.OUTPUT_RETENTION_MAX_DAYS)) {
135
+ throw new core_js_1.InvalidArgumentError(`outputRetentionDays must be a whole number from 1 to ${exports.OUTPUT_RETENTION_MAX_DAYS}, or null, not ${String(d)}`, "Pass null for the organization's setting.");
136
+ }
137
+ body["output_retention_days"] = d;
138
+ }
139
+ if (u.failureCaptures !== undefined) {
140
+ if (u.failureCaptures !== null && typeof u.failureCaptures !== "boolean") {
141
+ throw new core_js_1.InvalidArgumentError("failureCaptures must be true, false or null", "Pass null for the organization's setting.");
142
+ }
143
+ if (Object.keys(body).length) {
144
+ throw new core_js_1.InvalidArgumentError("failureCaptures is set on its own", "Change it in a call of its own, without name or outputRetentionDays.");
145
+ }
146
+ body["failure_captures"] = u.failureCaptures;
147
+ }
148
+ if (!Object.keys(body).length) {
149
+ throw new core_js_1.InvalidArgumentError("projects.update needs something to change", "Pass name, outputRetentionDays or failureCaptures.");
150
+ }
151
+ return body;
152
+ }
107
153
  function expiryFields(o) {
108
154
  const body = {};
109
155
  if (o.expiresInDays !== undefined)
@@ -976,7 +1022,8 @@ class PromigenceClient {
976
1022
  const idempotencyKey = opts.idempotencyKey ?? (keyedCreate ? newIdempotencyKey() : undefined);
977
1023
  if (idempotencyKey)
978
1024
  headers["idempotency-key"] = idempotencyKey;
979
- const replayable = opts.method === "GET" || opts.method === "DELETE" || (keyedCreate && Boolean(idempotencyKey));
1025
+ const replayable = opts.method === "GET" || opts.method === "DELETE" || opts.replayable === true
1026
+ || (keyedCreate && Boolean(idempotencyKey));
980
1027
  const timeoutMs = opts.timeoutMs ?? this.requestTimeoutMs;
981
1028
  let dispatcher;
982
1029
  let resentDelete = false;
@@ -1173,7 +1220,7 @@ class PromigenceClient {
1173
1220
  }
1174
1221
  }
1175
1222
  }
1176
- async bytes(method, path, body, opts = {}) {
1223
+ async bytes(method, path, body, opts = {}, read = method === "GET") {
1177
1224
  const url = `${this.apiUrl}${path}`;
1178
1225
  const headers = {
1179
1226
  accept: "application/octet-stream, application/json",
@@ -1183,7 +1230,7 @@ class PromigenceClient {
1183
1230
  if (this.caller)
1184
1231
  headers[caller_js_1.CALLER_HEADER] = this.caller;
1185
1232
  if (body !== undefined)
1186
- headers["content-type"] = "application/octet-stream";
1233
+ headers["content-type"] = body.contentType;
1187
1234
  const signals = [AbortSignal.timeout(opts.timeoutMs ?? exports.FILE_TIMEOUT_MS)];
1188
1235
  if (opts.signal)
1189
1236
  signals.push(opts.signal);
@@ -1197,7 +1244,7 @@ class PromigenceClient {
1197
1244
  method,
1198
1245
  headers,
1199
1246
  signal,
1200
- ...(body === undefined ? {} : { body: body }),
1247
+ ...(body === undefined ? {} : { body: body.data }),
1201
1248
  ...(dispatcher === undefined ? {} : { dispatcher }),
1202
1249
  });
1203
1250
  }
@@ -1217,7 +1264,7 @@ class PromigenceClient {
1217
1264
  }
1218
1265
  catch { }
1219
1266
  const retryAfterMs = parseRetryAfter(res.headers.get("retry-after"));
1220
- const wait = this.refusalBackoff(res.status, retryAfterMs, payload.error?.code, attempt, method === "GET", opts.signal);
1267
+ const wait = this.refusalBackoff(res.status, retryAfterMs, payload.error?.code, attempt, read, opts.signal);
1221
1268
  if (wait !== undefined) {
1222
1269
  this.stats.retries++;
1223
1270
  await sleep(wait);
@@ -1231,7 +1278,7 @@ class PromigenceClient {
1231
1278
  });
1232
1279
  }
1233
1280
  }
1234
- async stream(path, onChunk, opts = {}) {
1281
+ async stream(method, path, onChunk, opts = {}, body) {
1235
1282
  const url = `${this.apiUrl}${path}`;
1236
1283
  const headers = {
1237
1284
  accept: "application/octet-stream, application/json",
@@ -1240,17 +1287,25 @@ class PromigenceClient {
1240
1287
  };
1241
1288
  if (this.caller)
1242
1289
  headers[caller_js_1.CALLER_HEADER] = this.caller;
1290
+ if (body !== undefined)
1291
+ headers["content-type"] = body.contentType;
1243
1292
  const signals = [AbortSignal.timeout(opts.timeoutMs ?? exports.FILE_TIMEOUT_MS)];
1244
1293
  if (opts.signal)
1245
1294
  signals.push(opts.signal);
1246
1295
  const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
1247
- const timedOut = () => new core_js_1.TimeoutError(`GET ${path} timed out after ${opts.timeoutMs ?? exports.FILE_TIMEOUT_MS} ms`, "Retry; for a large artefact raise timeoutMs.");
1296
+ const timedOut = () => new core_js_1.TimeoutError(`${method} ${path} timed out after ${opts.timeoutMs ?? exports.FILE_TIMEOUT_MS} ms`, "Retry; for a large artefact raise timeoutMs.");
1248
1297
  const dispatcher = this.guardDispatcher();
1249
1298
  for (let attempt = 0;; attempt++) {
1250
1299
  let res;
1251
1300
  this.stats.requests++;
1252
1301
  try {
1253
- res = await this.fetchImpl(url, { method: "GET", headers, signal, ...(dispatcher === undefined ? {} : { dispatcher }) });
1302
+ res = await this.fetchImpl(url, {
1303
+ method,
1304
+ headers,
1305
+ signal,
1306
+ ...(body === undefined ? {} : { body: body.data }),
1307
+ ...(dispatcher === undefined ? {} : { dispatcher }),
1308
+ });
1254
1309
  }
1255
1310
  catch (err) {
1256
1311
  const e = err;
@@ -1299,7 +1354,7 @@ class PromigenceClient {
1299
1354
  continue;
1300
1355
  }
1301
1356
  const code = payload.error?.code ?? codeForStatus(res.status);
1302
- throw errorFor(code, payload.error?.message ?? `HTTP ${res.status} from GET ${path}`, payload.error?.fix ?? "See the API response for details.", {
1357
+ throw errorFor(code, payload.error?.message ?? `HTTP ${res.status} from ${method} ${path}`, payload.error?.fix ?? "See the API response for details.", {
1303
1358
  status: res.status,
1304
1359
  ...bodyExtra(payload),
1305
1360
  ...(retryAfterMs === undefined ? {} : { retryAfterMs }),
@@ -1378,6 +1433,29 @@ class PromigenceClient {
1378
1433
  const r = await this.request({ method: "GET", path: `/billing/history?${q.toString()}` });
1379
1434
  return pageOf(r, "entries", limit);
1380
1435
  },
1436
+ holds: () => this.request({ method: "GET", path: "/billing/holds" }),
1437
+ buyCredits: (amountUsd) => this.request({ method: "POST", path: "/billing/credits", body: { amount_usd: amountUsd } }),
1438
+ settleCredits: (sessionId) => {
1439
+ if (!sessionId?.trim())
1440
+ throw new core_js_1.InvalidArgumentError("billing.settleCredits needs a session id", "Pass the session_id buyCredits() answered.");
1441
+ return this.request({ method: "POST", path: `/billing/credits/${encodeURIComponent(sessionId)}/settle`, body: {} });
1442
+ },
1443
+ autoReload: () => this.request({ method: "GET", path: "/billing/auto-reload" }),
1444
+ setAutoReload: (opts) => this.request({
1445
+ method: "PUT",
1446
+ path: "/billing/auto-reload",
1447
+ body: {
1448
+ on: opts.on,
1449
+ ...(opts.thresholdUsd === undefined ? {} : { threshold_usd: opts.thresholdUsd }),
1450
+ ...(opts.amountUsd === undefined ? {} : { amount_usd: opts.amountUsd }),
1451
+ ...(opts.authorizationVersion === undefined ? {} : { authorization_version: opts.authorizationVersion }),
1452
+ },
1453
+ }),
1454
+ repayDispute: (disputeId) => {
1455
+ if (!disputeId?.trim())
1456
+ throw new core_js_1.InvalidArgumentError("billing.repayDispute needs a dispute id", "Pass the hold's dispute_id from billing.usage().");
1457
+ return this.request({ method: "POST", path: `/billing/disputes/${encodeURIComponent(disputeId)}/repay`, body: {} });
1458
+ },
1381
1459
  checkout: () => this.request({ method: "POST", path: "/billing/checkout", body: {} }),
1382
1460
  portal: () => this.request({ method: "POST", path: "/billing/portal", body: {} }),
1383
1461
  referrals: () => this.request({ method: "GET", path: "/referrals" }),
@@ -1436,6 +1514,9 @@ class PromigenceClient {
1436
1514
  rename: (org, project, name) => this.request({
1437
1515
  method: "PATCH", path: `/orgs/${orgSeg(org)}/projects/${encodeURIComponent(project)}`, body: { name },
1438
1516
  }),
1517
+ update: async (org, project, update) => this.request({
1518
+ method: "PATCH", path: `/orgs/${orgSeg(org)}/projects/${encodeURIComponent(project)}`, body: projectUpdateBody(update),
1519
+ }),
1439
1520
  },
1440
1521
  };
1441
1522
  }
@@ -2184,6 +2265,7 @@ class PromigenceClient {
2184
2265
  ...(opts.spendCap === undefined ? {} : { spend_cap: checkSpendCap(opts.spendCap) }),
2185
2266
  },
2186
2267
  }),
2268
+ resumePausedForCredits: () => this.request({ method: "POST", path: "/sandboxes/resume", body: { paused_for: "credits" } }),
2187
2269
  snapshot: (sandboxId, opts = {}) => this.request({
2188
2270
  method: "POST",
2189
2271
  path: `/sandboxes/${encodeURIComponent(sandboxId)}/snapshot`,
@@ -2254,38 +2336,42 @@ class PromigenceClient {
2254
2336
  },
2255
2337
  files: {
2256
2338
  list: (sandboxId, path = "/", opts = {}) => {
2257
- const q = [`path=${encodeURIComponent(path)}`];
2339
+ const body = { path };
2258
2340
  if (opts.recursive)
2259
- q.push("recursive=1");
2341
+ body.recursive = true;
2260
2342
  if (opts.depth !== undefined)
2261
- q.push(`depth=${Math.max(0, Math.floor(opts.depth))}`);
2343
+ body.depth = Math.max(0, Math.floor(opts.depth));
2262
2344
  if (opts.limit !== undefined)
2263
- q.push(`limit=${Math.max(1, Math.floor(opts.limit))}`);
2345
+ body.limit = Math.max(1, Math.floor(opts.limit));
2264
2346
  if (opts.hidden === false)
2265
- q.push("hidden=0");
2347
+ body.hidden = false;
2266
2348
  return this.request({
2267
- method: "GET",
2268
- path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/list?${q.join("&")}`,
2349
+ method: "POST",
2350
+ path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/list`,
2351
+ body,
2352
+ replayable: true,
2269
2353
  });
2270
2354
  },
2271
2355
  search: (sandboxId, path, opts = {}) => {
2272
2356
  if (!opts.pattern && !opts.contains) {
2273
2357
  throw new core_js_1.InvalidArgumentError("files.search needs `pattern`, `contains`, or both", "Pass { pattern: '*.py' } for a name glob, { contains: 'TODO' } for text inside files.");
2274
2358
  }
2275
- const q = [`path=${encodeURIComponent(path)}`];
2359
+ const body = { path };
2276
2360
  if (opts.pattern)
2277
- q.push(`pattern=${encodeURIComponent(opts.pattern)}`);
2361
+ body.pattern = opts.pattern;
2278
2362
  if (opts.contains)
2279
- q.push(`contains=${encodeURIComponent(opts.contains)}`);
2363
+ body.contains = opts.contains;
2280
2364
  if (opts.ignoreCase)
2281
- q.push("ignore_case=1");
2365
+ body.ignore_case = true;
2282
2366
  if (opts.limit !== undefined)
2283
- q.push(`limit=${Math.max(1, Math.floor(opts.limit))}`);
2367
+ body.limit = Math.max(1, Math.floor(opts.limit));
2284
2368
  if (opts.hidden === false)
2285
- q.push("hidden=0");
2369
+ body.hidden = false;
2286
2370
  return this.request({
2287
- method: "GET",
2288
- path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/search?${q.join("&")}`,
2371
+ method: "POST",
2372
+ path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/search`,
2373
+ body,
2374
+ replayable: true,
2289
2375
  });
2290
2376
  },
2291
2377
  mkdir: (sandboxId, path, opts = {}) => this.request({
@@ -2342,11 +2428,13 @@ class PromigenceClient {
2342
2428
  body: { archive, into, ...(opts.strip === undefined ? {} : { strip: Math.max(0, Math.floor(opts.strip)) }) },
2343
2429
  }),
2344
2430
  stat: (sandboxId, path) => this.request({
2345
- method: "GET",
2346
- path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/stat?path=${encodeURIComponent(path)}`,
2431
+ method: "POST",
2432
+ path: `/sandboxes/${encodeURIComponent(sandboxId)}/files/stat`,
2433
+ body: { path },
2434
+ replayable: true,
2347
2435
  }),
2348
- get: (sandboxId, path, opts = {}) => this.bytes("GET", `/sandboxes/${encodeURIComponent(sandboxId)}/files?path=${encodeURIComponent(path)}`, undefined, opts),
2349
- download: (sandboxId, path, onChunk, opts = {}) => this.stream(`/sandboxes/${encodeURIComponent(sandboxId)}/files?path=${encodeURIComponent(path)}`, onChunk, opts),
2436
+ get: (sandboxId, path, opts = {}) => this.bytes("POST", `/sandboxes/${encodeURIComponent(sandboxId)}/files/read`, jsonBody({ path }), opts, true),
2437
+ download: (sandboxId, path, onChunk, opts = {}) => this.stream("POST", `/sandboxes/${encodeURIComponent(sandboxId)}/files/read`, onChunk, opts, jsonBody({ path })),
2350
2438
  text: async (sandboxId, path, opts = {}) => new TextDecoder().decode(await this.sandboxes.files.get(sandboxId, path, opts)),
2351
2439
  put: async (sandboxId, path, data, opts = {}) => {
2352
2440
  const body = typeof data === "string" ? new TextEncoder().encode(data) : data;
@@ -2356,8 +2444,7 @@ class PromigenceClient {
2356
2444
  }, opts);
2357
2445
  },
2358
2446
  putFrom: async (sandboxId, path, source, opts = {}) => {
2359
- const mode = opts.mode === undefined ? "" : `&mode=${opts.mode.toString(8)}`;
2360
- const base = `/sandboxes/${encodeURIComponent(sandboxId)}/files?path=${encodeURIComponent(path)}${mode}`;
2447
+ const url = `/sandboxes/${encodeURIComponent(sandboxId)}/files`;
2361
2448
  let sent = 0;
2362
2449
  let chunks = 0;
2363
2450
  let last;
@@ -2366,7 +2453,12 @@ class PromigenceClient {
2366
2453
  if (slice.length === 0 && sent < source.size) {
2367
2454
  throw new core_js_1.InvalidArgumentError(`the source of ${path} ended at ${sent} of ${source.size} bytes`, "The file shrank while it was being sent; send it again once nothing is writing it.");
2368
2455
  }
2369
- const raw = await this.bytes("PUT", sent === 0 ? base : `${base}&offset=${sent}`, slice, opts);
2456
+ const fields = [["path", path]];
2457
+ if (opts.mode !== undefined)
2458
+ fields.push(["mode", opts.mode.toString(8)]);
2459
+ if (sent > 0)
2460
+ fields.push(["offset", String(sent)]);
2461
+ const raw = await this.bytes("PUT", url, fileForm(fields, slice), opts);
2370
2462
  last = JSON.parse(new TextDecoder().decode(raw));
2371
2463
  sent += slice.length;
2372
2464
  chunks += 1;
@@ -2375,8 +2467,8 @@ class PromigenceClient {
2375
2467
  },
2376
2468
  delete: (sandboxId, path, opts = {}) => this.request({
2377
2469
  method: "DELETE",
2378
- path: `/sandboxes/${encodeURIComponent(sandboxId)}/files?path=${encodeURIComponent(path)}`
2379
- + (opts.recursive ? "&recursive=1" : ""),
2470
+ path: `/sandboxes/${encodeURIComponent(sandboxId)}/files`,
2471
+ body: { path, ...(opts.recursive ? { recursive: true } : {}) },
2380
2472
  }),
2381
2473
  },
2382
2474
  output: async (execId, opts = {}) => {
@@ -1,5 +1,5 @@
1
1
  /** Promigence SDK — constants, error hierarchy and shared types. */
2
- export declare const VERSION = "0.1.0-beta.13";
2
+ export declare const VERSION = "0.1.0-beta.15";
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.13";
10
+ exports.VERSION = "0.1.0-beta.15";
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";
@@ -271,6 +271,8 @@ exports.EXIT_BY_CODE = {
271
271
  project_cap_reached: 5,
272
272
  spend_limit_reached: 5,
273
273
  billing_hold: 5,
274
+ credits_exhausted: 5, purchase_limit: 5, purchases_blocked: 5,
275
+ card_needed: 5, auto_reload_unavailable: 5,
274
276
  tier_over_plan: 2,
275
277
  network_host_refused: 9, network_policy_unsupported: 3,
276
278
  secrets_in_snapshot: 2, secrets_unavailable: 3,
@@ -189,6 +189,7 @@ export declare const __http2Internals: {
189
189
  closedReason: string;
190
190
  messages: number;
191
191
  posts: number;
192
+ startedAt: number;
192
193
  } | undefined;
193
194
  /** The transport thread's number of connections and spacing between new requests on one, for an environment (`threadKnobs`). */
194
195
  threadKnobs(env: NodeJS.ProcessEnv): {