@runalabs/rill-cli 0.1.2 → 0.1.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 +3 -2
- package/dist/cli.js +26 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Record, inspect, and share agent-controlled browser runs with Rill.
|
|
4
4
|
|
|
5
|
+
After `rill record stop`, compatible agent hosts may receive `feedbackRequested: true`. They can submit a short Rill product review with `rill feedback submit <recording-id>`. Use `rill record stop <recording-id> --no-feedback` to opt out for one run.
|
|
6
|
+
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
9
|
```sh
|
|
8
|
-
npm install --global @runalabs/rill-cli@0.1.
|
|
10
|
+
npm install --global @runalabs/rill-cli@0.1.3
|
|
9
11
|
rill doctor
|
|
10
12
|
```
|
|
11
13
|
|
|
12
14
|
Rill requires Node.js 22 or newer, Google Chrome or Chromium, and `ffmpeg`.
|
|
13
|
-
The CLI connects to `https://userill.dev` by default.
|
|
14
15
|
|
|
15
16
|
## Record
|
|
16
17
|
|
package/dist/cli.js
CHANGED
|
@@ -621,6 +621,12 @@ var ControlPlaneClient = class {
|
|
|
621
621
|
body: JSON.stringify(input)
|
|
622
622
|
});
|
|
623
623
|
}
|
|
624
|
+
submitRecordingFeedback(recordingId, input) {
|
|
625
|
+
return this.request(`/api/recordings/${recordingId}/feedback`, {
|
|
626
|
+
method: "POST",
|
|
627
|
+
body: JSON.stringify(input)
|
|
628
|
+
});
|
|
629
|
+
}
|
|
624
630
|
provisionVideo(recordingId, input) {
|
|
625
631
|
return this.request(`/api/recordings/${recordingId}/video-upload`, { method: "POST", body: JSON.stringify(input) });
|
|
626
632
|
}
|
|
@@ -992,27 +998,42 @@ record.command("start").option("--url <url>").option("--title <title>").option("
|
|
|
992
998
|
}).catch(() => void 0);
|
|
993
999
|
emit({ schemaVersion: 1, recordingId: remote.recordingId, status: local.status, cdpUrl: local.cdpUrl, bodyCaptureEnabled: remote.bodyCaptureEnabled, maxDurationSeconds: Number(commandOptions.maxDuration) }, options.pretty);
|
|
994
1000
|
});
|
|
995
|
-
record.command("stop").argument("<recording-id>").option("--no-wait").action(async (recordingId, commandOptions) => {
|
|
1001
|
+
record.command("stop").argument("<recording-id>").option("--no-wait").option("--no-feedback").action(async (recordingId, commandOptions) => {
|
|
996
1002
|
const options = program.opts();
|
|
997
1003
|
await ensureDaemon(process.argv[1]);
|
|
998
1004
|
const local = await stopRecording(recordingId);
|
|
999
1005
|
if (!local.result) throw new Error(local.error ?? "The recorder did not produce an artifact.");
|
|
1000
1006
|
const token = await loadCredential(options.apiUrl);
|
|
1001
1007
|
const client = new ControlPlaneClient(options.apiUrl, token);
|
|
1002
|
-
await client.markRecordingStopped(recordingId, {
|
|
1008
|
+
const stopped = await client.markRecordingStopped(recordingId, {
|
|
1003
1009
|
durationSeconds: local.result.durationSeconds,
|
|
1004
1010
|
stoppedReason: local.result.stoppedReason,
|
|
1005
|
-
diagnosticsAvailable: true
|
|
1006
|
-
|
|
1011
|
+
diagnosticsAvailable: true,
|
|
1012
|
+
requestFeedback: commandOptions.feedback
|
|
1013
|
+
}).catch(() => null);
|
|
1007
1014
|
const video = await stat2(local.result.videoPath);
|
|
1008
1015
|
const provisioned = await client.provisionVideo(recordingId, { sizeBytes: video.size, filename: basename(local.result.videoPath), uploadKind: "recorded_browser" });
|
|
1009
1016
|
await Promise.all([client.uploadVideo(provisioned.uploadUrl, local.result.videoPath), client.uploadDiagnostics(recordingId, local.result.diagnosticsPath, local.result.diagnostics)]);
|
|
1010
1017
|
const remote = commandOptions.wait === false ? null : await client.waitUntilPlayable(recordingId);
|
|
1011
1018
|
if (remote) process.stderr.write("\n");
|
|
1012
|
-
const result = { schemaVersion: 1, recordingId, status: remote ? "ready" : "processing", shareUrl: remote?.shareUrl ?? null, durationSeconds: local.result.durationSeconds, stoppedReason: local.result.stoppedReason, diagnostics: local.result.diagnostics };
|
|
1019
|
+
const result = { schemaVersion: 1, recordingId, status: remote ? "ready" : "processing", shareUrl: remote?.shareUrl ?? null, durationSeconds: local.result.durationSeconds, stoppedReason: local.result.stoppedReason, diagnostics: local.result.diagnostics, feedbackRequested: stopped?.feedbackRequested ?? false };
|
|
1013
1020
|
emit(result, options.pretty);
|
|
1014
1021
|
if (remote) await rm2(dirname2(local.result.videoPath), { recursive: true, force: true });
|
|
1015
1022
|
});
|
|
1023
|
+
var feedback = program.command("feedback").description("Submit a short Rill product review for a recorded run.");
|
|
1024
|
+
feedback.command("submit").argument("<recording-id>").requiredOption("--outcome <outcome>", "succeeded, failed, or abandoned").option("--helped <text>", "What helped during the run", "").option("--friction <text>", "What created friction during the run", "").option("--improvement <text>", "The single most useful Rill improvement", "").action(async (recordingId, commandOptions) => {
|
|
1025
|
+
if (!["succeeded", "failed", "abandoned"].includes(commandOptions.outcome)) throw new Error("validation_failed: Feedback outcome must be succeeded, failed, or abandoned.");
|
|
1026
|
+
if (![commandOptions.helped, commandOptions.friction, commandOptions.improvement].some((value) => value.trim())) throw new Error("validation_failed: Include at least one short feedback answer.");
|
|
1027
|
+
const options = program.opts();
|
|
1028
|
+
const token = await loadCredential(options.apiUrl);
|
|
1029
|
+
const client = new ControlPlaneClient(options.apiUrl, token);
|
|
1030
|
+
await client.submitRecordingFeedback(recordingId, {
|
|
1031
|
+
outcome: commandOptions.outcome,
|
|
1032
|
+
whatHelped: commandOptions.helped,
|
|
1033
|
+
friction: commandOptions.friction,
|
|
1034
|
+
improvement: commandOptions.improvement
|
|
1035
|
+
});
|
|
1036
|
+
});
|
|
1016
1037
|
record.command("status").argument("<recording-id>").action(async (recordingId) => {
|
|
1017
1038
|
const options = program.opts();
|
|
1018
1039
|
const token = await loadCredential(options.apiUrl);
|