@yoqa/runner 0.3.15 → 0.3.17

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.
Files changed (2) hide show
  1. package/dist/index.js +126 -30
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -16801,7 +16801,7 @@ function getCatalogDb() {
16801
16801
  // package.json
16802
16802
  var package_default = {
16803
16803
  name: "@yoqa/runner",
16804
- version: "0.3.15",
16804
+ version: "0.3.17",
16805
16805
  description: "Yoqa local runner \u2014 Appium + catalog HTTP API (Bun). Start with yoqa serve or the yoqa-runner bin.",
16806
16806
  license: "UNLICENSED",
16807
16807
  type: "module",
@@ -17020,6 +17020,12 @@ function mergeCapabilities(appCaps, caseCaps) {
17020
17020
  function looksLikePhysicalIosUdid(udid) {
17021
17021
  return /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{16}$/.test(udid.trim());
17022
17022
  }
17023
+ var PHYSICAL_IOS_SESSION_TIMEOUT_MS = 60000;
17024
+ var SIMULATOR_WDA_SESSION_TIMEOUT_MS = 600000;
17025
+ var SIMULATOR_WDA_DERIVED_DATA = join6(YOQA_ROOT4, "wda-sim");
17026
+ function iosSessionCreateTimeoutMs(deviceId) {
17027
+ return looksLikePhysicalIosUdid(deviceId) ? PHYSICAL_IOS_SESSION_TIMEOUT_MS : SIMULATOR_WDA_SESSION_TIMEOUT_MS;
17028
+ }
17023
17029
  async function buildW3cCapabilities(options, mjpegPort) {
17024
17030
  const merged = mergeCapabilities(options.appCaps, options.caseCaps);
17025
17031
  const platformName = options.platform === "ios" ? "iOS" : "Android";
@@ -17038,32 +17044,40 @@ async function buildW3cCapabilities(options, mjpegPort) {
17038
17044
  if (options.platform === "android" && options.appPackage && !caps["appium:appPackage"]) {
17039
17045
  caps["appium:appPackage"] = options.appPackage;
17040
17046
  }
17041
- if (options.platform === "ios" && looksLikePhysicalIosUdid(options.deviceId)) {
17042
- const prep = await loadDevicePrep(options.deviceId);
17043
- if (!prep) {
17044
- throw new Error(`iOS device ${options.deviceId} is not prepared. Run device setup so WebDriverAgent is installed before starting a run.`);
17045
- }
17046
- if (caps["appium:usePreinstalledWDA"] === undefined) {
17047
- caps["appium:usePreinstalledWDA"] = true;
17048
- }
17049
- if (!caps["appium:updatedWDABundleId"]) {
17050
- caps["appium:updatedWDABundleId"] = prep.bundleId;
17051
- }
17052
- if (!caps["appium:xcodeOrgId"]) {
17053
- caps["appium:xcodeOrgId"] = prep.developmentTeam;
17054
- }
17055
- if (!caps["appium:xcodeSigningId"]) {
17056
- caps["appium:xcodeSigningId"] = "Apple Development";
17047
+ if (options.platform === "ios") {
17048
+ const physical = looksLikePhysicalIosUdid(options.deviceId);
17049
+ if (physical) {
17050
+ const prep = await loadDevicePrep(options.deviceId);
17051
+ if (!prep) {
17052
+ throw new Error(`iOS device ${options.deviceId} is not prepared. Run device setup so WebDriverAgent is installed before starting a run.`);
17053
+ }
17054
+ if (caps["appium:usePreinstalledWDA"] === undefined) {
17055
+ caps["appium:usePreinstalledWDA"] = true;
17056
+ }
17057
+ if (!caps["appium:updatedWDABundleId"]) {
17058
+ caps["appium:updatedWDABundleId"] = prep.bundleId;
17059
+ }
17060
+ if (!caps["appium:xcodeOrgId"]) {
17061
+ caps["appium:xcodeOrgId"] = prep.developmentTeam;
17062
+ }
17063
+ if (!caps["appium:xcodeSigningId"]) {
17064
+ caps["appium:xcodeSigningId"] = "Apple Development";
17065
+ }
17057
17066
  }
17067
+ const wdaTimeoutMs = iosSessionCreateTimeoutMs(options.deviceId);
17058
17068
  if (caps["appium:wdaLaunchTimeout"] === undefined) {
17059
- caps["appium:wdaLaunchTimeout"] = 60000;
17069
+ caps["appium:wdaLaunchTimeout"] = wdaTimeoutMs;
17060
17070
  }
17061
17071
  if (caps["appium:wdaConnectionTimeout"] === undefined) {
17062
- caps["appium:wdaConnectionTimeout"] = 60000;
17072
+ caps["appium:wdaConnectionTimeout"] = wdaTimeoutMs;
17063
17073
  }
17064
17074
  if (caps["appium:waitForIdleTimeout"] === undefined) {
17065
17075
  caps["appium:waitForIdleTimeout"] = 0;
17066
17076
  }
17077
+ if (!physical && caps["appium:derivedDataPath"] === undefined) {
17078
+ await mkdir3(SIMULATOR_WDA_DERIVED_DATA, { recursive: true });
17079
+ caps["appium:derivedDataPath"] = SIMULATOR_WDA_DERIVED_DATA;
17080
+ }
17067
17081
  }
17068
17082
  return caps;
17069
17083
  }
@@ -17285,14 +17299,16 @@ async function createDeviceSession(options) {
17285
17299
  const port = await ensureAppiumServer();
17286
17300
  const mjpegPort = await pickMjpegPort();
17287
17301
  const capabilities = await buildW3cCapabilities(options, mjpegPort);
17302
+ const simulatorIos = options.platform === "ios" && !looksLikePhysicalIosUdid(options.deviceId);
17303
+ const sessionCreateTimeoutMs = simulatorIos ? iosSessionCreateTimeoutMs(options.deviceId) : 60000;
17288
17304
  const browser = await remote({
17289
17305
  hostname: APPIUM_HOST,
17290
17306
  port,
17291
17307
  path: "/",
17292
17308
  capabilities,
17293
17309
  logLevel: "silent",
17294
- connectionRetryCount: 2,
17295
- connectionRetryTimeout: 60000
17310
+ connectionRetryCount: simulatorIos ? 0 : 2,
17311
+ connectionRetryTimeout: sessionCreateTimeoutMs
17296
17312
  });
17297
17313
  await applyMjpegSettings(browser, {
17298
17314
  platform: options.platform,
@@ -23542,13 +23558,24 @@ var caseFlowStepSchema = exports_external.object({
23542
23558
  flowId: exports_external.string().min(1).nullable().optional()
23543
23559
  });
23544
23560
  var caseRunStatusSchema = exports_external.union([exports_external.literal("passed"), exports_external.literal("errored")]);
23545
- var caseScriptActionSchema = exports_external.discriminatedUnion("type", [
23546
- exports_external.object({
23547
- type: exports_external.literal("tap"),
23548
- x: exports_external.number().min(0).max(1000),
23549
- y: exports_external.number().min(0).max(1000),
23550
- reason: exports_external.string().optional()
23551
- }),
23561
+ var caseScriptTapSchema = exports_external.object({
23562
+ type: exports_external.literal("tap"),
23563
+ x: exports_external.number().min(0).max(1000).optional(),
23564
+ y: exports_external.number().min(0).max(1000).optional(),
23565
+ label: exports_external.string().min(1).optional(),
23566
+ id: exports_external.string().min(1).optional(),
23567
+ reason: exports_external.string().optional()
23568
+ }).superRefine((value, ctx) => {
23569
+ const hasCoords = value.x != null && value.y != null;
23570
+ if (!hasCoords && !value.label && !value.id) {
23571
+ ctx.addIssue({
23572
+ code: exports_external.ZodIssueCode.custom,
23573
+ message: "tap requires x/y or label or id"
23574
+ });
23575
+ }
23576
+ });
23577
+ var caseScriptActionSchema = exports_external.union([
23578
+ caseScriptTapSchema,
23552
23579
  exports_external.object({
23553
23580
  type: exports_external.literal("type"),
23554
23581
  text: exports_external.string(),
@@ -23558,6 +23585,13 @@ var caseScriptActionSchema = exports_external.discriminatedUnion("type", [
23558
23585
  type: exports_external.literal("wait"),
23559
23586
  ms: exports_external.number().min(0).max(1e4),
23560
23587
  reason: exports_external.string().optional()
23588
+ }),
23589
+ exports_external.object({
23590
+ type: exports_external.literal("assert"),
23591
+ assertion: exports_external.union([exports_external.literal("visible"), exports_external.literal("not-visible")]).default("visible"),
23592
+ text: exports_external.string().min(1),
23593
+ timeoutMs: exports_external.number().min(0).max(120000).optional(),
23594
+ reason: exports_external.string().optional()
23561
23595
  })
23562
23596
  ]);
23563
23597
  var caseScriptSchema = exports_external.object({
@@ -23966,6 +24000,9 @@ var createBuildRequestSchema = exports_external.object({
23966
24000
  var listBuildsResponseSchema = exports_external.object({
23967
24001
  builds: exports_external.array(buildSchema)
23968
24002
  });
24003
+
24004
+ // ../../packages/runner-client/src/wait-for-run.ts
24005
+ var DEFAULT_WAIT_FOR_RUN_TIMEOUT_MS = 30 * 60 * 1000;
23969
24006
  // ../../packages/runner-client/src/shell-script.ts
23970
24007
  var DEFAULT_SHELL_SCRIPT_HEADER = [
23971
24008
  "#!/usr/bin/env bash",
@@ -23975,6 +24012,16 @@ var DEFAULT_SHELL_SCRIPT_HEADER = [
23975
24012
  ""
23976
24013
  ].join(`
23977
24014
  `);
24015
+ function screenHasText(elements, text2) {
24016
+ const needle = text2.trim().toLowerCase();
24017
+ if (!needle)
24018
+ return false;
24019
+ return (elements ?? []).some((el) => {
24020
+ const label = el.label?.toLowerCase() ?? "";
24021
+ const type = el.type?.toLowerCase() ?? "";
24022
+ return label.includes(needle) || type.includes(needle);
24023
+ });
24024
+ }
23978
24025
  function findElementByLabel(elements, label) {
23979
24026
  const needle = label.trim().toLowerCase();
23980
24027
  if (!needle)
@@ -97420,6 +97467,10 @@ var defaultClock = {
97420
97467
  };
97421
97468
  async function executeScriptCase(deps) {
97422
97469
  const perform = deps.performAction ?? performAction;
97470
+ const readScreen = deps.readScreen ?? (async (session) => {
97471
+ const screen = await getScreen(session, { full: false });
97472
+ return { elements: screen.elements };
97473
+ });
97423
97474
  const clock = deps.clock ?? defaultClock;
97424
97475
  const settleMs = deps.settleMs ?? POST_ACTION_SETTLE_MS;
97425
97476
  let stepIdx = 0;
@@ -97437,7 +97488,16 @@ async function executeScriptCase(deps) {
97437
97488
  return "cancelled";
97438
97489
  }
97439
97490
  if (action.type === "tap") {
97440
- await perform(deps.session, { kind: "tap", x: action.x, y: action.y });
97491
+ const tapBody = { kind: "tap" };
97492
+ if (action.label)
97493
+ tapBody.label = action.label;
97494
+ if (action.id)
97495
+ tapBody.id = action.id;
97496
+ if (action.x != null)
97497
+ tapBody.x = action.x;
97498
+ if (action.y != null)
97499
+ tapBody.y = action.y;
97500
+ await perform(deps.session, tapBody);
97441
97501
  await clock.sleep(settleMs);
97442
97502
  await deps.appendStep({
97443
97503
  idx: stepIdx,
@@ -97445,13 +97505,15 @@ async function executeScriptCase(deps) {
97445
97505
  type: "tap",
97446
97506
  x: action.x,
97447
97507
  y: action.y,
97508
+ label: action.label,
97509
+ id: action.id,
97448
97510
  reason: action.reason ?? "Replayed saved script tap",
97449
97511
  thoughts: "Replaying the saved script without calling the AI agent."
97450
97512
  },
97451
97513
  screenshotUri: shot.path,
97452
97514
  ok: true,
97453
97515
  latencyMs,
97454
- detail: action.reason ?? null
97516
+ detail: action.reason ?? action.label ?? action.id ?? null
97455
97517
  });
97456
97518
  } else if (action.type === "type") {
97457
97519
  await perform(deps.session, { kind: "input", text: action.text });
@@ -97469,6 +97531,40 @@ async function executeScriptCase(deps) {
97469
97531
  latencyMs,
97470
97532
  detail: action.reason ?? null
97471
97533
  });
97534
+ } else if (action.type === "assert") {
97535
+ const timeoutMs = action.timeoutMs ?? 5000;
97536
+ const assertion = action.assertion;
97537
+ const deadline = clock.now() + timeoutMs;
97538
+ for (;; ) {
97539
+ if (deps.isAborted()) {
97540
+ return "cancelled";
97541
+ }
97542
+ const screen = await readScreen(deps.session);
97543
+ const found = screenHasText(screen.elements, action.text);
97544
+ if (assertion === "visible" && found)
97545
+ break;
97546
+ if (assertion === "not-visible" && !found)
97547
+ break;
97548
+ if (clock.now() >= deadline) {
97549
+ throw new Error(assertion === "visible" ? `Expected visible text not found within ${Math.round(timeoutMs / 1000)}s: ${action.text}` : `Unexpected text still visible after ${Math.round(timeoutMs / 1000)}s: ${action.text}`);
97550
+ }
97551
+ await clock.sleep(400);
97552
+ }
97553
+ await deps.appendStep({
97554
+ idx: stepIdx,
97555
+ action: {
97556
+ type: "assert",
97557
+ assertion,
97558
+ text: action.text,
97559
+ timeoutMs,
97560
+ reason: action.reason ?? `Assert ${assertion}: ${action.text}`,
97561
+ thoughts: "Replaying the saved script without calling the AI agent."
97562
+ },
97563
+ screenshotUri: shot.path,
97564
+ ok: true,
97565
+ latencyMs,
97566
+ detail: action.reason ?? `${assertion}: ${action.text}`
97567
+ });
97472
97568
  } else {
97473
97569
  const waitMs = Math.min(3000, Math.max(500, action.ms));
97474
97570
  await clock.sleep(waitMs);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoqa/runner",
3
- "version": "0.3.15",
3
+ "version": "0.3.17",
4
4
  "description": "Yoqa local runner — Appium + catalog HTTP API (Bun). Start with yoqa serve or the yoqa-runner bin.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",