@daytona/sdk 0.198.0 → 0.199.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/Daytona.d.ts +2 -25
- package/cjs/Daytona.js +7 -60
- package/cjs/Daytona.js.map +1 -1
- package/cjs/Sandbox.d.ts +48 -128
- package/cjs/Sandbox.js +237 -491
- package/cjs/Sandbox.js.map +1 -1
- package/cjs/Volume.d.ts +91 -4
- package/cjs/Volume.js +78 -5
- package/cjs/Volume.js.map +1 -1
- package/cjs/index.d.ts +3 -1
- package/cjs/index.js +3 -1
- package/cjs/index.js.map +1 -1
- package/esm/Daytona.d.ts +2 -25
- package/esm/Daytona.js +7 -60
- package/esm/Daytona.js.map +1 -1
- package/esm/Sandbox.d.ts +48 -128
- package/esm/Sandbox.js +238 -492
- package/esm/Sandbox.js.map +1 -1
- package/esm/Volume.d.ts +91 -4
- package/esm/Volume.js +77 -4
- package/esm/Volume.js.map +1 -1
- package/esm/index.d.ts +3 -1
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/utils/Import.js +1 -1
- package/package.json +3 -5
- package/cjs/utils/EventDispatcher.d.ts +0 -85
- package/cjs/utils/EventDispatcher.js +0 -398
- package/cjs/utils/EventDispatcher.js.map +0 -1
- package/cjs/utils/EventSubscriptionManager.d.ts +0 -14
- package/cjs/utils/EventSubscriptionManager.js +0 -98
- package/cjs/utils/EventSubscriptionManager.js.map +0 -1
- package/esm/utils/EventDispatcher.d.ts +0 -85
- package/esm/utils/EventDispatcher.js +0 -394
- package/esm/utils/EventDispatcher.js.map +0 -1
- package/esm/utils/EventSubscriptionManager.d.ts +0 -14
- package/esm/utils/EventSubscriptionManager.js +0 -94
- package/esm/utils/EventSubscriptionManager.js.map +0 -1
package/cjs/Sandbox.js
CHANGED
|
@@ -7,7 +7,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
exports.Sandbox = void 0;
|
|
8
8
|
const tslib_1 = require("tslib");
|
|
9
9
|
const api_client_1 = require("@daytona/api-client");
|
|
10
|
-
const analytics_api_client_1 = require("@daytona/analytics-api-client");
|
|
11
10
|
const Daytona_1 = require("./Daytona");
|
|
12
11
|
const toolbox_api_client_1 = require("@daytona/toolbox-api-client");
|
|
13
12
|
const FileSystem_1 = require("./FileSystem");
|
|
@@ -19,12 +18,35 @@ const Daytona_2 = require("./Daytona");
|
|
|
19
18
|
const ComputerUse_1 = require("./ComputerUse");
|
|
20
19
|
const CodeInterpreter_1 = require("./CodeInterpreter");
|
|
21
20
|
const otel_decorator_1 = require("./utils/otel.decorator");
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
const api_client_2 = require("@daytona/api-client");
|
|
22
|
+
/**
|
|
23
|
+
* Builds the shell command that bootstraps the hotmount agent and mounts the volume inside a
|
|
24
|
+
* Sandbox. It exports the SEAWEED_* environment contract and runs the region's `init.sh`, using
|
|
25
|
+
* passwordless sudo when not already root (sudo strips env, so the vars are passed via `env`).
|
|
26
|
+
*/
|
|
27
|
+
function buildHotmountMountCommand(mountToken, mountPath) {
|
|
28
|
+
const envVars = {
|
|
29
|
+
SEAWEED_TOKEN: mountToken.token,
|
|
30
|
+
SEAWEED_GATEWAY_GRPC: mountToken.gatewayGrpc,
|
|
31
|
+
SEAWEED_GATEWAY_HTTP: mountToken.gatewayHttp,
|
|
32
|
+
SEAWEED_BINARIES_URL: mountToken.binariesUrl,
|
|
33
|
+
SEAWEED_MOUNT_DIR: mountPath,
|
|
34
|
+
SEAWEED_VERSION: mountToken.version,
|
|
27
35
|
};
|
|
36
|
+
const envAssignments = Object.entries(envVars)
|
|
37
|
+
.filter(([, value]) => value !== undefined && value !== '')
|
|
38
|
+
.map(([key, value]) => `${key}='${value}'`)
|
|
39
|
+
.join(' ');
|
|
40
|
+
// The bootstrap (and init.sh itself) requires curl. Fail loudly if it is missing or the
|
|
41
|
+
// download fails, rather than letting a broken `curl … | bash` pipe exit 0 and mount nothing.
|
|
42
|
+
const inner = [
|
|
43
|
+
'set -e',
|
|
44
|
+
'if ! command -v curl >/dev/null 2>&1; then echo "hotmount: curl is required to bootstrap the agent but was not found in the sandbox" >&2; exit 1; fi',
|
|
45
|
+
'mkdir -p "$SEAWEED_MOUNT_DIR"',
|
|
46
|
+
'init_script="$(curl -fsSL "$SEAWEED_BINARIES_URL/init.sh")"',
|
|
47
|
+
'printf %s "$init_script" | bash',
|
|
48
|
+
].join('; ');
|
|
49
|
+
return (`if [ "$(id -u)" != 0 ]; then SUDO="sudo -n"; else SUDO=""; fi; ` + `$SUDO env ${envAssignments} bash -c '${inner}'`);
|
|
28
50
|
}
|
|
29
51
|
/**
|
|
30
52
|
* Represents a Daytona Sandbox.
|
|
@@ -55,7 +77,6 @@ function withEvents(target, _context) {
|
|
|
55
77
|
* @property {string} [backupCreatedAt] - When the backup was created (not returned by list results;
|
|
56
78
|
* call `refreshData()` on each item to populate)
|
|
57
79
|
* @property {number} [autoStopInterval] - Auto-stop interval in minutes
|
|
58
|
-
* @property {number} [autoPauseInterval] - Auto-pause interval in minutes
|
|
59
80
|
* @property {number} [autoArchiveInterval] - Auto-archive interval in minutes
|
|
60
81
|
* @property {number} [autoDeleteInterval] - Auto-delete interval in minutes
|
|
61
82
|
* @property {Array<SandboxVolume>} [volumes] - Volumes attached to the Sandbox (not returned by
|
|
@@ -79,14 +100,13 @@ function withEvents(target, _context) {
|
|
|
79
100
|
let Sandbox = (() => {
|
|
80
101
|
let _instanceExtraInitializers = [];
|
|
81
102
|
let _getUserHomeDir_decorators;
|
|
82
|
-
let _getMetricsLatest_decorators;
|
|
83
|
-
let _getMetrics_decorators;
|
|
84
103
|
let _getUserRootDir_decorators;
|
|
85
104
|
let _getWorkDir_decorators;
|
|
105
|
+
let _mountVolume_decorators;
|
|
106
|
+
let _pullVolumes_decorators;
|
|
86
107
|
let _createLspServer_decorators;
|
|
87
108
|
let _setLabels_decorators;
|
|
88
109
|
let _start_decorators;
|
|
89
|
-
let _recover_decorators;
|
|
90
110
|
let _stop_decorators;
|
|
91
111
|
let __experimental_fork_decorators;
|
|
92
112
|
let __experimental_createSnapshot_decorators;
|
|
@@ -95,17 +115,13 @@ let Sandbox = (() => {
|
|
|
95
115
|
let _waitUntilStarted_decorators;
|
|
96
116
|
let _waitUntilStopped_decorators;
|
|
97
117
|
let _refreshData_decorators;
|
|
98
|
-
let _refreshActivity_decorators;
|
|
99
118
|
let _setAutostopInterval_decorators;
|
|
100
|
-
let _setAutoPauseInterval_decorators;
|
|
101
119
|
let _setAutoArchiveInterval_decorators;
|
|
102
120
|
let _setAutoDeleteInterval_decorators;
|
|
103
121
|
let _updateNetworkSettings_decorators;
|
|
104
122
|
let _updateSecrets_decorators;
|
|
105
123
|
let _updateEnv_decorators;
|
|
106
124
|
let _getPreviewLink_decorators;
|
|
107
|
-
let _getSignedPreviewUrl_decorators;
|
|
108
|
-
let _expireSignedPreviewUrl_decorators;
|
|
109
125
|
let _archive_decorators;
|
|
110
126
|
let _resize_decorators;
|
|
111
127
|
let _waitForResizeComplete_decorators;
|
|
@@ -115,49 +131,43 @@ let Sandbox = (() => {
|
|
|
115
131
|
return class Sandbox {
|
|
116
132
|
static {
|
|
117
133
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
118
|
-
_getUserHomeDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
_createLspServer_decorators = [(0, otel_decorator_1.WithInstrumentation)()
|
|
124
|
-
_setLabels_decorators = [(0, otel_decorator_1.WithInstrumentation)()
|
|
125
|
-
_start_decorators = [(0, otel_decorator_1.WithInstrumentation)()
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
_setAutoPauseInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
138
|
-
_setAutoArchiveInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
139
|
-
_setAutoDeleteInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
134
|
+
_getUserHomeDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
135
|
+
_getUserRootDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
136
|
+
_getWorkDir_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
137
|
+
_mountVolume_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
138
|
+
_pullVolumes_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
139
|
+
_createLspServer_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
140
|
+
_setLabels_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
141
|
+
_start_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
142
|
+
_stop_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
143
|
+
__experimental_fork_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
144
|
+
__experimental_createSnapshot_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
145
|
+
_pause_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
146
|
+
_delete_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
147
|
+
_waitUntilStarted_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
148
|
+
_waitUntilStopped_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
149
|
+
_refreshData_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
150
|
+
_setAutostopInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
151
|
+
_setAutoArchiveInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
152
|
+
_setAutoDeleteInterval_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
140
153
|
_updateNetworkSettings_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
141
154
|
_updateSecrets_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
142
155
|
_updateEnv_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
143
|
-
_getPreviewLink_decorators = [(0, otel_decorator_1.WithInstrumentation)()
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
_revokeSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
151
|
-
_validateSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)(), withEvents];
|
|
156
|
+
_getPreviewLink_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
157
|
+
_archive_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
158
|
+
_resize_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
159
|
+
_waitForResizeComplete_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
160
|
+
_createSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
161
|
+
_revokeSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
162
|
+
_validateSshAccess_decorators = [(0, otel_decorator_1.WithInstrumentation)()];
|
|
152
163
|
tslib_1.__esDecorate(this, null, _getUserHomeDir_decorators, { kind: "method", name: "getUserHomeDir", static: false, private: false, access: { has: obj => "getUserHomeDir" in obj, get: obj => obj.getUserHomeDir }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
153
|
-
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);
|
|
154
|
-
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);
|
|
155
164
|
tslib_1.__esDecorate(this, null, _getUserRootDir_decorators, { kind: "method", name: "getUserRootDir", static: false, private: false, access: { has: obj => "getUserRootDir" in obj, get: obj => obj.getUserRootDir }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
156
165
|
tslib_1.__esDecorate(this, null, _getWorkDir_decorators, { kind: "method", name: "getWorkDir", static: false, private: false, access: { has: obj => "getWorkDir" in obj, get: obj => obj.getWorkDir }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
166
|
+
tslib_1.__esDecorate(this, null, _mountVolume_decorators, { kind: "method", name: "mountVolume", static: false, private: false, access: { has: obj => "mountVolume" in obj, get: obj => obj.mountVolume }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
167
|
+
tslib_1.__esDecorate(this, null, _pullVolumes_decorators, { kind: "method", name: "pullVolumes", static: false, private: false, access: { has: obj => "pullVolumes" in obj, get: obj => obj.pullVolumes }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
157
168
|
tslib_1.__esDecorate(this, null, _createLspServer_decorators, { kind: "method", name: "createLspServer", static: false, private: false, access: { has: obj => "createLspServer" in obj, get: obj => obj.createLspServer }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
158
169
|
tslib_1.__esDecorate(this, null, _setLabels_decorators, { kind: "method", name: "setLabels", static: false, private: false, access: { has: obj => "setLabels" in obj, get: obj => obj.setLabels }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
159
170
|
tslib_1.__esDecorate(this, null, _start_decorators, { kind: "method", name: "start", static: false, private: false, access: { has: obj => "start" in obj, get: obj => obj.start }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
160
|
-
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);
|
|
161
171
|
tslib_1.__esDecorate(this, null, _stop_decorators, { kind: "method", name: "stop", static: false, private: false, access: { has: obj => "stop" in obj, get: obj => obj.stop }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
162
172
|
tslib_1.__esDecorate(this, null, __experimental_fork_decorators, { kind: "method", name: "_experimental_fork", static: false, private: false, access: { has: obj => "_experimental_fork" in obj, get: obj => obj._experimental_fork }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
163
173
|
tslib_1.__esDecorate(this, null, __experimental_createSnapshot_decorators, { kind: "method", name: "_experimental_createSnapshot", static: false, private: false, access: { has: obj => "_experimental_createSnapshot" in obj, get: obj => obj._experimental_createSnapshot }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
@@ -166,17 +176,13 @@ let Sandbox = (() => {
|
|
|
166
176
|
tslib_1.__esDecorate(this, null, _waitUntilStarted_decorators, { kind: "method", name: "waitUntilStarted", static: false, private: false, access: { has: obj => "waitUntilStarted" in obj, get: obj => obj.waitUntilStarted }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
167
177
|
tslib_1.__esDecorate(this, null, _waitUntilStopped_decorators, { kind: "method", name: "waitUntilStopped", static: false, private: false, access: { has: obj => "waitUntilStopped" in obj, get: obj => obj.waitUntilStopped }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
168
178
|
tslib_1.__esDecorate(this, null, _refreshData_decorators, { kind: "method", name: "refreshData", static: false, private: false, access: { has: obj => "refreshData" in obj, get: obj => obj.refreshData }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
169
|
-
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);
|
|
170
179
|
tslib_1.__esDecorate(this, null, _setAutostopInterval_decorators, { kind: "method", name: "setAutostopInterval", static: false, private: false, access: { has: obj => "setAutostopInterval" in obj, get: obj => obj.setAutostopInterval }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
171
|
-
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);
|
|
172
180
|
tslib_1.__esDecorate(this, null, _setAutoArchiveInterval_decorators, { kind: "method", name: "setAutoArchiveInterval", static: false, private: false, access: { has: obj => "setAutoArchiveInterval" in obj, get: obj => obj.setAutoArchiveInterval }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
173
181
|
tslib_1.__esDecorate(this, null, _setAutoDeleteInterval_decorators, { kind: "method", name: "setAutoDeleteInterval", static: false, private: false, access: { has: obj => "setAutoDeleteInterval" in obj, get: obj => obj.setAutoDeleteInterval }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
174
182
|
tslib_1.__esDecorate(this, null, _updateNetworkSettings_decorators, { kind: "method", name: "updateNetworkSettings", static: false, private: false, access: { has: obj => "updateNetworkSettings" in obj, get: obj => obj.updateNetworkSettings }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
175
183
|
tslib_1.__esDecorate(this, null, _updateSecrets_decorators, { kind: "method", name: "updateSecrets", static: false, private: false, access: { has: obj => "updateSecrets" in obj, get: obj => obj.updateSecrets }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
176
184
|
tslib_1.__esDecorate(this, null, _updateEnv_decorators, { kind: "method", name: "updateEnv", static: false, private: false, access: { has: obj => "updateEnv" in obj, get: obj => obj.updateEnv }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
177
185
|
tslib_1.__esDecorate(this, null, _getPreviewLink_decorators, { kind: "method", name: "getPreviewLink", static: false, private: false, access: { has: obj => "getPreviewLink" in obj, get: obj => obj.getPreviewLink }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
178
|
-
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);
|
|
179
|
-
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);
|
|
180
186
|
tslib_1.__esDecorate(this, null, _archive_decorators, { kind: "method", name: "archive", static: false, private: false, access: { has: obj => "archive" in obj, get: obj => obj.archive }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
181
187
|
tslib_1.__esDecorate(this, null, _resize_decorators, { kind: "method", name: "resize", static: false, private: false, access: { has: obj => "resize" in obj, get: obj => obj.resize }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
182
188
|
tslib_1.__esDecorate(this, null, _waitForResizeComplete_decorators, { kind: "method", name: "waitForResizeComplete", static: false, private: false, access: { has: obj => "waitForResizeComplete" in obj, get: obj => obj.waitForResizeComplete }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
@@ -188,8 +194,7 @@ let Sandbox = (() => {
|
|
|
188
194
|
clientConfig = tslib_1.__runInitializers(this, _instanceExtraInitializers);
|
|
189
195
|
axiosInstance;
|
|
190
196
|
sandboxApi;
|
|
191
|
-
|
|
192
|
-
subscriptionManager;
|
|
197
|
+
volumeService;
|
|
193
198
|
fs;
|
|
194
199
|
git;
|
|
195
200
|
process;
|
|
@@ -214,7 +219,6 @@ let Sandbox = (() => {
|
|
|
214
219
|
backupState;
|
|
215
220
|
backupCreatedAt;
|
|
216
221
|
autoStopInterval;
|
|
217
|
-
autoPauseInterval;
|
|
218
222
|
autoArchiveInterval;
|
|
219
223
|
autoDeleteInterval;
|
|
220
224
|
volumes;
|
|
@@ -229,28 +233,25 @@ let Sandbox = (() => {
|
|
|
229
233
|
toolboxProxyUrl;
|
|
230
234
|
infoApi;
|
|
231
235
|
serverApi;
|
|
232
|
-
systemApi;
|
|
233
|
-
stateWaiters = [];
|
|
234
|
-
stateWaiterErrorMessageFns = new WeakMap();
|
|
235
|
-
subId;
|
|
236
236
|
/**
|
|
237
|
-
* Creates a new Sandbox instance
|
|
238
|
-
*
|
|
239
|
-
* Internal: obtain sandboxes via {@link Daytona.create}, {@link Daytona.get}, or
|
|
240
|
-
* {@link Daytona.list} rather than constructing directly.
|
|
237
|
+
* Creates a new Sandbox instance
|
|
241
238
|
*
|
|
242
239
|
* @param {SandboxDto} sandboxDto - The API Sandbox instance
|
|
243
|
-
* @param {SandboxApi} sandboxApi - API client for Sandbox operations
|
|
244
|
-
* @param {InfoApi} infoApi - API client for info operations
|
|
245
|
-
* @param {EventSubscriptionManager} subscriptionManager - Event subscription manager for real-time updates
|
|
246
240
|
*/
|
|
247
|
-
constructor(sandboxDto, clientConfig, axiosInstance, sandboxApi,
|
|
241
|
+
constructor(sandboxDto, clientConfig, axiosInstance, sandboxApi, volumeService) {
|
|
248
242
|
this.clientConfig = clientConfig;
|
|
249
243
|
this.axiosInstance = axiosInstance;
|
|
250
244
|
this.sandboxApi = sandboxApi;
|
|
251
|
-
this.
|
|
252
|
-
this.subscriptionManager = subscriptionManager;
|
|
245
|
+
this.volumeService = volumeService;
|
|
253
246
|
this.processSandboxDto(sandboxDto);
|
|
247
|
+
// Set the toolbox base URL
|
|
248
|
+
let baseUrl = this.toolboxProxyUrl;
|
|
249
|
+
if (!baseUrl.endsWith('/')) {
|
|
250
|
+
baseUrl += '/';
|
|
251
|
+
}
|
|
252
|
+
this.axiosInstance.defaults.baseURL = baseUrl + this.id;
|
|
253
|
+
this.clientConfig.basePath = this.axiosInstance.defaults.baseURL;
|
|
254
|
+
// Initialize Services
|
|
254
255
|
const getPreviewToken = async () => (await this.getPreviewLink(1)).token;
|
|
255
256
|
this.fs = new FileSystem_1.FileSystem(this.clientConfig, new toolbox_api_client_1.FileSystemApi(this.clientConfig, '', this.axiosInstance));
|
|
256
257
|
this.git = new Git_1.Git(new toolbox_api_client_1.GitApi(this.clientConfig, '', this.axiosInstance));
|
|
@@ -260,8 +261,6 @@ let Sandbox = (() => {
|
|
|
260
261
|
this.computerUse = new ComputerUse_1.ComputerUse(new toolbox_api_client_1.ComputerUseApi(this.clientConfig, '', this.axiosInstance));
|
|
261
262
|
this.infoApi = new toolbox_api_client_1.InfoApi(this.clientConfig, '', this.axiosInstance);
|
|
262
263
|
this.serverApi = new toolbox_api_client_1.ServerApi(this.clientConfig, '', this.axiosInstance);
|
|
263
|
-
this.systemApi = new toolbox_api_client_1.SystemApi(this.clientConfig, '', this.axiosInstance);
|
|
264
|
-
this.subscribeToEvents();
|
|
265
264
|
}
|
|
266
265
|
/**
|
|
267
266
|
* Gets the user's home directory path for the logged in user inside the Sandbox.
|
|
@@ -276,53 +275,6 @@ let Sandbox = (() => {
|
|
|
276
275
|
const response = await this.infoApi.getUserHomeDir();
|
|
277
276
|
return response.data.dir;
|
|
278
277
|
}
|
|
279
|
-
/**
|
|
280
|
-
* Gets the most recent resource usage sample directly from the sandbox daemon.
|
|
281
|
-
*
|
|
282
|
-
* Unlike {@link getMetrics}, which returns aggregated historical samples, this returns the
|
|
283
|
-
* single current reading without going through the telemetry backend.
|
|
284
|
-
*
|
|
285
|
-
* @returns The current resource usage sample for the sandbox.
|
|
286
|
-
*
|
|
287
|
-
* @example
|
|
288
|
-
* const m = await sandbox.getMetricsLatest()
|
|
289
|
-
* console.log(`CPU: ${m.cpuUsedPct}%, mem: ${m.memUsed}/${m.memTotal}`)
|
|
290
|
-
*/
|
|
291
|
-
async getMetricsLatest() {
|
|
292
|
-
const response = await this.systemApi.getSystemMetrics();
|
|
293
|
-
return sandboxMetricsFromSystemMetrics(response.data);
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Gets historical time-series resource usage metrics for the Sandbox.
|
|
297
|
-
*
|
|
298
|
-
* @param {Date} [start] - Start of the time range. Defaults to the Sandbox creation time.
|
|
299
|
-
* @param {Date} [end] - End of the time range. Defaults to the current time.
|
|
300
|
-
* @returns Time-ordered usage samples over the requested range.
|
|
301
|
-
*
|
|
302
|
-
* @example
|
|
303
|
-
* const samples = await sandbox.getMetrics()
|
|
304
|
-
* for (const s of samples) {
|
|
305
|
-
* console.log(`${s.timestamp.toISOString()} CPU: ${s.cpuUsedPct}% mem: ${s.memUsed}/${s.memTotal}`)
|
|
306
|
-
* }
|
|
307
|
-
*/
|
|
308
|
-
async getMetrics(start, end) {
|
|
309
|
-
const to = end ?? new Date();
|
|
310
|
-
const from = start ?? (this.createdAt ? new Date(this.createdAt) : to);
|
|
311
|
-
const analyticsApiUrl = await this.getAnalyticsApiUrl();
|
|
312
|
-
if (analyticsApiUrl) {
|
|
313
|
-
const response = await this.buildAnalyticsTelemetryApi(analyticsApiUrl).organizationOrganizationIdSandboxSandboxIdTelemetryMetricsGet(this.organizationId, this.id, from.toISOString(), to.toISOString(), SANDBOX_METRIC_NAMES.join(','));
|
|
314
|
-
return pivotSandboxMetricPoints(response.data);
|
|
315
|
-
}
|
|
316
|
-
const response = await this.sandboxApi.getSandboxMetrics(this.id, from, to, undefined, SANDBOX_METRIC_NAMES);
|
|
317
|
-
return pivotSandboxMetrics(response.data.series);
|
|
318
|
-
}
|
|
319
|
-
buildAnalyticsTelemetryApi(analyticsApiUrl) {
|
|
320
|
-
const analyticsConfig = new analytics_api_client_1.Configuration({
|
|
321
|
-
basePath: analyticsApiUrl,
|
|
322
|
-
apiKey: this.clientConfig.baseOptions?.headers?.Authorization,
|
|
323
|
-
});
|
|
324
|
-
return new analytics_api_client_1.TelemetryApi(analyticsConfig, undefined, Daytona_1.Daytona.createAxiosInstance());
|
|
325
|
-
}
|
|
326
278
|
/**
|
|
327
279
|
* @deprecated Use `getUserHomeDir` instead. This method will be removed in a future version.
|
|
328
280
|
*/
|
|
@@ -343,6 +295,71 @@ let Sandbox = (() => {
|
|
|
343
295
|
const response = await this.infoApi.getWorkDir();
|
|
344
296
|
return response.data.dir;
|
|
345
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Mounts a hotmount Volume into the running Sandbox on the fly.
|
|
300
|
+
*
|
|
301
|
+
* Unlike legacy and blockmount volumes (which are attached at Sandbox creation), hotmount
|
|
302
|
+
* volumes are mounted at runtime: this method requests a short-lived mount token from the API
|
|
303
|
+
* and bootstraps the hotmount agent inside the Sandbox (downloading and running the region's
|
|
304
|
+
* `init.sh`), mounting the filesystem at the given path.
|
|
305
|
+
*
|
|
306
|
+
* The Sandbox must have `/dev/fuse` available, outbound access to the region gateway (ports
|
|
307
|
+
* 443 and 18443) and binaries bucket, and passwordless sudo (or run as root).
|
|
308
|
+
*
|
|
309
|
+
* @param {Volume} volume - The hotmount Volume to mount. Must be of type `VolumeType.HOTMOUNT`.
|
|
310
|
+
* @param {string} mountPath - The absolute path inside the Sandbox to mount the Volume at
|
|
311
|
+
* @returns {Promise<void>}
|
|
312
|
+
* @throws {DaytonaError} If the Volume is not a hotmount volume, or if mounting fails
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* const volume = await daytona.volume.get("shared-fs");
|
|
316
|
+
* await sandbox.mountVolume(volume, "/mnt/shared");
|
|
317
|
+
*/
|
|
318
|
+
async mountVolume(volume, mountPath) {
|
|
319
|
+
if (volume.type !== api_client_2.VolumeType.HOTMOUNT) {
|
|
320
|
+
throw new DaytonaError_1.DaytonaValidationError(`Only hotmount volumes can be mounted on the fly. Volume '${volume.name}' is of type '${volume.type}'.`);
|
|
321
|
+
}
|
|
322
|
+
if (!this.volumeService) {
|
|
323
|
+
throw new DaytonaError_1.DaytonaError('Volume service is not available for this Sandbox instance.');
|
|
324
|
+
}
|
|
325
|
+
const mountToken = await this.volumeService.getMountToken(volume);
|
|
326
|
+
const command = buildHotmountMountCommand(mountToken, mountPath);
|
|
327
|
+
const response = await this.process.executeCommand(command);
|
|
328
|
+
if (response.exitCode !== 0) {
|
|
329
|
+
throw new DaytonaError_1.DaytonaError(`Failed to mount hotmount volume '${volume.name}': ${response.result}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Pulls the latest state of the Sandbox's blockmount Volumes into the running Sandbox.
|
|
334
|
+
*
|
|
335
|
+
* Blockmount volumes reconcile in the background: each sandbox writes to a private scratch
|
|
336
|
+
* that is committed to the shared store periodically, but other sandboxes' commits only
|
|
337
|
+
* appear locally on re-materialize. This method makes them appear immediately, without
|
|
338
|
+
* stopping the Sandbox: it commits this Sandbox's local changes (so they participate in the
|
|
339
|
+
* merge), then applies the volume's latest merged state in place. Files modified locally
|
|
340
|
+
* after another sandbox's change keep the local version (last-change-wins by mtime).
|
|
341
|
+
*
|
|
342
|
+
* @param {Volume} [volume] - The blockmount Volume to pull. Omit to pull every blockmount
|
|
343
|
+
* Volume attached to the Sandbox.
|
|
344
|
+
* @returns {Promise<VolumePullResult[]>} Per-volume pull results
|
|
345
|
+
* @throws {DaytonaError} If the Sandbox has no matching mounted blockmount Volume
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* // sandbox B picks up what sandbox A committed, while both keep running
|
|
349
|
+
* const results = await sandbox.pullVolumes();
|
|
350
|
+
* console.log(results); // [{ volumeId, upToDate, filesWritten, deleted, ... }]
|
|
351
|
+
*/
|
|
352
|
+
async pullVolumes(volume) {
|
|
353
|
+
if (volume && volume.type !== api_client_2.VolumeType.BLOCKMOUNT) {
|
|
354
|
+
throw new DaytonaError_1.DaytonaValidationError(`Only blockmount volumes can be pulled. Volume '${volume.name}' is of type '${volume.type}'.`);
|
|
355
|
+
}
|
|
356
|
+
// Raw toolbox call (the daemon's generated client has no pull op — the runner intercepts
|
|
357
|
+
// this path), so replicate what the generated clients do: resolve against this sandbox's
|
|
358
|
+
// clientConfig.basePath (the shared axios instance's default baseURL is mutated by every
|
|
359
|
+
// Sandbox construction and may point at another sandbox) and inject the auth headers.
|
|
360
|
+
const response = await this.axiosInstance.post(`${this.clientConfig.basePath}/volumes/pull`, volume ? { volumeId: volume.id } : {}, { headers: this.clientConfig.baseOptions?.headers });
|
|
361
|
+
return response.data.results ?? [];
|
|
362
|
+
}
|
|
346
363
|
/**
|
|
347
364
|
* Creates a new Language Server Protocol (LSP) server instance.
|
|
348
365
|
*
|
|
@@ -480,16 +497,9 @@ let Sandbox = (() => {
|
|
|
480
497
|
timeout: timeout * 1000,
|
|
481
498
|
});
|
|
482
499
|
const sandboxDto = response.data;
|
|
483
|
-
const
|
|
484
|
-
? sandboxDto
|
|
485
|
-
: {
|
|
486
|
-
...sandboxDto,
|
|
487
|
-
toolboxProxyUrl: (await this.sandboxApi.getToolboxProxyUrl(sandboxDto.id)).data.url,
|
|
488
|
-
};
|
|
489
|
-
const forkedSandbox = new Sandbox(sandboxWithProxyUrl, structuredClone(this.clientConfig), Daytona_1.Daytona.createAxiosInstance(), this.sandboxApi, this.getAnalyticsApiUrl, this.subscriptionManager);
|
|
500
|
+
const forkedSandbox = new Sandbox(sandboxDto, structuredClone(this.clientConfig), Daytona_1.Daytona.createAxiosInstance(), this.sandboxApi);
|
|
490
501
|
const timeElapsed = Date.now() - startTime;
|
|
491
|
-
|
|
492
|
-
await forkedSandbox.waitUntilStarted(remainingTimeout);
|
|
502
|
+
await forkedSandbox.waitUntilStarted(timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout);
|
|
493
503
|
return forkedSandbox;
|
|
494
504
|
}
|
|
495
505
|
/**
|
|
@@ -517,19 +527,34 @@ let Sandbox = (() => {
|
|
|
517
527
|
}
|
|
518
528
|
const startTime = Date.now();
|
|
519
529
|
const req = { name };
|
|
520
|
-
|
|
530
|
+
await this.sandboxApi.createSandboxSnapshot(this.id, req, undefined, {
|
|
521
531
|
timeout: timeout * 1000,
|
|
522
532
|
});
|
|
523
|
-
this.
|
|
533
|
+
await this.refreshData();
|
|
524
534
|
const timeElapsed = Date.now() - startTime;
|
|
525
535
|
const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
|
|
526
536
|
await this.waitForSnapshotComplete(remainingTimeout);
|
|
527
537
|
}
|
|
528
538
|
async waitForSnapshotComplete(timeout) {
|
|
529
|
-
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
539
|
+
let checkInterval = 100;
|
|
540
|
+
const startTime = Date.now();
|
|
541
|
+
while (this.state === api_client_1.SandboxState.SNAPSHOTTING) {
|
|
542
|
+
await this.refreshData();
|
|
543
|
+
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
544
|
+
if (this.state === api_client_1.SandboxState.ERROR || this.state === api_client_1.SandboxState.BUILD_FAILED) {
|
|
545
|
+
throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} snapshot failed with state: ${this.state}, error reason: ${this.errorReason}`);
|
|
546
|
+
}
|
|
547
|
+
if (this.state !== api_client_1.SandboxState.SNAPSHOTTING) {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
|
|
551
|
+
throw new DaytonaError_1.DaytonaTimeoutError('Sandbox snapshot did not complete within the timeout period');
|
|
552
|
+
}
|
|
553
|
+
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
554
|
+
if (Date.now() - startTime > 5000) {
|
|
555
|
+
checkInterval = Math.min(checkInterval * 1.1, 1000);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
533
558
|
}
|
|
534
559
|
/**
|
|
535
560
|
* Pauses the Sandbox, freezing all running processes.
|
|
@@ -560,48 +585,32 @@ let Sandbox = (() => {
|
|
|
560
585
|
await this.refreshData();
|
|
561
586
|
const timeElapsed = Date.now() - startTime;
|
|
562
587
|
const remainingTimeout = timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout;
|
|
563
|
-
|
|
564
|
-
// (paused, stopped, archived, ...), not only on exactly PAUSED.
|
|
565
|
-
const errorStates = [api_client_1.SandboxState.ERROR, api_client_1.SandboxState.BUILD_FAILED];
|
|
566
|
-
const excludeStates = new Set([api_client_1.SandboxState.PAUSING, ...errorStates]);
|
|
567
|
-
const targetStates = Object.values(api_client_1.SandboxState).filter((s) => !excludeStates.has(s));
|
|
568
|
-
await this.waitForState(targetStates, errorStates, remainingTimeout, 'Sandbox failed to become paused within the timeout period', (state) => `Sandbox ${this.id} pause failed with state: ${state}, error reason: ${this.errorReason}`);
|
|
588
|
+
await this.waitForPauseComplete(remainingTimeout);
|
|
569
589
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
*
|
|
573
|
-
* By default this returns as soon as the deletion request is accepted (matching
|
|
574
|
-
* historical behavior). Pass `wait = true` to block until the Sandbox reaches
|
|
575
|
-
* the 'destroyed' state.
|
|
576
|
-
*
|
|
577
|
-
* @param {number} [timeout] - Timeout in seconds for the request — and, when
|
|
578
|
-
* `wait` is true, for reaching 'destroyed'. 0 means no timeout.
|
|
579
|
-
* Defaults to 60-second timeout.
|
|
580
|
-
* @param {boolean} [wait] - If true, wait until the Sandbox is destroyed. Defaults to false.
|
|
581
|
-
* @returns {Promise<void>}
|
|
582
|
-
*/
|
|
583
|
-
async delete(timeout = 60, wait = false) {
|
|
584
|
-
if (timeout < 0) {
|
|
585
|
-
throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
|
|
586
|
-
}
|
|
590
|
+
async waitForPauseComplete(timeout) {
|
|
591
|
+
let checkInterval = 100;
|
|
587
592
|
const startTime = Date.now();
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
this.
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
if (wait && this.state !== api_client_1.SandboxState.DESTROYED) {
|
|
594
|
-
const timeElapsed = Date.now() - startTime;
|
|
595
|
-
await this.waitForState([api_client_1.SandboxState.DESTROYED], [api_client_1.SandboxState.ERROR, api_client_1.SandboxState.BUILD_FAILED], timeout ? Math.max(0.001, timeout - timeElapsed / 1000) : timeout, 'Sandbox failed to be destroyed within the timeout period', (state) => `Sandbox ${this.id} failed to delete with state: ${state}, error reason: ${this.errorReason}`, true);
|
|
593
|
+
while (this.state === api_client_1.SandboxState.PAUSING) {
|
|
594
|
+
await this.refreshData();
|
|
595
|
+
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
596
|
+
if (this.state === api_client_1.SandboxState.ERROR) {
|
|
597
|
+
throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} pause failed with state: ${this.state}, error reason: ${this.errorReason}`);
|
|
596
598
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
if (this.subId) {
|
|
600
|
-
this.subscriptionManager.unsubscribe(this.subId);
|
|
601
|
-
this.subId = undefined;
|
|
599
|
+
if (timeout > 0 && (Date.now() - startTime) / 1000 >= timeout) {
|
|
600
|
+
throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} failed to pause within ${timeout} seconds`);
|
|
602
601
|
}
|
|
602
|
+
await new Promise((resolve) => globalThis.setTimeout(resolve, checkInterval));
|
|
603
|
+
checkInterval = Math.min(checkInterval * 1.5, 1000);
|
|
603
604
|
}
|
|
604
605
|
}
|
|
606
|
+
/**
|
|
607
|
+
* Deletes the Sandbox.
|
|
608
|
+
* @returns {Promise<void>}
|
|
609
|
+
*/
|
|
610
|
+
async delete(timeout = 60) {
|
|
611
|
+
await this.sandboxApi.deleteSandbox(this.id, undefined, { timeout: timeout * 1000 });
|
|
612
|
+
this.refreshDataSafe();
|
|
613
|
+
}
|
|
605
614
|
/**
|
|
606
615
|
* Waits for the Sandbox to reach the 'started' state.
|
|
607
616
|
*
|
|
@@ -617,10 +626,26 @@ let Sandbox = (() => {
|
|
|
617
626
|
if (timeout < 0) {
|
|
618
627
|
throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
|
|
619
628
|
}
|
|
620
|
-
|
|
621
|
-
|
|
629
|
+
let checkInterval = 100;
|
|
630
|
+
const startTime = Date.now();
|
|
631
|
+
while (this.state !== 'started') {
|
|
632
|
+
await this.refreshData();
|
|
633
|
+
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
634
|
+
if (this.state === 'started') {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
if (this.state === 'error') {
|
|
638
|
+
const errMsg = `Sandbox ${this.id} failed to start with status: ${this.state}, error reason: ${this.errorReason}`;
|
|
639
|
+
throw new DaytonaError_1.DaytonaError(errMsg);
|
|
640
|
+
}
|
|
641
|
+
if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
|
|
642
|
+
throw new DaytonaError_1.DaytonaTimeoutError('Sandbox failed to become ready within the timeout period');
|
|
643
|
+
}
|
|
644
|
+
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
645
|
+
if (Date.now() - startTime > 5000) {
|
|
646
|
+
checkInterval = Math.min(checkInterval * 1.1, 1000);
|
|
647
|
+
}
|
|
622
648
|
}
|
|
623
|
-
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}`);
|
|
624
649
|
}
|
|
625
650
|
/**
|
|
626
651
|
* Wait for Sandbox to reach 'stopped' state.
|
|
@@ -637,11 +662,27 @@ let Sandbox = (() => {
|
|
|
637
662
|
if (timeout < 0) {
|
|
638
663
|
throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
|
|
639
664
|
}
|
|
665
|
+
let checkInterval = 100;
|
|
666
|
+
const startTime = Date.now();
|
|
640
667
|
// Treat destroyed as stopped to cover ephemeral sandboxes that are automatically deleted after stopping
|
|
641
|
-
|
|
642
|
-
|
|
668
|
+
while (this.state !== 'stopped' && this.state !== 'destroyed') {
|
|
669
|
+
this.refreshDataSafe();
|
|
670
|
+
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
671
|
+
if (this.state === 'stopped' || this.state === 'destroyed') {
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
if (this.state === 'error') {
|
|
675
|
+
const errMsg = `Sandbox failed to stop with status: ${this.state}, error reason: ${this.errorReason}`;
|
|
676
|
+
throw new DaytonaError_1.DaytonaError(errMsg);
|
|
677
|
+
}
|
|
678
|
+
if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
|
|
679
|
+
throw new DaytonaError_1.DaytonaTimeoutError('Sandbox failed to become stopped within the timeout period');
|
|
680
|
+
}
|
|
681
|
+
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
682
|
+
if (Date.now() - startTime > 5000) {
|
|
683
|
+
checkInterval = Math.min(checkInterval * 1.1, 1000);
|
|
684
|
+
}
|
|
643
685
|
}
|
|
644
|
-
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);
|
|
645
686
|
}
|
|
646
687
|
/**
|
|
647
688
|
* Refreshes the Sandbox data from the API.
|
|
@@ -698,36 +739,6 @@ let Sandbox = (() => {
|
|
|
698
739
|
await this.sandboxApi.setAutostopInterval(this.id, interval);
|
|
699
740
|
this.autoStopInterval = interval;
|
|
700
741
|
}
|
|
701
|
-
/**
|
|
702
|
-
* Set the auto-pause interval for the Sandbox.
|
|
703
|
-
*
|
|
704
|
-
* The Sandbox will automatically pause after being idle (no new events) for the specified interval.
|
|
705
|
-
* Events include any state changes or interactions with the Sandbox through the sdk.
|
|
706
|
-
* Interactions using Sandbox Previews are not included.
|
|
707
|
-
*
|
|
708
|
-
* Only supported for sandbox classes that support pausing. At most one of the auto-stop
|
|
709
|
-
* and auto-pause intervals may be non-zero, so disable auto-stop first by setting its
|
|
710
|
-
* interval to 0.
|
|
711
|
-
*
|
|
712
|
-
* @param {number} interval - Number of minutes of inactivity before auto-pausing.
|
|
713
|
-
* Set to 0 to disable auto-pause. For pause-supporting sandbox
|
|
714
|
-
* classes, creation defaults to 60 minutes when neither interval is provided.
|
|
715
|
-
* @returns {Promise<void>}
|
|
716
|
-
* @throws {DaytonaError} - `DaytonaError` - If interval is not a non-negative integer
|
|
717
|
-
*
|
|
718
|
-
* @example
|
|
719
|
-
* // Auto-pause after 1 hour
|
|
720
|
-
* await sandbox.setAutoPauseInterval(60);
|
|
721
|
-
* // Or disable auto-pause
|
|
722
|
-
* await sandbox.setAutoPauseInterval(0);
|
|
723
|
-
*/
|
|
724
|
-
async setAutoPauseInterval(interval) {
|
|
725
|
-
if (!Number.isInteger(interval) || interval < 0) {
|
|
726
|
-
throw new DaytonaError_1.DaytonaValidationError('autoPauseInterval must be a non-negative integer');
|
|
727
|
-
}
|
|
728
|
-
await this.sandboxApi.setAutoPauseInterval(this.id, interval);
|
|
729
|
-
this.autoPauseInterval = interval;
|
|
730
|
-
}
|
|
731
742
|
/**
|
|
732
743
|
* Set the auto-archive interval for the Sandbox.
|
|
733
744
|
*
|
|
@@ -950,13 +961,25 @@ let Sandbox = (() => {
|
|
|
950
961
|
if (timeout < 0) {
|
|
951
962
|
throw new DaytonaError_1.DaytonaValidationError('Timeout must be a non-negative number');
|
|
952
963
|
}
|
|
953
|
-
|
|
954
|
-
|
|
964
|
+
let checkInterval = 100;
|
|
965
|
+
const startTime = Date.now();
|
|
966
|
+
while (this.state === api_client_1.SandboxState.RESIZING) {
|
|
967
|
+
await this.refreshData();
|
|
968
|
+
// @ts-expect-error this.refreshData() can modify this.state so this check is fine
|
|
969
|
+
if (this.state === api_client_1.SandboxState.ERROR || this.state === api_client_1.SandboxState.BUILD_FAILED) {
|
|
970
|
+
throw new DaytonaError_1.DaytonaError(`Sandbox ${this.id} resize failed with state: ${this.state}, error reason: ${this.errorReason}`);
|
|
971
|
+
}
|
|
972
|
+
if (this.state !== api_client_1.SandboxState.RESIZING) {
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
if (timeout !== 0 && Date.now() - startTime > timeout * 1000) {
|
|
976
|
+
throw new DaytonaError_1.DaytonaTimeoutError('Sandbox resize did not complete within the timeout period');
|
|
977
|
+
}
|
|
978
|
+
await new Promise((resolve) => setTimeout(resolve, checkInterval));
|
|
979
|
+
if (Date.now() - startTime > 5000) {
|
|
980
|
+
checkInterval = Math.min(checkInterval * 1.1, 1000);
|
|
981
|
+
}
|
|
955
982
|
}
|
|
956
|
-
const errorStates = [api_client_1.SandboxState.ERROR, api_client_1.SandboxState.BUILD_FAILED];
|
|
957
|
-
const excludeStates = new Set([api_client_1.SandboxState.RESIZING, ...errorStates]);
|
|
958
|
-
const targetStates = Object.values(api_client_1.SandboxState).filter((s) => !excludeStates.has(s));
|
|
959
|
-
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}`);
|
|
960
983
|
}
|
|
961
984
|
/**
|
|
962
985
|
* Creates an SSH access token for the sandbox.
|
|
@@ -985,172 +1008,6 @@ let Sandbox = (() => {
|
|
|
985
1008
|
async validateSshAccess(token) {
|
|
986
1009
|
return (await this.sandboxApi.validateSshAccess(token)).data;
|
|
987
1010
|
}
|
|
988
|
-
/**
|
|
989
|
-
* Subscribes to real-time events for this sandbox.
|
|
990
|
-
* Auto-updates sandbox metadata on every event.
|
|
991
|
-
*/
|
|
992
|
-
subscribeToEvents() {
|
|
993
|
-
if (this.subId) {
|
|
994
|
-
return;
|
|
995
|
-
}
|
|
996
|
-
this.subId = this.subscriptionManager.subscribe(this.id, this.handleEvent.bind(this), [
|
|
997
|
-
'sandbox.state.updated',
|
|
998
|
-
'sandbox.created',
|
|
999
|
-
]);
|
|
1000
|
-
}
|
|
1001
|
-
ensureSubscribed() {
|
|
1002
|
-
if (this.subId) {
|
|
1003
|
-
if (this.subscriptionManager.refresh(this.subId)) {
|
|
1004
|
-
return;
|
|
1005
|
-
}
|
|
1006
|
-
this.subId = undefined;
|
|
1007
|
-
}
|
|
1008
|
-
this.subscribeToEvents();
|
|
1009
|
-
}
|
|
1010
|
-
handleEvent(eventName, data) {
|
|
1011
|
-
if (!data || typeof data !== 'object')
|
|
1012
|
-
return;
|
|
1013
|
-
const raw = data.sandbox ?? data;
|
|
1014
|
-
if (!raw || typeof raw !== 'object')
|
|
1015
|
-
return;
|
|
1016
|
-
if (eventName === 'sandbox.created') {
|
|
1017
|
-
this.processSandboxDto(raw);
|
|
1018
|
-
return;
|
|
1019
|
-
}
|
|
1020
|
-
const newState = raw.state ?? data.newState;
|
|
1021
|
-
if (newState) {
|
|
1022
|
-
this.applyState(newState);
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
/**
|
|
1026
|
-
* Waits for the sandbox to reach one of the target states.
|
|
1027
|
-
* Throws on error states or timeout.
|
|
1028
|
-
*
|
|
1029
|
-
* @param targetStates - States that indicate success.
|
|
1030
|
-
* @param errorStates - States that indicate failure.
|
|
1031
|
-
* @param timeout - Maximum time to wait in seconds. 0 means no timeout.
|
|
1032
|
-
* @param timeoutMessage - Error message when timeout is reached.
|
|
1033
|
-
* @param errorMessageFn - Function that produces an error message from the current state.
|
|
1034
|
-
* @param safeRefresh - If true, use refreshDataSafe() for polling (for delete operations where 404 is expected).
|
|
1035
|
-
*/
|
|
1036
|
-
waitForState(targetStates, errorStates, timeout, timeoutMessage, errorMessageFn, safeRefresh = false) {
|
|
1037
|
-
this.ensureSubscribed();
|
|
1038
|
-
return new Promise((resolve, reject) => {
|
|
1039
|
-
let timeoutTimer = null;
|
|
1040
|
-
let pollTimer = null;
|
|
1041
|
-
let settled = false;
|
|
1042
|
-
const waiter = {
|
|
1043
|
-
targetStates: new Set(targetStates),
|
|
1044
|
-
errorStates: new Set(errorStates),
|
|
1045
|
-
resolve: (_) => {
|
|
1046
|
-
if (settled)
|
|
1047
|
-
return;
|
|
1048
|
-
cleanup();
|
|
1049
|
-
resolve();
|
|
1050
|
-
},
|
|
1051
|
-
reject: (err) => {
|
|
1052
|
-
if (settled)
|
|
1053
|
-
return;
|
|
1054
|
-
cleanup();
|
|
1055
|
-
reject(err);
|
|
1056
|
-
},
|
|
1057
|
-
};
|
|
1058
|
-
this.stateWaiters.push(waiter);
|
|
1059
|
-
this.stateWaiterErrorMessageFns.set(waiter, errorMessageFn);
|
|
1060
|
-
const cleanup = () => {
|
|
1061
|
-
if (settled)
|
|
1062
|
-
return;
|
|
1063
|
-
settled = true;
|
|
1064
|
-
if (timeoutTimer)
|
|
1065
|
-
clearTimeout(timeoutTimer);
|
|
1066
|
-
if (pollTimer)
|
|
1067
|
-
clearTimeout(pollTimer);
|
|
1068
|
-
this.removeStateWaiter(waiter);
|
|
1069
|
-
};
|
|
1070
|
-
// Fast-path only on cached *target* states (parity with main's pre-check).
|
|
1071
|
-
// Cached error states are deliberately NOT evaluated here — main always
|
|
1072
|
-
// refreshed before failing, so a stale ERROR must survive one refresh.
|
|
1073
|
-
if (this.state && waiter.targetStates.has(this.state)) {
|
|
1074
|
-
waiter.resolve(this.state);
|
|
1075
|
-
return;
|
|
1076
|
-
}
|
|
1077
|
-
if (timeout !== 0) {
|
|
1078
|
-
timeoutTimer = setTimeout(() => {
|
|
1079
|
-
void (async () => {
|
|
1080
|
-
if (settled)
|
|
1081
|
-
return;
|
|
1082
|
-
// Parity with main: complete one final refresh-then-evaluate before
|
|
1083
|
-
// rejecting, so a clamped/short timeout still observes the latest state.
|
|
1084
|
-
let refreshed = false;
|
|
1085
|
-
try {
|
|
1086
|
-
if (safeRefresh) {
|
|
1087
|
-
refreshed = await this.refreshDataSafe();
|
|
1088
|
-
}
|
|
1089
|
-
else {
|
|
1090
|
-
await this.refreshData();
|
|
1091
|
-
refreshed = true;
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
catch {
|
|
1095
|
-
// fall through to the timeout rejection below
|
|
1096
|
-
}
|
|
1097
|
-
if (!settled) {
|
|
1098
|
-
// Explicit evaluation: applyState() no-ops on unchanged state, so a
|
|
1099
|
-
// persistent error state would otherwise time out generically here.
|
|
1100
|
-
// Only evaluate when the refresh succeeded — a failed refresh leaves
|
|
1101
|
-
// the cached state stale and must not be treated as authoritative.
|
|
1102
|
-
if (refreshed && this.checkStateWaiter(waiter, this.state)) {
|
|
1103
|
-
return;
|
|
1104
|
-
}
|
|
1105
|
-
cleanup();
|
|
1106
|
-
reject(new DaytonaError_1.DaytonaTimeoutError(timeoutMessage));
|
|
1107
|
-
}
|
|
1108
|
-
})();
|
|
1109
|
-
}, timeout * 1000);
|
|
1110
|
-
}
|
|
1111
|
-
// Poll as a safety net for missed state changes. With an active event subscription
|
|
1112
|
-
// a sparse 1s cadence suffices; without events (polling mode) replicate main's
|
|
1113
|
-
// cadence exactly: 100ms steady for the first 5s, then exponential backoff capped at 1s.
|
|
1114
|
-
const streaming = !!this.subId;
|
|
1115
|
-
const pollStart = Date.now();
|
|
1116
|
-
let pollInterval = streaming ? 1000 : 100;
|
|
1117
|
-
const doPoll = async () => {
|
|
1118
|
-
if (settled)
|
|
1119
|
-
return;
|
|
1120
|
-
let refreshed = false;
|
|
1121
|
-
try {
|
|
1122
|
-
if (safeRefresh) {
|
|
1123
|
-
refreshed = await this.refreshDataSafe();
|
|
1124
|
-
}
|
|
1125
|
-
else {
|
|
1126
|
-
await this.refreshData();
|
|
1127
|
-
refreshed = true;
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
catch (error) {
|
|
1131
|
-
waiter.reject(error instanceof Error ? error : new DaytonaError_1.DaytonaError(String(error)));
|
|
1132
|
-
return;
|
|
1133
|
-
}
|
|
1134
|
-
if (!settled) {
|
|
1135
|
-
// Evaluate the refreshed state explicitly: applyState() no-ops when the
|
|
1136
|
-
// state is unchanged, so a persistent error state would otherwise never
|
|
1137
|
-
// reject the waiter. Only evaluate when the refresh succeeded — a failed
|
|
1138
|
-
// safe refresh leaves the cached state stale, so keep polling instead.
|
|
1139
|
-
if (refreshed && this.checkStateWaiter(waiter, this.state)) {
|
|
1140
|
-
return;
|
|
1141
|
-
}
|
|
1142
|
-
if (!streaming && Date.now() - pollStart > 5000) {
|
|
1143
|
-
pollInterval = Math.min(pollInterval * 1.1, 1000);
|
|
1144
|
-
}
|
|
1145
|
-
pollTimer = setTimeout(() => {
|
|
1146
|
-
void doPoll();
|
|
1147
|
-
}, pollInterval);
|
|
1148
|
-
}
|
|
1149
|
-
};
|
|
1150
|
-
// First poll runs immediately (main refreshed before any state evaluation).
|
|
1151
|
-
void doPoll();
|
|
1152
|
-
});
|
|
1153
|
-
}
|
|
1154
1011
|
/**
|
|
1155
1012
|
* Assigns the API sandbox data to the Sandbox object.
|
|
1156
1013
|
*
|
|
@@ -1158,7 +1015,6 @@ let Sandbox = (() => {
|
|
|
1158
1015
|
* @returns {void}
|
|
1159
1016
|
*/
|
|
1160
1017
|
processSandboxDto(sandboxDto) {
|
|
1161
|
-
const newState = sandboxDto.state;
|
|
1162
1018
|
this.id = sandboxDto.id;
|
|
1163
1019
|
this.name = sandboxDto.name;
|
|
1164
1020
|
this.organizationId = sandboxDto.organizationId;
|
|
@@ -1171,16 +1027,17 @@ let Sandbox = (() => {
|
|
|
1171
1027
|
this.gpu = sandboxDto.gpu;
|
|
1172
1028
|
this.memory = sandboxDto.memory;
|
|
1173
1029
|
this.disk = sandboxDto.disk;
|
|
1030
|
+
this.state = sandboxDto.state;
|
|
1174
1031
|
this.errorReason = sandboxDto.errorReason;
|
|
1175
1032
|
this.recoverable = sandboxDto.recoverable;
|
|
1176
1033
|
this.backupState = sandboxDto.backupState;
|
|
1177
1034
|
this.autoStopInterval = sandboxDto.autoStopInterval;
|
|
1178
|
-
this.autoPauseInterval = sandboxDto.autoPauseInterval;
|
|
1179
1035
|
this.autoArchiveInterval = sandboxDto.autoArchiveInterval;
|
|
1180
1036
|
this.autoDeleteInterval = sandboxDto.autoDeleteInterval;
|
|
1181
1037
|
this.createdAt = sandboxDto.createdAt;
|
|
1182
1038
|
this.updatedAt = sandboxDto.updatedAt;
|
|
1183
1039
|
this.lastActivityAt = sandboxDto.lastActivityAt;
|
|
1040
|
+
this.toolboxProxyUrl = sandboxDto.toolboxProxyUrl;
|
|
1184
1041
|
// Fields only present in the full SandboxDto (not returned by list endpoint)
|
|
1185
1042
|
if ('env' in sandboxDto) {
|
|
1186
1043
|
this.env = sandboxDto.env;
|
|
@@ -1192,21 +1049,6 @@ let Sandbox = (() => {
|
|
|
1192
1049
|
this.buildInfo = sandboxDto.buildInfo;
|
|
1193
1050
|
this.backupCreatedAt = sandboxDto.backupCreatedAt;
|
|
1194
1051
|
}
|
|
1195
|
-
const newProxyUrl = sandboxDto.toolboxProxyUrl;
|
|
1196
|
-
if (newProxyUrl && newProxyUrl !== this.toolboxProxyUrl && this.axiosInstance) {
|
|
1197
|
-
let baseUrl = newProxyUrl;
|
|
1198
|
-
if (!baseUrl.endsWith('/')) {
|
|
1199
|
-
baseUrl += '/';
|
|
1200
|
-
}
|
|
1201
|
-
this.axiosInstance.defaults.baseURL = baseUrl + this.id;
|
|
1202
|
-
this.clientConfig.basePath = this.axiosInstance.defaults.baseURL;
|
|
1203
|
-
}
|
|
1204
|
-
if (newProxyUrl) {
|
|
1205
|
-
this.toolboxProxyUrl = newProxyUrl;
|
|
1206
|
-
}
|
|
1207
|
-
if (newState) {
|
|
1208
|
-
this.applyState(newState);
|
|
1209
|
-
}
|
|
1210
1052
|
}
|
|
1211
1053
|
/**
|
|
1212
1054
|
* Refreshes the Sandbox data from the API, but does not throw an error if the sandbox has been deleted.
|
|
@@ -1214,113 +1056,17 @@ let Sandbox = (() => {
|
|
|
1214
1056
|
*
|
|
1215
1057
|
* @returns {Promise<void>}
|
|
1216
1058
|
*/
|
|
1217
|
-
/**
|
|
1218
|
-
* @returns true when the refresh produced authoritative state (success, or 404
|
|
1219
|
-
* mapped to DESTROYED); false when a transient error was swallowed and
|
|
1220
|
-
* the cached state is stale.
|
|
1221
|
-
*/
|
|
1222
1059
|
async refreshDataSafe() {
|
|
1223
1060
|
try {
|
|
1224
1061
|
await this.refreshData();
|
|
1225
|
-
return true;
|
|
1226
1062
|
}
|
|
1227
1063
|
catch (error) {
|
|
1228
1064
|
if (error instanceof DaytonaError_1.DaytonaNotFoundError) {
|
|
1229
|
-
this.
|
|
1230
|
-
return true;
|
|
1065
|
+
this.state = api_client_1.SandboxState.DESTROYED;
|
|
1231
1066
|
}
|
|
1232
|
-
// Other errors are deliberately swallowed (parity with main): transient
|
|
1233
|
-
// failures (e.g. 502) mid-poll must not abort stop()/delete() waits.
|
|
1234
|
-
return false;
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
applyState(newState) {
|
|
1238
|
-
if (newState === this.state) {
|
|
1239
|
-
return;
|
|
1240
|
-
}
|
|
1241
|
-
this.state = newState;
|
|
1242
|
-
for (const waiter of [...this.stateWaiters]) {
|
|
1243
|
-
this.checkStateWaiter(waiter, this.state);
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
checkStateWaiter(waiter, state) {
|
|
1247
|
-
if (!state) {
|
|
1248
|
-
return false;
|
|
1249
|
-
}
|
|
1250
|
-
if (waiter.targetStates.has(state)) {
|
|
1251
|
-
waiter.resolve(state);
|
|
1252
|
-
return true;
|
|
1253
|
-
}
|
|
1254
|
-
if (waiter.errorStates.has(state)) {
|
|
1255
|
-
const errorMessageFn = this.stateWaiterErrorMessageFns.get(waiter);
|
|
1256
|
-
waiter.reject(new DaytonaError_1.DaytonaError(errorMessageFn ? errorMessageFn(state) : `Sandbox ${this.id} failed with state: ${state}`));
|
|
1257
|
-
return true;
|
|
1258
1067
|
}
|
|
1259
|
-
return false;
|
|
1260
|
-
}
|
|
1261
|
-
removeStateWaiter(waiter) {
|
|
1262
|
-
const index = this.stateWaiters.indexOf(waiter);
|
|
1263
|
-
if (index !== -1) {
|
|
1264
|
-
this.stateWaiters.splice(index, 1);
|
|
1265
|
-
}
|
|
1266
|
-
this.stateWaiterErrorMessageFns.delete(waiter);
|
|
1267
1068
|
}
|
|
1268
1069
|
};
|
|
1269
1070
|
})();
|
|
1270
1071
|
exports.Sandbox = Sandbox;
|
|
1271
|
-
const SANDBOX_METRIC_FIELD_BY_NAME = {
|
|
1272
|
-
'daytona.sandbox.cpu.utilization': 'cpuUsedPct',
|
|
1273
|
-
'daytona.sandbox.cpu.limit': 'cpuCount',
|
|
1274
|
-
'daytona.sandbox.memory.usage': 'memUsed',
|
|
1275
|
-
'daytona.sandbox.memory.limit': 'memTotal',
|
|
1276
|
-
'daytona.sandbox.memory.cache': 'memCache',
|
|
1277
|
-
'daytona.sandbox.filesystem.usage': 'diskUsed',
|
|
1278
|
-
'daytona.sandbox.filesystem.total': 'diskTotal',
|
|
1279
|
-
};
|
|
1280
|
-
const SANDBOX_METRIC_NAMES = Object.keys(SANDBOX_METRIC_FIELD_BY_NAME);
|
|
1281
|
-
function sandboxMetricsFromSystemMetrics(m) {
|
|
1282
|
-
return {
|
|
1283
|
-
cpuCount: m.cpuCount ?? 0,
|
|
1284
|
-
cpuUsedPct: m.cpuUsedPct ?? 0,
|
|
1285
|
-
diskTotal: m.diskTotal ?? 0,
|
|
1286
|
-
diskUsed: m.diskUsed ?? 0,
|
|
1287
|
-
memTotal: m.memTotal ?? 0,
|
|
1288
|
-
memUsed: m.memUsed ?? 0,
|
|
1289
|
-
memCache: m.memCache ?? 0,
|
|
1290
|
-
timestamp: m.timestamp ? new Date(m.timestamp) : new Date(),
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1293
|
-
function buildSandboxMetrics(buckets) {
|
|
1294
|
-
return [...buckets.keys()].sort().map((ts) => {
|
|
1295
|
-
const v = buckets.get(ts);
|
|
1296
|
-
return {
|
|
1297
|
-
cpuCount: v.cpuCount ?? 0,
|
|
1298
|
-
cpuUsedPct: v.cpuUsedPct ?? 0,
|
|
1299
|
-
diskTotal: v.diskTotal ?? 0,
|
|
1300
|
-
diskUsed: v.diskUsed ?? 0,
|
|
1301
|
-
memTotal: v.memTotal ?? 0,
|
|
1302
|
-
memUsed: v.memUsed ?? 0,
|
|
1303
|
-
memCache: v.memCache ?? 0,
|
|
1304
|
-
timestamp: new Date(ts),
|
|
1305
|
-
};
|
|
1306
|
-
});
|
|
1307
|
-
}
|
|
1308
|
-
function pivotMetricTriples(triples) {
|
|
1309
|
-
const buckets = new Map();
|
|
1310
|
-
for (const [name, timestamp, value] of triples) {
|
|
1311
|
-
const field = name ? SANDBOX_METRIC_FIELD_BY_NAME[name] : undefined;
|
|
1312
|
-
if (!field || timestamp === undefined || value === undefined)
|
|
1313
|
-
continue;
|
|
1314
|
-
const bucket = buckets.get(timestamp) ?? {};
|
|
1315
|
-
bucket[field] = value;
|
|
1316
|
-
buckets.set(timestamp, bucket);
|
|
1317
|
-
}
|
|
1318
|
-
return buildSandboxMetrics(buckets);
|
|
1319
|
-
}
|
|
1320
|
-
function pivotSandboxMetrics(series) {
|
|
1321
|
-
return pivotMetricTriples((series ?? []).flatMap((s) => (s.dataPoints ?? []).map((p) => [s.metricName, p.timestamp, p.value])));
|
|
1322
|
-
}
|
|
1323
|
-
function pivotSandboxMetricPoints(points) {
|
|
1324
|
-
return pivotMetricTriples((points ?? []).map((p) => [p.metricName, p.timestamp, p.value]));
|
|
1325
|
-
}
|
|
1326
1072
|
//# sourceMappingURL=Sandbox.js.map
|