@daytona/sdk 0.198.0-alpha.1 → 0.199.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.
package/cjs/Sandbox.js CHANGED
@@ -7,7 +7,6 @@ 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");
11
10
  const Daytona_1 = require("./Daytona");
12
11
  const toolbox_api_client_1 = require("@daytona/toolbox-api-client");
13
12
  const FileSystem_1 = require("./FileSystem");
@@ -19,12 +18,35 @@ const Daytona_2 = require("./Daytona");
19
18
  const ComputerUse_1 = require("./ComputerUse");
20
19
  const CodeInterpreter_1 = require("./CodeInterpreter");
21
20
  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);
21
+ const api_client_2 = require("@daytona/api-client");
22
+ /**
23
+ * Builds the shell command that bootstraps the hotmount agent and mounts the volume inside a
24
+ * Sandbox. It exports the SEAWEED_* environment contract and runs the region's `init.sh`, using
25
+ * passwordless sudo when not already root (sudo strips env, so the vars are passed via `env`).
26
+ */
27
+ function buildHotmountMountCommand(mountToken, mountPath) {
28
+ const envVars = {
29
+ SEAWEED_TOKEN: mountToken.token,
30
+ SEAWEED_GATEWAY_GRPC: mountToken.gatewayGrpc,
31
+ SEAWEED_GATEWAY_HTTP: mountToken.gatewayHttp,
32
+ SEAWEED_BINARIES_URL: mountToken.binariesUrl,
33
+ SEAWEED_MOUNT_DIR: mountPath,
34
+ SEAWEED_VERSION: mountToken.version,
27
35
  };
36
+ const envAssignments = Object.entries(envVars)
37
+ .filter(([, value]) => value !== undefined && value !== '')
38
+ .map(([key, value]) => `${key}='${value}'`)
39
+ .join(' ');
40
+ // The bootstrap (and init.sh itself) requires curl. Fail loudly if it is missing or the
41
+ // download fails, rather than letting a broken `curl … | bash` pipe exit 0 and mount nothing.
42
+ const inner = [
43
+ 'set -e',
44
+ 'if ! command -v curl >/dev/null 2>&1; then echo "hotmount: curl is required to bootstrap the agent but was not found in the sandbox" >&2; exit 1; fi',
45
+ 'mkdir -p "$SEAWEED_MOUNT_DIR"',
46
+ 'init_script="$(curl -fsSL "$SEAWEED_BINARIES_URL/init.sh")"',
47
+ 'printf %s "$init_script" | bash',
48
+ ].join('; ');
49
+ return (`if [ "$(id -u)" != 0 ]; then SUDO="sudo -n"; else SUDO=""; fi; ` + `$SUDO env ${envAssignments} bash -c '${inner}'`);
28
50
  }
29
51
  /**
30
52
  * Represents a Daytona Sandbox.
@@ -55,10 +77,8 @@ function withEvents(target, _context) {
55
77
  * @property {string} [backupCreatedAt] - When the backup was created (not returned by list results;
56
78
  * call `refreshData()` on each item to populate)
57
79
  * @property {number} [autoStopInterval] - Auto-stop interval in minutes
58
- * @property {number} [autoPauseInterval] - Auto-pause interval in minutes
59
80
  * @property {number} [autoArchiveInterval] - Auto-archive interval in minutes
60
81
  * @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)
62
82
  * @property {Array<SandboxVolume>} [volumes] - Volumes attached to the Sandbox (not returned by
63
83
  * list results; call `refreshData()` on each item to populate)
64
84
  * @property {BuildInfo} [buildInfo] - Build information for the Sandbox if it was created from dynamic build
@@ -80,14 +100,13 @@ function withEvents(target, _context) {
80
100
  let Sandbox = (() => {
81
101
  let _instanceExtraInitializers = [];
82
102
  let _getUserHomeDir_decorators;
83
- let _getMetricsLatest_decorators;
84
- let _getMetrics_decorators;
85
103
  let _getUserRootDir_decorators;
86
104
  let _getWorkDir_decorators;
105
+ let _mountVolume_decorators;
106
+ let _pullVolumes_decorators;
87
107
  let _createLspServer_decorators;
88
108
  let _setLabels_decorators;
89
109
  let _start_decorators;
90
- let _recover_decorators;
91
110
  let _stop_decorators;
92
111
  let __experimental_fork_decorators;
93
112
  let __experimental_createSnapshot_decorators;
@@ -96,18 +115,13 @@ let Sandbox = (() => {
96
115
  let _waitUntilStarted_decorators;
97
116
  let _waitUntilStopped_decorators;
98
117
  let _refreshData_decorators;
99
- let _refreshActivity_decorators;
100
118
  let _setAutostopInterval_decorators;
101
- let _setAutoPauseInterval_decorators;
102
- let _setTtl_decorators;
103
119
  let _setAutoArchiveInterval_decorators;
104
120
  let _setAutoDeleteInterval_decorators;
105
121
  let _updateNetworkSettings_decorators;
106
122
  let _updateSecrets_decorators;
107
123
  let _updateEnv_decorators;
108
124
  let _getPreviewLink_decorators;
109
- let _getSignedPreviewUrl_decorators;
110
- let _expireSignedPreviewUrl_decorators;
111
125
  let _archive_decorators;
112
126
  let _resize_decorators;
113
127
  let _waitForResizeComplete_decorators;
@@ -117,50 +131,43 @@ let Sandbox = (() => {
117
131
  return class Sandbox {
118
132
  static {
119
133
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
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];
134
+ _getUserHomeDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
135
+ _getUserRootDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
136
+ _getWorkDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
137
+ _mountVolume_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
138
+ _pullVolumes_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
139
+ _createLspServer_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
140
+ _setLabels_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
141
+ _start_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
142
+ _stop_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
143
+ __experimental_fork_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
144
+ __experimental_createSnapshot_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
145
+ _pause_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
146
+ _delete_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
147
+ _waitUntilStarted_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
148
+ _waitUntilStopped_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
149
+ _refreshData_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
150
+ _setAutostopInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
151
+ _setAutoArchiveInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
152
+ _setAutoDeleteInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
143
153
  _updateNetworkSettings_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
144
154
  _updateSecrets_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
145
155
  _updateEnv_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];
156
+ _getPreviewLink_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
157
+ _archive_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
158
+ _resize_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
159
+ _waitForResizeComplete_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
160
+ _createSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
161
+ _revokeSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
162
+ _validateSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
155
163
  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);
158
164
  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);
159
165
  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);
166
+ tslib_1.__esDecorate(this, null, _mountVolume_decorators, { kind: "method", name: "mountVolume", static: false, private: false, access: { has: obj => "mountVolume" in obj, get: obj => obj.mountVolume }, metadata: _metadata }, null, _instanceExtraInitializers);
167
+ tslib_1.__esDecorate(this, null, _pullVolumes_decorators, { kind: "method", name: "pullVolumes", static: false, private: false, access: { has: obj => "pullVolumes" in obj, get: obj => obj.pullVolumes }, metadata: _metadata }, null, _instanceExtraInitializers);
160
168
  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);
161
169
  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);
162
170
  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);
164
171
  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);
165
172
  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);
166
173
  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);
@@ -169,18 +176,13 @@ let Sandbox = (() => {
169
176
  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);
170
177
  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);
171
178
  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);
173
179
  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);
176
180
  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);
177
181
  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);
178
182
  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);
179
183
  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);
180
184
  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);
181
185
  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);
184
186
  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);
185
187
  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);
186
188
  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);
@@ -192,8 +194,7 @@ let Sandbox = (() => {
192
194
  clientConfig = tslib_1.__runInitializers(this, _instanceExtraInitializers);
193
195
  axiosInstance;
194
196
  sandboxApi;
195
- getAnalyticsApiUrl;
196
- subscriptionManager;
197
+ volumeService;
197
198
  fs;
198
199
  git;
199
200
  process;
@@ -218,10 +219,8 @@ let Sandbox = (() => {
218
219
  backupState;
219
220
  backupCreatedAt;
220
221
  autoStopInterval;
221
- autoPauseInterval;
222
222
  autoArchiveInterval;
223
223
  autoDeleteInterval;
224
- autoDestroyAt;
225
224
  volumes;
226
225
  buildInfo;
227
226
  createdAt;
@@ -234,28 +233,25 @@ let Sandbox = (() => {
234
233
  toolboxProxyUrl;
235
234
  infoApi;
236
235
  serverApi;
237
- systemApi;
238
- stateWaiters = [];
239
- stateWaiterErrorMessageFns = new WeakMap();
240
- subId;
241
236
  /**
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.
237
+ * Creates a new Sandbox instance
246
238
  *
247
239
  * @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
251
240
  */
252
- constructor(sandboxDto, clientConfig, axiosInstance, sandboxApi, getAnalyticsApiUrl, subscriptionManager) {
241
+ constructor(sandboxDto, clientConfig, axiosInstance, sandboxApi, volumeService) {
253
242
  this.clientConfig = clientConfig;
254
243
  this.axiosInstance = axiosInstance;
255
244
  this.sandboxApi = sandboxApi;
256
- this.getAnalyticsApiUrl = getAnalyticsApiUrl;
257
- this.subscriptionManager = subscriptionManager;
245
+ this.volumeService = volumeService;
258
246
  this.processSandboxDto(sandboxDto);
247
+ // Set the toolbox base URL
248
+ let baseUrl = this.toolboxProxyUrl;
249
+ if (!baseUrl.endsWith('/')) {
250
+ baseUrl += '/';
251
+ }
252
+ this.axiosInstance.defaults.baseURL = baseUrl + this.id;
253
+ this.clientConfig.basePath = this.axiosInstance.defaults.baseURL;
254
+ // Initialize Services
259
255
  const getPreviewToken = async () => (await this.getPreviewLink(1)).token;
260
256
  this.fs = new FileSystem_1.FileSystem(this.clientConfig, new toolbox_api_client_1.FileSystemApi(this.clientConfig, '', this.axiosInstance));
261
257
  this.git = new Git_1.Git(new toolbox_api_client_1.GitApi(this.clientConfig, '', this.axiosInstance));
@@ -265,8 +261,6 @@ let Sandbox = (() => {
265
261
  this.computerUse = new ComputerUse_1.ComputerUse(new toolbox_api_client_1.ComputerUseApi(this.clientConfig, '', this.axiosInstance));
266
262
  this.infoApi = new toolbox_api_client_1.InfoApi(this.clientConfig, '', this.axiosInstance);
267
263
  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();
270
264
  }
271
265
  /**
272
266
  * Gets the user's home directory path for the logged in user inside the Sandbox.
@@ -281,53 +275,6 @@ let Sandbox = (() => {
281
275
  const response = await this.infoApi.getUserHomeDir();
282
276
  return response.data.dir;
283
277
  }
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
- }
331
278
  /**
332
279
  * @deprecated Use `getUserHomeDir` instead. This method will be removed in a future version.
333
280
  */
@@ -348,6 +295,71 @@ let Sandbox = (() => {
348
295
  const response = await this.infoApi.getWorkDir();
349
296
  return response.data.dir;
350
297
  }
298
+ /**
299
+ * Mounts a hotmount Volume into the running Sandbox on the fly.
300
+ *
301
+ * Unlike legacy and blockmount volumes (which are attached at Sandbox creation), hotmount
302
+ * volumes are mounted at runtime: this method requests a short-lived mount token from the API
303
+ * and bootstraps the hotmount agent inside the Sandbox (downloading and running the region's
304
+ * `init.sh`), mounting the filesystem at the given path.
305
+ *
306
+ * The Sandbox must have `/dev/fuse` available, outbound access to the region gateway (ports
307
+ * 443 and 18443) and binaries bucket, and passwordless sudo (or run as root).
308
+ *
309
+ * @param {Volume} volume - The hotmount Volume to mount. Must be of type `VolumeType.HOTMOUNT`.
310
+ * @param {string} mountPath - The absolute path inside the Sandbox to mount the Volume at
311
+ * @returns {Promise<void>}
312
+ * @throws {DaytonaError} If the Volume is not a hotmount volume, or if mounting fails
313
+ *
314
+ * @example
315
+ * const volume = await daytona.volume.get("shared-fs");
316
+ * await sandbox.mountVolume(volume, "/mnt/shared");
317
+ */
318
+ async mountVolume(volume, mountPath) {
319
+ if (volume.type !== api_client_2.VolumeType.HOTMOUNT) {
320
+ throw new DaytonaError_1.DaytonaValidationError(`Only hotmount volumes can be mounted on the fly. Volume '${volume.name}' is of type '${volume.type}'.`);
321
+ }
322
+ if (!this.volumeService) {
323
+ throw new DaytonaError_1.DaytonaError('Volume service is not available for this Sandbox instance.');
324
+ }
325
+ const mountToken = await this.volumeService.getMountToken(volume);
326
+ const command = buildHotmountMountCommand(mountToken, mountPath);
327
+ const response = await this.process.executeCommand(command);
328
+ if (response.exitCode !== 0) {
329
+ throw new DaytonaError_1.DaytonaError(`Failed to mount hotmount volume '${volume.name}': ${response.result}`);
330
+ }
331
+ }
332
+ /**
333
+ * Pulls the latest state of the Sandbox's blockmount Volumes into the running Sandbox.
334
+ *
335
+ * Blockmount volumes reconcile in the background: each sandbox writes to a private scratch
336
+ * that is committed to the shared store periodically, but other sandboxes' commits only
337
+ * appear locally on re-materialize. This method makes them appear immediately, without
338
+ * stopping the Sandbox: it commits this Sandbox's local changes (so they participate in the
339
+ * merge), then applies the volume's latest merged state in place. Files modified locally
340
+ * after another sandbox's change keep the local version (last-change-wins by mtime).
341
+ *
342
+ * @param {Volume} [volume] - The blockmount Volume to pull. Omit to pull every blockmount
343
+ * Volume attached to the Sandbox.
344
+ * @returns {Promise<VolumePullResult[]>} Per-volume pull results
345
+ * @throws {DaytonaError} If the Sandbox has no matching mounted blockmount Volume
346
+ *
347
+ * @example
348
+ * // sandbox B picks up what sandbox A committed, while both keep running
349
+ * const results = await sandbox.pullVolumes();
350
+ * console.log(results); // [{ volumeId, upToDate, filesWritten, deleted, ... }]
351
+ */
352
+ async pullVolumes(volume) {
353
+ if (volume && volume.type !== api_client_2.VolumeType.BLOCKMOUNT) {
354
+ throw new DaytonaError_1.DaytonaValidationError(`Only blockmount volumes can be pulled. Volume '${volume.name}' is of type '${volume.type}'.`);
355
+ }
356
+ // Raw toolbox call (the daemon's generated client has no pull op — the runner intercepts
357
+ // this path), so replicate what the generated clients do: resolve against this sandbox's
358
+ // clientConfig.basePath (the shared axios instance's default baseURL is mutated by every
359
+ // Sandbox construction and may point at another sandbox) and inject the auth headers.
360
+ const response = await this.axiosInstance.post(`${this.clientConfig.basePath}/volumes/pull`, volume ? { volumeId: volume.id } : {}, { headers: this.clientConfig.baseOptions?.headers });
361
+ return response.data.results ?? [];
362
+ }
351
363
  /**
352
364
  * Creates a new Language Server Protocol (LSP) server instance.
353
365
  *
@@ -485,16 +497,9 @@ let Sandbox = (() => {
485
497
  timeout: timeout * 1000,
486
498
  });
487
499
  const sandboxDto = response.data;
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);
500
+ const forkedSandbox = new Sandbox(sandboxDto, structuredClone(this.clientConfig), Daytona_1.Daytona.createAxiosInstance(), this.sandboxApi);
495
501
  const timeElapsed = Date.now() - startTime;
496
- const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
497
- await forkedSandbox.waitUntilStarted(remainingTimeout);
502
+ await forkedSandbox.waitUntilStarted(timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout);
498
503
  return forkedSandbox;
499
504
  }
500
505
  /**
@@ -522,19 +527,34 @@ let Sandbox = (() => {
522
527
  }
523
528
  const startTime = Date.now();
524
529
  const req = { name };
525
- const response = await this.sandboxApi.createSandboxSnapshot(this.id, req, undefined, {
530
+ await this.sandboxApi.createSandboxSnapshot(this.id, req, undefined, {
526
531
  timeout: timeout * 1000,
527
532
  });
528
- this.processSandboxDto(response.data);
533
+ await this.refreshData();
529
534
  const timeElapsed = Date.now() - startTime;
530
535
  const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
531
536
  await this.waitForSnapshotComplete(remainingTimeout);
532
537
  }
533
538
  async waitForSnapshotComplete(timeout) {
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}`);
539
+ let checkInterval = 100;
540
+ const startTime = Date.now();
541
+ while (this.state === api_client_1.SandboxState.SNAPSHOTTING) {
542
+ await this.refreshData();
543
+ // @ts-expect-error this.refreshData() can modify this.state so this check is fine
544
+ if (this.state === api_client_1.SandboxState.ERROR || this.state === api_client_1.SandboxState.BUILD_FAILED) {
545
+ throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} snapshot failed with state: ${this.state}, error reason: ${this.errorReason}`);
546
+ }
547
+ if (this.state !== api_client_1.SandboxState.SNAPSHOTTING) {
548
+ return;
549
+ }
550
+ if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
551
+ throw new DaytonaError_1.DaytonaTimeoutError('Sandbox snapshot did not complete within the timeout period');
552
+ }
553
+ await new Promise((resolve) => setTimeout(resolve, checkInterval));
554
+ if (Date.now() - startTime > 5000) {
555
+ checkInterval = Math.min(checkInterval * 1.1, 1000);
556
+ }
557
+ }
538
558
  }
539
559
  /**
540
560
  * Pauses the Sandbox, freezing all running processes.
@@ -565,48 +585,32 @@ let Sandbox = (() => {
565
585
  await this.refreshData();
566
586
  const timeElapsed = Date.now() - startTime;
567
587
  const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
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}`);
588
+ await this.waitForPauseComplete(remainingTimeout);
574
589
  }
575
- /**
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.
586
- * @returns {Promise<void>}
587
- */
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
- }
590
+ async waitForPauseComplete(timeout) {
591
+ let checkInterval = 100;
592
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);
593
+ while (this.state === api_client_1.SandboxState.PAUSING) {
594
+ await this.refreshData();
595
+ // @ts-expect-error this.refreshData() can modify this.state so this check is fine
596
+ if (this.state === api_client_1.SandboxState.ERROR) {
597
+ throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} pause failed with state: ${this.state}, error reason: ${this.errorReason}`);
601
598
  }
602
- }
603
- finally {
604
- if (this.subId) {
605
- this.subscriptionManager.unsubscribe(this.subId);
606
- this.subId = undefined;
599
+ if (timeout > 0 && (Date.now() - startTime) / 1000 >= timeout) {
600
+ throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} failed to pause within ${timeout} seconds`);
607
601
  }
602
+ await new Promise((resolve) => globalThis.setTimeout(resolve, checkInterval));
603
+ checkInterval = Math.min(checkInterval * 1.5, 1000);
608
604
  }
609
605
  }
606
+ /**
607
+ * Deletes the Sandbox.
608
+ * @returns {Promise<void>}
609
+ */
610
+ async delete(timeout = 60) {
611
+ await this.sandboxApi.deleteSandbox(this.id, undefined, { timeout: timeout * 1000 });
612
+ this.refreshDataSafe();
613
+ }
610
614
  /**
611
615
  * Waits for the Sandbox to reach the 'started' state.
612
616
  *
@@ -622,10 +626,26 @@ let Sandbox = (() => {
622
626
  if (timeout < 0) {
623
627
  throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
624
628
  }
625
- if (this.state === api_client_1.SandboxState.STARTED) {
626
- return;
629
+ let checkInterval = 100;
630
+ const startTime = Date.now();
631
+ while (this.state !== 'started') {
632
+ await this.refreshData();
633
+ // @ts-expect-error this.refreshData() can modify this.state so this check is fine
634
+ if (this.state === 'started') {
635
+ return;
636
+ }
637
+ if (this.state === 'error') {
638
+ const errMsg = `Sandbox ${this.id} failed to start with status: ${this.state}, error reason: ${this.errorReason}`;
639
+ throw new DaytonaError_1.DaytonaError(errMsg);
640
+ }
641
+ if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
642
+ throw new DaytonaError_1.DaytonaTimeoutError('Sandbox failed to become ready within the timeout period');
643
+ }
644
+ await new Promise((resolve) => setTimeout(resolve, checkInterval));
645
+ if (Date.now() - startTime > 5000) {
646
+ checkInterval = Math.min(checkInterval * 1.1, 1000);
647
+ }
627
648
  }
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}`);
629
649
  }
630
650
  /**
631
651
  * Wait for Sandbox to reach 'stopped' state.
@@ -642,11 +662,27 @@ let Sandbox = (() => {
642
662
  if (timeout < 0) {
643
663
  throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
644
664
  }
665
+ let checkInterval = 100;
666
+ const startTime = Date.now();
645
667
  // Treat destroyed as stopped to cover ephemeral sandboxes that are automatically deleted after stopping
646
- if (this.state === api_client_1.SandboxState.STOPPED || this.state === api_client_1.SandboxState.DESTROYED) {
647
- return;
668
+ while (this.state !== 'stopped' && this.state !== 'destroyed') {
669
+ this.refreshDataSafe();
670
+ // @ts-expect-error this.refreshData() can modify this.state so this check is fine
671
+ if (this.state === 'stopped' || this.state === 'destroyed') {
672
+ return;
673
+ }
674
+ if (this.state === 'error') {
675
+ const errMsg = `Sandbox failed to stop with status: ${this.state}, error reason: ${this.errorReason}`;
676
+ throw new DaytonaError_1.DaytonaError(errMsg);
677
+ }
678
+ if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
679
+ throw new DaytonaError_1.DaytonaTimeoutError('Sandbox failed to become stopped within the timeout period');
680
+ }
681
+ await new Promise((resolve) => setTimeout(resolve, checkInterval));
682
+ if (Date.now() - startTime > 5000) {
683
+ checkInterval = Math.min(checkInterval * 1.1, 1000);
684
+ }
648
685
  }
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);
650
686
  }
651
687
  /**
652
688
  * Refreshes the Sandbox data from the API.
@@ -703,60 +739,6 @@ let Sandbox = (() => {
703
739
  await this.sandboxApi.setAutostopInterval(this.id, interval);
704
740
  this.autoStopInterval = interval;
705
741
  }
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
- }
760
742
  /**
761
743
  * Set the auto-archive interval for the Sandbox.
762
744
  *
@@ -979,13 +961,25 @@ let Sandbox = (() => {
979
961
  if (timeout < 0) {
980
962
  throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
981
963
  }
982
- if (this.state !== api_client_1.SandboxState.RESIZING) {
983
- return;
964
+ let checkInterval = 100;
965
+ const startTime = Date.now();
966
+ while (this.state === api_client_1.SandboxState.RESIZING) {
967
+ await this.refreshData();
968
+ // @ts-expect-error this.refreshData() can modify this.state so this check is fine
969
+ if (this.state === api_client_1.SandboxState.ERROR || this.state === api_client_1.SandboxState.BUILD_FAILED) {
970
+ throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} resize failed with state: ${this.state}, error reason: ${this.errorReason}`);
971
+ }
972
+ if (this.state !== api_client_1.SandboxState.RESIZING) {
973
+ return;
974
+ }
975
+ if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
976
+ throw new DaytonaError_1.DaytonaTimeoutError('Sandbox resize did not complete within the timeout period');
977
+ }
978
+ await new Promise((resolve) => setTimeout(resolve, checkInterval));
979
+ if (Date.now() - startTime > 5000) {
980
+ checkInterval = Math.min(checkInterval * 1.1, 1000);
981
+ }
984
982
  }
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}`);
989
983
  }
990
984
  /**
991
985
  * Creates an SSH access token for the sandbox.
@@ -1014,172 +1008,6 @@ let Sandbox = (() => {
1014
1008
  async validateSshAccess(token) {
1015
1009
  return (await this.sandboxApi.validateSshAccess(token)).data;
1016
1010
  }
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
- }
1183
1011
  /**
1184
1012
  * Assigns the API sandbox data to the Sandbox object.
1185
1013
  *
@@ -1187,7 +1015,6 @@ let Sandbox = (() => {
1187
1015
  * @returns {void}
1188
1016
  */
1189
1017
  processSandboxDto(sandboxDto) {
1190
- const newState = sandboxDto.state;
1191
1018
  this.id = sandboxDto.id;
1192
1019
  this.name = sandboxDto.name;
1193
1020
  this.organizationId = sandboxDto.organizationId;
@@ -1200,17 +1027,17 @@ let Sandbox = (() => {
1200
1027
  this.gpu = sandboxDto.gpu;
1201
1028
  this.memory = sandboxDto.memory;
1202
1029
  this.disk = sandboxDto.disk;
1030
+ this.state = sandboxDto.state;
1203
1031
  this.errorReason = sandboxDto.errorReason;
1204
1032
  this.recoverable = sandboxDto.recoverable;
1205
1033
  this.backupState = sandboxDto.backupState;
1206
1034
  this.autoStopInterval = sandboxDto.autoStopInterval;
1207
- this.autoPauseInterval = sandboxDto.autoPauseInterval;
1208
1035
  this.autoArchiveInterval = sandboxDto.autoArchiveInterval;
1209
1036
  this.autoDeleteInterval = sandboxDto.autoDeleteInterval;
1210
- this.autoDestroyAt = sandboxDto.autoDestroyAt;
1211
1037
  this.createdAt = sandboxDto.createdAt;
1212
1038
  this.updatedAt = sandboxDto.updatedAt;
1213
1039
  this.lastActivityAt = sandboxDto.lastActivityAt;
1040
+ this.toolboxProxyUrl = sandboxDto.toolboxProxyUrl;
1214
1041
  // Fields only present in the full SandboxDto (not returned by list endpoint)
1215
1042
  if ('env' in sandboxDto) {
1216
1043
  this.env = sandboxDto.env;
@@ -1222,21 +1049,6 @@ let Sandbox = (() => {
1222
1049
  this.buildInfo = sandboxDto.buildInfo;
1223
1050
  this.backupCreatedAt = sandboxDto.backupCreatedAt;
1224
1051
  }
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
- }
1240
1052
  }
1241
1053
  /**
1242
1054
  * Refreshes the Sandbox data from the API, but does not throw an error if the sandbox has been deleted.
@@ -1244,113 +1056,17 @@ let Sandbox = (() => {
1244
1056
  *
1245
1057
  * @returns {Promise<void>}
1246
1058
  */
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
- */
1252
1059
  async refreshDataSafe() {
1253
1060
  try {
1254
1061
  await this.refreshData();
1255
- return true;
1256
1062
  }
1257
1063
  catch (error) {
1258
1064
  if (error instanceof DaytonaError_1.DaytonaNotFoundError) {
1259
- this.applyState(api_client_1.SandboxState.DESTROYED);
1260
- return true;
1065
+ this.state = api_client_1.SandboxState.DESTROYED;
1261
1066
  }
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);
1274
1067
  }
1275
1068
  }
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
- }
1298
1069
  };
1299
1070
  })();
1300
1071
  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
- }
1356
1072
  //# sourceMappingURL=Sandbox.js.map