@buddy-works/sandbox-sdk 0.1.6-rc.0 → 0.1.6

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.
package/dist/index.mjs CHANGED
@@ -21,6 +21,10 @@ const getSandboxResponseTransformer = async (data) => {
21
21
  data = sandboxResponseSchemaResponseTransformer(data);
22
22
  return data;
23
23
  };
24
+ const updateSandboxResponseTransformer = async (data) => {
25
+ data = sandboxResponseSchemaResponseTransformer(data);
26
+ return data;
27
+ };
24
28
  const startSandboxAppResponseTransformer = async (data) => {
25
29
  data = sandboxResponseSchemaResponseTransformer(data);
26
30
  return data;
@@ -34,6 +38,14 @@ const sandboxCommandViewSchemaResponseTransformer = (data) => {
34
38
  if (data.finish_date) data.finish_date = new Date(data.finish_date);
35
39
  return data;
36
40
  };
41
+ const sandboxCommandsViewSchemaResponseTransformer = (data) => {
42
+ if (data.commands) data.commands = data.commands.map((item) => sandboxCommandViewSchemaResponseTransformer(item));
43
+ return data;
44
+ };
45
+ const getSandboxCommandsResponseTransformer = async (data) => {
46
+ data = sandboxCommandsViewSchemaResponseTransformer(data);
47
+ return data;
48
+ };
37
49
  const executeSandboxCommandResponseTransformer = async (data) => {
38
50
  data = sandboxCommandViewSchemaResponseTransformer(data);
39
51
  return data;
@@ -70,6 +82,30 @@ const restartSandboxResponseTransformer = async (data) => {
70
82
  data = sandboxResponseSchemaResponseTransformer(data);
71
83
  return data;
72
84
  };
85
+ const shortSnapshotViewSchemaResponseTransformer = (data) => {
86
+ if (data.create_date) data.create_date = new Date(data.create_date);
87
+ return data;
88
+ };
89
+ const snapshotsViewSchemaResponseTransformer = (data) => {
90
+ if (data.snapshots) data.snapshots = data.snapshots.map((item) => shortSnapshotViewSchemaResponseTransformer(item));
91
+ return data;
92
+ };
93
+ const getSandboxSnapshotsResponseTransformer = async (data) => {
94
+ data = snapshotsViewSchemaResponseTransformer(data);
95
+ return data;
96
+ };
97
+ const snapshotViewSchemaResponseTransformer = (data) => {
98
+ if (data.create_date) data.create_date = new Date(data.create_date);
99
+ return data;
100
+ };
101
+ const addSandboxSnapshotResponseTransformer = async (data) => {
102
+ data = snapshotViewSchemaResponseTransformer(data);
103
+ return data;
104
+ };
105
+ const getSandboxSnapshotResponseTransformer = async (data) => {
106
+ data = snapshotViewSchemaResponseTransformer(data);
107
+ return data;
108
+ };
73
109
  const startSandboxResponseTransformer = async (data) => {
74
110
  data = sandboxResponseSchemaResponseTransformer(data);
75
111
  return data;
@@ -78,6 +114,10 @@ const stopSandboxResponseTransformer = async (data) => {
78
114
  data = sandboxResponseSchemaResponseTransformer(data);
79
115
  return data;
80
116
  };
117
+ const getProjectSnapshotsResponseTransformer = async (data) => {
118
+ data = snapshotsViewSchemaResponseTransformer(data);
119
+ return data;
120
+ };
81
121
 
82
122
  //#endregion
83
123
  //#region src/api/openapi/zod.gen.ts
@@ -317,94 +357,6 @@ const zPipelinePropertyView = z.object({
317
357
  value: z.string().optional()
318
358
  });
319
359
  /**
320
- * MSSQL authentication credentials
321
- */
322
- const zMssqlAuthView = z.object({
323
- method: z.enum(["PASSWORD"]).optional(),
324
- username: z.string(),
325
- password: z.string()
326
- });
327
- /**
328
- * MongoDB authentication credentials
329
- */
330
- const zMongoAuthView = z.object({
331
- method: z.enum(["PASSWORD"]).optional(),
332
- username: z.string(),
333
- password: z.string()
334
- });
335
- /**
336
- * PostgreSQL authentication credentials
337
- */
338
- const zPostgresqlAuthView = z.object({
339
- method: z.enum(["PASSWORD"]).optional(),
340
- username: z.string(),
341
- password: z.string()
342
- });
343
- /**
344
- * MySQL authentication credentials
345
- */
346
- const zMysqlAuthView = z.object({
347
- method: z.enum(["PASSWORD"]).optional(),
348
- username: z.string(),
349
- password: z.string()
350
- });
351
- /**
352
- * Define proxy servers' authentication method using the following parameters
353
- */
354
- const zSshAuthView = z.object({
355
- method: z.enum([
356
- "PASSWORD",
357
- "SSH_KEY",
358
- "ASSETS_KEY",
359
- "PROXY_CREDENTIALS",
360
- "PROXY_KEY"
361
- ]),
362
- username: z.string().optional(),
363
- password: z.string().optional(),
364
- asset: z.string().optional(),
365
- passphrase: z.string().optional(),
366
- key: z.string().optional(),
367
- key_path: z.string().optional()
368
- });
369
- /**
370
- * Kubernetes cluster authentication method
371
- */
372
- const zK8sAuthView = z.object({
373
- method: z.enum([
374
- "PASS",
375
- "CERT",
376
- "TOKEN"
377
- ]),
378
- username: z.string().optional(),
379
- password: z.string().optional(),
380
- certificate_authority: z.string().optional(),
381
- client_certificate: z.string().optional(),
382
- client_key: z.string().optional(),
383
- token: z.string().optional()
384
- });
385
- /**
386
- * Authentication details
387
- */
388
- const zGitAuthView = z.object({
389
- method: z.enum([
390
- "HTTP",
391
- "SSH_KEY",
392
- "ASSETS_KEY",
393
- "CURRENT"
394
- ]),
395
- username: z.string().optional(),
396
- password: z.string().optional(),
397
- asset: z.string().optional(),
398
- key: z.string().optional()
399
- });
400
- /**
401
- * Authentication details
402
- */
403
- const zFtpAuthView = z.object({
404
- username: z.string(),
405
- password: z.string()
406
- });
407
- /**
408
360
  * Defines how the target can be used (as deployment target, proxy, or both)
409
361
  */
410
362
  const zUseAsView = z.object({
@@ -516,6 +468,94 @@ const zShortProjectView = z.object({
516
468
  access: z.enum(["PRIVATE", "PUBLIC"]).optional(),
517
469
  create_date: z.iso.datetime().optional()
518
470
  });
471
+ /**
472
+ * MSSQL authentication credentials
473
+ */
474
+ const zMssqlAuthView = z.object({
475
+ method: z.enum(["PASSWORD"]).optional(),
476
+ username: z.string(),
477
+ password: z.string()
478
+ });
479
+ /**
480
+ * MongoDB authentication credentials
481
+ */
482
+ const zMongoAuthView = z.object({
483
+ method: z.enum(["PASSWORD"]).optional(),
484
+ username: z.string(),
485
+ password: z.string()
486
+ });
487
+ /**
488
+ * PostgreSQL authentication credentials
489
+ */
490
+ const zPostgresqlAuthView = z.object({
491
+ method: z.enum(["PASSWORD"]).optional(),
492
+ username: z.string(),
493
+ password: z.string()
494
+ });
495
+ /**
496
+ * MySQL authentication credentials
497
+ */
498
+ const zMysqlAuthView = z.object({
499
+ method: z.enum(["PASSWORD"]).optional(),
500
+ username: z.string(),
501
+ password: z.string()
502
+ });
503
+ /**
504
+ * Define proxy servers' authentication method using the following parameters
505
+ */
506
+ const zSshAuthView = z.object({
507
+ method: z.enum([
508
+ "PASSWORD",
509
+ "SSH_KEY",
510
+ "ASSETS_KEY",
511
+ "PROXY_CREDENTIALS",
512
+ "PROXY_KEY"
513
+ ]),
514
+ username: z.string().optional(),
515
+ password: z.string().optional(),
516
+ asset: z.string().optional(),
517
+ passphrase: z.string().optional(),
518
+ key: z.string().optional(),
519
+ key_path: z.string().optional()
520
+ });
521
+ /**
522
+ * Kubernetes cluster authentication method
523
+ */
524
+ const zK8sAuthView = z.object({
525
+ method: z.enum([
526
+ "PASS",
527
+ "CERT",
528
+ "TOKEN"
529
+ ]),
530
+ username: z.string().optional(),
531
+ password: z.string().optional(),
532
+ certificate_authority: z.string().optional(),
533
+ client_certificate: z.string().optional(),
534
+ client_key: z.string().optional(),
535
+ token: z.string().optional()
536
+ });
537
+ /**
538
+ * Authentication details
539
+ */
540
+ const zGitAuthView = z.object({
541
+ method: z.enum([
542
+ "HTTP",
543
+ "SSH_KEY",
544
+ "ASSETS_KEY",
545
+ "CURRENT"
546
+ ]),
547
+ username: z.string().optional(),
548
+ password: z.string().optional(),
549
+ asset: z.string().optional(),
550
+ key: z.string().optional()
551
+ });
552
+ /**
553
+ * Authentication details
554
+ */
555
+ const zFtpAuthView = z.object({
556
+ username: z.string(),
557
+ password: z.string()
558
+ });
519
559
  const zIdsView = z.object({
520
560
  url: z.string().readonly().optional(),
521
561
  html_url: z.string().readonly().optional(),
@@ -2223,10 +2263,12 @@ const zGetSandboxPath = z.object({
2223
2263
  id: z.string()
2224
2264
  });
2225
2265
  const zGetSandboxResponse = zSandboxResponse;
2266
+ const zUpdateSandboxBody = zUpdateSandboxRequestWritable;
2226
2267
  const zUpdateSandboxPath = z.object({
2227
2268
  workspace_domain: z.string(),
2228
2269
  id: z.string()
2229
2270
  });
2271
+ const zUpdateSandboxResponse = zSandboxResponse;
2230
2272
  const zGetSandboxAppLogsByIdPath = z.object({
2231
2273
  workspace_domain: z.string(),
2232
2274
  sandbox_id: z.string(),
@@ -2250,6 +2292,7 @@ const zGetSandboxCommandsPath = z.object({
2250
2292
  workspace_domain: z.string(),
2251
2293
  sandbox_id: z.string()
2252
2294
  });
2295
+ const zGetSandboxCommandsResponse = zSandboxCommandsView;
2253
2296
  const zExecuteSandboxCommandBody = zExecuteSandboxCommandRequest;
2254
2297
  const zExecuteSandboxCommandPath = z.object({
2255
2298
  workspace_domain: z.string(),
@@ -2332,10 +2375,13 @@ const zGetSandboxSnapshotsPath = z.object({
2332
2375
  workspace_domain: z.string(),
2333
2376
  sandbox_id: z.string()
2334
2377
  });
2378
+ const zGetSandboxSnapshotsResponse = zSnapshotsView;
2379
+ const zAddSandboxSnapshotBody = zAddSnapshotRequest;
2335
2380
  const zAddSandboxSnapshotPath = z.object({
2336
2381
  workspace_domain: z.string(),
2337
2382
  sandbox_id: z.string()
2338
2383
  });
2384
+ const zAddSandboxSnapshotResponse = zSnapshotView;
2339
2385
  const zDeleteSandboxSnapshotPath = z.object({
2340
2386
  workspace_domain: z.string(),
2341
2387
  sandbox_id: z.string(),
@@ -2350,6 +2396,7 @@ const zGetSandboxSnapshotPath = z.object({
2350
2396
  sandbox_id: z.string(),
2351
2397
  id: z.string()
2352
2398
  });
2399
+ const zGetSandboxSnapshotResponse = zSnapshotView;
2353
2400
  const zStartSandboxPath = z.object({
2354
2401
  workspace_domain: z.string(),
2355
2402
  sandbox_id: z.string()
@@ -2370,6 +2417,7 @@ const zUpdateSandboxByYamlPath = z.object({
2370
2417
  });
2371
2418
  const zGetProjectSnapshotsPath = z.object({ workspace_domain: z.string() });
2372
2419
  const zGetProjectSnapshotsQuery = z.object({ project_name: z.string() });
2420
+ const zGetProjectSnapshotsResponse = zSnapshotsView;
2373
2421
  const zDeleteSnapshotPath = z.object({
2374
2422
  workspace_domain: z.string(),
2375
2423
  id: z.string()
@@ -2396,7 +2444,7 @@ const zUpdateIntegrationPath = z.object({
2396
2444
  //#endregion
2397
2445
  //#region package.json
2398
2446
  var name = "@buddy-works/sandbox-sdk";
2399
- var version = "0.1.6-rc.0";
2447
+ var version = "0.1.6";
2400
2448
 
2401
2449
  //#endregion
2402
2450
  //#region src/utils/environment.ts
@@ -2706,6 +2754,10 @@ var HttpClient = class {
2706
2754
  async delete(url, config) {
2707
2755
  return this.#request("DELETE", url, void 0, config);
2708
2756
  }
2757
+ /** Perform a PATCH request with optional body data */
2758
+ async patch(url, data, config) {
2759
+ return this.#request("PATCH", url, data ?? {}, config);
2760
+ }
2709
2761
  /** Set the Bearer token for authenticated requests */
2710
2762
  setAuthToken(token) {
2711
2763
  this.#authToken = token;
@@ -2777,6 +2829,9 @@ var BuddyApiClient = class extends HttpClient {
2777
2829
  case "DELETE":
2778
2830
  request = this.delete(parameterizedUrl, requestConfig);
2779
2831
  break;
2832
+ case "PATCH":
2833
+ request = this.patch(parameterizedUrl, validatedBody ?? {}, requestConfig);
2834
+ break;
2780
2835
  }
2781
2836
  const response = await request;
2782
2837
  return await this.#parseResponse(responseSchema, response);
@@ -2793,6 +2848,94 @@ var BuddyApiClient = class extends HttpClient {
2793
2848
  responseSchema: zAddSandboxResponse.transform(addSandboxResponseTransformer)
2794
2849
  });
2795
2850
  }
2851
+ /** Update an existing sandbox's configuration (timeout, apps, endpoints, etc.) */
2852
+ async updateSandbox(data) {
2853
+ return this.#requestWithValidation({
2854
+ method: "PATCH",
2855
+ data,
2856
+ url: "/workspaces/{workspace_domain}/sandboxes/{id}",
2857
+ bodySchema: zUpdateSandboxBody,
2858
+ pathSchema: zUpdateSandboxPath,
2859
+ responseSchema: zUpdateSandboxResponse.transform(updateSandboxResponseTransformer)
2860
+ });
2861
+ }
2862
+ /** List snapshots for a sandbox */
2863
+ async getSandboxSnapshots(data) {
2864
+ return this.#requestWithValidation({
2865
+ method: "GET",
2866
+ data,
2867
+ url: "/workspaces/{workspace_domain}/sandboxes/{sandbox_id}/snapshots",
2868
+ pathSchema: zGetSandboxSnapshotsPath,
2869
+ responseSchema: zGetSandboxSnapshotsResponse.transform(getSandboxSnapshotsResponseTransformer)
2870
+ });
2871
+ }
2872
+ /** List all snapshots in the project (across all sandboxes, including orphans) */
2873
+ async getProjectSnapshots(data) {
2874
+ return this.#requestWithValidation({
2875
+ method: "GET",
2876
+ data,
2877
+ url: "/workspaces/{workspace_domain}/sandboxes/snapshots",
2878
+ pathSchema: zGetProjectSnapshotsPath,
2879
+ querySchema: zGetProjectSnapshotsQuery,
2880
+ responseSchema: zGetProjectSnapshotsResponse.transform(getProjectSnapshotsResponseTransformer)
2881
+ });
2882
+ }
2883
+ /** Create a snapshot of a sandbox */
2884
+ async addSandboxSnapshot(data) {
2885
+ return this.#requestWithValidation({
2886
+ method: "POST",
2887
+ data,
2888
+ url: "/workspaces/{workspace_domain}/sandboxes/{sandbox_id}/snapshots",
2889
+ bodySchema: zAddSandboxSnapshotBody,
2890
+ pathSchema: zAddSandboxSnapshotPath,
2891
+ responseSchema: zAddSandboxSnapshotResponse.transform(addSandboxSnapshotResponseTransformer)
2892
+ });
2893
+ }
2894
+ /** Get a single sandbox snapshot by ID */
2895
+ async getSandboxSnapshot(data) {
2896
+ return this.#requestWithValidation({
2897
+ method: "GET",
2898
+ data,
2899
+ url: "/workspaces/{workspace_domain}/sandboxes/{sandbox_id}/snapshots/{id}",
2900
+ pathSchema: zGetSandboxSnapshotPath,
2901
+ responseSchema: zGetSandboxSnapshotResponse.transform(getSandboxSnapshotResponseTransformer)
2902
+ });
2903
+ }
2904
+ /** Delete a sandbox snapshot by ID */
2905
+ async deleteSandboxSnapshot(data) {
2906
+ try {
2907
+ return await this.#requestWithValidation({
2908
+ method: "DELETE",
2909
+ data,
2910
+ url: "/workspaces/{workspace_domain}/sandboxes/{sandbox_id}/snapshots/{id}",
2911
+ pathSchema: zDeleteSandboxSnapshotPath,
2912
+ responseSchema: zDeleteSandboxSnapshotResponse,
2913
+ skipRetry: true
2914
+ });
2915
+ } catch (error) {
2916
+ if (error instanceof HttpError && error.status === 404) return;
2917
+ throw error;
2918
+ }
2919
+ }
2920
+ /**
2921
+ * Delete a snapshot by ID at the project level (works for snapshots whose
2922
+ * parent sandbox has been deleted).
2923
+ */
2924
+ async deleteSnapshot(data) {
2925
+ try {
2926
+ return await this.#requestWithValidation({
2927
+ method: "DELETE",
2928
+ data,
2929
+ url: "/workspaces/{workspace_domain}/sandboxes/snapshots/{id}",
2930
+ pathSchema: zDeleteSnapshotPath,
2931
+ responseSchema: zDeleteSnapshotResponse,
2932
+ skipRetry: true
2933
+ });
2934
+ } catch (error) {
2935
+ if (error instanceof HttpError && error.status === 404) return;
2936
+ throw error;
2937
+ }
2938
+ }
2796
2939
  /** Get a specific sandbox by its ID */
2797
2940
  async getSandboxById(data) {
2798
2941
  return this.#requestWithValidation({
@@ -2825,6 +2968,16 @@ var BuddyApiClient = class extends HttpClient {
2825
2968
  responseSchema: zExecuteSandboxCommandResponse.transform(executeSandboxCommandResponseTransformer)
2826
2969
  });
2827
2970
  }
2971
+ /** List all command executions in a sandbox (history) */
2972
+ async getSandboxCommands(data) {
2973
+ return this.#requestWithValidation({
2974
+ method: "GET",
2975
+ data,
2976
+ url: "/workspaces/{workspace_domain}/sandboxes/{sandbox_id}/commands",
2977
+ pathSchema: zGetSandboxCommandsPath,
2978
+ responseSchema: zGetSandboxCommandsResponse.transform(getSandboxCommandsResponseTransformer)
2979
+ });
2980
+ }
2828
2981
  /** Get a specific command execution details */
2829
2982
  async getCommandDetails(data) {
2830
2983
  return this.#requestWithValidation({
@@ -3461,6 +3614,86 @@ var FileSystem = class FileSystem {
3461
3614
  }
3462
3615
  };
3463
3616
 
3617
+ //#endregion
3618
+ //#region src/entity/snapshot.ts
3619
+ const PRIVATE_CONSTRUCTOR_KEY$1 = Symbol("SnapshotConstructor");
3620
+ const INITIALIZE_INSTRUCTIONS$1 = "Snapshots are obtained via sandbox.createSnapshot(), sandbox.listSnapshots(), sandbox.getSnapshot(id), or Sandbox.getSnapshotById(sandboxId, snapshotId).";
3621
+ var Snapshot = class Snapshot {
3622
+ #data;
3623
+ #client;
3624
+ #sandboxId;
3625
+ /** The raw snapshot response data from the API */
3626
+ get data() {
3627
+ return this.#data;
3628
+ }
3629
+ /** The snapshot ID, throws if not present */
3630
+ get id() {
3631
+ const id = this.#data.id;
3632
+ if (!id) throw new Error(`Snapshot ID is missing. ${INITIALIZE_INSTRUCTIONS$1}`);
3633
+ return id;
3634
+ }
3635
+ /** ID of the sandbox this snapshot belongs to */
3636
+ get sandboxId() {
3637
+ return this.#sandboxId;
3638
+ }
3639
+ /**
3640
+ * Refresh the snapshot data from the API
3641
+ * Updates the internal state with the latest snapshot information
3642
+ */
3643
+ async refresh() {
3644
+ return withErrorHandler("Failed to refresh snapshot", async () => {
3645
+ this.#data = await this.#client.getSandboxSnapshot({ path: {
3646
+ sandbox_id: this.#sandboxId,
3647
+ id: this.id
3648
+ } });
3649
+ });
3650
+ }
3651
+ /**
3652
+ * Wait until the snapshot reaches CREATED state.
3653
+ *
3654
+ * `sandbox.createSnapshot()` returns immediately with `status: "CREATING"`.
3655
+ * The snapshot cannot be restored until it transitions to `"CREATED"`.
3656
+ *
3657
+ * @param pollIntervalMs - How often to check the status (default: 2000ms (2s))
3658
+ * @param maxWaitMs - Maximum time to wait before timing out (default: 180000ms (180s))
3659
+ */
3660
+ async waitUntilReady(pollIntervalMs = 2e3, maxWaitMs = 18e4) {
3661
+ return withErrorHandler("Snapshot not ready", async () => {
3662
+ const startTime = Date.now();
3663
+ while (true) {
3664
+ await this.refresh();
3665
+ if (this.#data.status === "CREATED") return;
3666
+ if (this.#data.status === "FAILED") throw new Error(`Snapshot ${this.id} failed. Status: ${this.#data.status}`);
3667
+ if (Date.now() - startTime > maxWaitMs) throw new Error(`Timeout waiting for snapshot ${this.id} to be CREATED. Current: ${this.#data.status}`);
3668
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
3669
+ }
3670
+ });
3671
+ }
3672
+ /**
3673
+ * Delete this snapshot permanently
3674
+ */
3675
+ async delete() {
3676
+ return withErrorHandler("Failed to delete snapshot", async () => {
3677
+ await this.#client.deleteSandboxSnapshot({ path: {
3678
+ sandbox_id: this.#sandboxId,
3679
+ id: this.id
3680
+ } });
3681
+ });
3682
+ }
3683
+ constructor(data, client, sandboxId, constructorKey) {
3684
+ if (constructorKey !== PRIVATE_CONSTRUCTOR_KEY$1) throw new Error(`Cannot construct Snapshot directly. ${INITIALIZE_INSTRUCTIONS$1}`);
3685
+ this.#data = data;
3686
+ this.#client = client;
3687
+ this.#sandboxId = sandboxId;
3688
+ }
3689
+ /**
3690
+ * @internal Factory used by the Sandbox class to construct Snapshot instances.
3691
+ */
3692
+ static _build(data, client, sandboxId) {
3693
+ return new Snapshot(data, client, sandboxId, PRIVATE_CONSTRUCTOR_KEY$1);
3694
+ }
3695
+ };
3696
+
3464
3697
  //#endregion
3465
3698
  //#region src/entity/sandbox.ts
3466
3699
  const PRIVATE_CONSTRUCTOR_KEY = Symbol("SandboxConstructor");
@@ -3489,6 +3722,19 @@ var Sandbox = class Sandbox {
3489
3722
  return this.#fs;
3490
3723
  }
3491
3724
  /**
3725
+ * Create a Sandbox instance from an `addSandbox` response and wait for it
3726
+ * to reach a RUNNING state. Shared between `create` and `createFromSnapshot`.
3727
+ */
3728
+ static async #finalizeNewSandbox(sandboxResponse, client) {
3729
+ const sandbox = new Sandbox(sandboxResponse, client, PRIVATE_CONSTRUCTOR_KEY);
3730
+ logger_default.debug(`Waiting for sandbox ${sandbox.data.id} to be ready...`);
3731
+ await sandbox.waitUntilReady();
3732
+ logger_default.debug(`Sandbox ${sandbox.data.id} is ready (Setup status: ${sandbox.data.setup_status})`);
3733
+ await sandbox.waitUntilRunning();
3734
+ logger_default.debug(`Sandbox ${sandbox.data.id} is now running. Status: ${sandbox.data.status}`);
3735
+ return sandbox;
3736
+ }
3737
+ /**
3492
3738
  * Create a new sandbox
3493
3739
  * @param config - Sandbox configuration including identifier, name, os, and optional connection settings
3494
3740
  * @returns A ready-to-use Sandbox instance
@@ -3499,17 +3745,57 @@ var Sandbox = class Sandbox {
3499
3745
  const client = createClient(connection);
3500
3746
  const requestBody = {
3501
3747
  name: `Sandbox ${(/* @__PURE__ */ new Date()).toISOString()}`,
3502
- identifier: config?.identifier || `sandbox_${String(Date.now())}`,
3748
+ identifier: `sandbox_${String(Date.now())}`,
3503
3749
  os: "ubuntu:24.04",
3504
3750
  ...sandboxConfig
3505
3751
  };
3506
- const sandbox = new Sandbox(await client.addSandbox({ body: requestBody }), client, PRIVATE_CONSTRUCTOR_KEY);
3507
- logger_default.debug(`Waiting for sandbox ${sandbox.data.id} to be ready...`);
3508
- await sandbox.waitUntilReady();
3509
- logger_default.debug(`Sandbox ${sandbox.data.id} is ready (Setup status: ${sandbox.data.setup_status})`);
3510
- await sandbox.waitUntilRunning();
3511
- logger_default.debug(`Sandbox ${sandbox.data.id} is now running. Status: ${sandbox.data.status}`);
3512
- return sandbox;
3752
+ const sandboxResponse = await client.addSandbox({ body: requestBody });
3753
+ return Sandbox.#finalizeNewSandbox(sandboxResponse, client);
3754
+ });
3755
+ }
3756
+ /**
3757
+ * Create a new sandbox from an existing snapshot
3758
+ *
3759
+ * @param snapshotId - ID of the snapshot to create from
3760
+ * @param config - Optional sandbox configuration overrides (name, identifier, …)
3761
+ * @returns A ready-to-use Sandbox instance restored from the snapshot
3762
+ */
3763
+ static async createFromSnapshot(snapshotId, config) {
3764
+ return withErrorHandler("Failed to create sandbox from snapshot", async () => {
3765
+ const { connection, ...snapshotConfig } = config ?? {};
3766
+ const client = createClient(connection);
3767
+ const requestBody = {
3768
+ snapshot_id: snapshotId,
3769
+ name: `Sandbox ${(/* @__PURE__ */ new Date()).toISOString()}`,
3770
+ identifier: `sandbox_${String(Date.now())}`,
3771
+ ...snapshotConfig
3772
+ };
3773
+ const sandboxResponse = await client.addSandbox({ body: requestBody });
3774
+ return Sandbox.#finalizeNewSandbox(sandboxResponse, client);
3775
+ });
3776
+ }
3777
+ /**
3778
+ * Clone an existing sandbox by ID into a new one.
3779
+ *
3780
+ * Differs from `createFromSnapshot` in that it copies the current state of
3781
+ * a live sandbox directly, without going through a snapshot.
3782
+ *
3783
+ * @param sourceSandboxId - ID of the sandbox to clone
3784
+ * @param config - Optional sandbox configuration overrides (name, identifier)
3785
+ * @returns A ready-to-use Sandbox instance cloned from the source
3786
+ */
3787
+ static async clone(sourceSandboxId, config) {
3788
+ return withErrorHandler("Failed to clone sandbox", async () => {
3789
+ const { connection, ...cloneConfig } = config ?? {};
3790
+ const client = createClient(connection);
3791
+ const requestBody = {
3792
+ source_sandbox_id: sourceSandboxId,
3793
+ name: `Sandbox ${(/* @__PURE__ */ new Date()).toISOString()}`,
3794
+ identifier: `sandbox_${String(Date.now())}`,
3795
+ ...cloneConfig
3796
+ };
3797
+ const sandboxResponse = await client.addSandbox({ body: requestBody });
3798
+ return Sandbox.#finalizeNewSandbox(sandboxResponse, client);
3513
3799
  });
3514
3800
  }
3515
3801
  /**
@@ -3528,10 +3814,9 @@ var Sandbox = class Sandbox {
3528
3814
  project: client.project_name,
3529
3815
  sandbox: identifier
3530
3816
  } })).sandbox_id;
3531
- } catch {}
3532
- if (!sandboxId) try {
3533
- sandboxId = (await client.getSandboxes({}))?.sandboxes?.find((s) => s.identifier === identifier)?.id;
3534
- } catch {}
3817
+ } catch (error) {
3818
+ if (!(error instanceof HttpError) || error.status !== 404) throw error;
3819
+ }
3535
3820
  if (!sandboxId) throw new Error(`Sandbox with identifier '${identifier}' not found`);
3536
3821
  const sandboxResponse = await client.getSandboxById({ path: { id: sandboxId } });
3537
3822
  if (!sandboxResponse) throw new Error(`Sandbox with ID '${sandboxId}' not found`);
@@ -3570,6 +3855,26 @@ var Sandbox = class Sandbox {
3570
3855
  });
3571
3856
  }
3572
3857
  /**
3858
+ * Get a single snapshot by its sandbox + snapshot ID without first fetching
3859
+ * the parent sandbox. Useful when you already have both IDs (e.g. from a
3860
+ * persisted reference).
3861
+ *
3862
+ * @param sandboxId - ID of the sandbox the snapshot belongs to
3863
+ * @param snapshotId - ID of the snapshot to fetch
3864
+ * @param config - Optional configuration including connection settings
3865
+ */
3866
+ static async getSnapshotById(sandboxId, snapshotId, config) {
3867
+ return withErrorHandler("Failed to get snapshot", async () => {
3868
+ const { connection } = config ?? {};
3869
+ const client = createClient(connection);
3870
+ const data = await client.getSandboxSnapshot({ path: {
3871
+ sandbox_id: sandboxId,
3872
+ id: snapshotId
3873
+ } });
3874
+ return Snapshot._build(data, client, sandboxId);
3875
+ });
3876
+ }
3877
+ /**
3573
3878
  * Execute a command in the sandbox
3574
3879
  * @returns Command instance (call wait() for blocking execution)
3575
3880
  */
@@ -3601,6 +3906,138 @@ var Sandbox = class Sandbox {
3601
3906
  });
3602
3907
  }
3603
3908
  /**
3909
+ * List all command executions in this sandbox (history).
3910
+ *
3911
+ * Returns one `Command` instance per past execution. Useful for inspecting
3912
+ * previous runs, fetching their logs, or killing still-running detached
3913
+ * commands.
3914
+ */
3915
+ async listCommands() {
3916
+ const sandboxId = this.initializedId;
3917
+ return withErrorHandler("Failed to list commands", async () => {
3918
+ return ((await this.#client.getSandboxCommands({ path: { sandbox_id: sandboxId } }))?.commands ?? []).map((commandResponse) => new Command({
3919
+ commandResponse,
3920
+ client: this.#client,
3921
+ sandboxId
3922
+ }));
3923
+ });
3924
+ }
3925
+ /**
3926
+ * Update the sandbox configuration in place
3927
+ *
3928
+ * Accepts a partial update of any mutable sandbox field (`timeout`, `apps`,
3929
+ * `endpoints`, `variables`, `tags`, `resources`, …). Updates the internal
3930
+ * state with the API's response.
3931
+ *
3932
+ * @remarks Changing `first_boot_commands` leaves the sandbox in
3933
+ * `setup_status: STALE` — the new commands only apply on first boot, so
3934
+ * the sandbox must be recreated to take effect.
3935
+ */
3936
+ async update(config) {
3937
+ const sandboxId = this.initializedId;
3938
+ return withErrorHandler("Failed to update sandbox", async () => {
3939
+ this.#sandboxData = await this.#client.updateSandbox({
3940
+ body: config,
3941
+ path: { id: sandboxId }
3942
+ });
3943
+ });
3944
+ }
3945
+ /**
3946
+ * Create a snapshot of the current sandbox
3947
+ *
3948
+ * Returns immediately with a snapshot whose `status` is `"CREATING"`. Call
3949
+ * `snapshot.waitUntilReady()` (or `sandbox.waitForSnapshotReady(snapshot.id)`)
3950
+ * before restoring from it.
3951
+ *
3952
+ * @param config - Optional snapshot configuration (name, description, …)
3953
+ */
3954
+ async createSnapshot(config) {
3955
+ const sandboxId = this.initializedId;
3956
+ return withErrorHandler("Failed to create snapshot", async () => {
3957
+ const data = await this.#client.addSandboxSnapshot({
3958
+ body: config ?? {},
3959
+ path: { sandbox_id: sandboxId }
3960
+ });
3961
+ return Snapshot._build(data, this.#client, sandboxId);
3962
+ });
3963
+ }
3964
+ /**
3965
+ * List all snapshots in the project across every sandbox, including
3966
+ * snapshots whose parent sandbox has been deleted.
3967
+ *
3968
+ * Use `Sandbox.createFromSnapshot(snapshot.id, …)` to provision a new
3969
+ * sandbox from any item in the list.
3970
+ *
3971
+ * @param config - Optional configuration including connection settings
3972
+ */
3973
+ static async listSnapshots(config) {
3974
+ return withErrorHandler("Failed to list project snapshots", async () => {
3975
+ const { connection } = config ?? {};
3976
+ return (await createClient(connection).getProjectSnapshots({}))?.snapshots ?? [];
3977
+ });
3978
+ }
3979
+ /**
3980
+ * List snapshots of this sandbox
3981
+ */
3982
+ async listSnapshots() {
3983
+ const sandboxId = this.initializedId;
3984
+ return withErrorHandler("Failed to list snapshots", async () => {
3985
+ return ((await this.#client.getSandboxSnapshots({ path: { sandbox_id: sandboxId } }))?.snapshots ?? []).map((data) => Snapshot._build(data, this.#client, sandboxId));
3986
+ });
3987
+ }
3988
+ /**
3989
+ * Get a single snapshot of this sandbox by ID
3990
+ */
3991
+ async getSnapshot(snapshotId) {
3992
+ const sandboxId = this.initializedId;
3993
+ return withErrorHandler("Failed to get snapshot", async () => {
3994
+ const data = await this.#client.getSandboxSnapshot({ path: {
3995
+ sandbox_id: sandboxId,
3996
+ id: snapshotId
3997
+ } });
3998
+ return Snapshot._build(data, this.#client, sandboxId);
3999
+ });
4000
+ }
4001
+ /**
4002
+ * Delete a snapshot by ID without needing the parent sandbox. Useful for
4003
+ * cleaning up snapshots whose parent sandbox has already been deleted.
4004
+ *
4005
+ * @param snapshotId - ID of the snapshot to delete
4006
+ * @param config - Optional configuration including connection settings
4007
+ */
4008
+ static async deleteSnapshot(snapshotId, config) {
4009
+ return withErrorHandler("Failed to delete snapshot", async () => {
4010
+ const { connection } = config ?? {};
4011
+ await createClient(connection).deleteSnapshot({ path: { id: snapshotId } });
4012
+ });
4013
+ }
4014
+ /**
4015
+ * Delete a snapshot of this sandbox by ID
4016
+ */
4017
+ async deleteSnapshot(snapshotId) {
4018
+ const sandboxId = this.initializedId;
4019
+ return withErrorHandler("Failed to delete snapshot", async () => {
4020
+ await this.#client.deleteSandboxSnapshot({ path: {
4021
+ sandbox_id: sandboxId,
4022
+ id: snapshotId
4023
+ } });
4024
+ });
4025
+ }
4026
+ /**
4027
+ * Wait until a snapshot reaches CREATED state.
4028
+ *
4029
+ * `createSnapshot()` returns immediately with `status: "CREATING"`. The
4030
+ * snapshot cannot be used as input to `Sandbox.createFromSnapshot()` until
4031
+ * it transitions to `"CREATED"`. Use this method to block until it does.
4032
+ *
4033
+ * @param snapshotId - ID of the snapshot to wait for
4034
+ * @param pollIntervalMs - How often to check the status (default: 2000ms (2s))
4035
+ * @param maxWaitMs - Maximum time to wait before timing out (default: 180000ms (180s))
4036
+ */
4037
+ async waitForSnapshotReady(snapshotId, pollIntervalMs = 2e3, maxWaitMs = 18e4) {
4038
+ await (await this.getSnapshot(snapshotId)).waitUntilReady(pollIntervalMs, maxWaitMs);
4039
+ }
4040
+ /**
3604
4041
  * Delete the sandbox permanently
3605
4042
  */
3606
4043
  async destroy() {
@@ -3786,4 +4223,4 @@ var Sandbox = class Sandbox {
3786
4223
  };
3787
4224
 
3788
4225
  //#endregion
3789
- export { API_URLS, BuddyApiClient, BuddySDKError, Command, ERROR_CODES, FileSystem, REGIONS, Sandbox };
4226
+ export { API_URLS, BuddyApiClient, BuddySDKError, Command, ERROR_CODES, FileSystem, REGIONS, Sandbox, Snapshot };