mobile-device-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +181 -0
- package/dist/ai/analyzer.d.ts +98 -0
- package/dist/ai/analyzer.d.ts.map +1 -0
- package/dist/ai/analyzer.js +451 -0
- package/dist/ai/analyzer.js.map +1 -0
- package/dist/ai/client.d.ts +92 -0
- package/dist/ai/client.d.ts.map +1 -0
- package/dist/ai/client.js +281 -0
- package/dist/ai/client.js.map +1 -0
- package/dist/ai/element-search.d.ts +12 -0
- package/dist/ai/element-search.d.ts.map +1 -0
- package/dist/ai/element-search.js +387 -0
- package/dist/ai/element-search.js.map +1 -0
- package/dist/ai/prompts.d.ts +27 -0
- package/dist/ai/prompts.d.ts.map +1 -0
- package/dist/ai/prompts.js +153 -0
- package/dist/ai/prompts.js.map +1 -0
- package/dist/drivers/android/adb.d.ts +21 -0
- package/dist/drivers/android/adb.d.ts.map +1 -0
- package/dist/drivers/android/adb.js +122 -0
- package/dist/drivers/android/adb.js.map +1 -0
- package/dist/drivers/android/index.d.ts +70 -0
- package/dist/drivers/android/index.d.ts.map +1 -0
- package/dist/drivers/android/index.js +529 -0
- package/dist/drivers/android/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +131 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +41 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/ai-tools.d.ts +11 -0
- package/dist/tools/ai-tools.d.ts.map +1 -0
- package/dist/tools/ai-tools.js +238 -0
- package/dist/tools/ai-tools.js.map +1 -0
- package/dist/tools/app-tools.d.ts +4 -0
- package/dist/tools/app-tools.d.ts.map +1 -0
- package/dist/tools/app-tools.js +222 -0
- package/dist/tools/app-tools.js.map +1 -0
- package/dist/tools/device-tools.d.ts +4 -0
- package/dist/tools/device-tools.d.ts.map +1 -0
- package/dist/tools/device-tools.js +104 -0
- package/dist/tools/device-tools.js.map +1 -0
- package/dist/tools/index.d.ts +21 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +30 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/interaction-tools.d.ts +4 -0
- package/dist/tools/interaction-tools.d.ts.map +1 -0
- package/dist/tools/interaction-tools.js +304 -0
- package/dist/tools/interaction-tools.js.map +1 -0
- package/dist/tools/log-tools.d.ts +4 -0
- package/dist/tools/log-tools.d.ts.map +1 -0
- package/dist/tools/log-tools.js +60 -0
- package/dist/tools/log-tools.js.map +1 -0
- package/dist/tools/screen-tools.d.ts +4 -0
- package/dist/tools/screen-tools.d.ts.map +1 -0
- package/dist/tools/screen-tools.js +105 -0
- package/dist/tools/screen-tools.js.map +1 -0
- package/dist/types.d.ts +219 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/discovery.d.ts +20 -0
- package/dist/utils/discovery.d.ts.map +1 -0
- package/dist/utils/discovery.js +156 -0
- package/dist/utils/discovery.js.map +1 -0
- package/dist/utils/image.d.ts +46 -0
- package/dist/utils/image.d.ts.map +1 -0
- package/dist/utils/image.js +170 -0
- package/dist/utils/image.js.map +1 -0
- package/package.json +69 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
export interface DeviceInfo {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
model: string;
|
|
5
|
+
manufacturer: string;
|
|
6
|
+
androidVersion: string;
|
|
7
|
+
sdkVersion: string;
|
|
8
|
+
status: "device" | "offline" | "unauthorized" | "unknown";
|
|
9
|
+
isEmulator: boolean;
|
|
10
|
+
screenSize?: {
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface ScreenshotResult {
|
|
16
|
+
base64: string;
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
format: "png" | "jpeg";
|
|
20
|
+
timestamp: number;
|
|
21
|
+
sizeBytes: number;
|
|
22
|
+
}
|
|
23
|
+
export interface UIElement {
|
|
24
|
+
index: number;
|
|
25
|
+
text: string;
|
|
26
|
+
contentDescription: string;
|
|
27
|
+
className: string;
|
|
28
|
+
packageName: string;
|
|
29
|
+
resourceId: string;
|
|
30
|
+
bounds: {
|
|
31
|
+
left: number;
|
|
32
|
+
top: number;
|
|
33
|
+
right: number;
|
|
34
|
+
bottom: number;
|
|
35
|
+
centerX: number;
|
|
36
|
+
centerY: number;
|
|
37
|
+
};
|
|
38
|
+
clickable: boolean;
|
|
39
|
+
scrollable: boolean;
|
|
40
|
+
focusable: boolean;
|
|
41
|
+
enabled: boolean;
|
|
42
|
+
selected: boolean;
|
|
43
|
+
checked: boolean;
|
|
44
|
+
children?: UIElement[];
|
|
45
|
+
}
|
|
46
|
+
export interface TapResult {
|
|
47
|
+
success: boolean;
|
|
48
|
+
x: number;
|
|
49
|
+
y: number;
|
|
50
|
+
}
|
|
51
|
+
export interface SwipeResult {
|
|
52
|
+
success: boolean;
|
|
53
|
+
startX: number;
|
|
54
|
+
startY: number;
|
|
55
|
+
endX: number;
|
|
56
|
+
endY: number;
|
|
57
|
+
duration: number;
|
|
58
|
+
}
|
|
59
|
+
export interface AppInfo {
|
|
60
|
+
packageName: string;
|
|
61
|
+
appName?: string;
|
|
62
|
+
versionName?: string;
|
|
63
|
+
versionCode?: number;
|
|
64
|
+
isSystemApp: boolean;
|
|
65
|
+
}
|
|
66
|
+
export interface LogEntry {
|
|
67
|
+
timestamp: string;
|
|
68
|
+
pid: number;
|
|
69
|
+
tid: number;
|
|
70
|
+
level: "V" | "D" | "I" | "W" | "E" | "F";
|
|
71
|
+
tag: string;
|
|
72
|
+
message: string;
|
|
73
|
+
}
|
|
74
|
+
export interface LogOptions {
|
|
75
|
+
filter?: string;
|
|
76
|
+
level?: LogEntry["level"];
|
|
77
|
+
lines?: number;
|
|
78
|
+
since?: string;
|
|
79
|
+
pid?: number;
|
|
80
|
+
}
|
|
81
|
+
export interface ADBResult {
|
|
82
|
+
stdout: string;
|
|
83
|
+
stderr: string;
|
|
84
|
+
exitCode: number;
|
|
85
|
+
}
|
|
86
|
+
export interface DeviceDriver {
|
|
87
|
+
listDevices(): Promise<DeviceInfo[]>;
|
|
88
|
+
getDeviceInfo(deviceId: string): Promise<DeviceInfo>;
|
|
89
|
+
getScreenSize(deviceId: string): Promise<{
|
|
90
|
+
width: number;
|
|
91
|
+
height: number;
|
|
92
|
+
}>;
|
|
93
|
+
takeScreenshot(deviceId: string, options?: ScreenshotOptions): Promise<ScreenshotResult>;
|
|
94
|
+
getUIElements(deviceId: string, options?: UIElementOptions): Promise<UIElement[]>;
|
|
95
|
+
tap(deviceId: string, x: number, y: number): Promise<TapResult>;
|
|
96
|
+
doubleTap(deviceId: string, x: number, y: number): Promise<TapResult>;
|
|
97
|
+
longPress(deviceId: string, x: number, y: number, duration?: number): Promise<TapResult>;
|
|
98
|
+
swipe(deviceId: string, startX: number, startY: number, endX: number, endY: number, duration?: number): Promise<SwipeResult>;
|
|
99
|
+
typeText(deviceId: string, text: string): Promise<{
|
|
100
|
+
success: boolean;
|
|
101
|
+
}>;
|
|
102
|
+
pressKey(deviceId: string, keycode: string): Promise<{
|
|
103
|
+
success: boolean;
|
|
104
|
+
}>;
|
|
105
|
+
listApps(deviceId: string, includeSystem?: boolean): Promise<AppInfo[]>;
|
|
106
|
+
getCurrentApp(deviceId: string): Promise<{
|
|
107
|
+
packageName: string;
|
|
108
|
+
activityName: string;
|
|
109
|
+
}>;
|
|
110
|
+
launchApp(deviceId: string, packageName: string): Promise<{
|
|
111
|
+
success: boolean;
|
|
112
|
+
}>;
|
|
113
|
+
stopApp(deviceId: string, packageName: string): Promise<{
|
|
114
|
+
success: boolean;
|
|
115
|
+
}>;
|
|
116
|
+
installApp(deviceId: string, apkPath: string): Promise<{
|
|
117
|
+
success: boolean;
|
|
118
|
+
}>;
|
|
119
|
+
uninstallApp(deviceId: string, packageName: string): Promise<{
|
|
120
|
+
success: boolean;
|
|
121
|
+
}>;
|
|
122
|
+
getLogs(deviceId: string, options?: LogOptions): Promise<LogEntry[]>;
|
|
123
|
+
shell(deviceId: string, command: string): Promise<ADBResult>;
|
|
124
|
+
}
|
|
125
|
+
export interface ScreenshotOptions {
|
|
126
|
+
format?: "png" | "jpeg";
|
|
127
|
+
quality?: number;
|
|
128
|
+
maxWidth?: number;
|
|
129
|
+
}
|
|
130
|
+
export interface UIElementOptions {
|
|
131
|
+
interactiveOnly?: boolean;
|
|
132
|
+
maxDepth?: number;
|
|
133
|
+
}
|
|
134
|
+
export interface ScreenAnalysis {
|
|
135
|
+
description: string;
|
|
136
|
+
appName: string;
|
|
137
|
+
screenType: string;
|
|
138
|
+
elements: AnalyzedElement[];
|
|
139
|
+
visibleText: string[];
|
|
140
|
+
suggestions: string[];
|
|
141
|
+
}
|
|
142
|
+
export interface AnalyzedElement {
|
|
143
|
+
description: string;
|
|
144
|
+
type: string;
|
|
145
|
+
text: string;
|
|
146
|
+
bounds: {
|
|
147
|
+
left: number;
|
|
148
|
+
top: number;
|
|
149
|
+
right: number;
|
|
150
|
+
bottom: number;
|
|
151
|
+
centerX: number;
|
|
152
|
+
centerY: number;
|
|
153
|
+
};
|
|
154
|
+
suggestedAction: string;
|
|
155
|
+
confidence: number;
|
|
156
|
+
}
|
|
157
|
+
export interface VisualDiff {
|
|
158
|
+
hasChanges: boolean;
|
|
159
|
+
summary: string;
|
|
160
|
+
changes: DiffChange[];
|
|
161
|
+
}
|
|
162
|
+
export interface DiffChange {
|
|
163
|
+
description: string;
|
|
164
|
+
region: string;
|
|
165
|
+
type: "added" | "removed" | "changed";
|
|
166
|
+
}
|
|
167
|
+
export interface ElementMatch {
|
|
168
|
+
found: boolean;
|
|
169
|
+
element?: AnalyzedElement;
|
|
170
|
+
confidence: number;
|
|
171
|
+
alternatives?: AnalyzedElement[];
|
|
172
|
+
}
|
|
173
|
+
export interface ActionPlan {
|
|
174
|
+
goal: string;
|
|
175
|
+
steps: PlannedAction[];
|
|
176
|
+
}
|
|
177
|
+
export interface PlannedAction {
|
|
178
|
+
step: number;
|
|
179
|
+
action: "tap" | "type" | "swipe" | "press_key" | "wait" | "long_press";
|
|
180
|
+
target: string;
|
|
181
|
+
coordinates?: {
|
|
182
|
+
x: number;
|
|
183
|
+
y: number;
|
|
184
|
+
};
|
|
185
|
+
text?: string;
|
|
186
|
+
key?: string;
|
|
187
|
+
swipe?: {
|
|
188
|
+
startX: number;
|
|
189
|
+
startY: number;
|
|
190
|
+
endX: number;
|
|
191
|
+
endY: number;
|
|
192
|
+
};
|
|
193
|
+
description: string;
|
|
194
|
+
}
|
|
195
|
+
export interface ScreenVerification {
|
|
196
|
+
verified: boolean;
|
|
197
|
+
confidence: number;
|
|
198
|
+
details: string;
|
|
199
|
+
evidence: string[];
|
|
200
|
+
}
|
|
201
|
+
export interface AIConfig {
|
|
202
|
+
provider: "anthropic" | "google";
|
|
203
|
+
apiKey: string;
|
|
204
|
+
model: string;
|
|
205
|
+
maxTokens: number;
|
|
206
|
+
analyzeWithScreenshot: boolean;
|
|
207
|
+
analyzeWithUITree: boolean;
|
|
208
|
+
}
|
|
209
|
+
export declare const DEFAULT_AI_CONFIG: AIConfig;
|
|
210
|
+
export interface ServerConfig {
|
|
211
|
+
adbPath: string;
|
|
212
|
+
defaultDevice?: string;
|
|
213
|
+
screenshotFormat: "png" | "jpeg";
|
|
214
|
+
screenshotQuality: number;
|
|
215
|
+
screenshotMaxWidth: number;
|
|
216
|
+
ai?: AIConfig;
|
|
217
|
+
}
|
|
218
|
+
export declare const DEFAULT_CONFIG: ServerConfig;
|
|
219
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Mobile Device MCP Server — Shared Types & Interfaces
|
|
3
|
+
// All modules implement against these contracts.
|
|
4
|
+
// ============================================================
|
|
5
|
+
export const DEFAULT_AI_CONFIG = {
|
|
6
|
+
provider: "anthropic",
|
|
7
|
+
apiKey: "",
|
|
8
|
+
model: "claude-sonnet-4-20250514",
|
|
9
|
+
maxTokens: 4096,
|
|
10
|
+
analyzeWithScreenshot: true,
|
|
11
|
+
analyzeWithUITree: true,
|
|
12
|
+
};
|
|
13
|
+
export const DEFAULT_CONFIG = {
|
|
14
|
+
adbPath: "adb",
|
|
15
|
+
screenshotFormat: "jpeg",
|
|
16
|
+
screenshotQuality: 80,
|
|
17
|
+
screenshotMaxWidth: 720,
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,uDAAuD;AACvD,iDAAiD;AACjD,+DAA+D;AAwO/D,MAAM,CAAC,MAAM,iBAAiB,GAAa;IACzC,QAAQ,EAAE,WAAW;IACrB,MAAM,EAAE,EAAE;IACV,KAAK,EAAE,0BAA0B;IACjC,SAAS,EAAE,IAAI;IACf,qBAAqB,EAAE,IAAI;IAC3B,iBAAiB,EAAE,IAAI;CACxB,CAAC;AAeF,MAAM,CAAC,MAAM,cAAc,GAAiB;IAC1C,OAAO,EAAE,KAAK;IACd,gBAAgB,EAAE,MAAM;IACxB,iBAAiB,EAAE,EAAE;IACrB,kBAAkB,EAAE,GAAG;CACxB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locate a working ADB binary.
|
|
3
|
+
*
|
|
4
|
+
* Search order:
|
|
5
|
+
* 1. ADB_PATH env var
|
|
6
|
+
* 2. ANDROID_HOME/platform-tools/adb
|
|
7
|
+
* 3. ANDROID_SDK_ROOT/platform-tools/adb
|
|
8
|
+
* 4. "adb" on PATH
|
|
9
|
+
* 5. Common OS-specific install locations
|
|
10
|
+
*
|
|
11
|
+
* Each candidate is verified by running `adb version`.
|
|
12
|
+
* Returns the first working path or throws with installation help.
|
|
13
|
+
*/
|
|
14
|
+
export declare function findAdbPath(): Promise<string>;
|
|
15
|
+
/**
|
|
16
|
+
* If exactly one device is connected, return its serial ID.
|
|
17
|
+
* Returns `undefined` when zero or multiple devices are present.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getDefaultDevice(adbPath: string): Promise<string | undefined>;
|
|
20
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/utils/discovery.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAmFnD;AAkCD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA6B7B"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// ADB auto-discovery & device detection
|
|
3
|
+
// Zero-friction setup: find ADB and connected devices automatically.
|
|
4
|
+
// ============================================================
|
|
5
|
+
import { execFile as execFileCb } from "node:child_process";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { homedir, platform } from "node:os";
|
|
10
|
+
const execFile = promisify(execFileCb);
|
|
11
|
+
/** Timeout for verification commands (ms). */
|
|
12
|
+
const VERIFY_TIMEOUT_MS = 10_000;
|
|
13
|
+
// ------------------------------------------------------------------
|
|
14
|
+
// findAdbPath
|
|
15
|
+
// ------------------------------------------------------------------
|
|
16
|
+
/**
|
|
17
|
+
* Locate a working ADB binary.
|
|
18
|
+
*
|
|
19
|
+
* Search order:
|
|
20
|
+
* 1. ADB_PATH env var
|
|
21
|
+
* 2. ANDROID_HOME/platform-tools/adb
|
|
22
|
+
* 3. ANDROID_SDK_ROOT/platform-tools/adb
|
|
23
|
+
* 4. "adb" on PATH
|
|
24
|
+
* 5. Common OS-specific install locations
|
|
25
|
+
*
|
|
26
|
+
* Each candidate is verified by running `adb version`.
|
|
27
|
+
* Returns the first working path or throws with installation help.
|
|
28
|
+
*/
|
|
29
|
+
export async function findAdbPath() {
|
|
30
|
+
const isWin = platform() === "win32";
|
|
31
|
+
const ext = isWin ? ".exe" : "";
|
|
32
|
+
const binaryName = `adb${ext}`;
|
|
33
|
+
const candidates = [];
|
|
34
|
+
// 1. Explicit env var
|
|
35
|
+
const envAdbPath = process.env["ADB_PATH"];
|
|
36
|
+
if (envAdbPath) {
|
|
37
|
+
candidates.push(envAdbPath);
|
|
38
|
+
}
|
|
39
|
+
// 2. ANDROID_HOME
|
|
40
|
+
const androidHome = process.env["ANDROID_HOME"];
|
|
41
|
+
if (androidHome) {
|
|
42
|
+
candidates.push(join(androidHome, "platform-tools", binaryName));
|
|
43
|
+
}
|
|
44
|
+
// 3. ANDROID_SDK_ROOT
|
|
45
|
+
const androidSdkRoot = process.env["ANDROID_SDK_ROOT"];
|
|
46
|
+
if (androidSdkRoot) {
|
|
47
|
+
candidates.push(join(androidSdkRoot, "platform-tools", binaryName));
|
|
48
|
+
}
|
|
49
|
+
// 4. Bare "adb" — rely on PATH
|
|
50
|
+
candidates.push("adb");
|
|
51
|
+
// 5. Common install locations
|
|
52
|
+
const home = homedir();
|
|
53
|
+
if (isWin) {
|
|
54
|
+
// Windows-specific paths
|
|
55
|
+
candidates.push(join(home, "AppData", "Local", "Android", "Sdk", "platform-tools", "adb.exe"), "C:\\Android\\sdk\\platform-tools\\adb.exe");
|
|
56
|
+
}
|
|
57
|
+
else if (platform() === "darwin") {
|
|
58
|
+
// macOS
|
|
59
|
+
candidates.push(join(home, "Library", "Android", "sdk", "platform-tools", "adb"));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// Linux
|
|
63
|
+
candidates.push(join(home, "Android", "Sdk", "platform-tools", "adb"), "/usr/lib/android-sdk/platform-tools/adb");
|
|
64
|
+
}
|
|
65
|
+
// De-duplicate while preserving order
|
|
66
|
+
const seen = new Set();
|
|
67
|
+
const unique = [];
|
|
68
|
+
for (const c of candidates) {
|
|
69
|
+
if (!seen.has(c)) {
|
|
70
|
+
seen.add(c);
|
|
71
|
+
unique.push(c);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Try each candidate
|
|
75
|
+
for (const candidate of unique) {
|
|
76
|
+
// For absolute paths, skip if file doesn't exist (except bare "adb" which relies on PATH)
|
|
77
|
+
if (candidate !== "adb" && !isAbsoluteOrRelativeBinary(candidate)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (await verifyAdb(candidate)) {
|
|
81
|
+
return candidate;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
throw new Error([
|
|
85
|
+
"Could not find a working ADB installation.",
|
|
86
|
+
"",
|
|
87
|
+
"To fix this, do ONE of the following:",
|
|
88
|
+
" 1. Install Android SDK Platform Tools:",
|
|
89
|
+
" https://developer.android.com/tools/releases/platform-tools",
|
|
90
|
+
" 2. Set the ADB_PATH environment variable to the full path of your adb binary.",
|
|
91
|
+
" 3. Set ANDROID_HOME or ANDROID_SDK_ROOT to your SDK directory.",
|
|
92
|
+
" 4. Add the platform-tools directory to your system PATH.",
|
|
93
|
+
].join("\n"));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Check whether a candidate path looks like it could be a binary
|
|
97
|
+
* (exists on disk, or is a bare name to be found on PATH).
|
|
98
|
+
*/
|
|
99
|
+
function isAbsoluteOrRelativeBinary(candidate) {
|
|
100
|
+
// Bare command names like "adb" are handled separately
|
|
101
|
+
if (!candidate.includes("/") && !candidate.includes("\\")) {
|
|
102
|
+
return true; // bare name — will be resolved by PATH
|
|
103
|
+
}
|
|
104
|
+
return existsSync(candidate);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Verify that `adb version` runs successfully with the given path.
|
|
108
|
+
*/
|
|
109
|
+
async function verifyAdb(adbPath) {
|
|
110
|
+
try {
|
|
111
|
+
const { stdout } = await execFile(adbPath, ["version"], {
|
|
112
|
+
timeout: VERIFY_TIMEOUT_MS,
|
|
113
|
+
windowsHide: true,
|
|
114
|
+
});
|
|
115
|
+
// Sanity-check output contains expected text
|
|
116
|
+
return stdout.toLowerCase().includes("android debug bridge");
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// ------------------------------------------------------------------
|
|
123
|
+
// getDefaultDevice
|
|
124
|
+
// ------------------------------------------------------------------
|
|
125
|
+
/**
|
|
126
|
+
* If exactly one device is connected, return its serial ID.
|
|
127
|
+
* Returns `undefined` when zero or multiple devices are present.
|
|
128
|
+
*/
|
|
129
|
+
export async function getDefaultDevice(adbPath) {
|
|
130
|
+
try {
|
|
131
|
+
const { stdout } = await execFile(adbPath, ["devices"], {
|
|
132
|
+
timeout: VERIFY_TIMEOUT_MS,
|
|
133
|
+
windowsHide: true,
|
|
134
|
+
});
|
|
135
|
+
const lines = stdout
|
|
136
|
+
.split("\n")
|
|
137
|
+
.map((l) => l.trim())
|
|
138
|
+
.filter((l) => l.length > 0 && !l.startsWith("List of"));
|
|
139
|
+
// Each line is "SERIAL\tSTATUS"
|
|
140
|
+
const devices = [];
|
|
141
|
+
for (const line of lines) {
|
|
142
|
+
const parts = line.split(/\s+/);
|
|
143
|
+
if (parts.length >= 2 && parts[1] === "device") {
|
|
144
|
+
devices.push(parts[0]);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (devices.length === 1) {
|
|
148
|
+
return devices[0];
|
|
149
|
+
}
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/utils/discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ScreenshotOptions } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Parse width and height from a PNG buffer by reading the IHDR chunk.
|
|
4
|
+
*
|
|
5
|
+
* PNG layout:
|
|
6
|
+
* Bytes 0-7: 8-byte PNG signature
|
|
7
|
+
* Bytes 8-11: IHDR chunk data length (always 13)
|
|
8
|
+
* Bytes 12-15: chunk type "IHDR"
|
|
9
|
+
* Bytes 16-19: width (big-endian uint32)
|
|
10
|
+
* Bytes 20-23: height (big-endian uint32)
|
|
11
|
+
*
|
|
12
|
+
* Throws if the buffer is too small or doesn't look like a valid PNG.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parsePngDimensions(buffer: Buffer): {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Convert a Buffer to a base64-encoded string.
|
|
20
|
+
*/
|
|
21
|
+
export declare function bufferToBase64(buffer: Buffer): string;
|
|
22
|
+
/**
|
|
23
|
+
* Calculate the approximate byte size of data represented by a base64 string.
|
|
24
|
+
*
|
|
25
|
+
* Base64 encodes 3 bytes into 4 characters. The formula accounts for
|
|
26
|
+
* padding characters ('=') which don't represent data.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getImageSizeBytes(base64: string): number;
|
|
29
|
+
export interface ProcessedScreenshot {
|
|
30
|
+
buffer: Buffer;
|
|
31
|
+
base64: string;
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
format: "png" | "jpeg";
|
|
35
|
+
sizeBytes: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Process a raw PNG screenshot buffer: optionally resize and convert to JPEG.
|
|
39
|
+
*
|
|
40
|
+
* Pipeline: PNG buffer → decode to RGBA → resize (if maxWidth set) → encode
|
|
41
|
+
*
|
|
42
|
+
* When format is "png" and no resize is needed, returns the original buffer
|
|
43
|
+
* untouched (zero-cost passthrough).
|
|
44
|
+
*/
|
|
45
|
+
export declare function processScreenshot(pngBuffer: Buffer, options?: ScreenshotOptions): ProcessedScreenshot;
|
|
46
|
+
//# sourceMappingURL=image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/utils/image.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAMrD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,GACb;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAgCnC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAQxD;AAMD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,mBAAmB,CA2DrB"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Image processing utilities
|
|
3
|
+
//
|
|
4
|
+
// Provides PNG parsing, JPEG compression, and resize capabilities
|
|
5
|
+
// using pure-JS libraries (no native dependencies) for zero-config
|
|
6
|
+
// npm distribution.
|
|
7
|
+
// ============================================================
|
|
8
|
+
import { PNG } from "pngjs";
|
|
9
|
+
import * as jpeg from "jpeg-js";
|
|
10
|
+
// ------------------------------------------------------------------
|
|
11
|
+
// PNG parsing (lightweight — no full decode needed)
|
|
12
|
+
// ------------------------------------------------------------------
|
|
13
|
+
/**
|
|
14
|
+
* Parse width and height from a PNG buffer by reading the IHDR chunk.
|
|
15
|
+
*
|
|
16
|
+
* PNG layout:
|
|
17
|
+
* Bytes 0-7: 8-byte PNG signature
|
|
18
|
+
* Bytes 8-11: IHDR chunk data length (always 13)
|
|
19
|
+
* Bytes 12-15: chunk type "IHDR"
|
|
20
|
+
* Bytes 16-19: width (big-endian uint32)
|
|
21
|
+
* Bytes 20-23: height (big-endian uint32)
|
|
22
|
+
*
|
|
23
|
+
* Throws if the buffer is too small or doesn't look like a valid PNG.
|
|
24
|
+
*/
|
|
25
|
+
export function parsePngDimensions(buffer) {
|
|
26
|
+
// Minimum size: 8 (sig) + 4 (length) + 4 (type) + 8 (w+h) = 24
|
|
27
|
+
if (buffer.length < 24) {
|
|
28
|
+
throw new Error(`Buffer too small to be a valid PNG (${buffer.length} bytes, need at least 24)`);
|
|
29
|
+
}
|
|
30
|
+
// Verify PNG signature (first 8 bytes)
|
|
31
|
+
const PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
32
|
+
if (!buffer.subarray(0, 8).equals(PNG_SIGNATURE)) {
|
|
33
|
+
throw new Error("Buffer does not contain a valid PNG signature");
|
|
34
|
+
}
|
|
35
|
+
// Verify IHDR chunk type at bytes 12-15
|
|
36
|
+
const chunkType = buffer.subarray(12, 16).toString("ascii");
|
|
37
|
+
if (chunkType !== "IHDR") {
|
|
38
|
+
throw new Error(`Expected IHDR chunk but found "${chunkType}"`);
|
|
39
|
+
}
|
|
40
|
+
const width = buffer.readUInt32BE(16);
|
|
41
|
+
const height = buffer.readUInt32BE(20);
|
|
42
|
+
if (width === 0 || height === 0) {
|
|
43
|
+
throw new Error(`Invalid PNG dimensions: ${width}x${height}`);
|
|
44
|
+
}
|
|
45
|
+
return { width, height };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Convert a Buffer to a base64-encoded string.
|
|
49
|
+
*/
|
|
50
|
+
export function bufferToBase64(buffer) {
|
|
51
|
+
return buffer.toString("base64");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Calculate the approximate byte size of data represented by a base64 string.
|
|
55
|
+
*
|
|
56
|
+
* Base64 encodes 3 bytes into 4 characters. The formula accounts for
|
|
57
|
+
* padding characters ('=') which don't represent data.
|
|
58
|
+
*/
|
|
59
|
+
export function getImageSizeBytes(base64) {
|
|
60
|
+
const len = base64.length;
|
|
61
|
+
// Count trailing '=' padding characters
|
|
62
|
+
let padding = 0;
|
|
63
|
+
if (len > 0 && base64[len - 1] === "=")
|
|
64
|
+
padding++;
|
|
65
|
+
if (len > 1 && base64[len - 2] === "=")
|
|
66
|
+
padding++;
|
|
67
|
+
return Math.floor((len * 3) / 4) - padding;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Process a raw PNG screenshot buffer: optionally resize and convert to JPEG.
|
|
71
|
+
*
|
|
72
|
+
* Pipeline: PNG buffer → decode to RGBA → resize (if maxWidth set) → encode
|
|
73
|
+
*
|
|
74
|
+
* When format is "png" and no resize is needed, returns the original buffer
|
|
75
|
+
* untouched (zero-cost passthrough).
|
|
76
|
+
*/
|
|
77
|
+
export function processScreenshot(pngBuffer, options) {
|
|
78
|
+
const format = options?.format ?? "png";
|
|
79
|
+
const quality = options?.quality ?? 80;
|
|
80
|
+
const maxWidth = options?.maxWidth;
|
|
81
|
+
const { width: origWidth, height: origHeight } = parsePngDimensions(pngBuffer);
|
|
82
|
+
// Fast path: no processing needed
|
|
83
|
+
const needsResize = maxWidth !== undefined && maxWidth > 0 && origWidth > maxWidth;
|
|
84
|
+
if (format === "png" && !needsResize) {
|
|
85
|
+
return {
|
|
86
|
+
buffer: pngBuffer,
|
|
87
|
+
base64: pngBuffer.toString("base64"),
|
|
88
|
+
width: origWidth,
|
|
89
|
+
height: origHeight,
|
|
90
|
+
format: "png",
|
|
91
|
+
sizeBytes: pngBuffer.length,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// Decode PNG to raw RGBA pixels
|
|
95
|
+
const decoded = PNG.sync.read(pngBuffer);
|
|
96
|
+
let { width, height, data } = decoded;
|
|
97
|
+
// Resize if needed (bilinear interpolation)
|
|
98
|
+
if (needsResize) {
|
|
99
|
+
const scale = maxWidth / width;
|
|
100
|
+
const newWidth = maxWidth;
|
|
101
|
+
const newHeight = Math.round(height * scale);
|
|
102
|
+
data = resizeBilinear(data, width, height, newWidth, newHeight);
|
|
103
|
+
width = newWidth;
|
|
104
|
+
height = newHeight;
|
|
105
|
+
}
|
|
106
|
+
// Encode to target format
|
|
107
|
+
let outputBuffer;
|
|
108
|
+
if (format === "jpeg") {
|
|
109
|
+
const rawImageData = {
|
|
110
|
+
data,
|
|
111
|
+
width,
|
|
112
|
+
height,
|
|
113
|
+
};
|
|
114
|
+
const encoded = jpeg.encode(rawImageData, quality);
|
|
115
|
+
outputBuffer = encoded.data;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// Re-encode as PNG (only reached if resize happened)
|
|
119
|
+
const png = new PNG({ width, height });
|
|
120
|
+
png.data = data;
|
|
121
|
+
outputBuffer = PNG.sync.write(png);
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
buffer: outputBuffer,
|
|
125
|
+
base64: outputBuffer.toString("base64"),
|
|
126
|
+
width,
|
|
127
|
+
height,
|
|
128
|
+
format,
|
|
129
|
+
sizeBytes: outputBuffer.length,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
// ------------------------------------------------------------------
|
|
133
|
+
// Bilinear interpolation resize
|
|
134
|
+
// ------------------------------------------------------------------
|
|
135
|
+
/**
|
|
136
|
+
* Resize RGBA pixel data using bilinear interpolation.
|
|
137
|
+
* Produces smooth results without jagged edges — important for
|
|
138
|
+
* AI vision models to read text accurately.
|
|
139
|
+
*/
|
|
140
|
+
function resizeBilinear(src, srcW, srcH, dstW, dstH) {
|
|
141
|
+
const dst = Buffer.alloc(dstW * dstH * 4);
|
|
142
|
+
const xRatio = (srcW - 1) / (dstW - 1);
|
|
143
|
+
const yRatio = (srcH - 1) / (dstH - 1);
|
|
144
|
+
for (let y = 0; y < dstH; y++) {
|
|
145
|
+
const srcY = y * yRatio;
|
|
146
|
+
const yFloor = Math.floor(srcY);
|
|
147
|
+
const yCeil = Math.min(yFloor + 1, srcH - 1);
|
|
148
|
+
const yFrac = srcY - yFloor;
|
|
149
|
+
for (let x = 0; x < dstW; x++) {
|
|
150
|
+
const srcX = x * xRatio;
|
|
151
|
+
const xFloor = Math.floor(srcX);
|
|
152
|
+
const xCeil = Math.min(xFloor + 1, srcW - 1);
|
|
153
|
+
const xFrac = srcX - xFloor;
|
|
154
|
+
// Four neighboring pixels
|
|
155
|
+
const tlIdx = (yFloor * srcW + xFloor) * 4;
|
|
156
|
+
const trIdx = (yFloor * srcW + xCeil) * 4;
|
|
157
|
+
const blIdx = (yCeil * srcW + xFloor) * 4;
|
|
158
|
+
const brIdx = (yCeil * srcW + xCeil) * 4;
|
|
159
|
+
const dstIdx = (y * dstW + x) * 4;
|
|
160
|
+
// Interpolate each channel (R, G, B, A)
|
|
161
|
+
for (let c = 0; c < 4; c++) {
|
|
162
|
+
const top = src[tlIdx + c] * (1 - xFrac) + src[trIdx + c] * xFrac;
|
|
163
|
+
const bottom = src[blIdx + c] * (1 - xFrac) + src[brIdx + c] * xFrac;
|
|
164
|
+
dst[dstIdx + c] = Math.round(top * (1 - yFrac) + bottom * yFrac);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return dst;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=image.js.map
|