@madewithremy/admin 0.1.40 → 0.1.41
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 +1 -1
- package/dist/index.d.ts +17 -11
- package/dist/index.js +56 -5
- package/dist/index.js.map +1 -1
- package/dist/prod.js +65 -13
- package/package.json +1 -1
package/dist/prod.js
CHANGED
|
@@ -9372,7 +9372,12 @@ function seqOf(key) {
|
|
|
9372
9372
|
);
|
|
9373
9373
|
return Number.isFinite(n) ? n : 0;
|
|
9374
9374
|
}
|
|
9375
|
-
var DEFAULT_EXPORT_TIMEOUT_MS =
|
|
9375
|
+
var DEFAULT_EXPORT_TIMEOUT_MS = 33 * 6e4;
|
|
9376
|
+
var EXPORT_PHASE_LABEL = {
|
|
9377
|
+
loading: "Preparing the replay",
|
|
9378
|
+
rendering: "Rendering the replay",
|
|
9379
|
+
encoding: "Encoding the video"
|
|
9380
|
+
};
|
|
9376
9381
|
async function exportVideo(_ctx, params) {
|
|
9377
9382
|
const body = {
|
|
9378
9383
|
recordingSessionId: params.sessionId,
|
|
@@ -9385,7 +9390,12 @@ async function exportVideo(_ctx, params) {
|
|
|
9385
9390
|
try {
|
|
9386
9391
|
res = await fetch(`${SIDECAR_URL}/export-recording`, {
|
|
9387
9392
|
method: "POST",
|
|
9388
|
-
|
|
9393
|
+
// A line per progress event, then the answer (the sidecar's
|
|
9394
|
+
// streamRecordingExport).
|
|
9395
|
+
headers: {
|
|
9396
|
+
"Content-Type": "application/json",
|
|
9397
|
+
Accept: "application/x-ndjson"
|
|
9398
|
+
},
|
|
9389
9399
|
body: JSON.stringify(body),
|
|
9390
9400
|
signal: AbortSignal.timeout(
|
|
9391
9401
|
params.timeoutMs ?? DEFAULT_EXPORT_TIMEOUT_MS
|
|
@@ -9396,12 +9406,53 @@ async function exportVideo(_ctx, params) {
|
|
|
9396
9406
|
`Could not reach the sandbox at ${SIDECAR_URL} (${err instanceof Error ? err.message : String(err)}). Rendering a replay needs the dev box that recorded it \u2014 this command only works inside a Remy sandbox.`
|
|
9397
9407
|
);
|
|
9398
9408
|
}
|
|
9399
|
-
|
|
9400
|
-
|
|
9409
|
+
if (!res.ok || !res.body) {
|
|
9410
|
+
const refused = await res.json().catch(() => ({}));
|
|
9401
9411
|
throw new Error(
|
|
9402
|
-
typeof
|
|
9412
|
+
typeof refused.error === "string" ? refused.error : `The sandbox refused the export (HTTP ${res.status}).`
|
|
9403
9413
|
);
|
|
9404
9414
|
}
|
|
9415
|
+
let payload = {};
|
|
9416
|
+
let shown = null;
|
|
9417
|
+
const decoder = new TextDecoder();
|
|
9418
|
+
let buffer = "";
|
|
9419
|
+
const read = (text) => {
|
|
9420
|
+
if (!text.trim()) {
|
|
9421
|
+
return;
|
|
9422
|
+
}
|
|
9423
|
+
let value;
|
|
9424
|
+
try {
|
|
9425
|
+
value = JSON.parse(text);
|
|
9426
|
+
} catch {
|
|
9427
|
+
return;
|
|
9428
|
+
}
|
|
9429
|
+
const progress2 = value.progress;
|
|
9430
|
+
if (!progress2) {
|
|
9431
|
+
payload = value;
|
|
9432
|
+
return;
|
|
9433
|
+
}
|
|
9434
|
+
const phase = progress2.phase ?? "rendering";
|
|
9435
|
+
const percent = Math.round(progress2.percent ?? 0);
|
|
9436
|
+
const step = Math.floor(percent / 10);
|
|
9437
|
+
if (shown?.phase !== phase || step > shown.step) {
|
|
9438
|
+
shown = { phase, step };
|
|
9439
|
+
params.onProgress?.(
|
|
9440
|
+
`${EXPORT_PHASE_LABEL[phase] ?? EXPORT_PHASE_LABEL.rendering}\u2026 ${percent}%`
|
|
9441
|
+
);
|
|
9442
|
+
}
|
|
9443
|
+
};
|
|
9444
|
+
for await (const chunk of res.body) {
|
|
9445
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
9446
|
+
let end;
|
|
9447
|
+
while ((end = buffer.indexOf("\n")) !== -1) {
|
|
9448
|
+
read(buffer.slice(0, end));
|
|
9449
|
+
buffer = buffer.slice(end + 1);
|
|
9450
|
+
}
|
|
9451
|
+
}
|
|
9452
|
+
read(buffer);
|
|
9453
|
+
if (typeof payload.error === "string") {
|
|
9454
|
+
throw new Error(payload.error);
|
|
9455
|
+
}
|
|
9405
9456
|
const status2 = payload.export;
|
|
9406
9457
|
if (!status2) {
|
|
9407
9458
|
throw new Error(
|
|
@@ -9458,12 +9509,11 @@ async function qaRecordingsExport(ctx, a) {
|
|
|
9458
9509
|
from,
|
|
9459
9510
|
to,
|
|
9460
9511
|
store: a.str("store"),
|
|
9461
|
-
access: a.bool("private") ? "private" : void 0
|
|
9512
|
+
access: a.bool("private") ? "private" : void 0,
|
|
9513
|
+
onProgress: progress
|
|
9462
9514
|
});
|
|
9463
9515
|
if (result.status !== "completed") {
|
|
9464
|
-
fatal(
|
|
9465
|
-
result.errorCode === "FFMPEG_UNAVAILABLE" ? "This sandbox has no ffmpeg \u2014 video export needs a box on the current devbox image." : result.error ?? `Export ${result.status}.`
|
|
9466
|
-
);
|
|
9516
|
+
fatal(result.error ?? `Export ${result.status}.`);
|
|
9467
9517
|
}
|
|
9468
9518
|
out(result);
|
|
9469
9519
|
}
|
|
@@ -9479,10 +9529,12 @@ recording, not video). The chunks are ordinary private files in the
|
|
|
9479
9529
|
for a link, download one, or copy one into a public store to embed it.
|
|
9480
9530
|
|
|
9481
9531
|
'export' turns a replay into an mp4 \u2014 the shareable artifact, for a changelog
|
|
9482
|
-
entry, a release note or a bug report.
|
|
9483
|
-
|
|
9484
|
-
|
|
9485
|
-
already running.
|
|
9532
|
+
entry, a release note or a bug report. The dev box that recorded it packages
|
|
9533
|
+
the replay, and the platform renders it in a real browser, frame by frame (no
|
|
9534
|
+
AI-generated frames). So it only works inside a sandbox, and it won't start
|
|
9535
|
+
while a video export is already running. It blocks until the video is written,
|
|
9536
|
+
which takes minutes for a long clip (up to about 30), printing its progress to
|
|
9537
|
+
stderr as it goes; run it with a timeout to match.
|
|
9486
9538
|
|
|
9487
9539
|
Subcommands:
|
|
9488
9540
|
ls Recording sessions in this app, grouped into runs
|