@daytona/sdk 0.197.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.
- package/cjs/Daytona.d.ts +17 -1
- package/cjs/Daytona.js +36 -7
- package/cjs/Daytona.js.map +1 -1
- package/cjs/Sandbox.d.ts +71 -4
- package/cjs/Sandbox.js +374 -136
- package/cjs/Sandbox.js.map +1 -1
- package/cjs/utils/EventDispatcher.d.ts +85 -0
- package/cjs/utils/EventDispatcher.js +398 -0
- package/cjs/utils/EventDispatcher.js.map +1 -0
- package/cjs/utils/EventSubscriptionManager.d.ts +14 -0
- package/cjs/utils/EventSubscriptionManager.js +98 -0
- package/cjs/utils/EventSubscriptionManager.js.map +1 -0
- package/esm/Daytona.d.ts +17 -1
- package/esm/Daytona.js +36 -7
- package/esm/Daytona.js.map +1 -1
- package/esm/Sandbox.d.ts +71 -4
- package/esm/Sandbox.js +374 -136
- package/esm/Sandbox.js.map +1 -1
- package/esm/utils/EventDispatcher.d.ts +85 -0
- package/esm/utils/EventDispatcher.js +394 -0
- package/esm/utils/EventDispatcher.js.map +1 -0
- package/esm/utils/EventSubscriptionManager.d.ts +14 -0
- package/esm/utils/EventSubscriptionManager.js +94 -0
- package/esm/utils/EventSubscriptionManager.js.map +1 -0
- package/esm/utils/Import.js +1 -1
- package/package.json +5 -4
package/cjs/Sandbox.js
CHANGED
|
@@ -19,6 +19,13 @@ const Daytona_2 = require("./Daytona");
|
|
|
19
19
|
const ComputerUse_1 = require("./ComputerUse");
|
|
20
20
|
const CodeInterpreter_1 = require("./CodeInterpreter");
|
|
21
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
|
+
}
|
|
22
29
|
/**
|
|
23
30
|
* Represents a Daytona Sandbox.
|
|
24
31
|
*
|
|
@@ -51,6 +58,7 @@ const otel_decorator_1 = require("./utils/otel.decorator");
|
|
|
51
58
|
* @property {number} [autoPauseInterval] - Auto-pause interval in minutes
|
|
52
59
|
* @property {number} [autoArchiveInterval] - Auto-archive interval in minutes
|
|
53
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)
|
|
54
62
|
* @property {Array<SandboxVolume>} [volumes] - Volumes attached to the Sandbox (not returned by
|
|
55
63
|
* list results; call `refreshData()` on each item to populate)
|
|
56
64
|
* @property {BuildInfo} [buildInfo] - Build information for the Sandbox if it was created from dynamic build
|
|
@@ -79,6 +87,7 @@ let Sandbox = (() => {
|
|
|
79
87
|
let _createLspServer_decorators;
|
|
80
88
|
let _setLabels_decorators;
|
|
81
89
|
let _start_decorators;
|
|
90
|
+
let _recover_decorators;
|
|
82
91
|
let _stop_decorators;
|
|
83
92
|
let __experimental_fork_decorators;
|
|
84
93
|
let __experimental_createSnapshot_decorators;
|
|
@@ -87,14 +96,18 @@ let Sandbox = (() => {
|
|
|
87
96
|
let _waitUntilStarted_decorators;
|
|
88
97
|
let _waitUntilStopped_decorators;
|
|
89
98
|
let _refreshData_decorators;
|
|
99
|
+
let _refreshActivity_decorators;
|
|
90
100
|
let _setAutostopInterval_decorators;
|
|
91
101
|
let _setAutoPauseInterval_decorators;
|
|
102
|
+
let _setTtl_decorators;
|
|
92
103
|
let _setAutoArchiveInterval_decorators;
|
|
93
104
|
let _setAutoDeleteInterval_decorators;
|
|
94
105
|
let _updateNetworkSettings_decorators;
|
|
95
106
|
let _updateSecrets_decorators;
|
|
96
107
|
let _updateEnv_decorators;
|
|
97
108
|
let _getPreviewLink_decorators;
|
|
109
|
+
let _getSignedPreviewUrl_decorators;
|
|
110
|
+
let _expireSignedPreviewUrl_decorators;
|
|
98
111
|
let _archive_decorators;
|
|
99
112
|
let _resize_decorators;
|
|
100
113
|
let _waitForResizeComplete_decorators;
|
|
@@ -104,36 +117,41 @@ let Sandbox = (() => {
|
|
|
104
117
|
return class Sandbox {
|
|
105
118
|
static {
|
|
106
119
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
107
|
-
_getUserHomeDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
120
|
+
_getUserHomeDir_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
108
121
|
_getMetricsLatest_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
109
122
|
_getMetrics_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
110
|
-
_getUserRootDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
111
|
-
_getWorkDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
112
|
-
_createLspServer_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
113
|
-
_setLabels_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
114
|
-
_start_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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];
|
|
124
139
|
_setAutoPauseInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
125
|
-
|
|
126
|
-
|
|
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];
|
|
127
143
|
_updateNetworkSettings_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
128
144
|
_updateSecrets_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
129
145
|
_updateEnv_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
130
|
-
_getPreviewLink_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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];
|
|
137
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);
|
|
138
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);
|
|
139
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);
|
|
@@ -142,6 +160,7 @@ let Sandbox = (() => {
|
|
|
142
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);
|
|
143
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);
|
|
144
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);
|
|
145
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);
|
|
146
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);
|
|
147
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);
|
|
@@ -150,14 +169,18 @@ let Sandbox = (() => {
|
|
|
150
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);
|
|
151
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);
|
|
152
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);
|
|
153
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);
|
|
154
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);
|
|
155
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);
|
|
156
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);
|
|
157
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);
|
|
158
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);
|
|
159
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);
|
|
160
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);
|
|
161
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);
|
|
162
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);
|
|
163
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);
|
|
@@ -170,6 +193,7 @@ let Sandbox = (() => {
|
|
|
170
193
|
axiosInstance;
|
|
171
194
|
sandboxApi;
|
|
172
195
|
getAnalyticsApiUrl;
|
|
196
|
+
subscriptionManager;
|
|
173
197
|
fs;
|
|
174
198
|
git;
|
|
175
199
|
process;
|
|
@@ -197,6 +221,7 @@ let Sandbox = (() => {
|
|
|
197
221
|
autoPauseInterval;
|
|
198
222
|
autoArchiveInterval;
|
|
199
223
|
autoDeleteInterval;
|
|
224
|
+
autoDestroyAt;
|
|
200
225
|
volumes;
|
|
201
226
|
buildInfo;
|
|
202
227
|
createdAt;
|
|
@@ -210,25 +235,27 @@ let Sandbox = (() => {
|
|
|
210
235
|
infoApi;
|
|
211
236
|
serverApi;
|
|
212
237
|
systemApi;
|
|
238
|
+
stateWaiters = [];
|
|
239
|
+
stateWaiterErrorMessageFns = new WeakMap();
|
|
240
|
+
subId;
|
|
213
241
|
/**
|
|
214
|
-
* 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.
|
|
215
246
|
*
|
|
216
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
|
|
217
251
|
*/
|
|
218
|
-
constructor(sandboxDto, clientConfig, axiosInstance, sandboxApi, getAnalyticsApiUrl) {
|
|
252
|
+
constructor(sandboxDto, clientConfig, axiosInstance, sandboxApi, getAnalyticsApiUrl, subscriptionManager) {
|
|
219
253
|
this.clientConfig = clientConfig;
|
|
220
254
|
this.axiosInstance = axiosInstance;
|
|
221
255
|
this.sandboxApi = sandboxApi;
|
|
222
256
|
this.getAnalyticsApiUrl = getAnalyticsApiUrl;
|
|
257
|
+
this.subscriptionManager = subscriptionManager;
|
|
223
258
|
this.processSandboxDto(sandboxDto);
|
|
224
|
-
// Set the toolbox base URL
|
|
225
|
-
let baseUrl = this.toolboxProxyUrl;
|
|
226
|
-
if (!baseUrl.endsWith('/')) {
|
|
227
|
-
baseUrl += '/';
|
|
228
|
-
}
|
|
229
|
-
this.axiosInstance.defaults.baseURL = baseUrl + this.id;
|
|
230
|
-
this.clientConfig.basePath = this.axiosInstance.defaults.baseURL;
|
|
231
|
-
// Initialize Services
|
|
232
259
|
const getPreviewToken = async () => (await this.getPreviewLink(1)).token;
|
|
233
260
|
this.fs = new FileSystem_1.FileSystem(this.clientConfig, new toolbox_api_client_1.FileSystemApi(this.clientConfig, '', this.axiosInstance));
|
|
234
261
|
this.git = new Git_1.Git(new toolbox_api_client_1.GitApi(this.clientConfig, '', this.axiosInstance));
|
|
@@ -239,6 +266,7 @@ let Sandbox = (() => {
|
|
|
239
266
|
this.infoApi = new toolbox_api_client_1.InfoApi(this.clientConfig, '', this.axiosInstance);
|
|
240
267
|
this.serverApi = new toolbox_api_client_1.ServerApi(this.clientConfig, '', this.axiosInstance);
|
|
241
268
|
this.systemApi = new toolbox_api_client_1.SystemApi(this.clientConfig, '', this.axiosInstance);
|
|
269
|
+
this.subscribeToEvents();
|
|
242
270
|
}
|
|
243
271
|
/**
|
|
244
272
|
* Gets the user's home directory path for the logged in user inside the Sandbox.
|
|
@@ -457,9 +485,16 @@ let Sandbox = (() => {
|
|
|
457
485
|
timeout: timeout * 1000,
|
|
458
486
|
});
|
|
459
487
|
const sandboxDto = response.data;
|
|
460
|
-
const
|
|
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);
|
|
461
495
|
const timeElapsed = Date.now() - startTime;
|
|
462
|
-
|
|
496
|
+
const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
|
|
497
|
+
await forkedSandbox.waitUntilStarted(remainingTimeout);
|
|
463
498
|
return forkedSandbox;
|
|
464
499
|
}
|
|
465
500
|
/**
|
|
@@ -487,34 +522,19 @@ let Sandbox = (() => {
|
|
|
487
522
|
}
|
|
488
523
|
const startTime = Date.now();
|
|
489
524
|
const req = { name };
|
|
490
|
-
await this.sandboxApi.createSandboxSnapshot(this.id, req, undefined, {
|
|
525
|
+
const response = await this.sandboxApi.createSandboxSnapshot(this.id, req, undefined, {
|
|
491
526
|
timeout: timeout * 1000,
|
|
492
527
|
});
|
|
493
|
-
|
|
528
|
+
this.processSandboxDto(response.data);
|
|
494
529
|
const timeElapsed = Date.now() - startTime;
|
|
495
530
|
const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
|
|
496
531
|
await this.waitForSnapshotComplete(remainingTimeout);
|
|
497
532
|
}
|
|
498
533
|
async waitForSnapshotComplete(timeout) {
|
|
499
|
-
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
504
|
-
if (this.state === api_client_1.SandboxState.ERROR || this.state === api_client_1.SandboxState.BUILD_FAILED) {
|
|
505
|
-
throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} snapshot failed with state: ${this.state}, error reason: ${this.errorReason}`);
|
|
506
|
-
}
|
|
507
|
-
if (this.state !== api_client_1.SandboxState.SNAPSHOTTING) {
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
|
|
511
|
-
throw new DaytonaError_1.DaytonaTimeoutError('Sandbox snapshot did not complete within the timeout period');
|
|
512
|
-
}
|
|
513
|
-
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
514
|
-
if (Date.now() - startTime > 5000) {
|
|
515
|
-
checkInterval = Math.min(checkInterval * 1.1, 1000);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
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}`);
|
|
518
538
|
}
|
|
519
539
|
/**
|
|
520
540
|
* Pauses the Sandbox, freezing all running processes.
|
|
@@ -545,31 +565,47 @@ let Sandbox = (() => {
|
|
|
545
565
|
await this.refreshData();
|
|
546
566
|
const timeElapsed = Date.now() - startTime;
|
|
547
567
|
const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
await this.refreshData();
|
|
555
|
-
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
556
|
-
if (this.state === api_client_1.SandboxState.ERROR) {
|
|
557
|
-
throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} pause failed with state: ${this.state}, error reason: ${this.errorReason}`);
|
|
558
|
-
}
|
|
559
|
-
if (timeout > 0 && (Date.now() - startTime) / 1000 >= timeout) {
|
|
560
|
-
throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} failed to pause within ${timeout} seconds`);
|
|
561
|
-
}
|
|
562
|
-
await new Promise((resolve) => globalThis.setTimeout(resolve, checkInterval));
|
|
563
|
-
checkInterval = Math.min(checkInterval * 1.5, 1000);
|
|
564
|
-
}
|
|
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}`);
|
|
565
574
|
}
|
|
566
575
|
/**
|
|
567
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.
|
|
568
586
|
* @returns {Promise<void>}
|
|
569
587
|
*/
|
|
570
|
-
async delete(timeout = 60) {
|
|
571
|
-
|
|
572
|
-
|
|
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
|
+
}
|
|
573
609
|
}
|
|
574
610
|
/**
|
|
575
611
|
* Waits for the Sandbox to reach the 'started' state.
|
|
@@ -586,26 +622,10 @@ let Sandbox = (() => {
|
|
|
586
622
|
if (timeout < 0) {
|
|
587
623
|
throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
|
|
588
624
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
while (this.state !== 'started') {
|
|
592
|
-
await this.refreshData();
|
|
593
|
-
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
594
|
-
if (this.state === 'started') {
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
if (this.state === 'error') {
|
|
598
|
-
const errMsg = `Sandbox ${this.id} failed to start with status: ${this.state}, error reason: ${this.errorReason}`;
|
|
599
|
-
throw new DaytonaError_1.DaytonaError(errMsg);
|
|
600
|
-
}
|
|
601
|
-
if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
|
|
602
|
-
throw new DaytonaError_1.DaytonaTimeoutError('Sandbox failed to become ready within the timeout period');
|
|
603
|
-
}
|
|
604
|
-
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
605
|
-
if (Date.now() - startTime > 5000) {
|
|
606
|
-
checkInterval = Math.min(checkInterval * 1.1, 1000);
|
|
607
|
-
}
|
|
625
|
+
if (this.state === api_client_1.SandboxState.STARTED) {
|
|
626
|
+
return;
|
|
608
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}`);
|
|
609
629
|
}
|
|
610
630
|
/**
|
|
611
631
|
* Wait for Sandbox to reach 'stopped' state.
|
|
@@ -622,27 +642,11 @@ let Sandbox = (() => {
|
|
|
622
642
|
if (timeout < 0) {
|
|
623
643
|
throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
|
|
624
644
|
}
|
|
625
|
-
let checkInterval = 100;
|
|
626
|
-
const startTime = Date.now();
|
|
627
645
|
// Treat destroyed as stopped to cover ephemeral sandboxes that are automatically deleted after stopping
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
631
|
-
if (this.state === 'stopped' || this.state === 'destroyed') {
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
if (this.state === 'error') {
|
|
635
|
-
const errMsg = `Sandbox failed to stop with status: ${this.state}, error reason: ${this.errorReason}`;
|
|
636
|
-
throw new DaytonaError_1.DaytonaError(errMsg);
|
|
637
|
-
}
|
|
638
|
-
if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
|
|
639
|
-
throw new DaytonaError_1.DaytonaTimeoutError('Sandbox failed to become stopped within the timeout period');
|
|
640
|
-
}
|
|
641
|
-
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
642
|
-
if (Date.now() - startTime > 5000) {
|
|
643
|
-
checkInterval = Math.min(checkInterval * 1.1, 1000);
|
|
644
|
-
}
|
|
646
|
+
if (this.state === api_client_1.SandboxState.STOPPED || this.state === api_client_1.SandboxState.DESTROYED) {
|
|
647
|
+
return;
|
|
645
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);
|
|
646
650
|
}
|
|
647
651
|
/**
|
|
648
652
|
* Refreshes the Sandbox data from the API.
|
|
@@ -729,6 +733,30 @@ let Sandbox = (() => {
|
|
|
729
733
|
await this.sandboxApi.setAutoPauseInterval(this.id, interval);
|
|
730
734
|
this.autoPauseInterval = interval;
|
|
731
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
|
+
}
|
|
732
760
|
/**
|
|
733
761
|
* Set the auto-archive interval for the Sandbox.
|
|
734
762
|
*
|
|
@@ -951,25 +979,13 @@ let Sandbox = (() => {
|
|
|
951
979
|
if (timeout < 0) {
|
|
952
980
|
throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
|
|
953
981
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
while (this.state === api_client_1.SandboxState.RESIZING) {
|
|
957
|
-
await this.refreshData();
|
|
958
|
-
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
959
|
-
if (this.state === api_client_1.SandboxState.ERROR || this.state === api_client_1.SandboxState.BUILD_FAILED) {
|
|
960
|
-
throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} resize failed with state: ${this.state}, error reason: ${this.errorReason}`);
|
|
961
|
-
}
|
|
962
|
-
if (this.state !== api_client_1.SandboxState.RESIZING) {
|
|
963
|
-
return;
|
|
964
|
-
}
|
|
965
|
-
if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
|
|
966
|
-
throw new DaytonaError_1.DaytonaTimeoutError('Sandbox resize did not complete within the timeout period');
|
|
967
|
-
}
|
|
968
|
-
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
969
|
-
if (Date.now() - startTime > 5000) {
|
|
970
|
-
checkInterval = Math.min(checkInterval * 1.1, 1000);
|
|
971
|
-
}
|
|
982
|
+
if (this.state !== api_client_1.SandboxState.RESIZING) {
|
|
983
|
+
return;
|
|
972
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}`);
|
|
973
989
|
}
|
|
974
990
|
/**
|
|
975
991
|
* Creates an SSH access token for the sandbox.
|
|
@@ -998,6 +1014,172 @@ let Sandbox = (() => {
|
|
|
998
1014
|
async validateSshAccess(token) {
|
|
999
1015
|
return (await this.sandboxApi.validateSshAccess(token)).data;
|
|
1000
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
|
+
}
|
|
1001
1183
|
/**
|
|
1002
1184
|
* Assigns the API sandbox data to the Sandbox object.
|
|
1003
1185
|
*
|
|
@@ -1005,6 +1187,7 @@ let Sandbox = (() => {
|
|
|
1005
1187
|
* @returns {void}
|
|
1006
1188
|
*/
|
|
1007
1189
|
processSandboxDto(sandboxDto) {
|
|
1190
|
+
const newState = sandboxDto.state;
|
|
1008
1191
|
this.id = sandboxDto.id;
|
|
1009
1192
|
this.name = sandboxDto.name;
|
|
1010
1193
|
this.organizationId = sandboxDto.organizationId;
|
|
@@ -1017,7 +1200,6 @@ let Sandbox = (() => {
|
|
|
1017
1200
|
this.gpu = sandboxDto.gpu;
|
|
1018
1201
|
this.memory = sandboxDto.memory;
|
|
1019
1202
|
this.disk = sandboxDto.disk;
|
|
1020
|
-
this.state = sandboxDto.state;
|
|
1021
1203
|
this.errorReason = sandboxDto.errorReason;
|
|
1022
1204
|
this.recoverable = sandboxDto.recoverable;
|
|
1023
1205
|
this.backupState = sandboxDto.backupState;
|
|
@@ -1025,10 +1207,10 @@ let Sandbox = (() => {
|
|
|
1025
1207
|
this.autoPauseInterval = sandboxDto.autoPauseInterval;
|
|
1026
1208
|
this.autoArchiveInterval = sandboxDto.autoArchiveInterval;
|
|
1027
1209
|
this.autoDeleteInterval = sandboxDto.autoDeleteInterval;
|
|
1210
|
+
this.autoDestroyAt = sandboxDto.autoDestroyAt;
|
|
1028
1211
|
this.createdAt = sandboxDto.createdAt;
|
|
1029
1212
|
this.updatedAt = sandboxDto.updatedAt;
|
|
1030
1213
|
this.lastActivityAt = sandboxDto.lastActivityAt;
|
|
1031
|
-
this.toolboxProxyUrl = sandboxDto.toolboxProxyUrl;
|
|
1032
1214
|
// Fields only present in the full SandboxDto (not returned by list endpoint)
|
|
1033
1215
|
if ('env' in sandboxDto) {
|
|
1034
1216
|
this.env = sandboxDto.env;
|
|
@@ -1040,6 +1222,21 @@ let Sandbox = (() => {
|
|
|
1040
1222
|
this.buildInfo = sandboxDto.buildInfo;
|
|
1041
1223
|
this.backupCreatedAt = sandboxDto.backupCreatedAt;
|
|
1042
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
|
+
}
|
|
1043
1240
|
}
|
|
1044
1241
|
/**
|
|
1045
1242
|
* Refreshes the Sandbox data from the API, but does not throw an error if the sandbox has been deleted.
|
|
@@ -1047,15 +1244,56 @@ let Sandbox = (() => {
|
|
|
1047
1244
|
*
|
|
1048
1245
|
* @returns {Promise<void>}
|
|
1049
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
|
+
*/
|
|
1050
1252
|
async refreshDataSafe() {
|
|
1051
1253
|
try {
|
|
1052
1254
|
await this.refreshData();
|
|
1255
|
+
return true;
|
|
1053
1256
|
}
|
|
1054
1257
|
catch (error) {
|
|
1055
1258
|
if (error instanceof DaytonaError_1.DaytonaNotFoundError) {
|
|
1056
|
-
this.
|
|
1259
|
+
this.applyState(api_client_1.SandboxState.DESTROYED);
|
|
1260
|
+
return true;
|
|
1057
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);
|
|
1274
|
+
}
|
|
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);
|
|
1058
1295
|
}
|
|
1296
|
+
this.stateWaiterErrorMessageFns.delete(waiter);
|
|
1059
1297
|
}
|
|
1060
1298
|
};
|
|
1061
1299
|
})();
|