@houwert/conductor 0.27.2 → 0.29.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.
- package/README.md +71 -47
- package/dist/android/device.js +94 -0
- package/dist/commands/debug.js +6 -6
- package/dist/commands/device-pool.js +24 -5
- package/dist/commands/focused.js +27 -0
- package/dist/commands/input-latency.js +348 -0
- package/dist/commands/list-apps.js +8 -1
- package/dist/commands/list-devices.js +30 -1
- package/dist/commands/metro.js +3 -1
- package/dist/commands/native-rn.js +2 -2
- package/dist/commands/network.js +2 -2
- package/dist/commands/press-key.js +106 -60
- package/dist/commands/profile-frames.js +793 -0
- package/dist/commands/profile-gc.js +90 -0
- package/dist/commands/profile-js.js +320 -0
- package/dist/commands/profile.js +391 -77
- package/dist/commands/run-flow.js +70 -4
- package/dist/drivers/bootstrap.js +6 -1
- package/dist/drivers/flow-runner.js +32 -2
- package/dist/drivers/metro-cdp.js +28 -3
- package/dist/index.js +81 -6
- package/dist/stats.js +71 -0
- package/package.json +1 -1
- package/skills/conductor-create-flow/SKILL.md +1 -1
- package/skills/conductor-device-interact/SKILL.md +1 -1
- package/skills/conductor-device-setup/SKILL.md +19 -2
- package/skills/conductor-profiler/SKILL.md +115 -7
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.VALID_KEYS = exports.HELP = void 0;
|
|
3
|
+
exports.ANDROID_KEYCODE = exports.VALID_KEYS = exports.HELP = void 0;
|
|
4
|
+
exports.dispatchKey = dispatchKey;
|
|
4
5
|
exports.pressKey = pressKey;
|
|
5
6
|
exports.HELP = ` press-key <key> Press a key (Enter, Backspace, Home, ...)
|
|
6
7
|
--long-press Hold the button ~1.5s (tvOS remote buttons)
|
|
7
|
-
--duration <seconds> Hold for a custom duration (tvOS remote buttons)
|
|
8
|
+
--duration <seconds> Hold for a custom duration (tvOS remote buttons)
|
|
9
|
+
--measure Time the app's response to the press
|
|
10
|
+
--repeat <n> With --measure, take n samples and report a distribution
|
|
11
|
+
--sequence <k1,k2,...> With --repeat, cycle these keys so focus oscillates
|
|
12
|
+
--timeout <ms> With --measure, give up on a sample after n ms (default 3000)
|
|
13
|
+
--poll-interval <ms> With --measure, delay between focus polls (default 0)
|
|
14
|
+
--settle <ms> With --measure on Android, render time to allow (default 700)`;
|
|
8
15
|
const runner_js_1 = require("../runner.js");
|
|
9
16
|
const output_js_1 = require("../output.js");
|
|
17
|
+
const input_latency_js_1 = require("./input-latency.js");
|
|
10
18
|
const ios_js_1 = require("../drivers/ios.js");
|
|
11
19
|
const android_js_1 = require("../drivers/android.js");
|
|
12
20
|
const web_js_1 = require("../drivers/web.js");
|
|
@@ -87,7 +95,7 @@ const VEGA_REMOTE_BUTTONS = {
|
|
|
87
95
|
VolumeDown: 'volumeDown',
|
|
88
96
|
};
|
|
89
97
|
// Android keyevent codes
|
|
90
|
-
|
|
98
|
+
exports.ANDROID_KEYCODE = {
|
|
91
99
|
Home: 3,
|
|
92
100
|
Back: 4,
|
|
93
101
|
Enter: 66,
|
|
@@ -124,6 +132,72 @@ const ANDROID_KEYCODE = {
|
|
|
124
132
|
'TV Input HDMI 2': 244,
|
|
125
133
|
'TV Input HDMI 3': 245,
|
|
126
134
|
};
|
|
135
|
+
/**
|
|
136
|
+
* Send `key` on an already-connected driver. Split out of `pressKey` so
|
|
137
|
+
* `--measure` can reuse one driver across repeats instead of paying driver
|
|
138
|
+
* setup on every sample.
|
|
139
|
+
*/
|
|
140
|
+
async function dispatchKey(driver, matched, holdSeconds) {
|
|
141
|
+
if (driver instanceof ios_js_1.IOSDriver) {
|
|
142
|
+
if (driver.platform === 'tvos') {
|
|
143
|
+
const tvosButton = TVOS_REMOTE_BUTTONS[matched];
|
|
144
|
+
const iosButton = IOS_BUTTON_MAP[matched];
|
|
145
|
+
if (tvosButton) {
|
|
146
|
+
await driver.pressButton(tvosButton, holdSeconds);
|
|
147
|
+
}
|
|
148
|
+
else if (iosButton) {
|
|
149
|
+
await driver.pressButton(iosButton, holdSeconds);
|
|
150
|
+
}
|
|
151
|
+
// Keys not mapped on tvOS are silently ignored
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
const iosKey = IOS_KEY_MAP[matched];
|
|
155
|
+
const iosButton = IOS_BUTTON_MAP[matched];
|
|
156
|
+
if (iosKey) {
|
|
157
|
+
await driver.pressKey(iosKey);
|
|
158
|
+
}
|
|
159
|
+
else if (iosButton) {
|
|
160
|
+
await driver.pressButton(iosButton);
|
|
161
|
+
}
|
|
162
|
+
// Keys not mapped on iOS (e.g. Back, VolumeUp) are silently ignored
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else if (driver instanceof web_js_1.WebDriver) {
|
|
166
|
+
const WEB_KEY_MAP = {
|
|
167
|
+
Enter: 'Enter',
|
|
168
|
+
Tab: 'Tab',
|
|
169
|
+
Backspace: 'Backspace',
|
|
170
|
+
Delete: 'Delete',
|
|
171
|
+
Escape: 'Escape',
|
|
172
|
+
Home: 'Home',
|
|
173
|
+
End: 'End',
|
|
174
|
+
// Canvas webtv apps (Lightning/WPE) navigate focus via the D-pad, which they listen
|
|
175
|
+
// for as arrow keys (and Enter for select). Maps the TV remote onto web keyboard.
|
|
176
|
+
'Remote Dpad Up': 'ArrowUp',
|
|
177
|
+
'Remote Dpad Down': 'ArrowDown',
|
|
178
|
+
'Remote Dpad Left': 'ArrowLeft',
|
|
179
|
+
'Remote Dpad Right': 'ArrowRight',
|
|
180
|
+
'Remote Dpad Center': 'Enter',
|
|
181
|
+
};
|
|
182
|
+
const webKey = WEB_KEY_MAP[matched];
|
|
183
|
+
if (webKey) {
|
|
184
|
+
await driver.pressKey(webKey);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else if (driver instanceof vega_js_1.VegaDriver) {
|
|
188
|
+
const vegaButton = VEGA_REMOTE_BUTTONS[matched];
|
|
189
|
+
if (vegaButton) {
|
|
190
|
+
await driver.pressButton(vegaButton);
|
|
191
|
+
}
|
|
192
|
+
// Keys not mapped on vega are silently ignored
|
|
193
|
+
}
|
|
194
|
+
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
195
|
+
const code = exports.ANDROID_KEYCODE[matched];
|
|
196
|
+
if (code !== undefined) {
|
|
197
|
+
await driver.pressKeyEvent(code);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
127
201
|
async function pressKey(key, opts = {}, sessionName = 'default', flags = {}) {
|
|
128
202
|
// A held press is requested via --long-press (default 1.5s) or an explicit --duration.
|
|
129
203
|
const holdSeconds = flags.duration ?? (flags.longPress ? 1.5 : undefined);
|
|
@@ -136,67 +210,39 @@ async function pressKey(key, opts = {}, sessionName = 'default', flags = {}) {
|
|
|
136
210
|
(0, output_js_1.printError)(`Unknown key "${key}". Valid keys: ${exports.VALID_KEYS.join(', ')}`, opts);
|
|
137
211
|
return 1;
|
|
138
212
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
else if (iosButton) {
|
|
148
|
-
await driver.pressButton(iosButton, holdSeconds);
|
|
149
|
-
}
|
|
150
|
-
// Keys not mapped on tvOS are silently ignored
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
const iosKey = IOS_KEY_MAP[matched];
|
|
154
|
-
const iosButton = IOS_BUTTON_MAP[matched];
|
|
155
|
-
if (iosKey) {
|
|
156
|
-
await driver.pressKey(iosKey);
|
|
157
|
-
}
|
|
158
|
-
else if (iosButton) {
|
|
159
|
-
await driver.pressButton(iosButton);
|
|
160
|
-
}
|
|
161
|
-
// Keys not mapped on iOS (e.g. Back, VolumeUp) are silently ignored
|
|
213
|
+
if (flags.measure) {
|
|
214
|
+
const sequence = [matched];
|
|
215
|
+
for (const raw of flags.sequence ?? []) {
|
|
216
|
+
const k = exports.VALID_KEYS.find((v) => v.toLowerCase() === raw.trim().toLowerCase());
|
|
217
|
+
if (!k) {
|
|
218
|
+
(0, output_js_1.printError)(`Unknown key "${raw}" in --sequence. Valid keys: ${exports.VALID_KEYS.join(', ')}`, opts);
|
|
219
|
+
return 1;
|
|
162
220
|
}
|
|
221
|
+
sequence.push(k);
|
|
163
222
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
};
|
|
181
|
-
const webKey = WEB_KEY_MAP[matched];
|
|
182
|
-
if (webKey) {
|
|
183
|
-
await driver.pressKey(webKey);
|
|
184
|
-
}
|
|
223
|
+
const measureOpts = {
|
|
224
|
+
repeat: flags.repeat ?? 1,
|
|
225
|
+
timeoutMs: flags.timeoutMs ?? 3000,
|
|
226
|
+
pollIntervalMs: flags.pollIntervalMs ?? 0,
|
|
227
|
+
settleMs: flags.settleMs ?? 700,
|
|
228
|
+
appId: flags.appId,
|
|
229
|
+
holdSeconds,
|
|
230
|
+
};
|
|
231
|
+
try {
|
|
232
|
+
const report = await (0, input_latency_js_1.measureKeyLatency)(sessionName, sequence, measureOpts);
|
|
233
|
+
if (opts.json)
|
|
234
|
+
(0, output_js_1.printData)({ status: 'ok', ...report }, opts);
|
|
235
|
+
else
|
|
236
|
+
(0, input_latency_js_1.printLatencyReport)(report);
|
|
237
|
+
// Nothing measurable at all is a failure; boundary refusals are not.
|
|
238
|
+
return report.outcomes.moved === 0 && !report.pressToFrame ? 1 : 0;
|
|
185
239
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
await driver.pressButton(vegaButton);
|
|
190
|
-
}
|
|
191
|
-
// Keys not mapped on vega are silently ignored
|
|
240
|
+
catch (err) {
|
|
241
|
+
(0, output_js_1.printError)(`press-key ${matched} --measure — ${err instanceof Error ? err.message : String(err)}`, opts);
|
|
242
|
+
return 1;
|
|
192
243
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (code !== undefined) {
|
|
196
|
-
await driver.pressKeyEvent(code);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}, sessionName);
|
|
244
|
+
}
|
|
245
|
+
const result = await (0, runner_js_1.runDirect)((driver) => dispatchKey(driver, matched, holdSeconds), sessionName);
|
|
200
246
|
if (result.success) {
|
|
201
247
|
(0, output_js_1.printSuccess)(`press-key ${matched} — done`, opts);
|
|
202
248
|
return 0;
|