@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
package/dist/devices.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { runAdb } from "./adb.js";
|
|
2
|
+
import { FlingError } from "./errors.js";
|
|
3
|
+
const KNOWN_STATES = [
|
|
4
|
+
"device",
|
|
5
|
+
"unauthorized",
|
|
6
|
+
"offline",
|
|
7
|
+
"no permissions",
|
|
8
|
+
"recovery",
|
|
9
|
+
"sideload",
|
|
10
|
+
"bootloader",
|
|
11
|
+
];
|
|
12
|
+
export function parseDevicesOutput(stdout) {
|
|
13
|
+
const lines = stdout.split(/\r?\n/);
|
|
14
|
+
const headerIdx = lines.findIndex((l) => l.trim().startsWith("List of devices attached"));
|
|
15
|
+
if (headerIdx === -1)
|
|
16
|
+
return [];
|
|
17
|
+
const devices = [];
|
|
18
|
+
for (let i = headerIdx + 1; i < lines.length; i++) {
|
|
19
|
+
const line = lines[i];
|
|
20
|
+
const trimmed = line.trim();
|
|
21
|
+
if (!trimmed)
|
|
22
|
+
continue;
|
|
23
|
+
if (trimmed.startsWith("*"))
|
|
24
|
+
continue;
|
|
25
|
+
const parts = trimmed.split(/\s+/);
|
|
26
|
+
if (parts.length < 2)
|
|
27
|
+
continue;
|
|
28
|
+
const serial = parts[0];
|
|
29
|
+
let state = "unknown";
|
|
30
|
+
let rest = [];
|
|
31
|
+
const twoWord = `${parts[1]} ${parts[2] ?? ""}`.trim();
|
|
32
|
+
if (KNOWN_STATES.includes(twoWord) &&
|
|
33
|
+
twoWord.includes(" ")) {
|
|
34
|
+
state = twoWord;
|
|
35
|
+
rest = parts.slice(3);
|
|
36
|
+
}
|
|
37
|
+
else if (KNOWN_STATES.includes(parts[1])) {
|
|
38
|
+
state = parts[1];
|
|
39
|
+
rest = parts.slice(2);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
state = "unknown";
|
|
43
|
+
rest = parts.slice(2);
|
|
44
|
+
}
|
|
45
|
+
const device = { serial, state, raw: trimmed };
|
|
46
|
+
for (const token of rest) {
|
|
47
|
+
const colonIdx = token.indexOf(":");
|
|
48
|
+
if (colonIdx === -1)
|
|
49
|
+
continue;
|
|
50
|
+
const key = token.slice(0, colonIdx);
|
|
51
|
+
const value = token.slice(colonIdx + 1);
|
|
52
|
+
switch (key) {
|
|
53
|
+
case "product":
|
|
54
|
+
device.product = value;
|
|
55
|
+
break;
|
|
56
|
+
case "model":
|
|
57
|
+
device.model = value;
|
|
58
|
+
break;
|
|
59
|
+
case "device":
|
|
60
|
+
device.device = value;
|
|
61
|
+
break;
|
|
62
|
+
case "transport_id":
|
|
63
|
+
device.transportId = value;
|
|
64
|
+
break;
|
|
65
|
+
case "usb":
|
|
66
|
+
device.usb = value;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
devices.push(device);
|
|
71
|
+
}
|
|
72
|
+
return devices;
|
|
73
|
+
}
|
|
74
|
+
export async function listDevices() {
|
|
75
|
+
const { stdout } = await runAdb(["devices", "-l"]);
|
|
76
|
+
return parseDevicesOutput(stdout);
|
|
77
|
+
}
|
|
78
|
+
export function formatDevicesSummary(devices) {
|
|
79
|
+
if (devices.length === 0) {
|
|
80
|
+
return [
|
|
81
|
+
"No Android devices detected.",
|
|
82
|
+
"",
|
|
83
|
+
"Checklist:",
|
|
84
|
+
" • Phone connected via USB (or paired over Wi-Fi).",
|
|
85
|
+
" • USB Debugging enabled (Settings → Developer options).",
|
|
86
|
+
" • RSA fingerprint prompt accepted on the phone.",
|
|
87
|
+
].join("\n");
|
|
88
|
+
}
|
|
89
|
+
const lines = [
|
|
90
|
+
`${devices.length} device${devices.length === 1 ? "" : "s"} detected:`,
|
|
91
|
+
"",
|
|
92
|
+
];
|
|
93
|
+
devices.forEach((d, idx) => {
|
|
94
|
+
const label = d.model ?? d.product ?? d.device ?? "unknown model";
|
|
95
|
+
lines.push(`${idx + 1}. ${d.serial} [${d.state}] ${label}`);
|
|
96
|
+
if (d.state === "unauthorized") {
|
|
97
|
+
lines.push(" → Accept the RSA fingerprint prompt on the phone, then re-run.");
|
|
98
|
+
}
|
|
99
|
+
else if (d.state === "offline") {
|
|
100
|
+
lines.push(" → Device is offline. Try unplugging/re-plugging or `adb kill-server`.");
|
|
101
|
+
}
|
|
102
|
+
else if (d.state === "no permissions") {
|
|
103
|
+
lines.push(" → adb lacks USB permissions. On Linux, configure udev rules; on Windows, reinstall the OEM USB driver.");
|
|
104
|
+
}
|
|
105
|
+
else if (d.state === "unknown") {
|
|
106
|
+
lines.push(` → Unrecognized state. Raw: ${d.raw}`);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
return lines.join("\n");
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Resolve which `-s <id>` args to pass to adb.
|
|
113
|
+
*
|
|
114
|
+
* Priority: explicit deviceId → ANDROID_SERIAL env → auto-pick the single ready
|
|
115
|
+
* device. Throws FlingError when ambiguous or impossible.
|
|
116
|
+
*/
|
|
117
|
+
export async function resolveDeviceArgs(deviceId) {
|
|
118
|
+
if (deviceId)
|
|
119
|
+
return { args: ["-s", deviceId], serial: deviceId };
|
|
120
|
+
const envSerial = process.env.ANDROID_SERIAL;
|
|
121
|
+
if (envSerial)
|
|
122
|
+
return { args: ["-s", envSerial], serial: envSerial };
|
|
123
|
+
const devices = await listDevices();
|
|
124
|
+
const ready = devices.filter((d) => d.state === "device");
|
|
125
|
+
if (ready.length === 1) {
|
|
126
|
+
return { args: ["-s", ready[0].serial], serial: ready[0].serial };
|
|
127
|
+
}
|
|
128
|
+
if (ready.length === 0) {
|
|
129
|
+
if (devices.length === 0) {
|
|
130
|
+
throw new FlingError("NO_DEVICE", "No Android devices detected. Plug in a phone with USB Debugging enabled, " +
|
|
131
|
+
"or run list_devices for the full checklist.");
|
|
132
|
+
}
|
|
133
|
+
const states = devices.map((d) => `${d.serial} (${d.state})`).join(", ");
|
|
134
|
+
const hint = devices.some((d) => d.state === "unauthorized")
|
|
135
|
+
? "Accept the RSA fingerprint prompt on the phone, then retry."
|
|
136
|
+
: "Run list_devices for per-device guidance.";
|
|
137
|
+
throw new FlingError("NO_READY_DEVICE", `No devices are in the 'device' state: ${states}. ${hint}`);
|
|
138
|
+
}
|
|
139
|
+
const serials = ready.map((d) => d.serial).join(", ");
|
|
140
|
+
throw new FlingError("MULTIPLE_DEVICES", `${ready.length} devices ready (${serials}). Pass device_id to pick one, ` +
|
|
141
|
+
"or set the ANDROID_SERIAL environment variable.");
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=devices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devices.js","sourceRoot":"","sources":["../src/devices.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type FlingErrorCode = "ADB_NOT_FOUND" | "ADB_TIMEOUT" | "ADB_FAILED" | "NO_DEVICE" | "NO_READY_DEVICE" | "MULTIPLE_DEVICES" | "APK_NOT_FOUND" | "INSTALL_FAILED" | "LAUNCH_FAILED" | "APP_NOT_RUNNING" | "INVALID_INPUT" | "UI_ELEMENT_NOT_FOUND" | "UI_WAIT_TIMEOUT" | "ADB_SHELL_SHUT_DOWN" | "ADB_SHELL_DIED" | "ADB_SHELL_RECYCLED" | "BUILD_TOOL_NOT_FOUND" | "BUILD_TIMEOUT" | "BUILD_FAILED" | "CONFIG_MISSING" | "PAIRING_TIMEOUT" | "ADB_PAIR_FAILED" | "ADB_CONNECT_FAILED" | "MDNS_UNAVAILABLE" | "UNKNOWN_SERVICE" | "PAIRING_IN_PROGRESS";
|
|
2
|
+
export declare class FlingError extends Error {
|
|
3
|
+
readonly code: FlingErrorCode;
|
|
4
|
+
readonly stderr?: string;
|
|
5
|
+
readonly stdout?: string;
|
|
6
|
+
readonly exitCode?: number;
|
|
7
|
+
constructor(code: FlingErrorCode, message: string, extras?: {
|
|
8
|
+
stderr?: string;
|
|
9
|
+
stdout?: string;
|
|
10
|
+
exitCode?: number;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export declare function describeError(err: unknown): string;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export class FlingError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
stderr;
|
|
4
|
+
stdout;
|
|
5
|
+
exitCode;
|
|
6
|
+
constructor(code, message, extras = {}) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "FlingError";
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.stderr = extras.stderr;
|
|
11
|
+
this.stdout = extras.stdout;
|
|
12
|
+
this.exitCode = extras.exitCode;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function describeError(err) {
|
|
16
|
+
if (err instanceof FlingError)
|
|
17
|
+
return `${err.code}: ${err.message}`;
|
|
18
|
+
if (err instanceof Error)
|
|
19
|
+
return err.message;
|
|
20
|
+
return String(err);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AA4BA,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC1B,IAAI,CAAiB;IACrB,MAAM,CAAU;IAChB,MAAM,CAAU;IAChB,QAAQ,CAAU;IAE3B,YACE,IAAoB,EACpB,OAAe,EACf,SAAkE,EAAE;QAEpE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAClC,CAAC;CACF;AAED,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,IAAI,GAAG,YAAY,UAAU;QAAE,OAAO,GAAG,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC;IACpE,IAAI,GAAG,YAAY,KAAK;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC;IAC7C,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FLING_PERSISTENT_SHELL gates the persistent adb-shell pool path during
|
|
3
|
+
* rollout. Off by default; on for any of 1/true/yes/on (case-insensitive).
|
|
4
|
+
* Removed in Stage 4 once the pool path is stable.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isPersistentShellEnabled(): boolean;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
2
|
+
/**
|
|
3
|
+
* FLING_PERSISTENT_SHELL gates the persistent adb-shell pool path during
|
|
4
|
+
* rollout. Off by default; on for any of 1/true/yes/on (case-insensitive).
|
|
5
|
+
* Removed in Stage 4 once the pool path is stable.
|
|
6
|
+
*/
|
|
7
|
+
export function isPersistentShellEnabled() {
|
|
8
|
+
const v = (process.env.FLING_PERSISTENT_SHELL ?? "").trim().toLowerCase();
|
|
9
|
+
return TRUTHY.has(v);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=featureFlags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"featureFlags.js","sourceRoot":"","sources":["../src/featureFlags.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;AAEnD;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACtC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1E,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC"}
|
package/dist/gradle.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import type { FlingConfig } from "./config.js";
|
|
3
|
+
declare const DEFAULT_GRADLE_TASK = "assembleDebug";
|
|
4
|
+
export interface BuildCommand {
|
|
5
|
+
command: string;
|
|
6
|
+
args: string[];
|
|
7
|
+
source: "config" | "wrapper" | "path";
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the command Fling should run for a build, given a buildCwd and
|
|
11
|
+
* the user's (possibly empty) config.
|
|
12
|
+
*
|
|
13
|
+
* Order of preference:
|
|
14
|
+
* 1. config.buildCommand (string split on whitespace, or array)
|
|
15
|
+
* 2. gradle wrapper at buildCwd (gradlew.bat on Windows, gradlew otherwise)
|
|
16
|
+
* 3. `gradle` on PATH
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveBuildCommand(buildCwd: string, config: FlingConfig): Promise<BuildCommand>;
|
|
19
|
+
export interface RunBuildOptions {
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
maxBufferBytes?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface BuildOutcome {
|
|
24
|
+
command: BuildCommand;
|
|
25
|
+
stdout: string;
|
|
26
|
+
stderr: string;
|
|
27
|
+
durationMs: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Run a build command synchronously (no streaming) and return the captured
|
|
31
|
+
* output. Throws FlingError on non-zero exit, timeout, or missing tool.
|
|
32
|
+
*/
|
|
33
|
+
export declare function runBuild(cwd: string, command: BuildCommand, options?: RunBuildOptions): Promise<BuildOutcome>;
|
|
34
|
+
/**
|
|
35
|
+
* Pull the most useful failure block out of a verbose gradle log. Falls
|
|
36
|
+
* back to the last 25 non-empty lines if no marker is found.
|
|
37
|
+
*/
|
|
38
|
+
export declare function extractBuildFailureReason(stdout: string, stderr: string): string;
|
|
39
|
+
export { DEFAULT_GRADLE_TASK, join as joinPath };
|
package/dist/gradle.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { stat } from "node:fs/promises";
|
|
3
|
+
import { join, resolve as resolvePath } from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { FlingError } from "./errors.js";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
const DEFAULT_GRADLE_TASK = "assembleDebug";
|
|
8
|
+
const DEFAULT_BUILD_TIMEOUT_MS = 10 * 60 * 1000;
|
|
9
|
+
const DEFAULT_BUILD_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
10
|
+
const IS_WINDOWS = process.platform === "win32";
|
|
11
|
+
const TASK_NAME_RE = /^[A-Za-z][\w:.\-]*$/;
|
|
12
|
+
async function fileExists(path) {
|
|
13
|
+
try {
|
|
14
|
+
const s = await stat(path);
|
|
15
|
+
return s.isFile();
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function splitBuildCommand(cmd) {
|
|
22
|
+
if (Array.isArray(cmd))
|
|
23
|
+
return cmd;
|
|
24
|
+
return cmd.trim().split(/\s+/).filter(Boolean);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Wrap an invocation through cmd.exe so we can run .bat/.cmd on Windows
|
|
28
|
+
* without enabling `shell: true`. NOTE: cmd.exe still interprets `&`, `|`,
|
|
29
|
+
* `>`, `<`, `^`, and parens in its arguments, so this is only safe when
|
|
30
|
+
* the args are validated/trusted. The two callers that reach this:
|
|
31
|
+
* - gradleTask, validated by TASK_NAME_RE.
|
|
32
|
+
* - config.buildCommand, which is part of the user's own project config
|
|
33
|
+
* and is treated as trusted (the threat model is the same as a shell
|
|
34
|
+
* script committed to the repo).
|
|
35
|
+
* Future callers must enforce equivalent validation.
|
|
36
|
+
*/
|
|
37
|
+
function wrapForWindowsBatch(command, args) {
|
|
38
|
+
return { command: "cmd.exe", args: ["/c", command, ...args] };
|
|
39
|
+
}
|
|
40
|
+
function looksLikeBatch(command) {
|
|
41
|
+
const lower = command.toLowerCase();
|
|
42
|
+
return lower.endsWith(".bat") || lower.endsWith(".cmd");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Resolve the command Fling should run for a build, given a buildCwd and
|
|
46
|
+
* the user's (possibly empty) config.
|
|
47
|
+
*
|
|
48
|
+
* Order of preference:
|
|
49
|
+
* 1. config.buildCommand (string split on whitespace, or array)
|
|
50
|
+
* 2. gradle wrapper at buildCwd (gradlew.bat on Windows, gradlew otherwise)
|
|
51
|
+
* 3. `gradle` on PATH
|
|
52
|
+
*/
|
|
53
|
+
export async function resolveBuildCommand(buildCwd, config) {
|
|
54
|
+
if (config.buildCommand) {
|
|
55
|
+
const tokens = splitBuildCommand(config.buildCommand);
|
|
56
|
+
if (tokens.length === 0) {
|
|
57
|
+
throw new FlingError("INVALID_INPUT", "buildCommand in fling config is empty.");
|
|
58
|
+
}
|
|
59
|
+
return { command: tokens[0], args: tokens.slice(1), source: "config" };
|
|
60
|
+
}
|
|
61
|
+
const task = config.gradleTask ?? DEFAULT_GRADLE_TASK;
|
|
62
|
+
if (!TASK_NAME_RE.test(task)) {
|
|
63
|
+
throw new FlingError("INVALID_INPUT", `Invalid gradleTask "${task}". Expected a gradle task identifier.`);
|
|
64
|
+
}
|
|
65
|
+
const wrapperName = IS_WINDOWS ? "gradlew.bat" : "gradlew";
|
|
66
|
+
const wrapperPath = resolvePath(buildCwd, wrapperName);
|
|
67
|
+
if (await fileExists(wrapperPath)) {
|
|
68
|
+
return { command: wrapperPath, args: [task], source: "wrapper" };
|
|
69
|
+
}
|
|
70
|
+
return { command: "gradle", args: [task], source: "path" };
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Run a build command synchronously (no streaming) and return the captured
|
|
74
|
+
* output. Throws FlingError on non-zero exit, timeout, or missing tool.
|
|
75
|
+
*/
|
|
76
|
+
export async function runBuild(cwd, command, options = {}) {
|
|
77
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_BUILD_TIMEOUT_MS;
|
|
78
|
+
const maxBuffer = options.maxBufferBytes ?? DEFAULT_BUILD_BUFFER_BYTES;
|
|
79
|
+
let invocation = { command: command.command, args: command.args };
|
|
80
|
+
if (IS_WINDOWS && looksLikeBatch(command.command)) {
|
|
81
|
+
invocation = wrapForWindowsBatch(command.command, command.args);
|
|
82
|
+
}
|
|
83
|
+
const start = Date.now();
|
|
84
|
+
try {
|
|
85
|
+
const { stdout, stderr } = await execFileAsync(invocation.command, invocation.args, {
|
|
86
|
+
cwd,
|
|
87
|
+
timeout: timeoutMs,
|
|
88
|
+
maxBuffer,
|
|
89
|
+
windowsHide: true,
|
|
90
|
+
});
|
|
91
|
+
return { command, stdout, stderr, durationMs: Date.now() - start };
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
const e = err;
|
|
95
|
+
if (e.code === "ENOENT") {
|
|
96
|
+
throw new FlingError("BUILD_TOOL_NOT_FOUND", command.source === "wrapper"
|
|
97
|
+
? `Gradle wrapper not found at ${command.command}.`
|
|
98
|
+
: command.source === "path"
|
|
99
|
+
? "`gradle` was not found on PATH and no wrapper exists at the build cwd. " +
|
|
100
|
+
"Either commit a gradle wrapper to the project or install Gradle and add it to PATH."
|
|
101
|
+
: `Build command ${command.command} was not found.`);
|
|
102
|
+
}
|
|
103
|
+
if (e.killed) {
|
|
104
|
+
throw new FlingError("BUILD_TIMEOUT", `Build timed out after ${timeoutMs}ms`, { stdout: e.stdout, stderr: e.stderr });
|
|
105
|
+
}
|
|
106
|
+
const reason = extractBuildFailureReason(e.stdout ?? "", e.stderr ?? "");
|
|
107
|
+
const exitCode = typeof e.code === "number" ? e.code : undefined;
|
|
108
|
+
throw new FlingError("BUILD_FAILED", `Build failed${exitCode !== undefined ? ` (exit ${exitCode})` : ""}:\n${reason}`, { stdout: e.stdout, stderr: e.stderr, exitCode });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Pull the most useful failure block out of a verbose gradle log. Falls
|
|
113
|
+
* back to the last 25 non-empty lines if no marker is found.
|
|
114
|
+
*/
|
|
115
|
+
export function extractBuildFailureReason(stdout, stderr) {
|
|
116
|
+
const combined = `${stdout}\n${stderr}`.replace(/\r\n/g, "\n");
|
|
117
|
+
const whatWentWrong = combined.match(/\* What went wrong:[\s\S]*?(?=\n\* Try:|\n\* Exception|\nBUILD FAILED|\n\* Get more help|$)/);
|
|
118
|
+
if (whatWentWrong)
|
|
119
|
+
return whatWentWrong[0].trim();
|
|
120
|
+
const buildFailedIdx = combined.lastIndexOf("BUILD FAILED");
|
|
121
|
+
if (buildFailedIdx >= 0) {
|
|
122
|
+
const tail = combined.slice(Math.max(0, buildFailedIdx - 4000));
|
|
123
|
+
return tail.trim();
|
|
124
|
+
}
|
|
125
|
+
const lines = combined.split("\n").filter((l) => l.trim().length > 0);
|
|
126
|
+
return lines.slice(-25).join("\n").trim() || "(no output)";
|
|
127
|
+
}
|
|
128
|
+
export { DEFAULT_GRADLE_TASK, join as joinPath };
|
|
129
|
+
//# sourceMappingURL=gradle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gradle.js","sourceRoot":"","sources":["../src/gradle.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
import { registerBuildApp } from "./tools/build-app.js";
|
|
8
|
+
import { registerDeployAndRun } from "./tools/deploy-and-run.js";
|
|
9
|
+
import { registerDumpUi } from "./tools/dump-ui.js";
|
|
10
|
+
import { registerFindOnScreen } from "./tools/find-on-screen.js";
|
|
11
|
+
import { registerInstallApp } from "./tools/install-app.js";
|
|
12
|
+
import { registerLaunchApp } from "./tools/launch-app.js";
|
|
13
|
+
import { registerListDevices } from "./tools/list-devices.js";
|
|
14
|
+
import { registerReadLogs } from "./tools/read-logs.js";
|
|
15
|
+
import { registerScreenshot } from "./tools/screenshot.js";
|
|
16
|
+
import { registerStopApp } from "./tools/stop-app.js";
|
|
17
|
+
import { registerTapByContentDesc } from "./tools/tap-by-content-desc.js";
|
|
18
|
+
import { registerTapByResourceId } from "./tools/tap-by-resource-id.js";
|
|
19
|
+
import { registerTapByText } from "./tools/tap-by-text.js";
|
|
20
|
+
import { registerDismissDialog } from "./tools/dismiss-dialog.js";
|
|
21
|
+
import { registerUninstallApp } from "./tools/uninstall-app.js";
|
|
22
|
+
import { registerWaitFor } from "./tools/wait-for.js";
|
|
23
|
+
import { registerScrollUntilVisible } from "./tools/scroll-until-visible.js";
|
|
24
|
+
import { registerOpenSetting } from "./tools/open-setting.js";
|
|
25
|
+
import { registerTapTextVerified } from "./tools/tap-text-verified.js";
|
|
26
|
+
import { registerLaunchAndWait } from "./tools/launch-and-wait.js";
|
|
27
|
+
import { registerScreenshotWithUi } from "./tools/screenshot-with-ui.js";
|
|
28
|
+
import { registerDeviceState } from "./tools/device-state.js";
|
|
29
|
+
import { registerStartPairQr } from "./tools/start-pair-qr.js";
|
|
30
|
+
import { registerWaitForPair } from "./tools/wait-for-pair.js";
|
|
31
|
+
import { shutdownPool } from "./shellPool.js";
|
|
32
|
+
const SERVER_NAME = "fling";
|
|
33
|
+
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
34
|
+
const SERVER_VERSION = JSON.parse(readFileSync(pkgPath, "utf8")).version;
|
|
35
|
+
async function main() {
|
|
36
|
+
process.on("exit", () => shutdownPool());
|
|
37
|
+
process.on("SIGINT", () => {
|
|
38
|
+
shutdownPool();
|
|
39
|
+
process.exit(0);
|
|
40
|
+
});
|
|
41
|
+
const server = new McpServer({
|
|
42
|
+
name: SERVER_NAME,
|
|
43
|
+
version: SERVER_VERSION,
|
|
44
|
+
});
|
|
45
|
+
registerListDevices(server);
|
|
46
|
+
registerBuildApp(server);
|
|
47
|
+
registerInstallApp(server);
|
|
48
|
+
registerLaunchApp(server);
|
|
49
|
+
registerStopApp(server);
|
|
50
|
+
registerUninstallApp(server);
|
|
51
|
+
registerReadLogs(server);
|
|
52
|
+
registerScreenshot(server);
|
|
53
|
+
registerTapByText(server);
|
|
54
|
+
registerTapByResourceId(server);
|
|
55
|
+
registerTapByContentDesc(server);
|
|
56
|
+
registerDismissDialog(server);
|
|
57
|
+
registerWaitFor(server);
|
|
58
|
+
registerScrollUntilVisible(server);
|
|
59
|
+
registerDumpUi(server);
|
|
60
|
+
registerFindOnScreen(server);
|
|
61
|
+
registerDeployAndRun(server);
|
|
62
|
+
registerOpenSetting(server);
|
|
63
|
+
registerTapTextVerified(server);
|
|
64
|
+
registerLaunchAndWait(server);
|
|
65
|
+
registerScreenshotWithUi(server);
|
|
66
|
+
registerDeviceState(server);
|
|
67
|
+
registerStartPairQr(server);
|
|
68
|
+
registerWaitForPair(server);
|
|
69
|
+
const transport = new StdioServerTransport();
|
|
70
|
+
await server.connect(transport);
|
|
71
|
+
}
|
|
72
|
+
main().catch((err) => {
|
|
73
|
+
process.stderr.write(`[fling] fatal: ${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/mdns.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface MdnsService {
|
|
2
|
+
serviceName: string;
|
|
3
|
+
host: string;
|
|
4
|
+
port: number;
|
|
5
|
+
}
|
|
6
|
+
export interface ParsedMdns {
|
|
7
|
+
pairing: MdnsService[];
|
|
8
|
+
connect: MdnsService[];
|
|
9
|
+
daemonAvailable: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function parseMdnsServices(stdout: string): ParsedMdns;
|
|
12
|
+
type RunAdbFn = (args: string[]) => Promise<{
|
|
13
|
+
stdout: string;
|
|
14
|
+
stderr: string;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function __setRunAdbForTest(fn: RunAdbFn | null): void;
|
|
17
|
+
export declare function discoverPairingPort(serviceName: string, timeoutMs: number, pollIntervalMs?: number): Promise<MdnsService>;
|
|
18
|
+
export declare function discoverConnectByHost(host: string, timeoutMs: number, pollIntervalMs?: number): Promise<MdnsService>;
|
|
19
|
+
export {};
|
package/dist/mdns.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const PAIRING_TYPE = "_adb-tls-pairing._tcp.";
|
|
2
|
+
const CONNECT_TYPE = "_adb-tls-connect._tcp.";
|
|
3
|
+
export function parseMdnsServices(stdout) {
|
|
4
|
+
const lines = stdout.split(/\r?\n/);
|
|
5
|
+
for (const line of lines) {
|
|
6
|
+
if (line.trim().startsWith("ERROR")) {
|
|
7
|
+
return { pairing: [], connect: [], daemonAvailable: false };
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const pairing = [];
|
|
11
|
+
const connect = [];
|
|
12
|
+
for (const line of lines) {
|
|
13
|
+
if (!line.trim() || line.startsWith("List of discovered"))
|
|
14
|
+
continue;
|
|
15
|
+
const parts = line.split(/\t+/).map((s) => s.trim()).filter(Boolean);
|
|
16
|
+
if (parts.length !== 3)
|
|
17
|
+
continue;
|
|
18
|
+
const [serviceName, type, hostPort] = parts;
|
|
19
|
+
const m = /^([0-9.]+):(\d+)$/.exec(hostPort);
|
|
20
|
+
if (!m)
|
|
21
|
+
continue;
|
|
22
|
+
const entry = { serviceName, host: m[1], port: parseInt(m[2], 10) };
|
|
23
|
+
if (type === PAIRING_TYPE)
|
|
24
|
+
pairing.push(entry);
|
|
25
|
+
else if (type === CONNECT_TYPE)
|
|
26
|
+
connect.push(entry);
|
|
27
|
+
}
|
|
28
|
+
return { pairing, connect, daemonAvailable: true };
|
|
29
|
+
}
|
|
30
|
+
import { runAdb } from "./adb.js";
|
|
31
|
+
import { FlingError } from "./errors.js";
|
|
32
|
+
let runAdbImpl = runAdb;
|
|
33
|
+
export function __setRunAdbForTest(fn) {
|
|
34
|
+
runAdbImpl = fn ?? runAdb;
|
|
35
|
+
}
|
|
36
|
+
async function pollMdns() {
|
|
37
|
+
const { stdout } = await runAdbImpl(["mdns", "services"]);
|
|
38
|
+
return parseMdnsServices(stdout);
|
|
39
|
+
}
|
|
40
|
+
export async function discoverPairingPort(serviceName, timeoutMs, pollIntervalMs = 500) {
|
|
41
|
+
const deadline = Date.now() + timeoutMs;
|
|
42
|
+
while (Date.now() < deadline) {
|
|
43
|
+
const r = await pollMdns();
|
|
44
|
+
if (!r.daemonAvailable) {
|
|
45
|
+
throw new FlingError("MDNS_UNAVAILABLE", "adb mdns daemon is not available.");
|
|
46
|
+
}
|
|
47
|
+
const match = r.pairing.find((s) => s.serviceName === serviceName);
|
|
48
|
+
if (match)
|
|
49
|
+
return match;
|
|
50
|
+
await new Promise((res) => setTimeout(res, pollIntervalMs));
|
|
51
|
+
}
|
|
52
|
+
throw new FlingError("PAIRING_TIMEOUT", `No mDNS pairing service '${serviceName}' within ${timeoutMs}ms.`);
|
|
53
|
+
}
|
|
54
|
+
export async function discoverConnectByHost(host, timeoutMs, pollIntervalMs = 500) {
|
|
55
|
+
const deadline = Date.now() + timeoutMs;
|
|
56
|
+
while (Date.now() < deadline) {
|
|
57
|
+
const r = await pollMdns();
|
|
58
|
+
if (!r.daemonAvailable) {
|
|
59
|
+
throw new FlingError("MDNS_UNAVAILABLE", "adb mdns daemon is not available.");
|
|
60
|
+
}
|
|
61
|
+
const match = r.connect.find((s) => s.host === host);
|
|
62
|
+
if (match)
|
|
63
|
+
return match;
|
|
64
|
+
await new Promise((res) => setTimeout(res, pollIntervalMs));
|
|
65
|
+
}
|
|
66
|
+
throw new FlingError("PAIRING_TIMEOUT", `No mDNS connect service on host '${host}' within ${timeoutMs}ms.`);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=mdns.js.map
|
package/dist/mdns.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mdns.js","sourceRoot":"","sources":["../src/mdns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { discoverPairingPort as realDiscoverPairing, discoverConnectByHost as realDiscoverConnect } from "./mdns.js";
|
|
2
|
+
export { discoverConnectByHost } from "./mdns.js";
|
|
3
|
+
export type PairStatus = {
|
|
4
|
+
kind: "waiting";
|
|
5
|
+
} | {
|
|
6
|
+
kind: "pairing";
|
|
7
|
+
} | {
|
|
8
|
+
kind: "connecting";
|
|
9
|
+
} | {
|
|
10
|
+
kind: "success";
|
|
11
|
+
serial: string;
|
|
12
|
+
model: string;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "error";
|
|
15
|
+
reason: string;
|
|
16
|
+
rawAdbError?: string;
|
|
17
|
+
} | {
|
|
18
|
+
kind: "timeout";
|
|
19
|
+
};
|
|
20
|
+
export interface StartPairQrOptions {
|
|
21
|
+
onStatus?: (s: PairStatus) => void;
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
}
|
|
25
|
+
export interface StartPairQrResult {
|
|
26
|
+
qrText: string;
|
|
27
|
+
serviceName: string;
|
|
28
|
+
done: Promise<PairStatus>;
|
|
29
|
+
}
|
|
30
|
+
export interface PairWithCodeOptions {
|
|
31
|
+
host: string;
|
|
32
|
+
port: number;
|
|
33
|
+
code: string;
|
|
34
|
+
onStatus?: (s: PairStatus) => void;
|
|
35
|
+
timeoutMs?: number;
|
|
36
|
+
signal?: AbortSignal;
|
|
37
|
+
}
|
|
38
|
+
type RunAdbFn = (args: string[], opts?: {
|
|
39
|
+
stdin?: string;
|
|
40
|
+
timeoutMs?: number;
|
|
41
|
+
}) => Promise<{
|
|
42
|
+
stdout: string;
|
|
43
|
+
stderr: string;
|
|
44
|
+
}>;
|
|
45
|
+
export declare function __setRunAdbForTest(fn: RunAdbFn | null): void;
|
|
46
|
+
interface Discover {
|
|
47
|
+
discoverPairingPort: typeof realDiscoverPairing;
|
|
48
|
+
discoverConnectByHost: typeof realDiscoverConnect;
|
|
49
|
+
}
|
|
50
|
+
export declare function __setDiscoverForTest(d: Discover | null): void;
|
|
51
|
+
export declare function startPairQr(opts?: StartPairQrOptions): StartPairQrResult;
|
|
52
|
+
export declare function pairWithCode(opts: PairWithCodeOptions): Promise<PairStatus>;
|