@yoqa/cli 1.0.4 → 1.0.6
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/dist/main.js +270 -49
- 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
|
|
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,27 @@ 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
|
+
function formatDeviceState(device) {
|
|
15702
|
+
const parts = [device.state, device.verified === false ? "unverified" : null].filter(Boolean);
|
|
15703
|
+
return parts.length > 0 ? parts.join(" · ") : null;
|
|
15704
|
+
}
|
|
15626
15705
|
// ../runner-client/src/index.ts
|
|
15627
15706
|
var DEFAULT_BASE_URL = "http://127.0.0.1:7420";
|
|
15628
15707
|
function errorMessageFromBody(json, fallback) {
|
|
@@ -15657,6 +15736,36 @@ class RunnerRequestError extends Error {
|
|
|
15657
15736
|
this.name = "RunnerRequestError";
|
|
15658
15737
|
}
|
|
15659
15738
|
}
|
|
15739
|
+
|
|
15740
|
+
class RunnerUnreachableError extends Error {
|
|
15741
|
+
url;
|
|
15742
|
+
constructor(url, options) {
|
|
15743
|
+
const reason = options.cause instanceof Error ? options.cause.message : String(options.cause);
|
|
15744
|
+
super(reason, options);
|
|
15745
|
+
this.url = url;
|
|
15746
|
+
this.name = "RunnerUnreachableError";
|
|
15747
|
+
}
|
|
15748
|
+
}
|
|
15749
|
+
function answeredFetch(fetchImpl) {
|
|
15750
|
+
const wrapped = async (input, init) => {
|
|
15751
|
+
try {
|
|
15752
|
+
return await fetchImpl(input, init);
|
|
15753
|
+
} catch (error) {
|
|
15754
|
+
if (error instanceof Error && error.name === "AbortError")
|
|
15755
|
+
throw error;
|
|
15756
|
+
throw new RunnerUnreachableError(String(input instanceof Request ? input.url : input), {
|
|
15757
|
+
cause: error
|
|
15758
|
+
});
|
|
15759
|
+
}
|
|
15760
|
+
};
|
|
15761
|
+
return Object.assign(wrapped, fetchImpl);
|
|
15762
|
+
}
|
|
15763
|
+
function everydayWarningRequired(error) {
|
|
15764
|
+
if (!(error instanceof RunnerRequestError) || error.status !== 428)
|
|
15765
|
+
return null;
|
|
15766
|
+
const parsed = everydayWarningRequiredSchema.safeParse(error.body);
|
|
15767
|
+
return parsed.success ? parsed.data : null;
|
|
15768
|
+
}
|
|
15660
15769
|
function developerNotTrusted(error) {
|
|
15661
15770
|
if (!(error instanceof RunnerRequestError) || error.status !== 428)
|
|
15662
15771
|
return null;
|
|
@@ -15679,7 +15788,7 @@ class RunnerClient {
|
|
|
15679
15788
|
fetchImpl;
|
|
15680
15789
|
constructor(options = {}) {
|
|
15681
15790
|
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
15682
|
-
this.fetchImpl = options.fetchImpl ?? fetch.bind(globalThis);
|
|
15791
|
+
this.fetchImpl = answeredFetch(options.fetchImpl ?? fetch.bind(globalThis));
|
|
15683
15792
|
}
|
|
15684
15793
|
async requestJson(path, init, fallbackError) {
|
|
15685
15794
|
const response = await this.fetchImpl(`${this.baseUrl}${path}`, uncached(init));
|
|
@@ -16013,15 +16122,27 @@ class RunnerClient {
|
|
|
16013
16122
|
async deleteRunTestVideo(runId, runTestId) {
|
|
16014
16123
|
await this.requestJson(`/runs/${encodeURIComponent(runId)}/tests/${encodeURIComponent(runTestId)}/video`, { method: "DELETE" }, "Delete video failed");
|
|
16015
16124
|
}
|
|
16016
|
-
async connectDevice(request) {
|
|
16125
|
+
async connectDevice(request, options = {}) {
|
|
16017
16126
|
const body = connectDeviceRequestSchema.parse(request);
|
|
16018
16127
|
const json = await this.requestJson("/devices/connect", {
|
|
16019
16128
|
method: "POST",
|
|
16020
16129
|
headers: { "Content-Type": "application/json" },
|
|
16021
|
-
body: JSON.stringify(body)
|
|
16130
|
+
body: JSON.stringify(body),
|
|
16131
|
+
signal: options.signal
|
|
16022
16132
|
}, "Connect device failed");
|
|
16023
16133
|
return activeDeviceResponseSchema.parse(json);
|
|
16024
16134
|
}
|
|
16135
|
+
async resetWebProfile(request = {}) {
|
|
16136
|
+
const { appId, browserId } = resetWebProfileRequestSchema.parse(request);
|
|
16137
|
+
const params = new URLSearchParams;
|
|
16138
|
+
if (appId)
|
|
16139
|
+
params.set("appId", appId);
|
|
16140
|
+
if (browserId)
|
|
16141
|
+
params.set("browserId", browserId);
|
|
16142
|
+
const query = params.toString();
|
|
16143
|
+
const json = await this.requestJson(`/devices/web/profiles${query ? `?${query}` : ""}`, { method: "DELETE" }, "Reset browser profile failed");
|
|
16144
|
+
return resetWebProfileResponseSchema.parse(json);
|
|
16145
|
+
}
|
|
16025
16146
|
async getActiveDevice() {
|
|
16026
16147
|
const response = await this.fetchImpl(`${this.baseUrl}/devices/active?_=${Date.now()}`, uncached());
|
|
16027
16148
|
if (response.status === 404)
|
|
@@ -16512,7 +16633,7 @@ function recommendScreenDefault(vision, tree) {
|
|
|
16512
16633
|
// package.json
|
|
16513
16634
|
var package_default = {
|
|
16514
16635
|
name: "@yoqa/cli",
|
|
16515
|
-
version: "1.0.
|
|
16636
|
+
version: "1.0.6",
|
|
16516
16637
|
description: "Yoqa CLI — drive local mobile QA from the terminal or CI (talks to the Yoqa runner over HTTP)",
|
|
16517
16638
|
license: "UNLICENSED",
|
|
16518
16639
|
type: "module",
|
|
@@ -16549,7 +16670,7 @@ var package_default = {
|
|
|
16549
16670
|
health: "bun run src/main.ts health"
|
|
16550
16671
|
},
|
|
16551
16672
|
dependencies: {
|
|
16552
|
-
"@yoqa/runner": "1.0.
|
|
16673
|
+
"@yoqa/runner": "1.0.6"
|
|
16553
16674
|
},
|
|
16554
16675
|
devDependencies: {
|
|
16555
16676
|
"@types/node": "^22.15.30",
|
|
@@ -16653,7 +16774,7 @@ import { existsSync } from "node:fs";
|
|
|
16653
16774
|
import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
16654
16775
|
import { createRequire as createRequire2 } from "node:module";
|
|
16655
16776
|
import { homedir as osHomedir } from "node:os";
|
|
16656
|
-
import { dirname, join } from "node:path";
|
|
16777
|
+
import { dirname, join, posix, win32 } from "node:path";
|
|
16657
16778
|
import { fileURLToPath } from "node:url";
|
|
16658
16779
|
var DEFAULT_RUNNER_HOST = "127.0.0.1";
|
|
16659
16780
|
var DEFAULT_RUNNER_PORT = 7420;
|
|
@@ -16698,11 +16819,12 @@ function execRootsFrom(execPath, argv0) {
|
|
|
16698
16819
|
}
|
|
16699
16820
|
return roots;
|
|
16700
16821
|
}
|
|
16701
|
-
function packagedYoqaRunnerCandidates(roots) {
|
|
16702
|
-
const
|
|
16822
|
+
function packagedYoqaRunnerCandidates(roots, platform = process.platform) {
|
|
16823
|
+
const { join: join2 } = pathModule(platform);
|
|
16824
|
+
const fileName = platform === "win32" ? "yoqa-runner.exe" : "yoqa-runner";
|
|
16703
16825
|
const paths = [];
|
|
16704
16826
|
for (const root of roots) {
|
|
16705
|
-
paths.push(
|
|
16827
|
+
paths.push(join2(root, fileName), join2(root, "../Resources/app.asar.unpacked/runner", fileName), join2(root, "../Resources/runner", fileName), join2(root, "Resources/app.asar.unpacked/runner", fileName), join2(root, "Resources/runner", fileName));
|
|
16706
16828
|
}
|
|
16707
16829
|
return paths;
|
|
16708
16830
|
}
|
|
@@ -16804,44 +16926,49 @@ async function waitForRunnerHealth(fetchHealth, options) {
|
|
|
16804
16926
|
}
|
|
16805
16927
|
return fetchHealth();
|
|
16806
16928
|
}
|
|
16807
|
-
function
|
|
16929
|
+
function pathModule(platform) {
|
|
16930
|
+
return platform === "win32" ? win32 : posix;
|
|
16931
|
+
}
|
|
16932
|
+
function pathWithHostTools(env, homedir, platform = process.platform) {
|
|
16933
|
+
const { delimiter, join: join2 } = pathModule(platform);
|
|
16808
16934
|
const extras = [
|
|
16809
|
-
"/opt/homebrew/bin",
|
|
16810
|
-
"
|
|
16811
|
-
"
|
|
16812
|
-
join(homedir, ".local", "bin"),
|
|
16813
|
-
join(homedir, ".bun", "bin")
|
|
16935
|
+
...platform === "win32" ? [] : ["/opt/homebrew/bin", "/opt/homebrew/sbin", "/usr/local/bin"],
|
|
16936
|
+
join2(homedir, ".local", "bin"),
|
|
16937
|
+
join2(homedir, ".bun", "bin")
|
|
16814
16938
|
];
|
|
16815
|
-
const existing = (env.PATH ?? "").split(
|
|
16939
|
+
const existing = (env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
16816
16940
|
const prepend = extras.filter((dir) => !existing.includes(dir));
|
|
16817
|
-
return [...prepend, ...existing].join(
|
|
16941
|
+
return [...prepend, ...existing].join(delimiter);
|
|
16942
|
+
}
|
|
16943
|
+
function bunCandidates(homedir, env, platform = process.platform) {
|
|
16944
|
+
const { join: join2 } = pathModule(platform);
|
|
16945
|
+
const bin = platform === "win32" ? "bun.exe" : "bun";
|
|
16946
|
+
const fromEnv = env.BUN_INSTALL ? join2(env.BUN_INSTALL, "bin", bin) : null;
|
|
16947
|
+
return [
|
|
16948
|
+
fromEnv,
|
|
16949
|
+
join2(homedir, ".bun", "bin", bin),
|
|
16950
|
+
...platform === "win32" ? [] : ["/opt/homebrew/bin/bun", "/usr/local/bin/bun"]
|
|
16951
|
+
].filter((path) => Boolean(path));
|
|
16818
16952
|
}
|
|
16819
16953
|
async function defaultPathExists(path) {
|
|
16820
16954
|
return existsSync(path);
|
|
16821
16955
|
}
|
|
16822
16956
|
async function defaultWhich(bin) {
|
|
16823
16957
|
const { execFile } = await import("node:child_process");
|
|
16958
|
+
const locator = process.platform === "win32" ? "where" : "which";
|
|
16824
16959
|
return new Promise((resolve) => {
|
|
16825
|
-
execFile(
|
|
16960
|
+
execFile(locator, [bin], (error, stdout) => {
|
|
16826
16961
|
if (error) {
|
|
16827
16962
|
resolve(null);
|
|
16828
16963
|
return;
|
|
16829
16964
|
}
|
|
16830
|
-
const path = stdout.trim().split(
|
|
16831
|
-
`)[0]?.trim();
|
|
16965
|
+
const path = stdout.trim().split(/\r?\n/)[0]?.trim();
|
|
16832
16966
|
resolve(path || null);
|
|
16833
16967
|
});
|
|
16834
16968
|
});
|
|
16835
16969
|
}
|
|
16836
16970
|
async function defaultFindBun(pathExists, which, homedir, env) {
|
|
16837
|
-
const
|
|
16838
|
-
const candidates = [
|
|
16839
|
-
fromEnv,
|
|
16840
|
-
join(homedir, ".bun", "bin", "bun"),
|
|
16841
|
-
"/opt/homebrew/bin/bun",
|
|
16842
|
-
"/usr/local/bin/bun"
|
|
16843
|
-
].filter((path) => Boolean(path));
|
|
16844
|
-
for (const candidate of candidates) {
|
|
16971
|
+
for (const candidate of bunCandidates(homedir, env)) {
|
|
16845
16972
|
if (await pathExists(candidate))
|
|
16846
16973
|
return candidate;
|
|
16847
16974
|
}
|
|
@@ -17043,7 +17170,7 @@ function commandChainIncludes(command, name) {
|
|
|
17043
17170
|
var PLATFORM_DEVICES_DESCRIPTION = {
|
|
17044
17171
|
ios: "List iOS devices and simulators",
|
|
17045
17172
|
android: "List Android devices and emulators",
|
|
17046
|
-
web: "List installed web browsers (attach a Chromium you started by naming its port
|
|
17173
|
+
web: "List installed web browsers and your Everyday browsers (CI: attach a Chromium you started by naming its port, chromium-cdp-<port>)"
|
|
17047
17174
|
};
|
|
17048
17175
|
function parsePlatform(value) {
|
|
17049
17176
|
const parsed = devicePlatformSchema.safeParse(value);
|
|
@@ -17053,9 +17180,18 @@ function parsePlatform(value) {
|
|
|
17053
17180
|
}
|
|
17054
17181
|
return parsed.data;
|
|
17055
17182
|
}
|
|
17056
|
-
var
|
|
17183
|
+
var INSTALLED_BROWSER_IDS = new Set(WEB_BROWSERS.map(({ browser }) => installedBrowserId(browser)));
|
|
17184
|
+
var ATTACH_DEVICE_ID = /^chromium-cdp-\d+$/;
|
|
17185
|
+
var EVERYDAY_BROWSER_NAMES = new Map(WEB_BROWSERS.map(({ browser, name }) => [everydayBrowserId(browser), name]));
|
|
17186
|
+
function everydayBrowserName(deviceId) {
|
|
17187
|
+
return EVERYDAY_BROWSER_NAMES.get(deviceId) ?? null;
|
|
17188
|
+
}
|
|
17189
|
+
function isEverydayDeviceId(deviceId) {
|
|
17190
|
+
return everydayBrowserName(deviceId) !== null;
|
|
17191
|
+
}
|
|
17192
|
+
var UNSUPPORTED_BROWSER_ID = /^web-(firefox|safari)(-everyday)?$/;
|
|
17057
17193
|
function isWebDeviceId(deviceId) {
|
|
17058
|
-
return
|
|
17194
|
+
return INSTALLED_BROWSER_IDS.has(deviceId) || ATTACH_DEVICE_ID.test(deviceId) || isEverydayDeviceId(deviceId) || UNSUPPORTED_BROWSER_ID.test(deviceId);
|
|
17059
17195
|
}
|
|
17060
17196
|
function connectPlatform(deviceId, value) {
|
|
17061
17197
|
if (value === undefined && isWebDeviceId(deviceId))
|
|
@@ -17364,12 +17500,13 @@ function client(baseUrl) {
|
|
|
17364
17500
|
}
|
|
17365
17501
|
function describeSession(body) {
|
|
17366
17502
|
const viewport = body.viewport ? ` viewport ${formatViewportPreset(body.viewport)}` : "";
|
|
17367
|
-
|
|
17503
|
+
const profile = body.browserProfileName ? ` profile ${body.browserProfileName}` : "";
|
|
17504
|
+
return `${body.platform} ${body.deviceId} lane ${body.lane ?? "direct"}${viewport}${profile}`;
|
|
17368
17505
|
}
|
|
17369
17506
|
function fail(command, error) {
|
|
17370
17507
|
const message = error instanceof Error ? error.message : String(error);
|
|
17371
17508
|
console.error(`yoqa ${command} failed: ${message}`);
|
|
17372
|
-
if (
|
|
17509
|
+
if (error instanceof RunnerUnreachableError) {
|
|
17373
17510
|
console.error("Is the runner up? Try: yoqa serve");
|
|
17374
17511
|
}
|
|
17375
17512
|
process.exitCode = 1;
|
|
@@ -17494,16 +17631,21 @@ function printDevicesTable(devicesList) {
|
|
|
17494
17631
|
}
|
|
17495
17632
|
for (const device of devicesList) {
|
|
17496
17633
|
const label = device.owner ? `${device.name} (${device.owner})` : device.name;
|
|
17497
|
-
const
|
|
17634
|
+
const stateLine = formatDeviceState(device);
|
|
17635
|
+
const state = stateLine ? ` [${stateLine}]` : "";
|
|
17498
17636
|
console.log(`${device.kind.padEnd(10)} ${label} — ${device.osVersion}${state}`);
|
|
17499
17637
|
console.log(` ${device.id}`);
|
|
17500
17638
|
if (device.unavailableReason) {
|
|
17501
17639
|
console.log(` Not a target: ${device.unavailableReason}`);
|
|
17502
17640
|
}
|
|
17641
|
+
if (isEverydayDeviceId(device.id) && device.state === "remote debugging off") {
|
|
17642
|
+
console.log(" Turn it on at chrome://inspect/#remote-debugging, then connect");
|
|
17643
|
+
}
|
|
17503
17644
|
}
|
|
17504
17645
|
}
|
|
17646
|
+
var platformDevices = {};
|
|
17505
17647
|
for (const platform of Object.keys(PLATFORM_DEVICES_DESCRIPTION)) {
|
|
17506
|
-
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) => {
|
|
17648
|
+
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) => {
|
|
17507
17649
|
try {
|
|
17508
17650
|
const includeUnavailable = options.bootedOnly ? false : options.all !== false;
|
|
17509
17651
|
const body = await client(options.baseUrl).listDevices(platform, {
|
|
@@ -17519,7 +17661,45 @@ for (const platform of Object.keys(PLATFORM_DEVICES_DESCRIPTION)) {
|
|
|
17519
17661
|
}
|
|
17520
17662
|
});
|
|
17521
17663
|
}
|
|
17522
|
-
|
|
17664
|
+
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) => {
|
|
17665
|
+
const options = command.optsWithGlobals();
|
|
17666
|
+
try {
|
|
17667
|
+
const c = client(options.baseUrl);
|
|
17668
|
+
const appId = options.app ? findCatalogApp(await c.listApps(), options.app)?.id ?? options.app : undefined;
|
|
17669
|
+
const body = await c.resetWebProfile({ appId, browserId: options.browser });
|
|
17670
|
+
if (options.json) {
|
|
17671
|
+
console.log(JSON.stringify(body, null, 2));
|
|
17672
|
+
return;
|
|
17673
|
+
}
|
|
17674
|
+
if (body.browserIds.length === 0) {
|
|
17675
|
+
const browser = options.browser ? ` in ${options.browser}` : "";
|
|
17676
|
+
console.log(`No Kept profile for ${body.appId}${browser} to reset.`);
|
|
17677
|
+
return;
|
|
17678
|
+
}
|
|
17679
|
+
console.log(`reset the Kept profile for ${body.appId} in ${body.browserIds.join(", ")}`);
|
|
17680
|
+
} catch (error) {
|
|
17681
|
+
fail("devices web reset-profile", error);
|
|
17682
|
+
}
|
|
17683
|
+
});
|
|
17684
|
+
var ALLOW_LINE_DELAY_MS = 250;
|
|
17685
|
+
async function connectEveryday(c, request, browser) {
|
|
17686
|
+
const controller = new AbortController;
|
|
17687
|
+
const cancel = () => controller.abort();
|
|
17688
|
+
process.once("SIGINT", cancel);
|
|
17689
|
+
const allowLine = setTimeout(() => console.error(`Click Allow in ${browser}… (up to 60 s; Ctrl-C cancels)`), ALLOW_LINE_DELAY_MS);
|
|
17690
|
+
try {
|
|
17691
|
+
return await c.connectDevice(request, { signal: controller.signal });
|
|
17692
|
+
} catch (error) {
|
|
17693
|
+
if (controller.signal.aborted)
|
|
17694
|
+
return null;
|
|
17695
|
+
throw error;
|
|
17696
|
+
} finally {
|
|
17697
|
+
clearTimeout(allowLine);
|
|
17698
|
+
process.off("SIGINT", cancel);
|
|
17699
|
+
}
|
|
17700
|
+
}
|
|
17701
|
+
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) => {
|
|
17702
|
+
const everyday = everydayBrowserName(deviceId);
|
|
17523
17703
|
try {
|
|
17524
17704
|
const platform = connectPlatform(deviceId, options.platform);
|
|
17525
17705
|
const lane = options.lane;
|
|
@@ -17527,14 +17707,27 @@ devices.command("connect").description("Open a Device Session on a device").argu
|
|
|
17527
17707
|
throw new Error("--lane must be direct or auto");
|
|
17528
17708
|
}
|
|
17529
17709
|
const viewport = parseViewportPreset(options.viewport, platform);
|
|
17530
|
-
|
|
17710
|
+
if (options.app && platform !== "web") {
|
|
17711
|
+
throw new Error("--app is for web devices: a phone takes --bundle-id or --app-package");
|
|
17712
|
+
}
|
|
17713
|
+
const c = client(options.baseUrl);
|
|
17714
|
+
const appId = options.app ? (await resolveAppId(c, options.app)).id : undefined;
|
|
17715
|
+
const request = {
|
|
17531
17716
|
deviceId,
|
|
17532
17717
|
platform,
|
|
17533
17718
|
bundleId: options.bundleId,
|
|
17534
17719
|
appPackage: options.appPackage,
|
|
17535
17720
|
lane,
|
|
17536
|
-
...viewport ? { viewport } : {}
|
|
17537
|
-
|
|
17721
|
+
...viewport ? { viewport } : {},
|
|
17722
|
+
...appId ? { appId } : {},
|
|
17723
|
+
...everyday && options.yes ? { acceptEverydayWarning: true } : {}
|
|
17724
|
+
};
|
|
17725
|
+
const body = everyday ? await connectEveryday(c, request, everyday) : await c.connectDevice(request);
|
|
17726
|
+
if (!body) {
|
|
17727
|
+
console.error(`yoqa devices connect cancelled before Allow in ${everyday}`);
|
|
17728
|
+
process.exitCode = 130;
|
|
17729
|
+
return;
|
|
17730
|
+
}
|
|
17538
17731
|
if (options.json) {
|
|
17539
17732
|
console.log(JSON.stringify(body, null, 2));
|
|
17540
17733
|
return;
|
|
@@ -17543,6 +17736,16 @@ devices.command("connect").description("Open a Device Session on a device").argu
|
|
|
17543
17736
|
if (body.laneWarning)
|
|
17544
17737
|
console.log(`lane warning: ${body.laneWarning}`);
|
|
17545
17738
|
} catch (error) {
|
|
17739
|
+
const warning = everydayWarningRequired(error);
|
|
17740
|
+
if (warning) {
|
|
17741
|
+
const app = options.app ? ` --app ${options.app}` : "";
|
|
17742
|
+
console.error(`${warning.warning}
|
|
17743
|
+
|
|
17744
|
+
To accept this for ${warning.appId} and connect, run:
|
|
17745
|
+
yoqa devices connect ${deviceId}${app} --yes`);
|
|
17746
|
+
process.exitCode = 1;
|
|
17747
|
+
return;
|
|
17748
|
+
}
|
|
17546
17749
|
const untrusted = developerNotTrusted(error);
|
|
17547
17750
|
if (untrusted && !options.json) {
|
|
17548
17751
|
console.error(formatDeveloperNotTrusted(untrusted, `yoqa devices connect ${deviceId} --platform ${options.platform}`));
|
|
@@ -17583,6 +17786,12 @@ devices.command("disconnect").description("Close the active Device Session").opt
|
|
|
17583
17786
|
fail("devices disconnect", error);
|
|
17584
17787
|
}
|
|
17585
17788
|
});
|
|
17789
|
+
function screenSourceLine(source) {
|
|
17790
|
+
if (source.kind === "system")
|
|
17791
|
+
return "Read from the system (SpringBoard), not an app";
|
|
17792
|
+
const app = source.bundleId ?? "an unnamed app";
|
|
17793
|
+
return `Read from ${app} (${source.kind === "target" ? "target app" : "foreground app"})`;
|
|
17794
|
+
}
|
|
17586
17795
|
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) => {
|
|
17587
17796
|
try {
|
|
17588
17797
|
const body = await client(options.baseUrl).getScreen({ full: options.full });
|
|
@@ -17590,6 +17799,8 @@ program2.command("screen").description("Inspect the active device screen (cleane
|
|
|
17590
17799
|
console.log(JSON.stringify(body, null, 2));
|
|
17591
17800
|
return;
|
|
17592
17801
|
}
|
|
17802
|
+
if (body.source)
|
|
17803
|
+
console.log(screenSourceLine(body.source));
|
|
17593
17804
|
for (const el of body.elements ?? []) {
|
|
17594
17805
|
console.log(`${String(el.x).padStart(4)},${String(el.y).padStart(4)} ${el.width}x${el.height} ${el.label}`);
|
|
17595
17806
|
}
|
|
@@ -18359,7 +18570,7 @@ runsCmd.command("delete").argument("<runId>", "Run id").option("--base-url <url>
|
|
|
18359
18570
|
fail("runs delete", error);
|
|
18360
18571
|
}
|
|
18361
18572
|
});
|
|
18362
|
-
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) => {
|
|
18573
|
+
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) => {
|
|
18363
18574
|
try {
|
|
18364
18575
|
const c = client(options.baseUrl);
|
|
18365
18576
|
const resolved = await resolveAppId(c, appArg);
|
|
@@ -18381,6 +18592,9 @@ runsCmd.command("create").argument("<app>", "App prefix or id").requiredOption("
|
|
|
18381
18592
|
if (!active) {
|
|
18382
18593
|
throw new Error("No --device/--platform and no active session. Connect first or pass both flags.");
|
|
18383
18594
|
}
|
|
18595
|
+
if (!deviceId && isEverydayDeviceId(active.deviceId)) {
|
|
18596
|
+
throw new Error(`The session is on your Everyday browser, which a Run uses only when named. Pass --device ${active.deviceId} to run there.`);
|
|
18597
|
+
}
|
|
18384
18598
|
deviceId = deviceId ?? active.deviceId;
|
|
18385
18599
|
platformOption = platformOption ?? active.platform;
|
|
18386
18600
|
}
|
|
@@ -18397,6 +18611,11 @@ runsCmd.command("create").argument("<app>", "App prefix or id").requiredOption("
|
|
|
18397
18611
|
if (options.lane && !lane) {
|
|
18398
18612
|
throw new Error("--lane must be direct or auto");
|
|
18399
18613
|
}
|
|
18614
|
+
const profile = options.profile === "kept" || options.profile === "clean" ? options.profile : undefined;
|
|
18615
|
+
if (options.profile && !profile) {
|
|
18616
|
+
throw new Error("--profile must be kept or clean");
|
|
18617
|
+
}
|
|
18618
|
+
const viewport = parseViewportPreset(options.viewport, platform);
|
|
18400
18619
|
let run = await c.createRun({
|
|
18401
18620
|
appId: resolved.id,
|
|
18402
18621
|
caseIds,
|
|
@@ -18407,7 +18626,9 @@ runsCmd.command("create").argument("<app>", "App prefix or id").requiredOption("
|
|
|
18407
18626
|
executionMode,
|
|
18408
18627
|
screenMode,
|
|
18409
18628
|
lane,
|
|
18410
|
-
recordVideo: options.recordVideo || undefined
|
|
18629
|
+
recordVideo: options.recordVideo || undefined,
|
|
18630
|
+
profile,
|
|
18631
|
+
viewport
|
|
18411
18632
|
});
|
|
18412
18633
|
if (options.wait) {
|
|
18413
18634
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yoqa/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
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.
|
|
40
|
+
"@yoqa/runner": "1.0.6"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^22.15.30",
|