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.
@@ -100,13 +100,16 @@ __export(code_interpreter_exports, {
100
100
  Sandbox: () => Sandbox2,
101
101
  SandboxError: () => SandboxError,
102
102
  SandboxNotFoundError: () => SandboxNotFoundError,
103
+ Secret: () => Secret,
104
+ SecretError: () => SecretError,
103
105
  Template: () => Template,
106
+ TemplateApi: () => TemplateApi,
104
107
  TemplateBase: () => TemplateBase,
105
108
  TemplateError: () => TemplateError,
109
+ TemplatePaginator: () => TemplatePaginator,
106
110
  TimeoutError: () => TimeoutError,
107
111
  Volume: () => Volume,
108
112
  VolumeError: () => VolumeError,
109
- VolumeFileType: () => VolumeFileType,
110
113
  default: () => code_interpreter_default,
111
114
  defaultBuildLogger: () => defaultBuildLogger,
112
115
  getSignature: () => getSignature,
@@ -125,7 +128,7 @@ var import_openapi_fetch = __toESM(require("openapi-fetch"));
125
128
  var import_platform2 = __toESM(require("platform"));
126
129
 
127
130
  // package.json
128
- var version = "2.0.0-a2";
131
+ var version = "2.0.2-a1";
129
132
 
130
133
  // src/core/utils.ts
131
134
  var import_platform = __toESM(require("platform"));
@@ -333,6 +336,12 @@ var VolumeError = class extends Error {
333
336
  this.name = "VolumeError";
334
337
  }
335
338
  };
339
+ var SecretError = class extends SandboxError {
340
+ constructor(message, stackTrace) {
341
+ super(message, stackTrace);
342
+ this.name = "SecretError";
343
+ }
344
+ };
336
345
 
337
346
  // src/core/logs.ts
338
347
  function formatLog(log) {
@@ -376,10 +385,10 @@ function createRpcLogger(logger) {
376
385
  }
377
386
  function createApiLogger(logger) {
378
387
  return {
379
- async onRequest({ request }) {
388
+ async onRequest({ request: request2 }) {
380
389
  var _a3;
381
- (_a3 = logger.info) == null ? void 0 : _a3.call(logger, `Request ${request.method} ${request.url}`);
382
- return request;
390
+ (_a3 = logger.info) == null ? void 0 : _a3.call(logger, `Request ${request2.method} ${request2.url}`);
391
+ return request2;
383
392
  },
384
393
  async onResponse({ response }) {
385
394
  var _a3, _b;
@@ -455,12 +464,27 @@ var REQUEST_TIMEOUT_MS = 6e4;
455
464
  var DEFAULT_SANDBOX_TIMEOUT_MS = 3e5;
456
465
  var KEEPALIVE_PING_INTERVAL_SEC = 50;
457
466
  var KEEPALIVE_PING_HEADER = "Keepalive-Ping-Interval";
467
+ var LEGACY_DOMAINS = ["sandbox.ppio.cn", "cn-beijing-1.sandbox.ppio.com"];
468
+ function isLegacyDomain(domain) {
469
+ return domain ? LEGACY_DOMAINS.includes(domain) : true;
470
+ }
471
+ function raiseIfLegacy(config, feature, ErrorType = Error) {
472
+ if (config.isLegacyDomain) {
473
+ throw new ErrorType(`${feature} is not supported on legacy domains`);
474
+ }
475
+ }
476
+ function validateDomain(domain) {
477
+ if (!domain) {
478
+ throw new Error("Domain cannot be empty");
479
+ }
480
+ }
458
481
  var _ConnectionConfig = class _ConnectionConfig {
459
482
  constructor(opts) {
460
483
  var _a3;
461
484
  this.apiKey = (opts == null ? void 0 : opts.apiKey) || _ConnectionConfig.apiKey;
462
485
  this.debug = (opts == null ? void 0 : opts.debug) || _ConnectionConfig.debug;
463
486
  this.domain = (opts == null ? void 0 : opts.domain) || _ConnectionConfig.domain;
487
+ this.isLegacyDomain = isLegacyDomain(this.domain);
464
488
  this.accessToken = (opts == null ? void 0 : opts.accessToken) || _ConnectionConfig.accessToken;
465
489
  this.requestTimeoutMs = (_a3 = opts == null ? void 0 : opts.requestTimeoutMs) != null ? _a3 : REQUEST_TIMEOUT_MS;
466
490
  this.logger = opts == null ? void 0 : opts.logger;
@@ -861,6 +885,9 @@ var Filesystem2 = class {
861
885
  return new Uint8Array(res.data);
862
886
  }
863
887
  if (res.response.headers.get("content-length") === "0") {
888
+ if (format === "blob") {
889
+ return new Blob([]);
890
+ }
864
891
  return "";
865
892
  }
866
893
  return res.data;
@@ -2373,64 +2400,7 @@ var Git = class {
2373
2400
  }
2374
2401
  };
2375
2402
 
2376
- // src/core/volume/client.ts
2377
- var import_openapi_fetch3 = __toESM(require("openapi-fetch"));
2378
- var FILE_TIMEOUT_MS = 36e5;
2379
- var VolumeConnectionConfig = class _VolumeConnectionConfig {
2380
- constructor(volume, opts) {
2381
- var _a3, _b;
2382
- this.domain = (opts == null ? void 0 : opts.domain) || volume.domain || _VolumeConnectionConfig.domain;
2383
- this.debug = (_b = (_a3 = opts == null ? void 0 : opts.debug) != null ? _a3 : volume.debug) != null ? _b : _VolumeConnectionConfig.debug;
2384
- this.apiUrl = (opts == null ? void 0 : opts.apiUrl) || _VolumeConnectionConfig.volumeApiUrl || (this.debug ? "http://localhost:8080" : `https://api.${this.domain}`);
2385
- this.token = (opts == null ? void 0 : opts.token) || volume.token;
2386
- this.headers = opts == null ? void 0 : opts.headers;
2387
- this.logger = opts == null ? void 0 : opts.logger;
2388
- this.requestTimeoutMs = opts == null ? void 0 : opts.requestTimeoutMs;
2389
- }
2390
- static get domain() {
2391
- return getEnvVar("PPIO_DOMAIN") || "sandbox.ppio.cn";
2392
- }
2393
- static get debug() {
2394
- return (getEnvVar("PPIO_DEBUG") || "false").toLowerCase() === "true";
2395
- }
2396
- static get volumeApiUrl() {
2397
- return getEnvVar("PPIO_SANDBOX_VOLUME_API_URL");
2398
- }
2399
- getSignal(requestTimeoutMs) {
2400
- const timeout = requestTimeoutMs != null ? requestTimeoutMs : this.requestTimeoutMs;
2401
- return timeout ? AbortSignal.timeout(timeout) : void 0;
2402
- }
2403
- };
2404
- var VolumeApiClient = class {
2405
- constructor(config) {
2406
- this.api = (0, import_openapi_fetch3.default)({
2407
- baseUrl: config.apiUrl,
2408
- headers: __spreadValues(__spreadValues(__spreadValues({}, defaultHeaders), config.token && { Authorization: `Bearer ${config.token}` }), config.headers)
2409
- });
2410
- if (config.logger) {
2411
- this.api.use(createApiLogger(config.logger));
2412
- }
2413
- }
2414
- };
2415
-
2416
- // src/core/volume/types.ts
2417
- var VolumeFileType = /* @__PURE__ */ ((VolumeFileType2) => {
2418
- VolumeFileType2["UNKNOWN"] = "unknown";
2419
- VolumeFileType2["FILE"] = "file";
2420
- VolumeFileType2["DIRECTORY"] = "directory";
2421
- VolumeFileType2["SYMLINK"] = "symlink";
2422
- return VolumeFileType2;
2423
- })(VolumeFileType || {});
2424
-
2425
2403
  // src/core/volume/index.ts
2426
- function convertVolumeEntryStat(entry) {
2427
- return __spreadProps(__spreadValues({}, entry), {
2428
- type: entry.type,
2429
- atime: new Date(entry.atime),
2430
- mtime: new Date(entry.mtime),
2431
- ctime: new Date(entry.ctime)
2432
- });
2433
- }
2434
2404
  var Volume = class _Volume {
2435
2405
  /**
2436
2406
  * Create a local Volume instance with no API call.
@@ -2442,6 +2412,9 @@ var Volume = class _Volume {
2442
2412
  * @param debug whether to use debug mode.
2443
2413
  */
2444
2414
  constructor(volumeId, name, token, domain, debug) {
2415
+ if (domain) {
2416
+ validateDomain(domain);
2417
+ }
2445
2418
  this.volumeId = volumeId;
2446
2419
  this.name = name;
2447
2420
  this.token = token;
@@ -2453,15 +2426,20 @@ var Volume = class _Volume {
2453
2426
  *
2454
2427
  * @param name name of the volume.
2455
2428
  * @param opts connection options.
2429
+ * @param [opts.quotaSizeGiB] capacity quota in GiB.
2430
+ * @param [opts.quotaInodes] inode quota.
2456
2431
  *
2457
2432
  * @returns new Volume instance.
2458
2433
  */
2459
2434
  static async create(name, opts) {
2460
2435
  const config = new ConnectionConfig(opts);
2436
+ raiseIfLegacy(config, "Volume.create", VolumeError);
2461
2437
  const client = new ApiClient(config);
2462
2438
  const res = await client.api.POST("/volumes", {
2463
2439
  body: {
2464
- name
2440
+ name,
2441
+ quotaSizeGiB: opts == null ? void 0 : opts.quotaSizeGiB,
2442
+ quotaInodes: opts == null ? void 0 : opts.quotaInodes
2465
2443
  },
2466
2444
  signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2467
2445
  });
@@ -2490,6 +2468,7 @@ var Volume = class _Volume {
2490
2468
  */
2491
2469
  static async connect(volumeId, opts) {
2492
2470
  const config = new ConnectionConfig(opts);
2471
+ raiseIfLegacy(config, "Volume.connect", VolumeError);
2493
2472
  const { name, token } = await _Volume.getInfo(volumeId, opts);
2494
2473
  return new _Volume(volumeId, name, token, config.domain, config.debug);
2495
2474
  }
@@ -2502,7 +2481,9 @@ var Volume = class _Volume {
2502
2481
  * @returns volume information.
2503
2482
  */
2504
2483
  static async getInfo(volumeId, opts) {
2484
+ var _a3, _b, _c, _d;
2505
2485
  const config = new ConnectionConfig(opts);
2486
+ raiseIfLegacy(config, "Volume.getInfo", VolumeError);
2506
2487
  const client = new ApiClient(config);
2507
2488
  const res = await client.api.GET("/volumes/{volumeID}", {
2508
2489
  params: {
@@ -2519,10 +2500,15 @@ var Volume = class _Volume {
2519
2500
  if (err) {
2520
2501
  throw err;
2521
2502
  }
2503
+ const data = res.data;
2522
2504
  return {
2523
- volumeId: res.data.volumeID,
2524
- name: res.data.name,
2525
- token: res.data.token
2505
+ volumeId: data.volumeID,
2506
+ name: data.name,
2507
+ token: data.token,
2508
+ quotaSizeGiB: (_a3 = data.quotaSizeGiB) != null ? _a3 : 0,
2509
+ quotaInodes: (_b = data.quotaInodes) != null ? _b : 0,
2510
+ usedSizeBytes: (_c = data.usedSizeBytes) != null ? _c : 0,
2511
+ usedInodes: (_d = data.usedInodes) != null ? _d : 0
2526
2512
  };
2527
2513
  }
2528
2514
  /**
@@ -2535,6 +2521,7 @@ var Volume = class _Volume {
2535
2521
  static async list(opts) {
2536
2522
  var _a3;
2537
2523
  const config = new ConnectionConfig(opts);
2524
+ raiseIfLegacy(config, "Volume.list", VolumeError);
2538
2525
  const client = new ApiClient(config);
2539
2526
  const res = await client.api.GET("/volumes", {
2540
2527
  signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
@@ -2543,10 +2530,61 @@ var Volume = class _Volume {
2543
2530
  if (err) {
2544
2531
  throw err;
2545
2532
  }
2546
- return ((_a3 = res.data) != null ? _a3 : []).map((vol) => ({
2547
- volumeId: vol.volumeID,
2548
- name: vol.name
2549
- }));
2533
+ return ((_a3 = res.data) != null ? _a3 : []).map((vol) => {
2534
+ var _a4, _b, _c, _d;
2535
+ return {
2536
+ volumeId: vol.volumeID,
2537
+ name: vol.name,
2538
+ quotaSizeGiB: (_a4 = vol.quotaSizeGiB) != null ? _a4 : 0,
2539
+ quotaInodes: (_b = vol.quotaInodes) != null ? _b : 0,
2540
+ usedSizeBytes: (_c = vol.usedSizeBytes) != null ? _c : 0,
2541
+ usedInodes: (_d = vol.usedInodes) != null ? _d : 0
2542
+ };
2543
+ });
2544
+ }
2545
+ /**
2546
+ * Update volume quota.
2547
+ *
2548
+ * @param volumeId volume ID.
2549
+ * @param params quota parameters.
2550
+ * @param opts connection options.
2551
+ *
2552
+ * @returns updated volume information.
2553
+ */
2554
+ static async updateQuota(volumeId, params, opts) {
2555
+ var _a3, _b, _c, _d;
2556
+ const config = new ConnectionConfig(opts);
2557
+ raiseIfLegacy(config, "Volume.updateQuota", VolumeError);
2558
+ const res = await fetch(
2559
+ `${config.apiUrl}/volumes/${encodeURIComponent(volumeId)}`,
2560
+ {
2561
+ method: "PATCH",
2562
+ headers: __spreadValues(__spreadValues(__spreadProps(__spreadValues({}, config.headers), {
2563
+ "Content-Type": "application/json"
2564
+ }), config.apiKey ? { "X-API-KEY": config.apiKey } : {}), config.accessToken ? { Authorization: `Bearer ${config.accessToken}` } : {}),
2565
+ body: JSON.stringify({
2566
+ quotaSizeGiB: params.quotaSizeGiB,
2567
+ quotaInodes: params.quotaInodes
2568
+ }),
2569
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2570
+ }
2571
+ );
2572
+ if (res.status === 404) {
2573
+ throw new NotFoundError(`Volume ${volumeId} not found`);
2574
+ }
2575
+ if (!res.ok) {
2576
+ throw new VolumeError(`${res.status}: ${res.statusText}`);
2577
+ }
2578
+ const data = await res.json();
2579
+ return {
2580
+ volumeId: data.volumeID,
2581
+ name: data.name,
2582
+ token: data.token,
2583
+ quotaSizeGiB: (_a3 = data.quotaSizeGiB) != null ? _a3 : 0,
2584
+ quotaInodes: (_b = data.quotaInodes) != null ? _b : 0,
2585
+ usedSizeBytes: (_c = data.usedSizeBytes) != null ? _c : 0,
2586
+ usedInodes: (_d = data.usedInodes) != null ? _d : 0
2587
+ };
2550
2588
  }
2551
2589
  /**
2552
2590
  * Destroy a volume.
@@ -2556,6 +2594,7 @@ var Volume = class _Volume {
2556
2594
  */
2557
2595
  static async destroy(volumeId, opts) {
2558
2596
  const config = new ConnectionConfig(opts);
2597
+ raiseIfLegacy(config, "Volume.destroy", VolumeError);
2559
2598
  const client = new ApiClient(config);
2560
2599
  const res = await client.api.DELETE("/volumes/{volumeID}", {
2561
2600
  params: {
@@ -2574,296 +2613,196 @@ var Volume = class _Volume {
2574
2613
  }
2575
2614
  return true;
2576
2615
  }
2577
- /**
2578
- * List directory contents.
2579
- *
2580
- * @param path path to the directory.
2581
- * @param opts connection options.
2582
- * @param [opts.depth] number of layers deep to recurse into the directory (default: 1).
2583
- *
2584
- * @returns list of entries in the directory.
2585
- */
2586
- async list(path2, opts) {
2587
- const config = new VolumeConnectionConfig(this, opts);
2588
- const client = new VolumeApiClient(config);
2589
- const res = await client.api.GET("/volumecontent/{volumeID}/dir", {
2590
- params: {
2591
- path: {
2592
- volumeID: this.volumeId
2593
- },
2594
- query: {
2595
- path: path2,
2596
- depth: opts == null ? void 0 : opts.depth
2597
- }
2598
- },
2599
- signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2600
- });
2601
- if (res.response.status === 404) {
2602
- throw new NotFoundError(`Path ${path2} not found`);
2603
- }
2604
- const err = handleApiError(res, VolumeError);
2605
- if (err) {
2606
- throw err;
2607
- }
2608
- const entries = Array.isArray(res.data) ? res.data : [];
2609
- return entries.map(convertVolumeEntryStat);
2616
+ };
2617
+
2618
+ // src/core/secret/index.ts
2619
+ var SECRET_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
2620
+ var ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
2621
+ 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])?)*$/;
2622
+ function validateSecretEnvs(secretEnvs, envs) {
2623
+ if (!secretEnvs) {
2624
+ return {};
2610
2625
  }
2611
- /**
2612
- * Create a directory.
2613
- *
2614
- * @param path path to the directory to create.
2615
- * @param options directory creation options.
2616
- * @param opts connection options.
2617
- */
2618
- async makeDir(path2, opts) {
2619
- const config = new VolumeConnectionConfig(this, opts);
2620
- const client = new VolumeApiClient(config);
2621
- const res = await client.api.POST("/volumecontent/{volumeID}/dir", {
2622
- params: {
2623
- path: {
2624
- volumeID: this.volumeId
2625
- },
2626
- query: {
2627
- path: path2,
2628
- uid: opts == null ? void 0 : opts.uid,
2629
- gid: opts == null ? void 0 : opts.gid,
2630
- mode: opts == null ? void 0 : opts.mode,
2631
- force: opts == null ? void 0 : opts.force
2632
- }
2633
- },
2634
- signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2635
- });
2636
- if (res.response.status === 404) {
2637
- throw new NotFoundError(`Path ${path2} not found`);
2638
- }
2639
- const err = handleApiError(res, VolumeError);
2640
- if (err) {
2641
- throw err;
2626
+ if (typeof secretEnvs !== "object" || Array.isArray(secretEnvs)) {
2627
+ throw new InvalidArgumentError("secretEnvs must be a mapping");
2628
+ }
2629
+ const envNames = new Set(Object.keys(envs != null ? envs : {}));
2630
+ const validated = {};
2631
+ for (const [envName, secretName] of Object.entries(secretEnvs)) {
2632
+ if (!ENV_NAME_PATTERN.test(envName)) {
2633
+ throw new InvalidArgumentError(
2634
+ `secretEnvs key ${JSON.stringify(envName)} must be a valid sandbox environment variable name`
2635
+ );
2642
2636
  }
2643
- if (!res.data) {
2644
- throw new Error("Response data is missing");
2637
+ if (envNames.has(envName)) {
2638
+ throw new InvalidArgumentError(
2639
+ `envs and secretEnvs both define ${JSON.stringify(envName)}`
2640
+ );
2645
2641
  }
2646
- return convertVolumeEntryStat(
2647
- res.data
2642
+ validateSecretName(secretName);
2643
+ validated[envName] = secretName;
2644
+ }
2645
+ return validated;
2646
+ }
2647
+ var _Secret = class _Secret {
2648
+ static async create(params, opts) {
2649
+ validateSecretName(params.name);
2650
+ if (!params.value) {
2651
+ throw new InvalidArgumentError("value is required");
2652
+ }
2653
+ const body = {
2654
+ name: params.name,
2655
+ value: params.value,
2656
+ hosts: normalizeSecretHosts(params.hosts)
2657
+ };
2658
+ if (params.description !== void 0) {
2659
+ body.description = params.description;
2660
+ }
2661
+ return bindingFromJson(
2662
+ await request(
2663
+ "POST",
2664
+ _Secret.base,
2665
+ "Secret.create",
2666
+ opts,
2667
+ body
2668
+ )
2648
2669
  );
2649
2670
  }
2650
- /**
2651
- * Get information about a file or directory.
2652
- *
2653
- * @param path path to the file or directory.
2654
- * @param opts connection options.
2655
- *
2656
- * @returns information about the entry.
2657
- */
2658
- async getInfo(path2, opts) {
2659
- const config = new VolumeConnectionConfig(this, opts);
2660
- const client = new VolumeApiClient(config);
2661
- const res = await client.api.GET("/volumecontent/{volumeID}/path", {
2662
- params: {
2663
- path: {
2664
- volumeID: this.volumeId
2665
- },
2666
- query: {
2667
- path: path2
2668
- }
2669
- },
2670
- signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2671
- });
2672
- if (res.response.status === 404) {
2673
- throw new NotFoundError(`Path ${path2} not found`);
2674
- }
2675
- const err = handleApiError(res, VolumeError);
2676
- if (err) {
2677
- throw err;
2678
- }
2679
- if (!res.data) {
2680
- throw new Error("Response data is missing");
2681
- }
2682
- return convertVolumeEntryStat(
2683
- res.data
2671
+ static async list(opts) {
2672
+ var _a3;
2673
+ const data = await request(
2674
+ "GET",
2675
+ _Secret.base,
2676
+ "Secret.list",
2677
+ opts
2678
+ );
2679
+ return ((_a3 = data.secrets) != null ? _a3 : []).map(bindingFromJson);
2680
+ }
2681
+ static async get(name, opts) {
2682
+ validateSecretName(name);
2683
+ return bindingFromJson(
2684
+ await request(
2685
+ "GET",
2686
+ `${_Secret.base}/${encodeURIComponent(name)}`,
2687
+ "Secret.get",
2688
+ opts
2689
+ )
2684
2690
  );
2685
2691
  }
2686
- /**
2687
- * Check whether a file or directory exists.
2688
- *
2689
- * Uses {@link getInfo} under the hood. Returns `true` if the path exists,
2690
- * `false` if it does not (404). Other errors are rethrown.
2691
- *
2692
- * @param path path to the file or directory.
2693
- * @param opts connection options.
2694
- *
2695
- * @returns `true` if the path exists, `false` otherwise.
2696
- */
2697
- async exists(path2, opts) {
2698
- try {
2699
- await this.getInfo(path2, opts);
2700
- return true;
2701
- } catch (err) {
2702
- if (err instanceof NotFoundError) {
2703
- return false;
2704
- }
2705
- throw err;
2692
+ static async update(params, opts) {
2693
+ validateSecretName(params.name);
2694
+ if (!params.value) {
2695
+ throw new InvalidArgumentError("value is required");
2706
2696
  }
2697
+ const body = {
2698
+ value: params.value,
2699
+ hosts: normalizeSecretHosts(params.hosts)
2700
+ };
2701
+ if (params.description !== void 0) {
2702
+ body.description = params.description;
2703
+ }
2704
+ return bindingFromJson(
2705
+ await request(
2706
+ "PUT",
2707
+ `${_Secret.base}/${encodeURIComponent(params.name)}`,
2708
+ "Secret.update",
2709
+ opts,
2710
+ body
2711
+ )
2712
+ );
2707
2713
  }
2708
- /**
2709
- * Update file or directory metadata.
2710
- *
2711
- * @param path path to the file or directory.
2712
- * @param metadata metadata to update (uid, gid, mode).
2713
- * @param opts connection options.
2714
- *
2715
- * @returns updated entry information.
2716
- */
2717
- async updateMetadata(path2, metadata, opts) {
2718
- const config = new VolumeConnectionConfig(this, opts);
2719
- const client = new VolumeApiClient(config);
2720
- const res = await client.api.PATCH("/volumecontent/{volumeID}/path", {
2721
- params: {
2722
- path: {
2723
- volumeID: this.volumeId
2724
- },
2725
- query: {
2726
- path: path2
2727
- }
2728
- },
2729
- body: {
2730
- uid: metadata.uid,
2731
- gid: metadata.gid,
2732
- mode: metadata.mode
2733
- },
2734
- signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2735
- });
2736
- if (res.response.status === 404) {
2737
- throw new NotFoundError(`Path ${path2} not found`);
2738
- }
2739
- const err = handleApiError(res, VolumeError);
2740
- if (err) {
2741
- throw err;
2742
- }
2743
- if (!res.data) {
2744
- throw new Error("Response data is missing");
2745
- }
2746
- return convertVolumeEntryStat(
2747
- res.data
2714
+ static async delete(name, opts) {
2715
+ var _a3;
2716
+ validateSecretName(name);
2717
+ const data = await request(
2718
+ "DELETE",
2719
+ `${_Secret.base}/${encodeURIComponent(name)}`,
2720
+ "Secret.delete",
2721
+ opts
2748
2722
  );
2723
+ return String((_a3 = data.deleted) != null ? _a3 : "");
2749
2724
  }
2750
- async readFile(path2, opts) {
2751
- var _a3, _b;
2752
- const format = (_a3 = opts == null ? void 0 : opts.format) != null ? _a3 : "text";
2753
- const config = new VolumeConnectionConfig(this, opts);
2754
- const client = new VolumeApiClient(config);
2755
- const res = await client.api.GET("/volumecontent/{volumeID}/file", {
2756
- params: {
2757
- path: {
2758
- volumeID: this.volumeId
2759
- },
2760
- query: {
2761
- path: path2
2762
- }
2763
- },
2764
- parseAs: format === "bytes" ? "arrayBuffer" : format,
2765
- signal: config.getSignal((_b = opts == null ? void 0 : opts.requestTimeoutMs) != null ? _b : FILE_TIMEOUT_MS)
2766
- });
2767
- if (res.response.status === 404) {
2768
- throw new NotFoundError(`Path ${path2} not found`);
2725
+ };
2726
+ _Secret.base = "/secrets";
2727
+ var Secret = _Secret;
2728
+ function validateSecretName(name) {
2729
+ if (typeof name !== "string" || !SECRET_NAME_PATTERN.test(name)) {
2730
+ throw new InvalidArgumentError(
2731
+ `secret name ${JSON.stringify(name)} must be 1-128 characters and contain only letters, numbers, dots, underscores, and hyphens`
2732
+ );
2733
+ }
2734
+ }
2735
+ function normalizeSecretHosts(hosts) {
2736
+ if (!hosts.length) {
2737
+ throw new InvalidArgumentError("hosts is required");
2738
+ }
2739
+ const seen = /* @__PURE__ */ new Set();
2740
+ const normalizedHosts = [];
2741
+ for (const rawHost of hosts) {
2742
+ if (typeof rawHost !== "string") {
2743
+ throw new InvalidArgumentError("host must be a string");
2769
2744
  }
2770
- const err = handleApiError(res, VolumeError);
2771
- if (err) {
2772
- throw err;
2745
+ const host = rawHost.trim().toLowerCase();
2746
+ if (!host) {
2747
+ throw new InvalidArgumentError("host cannot be empty");
2773
2748
  }
2774
- if (format === "bytes") {
2775
- return new Uint8Array(res.data);
2776
- }
2777
- if (format === "text") {
2778
- if (res.response.headers.get("content-length") === "0") {
2779
- return "";
2780
- }
2781
- return typeof res.data === "string" ? res.data : "";
2749
+ if (host.includes("://") || /[/?#@:]/.test(host)) {
2750
+ throw new InvalidArgumentError(
2751
+ `host ${JSON.stringify(host)} must be a hostname only`
2752
+ );
2782
2753
  }
2783
- return res.data;
2784
- }
2785
- /**
2786
- * Write content to a file.
2787
- *
2788
- * Writing to a file that doesn't exist creates the file.
2789
- *
2790
- * Writing to a file that already exists overwrites the file.
2791
- *
2792
- * @param path path to the file.
2793
- * @param data data to write to the file. Data can be a string, `ArrayBuffer`, `Blob`, or `ReadableStream`.
2794
- * @param options file creation options.
2795
- * @param opts connection options.
2796
- *
2797
- * @returns information about the written file
2798
- */
2799
- async writeFile(path2, data, opts) {
2800
- var _a3;
2801
- const config = new VolumeConnectionConfig(this, opts);
2802
- const client = new VolumeApiClient(config);
2803
- const blob = await toBlob(data);
2804
- const res = await client.api.PUT("/volumecontent/{volumeID}/file", {
2805
- params: {
2806
- path: {
2807
- volumeID: this.volumeId
2808
- },
2809
- query: {
2810
- path: path2,
2811
- uid: opts == null ? void 0 : opts.uid,
2812
- gid: opts == null ? void 0 : opts.gid,
2813
- mode: opts == null ? void 0 : opts.mode,
2814
- force: opts == null ? void 0 : opts.force
2815
- }
2816
- },
2817
- bodySerializer: () => blob,
2818
- body: {},
2819
- headers: {
2820
- "Content-Type": "application/octet-stream"
2821
- },
2822
- signal: config.getSignal((_a3 = opts == null ? void 0 : opts.requestTimeoutMs) != null ? _a3 : FILE_TIMEOUT_MS)
2823
- });
2824
- if (res.response.status === 404) {
2825
- throw new NotFoundError(`Path ${path2} not found`);
2754
+ const wildcard = host.startsWith("*.");
2755
+ const lookupHost = wildcard ? host.slice(2) : host;
2756
+ if (!lookupHost || lookupHost.includes("*")) {
2757
+ throw new InvalidArgumentError(`invalid host ${JSON.stringify(host)}`);
2826
2758
  }
2827
- const err = handleApiError(res, VolumeError);
2828
- if (err) {
2829
- throw err;
2759
+ const normalized = wildcard ? `*.${lookupHost}` : lookupHost;
2760
+ if (!HOST_PATTERN.test(normalized)) {
2761
+ throw new InvalidArgumentError(
2762
+ `host ${JSON.stringify(host)} must be a hostname or wildcard hostname`
2763
+ );
2830
2764
  }
2831
- if (!res.data) {
2832
- throw new Error("Response data is missing");
2765
+ if (seen.has(normalized)) {
2766
+ continue;
2833
2767
  }
2834
- return convertVolumeEntryStat(
2835
- res.data
2836
- );
2768
+ seen.add(normalized);
2769
+ normalizedHosts.push(normalized);
2837
2770
  }
2838
- /**
2839
- * Remove a file or directory.
2840
- *
2841
- * @param path path to the file or directory to remove.
2842
- * @param opts connection options.
2843
- */
2844
- async remove(path2, opts) {
2845
- const config = new VolumeConnectionConfig(this, opts);
2846
- const client = new VolumeApiClient(config);
2847
- const res = await client.api.DELETE("/volumecontent/{volumeID}/path", {
2848
- params: {
2849
- path: {
2850
- volumeID: this.volumeId
2851
- },
2852
- query: {
2853
- path: path2
2854
- }
2855
- },
2856
- signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
2857
- });
2858
- if (res.response.status === 404) {
2859
- throw new NotFoundError(`Path ${path2} not found`);
2860
- }
2861
- const err = handleApiError(res, VolumeError);
2862
- if (err) {
2863
- throw err;
2864
- }
2771
+ return normalizedHosts;
2772
+ }
2773
+ async function request(method, path2, feature, opts, body) {
2774
+ const config = new ConnectionConfig(opts);
2775
+ raiseIfLegacy(config, feature, SecretError);
2776
+ 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}` } : {});
2777
+ const response = await fetch(`${config.apiUrl}${path2}`, {
2778
+ method,
2779
+ headers,
2780
+ body: body ? JSON.stringify(body) : void 0,
2781
+ signal: config.getSignal()
2782
+ });
2783
+ const data = await parseJson(response);
2784
+ if (!response.ok) {
2785
+ const message = typeof (data == null ? void 0 : data.message) === "string" ? data.message : response.statusText;
2786
+ throw new SecretError(`${response.status}: ${message}`);
2865
2787
  }
2866
- };
2788
+ return data;
2789
+ }
2790
+ async function parseJson(response) {
2791
+ const text = await response.text();
2792
+ return text ? JSON.parse(text) : {};
2793
+ }
2794
+ function bindingFromJson(data) {
2795
+ var _a3, _b;
2796
+ return {
2797
+ name: data.name,
2798
+ hosts: (_a3 = data.hosts) != null ? _a3 : [],
2799
+ placeholder: data.placeholder,
2800
+ description: (_b = data.description) != null ? _b : null,
2801
+ hasSecret: data.hasSecret,
2802
+ status: data.status,
2803
+ updatedAt: data.updatedAt
2804
+ };
2805
+ }
2867
2806
 
2868
2807
  // src/core/sandbox/index.ts
2869
2808
  var import_connect_web = require("@connectrpc/connect-web");
@@ -3334,6 +3273,33 @@ var Commands = class {
3334
3273
 
3335
3274
  // src/core/sandbox/sandboxApi.ts
3336
3275
  var import_compare_versions4 = require("compare-versions");
3276
+ async function createRawApiError(response) {
3277
+ var _a3;
3278
+ let message = response.statusText;
3279
+ try {
3280
+ const body = await response.json();
3281
+ message = (_a3 = body == null ? void 0 : body.message) != null ? _a3 : JSON.stringify(body);
3282
+ } catch (e) {
3283
+ try {
3284
+ message = await response.text();
3285
+ } catch (e2) {
3286
+ }
3287
+ }
3288
+ if (response.status === 401) {
3289
+ return new AuthenticationError(
3290
+ `Unauthorized, please check your credentials. - ${message}`
3291
+ );
3292
+ }
3293
+ if (response.status === 429) {
3294
+ return new RateLimitError(
3295
+ `Rate limit exceeded, please try again later - ${message}`
3296
+ );
3297
+ }
3298
+ return new SandboxError(`${response.status}: ${message}`);
3299
+ }
3300
+ function getRawApiHeaders(config, headers) {
3301
+ 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 : {});
3302
+ }
3337
3303
  function getLifecycle(opts) {
3338
3304
  if (opts == null ? void 0 : opts.lifecycle) {
3339
3305
  return opts.lifecycle;
@@ -3404,15 +3370,40 @@ var SandboxApi = class {
3404
3370
  * @returns List of sandbox metrics containing CPU, memory and disk usage information.
3405
3371
  */
3406
3372
  static async getMetrics(sandboxId, opts) {
3407
- var _a3, _b;
3373
+ var _a3, _b, _c, _d;
3408
3374
  const config = new ConnectionConfig(opts);
3409
3375
  const client = new ApiClient(config);
3376
+ if (config.isLegacyDomain) {
3377
+ const res2 = await client.api.GET("/sandboxes/{sandboxID}/metrics", {
3378
+ params: {
3379
+ path: {
3380
+ sandboxID: sandboxId
3381
+ }
3382
+ },
3383
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3384
+ });
3385
+ const err2 = handleApiError(res2);
3386
+ if (err2) {
3387
+ throw err2;
3388
+ }
3389
+ return (_b = ((_a3 = res2.data) != null ? _a3 : []).map((metric) => ({
3390
+ timestamp: new Date(metric.timestamp),
3391
+ cpuUsedPct: metric.cpuUsedPct,
3392
+ cpuCount: metric.cpuCount,
3393
+ memUsed: metric.memUsedMiB * 1024 * 1024,
3394
+ memTotal: metric.memTotalMiB * 1024 * 1024,
3395
+ diskUsed: -1,
3396
+ diskTotal: -1
3397
+ }))) != null ? _b : [];
3398
+ }
3410
3399
  const start = (opts == null ? void 0 : opts.start) ? Math.round(opts.start.getTime() / 1e3) : void 0;
3411
3400
  const end = (opts == null ? void 0 : opts.end) ? Math.round(opts.end.getTime() / 1e3) : void 0;
3412
3401
  const res = await client.api.GET("/sandboxes/{sandboxID}/metrics", {
3413
3402
  params: {
3414
3403
  path: {
3415
- sandboxID: sandboxId,
3404
+ sandboxID: sandboxId
3405
+ },
3406
+ query: {
3416
3407
  start,
3417
3408
  end
3418
3409
  }
@@ -3423,7 +3414,7 @@ var SandboxApi = class {
3423
3414
  if (err) {
3424
3415
  throw err;
3425
3416
  }
3426
- return (_b = (_a3 = res.data) == null ? void 0 : _a3.map((metric) => ({
3417
+ return (_d = (_c = res.data) == null ? void 0 : _c.map((metric) => ({
3427
3418
  timestamp: new Date(metric.timestamp),
3428
3419
  cpuUsedPct: metric.cpuUsedPct,
3429
3420
  cpuCount: metric.cpuCount,
@@ -3431,7 +3422,7 @@ var SandboxApi = class {
3431
3422
  memTotal: metric.memTotal,
3432
3423
  diskUsed: metric.diskUsed,
3433
3424
  diskTotal: metric.diskTotal
3434
- }))) != null ? _b : [];
3425
+ }))) != null ? _d : [];
3435
3426
  }
3436
3427
  /**
3437
3428
  * Set the timeout of the specified sandbox.
@@ -3448,28 +3439,249 @@ var SandboxApi = class {
3448
3439
  static async setTimeout(sandboxId, timeoutMs, opts) {
3449
3440
  var _a3;
3450
3441
  const config = new ConnectionConfig(opts);
3451
- const client = new ApiClient(config);
3452
- const res = await client.api.POST("/sandboxes/{sandboxID}/timeout", {
3453
- params: {
3454
- path: {
3455
- sandboxID: sandboxId
3456
- }
3457
- },
3458
- body: {
3459
- timeout: timeoutToSeconds(timeoutMs)
3460
- },
3461
- signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3462
- });
3463
- if (((_a3 = res.error) == null ? void 0 : _a3.code) === 404) {
3442
+ const client = new ApiClient(config);
3443
+ const res = await client.api.POST("/sandboxes/{sandboxID}/timeout", {
3444
+ params: {
3445
+ path: {
3446
+ sandboxID: sandboxId
3447
+ }
3448
+ },
3449
+ body: {
3450
+ timeout: timeoutToSeconds(timeoutMs)
3451
+ },
3452
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3453
+ });
3454
+ if (((_a3 = res.error) == null ? void 0 : _a3.code) === 404) {
3455
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3456
+ }
3457
+ const err = handleApiError(res);
3458
+ if (err) {
3459
+ throw err;
3460
+ }
3461
+ }
3462
+ /**
3463
+ * @deprecated Only legacy domains support this method. Use {@link SandboxApi.createSnapshot} for modern domains.
3464
+ */
3465
+ static async commit(sandboxId, opts) {
3466
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
3467
+ const config = new ConnectionConfig(opts);
3468
+ if (!config.isLegacyDomain) {
3469
+ throw new SandboxError("Sandbox.commit is only supported for legacy domains");
3470
+ }
3471
+ const response = await fetch(
3472
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/commit`,
3473
+ {
3474
+ method: "POST",
3475
+ headers: getRawApiHeaders(config, {
3476
+ "Content-Type": "application/json"
3477
+ }),
3478
+ body: JSON.stringify((opts == null ? void 0 : opts.alias) ? { alias: opts.alias } : {}),
3479
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3480
+ }
3481
+ );
3482
+ if (response.status === 404) {
3483
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3484
+ }
3485
+ if (!response.ok) {
3486
+ throw await createRawApiError(response);
3487
+ }
3488
+ const data = await response.json();
3489
+ return {
3490
+ alias: (_d = (_c = (_a3 = data.aliases) == null ? void 0 : _a3[0]) != null ? _c : (_b = data.names) == null ? void 0 : _b[0]) != null ? _d : "",
3491
+ name: (_h = (_g = (_e = data.names) == null ? void 0 : _e[0]) != null ? _g : (_f = data.aliases) == null ? void 0 : _f[0]) != null ? _h : "",
3492
+ tags: [],
3493
+ templateId: data.templateID,
3494
+ buildId: data.buildID,
3495
+ aliases: (_i = data.aliases) != null ? _i : [],
3496
+ names: (_j = data.names) != null ? _j : [],
3497
+ templateType: "snapshot_template"
3498
+ };
3499
+ }
3500
+ static async hotplugMemory(sandboxId, requestedSizeMib, opts) {
3501
+ const config = new ConnectionConfig(opts);
3502
+ raiseIfLegacy(config, "Sandbox.hotplugMemory", SandboxError);
3503
+ const response = await fetch(
3504
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/hotplug-memory`,
3505
+ {
3506
+ method: "POST",
3507
+ headers: getRawApiHeaders(config, {
3508
+ "Content-Type": "application/json"
3509
+ }),
3510
+ body: JSON.stringify({
3511
+ requested_size_mib: requestedSizeMib
3512
+ }),
3513
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3514
+ }
3515
+ );
3516
+ if (response.status === 404) {
3517
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3518
+ }
3519
+ if (!response.ok) {
3520
+ throw await createRawApiError(response);
3521
+ }
3522
+ }
3523
+ static async reset(sandboxId, opts) {
3524
+ var _a3;
3525
+ const config = new ConnectionConfig(opts);
3526
+ const response = await fetch(
3527
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/reset`,
3528
+ {
3529
+ method: "POST",
3530
+ headers: getRawApiHeaders(config, {
3531
+ "Content-Type": "application/json"
3532
+ }),
3533
+ body: JSON.stringify(__spreadValues({
3534
+ resume: (_a3 = opts == null ? void 0 : opts.resume) != null ? _a3 : true
3535
+ }, (opts == null ? void 0 : opts.timeoutMs) !== void 0 ? { timeout: timeoutToSeconds(opts.timeoutMs) } : {})),
3536
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3537
+ }
3538
+ );
3539
+ if (response.status === 404) {
3540
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3541
+ }
3542
+ if (response.status === 409) {
3543
+ return false;
3544
+ }
3545
+ if (!response.ok) {
3546
+ throw await createRawApiError(response);
3547
+ }
3548
+ return true;
3549
+ }
3550
+ static async mountVolume(sandboxId, name, path2, opts) {
3551
+ var _a3, _b;
3552
+ const config = new ConnectionConfig(opts);
3553
+ raiseIfLegacy(config, "Sandbox.mountVolume", SandboxError);
3554
+ const response = await fetch(
3555
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/volume-mounts`,
3556
+ {
3557
+ method: "POST",
3558
+ headers: getRawApiHeaders(config, {
3559
+ "Content-Type": "application/json"
3560
+ }),
3561
+ body: JSON.stringify({ name, path: path2 }),
3562
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3563
+ }
3564
+ );
3565
+ if (response.status === 404) {
3566
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3567
+ }
3568
+ if (!response.ok) {
3569
+ throw await createRawApiError(response);
3570
+ }
3571
+ const data = await response.json();
3572
+ return {
3573
+ sandboxId: data.sandboxID,
3574
+ templateId: data.templateID,
3575
+ name: data.name,
3576
+ metadata: (_a3 = data.metadata) != null ? _a3 : {},
3577
+ startedAt: new Date(data.startedAt),
3578
+ endAt: new Date(data.endAt),
3579
+ state: data.state,
3580
+ cpuCount: data.cpuCount,
3581
+ memoryMB: data.memoryMB,
3582
+ envdVersion: data.envdVersion,
3583
+ volumeMounts: (_b = data.volumeMounts) != null ? _b : []
3584
+ };
3585
+ }
3586
+ static async unmountVolume(sandboxId, path2, opts) {
3587
+ var _a3, _b;
3588
+ const config = new ConnectionConfig(opts);
3589
+ raiseIfLegacy(config, "Sandbox.unmountVolume", SandboxError);
3590
+ const url = new URL(
3591
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/volume-mounts`
3592
+ );
3593
+ url.searchParams.set("path", path2);
3594
+ if (opts == null ? void 0 : opts.force) {
3595
+ url.searchParams.set("force", "true");
3596
+ }
3597
+ const response = await fetch(url, {
3598
+ method: "DELETE",
3599
+ headers: getRawApiHeaders(config),
3600
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3601
+ });
3602
+ if (response.status === 404) {
3603
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3604
+ }
3605
+ if (!response.ok) {
3606
+ throw await createRawApiError(response);
3607
+ }
3608
+ const data = await response.json();
3609
+ return {
3610
+ sandboxId: data.sandboxID,
3611
+ templateId: data.templateID,
3612
+ name: data.name,
3613
+ metadata: (_a3 = data.metadata) != null ? _a3 : {},
3614
+ startedAt: new Date(data.startedAt),
3615
+ endAt: new Date(data.endAt),
3616
+ state: data.state,
3617
+ cpuCount: data.cpuCount,
3618
+ memoryMB: data.memoryMB,
3619
+ envdVersion: data.envdVersion,
3620
+ volumeMounts: (_b = data.volumeMounts) != null ? _b : []
3621
+ };
3622
+ }
3623
+ static async setNetwork(sandboxId, opts) {
3624
+ const config = new ConnectionConfig(opts);
3625
+ raiseIfLegacy(config, "Sandbox.setNetwork", SandboxError);
3626
+ const response = await fetch(
3627
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/network`,
3628
+ {
3629
+ method: "PUT",
3630
+ headers: getRawApiHeaders(config, {
3631
+ "Content-Type": "application/json"
3632
+ }),
3633
+ body: JSON.stringify(__spreadValues(__spreadValues({}, (opts == null ? void 0 : opts.allowOut) ? { allowOut: opts.allowOut } : {}), (opts == null ? void 0 : opts.denyOut) ? { denyOut: opts.denyOut } : {})),
3634
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3635
+ }
3636
+ );
3637
+ if (response.status === 404) {
3638
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3639
+ }
3640
+ if (!response.ok) {
3641
+ throw await createRawApiError(response);
3642
+ }
3643
+ }
3644
+ static async cloneSandboxes(sandboxId, opts) {
3645
+ var _a3, _b, _c, _d;
3646
+ const config = new ConnectionConfig(opts);
3647
+ const response = await fetch(
3648
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/clone`,
3649
+ {
3650
+ method: "POST",
3651
+ headers: getRawApiHeaders(config, {
3652
+ "Content-Type": "application/json"
3653
+ }),
3654
+ body: JSON.stringify(__spreadValues(__spreadValues({
3655
+ count: (_a3 = opts == null ? void 0 : opts.count) != null ? _a3 : 1,
3656
+ strict: (_b = opts == null ? void 0 : opts.strict) != null ? _b : false
3657
+ }, (opts == null ? void 0 : opts.nodeId) ? { nodeID: opts.nodeId } : {}), (opts == null ? void 0 : opts.timeoutMs) !== void 0 ? { timeout: timeoutToSeconds(opts.timeoutMs) } : {})),
3658
+ signal: config.getSignal((_c = opts == null ? void 0 : opts.requestTimeoutMs) != null ? _c : 3e5)
3659
+ }
3660
+ );
3661
+ if (response.status === 404) {
3464
3662
  throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3465
3663
  }
3466
- const err = handleApiError(res);
3467
- if (err) {
3468
- throw err;
3664
+ if (!response.ok) {
3665
+ throw await createRawApiError(response);
3469
3666
  }
3667
+ const data = await response.json();
3668
+ return {
3669
+ sandboxes: ((_d = data.sandboxes) != null ? _d : []).map((sandbox) => {
3670
+ var _a4, _b2;
3671
+ const baseSandboxId = (_a4 = sandbox.sandboxID) != null ? _a4 : "";
3672
+ const sandboxId2 = sandbox.clientID ? `${baseSandboxId}-${sandbox.clientID}` : baseSandboxId;
3673
+ return {
3674
+ sandboxId: sandboxId2,
3675
+ envdVersion: (_b2 = sandbox.envdVersion) != null ? _b2 : "0.0.0",
3676
+ envdAccessToken: sandbox.envdAccessToken,
3677
+ trafficAccessToken: sandbox.trafficAccessToken
3678
+ };
3679
+ }),
3680
+ snapshotTemplateId: data.snapshotTemplateId
3681
+ };
3470
3682
  }
3471
3683
  static async getFullInfo(sandboxId, opts) {
3472
- var _a3, _b, _c, _d;
3684
+ var _a3, _b, _c, _d, _e, _f;
3473
3685
  const config = new ConnectionConfig(opts);
3474
3686
  const client = new ApiClient(config);
3475
3687
  const res = await client.api.GET("/sandboxes/{sandboxID}", {
@@ -3490,12 +3702,29 @@ var SandboxApi = class {
3490
3702
  if (!res.data) {
3491
3703
  throw new Error("Sandbox not found");
3492
3704
  }
3705
+ if (config.isLegacyDomain) {
3706
+ const data = res.data;
3707
+ return __spreadProps(__spreadValues({
3708
+ sandboxId: data.clientID != null ? `${data.sandboxID}-${data.clientID}` : data.sandboxID,
3709
+ templateId: data.templateID
3710
+ }, data.alias && { name: data.alias }), {
3711
+ metadata: (_b = data.metadata) != null ? _b : {},
3712
+ envdVersion: (_c = data.envdVersion) != null ? _c : "0.0.0",
3713
+ envdAccessToken: data.envdAccessToken,
3714
+ startedAt: new Date(data.startedAt),
3715
+ endAt: new Date(data.endAt),
3716
+ state: data.state,
3717
+ cpuCount: data.cpuCount,
3718
+ memoryMB: data.memoryMB,
3719
+ sandboxDomain: data.domain || void 0
3720
+ });
3721
+ }
3493
3722
  return __spreadProps(__spreadValues({
3494
3723
  sandboxId: res.data.sandboxID,
3495
3724
  templateId: res.data.templateID
3496
3725
  }, res.data.alias && { name: res.data.alias }), {
3497
- metadata: (_b = res.data.metadata) != null ? _b : {},
3498
- allowInternetAccess: (_c = res.data.allowInternetAccess) != null ? _c : void 0,
3726
+ metadata: (_d = res.data.metadata) != null ? _d : {},
3727
+ allowInternetAccess: (_e = res.data.allowInternetAccess) != null ? _e : void 0,
3499
3728
  envdVersion: res.data.envdVersion,
3500
3729
  envdAccessToken: res.data.envdAccessToken,
3501
3730
  startedAt: new Date(res.data.startedAt),
@@ -3514,7 +3743,7 @@ var SandboxApi = class {
3514
3743
  autoResume: res.data.lifecycle.autoResume
3515
3744
  } : void 0,
3516
3745
  sandboxDomain: res.data.domain || void 0,
3517
- volumeMounts: (_d = res.data.volumeMounts) != null ? _d : []
3746
+ volumeMounts: (_f = res.data.volumeMounts) != null ? _f : []
3518
3747
  });
3519
3748
  }
3520
3749
  /**
@@ -3526,8 +3755,31 @@ var SandboxApi = class {
3526
3755
  * @returns `true` if the sandbox got paused, `false` if the sandbox was already paused.
3527
3756
  */
3528
3757
  static async pause(sandboxId, opts) {
3529
- var _a3, _b;
3758
+ var _a3, _b, _c;
3530
3759
  const config = new ConnectionConfig(opts);
3760
+ if (config.isLegacyDomain) {
3761
+ const response = await fetch(
3762
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/pause`,
3763
+ {
3764
+ method: "POST",
3765
+ headers: getRawApiHeaders(config, {
3766
+ "Content-Type": "application/json"
3767
+ }),
3768
+ body: JSON.stringify({ sync: (_a3 = opts == null ? void 0 : opts.sync) != null ? _a3 : false }),
3769
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3770
+ }
3771
+ );
3772
+ if (response.status === 404) {
3773
+ throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3774
+ }
3775
+ if (response.status === 409) {
3776
+ return false;
3777
+ }
3778
+ if (!response.ok) {
3779
+ throw await createRawApiError(response);
3780
+ }
3781
+ return true;
3782
+ }
3531
3783
  const client = new ApiClient(config);
3532
3784
  const res = await client.api.POST("/sandboxes/{sandboxID}/pause", {
3533
3785
  params: {
@@ -3537,10 +3789,10 @@ var SandboxApi = class {
3537
3789
  },
3538
3790
  signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3539
3791
  });
3540
- if (((_a3 = res.error) == null ? void 0 : _a3.code) === 404) {
3792
+ if (((_b = res.error) == null ? void 0 : _b.code) === 404) {
3541
3793
  throw new SandboxNotFoundError(`Sandbox ${sandboxId} not found`);
3542
3794
  }
3543
- if (((_b = res.error) == null ? void 0 : _b.code) === 409) {
3795
+ if (((_c = res.error) == null ? void 0 : _c.code) === 409) {
3544
3796
  return false;
3545
3797
  }
3546
3798
  const err = handleApiError(res);
@@ -3570,6 +3822,7 @@ var SandboxApi = class {
3570
3822
  static async createSnapshot(sandboxId, opts) {
3571
3823
  var _a3;
3572
3824
  const config = new ConnectionConfig(opts);
3825
+ raiseIfLegacy(config, "Sandbox.createSnapshot", SandboxError);
3573
3826
  const client = new ApiClient(config);
3574
3827
  const res = await client.api.POST("/sandboxes/{sandboxID}/snapshots", {
3575
3828
  params: {
@@ -3631,22 +3884,69 @@ var SandboxApi = class {
3631
3884
  return true;
3632
3885
  }
3633
3886
  static async createSandbox(template, timeoutMs, opts) {
3634
- var _a3, _b, _c;
3887
+ var _a3, _b, _c, _d, _e, _f;
3635
3888
  const config = new ConnectionConfig(opts);
3636
- const client = new ApiClient(config);
3637
3889
  const lifecycle = getLifecycle(opts);
3638
3890
  const autoPause = lifecycle.onTimeout === "pause";
3639
3891
  const autoResumeEnabled = lifecycle.onTimeout === "pause" ? (_a3 = lifecycle.autoResume) != null ? _a3 : false : void 0;
3640
- const body = __spreadValues(__spreadValues({
3892
+ if (config.isLegacyDomain) {
3893
+ if (opts == null ? void 0 : opts.mcp) {
3894
+ raiseIfLegacy(config, "Sandbox.create mcp", SandboxError);
3895
+ }
3896
+ if (opts == null ? void 0 : opts.network) {
3897
+ raiseIfLegacy(config, "Sandbox.create network", SandboxError);
3898
+ }
3899
+ if (opts == null ? void 0 : opts.volumeMounts) {
3900
+ raiseIfLegacy(config, "Sandbox.create volumeMounts", SandboxError);
3901
+ }
3902
+ if (opts == null ? void 0 : opts.secretEnvs) {
3903
+ raiseIfLegacy(config, "Sandbox.create secretEnvs", SandboxError);
3904
+ }
3905
+ const body2 = __spreadValues({
3906
+ templateID: template,
3907
+ timeout: timeoutToSeconds(timeoutMs),
3908
+ metadata: (_b = opts == null ? void 0 : opts.metadata) != null ? _b : {},
3909
+ envVars: (_c = opts == null ? void 0 : opts.envs) != null ? _c : {},
3910
+ secure: (_d = opts == null ? void 0 : opts.secure) != null ? _d : false,
3911
+ autoPause: autoPause != null ? autoPause : false
3912
+ }, (opts == null ? void 0 : opts.nodeId) ? { nodeID: opts.nodeId } : {});
3913
+ const response = await fetch(`${config.apiUrl}/sandboxes`, {
3914
+ method: "POST",
3915
+ headers: getRawApiHeaders(config, {
3916
+ "Content-Type": "application/json"
3917
+ }),
3918
+ body: JSON.stringify(body2),
3919
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
3920
+ });
3921
+ if (!response.ok) {
3922
+ throw await createRawApiError(response);
3923
+ }
3924
+ const data = await response.json();
3925
+ if ((0, import_compare_versions4.compareVersions)(data.envdVersion, "0.1.0") < 0) {
3926
+ await this.kill(data.sandboxID, opts);
3927
+ throw new TemplateError(
3928
+ "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."
3929
+ );
3930
+ }
3931
+ return {
3932
+ sandboxId: data.clientID ? `${data.sandboxID}-${data.clientID}` : data.sandboxID,
3933
+ sandboxDomain: data.domain || void 0,
3934
+ envdVersion: data.envdVersion,
3935
+ envdAccessToken: data.envdAccessToken,
3936
+ trafficAccessToken: void 0
3937
+ };
3938
+ }
3939
+ const client = new ApiClient(config);
3940
+ const body = __spreadValues(__spreadValues(__spreadValues({
3641
3941
  templateID: template,
3642
3942
  metadata: opts == null ? void 0 : opts.metadata,
3643
3943
  mcp: opts == null ? void 0 : opts.mcp,
3644
3944
  envVars: opts == null ? void 0 : opts.envs,
3645
3945
  timeout: timeoutToSeconds(timeoutMs),
3646
- secure: (_b = opts == null ? void 0 : opts.secure) != null ? _b : true,
3647
- allow_internet_access: (_c = opts == null ? void 0 : opts.allowInternetAccess) != null ? _c : true,
3946
+ secure: (_e = opts == null ? void 0 : opts.secure) != null ? _e : true,
3947
+ allow_internet_access: (_f = opts == null ? void 0 : opts.allowInternetAccess) != null ? _f : true,
3648
3948
  network: opts == null ? void 0 : opts.network
3649
- }, autoPause !== void 0 ? { autoPause } : {}), autoResumeEnabled !== void 0 ? { autoResume: { enabled: autoResumeEnabled } } : {});
3949
+ }, (opts == null ? void 0 : opts.nodeId) ? { nodeID: opts.nodeId } : {}), autoPause !== void 0 ? { autoPause } : {}), autoResumeEnabled !== void 0 ? { autoResume: { enabled: autoResumeEnabled } } : {});
3650
3950
  if (opts == null ? void 0 : opts.volumeMounts) {
3651
3951
  body.volumeMounts = Object.entries(opts.volumeMounts).map(
3652
3952
  ([mountPath, vol]) => ({
@@ -3655,6 +3955,14 @@ var SandboxApi = class {
3655
3955
  })
3656
3956
  );
3657
3957
  }
3958
+ const validatedSecretEnvs = validateSecretEnvs(
3959
+ opts == null ? void 0 : opts.secretEnvs,
3960
+ opts == null ? void 0 : opts.envs
3961
+ );
3962
+ if (Object.keys(validatedSecretEnvs).length > 0) {
3963
+ ;
3964
+ body.secrets = validatedSecretEnvs;
3965
+ }
3658
3966
  const res = await client.api.POST("/sandboxes", {
3659
3967
  body,
3660
3968
  signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
@@ -3681,6 +3989,33 @@ var SandboxApi = class {
3681
3989
  var _a3, _b;
3682
3990
  const timeoutMs = (_a3 = opts == null ? void 0 : opts.timeoutMs) != null ? _a3 : DEFAULT_SANDBOX_TIMEOUT_MS;
3683
3991
  const config = new ConnectionConfig(opts);
3992
+ if (config.isLegacyDomain) {
3993
+ const response = await fetch(
3994
+ `${config.apiUrl}/sandboxes/${encodeURIComponent(sandboxId)}/connect`,
3995
+ {
3996
+ method: "POST",
3997
+ headers: getRawApiHeaders(config, {
3998
+ "Content-Type": "application/json"
3999
+ }),
4000
+ body: JSON.stringify({ timeout: timeoutToSeconds(timeoutMs) }),
4001
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
4002
+ }
4003
+ );
4004
+ if (response.status === 404) {
4005
+ throw new SandboxNotFoundError(`Paused sandbox ${sandboxId} not found`);
4006
+ }
4007
+ if (!response.ok) {
4008
+ throw await createRawApiError(response);
4009
+ }
4010
+ const data = await response.json();
4011
+ return {
4012
+ sandboxId: data.clientID ? `${data.sandboxID}-${data.clientID}` : data.sandboxID,
4013
+ sandboxDomain: data.domain || void 0,
4014
+ envdVersion: data.envdVersion || "0.0.0",
4015
+ envdAccessToken: data.envdAccessToken,
4016
+ trafficAccessToken: void 0
4017
+ };
4018
+ }
3684
4019
  const client = new ApiClient(config);
3685
4020
  const res = await client.api.POST("/sandboxes/{sandboxID}/connect", {
3686
4021
  params: {
@@ -3708,6 +4043,69 @@ var SandboxApi = class {
3708
4043
  trafficAccessToken: res.data.trafficAccessToken || void 0
3709
4044
  };
3710
4045
  }
4046
+ static async getQuota(opts) {
4047
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
4048
+ const config = new ConnectionConfig(opts);
4049
+ const url = new URL(`${config.apiUrl}/sandboxes/quota`);
4050
+ const response = await fetch(url, {
4051
+ method: "GET",
4052
+ headers: __spreadValues(__spreadValues(__spreadValues({}, config.headers), config.apiKey ? { "X-API-KEY": config.apiKey } : {}), config.accessToken ? { Authorization: `Bearer ${config.accessToken}` } : {}),
4053
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
4054
+ });
4055
+ if (!response.ok) {
4056
+ throw await createRawApiError(response);
4057
+ }
4058
+ const data = await response.json();
4059
+ return {
4060
+ limit: {
4061
+ concurrentInstances: (_b = (_a3 = data.limit) == null ? void 0 : _a3.concurrentInstances) != null ? _b : 0,
4062
+ concurrentVcpu: (_d = (_c = data.limit) == null ? void 0 : _c.concurrentVcpu) != null ? _d : 0,
4063
+ concurrentRamMb: (_f = (_e = data.limit) == null ? void 0 : _e.concurrentRamMb) != null ? _f : 0,
4064
+ maxLengthHours: (_h = (_g = data.limit) == null ? void 0 : _g.maxLengthHours) != null ? _h : 0,
4065
+ diskMb: (_j = (_i = data.limit) == null ? void 0 : _i.diskMb) != null ? _j : 0,
4066
+ maxVcpu: (_l = (_k = data.limit) == null ? void 0 : _k.maxVcpu) != null ? _l : 0,
4067
+ maxRamMb: (_n = (_m = data.limit) == null ? void 0 : _m.maxRamMb) != null ? _n : 0
4068
+ },
4069
+ usage: {
4070
+ concurrentInstances: (_p = (_o = data.usage) == null ? void 0 : _o.concurrentInstances) != null ? _p : 0,
4071
+ concurrentVcpu: (_r = (_q = data.usage) == null ? void 0 : _q.concurrentVcpu) != null ? _r : 0,
4072
+ concurrentRamMb: (_t = (_s = data.usage) == null ? void 0 : _s.concurrentRamMb) != null ? _t : 0
4073
+ }
4074
+ };
4075
+ }
4076
+ static async getEvents(opts) {
4077
+ var _a3, _b, _c;
4078
+ const config = new ConnectionConfig(opts);
4079
+ const url = new URL(`${config.apiUrl}/sandboxes/events`);
4080
+ if ((opts == null ? void 0 : opts.startTime) !== void 0)
4081
+ url.searchParams.set("startTime", String(opts.startTime));
4082
+ if ((opts == null ? void 0 : opts.endTime) !== void 0)
4083
+ url.searchParams.set("endTime", String(opts.endTime));
4084
+ if (opts == null ? void 0 : opts.sandboxID) url.searchParams.set("sandboxID", opts.sandboxID);
4085
+ if (opts == null ? void 0 : opts.templateID) url.searchParams.set("templateID", opts.templateID);
4086
+ if (opts == null ? void 0 : opts.events) url.searchParams.set("events", opts.events);
4087
+ if (opts == null ? void 0 : opts.state) url.searchParams.set("state", opts.state);
4088
+ if ((opts == null ? void 0 : opts.orderAsc) !== void 0)
4089
+ url.searchParams.set("orderAsc", String(opts.orderAsc));
4090
+ if ((opts == null ? void 0 : opts.offset) !== void 0)
4091
+ url.searchParams.set("offset", String(opts.offset));
4092
+ if ((opts == null ? void 0 : opts.limit) !== void 0)
4093
+ url.searchParams.set("limit", String(opts.limit));
4094
+ const response = await fetch(url, {
4095
+ method: "GET",
4096
+ headers: __spreadValues(__spreadValues(__spreadValues({}, config.headers), config.apiKey ? { "X-API-KEY": config.apiKey } : {}), config.accessToken ? { Authorization: `Bearer ${config.accessToken}` } : {}),
4097
+ signal: config.getSignal(opts == null ? void 0 : opts.requestTimeoutMs)
4098
+ });
4099
+ if (!response.ok) {
4100
+ throw await createRawApiError(response);
4101
+ }
4102
+ const data = await response.json();
4103
+ return {
4104
+ items: (_a3 = data.items) != null ? _a3 : [],
4105
+ total: (_b = data.total) != null ? _b : 0,
4106
+ hasMore: (_c = data.hasMore) != null ? _c : false
4107
+ };
4108
+ }
3711
4109
  };
3712
4110
  var BasePaginator = class {
3713
4111
  constructor(config, limit, nextToken) {
@@ -3740,7 +4138,7 @@ var SandboxPaginator = class extends BasePaginator {
3740
4138
  this.query = opts == null ? void 0 : opts.query;
3741
4139
  }
3742
4140
  async nextItems() {
3743
- var _a3, _b, _c;
4141
+ var _a3, _b, _c, _d, _e;
3744
4142
  if (!this.hasNext) {
3745
4143
  throw new Error("No more items to fetch");
3746
4144
  }
@@ -3754,11 +4152,55 @@ var SandboxPaginator = class extends BasePaginator {
3754
4152
  );
3755
4153
  metadata = new URLSearchParams(encodedPairs).toString();
3756
4154
  }
4155
+ if (this.config.isLegacyDomain) {
4156
+ const params = new URLSearchParams();
4157
+ if (metadata) {
4158
+ params.set("metadata", metadata);
4159
+ }
4160
+ if ((_c = (_b = this.query) == null ? void 0 : _b.state) == null ? void 0 : _c.length) {
4161
+ params.set("state", this.query.state.join(","));
4162
+ }
4163
+ if (this.limit) {
4164
+ params.set("limit", String(this.limit));
4165
+ }
4166
+ if (this.nextToken) {
4167
+ params.set("nextToken", this.nextToken);
4168
+ }
4169
+ const query = params.toString();
4170
+ const response = await fetch(
4171
+ `${this.config.apiUrl}/v2/sandboxes${query ? `?${query}` : ""}`,
4172
+ {
4173
+ headers: getRawApiHeaders(this.config),
4174
+ signal: this.config.getSignal()
4175
+ }
4176
+ );
4177
+ if (!response.ok) {
4178
+ throw await createRawApiError(response);
4179
+ }
4180
+ this.updatePagination(response);
4181
+ const data = await response.json();
4182
+ return (data != null ? data : []).map((sandbox) => {
4183
+ var _a4, _b2;
4184
+ return __spreadProps(__spreadValues({
4185
+ sandboxId: sandbox.clientID != null ? `${sandbox.sandboxID}-${sandbox.clientID}` : sandbox.sandboxID,
4186
+ templateId: sandbox.templateID
4187
+ }, sandbox.alias && { name: sandbox.alias }), {
4188
+ metadata: (_a4 = sandbox.metadata) != null ? _a4 : {},
4189
+ startedAt: new Date(sandbox.startedAt),
4190
+ endAt: new Date(sandbox.endAt),
4191
+ state: sandbox.state,
4192
+ cpuCount: sandbox.cpuCount,
4193
+ memoryMB: sandbox.memoryMB,
4194
+ envdVersion: sandbox.envdVersion,
4195
+ volumeMounts: (_b2 = sandbox.volumeMounts) != null ? _b2 : []
4196
+ });
4197
+ });
4198
+ }
3757
4199
  const res = await this.client.api.GET("/v2/sandboxes", {
3758
4200
  params: {
3759
4201
  query: {
3760
4202
  metadata,
3761
- state: (_b = this.query) == null ? void 0 : _b.state,
4203
+ state: (_d = this.query) == null ? void 0 : _d.state,
3762
4204
  limit: this.limit,
3763
4205
  nextToken: this.nextToken
3764
4206
  }
@@ -3771,11 +4213,11 @@ var SandboxPaginator = class extends BasePaginator {
3771
4213
  throw err;
3772
4214
  }
3773
4215
  this.updatePagination(res.response);
3774
- return ((_c = res.data) != null ? _c : []).map(
4216
+ return ((_e = res.data) != null ? _e : []).map(
3775
4217
  (sandbox) => {
3776
4218
  var _a4, _b2;
3777
4219
  return __spreadProps(__spreadValues({
3778
- sandboxId: sandbox.sandboxID,
4220
+ sandboxId: this.config.isLegacyDomain && sandbox.clientID != null ? `${sandbox.sandboxID}-${sandbox.clientID}` : sandbox.sandboxID,
3779
4221
  templateId: sandbox.templateID
3780
4222
  }, sandbox.alias && { name: sandbox.alias }), {
3781
4223
  metadata: (_a4 = sandbox.metadata) != null ? _a4 : {},
@@ -3794,6 +4236,7 @@ var SandboxPaginator = class extends BasePaginator {
3794
4236
  var SnapshotPaginator = class extends BasePaginator {
3795
4237
  constructor(opts) {
3796
4238
  super(new ConnectionConfig(opts), opts == null ? void 0 : opts.limit, opts == null ? void 0 : opts.nextToken);
4239
+ raiseIfLegacy(this.config, "Sandbox.listSnapshots", SandboxError);
3797
4240
  this.sandboxId = opts == null ? void 0 : opts.sandboxId;
3798
4241
  }
3799
4242
  async nextItems() {
@@ -3933,7 +4376,7 @@ var Sandbox = class extends SandboxApi {
3933
4376
  sandboxOpts
3934
4377
  );
3935
4378
  const sandbox = new this(__spreadValues(__spreadValues({}, sandboxInfo), config));
3936
- if (sandboxOpts == null ? void 0 : sandboxOpts.mcp) {
4379
+ if ((sandboxOpts == null ? void 0 : sandboxOpts.mcp) && !config.isLegacyDomain) {
3937
4380
  sandbox.mcpToken = crypto.randomUUID();
3938
4381
  const res = await sandbox.commands.run(
3939
4382
  `mcp-gateway --config ${shellQuote(JSON.stringify(sandboxOpts.mcp))}`,
@@ -3950,6 +4393,101 @@ var Sandbox = class extends SandboxApi {
3950
4393
  }
3951
4394
  return sandbox;
3952
4395
  }
4396
+ /**
4397
+ * Clone a sandbox to create one or more identical sandboxes.
4398
+ *
4399
+ * @param sandboxId source sandbox ID.
4400
+ * @param opts clone options.
4401
+ *
4402
+ * @returns cloned sandbox instances plus snapshot template ID.
4403
+ */
4404
+ static async clone(sandboxId, opts) {
4405
+ const cloneResult = await SandboxApi.cloneSandboxes(sandboxId, opts);
4406
+ const config = new ConnectionConfig(opts);
4407
+ const sandboxes = cloneResult.sandboxes.map(
4408
+ (sandbox) => new this(__spreadValues({
4409
+ sandboxId: sandbox.sandboxId,
4410
+ envdVersion: sandbox.envdVersion,
4411
+ envdAccessToken: sandbox.envdAccessToken,
4412
+ trafficAccessToken: sandbox.trafficAccessToken
4413
+ }, config))
4414
+ );
4415
+ return {
4416
+ sandboxes,
4417
+ count: sandboxes.length,
4418
+ snapshotTemplateId: cloneResult.snapshotTemplateId
4419
+ };
4420
+ }
4421
+ /**
4422
+ * Dynamically adjust the hotplugged memory on a running sandbox.
4423
+ *
4424
+ * The value represents the new *total* plugged size in MiB, not a delta.
4425
+ * Use `0` to unplug all hotplugged memory.
4426
+ */
4427
+ static async hotplugMemory(sandboxId, requestedSizeMib, opts) {
4428
+ return await SandboxApi.hotplugMemory(
4429
+ sandboxId,
4430
+ requestedSizeMib,
4431
+ opts
4432
+ );
4433
+ }
4434
+ /**
4435
+ * Mount a volume to a sandbox.
4436
+ *
4437
+ * @param sandboxId sandbox ID.
4438
+ * @param name volume name.
4439
+ * @param path mount path inside the sandbox.
4440
+ * @param opts connection options.
4441
+ *
4442
+ * @returns updated sandbox information.
4443
+ */
4444
+ static async mountVolume(sandboxId, name, path2, opts) {
4445
+ return await SandboxApi.mountVolume(sandboxId, name, path2, opts);
4446
+ }
4447
+ /**
4448
+ * Unmount a volume from a sandbox.
4449
+ *
4450
+ * @param sandboxId sandbox ID.
4451
+ * @param path mount path to unmount.
4452
+ * @param opts connection options.
4453
+ * @param [opts.force] force unmount.
4454
+ *
4455
+ * @returns updated sandbox information.
4456
+ */
4457
+ static async unmountVolume(sandboxId, path2, opts) {
4458
+ return await SandboxApi.unmountVolume(sandboxId, path2, opts);
4459
+ }
4460
+ /**
4461
+ * Commit a sandbox to create a snapshot template.
4462
+ *
4463
+ * @deprecated Use snapshot APIs instead. This method is only supported for modern domains.
4464
+ *
4465
+ * @param sandboxId source sandbox ID.
4466
+ * @param opts commit options.
4467
+ *
4468
+ * @returns created snapshot template.
4469
+ */
4470
+ static async commit(sandboxId, opts) {
4471
+ return await SandboxApi.commit(sandboxId, opts);
4472
+ }
4473
+ /**
4474
+ * Reset sandbox memory and optionally resume the sandbox.
4475
+ *
4476
+ * @param sandboxId sandbox ID.
4477
+ * @param opts reset options.
4478
+ *
4479
+ * @returns `true` if reset succeeded, `false` if the sandbox is in a conflicting state.
4480
+ */
4481
+ static async reset(sandboxId, opts) {
4482
+ return await SandboxApi.reset(sandboxId, opts);
4483
+ }
4484
+ /**
4485
+ * Update network egress rules for a running sandbox.
4486
+ * Omitting both `allowOut` and `denyOut` clears all egress rules.
4487
+ */
4488
+ static async setNetwork(sandboxId, opts) {
4489
+ await SandboxApi.setNetwork(sandboxId, opts);
4490
+ }
3953
4491
  static async betaCreate(templateOrOpts, opts) {
3954
4492
  var _a3, _b, _c;
3955
4493
  const { template, sandboxOpts } = typeof templateOrOpts === "string" ? {
@@ -3972,7 +4510,7 @@ var Sandbox = class extends SandboxApi {
3972
4510
  sandboxOpts
3973
4511
  );
3974
4512
  const sandbox = new this(__spreadValues(__spreadValues({}, sandboxInfo), config));
3975
- if (sandboxOpts == null ? void 0 : sandboxOpts.mcp) {
4513
+ if ((sandboxOpts == null ? void 0 : sandboxOpts.mcp) && !config.isLegacyDomain) {
3976
4514
  sandbox.mcpToken = crypto.randomUUID();
3977
4515
  const res = await sandbox.commands.run(
3978
4516
  `mcp-gateway --config ${shellQuote(JSON.stringify(sandboxOpts.mcp))}`,
@@ -4114,6 +4652,68 @@ var Sandbox = class extends SandboxApi {
4114
4652
  this.resolveApiOpts(opts)
4115
4653
  );
4116
4654
  }
4655
+ /**
4656
+ * Reset sandbox memory and optionally resume the sandbox.
4657
+ *
4658
+ * @param opts reset options.
4659
+ *
4660
+ * @returns `true` if reset succeeded, `false` if the sandbox is in a conflicting state.
4661
+ */
4662
+ async reset(opts) {
4663
+ return await SandboxApi.reset(this.sandboxId, this.resolveApiOpts(opts));
4664
+ }
4665
+ /**
4666
+ * Update network egress rules for this running sandbox.
4667
+ * Omitting both `allowOut` and `denyOut` clears all egress rules.
4668
+ */
4669
+ async setNetwork(opts) {
4670
+ await SandboxApi.setNetwork(this.sandboxId, this.resolveApiOpts(opts));
4671
+ }
4672
+ /**
4673
+ * Commit this sandbox to create a snapshot template.
4674
+ *
4675
+ * @deprecated Use snapshot APIs instead. This method is only supported for modern domains.
4676
+ *
4677
+ * @param opts commit options.
4678
+ *
4679
+ * @returns created snapshot template.
4680
+ */
4681
+ async commit(opts) {
4682
+ return await SandboxApi.commit(this.sandboxId, this.resolveApiOpts(opts));
4683
+ }
4684
+ /**
4685
+ * Mount a volume to this sandbox.
4686
+ *
4687
+ * @param name volume name.
4688
+ * @param path mount path inside the sandbox.
4689
+ * @param opts connection options.
4690
+ *
4691
+ * @returns updated sandbox information.
4692
+ */
4693
+ async mountVolume(name, path2, opts) {
4694
+ return await SandboxApi.mountVolume(
4695
+ this.sandboxId,
4696
+ name,
4697
+ path2,
4698
+ this.resolveApiOpts(opts)
4699
+ );
4700
+ }
4701
+ /**
4702
+ * Unmount a volume from this sandbox.
4703
+ *
4704
+ * @param path mount path to unmount.
4705
+ * @param opts connection options.
4706
+ * @param [opts.force] force unmount.
4707
+ *
4708
+ * @returns updated sandbox information.
4709
+ */
4710
+ async unmountVolume(path2, opts) {
4711
+ return await SandboxApi.unmountVolume(
4712
+ this.sandboxId,
4713
+ path2,
4714
+ this.resolveApiOpts(opts)
4715
+ );
4716
+ }
4117
4717
  /**
4118
4718
  * Kill the sandbox.
4119
4719
  *
@@ -4125,6 +4725,22 @@ var Sandbox = class extends SandboxApi {
4125
4725
  }
4126
4726
  await SandboxApi.kill(this.sandboxId, this.resolveApiOpts(opts));
4127
4727
  }
4728
+ /**
4729
+ * Dynamically adjust the hotplugged memory on a running sandbox.
4730
+ *
4731
+ * The value represents the new *total* plugged size in MiB, not a delta.
4732
+ * Use `0` to unplug all hotplugged memory.
4733
+ *
4734
+ * @param requestedSizeMib new total plugged memory size in MiB.
4735
+ * @param opts connection options.
4736
+ */
4737
+ async hotplugMemory(requestedSizeMib, opts) {
4738
+ await SandboxApi.hotplugMemory(
4739
+ this.sandboxId,
4740
+ requestedSizeMib,
4741
+ this.resolveApiOpts(opts)
4742
+ );
4743
+ }
4128
4744
  /**
4129
4745
  * Pause a sandbox by its ID.
4130
4746
  *
@@ -4145,7 +4761,10 @@ var Sandbox = class extends SandboxApi {
4145
4761
  * @deprecated Use {@link Sandbox.pause} instead.
4146
4762
  */
4147
4763
  async betaPause(opts) {
4148
- return await SandboxApi.betaPause(this.sandboxId, this.resolveApiOpts(opts));
4764
+ return await SandboxApi.betaPause(
4765
+ this.sandboxId,
4766
+ this.resolveApiOpts(opts)
4767
+ );
4149
4768
  }
4150
4769
  /**
4151
4770
  * Create a snapshot of the sandbox's current state.
@@ -4197,6 +4816,7 @@ var Sandbox = class extends SandboxApi {
4197
4816
  * @returns MCP URL for the sandbox.
4198
4817
  */
4199
4818
  getMcpUrl() {
4819
+ raiseIfLegacy(this.connectionConfig, "Sandbox.getMcpUrl", SandboxError);
4200
4820
  return `https://${this.getHost(this.mcpPort)}/mcp`;
4201
4821
  }
4202
4822
  /**
@@ -4205,6 +4825,7 @@ var Sandbox = class extends SandboxApi {
4205
4825
  * @returns MCP token for the sandbox, or undefined if MCP is not enabled.
4206
4826
  */
4207
4827
  async getMcpToken() {
4828
+ raiseIfLegacy(this.connectionConfig, "Sandbox.getMcpToken", SandboxError);
4208
4829
  if (!this.mcpToken) {
4209
4830
  this.mcpToken = await this.files.read("/etc/mcp-gateway/.token", {
4210
4831
  user: "root"
@@ -5158,6 +5779,123 @@ function handleCmdEntrypointInstruction(instruction, templateBuilder) {
5158
5779
  }
5159
5780
 
5160
5781
  // src/core/template/index.ts
5782
+ var TemplateApi = class {
5783
+ static async listTemplates(options) {
5784
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
5785
+ const templateType = (_a3 = options == null ? void 0 : options.templateType) != null ? _a3 : "template_build";
5786
+ const page = (_b = options == null ? void 0 : options.page) != null ? _b : 1;
5787
+ const limit = (_c = options == null ? void 0 : options.limit) != null ? _c : 20;
5788
+ if (!["template_build", "snapshot_template"].includes(templateType)) {
5789
+ throw new Error(
5790
+ 'templateType must be either "template_build" or "snapshot_template"'
5791
+ );
5792
+ }
5793
+ if (page < 1) {
5794
+ throw new Error("page must be >= 1");
5795
+ }
5796
+ if (limit < 1 || limit > 100) {
5797
+ throw new Error("limit must be between 1 and 100");
5798
+ }
5799
+ const config = new ConnectionConfig(options);
5800
+ const url = new URL(`${config.apiUrl}/v2/templates`);
5801
+ url.searchParams.set("template_type", templateType);
5802
+ url.searchParams.set("page", String(page));
5803
+ url.searchParams.set("limit", String(limit));
5804
+ const response = await fetch(url, {
5805
+ method: "GET",
5806
+ headers: __spreadValues(__spreadValues(__spreadValues({}, config.headers), config.apiKey ? { "X-API-KEY": config.apiKey } : {}), config.accessToken ? { Authorization: `Bearer ${config.accessToken}` } : {}),
5807
+ signal: config.getSignal(options == null ? void 0 : options.requestTimeoutMs)
5808
+ });
5809
+ if (!response.ok) {
5810
+ throw new Error(`${response.status}: ${await response.text()}`);
5811
+ }
5812
+ const data = await response.json();
5813
+ if (!data) {
5814
+ throw new Error("Template list response data is missing");
5815
+ }
5816
+ return {
5817
+ templates: ((_d = data.templates) != null ? _d : []).map(
5818
+ (template) => ({
5819
+ templateId: template.templateID,
5820
+ aliases: template.aliases,
5821
+ buildCount: template.buildCount,
5822
+ buildId: template.buildID,
5823
+ cpuCount: template.cpuCount,
5824
+ createdAt: new Date(template.createdAt),
5825
+ createdBy: template.createdBy,
5826
+ diskSizeMB: template.diskSizeMB,
5827
+ envdVersion: template.envdVersion,
5828
+ lastSpawnedAt: template.lastSpawnedAt ? new Date(template.lastSpawnedAt) : null,
5829
+ memoryMB: template.memoryMB,
5830
+ metadata: template.metadata,
5831
+ public: template.public,
5832
+ spawnCount: template.spawnCount,
5833
+ templateType: template.templateType,
5834
+ originSandboxID: template.originSandboxID,
5835
+ updatedAt: new Date(template.updatedAt)
5836
+ })
5837
+ ),
5838
+ page: (_e = data.page) != null ? _e : page,
5839
+ limit: (_f = data.limit) != null ? _f : limit,
5840
+ total: (_g = data.total) != null ? _g : 0,
5841
+ totalPages: (_h = data.totalPages) != null ? _h : 0
5842
+ };
5843
+ }
5844
+ static async deleteTemplate(templateId, options) {
5845
+ var _a3;
5846
+ if (!templateId) {
5847
+ throw new Error("templateId cannot be empty");
5848
+ }
5849
+ const config = new ConnectionConfig(options);
5850
+ const client = new ApiClient(config);
5851
+ const res = await client.api.DELETE("/templates/{templateID}", {
5852
+ params: {
5853
+ path: {
5854
+ templateID: templateId
5855
+ }
5856
+ },
5857
+ signal: config.getSignal(options == null ? void 0 : options.requestTimeoutMs)
5858
+ });
5859
+ if (((_a3 = res.error) == null ? void 0 : _a3.code) === 404 || res.response.status === 404) {
5860
+ return false;
5861
+ }
5862
+ const err = handleApiError(res, BuildError);
5863
+ if (err) {
5864
+ throw err;
5865
+ }
5866
+ return true;
5867
+ }
5868
+ };
5869
+ var TemplatePaginator = class {
5870
+ constructor(options) {
5871
+ this.hasNextPage = true;
5872
+ var _a3;
5873
+ this.options = options;
5874
+ this.nextPageNumber = (_a3 = options == null ? void 0 : options.page) != null ? _a3 : 1;
5875
+ }
5876
+ get hasNext() {
5877
+ return this.hasNextPage;
5878
+ }
5879
+ get currentPage() {
5880
+ return this.nextPageNumber;
5881
+ }
5882
+ async nextPage() {
5883
+ if (!this.hasNextPage) {
5884
+ throw new Error("No more pages to fetch");
5885
+ }
5886
+ const response = await TemplateApi.listTemplates(__spreadProps(__spreadValues({}, this.options), {
5887
+ page: this.nextPageNumber
5888
+ }));
5889
+ this.hasNextPage = response.page < response.totalPages;
5890
+ this.nextPageNumber = response.page + 1;
5891
+ return response;
5892
+ }
5893
+ reset() {
5894
+ var _a3, _b;
5895
+ this.nextPageNumber = (_b = (_a3 = this.options) == null ? void 0 : _a3.page) != null ? _b : 1;
5896
+ this.hasNextPage = true;
5897
+ }
5898
+ };
5161
5899
  var _a2;
5162
5900
  var TemplateBase = class _TemplateBase {
5163
5901
  constructor(options) {
@@ -5167,6 +5905,7 @@ var TemplateBase = class _TemplateBase {
5167
5905
  this.registryConfig = void 0;
5168
5906
  this.startCmd = void 0;
5169
5907
  this.readyCmd = void 0;
5908
+ this.patchCmd = void 0;
5170
5909
  // Force the whole template to be rebuilt
5171
5910
  this.force = false;
5172
5911
  // Force the next layer to be rebuilt
@@ -5182,6 +5921,9 @@ var TemplateBase = class _TemplateBase {
5182
5921
  this.fileContextPath = (_a3 = options == null ? void 0 : options.fileContextPath) != null ? _a3 : this.fileContextPath;
5183
5922
  this.fileIgnorePatterns = (_b = options == null ? void 0 : options.fileIgnorePatterns) != null ? _b : this.fileIgnorePatterns;
5184
5923
  }
5924
+ static list(options) {
5925
+ return new TemplatePaginator(options);
5926
+ }
5185
5927
  /**
5186
5928
  * Convert a template to JSON representation.
5187
5929
  *
@@ -5260,6 +6002,15 @@ var TemplateBase = class _TemplateBase {
5260
6002
  logsOffset: options == null ? void 0 : options.logsOffset
5261
6003
  });
5262
6004
  }
6005
+ static async listTemplates(options) {
6006
+ return TemplateApi.listTemplates(options);
6007
+ }
6008
+ static async delete(templateId, options) {
6009
+ await TemplateApi.deleteTemplate(templateId, options);
6010
+ }
6011
+ static async deleteTemplate(templateId, options) {
6012
+ return TemplateApi.deleteTemplate(templateId, options);
6013
+ }
5263
6014
  /**
5264
6015
  * Check if a template with the given name exists.
5265
6016
  *
@@ -5728,6 +6479,11 @@ var TemplateBase = class _TemplateBase {
5728
6479
  this.collectStackTrace();
5729
6480
  return this;
5730
6481
  }
6482
+ setPatchCmd(cmd) {
6483
+ this.patchCmd = cmd;
6484
+ this.collectStackTrace();
6485
+ return this;
6486
+ }
5731
6487
  setEnvs(envs) {
5732
6488
  if (Object.keys(envs).length === 0) {
5733
6489
  return this;
@@ -6053,6 +6809,7 @@ var TemplateBase = class _TemplateBase {
6053
6809
  const templateData = {
6054
6810
  startCmd: this.startCmd,
6055
6811
  readyCmd: this.readyCmd,
6812
+ preBootScript: this.patchCmd,
6056
6813
  steps,
6057
6814
  force: this.force
6058
6815
  };
@@ -6079,6 +6836,8 @@ Template.aliasExists = TemplateBase.aliasExists;
6079
6836
  Template.assignTags = TemplateBase.assignTags;
6080
6837
  Template.removeTags = TemplateBase.removeTags;
6081
6838
  Template.getTags = TemplateBase.getTags;
6839
+ Template.list = TemplateBase.list;
6840
+ Template.delete = TemplateBase.delete;
6082
6841
  Template.toJSON = TemplateBase.toJSON;
6083
6842
  Template.toDockerfile = TemplateBase.toDockerfile;
6084
6843
 
@@ -6561,13 +7320,16 @@ var code_interpreter_default = Sandbox2;
6561
7320
  Sandbox,
6562
7321
  SandboxError,
6563
7322
  SandboxNotFoundError,
7323
+ Secret,
7324
+ SecretError,
6564
7325
  Template,
7326
+ TemplateApi,
6565
7327
  TemplateBase,
6566
7328
  TemplateError,
7329
+ TemplatePaginator,
6567
7330
  TimeoutError,
6568
7331
  Volume,
6569
7332
  VolumeError,
6570
- VolumeFileType,
6571
7333
  defaultBuildLogger,
6572
7334
  getSignature,
6573
7335
  waitForFile,