@daytona/sdk 0.196.0 → 0.198.0-alpha.1

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.
Files changed (44) hide show
  1. package/cjs/Daytona.d.ts +27 -2
  2. package/cjs/Daytona.js +64 -7
  3. package/cjs/Daytona.js.map +1 -1
  4. package/cjs/Process.js +52 -12
  5. package/cjs/Process.js.map +1 -1
  6. package/cjs/PtyHandle.d.ts +14 -0
  7. package/cjs/PtyHandle.js +100 -34
  8. package/cjs/PtyHandle.js.map +1 -1
  9. package/cjs/Sandbox.d.ts +149 -4
  10. package/cjs/Sandbox.js +523 -136
  11. package/cjs/Sandbox.js.map +1 -1
  12. package/cjs/index.d.ts +1 -1
  13. package/cjs/utils/EventDispatcher.d.ts +85 -0
  14. package/cjs/utils/EventDispatcher.js +398 -0
  15. package/cjs/utils/EventDispatcher.js.map +1 -0
  16. package/cjs/utils/EventSubscriptionManager.d.ts +14 -0
  17. package/cjs/utils/EventSubscriptionManager.js +98 -0
  18. package/cjs/utils/EventSubscriptionManager.js.map +1 -0
  19. package/cjs/utils/WebSocket.d.ts +2 -1
  20. package/cjs/utils/WebSocket.js +10 -3
  21. package/cjs/utils/WebSocket.js.map +1 -1
  22. package/esm/Daytona.d.ts +27 -2
  23. package/esm/Daytona.js +64 -7
  24. package/esm/Daytona.js.map +1 -1
  25. package/esm/Process.js +52 -12
  26. package/esm/Process.js.map +1 -1
  27. package/esm/PtyHandle.d.ts +14 -0
  28. package/esm/PtyHandle.js +100 -34
  29. package/esm/PtyHandle.js.map +1 -1
  30. package/esm/Sandbox.d.ts +149 -4
  31. package/esm/Sandbox.js +524 -137
  32. package/esm/Sandbox.js.map +1 -1
  33. package/esm/index.d.ts +1 -1
  34. package/esm/utils/EventDispatcher.d.ts +85 -0
  35. package/esm/utils/EventDispatcher.js +394 -0
  36. package/esm/utils/EventDispatcher.js.map +1 -0
  37. package/esm/utils/EventSubscriptionManager.d.ts +14 -0
  38. package/esm/utils/EventSubscriptionManager.js +94 -0
  39. package/esm/utils/EventSubscriptionManager.js.map +1 -0
  40. package/esm/utils/Import.js +1 -1
  41. package/esm/utils/WebSocket.d.ts +2 -1
  42. package/esm/utils/WebSocket.js +10 -3
  43. package/esm/utils/WebSocket.js.map +1 -1
  44. package/package.json +5 -3
package/cjs/Sandbox.js CHANGED
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.Sandbox = void 0;
8
8
  const tslib_1 = require("tslib");
9
9
  const api_client_1 = require("@daytona/api-client");
10
+ const analytics_api_client_1 = require("@daytona/analytics-api-client");
10
11
  const Daytona_1 = require("./Daytona");
11
12
  const toolbox_api_client_1 = require("@daytona/toolbox-api-client");
12
13
  const FileSystem_1 = require("./FileSystem");
@@ -18,6 +19,13 @@ const Daytona_2 = require("./Daytona");
18
19
  const ComputerUse_1 = require("./ComputerUse");
19
20
  const CodeInterpreter_1 = require("./CodeInterpreter");
20
21
  const otel_decorator_1 = require("./utils/otel.decorator");
22
+ function withEvents(target, _context) {
23
+ return function (...args) {
24
+ ;
25
+ this.ensureSubscribed();
26
+ return target.apply(this, args);
27
+ };
28
+ }
21
29
  /**
22
30
  * Represents a Daytona Sandbox.
23
31
  *
@@ -47,8 +55,10 @@ const otel_decorator_1 = require("./utils/otel.decorator");
47
55
  * @property {string} [backupCreatedAt] - When the backup was created (not returned by list results;
48
56
  * call `refreshData()` on each item to populate)
49
57
  * @property {number} [autoStopInterval] - Auto-stop interval in minutes
58
+ * @property {number} [autoPauseInterval] - Auto-pause interval in minutes
50
59
  * @property {number} [autoArchiveInterval] - Auto-archive interval in minutes
51
60
  * @property {number} [autoDeleteInterval] - Auto-delete interval in minutes
61
+ * @property {string} [autoDestroyAt] - When the Sandbox will be automatically destroyed (only set when a TTL is configured)
52
62
  * @property {Array<SandboxVolume>} [volumes] - Volumes attached to the Sandbox (not returned by
53
63
  * list results; call `refreshData()` on each item to populate)
54
64
  * @property {BuildInfo} [buildInfo] - Build information for the Sandbox if it was created from dynamic build
@@ -70,11 +80,14 @@ const otel_decorator_1 = require("./utils/otel.decorator");
70
80
  let Sandbox = (() => {
71
81
  let _instanceExtraInitializers = [];
72
82
  let _getUserHomeDir_decorators;
83
+ let _getMetricsLatest_decorators;
84
+ let _getMetrics_decorators;
73
85
  let _getUserRootDir_decorators;
74
86
  let _getWorkDir_decorators;
75
87
  let _createLspServer_decorators;
76
88
  let _setLabels_decorators;
77
89
  let _start_decorators;
90
+ let _recover_decorators;
78
91
  let _stop_decorators;
79
92
  let __experimental_fork_decorators;
80
93
  let __experimental_createSnapshot_decorators;
@@ -83,13 +96,18 @@ let Sandbox = (() => {
83
96
  let _waitUntilStarted_decorators;
84
97
  let _waitUntilStopped_decorators;
85
98
  let _refreshData_decorators;
99
+ let _refreshActivity_decorators;
86
100
  let _setAutostopInterval_decorators;
101
+ let _setAutoPauseInterval_decorators;
102
+ let _setTtl_decorators;
87
103
  let _setAutoArchiveInterval_decorators;
88
104
  let _setAutoDeleteInterval_decorators;
89
105
  let _updateNetworkSettings_decorators;
90
106
  let _updateSecrets_decorators;
91
107
  let _updateEnv_decorators;
92
108
  let _getPreviewLink_decorators;
109
+ let _getSignedPreviewUrl_decorators;
110
+ let _expireSignedPreviewUrl_decorators;
93
111
  let _archive_decorators;
94
112
  let _resize_decorators;
95
113
  let _waitForResizeComplete_decorators;
@@ -99,39 +117,50 @@ let Sandbox = (() => {
99
117
  return class Sandbox {
100
118
  static {
101
119
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
102
- _getUserHomeDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
103
- _getUserRootDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
104
- _getWorkDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
105
- _createLspServer_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
106
- _setLabels_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
107
- _start_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
108
- _stop_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
109
- __experimental_fork_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
110
- __experimental_createSnapshot_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
111
- _pause_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
112
- _delete_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
113
- _waitUntilStarted_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
114
- _waitUntilStopped_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
115
- _refreshData_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
116
- _setAutostopInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
117
- _setAutoArchiveInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
118
- _setAutoDeleteInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
120
+ _getUserHomeDir_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
121
+ _getMetricsLatest_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
122
+ _getMetrics_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
123
+ _getUserRootDir_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
124
+ _getWorkDir_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
125
+ _createLspServer_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
126
+ _setLabels_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
127
+ _start_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
128
+ _recover_decorators = [withEvents];
129
+ _stop_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
130
+ __experimental_fork_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
131
+ __experimental_createSnapshot_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
132
+ _pause_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
133
+ _delete_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
134
+ _waitUntilStarted_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
135
+ _waitUntilStopped_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
136
+ _refreshData_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
137
+ _refreshActivity_decorators = [withEvents];
138
+ _setAutostopInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
139
+ _setAutoPauseInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
140
+ _setTtl_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
141
+ _setAutoArchiveInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
142
+ _setAutoDeleteInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
119
143
  _updateNetworkSettings_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
120
144
  _updateSecrets_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
121
145
  _updateEnv_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
122
- _getPreviewLink_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
123
- _archive_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
124
- _resize_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
125
- _waitForResizeComplete_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
126
- _createSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
127
- _revokeSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
128
- _validateSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
146
+ _getPreviewLink_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
147
+ _getSignedPreviewUrl_decorators = [withEvents];
148
+ _expireSignedPreviewUrl_decorators = [withEvents];
149
+ _archive_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
150
+ _resize_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
151
+ _waitForResizeComplete_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
152
+ _createSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
153
+ _revokeSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
154
+ _validateSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
129
155
  tslib_1.__esDecorate(this, null, _getUserHomeDir_decorators, { kind: "method", name: "getUserHomeDir", static: false, private: false, access: { has: obj => "getUserHomeDir" in obj, get: obj => obj.getUserHomeDir }, metadata: _metadata }, null, _instanceExtraInitializers);
156
+ tslib_1.__esDecorate(this, null, _getMetricsLatest_decorators, { kind: "method", name: "getMetricsLatest", static: false, private: false, access: { has: obj => "getMetricsLatest" in obj, get: obj => obj.getMetricsLatest }, metadata: _metadata }, null, _instanceExtraInitializers);
157
+ tslib_1.__esDecorate(this, null, _getMetrics_decorators, { kind: "method", name: "getMetrics", static: false, private: false, access: { has: obj => "getMetrics" in obj, get: obj => obj.getMetrics }, metadata: _metadata }, null, _instanceExtraInitializers);
130
158
  tslib_1.__esDecorate(this, null, _getUserRootDir_decorators, { kind: "method", name: "getUserRootDir", static: false, private: false, access: { has: obj => "getUserRootDir" in obj, get: obj => obj.getUserRootDir }, metadata: _metadata }, null, _instanceExtraInitializers);
131
159
  tslib_1.__esDecorate(this, null, _getWorkDir_decorators, { kind: "method", name: "getWorkDir", static: false, private: false, access: { has: obj => "getWorkDir" in obj, get: obj => obj.getWorkDir }, metadata: _metadata }, null, _instanceExtraInitializers);
132
160
  tslib_1.__esDecorate(this, null, _createLspServer_decorators, { kind: "method", name: "createLspServer", static: false, private: false, access: { has: obj => "createLspServer" in obj, get: obj => obj.createLspServer }, metadata: _metadata }, null, _instanceExtraInitializers);
133
161
  tslib_1.__esDecorate(this, null, _setLabels_decorators, { kind: "method", name: "setLabels", static: false, private: false, access: { has: obj => "setLabels" in obj, get: obj => obj.setLabels }, metadata: _metadata }, null, _instanceExtraInitializers);
134
162
  tslib_1.__esDecorate(this, null, _start_decorators, { kind: "method", name: "start", static: false, private: false, access: { has: obj => "start" in obj, get: obj => obj.start }, metadata: _metadata }, null, _instanceExtraInitializers);
163
+ tslib_1.__esDecorate(this, null, _recover_decorators, { kind: "method", name: "recover", static: false, private: false, access: { has: obj => "recover" in obj, get: obj => obj.recover }, metadata: _metadata }, null, _instanceExtraInitializers);
135
164
  tslib_1.__esDecorate(this, null, _stop_decorators, { kind: "method", name: "stop", static: false, private: false, access: { has: obj => "stop" in obj, get: obj => obj.stop }, metadata: _metadata }, null, _instanceExtraInitializers);
136
165
  tslib_1.__esDecorate(this, null, __experimental_fork_decorators, { kind: "method", name: "_experimental_fork", static: false, private: false, access: { has: obj => "_experimental_fork" in obj, get: obj => obj._experimental_fork }, metadata: _metadata }, null, _instanceExtraInitializers);
137
166
  tslib_1.__esDecorate(this, null, __experimental_createSnapshot_decorators, { kind: "method", name: "_experimental_createSnapshot", static: false, private: false, access: { has: obj => "_experimental_createSnapshot" in obj, get: obj => obj._experimental_createSnapshot }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -140,13 +169,18 @@ let Sandbox = (() => {
140
169
  tslib_1.__esDecorate(this, null, _waitUntilStarted_decorators, { kind: "method", name: "waitUntilStarted", static: false, private: false, access: { has: obj => "waitUntilStarted" in obj, get: obj => obj.waitUntilStarted }, metadata: _metadata }, null, _instanceExtraInitializers);
141
170
  tslib_1.__esDecorate(this, null, _waitUntilStopped_decorators, { kind: "method", name: "waitUntilStopped", static: false, private: false, access: { has: obj => "waitUntilStopped" in obj, get: obj => obj.waitUntilStopped }, metadata: _metadata }, null, _instanceExtraInitializers);
142
171
  tslib_1.__esDecorate(this, null, _refreshData_decorators, { kind: "method", name: "refreshData", static: false, private: false, access: { has: obj => "refreshData" in obj, get: obj => obj.refreshData }, metadata: _metadata }, null, _instanceExtraInitializers);
172
+ tslib_1.__esDecorate(this, null, _refreshActivity_decorators, { kind: "method", name: "refreshActivity", static: false, private: false, access: { has: obj => "refreshActivity" in obj, get: obj => obj.refreshActivity }, metadata: _metadata }, null, _instanceExtraInitializers);
143
173
  tslib_1.__esDecorate(this, null, _setAutostopInterval_decorators, { kind: "method", name: "setAutostopInterval", static: false, private: false, access: { has: obj => "setAutostopInterval" in obj, get: obj => obj.setAutostopInterval }, metadata: _metadata }, null, _instanceExtraInitializers);
174
+ tslib_1.__esDecorate(this, null, _setAutoPauseInterval_decorators, { kind: "method", name: "setAutoPauseInterval", static: false, private: false, access: { has: obj => "setAutoPauseInterval" in obj, get: obj => obj.setAutoPauseInterval }, metadata: _metadata }, null, _instanceExtraInitializers);
175
+ tslib_1.__esDecorate(this, null, _setTtl_decorators, { kind: "method", name: "setTtl", static: false, private: false, access: { has: obj => "setTtl" in obj, get: obj => obj.setTtl }, metadata: _metadata }, null, _instanceExtraInitializers);
144
176
  tslib_1.__esDecorate(this, null, _setAutoArchiveInterval_decorators, { kind: "method", name: "setAutoArchiveInterval", static: false, private: false, access: { has: obj => "setAutoArchiveInterval" in obj, get: obj => obj.setAutoArchiveInterval }, metadata: _metadata }, null, _instanceExtraInitializers);
145
177
  tslib_1.__esDecorate(this, null, _setAutoDeleteInterval_decorators, { kind: "method", name: "setAutoDeleteInterval", static: false, private: false, access: { has: obj => "setAutoDeleteInterval" in obj, get: obj => obj.setAutoDeleteInterval }, metadata: _metadata }, null, _instanceExtraInitializers);
146
178
  tslib_1.__esDecorate(this, null, _updateNetworkSettings_decorators, { kind: "method", name: "updateNetworkSettings", static: false, private: false, access: { has: obj => "updateNetworkSettings" in obj, get: obj => obj.updateNetworkSettings }, metadata: _metadata }, null, _instanceExtraInitializers);
147
179
  tslib_1.__esDecorate(this, null, _updateSecrets_decorators, { kind: "method", name: "updateSecrets", static: false, private: false, access: { has: obj => "updateSecrets" in obj, get: obj => obj.updateSecrets }, metadata: _metadata }, null, _instanceExtraInitializers);
148
180
  tslib_1.__esDecorate(this, null, _updateEnv_decorators, { kind: "method", name: "updateEnv", static: false, private: false, access: { has: obj => "updateEnv" in obj, get: obj => obj.updateEnv }, metadata: _metadata }, null, _instanceExtraInitializers);
149
181
  tslib_1.__esDecorate(this, null, _getPreviewLink_decorators, { kind: "method", name: "getPreviewLink", static: false, private: false, access: { has: obj => "getPreviewLink" in obj, get: obj => obj.getPreviewLink }, metadata: _metadata }, null, _instanceExtraInitializers);
182
+ tslib_1.__esDecorate(this, null, _getSignedPreviewUrl_decorators, { kind: "method", name: "getSignedPreviewUrl", static: false, private: false, access: { has: obj => "getSignedPreviewUrl" in obj, get: obj => obj.getSignedPreviewUrl }, metadata: _metadata }, null, _instanceExtraInitializers);
183
+ tslib_1.__esDecorate(this, null, _expireSignedPreviewUrl_decorators, { kind: "method", name: "expireSignedPreviewUrl", static: false, private: false, access: { has: obj => "expireSignedPreviewUrl" in obj, get: obj => obj.expireSignedPreviewUrl }, metadata: _metadata }, null, _instanceExtraInitializers);
150
184
  tslib_1.__esDecorate(this, null, _archive_decorators, { kind: "method", name: "archive", static: false, private: false, access: { has: obj => "archive" in obj, get: obj => obj.archive }, metadata: _metadata }, null, _instanceExtraInitializers);
151
185
  tslib_1.__esDecorate(this, null, _resize_decorators, { kind: "method", name: "resize", static: false, private: false, access: { has: obj => "resize" in obj, get: obj => obj.resize }, metadata: _metadata }, null, _instanceExtraInitializers);
152
186
  tslib_1.__esDecorate(this, null, _waitForResizeComplete_decorators, { kind: "method", name: "waitForResizeComplete", static: false, private: false, access: { has: obj => "waitForResizeComplete" in obj, get: obj => obj.waitForResizeComplete }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -158,6 +192,8 @@ let Sandbox = (() => {
158
192
  clientConfig = tslib_1.__runInitializers(this, _instanceExtraInitializers);
159
193
  axiosInstance;
160
194
  sandboxApi;
195
+ getAnalyticsApiUrl;
196
+ subscriptionManager;
161
197
  fs;
162
198
  git;
163
199
  process;
@@ -182,8 +218,10 @@ let Sandbox = (() => {
182
218
  backupState;
183
219
  backupCreatedAt;
184
220
  autoStopInterval;
221
+ autoPauseInterval;
185
222
  autoArchiveInterval;
186
223
  autoDeleteInterval;
224
+ autoDestroyAt;
187
225
  volumes;
188
226
  buildInfo;
189
227
  createdAt;
@@ -196,24 +234,28 @@ let Sandbox = (() => {
196
234
  toolboxProxyUrl;
197
235
  infoApi;
198
236
  serverApi;
237
+ systemApi;
238
+ stateWaiters = [];
239
+ stateWaiterErrorMessageFns = new WeakMap();
240
+ subId;
199
241
  /**
200
- * Creates a new Sandbox instance
242
+ * Creates a new Sandbox instance.
243
+ *
244
+ * Internal: obtain sandboxes via {@link Daytona.create}, {@link Daytona.get}, or
245
+ * {@link Daytona.list} rather than constructing directly.
201
246
  *
202
247
  * @param {SandboxDto} sandboxDto - The API Sandbox instance
248
+ * @param {SandboxApi} sandboxApi - API client for Sandbox operations
249
+ * @param {InfoApi} infoApi - API client for info operations
250
+ * @param {EventSubscriptionManager} subscriptionManager - Event subscription manager for real-time updates
203
251
  */
204
- constructor(sandboxDto, clientConfig, axiosInstance, sandboxApi) {
252
+ constructor(sandboxDto, clientConfig, axiosInstance, sandboxApi, getAnalyticsApiUrl, subscriptionManager) {
205
253
  this.clientConfig = clientConfig;
206
254
  this.axiosInstance = axiosInstance;
207
255
  this.sandboxApi = sandboxApi;
256
+ this.getAnalyticsApiUrl = getAnalyticsApiUrl;
257
+ this.subscriptionManager = subscriptionManager;
208
258
  this.processSandboxDto(sandboxDto);
209
- // Set the toolbox base URL
210
- let baseUrl = this.toolboxProxyUrl;
211
- if (!baseUrl.endsWith('/')) {
212
- baseUrl += '/';
213
- }
214
- this.axiosInstance.defaults.baseURL = baseUrl + this.id;
215
- this.clientConfig.basePath = this.axiosInstance.defaults.baseURL;
216
- // Initialize Services
217
259
  const getPreviewToken = async () => (await this.getPreviewLink(1)).token;
218
260
  this.fs = new FileSystem_1.FileSystem(this.clientConfig, new toolbox_api_client_1.FileSystemApi(this.clientConfig, '', this.axiosInstance));
219
261
  this.git = new Git_1.Git(new toolbox_api_client_1.GitApi(this.clientConfig, '', this.axiosInstance));
@@ -223,6 +265,8 @@ let Sandbox = (() => {
223
265
  this.computerUse = new ComputerUse_1.ComputerUse(new toolbox_api_client_1.ComputerUseApi(this.clientConfig, '', this.axiosInstance));
224
266
  this.infoApi = new toolbox_api_client_1.InfoApi(this.clientConfig, '', this.axiosInstance);
225
267
  this.serverApi = new toolbox_api_client_1.ServerApi(this.clientConfig, '', this.axiosInstance);
268
+ this.systemApi = new toolbox_api_client_1.SystemApi(this.clientConfig, '', this.axiosInstance);
269
+ this.subscribeToEvents();
226
270
  }
227
271
  /**
228
272
  * Gets the user's home directory path for the logged in user inside the Sandbox.
@@ -237,6 +281,53 @@ let Sandbox = (() => {
237
281
  const response = await this.infoApi.getUserHomeDir();
238
282
  return response.data.dir;
239
283
  }
284
+ /**
285
+ * Gets the most recent resource usage sample directly from the sandbox daemon.
286
+ *
287
+ * Unlike {@link getMetrics}, which returns aggregated historical samples, this returns the
288
+ * single current reading without going through the telemetry backend.
289
+ *
290
+ * @returns The current resource usage sample for the sandbox.
291
+ *
292
+ * @example
293
+ * const m = await sandbox.getMetricsLatest()
294
+ * console.log(`CPU: ${m.cpuUsedPct}%, mem: ${m.memUsed}/${m.memTotal}`)
295
+ */
296
+ async getMetricsLatest() {
297
+ const response = await this.systemApi.getSystemMetrics();
298
+ return sandboxMetricsFromSystemMetrics(response.data);
299
+ }
300
+ /**
301
+ * Gets historical time-series resource usage metrics for the Sandbox.
302
+ *
303
+ * @param {Date} [start] - Start of the time range. Defaults to the Sandbox creation time.
304
+ * @param {Date} [end] - End of the time range. Defaults to the current time.
305
+ * @returns Time-ordered usage samples over the requested range.
306
+ *
307
+ * @example
308
+ * const samples = await sandbox.getMetrics()
309
+ * for (const s of samples) {
310
+ * console.log(`${s.timestamp.toISOString()} CPU: ${s.cpuUsedPct}% mem: ${s.memUsed}/${s.memTotal}`)
311
+ * }
312
+ */
313
+ async getMetrics(start, end) {
314
+ const to = end ?? new Date();
315
+ const from = start ?? (this.createdAt ? new Date(this.createdAt) : to);
316
+ const analyticsApiUrl = await this.getAnalyticsApiUrl();
317
+ if (analyticsApiUrl) {
318
+ const response = await this.buildAnalyticsTelemetryApi(analyticsApiUrl).organizationOrganizationIdSandboxSandboxIdTelemetryMetricsGet(this.organizationId, this.id, from.toISOString(), to.toISOString(), SANDBOX_METRIC_NAMES.join(','));
319
+ return pivotSandboxMetricPoints(response.data);
320
+ }
321
+ const response = await this.sandboxApi.getSandboxMetrics(this.id, from, to, undefined, SANDBOX_METRIC_NAMES);
322
+ return pivotSandboxMetrics(response.data.series);
323
+ }
324
+ buildAnalyticsTelemetryApi(analyticsApiUrl) {
325
+ const analyticsConfig = new analytics_api_client_1.Configuration({
326
+ basePath: analyticsApiUrl,
327
+ apiKey: this.clientConfig.baseOptions?.headers?.Authorization,
328
+ });
329
+ return new analytics_api_client_1.TelemetryApi(analyticsConfig, undefined, Daytona_1.Daytona.createAxiosInstance());
330
+ }
240
331
  /**
241
332
  * @deprecated Use `getUserHomeDir` instead. This method will be removed in a future version.
242
333
  */
@@ -394,9 +485,16 @@ let Sandbox = (() => {
394
485
  timeout: timeout * 1000,
395
486
  });
396
487
  const sandboxDto = response.data;
397
- const forkedSandbox = new Sandbox(sandboxDto, structuredClone(this.clientConfig), Daytona_1.Daytona.createAxiosInstance(), this.sandboxApi);
488
+ const sandboxWithProxyUrl = sandboxDto.toolboxProxyUrl
489
+ ? sandboxDto
490
+ : {
491
+ ...sandboxDto,
492
+ toolboxProxyUrl: (await this.sandboxApi.getToolboxProxyUrl(sandboxDto.id)).data.url,
493
+ };
494
+ const forkedSandbox = new Sandbox(sandboxWithProxyUrl, structuredClone(this.clientConfig), Daytona_1.Daytona.createAxiosInstance(), this.sandboxApi, this.getAnalyticsApiUrl, this.subscriptionManager);
398
495
  const timeElapsed = Date.now() - startTime;
399
- await forkedSandbox.waitUntilStarted(timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout);
496
+ const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
497
+ await forkedSandbox.waitUntilStarted(remainingTimeout);
400
498
  return forkedSandbox;
401
499
  }
402
500
  /**
@@ -424,34 +522,19 @@ let Sandbox = (() => {
424
522
  }
425
523
  const startTime = Date.now();
426
524
  const req = { name };
427
- await this.sandboxApi.createSandboxSnapshot(this.id, req, undefined, {
525
+ const response = await this.sandboxApi.createSandboxSnapshot(this.id, req, undefined, {
428
526
  timeout: timeout * 1000,
429
527
  });
430
- await this.refreshData();
528
+ this.processSandboxDto(response.data);
431
529
  const timeElapsed = Date.now() - startTime;
432
530
  const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
433
531
  await this.waitForSnapshotComplete(remainingTimeout);
434
532
  }
435
533
  async waitForSnapshotComplete(timeout) {
436
- let checkInterval = 100;
437
- const startTime = Date.now();
438
- while (this.state === api_client_1.SandboxState.SNAPSHOTTING) {
439
- await this.refreshData();
440
- // @ts-expect-error this.refreshData() can modify this.state so this check is fine
441
- if (this.state === api_client_1.SandboxState.ERROR || this.state === api_client_1.SandboxState.BUILD_FAILED) {
442
- throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} snapshot failed with state: ${this.state}, error reason: ${this.errorReason}`);
443
- }
444
- if (this.state !== api_client_1.SandboxState.SNAPSHOTTING) {
445
- return;
446
- }
447
- if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
448
- throw new DaytonaError_1.DaytonaTimeoutError('Sandbox snapshot did not complete within the timeout period');
449
- }
450
- await new Promise((resolve) => setTimeout(resolve, checkInterval));
451
- if (Date.now() - startTime > 5000) {
452
- checkInterval = Math.min(checkInterval * 1.1, 1000);
453
- }
454
- }
534
+ const errorStates = [api_client_1.SandboxState.ERROR, api_client_1.SandboxState.BUILD_FAILED];
535
+ const excludeStates = new Set([api_client_1.SandboxState.SNAPSHOTTING, ...errorStates]);
536
+ const targetStates = Object.values(api_client_1.SandboxState).filter((s) => !excludeStates.has(s));
537
+ return this.waitForState(targetStates, errorStates, timeout, 'Sandbox snapshot did not complete within the timeout period', (state) => `Sandbox ${this.id} snapshot failed with state: ${state}, error reason: ${this.errorReason}`);
455
538
  }
456
539
  /**
457
540
  * Pauses the Sandbox, freezing all running processes.
@@ -482,31 +565,47 @@ let Sandbox = (() => {
482
565
  await this.refreshData();
483
566
  const timeElapsed = Date.now() - startTime;
484
567
  const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
485
- await this.waitForPauseComplete(remainingTimeout);
486
- }
487
- async waitForPauseComplete(timeout) {
488
- let checkInterval = 100;
489
- const startTime = Date.now();
490
- while (this.state === api_client_1.SandboxState.PAUSING) {
491
- await this.refreshData();
492
- // @ts-expect-error this.refreshData() can modify this.state so this check is fine
493
- if (this.state === api_client_1.SandboxState.ERROR) {
494
- throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} pause failed with state: ${this.state}, error reason: ${this.errorReason}`);
495
- }
496
- if (timeout > 0 && (Date.now() - startTime) / 1000 >= timeout) {
497
- throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} failed to pause within ${timeout} seconds`);
498
- }
499
- await new Promise((resolve) => globalThis.setTimeout(resolve, checkInterval));
500
- checkInterval = Math.min(checkInterval * 1.5, 1000);
501
- }
568
+ // Main's contract: pause completes when the sandbox has *left* PAUSING
569
+ // (paused, stopped, archived, ...), not only on exactly PAUSED.
570
+ const errorStates = [api_client_1.SandboxState.ERROR, api_client_1.SandboxState.BUILD_FAILED];
571
+ const excludeStates = new Set([api_client_1.SandboxState.PAUSING, ...errorStates]);
572
+ const targetStates = Object.values(api_client_1.SandboxState).filter((s) => !excludeStates.has(s));
573
+ await this.waitForState(targetStates, errorStates, remainingTimeout, 'Sandbox failed to become paused within the timeout period', (state) => `Sandbox ${this.id} pause failed with state: ${state}, error reason: ${this.errorReason}`);
502
574
  }
503
575
  /**
504
576
  * Deletes the Sandbox.
577
+ *
578
+ * By default this returns as soon as the deletion request is accepted (matching
579
+ * historical behavior). Pass `wait = true` to block until the Sandbox reaches
580
+ * the 'destroyed' state.
581
+ *
582
+ * @param {number} [timeout] - Timeout in seconds for the request — and, when
583
+ * `wait` is true, for reaching 'destroyed'. 0 means no timeout.
584
+ * Defaults to 60-second timeout.
585
+ * @param {boolean} [wait] - If true, wait until the Sandbox is destroyed. Defaults to false.
505
586
  * @returns {Promise<void>}
506
587
  */
507
- async delete(timeout = 60) {
508
- await this.sandboxApi.deleteSandbox(this.id, undefined, { timeout: timeout * 1000 });
509
- this.refreshDataSafe();
588
+ async delete(timeout = 60, wait = false) {
589
+ if (timeout < 0) {
590
+ throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
591
+ }
592
+ const startTime = Date.now();
593
+ const response = await this.sandboxApi.deleteSandbox(this.id, undefined, { timeout: timeout * 1000 });
594
+ if (response.data) {
595
+ this.processSandboxDto(response.data);
596
+ }
597
+ try {
598
+ if (wait && this.state !== api_client_1.SandboxState.DESTROYED) {
599
+ const timeElapsed = Date.now() - startTime;
600
+ await this.waitForState([api_client_1.SandboxState.DESTROYED], [api_client_1.SandboxState.ERROR, api_client_1.SandboxState.BUILD_FAILED], timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout, 'Sandbox failed to be destroyed within the timeout period', (state) => `Sandbox ${this.id} failed to delete with state: ${state}, error reason: ${this.errorReason}`, true);
601
+ }
602
+ }
603
+ finally {
604
+ if (this.subId) {
605
+ this.subscriptionManager.unsubscribe(this.subId);
606
+ this.subId = undefined;
607
+ }
608
+ }
510
609
  }
511
610
  /**
512
611
  * Waits for the Sandbox to reach the 'started' state.
@@ -523,26 +622,10 @@ let Sandbox = (() => {
523
622
  if (timeout < 0) {
524
623
  throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
525
624
  }
526
- let checkInterval = 100;
527
- const startTime = Date.now();
528
- while (this.state !== 'started') {
529
- await this.refreshData();
530
- // @ts-expect-error this.refreshData() can modify this.state so this check is fine
531
- if (this.state === 'started') {
532
- return;
533
- }
534
- if (this.state === 'error') {
535
- const errMsg = `Sandbox ${this.id} failed to start with status: ${this.state}, error reason: ${this.errorReason}`;
536
- throw new DaytonaError_1.DaytonaError(errMsg);
537
- }
538
- if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
539
- throw new DaytonaError_1.DaytonaTimeoutError('Sandbox failed to become ready within the timeout period');
540
- }
541
- await new Promise((resolve) => setTimeout(resolve, checkInterval));
542
- if (Date.now() - startTime > 5000) {
543
- checkInterval = Math.min(checkInterval * 1.1, 1000);
544
- }
625
+ if (this.state === api_client_1.SandboxState.STARTED) {
626
+ return;
545
627
  }
628
+ return this.waitForState([api_client_1.SandboxState.STARTED], [api_client_1.SandboxState.ERROR, api_client_1.SandboxState.BUILD_FAILED], timeout, 'Sandbox failed to become ready within the timeout period', (state) => `Sandbox ${this.id} failed to start with status: ${state}, error reason: ${this.errorReason}`);
546
629
  }
547
630
  /**
548
631
  * Wait for Sandbox to reach 'stopped' state.
@@ -559,27 +642,11 @@ let Sandbox = (() => {
559
642
  if (timeout < 0) {
560
643
  throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
561
644
  }
562
- let checkInterval = 100;
563
- const startTime = Date.now();
564
645
  // Treat destroyed as stopped to cover ephemeral sandboxes that are automatically deleted after stopping
565
- while (this.state !== 'stopped' && this.state !== 'destroyed') {
566
- this.refreshDataSafe();
567
- // @ts-expect-error this.refreshData() can modify this.state so this check is fine
568
- if (this.state === 'stopped' || this.state === 'destroyed') {
569
- return;
570
- }
571
- if (this.state === 'error') {
572
- const errMsg = `Sandbox failed to stop with status: ${this.state}, error reason: ${this.errorReason}`;
573
- throw new DaytonaError_1.DaytonaError(errMsg);
574
- }
575
- if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
576
- throw new DaytonaError_1.DaytonaTimeoutError('Sandbox failed to become stopped within the timeout period');
577
- }
578
- await new Promise((resolve) => setTimeout(resolve, checkInterval));
579
- if (Date.now() - startTime > 5000) {
580
- checkInterval = Math.min(checkInterval * 1.1, 1000);
581
- }
646
+ if (this.state === api_client_1.SandboxState.STOPPED || this.state === api_client_1.SandboxState.DESTROYED) {
647
+ return;
582
648
  }
649
+ return this.waitForState([api_client_1.SandboxState.STOPPED, api_client_1.SandboxState.DESTROYED], [api_client_1.SandboxState.ERROR, api_client_1.SandboxState.BUILD_FAILED], timeout, 'Sandbox failed to become stopped within the timeout period', (state) => `Sandbox failed to stop with status: ${state}, error reason: ${this.errorReason}`, true);
583
650
  }
584
651
  /**
585
652
  * Refreshes the Sandbox data from the API.
@@ -636,6 +703,60 @@ let Sandbox = (() => {
636
703
  await this.sandboxApi.setAutostopInterval(this.id, interval);
637
704
  this.autoStopInterval = interval;
638
705
  }
706
+ /**
707
+ * Set the auto-pause interval for the Sandbox.
708
+ *
709
+ * The Sandbox will automatically pause after being idle (no new events) for the specified interval.
710
+ * Events include any state changes or interactions with the Sandbox through the sdk.
711
+ * Interactions using Sandbox Previews are not included.
712
+ *
713
+ * Only supported for sandbox classes that support pausing. At most one of the auto-stop
714
+ * and auto-pause intervals may be non-zero, so disable auto-stop first by setting its
715
+ * interval to 0.
716
+ *
717
+ * @param {number} interval - Number of minutes of inactivity before auto-pausing.
718
+ * Set to 0 to disable auto-pause. For pause-supporting sandbox
719
+ * classes, creation defaults to 60 minutes when neither interval is provided.
720
+ * @returns {Promise<void>}
721
+ * @throws {DaytonaError} - `DaytonaError` - If interval is not a non-negative integer
722
+ *
723
+ * @example
724
+ * // Auto-pause after 1 hour
725
+ * await sandbox.setAutoPauseInterval(60);
726
+ * // Or disable auto-pause
727
+ * await sandbox.setAutoPauseInterval(0);
728
+ */
729
+ async setAutoPauseInterval(interval) {
730
+ if (!Number.isInteger(interval) || interval < 0) {
731
+ throw new DaytonaError_1.DaytonaValidationError('autoPauseInterval must be a non-negative integer');
732
+ }
733
+ await this.sandboxApi.setAutoPauseInterval(this.id, interval);
734
+ this.autoPauseInterval = interval;
735
+ }
736
+ /**
737
+ * Set the TTL (maximum time to live) for the Sandbox.
738
+ *
739
+ * The Sandbox will be destroyed once the TTL elapses, counted as wall-clock time regardless of the
740
+ * Sandbox state - even if it is stopped, paused, or archived. Calling this method re-anchors the
741
+ * deadline from the current time. Call `refreshData()` afterwards to read the updated `autoDestroyAt`.
742
+ *
743
+ * @param {number} ttlMinutes - Number of minutes from now after which the Sandbox will be destroyed.
744
+ * Set to 0 to disable the TTL.
745
+ * @returns {Promise<void>}
746
+ * @throws {DaytonaError} - `DaytonaError` - If ttlMinutes is not a non-negative integer
747
+ *
748
+ * @example
749
+ * // Destroy the Sandbox 1 hour from now
750
+ * await sandbox.setTtl(60);
751
+ * // Or disable the TTL
752
+ * await sandbox.setTtl(0);
753
+ */
754
+ async setTtl(ttlMinutes) {
755
+ if (!Number.isInteger(ttlMinutes) || ttlMinutes < 0) {
756
+ throw new DaytonaError_1.DaytonaValidationError('ttlMinutes must be a non-negative integer');
757
+ }
758
+ await this.sandboxApi.setTtl(this.id, ttlMinutes);
759
+ }
639
760
  /**
640
761
  * Set the auto-archive interval for the Sandbox.
641
762
  *
@@ -858,25 +979,13 @@ let Sandbox = (() => {
858
979
  if (timeout < 0) {
859
980
  throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
860
981
  }
861
- let checkInterval = 100;
862
- const startTime = Date.now();
863
- while (this.state === api_client_1.SandboxState.RESIZING) {
864
- await this.refreshData();
865
- // @ts-expect-error this.refreshData() can modify this.state so this check is fine
866
- if (this.state === api_client_1.SandboxState.ERROR || this.state === api_client_1.SandboxState.BUILD_FAILED) {
867
- throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} resize failed with state: ${this.state}, error reason: ${this.errorReason}`);
868
- }
869
- if (this.state !== api_client_1.SandboxState.RESIZING) {
870
- return;
871
- }
872
- if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
873
- throw new DaytonaError_1.DaytonaTimeoutError('Sandbox resize did not complete within the timeout period');
874
- }
875
- await new Promise((resolve) => setTimeout(resolve, checkInterval));
876
- if (Date.now() - startTime > 5000) {
877
- checkInterval = Math.min(checkInterval * 1.1, 1000);
878
- }
982
+ if (this.state !== api_client_1.SandboxState.RESIZING) {
983
+ return;
879
984
  }
985
+ const errorStates = [api_client_1.SandboxState.ERROR, api_client_1.SandboxState.BUILD_FAILED];
986
+ const excludeStates = new Set([api_client_1.SandboxState.RESIZING, ...errorStates]);
987
+ const targetStates = Object.values(api_client_1.SandboxState).filter((s) => !excludeStates.has(s));
988
+ return this.waitForState(targetStates, errorStates, timeout, 'Sandbox resize did not complete within the timeout period', (state) => `Sandbox ${this.id} resize failed with state: ${state}, error reason: ${this.errorReason}`);
880
989
  }
881
990
  /**
882
991
  * Creates an SSH access token for the sandbox.
@@ -905,6 +1014,172 @@ let Sandbox = (() => {
905
1014
  async validateSshAccess(token) {
906
1015
  return (await this.sandboxApi.validateSshAccess(token)).data;
907
1016
  }
1017
+ /**
1018
+ * Subscribes to real-time events for this sandbox.
1019
+ * Auto-updates sandbox metadata on every event.
1020
+ */
1021
+ subscribeToEvents() {
1022
+ if (this.subId) {
1023
+ return;
1024
+ }
1025
+ this.subId = this.subscriptionManager.subscribe(this.id, this.handleEvent.bind(this), [
1026
+ 'sandbox.state.updated',
1027
+ 'sandbox.created',
1028
+ ]);
1029
+ }
1030
+ ensureSubscribed() {
1031
+ if (this.subId) {
1032
+ if (this.subscriptionManager.refresh(this.subId)) {
1033
+ return;
1034
+ }
1035
+ this.subId = undefined;
1036
+ }
1037
+ this.subscribeToEvents();
1038
+ }
1039
+ handleEvent(eventName, data) {
1040
+ if (!data || typeof data !== 'object')
1041
+ return;
1042
+ const raw = data.sandbox ?? data;
1043
+ if (!raw || typeof raw !== 'object')
1044
+ return;
1045
+ if (eventName === 'sandbox.created') {
1046
+ this.processSandboxDto(raw);
1047
+ return;
1048
+ }
1049
+ const newState = raw.state ?? data.newState;
1050
+ if (newState) {
1051
+ this.applyState(newState);
1052
+ }
1053
+ }
1054
+ /**
1055
+ * Waits for the sandbox to reach one of the target states.
1056
+ * Throws on error states or timeout.
1057
+ *
1058
+ * @param targetStates - States that indicate success.
1059
+ * @param errorStates - States that indicate failure.
1060
+ * @param timeout - Maximum time to wait in seconds. 0 means no timeout.
1061
+ * @param timeoutMessage - Error message when timeout is reached.
1062
+ * @param errorMessageFn - Function that produces an error message from the current state.
1063
+ * @param safeRefresh - If true, use refreshDataSafe() for polling (for delete operations where 404 is expected).
1064
+ */
1065
+ waitForState(targetStates, errorStates, timeout, timeoutMessage, errorMessageFn, safeRefresh = false) {
1066
+ this.ensureSubscribed();
1067
+ return new Promise((resolve, reject) => {
1068
+ let timeoutTimer = null;
1069
+ let pollTimer = null;
1070
+ let settled = false;
1071
+ const waiter = {
1072
+ targetStates: new Set(targetStates),
1073
+ errorStates: new Set(errorStates),
1074
+ resolve: (_) => {
1075
+ if (settled)
1076
+ return;
1077
+ cleanup();
1078
+ resolve();
1079
+ },
1080
+ reject: (err) => {
1081
+ if (settled)
1082
+ return;
1083
+ cleanup();
1084
+ reject(err);
1085
+ },
1086
+ };
1087
+ this.stateWaiters.push(waiter);
1088
+ this.stateWaiterErrorMessageFns.set(waiter, errorMessageFn);
1089
+ const cleanup = () => {
1090
+ if (settled)
1091
+ return;
1092
+ settled = true;
1093
+ if (timeoutTimer)
1094
+ clearTimeout(timeoutTimer);
1095
+ if (pollTimer)
1096
+ clearTimeout(pollTimer);
1097
+ this.removeStateWaiter(waiter);
1098
+ };
1099
+ // Fast-path only on cached *target* states (parity with main's pre-check).
1100
+ // Cached error states are deliberately NOT evaluated here — main always
1101
+ // refreshed before failing, so a stale ERROR must survive one refresh.
1102
+ if (this.state && waiter.targetStates.has(this.state)) {
1103
+ waiter.resolve(this.state);
1104
+ return;
1105
+ }
1106
+ if (timeout !== 0) {
1107
+ timeoutTimer = setTimeout(() => {
1108
+ void (async () => {
1109
+ if (settled)
1110
+ return;
1111
+ // Parity with main: complete one final refresh-then-evaluate before
1112
+ // rejecting, so a clamped/short timeout still observes the latest state.
1113
+ let refreshed = false;
1114
+ try {
1115
+ if (safeRefresh) {
1116
+ refreshed = await this.refreshDataSafe();
1117
+ }
1118
+ else {
1119
+ await this.refreshData();
1120
+ refreshed = true;
1121
+ }
1122
+ }
1123
+ catch {
1124
+ // fall through to the timeout rejection below
1125
+ }
1126
+ if (!settled) {
1127
+ // Explicit evaluation: applyState() no-ops on unchanged state, so a
1128
+ // persistent error state would otherwise time out generically here.
1129
+ // Only evaluate when the refresh succeeded — a failed refresh leaves
1130
+ // the cached state stale and must not be treated as authoritative.
1131
+ if (refreshed && this.checkStateWaiter(waiter, this.state)) {
1132
+ return;
1133
+ }
1134
+ cleanup();
1135
+ reject(new DaytonaError_1.DaytonaTimeoutError(timeoutMessage));
1136
+ }
1137
+ })();
1138
+ }, timeout * 1000);
1139
+ }
1140
+ // Poll as a safety net for missed state changes. With an active event subscription
1141
+ // a sparse 1s cadence suffices; without events (polling mode) replicate main's
1142
+ // cadence exactly: 100ms steady for the first 5s, then exponential backoff capped at 1s.
1143
+ const streaming = !!this.subId;
1144
+ const pollStart = Date.now();
1145
+ let pollInterval = streaming ? 1000 : 100;
1146
+ const doPoll = async () => {
1147
+ if (settled)
1148
+ return;
1149
+ let refreshed = false;
1150
+ try {
1151
+ if (safeRefresh) {
1152
+ refreshed = await this.refreshDataSafe();
1153
+ }
1154
+ else {
1155
+ await this.refreshData();
1156
+ refreshed = true;
1157
+ }
1158
+ }
1159
+ catch (error) {
1160
+ waiter.reject(error instanceof Error ? error : new DaytonaError_1.DaytonaError(String(error)));
1161
+ return;
1162
+ }
1163
+ if (!settled) {
1164
+ // Evaluate the refreshed state explicitly: applyState() no-ops when the
1165
+ // state is unchanged, so a persistent error state would otherwise never
1166
+ // reject the waiter. Only evaluate when the refresh succeeded — a failed
1167
+ // safe refresh leaves the cached state stale, so keep polling instead.
1168
+ if (refreshed && this.checkStateWaiter(waiter, this.state)) {
1169
+ return;
1170
+ }
1171
+ if (!streaming && Date.now() - pollStart > 5000) {
1172
+ pollInterval = Math.min(pollInterval * 1.1, 1000);
1173
+ }
1174
+ pollTimer = setTimeout(() => {
1175
+ void doPoll();
1176
+ }, pollInterval);
1177
+ }
1178
+ };
1179
+ // First poll runs immediately (main refreshed before any state evaluation).
1180
+ void doPoll();
1181
+ });
1182
+ }
908
1183
  /**
909
1184
  * Assigns the API sandbox data to the Sandbox object.
910
1185
  *
@@ -912,6 +1187,7 @@ let Sandbox = (() => {
912
1187
  * @returns {void}
913
1188
  */
914
1189
  processSandboxDto(sandboxDto) {
1190
+ const newState = sandboxDto.state;
915
1191
  this.id = sandboxDto.id;
916
1192
  this.name = sandboxDto.name;
917
1193
  this.organizationId = sandboxDto.organizationId;
@@ -924,17 +1200,17 @@ let Sandbox = (() => {
924
1200
  this.gpu = sandboxDto.gpu;
925
1201
  this.memory = sandboxDto.memory;
926
1202
  this.disk = sandboxDto.disk;
927
- this.state = sandboxDto.state;
928
1203
  this.errorReason = sandboxDto.errorReason;
929
1204
  this.recoverable = sandboxDto.recoverable;
930
1205
  this.backupState = sandboxDto.backupState;
931
1206
  this.autoStopInterval = sandboxDto.autoStopInterval;
1207
+ this.autoPauseInterval = sandboxDto.autoPauseInterval;
932
1208
  this.autoArchiveInterval = sandboxDto.autoArchiveInterval;
933
1209
  this.autoDeleteInterval = sandboxDto.autoDeleteInterval;
1210
+ this.autoDestroyAt = sandboxDto.autoDestroyAt;
934
1211
  this.createdAt = sandboxDto.createdAt;
935
1212
  this.updatedAt = sandboxDto.updatedAt;
936
1213
  this.lastActivityAt = sandboxDto.lastActivityAt;
937
- this.toolboxProxyUrl = sandboxDto.toolboxProxyUrl;
938
1214
  // Fields only present in the full SandboxDto (not returned by list endpoint)
939
1215
  if ('env' in sandboxDto) {
940
1216
  this.env = sandboxDto.env;
@@ -946,6 +1222,21 @@ let Sandbox = (() => {
946
1222
  this.buildInfo = sandboxDto.buildInfo;
947
1223
  this.backupCreatedAt = sandboxDto.backupCreatedAt;
948
1224
  }
1225
+ const newProxyUrl = sandboxDto.toolboxProxyUrl;
1226
+ if (newProxyUrl && newProxyUrl !== this.toolboxProxyUrl && this.axiosInstance) {
1227
+ let baseUrl = newProxyUrl;
1228
+ if (!baseUrl.endsWith('/')) {
1229
+ baseUrl += '/';
1230
+ }
1231
+ this.axiosInstance.defaults.baseURL = baseUrl + this.id;
1232
+ this.clientConfig.basePath = this.axiosInstance.defaults.baseURL;
1233
+ }
1234
+ if (newProxyUrl) {
1235
+ this.toolboxProxyUrl = newProxyUrl;
1236
+ }
1237
+ if (newState) {
1238
+ this.applyState(newState);
1239
+ }
949
1240
  }
950
1241
  /**
951
1242
  * Refreshes the Sandbox data from the API, but does not throw an error if the sandbox has been deleted.
@@ -953,17 +1244,113 @@ let Sandbox = (() => {
953
1244
  *
954
1245
  * @returns {Promise<void>}
955
1246
  */
1247
+ /**
1248
+ * @returns true when the refresh produced authoritative state (success, or 404
1249
+ * mapped to DESTROYED); false when a transient error was swallowed and
1250
+ * the cached state is stale.
1251
+ */
956
1252
  async refreshDataSafe() {
957
1253
  try {
958
1254
  await this.refreshData();
1255
+ return true;
959
1256
  }
960
1257
  catch (error) {
961
1258
  if (error instanceof DaytonaError_1.DaytonaNotFoundError) {
962
- this.state = api_client_1.SandboxState.DESTROYED;
1259
+ this.applyState(api_client_1.SandboxState.DESTROYED);
1260
+ return true;
963
1261
  }
1262
+ // Other errors are deliberately swallowed (parity with main): transient
1263
+ // failures (e.g. 502) mid-poll must not abort stop()/delete() waits.
1264
+ return false;
1265
+ }
1266
+ }
1267
+ applyState(newState) {
1268
+ if (newState === this.state) {
1269
+ return;
1270
+ }
1271
+ this.state = newState;
1272
+ for (const waiter of [...this.stateWaiters]) {
1273
+ this.checkStateWaiter(waiter, this.state);
964
1274
  }
965
1275
  }
1276
+ checkStateWaiter(waiter, state) {
1277
+ if (!state) {
1278
+ return false;
1279
+ }
1280
+ if (waiter.targetStates.has(state)) {
1281
+ waiter.resolve(state);
1282
+ return true;
1283
+ }
1284
+ if (waiter.errorStates.has(state)) {
1285
+ const errorMessageFn = this.stateWaiterErrorMessageFns.get(waiter);
1286
+ waiter.reject(new DaytonaError_1.DaytonaError(errorMessageFn ? errorMessageFn(state) : `Sandbox ${this.id} failed with state: ${state}`));
1287
+ return true;
1288
+ }
1289
+ return false;
1290
+ }
1291
+ removeStateWaiter(waiter) {
1292
+ const index = this.stateWaiters.indexOf(waiter);
1293
+ if (index !== -1) {
1294
+ this.stateWaiters.splice(index, 1);
1295
+ }
1296
+ this.stateWaiterErrorMessageFns.delete(waiter);
1297
+ }
966
1298
  };
967
1299
  })();
968
1300
  exports.Sandbox = Sandbox;
1301
+ const SANDBOX_METRIC_FIELD_BY_NAME = {
1302
+ 'daytona.sandbox.cpu.utilization': 'cpuUsedPct',
1303
+ 'daytona.sandbox.cpu.limit': 'cpuCount',
1304
+ 'daytona.sandbox.memory.usage': 'memUsed',
1305
+ 'daytona.sandbox.memory.limit': 'memTotal',
1306
+ 'daytona.sandbox.memory.cache': 'memCache',
1307
+ 'daytona.sandbox.filesystem.usage': 'diskUsed',
1308
+ 'daytona.sandbox.filesystem.total': 'diskTotal',
1309
+ };
1310
+ const SANDBOX_METRIC_NAMES = Object.keys(SANDBOX_METRIC_FIELD_BY_NAME);
1311
+ function sandboxMetricsFromSystemMetrics(m) {
1312
+ return {
1313
+ cpuCount: m.cpuCount ?? 0,
1314
+ cpuUsedPct: m.cpuUsedPct ?? 0,
1315
+ diskTotal: m.diskTotal ?? 0,
1316
+ diskUsed: m.diskUsed ?? 0,
1317
+ memTotal: m.memTotal ?? 0,
1318
+ memUsed: m.memUsed ?? 0,
1319
+ memCache: m.memCache ?? 0,
1320
+ timestamp: m.timestamp ? new Date(m.timestamp) : new Date(),
1321
+ };
1322
+ }
1323
+ function buildSandboxMetrics(buckets) {
1324
+ return [...buckets.keys()].sort().map((ts) => {
1325
+ const v = buckets.get(ts);
1326
+ return {
1327
+ cpuCount: v.cpuCount ?? 0,
1328
+ cpuUsedPct: v.cpuUsedPct ?? 0,
1329
+ diskTotal: v.diskTotal ?? 0,
1330
+ diskUsed: v.diskUsed ?? 0,
1331
+ memTotal: v.memTotal ?? 0,
1332
+ memUsed: v.memUsed ?? 0,
1333
+ memCache: v.memCache ?? 0,
1334
+ timestamp: new Date(ts),
1335
+ };
1336
+ });
1337
+ }
1338
+ function pivotMetricTriples(triples) {
1339
+ const buckets = new Map();
1340
+ for (const [name, timestamp, value] of triples) {
1341
+ const field = name ? SANDBOX_METRIC_FIELD_BY_NAME[name] : undefined;
1342
+ if (!field || timestamp === undefined || value === undefined)
1343
+ continue;
1344
+ const bucket = buckets.get(timestamp) ?? {};
1345
+ bucket[field] = value;
1346
+ buckets.set(timestamp, bucket);
1347
+ }
1348
+ return buildSandboxMetrics(buckets);
1349
+ }
1350
+ function pivotSandboxMetrics(series) {
1351
+ return pivotMetricTriples((series ?? []).flatMap((s) => (s.dataPoints ?? []).map((p) => [s.metricName, p.timestamp, p.value])));
1352
+ }
1353
+ function pivotSandboxMetricPoints(points) {
1354
+ return pivotMetricTriples((points ?? []).map((p) => [p.metricName, p.timestamp, p.value]));
1355
+ }
969
1356
  //# sourceMappingURL=Sandbox.js.map