ppio-sandbox 2.0.0-a2 → 2.0.2-a1

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.
@@ -60,7 +60,7 @@ import createClient from "openapi-fetch";
60
60
  import platform2 from "platform";
61
61
 
62
62
  // package.json
63
- var version = "2.0.0-a2";
63
+ var version = "2.0.2-a1";
64
64
 
65
65
  // src/core/utils.ts
66
66
  import platform from "platform";
@@ -268,6 +268,12 @@ var VolumeError = class extends Error {
268
268
  this.name = "VolumeError";
269
269
  }
270
270
  };
271
+ var SecretError = class extends SandboxError {
272
+ constructor(message, stackTrace) {
273
+ super(message, stackTrace);
274
+ this.name = "SecretError";
275
+ }
276
+ };
271
277
 
272
278
  // src/core/logs.ts
273
279
  function formatLog(log) {
@@ -311,10 +317,10 @@ function createRpcLogger(logger) {
311
317
  }
312
318
  function createApiLogger(logger) {
313
319
  return {
314
- async onRequest({ request }) {
320
+ async onRequest({ request: request2 }) {
315
321
  var _a3;
316
- (_a3 = logger.info) == null ? void 0 : _a3.call(logger, `Request ${request.method} ${request.url}`);
317
- return request;
322
+ (_a3 = logger.info) == null ? void 0 : _a3.call(logger, `Request ${request2.method} ${request2.url}`);
323
+ return request2;
318
324
  },
319
325
  async onResponse({ response }) {
320
326
  var _a3, _b;
@@ -390,12 +396,27 @@ var REQUEST_TIMEOUT_MS = 6e4;
390
396
  var DEFAULT_SANDBOX_TIMEOUT_MS = 3e5;
391
397
  var KEEPALIVE_PING_INTERVAL_SEC = 50;
392
398
  var KEEPALIVE_PING_HEADER = "Keepalive-Ping-Interval";
399
+ var LEGACY_DOMAINS = ["sandbox.ppio.cn", "cn-beijing-1.sandbox.ppio.com"];
400
+ function isLegacyDomain(domain) {
401
+ return domain ? LEGACY_DOMAINS.includes(domain) : true;
402
+ }
403
+ function raiseIfLegacy(config, feature, ErrorType = Error) {
404
+ if (config.isLegacyDomain) {
405
+ throw new ErrorType(`${feature} is not supported on legacy domains`);
406
+ }
407
+ }
408
+ function validateDomain(domain) {
409
+ if (!domain) {
410
+ throw new Error("Domain cannot be empty");
411
+ }
412
+ }
393
413
  var _ConnectionConfig = class _ConnectionConfig {
394
414
  constructor(opts) {
395
415
  var _a3;
396
416
  this.apiKey = (opts == null ? void 0 : opts.apiKey) || _ConnectionConfig.apiKey;
397
417
  this.debug = (opts == null ? void 0 : opts.debug) || _ConnectionConfig.debug;
398
418
  this.domain = (opts == null ? void 0 : opts.domain) || _ConnectionConfig.domain;
419
+ this.isLegacyDomain = isLegacyDomain(this.domain);
399
420
  this.accessToken = (opts == null ? void 0 : opts.accessToken) || _ConnectionConfig.accessToken;
400
421
  this.requestTimeoutMs = (_a3 = opts == null ? void 0 : opts.requestTimeoutMs) != null ? _a3 : REQUEST_TIMEOUT_MS;
401
422
  this.logger = opts == null ? void 0 : opts.logger;
@@ -805,6 +826,9 @@ var Filesystem2 = class {
805
826
  return new Uint8Array(res.data);
806
827
  }
807
828
  if (res.response.headers.get("content-length") === "0") {
829
+ if (format === "blob") {
830
+ return new Blob([]);
831
+ }
808
832
  return "";
809
833
  }
810
834
  return res.data;
@@ -2317,64 +2341,7 @@ var Git = class {
2317
2341
  }
2318
2342
  };
2319
2343
 
2320
- // src/core/volume/client.ts
2321
- import createClient4 from "openapi-fetch";
2322
- var FILE_TIMEOUT_MS = 36e5;
2323
- var VolumeConnectionConfig = class _VolumeConnectionConfig {
2324
- constructor(volume, opts) {
2325
- var _a3, _b;
2326
- this.domain = (opts == null ? void 0 : opts.domain) || volume.domain || _VolumeConnectionConfig.domain;
2327
- this.debug = (_b = (_a3 = opts == null ? void 0 : opts.debug) != null ? _a3 : volume.debug) != null ? _b : _VolumeConnectionConfig.debug;
2328
- this.apiUrl = (opts == null ? void 0 : opts.apiUrl) || _VolumeConnectionConfig.volumeApiUrl || (this.debug ? "http://localhost:8080" : `https://api.${this.domain}`);
2329
- this.token = (opts == null ? void 0 : opts.token) || volume.token;
2330
- this.headers = opts == null ? void 0 : opts.headers;
2331
- this.logger = opts == null ? void 0 : opts.logger;
2332
- this.requestTimeoutMs = opts == null ? void 0 : opts.requestTimeoutMs;
2333
- }
2334
- static get domain() {
2335
- return getEnvVar("PPIO_DOMAIN") || "sandbox.ppio.cn";
2336
- }
2337
- static get debug() {
2338
- return (getEnvVar("PPIO_DEBUG") || "false").toLowerCase() === "true";
2339
- }
2340
- static get volumeApiUrl() {
2341
- return getEnvVar("PPIO_SANDBOX_VOLUME_API_URL");
2342
- }
2343
- getSignal(requestTimeoutMs) {
2344
- const timeout = requestTimeoutMs != null ? requestTimeoutMs : this.requestTimeoutMs;
2345
- return timeout ? AbortSignal.timeout(timeout) : void 0;
2346
- }
2347
- };
2348
- var VolumeApiClient = class {
2349
- constructor(config) {
2350
- this.api = createClient4({
2351
- baseUrl: config.apiUrl,
2352
- headers: __spreadValues(__spreadValues(__spreadValues({}, defaultHeaders), config.token && { Authorization: `Bearer ${config.token}` }), config.headers)
2353
- });
2354
- if (config.logger) {
2355
- this.api.use(createApiLogger(config.logger));
2356
- }
2357
- }
2358
- };
2359
-
2360
- // src/core/volume/types.ts
2361
- var VolumeFileType = /* @__PURE__ */ ((VolumeFileType2) => {
2362
- VolumeFileType2["UNKNOWN"] = "unknown";
2363
- VolumeFileType2["FILE"] = "file";
2364
- VolumeFileType2["DIRECTORY"] = "directory";
2365
- VolumeFileType2["SYMLINK"] = "symlink";
2366
- return VolumeFileType2;
2367
- })(VolumeFileType || {});
2368
-
2369
2344
  // src/core/volume/index.ts
2370
- function convertVolumeEntryStat(entry) {
2371
- return __spreadProps(__spreadValues({}, entry), {
2372
- type: entry.type,
2373
- atime: new Date(entry.atime),
2374
- mtime: new Date(entry.mtime),
2375
- ctime: new Date(entry.ctime)
2376
- });
2377
- }
2378
2345
  var Volume = class _Volume {
2379
2346
  /**
2380
2347
  * Create a local Volume instance with no API call.
@@ -2386,6 +2353,9 @@ var Volume = class _Volume {
2386
2353
  * @param debug whether to use debug mode.
2387
2354
  */
2388
2355
  constructor(volumeId, name, token, domain, debug) {
2356
+ if (domain) {
2357
+ validateDomain(domain);
2358
+ }
2389
2359
  this.volumeId = volumeId;
2390
2360
  this.name = name;
2391
2361
  this.token = token;
@@ -2397,15 +2367,20 @@ var Volume = class _Volume {
2397
2367
  *
2398
2368
  * @param name name of the volume.
2399
2369
  * @param opts connection options.
2370
+ * @param [opts.quotaSizeGiB] capacity quota in GiB.
2371
+ * @param [opts.quotaInodes] inode quota.
2400
2372
  *
2401
2373
  * @returns new Volume instance.
2402
2374
  */
2403
2375
  static async create(name, opts) {
2404
2376
  const config = new ConnectionConfig(opts);
2377
+ raiseIfLegacy(config, "Volume.create", VolumeError);
2405
2378
  const client = new ApiClient(config);
2406
2379
  const res = await client.api.POST("/volumes", {
2407
2380
  body: {
2408
- name
2381
+ name,
2382
+ quotaSizeGiB: opts == null ? void 0 : opts.quotaSizeGiB,
2383
+ quotaInodes: opts == null ? void 0 : opts.quotaInodes
2409
2384
  },
2410
2385
  signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2411
2386
  });
@@ -2434,6 +2409,7 @@ var Volume = class _Volume {
2434
2409
  */
2435
2410
  static async connect(volumeId, opts) {
2436
2411
  const config = new ConnectionConfig(opts);
2412
+ raiseIfLegacy(config, "Volume.connect", VolumeError);
2437
2413
  const { name, token } = await _Volume.getInfo(volumeId, opts);
2438
2414
  return new _Volume(volumeId, name, token, config.domain, config.debug);
2439
2415
  }
@@ -2446,7 +2422,9 @@ var Volume = class _Volume {
2446
2422
  * @returns volume information.
2447
2423
  */
2448
2424
  static async getInfo(volumeId, opts) {
2425
+ var _a3, _b, _c, _d;
2449
2426
  const config = new ConnectionConfig(opts);
2427
+ raiseIfLegacy(config, "Volume.getInfo", VolumeError);
2450
2428
  const client = new ApiClient(config);
2451
2429
  const res = await client.api.GET("/volumes/{volumeID}", {
2452
2430
  params: {
@@ -2463,10 +2441,15 @@ var Volume = class _Volume {
2463
2441
  if (err) {
2464
2442
  throw err;
2465
2443
  }
2444
+ const data = res.data;
2466
2445
  return {
2467
- volumeId: res.data.volumeID,
2468
- name: res.data.name,
2469
- token: res.data.token
2446
+ volumeId: data.volumeID,
2447
+ name: data.name,
2448
+ token: data.token,
2449
+ quotaSizeGiB: (_a3 = data.quotaSizeGiB) != null ? _a3 : 0,
2450
+ quotaInodes: (_b = data.quotaInodes) != null ? _b : 0,
2451
+ usedSizeBytes: (_c = data.usedSizeBytes) != null ? _c : 0,
2452
+ usedInodes: (_d = data.usedInodes) != null ? _d : 0
2470
2453
  };
2471
2454
  }
2472
2455
  /**
@@ -2479,6 +2462,7 @@ var Volume = class _Volume {
2479
2462
  static async list(opts) {
2480
2463
  var _a3;
2481
2464
  const config = new ConnectionConfig(opts);
2465
+ raiseIfLegacy(config, "Volume.list", VolumeError);
2482
2466
  const client = new ApiClient(config);
2483
2467
  const res = await client.api.GET("/volumes", {
2484
2468
  signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
@@ -2487,10 +2471,61 @@ var Volume = class _Volume {
2487
2471
  if (err) {
2488
2472
  throw err;
2489
2473
  }
2490
- return ((_a3 = res.data) != null ? _a3 : []).map((vol) => ({
2491
- volumeId: vol.volumeID,
2492
- name: vol.name
2493
- }));
2474
+ return ((_a3 = res.data) != null ? _a3 : []).map((vol) => {
2475
+ var _a4, _b, _c, _d;
2476
+ return {
2477
+ volumeId: vol.volumeID,
2478
+ name: vol.name,
2479
+ quotaSizeGiB: (_a4 = vol.quotaSizeGiB) != null ? _a4 : 0,
2480
+ quotaInodes: (_b = vol.quotaInodes) != null ? _b : 0,
2481
+ usedSizeBytes: (_c = vol.usedSizeBytes) != null ? _c : 0,
2482
+ usedInodes: (_d = vol.usedInodes) != null ? _d : 0
2483
+ };
2484
+ });
2485
+ }
2486
+ /**
2487
+ * Update volume quota.
2488
+ *
2489
+ * @param volumeId volume ID.
2490
+ * @param params quota parameters.
2491
+ * @param opts connection options.
2492
+ *
2493
+ * @returns updated volume information.
2494
+ */
2495
+ static async updateQuota(volumeId, params, opts) {
2496
+ var _a3, _b, _c, _d;
2497
+ const config = new ConnectionConfig(opts);
2498
+ raiseIfLegacy(config, "Volume.updateQuota", VolumeError);
2499
+ const res = await fetch(
2500
+ `${config.apiUrl}/volumes/${encodeURIComponent(volumeId)}`,
2501
+ {
2502
+ method: "PATCH",
2503
+ headers: __spreadValues(__spreadValues(__spreadProps(__spreadValues({}, config.headers), {
2504
+ "Content-Type": "application/json"
2505
+ }), config.apiKey ? { "X-API-KEY": config.apiKey } : {}), config.accessToken ? { Authorization: `Bearer ${config.accessToken}` } : {}),
2506
+ body: JSON.stringify({
2507
+ quotaSizeGiB: params.quotaSizeGiB,
2508
+ quotaInodes: params.quotaInodes
2509
+ }),
2510
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2511
+ }
2512
+ );
2513
+ if (res.status === 404) {
2514
+ throw new NotFoundError(`Volume ${volumeId} not found`);
2515
+ }
2516
+ if (!res.ok) {
2517
+ throw new VolumeError(`${res.status}: ${res.statusText}`);
2518
+ }
2519
+ const data = await res.json();
2520
+ return {
2521
+ volumeId: data.volumeID,
2522
+ name: data.name,
2523
+ token: data.token,
2524
+ quotaSizeGiB: (_a3 = data.quotaSizeGiB) != null ? _a3 : 0,
2525
+ quotaInodes: (_b = data.quotaInodes) != null ? _b : 0,
2526
+ usedSizeBytes: (_c = data.usedSizeBytes) != null ? _c : 0,
2527
+ usedInodes: (_d = data.usedInodes) != null ? _d : 0
2528
+ };
2494
2529
  }
2495
2530
  /**
2496
2531
  * Destroy a volume.
@@ -2500,6 +2535,7 @@ var Volume = class _Volume {
2500
2535
  */
2501
2536
  static async destroy(volumeId, opts) {
2502
2537
  const config = new ConnectionConfig(opts);
2538
+ raiseIfLegacy(config, "Volume.destroy", VolumeError);
2503
2539
  const client = new ApiClient(config);
2504
2540
  const res = await client.api.DELETE("/volumes/{volumeID}", {
2505
2541
  params: {
@@ -2518,296 +2554,196 @@ var Volume = class _Volume {
2518
2554
  }
2519
2555
  return true;
2520
2556
  }
2521
- /**
2522
- * List directory contents.
2523
- *
2524
- * @param path path to the directory.
2525
- * @param opts connection options.
2526
- * @param [opts.depth] number of layers deep to recurse into the directory (default: 1).
2527
- *
2528
- * @returns list of entries in the directory.
2529
- */
2530
- async list(path2, opts) {
2531
- const config = new VolumeConnectionConfig(this, opts);
2532
- const client = new VolumeApiClient(config);
2533
- const res = await client.api.GET("/volumecontent/{volumeID}/dir", {
2534
- params: {
2535
- path: {
2536
- volumeID: this.volumeId
2537
- },
2538
- query: {
2539
- path: path2,
2540
- depth: opts == null ? void 0 : opts.depth
2541
- }
2542
- },
2543
- signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2544
- });
2545
- if (res.response.status === 404) {
2546
- throw new NotFoundError(`Path ${path2} not found`);
2547
- }
2548
- const err = handleApiError(res, VolumeError);
2549
- if (err) {
2550
- throw err;
2551
- }
2552
- const entries = Array.isArray(res.data) ? res.data : [];
2553
- return entries.map(convertVolumeEntryStat);
2557
+ };
2558
+
2559
+ // src/core/secret/index.ts
2560
+ var SECRET_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
2561
+ var ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
2562
+ var HOST_PATTERN = /^(\*\.)?[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
2563
+ function validateSecretEnvs(secretEnvs, envs) {
2564
+ if (!secretEnvs) {
2565
+ return {};
2554
2566
  }
2555
- /**
2556
- * Create a directory.
2557
- *
2558
- * @param path path to the directory to create.
2559
- * @param options directory creation options.
2560
- * @param opts connection options.
2561
- */
2562
- async makeDir(path2, opts) {
2563
- const config = new VolumeConnectionConfig(this, opts);
2564
- const client = new VolumeApiClient(config);
2565
- const res = await client.api.POST("/volumecontent/{volumeID}/dir", {
2566
- params: {
2567
- path: {
2568
- volumeID: this.volumeId
2569
- },
2570
- query: {
2571
- path: path2,
2572
- uid: opts == null ? void 0 : opts.uid,
2573
- gid: opts == null ? void 0 : opts.gid,
2574
- mode: opts == null ? void 0 : opts.mode,
2575
- force: opts == null ? void 0 : opts.force
2576
- }
2577
- },
2578
- signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2579
- });
2580
- if (res.response.status === 404) {
2581
- throw new NotFoundError(`Path ${path2} not found`);
2582
- }
2583
- const err = handleApiError(res, VolumeError);
2584
- if (err) {
2585
- throw err;
2567
+ if (typeof secretEnvs !== "object" || Array.isArray(secretEnvs)) {
2568
+ throw new InvalidArgumentError("secretEnvs must be a mapping");
2569
+ }
2570
+ const envNames = new Set(Object.keys(envs != null ? envs : {}));
2571
+ const validated = {};
2572
+ for (const [envName, secretName] of Object.entries(secretEnvs)) {
2573
+ if (!ENV_NAME_PATTERN.test(envName)) {
2574
+ throw new InvalidArgumentError(
2575
+ `secretEnvs key ${JSON.stringify(envName)} must be a valid sandbox environment variable name`
2576
+ );
2586
2577
  }
2587
- if (!res.data) {
2588
- throw new Error("Response data is missing");
2578
+ if (envNames.has(envName)) {
2579
+ throw new InvalidArgumentError(
2580
+ `envs and secretEnvs both define ${JSON.stringify(envName)}`
2581
+ );
2589
2582
  }
2590
- return convertVolumeEntryStat(
2591
- res.data
2583
+ validateSecretName(secretName);
2584
+ validated[envName] = secretName;
2585
+ }
2586
+ return validated;
2587
+ }
2588
+ var _Secret = class _Secret {
2589
+ static async create(params, opts) {
2590
+ validateSecretName(params.name);
2591
+ if (!params.value) {
2592
+ throw new InvalidArgumentError("value is required");
2593
+ }
2594
+ const body = {
2595
+ name: params.name,
2596
+ value: params.value,
2597
+ hosts: normalizeSecretHosts(params.hosts)
2598
+ };
2599
+ if (params.description !== void 0) {
2600
+ body.description = params.description;
2601
+ }
2602
+ return bindingFromJson(
2603
+ await request(
2604
+ "POST",
2605
+ _Secret.base,
2606
+ "Secret.create",
2607
+ opts,
2608
+ body
2609
+ )
2592
2610
  );
2593
2611
  }
2594
- /**
2595
- * Get information about a file or directory.
2596
- *
2597
- * @param path path to the file or directory.
2598
- * @param opts connection options.
2599
- *
2600
- * @returns information about the entry.
2601
- */
2602
- async getInfo(path2, opts) {
2603
- const config = new VolumeConnectionConfig(this, opts);
2604
- const client = new VolumeApiClient(config);
2605
- const res = await client.api.GET("/volumecontent/{volumeID}/path", {
2606
- params: {
2607
- path: {
2608
- volumeID: this.volumeId
2609
- },
2610
- query: {
2611
- path: path2
2612
- }
2613
- },
2614
- signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2615
- });
2616
- if (res.response.status === 404) {
2617
- throw new NotFoundError(`Path ${path2} not found`);
2618
- }
2619
- const err = handleApiError(res, VolumeError);
2620
- if (err) {
2621
- throw err;
2622
- }
2623
- if (!res.data) {
2624
- throw new Error("Response data is missing");
2625
- }
2626
- return convertVolumeEntryStat(
2627
- res.data
2612
+ static async list(opts) {
2613
+ var _a3;
2614
+ const data = await request(
2615
+ "GET",
2616
+ _Secret.base,
2617
+ "Secret.list",
2618
+ opts
2619
+ );
2620
+ return ((_a3 = data.secrets) != null ? _a3 : []).map(bindingFromJson);
2621
+ }
2622
+ static async get(name, opts) {
2623
+ validateSecretName(name);
2624
+ return bindingFromJson(
2625
+ await request(
2626
+ "GET",
2627
+ `${_Secret.base}/${encodeURIComponent(name)}`,
2628
+ "Secret.get",
2629
+ opts
2630
+ )
2628
2631
  );
2629
2632
  }
2630
- /**
2631
- * Check whether a file or directory exists.
2632
- *
2633
- * Uses {@link getInfo} under the hood. Returns `true` if the path exists,
2634
- * `false` if it does not (404). Other errors are rethrown.
2635
- *
2636
- * @param path path to the file or directory.
2637
- * @param opts connection options.
2638
- *
2639
- * @returns `true` if the path exists, `false` otherwise.
2640
- */
2641
- async exists(path2, opts) {
2642
- try {
2643
- await this.getInfo(path2, opts);
2644
- return true;
2645
- } catch (err) {
2646
- if (err instanceof NotFoundError) {
2647
- return false;
2648
- }
2649
- throw err;
2633
+ static async update(params, opts) {
2634
+ validateSecretName(params.name);
2635
+ if (!params.value) {
2636
+ throw new InvalidArgumentError("value is required");
2650
2637
  }
2638
+ const body = {
2639
+ value: params.value,
2640
+ hosts: normalizeSecretHosts(params.hosts)
2641
+ };
2642
+ if (params.description !== void 0) {
2643
+ body.description = params.description;
2644
+ }
2645
+ return bindingFromJson(
2646
+ await request(
2647
+ "PUT",
2648
+ `${_Secret.base}/${encodeURIComponent(params.name)}`,
2649
+ "Secret.update",
2650
+ opts,
2651
+ body
2652
+ )
2653
+ );
2651
2654
  }
2652
- /**
2653
- * Update file or directory metadata.
2654
- *
2655
- * @param path path to the file or directory.
2656
- * @param metadata metadata to update (uid, gid, mode).
2657
- * @param opts connection options.
2658
- *
2659
- * @returns updated entry information.
2660
- */
2661
- async updateMetadata(path2, metadata, opts) {
2662
- const config = new VolumeConnectionConfig(this, opts);
2663
- const client = new VolumeApiClient(config);
2664
- const res = await client.api.PATCH("/volumecontent/{volumeID}/path", {
2665
- params: {
2666
- path: {
2667
- volumeID: this.volumeId
2668
- },
2669
- query: {
2670
- path: path2
2671
- }
2672
- },
2673
- body: {
2674
- uid: metadata.uid,
2675
- gid: metadata.gid,
2676
- mode: metadata.mode
2677
- },
2678
- signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2679
- });
2680
- if (res.response.status === 404) {
2681
- throw new NotFoundError(`Path ${path2} not found`);
2682
- }
2683
- const err = handleApiError(res, VolumeError);
2684
- if (err) {
2685
- throw err;
2686
- }
2687
- if (!res.data) {
2688
- throw new Error("Response data is missing");
2689
- }
2690
- return convertVolumeEntryStat(
2691
- res.data
2655
+ static async delete(name, opts) {
2656
+ var _a3;
2657
+ validateSecretName(name);
2658
+ const data = await request(
2659
+ "DELETE",
2660
+ `${_Secret.base}/${encodeURIComponent(name)}`,
2661
+ "Secret.delete",
2662
+ opts
2692
2663
  );
2664
+ return String((_a3 = data.deleted) != null ? _a3 : "");
2693
2665
  }
2694
- async readFile(path2, opts) {
2695
- var _a3, _b;
2696
- const format = (_a3 = opts == null ? void 0 : opts.format) != null ? _a3 : "text";
2697
- const config = new VolumeConnectionConfig(this, opts);
2698
- const client = new VolumeApiClient(config);
2699
- const res = await client.api.GET("/volumecontent/{volumeID}/file", {
2700
- params: {
2701
- path: {
2702
- volumeID: this.volumeId
2703
- },
2704
- query: {
2705
- path: path2
2706
- }
2707
- },
2708
- parseAs: format === "bytes" ? "arrayBuffer" : format,
2709
- signal: config.getSignal((_b = opts == null ? void 0 : opts.requestTimeoutMs) != null ? _b : FILE_TIMEOUT_MS)
2710
- });
2711
- if (res.response.status === 404) {
2712
- throw new NotFoundError(`Path ${path2} not found`);
2666
+ };
2667
+ _Secret.base = "/secrets";
2668
+ var Secret = _Secret;
2669
+ function validateSecretName(name) {
2670
+ if (typeof name !== "string" || !SECRET_NAME_PATTERN.test(name)) {
2671
+ throw new InvalidArgumentError(
2672
+ `secret name ${JSON.stringify(name)} must be 1-128 characters and contain only letters, numbers, dots, underscores, and hyphens`
2673
+ );
2674
+ }
2675
+ }
2676
+ function normalizeSecretHosts(hosts) {
2677
+ if (!hosts.length) {
2678
+ throw new InvalidArgumentError("hosts is required");
2679
+ }
2680
+ const seen = /* @__PURE__ */ new Set();
2681
+ const normalizedHosts = [];
2682
+ for (const rawHost of hosts) {
2683
+ if (typeof rawHost !== "string") {
2684
+ throw new InvalidArgumentError("host must be a string");
2713
2685
  }
2714
- const err = handleApiError(res, VolumeError);
2715
- if (err) {
2716
- throw err;
2686
+ const host = rawHost.trim().toLowerCase();
2687
+ if (!host) {
2688
+ throw new InvalidArgumentError("host cannot be empty");
2717
2689
  }
2718
- if (format === "bytes") {
2719
- return new Uint8Array(res.data);
2690
+ if (host.includes("://") || /[/?#@:]/.test(host)) {
2691
+ throw new InvalidArgumentError(
2692
+ `host ${JSON.stringify(host)} must be a hostname only`
2693
+ );
2720
2694
  }
2721
- if (format === "text") {
2722
- if (res.response.headers.get("content-length") === "0") {
2723
- return "";
2724
- }
2725
- return typeof res.data === "string" ? res.data : "";
2695
+ const wildcard = host.startsWith("*.");
2696
+ const lookupHost = wildcard ? host.slice(2) : host;
2697
+ if (!lookupHost || lookupHost.includes("*")) {
2698
+ throw new InvalidArgumentError(`invalid host ${JSON.stringify(host)}`);
2726
2699
  }
2727
- return res.data;
2728
- }
2729
- /**
2730
- * Write content to a file.
2731
- *
2732
- * Writing to a file that doesn't exist creates the file.
2733
- *
2734
- * Writing to a file that already exists overwrites the file.
2735
- *
2736
- * @param path path to the file.
2737
- * @param data data to write to the file. Data can be a string, `ArrayBuffer`, `Blob`, or `ReadableStream`.
2738
- * @param options file creation options.
2739
- * @param opts connection options.
2740
- *
2741
- * @returns information about the written file
2742
- */
2743
- async writeFile(path2, data, opts) {
2744
- var _a3;
2745
- const config = new VolumeConnectionConfig(this, opts);
2746
- const client = new VolumeApiClient(config);
2747
- const blob = await toBlob(data);
2748
- const res = await client.api.PUT("/volumecontent/{volumeID}/file", {
2749
- params: {
2750
- path: {
2751
- volumeID: this.volumeId
2752
- },
2753
- query: {
2754
- path: path2,
2755
- uid: opts == null ? void 0 : opts.uid,
2756
- gid: opts == null ? void 0 : opts.gid,
2757
- mode: opts == null ? void 0 : opts.mode,
2758
- force: opts == null ? void 0 : opts.force
2759
- }
2760
- },
2761
- bodySerializer: () => blob,
2762
- body: {},
2763
- headers: {
2764
- "Content-Type": "application/octet-stream"
2765
- },
2766
- signal: config.getSignal((_a3 = opts == null ? void 0 : opts.requestTimeoutMs) != null ? _a3 : FILE_TIMEOUT_MS)
2767
- });
2768
- if (res.response.status === 404) {
2769
- throw new NotFoundError(`Path ${path2} not found`);
2770
- }
2771
- const err = handleApiError(res, VolumeError);
2772
- if (err) {
2773
- throw err;
2700
+ const normalized = wildcard ? `*.${lookupHost}` : lookupHost;
2701
+ if (!HOST_PATTERN.test(normalized)) {
2702
+ throw new InvalidArgumentError(
2703
+ `host ${JSON.stringify(host)} must be a hostname or wildcard hostname`
2704
+ );
2774
2705
  }
2775
- if (!res.data) {
2776
- throw new Error("Response data is missing");
2706
+ if (seen.has(normalized)) {
2707
+ continue;
2777
2708
  }
2778
- return convertVolumeEntryStat(
2779
- res.data
2780
- );
2709
+ seen.add(normalized);
2710
+ normalizedHosts.push(normalized);
2781
2711
  }
2782
- /**
2783
- * Remove a file or directory.
2784
- *
2785
- * @param path path to the file or directory to remove.
2786
- * @param opts connection options.
2787
- */
2788
- async remove(path2, opts) {
2789
- const config = new VolumeConnectionConfig(this, opts);
2790
- const client = new VolumeApiClient(config);
2791
- const res = await client.api.DELETE("/volumecontent/{volumeID}/path", {
2792
- params: {
2793
- path: {
2794
- volumeID: this.volumeId
2795
- },
2796
- query: {
2797
- path: path2
2798
- }
2799
- },
2800
- signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2801
- });
2802
- if (res.response.status === 404) {
2803
- throw new NotFoundError(`Path ${path2} not found`);
2804
- }
2805
- const err = handleApiError(res, VolumeError);
2806
- if (err) {
2807
- throw err;
2808
- }
2712
+ return normalizedHosts;
2713
+ }
2714
+ async function request(method, path2, feature, opts, body) {
2715
+ const config = new ConnectionConfig(opts);
2716
+ raiseIfLegacy(config, feature, SecretError);
2717
+ const headers = __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, config.headers), body ? { "Content-Type": "application/json" } : {}), config.apiKey ? { "X-API-KEY": config.apiKey } : {}), config.accessToken ? { Authorization: `Bearer ${config.accessToken}` } : {});
2718
+ const response = await fetch(`${config.apiUrl}${path2}`, {
2719
+ method,
2720
+ headers,
2721
+ body: body ? JSON.stringify(body) : void 0,
2722
+ signal: config.getSignal()
2723
+ });
2724
+ const data = await parseJson(response);
2725
+ if (!response.ok) {
2726
+ const message = typeof (data == null ? void 0 : data.message) === "string" ? data.message : response.statusText;
2727
+ throw new SecretError(`${response.status}: ${message}`);
2809
2728
  }
2810
- };
2729
+ return data;
2730
+ }
2731
+ async function parseJson(response) {
2732
+ const text = await response.text();
2733
+ return text ? JSON.parse(text) : {};
2734
+ }
2735
+ function bindingFromJson(data) {
2736
+ var _a3, _b;
2737
+ return {
2738
+ name: data.name,
2739
+ hosts: (_a3 = data.hosts) != null ? _a3 : [],
2740
+ placeholder: data.placeholder,
2741
+ description: (_b = data.description) != null ? _b : null,
2742
+ hasSecret: data.hasSecret,
2743
+ status: data.status,
2744
+ updatedAt: data.updatedAt
2745
+ };
2746
+ }
2811
2747
 
2812
2748
  // src/core/sandbox/index.ts
2813
2749
  import { createConnectTransport } from "@connectrpc/connect-web";
@@ -2816,7 +2752,7 @@ import { createConnectTransport } from "@connectrpc/connect-web";
2816
2752
  import {
2817
2753
  Code as Code5,
2818
2754
  ConnectError as ConnectError5,
2819
- createClient as createClient6
2755
+ createClient as createClient5
2820
2756
  } from "@connectrpc/connect";
2821
2757
  import { compareVersions as compareVersions3 } from "compare-versions";
2822
2758
 
@@ -2836,7 +2772,7 @@ var Process = /* @__PURE__ */ serviceDesc2(file_process_process, 0);
2836
2772
  import {
2837
2773
  Code as Code4,
2838
2774
  ConnectError as ConnectError4,
2839
- createClient as createClient5
2775
+ createClient as createClient4
2840
2776
  } from "@connectrpc/connect";
2841
2777
  var Pty = class {
2842
2778
  // 60 seconds
@@ -2844,7 +2780,7 @@ var Pty = class {
2844
2780
  this.transport = transport;
2845
2781
  this.connectionConfig = connectionConfig;
2846
2782
  this.defaultPtyConnectionTimeout = 6e4;
2847
- this.rpc = createClient5(Process, this.transport);
2783
+ this.rpc = createClient4(Process, this.transport);
2848
2784
  this.envdVersion = metadata.version;
2849
2785
  }
2850
2786
  /**
@@ -3056,7 +2992,7 @@ var Commands = class {
3056
2992
  constructor(transport, connectionConfig, metadata) {
3057
2993
  this.connectionConfig = connectionConfig;
3058
2994
  this.defaultProcessConnectionTimeout = 6e4;
3059
- this.rpc = createClient6(Process, transport);
2995
+ this.rpc = createClient5(Process, transport);
3060
2996
  this.envdVersion = metadata.version;
3061
2997
  }
3062
2998
  /**
@@ -3291,6 +3227,33 @@ var Commands = class {
3291
3227
 
3292
3228
  // src/core/sandbox/sandboxApi.ts
3293
3229
  import { compareVersions as compareVersions4 } from "compare-versions";
3230
+ async function createRawApiError(response) {
3231
+ var _a3;
3232
+ let message = response.statusText;
3233
+ try {
3234
+ const body = await response.json();
3235
+ message = (_a3 = body == null ? void 0 : body.message) != null ? _a3 : JSON.stringify(body);
3236
+ } catch (e) {
3237
+ try {
3238
+ message = await response.text();
3239
+ } catch (e2) {
3240
+ }
3241
+ }
3242
+ if (response.status === 401) {
3243
+ return new AuthenticationError(
3244
+ `Unauthorized, please check your credentials. - ${message}`
3245
+ );
3246
+ }
3247
+ if (response.status === 429) {
3248
+ return new RateLimitError(
3249
+ `Rate limit exceeded, please try again later - ${message}`
3250
+ );
3251
+ }
3252
+ return new SandboxError(`${response.status}: ${message}`);
3253
+ }
3254
+ function getRawApiHeaders(config, headers) {
3255
+ return __spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({}, defaultHeaders), config.apiKey ? { "X-API-KEY": config.apiKey } : {}), config.accessToken ? { Authorization: `Bearer ${config.accessToken}` } : {}), config.headers), headers != null ? headers : {});
3256
+ }
3294
3257
  function getLifecycle(opts) {
3295
3258
  if (opts == null ? void 0 : opts.lifecycle) {
3296
3259
  return opts.lifecycle;
@@ -3361,15 +3324,40 @@ var SandboxApi = class {
3361
3324
  * @returns List of sandbox metrics containing CPU, memory and disk usage information.
3362
3325
  */
3363
3326
  static async getMetrics(sandboxId, opts) {
3364
- var _a3, _b;
3327
+ var _a3, _b, _c, _d;
3365
3328
  const config = new ConnectionConfig(opts);
3366
3329
  const client = new ApiClient(config);
3330
+ if (config.isLegacyDomain) {
3331
+ const res2 = await client.api.GET("/sandboxes/{sandboxID}/metrics", {
3332
+ params: {
3333
+ path: {
3334
+ sandboxID: sandboxId
3335
+ }
3336
+ },
3337
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3338
+ });
3339
+ const err2 = handleApiError(res2);
3340
+ if (err2) {
3341
+ throw err2;
3342
+ }
3343
+ return (_b = ((_a3 = res2.data) != null ? _a3 : []).map((metric) => ({
3344
+ timestamp: new Date(metric.timestamp),
3345
+ cpuUsedPct: metric.cpuUsedPct,
3346
+ cpuCount: metric.cpuCount,
3347
+ memUsed: metric.memUsedMiB * 1024 * 1024,
3348
+ memTotal: metric.memTotalMiB * 1024 * 1024,
3349
+ diskUsed: -1,
3350
+ diskTotal: -1
3351
+ }))) != null ? _b : [];
3352
+ }
3367
3353
  const start = (opts == null ? void 0 : opts.start) ? Math.round(opts.start.getTime() / 1e3) : void 0;
3368
3354
  const end = (opts == null ? void 0 : opts.end) ? Math.round(opts.end.getTime() / 1e3) : void 0;
3369
3355
  const res = await client.api.GET("/sandboxes/{sandboxID}/metrics", {
3370
3356
  params: {
3371
3357
  path: {
3372
- sandboxID: sandboxId,
3358
+ sandboxID: sandboxId
3359
+ },
3360
+ query: {
3373
3361
  start,
3374
3362
  end
3375
3363
  }
@@ -3380,7 +3368,7 @@ var SandboxApi = class {
3380
3368
  if (err) {
3381
3369
  throw err;
3382
3370
  }
3383
- return (_b = (_a3 = res.data) == null ? void 0 : _a3.map((metric) => ({
3371
+ return (_d = (_c = res.data) == null ? void 0 : _c.map((metric) => ({
3384
3372
  timestamp: new Date(metric.timestamp),
3385
3373
  cpuUsedPct: metric.cpuUsedPct,
3386
3374
  cpuCount: metric.cpuCount,
@@ -3388,7 +3376,7 @@ var SandboxApi = class {
3388
3376
  memTotal: metric.memTotal,
3389
3377
  diskUsed: metric.diskUsed,
3390
3378
  diskTotal: metric.diskTotal
3391
- }))) != null ? _b : [];
3379
+ }))) != null ? _d : [];
3392
3380
  }
3393
3381
  /**
3394
3382
  * Set the timeout of the specified sandbox.
@@ -3417,17 +3405,238 @@ var SandboxApi = class {
3417
3405
  },
3418
3406
  signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3419
3407
  });
3420
- if (((_a3 = res.error) == null ? void 0 : _a3.code) === 404) {
3408
+ if (((_a3 = res.error) == null ? void 0 : _a3.code) === 404) {
3409
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3410
+ }
3411
+ const err = handleApiError(res);
3412
+ if (err) {
3413
+ throw err;
3414
+ }
3415
+ }
3416
+ /**
3417
+ * @deprecated Only legacy domains support this method. Use {@link SandboxApi.createSnapshot} for modern domains.
3418
+ */
3419
+ static async commit(sandboxId, opts) {
3420
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
3421
+ const config = new ConnectionConfig(opts);
3422
+ if (!config.isLegacyDomain) {
3423
+ throw new SandboxError("Sandbox.commit is only supported for legacy domains");
3424
+ }
3425
+ const response = await fetch(
3426
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/commit`,
3427
+ {
3428
+ method: "POST",
3429
+ headers: getRawApiHeaders(config, {
3430
+ "Content-Type": "application/json"
3431
+ }),
3432
+ body: JSON.stringify((opts == null ? void 0 : opts.alias) ? { alias: opts.alias } : {}),
3433
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3434
+ }
3435
+ );
3436
+ if (response.status === 404) {
3437
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3438
+ }
3439
+ if (!response.ok) {
3440
+ throw await createRawApiError(response);
3441
+ }
3442
+ const data = await response.json();
3443
+ return {
3444
+ alias: (_d = (_c = (_a3 = data.aliases) == null ? void 0 : _a3[0]) != null ? _c : (_b = data.names) == null ? void 0 : _b[0]) != null ? _d : "",
3445
+ name: (_h = (_g = (_e = data.names) == null ? void 0 : _e[0]) != null ? _g : (_f = data.aliases) == null ? void 0 : _f[0]) != null ? _h : "",
3446
+ tags: [],
3447
+ templateId: data.templateID,
3448
+ buildId: data.buildID,
3449
+ aliases: (_i = data.aliases) != null ? _i : [],
3450
+ names: (_j = data.names) != null ? _j : [],
3451
+ templateType: "snapshot_template"
3452
+ };
3453
+ }
3454
+ static async hotplugMemory(sandboxId, requestedSizeMib, opts) {
3455
+ const config = new ConnectionConfig(opts);
3456
+ raiseIfLegacy(config, "Sandbox.hotplugMemory", SandboxError);
3457
+ const response = await fetch(
3458
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/hotplug-memory`,
3459
+ {
3460
+ method: "POST",
3461
+ headers: getRawApiHeaders(config, {
3462
+ "Content-Type": "application/json"
3463
+ }),
3464
+ body: JSON.stringify({
3465
+ requested_size_mib: requestedSizeMib
3466
+ }),
3467
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3468
+ }
3469
+ );
3470
+ if (response.status === 404) {
3471
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3472
+ }
3473
+ if (!response.ok) {
3474
+ throw await createRawApiError(response);
3475
+ }
3476
+ }
3477
+ static async reset(sandboxId, opts) {
3478
+ var _a3;
3479
+ const config = new ConnectionConfig(opts);
3480
+ const response = await fetch(
3481
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/reset`,
3482
+ {
3483
+ method: "POST",
3484
+ headers: getRawApiHeaders(config, {
3485
+ "Content-Type": "application/json"
3486
+ }),
3487
+ body: JSON.stringify(__spreadValues({
3488
+ resume: (_a3 = opts == null ? void 0 : opts.resume) != null ? _a3 : true
3489
+ }, (opts == null ? void 0 : opts.timeoutMs) !== void 0 ? { timeout: timeoutToSeconds(opts.timeoutMs) } : {})),
3490
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3491
+ }
3492
+ );
3493
+ if (response.status === 404) {
3494
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3495
+ }
3496
+ if (response.status === 409) {
3497
+ return false;
3498
+ }
3499
+ if (!response.ok) {
3500
+ throw await createRawApiError(response);
3501
+ }
3502
+ return true;
3503
+ }
3504
+ static async mountVolume(sandboxId, name, path2, opts) {
3505
+ var _a3, _b;
3506
+ const config = new ConnectionConfig(opts);
3507
+ raiseIfLegacy(config, "Sandbox.mountVolume", SandboxError);
3508
+ const response = await fetch(
3509
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/volume-mounts`,
3510
+ {
3511
+ method: "POST",
3512
+ headers: getRawApiHeaders(config, {
3513
+ "Content-Type": "application/json"
3514
+ }),
3515
+ body: JSON.stringify({ name, path: path2 }),
3516
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3517
+ }
3518
+ );
3519
+ if (response.status === 404) {
3520
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3521
+ }
3522
+ if (!response.ok) {
3523
+ throw await createRawApiError(response);
3524
+ }
3525
+ const data = await response.json();
3526
+ return {
3527
+ sandboxId: data.sandboxID,
3528
+ templateId: data.templateID,
3529
+ name: data.name,
3530
+ metadata: (_a3 = data.metadata) != null ? _a3 : {},
3531
+ startedAt: new Date(data.startedAt),
3532
+ endAt: new Date(data.endAt),
3533
+ state: data.state,
3534
+ cpuCount: data.cpuCount,
3535
+ memoryMB: data.memoryMB,
3536
+ envdVersion: data.envdVersion,
3537
+ volumeMounts: (_b = data.volumeMounts) != null ? _b : []
3538
+ };
3539
+ }
3540
+ static async unmountVolume(sandboxId, path2, opts) {
3541
+ var _a3, _b;
3542
+ const config = new ConnectionConfig(opts);
3543
+ raiseIfLegacy(config, "Sandbox.unmountVolume", SandboxError);
3544
+ const url = new URL(
3545
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/volume-mounts`
3546
+ );
3547
+ url.searchParams.set("path", path2);
3548
+ if (opts == null ? void 0 : opts.force) {
3549
+ url.searchParams.set("force", "true");
3550
+ }
3551
+ const response = await fetch(url, {
3552
+ method: "DELETE",
3553
+ headers: getRawApiHeaders(config),
3554
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3555
+ });
3556
+ if (response.status === 404) {
3421
3557
  throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3422
3558
  }
3423
- const err = handleApiError(res);
3424
- if (err) {
3425
- throw err;
3559
+ if (!response.ok) {
3560
+ throw await createRawApiError(response);
3426
3561
  }
3562
+ const data = await response.json();
3563
+ return {
3564
+ sandboxId: data.sandboxID,
3565
+ templateId: data.templateID,
3566
+ name: data.name,
3567
+ metadata: (_a3 = data.metadata) != null ? _a3 : {},
3568
+ startedAt: new Date(data.startedAt),
3569
+ endAt: new Date(data.endAt),
3570
+ state: data.state,
3571
+ cpuCount: data.cpuCount,
3572
+ memoryMB: data.memoryMB,
3573
+ envdVersion: data.envdVersion,
3574
+ volumeMounts: (_b = data.volumeMounts) != null ? _b : []
3575
+ };
3427
3576
  }
3428
- static async getFullInfo(sandboxId, opts) {
3577
+ static async setNetwork(sandboxId, opts) {
3578
+ const config = new ConnectionConfig(opts);
3579
+ raiseIfLegacy(config, "Sandbox.setNetwork", SandboxError);
3580
+ const response = await fetch(
3581
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/network`,
3582
+ {
3583
+ method: "PUT",
3584
+ headers: getRawApiHeaders(config, {
3585
+ "Content-Type": "application/json"
3586
+ }),
3587
+ body: JSON.stringify(__spreadValues(__spreadValues({}, (opts == null ? void 0 : opts.allowOut) ? { allowOut: opts.allowOut } : {}), (opts == null ? void 0 : opts.denyOut) ? { denyOut: opts.denyOut } : {})),
3588
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3589
+ }
3590
+ );
3591
+ if (response.status === 404) {
3592
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3593
+ }
3594
+ if (!response.ok) {
3595
+ throw await createRawApiError(response);
3596
+ }
3597
+ }
3598
+ static async cloneSandboxes(sandboxId, opts) {
3429
3599
  var _a3, _b, _c, _d;
3430
3600
  const config = new ConnectionConfig(opts);
3601
+ const response = await fetch(
3602
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/clone`,
3603
+ {
3604
+ method: "POST",
3605
+ headers: getRawApiHeaders(config, {
3606
+ "Content-Type": "application/json"
3607
+ }),
3608
+ body: JSON.stringify(__spreadValues(__spreadValues({
3609
+ count: (_a3 = opts == null ? void 0 : opts.count) != null ? _a3 : 1,
3610
+ strict: (_b = opts == null ? void 0 : opts.strict) != null ? _b : false
3611
+ }, (opts == null ? void 0 : opts.nodeId) ? { nodeID: opts.nodeId } : {}), (opts == null ? void 0 : opts.timeoutMs) !== void 0 ? { timeout: timeoutToSeconds(opts.timeoutMs) } : {})),
3612
+ signal: config.getSignal((_c = opts == null ? void 0 : opts.requestTimeoutMs) != null ? _c : 3e5)
3613
+ }
3614
+ );
3615
+ if (response.status === 404) {
3616
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3617
+ }
3618
+ if (!response.ok) {
3619
+ throw await createRawApiError(response);
3620
+ }
3621
+ const data = await response.json();
3622
+ return {
3623
+ sandboxes: ((_d = data.sandboxes) != null ? _d : []).map((sandbox) => {
3624
+ var _a4, _b2;
3625
+ const baseSandboxId = (_a4 = sandbox.sandboxID) != null ? _a4 : "";
3626
+ const sandboxId2 = sandbox.clientID ? `${baseSandboxId}-${sandbox.clientID}` : baseSandboxId;
3627
+ return {
3628
+ sandboxId: sandboxId2,
3629
+ envdVersion: (_b2 = sandbox.envdVersion) != null ? _b2 : "0.0.0",
3630
+ envdAccessToken: sandbox.envdAccessToken,
3631
+ trafficAccessToken: sandbox.trafficAccessToken
3632
+ };
3633
+ }),
3634
+ snapshotTemplateId: data.snapshotTemplateId
3635
+ };
3636
+ }
3637
+ static async getFullInfo(sandboxId, opts) {
3638
+ var _a3, _b, _c, _d, _e, _f;
3639
+ const config = new ConnectionConfig(opts);
3431
3640
  const client = new ApiClient(config);
3432
3641
  const res = await client.api.GET("/sandboxes/{sandboxID}", {
3433
3642
  params: {
@@ -3447,12 +3656,29 @@ var SandboxApi = class {
3447
3656
  if (!res.data) {
3448
3657
  throw new Error("Sandbox not found");
3449
3658
  }
3659
+ if (config.isLegacyDomain) {
3660
+ const data = res.data;
3661
+ return __spreadProps(__spreadValues({
3662
+ sandboxId: data.clientID != null ? `${data.sandboxID}-${data.clientID}` : data.sandboxID,
3663
+ templateId: data.templateID
3664
+ }, data.alias && { name: data.alias }), {
3665
+ metadata: (_b = data.metadata) != null ? _b : {},
3666
+ envdVersion: (_c = data.envdVersion) != null ? _c : "0.0.0",
3667
+ envdAccessToken: data.envdAccessToken,
3668
+ startedAt: new Date(data.startedAt),
3669
+ endAt: new Date(data.endAt),
3670
+ state: data.state,
3671
+ cpuCount: data.cpuCount,
3672
+ memoryMB: data.memoryMB,
3673
+ sandboxDomain: data.domain || void 0
3674
+ });
3675
+ }
3450
3676
  return __spreadProps(__spreadValues({
3451
3677
  sandboxId: res.data.sandboxID,
3452
3678
  templateId: res.data.templateID
3453
3679
  }, res.data.alias && { name: res.data.alias }), {
3454
- metadata: (_b = res.data.metadata) != null ? _b : {},
3455
- allowInternetAccess: (_c = res.data.allowInternetAccess) != null ? _c : void 0,
3680
+ metadata: (_d = res.data.metadata) != null ? _d : {},
3681
+ allowInternetAccess: (_e = res.data.allowInternetAccess) != null ? _e : void 0,
3456
3682
  envdVersion: res.data.envdVersion,
3457
3683
  envdAccessToken: res.data.envdAccessToken,
3458
3684
  startedAt: new Date(res.data.startedAt),
@@ -3471,7 +3697,7 @@ var SandboxApi = class {
3471
3697
  autoResume: res.data.lifecycle.autoResume
3472
3698
  } : void 0,
3473
3699
  sandboxDomain: res.data.domain || void 0,
3474
- volumeMounts: (_d = res.data.volumeMounts) != null ? _d : []
3700
+ volumeMounts: (_f = res.data.volumeMounts) != null ? _f : []
3475
3701
  });
3476
3702
  }
3477
3703
  /**
@@ -3483,8 +3709,31 @@ var SandboxApi = class {
3483
3709
  * @returns `true` if the sandbox got paused, `false` if the sandbox was already paused.
3484
3710
  */
3485
3711
  static async pause(sandboxId, opts) {
3486
- var _a3, _b;
3712
+ var _a3, _b, _c;
3487
3713
  const config = new ConnectionConfig(opts);
3714
+ if (config.isLegacyDomain) {
3715
+ const response = await fetch(
3716
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/pause`,
3717
+ {
3718
+ method: "POST",
3719
+ headers: getRawApiHeaders(config, {
3720
+ "Content-Type": "application/json"
3721
+ }),
3722
+ body: JSON.stringify({ sync: (_a3 = opts == null ? void 0 : opts.sync) != null ? _a3 : false }),
3723
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3724
+ }
3725
+ );
3726
+ if (response.status === 404) {
3727
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3728
+ }
3729
+ if (response.status === 409) {
3730
+ return false;
3731
+ }
3732
+ if (!response.ok) {
3733
+ throw await createRawApiError(response);
3734
+ }
3735
+ return true;
3736
+ }
3488
3737
  const client = new ApiClient(config);
3489
3738
  const res = await client.api.POST("/sandboxes/{sandboxID}/pause", {
3490
3739
  params: {
@@ -3494,10 +3743,10 @@ var SandboxApi = class {
3494
3743
  },
3495
3744
  signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3496
3745
  });
3497
- if (((_a3 = res.error) == null ? void 0 : _a3.code) === 404) {
3746
+ if (((_b = res.error) == null ? void 0 : _b.code) === 404) {
3498
3747
  throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3499
3748
  }
3500
- if (((_b = res.error) == null ? void 0 : _b.code) === 409) {
3749
+ if (((_c = res.error) == null ? void 0 : _c.code) === 409) {
3501
3750
  return false;
3502
3751
  }
3503
3752
  const err = handleApiError(res);
@@ -3527,6 +3776,7 @@ var SandboxApi = class {
3527
3776
  static async createSnapshot(sandboxId, opts) {
3528
3777
  var _a3;
3529
3778
  const config = new ConnectionConfig(opts);
3779
+ raiseIfLegacy(config, "Sandbox.createSnapshot", SandboxError);
3530
3780
  const client = new ApiClient(config);
3531
3781
  const res = await client.api.POST("/sandboxes/{sandboxID}/snapshots", {
3532
3782
  params: {
@@ -3588,22 +3838,69 @@ var SandboxApi = class {
3588
3838
  return true;
3589
3839
  }
3590
3840
  static async createSandbox(template, timeoutMs, opts) {
3591
- var _a3, _b, _c;
3841
+ var _a3, _b, _c, _d, _e, _f;
3592
3842
  const config = new ConnectionConfig(opts);
3593
- const client = new ApiClient(config);
3594
3843
  const lifecycle = getLifecycle(opts);
3595
3844
  const autoPause = lifecycle.onTimeout === "pause";
3596
3845
  const autoResumeEnabled = lifecycle.onTimeout === "pause" ? (_a3 = lifecycle.autoResume) != null ? _a3 : false : void 0;
3597
- const body = __spreadValues(__spreadValues({
3846
+ if (config.isLegacyDomain) {
3847
+ if (opts == null ? void 0 : opts.mcp) {
3848
+ raiseIfLegacy(config, "Sandbox.create mcp", SandboxError);
3849
+ }
3850
+ if (opts == null ? void 0 : opts.network) {
3851
+ raiseIfLegacy(config, "Sandbox.create network", SandboxError);
3852
+ }
3853
+ if (opts == null ? void 0 : opts.volumeMounts) {
3854
+ raiseIfLegacy(config, "Sandbox.create volumeMounts", SandboxError);
3855
+ }
3856
+ if (opts == null ? void 0 : opts.secretEnvs) {
3857
+ raiseIfLegacy(config, "Sandbox.create secretEnvs", SandboxError);
3858
+ }
3859
+ const body2 = __spreadValues({
3860
+ templateID: template,
3861
+ timeout: timeoutToSeconds(timeoutMs),
3862
+ metadata: (_b = opts == null ? void 0 : opts.metadata) != null ? _b : {},
3863
+ envVars: (_c = opts == null ? void 0 : opts.envs) != null ? _c : {},
3864
+ secure: (_d = opts == null ? void 0 : opts.secure) != null ? _d : false,
3865
+ autoPause: autoPause != null ? autoPause : false
3866
+ }, (opts == null ? void 0 : opts.nodeId) ? { nodeID: opts.nodeId } : {});
3867
+ const response = await fetch(`${config.apiUrl}/sandboxes`, {
3868
+ method: "POST",
3869
+ headers: getRawApiHeaders(config, {
3870
+ "Content-Type": "application/json"
3871
+ }),
3872
+ body: JSON.stringify(body2),
3873
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3874
+ });
3875
+ if (!response.ok) {
3876
+ throw await createRawApiError(response);
3877
+ }
3878
+ const data = await response.json();
3879
+ if (compareVersions4(data.envdVersion, "0.1.0") < 0) {
3880
+ await this.kill(data.sandboxID, opts);
3881
+ throw new TemplateError(
3882
+ "You need to update the template to use the new SDK. You can do this by running `ppio-sandbox template build` in the directory with the template."
3883
+ );
3884
+ }
3885
+ return {
3886
+ sandboxId: data.clientID ? `${data.sandboxID}-${data.clientID}` : data.sandboxID,
3887
+ sandboxDomain: data.domain || void 0,
3888
+ envdVersion: data.envdVersion,
3889
+ envdAccessToken: data.envdAccessToken,
3890
+ trafficAccessToken: void 0
3891
+ };
3892
+ }
3893
+ const client = new ApiClient(config);
3894
+ const body = __spreadValues(__spreadValues(__spreadValues({
3598
3895
  templateID: template,
3599
3896
  metadata: opts == null ? void 0 : opts.metadata,
3600
3897
  mcp: opts == null ? void 0 : opts.mcp,
3601
3898
  envVars: opts == null ? void 0 : opts.envs,
3602
3899
  timeout: timeoutToSeconds(timeoutMs),
3603
- secure: (_b = opts == null ? void 0 : opts.secure) != null ? _b : true,
3604
- allow_internet_access: (_c = opts == null ? void 0 : opts.allowInternetAccess) != null ? _c : true,
3900
+ secure: (_e = opts == null ? void 0 : opts.secure) != null ? _e : true,
3901
+ allow_internet_access: (_f = opts == null ? void 0 : opts.allowInternetAccess) != null ? _f : true,
3605
3902
  network: opts == null ? void 0 : opts.network
3606
- }, autoPause !== void 0 ? { autoPause } : {}), autoResumeEnabled !== void 0 ? { autoResume: { enabled: autoResumeEnabled } } : {});
3903
+ }, (opts == null ? void 0 : opts.nodeId) ? { nodeID: opts.nodeId } : {}), autoPause !== void 0 ? { autoPause } : {}), autoResumeEnabled !== void 0 ? { autoResume: { enabled: autoResumeEnabled } } : {});
3607
3904
  if (opts == null ? void 0 : opts.volumeMounts) {
3608
3905
  body.volumeMounts = Object.entries(opts.volumeMounts).map(
3609
3906
  ([mountPath, vol]) => ({
@@ -3612,6 +3909,14 @@ var SandboxApi = class {
3612
3909
  })
3613
3910
  );
3614
3911
  }
3912
+ const validatedSecretEnvs = validateSecretEnvs(
3913
+ opts == null ? void 0 : opts.secretEnvs,
3914
+ opts == null ? void 0 : opts.envs
3915
+ );
3916
+ if (Object.keys(validatedSecretEnvs).length > 0) {
3917
+ ;
3918
+ body.secrets = validatedSecretEnvs;
3919
+ }
3615
3920
  const res = await client.api.POST("/sandboxes", {
3616
3921
  body,
3617
3922
  signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
@@ -3638,6 +3943,33 @@ var SandboxApi = class {
3638
3943
  var _a3, _b;
3639
3944
  const timeoutMs = (_a3 = opts == null ? void 0 : opts.timeoutMs) != null ? _a3 : DEFAULT_SANDBOX_TIMEOUT_MS;
3640
3945
  const config = new ConnectionConfig(opts);
3946
+ if (config.isLegacyDomain) {
3947
+ const response = await fetch(
3948
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/connect`,
3949
+ {
3950
+ method: "POST",
3951
+ headers: getRawApiHeaders(config, {
3952
+ "Content-Type": "application/json"
3953
+ }),
3954
+ body: JSON.stringify({ timeout: timeoutToSeconds(timeoutMs) }),
3955
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3956
+ }
3957
+ );
3958
+ if (response.status === 404) {
3959
+ throw new SandboxNotFoundError(`Paused sandbox ${sandboxId} not found`);
3960
+ }
3961
+ if (!response.ok) {
3962
+ throw await createRawApiError(response);
3963
+ }
3964
+ const data = await response.json();
3965
+ return {
3966
+ sandboxId: data.clientID ? `${data.sandboxID}-${data.clientID}` : data.sandboxID,
3967
+ sandboxDomain: data.domain || void 0,
3968
+ envdVersion: data.envdVersion || "0.0.0",
3969
+ envdAccessToken: data.envdAccessToken,
3970
+ trafficAccessToken: void 0
3971
+ };
3972
+ }
3641
3973
  const client = new ApiClient(config);
3642
3974
  const res = await client.api.POST("/sandboxes/{sandboxID}/connect", {
3643
3975
  params: {
@@ -3665,6 +3997,69 @@ var SandboxApi = class {
3665
3997
  trafficAccessToken: res.data.trafficAccessToken || void 0
3666
3998
  };
3667
3999
  }
4000
+ static async getQuota(opts) {
4001
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
4002
+ const config = new ConnectionConfig(opts);
4003
+ const url = new URL(`${config.apiUrl}/sandboxes/quota`);
4004
+ const response = await fetch(url, {
4005
+ method: "GET",
4006
+ headers: __spreadValues(__spreadValues(__spreadValues({}, config.headers), config.apiKey ? { "X-API-KEY": config.apiKey } : {}), config.accessToken ? { Authorization: `Bearer ${config.accessToken}` } : {}),
4007
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
4008
+ });
4009
+ if (!response.ok) {
4010
+ throw await createRawApiError(response);
4011
+ }
4012
+ const data = await response.json();
4013
+ return {
4014
+ limit: {
4015
+ concurrentInstances: (_b = (_a3 = data.limit) == null ? void 0 : _a3.concurrentInstances) != null ? _b : 0,
4016
+ concurrentVcpu: (_d = (_c = data.limit) == null ? void 0 : _c.concurrentVcpu) != null ? _d : 0,
4017
+ concurrentRamMb: (_f = (_e = data.limit) == null ? void 0 : _e.concurrentRamMb) != null ? _f : 0,
4018
+ maxLengthHours: (_h = (_g = data.limit) == null ? void 0 : _g.maxLengthHours) != null ? _h : 0,
4019
+ diskMb: (_j = (_i = data.limit) == null ? void 0 : _i.diskMb) != null ? _j : 0,
4020
+ maxVcpu: (_l = (_k = data.limit) == null ? void 0 : _k.maxVcpu) != null ? _l : 0,
4021
+ maxRamMb: (_n = (_m = data.limit) == null ? void 0 : _m.maxRamMb) != null ? _n : 0
4022
+ },
4023
+ usage: {
4024
+ concurrentInstances: (_p = (_o = data.usage) == null ? void 0 : _o.concurrentInstances) != null ? _p : 0,
4025
+ concurrentVcpu: (_r = (_q = data.usage) == null ? void 0 : _q.concurrentVcpu) != null ? _r : 0,
4026
+ concurrentRamMb: (_t = (_s = data.usage) == null ? void 0 : _s.concurrentRamMb) != null ? _t : 0
4027
+ }
4028
+ };
4029
+ }
4030
+ static async getEvents(opts) {
4031
+ var _a3, _b, _c;
4032
+ const config = new ConnectionConfig(opts);
4033
+ const url = new URL(`${config.apiUrl}/sandboxes/events`);
4034
+ if ((opts == null ? void 0 : opts.startTime) !== void 0)
4035
+ url.searchParams.set("startTime", String(opts.startTime));
4036
+ if ((opts == null ? void 0 : opts.endTime) !== void 0)
4037
+ url.searchParams.set("endTime", String(opts.endTime));
4038
+ if (opts == null ? void 0 : opts.sandboxID) url.searchParams.set("sandboxID", opts.sandboxID);
4039
+ if (opts == null ? void 0 : opts.templateID) url.searchParams.set("templateID", opts.templateID);
4040
+ if (opts == null ? void 0 : opts.events) url.searchParams.set("events", opts.events);
4041
+ if (opts == null ? void 0 : opts.state) url.searchParams.set("state", opts.state);
4042
+ if ((opts == null ? void 0 : opts.orderAsc) !== void 0)
4043
+ url.searchParams.set("orderAsc", String(opts.orderAsc));
4044
+ if ((opts == null ? void 0 : opts.offset) !== void 0)
4045
+ url.searchParams.set("offset", String(opts.offset));
4046
+ if ((opts == null ? void 0 : opts.limit) !== void 0)
4047
+ url.searchParams.set("limit", String(opts.limit));
4048
+ const response = await fetch(url, {
4049
+ method: "GET",
4050
+ headers: __spreadValues(__spreadValues(__spreadValues({}, config.headers), config.apiKey ? { "X-API-KEY": config.apiKey } : {}), config.accessToken ? { Authorization: `Bearer ${config.accessToken}` } : {}),
4051
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
4052
+ });
4053
+ if (!response.ok) {
4054
+ throw await createRawApiError(response);
4055
+ }
4056
+ const data = await response.json();
4057
+ return {
4058
+ items: (_a3 = data.items) != null ? _a3 : [],
4059
+ total: (_b = data.total) != null ? _b : 0,
4060
+ hasMore: (_c = data.hasMore) != null ? _c : false
4061
+ };
4062
+ }
3668
4063
  };
3669
4064
  var BasePaginator = class {
3670
4065
  constructor(config, limit, nextToken) {
@@ -3697,7 +4092,7 @@ var SandboxPaginator = class extends BasePaginator {
3697
4092
  this.query = opts == null ? void 0 : opts.query;
3698
4093
  }
3699
4094
  async nextItems() {
3700
- var _a3, _b, _c;
4095
+ var _a3, _b, _c, _d, _e;
3701
4096
  if (!this.hasNext) {
3702
4097
  throw new Error("No more items to fetch");
3703
4098
  }
@@ -3711,11 +4106,55 @@ var SandboxPaginator = class extends BasePaginator {
3711
4106
  );
3712
4107
  metadata = new URLSearchParams(encodedPairs).toString();
3713
4108
  }
4109
+ if (this.config.isLegacyDomain) {
4110
+ const params = new URLSearchParams();
4111
+ if (metadata) {
4112
+ params.set("metadata", metadata);
4113
+ }
4114
+ if ((_c = (_b = this.query) == null ? void 0 : _b.state) == null ? void 0 : _c.length) {
4115
+ params.set("state", this.query.state.join(","));
4116
+ }
4117
+ if (this.limit) {
4118
+ params.set("limit", String(this.limit));
4119
+ }
4120
+ if (this.nextToken) {
4121
+ params.set("nextToken", this.nextToken);
4122
+ }
4123
+ const query = params.toString();
4124
+ const response = await fetch(
4125
+ `${this.config.apiUrl}/v2/sandboxes${query ? `?${query}` : ""}`,
4126
+ {
4127
+ headers: getRawApiHeaders(this.config),
4128
+ signal: this.config.getSignal()
4129
+ }
4130
+ );
4131
+ if (!response.ok) {
4132
+ throw await createRawApiError(response);
4133
+ }
4134
+ this.updatePagination(response);
4135
+ const data = await response.json();
4136
+ return (data != null ? data : []).map((sandbox) => {
4137
+ var _a4, _b2;
4138
+ return __spreadProps(__spreadValues({
4139
+ sandboxId: sandbox.clientID != null ? `${sandbox.sandboxID}-${sandbox.clientID}` : sandbox.sandboxID,
4140
+ templateId: sandbox.templateID
4141
+ }, sandbox.alias && { name: sandbox.alias }), {
4142
+ metadata: (_a4 = sandbox.metadata) != null ? _a4 : {},
4143
+ startedAt: new Date(sandbox.startedAt),
4144
+ endAt: new Date(sandbox.endAt),
4145
+ state: sandbox.state,
4146
+ cpuCount: sandbox.cpuCount,
4147
+ memoryMB: sandbox.memoryMB,
4148
+ envdVersion: sandbox.envdVersion,
4149
+ volumeMounts: (_b2 = sandbox.volumeMounts) != null ? _b2 : []
4150
+ });
4151
+ });
4152
+ }
3714
4153
  const res = await this.client.api.GET("/v2/sandboxes", {
3715
4154
  params: {
3716
4155
  query: {
3717
4156
  metadata,
3718
- state: (_b = this.query) == null ? void 0 : _b.state,
4157
+ state: (_d = this.query) == null ? void 0 : _d.state,
3719
4158
  limit: this.limit,
3720
4159
  nextToken: this.nextToken
3721
4160
  }
@@ -3728,11 +4167,11 @@ var SandboxPaginator = class extends BasePaginator {
3728
4167
  throw err;
3729
4168
  }
3730
4169
  this.updatePagination(res.response);
3731
- return ((_c = res.data) != null ? _c : []).map(
4170
+ return ((_e = res.data) != null ? _e : []).map(
3732
4171
  (sandbox) => {
3733
4172
  var _a4, _b2;
3734
4173
  return __spreadProps(__spreadValues({
3735
- sandboxId: sandbox.sandboxID,
4174
+ sandboxId: this.config.isLegacyDomain && sandbox.clientID != null ? `${sandbox.sandboxID}-${sandbox.clientID}` : sandbox.sandboxID,
3736
4175
  templateId: sandbox.templateID
3737
4176
  }, sandbox.alias && { name: sandbox.alias }), {
3738
4177
  metadata: (_a4 = sandbox.metadata) != null ? _a4 : {},
@@ -3751,6 +4190,7 @@ var SandboxPaginator = class extends BasePaginator {
3751
4190
  var SnapshotPaginator = class extends BasePaginator {
3752
4191
  constructor(opts) {
3753
4192
  super(new ConnectionConfig(opts), opts == null ? void 0 : opts.limit, opts == null ? void 0 : opts.nextToken);
4193
+ raiseIfLegacy(this.config, "Sandbox.listSnapshots", SandboxError);
3754
4194
  this.sandboxId = opts == null ? void 0 : opts.sandboxId;
3755
4195
  }
3756
4196
  async nextItems() {
@@ -3890,7 +4330,7 @@ var Sandbox = class extends SandboxApi {
3890
4330
  sandboxOpts
3891
4331
  );
3892
4332
  const sandbox = new this(__spreadValues(__spreadValues({}, sandboxInfo), config));
3893
- if (sandboxOpts == null ? void 0 : sandboxOpts.mcp) {
4333
+ if ((sandboxOpts == null ? void 0 : sandboxOpts.mcp) && !config.isLegacyDomain) {
3894
4334
  sandbox.mcpToken = crypto.randomUUID();
3895
4335
  const res = await sandbox.commands.run(
3896
4336
  `mcp-gateway --config ${shellQuote(JSON.stringify(sandboxOpts.mcp))}`,
@@ -3907,6 +4347,101 @@ var Sandbox = class extends SandboxApi {
3907
4347
  }
3908
4348
  return sandbox;
3909
4349
  }
4350
+ /**
4351
+ * Clone a sandbox to create one or more identical sandboxes.
4352
+ *
4353
+ * @param sandboxId source sandbox ID.
4354
+ * @param opts clone options.
4355
+ *
4356
+ * @returns cloned sandbox instances plus snapshot template ID.
4357
+ */
4358
+ static async clone(sandboxId, opts) {
4359
+ const cloneResult = await SandboxApi.cloneSandboxes(sandboxId, opts);
4360
+ const config = new ConnectionConfig(opts);
4361
+ const sandboxes = cloneResult.sandboxes.map(
4362
+ (sandbox) => new this(__spreadValues({
4363
+ sandboxId: sandbox.sandboxId,
4364
+ envdVersion: sandbox.envdVersion,
4365
+ envdAccessToken: sandbox.envdAccessToken,
4366
+ trafficAccessToken: sandbox.trafficAccessToken
4367
+ }, config))
4368
+ );
4369
+ return {
4370
+ sandboxes,
4371
+ count: sandboxes.length,
4372
+ snapshotTemplateId: cloneResult.snapshotTemplateId
4373
+ };
4374
+ }
4375
+ /**
4376
+ * Dynamically adjust the hotplugged memory on a running sandbox.
4377
+ *
4378
+ * The value represents the new *total* plugged size in MiB, not a delta.
4379
+ * Use `0` to unplug all hotplugged memory.
4380
+ */
4381
+ static async hotplugMemory(sandboxId, requestedSizeMib, opts) {
4382
+ return await SandboxApi.hotplugMemory(
4383
+ sandboxId,
4384
+ requestedSizeMib,
4385
+ opts
4386
+ );
4387
+ }
4388
+ /**
4389
+ * Mount a volume to a sandbox.
4390
+ *
4391
+ * @param sandboxId sandbox ID.
4392
+ * @param name volume name.
4393
+ * @param path mount path inside the sandbox.
4394
+ * @param opts connection options.
4395
+ *
4396
+ * @returns updated sandbox information.
4397
+ */
4398
+ static async mountVolume(sandboxId, name, path2, opts) {
4399
+ return await SandboxApi.mountVolume(sandboxId, name, path2, opts);
4400
+ }
4401
+ /**
4402
+ * Unmount a volume from a sandbox.
4403
+ *
4404
+ * @param sandboxId sandbox ID.
4405
+ * @param path mount path to unmount.
4406
+ * @param opts connection options.
4407
+ * @param [opts.force] force unmount.
4408
+ *
4409
+ * @returns updated sandbox information.
4410
+ */
4411
+ static async unmountVolume(sandboxId, path2, opts) {
4412
+ return await SandboxApi.unmountVolume(sandboxId, path2, opts);
4413
+ }
4414
+ /**
4415
+ * Commit a sandbox to create a snapshot template.
4416
+ *
4417
+ * @deprecated Use snapshot APIs instead. This method is only supported for modern domains.
4418
+ *
4419
+ * @param sandboxId source sandbox ID.
4420
+ * @param opts commit options.
4421
+ *
4422
+ * @returns created snapshot template.
4423
+ */
4424
+ static async commit(sandboxId, opts) {
4425
+ return await SandboxApi.commit(sandboxId, opts);
4426
+ }
4427
+ /**
4428
+ * Reset sandbox memory and optionally resume the sandbox.
4429
+ *
4430
+ * @param sandboxId sandbox ID.
4431
+ * @param opts reset options.
4432
+ *
4433
+ * @returns `true` if reset succeeded, `false` if the sandbox is in a conflicting state.
4434
+ */
4435
+ static async reset(sandboxId, opts) {
4436
+ return await SandboxApi.reset(sandboxId, opts);
4437
+ }
4438
+ /**
4439
+ * Update network egress rules for a running sandbox.
4440
+ * Omitting both `allowOut` and `denyOut` clears all egress rules.
4441
+ */
4442
+ static async setNetwork(sandboxId, opts) {
4443
+ await SandboxApi.setNetwork(sandboxId, opts);
4444
+ }
3910
4445
  static async betaCreate(templateOrOpts, opts) {
3911
4446
  var _a3, _b, _c;
3912
4447
  const { template, sandboxOpts } = typeof templateOrOpts === "string" ? {
@@ -3929,7 +4464,7 @@ var Sandbox = class extends SandboxApi {
3929
4464
  sandboxOpts
3930
4465
  );
3931
4466
  const sandbox = new this(__spreadValues(__spreadValues({}, sandboxInfo), config));
3932
- if (sandboxOpts == null ? void 0 : sandboxOpts.mcp) {
4467
+ if ((sandboxOpts == null ? void 0 : sandboxOpts.mcp) && !config.isLegacyDomain) {
3933
4468
  sandbox.mcpToken = crypto.randomUUID();
3934
4469
  const res = await sandbox.commands.run(
3935
4470
  `mcp-gateway --config ${shellQuote(JSON.stringify(sandboxOpts.mcp))}`,
@@ -4071,6 +4606,68 @@ var Sandbox = class extends SandboxApi {
4071
4606
  this.resolveApiOpts(opts)
4072
4607
  );
4073
4608
  }
4609
+ /**
4610
+ * Reset sandbox memory and optionally resume the sandbox.
4611
+ *
4612
+ * @param opts reset options.
4613
+ *
4614
+ * @returns `true` if reset succeeded, `false` if the sandbox is in a conflicting state.
4615
+ */
4616
+ async reset(opts) {
4617
+ return await SandboxApi.reset(this.sandboxId, this.resolveApiOpts(opts));
4618
+ }
4619
+ /**
4620
+ * Update network egress rules for this running sandbox.
4621
+ * Omitting both `allowOut` and `denyOut` clears all egress rules.
4622
+ */
4623
+ async setNetwork(opts) {
4624
+ await SandboxApi.setNetwork(this.sandboxId, this.resolveApiOpts(opts));
4625
+ }
4626
+ /**
4627
+ * Commit this sandbox to create a snapshot template.
4628
+ *
4629
+ * @deprecated Use snapshot APIs instead. This method is only supported for modern domains.
4630
+ *
4631
+ * @param opts commit options.
4632
+ *
4633
+ * @returns created snapshot template.
4634
+ */
4635
+ async commit(opts) {
4636
+ return await SandboxApi.commit(this.sandboxId, this.resolveApiOpts(opts));
4637
+ }
4638
+ /**
4639
+ * Mount a volume to this sandbox.
4640
+ *
4641
+ * @param name volume name.
4642
+ * @param path mount path inside the sandbox.
4643
+ * @param opts connection options.
4644
+ *
4645
+ * @returns updated sandbox information.
4646
+ */
4647
+ async mountVolume(name, path2, opts) {
4648
+ return await SandboxApi.mountVolume(
4649
+ this.sandboxId,
4650
+ name,
4651
+ path2,
4652
+ this.resolveApiOpts(opts)
4653
+ );
4654
+ }
4655
+ /**
4656
+ * Unmount a volume from this sandbox.
4657
+ *
4658
+ * @param path mount path to unmount.
4659
+ * @param opts connection options.
4660
+ * @param [opts.force] force unmount.
4661
+ *
4662
+ * @returns updated sandbox information.
4663
+ */
4664
+ async unmountVolume(path2, opts) {
4665
+ return await SandboxApi.unmountVolume(
4666
+ this.sandboxId,
4667
+ path2,
4668
+ this.resolveApiOpts(opts)
4669
+ );
4670
+ }
4074
4671
  /**
4075
4672
  * Kill the sandbox.
4076
4673
  *
@@ -4082,6 +4679,22 @@ var Sandbox = class extends SandboxApi {
4082
4679
  }
4083
4680
  await SandboxApi.kill(this.sandboxId, this.resolveApiOpts(opts));
4084
4681
  }
4682
+ /**
4683
+ * Dynamically adjust the hotplugged memory on a running sandbox.
4684
+ *
4685
+ * The value represents the new *total* plugged size in MiB, not a delta.
4686
+ * Use `0` to unplug all hotplugged memory.
4687
+ *
4688
+ * @param requestedSizeMib new total plugged memory size in MiB.
4689
+ * @param opts connection options.
4690
+ */
4691
+ async hotplugMemory(requestedSizeMib, opts) {
4692
+ await SandboxApi.hotplugMemory(
4693
+ this.sandboxId,
4694
+ requestedSizeMib,
4695
+ this.resolveApiOpts(opts)
4696
+ );
4697
+ }
4085
4698
  /**
4086
4699
  * Pause a sandbox by its ID.
4087
4700
  *
@@ -4102,7 +4715,10 @@ var Sandbox = class extends SandboxApi {
4102
4715
  * @deprecated Use {@link Sandbox.pause} instead.
4103
4716
  */
4104
4717
  async betaPause(opts) {
4105
- return await SandboxApi.betaPause(this.sandboxId, this.resolveApiOpts(opts));
4718
+ return await SandboxApi.betaPause(
4719
+ this.sandboxId,
4720
+ this.resolveApiOpts(opts)
4721
+ );
4106
4722
  }
4107
4723
  /**
4108
4724
  * Create a snapshot of the sandbox's current state.
@@ -4154,6 +4770,7 @@ var Sandbox = class extends SandboxApi {
4154
4770
  * @returns MCP URL for the sandbox.
4155
4771
  */
4156
4772
  getMcpUrl() {
4773
+ raiseIfLegacy(this.connectionConfig, "Sandbox.getMcpUrl", SandboxError);
4157
4774
  return `https://${this.getHost(this.mcpPort)}/mcp`;
4158
4775
  }
4159
4776
  /**
@@ -4162,6 +4779,7 @@ var Sandbox = class extends SandboxApi {
4162
4779
  * @returns MCP token for the sandbox, or undefined if MCP is not enabled.
4163
4780
  */
4164
4781
  async getMcpToken() {
4782
+ raiseIfLegacy(this.connectionConfig, "Sandbox.getMcpToken", SandboxError);
4165
4783
  if (!this.mcpToken) {
4166
4784
  this.mcpToken = await this.files.read("/etc/mcp-gateway/.token", {
4167
4785
  user: "root"
@@ -5117,6 +5735,123 @@ function handleCmdEntrypointInstruction(instruction, templateBuilder) {
5117
5735
  }
5118
5736
 
5119
5737
  // src/core/template/index.ts
5738
+ var TemplateApi = class {
5739
+ static async listTemplates(options) {
5740
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
5741
+ const templateType = (_a3 = options == null ? void 0 : options.templateType) != null ? _a3 : "template_build";
5742
+ const page = (_b = options == null ? void 0 : options.page) != null ? _b : 1;
5743
+ const limit = (_c = options == null ? void 0 : options.limit) != null ? _c : 20;
5744
+ if (!["template_build", "snapshot_template"].includes(templateType)) {
5745
+ throw new Error(
5746
+ 'templateType must be either "template_build" or "snapshot_template"'
5747
+ );
5748
+ }
5749
+ if (page < 1) {
5750
+ throw new Error("page must be >= 1");
5751
+ }
5752
+ if (limit < 1 || limit > 100) {
5753
+ throw new Error("limit must be between 1 and 100");
5754
+ }
5755
+ const config = new ConnectionConfig(options);
5756
+ const url = new URL(`${config.apiUrl}/v2/templates`);
5757
+ url.searchParams.set("template_type", templateType);
5758
+ url.searchParams.set("page", String(page));
5759
+ url.searchParams.set("limit", String(limit));
5760
+ const response = await fetch(url, {
5761
+ method: "GET",
5762
+ headers: __spreadValues(__spreadValues(__spreadValues({}, config.headers), config.apiKey ? { "X-API-KEY": config.apiKey } : {}), config.accessToken ? { Authorization: `Bearer ${config.accessToken}` } : {}),
5763
+ signal: config.getSignal(options == null ? void 0 : options.requestTimeoutMs)
5764
+ });
5765
+ if (!response.ok) {
5766
+ throw new Error(`${response.status}: ${await response.text()}`);
5767
+ }
5768
+ const data = await response.json();
5769
+ if (!data) {
5770
+ throw new Error("Template list response data is missing");
5771
+ }
5772
+ return {
5773
+ templates: ((_d = data.templates) != null ? _d : []).map(
5774
+ (template) => ({
5775
+ templateId: template.templateID,
5776
+ aliases: template.aliases,
5777
+ buildCount: template.buildCount,
5778
+ buildId: template.buildID,
5779
+ cpuCount: template.cpuCount,
5780
+ createdAt: new Date(template.createdAt),
5781
+ createdBy: template.createdBy,
5782
+ diskSizeMB: template.diskSizeMB,
5783
+ envdVersion: template.envdVersion,
5784
+ lastSpawnedAt: template.lastSpawnedAt ? new Date(template.lastSpawnedAt) : null,
5785
+ memoryMB: template.memoryMB,
5786
+ metadata: template.metadata,
5787
+ public: template.public,
5788
+ spawnCount: template.spawnCount,
5789
+ templateType: template.templateType,
5790
+ originSandboxID: template.originSandboxID,
5791
+ updatedAt: new Date(template.updatedAt)
5792
+ })
5793
+ ),
5794
+ page: (_e = data.page) != null ? _e : page,
5795
+ limit: (_f = data.limit) != null ? _f : limit,
5796
+ total: (_g = data.total) != null ? _g : 0,
5797
+ totalPages: (_h = data.totalPages) != null ? _h : 0
5798
+ };
5799
+ }
5800
+ static async deleteTemplate(templateId, options) {
5801
+ var _a3;
5802
+ if (!templateId) {
5803
+ throw new Error("templateId cannot be empty");
5804
+ }
5805
+ const config = new ConnectionConfig(options);
5806
+ const client = new ApiClient(config);
5807
+ const res = await client.api.DELETE("/templates/{templateID}", {
5808
+ params: {
5809
+ path: {
5810
+ templateID: templateId
5811
+ }
5812
+ },
5813
+ signal: config.getSignal(options == null ? void 0 : options.requestTimeoutMs)
5814
+ });
5815
+ if (((_a3 = res.error) == null ? void 0 : _a3.code) === 404 || res.response.status === 404) {
5816
+ return false;
5817
+ }
5818
+ const err = handleApiError(res, BuildError);
5819
+ if (err) {
5820
+ throw err;
5821
+ }
5822
+ return true;
5823
+ }
5824
+ };
5825
+ var TemplatePaginator = class {
5826
+ constructor(options) {
5827
+ this.hasNextPage = true;
5828
+ var _a3;
5829
+ this.options = options;
5830
+ this.nextPageNumber = (_a3 = options == null ? void 0 : options.page) != null ? _a3 : 1;
5831
+ }
5832
+ get hasNext() {
5833
+ return this.hasNextPage;
5834
+ }
5835
+ get currentPage() {
5836
+ return this.nextPageNumber;
5837
+ }
5838
+ async nextPage() {
5839
+ if (!this.hasNextPage) {
5840
+ throw new Error("No more pages to fetch");
5841
+ }
5842
+ const response = await TemplateApi.listTemplates(__spreadProps(__spreadValues({}, this.options), {
5843
+ page: this.nextPageNumber
5844
+ }));
5845
+ this.hasNextPage = response.page < response.totalPages;
5846
+ this.nextPageNumber = response.page + 1;
5847
+ return response;
5848
+ }
5849
+ reset() {
5850
+ var _a3, _b;
5851
+ this.nextPageNumber = (_b = (_a3 = this.options) == null ? void 0 : _a3.page) != null ? _b : 1;
5852
+ this.hasNextPage = true;
5853
+ }
5854
+ };
5120
5855
  var _a2;
5121
5856
  var TemplateBase = class _TemplateBase {
5122
5857
  constructor(options) {
@@ -5126,6 +5861,7 @@ var TemplateBase = class _TemplateBase {
5126
5861
  this.registryConfig = void 0;
5127
5862
  this.startCmd = void 0;
5128
5863
  this.readyCmd = void 0;
5864
+ this.patchCmd = void 0;
5129
5865
  // Force the whole template to be rebuilt
5130
5866
  this.force = false;
5131
5867
  // Force the next layer to be rebuilt
@@ -5141,6 +5877,9 @@ var TemplateBase = class _TemplateBase {
5141
5877
  this.fileContextPath = (_a3 = options == null ? void 0 : options.fileContextPath) != null ? _a3 : this.fileContextPath;
5142
5878
  this.fileIgnorePatterns = (_b = options == null ? void 0 : options.fileIgnorePatterns) != null ? _b : this.fileIgnorePatterns;
5143
5879
  }
5880
+ static list(options) {
5881
+ return new TemplatePaginator(options);
5882
+ }
5144
5883
  /**
5145
5884
  * Convert a template to JSON representation.
5146
5885
  *
@@ -5219,6 +5958,15 @@ var TemplateBase = class _TemplateBase {
5219
5958
  logsOffset: options == null ? void 0 : options.logsOffset
5220
5959
  });
5221
5960
  }
5961
+ static async listTemplates(options) {
5962
+ return TemplateApi.listTemplates(options);
5963
+ }
5964
+ static async delete(templateId, options) {
5965
+ await TemplateApi.deleteTemplate(templateId, options);
5966
+ }
5967
+ static async deleteTemplate(templateId, options) {
5968
+ return TemplateApi.deleteTemplate(templateId, options);
5969
+ }
5222
5970
  /**
5223
5971
  * Check if a template with the given name exists.
5224
5972
  *
@@ -5687,6 +6435,11 @@ var TemplateBase = class _TemplateBase {
5687
6435
  this.collectStackTrace();
5688
6436
  return this;
5689
6437
  }
6438
+ setPatchCmd(cmd) {
6439
+ this.patchCmd = cmd;
6440
+ this.collectStackTrace();
6441
+ return this;
6442
+ }
5690
6443
  setEnvs(envs) {
5691
6444
  if (Object.keys(envs).length === 0) {
5692
6445
  return this;
@@ -6012,6 +6765,7 @@ var TemplateBase = class _TemplateBase {
6012
6765
  const templateData = {
6013
6766
  startCmd: this.startCmd,
6014
6767
  readyCmd: this.readyCmd,
6768
+ preBootScript: this.patchCmd,
6015
6769
  steps,
6016
6770
  force: this.force
6017
6771
  };
@@ -6038,6 +6792,8 @@ Template.aliasExists = TemplateBase.aliasExists;
6038
6792
  Template.assignTags = TemplateBase.assignTags;
6039
6793
  Template.removeTags = TemplateBase.removeTags;
6040
6794
  Template.getTags = TemplateBase.getTags;
6795
+ Template.list = TemplateBase.list;
6796
+ Template.delete = TemplateBase.delete;
6041
6797
  Template.toJSON = TemplateBase.toJSON;
6042
6798
  Template.toDockerfile = TemplateBase.toDockerfile;
6043
6799
 
@@ -6065,6 +6821,7 @@ export {
6065
6821
  BuildError,
6066
6822
  FileUploadError,
6067
6823
  VolumeError,
6824
+ SecretError,
6068
6825
  ApiClient,
6069
6826
  ConnectionConfig,
6070
6827
  getSignature,
@@ -6073,8 +6830,8 @@ export {
6073
6830
  CommandExitError,
6074
6831
  ALL_TRAFFIC,
6075
6832
  Git,
6076
- VolumeFileType,
6077
6833
  Volume,
6834
+ Secret,
6078
6835
  Sandbox,
6079
6836
  LogEntry,
6080
6837
  LogEntryStart,
@@ -6086,8 +6843,10 @@ export {
6086
6843
  waitForProcess,
6087
6844
  waitForFile,
6088
6845
  waitForTimeout,
6846
+ TemplateApi,
6847
+ TemplatePaginator,
6089
6848
  TemplateBase,
6090
6849
  Template,
6091
6850
  core_default
6092
6851
  };
6093
- //# sourceMappingURL=chunk-P33RTZCB.mjs.map
6852
+ //# sourceMappingURL=chunk-V2Q62RZL.mjs.map