@miosa/sdk 3.0.0 → 3.0.2
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 +0 -44
- package/dist/index.d.ts +87 -180
- package/dist/index.js +81 -203
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -168,7 +168,7 @@ var TokenRefreshFailedError = class extends MiosaError {
|
|
|
168
168
|
};
|
|
169
169
|
|
|
170
170
|
// src/version.ts
|
|
171
|
-
var SDK_VERSION = "
|
|
171
|
+
var SDK_VERSION = "2.0.7";
|
|
172
172
|
var SDK_USER_AGENT = `@miosa/sdk/${SDK_VERSION}`;
|
|
173
173
|
|
|
174
174
|
// src/http.ts
|
|
@@ -198,7 +198,6 @@ async function ensureHttp2Agent() {
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
ensureHttp2Agent();
|
|
201
|
-
var SSE_ACCEPT = "text/event-stream, application/json;q=0.9";
|
|
202
201
|
var DEFAULT_TIMEOUT = 3e4;
|
|
203
202
|
var DEFAULT_MAX_RETRIES = 3;
|
|
204
203
|
var RETRY_STATUS = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
@@ -365,13 +364,16 @@ var HttpClient = class {
|
|
|
365
364
|
return this.request(path, { method: "POST", formData });
|
|
366
365
|
}
|
|
367
366
|
/**
|
|
368
|
-
* Open a Server-Sent Events stream
|
|
369
|
-
*
|
|
367
|
+
* Open a Server-Sent Events stream and yield each frame's parsed `data:`
|
|
368
|
+
* payload together with its SSE `event:` name (when present). Most callers
|
|
369
|
+
* want {@link stream}; use this when the event name carries meaning — e.g. the
|
|
370
|
+
* sandbox exec stream tags frames as `stdout` / `stderr` / `exit`. The caller
|
|
371
|
+
* is responsible for breaking the loop.
|
|
370
372
|
*/
|
|
371
|
-
async *
|
|
373
|
+
async *streamFrames(path, options = {}) {
|
|
372
374
|
const method = options.method ?? "GET";
|
|
373
375
|
let headers = this.baseHeaders({
|
|
374
|
-
Accept:
|
|
376
|
+
Accept: "text/event-stream",
|
|
375
377
|
...options.headers
|
|
376
378
|
});
|
|
377
379
|
let body5 = null;
|
|
@@ -409,14 +411,13 @@ var HttpClient = class {
|
|
|
409
411
|
response.headers.get("x-request-id") ?? void 0
|
|
410
412
|
);
|
|
411
413
|
}
|
|
412
|
-
if (response.status === 204) return;
|
|
413
414
|
if (!response.body) {
|
|
414
415
|
throw new MiosaError("SSE stream has no body", 0, "NO_STREAM_BODY");
|
|
415
416
|
}
|
|
416
417
|
const reader = response.body.getReader();
|
|
417
418
|
const decoder = new TextDecoder();
|
|
418
419
|
let buffer = "";
|
|
419
|
-
let
|
|
420
|
+
let event = null;
|
|
420
421
|
try {
|
|
421
422
|
while (true) {
|
|
422
423
|
const { done, value } = await reader.read();
|
|
@@ -424,23 +425,22 @@ var HttpClient = class {
|
|
|
424
425
|
buffer += decoder.decode(value, { stream: true });
|
|
425
426
|
const lines = buffer.split("\n");
|
|
426
427
|
buffer = lines.pop() ?? "";
|
|
427
|
-
for (const
|
|
428
|
-
|
|
429
|
-
|
|
428
|
+
for (const rawLine of lines) {
|
|
429
|
+
const line = rawLine.replace(/\r$/, "");
|
|
430
|
+
if (line === "") {
|
|
431
|
+
event = null;
|
|
430
432
|
continue;
|
|
431
433
|
}
|
|
432
|
-
if (line
|
|
433
|
-
|
|
434
|
+
if (line.startsWith(":")) continue;
|
|
435
|
+
if (line.startsWith("event:")) {
|
|
436
|
+
event = line.slice(6).trim();
|
|
434
437
|
continue;
|
|
435
438
|
}
|
|
436
439
|
if (line.startsWith("data:")) {
|
|
437
440
|
const raw = line.slice(5).trim();
|
|
438
|
-
const name = eventName;
|
|
439
|
-
eventName = void 0;
|
|
440
441
|
if (raw === "[DONE]" || raw === "") continue;
|
|
441
442
|
try {
|
|
442
|
-
|
|
443
|
-
yield name !== void 0 && parsed !== null && typeof parsed === "object" && !("type" in parsed) ? { type: name, ...parsed } : parsed;
|
|
443
|
+
yield { event, data: JSON.parse(raw) };
|
|
444
444
|
} catch {
|
|
445
445
|
}
|
|
446
446
|
}
|
|
@@ -451,6 +451,15 @@ var HttpClient = class {
|
|
|
451
451
|
reader.releaseLock();
|
|
452
452
|
}
|
|
453
453
|
}
|
|
454
|
+
/**
|
|
455
|
+
* Open a Server-Sent Events stream. Returns an AsyncIterableIterator of
|
|
456
|
+
* parsed event data objects. The caller is responsible for breaking the loop.
|
|
457
|
+
*/
|
|
458
|
+
async *stream(path, options = {}) {
|
|
459
|
+
for await (const frame of this.streamFrames(path, options)) {
|
|
460
|
+
yield frame.data;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
454
463
|
};
|
|
455
464
|
|
|
456
465
|
// src/resources/admin.ts
|
|
@@ -7788,130 +7797,41 @@ var Jobs = class {
|
|
|
7788
7797
|
return `/opencomputers/hosts/${hostId}`;
|
|
7789
7798
|
}
|
|
7790
7799
|
/**
|
|
7791
|
-
*
|
|
7792
|
-
*
|
|
7793
|
-
* Sends `stream: false` so the server buffers output and answers with JSON.
|
|
7794
|
-
* `stream` defaults to *true* server-side, which answers with a chunked SSE
|
|
7795
|
-
* body instead - use {@link runStream} for that.
|
|
7796
|
-
*
|
|
7797
|
-
* Note this holds the connection open for the life of the command
|
|
7798
|
-
* (`timeout_ms`, 30 s default), because the server does not answer until
|
|
7799
|
-
* the command finishes.
|
|
7800
|
+
* Dispatch a command to run on the remote host.
|
|
7800
7801
|
*/
|
|
7801
7802
|
async run(hostId, params) {
|
|
7802
|
-
return this.http.post(`${this.base(hostId)}/exec`,
|
|
7803
|
-
...params,
|
|
7804
|
-
stream: false
|
|
7805
|
-
});
|
|
7803
|
+
return this.http.post(`${this.base(hostId)}/exec`, params);
|
|
7806
7804
|
}
|
|
7807
7805
|
/**
|
|
7808
|
-
*
|
|
7809
|
-
*
|
|
7810
|
-
* Same endpoint as {@link run} with `stream: true`, which is the server's
|
|
7811
|
-
* default mode. Terminates with one `exec_result` or one `exec_error`.
|
|
7806
|
+
* List all jobs for a host.
|
|
7812
7807
|
*/
|
|
7813
|
-
|
|
7814
|
-
return
|
|
7815
|
-
this.http.stream(`${this.base(hostId)}/exec`, {
|
|
7816
|
-
method: "POST",
|
|
7817
|
-
body: { ...params, stream: true }
|
|
7818
|
-
})
|
|
7819
|
-
);
|
|
7820
|
-
}
|
|
7821
|
-
/**
|
|
7822
|
-
* List recent job activity for a host.
|
|
7823
|
-
*
|
|
7824
|
-
* Hits `/jobs`, not `/exec`: there is no `GET /opencomputers/hosts/{id}/exec`
|
|
7825
|
-
* route on the server, so the old path 404'd. The rows are audit-log events
|
|
7826
|
-
* about jobs (`job.dispatched` / `job.done` / `job.failed`), newest first,
|
|
7827
|
-
* not job records - see {@link JobAuditEvent}.
|
|
7828
|
-
*
|
|
7829
|
-
* @param limit Server caps this at 100 and defaults to 20.
|
|
7830
|
-
*/
|
|
7831
|
-
async list(hostId, limit) {
|
|
7832
|
-
return this.http.get(
|
|
7833
|
-
`${this.base(hostId)}/jobs`,
|
|
7834
|
-
limit === void 0 ? void 0 : { limit }
|
|
7835
|
-
);
|
|
7808
|
+
async list(hostId) {
|
|
7809
|
+
return this.http.get(`${this.base(hostId)}/exec`);
|
|
7836
7810
|
}
|
|
7837
7811
|
/**
|
|
7838
7812
|
* Fetch the current state of a job.
|
|
7839
|
-
*
|
|
7840
|
-
* The server wraps the record in a `job` envelope; this unwraps it.
|
|
7841
7813
|
*/
|
|
7842
7814
|
async get(hostId, jobId) {
|
|
7843
|
-
|
|
7844
|
-
`${this.base(hostId)}/exec/${jobId}`
|
|
7845
|
-
);
|
|
7846
|
-
if (response && typeof response === "object" && "job" in response) {
|
|
7847
|
-
return response.job;
|
|
7848
|
-
}
|
|
7849
|
-
return response;
|
|
7815
|
+
return this.http.get(`${this.base(hostId)}/exec/${jobId}`);
|
|
7850
7816
|
}
|
|
7851
7817
|
/**
|
|
7852
|
-
*
|
|
7818
|
+
* Stream live output from a running job.
|
|
7853
7819
|
*
|
|
7854
|
-
*
|
|
7855
|
-
*
|
|
7856
|
-
* than replaying output - the server never stored it.
|
|
7820
|
+
* Yields `JobEvent` objects with `type` of `stdout`, `stderr`, `exit`, or
|
|
7821
|
+
* `done`. Break the loop when you receive `done` or `exit`.
|
|
7857
7822
|
*/
|
|
7858
7823
|
stream(hostId, jobId) {
|
|
7859
|
-
return
|
|
7860
|
-
|
|
7824
|
+
return this.http.stream(
|
|
7825
|
+
`${this.base(hostId)}/exec/${jobId}/stream`
|
|
7861
7826
|
);
|
|
7862
7827
|
}
|
|
7863
7828
|
/**
|
|
7864
7829
|
* Cancel a running or queued job.
|
|
7865
|
-
*
|
|
7866
|
-
* Answers `204` on success and `409` when the job is already terminal.
|
|
7867
7830
|
*/
|
|
7868
7831
|
async cancel(hostId, jobId) {
|
|
7869
7832
|
return this.http.delete(`${this.base(hostId)}/exec/${jobId}`);
|
|
7870
7833
|
}
|
|
7871
7834
|
};
|
|
7872
|
-
var EXEC_EVENT_NAMES = /* @__PURE__ */ new Set(["exec_chunk", "exec_result", "exec_error"]);
|
|
7873
|
-
function asString(value) {
|
|
7874
|
-
return typeof value === "string" ? value : null;
|
|
7875
|
-
}
|
|
7876
|
-
function asNumber(value) {
|
|
7877
|
-
return typeof value === "number" ? value : null;
|
|
7878
|
-
}
|
|
7879
|
-
function toJobEvent(raw) {
|
|
7880
|
-
if (raw === null || typeof raw !== "object") return null;
|
|
7881
|
-
const record = raw;
|
|
7882
|
-
const name = asString(record["type"]);
|
|
7883
|
-
if (name === null || !EXEC_EVENT_NAMES.has(name)) return null;
|
|
7884
|
-
if (name === "exec_chunk") {
|
|
7885
|
-
return {
|
|
7886
|
-
type: "exec_chunk",
|
|
7887
|
-
// The server defaults this to :stdout when the host omits it; anything
|
|
7888
|
-
// other than "stderr" is stdout by the same rule.
|
|
7889
|
-
stream: record["stream"] === "stderr" ? "stderr" : "stdout",
|
|
7890
|
-
data: asString(record["data"])
|
|
7891
|
-
};
|
|
7892
|
-
}
|
|
7893
|
-
if (name === "exec_result") {
|
|
7894
|
-
return {
|
|
7895
|
-
type: "exec_result",
|
|
7896
|
-
exit_code: asNumber(record["exit_code"]),
|
|
7897
|
-
elapsed_ms: asNumber(record["elapsed_ms"])
|
|
7898
|
-
};
|
|
7899
|
-
}
|
|
7900
|
-
const jobId = asString(record["job_id"]);
|
|
7901
|
-
return {
|
|
7902
|
-
type: "exec_error",
|
|
7903
|
-
reason: asString(record["reason"]),
|
|
7904
|
-
// Only the timeout path carries job_id. exactOptionalPropertyTypes means
|
|
7905
|
-
// the key has to be omitted rather than set to undefined.
|
|
7906
|
-
...jobId === null ? {} : { job_id: jobId }
|
|
7907
|
-
};
|
|
7908
|
-
}
|
|
7909
|
-
async function* translateExecStream(source) {
|
|
7910
|
-
for await (const raw of source) {
|
|
7911
|
-
const event = toJobEvent(raw);
|
|
7912
|
-
if (event !== null) yield event;
|
|
7913
|
-
}
|
|
7914
|
-
}
|
|
7915
7835
|
|
|
7916
7836
|
// src/resources/open-computers/secrets.ts
|
|
7917
7837
|
var Secrets = class {
|
|
@@ -8522,32 +8442,6 @@ var RuntimeCapabilitiesResource = class {
|
|
|
8522
8442
|
}
|
|
8523
8443
|
};
|
|
8524
8444
|
|
|
8525
|
-
// src/resources/sandbox-host-routes.ts
|
|
8526
|
-
var SANDBOXES_HOST_BASE_URL = "https://sandboxes.miosa.ai/api/v1";
|
|
8527
|
-
var SANDBOXES_HOST = "sandboxes.miosa.ai";
|
|
8528
|
-
function isSandboxesHost(baseUrl) {
|
|
8529
|
-
try {
|
|
8530
|
-
return new URL(baseUrl).host === SANDBOXES_HOST;
|
|
8531
|
-
} catch {
|
|
8532
|
-
return false;
|
|
8533
|
-
}
|
|
8534
|
-
}
|
|
8535
|
-
async function* streamSandboxesHostRoute(http, path, alternative) {
|
|
8536
|
-
try {
|
|
8537
|
-
yield* http.stream(path);
|
|
8538
|
-
} catch (err) {
|
|
8539
|
-
if (err instanceof MiosaError && err.status === 404 && !isSandboxesHost(http.baseUrl)) {
|
|
8540
|
-
throw new NotFoundError(
|
|
8541
|
-
`HTTP 404 for GET ${path}. This route is mounted only on the sandboxes host router (${SANDBOXES_HOST}); it is not part of the platform router that serves this client's baseUrl (${http.baseUrl}). Either construct the client with baseUrl: "${SANDBOXES_HOST_BASE_URL}", or ${alternative}. A wrong sandbox id also produces a 404.`,
|
|
8542
|
-
err.code,
|
|
8543
|
-
err.details,
|
|
8544
|
-
err.requestId
|
|
8545
|
-
);
|
|
8546
|
-
}
|
|
8547
|
-
throw err;
|
|
8548
|
-
}
|
|
8549
|
-
}
|
|
8550
|
-
|
|
8551
8445
|
// src/resources/sandboxes.ts
|
|
8552
8446
|
function encodeContent(content) {
|
|
8553
8447
|
const bytes = typeof content === "string" ? new TextEncoder().encode(content) : content;
|
|
@@ -8558,12 +8452,12 @@ function encodeContent(content) {
|
|
|
8558
8452
|
return btoa(bin);
|
|
8559
8453
|
}
|
|
8560
8454
|
var SANDBOX_TEMPLATE = "miosa-sandbox";
|
|
8561
|
-
var
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8455
|
+
var SANDBOX_TIER_BY_CPU = {
|
|
8456
|
+
1: { size: "xs", memoryMb: 2048 },
|
|
8457
|
+
2: { size: "small", memoryMb: 4096 },
|
|
8458
|
+
4: { size: "medium", memoryMb: 8192 },
|
|
8459
|
+
8: { size: "large", memoryMb: 16384 },
|
|
8460
|
+
16: { size: "xl", memoryMb: 32768 }
|
|
8567
8461
|
};
|
|
8568
8462
|
var AGENT_WORKSPACE_TIMEOUT_SEC = 86400;
|
|
8569
8463
|
var AGENT_WORKSPACE_IDLE_TIMEOUT_SEC = 1800;
|
|
@@ -8572,12 +8466,6 @@ var AGENT_WORKSPACE_KEEP_LAST_SNAPSHOTS = 1;
|
|
|
8572
8466
|
function isLegacyForkParams(opts) {
|
|
8573
8467
|
return "name" in opts || "metadata" in opts;
|
|
8574
8468
|
}
|
|
8575
|
-
async function* dropWatchHandshake(source) {
|
|
8576
|
-
for await (const event of source) {
|
|
8577
|
-
if (event.type === "connected") continue;
|
|
8578
|
-
yield event;
|
|
8579
|
-
}
|
|
8580
|
-
}
|
|
8581
8469
|
function unwrap48(payload) {
|
|
8582
8470
|
if (payload !== null && typeof payload === "object" && "data" in payload && payload.data !== void 0) {
|
|
8583
8471
|
return payload.data;
|
|
@@ -8622,31 +8510,9 @@ function createBody(params = {}) {
|
|
|
8622
8510
|
if (legacyPersistencePolicy) metadata.miosa_persistent = persistent;
|
|
8623
8511
|
const cpuCount = params.cpuCount ?? params.cpu_count;
|
|
8624
8512
|
const memoryMb = params.memoryMb ?? params.memory_mb;
|
|
8625
|
-
const
|
|
8626
|
-
|
|
8627
|
-
(
|
|
8628
|
-
).length;
|
|
8629
|
-
if (suppliedResources !== 0 && suppliedResources !== 3) {
|
|
8630
|
-
throw new TypeError(
|
|
8631
|
-
"Raw sandbox resources require cpuCount, memoryMb, and diskSizeMb together. Prefer size."
|
|
8632
|
-
);
|
|
8633
|
-
}
|
|
8634
|
-
let resolvedSize = params.size;
|
|
8635
|
-
if (suppliedResources === 3) {
|
|
8636
|
-
const matchingSize = Object.entries(SANDBOX_SHAPE_CONTRACTS).find(
|
|
8637
|
-
([, contract]) => contract.cpuCount === cpuCount && contract.memoryMb === memoryMb && contract.diskSizeMb === diskMb
|
|
8638
|
-
)?.[0];
|
|
8639
|
-
if (!matchingSize) {
|
|
8640
|
-
throw new TypeError(
|
|
8641
|
-
"Raw sandbox resources must exactly match a named size contract."
|
|
8642
|
-
);
|
|
8643
|
-
}
|
|
8644
|
-
if (resolvedSize && resolvedSize !== matchingSize) {
|
|
8645
|
-
throw new TypeError(
|
|
8646
|
-
`Raw sandbox resources match ${matchingSize}, not requested size ${resolvedSize}.`
|
|
8647
|
-
);
|
|
8648
|
-
}
|
|
8649
|
-
resolvedSize = matchingSize;
|
|
8513
|
+
const resolvedSize = params.size;
|
|
8514
|
+
if (cpuCount !== void 0 || memoryMb !== void 0) {
|
|
8515
|
+
assertPublishedShape(cpuCount, memoryMb);
|
|
8650
8516
|
}
|
|
8651
8517
|
if (snapshotExpirationSec !== void 0) {
|
|
8652
8518
|
metadata.snapshot_expiration_sec = snapshotExpirationSec;
|
|
@@ -8692,6 +8558,19 @@ function createBody(params = {}) {
|
|
|
8692
8558
|
external_project_id: params.externalProjectId ?? params.external_project_id
|
|
8693
8559
|
});
|
|
8694
8560
|
}
|
|
8561
|
+
function assertPublishedShape(cpuCount, memoryMb) {
|
|
8562
|
+
const tier = cpuCount === void 0 ? void 0 : SANDBOX_TIER_BY_CPU[cpuCount];
|
|
8563
|
+
if (tier && tier.memoryMb === memoryMb) return;
|
|
8564
|
+
const supplied = `${cpuCount ?? "?"} vCPU / ${memoryMb ?? "?"} MiB`;
|
|
8565
|
+
if (tier) {
|
|
8566
|
+
throw new TypeError(
|
|
8567
|
+
`Unsupported cpu/memory combination (${supplied}); nearest supported is ${tier.size} (${cpuCount} vCPU / ${tier.memoryMb} MiB). Pass size: "${tier.size}" or memoryMb: ${tier.memoryMb}. Custom disk sizes are allowed on top of any tier.`
|
|
8568
|
+
);
|
|
8569
|
+
}
|
|
8570
|
+
throw new TypeError(
|
|
8571
|
+
`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) or use size.`
|
|
8572
|
+
);
|
|
8573
|
+
}
|
|
8695
8574
|
function execBody(command, options = {}) {
|
|
8696
8575
|
return stripUndefined26({
|
|
8697
8576
|
command,
|
|
@@ -8700,6 +8579,19 @@ function execBody(command, options = {}) {
|
|
|
8700
8579
|
timeout: options.timeout ?? options.timeoutSec ?? options.timeout_sec
|
|
8701
8580
|
});
|
|
8702
8581
|
}
|
|
8582
|
+
function normalizeExecEvent(event, payload) {
|
|
8583
|
+
const record = payload !== null && typeof payload === "object" ? payload : {};
|
|
8584
|
+
const isExit = event === "exit" || record.exit_code !== void 0 || record.exitCode !== void 0 || event === null && typeof payload === "number";
|
|
8585
|
+
if (isExit) {
|
|
8586
|
+
const code = Number(
|
|
8587
|
+
record.exit_code ?? record.exitCode ?? (typeof payload === "number" ? payload : 0)
|
|
8588
|
+
);
|
|
8589
|
+
return { type: "exit", exit_code: code, exitCode: code };
|
|
8590
|
+
}
|
|
8591
|
+
const type = event === "stderr" ? "stderr" : "stdout";
|
|
8592
|
+
const data = typeof payload === "string" ? payload : String(record.line ?? record.data ?? "");
|
|
8593
|
+
return { type, data, line: data };
|
|
8594
|
+
}
|
|
8703
8595
|
function stripUndefined26(input) {
|
|
8704
8596
|
return Object.fromEntries(
|
|
8705
8597
|
Object.entries(input).filter(([, value]) => value !== void 0)
|
|
@@ -8777,30 +8669,11 @@ var SandboxFiles = class {
|
|
|
8777
8669
|
}
|
|
8778
8670
|
return response;
|
|
8779
8671
|
}
|
|
8780
|
-
/**
|
|
8781
|
-
* GET /api/v1/sandboxes/{id}/files/watch (SSE) — live file change events.
|
|
8782
|
-
*
|
|
8783
|
-
* SANDBOXES-HOST ONLY. This route is declared in `Web.Router.Sandboxes`
|
|
8784
|
-
* and not in `Web.Router.Platform`, so it 404s against the SDK's default
|
|
8785
|
-
* `https://api.miosa.ai/api/v1` base URL. Construct the client with
|
|
8786
|
-
* `baseUrl: "https://sandboxes.miosa.ai/api/v1"` to reach it, or poll
|
|
8787
|
-
* {@link list}/{@link stat} instead. See `./sandbox-host-routes.ts`.
|
|
8788
|
-
*
|
|
8789
|
-
* The server opens with a handshake record (`event: connected` /
|
|
8790
|
-
* `data: {"sandbox_id":...}`) before any change events. That record is
|
|
8791
|
-
* not a file change, so it is dropped here rather than yielded as a
|
|
8792
|
-
* `SandboxFileChange` with `type: "connected"` and no `path`. Real change
|
|
8793
|
-
* records carry their own `type` (`created` | `modified` | `deleted`) in
|
|
8794
|
-
* the JSON body, so the SSE `event: file_change` name never overwrites it.
|
|
8795
|
-
*/
|
|
8672
|
+
/** GET /api/v1/sandboxes/{id}/files/watch (SSE) — live file change events. */
|
|
8796
8673
|
watch() {
|
|
8797
8674
|
const http = this.sandbox.http;
|
|
8798
|
-
return
|
|
8799
|
-
|
|
8800
|
-
http,
|
|
8801
|
-
`/sandboxes/${this.sandbox.id}/files/watch`,
|
|
8802
|
-
"poll list()/stat() instead"
|
|
8803
|
-
)
|
|
8675
|
+
return http.stream(
|
|
8676
|
+
`/sandboxes/${this.sandbox.id}/files/watch`
|
|
8804
8677
|
);
|
|
8805
8678
|
}
|
|
8806
8679
|
};
|
|
@@ -9149,13 +9022,18 @@ var Sandbox = class _Sandbox {
|
|
|
9149
9022
|
}
|
|
9150
9023
|
execStream(command, options) {
|
|
9151
9024
|
this.assertRunning("exec.stream");
|
|
9152
|
-
|
|
9025
|
+
const frames = this.http.streamFrames(
|
|
9153
9026
|
`/sandboxes/${this.id}/exec/stream`,
|
|
9154
9027
|
{
|
|
9155
9028
|
method: "POST",
|
|
9156
9029
|
body: execBody(command, options)
|
|
9157
9030
|
}
|
|
9158
9031
|
);
|
|
9032
|
+
return (async function* () {
|
|
9033
|
+
for await (const frame of frames) {
|
|
9034
|
+
yield normalizeExecEvent(frame.event, frame.data);
|
|
9035
|
+
}
|
|
9036
|
+
})();
|
|
9159
9037
|
}
|
|
9160
9038
|
async writeFile(path, content) {
|
|
9161
9039
|
this.assertRunning("writeFile");
|