@houwert/conductor 0.21.0 → 0.23.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/dist/commands/assert-not-visible.js +3 -1
- package/dist/commands/assert-visible.js +3 -1
- package/dist/commands/back.js +4 -0
- package/dist/commands/capture-ui.js +4 -2
- package/dist/commands/clipboard.js +9 -0
- package/dist/commands/crashes.js +5 -0
- package/dist/commands/delete-device.js +6 -1
- package/dist/commands/download-app.js +4 -0
- package/dist/commands/erase-text.js +2 -1
- package/dist/commands/focused.js +3 -1
- package/dist/commands/foreground-app.js +2 -1
- package/dist/commands/gestures.js +7 -0
- package/dist/commands/hide-keyboard.js +4 -0
- package/dist/commands/inspect.js +4 -3
- package/dist/commands/install-app.js +11 -0
- package/dist/commands/launch-app.js +6 -0
- package/dist/commands/list-apps.js +5 -0
- package/dist/commands/list-devices.js +16 -0
- package/dist/commands/memory.js +5 -0
- package/dist/commands/press-key.js +32 -4
- package/dist/commands/profile.js +5 -0
- package/dist/commands/screenshot.js +3 -1
- package/dist/commands/scroll-until-visible.js +3 -1
- package/dist/commands/scroll.js +2 -1
- package/dist/commands/start-device.js +60 -3
- package/dist/commands/stop-app.js +4 -0
- package/dist/commands/stop-device.js +8 -3
- package/dist/commands/swipe.js +2 -1
- package/dist/commands/tap.js +9 -6
- package/dist/commands/uninstall-app.js +4 -0
- package/dist/commands/web-targets.js +84 -0
- package/dist/commands/workspace.js +4 -1
- package/dist/daemon/log-collector.js +9 -1
- package/dist/daemon/server.js +70 -50
- package/dist/drivers/bootstrap.js +12 -0
- package/dist/drivers/flow-runner.js +38 -8
- package/dist/drivers/ios.js +5 -2
- package/dist/drivers/log-sources/metro-discovery.js +56 -2
- package/dist/drivers/log-sources/vega.js +77 -0
- package/dist/drivers/metro-cdp.js +11 -3
- package/dist/drivers/vega/automation-client.js +79 -0
- package/dist/drivers/vega/cli.js +199 -0
- package/dist/drivers/vega/connection.js +48 -0
- package/dist/drivers/vega/input.js +100 -0
- package/dist/drivers/vega/page-source-parser.js +229 -0
- package/dist/drivers/vega.js +165 -0
- package/dist/enum-options.js +15 -3
- package/dist/index.js +46 -2
- package/dist/runner.js +31 -0
- package/package.json +1 -1
- package/skills/conductor-device-interact/SKILL.md +4 -3
- package/skills/conductor-device-setup/SKILL.md +59 -28
|
@@ -11,6 +11,7 @@ const output_js_1 = require("../output.js");
|
|
|
11
11
|
const ios_js_1 = require("../drivers/ios.js");
|
|
12
12
|
const android_js_1 = require("../drivers/android.js");
|
|
13
13
|
const web_js_1 = require("../drivers/web.js");
|
|
14
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
14
15
|
const wait_js_1 = require("../drivers/wait.js");
|
|
15
16
|
async function assertNotVisible(element, opts = {}, sessionName = 'default', flags = {}) {
|
|
16
17
|
if (!element && !flags.id && !flags.text) {
|
|
@@ -42,7 +43,8 @@ async function assertNotVisible(element, opts = {}, sessionName = 'default', fla
|
|
|
42
43
|
else if (driver instanceof web_js_1.WebDriver) {
|
|
43
44
|
await (0, wait_js_1.waitUntilWebElementGone)(() => driver.viewHierarchy(), sel, flags.timeout);
|
|
44
45
|
}
|
|
45
|
-
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
46
|
+
else if (driver instanceof android_js_1.AndroidDriver || driver instanceof vega_js_1.VegaDriver) {
|
|
47
|
+
// Vega emits uiautomator-style XML, so it reuses the Android resolver.
|
|
46
48
|
await (0, wait_js_1.waitUntilAndroidElementGone)(() => driver.viewHierarchy(), sel, flags.timeout);
|
|
47
49
|
}
|
|
48
50
|
(0, output_js_1.printSuccess)(`assert-not-visible ${label} — element not found`, opts);
|
|
@@ -21,6 +21,7 @@ const output_js_1 = require("../output.js");
|
|
|
21
21
|
const ios_js_1 = require("../drivers/ios.js");
|
|
22
22
|
const android_js_1 = require("../drivers/android.js");
|
|
23
23
|
const web_js_1 = require("../drivers/web.js");
|
|
24
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
24
25
|
const wait_js_1 = require("../drivers/wait.js");
|
|
25
26
|
const direct_ios_selector_js_1 = require("../drivers/direct-ios-selector.js");
|
|
26
27
|
async function assertVisible(element, opts = {}, sessionName = 'default', flags = {}) {
|
|
@@ -55,7 +56,8 @@ async function assertVisible(element, opts = {}, sessionName = 'default', flags
|
|
|
55
56
|
else if (driver instanceof web_js_1.WebDriver) {
|
|
56
57
|
return (0, wait_js_1.waitForWebElement)(() => driver.viewHierarchy(), sel, timeoutMs);
|
|
57
58
|
}
|
|
58
|
-
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
59
|
+
else if (driver instanceof android_js_1.AndroidDriver || driver instanceof vega_js_1.VegaDriver) {
|
|
60
|
+
// Vega emits uiautomator-style XML, so it reuses the Android resolver.
|
|
59
61
|
return (0, wait_js_1.waitForAndroidElement)(() => driver.viewHierarchy(), sel, timeoutMs);
|
|
60
62
|
}
|
|
61
63
|
};
|
package/dist/commands/back.js
CHANGED
|
@@ -8,6 +8,7 @@ const output_js_1 = require("../output.js");
|
|
|
8
8
|
const ios_js_1 = require("../drivers/ios.js");
|
|
9
9
|
const android_js_1 = require("../drivers/android.js");
|
|
10
10
|
const web_js_1 = require("../drivers/web.js");
|
|
11
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
11
12
|
async function back(opts = {}, sessionName = 'default') {
|
|
12
13
|
const result = await (0, runner_js_1.runDirect)(async (driver) => {
|
|
13
14
|
if (driver instanceof ios_js_1.IOSDriver) {
|
|
@@ -19,6 +20,9 @@ async function back(opts = {}, sessionName = 'default') {
|
|
|
19
20
|
else if (driver instanceof web_js_1.WebDriver) {
|
|
20
21
|
await driver.goBack();
|
|
21
22
|
}
|
|
23
|
+
else if (driver instanceof vega_js_1.VegaDriver) {
|
|
24
|
+
await driver.back();
|
|
25
|
+
}
|
|
22
26
|
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
23
27
|
await driver.back(); // adb shell input keyevent 4
|
|
24
28
|
}
|
|
@@ -13,6 +13,7 @@ const output_js_1 = require("../output.js");
|
|
|
13
13
|
const ios_js_1 = require("../drivers/ios.js");
|
|
14
14
|
const android_js_1 = require("../drivers/android.js");
|
|
15
15
|
const web_js_1 = require("../drivers/web.js");
|
|
16
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
16
17
|
const a11y_js_1 = require("../drivers/a11y.js");
|
|
17
18
|
const snapshot_store_js_1 = require("../snapshot-store.js");
|
|
18
19
|
async function captureUI(outputPath, opts = {}, sessionName = 'default') {
|
|
@@ -66,8 +67,9 @@ async function captureUI(outputPath, opts = {}, sessionName = 'default') {
|
|
|
66
67
|
a11ySnapshot = built.a11ySnapshot;
|
|
67
68
|
screenshotBuf = shot;
|
|
68
69
|
}
|
|
69
|
-
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
70
|
-
|
|
70
|
+
else if (driver instanceof android_js_1.AndroidDriver || driver instanceof vega_js_1.VegaDriver) {
|
|
71
|
+
// Vega emits uiautomator-style XML, so it reuses the Android a11y builder.
|
|
72
|
+
platform = driver instanceof vega_js_1.VegaDriver ? 'vega' : 'android';
|
|
71
73
|
const [info, xml, shot] = await Promise.all([
|
|
72
74
|
driver.deviceInfo(),
|
|
73
75
|
driver.viewHierarchy(),
|
|
@@ -12,6 +12,7 @@ const output_js_1 = require("../output.js");
|
|
|
12
12
|
const ios_js_1 = require("../drivers/ios.js");
|
|
13
13
|
const android_js_1 = require("../drivers/android.js");
|
|
14
14
|
const web_js_1 = require("../drivers/web.js");
|
|
15
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
15
16
|
const ANDROID_MSG = 'clipboard is iOS-only. On Android, use `conductor input-text` to type instead.';
|
|
16
17
|
async function clipboardRead(opts = {}, sessionName = 'default') {
|
|
17
18
|
const result = await (0, runner_js_1.runDirect)(async (driver) => {
|
|
@@ -21,6 +22,8 @@ async function clipboardRead(opts = {}, sessionName = 'default') {
|
|
|
21
22
|
throw new Error(ANDROID_MSG);
|
|
22
23
|
if (driver instanceof web_js_1.WebDriver)
|
|
23
24
|
throw new Error('clipboard read is not supported on Web');
|
|
25
|
+
if (driver instanceof vega_js_1.VegaDriver)
|
|
26
|
+
throw new Error('clipboard read is not supported on vega');
|
|
24
27
|
return '';
|
|
25
28
|
}, sessionName);
|
|
26
29
|
if (result.success) {
|
|
@@ -46,6 +49,9 @@ async function clipboardWrite(text, opts = {}, sessionName = 'default') {
|
|
|
46
49
|
else if (driver instanceof web_js_1.WebDriver) {
|
|
47
50
|
throw new Error('clipboard write is not supported on Web');
|
|
48
51
|
}
|
|
52
|
+
else if (driver instanceof vega_js_1.VegaDriver) {
|
|
53
|
+
throw new Error('clipboard write is not supported on vega');
|
|
54
|
+
}
|
|
49
55
|
}, sessionName);
|
|
50
56
|
if (result.success) {
|
|
51
57
|
(0, output_js_1.printSuccess)('clipboard write — done', opts);
|
|
@@ -72,6 +78,9 @@ async function paste(opts = {}, sessionName = 'default') {
|
|
|
72
78
|
else if (driver instanceof web_js_1.WebDriver) {
|
|
73
79
|
throw new Error('paste is not supported on Web');
|
|
74
80
|
}
|
|
81
|
+
else if (driver instanceof vega_js_1.VegaDriver) {
|
|
82
|
+
throw new Error('paste is not supported on vega');
|
|
83
|
+
}
|
|
75
84
|
}, sessionName);
|
|
76
85
|
if (result.success) {
|
|
77
86
|
(0, output_js_1.printSuccess)('paste — done', opts);
|
package/dist/commands/crashes.js
CHANGED
|
@@ -175,6 +175,11 @@ async function crashesList(opts, sessionName, listOpts) {
|
|
|
175
175
|
if (platform === 'android' && sessionName !== 'default') {
|
|
176
176
|
reports.push(...(await listAndroidReports(sessionName, { app: listOpts.app, sinceMs })));
|
|
177
177
|
}
|
|
178
|
+
else if (platform === 'vega') {
|
|
179
|
+
// No crash-log retrieval channel on Vega in v1.
|
|
180
|
+
if (!opts.json)
|
|
181
|
+
console.error('note: crash reports are not supported on vega (Amazon Fire TV)');
|
|
182
|
+
}
|
|
178
183
|
if (opts.json) {
|
|
179
184
|
(0, output_js_1.printData)({ count: reports.length, reports }, opts);
|
|
180
185
|
}
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.HELP = void 0;
|
|
7
7
|
exports.deleteDevice = deleteDevice;
|
|
8
8
|
exports.HELP = ` delete-device <name-or-id>
|
|
9
|
-
--platform <ios|tvos|android|web> Scope to a single platform
|
|
9
|
+
--platform <ios|tvos|android|web> Scope to a single platform (Vega VVDs are managed by Amazon's tooling)
|
|
10
10
|
--all Delete all shutdown simulators / non-running AVDs / web sessions`;
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
12
|
const runner_js_1 = require("../runner.js");
|
|
@@ -105,6 +105,11 @@ async function deleteDevice(nameOrId, opts, flags) {
|
|
|
105
105
|
return 1;
|
|
106
106
|
}
|
|
107
107
|
const platform = flags.platform?.toLowerCase();
|
|
108
|
+
// Vega VVD lifecycle is owned by Amazon's tooling — we can't delete one.
|
|
109
|
+
if (platform === 'vega' || (nameOrId && nameOrId.startsWith('vega:'))) {
|
|
110
|
+
(0, output_js_1.printError)("delete-device is not supported on vega (Amazon Fire TV) — manage VVDs via Amazon's tooling.", opts);
|
|
111
|
+
return 1;
|
|
112
|
+
}
|
|
108
113
|
const includeIOS = !platform || platform === 'ios';
|
|
109
114
|
const includeTvOS = !platform || platform === 'tvos';
|
|
110
115
|
const includeAndroid = !platform || platform === 'android';
|
|
@@ -33,6 +33,10 @@ async function downloadApp(appId, output, opts = {}, sessionName = 'default') {
|
|
|
33
33
|
(0, output_js_1.printError)('download-app is not supported on web.', opts);
|
|
34
34
|
return 1;
|
|
35
35
|
}
|
|
36
|
+
if (platform === 'vega') {
|
|
37
|
+
(0, output_js_1.printError)('download-app is not supported on vega (Amazon Fire TV).', opts);
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
36
40
|
if (platform === 'ios' || platform === 'tvos') {
|
|
37
41
|
// Get the .app bundle path from the simulator
|
|
38
42
|
const getPath = await (0, runner_js_1.spawnCommand)('xcrun', [
|
|
@@ -8,9 +8,10 @@ const output_js_1 = require("../output.js");
|
|
|
8
8
|
const ios_js_1 = require("../drivers/ios.js");
|
|
9
9
|
const android_js_1 = require("../drivers/android.js");
|
|
10
10
|
const web_js_1 = require("../drivers/web.js");
|
|
11
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
11
12
|
async function eraseText(characters, opts = {}, sessionName = 'default') {
|
|
12
13
|
const result = await (0, runner_js_1.runDirect)(async (driver) => {
|
|
13
|
-
if (driver instanceof android_js_1.AndroidDriver) {
|
|
14
|
+
if (driver instanceof android_js_1.AndroidDriver || driver instanceof vega_js_1.VegaDriver) {
|
|
14
15
|
await driver.eraseAllText(characters);
|
|
15
16
|
}
|
|
16
17
|
else if (driver instanceof web_js_1.WebDriver) {
|
package/dist/commands/focused.js
CHANGED
|
@@ -9,6 +9,7 @@ const output_js_1 = require("../output.js");
|
|
|
9
9
|
const ios_js_1 = require("../drivers/ios.js");
|
|
10
10
|
const android_js_1 = require("../drivers/android.js");
|
|
11
11
|
const web_js_1 = require("../drivers/web.js");
|
|
12
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
12
13
|
const element_resolver_js_1 = require("../drivers/element-resolver.js");
|
|
13
14
|
// XCUIElementType rawValue → human-readable name.
|
|
14
15
|
// Source: XCUIElementType enum in XCTest framework.
|
|
@@ -206,7 +207,8 @@ async function queryFocused(driver) {
|
|
|
206
207
|
const node = findFocusedWeb(hierarchy.elements);
|
|
207
208
|
return node ? formatWebElement(node) : null;
|
|
208
209
|
}
|
|
209
|
-
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
210
|
+
else if (driver instanceof android_js_1.AndroidDriver || driver instanceof vega_js_1.VegaDriver) {
|
|
211
|
+
// Vega emits uiautomator-style XML, so it reuses the Android parser.
|
|
210
212
|
const xml = await driver.viewHierarchy();
|
|
211
213
|
const nodes = (0, element_resolver_js_1.parseAndroidHierarchy)(xml);
|
|
212
214
|
const node = nodes.find((n) => n.focused);
|
|
@@ -9,6 +9,7 @@ const output_js_1 = require("../output.js");
|
|
|
9
9
|
const ios_js_1 = require("../drivers/ios.js");
|
|
10
10
|
const android_js_1 = require("../drivers/android.js");
|
|
11
11
|
const web_js_1 = require("../drivers/web.js");
|
|
12
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
12
13
|
async function resolveDeviceId(sessionName) {
|
|
13
14
|
if (sessionName !== 'default')
|
|
14
15
|
return sessionName;
|
|
@@ -40,7 +41,7 @@ async function foregroundApp(opts = {}, sessionName = 'default') {
|
|
|
40
41
|
else if (driver instanceof web_js_1.WebDriver) {
|
|
41
42
|
appId = await driver.runningApp();
|
|
42
43
|
}
|
|
43
|
-
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
44
|
+
else if (driver instanceof android_js_1.AndroidDriver || driver instanceof vega_js_1.VegaDriver) {
|
|
44
45
|
appId = await driver.getForegroundApp();
|
|
45
46
|
}
|
|
46
47
|
else {
|
|
@@ -19,6 +19,7 @@ const runner_js_1 = require("../runner.js");
|
|
|
19
19
|
const output_js_1 = require("../output.js");
|
|
20
20
|
const ios_js_1 = require("../drivers/ios.js");
|
|
21
21
|
const web_js_1 = require("../drivers/web.js");
|
|
22
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
22
23
|
function parseCenter(s) {
|
|
23
24
|
if (!s)
|
|
24
25
|
return null;
|
|
@@ -108,6 +109,8 @@ async function pinch(opts, sessionName, pinchOpts) {
|
|
|
108
109
|
const result = await (0, runner_js_1.runDirect)(async (driver) => {
|
|
109
110
|
if (driver instanceof web_js_1.WebDriver)
|
|
110
111
|
throw new Error('pinch is not supported on Web');
|
|
112
|
+
if (driver instanceof vega_js_1.VegaDriver)
|
|
113
|
+
throw new Error('pinch is not supported on vega (Amazon Fire TV)');
|
|
111
114
|
const { width, height, cx: defCx, cy: defCy } = await screenCenter(driver);
|
|
112
115
|
const center = parseCenter(pinchOpts.center) ?? { x: defCx, y: defCy };
|
|
113
116
|
// Start span is half the shorter screen dimension; end is scaled.
|
|
@@ -130,6 +133,8 @@ async function rotateGesture(opts, sessionName, rotateOpts) {
|
|
|
130
133
|
const result = await (0, runner_js_1.runDirect)(async (driver) => {
|
|
131
134
|
if (driver instanceof web_js_1.WebDriver)
|
|
132
135
|
throw new Error('rotate-gesture is not supported on Web');
|
|
136
|
+
if (driver instanceof vega_js_1.VegaDriver)
|
|
137
|
+
throw new Error('rotate-gesture is not supported on vega (Amazon Fire TV)');
|
|
133
138
|
const { width, height, cx: defCx, cy: defCy } = await screenCenter(driver);
|
|
134
139
|
const center = parseCenter(rotateOpts.center) ?? { x: defCx, y: defCy };
|
|
135
140
|
const radius = Math.min(width, height) * 0.25;
|
|
@@ -185,6 +190,8 @@ async function gesture(rawJson, filePath, opts, sessionName) {
|
|
|
185
190
|
const result = await (0, runner_js_1.runDirect)(async (driver) => {
|
|
186
191
|
if (driver instanceof web_js_1.WebDriver)
|
|
187
192
|
throw new Error('gesture is not supported on Web');
|
|
193
|
+
if (driver instanceof vega_js_1.VegaDriver)
|
|
194
|
+
throw new Error('gesture is not supported on vega (Amazon Fire TV)');
|
|
188
195
|
await playPaths(driver, paths);
|
|
189
196
|
}, sessionName);
|
|
190
197
|
if (result.success) {
|
|
@@ -8,6 +8,7 @@ const output_js_1 = require("../output.js");
|
|
|
8
8
|
const ios_js_1 = require("../drivers/ios.js");
|
|
9
9
|
const android_js_1 = require("../drivers/android.js");
|
|
10
10
|
const web_js_1 = require("../drivers/web.js");
|
|
11
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
11
12
|
async function hideKeyboard(opts = {}, sessionName = 'default') {
|
|
12
13
|
const result = await (0, runner_js_1.runDirect)(async (driver) => {
|
|
13
14
|
if (driver instanceof ios_js_1.IOSDriver) {
|
|
@@ -18,6 +19,9 @@ async function hideKeyboard(opts = {}, sessionName = 'default') {
|
|
|
18
19
|
else if (driver instanceof web_js_1.WebDriver) {
|
|
19
20
|
// No virtual keyboard on web — no-op
|
|
20
21
|
}
|
|
22
|
+
else if (driver instanceof vega_js_1.VegaDriver) {
|
|
23
|
+
// Vega has no keyboard-hide primitive — no-op
|
|
24
|
+
}
|
|
21
25
|
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
22
26
|
await driver.pressKeyEvent(111); // KEYCODE_ESCAPE
|
|
23
27
|
}
|
package/dist/commands/inspect.js
CHANGED
|
@@ -9,6 +9,7 @@ const output_js_1 = require("../output.js");
|
|
|
9
9
|
const ios_js_1 = require("../drivers/ios.js");
|
|
10
10
|
const android_js_1 = require("../drivers/android.js");
|
|
11
11
|
const web_js_1 = require("../drivers/web.js");
|
|
12
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
12
13
|
const element_resolver_js_1 = require("../drivers/element-resolver.js");
|
|
13
14
|
const a11y_js_1 = require("../drivers/a11y.js");
|
|
14
15
|
async function inspect(opts = {}, sessionName = 'default', inspectOpts = {}) {
|
|
@@ -28,7 +29,7 @@ async function inspect(opts = {}, sessionName = 'default', inspectOpts = {}) {
|
|
|
28
29
|
const hierarchy = await driver.viewHierarchy(false);
|
|
29
30
|
hit = (0, element_resolver_js_1.findIOSViewAtPoint)(hierarchy.axElement, x, y, tappable);
|
|
30
31
|
}
|
|
31
|
-
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
32
|
+
else if (driver instanceof android_js_1.AndroidDriver || driver instanceof vega_js_1.VegaDriver) {
|
|
32
33
|
const xml = await driver.viewHierarchy();
|
|
33
34
|
hit = (0, element_resolver_js_1.findAndroidViewAtPoint)(xml, x, y, tappable);
|
|
34
35
|
}
|
|
@@ -67,7 +68,7 @@ async function inspect(opts = {}, sessionName = 'default', inspectOpts = {}) {
|
|
|
67
68
|
const built = (0, a11y_js_1.buildWebA11y)(vh);
|
|
68
69
|
raw = JSON.stringify({ ...vh, elements: built.hierarchy }, null, 2);
|
|
69
70
|
}
|
|
70
|
-
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
71
|
+
else if (driver instanceof android_js_1.AndroidDriver || driver instanceof vega_js_1.VegaDriver) {
|
|
71
72
|
const xml = await driver.viewHierarchy();
|
|
72
73
|
const built = (0, a11y_js_1.buildAndroidA11y)(xml);
|
|
73
74
|
raw = JSON.stringify(built.hierarchy, null, 2);
|
|
@@ -92,7 +93,7 @@ async function inspect(opts = {}, sessionName = 'default', inspectOpts = {}) {
|
|
|
92
93
|
const hierarchy = await driver.viewHierarchy();
|
|
93
94
|
text = (0, element_resolver_js_1.inspectWebToText)(hierarchy);
|
|
94
95
|
}
|
|
95
|
-
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
96
|
+
else if (driver instanceof android_js_1.AndroidDriver || driver instanceof vega_js_1.VegaDriver) {
|
|
96
97
|
const xml = await driver.viewHierarchy();
|
|
97
98
|
text = (0, element_resolver_js_1.inspectAndroidToText)(xml);
|
|
98
99
|
}
|
|
@@ -8,6 +8,7 @@ const sdk_js_1 = require("../android/sdk.js");
|
|
|
8
8
|
const session_js_1 = require("../session.js");
|
|
9
9
|
const output_js_1 = require("../output.js");
|
|
10
10
|
const bootstrap_js_1 = require("../drivers/bootstrap.js");
|
|
11
|
+
const cli_js_1 = require("../drivers/vega/cli.js");
|
|
11
12
|
async function resolveDeviceId(sessionName) {
|
|
12
13
|
if (sessionName !== 'default')
|
|
13
14
|
return sessionName;
|
|
@@ -29,6 +30,16 @@ async function installApp(appPath, opts = {}, sessionName = 'default') {
|
|
|
29
30
|
(0, output_js_1.printError)('install-app is not supported on web. Use launch-app with a URL instead.', opts);
|
|
30
31
|
return 1;
|
|
31
32
|
}
|
|
33
|
+
else if (platform === 'vega') {
|
|
34
|
+
// Vega installs a `.vpkg` through Amazon's CLI; strip the `vega:` id prefix.
|
|
35
|
+
try {
|
|
36
|
+
await new cli_js_1.VegaCli(deviceId.replace(/^vega:/, '')).installApp(appPath);
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
(0, output_js_1.printError)(`install-app failed: ${e instanceof Error ? e.message : String(e)}`, opts);
|
|
40
|
+
return 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
32
43
|
else if (platform === 'ios' || platform === 'tvos') {
|
|
33
44
|
const result = await (0, runner_js_1.spawnCommand)('xcrun', ['simctl', 'install', deviceId, appPath]);
|
|
34
45
|
if (!result.success) {
|
|
@@ -19,6 +19,7 @@ const output_js_1 = require("../output.js");
|
|
|
19
19
|
const ios_js_1 = require("../drivers/ios.js");
|
|
20
20
|
const android_js_1 = require("../drivers/android.js");
|
|
21
21
|
const web_js_1 = require("../drivers/web.js");
|
|
22
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
22
23
|
async function launchApp(appId, deviceId, opts = {}, sessionName = 'default', flags = {}) {
|
|
23
24
|
if (!appId) {
|
|
24
25
|
(0, output_js_1.printError)('launch-app requires <appId>', opts);
|
|
@@ -40,6 +41,8 @@ async function launchApp(appId, deviceId, opts = {}, sessionName = 'default', fl
|
|
|
40
41
|
await driver.terminateApp(appId);
|
|
41
42
|
else if (driver instanceof web_js_1.WebDriver)
|
|
42
43
|
await driver.terminateApp();
|
|
44
|
+
else if (driver instanceof vega_js_1.VegaDriver)
|
|
45
|
+
await driver.terminateApp(appId);
|
|
43
46
|
else if (driver instanceof android_js_1.AndroidDriver)
|
|
44
47
|
await driver.stopApp(appId);
|
|
45
48
|
}
|
|
@@ -49,6 +52,9 @@ async function launchApp(appId, deviceId, opts = {}, sessionName = 'default', fl
|
|
|
49
52
|
else if (driver instanceof web_js_1.WebDriver) {
|
|
50
53
|
await driver.launchApp(appId);
|
|
51
54
|
}
|
|
55
|
+
else if (driver instanceof vega_js_1.VegaDriver) {
|
|
56
|
+
await driver.launchApp(appId, flags.launchArgs);
|
|
57
|
+
}
|
|
52
58
|
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
53
59
|
await driver.launchApp(appId, flags.launchArgs);
|
|
54
60
|
}
|
|
@@ -8,6 +8,7 @@ const sdk_js_1 = require("../android/sdk.js");
|
|
|
8
8
|
const session_js_1 = require("../session.js");
|
|
9
9
|
const output_js_1 = require("../output.js");
|
|
10
10
|
const bootstrap_js_1 = require("../drivers/bootstrap.js");
|
|
11
|
+
const cli_js_1 = require("../drivers/vega/cli.js");
|
|
11
12
|
async function resolveDeviceId(sessionName) {
|
|
12
13
|
if (sessionName !== 'default')
|
|
13
14
|
return sessionName;
|
|
@@ -26,6 +27,10 @@ async function listApps(opts = {}, sessionName = 'default') {
|
|
|
26
27
|
(0, output_js_1.printError)('list-apps is not supported on web. Use foreground-app to get the current URL.', opts);
|
|
27
28
|
return 1;
|
|
28
29
|
}
|
|
30
|
+
else if (platform === 'vega') {
|
|
31
|
+
// Vega is driven through Amazon's CLI, not adb; strip the `vega:` id prefix.
|
|
32
|
+
appIds = (await new cli_js_1.VegaCli(deviceId.replace(/^vega:/, '')).listInstalledApps()).sort();
|
|
33
|
+
}
|
|
29
34
|
else if (platform === 'ios' || platform === 'tvos') {
|
|
30
35
|
const result = await (0, runner_js_1.spawnCommand)('bash', [
|
|
31
36
|
'-c',
|
|
@@ -15,6 +15,7 @@ const output_js_1 = require("../output.js");
|
|
|
15
15
|
const bootstrap_js_1 = require("../drivers/bootstrap.js");
|
|
16
16
|
const client_js_1 = require("../daemon/client.js");
|
|
17
17
|
const protocol_js_1 = require("../daemon/protocol.js");
|
|
18
|
+
const cli_js_1 = require("../drivers/vega/cli.js");
|
|
18
19
|
// Captured during discoverAvailableDevices so listDevices() can report it.
|
|
19
20
|
// Module-scoped because the discover function returns Device[]; bolting an
|
|
20
21
|
// extra return field onto the public type would ripple beyond this fix.
|
|
@@ -88,6 +89,21 @@ async function discoverBootedDevices() {
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
}
|
|
92
|
+
// Vega (Amazon Fire TV): booted devices reported by the vega CLI. Best-effort —
|
|
93
|
+
// the CLI is absent unless the Vega SDK is installed.
|
|
94
|
+
try {
|
|
95
|
+
for (const d of await new cli_js_1.VegaCli().listDevices()) {
|
|
96
|
+
devices.push({
|
|
97
|
+
id: `vega:${d.serial}`,
|
|
98
|
+
name: d.description,
|
|
99
|
+
platform: 'vega',
|
|
100
|
+
status: 'booted',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
/* vega CLI not installed */
|
|
106
|
+
}
|
|
91
107
|
return devices;
|
|
92
108
|
}
|
|
93
109
|
async function discoverAvailableDevices() {
|
package/dist/commands/memory.js
CHANGED
|
@@ -1116,6 +1116,11 @@ async function memory(appIdArg, opts = {}, sessionName = 'default', memOpts = {}
|
|
|
1116
1116
|
return 1;
|
|
1117
1117
|
}
|
|
1118
1118
|
const platform = await (0, bootstrap_js_1.detectPlatform)(deviceId);
|
|
1119
|
+
if (platform === 'vega') {
|
|
1120
|
+
// Memory relies on adb/simctl host tooling that has no Vega equivalent yet.
|
|
1121
|
+
(0, output_js_1.printError)('memory is not yet supported on vega (Amazon Fire TV)', opts);
|
|
1122
|
+
return 1;
|
|
1123
|
+
}
|
|
1119
1124
|
let report;
|
|
1120
1125
|
if (platform === 'web') {
|
|
1121
1126
|
report = await collectWeb(deviceId, sessionName, memOpts);
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.VALID_KEYS = exports.HELP = void 0;
|
|
4
4
|
exports.pressKey = pressKey;
|
|
5
|
-
exports.HELP = ` press-key <key> Press a key (Enter, Backspace, Home, ...)
|
|
5
|
+
exports.HELP = ` press-key <key> Press a key (Enter, Backspace, Home, ...)
|
|
6
|
+
--long-press Hold the button ~1.5s (tvOS remote buttons)
|
|
7
|
+
--duration <seconds> Hold for a custom duration (tvOS remote buttons)`;
|
|
6
8
|
const runner_js_1 = require("../runner.js");
|
|
7
9
|
const output_js_1 = require("../output.js");
|
|
8
10
|
const ios_js_1 = require("../drivers/ios.js");
|
|
9
11
|
const android_js_1 = require("../drivers/android.js");
|
|
10
12
|
const web_js_1 = require("../drivers/web.js");
|
|
13
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
11
14
|
exports.VALID_KEYS = [
|
|
12
15
|
'Enter',
|
|
13
16
|
'Backspace',
|
|
@@ -67,6 +70,22 @@ const TVOS_REMOTE_BUTTONS = {
|
|
|
67
70
|
'Remote Menu': 'menu',
|
|
68
71
|
'Remote Media Play Pause': 'playPause',
|
|
69
72
|
};
|
|
73
|
+
// vega (Amazon Fire TV) remote: map key names to VegaDriver button values.
|
|
74
|
+
const VEGA_REMOTE_BUTTONS = {
|
|
75
|
+
'Remote Dpad Up': 'up',
|
|
76
|
+
'Remote Dpad Down': 'down',
|
|
77
|
+
'Remote Dpad Left': 'left',
|
|
78
|
+
'Remote Dpad Right': 'right',
|
|
79
|
+
'Remote Dpad Center': 'select',
|
|
80
|
+
'Remote Menu': 'menu',
|
|
81
|
+
'Remote Media Play Pause': 'playPause',
|
|
82
|
+
'Remote Media Rewind': 'rewind',
|
|
83
|
+
'Remote Media Fast Forward': 'fastForward',
|
|
84
|
+
Home: 'home',
|
|
85
|
+
Back: 'back',
|
|
86
|
+
VolumeUp: 'volumeUp',
|
|
87
|
+
VolumeDown: 'volumeDown',
|
|
88
|
+
};
|
|
70
89
|
// Android keyevent codes
|
|
71
90
|
const ANDROID_KEYCODE = {
|
|
72
91
|
Home: 3,
|
|
@@ -105,7 +124,9 @@ const ANDROID_KEYCODE = {
|
|
|
105
124
|
'TV Input HDMI 2': 244,
|
|
106
125
|
'TV Input HDMI 3': 245,
|
|
107
126
|
};
|
|
108
|
-
async function pressKey(key, opts = {}, sessionName = 'default') {
|
|
127
|
+
async function pressKey(key, opts = {}, sessionName = 'default', flags = {}) {
|
|
128
|
+
// A held press is requested via --long-press (default 1.5s) or an explicit --duration.
|
|
129
|
+
const holdSeconds = flags.duration ?? (flags.longPress ? 1.5 : undefined);
|
|
109
130
|
if (!key) {
|
|
110
131
|
(0, output_js_1.printError)(`press-key requires <key>. Valid keys: ${exports.VALID_KEYS.join(', ')}`, opts);
|
|
111
132
|
return 1;
|
|
@@ -121,10 +142,10 @@ async function pressKey(key, opts = {}, sessionName = 'default') {
|
|
|
121
142
|
const tvosButton = TVOS_REMOTE_BUTTONS[matched];
|
|
122
143
|
const iosButton = IOS_BUTTON_MAP[matched];
|
|
123
144
|
if (tvosButton) {
|
|
124
|
-
await driver.pressButton(tvosButton);
|
|
145
|
+
await driver.pressButton(tvosButton, holdSeconds);
|
|
125
146
|
}
|
|
126
147
|
else if (iosButton) {
|
|
127
|
-
await driver.pressButton(iosButton);
|
|
148
|
+
await driver.pressButton(iosButton, holdSeconds);
|
|
128
149
|
}
|
|
129
150
|
// Keys not mapped on tvOS are silently ignored
|
|
130
151
|
}
|
|
@@ -162,6 +183,13 @@ async function pressKey(key, opts = {}, sessionName = 'default') {
|
|
|
162
183
|
await driver.pressKey(webKey);
|
|
163
184
|
}
|
|
164
185
|
}
|
|
186
|
+
else if (driver instanceof vega_js_1.VegaDriver) {
|
|
187
|
+
const vegaButton = VEGA_REMOTE_BUTTONS[matched];
|
|
188
|
+
if (vegaButton) {
|
|
189
|
+
await driver.pressButton(vegaButton);
|
|
190
|
+
}
|
|
191
|
+
// Keys not mapped on vega are silently ignored
|
|
192
|
+
}
|
|
165
193
|
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
166
194
|
const code = ANDROID_KEYCODE[matched];
|
|
167
195
|
if (code !== undefined) {
|
package/dist/commands/profile.js
CHANGED
|
@@ -99,6 +99,11 @@ async function profileCpu(opts, sessionName, profileOpts) {
|
|
|
99
99
|
else if (platform === 'android') {
|
|
100
100
|
await recordAndroidCpu(sessionName, profileOpts.appId, profileOpts.durationSec, out);
|
|
101
101
|
}
|
|
102
|
+
else if (platform === 'vega') {
|
|
103
|
+
// No native CPU-trace tooling (xctrace/simpleperf) on Vega yet.
|
|
104
|
+
(0, output_js_1.printError)('profile cpu is not yet supported on vega (Amazon Fire TV)', opts);
|
|
105
|
+
return 1;
|
|
106
|
+
}
|
|
102
107
|
else {
|
|
103
108
|
(0, output_js_1.printError)(`profile cpu is not supported on platform ${platform ?? '(unknown)'}`, opts);
|
|
104
109
|
return 1;
|
|
@@ -27,6 +27,7 @@ const output_js_1 = require("../output.js");
|
|
|
27
27
|
const ios_js_1 = require("../drivers/ios.js");
|
|
28
28
|
const android_js_1 = require("../drivers/android.js");
|
|
29
29
|
const web_js_1 = require("../drivers/web.js");
|
|
30
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
30
31
|
const wait_js_1 = require("../drivers/wait.js");
|
|
31
32
|
const direct_ios_selector_js_1 = require("../drivers/direct-ios-selector.js");
|
|
32
33
|
const png_crop_js_1 = require("../png-crop.js");
|
|
@@ -84,7 +85,8 @@ async function screenshot(outputPath, opts = {}, sessionName = 'default', fullPa
|
|
|
84
85
|
hierarchyH = info.heightPixels;
|
|
85
86
|
el = await (0, wait_js_1.waitForWebElement)(() => driver.viewHierarchy(), sel);
|
|
86
87
|
}
|
|
87
|
-
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
88
|
+
else if (driver instanceof android_js_1.AndroidDriver || driver instanceof vega_js_1.VegaDriver) {
|
|
89
|
+
// Vega emits uiautomator-style XML, so it reuses the Android resolver.
|
|
88
90
|
const xml = await driver.viewHierarchy();
|
|
89
91
|
// Android XML root bounds: derive from the first parseable <node bounds="[0,0][W,H]">
|
|
90
92
|
const m = xml.match(/<node[^>]*bounds="\[0,0\]\[(\d+),(\d+)\]"/);
|
|
@@ -12,6 +12,7 @@ const output_js_1 = require("../output.js");
|
|
|
12
12
|
const ios_js_1 = require("../drivers/ios.js");
|
|
13
13
|
const android_js_1 = require("../drivers/android.js");
|
|
14
14
|
const web_js_1 = require("../drivers/web.js");
|
|
15
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
15
16
|
const element_resolver_js_1 = require("../drivers/element-resolver.js");
|
|
16
17
|
const direct_ios_selector_js_1 = require("../drivers/direct-ios-selector.js");
|
|
17
18
|
const utils_js_1 = require("../utils.js");
|
|
@@ -77,7 +78,8 @@ async function scrollUntilVisible(element, opts = {}, sessionName = 'default', f
|
|
|
77
78
|
const { widthPixels: w, heightPixels: h } = info;
|
|
78
79
|
await driver.swipe(coords.startX * w, coords.startY * h, coords.endX * w, coords.endY * h, 500);
|
|
79
80
|
}
|
|
80
|
-
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
81
|
+
else if (driver instanceof android_js_1.AndroidDriver || driver instanceof vega_js_1.VegaDriver) {
|
|
82
|
+
// Vega emits uiautomator-style XML, so it reuses the Android resolver.
|
|
81
83
|
const xml = await driver.viewHierarchy();
|
|
82
84
|
if ((0, element_resolver_js_1.findAndroidElement)(xml, sel)) {
|
|
83
85
|
(0, output_js_1.printSuccess)(`scroll-until-visible ${label} — found`, opts);
|
package/dist/commands/scroll.js
CHANGED
|
@@ -8,6 +8,7 @@ const output_js_1 = require("../output.js");
|
|
|
8
8
|
const ios_js_1 = require("../drivers/ios.js");
|
|
9
9
|
const android_js_1 = require("../drivers/android.js");
|
|
10
10
|
const web_js_1 = require("../drivers/web.js");
|
|
11
|
+
const vega_js_1 = require("../drivers/vega.js");
|
|
11
12
|
const utils_js_1 = require("../utils.js");
|
|
12
13
|
async function scroll(direction = 'down', opts = {}, sessionName = 'default') {
|
|
13
14
|
const valid = ['down', 'up', 'left', 'right'];
|
|
@@ -31,7 +32,7 @@ async function scroll(direction = 'down', opts = {}, sessionName = 'default') {
|
|
|
31
32
|
const { widthPixels: w, heightPixels: h } = info;
|
|
32
33
|
await driver.swipe(coords.startX * w, coords.startY * h, coords.endX * w, coords.endY * h, 500);
|
|
33
34
|
}
|
|
34
|
-
else if (driver instanceof android_js_1.AndroidDriver) {
|
|
35
|
+
else if (driver instanceof android_js_1.AndroidDriver || driver instanceof vega_js_1.VegaDriver) {
|
|
35
36
|
const info = await driver.deviceInfo();
|
|
36
37
|
const { widthPixels: w, heightPixels: h } = info;
|
|
37
38
|
await driver.swipe(coords.startX * w, coords.startY * h, coords.endX * w, coords.endY * h, 500);
|