@houwert/conductor 0.16.0 → 0.18.0

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.
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.HELP = void 0;
7
+ exports.pinch = pinch;
8
+ exports.rotateGesture = rotateGesture;
9
+ exports.gesture = gesture;
10
+ exports.HELP = ` pinch [--scale N] [--center x,y] [--duration ms] [--angle deg]
11
+ Two-finger pinch (scale<1 zoom out, scale>1 zoom in)
12
+ rotate-gesture [--degrees N] [--center x,y] [--duration ms]
13
+ Two-finger rotate gesture
14
+ gesture <json|--file path> Play a multi-touch path
15
+ JSON: [{"steps":[{"x":,"y":,"dt":}]},...]
16
+ dt is delay since previous step (seconds for iOS, see docs)`;
17
+ const fs_1 = __importDefault(require("fs"));
18
+ const runner_js_1 = require("../runner.js");
19
+ const output_js_1 = require("../output.js");
20
+ const ios_js_1 = require("../drivers/ios.js");
21
+ const web_js_1 = require("../drivers/web.js");
22
+ function parseCenter(s) {
23
+ if (!s)
24
+ return null;
25
+ const m = s.match(/^\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*$/);
26
+ if (!m)
27
+ return null;
28
+ return { x: Number(m[1]), y: Number(m[2]) };
29
+ }
30
+ async function screenCenter(driver) {
31
+ if (driver instanceof ios_js_1.IOSDriver) {
32
+ const info = await driver.deviceInfo();
33
+ return {
34
+ width: info.widthPoints,
35
+ height: info.heightPoints,
36
+ cx: info.widthPoints / 2,
37
+ cy: info.heightPoints / 2,
38
+ };
39
+ }
40
+ const info = await driver.deviceInfo();
41
+ return {
42
+ width: info.widthPixels,
43
+ height: info.heightPixels,
44
+ cx: info.widthPixels / 2,
45
+ cy: info.heightPixels / 2,
46
+ };
47
+ }
48
+ /**
49
+ * Build two synchronized finger paths for a pinch. The two fingers start at
50
+ * `startDistance` apart, end at `endDistance`, along an axis rotated by
51
+ * `angleDeg`. We emit a coarse path (~60fps) — the drivers interpolate inside.
52
+ *
53
+ * Distances are in pixels (iOS uses points, Android uses pixels — caller
54
+ * passes the raw value; we don't normalize).
55
+ */
56
+ function buildPinchPaths(cx, cy, startDist, endDist, angleDeg, durationMs) {
57
+ const rad = (angleDeg * Math.PI) / 180;
58
+ const cos = Math.cos(rad);
59
+ const sin = Math.sin(rad);
60
+ const steps = Math.max(2, Math.round(durationMs / 16));
61
+ const dtStep = durationMs / steps / 1000;
62
+ const finger1 = [];
63
+ const finger2 = [];
64
+ for (let i = 0; i <= steps; i++) {
65
+ const t = i / steps;
66
+ const dist = startDist + (endDist - startDist) * t;
67
+ const half = dist / 2;
68
+ const dx = half * cos;
69
+ const dy = half * sin;
70
+ const dt = i === 0 ? 0 : dtStep;
71
+ finger1.push({ x: cx - dx, y: cy - dy, dt });
72
+ finger2.push({ x: cx + dx, y: cy + dy, dt });
73
+ }
74
+ return [{ steps: finger1 }, { steps: finger2 }];
75
+ }
76
+ function buildRotatePaths(cx, cy, radius, degrees, durationMs) {
77
+ const steps = Math.max(2, Math.round(durationMs / 16));
78
+ const dtStep = durationMs / steps / 1000;
79
+ const finger1 = [];
80
+ const finger2 = [];
81
+ for (let i = 0; i <= steps; i++) {
82
+ const t = i / steps;
83
+ const theta = (degrees * Math.PI * t) / 180;
84
+ const dx = radius * Math.cos(theta);
85
+ const dy = radius * Math.sin(theta);
86
+ const dt = i === 0 ? 0 : dtStep;
87
+ finger1.push({ x: cx - dx, y: cy - dy, dt });
88
+ finger2.push({ x: cx + dx, y: cy + dy, dt });
89
+ }
90
+ return [{ steps: finger1 }, { steps: finger2 }];
91
+ }
92
+ async function playPaths(driver, paths) {
93
+ if (driver instanceof ios_js_1.IOSDriver) {
94
+ await driver.gesturePath(paths);
95
+ }
96
+ else {
97
+ // Android proto uses dt_ms (ms), our internal model uses seconds.
98
+ const androidPaths = paths.map((p) => ({
99
+ steps: p.steps.map((s) => ({ x: s.x, y: s.y, dt_ms: Math.round(s.dt * 1000) })),
100
+ }));
101
+ await driver.gesturePath(androidPaths);
102
+ }
103
+ }
104
+ async function pinch(opts, sessionName, pinchOpts) {
105
+ const scale = pinchOpts.scale ?? 0.5;
106
+ const duration = pinchOpts.duration ?? 400;
107
+ const angle = pinchOpts.angle ?? 0;
108
+ const result = await (0, runner_js_1.runDirect)(async (driver) => {
109
+ if (driver instanceof web_js_1.WebDriver)
110
+ throw new Error('pinch is not supported on Web');
111
+ const { width, height, cx: defCx, cy: defCy } = await screenCenter(driver);
112
+ const center = parseCenter(pinchOpts.center) ?? { x: defCx, y: defCy };
113
+ // Start span is half the shorter screen dimension; end is scaled.
114
+ const baseSpan = Math.min(width, height) * 0.5;
115
+ const startDist = scale < 1 ? baseSpan : baseSpan * scale;
116
+ const endDist = scale < 1 ? baseSpan * scale : baseSpan;
117
+ const paths = buildPinchPaths(center.x, center.y, startDist, endDist, angle, duration);
118
+ await playPaths(driver, paths);
119
+ }, sessionName);
120
+ if (result.success) {
121
+ (0, output_js_1.printSuccess)('pinch — done', opts);
122
+ return 0;
123
+ }
124
+ (0, output_js_1.printError)(`pinch — failed\n${result.stderr}`, opts);
125
+ return 1;
126
+ }
127
+ async function rotateGesture(opts, sessionName, rotateOpts) {
128
+ const degrees = rotateOpts.degrees ?? 90;
129
+ const duration = rotateOpts.duration ?? 500;
130
+ const result = await (0, runner_js_1.runDirect)(async (driver) => {
131
+ if (driver instanceof web_js_1.WebDriver)
132
+ throw new Error('rotate-gesture is not supported on Web');
133
+ const { width, height, cx: defCx, cy: defCy } = await screenCenter(driver);
134
+ const center = parseCenter(rotateOpts.center) ?? { x: defCx, y: defCy };
135
+ const radius = Math.min(width, height) * 0.25;
136
+ const paths = buildRotatePaths(center.x, center.y, radius, degrees, duration);
137
+ await playPaths(driver, paths);
138
+ }, sessionName);
139
+ if (result.success) {
140
+ (0, output_js_1.printSuccess)('rotate-gesture — done', opts);
141
+ return 0;
142
+ }
143
+ (0, output_js_1.printError)(`rotate-gesture — failed\n${result.stderr}`, opts);
144
+ return 1;
145
+ }
146
+ function inputToPaths(tracks) {
147
+ return tracks.map((t) => {
148
+ const pts = t.steps ?? t.points ?? [];
149
+ let prevT = 0;
150
+ return {
151
+ steps: pts.map((p) => {
152
+ let dt = p.dt;
153
+ if (dt === undefined && p.t !== undefined) {
154
+ dt = (p.t - prevT) / 1000;
155
+ prevT = p.t;
156
+ }
157
+ return { x: p.x, y: p.y, dt: dt ?? 0 };
158
+ }),
159
+ };
160
+ });
161
+ }
162
+ function readGestureInput(raw, filePath) {
163
+ let json;
164
+ if (filePath)
165
+ json = fs_1.default.readFileSync(filePath, 'utf-8');
166
+ else if (raw)
167
+ json = raw;
168
+ else
169
+ throw new Error('gesture requires a JSON argument or --file <path>');
170
+ const parsed = JSON.parse(json);
171
+ if (!Array.isArray(parsed))
172
+ throw new Error('gesture input must be a JSON array of tracks');
173
+ return parsed;
174
+ }
175
+ async function gesture(rawJson, filePath, opts, sessionName) {
176
+ let tracks;
177
+ try {
178
+ tracks = readGestureInput(rawJson, filePath);
179
+ }
180
+ catch (err) {
181
+ (0, output_js_1.printError)(`gesture — ${err instanceof Error ? err.message : String(err)}`, opts);
182
+ return 1;
183
+ }
184
+ const paths = inputToPaths(tracks);
185
+ const result = await (0, runner_js_1.runDirect)(async (driver) => {
186
+ if (driver instanceof web_js_1.WebDriver)
187
+ throw new Error('gesture is not supported on Web');
188
+ await playPaths(driver, paths);
189
+ }, sessionName);
190
+ if (result.success) {
191
+ (0, output_js_1.printSuccess)(`gesture — played ${tracks.length} track(s)`, opts);
192
+ return 0;
193
+ }
194
+ (0, output_js_1.printError)(`gesture — failed\n${result.stderr}`, opts);
195
+ return 1;
196
+ }
@@ -2,7 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HELP = void 0;
4
4
  exports.inspect = inspect;
5
- exports.HELP = ` inspect [--dump] Print UI hierarchy (--dump for raw driver output)`;
5
+ exports.HELP = ` inspect [--dump] Print UI hierarchy (--dump for raw driver output)
6
+ inspect --at <x,y> [--tappable] Print the topmost view at a screen point`;
6
7
  const runner_js_1 = require("../runner.js");
7
8
  const output_js_1 = require("../output.js");
8
9
  const ios_js_1 = require("../drivers/ios.js");
@@ -13,6 +14,45 @@ const a11y_js_1 = require("../drivers/a11y.js");
13
14
  async function inspect(opts = {}, sessionName = 'default', inspectOpts = {}) {
14
15
  try {
15
16
  const driver = await (0, runner_js_1.getDriver)(sessionName);
17
+ if (inspectOpts.at) {
18
+ const m = inspectOpts.at.match(/^\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*$/);
19
+ if (!m) {
20
+ (0, output_js_1.printError)(`inspect --at expects "<x>,<y>", got "${inspectOpts.at}"`, opts);
21
+ return 1;
22
+ }
23
+ const x = Number(m[1]);
24
+ const y = Number(m[2]);
25
+ const tappable = !!inspectOpts.tappableOnly;
26
+ let hit;
27
+ if (driver instanceof ios_js_1.IOSDriver) {
28
+ const hierarchy = await driver.viewHierarchy(false);
29
+ hit = (0, element_resolver_js_1.findIOSViewAtPoint)(hierarchy.axElement, x, y, tappable);
30
+ }
31
+ else if (driver instanceof android_js_1.AndroidDriver) {
32
+ const xml = await driver.viewHierarchy();
33
+ hit = (0, element_resolver_js_1.findAndroidViewAtPoint)(xml, x, y, tappable);
34
+ }
35
+ else if (driver instanceof web_js_1.WebDriver) {
36
+ const vh = await driver.viewHierarchy();
37
+ hit = (0, element_resolver_js_1.findWebViewAtPoint)(vh, x, y, tappable);
38
+ }
39
+ else {
40
+ throw new Error('Unknown driver type');
41
+ }
42
+ if (!hit) {
43
+ const message = `No ${tappable ? 'tappable view' : 'view'} found at (${x}, ${y})`;
44
+ if (opts.json)
45
+ (0, output_js_1.printData)({ found: false, x, y, tappable }, opts);
46
+ else
47
+ console.log(message);
48
+ return tappable ? 1 : 0;
49
+ }
50
+ if (opts.json)
51
+ (0, output_js_1.printData)({ found: true, x, y, tappable, hit }, opts);
52
+ else
53
+ console.log(hit.summary);
54
+ return 0;
55
+ }
16
56
  if (inspectOpts.dump) {
17
57
  let raw;
18
58
  if (driver instanceof ios_js_1.IOSDriver) {
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HELP = void 0;
4
+ exports.metroStop = metroStop;
5
+ exports.metroReload = metroReload;
6
+ exports.HELP = ` metro stop [--port N] Stop the Metro bundler process on a port (default 8081)
7
+ metro reload [--port N] [--target N] Reload the JS bundle without restarting native`;
8
+ const child_process_1 = require("child_process");
9
+ const output_js_1 = require("../output.js");
10
+ const metro_cdp_js_1 = require("../drivers/metro-cdp.js");
11
+ const bootstrap_js_1 = require("../drivers/bootstrap.js");
12
+ async function pidsOnPort(port) {
13
+ return new Promise((resolve) => {
14
+ const proc = (0, child_process_1.spawn)('lsof', ['-ti', `tcp:${port}`], {
15
+ stdio: ['ignore', 'pipe', 'ignore'],
16
+ });
17
+ let out = '';
18
+ proc.stdout.on('data', (c) => {
19
+ out += c.toString();
20
+ });
21
+ proc.on('close', () => {
22
+ const pids = out
23
+ .split('\n')
24
+ .map((s) => parseInt(s.trim(), 10))
25
+ .filter((n) => Number.isFinite(n) && n > 0);
26
+ resolve(pids);
27
+ });
28
+ proc.on('error', () => resolve([]));
29
+ });
30
+ }
31
+ async function metroStop(opts, metroOpts) {
32
+ const port = metroOpts.port ?? 8081;
33
+ const pids = await pidsOnPort(port);
34
+ if (pids.length === 0) {
35
+ (0, output_js_1.printData)({ stopped: false, port, pids: [] }, opts);
36
+ if (!opts.json)
37
+ (0, output_js_1.printSuccess)(`No Metro process found on port ${port}`, opts);
38
+ return 0;
39
+ }
40
+ for (const pid of pids) {
41
+ try {
42
+ process.kill(pid, 'SIGTERM');
43
+ }
44
+ catch {
45
+ // Process may already be gone
46
+ }
47
+ }
48
+ // Give SIGTERM 2s, then SIGKILL anything still alive.
49
+ await new Promise((r) => setTimeout(r, 2000));
50
+ const survivors = await pidsOnPort(port);
51
+ for (const pid of survivors) {
52
+ try {
53
+ process.kill(pid, 'SIGKILL');
54
+ }
55
+ catch {
56
+ // ignore
57
+ }
58
+ }
59
+ if (opts.json) {
60
+ (0, output_js_1.printData)({ stopped: true, port, pids }, opts);
61
+ }
62
+ else {
63
+ (0, output_js_1.printSuccess)(`Stopped Metro on port ${port} (pids: ${pids.join(', ')})`, opts);
64
+ }
65
+ return 0;
66
+ }
67
+ async function metroReload(opts, sessionName, metroOpts) {
68
+ const port = metroOpts.port ?? 8081;
69
+ let deviceId;
70
+ let platform;
71
+ if (sessionName && sessionName !== 'default') {
72
+ deviceId = sessionName;
73
+ platform = await (0, bootstrap_js_1.detectPlatform)(deviceId).catch(() => undefined);
74
+ }
75
+ // Try CDP Page.reload first (works on Hermes/Fusebox).
76
+ try {
77
+ await (0, metro_cdp_js_1.cdpCall)('Page.reload', undefined, {
78
+ port,
79
+ deviceId,
80
+ platform,
81
+ targetIndex: metroOpts.targetIndex,
82
+ });
83
+ if (opts.json)
84
+ (0, output_js_1.printData)({ reloaded: true, port, method: 'cdp' }, opts);
85
+ else
86
+ (0, output_js_1.printSuccess)(`Reloaded Metro bundle on port ${port} (cdp)`, opts);
87
+ return 0;
88
+ }
89
+ catch (cdpErr) {
90
+ // Fall back to Metro's HTTP /reload endpoint.
91
+ try {
92
+ const res = await fetch(`http://127.0.0.1:${port}/reload`);
93
+ if (!res.ok) {
94
+ throw new Error(`HTTP /reload returned ${res.status}`);
95
+ }
96
+ if (opts.json)
97
+ (0, output_js_1.printData)({ reloaded: true, port, method: 'http' }, opts);
98
+ else
99
+ (0, output_js_1.printSuccess)(`Reloaded Metro bundle on port ${port} (http)`, opts);
100
+ return 0;
101
+ }
102
+ catch (httpErr) {
103
+ const cdpMsg = cdpErr instanceof Error ? cdpErr.message : String(cdpErr);
104
+ const httpMsg = httpErr instanceof Error ? httpErr.message : String(httpErr);
105
+ (0, output_js_1.printError)(`metro reload failed.\n cdp: ${cdpMsg}\n http: ${httpMsg}`, opts);
106
+ return 1;
107
+ }
108
+ }
109
+ }
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HELP = void 0;
4
+ exports.networkLogs = networkLogs;
5
+ exports.networkRequest = networkRequest;
6
+ exports.HELP = ` network logs [--port N] [--limit N] Read recent HTTP traffic (installs a fetch/XHR shim once)
7
+ network request <url> [--method M] [--body STR] [--header K=V] [--port N]
8
+ Issue an HTTP request from the app's context`;
9
+ const output_js_1 = require("../output.js");
10
+ const metro_cdp_js_1 = require("../drivers/metro-cdp.js");
11
+ const bootstrap_js_1 = require("../drivers/bootstrap.js");
12
+ const INSTALL_SHIM_SCRIPT = `
13
+ (() => {
14
+ if (globalThis.__CONDUCTOR_NET__ && globalThis.__CONDUCTOR_NET__.installed) {
15
+ return { installed: true, already: true };
16
+ }
17
+ const ring = [];
18
+ const MAX = 200;
19
+ function push(entry) {
20
+ ring.push(entry);
21
+ if (ring.length > MAX) ring.shift();
22
+ }
23
+ globalThis.__CONDUCTOR_NET__ = {
24
+ installed: true,
25
+ entries: ring,
26
+ clear: () => { ring.length = 0; },
27
+ };
28
+
29
+ const realFetch = globalThis.fetch;
30
+ if (typeof realFetch === 'function') {
31
+ globalThis.fetch = function patchedFetch(input, init) {
32
+ const url = typeof input === 'string' ? input : (input && input.url) || String(input);
33
+ const method = (init && init.method) || (input && input.method) || 'GET';
34
+ const start = Date.now();
35
+ const id = Math.random().toString(36).slice(2, 10);
36
+ const entry = { id, kind: 'fetch', method, url, status: null, durationMs: null, error: null, start };
37
+ push(entry);
38
+ let p;
39
+ try { p = realFetch.apply(this, arguments); } catch (e) {
40
+ entry.error = String(e && e.message || e);
41
+ entry.durationMs = Date.now() - start;
42
+ throw e;
43
+ }
44
+ return p.then(res => {
45
+ entry.status = res.status;
46
+ entry.durationMs = Date.now() - start;
47
+ return res;
48
+ }, err => {
49
+ entry.error = String(err && err.message || err);
50
+ entry.durationMs = Date.now() - start;
51
+ throw err;
52
+ });
53
+ };
54
+ }
55
+
56
+ const RealXHR = globalThis.XMLHttpRequest;
57
+ if (RealXHR) {
58
+ const origOpen = RealXHR.prototype.open;
59
+ const origSend = RealXHR.prototype.send;
60
+ RealXHR.prototype.open = function open(method, url) {
61
+ this.__c_meta = { id: Math.random().toString(36).slice(2, 10), kind: 'xhr', method, url, status: null, durationMs: null, error: null, start: 0 };
62
+ return origOpen.apply(this, arguments);
63
+ };
64
+ RealXHR.prototype.send = function send() {
65
+ const meta = this.__c_meta;
66
+ if (meta) {
67
+ meta.start = Date.now();
68
+ push(meta);
69
+ this.addEventListener('loadend', () => {
70
+ meta.status = this.status;
71
+ meta.durationMs = Date.now() - meta.start;
72
+ });
73
+ this.addEventListener('error', () => {
74
+ meta.error = 'xhr error';
75
+ meta.durationMs = Date.now() - meta.start;
76
+ });
77
+ }
78
+ return origSend.apply(this, arguments);
79
+ };
80
+ }
81
+ return { installed: true, already: false };
82
+ })()
83
+ `;
84
+ const READ_SCRIPT = (limit) => `
85
+ (() => {
86
+ const tap = globalThis.__CONDUCTOR_NET__;
87
+ if (!tap) return { installed: false, entries: [] };
88
+ const entries = tap.entries.slice(-${limit});
89
+ return { installed: true, count: entries.length, entries };
90
+ })()
91
+ `;
92
+ function resolveSession(sessionName) {
93
+ if (!sessionName || sessionName === 'default') {
94
+ return { deviceId: undefined, platformPromise: Promise.resolve(undefined) };
95
+ }
96
+ return {
97
+ deviceId: sessionName,
98
+ platformPromise: (0, bootstrap_js_1.detectPlatform)(sessionName).catch(() => undefined),
99
+ };
100
+ }
101
+ async function networkLogs(opts, sessionName, netOpts) {
102
+ const port = netOpts.port ?? 8081;
103
+ const limit = netOpts.limit ?? 50;
104
+ const { deviceId, platformPromise } = resolveSession(sessionName);
105
+ try {
106
+ const platform = await platformPromise;
107
+ const client = new metro_cdp_js_1.MetroCdpClient();
108
+ await client.connect({ port, deviceId, platform, targetIndex: netOpts.targetIndex });
109
+ await client.evaluate(INSTALL_SHIM_SCRIPT);
110
+ const result = await client.evaluate(READ_SCRIPT(limit));
111
+ client.close();
112
+ const entries = result.entries ?? [];
113
+ if (opts.json) {
114
+ (0, output_js_1.printData)({ installed: result.installed, count: entries.length, entries }, opts);
115
+ }
116
+ else {
117
+ if (entries.length === 0) {
118
+ console.log('No network entries captured yet. The shim is installed; reload the app and try again.');
119
+ }
120
+ for (const e of entries) {
121
+ const ts = new Date(e.start).toISOString().slice(11, 23);
122
+ const status = e.error ? `ERR ${e.error}` : e.status !== null ? String(e.status) : '...';
123
+ const dur = e.durationMs !== null ? `${e.durationMs}ms` : '-';
124
+ console.log(`${ts} ${status.padEnd(6)} ${e.method.padEnd(6)} ${e.url} (${dur}, ${e.kind})`);
125
+ }
126
+ }
127
+ return 0;
128
+ }
129
+ catch (err) {
130
+ (0, output_js_1.printError)(`network logs — ${err instanceof Error ? err.message : String(err)}`, opts);
131
+ return 1;
132
+ }
133
+ }
134
+ async function networkRequest(url, opts, sessionName, reqOpts) {
135
+ if (!url) {
136
+ (0, output_js_1.printError)('network request requires a URL', opts);
137
+ return 1;
138
+ }
139
+ const method = (reqOpts.method ?? 'GET').toUpperCase();
140
+ const headers = {};
141
+ for (const h of reqOpts.headers ?? []) {
142
+ const idx = h.indexOf('=');
143
+ if (idx > 0)
144
+ headers[h.slice(0, idx)] = h.slice(idx + 1);
145
+ }
146
+ const body = reqOpts.body;
147
+ const init = JSON.stringify({
148
+ method,
149
+ headers,
150
+ ...(body !== undefined ? { body } : {}),
151
+ });
152
+ const script = `
153
+ (async () => {
154
+ try {
155
+ const res = await fetch(${JSON.stringify(url)}, ${init});
156
+ const text = await res.text();
157
+ const hdrs = {};
158
+ res.headers.forEach((v, k) => { hdrs[k] = v; });
159
+ return { ok: res.ok, status: res.status, headers: hdrs, body: text };
160
+ } catch (e) {
161
+ return { ok: false, error: String(e && e.message || e) };
162
+ }
163
+ })()
164
+ `;
165
+ const port = reqOpts.port ?? 8081;
166
+ const { deviceId, platformPromise } = resolveSession(sessionName);
167
+ try {
168
+ const platform = await platformPromise;
169
+ const client = new metro_cdp_js_1.MetroCdpClient();
170
+ await client.connect({ port, deviceId, platform, targetIndex: reqOpts.targetIndex });
171
+ const result = await client.evaluate(script);
172
+ client.close();
173
+ if (opts.json)
174
+ (0, output_js_1.printData)(result, opts);
175
+ else {
176
+ if (result.error)
177
+ console.error(`error: ${result.error}`);
178
+ console.log(`status: ${result.status ?? 'n/a'}`);
179
+ if (result.headers) {
180
+ for (const [k, v] of Object.entries(result.headers))
181
+ console.log(`${k}: ${v}`);
182
+ }
183
+ if (result.body !== undefined) {
184
+ console.log('');
185
+ console.log(result.body);
186
+ }
187
+ }
188
+ return result.ok ? 0 : 1;
189
+ }
190
+ catch (err) {
191
+ (0, output_js_1.printError)(`network request — ${err instanceof Error ? err.message : String(err)}`, opts);
192
+ return 1;
193
+ }
194
+ }