@yoqa/runner 1.0.6 → 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.
- package/dist/index.js +141 -37
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -27918,6 +27918,8 @@ function installedBrowserId(browser) {
|
|
|
27918
27918
|
function everydayBrowserId(browser) {
|
|
27919
27919
|
return `web-${browser}-everyday`;
|
|
27920
27920
|
}
|
|
27921
|
+
var REMOTE_DEBUGGING_SETTING_URL = "chrome://inspect/#remote-debugging";
|
|
27922
|
+
var EVERYDAY_ALLOW_TIMEOUT_MS = 60000;
|
|
27921
27923
|
function webBrowserOf(deviceId) {
|
|
27922
27924
|
return WEB_BROWSERS.find(({ browser }) => deviceId === installedBrowserId(browser) || deviceId === everydayBrowserId(browser));
|
|
27923
27925
|
}
|
|
@@ -28149,7 +28151,9 @@ async function launchChromium(browser, deps = {}, request = {}) {
|
|
|
28149
28151
|
}
|
|
28150
28152
|
|
|
28151
28153
|
// src/domains/devices/chromium-everyday.ts
|
|
28154
|
+
import { execFile } from "child_process";
|
|
28152
28155
|
import { join as join3 } from "path";
|
|
28156
|
+
import { promisify } from "util";
|
|
28153
28157
|
|
|
28154
28158
|
// src/domains/devices/cdp-client.ts
|
|
28155
28159
|
class CdpClosedError extends Error {
|
|
@@ -28339,9 +28343,15 @@ async function findEverydayBrowsers(deps = discoveryDeps()) {
|
|
|
28339
28343
|
const roots = ["/Applications", join3(deps.home, "Applications")];
|
|
28340
28344
|
const found = await Promise.all(EVERYDAY_BROWSERS.map(async ({ id, name, app, userData }) => {
|
|
28341
28345
|
for (const root of roots) {
|
|
28342
|
-
|
|
28346
|
+
const executable = join3(root, `${app}.app`, "Contents/MacOS", app);
|
|
28347
|
+
if (!await deps.isExecutable(executable))
|
|
28343
28348
|
continue;
|
|
28344
|
-
return {
|
|
28349
|
+
return {
|
|
28350
|
+
id,
|
|
28351
|
+
name,
|
|
28352
|
+
userDataDir: join3(deps.home, "Library/Application Support", userData),
|
|
28353
|
+
executable
|
|
28354
|
+
};
|
|
28345
28355
|
}
|
|
28346
28356
|
return null;
|
|
28347
28357
|
}));
|
|
@@ -28363,13 +28373,26 @@ async function portAnswers(port, fetchFn) {
|
|
|
28363
28373
|
signal: AbortSignal.timeout(PROBE_TIMEOUT_MS)
|
|
28364
28374
|
}).then(() => true, () => false);
|
|
28365
28375
|
}
|
|
28376
|
+
async function debuggingAnswers(browser, deps) {
|
|
28377
|
+
const active = await readActivePort2(browser, deps.readText);
|
|
28378
|
+
return active && await portAnswers(active.port, deps.fetch ?? fetch) ? active : null;
|
|
28379
|
+
}
|
|
28380
|
+
async function psArgs() {
|
|
28381
|
+
const { stdout } = await promisify(execFile)("ps", ["-axww", "-o", "args="]);
|
|
28382
|
+
return stdout.split(`
|
|
28383
|
+
`);
|
|
28384
|
+
}
|
|
28385
|
+
async function isEverydayRunning(executable, deps) {
|
|
28386
|
+
const lines = await (deps.processArgs ?? psArgs)().catch(() => null);
|
|
28387
|
+
if (lines === null)
|
|
28388
|
+
return true;
|
|
28389
|
+
return lines.some((line) => (line === executable || line.startsWith(`${executable} `)) && !line.includes(" --user-data-dir"));
|
|
28390
|
+
}
|
|
28366
28391
|
async function listEverydayDevices(options = {}, deps = discoveryDeps()) {
|
|
28367
28392
|
const includeUnavailable = options.includeUnavailable ?? true;
|
|
28368
28393
|
const browsers = await findEverydayBrowsers(deps);
|
|
28369
28394
|
const devices = await Promise.all(browsers.map(async (browser) => {
|
|
28370
|
-
const
|
|
28371
|
-
const ready = active !== null && await portAnswers(active.port, deps.fetch ?? fetch);
|
|
28372
|
-
const state = ready ? "ready" : "remote debugging off";
|
|
28395
|
+
const state = await debuggingAnswers(browser, deps) ? "ready" : await isEverydayRunning(browser.executable, deps) ? "remote debugging off" : "not running";
|
|
28373
28396
|
return {
|
|
28374
28397
|
id: browser.id,
|
|
28375
28398
|
name: `${browser.name} (everyday)`,
|
|
@@ -28382,7 +28405,7 @@ async function listEverydayDevices(options = {}, deps = discoveryDeps()) {
|
|
|
28382
28405
|
}));
|
|
28383
28406
|
return devices.filter((device) => includeUnavailable || device.state === "ready");
|
|
28384
28407
|
}
|
|
28385
|
-
var ALLOW_TIMEOUT_MS =
|
|
28408
|
+
var ALLOW_TIMEOUT_MS = EVERYDAY_ALLOW_TIMEOUT_MS;
|
|
28386
28409
|
|
|
28387
28410
|
class EverydayConnectError extends Error {
|
|
28388
28411
|
constructor(message) {
|
|
@@ -28394,9 +28417,9 @@ function seconds(ms) {
|
|
|
28394
28417
|
return `${Number((ms / 1000).toFixed(3))} s`;
|
|
28395
28418
|
}
|
|
28396
28419
|
async function openEverydaySocket(browser, deps) {
|
|
28397
|
-
const active = await
|
|
28420
|
+
const active = await debuggingAnswers(browser, deps);
|
|
28398
28421
|
if (!active) {
|
|
28399
|
-
throw new EverydayConnectError(`Remote debugging is off in ${browser.name}. Turn it on at
|
|
28422
|
+
throw new EverydayConnectError(await isEverydayRunning(browser.executable, deps) ? `Remote debugging is off in ${browser.name}. Turn it on at ${REMOTE_DEBUGGING_SETTING_URL}, then connect again` : `${browser.name} is not running. Open it, turn on remote debugging at ${REMOTE_DEBUGGING_SETTING_URL}, then connect again`);
|
|
28400
28423
|
}
|
|
28401
28424
|
const allowTimeoutMs = deps.allowTimeoutMs ?? ALLOW_TIMEOUT_MS;
|
|
28402
28425
|
const connect = deps.connect ?? connectCdp;
|
|
@@ -28876,6 +28899,42 @@ function parseDevicectlApps(json) {
|
|
|
28876
28899
|
const others = apps2.filter((app) => !app.builtByDeveloper);
|
|
28877
28900
|
return [...developerBuilt, ...others].map((app) => app.bundleIdentifier);
|
|
28878
28901
|
}
|
|
28902
|
+
function parseDevicectlAppUrl(json, bundleId) {
|
|
28903
|
+
const items = json?.result?.apps;
|
|
28904
|
+
if (!Array.isArray(items))
|
|
28905
|
+
return;
|
|
28906
|
+
const app = items.find((item) => item.bundleIdentifier === bundleId);
|
|
28907
|
+
return typeof app?.url === "string" && app.url ? app.url : undefined;
|
|
28908
|
+
}
|
|
28909
|
+
function parseDevicectlProcesses(json) {
|
|
28910
|
+
const items = json?.result?.runningProcesses;
|
|
28911
|
+
if (!Array.isArray(items))
|
|
28912
|
+
return null;
|
|
28913
|
+
const processes = [];
|
|
28914
|
+
for (const item of items) {
|
|
28915
|
+
if (typeof item?.processIdentifier !== "number" || typeof item.executable !== "string")
|
|
28916
|
+
continue;
|
|
28917
|
+
processes.push({ pid: item.processIdentifier, executable: item.executable });
|
|
28918
|
+
}
|
|
28919
|
+
return processes;
|
|
28920
|
+
}
|
|
28921
|
+
function decoded(url) {
|
|
28922
|
+
try {
|
|
28923
|
+
return decodeURI(url);
|
|
28924
|
+
} catch {
|
|
28925
|
+
return url;
|
|
28926
|
+
}
|
|
28927
|
+
}
|
|
28928
|
+
function appMainProcess(processes, appUrl) {
|
|
28929
|
+
const bundle = decoded(appUrl.endsWith("/") ? appUrl : `${appUrl}/`);
|
|
28930
|
+
return processes.find((process2) => {
|
|
28931
|
+
const executable = decoded(process2.executable);
|
|
28932
|
+
if (!executable.startsWith(bundle))
|
|
28933
|
+
return false;
|
|
28934
|
+
const inside = executable.slice(bundle.length);
|
|
28935
|
+
return inside !== "" && !inside.includes("/");
|
|
28936
|
+
});
|
|
28937
|
+
}
|
|
28879
28938
|
|
|
28880
28939
|
// src/domains/devices/application.ts
|
|
28881
28940
|
async function runCommand(command, options) {
|
|
@@ -31818,8 +31877,8 @@ async function readProfiles(products, exec) {
|
|
|
31818
31877
|
const path = join12(platform, app, "embedded.mobileprovision");
|
|
31819
31878
|
if (!existsSync6(path))
|
|
31820
31879
|
continue;
|
|
31821
|
-
const
|
|
31822
|
-
const profile =
|
|
31880
|
+
const decoded2 = await exec(["security", "cms", "-D", "-i", path]);
|
|
31881
|
+
const profile = decoded2.exitCode === 0 ? parseProfile(decoded2.stdout) : null;
|
|
31823
31882
|
if (profile)
|
|
31824
31883
|
profiles.push(profile);
|
|
31825
31884
|
}
|
|
@@ -32224,6 +32283,9 @@ var PHONE_KEY_SUPPORT = {
|
|
|
32224
32283
|
};
|
|
32225
32284
|
var RECORDING_PULL_MS = 400;
|
|
32226
32285
|
var UNREACHABLE_GRACE_MS = 20000;
|
|
32286
|
+
var TERMINATE_GRACE_MS = 3000;
|
|
32287
|
+
var TERMINATE_POLL_MS = 250;
|
|
32288
|
+
var KILL_REAP_MS = 1000;
|
|
32227
32289
|
function errorMessage5(error) {
|
|
32228
32290
|
return error instanceof Error ? error.message : String(error);
|
|
32229
32291
|
}
|
|
@@ -32371,28 +32433,28 @@ async function createIosDeviceSession(options, deps = iosDeviceDeps()) {
|
|
|
32371
32433
|
}
|
|
32372
32434
|
return { width: size.width, height: size.height };
|
|
32373
32435
|
});
|
|
32436
|
+
const devicectl = async (args, label) => {
|
|
32437
|
+
const result = await deps.devicectl(args);
|
|
32438
|
+
if (result.exitCode !== 0) {
|
|
32439
|
+
throw new Error(`${label}: ${result.stderr.trim() || result.stdout.trim() || `exit ${result.exitCode}`}`);
|
|
32440
|
+
}
|
|
32441
|
+
};
|
|
32442
|
+
const devicectlJson = async (args, label) => {
|
|
32443
|
+
const dir = await mkdtemp4(join13(tmpdir5(), "yoqa-devicectl-"));
|
|
32444
|
+
const jsonPath = join13(dir, "out.json");
|
|
32445
|
+
try {
|
|
32446
|
+
await devicectl([...args, "--json-output", jsonPath], label);
|
|
32447
|
+
return JSON.parse(await readFile5(jsonPath, "utf8"));
|
|
32448
|
+
} finally {
|
|
32449
|
+
await rm5(dir, { recursive: true, force: true });
|
|
32450
|
+
}
|
|
32451
|
+
};
|
|
32374
32452
|
const installedApps = remember(async () => {
|
|
32375
|
-
const dir = await mkdtemp4(join13(tmpdir5(), "yoqa-apps-"));
|
|
32376
|
-
const jsonPath = join13(dir, "apps.json");
|
|
32377
32453
|
try {
|
|
32378
|
-
|
|
32379
|
-
"device",
|
|
32380
|
-
"info",
|
|
32381
|
-
"apps",
|
|
32382
|
-
"--device",
|
|
32383
|
-
udid,
|
|
32384
|
-
"--json-output",
|
|
32385
|
-
jsonPath
|
|
32386
|
-
]);
|
|
32387
|
-
if (result.exitCode !== 0) {
|
|
32388
|
-
throw new Error(result.stderr.trim() || result.stdout.trim() || `exit ${result.exitCode}`);
|
|
32389
|
-
}
|
|
32390
|
-
return parseDevicectlApps(JSON.parse(await readFile5(jsonPath, "utf8")));
|
|
32454
|
+
return parseDevicectlApps(await devicectlJson(["device", "info", "apps", "--device", udid], "devicectl apps"));
|
|
32391
32455
|
} catch (error) {
|
|
32392
32456
|
console.warn(`[yoqa-runner] devicectl could not list the apps on iPhone ${udid} (${errorMessage5(error)}); with no target app, the tree is SpringBoard's`);
|
|
32393
32457
|
return [];
|
|
32394
|
-
} finally {
|
|
32395
|
-
await rm5(dir, { recursive: true, force: true });
|
|
32396
32458
|
}
|
|
32397
32459
|
});
|
|
32398
32460
|
const readPageSource = async () => {
|
|
@@ -32503,11 +32565,52 @@ async function createIosDeviceSession(options, deps = iosDeviceDeps()) {
|
|
|
32503
32565
|
};
|
|
32504
32566
|
let pointerStart = null;
|
|
32505
32567
|
let pointerActive = false;
|
|
32506
|
-
const
|
|
32507
|
-
const
|
|
32508
|
-
if (
|
|
32509
|
-
throw new Error(`${
|
|
32510
|
-
|
|
32568
|
+
const installedAt = async (id) => {
|
|
32569
|
+
const appUrl = parseDevicectlAppUrl(await devicectlJson(["device", "info", "apps", "--device", udid, "--bundle-id", id, "--include-all-apps"], "devicectl apps"), id);
|
|
32570
|
+
if (!appUrl)
|
|
32571
|
+
throw new Error(`${id} is not installed on the iPhone`);
|
|
32572
|
+
return appUrl;
|
|
32573
|
+
};
|
|
32574
|
+
const appProcess = async (appUrl) => {
|
|
32575
|
+
const processes = parseDevicectlProcesses(await devicectlJson(["device", "info", "processes", "--device", udid], "devicectl processes"));
|
|
32576
|
+
if (!processes)
|
|
32577
|
+
throw new Error("devicectl processes: the process list was unreadable");
|
|
32578
|
+
return appMainProcess(processes, appUrl);
|
|
32579
|
+
};
|
|
32580
|
+
const terminateGraceMs = deps.terminateGraceMs ?? TERMINATE_GRACE_MS;
|
|
32581
|
+
const terminate = async (id) => {
|
|
32582
|
+
const appUrl = await installedAt(id);
|
|
32583
|
+
const running2 = await appProcess(appUrl);
|
|
32584
|
+
if (!running2)
|
|
32585
|
+
return;
|
|
32586
|
+
const signal = (force) => devicectl([
|
|
32587
|
+
"device",
|
|
32588
|
+
"process",
|
|
32589
|
+
"terminate",
|
|
32590
|
+
"--device",
|
|
32591
|
+
udid,
|
|
32592
|
+
"--pid",
|
|
32593
|
+
String(running2.pid),
|
|
32594
|
+
...force ? ["--kill"] : []
|
|
32595
|
+
], "devicectl terminate");
|
|
32596
|
+
const exited = async (waitMs) => {
|
|
32597
|
+
const deadline = performance.now() + waitMs;
|
|
32598
|
+
for (;; ) {
|
|
32599
|
+
if ((await appProcess(appUrl))?.pid !== running2.pid)
|
|
32600
|
+
return true;
|
|
32601
|
+
const left = deadline - performance.now();
|
|
32602
|
+
if (left <= 0)
|
|
32603
|
+
return false;
|
|
32604
|
+
await Bun.sleep(Math.min(TERMINATE_POLL_MS, left));
|
|
32605
|
+
}
|
|
32606
|
+
};
|
|
32607
|
+
await signal(false);
|
|
32608
|
+
if (await exited(terminateGraceMs))
|
|
32609
|
+
return;
|
|
32610
|
+
await signal(true);
|
|
32611
|
+
if (await exited(Math.min(KILL_REAP_MS, terminateGraceMs)))
|
|
32612
|
+
return;
|
|
32613
|
+
throw new Error(`${id} is still running on the iPhone after a force-kill (pid ${running2.pid}); close it on the phone`);
|
|
32511
32614
|
};
|
|
32512
32615
|
return {
|
|
32513
32616
|
lane: "direct",
|
|
@@ -32547,7 +32650,7 @@ async function createIosDeviceSession(options, deps = iosDeviceDeps()) {
|
|
|
32547
32650
|
appId = id;
|
|
32548
32651
|
},
|
|
32549
32652
|
targetApp: () => appId,
|
|
32550
|
-
terminateApp:
|
|
32653
|
+
terminateApp: (id) => withActionLock(() => terminate(id)),
|
|
32551
32654
|
backgroundApp: (seconds2 = 3) => withActionLock(async () => {
|
|
32552
32655
|
await call("button", { name: "home" }, true);
|
|
32553
32656
|
if (!appId)
|
|
@@ -107960,6 +108063,7 @@ async function performAction(session, body, options) {
|
|
|
107960
108063
|
} else {
|
|
107961
108064
|
await session.terminateApp(body.appId);
|
|
107962
108065
|
await session.activateApp(body.appId);
|
|
108066
|
+
storage = "kept";
|
|
107963
108067
|
}
|
|
107964
108068
|
break;
|
|
107965
108069
|
}
|
|
@@ -110241,15 +110345,15 @@ var HonoRequest = class {
|
|
|
110241
110345
|
return param && tryDecodeURIComponent(param);
|
|
110242
110346
|
}
|
|
110243
110347
|
#getAllDecodedParams() {
|
|
110244
|
-
const
|
|
110348
|
+
const decoded2 = {};
|
|
110245
110349
|
const keys = Object.keys(this.#matchResult[0][this.routeIndex][1]);
|
|
110246
110350
|
for (const key of keys) {
|
|
110247
110351
|
const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
|
|
110248
110352
|
if (value !== undefined) {
|
|
110249
|
-
|
|
110353
|
+
decoded2[key] = tryDecodeURIComponent(value);
|
|
110250
110354
|
}
|
|
110251
110355
|
}
|
|
110252
|
-
return
|
|
110356
|
+
return decoded2;
|
|
110253
110357
|
}
|
|
110254
110358
|
#getParamValue(paramKey) {
|
|
110255
110359
|
return this.#matchResult[1] ? this.#matchResult[1][paramKey] : paramKey;
|
|
@@ -111859,7 +111963,7 @@ import { join as join23 } from "path";
|
|
|
111859
111963
|
// package.json
|
|
111860
111964
|
var package_default = {
|
|
111861
111965
|
name: "@yoqa/runner",
|
|
111862
|
-
version: "1.0.
|
|
111966
|
+
version: "1.0.7",
|
|
111863
111967
|
description: "Yoqa local runner \u2014 device control + catalog HTTP API (Bun). Start with yoqa serve or the yoqa-runner bin.",
|
|
111864
111968
|
license: "UNLICENSED",
|
|
111865
111969
|
type: "module",
|
package/package.json
CHANGED