@putdotio/rokit 2.0.1 → 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 CHANGED
@@ -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. For live proof, use `rokit snapshot` for a quick state read,
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
- 6. Use `rokit wait-ready <app-id>` after launch when the app can race ECP or
28
+ 7. Use `rokit wait-ready <app-id>` after launch when the app can race ECP or
26
29
  SceneGraph readiness.
27
- 7. Use `rokit press --until-node ...` for bounded navigation loops instead of
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.1",
3
+ "version": "2.0.2",
4
4
  "description": "A tiny CLI companion for Roku device harness work.",
5
5
  "keywords": [
6
6
  "cli",