kryptheon 0.1.14 → 0.1.16

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/bin/kryptheon.js CHANGED
@@ -40,10 +40,14 @@ const MIN_NODE = [20, 6, 0];
40
40
  const WINDOW_POLL_MS = 5000;
41
41
  const WINDOW_TIMEOUT_MS = 30000;
42
42
  const WINDOW_HARD_LIMIT_MS = 90000;
43
- // Lower than the poll interval on purpose: a probe still running when the next
44
- // one is due is of no use, and every extra second it holds the loop is a second
45
- // the watchdog is blind.
46
- const WINDOW_PROBE_TIMEOUT_MS = 4000;
43
+ // This was 4 seconds, kept under the poll interval so a probe never held the
44
+ // loop past the next one. Measured since, on a Windows 11 machine: the probe
45
+ // takes 2.5-3 seconds with nothing else running, and while the browser was
46
+ // starting it ran past 4 seconds three times in a row. On a slower machine
47
+ // every probe ran out of time and nothing was ever learned. A probe that
48
+ // answers late is worth more than one that never answers; the budget is
49
+ // counted by the clock, so a slow tick costs nothing extra.
50
+ const WINDOW_PROBE_TIMEOUT_MS = 10000;
47
51
 
48
52
  function nodeIsTooOld(version) {
49
53
  const parts = String(version || process.versions.node).split('.').map(Number);
@@ -614,6 +618,49 @@ function windowUndetectedLines(seconds) {
614
618
  ];
615
619
  }
616
620
 
621
+ /**
622
+ * What to do with a browser whose window the watchdog could not confirm:
623
+ * 'leave' it running, or 'stop' it.
624
+ *
625
+ * Found on the first run by somebody new, on Windows 10: the browser opened,
626
+ * they used their app, and it closed under them - "almost the whole thing" -
627
+ * with nothing recorded. The probe is a fresh PowerShell each time: measured on
628
+ * a Windows 11 machine it takes 2.5-3 seconds idle, and ran past its 4 second
629
+ * limit three times in a row while the browser was starting. On a slower
630
+ * machine every probe runs out of time, the watchdog learns nothing for 90
631
+ * seconds, calls that 'unclear' - and the browser somebody was using was
632
+ * stopped on a guess.
633
+ *
634
+ * So the browser is only ever stopped on something known, and never when a
635
+ * person is at the keyboard to close it themselves:
636
+ *
637
+ * window leave - it is up
638
+ * unclear leave - nothing was learned, so nothing is done
639
+ * no-window / never-started leave with a keyboard, stop without one
640
+ *
641
+ * The watchdog exists for the case with no keyboard: an AI assistant, where
642
+ * there is no desktop and codegen would wait for ever. isTTY can be false in a
643
+ * perfectly good terminal, so false is not taken as proof of anything - that
644
+ * case keeps the old behaviour. True is taken as a person being there.
645
+ */
646
+ function whenWindowUnconfirmed(verdict, atKeyboard) {
647
+ if (verdict === 'window' || verdict === 'unclear') return 'leave';
648
+ return atKeyboard ? 'leave' : 'stop';
649
+ }
650
+
651
+ // Said once, while the person may still be recording, instead of taking the
652
+ // browser away from them.
653
+ function windowUnconfirmedLines() {
654
+ return [
655
+ '',
656
+ ' (I could not confirm the browser window from here - that check is slow',
657
+ ' on some machines - so I am leaving it alone. If it is on your screen,',
658
+ ' carry on and close it when you are done. If there is no window',
659
+ ' anywhere, press Ctrl+C to stop.)',
660
+ '',
661
+ ];
662
+ }
663
+
617
664
  // The probe kept failing to answer. Nothing was learned about the browser
618
665
  // either way, and saying "no window appeared" would be inventing a fact.
619
666
  function probeUnclearLines(answered, unanswered) {
@@ -1520,7 +1567,12 @@ async function record(url) {
1520
1567
  const outcome = watch.tick();
1521
1568
  if (!outcome) return;
1522
1569
  clearInterval(watchdog);
1523
- if (outcome.verdict === 'window') return;
1570
+ if (whenWindowUnconfirmed(outcome.verdict, !!process.stdin.isTTY) === 'leave') {
1571
+ if (outcome.verdict !== 'window') {
1572
+ for (const line of windowUnconfirmedLines()) console.log(line);
1573
+ }
1574
+ return;
1575
+ }
1524
1576
  stopChild();
1525
1577
  finalise(outcome.verdict, outcome).then(resolve);
1526
1578
  }, WINDOW_POLL_MS);
@@ -2091,9 +2143,12 @@ module.exports = {
2091
2143
  dropOldRecordings: dropOldRecordings,
2092
2144
  askAboutExisting: askAboutExisting,
2093
2145
  askKey: askKey,
2146
+ whenWindowUnconfirmed: whenWindowUnconfirmed,
2147
+ windowUnconfirmedLines: windowUnconfirmedLines,
2094
2148
  WINDOW_TIMEOUT_MS: WINDOW_TIMEOUT_MS,
2095
2149
  WINDOW_HARD_LIMIT_MS: WINDOW_HARD_LIMIT_MS,
2096
2150
  WINDOW_POLL_MS: WINDOW_POLL_MS,
2151
+ WINDOW_PROBE_TIMEOUT_MS: WINDOW_PROBE_TIMEOUT_MS,
2097
2152
  classifyFetchError: classifyFetchError,
2098
2153
  normaliseRecordUrl: normaliseRecordUrl,
2099
2154
  isLocalAddress: isLocalAddress,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kryptheon",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Record what you do in your app and get plain-language reports when it breaks.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -38,7 +38,7 @@
38
38
  "@playwright/test": "^1.62.1"
39
39
  },
40
40
  "scripts": {
41
- "check": "node kryptheon-reporter.check.js && node kryptheon-signature.check.js && node kryptheon-signature.stream.check.js && node kryptheon-signature.baseline.check.js && node kryptheon-signature.settle.check.js && node kryptheon-signature.number.check.js && node kryptheon-env.check.js && node kryptheon-watchdog.check.js && node kryptheon-record.check.js && node kryptheon-selectors.check.js && node kryptheon-recordings.check.js && node kryptheon-cleanup.check.js && node kryptheon-orphan.check.js && node kryptheon-project.check.js && node kryptheon-setup.check.js && node kryptheon-load.check.js && node kryptheon-package.check.js",
41
+ "check": "node kryptheon-reporter.check.js && node kryptheon-signature.check.js && node kryptheon-signature.stream.check.js && node kryptheon-signature.baseline.check.js && node kryptheon-signature.settle.check.js && node kryptheon-signature.number.check.js && node kryptheon-env.check.js && node kryptheon-watchdog.check.js && node kryptheon-record.check.js && node kryptheon-selectors.check.js && node kryptheon-recordings.check.js && node kryptheon-cleanup.check.js && node kryptheon-orphan.check.js && node kryptheon-project.check.js && node kryptheon-setup.check.js && node kryptheon-browser.check.js && node kryptheon-load.check.js && node kryptheon-package.check.js",
42
42
  "check:reporter": "node kryptheon-reporter.check.js",
43
43
  "check:signature": "node kryptheon-signature.check.js",
44
44
  "check:stream": "node kryptheon-signature.stream.check.js",
@@ -54,6 +54,7 @@
54
54
  "check:orphan": "node kryptheon-orphan.check.js",
55
55
  "check:project": "node kryptheon-project.check.js",
56
56
  "check:setup": "node kryptheon-setup.check.js",
57
+ "check:browser": "node kryptheon-browser.check.js",
57
58
  "check:load": "node kryptheon-load.check.js",
58
59
  "check:package": "node kryptheon-package.check.js",
59
60
  "prepublishOnly": "node kryptheon-package.check.js"
@@ -55,6 +55,13 @@ module.exports = defineConfig({
55
55
  reporter: [[path.join(PACKAGE_DIR, 'kryptheon-reporter.js')]],
56
56
  use: {
57
57
  headless: true,
58
+ // The full Chromium, run headless - not Playwright's separate headless
59
+ // shell. Measured on a Windows 11 machine: the shell took 23 seconds to
60
+ // open each new page, every time, and that time counts against the 30
61
+ // second test timeout. A correct flow longer than about 7 seconds failed,
62
+ // and the report blamed the app. The full Chromium opened the same page in
63
+ // 1.5 seconds. It is also the browser `record` checks is installed.
64
+ channel: 'chromium',
58
65
  screenshot: 'only-on-failure',
59
66
  },
60
67
  });