@flowriki/agent 0.1.0-beta.24 → 0.1.0-beta.26

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.
@@ -6,7 +6,7 @@ import os from "os";
6
6
  import path from "path";
7
7
  import { createRequire } from "module";
8
8
  import { spawnSync } from "child_process";
9
- var AGENT_VERSION = "0.1.0-beta.24";
9
+ var AGENT_VERSION = "0.1.0-beta.26";
10
10
  async function prepareBrowsers() {
11
11
  if (browsersReady()) {
12
12
  return { ok: true, already: true, message: "chromium is already installed" };
@@ -1053,7 +1053,25 @@ var FlowOccurrenceSchema = z22.object({
1053
1053
  * against the source can line the two up; nothing depends on it to run. */
1054
1054
  stepIds: z22.record(z22.string(), z22.string()).default({}),
1055
1055
  copiedAt: z22.string()
1056
- })
1056
+ }),
1057
+ /** WHICH OF THIS ROW'S STEPS ARE WHAT RUNS BEFORE ITS FLOW — the flow's
1058
+ * sign-in and the prerequisites copied in with it — so the row can be shown
1059
+ * folded, the way the Flows screen shows the same flow.
1060
+ *
1061
+ * A description of steps the row already holds, and nothing more: they are
1062
+ * ordinary copies in `steps` and run in order whatever this says. Kept by
1063
+ * id, so editing or deleting one of them leaves the rest still grouped.
1064
+ * Absent on every row copied before this existed, which simply show flat.
1065
+ * Never sent to a machine — see `withoutFolding`. */
1066
+ before: z22.object({
1067
+ /** The flow's own sign-in, when the copy brought one. */
1068
+ signInStepId: z22.string().optional(),
1069
+ flows: z22.array(z22.object({
1070
+ flowId: z22.string(),
1071
+ name: z22.string(),
1072
+ stepIds: z22.array(z22.string())
1073
+ }).strict()).default([])
1074
+ }).strict().optional()
1057
1075
  }).strict();
1058
1076
  var CASE_SCHEMA_VERSION = 1;
1059
1077
  var TestCaseSchema = z22.object({
@@ -1575,7 +1593,7 @@ function expandForRun(flow, lookup) {
1575
1593
 
1576
1594
  // ../engine-playwright/dist/index.js
1577
1595
  import { createHmac, randomBytes } from "crypto";
1578
- import { errors } from "playwright";
1596
+ import { errors as errors2 } from "playwright";
1579
1597
 
1580
1598
  // ../core/dist/index.js
1581
1599
  function proposeAssertions(before, after) {
@@ -1896,7 +1914,7 @@ var STEP_PRESENTATION = {
1896
1914
  // ../engine-playwright/dist/index.js
1897
1915
  import fs4 from "fs";
1898
1916
  import path22 from "path";
1899
- import { chromium, firefox, webkit } from "playwright";
1917
+ import { chromium, errors, firefox, webkit } from "playwright";
1900
1918
 
1901
1919
  // ../recorder/dist/index.js
1902
1920
  function openShadowRoots() {
@@ -3569,8 +3587,11 @@ var RunActors = class {
3569
3587
  this.sessions.set(id, session);
3570
3588
  await this.opts.onCreate?.(session);
3571
3589
  }
3590
+ const switching = this.current !== session;
3572
3591
  this.current = session;
3573
3592
  this.currentId = id;
3593
+ if (switching && this.opts.headless === false) await session.page.bringToFront().catch(() => {
3594
+ });
3574
3595
  if (this.initialized.has(id)) return;
3575
3596
  if (startUrl) await session.page.goto(startUrl, { waitUntil: "domcontentloaded" });
3576
3597
  if (actor?.startSignedIn) {
@@ -3923,38 +3944,63 @@ function resolveCapture(options = {}) {
3923
3944
  };
3924
3945
  }
3925
3946
  var forcedHeadless = () => process.env["FLOWRIKI_FORCE_HEADLESS"] === "1";
3947
+ var LAUNCH_ATTEMPT_MS = () => Number(process.env["FLOWRIKI_LAUNCH_TIMEOUT_MS"] ?? 45e3);
3948
+ var isStartupFailure = (error) => error instanceof errors.TimeoutError || /Target page, context or browser has been closed|Browser has been closed|browser has disconnected/i.test(String(error?.message ?? error));
3949
+ async function launchWithRetry(launch, setUp, { attempts = 2, pauseMs = 1e3 } = {}) {
3950
+ let last;
3951
+ for (let attempt = 1; attempt <= attempts; attempt++) {
3952
+ let browser = null;
3953
+ try {
3954
+ browser = await launch(LAUNCH_ATTEMPT_MS());
3955
+ return { browser, value: await setUp(browser) };
3956
+ } catch (error) {
3957
+ last = error;
3958
+ await browser?.close().catch(() => {
3959
+ });
3960
+ if (!isStartupFailure(error)) throw error;
3961
+ if (attempt < attempts) await new Promise((r) => setTimeout(r, pauseMs));
3962
+ }
3963
+ }
3964
+ const seconds = Math.round(LAUNCH_ATTEMPT_MS() / 1e3);
3965
+ const why = String(last?.message ?? last).split("\n")[0];
3966
+ throw new Error(
3967
+ `the browser did not start after ${attempts} attempts of ${seconds}s each (${why}) \u2014 on a Mac this is usually a visible browser that could not open a window; run it again, or run it headless`
3968
+ );
3969
+ }
3926
3970
  async function launchSession(environment = {}, headlessRequested = true, slowMo = 0, capture = {}, existingBrowser, storageState) {
3927
3971
  const headless = headlessRequested || forcedHeadless();
3928
3972
  const config = { ...DEFAULT_ENVIRONMENT, ...ExecutionEnvironmentSchema.parse(environment) };
3929
3973
  const fullWindow = config.fullWindow === true && !headless;
3930
3974
  const engine = { chromium, firefox, webkit }[config.browser];
3931
- const browser = existingBrowser ?? await engine.launch({
3975
+ const launch = (timeout) => engine.launch({
3932
3976
  headless,
3933
3977
  slowMo,
3978
+ timeout,
3934
3979
  // Chromium's flag. Firefox and WebKit have no equivalent, and `viewport:
3935
3980
  // null` still gives them a page that fills their default window — bigger
3936
3981
  // than the fixed box, if not maximised.
3937
3982
  ...fullWindow && config.browser === "chromium" ? { args: ["--start-maximized"] } : {}
3938
3983
  });
3984
+ const { viewport, locale, timezoneId, colorScheme } = config;
3985
+ const artifacts = resolveCapture(capture);
3986
+ const makeContext = (browser2) => browser2.newContext({
3987
+ viewport: fullWindow ? null : viewport,
3988
+ locale,
3989
+ timezoneId,
3990
+ colorScheme,
3991
+ ...storageState ? { storageState } : {},
3992
+ // Playwright can only be told to record at context creation, so a video
3993
+ // wanted "on failure" is recorded for every run and thrown away when the
3994
+ // run passes. There is no way to start one after the fact.
3995
+ //
3996
+ // The video still needs a fixed size even when the page has none —
3997
+ // `size` is what the file is encoded at, and Playwright scales the frame
3998
+ // to fit it. The configured viewport is the closest thing to an intended
3999
+ // aspect ratio we have.
4000
+ ...artifacts.video !== "off" && capture.dir ? { recordVideo: { dir: capture.dir, size: viewport } } : {}
4001
+ });
4002
+ const { browser, value: context } = existingBrowser ? { browser: existingBrowser, value: await makeContext(existingBrowser) } : await launchWithRetry(launch, makeContext);
3939
4003
  try {
3940
- const { viewport, locale, timezoneId, colorScheme } = config;
3941
- const artifacts = resolveCapture(capture);
3942
- const context = await browser.newContext({
3943
- viewport: fullWindow ? null : viewport,
3944
- locale,
3945
- timezoneId,
3946
- colorScheme,
3947
- ...storageState ? { storageState } : {},
3948
- // Playwright can only be told to record at context creation, so a video
3949
- // wanted "on failure" is recorded for every run and thrown away when the
3950
- // run passes. There is no way to start one after the fact.
3951
- //
3952
- // The video still needs a fixed size even when the page has none —
3953
- // `size` is what the file is encoded at, and Playwright scales the frame
3954
- // to fit it. The configured viewport is the closest thing to an intended
3955
- // aspect ratio we have.
3956
- ...artifacts.video !== "off" && capture.dir ? { recordVideo: { dir: capture.dir, size: viewport } } : {}
3957
- });
3958
4004
  if (artifacts.trace !== "off" && capture.dir) {
3959
4005
  await context.tracing.start({ screenshots: true, snapshots: true, sources: false });
3960
4006
  }
@@ -4839,7 +4885,7 @@ async function runStepsIn(session, steps, opts) {
4839
4885
  }
4840
4886
  } catch (err) {
4841
4887
  result.status = "failed";
4842
- result.failureKind = err.kind ?? (/strict mode violation/.test(String(err)) ? "ambiguous" : err instanceof errors.TimeoutError ? "timeout" : "unknown");
4888
+ result.failureKind = err.kind ?? (/strict mode violation/.test(String(err)) ? "ambiguous" : err instanceof errors2.TimeoutError ? "timeout" : "unknown");
4843
4889
  result.detail = firstLine2(err);
4844
4890
  }
4845
4891
  const wantsShot = artifacts.screenshot === "always" || artifacts.screenshot === "on-failure" && result.status === "failed";
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  resolveCheckouts,
9
9
  start,
10
10
  status
11
- } from "./chunk-EBT7VSPY.js";
11
+ } from "./chunk-IH7KFCV4.js";
12
12
 
13
13
  // src/cli.ts
14
14
  import { parseArgs } from "util";
package/dist/engine.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  resolveCheckouts,
10
10
  start,
11
11
  status
12
- } from "./chunk-EBT7VSPY.js";
12
+ } from "./chunk-IH7KFCV4.js";
13
13
  export {
14
14
  AGENT_VERSION,
15
15
  browsersReady,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowriki/agent",
3
- "version": "0.1.0-beta.24",
3
+ "version": "0.1.0-beta.26",
4
4
  "description": "Run Flowriki UI tests on your own computer. Connects this machine to your Flowriki account.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",