@daytona/sdk 0.199.0-alpha.1 → 0.200.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 +27 -2
- package/cjs/Daytona.js +64 -7
- package/cjs/Daytona.js.map +1 -1
- package/cjs/Sandbox.d.ts +149 -48
- package/cjs/Sandbox.js +521 -237
- package/cjs/Sandbox.js.map +1 -1
- package/cjs/Volume.d.ts +4 -91
- package/cjs/Volume.js +5 -78
- package/cjs/Volume.js.map +1 -1
- package/cjs/index.d.ts +1 -3
- package/cjs/index.js +1 -3
- package/cjs/index.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 +27 -2
- package/esm/Daytona.js +64 -7
- package/esm/Daytona.js.map +1 -1
- package/esm/Sandbox.d.ts +149 -48
- package/esm/Sandbox.js +522 -238
- package/esm/Sandbox.js.map +1 -1
- package/esm/Volume.d.ts +4 -91
- package/esm/Volume.js +4 -77
- package/esm/Volume.js.map +1 -1
- package/esm/index.d.ts +1 -3
- package/esm/index.js +0 -1
- package/esm/index.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 -3
package/cjs/Sandbox.js
CHANGED
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
exports.Sandbox = void 0;
|
|
8
8
|
const tslib_1 = require("tslib");
|
|
9
9
|
const api_client_1 = require("@daytona/api-client");
|
|
10
|
+
const analytics_api_client_1 = require("@daytona/analytics-api-client");
|
|
10
11
|
const Daytona_1 = require("./Daytona");
|
|
11
12
|
const toolbox_api_client_1 = require("@daytona/toolbox-api-client");
|
|
12
13
|
const FileSystem_1 = require("./FileSystem");
|
|
@@ -18,35 +19,12 @@ const Daytona_2 = require("./Daytona");
|
|
|
18
19
|
const ComputerUse_1 = require("./ComputerUse");
|
|
19
20
|
const CodeInterpreter_1 = require("./CodeInterpreter");
|
|
20
21
|
const otel_decorator_1 = require("./utils/otel.decorator");
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*/
|
|
27
|
-
function buildHotmountMountCommand(mountToken, mountPath) {
|
|
28
|
-
const envVars = {
|
|
29
|
-
SEAWEED_TOKEN: mountToken.token,
|
|
30
|
-
SEAWEED_GATEWAY_GRPC: mountToken.gatewayGrpc,
|
|
31
|
-
SEAWEED_GATEWAY_HTTP: mountToken.gatewayHttp,
|
|
32
|
-
SEAWEED_BINARIES_URL: mountToken.binariesUrl,
|
|
33
|
-
SEAWEED_MOUNT_DIR: mountPath,
|
|
34
|
-
SEAWEED_VERSION: mountToken.version,
|
|
22
|
+
function withEvents(target, _context) {
|
|
23
|
+
return function (...args) {
|
|
24
|
+
;
|
|
25
|
+
this.ensureSubscribed();
|
|
26
|
+
return target.apply(this, args);
|
|
35
27
|
};
|
|
36
|
-
const envAssignments = Object.entries(envVars)
|
|
37
|
-
.filter(([, value]) => value !== undefined && value !== '')
|
|
38
|
-
.map(([key, value]) => `${key}='${value}'`)
|
|
39
|
-
.join(' ');
|
|
40
|
-
// The bootstrap (and init.sh itself) requires curl. Fail loudly if it is missing or the
|
|
41
|
-
// download fails, rather than letting a broken `curl … | bash` pipe exit 0 and mount nothing.
|
|
42
|
-
const inner = [
|
|
43
|
-
'set -e',
|
|
44
|
-
'if ! command -v curl >/dev/null 2>&1; then echo "hotmount: curl is required to bootstrap the agent but was not found in the sandbox" >&2; exit 1; fi',
|
|
45
|
-
'mkdir -p "$SEAWEED_MOUNT_DIR"',
|
|
46
|
-
'init_script="$(curl -fsSL "$SEAWEED_BINARIES_URL/init.sh")"',
|
|
47
|
-
'printf %s "$init_script" | bash',
|
|
48
|
-
].join('; ');
|
|
49
|
-
return (`if [ "$(id -u)" != 0 ]; then SUDO="sudo -n"; else SUDO=""; fi; ` + `$SUDO env ${envAssignments} bash -c '${inner}'`);
|
|
50
28
|
}
|
|
51
29
|
/**
|
|
52
30
|
* Represents a Daytona Sandbox.
|
|
@@ -77,8 +55,10 @@ function buildHotmountMountCommand(mountToken, mountPath) {
|
|
|
77
55
|
* @property {string} [backupCreatedAt] - When the backup was created (not returned by list results;
|
|
78
56
|
* call `refreshData()` on each item to populate)
|
|
79
57
|
* @property {number} [autoStopInterval] - Auto-stop interval in minutes
|
|
58
|
+
* @property {number} [autoPauseInterval] - Auto-pause interval in minutes
|
|
80
59
|
* @property {number} [autoArchiveInterval] - Auto-archive interval in minutes
|
|
81
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)
|
|
82
62
|
* @property {Array<SandboxVolume>} [volumes] - Volumes attached to the Sandbox (not returned by
|
|
83
63
|
* list results; call `refreshData()` on each item to populate)
|
|
84
64
|
* @property {BuildInfo} [buildInfo] - Build information for the Sandbox if it was created from dynamic build
|
|
@@ -100,13 +80,14 @@ function buildHotmountMountCommand(mountToken, mountPath) {
|
|
|
100
80
|
let Sandbox = (() => {
|
|
101
81
|
let _instanceExtraInitializers = [];
|
|
102
82
|
let _getUserHomeDir_decorators;
|
|
83
|
+
let _getMetricsLatest_decorators;
|
|
84
|
+
let _getMetrics_decorators;
|
|
103
85
|
let _getUserRootDir_decorators;
|
|
104
86
|
let _getWorkDir_decorators;
|
|
105
|
-
let _mountVolume_decorators;
|
|
106
|
-
let _pullVolumes_decorators;
|
|
107
87
|
let _createLspServer_decorators;
|
|
108
88
|
let _setLabels_decorators;
|
|
109
89
|
let _start_decorators;
|
|
90
|
+
let _recover_decorators;
|
|
110
91
|
let _stop_decorators;
|
|
111
92
|
let __experimental_fork_decorators;
|
|
112
93
|
let __experimental_createSnapshot_decorators;
|
|
@@ -115,13 +96,18 @@ let Sandbox = (() => {
|
|
|
115
96
|
let _waitUntilStarted_decorators;
|
|
116
97
|
let _waitUntilStopped_decorators;
|
|
117
98
|
let _refreshData_decorators;
|
|
99
|
+
let _refreshActivity_decorators;
|
|
118
100
|
let _setAutostopInterval_decorators;
|
|
101
|
+
let _setAutoPauseInterval_decorators;
|
|
102
|
+
let _setTtl_decorators;
|
|
119
103
|
let _setAutoArchiveInterval_decorators;
|
|
120
104
|
let _setAutoDeleteInterval_decorators;
|
|
121
105
|
let _updateNetworkSettings_decorators;
|
|
122
106
|
let _updateSecrets_decorators;
|
|
123
107
|
let _updateEnv_decorators;
|
|
124
108
|
let _getPreviewLink_decorators;
|
|
109
|
+
let _getSignedPreviewUrl_decorators;
|
|
110
|
+
let _expireSignedPreviewUrl_decorators;
|
|
125
111
|
let _archive_decorators;
|
|
126
112
|
let _resize_decorators;
|
|
127
113
|
let _waitForResizeComplete_decorators;
|
|
@@ -131,43 +117,50 @@ let Sandbox = (() => {
|
|
|
131
117
|
return class Sandbox {
|
|
132
118
|
static {
|
|
133
119
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
134
|
-
_getUserHomeDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
_createLspServer_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
140
|
-
_setLabels_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
141
|
-
_start_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
120
|
+
_getUserHomeDir_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
121
|
+
_getMetricsLatest_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
122
|
+
_getMetrics_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
123
|
+
_getUserRootDir_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
124
|
+
_getWorkDir_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
125
|
+
_createLspServer_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
126
|
+
_setLabels_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
127
|
+
_start_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
128
|
+
_recover_decorators = [withEvents];
|
|
129
|
+
_stop_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
130
|
+
__experimental_fork_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
131
|
+
__experimental_createSnapshot_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
132
|
+
_pause_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
133
|
+
_delete_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
134
|
+
_waitUntilStarted_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
135
|
+
_waitUntilStopped_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
136
|
+
_refreshData_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
137
|
+
_refreshActivity_decorators = [withEvents];
|
|
138
|
+
_setAutostopInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
139
|
+
_setAutoPauseInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
140
|
+
_setTtl_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
141
|
+
_setAutoArchiveInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
142
|
+
_setAutoDeleteInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
153
143
|
_updateNetworkSettings_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
154
144
|
_updateSecrets_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
155
145
|
_updateEnv_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
156
|
-
_getPreviewLink_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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];
|
|
163
155
|
tslib_1.__esDecorate(this, null, _getUserHomeDir_decorators, { kind: "method", name: "getUserHomeDir", static: false, private: false, access: { has: obj => "getUserHomeDir" in obj, get: obj => obj.getUserHomeDir }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
156
|
+
tslib_1.__esDecorate(this, null, _getMetricsLatest_decorators, { kind: "method", name: "getMetricsLatest", static: false, private: false, access: { has: obj => "getMetricsLatest" in obj, get: obj => obj.getMetricsLatest }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
157
|
+
tslib_1.__esDecorate(this, null, _getMetrics_decorators, { kind: "method", name: "getMetrics", static: false, private: false, access: { has: obj => "getMetrics" in obj, get: obj => obj.getMetrics }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
164
158
|
tslib_1.__esDecorate(this, null, _getUserRootDir_decorators, { kind: "method", name: "getUserRootDir", static: false, private: false, access: { has: obj => "getUserRootDir" in obj, get: obj => obj.getUserRootDir }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
165
159
|
tslib_1.__esDecorate(this, null, _getWorkDir_decorators, { kind: "method", name: "getWorkDir", static: false, private: false, access: { has: obj => "getWorkDir" in obj, get: obj => obj.getWorkDir }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
166
|
-
tslib_1.__esDecorate(this, null, _mountVolume_decorators, { kind: "method", name: "mountVolume", static: false, private: false, access: { has: obj => "mountVolume" in obj, get: obj => obj.mountVolume }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
167
|
-
tslib_1.__esDecorate(this, null, _pullVolumes_decorators, { kind: "method", name: "pullVolumes", static: false, private: false, access: { has: obj => "pullVolumes" in obj, get: obj => obj.pullVolumes }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
168
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);
|
|
169
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);
|
|
170
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);
|
|
171
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);
|
|
172
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);
|
|
173
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);
|
|
@@ -176,13 +169,18 @@ let Sandbox = (() => {
|
|
|
176
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);
|
|
177
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);
|
|
178
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);
|
|
179
173
|
tslib_1.__esDecorate(this, null, _setAutostopInterval_decorators, { kind: "method", name: "setAutostopInterval", static: false, private: false, access: { has: obj => "setAutostopInterval" in obj, get: obj => obj.setAutostopInterval }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
174
|
+
tslib_1.__esDecorate(this, null, _setAutoPauseInterval_decorators, { kind: "method", name: "setAutoPauseInterval", static: false, private: false, access: { has: obj => "setAutoPauseInterval" in obj, get: obj => obj.setAutoPauseInterval }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
175
|
+
tslib_1.__esDecorate(this, null, _setTtl_decorators, { kind: "method", name: "setTtl", static: false, private: false, access: { has: obj => "setTtl" in obj, get: obj => obj.setTtl }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
180
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);
|
|
181
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);
|
|
182
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);
|
|
183
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);
|
|
184
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);
|
|
185
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);
|
|
186
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);
|
|
187
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);
|
|
188
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);
|
|
@@ -194,7 +192,8 @@ let Sandbox = (() => {
|
|
|
194
192
|
clientConfig = tslib_1.__runInitializers(this, _instanceExtraInitializers);
|
|
195
193
|
axiosInstance;
|
|
196
194
|
sandboxApi;
|
|
197
|
-
|
|
195
|
+
getAnalyticsApiUrl;
|
|
196
|
+
subscriptionManager;
|
|
198
197
|
fs;
|
|
199
198
|
git;
|
|
200
199
|
process;
|
|
@@ -219,8 +218,10 @@ let Sandbox = (() => {
|
|
|
219
218
|
backupState;
|
|
220
219
|
backupCreatedAt;
|
|
221
220
|
autoStopInterval;
|
|
221
|
+
autoPauseInterval;
|
|
222
222
|
autoArchiveInterval;
|
|
223
223
|
autoDeleteInterval;
|
|
224
|
+
autoDestroyAt;
|
|
224
225
|
volumes;
|
|
225
226
|
buildInfo;
|
|
226
227
|
createdAt;
|
|
@@ -233,25 +234,28 @@ let Sandbox = (() => {
|
|
|
233
234
|
toolboxProxyUrl;
|
|
234
235
|
infoApi;
|
|
235
236
|
serverApi;
|
|
237
|
+
systemApi;
|
|
238
|
+
stateWaiters = [];
|
|
239
|
+
stateWaiterErrorMessageFns = new WeakMap();
|
|
240
|
+
subId;
|
|
236
241
|
/**
|
|
237
|
-
* 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.
|
|
238
246
|
*
|
|
239
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
|
|
240
251
|
*/
|
|
241
|
-
constructor(sandboxDto, clientConfig, axiosInstance, sandboxApi,
|
|
252
|
+
constructor(sandboxDto, clientConfig, axiosInstance, sandboxApi, getAnalyticsApiUrl, subscriptionManager) {
|
|
242
253
|
this.clientConfig = clientConfig;
|
|
243
254
|
this.axiosInstance = axiosInstance;
|
|
244
255
|
this.sandboxApi = sandboxApi;
|
|
245
|
-
this.
|
|
256
|
+
this.getAnalyticsApiUrl = getAnalyticsApiUrl;
|
|
257
|
+
this.subscriptionManager = subscriptionManager;
|
|
246
258
|
this.processSandboxDto(sandboxDto);
|
|
247
|
-
// Set the toolbox base URL
|
|
248
|
-
let baseUrl = this.toolboxProxyUrl;
|
|
249
|
-
if (!baseUrl.endsWith('/')) {
|
|
250
|
-
baseUrl += '/';
|
|
251
|
-
}
|
|
252
|
-
this.axiosInstance.defaults.baseURL = baseUrl + this.id;
|
|
253
|
-
this.clientConfig.basePath = this.axiosInstance.defaults.baseURL;
|
|
254
|
-
// Initialize Services
|
|
255
259
|
const getPreviewToken = async () => (await this.getPreviewLink(1)).token;
|
|
256
260
|
this.fs = new FileSystem_1.FileSystem(this.clientConfig, new toolbox_api_client_1.FileSystemApi(this.clientConfig, '', this.axiosInstance));
|
|
257
261
|
this.git = new Git_1.Git(new toolbox_api_client_1.GitApi(this.clientConfig, '', this.axiosInstance));
|
|
@@ -261,6 +265,8 @@ let Sandbox = (() => {
|
|
|
261
265
|
this.computerUse = new ComputerUse_1.ComputerUse(new toolbox_api_client_1.ComputerUseApi(this.clientConfig, '', this.axiosInstance));
|
|
262
266
|
this.infoApi = new toolbox_api_client_1.InfoApi(this.clientConfig, '', this.axiosInstance);
|
|
263
267
|
this.serverApi = new toolbox_api_client_1.ServerApi(this.clientConfig, '', this.axiosInstance);
|
|
268
|
+
this.systemApi = new toolbox_api_client_1.SystemApi(this.clientConfig, '', this.axiosInstance);
|
|
269
|
+
this.subscribeToEvents();
|
|
264
270
|
}
|
|
265
271
|
/**
|
|
266
272
|
* Gets the user's home directory path for the logged in user inside the Sandbox.
|
|
@@ -275,6 +281,53 @@ let Sandbox = (() => {
|
|
|
275
281
|
const response = await this.infoApi.getUserHomeDir();
|
|
276
282
|
return response.data.dir;
|
|
277
283
|
}
|
|
284
|
+
/**
|
|
285
|
+
* Gets the most recent resource usage sample directly from the sandbox daemon.
|
|
286
|
+
*
|
|
287
|
+
* Unlike {@link getMetrics}, which returns aggregated historical samples, this returns the
|
|
288
|
+
* single current reading without going through the telemetry backend.
|
|
289
|
+
*
|
|
290
|
+
* @returns The current resource usage sample for the sandbox.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* const m = await sandbox.getMetricsLatest()
|
|
294
|
+
* console.log(`CPU: ${m.cpuUsedPct}%, mem: ${m.memUsed}/${m.memTotal}`)
|
|
295
|
+
*/
|
|
296
|
+
async getMetricsLatest() {
|
|
297
|
+
const response = await this.systemApi.getSystemMetrics();
|
|
298
|
+
return sandboxMetricsFromSystemMetrics(response.data);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Gets historical time-series resource usage metrics for the Sandbox.
|
|
302
|
+
*
|
|
303
|
+
* @param {Date} [start] - Start of the time range. Defaults to the Sandbox creation time.
|
|
304
|
+
* @param {Date} [end] - End of the time range. Defaults to the current time.
|
|
305
|
+
* @returns Time-ordered usage samples over the requested range.
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* const samples = await sandbox.getMetrics()
|
|
309
|
+
* for (const s of samples) {
|
|
310
|
+
* console.log(`${s.timestamp.toISOString()} CPU: ${s.cpuUsedPct}% mem: ${s.memUsed}/${s.memTotal}`)
|
|
311
|
+
* }
|
|
312
|
+
*/
|
|
313
|
+
async getMetrics(start, end) {
|
|
314
|
+
const to = end ?? new Date();
|
|
315
|
+
const from = start ?? (this.createdAt ? new Date(this.createdAt) : to);
|
|
316
|
+
const analyticsApiUrl = await this.getAnalyticsApiUrl();
|
|
317
|
+
if (analyticsApiUrl) {
|
|
318
|
+
const response = await this.buildAnalyticsTelemetryApi(analyticsApiUrl).organizationOrganizationIdSandboxSandboxIdTelemetryMetricsGet(this.organizationId, this.id, from.toISOString(), to.toISOString(), SANDBOX_METRIC_NAMES.join(','));
|
|
319
|
+
return pivotSandboxMetricPoints(response.data);
|
|
320
|
+
}
|
|
321
|
+
const response = await this.sandboxApi.getSandboxMetrics(this.id, from, to, undefined, SANDBOX_METRIC_NAMES);
|
|
322
|
+
return pivotSandboxMetrics(response.data.series);
|
|
323
|
+
}
|
|
324
|
+
buildAnalyticsTelemetryApi(analyticsApiUrl) {
|
|
325
|
+
const analyticsConfig = new analytics_api_client_1.Configuration({
|
|
326
|
+
basePath: analyticsApiUrl,
|
|
327
|
+
apiKey: this.clientConfig.baseOptions?.headers?.Authorization,
|
|
328
|
+
});
|
|
329
|
+
return new analytics_api_client_1.TelemetryApi(analyticsConfig, undefined, Daytona_1.Daytona.createAxiosInstance());
|
|
330
|
+
}
|
|
278
331
|
/**
|
|
279
332
|
* @deprecated Use `getUserHomeDir` instead. This method will be removed in a future version.
|
|
280
333
|
*/
|
|
@@ -295,71 +348,6 @@ let Sandbox = (() => {
|
|
|
295
348
|
const response = await this.infoApi.getWorkDir();
|
|
296
349
|
return response.data.dir;
|
|
297
350
|
}
|
|
298
|
-
/**
|
|
299
|
-
* Mounts a hotmount Volume into the running Sandbox on the fly.
|
|
300
|
-
*
|
|
301
|
-
* Unlike legacy and blockmount volumes (which are attached at Sandbox creation), hotmount
|
|
302
|
-
* volumes are mounted at runtime: this method requests a short-lived mount token from the API
|
|
303
|
-
* and bootstraps the hotmount agent inside the Sandbox (downloading and running the region's
|
|
304
|
-
* `init.sh`), mounting the filesystem at the given path.
|
|
305
|
-
*
|
|
306
|
-
* The Sandbox must have `/dev/fuse` available, outbound access to the region gateway (ports
|
|
307
|
-
* 443 and 18443) and binaries bucket, and passwordless sudo (or run as root).
|
|
308
|
-
*
|
|
309
|
-
* @param {Volume} volume - The hotmount Volume to mount. Must be of type `VolumeType.HOTMOUNT`.
|
|
310
|
-
* @param {string} mountPath - The absolute path inside the Sandbox to mount the Volume at
|
|
311
|
-
* @returns {Promise<void>}
|
|
312
|
-
* @throws {DaytonaError} If the Volume is not a hotmount volume, or if mounting fails
|
|
313
|
-
*
|
|
314
|
-
* @example
|
|
315
|
-
* const volume = await daytona.volume.get("shared-fs");
|
|
316
|
-
* await sandbox.mountVolume(volume, "/mnt/shared");
|
|
317
|
-
*/
|
|
318
|
-
async mountVolume(volume, mountPath) {
|
|
319
|
-
if (volume.type !== api_client_2.VolumeType.HOTMOUNT) {
|
|
320
|
-
throw new DaytonaError_1.DaytonaValidationError(`Only hotmount volumes can be mounted on the fly. Volume '${volume.name}' is of type '${volume.type}'.`);
|
|
321
|
-
}
|
|
322
|
-
if (!this.volumeService) {
|
|
323
|
-
throw new DaytonaError_1.DaytonaError('Volume service is not available for this Sandbox instance.');
|
|
324
|
-
}
|
|
325
|
-
const mountToken = await this.volumeService.getMountToken(volume);
|
|
326
|
-
const command = buildHotmountMountCommand(mountToken, mountPath);
|
|
327
|
-
const response = await this.process.executeCommand(command);
|
|
328
|
-
if (response.exitCode !== 0) {
|
|
329
|
-
throw new DaytonaError_1.DaytonaError(`Failed to mount hotmount volume '${volume.name}': ${response.result}`);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Pulls the latest state of the Sandbox's blockmount Volumes into the running Sandbox.
|
|
334
|
-
*
|
|
335
|
-
* Blockmount volumes reconcile in the background: each sandbox writes to a private scratch
|
|
336
|
-
* that is committed to the shared store periodically, but other sandboxes' commits only
|
|
337
|
-
* appear locally on re-materialize. This method makes them appear immediately, without
|
|
338
|
-
* stopping the Sandbox: it commits this Sandbox's local changes (so they participate in the
|
|
339
|
-
* merge), then applies the volume's latest merged state in place. Files modified locally
|
|
340
|
-
* after another sandbox's change keep the local version (last-change-wins by mtime).
|
|
341
|
-
*
|
|
342
|
-
* @param {Volume} [volume] - The blockmount Volume to pull. Omit to pull every blockmount
|
|
343
|
-
* Volume attached to the Sandbox.
|
|
344
|
-
* @returns {Promise<VolumePullResult[]>} Per-volume pull results
|
|
345
|
-
* @throws {DaytonaError} If the Sandbox has no matching mounted blockmount Volume
|
|
346
|
-
*
|
|
347
|
-
* @example
|
|
348
|
-
* // sandbox B picks up what sandbox A committed, while both keep running
|
|
349
|
-
* const results = await sandbox.pullVolumes();
|
|
350
|
-
* console.log(results); // [{ volumeId, upToDate, filesWritten, deleted, ... }]
|
|
351
|
-
*/
|
|
352
|
-
async pullVolumes(volume) {
|
|
353
|
-
if (volume && volume.type !== api_client_2.VolumeType.BLOCKMOUNT) {
|
|
354
|
-
throw new DaytonaError_1.DaytonaValidationError(`Only blockmount volumes can be pulled. Volume '${volume.name}' is of type '${volume.type}'.`);
|
|
355
|
-
}
|
|
356
|
-
// Raw toolbox call (the daemon's generated client has no pull op — the runner intercepts
|
|
357
|
-
// this path), so replicate what the generated clients do: resolve against this sandbox's
|
|
358
|
-
// clientConfig.basePath (the shared axios instance's default baseURL is mutated by every
|
|
359
|
-
// Sandbox construction and may point at another sandbox) and inject the auth headers.
|
|
360
|
-
const response = await this.axiosInstance.post(`${this.clientConfig.basePath}/volumes/pull`, volume ? { volumeId: volume.id } : {}, { headers: this.clientConfig.baseOptions?.headers });
|
|
361
|
-
return response.data.results ?? [];
|
|
362
|
-
}
|
|
363
351
|
/**
|
|
364
352
|
* Creates a new Language Server Protocol (LSP) server instance.
|
|
365
353
|
*
|
|
@@ -497,9 +485,16 @@ let Sandbox = (() => {
|
|
|
497
485
|
timeout: timeout * 1000,
|
|
498
486
|
});
|
|
499
487
|
const sandboxDto = response.data;
|
|
500
|
-
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);
|
|
501
495
|
const timeElapsed = Date.now() - startTime;
|
|
502
|
-
|
|
496
|
+
const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
|
|
497
|
+
await forkedSandbox.waitUntilStarted(remainingTimeout);
|
|
503
498
|
return forkedSandbox;
|
|
504
499
|
}
|
|
505
500
|
/**
|
|
@@ -527,34 +522,19 @@ let Sandbox = (() => {
|
|
|
527
522
|
}
|
|
528
523
|
const startTime = Date.now();
|
|
529
524
|
const req = { name };
|
|
530
|
-
await this.sandboxApi.createSandboxSnapshot(this.id, req, undefined, {
|
|
525
|
+
const response = await this.sandboxApi.createSandboxSnapshot(this.id, req, undefined, {
|
|
531
526
|
timeout: timeout * 1000,
|
|
532
527
|
});
|
|
533
|
-
|
|
528
|
+
this.processSandboxDto(response.data);
|
|
534
529
|
const timeElapsed = Date.now() - startTime;
|
|
535
530
|
const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
|
|
536
531
|
await this.waitForSnapshotComplete(remainingTimeout);
|
|
537
532
|
}
|
|
538
533
|
async waitForSnapshotComplete(timeout) {
|
|
539
|
-
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
544
|
-
if (this.state === api_client_1.SandboxState.ERROR || this.state === api_client_1.SandboxState.BUILD_FAILED) {
|
|
545
|
-
throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} snapshot failed with state: ${this.state}, error reason: ${this.errorReason}`);
|
|
546
|
-
}
|
|
547
|
-
if (this.state !== api_client_1.SandboxState.SNAPSHOTTING) {
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
|
|
551
|
-
throw new DaytonaError_1.DaytonaTimeoutError('Sandbox snapshot did not complete within the timeout period');
|
|
552
|
-
}
|
|
553
|
-
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
554
|
-
if (Date.now() - startTime > 5000) {
|
|
555
|
-
checkInterval = Math.min(checkInterval * 1.1, 1000);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
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}`);
|
|
558
538
|
}
|
|
559
539
|
/**
|
|
560
540
|
* Pauses the Sandbox, freezing all running processes.
|
|
@@ -585,31 +565,47 @@ let Sandbox = (() => {
|
|
|
585
565
|
await this.refreshData();
|
|
586
566
|
const timeElapsed = Date.now() - startTime;
|
|
587
567
|
const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
await this.refreshData();
|
|
595
|
-
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
596
|
-
if (this.state === api_client_1.SandboxState.ERROR) {
|
|
597
|
-
throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} pause failed with state: ${this.state}, error reason: ${this.errorReason}`);
|
|
598
|
-
}
|
|
599
|
-
if (timeout > 0 && (Date.now() - startTime) / 1000 >= timeout) {
|
|
600
|
-
throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} failed to pause within ${timeout} seconds`);
|
|
601
|
-
}
|
|
602
|
-
await new Promise((resolve) => globalThis.setTimeout(resolve, checkInterval));
|
|
603
|
-
checkInterval = Math.min(checkInterval * 1.5, 1000);
|
|
604
|
-
}
|
|
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}`);
|
|
605
574
|
}
|
|
606
575
|
/**
|
|
607
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.
|
|
608
586
|
* @returns {Promise<void>}
|
|
609
587
|
*/
|
|
610
|
-
async delete(timeout = 60) {
|
|
611
|
-
|
|
612
|
-
|
|
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
|
+
}
|
|
613
609
|
}
|
|
614
610
|
/**
|
|
615
611
|
* Waits for the Sandbox to reach the 'started' state.
|
|
@@ -626,26 +622,10 @@ let Sandbox = (() => {
|
|
|
626
622
|
if (timeout < 0) {
|
|
627
623
|
throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
|
|
628
624
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
while (this.state !== 'started') {
|
|
632
|
-
await this.refreshData();
|
|
633
|
-
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
634
|
-
if (this.state === 'started') {
|
|
635
|
-
return;
|
|
636
|
-
}
|
|
637
|
-
if (this.state === 'error') {
|
|
638
|
-
const errMsg = `Sandbox ${this.id} failed to start with status: ${this.state}, error reason: ${this.errorReason}`;
|
|
639
|
-
throw new DaytonaError_1.DaytonaError(errMsg);
|
|
640
|
-
}
|
|
641
|
-
if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
|
|
642
|
-
throw new DaytonaError_1.DaytonaTimeoutError('Sandbox failed to become ready within the timeout period');
|
|
643
|
-
}
|
|
644
|
-
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
645
|
-
if (Date.now() - startTime > 5000) {
|
|
646
|
-
checkInterval = Math.min(checkInterval * 1.1, 1000);
|
|
647
|
-
}
|
|
625
|
+
if (this.state === api_client_1.SandboxState.STARTED) {
|
|
626
|
+
return;
|
|
648
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}`);
|
|
649
629
|
}
|
|
650
630
|
/**
|
|
651
631
|
* Wait for Sandbox to reach 'stopped' state.
|
|
@@ -662,27 +642,11 @@ let Sandbox = (() => {
|
|
|
662
642
|
if (timeout < 0) {
|
|
663
643
|
throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
|
|
664
644
|
}
|
|
665
|
-
let checkInterval = 100;
|
|
666
|
-
const startTime = Date.now();
|
|
667
645
|
// Treat destroyed as stopped to cover ephemeral sandboxes that are automatically deleted after stopping
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
671
|
-
if (this.state === 'stopped' || this.state === 'destroyed') {
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
674
|
-
if (this.state === 'error') {
|
|
675
|
-
const errMsg = `Sandbox failed to stop with status: ${this.state}, error reason: ${this.errorReason}`;
|
|
676
|
-
throw new DaytonaError_1.DaytonaError(errMsg);
|
|
677
|
-
}
|
|
678
|
-
if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
|
|
679
|
-
throw new DaytonaError_1.DaytonaTimeoutError('Sandbox failed to become stopped within the timeout period');
|
|
680
|
-
}
|
|
681
|
-
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
682
|
-
if (Date.now() - startTime > 5000) {
|
|
683
|
-
checkInterval = Math.min(checkInterval * 1.1, 1000);
|
|
684
|
-
}
|
|
646
|
+
if (this.state === api_client_1.SandboxState.STOPPED || this.state === api_client_1.SandboxState.DESTROYED) {
|
|
647
|
+
return;
|
|
685
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);
|
|
686
650
|
}
|
|
687
651
|
/**
|
|
688
652
|
* Refreshes the Sandbox data from the API.
|
|
@@ -739,6 +703,60 @@ let Sandbox = (() => {
|
|
|
739
703
|
await this.sandboxApi.setAutostopInterval(this.id, interval);
|
|
740
704
|
this.autoStopInterval = interval;
|
|
741
705
|
}
|
|
706
|
+
/**
|
|
707
|
+
* Set the auto-pause interval for the Sandbox.
|
|
708
|
+
*
|
|
709
|
+
* The Sandbox will automatically pause after being idle (no new events) for the specified interval.
|
|
710
|
+
* Events include any state changes or interactions with the Sandbox through the sdk.
|
|
711
|
+
* Interactions using Sandbox Previews are not included.
|
|
712
|
+
*
|
|
713
|
+
* Only supported for sandbox classes that support pausing. At most one of the auto-stop
|
|
714
|
+
* and auto-pause intervals may be non-zero, so disable auto-stop first by setting its
|
|
715
|
+
* interval to 0.
|
|
716
|
+
*
|
|
717
|
+
* @param {number} interval - Number of minutes of inactivity before auto-pausing.
|
|
718
|
+
* Set to 0 to disable auto-pause. For pause-supporting sandbox
|
|
719
|
+
* classes, creation defaults to 60 minutes when neither interval is provided.
|
|
720
|
+
* @returns {Promise<void>}
|
|
721
|
+
* @throws {DaytonaError} - `DaytonaError` - If interval is not a non-negative integer
|
|
722
|
+
*
|
|
723
|
+
* @example
|
|
724
|
+
* // Auto-pause after 1 hour
|
|
725
|
+
* await sandbox.setAutoPauseInterval(60);
|
|
726
|
+
* // Or disable auto-pause
|
|
727
|
+
* await sandbox.setAutoPauseInterval(0);
|
|
728
|
+
*/
|
|
729
|
+
async setAutoPauseInterval(interval) {
|
|
730
|
+
if (!Number.isInteger(interval) || interval < 0) {
|
|
731
|
+
throw new DaytonaError_1.DaytonaValidationError('autoPauseInterval must be a non-negative integer');
|
|
732
|
+
}
|
|
733
|
+
await this.sandboxApi.setAutoPauseInterval(this.id, interval);
|
|
734
|
+
this.autoPauseInterval = interval;
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Set the TTL (maximum time to live) for the Sandbox.
|
|
738
|
+
*
|
|
739
|
+
* The Sandbox will be destroyed once the TTL elapses, counted as wall-clock time regardless of the
|
|
740
|
+
* Sandbox state - even if it is stopped, paused, or archived. Calling this method re-anchors the
|
|
741
|
+
* deadline from the current time. Call `refreshData()` afterwards to read the updated `autoDestroyAt`.
|
|
742
|
+
*
|
|
743
|
+
* @param {number} ttlMinutes - Number of minutes from now after which the Sandbox will be destroyed.
|
|
744
|
+
* Set to 0 to disable the TTL.
|
|
745
|
+
* @returns {Promise<void>}
|
|
746
|
+
* @throws {DaytonaError} - `DaytonaError` - If ttlMinutes is not a non-negative integer
|
|
747
|
+
*
|
|
748
|
+
* @example
|
|
749
|
+
* // Destroy the Sandbox 1 hour from now
|
|
750
|
+
* await sandbox.setTtl(60);
|
|
751
|
+
* // Or disable the TTL
|
|
752
|
+
* await sandbox.setTtl(0);
|
|
753
|
+
*/
|
|
754
|
+
async setTtl(ttlMinutes) {
|
|
755
|
+
if (!Number.isInteger(ttlMinutes) || ttlMinutes < 0) {
|
|
756
|
+
throw new DaytonaError_1.DaytonaValidationError('ttlMinutes must be a non-negative integer');
|
|
757
|
+
}
|
|
758
|
+
await this.sandboxApi.setTtl(this.id, ttlMinutes);
|
|
759
|
+
}
|
|
742
760
|
/**
|
|
743
761
|
* Set the auto-archive interval for the Sandbox.
|
|
744
762
|
*
|
|
@@ -961,25 +979,13 @@ let Sandbox = (() => {
|
|
|
961
979
|
if (timeout < 0) {
|
|
962
980
|
throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
|
|
963
981
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
while (this.state === api_client_1.SandboxState.RESIZING) {
|
|
967
|
-
await this.refreshData();
|
|
968
|
-
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
969
|
-
if (this.state === api_client_1.SandboxState.ERROR || this.state === api_client_1.SandboxState.BUILD_FAILED) {
|
|
970
|
-
throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} resize failed with state: ${this.state}, error reason: ${this.errorReason}`);
|
|
971
|
-
}
|
|
972
|
-
if (this.state !== api_client_1.SandboxState.RESIZING) {
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
|
|
976
|
-
throw new DaytonaError_1.DaytonaTimeoutError('Sandbox resize did not complete within the timeout period');
|
|
977
|
-
}
|
|
978
|
-
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
979
|
-
if (Date.now() - startTime > 5000) {
|
|
980
|
-
checkInterval = Math.min(checkInterval * 1.1, 1000);
|
|
981
|
-
}
|
|
982
|
+
if (this.state !== api_client_1.SandboxState.RESIZING) {
|
|
983
|
+
return;
|
|
982
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}`);
|
|
983
989
|
}
|
|
984
990
|
/**
|
|
985
991
|
* Creates an SSH access token for the sandbox.
|
|
@@ -1008,6 +1014,172 @@ let Sandbox = (() => {
|
|
|
1008
1014
|
async validateSshAccess(token) {
|
|
1009
1015
|
return (await this.sandboxApi.validateSshAccess(token)).data;
|
|
1010
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
|
+
}
|
|
1011
1183
|
/**
|
|
1012
1184
|
* Assigns the API sandbox data to the Sandbox object.
|
|
1013
1185
|
*
|
|
@@ -1015,6 +1187,7 @@ let Sandbox = (() => {
|
|
|
1015
1187
|
* @returns {void}
|
|
1016
1188
|
*/
|
|
1017
1189
|
processSandboxDto(sandboxDto) {
|
|
1190
|
+
const newState = sandboxDto.state;
|
|
1018
1191
|
this.id = sandboxDto.id;
|
|
1019
1192
|
this.name = sandboxDto.name;
|
|
1020
1193
|
this.organizationId = sandboxDto.organizationId;
|
|
@@ -1027,17 +1200,17 @@ let Sandbox = (() => {
|
|
|
1027
1200
|
this.gpu = sandboxDto.gpu;
|
|
1028
1201
|
this.memory = sandboxDto.memory;
|
|
1029
1202
|
this.disk = sandboxDto.disk;
|
|
1030
|
-
this.state = sandboxDto.state;
|
|
1031
1203
|
this.errorReason = sandboxDto.errorReason;
|
|
1032
1204
|
this.recoverable = sandboxDto.recoverable;
|
|
1033
1205
|
this.backupState = sandboxDto.backupState;
|
|
1034
1206
|
this.autoStopInterval = sandboxDto.autoStopInterval;
|
|
1207
|
+
this.autoPauseInterval = sandboxDto.autoPauseInterval;
|
|
1035
1208
|
this.autoArchiveInterval = sandboxDto.autoArchiveInterval;
|
|
1036
1209
|
this.autoDeleteInterval = sandboxDto.autoDeleteInterval;
|
|
1210
|
+
this.autoDestroyAt = sandboxDto.autoDestroyAt;
|
|
1037
1211
|
this.createdAt = sandboxDto.createdAt;
|
|
1038
1212
|
this.updatedAt = sandboxDto.updatedAt;
|
|
1039
1213
|
this.lastActivityAt = sandboxDto.lastActivityAt;
|
|
1040
|
-
this.toolboxProxyUrl = sandboxDto.toolboxProxyUrl;
|
|
1041
1214
|
// Fields only present in the full SandboxDto (not returned by list endpoint)
|
|
1042
1215
|
if ('env' in sandboxDto) {
|
|
1043
1216
|
this.env = sandboxDto.env;
|
|
@@ -1049,6 +1222,21 @@ let Sandbox = (() => {
|
|
|
1049
1222
|
this.buildInfo = sandboxDto.buildInfo;
|
|
1050
1223
|
this.backupCreatedAt = sandboxDto.backupCreatedAt;
|
|
1051
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
|
+
}
|
|
1052
1240
|
}
|
|
1053
1241
|
/**
|
|
1054
1242
|
* Refreshes the Sandbox data from the API, but does not throw an error if the sandbox has been deleted.
|
|
@@ -1056,17 +1244,113 @@ let Sandbox = (() => {
|
|
|
1056
1244
|
*
|
|
1057
1245
|
* @returns {Promise<void>}
|
|
1058
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
|
+
*/
|
|
1059
1252
|
async refreshDataSafe() {
|
|
1060
1253
|
try {
|
|
1061
1254
|
await this.refreshData();
|
|
1255
|
+
return true;
|
|
1062
1256
|
}
|
|
1063
1257
|
catch (error) {
|
|
1064
1258
|
if (error instanceof DaytonaError_1.DaytonaNotFoundError) {
|
|
1065
|
-
this.
|
|
1259
|
+
this.applyState(api_client_1.SandboxState.DESTROYED);
|
|
1260
|
+
return true;
|
|
1066
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);
|
|
1067
1274
|
}
|
|
1068
1275
|
}
|
|
1276
|
+
checkStateWaiter(waiter, state) {
|
|
1277
|
+
if (!state) {
|
|
1278
|
+
return false;
|
|
1279
|
+
}
|
|
1280
|
+
if (waiter.targetStates.has(state)) {
|
|
1281
|
+
waiter.resolve(state);
|
|
1282
|
+
return true;
|
|
1283
|
+
}
|
|
1284
|
+
if (waiter.errorStates.has(state)) {
|
|
1285
|
+
const errorMessageFn = this.stateWaiterErrorMessageFns.get(waiter);
|
|
1286
|
+
waiter.reject(new DaytonaError_1.DaytonaError(errorMessageFn ? errorMessageFn(state) : `Sandbox ${this.id} failed with state: ${state}`));
|
|
1287
|
+
return true;
|
|
1288
|
+
}
|
|
1289
|
+
return false;
|
|
1290
|
+
}
|
|
1291
|
+
removeStateWaiter(waiter) {
|
|
1292
|
+
const index = this.stateWaiters.indexOf(waiter);
|
|
1293
|
+
if (index !== -1) {
|
|
1294
|
+
this.stateWaiters.splice(index, 1);
|
|
1295
|
+
}
|
|
1296
|
+
this.stateWaiterErrorMessageFns.delete(waiter);
|
|
1297
|
+
}
|
|
1069
1298
|
};
|
|
1070
1299
|
})();
|
|
1071
1300
|
exports.Sandbox = Sandbox;
|
|
1301
|
+
const SANDBOX_METRIC_FIELD_BY_NAME = {
|
|
1302
|
+
'daytona.sandbox.cpu.utilization': 'cpuUsedPct',
|
|
1303
|
+
'daytona.sandbox.cpu.limit': 'cpuCount',
|
|
1304
|
+
'daytona.sandbox.memory.usage': 'memUsed',
|
|
1305
|
+
'daytona.sandbox.memory.limit': 'memTotal',
|
|
1306
|
+
'daytona.sandbox.memory.cache': 'memCache',
|
|
1307
|
+
'daytona.sandbox.filesystem.usage': 'diskUsed',
|
|
1308
|
+
'daytona.sandbox.filesystem.total': 'diskTotal',
|
|
1309
|
+
};
|
|
1310
|
+
const SANDBOX_METRIC_NAMES = Object.keys(SANDBOX_METRIC_FIELD_BY_NAME);
|
|
1311
|
+
function sandboxMetricsFromSystemMetrics(m) {
|
|
1312
|
+
return {
|
|
1313
|
+
cpuCount: m.cpuCount ?? 0,
|
|
1314
|
+
cpuUsedPct: m.cpuUsedPct ?? 0,
|
|
1315
|
+
diskTotal: m.diskTotal ?? 0,
|
|
1316
|
+
diskUsed: m.diskUsed ?? 0,
|
|
1317
|
+
memTotal: m.memTotal ?? 0,
|
|
1318
|
+
memUsed: m.memUsed ?? 0,
|
|
1319
|
+
memCache: m.memCache ?? 0,
|
|
1320
|
+
timestamp: m.timestamp ? new Date(m.timestamp) : new Date(),
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
function buildSandboxMetrics(buckets) {
|
|
1324
|
+
return [...buckets.keys()].sort().map((ts) => {
|
|
1325
|
+
const v = buckets.get(ts);
|
|
1326
|
+
return {
|
|
1327
|
+
cpuCount: v.cpuCount ?? 0,
|
|
1328
|
+
cpuUsedPct: v.cpuUsedPct ?? 0,
|
|
1329
|
+
diskTotal: v.diskTotal ?? 0,
|
|
1330
|
+
diskUsed: v.diskUsed ?? 0,
|
|
1331
|
+
memTotal: v.memTotal ?? 0,
|
|
1332
|
+
memUsed: v.memUsed ?? 0,
|
|
1333
|
+
memCache: v.memCache ?? 0,
|
|
1334
|
+
timestamp: new Date(ts),
|
|
1335
|
+
};
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
function pivotMetricTriples(triples) {
|
|
1339
|
+
const buckets = new Map();
|
|
1340
|
+
for (const [name, timestamp, value] of triples) {
|
|
1341
|
+
const field = name ? SANDBOX_METRIC_FIELD_BY_NAME[name] : undefined;
|
|
1342
|
+
if (!field || timestamp === undefined || value === undefined)
|
|
1343
|
+
continue;
|
|
1344
|
+
const bucket = buckets.get(timestamp) ?? {};
|
|
1345
|
+
bucket[field] = value;
|
|
1346
|
+
buckets.set(timestamp, bucket);
|
|
1347
|
+
}
|
|
1348
|
+
return buildSandboxMetrics(buckets);
|
|
1349
|
+
}
|
|
1350
|
+
function pivotSandboxMetrics(series) {
|
|
1351
|
+
return pivotMetricTriples((series ?? []).flatMap((s) => (s.dataPoints ?? []).map((p) => [s.metricName, p.timestamp, p.value])));
|
|
1352
|
+
}
|
|
1353
|
+
function pivotSandboxMetricPoints(points) {
|
|
1354
|
+
return pivotMetricTriples((points ?? []).map((p) => [p.metricName, p.timestamp, p.value]));
|
|
1355
|
+
}
|
|
1072
1356
|
//# sourceMappingURL=Sandbox.js.map
|