@putdotio/rokit 2.0.0 → 2.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 +5 -3
- package/dist/rokit.mjs +22 -5
- package/docs/skills/rokit-harness/SKILL.md +6 -3
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
pnpm add -D @putdotio/rokit
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
Node `>=24.14
|
|
24
|
+
Node `>=24.14`
|
|
25
25
|
|
|
26
26
|
## Quick Start
|
|
27
27
|
|
|
@@ -87,12 +87,14 @@ Common commands:
|
|
|
87
87
|
| `sgnodes` | Print the raw SceneGraph tree |
|
|
88
88
|
| `assert-node` / `wait-node` | Check generic SceneGraph node state |
|
|
89
89
|
| `wait-active` / `wait-media-player` / `wait-ready` | Poll generic runtime readiness |
|
|
90
|
-
| `screenshot <output-path>` | Save a developer screenshot
|
|
90
|
+
| `screenshot <output-path>` | Save a timestamped developer screenshot |
|
|
91
91
|
|
|
92
92
|
Mutating commands support `--dry-run` where the platform can validate without
|
|
93
93
|
changing device or filesystem state. ECP paths reject query strings, fragments,
|
|
94
94
|
traversal, backslashes, control characters, and percent-encoded path segments.
|
|
95
95
|
Generated output paths must stay within the current working directory.
|
|
96
|
+
Screenshots append a timestamp to the requested filename and report the actual
|
|
97
|
+
path written, so repeated captures do not reuse cache-prone filenames.
|
|
96
98
|
|
|
97
99
|
## Library Use
|
|
98
100
|
|
|
@@ -137,7 +139,7 @@ await waitForSceneGraphAssertion(context, "player ready", (xml) => {
|
|
|
137
139
|
- package, install, launch, deeplink params, and remote keypresses
|
|
138
140
|
- raw ECP queries and parsed media-player state
|
|
139
141
|
- SceneGraph state queries and named-node assertions
|
|
140
|
-
- screenshots, snapshots, and proof artifacts
|
|
142
|
+
- timestamped screenshots, snapshots, and proof artifacts
|
|
141
143
|
|
|
142
144
|
Consumer app repos own product behavior: opening specific routes, asserting
|
|
143
145
|
playback for real content, checking app-specific UI nodes, and generating review
|
package/dist/rokit.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { C as takeScreenshot, D as waitForMediaPlayerState, E as waitForActiveApp, G as readSceneGraphFailure, K as readSceneGraphStatus, S as resolvePackageOutputPath, T as validateRemoteKey, _ as querySceneGraph, a as getDeviceInfo, c as launchApp, d as queryActiveApp, f as queryEcp, i as discoverRokuDevices, k as waitForSceneGraphNode, l as packageChannel, n as assertSceneGraphNode, o as installPackage, p as queryMediaPlayer, r as checkDevice, u as pressKey, w as validateEcpPath } from "./roku-BxnS6Axs.mjs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
|
-
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
5
|
import { NodeRuntime } from "@effect/platform-node";
|
|
6
6
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { Effect, Schema } from "effect";
|
|
@@ -307,7 +307,7 @@ const runCommand = async (context, command, dryRun) => {
|
|
|
307
307
|
};
|
|
308
308
|
}
|
|
309
309
|
if (command.name === "screenshot") {
|
|
310
|
-
const path = resolveFileOutputPath(command.outputPath, "screenshot output path");
|
|
310
|
+
const path = timestampOutputPath(resolveFileOutputPath(command.outputPath, "screenshot output path"));
|
|
311
311
|
if (dryRun) return dryRunResult(command.name, { path });
|
|
312
312
|
const password = requirePassword(deviceContext);
|
|
313
313
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -463,7 +463,7 @@ const writeProof = async (context, outputDir, includeScreenshot) => {
|
|
|
463
463
|
const path = await takeScreenshot({
|
|
464
464
|
...context,
|
|
465
465
|
password
|
|
466
|
-
}, `${outputDir}/screenshot.png`);
|
|
466
|
+
}, timestampOutputPath(`${outputDir}/screenshot.png`));
|
|
467
467
|
artifacts.push({
|
|
468
468
|
kind: "screenshot",
|
|
469
469
|
path
|
|
@@ -475,6 +475,23 @@ const writeProof = async (context, outputDir, includeScreenshot) => {
|
|
|
475
475
|
snapshot
|
|
476
476
|
};
|
|
477
477
|
};
|
|
478
|
+
const timestampOutputPath = (path, date = /* @__PURE__ */ new Date()) => {
|
|
479
|
+
const extension = extname(path);
|
|
480
|
+
const name = basename(path, extension);
|
|
481
|
+
return join(dirname(path), `${name}-${formatTimestamp(date)}${extension}`);
|
|
482
|
+
};
|
|
483
|
+
const formatTimestamp = (date) => [
|
|
484
|
+
date.getFullYear().toString(),
|
|
485
|
+
padDatePart(date.getMonth() + 1),
|
|
486
|
+
padDatePart(date.getDate()),
|
|
487
|
+
"-",
|
|
488
|
+
padDatePart(date.getHours()),
|
|
489
|
+
padDatePart(date.getMinutes()),
|
|
490
|
+
padDatePart(date.getSeconds()),
|
|
491
|
+
"-",
|
|
492
|
+
padDatePart(date.getMilliseconds(), 3)
|
|
493
|
+
].join("");
|
|
494
|
+
const padDatePart = (value, length = 2) => value.toString().padStart(length, "0");
|
|
478
495
|
const waitForReady = async (context, command) => {
|
|
479
496
|
const activeApp = await waitForActiveApp(context, command.appId, command.timeoutMs);
|
|
480
497
|
const sceneGraph = await observe(async () => querySceneGraph(context, {
|
|
@@ -516,7 +533,7 @@ const dryRunData = (command) => {
|
|
|
516
533
|
until: command.args.until ? formatNodeData(command.args.until) : void 0
|
|
517
534
|
};
|
|
518
535
|
}
|
|
519
|
-
if (command.name === "screenshot") return { path: resolveFileOutputPath(command.outputPath, "screenshot output path") };
|
|
536
|
+
if (command.name === "screenshot") return { path: timestampOutputPath(resolveFileOutputPath(command.outputPath, "screenshot output path")) };
|
|
520
537
|
if (command.name === "proof") return {
|
|
521
538
|
outputDir: resolveOutputPath(command.outputDir, "proof output directory"),
|
|
522
539
|
screenshot: command.screenshot
|
|
@@ -1101,7 +1118,7 @@ const describeCli = () => ({
|
|
|
1101
1118
|
optionField("node", "node-condition", "Optional SceneGraph node condition."),
|
|
1102
1119
|
optionField("timeoutMs", "positive-integer", "Wait timeout in milliseconds.")
|
|
1103
1120
|
]),
|
|
1104
|
-
commandSchema("screenshot", "Write a developer screenshot.", true, true, [argumentField("outputPath", "path", "Screenshot output path inside the current app root.")])
|
|
1121
|
+
commandSchema("screenshot", "Write a timestamped developer screenshot.", true, true, [argumentField("outputPath", "path", "Screenshot base output path inside the current app root.")])
|
|
1105
1122
|
],
|
|
1106
1123
|
globalOptions: [
|
|
1107
1124
|
globalField("json", "boolean", "Print structured JSON output."),
|
|
@@ -19,12 +19,15 @@ repo.
|
|
|
19
19
|
3. Prefer structured output. In automation, rely on the non-TTY JSON default or
|
|
20
20
|
pass `--json` explicitly.
|
|
21
21
|
4. Use `--fields` to keep observations small when only a few values are needed.
|
|
22
|
-
5.
|
|
22
|
+
5. Screenshot commands append a timestamp to the requested filename. Use the
|
|
23
|
+
returned JSON `data.path` as the artifact path instead of assuming the input
|
|
24
|
+
path was written.
|
|
25
|
+
6. For live proof, use `rokit snapshot` for a quick state read,
|
|
23
26
|
`rokit proof <output-dir>` for review artifacts, or `pnpm live:probe` in the
|
|
24
27
|
rokit repo for the full generic package/install/launch/input/proof probe.
|
|
25
|
-
|
|
28
|
+
7. Use `rokit wait-ready <app-id>` after launch when the app can race ECP or
|
|
26
29
|
SceneGraph readiness.
|
|
27
|
-
|
|
30
|
+
8. Use `rokit press --until-node ...` for bounded navigation loops instead of
|
|
28
31
|
arbitrary sleeps.
|
|
29
32
|
|
|
30
33
|
## Boundaries
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@putdotio/rokit",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "A tiny CLI companion for Roku device harness work.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -63,13 +63,13 @@
|
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@effect/vitest": "4.0.0-beta.66",
|
|
66
|
-
"@types/node": "^
|
|
66
|
+
"@types/node": "^25.8.0",
|
|
67
67
|
"typescript": "^6.0.2",
|
|
68
68
|
"vite-plus": "^0.1.20",
|
|
69
69
|
"vitest": "^4.1.6"
|
|
70
70
|
},
|
|
71
71
|
"engines": {
|
|
72
|
-
"node": ">=24.14.0
|
|
72
|
+
"node": ">=24.14.0"
|
|
73
73
|
},
|
|
74
74
|
"packageManager": "pnpm@11.0.0"
|
|
75
75
|
}
|