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