@yoqa/cli 1.0.5 → 1.0.7

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/main.js +246 -26
  2. package/package.json +2 -2
package/dist/main.js CHANGED
@@ -13059,6 +13059,8 @@ var deviceKindSchema = exports_external.union([
13059
13059
  ]);
13060
13060
  var VIEWPORT_PRESET_NAMES = ["desktop", "mobile"];
13061
13061
  var viewportPresetSchema = exports_external.enum(VIEWPORT_PRESET_NAMES);
13062
+ var browserProfileSchema = exports_external.enum(["kept", "clean"]);
13063
+ var PROFILE_APP_ID_PATTERN = /^(?!\.{1,2}$)[A-Za-z0-9._-]+$/;
13062
13064
  var deviceSchema = exports_external.object({
13063
13065
  id: exports_external.string().min(1),
13064
13066
  name: exports_external.string().min(1),
@@ -13068,7 +13070,8 @@ var deviceSchema = exports_external.object({
13068
13070
  kind: deviceKindSchema,
13069
13071
  state: exports_external.string().optional(),
13070
13072
  model: exports_external.string().optional(),
13071
- unavailableReason: exports_external.string().optional()
13073
+ unavailableReason: exports_external.string().optional(),
13074
+ verified: exports_external.boolean().optional()
13072
13075
  });
13073
13076
  var listDevicesResponseSchema = exports_external.object({
13074
13077
  platform: devicePlatformSchema,
@@ -13388,6 +13391,7 @@ var caseScriptSchema = exports_external.object({
13388
13391
  version: exports_external.literal(1),
13389
13392
  sourceRunId: exports_external.string().min(1).optional(),
13390
13393
  savedAt: exports_external.number().int().nonnegative(),
13394
+ profile: browserProfileSchema.optional(),
13391
13395
  actions: exports_external.array(caseScriptActionSchema).min(1)
13392
13396
  });
13393
13397
  var catalogCaseSchema = exports_external.object({
@@ -13635,7 +13639,9 @@ var createRunRequestSchema = exports_external.object({
13635
13639
  executionMode: runExecutionModeSchema.optional(),
13636
13640
  screenMode: runScreenModeSchema.optional(),
13637
13641
  lane: exports_external.union([laneNameSchema, exports_external.literal("auto")]).optional(),
13638
- recordVideo: exports_external.boolean().optional()
13642
+ recordVideo: exports_external.boolean().optional(),
13643
+ profile: browserProfileSchema.optional(),
13644
+ viewport: viewportPresetSchema.optional()
13639
13645
  });
13640
13646
  var callUsageSchema = exports_external.object({
13641
13647
  inputTokens: exports_external.number().int().nonnegative().nullable(),
@@ -13695,7 +13701,9 @@ var runSchema = exports_external.object({
13695
13701
  lane: laneNameSchema.optional(),
13696
13702
  laneWarning: exports_external.string().min(1).optional(),
13697
13703
  browser: exports_external.string().min(1).optional(),
13704
+ browserProfileName: exports_external.string().min(1).optional(),
13698
13705
  viewport: viewportPresetSchema.optional(),
13706
+ profile: browserProfileSchema.optional(),
13699
13707
  error: exports_external.string().nullable(),
13700
13708
  createdAt: exports_external.number().int().nonnegative(),
13701
13709
  startedAt: exports_external.number().int().nonnegative().nullable(),
@@ -13715,7 +13723,24 @@ var connectDeviceRequestSchema = exports_external.object({
13715
13723
  bundleId: exports_external.string().min(1).optional(),
13716
13724
  appPackage: exports_external.string().min(1).optional(),
13717
13725
  lane: exports_external.union([laneNameSchema, exports_external.literal("auto")]).optional(),
13718
- viewport: viewportPresetSchema.optional()
13726
+ viewport: viewportPresetSchema.optional(),
13727
+ appId: exports_external.string().regex(PROFILE_APP_ID_PATTERN).optional(),
13728
+ profile: browserProfileSchema.optional(),
13729
+ acceptEverydayWarning: exports_external.boolean().optional()
13730
+ });
13731
+ var everydayWarningRequiredSchema = exports_external.object({
13732
+ error: exports_external.string(),
13733
+ code: exports_external.literal("EVERYDAY_WARNING_REQUIRED"),
13734
+ appId: exports_external.string().min(1),
13735
+ warning: exports_external.string().min(1)
13736
+ });
13737
+ var resetWebProfileRequestSchema = exports_external.object({
13738
+ appId: exports_external.string().regex(PROFILE_APP_ID_PATTERN).optional(),
13739
+ browserId: exports_external.string().min(1).optional()
13740
+ });
13741
+ var resetWebProfileResponseSchema = exports_external.object({
13742
+ appId: exports_external.string(),
13743
+ browserIds: exports_external.array(exports_external.string())
13719
13744
  });
13720
13745
  var streamStateSchema = exports_external.union([
13721
13746
  exports_external.literal("ready"),
@@ -13734,7 +13759,9 @@ var activeDeviceResponseSchema = exports_external.object({
13734
13759
  heldByRunId: exports_external.string().min(1).optional(),
13735
13760
  lane: laneNameSchema.optional(),
13736
13761
  laneWarning: exports_external.string().min(1).optional(),
13737
- viewport: viewportPresetSchema.optional()
13762
+ viewport: viewportPresetSchema.optional(),
13763
+ browserProfileName: exports_external.string().nullable().optional(),
13764
+ targetApp: exports_external.string().min(1).optional()
13738
13765
  });
13739
13766
  var retargetDeviceRequestSchema = exports_external.object({
13740
13767
  bundleId: exports_external.string().min(1).optional(),
@@ -13747,6 +13774,10 @@ var controlPointerMessageSchema = exports_external.object({
13747
13774
  y: exports_external.number().min(0).max(1000),
13748
13775
  seq: exports_external.number().int().nonnegative()
13749
13776
  });
13777
+ var controlScreenChangedMessageSchema = exports_external.object({
13778
+ ok: exports_external.literal(true),
13779
+ type: exports_external.literal("screen-changed")
13780
+ });
13750
13781
  var screenElementSchema = exports_external.object({
13751
13782
  type: exports_external.string(),
13752
13783
  label: exports_external.string(),
@@ -13758,11 +13789,16 @@ var screenElementSchema = exports_external.object({
13758
13789
  enabled: exports_external.boolean().optional(),
13759
13790
  visible: exports_external.boolean().optional()
13760
13791
  });
13792
+ var screenSourceSchema = exports_external.object({
13793
+ kind: exports_external.enum(["target", "foreground", "system"]),
13794
+ bundleId: exports_external.string().optional()
13795
+ });
13761
13796
  var screenResponseSchema = exports_external.object({
13762
13797
  full: exports_external.boolean(),
13763
13798
  window: exports_external.object({ width: exports_external.number(), height: exports_external.number() }),
13764
13799
  elements: exports_external.array(screenElementSchema).optional(),
13765
- raw: exports_external.string().optional()
13800
+ raw: exports_external.string().optional(),
13801
+ source: screenSourceSchema.optional()
13766
13802
  });
13767
13803
  var screenshotRequestSchema = exports_external.object({
13768
13804
  path: exports_external.string().min(1).optional()
@@ -13819,6 +13855,7 @@ var actionPhasesSchema = exports_external.object({
13819
13855
  actionMs: exports_external.number().nonnegative(),
13820
13856
  settleMs: exports_external.number().nonnegative()
13821
13857
  });
13858
+ var appStorageSchema = exports_external.union([exports_external.literal("cleared"), exports_external.literal("kept")]);
13822
13859
  var actionResponseSchema = exports_external.object({
13823
13860
  ok: exports_external.literal(true),
13824
13861
  kind: actionKindSchema,
@@ -13827,7 +13864,8 @@ var actionResponseSchema = exports_external.object({
13827
13864
  y: exports_external.number().optional()
13828
13865
  }).optional(),
13829
13866
  screenshot: actionResultScreenshotSchema.optional(),
13830
- phases: actionPhasesSchema.optional()
13867
+ phases: actionPhasesSchema.optional(),
13868
+ storage: appStorageSchema.optional()
13831
13869
  });
13832
13870
  var actionBatchRequestSchema = exports_external.object({
13833
13871
  steps: exports_external.array(actionRequestSchema).min(1),
@@ -14198,7 +14236,7 @@ var FIELD_ORDER = [
14198
14236
  "durationMs",
14199
14237
  "reason"
14200
14238
  ];
14201
- var TOP_LEVEL_KEYS = new Set(["version", "name", "sourceRunId", "savedAt", "actions"]);
14239
+ var TOP_LEVEL_KEYS = new Set(["version", "name", "sourceRunId", "savedAt", "profile", "actions"]);
14202
14240
  function isActionType(value) {
14203
14241
  return typeof value === "string" && ACTION_TYPES.includes(value);
14204
14242
  }
@@ -14441,6 +14479,14 @@ function parseCaseScriptFile(text, options = {}) {
14441
14479
  message: "sourceRunId must be text"
14442
14480
  });
14443
14481
  }
14482
+ const profile = browserProfileSchema.safeParse(top.profile);
14483
+ if (top.profile != null && !profile.success) {
14484
+ errors3.push({
14485
+ line: lineOf(root.get("profile", true), lineCounter2),
14486
+ action: null,
14487
+ message: `profile must be ${browserProfileSchema.options.join(" or ")}`
14488
+ });
14489
+ }
14444
14490
  const savedAt = top.savedAt;
14445
14491
  const legacy = savedAt !== undefined;
14446
14492
  if (legacy && (typeof savedAt !== "number" || !Number.isInteger(savedAt) || savedAt < 0)) {
@@ -14494,6 +14540,7 @@ function parseCaseScriptFile(text, options = {}) {
14494
14540
  version: 1,
14495
14541
  ...typeof sourceRunId === "string" ? { sourceRunId } : {},
14496
14542
  savedAt: typeof savedAt === "number" ? savedAt : options.savedAt ?? Date.now(),
14543
+ ...profile.success ? { profile: profile.data } : {},
14497
14544
  actions
14498
14545
  }
14499
14546
  };
@@ -14594,6 +14641,8 @@ function formatCaseScriptFile(script, options = {}) {
14594
14641
  lines.push(`name: ${yamlScalar(name)}`);
14595
14642
  if (script.sourceRunId)
14596
14643
  lines.push(`sourceRunId: ${yamlScalar(script.sourceRunId)}`);
14644
+ if (script.profile)
14645
+ lines.push(`profile: ${script.profile}`);
14597
14646
  lines.push("actions:");
14598
14647
  for (const action of script.actions) {
14599
14648
  lines.push(...formatCaseScriptFileItemLines(action, " "));
@@ -15296,6 +15345,11 @@ function mapCatalogStep(step, screenshotsByStepId) {
15296
15345
  screenshotBase64: screenshotsByStepId[step.id] ?? null
15297
15346
  };
15298
15347
  }
15348
+ function reportedBrowser(run) {
15349
+ if (!run.browser)
15350
+ return null;
15351
+ return run.browserProfileName ? `${run.browser} · profile ${run.browserProfileName}` : run.browser;
15352
+ }
15299
15353
  function buildRunReportFromCatalogRun(run, meta = {}, screenshotsByStepId = {}) {
15300
15354
  const status = toReportStatus(run.status);
15301
15355
  if (!status) {
@@ -15325,8 +15379,9 @@ function buildRunReportFromCatalogRun(run, meta = {}, screenshotsByStepId = {})
15325
15379
  screenMode: run.screenMode ?? null,
15326
15380
  lane: run.lane ?? null,
15327
15381
  laneWarning: run.laneWarning ?? null,
15328
- browser: run.browser ?? null,
15382
+ browser: reportedBrowser(run),
15329
15383
  viewport: run.viewport ?? null,
15384
+ profile: run.profile ?? null,
15330
15385
  error: run.error,
15331
15386
  createdAt: run.createdAt,
15332
15387
  startedAt: run.startedAt,
@@ -15387,6 +15442,7 @@ function formatRunReportHtml(doc) {
15387
15442
  ["Platform", doc.platform ?? "—"],
15388
15443
  ...doc.browser ? [["Browser", doc.browser]] : [],
15389
15444
  ...doc.viewport ? [["Viewport", formatViewportPreset(doc.viewport)]] : [],
15445
+ ...doc.profile ? [["Profile", doc.profile]] : [],
15390
15446
  ["Mode", doc.executionMode ?? "—"],
15391
15447
  ...screenRows,
15392
15448
  ["Started", formatWhen(doc.startedAt ?? doc.createdAt)],
@@ -15515,6 +15571,7 @@ function formatRunReportMarkdown(doc) {
15515
15571
  `| Platform | ${escapeMd(doc.platform ?? "—")} |`,
15516
15572
  ...doc.browser ? [`| Browser | ${escapeMd(doc.browser)} |`] : [],
15517
15573
  ...doc.viewport ? [`| Viewport | ${escapeMd(formatViewportPreset(doc.viewport))} |`] : [],
15574
+ ...doc.profile ? [`| Profile | ${escapeMd(doc.profile)} |`] : [],
15518
15575
  `| Mode | ${escapeMd(doc.executionMode ?? "—")} |`,
15519
15576
  ...doc.screenMode ? [`| Screen | ${escapeMd(doc.screenMode)} |`] : [],
15520
15577
  ...doc.lane ? [`| Lane | ${escapeMd(doc.lane)} |`] : [],
@@ -15586,6 +15643,7 @@ function formatRunReportGithubSummary(doc) {
15586
15643
  `| Platform | ${escapeMd(doc.platform ?? "—")} |`,
15587
15644
  ...doc.browser ? [`| Browser | ${escapeMd(doc.browser)} |`] : [],
15588
15645
  ...doc.viewport ? [`| Viewport | ${escapeMd(formatViewportPreset(doc.viewport))} |`] : [],
15646
+ ...doc.profile ? [`| Profile | ${escapeMd(doc.profile)} |`] : [],
15589
15647
  `| Mode | ${escapeMd(doc.executionMode ?? "—")} |`,
15590
15648
  ...doc.screenMode ? [`| Screen | ${escapeMd(doc.screenMode)} |`] : [],
15591
15649
  ...doc.lane ? [`| Lane | ${escapeMd(doc.lane)} |`] : [],
@@ -15623,6 +15681,28 @@ function formatRunReportGithubSummary(doc) {
15623
15681
  return lines.join(`
15624
15682
  `);
15625
15683
  }
15684
+ // ../runner-client/src/web-browsers.ts
15685
+ var WEB_BROWSERS = [
15686
+ { browser: "chrome", name: "Google Chrome", verified: true },
15687
+ { browser: "edge", name: "Microsoft Edge", verified: true },
15688
+ { browser: "brave", name: "Brave Browser", verified: true },
15689
+ { browser: "chromium", name: "Chromium", verified: true },
15690
+ { browser: "dia", name: "Dia", verified: true },
15691
+ { browser: "arc", name: "Arc", verified: false },
15692
+ { browser: "opera", name: "Opera", verified: false },
15693
+ { browser: "vivaldi", name: "Vivaldi", verified: false }
15694
+ ];
15695
+ function installedBrowserId(browser) {
15696
+ return `web-${browser}`;
15697
+ }
15698
+ function everydayBrowserId(browser) {
15699
+ return `web-${browser}-everyday`;
15700
+ }
15701
+ var REMOTE_DEBUGGING_SETTING_URL = "chrome://inspect/#remote-debugging";
15702
+ function formatDeviceState(device) {
15703
+ const parts = [device.state, device.verified === false ? "unverified" : null].filter(Boolean);
15704
+ return parts.length > 0 ? parts.join(" · ") : null;
15705
+ }
15626
15706
  // ../runner-client/src/index.ts
15627
15707
  var DEFAULT_BASE_URL = "http://127.0.0.1:7420";
15628
15708
  function errorMessageFromBody(json, fallback) {
@@ -15657,6 +15737,36 @@ class RunnerRequestError extends Error {
15657
15737
  this.name = "RunnerRequestError";
15658
15738
  }
15659
15739
  }
15740
+
15741
+ class RunnerUnreachableError extends Error {
15742
+ url;
15743
+ constructor(url, options) {
15744
+ const reason = options.cause instanceof Error ? options.cause.message : String(options.cause);
15745
+ super(reason, options);
15746
+ this.url = url;
15747
+ this.name = "RunnerUnreachableError";
15748
+ }
15749
+ }
15750
+ function answeredFetch(fetchImpl) {
15751
+ const wrapped = async (input, init) => {
15752
+ try {
15753
+ return await fetchImpl(input, init);
15754
+ } catch (error) {
15755
+ if (error instanceof Error && error.name === "AbortError")
15756
+ throw error;
15757
+ throw new RunnerUnreachableError(String(input instanceof Request ? input.url : input), {
15758
+ cause: error
15759
+ });
15760
+ }
15761
+ };
15762
+ return Object.assign(wrapped, fetchImpl);
15763
+ }
15764
+ function everydayWarningRequired(error) {
15765
+ if (!(error instanceof RunnerRequestError) || error.status !== 428)
15766
+ return null;
15767
+ const parsed = everydayWarningRequiredSchema.safeParse(error.body);
15768
+ return parsed.success ? parsed.data : null;
15769
+ }
15660
15770
  function developerNotTrusted(error) {
15661
15771
  if (!(error instanceof RunnerRequestError) || error.status !== 428)
15662
15772
  return null;
@@ -15679,7 +15789,7 @@ class RunnerClient {
15679
15789
  fetchImpl;
15680
15790
  constructor(options = {}) {
15681
15791
  this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
15682
- this.fetchImpl = options.fetchImpl ?? fetch.bind(globalThis);
15792
+ this.fetchImpl = answeredFetch(options.fetchImpl ?? fetch.bind(globalThis));
15683
15793
  }
15684
15794
  async requestJson(path, init, fallbackError) {
15685
15795
  const response = await this.fetchImpl(`${this.baseUrl}${path}`, uncached(init));
@@ -16013,15 +16123,27 @@ class RunnerClient {
16013
16123
  async deleteRunTestVideo(runId, runTestId) {
16014
16124
  await this.requestJson(`/runs/${encodeURIComponent(runId)}/tests/${encodeURIComponent(runTestId)}/video`, { method: "DELETE" }, "Delete video failed");
16015
16125
  }
16016
- async connectDevice(request) {
16126
+ async connectDevice(request, options = {}) {
16017
16127
  const body = connectDeviceRequestSchema.parse(request);
16018
16128
  const json = await this.requestJson("/devices/connect", {
16019
16129
  method: "POST",
16020
16130
  headers: { "Content-Type": "application/json" },
16021
- body: JSON.stringify(body)
16131
+ body: JSON.stringify(body),
16132
+ signal: options.signal
16022
16133
  }, "Connect device failed");
16023
16134
  return activeDeviceResponseSchema.parse(json);
16024
16135
  }
16136
+ async resetWebProfile(request = {}) {
16137
+ const { appId, browserId } = resetWebProfileRequestSchema.parse(request);
16138
+ const params = new URLSearchParams;
16139
+ if (appId)
16140
+ params.set("appId", appId);
16141
+ if (browserId)
16142
+ params.set("browserId", browserId);
16143
+ const query = params.toString();
16144
+ const json = await this.requestJson(`/devices/web/profiles${query ? `?${query}` : ""}`, { method: "DELETE" }, "Reset browser profile failed");
16145
+ return resetWebProfileResponseSchema.parse(json);
16146
+ }
16025
16147
  async getActiveDevice() {
16026
16148
  const response = await this.fetchImpl(`${this.baseUrl}/devices/active?_=${Date.now()}`, uncached());
16027
16149
  if (response.status === 404)
@@ -16512,7 +16634,7 @@ function recommendScreenDefault(vision, tree) {
16512
16634
  // package.json
16513
16635
  var package_default = {
16514
16636
  name: "@yoqa/cli",
16515
- version: "1.0.5",
16637
+ version: "1.0.7",
16516
16638
  description: "Yoqa CLI — drive local mobile QA from the terminal or CI (talks to the Yoqa runner over HTTP)",
16517
16639
  license: "UNLICENSED",
16518
16640
  type: "module",
@@ -16549,7 +16671,7 @@ var package_default = {
16549
16671
  health: "bun run src/main.ts health"
16550
16672
  },
16551
16673
  dependencies: {
16552
- "@yoqa/runner": "1.0.5"
16674
+ "@yoqa/runner": "1.0.7"
16553
16675
  },
16554
16676
  devDependencies: {
16555
16677
  "@types/node": "^22.15.30",
@@ -17049,7 +17171,7 @@ function commandChainIncludes(command, name) {
17049
17171
  var PLATFORM_DEVICES_DESCRIPTION = {
17050
17172
  ios: "List iOS devices and simulators",
17051
17173
  android: "List Android devices and emulators",
17052
- web: "List installed web browsers (attach a Chromium you started by naming its port: chromium-cdp-<port>)"
17174
+ web: "List installed web browsers and your Everyday browsers (CI: attach a Chromium you started by naming its port, chromium-cdp-<port>)"
17053
17175
  };
17054
17176
  function parsePlatform(value) {
17055
17177
  const parsed = devicePlatformSchema.safeParse(value);
@@ -17059,9 +17181,18 @@ function parsePlatform(value) {
17059
17181
  }
17060
17182
  return parsed.data;
17061
17183
  }
17062
- var WEB_DEVICE_ID = /^(web-(chrome|edge|brave|chromium)|chromium-cdp-\d+)$/;
17184
+ var INSTALLED_BROWSER_IDS = new Set(WEB_BROWSERS.map(({ browser }) => installedBrowserId(browser)));
17185
+ var ATTACH_DEVICE_ID = /^chromium-cdp-\d+$/;
17186
+ var EVERYDAY_BROWSER_NAMES = new Map(WEB_BROWSERS.map(({ browser, name }) => [everydayBrowserId(browser), name]));
17187
+ function everydayBrowserName(deviceId) {
17188
+ return EVERYDAY_BROWSER_NAMES.get(deviceId) ?? null;
17189
+ }
17190
+ function isEverydayDeviceId(deviceId) {
17191
+ return everydayBrowserName(deviceId) !== null;
17192
+ }
17193
+ var UNSUPPORTED_BROWSER_ID = /^web-(firefox|safari)(-everyday)?$/;
17063
17194
  function isWebDeviceId(deviceId) {
17064
- return WEB_DEVICE_ID.test(deviceId);
17195
+ return INSTALLED_BROWSER_IDS.has(deviceId) || ATTACH_DEVICE_ID.test(deviceId) || isEverydayDeviceId(deviceId) || UNSUPPORTED_BROWSER_ID.test(deviceId);
17065
17196
  }
17066
17197
  function connectPlatform(deviceId, value) {
17067
17198
  if (value === undefined && isWebDeviceId(deviceId))
@@ -17370,12 +17501,13 @@ function client(baseUrl) {
17370
17501
  }
17371
17502
  function describeSession(body) {
17372
17503
  const viewport = body.viewport ? ` viewport ${formatViewportPreset(body.viewport)}` : "";
17373
- return `${body.platform} ${body.deviceId} lane ${body.lane ?? "direct"}${viewport}`;
17504
+ const profile = body.browserProfileName ? ` profile ${body.browserProfileName}` : "";
17505
+ return `${body.platform} ${body.deviceId} lane ${body.lane ?? "direct"}${viewport}${profile}`;
17374
17506
  }
17375
17507
  function fail(command, error) {
17376
17508
  const message = error instanceof Error ? error.message : String(error);
17377
17509
  console.error(`yoqa ${command} failed: ${message}`);
17378
- if (!(error instanceof RunnerRequestError) && !message.includes("yoqa serve") && !message.includes("Bun")) {
17510
+ if (error instanceof RunnerUnreachableError) {
17379
17511
  console.error("Is the runner up? Try: yoqa serve");
17380
17512
  }
17381
17513
  process.exitCode = 1;
@@ -17493,6 +17625,10 @@ program2.command("status").description("Show local runner, provider, and active
17493
17625
  }
17494
17626
  });
17495
17627
  var devices = program2.command("devices").description("List local devices, simulators, and emulators");
17628
+ var EVERYDAY_HINTS = {
17629
+ "remote debugging off": `Turn it on at ${REMOTE_DEBUGGING_SETTING_URL}, then connect`,
17630
+ "not running": `Open it, turn on remote debugging at ${REMOTE_DEBUGGING_SETTING_URL}, then connect`
17631
+ };
17496
17632
  function printDevicesTable(devicesList) {
17497
17633
  if (devicesList.length === 0) {
17498
17634
  console.log("No devices found.");
@@ -17500,16 +17636,21 @@ function printDevicesTable(devicesList) {
17500
17636
  }
17501
17637
  for (const device of devicesList) {
17502
17638
  const label = device.owner ? `${device.name} (${device.owner})` : device.name;
17503
- const state = device.state ? ` [${device.state}]` : "";
17639
+ const stateLine = formatDeviceState(device);
17640
+ const state = stateLine ? ` [${stateLine}]` : "";
17504
17641
  console.log(`${device.kind.padEnd(10)} ${label} — ${device.osVersion}${state}`);
17505
17642
  console.log(` ${device.id}`);
17506
17643
  if (device.unavailableReason) {
17507
17644
  console.log(` Not a target: ${device.unavailableReason}`);
17508
17645
  }
17646
+ const hint = isEverydayDeviceId(device.id) ? EVERYDAY_HINTS[device.state ?? ""] : undefined;
17647
+ if (hint)
17648
+ console.log(` ${hint}`);
17509
17649
  }
17510
17650
  }
17651
+ var platformDevices = {};
17511
17652
  for (const platform of Object.keys(PLATFORM_DEVICES_DESCRIPTION)) {
17512
- devices.command(platform).description(PLATFORM_DEVICES_DESCRIPTION[platform]).option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").option("--all", "Include unavailable / shutdown devices", true).option("--booted-only", "Only show booted / online devices").action(async (options) => {
17653
+ platformDevices[platform] = devices.command(platform).description(PLATFORM_DEVICES_DESCRIPTION[platform]).option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").option("--all", "Include unavailable / shutdown devices", true).option("--booted-only", "Only show booted / online devices").action(async (options) => {
17513
17654
  try {
17514
17655
  const includeUnavailable = options.bootedOnly ? false : options.all !== false;
17515
17656
  const body = await client(options.baseUrl).listDevices(platform, {
@@ -17525,7 +17666,45 @@ for (const platform of Object.keys(PLATFORM_DEVICES_DESCRIPTION)) {
17525
17666
  }
17526
17667
  });
17527
17668
  }
17528
- devices.command("connect").description("Open a Device Session on a device").argument("<deviceId>", "Device UDID / serial, a browser id (web-chrome), or chromium-cdp-<port> for a Chromium you started").option("--platform <platform>", "ios | android | web (web for web-* and chromium-cdp-<port>)").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--bundle-id <id>", "iOS bundle id to launch").option("--app-package <id>", "Android application id to launch").option("--lane <lane>", "direct | auto (the only Lane is Direct)").option("--viewport <preset>", "Web only: desktop (1280×800, the default) | mobile (390×844, touch and a mobile user agent)").option("--json", "Print raw JSON").action(async (deviceId, options) => {
17669
+ platformDevices.web.command("reset-profile").description("Delete a Kept browser profile, signing out of every site in it (refused while its browser is open)").option("--app <app>", "App prefix or id whose profile to reset (default: the default profile)").option("--browser <id>", "One browser's profile, e.g. web-chrome (default: every browser's)").configureHelp({ showGlobalOptions: true }).action(async (_options, command) => {
17670
+ const options = command.optsWithGlobals();
17671
+ try {
17672
+ const c = client(options.baseUrl);
17673
+ const appId = options.app ? findCatalogApp(await c.listApps(), options.app)?.id ?? options.app : undefined;
17674
+ const body = await c.resetWebProfile({ appId, browserId: options.browser });
17675
+ if (options.json) {
17676
+ console.log(JSON.stringify(body, null, 2));
17677
+ return;
17678
+ }
17679
+ if (body.browserIds.length === 0) {
17680
+ const browser = options.browser ? ` in ${options.browser}` : "";
17681
+ console.log(`No Kept profile for ${body.appId}${browser} to reset.`);
17682
+ return;
17683
+ }
17684
+ console.log(`reset the Kept profile for ${body.appId} in ${body.browserIds.join(", ")}`);
17685
+ } catch (error) {
17686
+ fail("devices web reset-profile", error);
17687
+ }
17688
+ });
17689
+ var ALLOW_LINE_DELAY_MS = 250;
17690
+ async function connectEveryday(c, request, browser) {
17691
+ const controller = new AbortController;
17692
+ const cancel = () => controller.abort();
17693
+ process.once("SIGINT", cancel);
17694
+ const allowLine = setTimeout(() => console.error(`Click Allow in ${browser}… (up to 60 s; Ctrl-C cancels)`), ALLOW_LINE_DELAY_MS);
17695
+ try {
17696
+ return await c.connectDevice(request, { signal: controller.signal });
17697
+ } catch (error) {
17698
+ if (controller.signal.aborted)
17699
+ return null;
17700
+ throw error;
17701
+ } finally {
17702
+ clearTimeout(allowLine);
17703
+ process.off("SIGINT", cancel);
17704
+ }
17705
+ }
17706
+ devices.command("connect").description("Open a Device Session on a device").argument("<deviceId>", "Device UDID / serial, a browser id (web-chrome), your Everyday browser (web-chrome-everyday), or chromium-cdp-<port> for a Chromium you started (CI)").option("--platform <platform>", "ios | android | web (web for web-* and chromium-cdp-<port>)").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--bundle-id <id>", "iOS bundle id to launch").option("--app-package <id>", "Android application id to launch").option("--lane <lane>", "direct | auto (the only Lane is Direct)").option("--viewport <preset>", "Web only: desktop (1280×800, the default) | mobile (390×844, touch and a mobile user agent)").option("--app <app>", "Web only: App prefix or id whose Kept profile to open, and whose Everyday browser warning applies (default: the default profile)").option("--yes", "Everyday browser only: accept its first-use warning for the App").option("--json", "Print raw JSON").action(async (deviceId, options) => {
17707
+ const everyday = everydayBrowserName(deviceId);
17529
17708
  try {
17530
17709
  const platform = connectPlatform(deviceId, options.platform);
17531
17710
  const lane = options.lane;
@@ -17533,14 +17712,27 @@ devices.command("connect").description("Open a Device Session on a device").argu
17533
17712
  throw new Error("--lane must be direct or auto");
17534
17713
  }
17535
17714
  const viewport = parseViewportPreset(options.viewport, platform);
17536
- const body = await client(options.baseUrl).connectDevice({
17715
+ if (options.app && platform !== "web") {
17716
+ throw new Error("--app is for web devices: a phone takes --bundle-id or --app-package");
17717
+ }
17718
+ const c = client(options.baseUrl);
17719
+ const appId = options.app ? (await resolveAppId(c, options.app)).id : undefined;
17720
+ const request = {
17537
17721
  deviceId,
17538
17722
  platform,
17539
17723
  bundleId: options.bundleId,
17540
17724
  appPackage: options.appPackage,
17541
17725
  lane,
17542
- ...viewport ? { viewport } : {}
17543
- });
17726
+ ...viewport ? { viewport } : {},
17727
+ ...appId ? { appId } : {},
17728
+ ...everyday && options.yes ? { acceptEverydayWarning: true } : {}
17729
+ };
17730
+ const body = everyday ? await connectEveryday(c, request, everyday) : await c.connectDevice(request);
17731
+ if (!body) {
17732
+ console.error(`yoqa devices connect cancelled before Allow in ${everyday}`);
17733
+ process.exitCode = 130;
17734
+ return;
17735
+ }
17544
17736
  if (options.json) {
17545
17737
  console.log(JSON.stringify(body, null, 2));
17546
17738
  return;
@@ -17549,6 +17741,16 @@ devices.command("connect").description("Open a Device Session on a device").argu
17549
17741
  if (body.laneWarning)
17550
17742
  console.log(`lane warning: ${body.laneWarning}`);
17551
17743
  } catch (error) {
17744
+ const warning = everydayWarningRequired(error);
17745
+ if (warning) {
17746
+ const app = options.app ? ` --app ${options.app}` : "";
17747
+ console.error(`${warning.warning}
17748
+
17749
+ To accept this for ${warning.appId} and connect, run:
17750
+ yoqa devices connect ${deviceId}${app} --yes`);
17751
+ process.exitCode = 1;
17752
+ return;
17753
+ }
17552
17754
  const untrusted = developerNotTrusted(error);
17553
17755
  if (untrusted && !options.json) {
17554
17756
  console.error(formatDeveloperNotTrusted(untrusted, `yoqa devices connect ${deviceId} --platform ${options.platform}`));
@@ -17589,6 +17791,12 @@ devices.command("disconnect").description("Close the active Device Session").opt
17589
17791
  fail("devices disconnect", error);
17590
17792
  }
17591
17793
  });
17794
+ function screenSourceLine(source) {
17795
+ if (source.kind === "system")
17796
+ return "Read from the system (SpringBoard), not an app";
17797
+ const app = source.bundleId ?? "an unnamed app";
17798
+ return `Read from ${app} (${source.kind === "target" ? "target app" : "foreground app"})`;
17799
+ }
17592
17800
  program2.command("screen").description("Inspect the active device screen (cleaned tree by default)").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--full", "Return the raw accessibility tree").option("--json", "Print raw JSON").action(async (options) => {
17593
17801
  try {
17594
17802
  const body = await client(options.baseUrl).getScreen({ full: options.full });
@@ -17596,6 +17804,8 @@ program2.command("screen").description("Inspect the active device screen (cleane
17596
17804
  console.log(JSON.stringify(body, null, 2));
17597
17805
  return;
17598
17806
  }
17807
+ if (body.source)
17808
+ console.log(screenSourceLine(body.source));
17599
17809
  for (const el of body.elements ?? []) {
17600
17810
  console.log(`${String(el.x).padStart(4)},${String(el.y).padStart(4)} ${el.width}x${el.height} ${el.label}`);
17601
17811
  }
@@ -18365,7 +18575,7 @@ runsCmd.command("delete").argument("<runId>", "Run id").option("--base-url <url>
18365
18575
  fail("runs delete", error);
18366
18576
  }
18367
18577
  });
18368
- runsCmd.command("create").argument("<app>", "App prefix or id").requiredOption("--cases <numbers>", "Comma-separated case numbers, e.g. 1,2,5").option("--device <id>", "Device id (defaults to active session)").option("--platform <platform>", "ios | android | web (web for web-* and chromium-cdp-<port>; defaults to active session)").option("--build-id <id>", "Registered build id to install before run").option("--build-path <path>", "Absolute build path to register + install").option("--mode <mode>", "auto | script | agent (default: auto)").option("--screen-mode <mode>", "vision | tree — what the agent sees: screenshot only, or also the accessibility tree (default: vision)").option("--lane <lane>", "direct | auto").option("--record-video", "Record a screen video of every case in the run, even cases not set to record").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").option("--wait", "Wait until the run finishes (passed / errored / cancelled)").option("--timeout <seconds>", "Wait timeout in seconds (default: 1800)", String(DEFAULT_RUN_WAIT_TIMEOUT_SECONDS)).option("--github-output", "Write run_id and status to $GITHUB_OUTPUT").option("--no-github-output", "Do not write GitHub step outputs").action(async (appArg, options) => {
18578
+ runsCmd.command("create").argument("<app>", "App prefix or id").requiredOption("--cases <numbers>", "Comma-separated case numbers, e.g. 1,2,5").option("--device <id>", "Device id (defaults to active session)").option("--platform <platform>", "ios | android | web (web for web-* and chromium-cdp-<port>; defaults to active session)").option("--build-id <id>", "Registered build id to install before run").option("--build-path <path>", "Absolute build path to register + install").option("--mode <mode>", "auto | script | agent (default: auto)").option("--screen-mode <mode>", "vision | tree — what the agent sees: screenshot only, or also the accessibility tree (default: vision)").option("--lane <lane>", "direct | auto").option("--record-video", "Record a screen video of every case in the run, even cases not set to record").option("--profile <profile>", "web: kept | clean — the App's Kept browser profile or a throwaway one, whatever the cases ask (default: clean if a case asks, else kept)").option("--viewport <preset>", "web: desktop | mobile — reopens a session at another preset (default: the session's, else desktop)").option("--base-url <url>", "Runner base URL", runnerBaseUrl()).option("--json", "Print raw JSON").option("--wait", "Wait until the run finishes (passed / errored / cancelled)").option("--timeout <seconds>", "Wait timeout in seconds (default: 1800)", String(DEFAULT_RUN_WAIT_TIMEOUT_SECONDS)).option("--github-output", "Write run_id and status to $GITHUB_OUTPUT").option("--no-github-output", "Do not write GitHub step outputs").action(async (appArg, options) => {
18369
18579
  try {
18370
18580
  const c = client(options.baseUrl);
18371
18581
  const resolved = await resolveAppId(c, appArg);
@@ -18387,6 +18597,9 @@ runsCmd.command("create").argument("<app>", "App prefix or id").requiredOption("
18387
18597
  if (!active) {
18388
18598
  throw new Error("No --device/--platform and no active session. Connect first or pass both flags.");
18389
18599
  }
18600
+ if (!deviceId && isEverydayDeviceId(active.deviceId)) {
18601
+ throw new Error(`The session is on your Everyday browser, which a Run uses only when named. Pass --device ${active.deviceId} to run there.`);
18602
+ }
18390
18603
  deviceId = deviceId ?? active.deviceId;
18391
18604
  platformOption = platformOption ?? active.platform;
18392
18605
  }
@@ -18403,6 +18616,11 @@ runsCmd.command("create").argument("<app>", "App prefix or id").requiredOption("
18403
18616
  if (options.lane && !lane) {
18404
18617
  throw new Error("--lane must be direct or auto");
18405
18618
  }
18619
+ const profile = options.profile === "kept" || options.profile === "clean" ? options.profile : undefined;
18620
+ if (options.profile && !profile) {
18621
+ throw new Error("--profile must be kept or clean");
18622
+ }
18623
+ const viewport = parseViewportPreset(options.viewport, platform);
18406
18624
  let run = await c.createRun({
18407
18625
  appId: resolved.id,
18408
18626
  caseIds,
@@ -18413,7 +18631,9 @@ runsCmd.command("create").argument("<app>", "App prefix or id").requiredOption("
18413
18631
  executionMode,
18414
18632
  screenMode,
18415
18633
  lane,
18416
- recordVideo: options.recordVideo || undefined
18634
+ recordVideo: options.recordVideo || undefined,
18635
+ profile,
18636
+ viewport
18417
18637
  });
18418
18638
  if (options.wait) {
18419
18639
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoqa/cli",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Yoqa CLI — drive local mobile QA from the terminal or CI (talks to the Yoqa runner over HTTP)",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -37,7 +37,7 @@
37
37
  "health": "bun run src/main.ts health"
38
38
  },
39
39
  "dependencies": {
40
- "@yoqa/runner": "1.0.5"
40
+ "@yoqa/runner": "1.0.7"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^22.15.30",