@0xhayd3n/fling 0.6.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/adb.d.ts +18 -0
- package/dist/adb.js +109 -0
- package/dist/adb.js.map +1 -0
- package/dist/apkFinder.d.ts +19 -0
- package/dist/apkFinder.js +113 -0
- package/dist/apkFinder.js.map +1 -0
- package/dist/apkResolver.d.ts +14 -0
- package/dist/apkResolver.js +55 -0
- package/dist/apkResolver.js.map +1 -0
- package/dist/config.d.ts +44 -0
- package/dist/config.js +113 -0
- package/dist/config.js.map +1 -0
- package/dist/devices.d.ts +24 -0
- package/dist/devices.js +143 -0
- package/dist/devices.js.map +1 -0
- package/dist/errors.d.ts +13 -0
- package/dist/errors.js +22 -0
- package/dist/errors.js.map +1 -0
- package/dist/featureFlags.d.ts +6 -0
- package/dist/featureFlags.js +11 -0
- package/dist/featureFlags.js.map +1 -0
- package/dist/gradle.d.ts +39 -0
- package/dist/gradle.js +129 -0
- package/dist/gradle.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/mdns.d.ts +19 -0
- package/dist/mdns.js +68 -0
- package/dist/mdns.js.map +1 -0
- package/dist/pairing.d.ts +52 -0
- package/dist/pairing.js +212 -0
- package/dist/pairing.js.map +1 -0
- package/dist/qrText.d.ts +5 -0
- package/dist/qrText.js +8 -0
- package/dist/qrText.js.map +1 -0
- package/dist/schemas.d.ts +31 -0
- package/dist/schemas.js +26 -0
- package/dist/schemas.js.map +1 -0
- package/dist/shellFraming.d.ts +28 -0
- package/dist/shellFraming.js +43 -0
- package/dist/shellFraming.js.map +1 -0
- package/dist/shellPool.d.ts +54 -0
- package/dist/shellPool.js +181 -0
- package/dist/shellPool.js.map +1 -0
- package/dist/toolResult.d.ts +10 -0
- package/dist/toolResult.js +11 -0
- package/dist/toolResult.js.map +1 -0
- package/dist/tools/build-app.d.ts +2 -0
- package/dist/tools/build-app.js +74 -0
- package/dist/tools/build-app.js.map +1 -0
- package/dist/tools/deploy-and-run.d.ts +2 -0
- package/dist/tools/deploy-and-run.js +195 -0
- package/dist/tools/deploy-and-run.js.map +1 -0
- package/dist/tools/device-state.d.ts +11 -0
- package/dist/tools/device-state.js +140 -0
- package/dist/tools/device-state.js.map +1 -0
- package/dist/tools/dismiss-dialog.d.ts +15 -0
- package/dist/tools/dismiss-dialog.js +89 -0
- package/dist/tools/dismiss-dialog.js.map +1 -0
- package/dist/tools/dump-ui.d.ts +4 -0
- package/dist/tools/dump-ui.js +93 -0
- package/dist/tools/dump-ui.js.map +1 -0
- package/dist/tools/find-on-screen.d.ts +27 -0
- package/dist/tools/find-on-screen.js +92 -0
- package/dist/tools/find-on-screen.js.map +1 -0
- package/dist/tools/install-app.d.ts +23 -0
- package/dist/tools/install-app.js +127 -0
- package/dist/tools/install-app.js.map +1 -0
- package/dist/tools/launch-and-wait.d.ts +27 -0
- package/dist/tools/launch-and-wait.js +103 -0
- package/dist/tools/launch-and-wait.js.map +1 -0
- package/dist/tools/launch-app.d.ts +20 -0
- package/dist/tools/launch-app.js +131 -0
- package/dist/tools/launch-app.js.map +1 -0
- package/dist/tools/launch-settings.d.ts +42 -0
- package/dist/tools/launch-settings.js +211 -0
- package/dist/tools/launch-settings.js.map +1 -0
- package/dist/tools/list-devices.d.ts +2 -0
- package/dist/tools/list-devices.js +35 -0
- package/dist/tools/list-devices.js.map +1 -0
- package/dist/tools/long-press-by-text.d.ts +3 -0
- package/dist/tools/long-press-by-text.js +99 -0
- package/dist/tools/long-press-by-text.js.map +1 -0
- package/dist/tools/open-setting.d.ts +55 -0
- package/dist/tools/open-setting.js +257 -0
- package/dist/tools/open-setting.js.map +1 -0
- package/dist/tools/read-logs.d.ts +2 -0
- package/dist/tools/read-logs.js +147 -0
- package/dist/tools/read-logs.js.map +1 -0
- package/dist/tools/screenshot-with-ui.d.ts +21 -0
- package/dist/tools/screenshot-with-ui.js +74 -0
- package/dist/tools/screenshot-with-ui.js.map +1 -0
- package/dist/tools/screenshot.d.ts +8 -0
- package/dist/tools/screenshot.js +97 -0
- package/dist/tools/screenshot.js.map +1 -0
- package/dist/tools/scroll-until-visible.d.ts +23 -0
- package/dist/tools/scroll-until-visible.js +138 -0
- package/dist/tools/scroll-until-visible.js.map +1 -0
- package/dist/tools/start-pair-qr.d.ts +11 -0
- package/dist/tools/start-pair-qr.js +62 -0
- package/dist/tools/start-pair-qr.js.map +1 -0
- package/dist/tools/stop-app.d.ts +2 -0
- package/dist/tools/stop-app.js +63 -0
- package/dist/tools/stop-app.js.map +1 -0
- package/dist/tools/tap-by-content-desc.d.ts +17 -0
- package/dist/tools/tap-by-content-desc.js +97 -0
- package/dist/tools/tap-by-content-desc.js.map +1 -0
- package/dist/tools/tap-by-resource-id.d.ts +17 -0
- package/dist/tools/tap-by-resource-id.js +89 -0
- package/dist/tools/tap-by-resource-id.js.map +1 -0
- package/dist/tools/tap-by-text.d.ts +40 -0
- package/dist/tools/tap-by-text.js +180 -0
- package/dist/tools/tap-by-text.js.map +1 -0
- package/dist/tools/tap-text-verified.d.ts +37 -0
- package/dist/tools/tap-text-verified.js +111 -0
- package/dist/tools/tap-text-verified.js.map +1 -0
- package/dist/tools/uninstall-app.d.ts +2 -0
- package/dist/tools/uninstall-app.js +96 -0
- package/dist/tools/uninstall-app.js.map +1 -0
- package/dist/tools/wait-for-pair.d.ts +29 -0
- package/dist/tools/wait-for-pair.js +132 -0
- package/dist/tools/wait-for-pair.js.map +1 -0
- package/dist/tools/wait-for.d.ts +26 -0
- package/dist/tools/wait-for.js +109 -0
- package/dist/tools/wait-for.js.map +1 -0
- package/dist/uiDump.d.ts +34 -0
- package/dist/uiDump.js +108 -0
- package/dist/uiDump.js.map +1 -0
- package/dist/uiSelector.d.ts +21 -0
- package/dist/uiSelector.js +62 -0
- package/dist/uiSelector.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { fetchUiDump } from "../uiDump.js";
|
|
3
|
+
import { findNodes } from "../uiSelector.js";
|
|
4
|
+
import { resolveDeviceArgs } from "../devices.js";
|
|
5
|
+
import { FlingError } from "../errors.js";
|
|
6
|
+
import { deviceIdInput } from "../schemas.js";
|
|
7
|
+
import { toolErrorFrom } from "../toolResult.js";
|
|
8
|
+
const defaultNow = () => Date.now();
|
|
9
|
+
const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
|
+
export async function pollUntilFound(dumpFn, selector, options) {
|
|
11
|
+
const now = options.now ?? defaultNow;
|
|
12
|
+
const sleep = options.sleep ?? defaultSleep;
|
|
13
|
+
const start = now();
|
|
14
|
+
let attempts = 0;
|
|
15
|
+
while (true) {
|
|
16
|
+
attempts++;
|
|
17
|
+
const nodes = await dumpFn();
|
|
18
|
+
const matches = findNodes(nodes, selector);
|
|
19
|
+
if (matches.length > 0) {
|
|
20
|
+
return {
|
|
21
|
+
found: true,
|
|
22
|
+
attempts,
|
|
23
|
+
elapsed_ms: now() - start,
|
|
24
|
+
bounds: matches[0].bounds,
|
|
25
|
+
center: matches[0].center,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (now() - start + options.pollIntervalMs >= options.timeoutMs) {
|
|
29
|
+
throw new FlingError("UI_WAIT_TIMEOUT", `wait_for timed out after ${options.timeoutMs}ms (${attempts} attempts) looking for ${selector.by}="${selector.value}".`);
|
|
30
|
+
}
|
|
31
|
+
await sleep(options.pollIntervalMs);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function registerWaitFor(server) {
|
|
35
|
+
server.registerTool("wait_for", {
|
|
36
|
+
title: "Wait for a UI element to appear",
|
|
37
|
+
description: "Polls dump_ui at the given interval until the selector matches, or " +
|
|
38
|
+
"throws UI_WAIT_TIMEOUT after timeout_ms elapses. Use after launching " +
|
|
39
|
+
"an app or triggering an async transition. Costs roughly one dump every " +
|
|
40
|
+
"poll_interval_ms (default 500ms) until found.",
|
|
41
|
+
inputSchema: {
|
|
42
|
+
by: z
|
|
43
|
+
.enum(["text", "resource_id", "content_desc"])
|
|
44
|
+
.describe("Which attribute to match on."),
|
|
45
|
+
value: z.string().min(1).describe("Value to look up."),
|
|
46
|
+
exact: z
|
|
47
|
+
.boolean()
|
|
48
|
+
.optional()
|
|
49
|
+
.default(false)
|
|
50
|
+
.describe("Exact match (ignored for resource_id which is always exact)."),
|
|
51
|
+
timeout_ms: z
|
|
52
|
+
.number()
|
|
53
|
+
.int()
|
|
54
|
+
.positive()
|
|
55
|
+
.max(60_000)
|
|
56
|
+
.optional()
|
|
57
|
+
.default(5000)
|
|
58
|
+
.describe("Maximum total wait, milliseconds (default 5000)."),
|
|
59
|
+
poll_interval_ms: z
|
|
60
|
+
.number()
|
|
61
|
+
.int()
|
|
62
|
+
.positive()
|
|
63
|
+
.max(5000)
|
|
64
|
+
.optional()
|
|
65
|
+
.default(500)
|
|
66
|
+
.describe("Wait between dumps, milliseconds (default 500)."),
|
|
67
|
+
device_id: deviceIdInput,
|
|
68
|
+
},
|
|
69
|
+
outputSchema: {
|
|
70
|
+
device_id: z.string(),
|
|
71
|
+
found_after_ms: z.number().int().nonnegative(),
|
|
72
|
+
attempts: z.number().int().positive(),
|
|
73
|
+
bounds: z.object({
|
|
74
|
+
x1: z.number(),
|
|
75
|
+
y1: z.number(),
|
|
76
|
+
x2: z.number(),
|
|
77
|
+
y2: z.number(),
|
|
78
|
+
}),
|
|
79
|
+
center: z.object({ x: z.number(), y: z.number() }),
|
|
80
|
+
},
|
|
81
|
+
annotations: {
|
|
82
|
+
readOnlyHint: true,
|
|
83
|
+
destructiveHint: false,
|
|
84
|
+
idempotentHint: true,
|
|
85
|
+
openWorldHint: false,
|
|
86
|
+
},
|
|
87
|
+
}, async ({ by, value, exact, timeout_ms, poll_interval_ms, device_id }) => {
|
|
88
|
+
try {
|
|
89
|
+
const { args: deviceArgs, serial } = await resolveDeviceArgs(device_id);
|
|
90
|
+
const selector = by === "resource_id" ? { by, value } : { by, value, exact };
|
|
91
|
+
const result = await pollUntilFound(async () => (await fetchUiDump(deviceArgs)).nodes, selector, { timeoutMs: timeout_ms, pollIntervalMs: poll_interval_ms });
|
|
92
|
+
const msg = `Found ${by}="${value}" after ${result.elapsed_ms}ms (${result.attempts} dumps) on ${serial}.`;
|
|
93
|
+
return {
|
|
94
|
+
content: [{ type: "text", text: msg }],
|
|
95
|
+
structuredContent: {
|
|
96
|
+
device_id: serial,
|
|
97
|
+
found_after_ms: result.elapsed_ms,
|
|
98
|
+
attempts: result.attempts,
|
|
99
|
+
bounds: result.bounds,
|
|
100
|
+
center: result.center,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
return toolErrorFrom(err);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=wait-for.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-for.js","sourceRoot":"","sources":["../../src/tools/wait-for.ts"],"names":[],"mappings":"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"}
|
package/dist/uiDump.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface UiBounds {
|
|
2
|
+
x1: number;
|
|
3
|
+
y1: number;
|
|
4
|
+
x2: number;
|
|
5
|
+
y2: number;
|
|
6
|
+
}
|
|
7
|
+
export interface UiNode {
|
|
8
|
+
text: string;
|
|
9
|
+
content_desc: string;
|
|
10
|
+
resource_id: string;
|
|
11
|
+
class: string;
|
|
12
|
+
package: string;
|
|
13
|
+
bounds: UiBounds;
|
|
14
|
+
center: {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
};
|
|
18
|
+
clickable: boolean;
|
|
19
|
+
long_clickable: boolean;
|
|
20
|
+
scrollable: boolean;
|
|
21
|
+
focusable: boolean;
|
|
22
|
+
focused: boolean;
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
selected: boolean;
|
|
25
|
+
checkable: boolean;
|
|
26
|
+
checked: boolean;
|
|
27
|
+
}
|
|
28
|
+
export declare function parseUiHierarchy(xml: string): UiNode[];
|
|
29
|
+
export declare function isInteresting(node: UiNode): boolean;
|
|
30
|
+
export interface UiDumpResult {
|
|
31
|
+
nodes: UiNode[];
|
|
32
|
+
raw_xml: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function fetchUiDump(deviceArgs: string[]): Promise<UiDumpResult>;
|
package/dist/uiDump.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { runAdb } from "./adb.js";
|
|
2
|
+
import { FlingError } from "./errors.js";
|
|
3
|
+
const DUMP_TIMEOUT_MS = 15_000;
|
|
4
|
+
const DEVICE_DUMP_PATH = "/sdcard/window_dump.xml";
|
|
5
|
+
// uiautomator emits each node as a single self-closing tag OR a paired opening
|
|
6
|
+
// tag whose attributes live entirely on one line, e.g.
|
|
7
|
+
// <node bounds="[0,0][1080,200]" text="Settings" clickable="true" .../>
|
|
8
|
+
// The attribute matcher is quoted-value aware so it doesn't stop at a `>` that
|
|
9
|
+
// appears inside an attribute value (WebView content-desc can contain raw `>`
|
|
10
|
+
// on some Android versions). Closing tags `</node>` start with `</` and are
|
|
11
|
+
// not matched.
|
|
12
|
+
const NODE_OPEN_RE = /<node((?:\s+[\w-]+="[^"]*")*)\s*\/?>/g;
|
|
13
|
+
const BOUNDS_RE = /^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/;
|
|
14
|
+
function decodeXmlAttr(s) {
|
|
15
|
+
// Order matters: & last so we don't double-decode entities that contain
|
|
16
|
+
// an ampersand in their replacement.
|
|
17
|
+
return s
|
|
18
|
+
.replace(/</g, "<")
|
|
19
|
+
.replace(/>/g, ">")
|
|
20
|
+
.replace(/"/g, '"')
|
|
21
|
+
.replace(/'/g, "'")
|
|
22
|
+
.replace(/&/g, "&");
|
|
23
|
+
}
|
|
24
|
+
function parseAttrs(attrStr) {
|
|
25
|
+
const out = {};
|
|
26
|
+
// Fresh regex per call so a future early-return inside the loop can't leave
|
|
27
|
+
// stale `lastIndex` state across calls. Compile cost is negligible.
|
|
28
|
+
const attrRe = /([\w-]+)="([^"]*)"/g;
|
|
29
|
+
let m;
|
|
30
|
+
while ((m = attrRe.exec(attrStr)) !== null) {
|
|
31
|
+
out[m[1]] = decodeXmlAttr(m[2]);
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
function parseBool(v) {
|
|
36
|
+
return v === "true";
|
|
37
|
+
}
|
|
38
|
+
export function parseUiHierarchy(xml) {
|
|
39
|
+
const nodes = [];
|
|
40
|
+
NODE_OPEN_RE.lastIndex = 0;
|
|
41
|
+
let m;
|
|
42
|
+
while ((m = NODE_OPEN_RE.exec(xml)) !== null) {
|
|
43
|
+
const attrs = parseAttrs(m[1]);
|
|
44
|
+
const boundsMatch = attrs.bounds ? BOUNDS_RE.exec(attrs.bounds) : null;
|
|
45
|
+
// A node without parseable bounds isn't tappable — skip it. This also
|
|
46
|
+
// protects downstream callers that compute taps from `center` without
|
|
47
|
+
// re-checking.
|
|
48
|
+
if (!boundsMatch)
|
|
49
|
+
continue;
|
|
50
|
+
const x1 = Number(boundsMatch[1]);
|
|
51
|
+
const y1 = Number(boundsMatch[2]);
|
|
52
|
+
const x2 = Number(boundsMatch[3]);
|
|
53
|
+
const y2 = Number(boundsMatch[4]);
|
|
54
|
+
nodes.push({
|
|
55
|
+
text: attrs.text ?? "",
|
|
56
|
+
content_desc: attrs["content-desc"] ?? "",
|
|
57
|
+
resource_id: attrs["resource-id"] ?? "",
|
|
58
|
+
class: attrs.class ?? "",
|
|
59
|
+
package: attrs.package ?? "",
|
|
60
|
+
bounds: { x1, y1, x2, y2 },
|
|
61
|
+
center: {
|
|
62
|
+
x: Math.round((x1 + x2) / 2),
|
|
63
|
+
y: Math.round((y1 + y2) / 2),
|
|
64
|
+
},
|
|
65
|
+
clickable: parseBool(attrs.clickable),
|
|
66
|
+
long_clickable: parseBool(attrs["long-clickable"]),
|
|
67
|
+
scrollable: parseBool(attrs.scrollable),
|
|
68
|
+
focusable: parseBool(attrs.focusable),
|
|
69
|
+
focused: parseBool(attrs.focused),
|
|
70
|
+
enabled: parseBool(attrs.enabled),
|
|
71
|
+
selected: parseBool(attrs.selected),
|
|
72
|
+
checkable: parseBool(attrs.checkable),
|
|
73
|
+
checked: parseBool(attrs.checked),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return nodes;
|
|
77
|
+
}
|
|
78
|
+
export function isInteresting(node) {
|
|
79
|
+
// `focusable` alone is intentionally NOT a qualifier: in dense lists almost
|
|
80
|
+
// every TextView is focusable for D-pad navigation, which drowns the result
|
|
81
|
+
// in noise. Anything truly tap-targetable will be clickable; anything with
|
|
82
|
+
// semantic meaning will have text / content-desc / resource-id.
|
|
83
|
+
return (node.clickable ||
|
|
84
|
+
node.long_clickable ||
|
|
85
|
+
node.scrollable ||
|
|
86
|
+
node.text !== "" ||
|
|
87
|
+
node.content_desc !== "" ||
|
|
88
|
+
node.resource_id !== "");
|
|
89
|
+
}
|
|
90
|
+
export async function fetchUiDump(deviceArgs) {
|
|
91
|
+
// uiautomator prints "ERROR: could not get idle state" (and similar) when
|
|
92
|
+
// the foreground app blocks accessibility traversal. adb merges device-side
|
|
93
|
+
// stderr into the shell's stdout, so we have to inspect the combined output
|
|
94
|
+
// — but only at the start of a line. Anchoring to ^ERROR: avoids matching a
|
|
95
|
+
// node attribute like text="ERROR: connection refused" that lives inside an
|
|
96
|
+
// XML payload, since uiautomator's status lines always start in column 0.
|
|
97
|
+
const { stdout: dumpOut, stderr: dumpErr } = await runAdb([...deviceArgs, "shell", "uiautomator", "dump", DEVICE_DUMP_PATH], { timeoutMs: DUMP_TIMEOUT_MS });
|
|
98
|
+
const combined = `${dumpOut}\n${dumpErr}`;
|
|
99
|
+
if (/^ERROR:/m.test(combined)) {
|
|
100
|
+
throw new FlingError("ADB_FAILED", `uiautomator dump failed: ${combined.trim()}`);
|
|
101
|
+
}
|
|
102
|
+
const { stdout: xml } = await runAdb([...deviceArgs, "shell", "cat", DEVICE_DUMP_PATH], { timeoutMs: DUMP_TIMEOUT_MS });
|
|
103
|
+
if (!xml.includes("<hierarchy") && !xml.includes("<node")) {
|
|
104
|
+
throw new FlingError("ADB_FAILED", `uiautomator dump produced no XML hierarchy. Got: ${xml.slice(0, 200)}`);
|
|
105
|
+
}
|
|
106
|
+
return { nodes: parseUiHierarchy(xml), raw_xml: xml };
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=uiDump.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uiDump.js","sourceRoot":"","sources":["../src/uiDump.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { UiBounds, UiNode } from "./uiDump.js";
|
|
2
|
+
export type Selector = {
|
|
3
|
+
by: "text";
|
|
4
|
+
value: string;
|
|
5
|
+
exact?: boolean;
|
|
6
|
+
} | {
|
|
7
|
+
by: "resource_id";
|
|
8
|
+
value: string;
|
|
9
|
+
} | {
|
|
10
|
+
by: "content_desc";
|
|
11
|
+
value: string;
|
|
12
|
+
exact?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function containsBounds(outer: UiBounds, inner: UiBounds): boolean;
|
|
15
|
+
export declare function findNodes(nodes: UiNode[], selector: Selector): UiNode[];
|
|
16
|
+
export interface PickBestResult {
|
|
17
|
+
node: UiNode;
|
|
18
|
+
fellBackToMatch: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function pickBest(matches: UiNode[], allNodes: UiNode[]): PickBestResult | undefined;
|
|
21
|
+
export declare const DENY_LABELS: readonly string[];
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function containsBounds(outer, inner) {
|
|
2
|
+
return (outer.x1 <= inner.x1 &&
|
|
3
|
+
outer.y1 <= inner.y1 &&
|
|
4
|
+
outer.x2 >= inner.x2 &&
|
|
5
|
+
outer.y2 >= inner.y2);
|
|
6
|
+
}
|
|
7
|
+
function area(b) {
|
|
8
|
+
return Math.max(0, b.x2 - b.x1) * Math.max(0, b.y2 - b.y1);
|
|
9
|
+
}
|
|
10
|
+
export function findNodes(nodes, selector) {
|
|
11
|
+
if (selector.by === "resource_id") {
|
|
12
|
+
return nodes.filter((n) => n.resource_id === selector.value);
|
|
13
|
+
}
|
|
14
|
+
const field = selector.by === "text" ? "text" : "content_desc";
|
|
15
|
+
const exact = selector.exact === true;
|
|
16
|
+
return nodes.filter((n) => {
|
|
17
|
+
const haystack = n[field];
|
|
18
|
+
if (!haystack)
|
|
19
|
+
return false;
|
|
20
|
+
return exact ? haystack === selector.value : haystack.includes(selector.value);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export function pickBest(matches, allNodes) {
|
|
24
|
+
if (matches.length === 0)
|
|
25
|
+
return undefined;
|
|
26
|
+
const match = matches[0]; // first in document order
|
|
27
|
+
if (match.clickable) {
|
|
28
|
+
return { node: match, fellBackToMatch: false };
|
|
29
|
+
}
|
|
30
|
+
// Find all clickable nodes whose bounds contain the match.
|
|
31
|
+
const clickableAncestors = allNodes.filter((n) => n.clickable && n !== match && containsBounds(n.bounds, match.bounds));
|
|
32
|
+
if (clickableAncestors.length === 0) {
|
|
33
|
+
return { node: match, fellBackToMatch: true };
|
|
34
|
+
}
|
|
35
|
+
// Smallest by area.
|
|
36
|
+
let best = clickableAncestors[0];
|
|
37
|
+
let bestArea = area(best.bounds);
|
|
38
|
+
for (let i = 1; i < clickableAncestors.length; i++) {
|
|
39
|
+
const a = area(clickableAncestors[i].bounds);
|
|
40
|
+
if (a < bestArea) {
|
|
41
|
+
best = clickableAncestors[i];
|
|
42
|
+
bestArea = a;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { node: best, fellBackToMatch: false };
|
|
46
|
+
}
|
|
47
|
+
export const DENY_LABELS = [
|
|
48
|
+
"Don’t allow", // U+2019 RIGHT SINGLE QUOTATION MARK (Android's actual rendering)
|
|
49
|
+
"Don’t allow & don’t ask again",
|
|
50
|
+
"Don't allow", // ASCII apostrophe fallback
|
|
51
|
+
"Don't allow & don't ask again",
|
|
52
|
+
"Not now",
|
|
53
|
+
"Skip",
|
|
54
|
+
"Cancel",
|
|
55
|
+
"Dismiss",
|
|
56
|
+
"No thanks",
|
|
57
|
+
"Maybe later",
|
|
58
|
+
"Deny",
|
|
59
|
+
"Close",
|
|
60
|
+
"No",
|
|
61
|
+
];
|
|
62
|
+
//# sourceMappingURL=uiSelector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uiSelector.js","sourceRoot":"","sources":["../src/uiSelector.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@0xhayd3n/fling",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "MCP server for deploying and running Android apps via adb.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"fling": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/index.js",
|
|
11
|
+
"./devices": "./dist/devices.js",
|
|
12
|
+
"./config": "./dist/config.js",
|
|
13
|
+
"./gradle": "./dist/gradle.js",
|
|
14
|
+
"./errors": "./dist/errors.js",
|
|
15
|
+
"./apkResolver": "./dist/apkResolver.js",
|
|
16
|
+
"./apkFinder": "./dist/apkFinder.js",
|
|
17
|
+
"./pairing": "./dist/pairing.js",
|
|
18
|
+
"./adb": "./dist/adb.js",
|
|
19
|
+
"./operations/install": "./dist/tools/install-app.js",
|
|
20
|
+
"./operations/launch": "./dist/tools/launch-app.js"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"dev": "tsx src/index.ts",
|
|
30
|
+
"start": "node dist/index.js",
|
|
31
|
+
"clean": "rimraf dist",
|
|
32
|
+
"test": "npm run build && node --test \"tests/**/*.test.mjs\"",
|
|
33
|
+
"smoke": "node scripts/smoke.mjs"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"mcp",
|
|
37
|
+
"model-context-protocol",
|
|
38
|
+
"android",
|
|
39
|
+
"adb",
|
|
40
|
+
"deploy",
|
|
41
|
+
"gradle"
|
|
42
|
+
],
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"author": "Hayden Seymour <contact@haydenseymour.com>",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/0xHayd3n/Fling.git",
|
|
48
|
+
"directory": "packages/mcp-server"
|
|
49
|
+
},
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/0xHayd3n/Fling/issues"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://github.com/0xHayd3n/Fling#readme",
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=18.0.0"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
59
|
+
"zod": "^3.23.8"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^22.0.0",
|
|
63
|
+
"rimraf": "^6.0.1",
|
|
64
|
+
"tsx": "^4.19.0",
|
|
65
|
+
"typescript": "^5.6.0"
|
|
66
|
+
}
|
|
67
|
+
}
|