@miosa/sdk 3.2.1 → 3.2.3
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/README.md +24 -0
- package/dist/index.d.ts +71 -24
- package/dist/index.js +235 -95
- package/dist/index.js.map +1 -1
- package/package.json +13 -12
package/dist/index.js
CHANGED
|
@@ -9,18 +9,23 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
// src/errors.ts
|
|
12
|
+
function defaultRetryable(status) {
|
|
13
|
+
return status === 408 || status === 429 || status >= 500;
|
|
14
|
+
}
|
|
12
15
|
var MiosaError = class _MiosaError extends Error {
|
|
13
16
|
status;
|
|
14
17
|
code;
|
|
15
18
|
details;
|
|
16
19
|
requestId;
|
|
17
|
-
|
|
20
|
+
retryable;
|
|
21
|
+
constructor(message, status, code, details, requestId, retryable = defaultRetryable(status)) {
|
|
18
22
|
super(message);
|
|
19
23
|
this.name = "MiosaError";
|
|
20
24
|
this.status = status;
|
|
21
25
|
this.code = code;
|
|
22
26
|
this.details = details;
|
|
23
27
|
this.requestId = requestId;
|
|
28
|
+
this.retryable = retryable;
|
|
24
29
|
}
|
|
25
30
|
static fromResponse(status, body5, requestId) {
|
|
26
31
|
const nested = typeof body5.error === "object" ? body5.error : void 0;
|
|
@@ -30,145 +35,146 @@ var MiosaError = class _MiosaError extends Error {
|
|
|
30
35
|
const code = nested?.code ?? body5.code ?? (flatErrorIsCode ? flatError : "UNKNOWN_ERROR");
|
|
31
36
|
const details = nested?.details ?? body5.details ?? (body5.detail || body5.reason ? { detail: body5.detail, reason: body5.reason } : void 0);
|
|
32
37
|
requestId ??= body5.request_id;
|
|
33
|
-
const
|
|
38
|
+
const retryable = nested?.retryable ?? body5.retryable ?? defaultRetryable(status);
|
|
39
|
+
const connectError = connectErrorFromCode(code, message, status, details, requestId, retryable);
|
|
34
40
|
if (connectError) return connectError;
|
|
35
41
|
if (status === 401 || status === 403) {
|
|
36
|
-
return new AuthError(message, status, code, details, requestId);
|
|
42
|
+
return new AuthError(message, status, code, details, requestId, retryable);
|
|
37
43
|
}
|
|
38
44
|
if (status === 404) {
|
|
39
|
-
return new NotFoundError(message, code, details, requestId);
|
|
45
|
+
return new NotFoundError(message, code, details, requestId, retryable);
|
|
40
46
|
}
|
|
41
47
|
if (status === 429) {
|
|
42
|
-
return new RateLimitError(message, details, requestId);
|
|
48
|
+
return new RateLimitError(message, details, requestId, void 0, retryable);
|
|
43
49
|
}
|
|
44
50
|
if (status === 402) {
|
|
45
|
-
return new InsufficientCreditsError(message, details, requestId);
|
|
51
|
+
return new InsufficientCreditsError(message, details, requestId, retryable);
|
|
46
52
|
}
|
|
47
53
|
if (status >= 400 && status < 500) {
|
|
48
|
-
return new ValidationError(message, status, code, details, requestId);
|
|
54
|
+
return new ValidationError(message, status, code, details, requestId, retryable);
|
|
49
55
|
}
|
|
50
|
-
return new _MiosaError(message, status, code, details, requestId);
|
|
56
|
+
return new _MiosaError(message, status, code, details, requestId, retryable);
|
|
51
57
|
}
|
|
52
58
|
};
|
|
53
|
-
function connectErrorFromCode(code, message, status, details, requestId) {
|
|
59
|
+
function connectErrorFromCode(code, message, status, details, requestId, retryable) {
|
|
54
60
|
switch (code) {
|
|
55
61
|
case "PROJECT_NOT_LINKED":
|
|
56
|
-
return new ProjectNotLinkedError(message, status, details, requestId);
|
|
62
|
+
return new ProjectNotLinkedError(message, status, details, requestId, retryable);
|
|
57
63
|
case "SUBJECT_NOT_ALLOWED":
|
|
58
|
-
return new SubjectNotAllowedError(message, status, details, requestId);
|
|
64
|
+
return new SubjectNotAllowedError(message, status, details, requestId, retryable);
|
|
59
65
|
case "SCOPE_NOT_ALLOWED":
|
|
60
|
-
return new ScopeNotAllowedError(message, status, details, requestId);
|
|
66
|
+
return new ScopeNotAllowedError(message, status, details, requestId, retryable);
|
|
61
67
|
case "MANAGED_PROVIDER_BINDING_ONLY":
|
|
62
|
-
return new ManagedProviderBindingOnlyError(message, status, details, requestId);
|
|
68
|
+
return new ManagedProviderBindingOnlyError(message, status, details, requestId, retryable);
|
|
63
69
|
case "INSTALLATION_REQUIRED":
|
|
64
|
-
return new InstallationRequiredError(message, status, details, requestId);
|
|
70
|
+
return new InstallationRequiredError(message, status, details, requestId, retryable);
|
|
65
71
|
case "USER_AUTHORIZATION_REQUIRED":
|
|
66
|
-
return new UserAuthorizationRequiredError(message, status, details, requestId);
|
|
72
|
+
return new UserAuthorizationRequiredError(message, status, details, requestId, retryable);
|
|
67
73
|
case "EGRESS_HOST_NOT_ALLOWED":
|
|
68
|
-
return new EgressHostNotAllowedError(message, status, details, requestId);
|
|
74
|
+
return new EgressHostNotAllowedError(message, status, details, requestId, retryable);
|
|
69
75
|
case "TOKEN_REFRESH_FAILED":
|
|
70
|
-
return new TokenRefreshFailedError(message, status, details, requestId);
|
|
76
|
+
return new TokenRefreshFailedError(message, status, details, requestId, retryable);
|
|
71
77
|
default:
|
|
72
78
|
return void 0;
|
|
73
79
|
}
|
|
74
80
|
}
|
|
75
81
|
var AuthError = class extends MiosaError {
|
|
76
|
-
constructor(message, status = 401, code = "UNAUTHORIZED", details, requestId) {
|
|
77
|
-
super(message, status, code, details, requestId);
|
|
82
|
+
constructor(message, status = 401, code = "UNAUTHORIZED", details, requestId, retryable) {
|
|
83
|
+
super(message, status, code, details, requestId, retryable);
|
|
78
84
|
this.name = "AuthError";
|
|
79
85
|
}
|
|
80
86
|
};
|
|
81
87
|
var NotFoundError = class extends MiosaError {
|
|
82
|
-
constructor(message, code = "NOT_FOUND", details, requestId) {
|
|
83
|
-
super(message, 404, code, details, requestId);
|
|
88
|
+
constructor(message, code = "NOT_FOUND", details, requestId, retryable) {
|
|
89
|
+
super(message, 404, code, details, requestId, retryable);
|
|
84
90
|
this.name = "NotFoundError";
|
|
85
91
|
}
|
|
86
92
|
};
|
|
87
93
|
var RateLimitError = class extends MiosaError {
|
|
88
94
|
retryAfter;
|
|
89
|
-
constructor(message, details, requestId, retryAfter) {
|
|
90
|
-
super(message, 429, "RATE_LIMITED", details, requestId);
|
|
95
|
+
constructor(message, details, requestId, retryAfter, retryable) {
|
|
96
|
+
super(message, 429, "RATE_LIMITED", details, requestId, retryable);
|
|
91
97
|
this.name = "RateLimitError";
|
|
92
98
|
this.retryAfter = retryAfter;
|
|
93
99
|
}
|
|
94
100
|
};
|
|
95
101
|
var InsufficientCreditsError = class extends MiosaError {
|
|
96
|
-
constructor(message, details, requestId) {
|
|
97
|
-
super(message, 402, "INSUFFICIENT_CREDITS", details, requestId);
|
|
102
|
+
constructor(message, details, requestId, retryable) {
|
|
103
|
+
super(message, 402, "INSUFFICIENT_CREDITS", details, requestId, retryable);
|
|
98
104
|
this.name = "InsufficientCreditsError";
|
|
99
105
|
}
|
|
100
106
|
};
|
|
101
107
|
var ValidationError = class extends MiosaError {
|
|
102
|
-
constructor(message, status, code = "VALIDATION_ERROR", details, requestId) {
|
|
103
|
-
super(message, status, code, details, requestId);
|
|
108
|
+
constructor(message, status, code = "VALIDATION_ERROR", details, requestId, retryable) {
|
|
109
|
+
super(message, status, code, details, requestId, retryable);
|
|
104
110
|
this.name = "ValidationError";
|
|
105
111
|
}
|
|
106
112
|
};
|
|
107
113
|
var TimeoutError = class extends MiosaError {
|
|
108
114
|
constructor(message = "Request timed out") {
|
|
109
|
-
super(message, 408, "TIMEOUT");
|
|
115
|
+
super(message, 408, "TIMEOUT", void 0, void 0, true);
|
|
110
116
|
this.name = "TimeoutError";
|
|
111
117
|
}
|
|
112
118
|
};
|
|
113
119
|
var NetworkError = class extends MiosaError {
|
|
114
120
|
cause;
|
|
115
121
|
constructor(message, cause) {
|
|
116
|
-
super(message, 0, "NETWORK_ERROR");
|
|
122
|
+
super(message, 0, "NETWORK_ERROR", void 0, void 0, true);
|
|
117
123
|
this.name = "NetworkError";
|
|
118
124
|
this.cause = cause;
|
|
119
125
|
}
|
|
120
126
|
};
|
|
121
127
|
var ProjectNotLinkedError = class extends MiosaError {
|
|
122
|
-
constructor(message, status = 403, details, requestId) {
|
|
123
|
-
super(message, status, "PROJECT_NOT_LINKED", details, requestId);
|
|
128
|
+
constructor(message, status = 403, details, requestId, retryable) {
|
|
129
|
+
super(message, status, "PROJECT_NOT_LINKED", details, requestId, retryable);
|
|
124
130
|
this.name = "ProjectNotLinkedError";
|
|
125
131
|
}
|
|
126
132
|
};
|
|
127
133
|
var SubjectNotAllowedError = class extends MiosaError {
|
|
128
|
-
constructor(message, status = 403, details, requestId) {
|
|
129
|
-
super(message, status, "SUBJECT_NOT_ALLOWED", details, requestId);
|
|
134
|
+
constructor(message, status = 403, details, requestId, retryable) {
|
|
135
|
+
super(message, status, "SUBJECT_NOT_ALLOWED", details, requestId, retryable);
|
|
130
136
|
this.name = "SubjectNotAllowedError";
|
|
131
137
|
}
|
|
132
138
|
};
|
|
133
139
|
var ScopeNotAllowedError = class extends MiosaError {
|
|
134
|
-
constructor(message, status = 403, details, requestId) {
|
|
135
|
-
super(message, status, "SCOPE_NOT_ALLOWED", details, requestId);
|
|
140
|
+
constructor(message, status = 403, details, requestId, retryable) {
|
|
141
|
+
super(message, status, "SCOPE_NOT_ALLOWED", details, requestId, retryable);
|
|
136
142
|
this.name = "ScopeNotAllowedError";
|
|
137
143
|
}
|
|
138
144
|
};
|
|
139
145
|
var ManagedProviderBindingOnlyError = class extends MiosaError {
|
|
140
|
-
constructor(message, status = 403, details, requestId) {
|
|
141
|
-
super(message, status, "MANAGED_PROVIDER_BINDING_ONLY", details, requestId);
|
|
146
|
+
constructor(message, status = 403, details, requestId, retryable) {
|
|
147
|
+
super(message, status, "MANAGED_PROVIDER_BINDING_ONLY", details, requestId, retryable);
|
|
142
148
|
this.name = "ManagedProviderBindingOnlyError";
|
|
143
149
|
}
|
|
144
150
|
};
|
|
145
151
|
var InstallationRequiredError = class extends MiosaError {
|
|
146
|
-
constructor(message, status = 409, details, requestId) {
|
|
147
|
-
super(message, status, "INSTALLATION_REQUIRED", details, requestId);
|
|
152
|
+
constructor(message, status = 409, details, requestId, retryable) {
|
|
153
|
+
super(message, status, "INSTALLATION_REQUIRED", details, requestId, retryable);
|
|
148
154
|
this.name = "InstallationRequiredError";
|
|
149
155
|
}
|
|
150
156
|
};
|
|
151
157
|
var UserAuthorizationRequiredError = class extends MiosaError {
|
|
152
|
-
constructor(message, status = 403, details, requestId) {
|
|
153
|
-
super(message, status, "USER_AUTHORIZATION_REQUIRED", details, requestId);
|
|
158
|
+
constructor(message, status = 403, details, requestId, retryable) {
|
|
159
|
+
super(message, status, "USER_AUTHORIZATION_REQUIRED", details, requestId, retryable);
|
|
154
160
|
this.name = "UserAuthorizationRequiredError";
|
|
155
161
|
}
|
|
156
162
|
};
|
|
157
163
|
var EgressHostNotAllowedError = class extends MiosaError {
|
|
158
|
-
constructor(message, status = 403, details, requestId) {
|
|
159
|
-
super(message, status, "EGRESS_HOST_NOT_ALLOWED", details, requestId);
|
|
164
|
+
constructor(message, status = 403, details, requestId, retryable) {
|
|
165
|
+
super(message, status, "EGRESS_HOST_NOT_ALLOWED", details, requestId, retryable);
|
|
160
166
|
this.name = "EgressHostNotAllowedError";
|
|
161
167
|
}
|
|
162
168
|
};
|
|
163
169
|
var TokenRefreshFailedError = class extends MiosaError {
|
|
164
|
-
constructor(message, status = 502, details, requestId) {
|
|
165
|
-
super(message, status, "TOKEN_REFRESH_FAILED", details, requestId);
|
|
170
|
+
constructor(message, status = 502, details, requestId, retryable) {
|
|
171
|
+
super(message, status, "TOKEN_REFRESH_FAILED", details, requestId, retryable);
|
|
166
172
|
this.name = "TokenRefreshFailedError";
|
|
167
173
|
}
|
|
168
174
|
};
|
|
169
175
|
|
|
170
176
|
// src/version.ts
|
|
171
|
-
var SDK_VERSION = "3.2.
|
|
177
|
+
var SDK_VERSION = "3.2.3";
|
|
172
178
|
var SDK_USER_AGENT = `@miosa/sdk/${SDK_VERSION}`;
|
|
173
179
|
|
|
174
180
|
// src/http.ts
|
|
@@ -265,8 +271,12 @@ var HttpClient = class {
|
|
|
265
271
|
fetchBody = JSON.stringify(body5);
|
|
266
272
|
}
|
|
267
273
|
let lastError;
|
|
268
|
-
|
|
274
|
+
const maxRetries = options.maxRetries ?? this.maxRetries;
|
|
275
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
276
|
+
options.signal?.throwIfAborted();
|
|
269
277
|
const controller = new AbortController();
|
|
278
|
+
const cancel = () => controller.abort(options.signal?.reason);
|
|
279
|
+
options.signal?.addEventListener("abort", cancel, { once: true });
|
|
270
280
|
const timer = setTimeout(() => controller.abort(), timeout);
|
|
271
281
|
try {
|
|
272
282
|
const response = await fetch(this.buildUrl(path), {
|
|
@@ -290,7 +300,7 @@ var HttpClient = class {
|
|
|
290
300
|
errorBody,
|
|
291
301
|
requestId
|
|
292
302
|
);
|
|
293
|
-
if (RETRY_STATUS.has(response.status) && attempt <
|
|
303
|
+
if (RETRY_STATUS.has(response.status) && attempt < maxRetries) {
|
|
294
304
|
lastError = error;
|
|
295
305
|
await sleep(retryDelay(attempt, retryAfter));
|
|
296
306
|
continue;
|
|
@@ -307,7 +317,7 @@ var HttpClient = class {
|
|
|
307
317
|
if (options.rawResponse) return response;
|
|
308
318
|
return await response.json();
|
|
309
319
|
} catch (err) {
|
|
310
|
-
|
|
320
|
+
options.signal?.throwIfAborted();
|
|
311
321
|
if (err instanceof MiosaError) {
|
|
312
322
|
throw err;
|
|
313
323
|
}
|
|
@@ -321,7 +331,7 @@ var HttpClient = class {
|
|
|
321
331
|
`Network error reaching ${path}: ${err.message}`,
|
|
322
332
|
err
|
|
323
333
|
);
|
|
324
|
-
if (attempt <
|
|
334
|
+
if (attempt < maxRetries) {
|
|
325
335
|
lastError = networkErr;
|
|
326
336
|
await sleep(retryDelay(attempt, null));
|
|
327
337
|
continue;
|
|
@@ -329,9 +339,19 @@ var HttpClient = class {
|
|
|
329
339
|
throw networkErr;
|
|
330
340
|
}
|
|
331
341
|
throw err;
|
|
342
|
+
} finally {
|
|
343
|
+
clearTimeout(timer);
|
|
344
|
+
options.signal?.removeEventListener("abort", cancel);
|
|
332
345
|
}
|
|
333
346
|
}
|
|
334
|
-
throw lastError ?? new MiosaError(
|
|
347
|
+
throw lastError ?? new MiosaError(
|
|
348
|
+
"Max retries exceeded",
|
|
349
|
+
0,
|
|
350
|
+
"MAX_RETRIES_EXCEEDED",
|
|
351
|
+
void 0,
|
|
352
|
+
void 0,
|
|
353
|
+
true
|
|
354
|
+
);
|
|
335
355
|
}
|
|
336
356
|
async get(path, query3) {
|
|
337
357
|
let fullPath = path;
|
|
@@ -381,44 +401,52 @@ var HttpClient = class {
|
|
|
381
401
|
headers = { ...headers, "Content-Type": "application/json" };
|
|
382
402
|
body5 = JSON.stringify(options.body);
|
|
383
403
|
}
|
|
404
|
+
options.signal?.throwIfAborted();
|
|
384
405
|
const controller = new AbortController();
|
|
385
|
-
|
|
406
|
+
const cancel = () => controller.abort(options.signal?.reason);
|
|
407
|
+
options.signal?.addEventListener("abort", cancel, { once: true });
|
|
408
|
+
const deadline = options.timeout === void 0 ? void 0 : new TimeoutError(
|
|
409
|
+
`Stream ${path} timed out after ${options.timeout}ms`
|
|
410
|
+
);
|
|
411
|
+
const timer = deadline === void 0 ? void 0 : setTimeout(() => controller.abort(deadline), options.timeout);
|
|
412
|
+
let reader;
|
|
386
413
|
try {
|
|
387
|
-
response
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
414
|
+
let response;
|
|
415
|
+
try {
|
|
416
|
+
response = await fetch(this.buildUrl(path), {
|
|
417
|
+
method,
|
|
418
|
+
headers,
|
|
419
|
+
body: body5,
|
|
420
|
+
signal: controller.signal
|
|
421
|
+
});
|
|
422
|
+
} catch (err) {
|
|
423
|
+
if (err instanceof TypeError) {
|
|
424
|
+
throw new NetworkError(
|
|
425
|
+
`Network error opening SSE stream: ${err.message}`,
|
|
426
|
+
err
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
throw err;
|
|
430
|
+
}
|
|
431
|
+
if (!response.ok) {
|
|
432
|
+
let errorBody = {};
|
|
433
|
+
try {
|
|
434
|
+
errorBody = await response.json();
|
|
435
|
+
} catch {
|
|
436
|
+
}
|
|
437
|
+
throw MiosaError.fromResponse(
|
|
438
|
+
response.status,
|
|
439
|
+
errorBody,
|
|
440
|
+
response.headers.get("x-request-id") ?? void 0
|
|
398
441
|
);
|
|
399
442
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
if (!response.ok) {
|
|
403
|
-
let errorBody = {};
|
|
404
|
-
try {
|
|
405
|
-
errorBody = await response.json();
|
|
406
|
-
} catch {
|
|
443
|
+
if (!response.body) {
|
|
444
|
+
throw new MiosaError("SSE stream has no body", 0, "NO_STREAM_BODY");
|
|
407
445
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
if (!response.body) {
|
|
415
|
-
throw new MiosaError("SSE stream has no body", 0, "NO_STREAM_BODY");
|
|
416
|
-
}
|
|
417
|
-
const reader = response.body.getReader();
|
|
418
|
-
const decoder = new TextDecoder();
|
|
419
|
-
let buffer = "";
|
|
420
|
-
let event = null;
|
|
421
|
-
try {
|
|
446
|
+
reader = response.body.getReader();
|
|
447
|
+
const decoder = new TextDecoder();
|
|
448
|
+
let buffer = "";
|
|
449
|
+
let event = null;
|
|
422
450
|
while (true) {
|
|
423
451
|
const { done, value } = await reader.read();
|
|
424
452
|
if (done) break;
|
|
@@ -446,9 +474,17 @@ var HttpClient = class {
|
|
|
446
474
|
}
|
|
447
475
|
}
|
|
448
476
|
}
|
|
477
|
+
} catch (err) {
|
|
478
|
+
options.signal?.throwIfAborted();
|
|
479
|
+
if (deadline !== void 0 && controller.signal.reason === deadline) {
|
|
480
|
+
throw deadline;
|
|
481
|
+
}
|
|
482
|
+
throw err;
|
|
449
483
|
} finally {
|
|
484
|
+
clearTimeout(timer);
|
|
485
|
+
options.signal?.removeEventListener("abort", cancel);
|
|
450
486
|
controller.abort();
|
|
451
|
-
reader
|
|
487
|
+
reader?.releaseLock();
|
|
452
488
|
}
|
|
453
489
|
}
|
|
454
490
|
/**
|
|
@@ -8615,6 +8651,16 @@ function raiseUnresolvableShape(cpuCount, memoryMb, tier) {
|
|
|
8615
8651
|
`Unsupported cpu/memory combination (${supplied}). Supported tiers: xs (1/2048), small (2/4096), medium (4/8192), large (8/16384), xl (16/32768). Pass a matching cpuCount + memoryMb (any diskSizeMb is allowed), use size, or supply cpuCount, memoryMb, and diskSizeMb together for a custom shape.`
|
|
8616
8652
|
);
|
|
8617
8653
|
}
|
|
8654
|
+
function execDeadline(options) {
|
|
8655
|
+
const seconds = options?.timeout ?? options?.timeoutSec ?? options?.timeout_sec;
|
|
8656
|
+
if (seconds === void 0) return {};
|
|
8657
|
+
if (!Number.isFinite(seconds) || seconds <= 0 || seconds > 86400) {
|
|
8658
|
+
throw new RangeError(
|
|
8659
|
+
"Exec timeout must be greater than zero and at most 86400 seconds"
|
|
8660
|
+
);
|
|
8661
|
+
}
|
|
8662
|
+
return { timeout: seconds * 1e3 + 5e3 };
|
|
8663
|
+
}
|
|
8618
8664
|
function execBody(command, options = {}) {
|
|
8619
8665
|
return stripUndefined26({
|
|
8620
8666
|
command,
|
|
@@ -8784,7 +8830,7 @@ var SandboxTerminal = class {
|
|
|
8784
8830
|
const response = unwrap48(
|
|
8785
8831
|
await this.sandbox.http.post(`/sandboxes/${this.sandbox.id}/terminal`, body5)
|
|
8786
8832
|
);
|
|
8787
|
-
return response;
|
|
8833
|
+
return normalizeTerminalSession(response);
|
|
8788
8834
|
}
|
|
8789
8835
|
async delete(sessionId) {
|
|
8790
8836
|
await this.sandbox.http.delete(
|
|
@@ -8792,6 +8838,35 @@ var SandboxTerminal = class {
|
|
|
8792
8838
|
);
|
|
8793
8839
|
}
|
|
8794
8840
|
};
|
|
8841
|
+
function normalizeTerminalSession(payload) {
|
|
8842
|
+
const sessionId = payload.session_id;
|
|
8843
|
+
const wsUrl = payload.ws_url;
|
|
8844
|
+
const streamAuth = payload.stream_auth;
|
|
8845
|
+
const streamAuthExpiresAt = payload.stream_auth_expires_at;
|
|
8846
|
+
const expiresAt = payload.expires_at;
|
|
8847
|
+
if (typeof sessionId !== "string" || typeof wsUrl !== "string" || typeof streamAuth !== "string" || typeof streamAuthExpiresAt !== "number" || typeof expiresAt !== "number") {
|
|
8848
|
+
throw new MiosaError(
|
|
8849
|
+
"Terminal create response did not contain a valid miosa-terminal-v1 session",
|
|
8850
|
+
502,
|
|
8851
|
+
"INVALID_TERMINAL_SESSION",
|
|
8852
|
+
{ received: Object.keys(payload) },
|
|
8853
|
+
void 0,
|
|
8854
|
+
false
|
|
8855
|
+
);
|
|
8856
|
+
}
|
|
8857
|
+
return {
|
|
8858
|
+
sessionId,
|
|
8859
|
+
wsUrl,
|
|
8860
|
+
streamAuth,
|
|
8861
|
+
streamAuthExpiresAt,
|
|
8862
|
+
expiresAt,
|
|
8863
|
+
session_id: sessionId,
|
|
8864
|
+
ws_url: wsUrl,
|
|
8865
|
+
stream_auth: streamAuth,
|
|
8866
|
+
stream_auth_expires_at: streamAuthExpiresAt,
|
|
8867
|
+
expires_at: expiresAt
|
|
8868
|
+
};
|
|
8869
|
+
}
|
|
8795
8870
|
var SandboxEvents = class {
|
|
8796
8871
|
constructor(sandbox) {
|
|
8797
8872
|
this.sandbox = sandbox;
|
|
@@ -9045,10 +9120,17 @@ var Sandbox = class _Sandbox {
|
|
|
9045
9120
|
}
|
|
9046
9121
|
async runExec(command, options) {
|
|
9047
9122
|
this.assertRunning("exec");
|
|
9123
|
+
if (options?.signal) return this.runCancellableExec(command, options);
|
|
9048
9124
|
const response = unwrap48(
|
|
9049
|
-
await this.http.
|
|
9125
|
+
await this.http.request(
|
|
9050
9126
|
`/sandboxes/${this.id}/exec`,
|
|
9051
|
-
|
|
9127
|
+
{
|
|
9128
|
+
method: "POST",
|
|
9129
|
+
body: execBody(command, options),
|
|
9130
|
+
signal: options?.signal,
|
|
9131
|
+
maxRetries: 0,
|
|
9132
|
+
...execDeadline(options)
|
|
9133
|
+
}
|
|
9052
9134
|
)
|
|
9053
9135
|
);
|
|
9054
9136
|
const exitCode = Number(response.exit_code ?? response.exitCode ?? 0);
|
|
@@ -9064,13 +9146,42 @@ var Sandbox = class _Sandbox {
|
|
|
9064
9146
|
}
|
|
9065
9147
|
return result;
|
|
9066
9148
|
}
|
|
9149
|
+
async runCancellableExec(command, options) {
|
|
9150
|
+
let stdout = "";
|
|
9151
|
+
let stderr = "";
|
|
9152
|
+
let exitCode;
|
|
9153
|
+
for await (const event of this.execStream(command, options)) {
|
|
9154
|
+
if (event.type === "stdout" && typeof event.line === "string")
|
|
9155
|
+
stdout += event.line;
|
|
9156
|
+
if (event.type === "stderr" && typeof event.line === "string")
|
|
9157
|
+
stderr += event.line;
|
|
9158
|
+
if (event.type === "exit" && typeof event.exit_code === "number") {
|
|
9159
|
+
exitCode = event.exit_code;
|
|
9160
|
+
break;
|
|
9161
|
+
}
|
|
9162
|
+
}
|
|
9163
|
+
options.signal?.throwIfAborted();
|
|
9164
|
+
if (exitCode === void 0) {
|
|
9165
|
+
throw new MiosaError(
|
|
9166
|
+
"Execution stream ended without a terminal result",
|
|
9167
|
+
502,
|
|
9168
|
+
"EXEC_STREAM_INCOMPLETE",
|
|
9169
|
+
void 0,
|
|
9170
|
+
void 0,
|
|
9171
|
+
false
|
|
9172
|
+
);
|
|
9173
|
+
}
|
|
9174
|
+
return { stdout, stderr, exitCode, exit_code: exitCode };
|
|
9175
|
+
}
|
|
9067
9176
|
execStream(command, options) {
|
|
9068
9177
|
this.assertRunning("exec.stream");
|
|
9069
9178
|
const frames = this.http.streamFrames(
|
|
9070
9179
|
`/sandboxes/${this.id}/exec/stream`,
|
|
9071
9180
|
{
|
|
9072
9181
|
method: "POST",
|
|
9073
|
-
body: execBody(command, options)
|
|
9182
|
+
body: execBody(command, options),
|
|
9183
|
+
signal: options?.signal,
|
|
9184
|
+
...execDeadline(options)
|
|
9074
9185
|
}
|
|
9075
9186
|
);
|
|
9076
9187
|
return (async function* () {
|
|
@@ -9520,7 +9631,9 @@ var Sandbox = class _Sandbox {
|
|
|
9520
9631
|
`Sandbox ${this.id} entered terminal state '${state}' while waiting for readiness`,
|
|
9521
9632
|
502,
|
|
9522
9633
|
"SANDBOX_BOOT_FAILED",
|
|
9523
|
-
{ sandbox_id: this.id, state }
|
|
9634
|
+
{ sandbox_id: this.id, state },
|
|
9635
|
+
void 0,
|
|
9636
|
+
false
|
|
9524
9637
|
);
|
|
9525
9638
|
}
|
|
9526
9639
|
/** Best-effort final check so an SSE `event: timeout` does not mask a boot failure that just landed. */
|
|
@@ -9612,12 +9725,21 @@ var Sandbox = class _Sandbox {
|
|
|
9612
9725
|
}
|
|
9613
9726
|
}
|
|
9614
9727
|
async destroy() {
|
|
9615
|
-
if (this.data.state === "destroyed")
|
|
9616
|
-
|
|
9728
|
+
if (this.data.state === "destroyed" || this.data.state === "destroying") {
|
|
9729
|
+
return;
|
|
9730
|
+
}
|
|
9731
|
+
const receipt = unwrap48(
|
|
9732
|
+
await this.http.delete(
|
|
9733
|
+
`/sandboxes/${this.id}`
|
|
9734
|
+
)
|
|
9735
|
+
) ?? {};
|
|
9736
|
+
const settled = receipt.state === "destroyed" || receipt.state === void 0 && receipt.operation_id == null;
|
|
9617
9737
|
this.data = {
|
|
9618
9738
|
...this.data,
|
|
9619
|
-
state: "destroyed",
|
|
9620
|
-
|
|
9739
|
+
state: settled ? "destroyed" : "destroying",
|
|
9740
|
+
ready: false,
|
|
9741
|
+
operation_id: receipt.operation_id ?? this.data.operation_id ?? null,
|
|
9742
|
+
destroyed_at: receipt.destroyed_at ?? this.data.destroyed_at ?? null
|
|
9621
9743
|
};
|
|
9622
9744
|
}
|
|
9623
9745
|
delete() {
|
|
@@ -9754,7 +9876,25 @@ var Sandboxes = class {
|
|
|
9754
9876
|
await sandbox.resume();
|
|
9755
9877
|
}
|
|
9756
9878
|
if (params.waitUntilReady === true) {
|
|
9757
|
-
await sandbox.waitUntilReady({
|
|
9879
|
+
const ready = await sandbox.waitUntilReady({
|
|
9880
|
+
timeout: params.waitTimeoutSec ?? 60
|
|
9881
|
+
});
|
|
9882
|
+
if (!ready) {
|
|
9883
|
+
throw new MiosaError(
|
|
9884
|
+
`Sandbox ${sandbox.id} did not become ready before the timeout`,
|
|
9885
|
+
504,
|
|
9886
|
+
"SANDBOX_READY_TIMEOUT"
|
|
9887
|
+
);
|
|
9888
|
+
}
|
|
9889
|
+
await sandbox.refresh();
|
|
9890
|
+
const executable = sandbox.state === "running" && (sandbox.ready || isCommandReady(await sandbox.readiness()));
|
|
9891
|
+
if (!executable) {
|
|
9892
|
+
throw new MiosaError(
|
|
9893
|
+
`Sandbox ${sandbox.id} reported readiness but is not executable`,
|
|
9894
|
+
409,
|
|
9895
|
+
"SANDBOX_NOT_READY"
|
|
9896
|
+
);
|
|
9897
|
+
}
|
|
9758
9898
|
}
|
|
9759
9899
|
return sandbox;
|
|
9760
9900
|
}
|