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.
Files changed (74) hide show
  1. package/README.md +181 -0
  2. package/dist/ai/analyzer.d.ts +98 -0
  3. package/dist/ai/analyzer.d.ts.map +1 -0
  4. package/dist/ai/analyzer.js +451 -0
  5. package/dist/ai/analyzer.js.map +1 -0
  6. package/dist/ai/client.d.ts +92 -0
  7. package/dist/ai/client.d.ts.map +1 -0
  8. package/dist/ai/client.js +281 -0
  9. package/dist/ai/client.js.map +1 -0
  10. package/dist/ai/element-search.d.ts +12 -0
  11. package/dist/ai/element-search.d.ts.map +1 -0
  12. package/dist/ai/element-search.js +387 -0
  13. package/dist/ai/element-search.js.map +1 -0
  14. package/dist/ai/prompts.d.ts +27 -0
  15. package/dist/ai/prompts.d.ts.map +1 -0
  16. package/dist/ai/prompts.js +153 -0
  17. package/dist/ai/prompts.js.map +1 -0
  18. package/dist/drivers/android/adb.d.ts +21 -0
  19. package/dist/drivers/android/adb.d.ts.map +1 -0
  20. package/dist/drivers/android/adb.js +122 -0
  21. package/dist/drivers/android/adb.js.map +1 -0
  22. package/dist/drivers/android/index.d.ts +70 -0
  23. package/dist/drivers/android/index.d.ts.map +1 -0
  24. package/dist/drivers/android/index.js +529 -0
  25. package/dist/drivers/android/index.js.map +1 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +131 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/server.d.ts +13 -0
  31. package/dist/server.d.ts.map +1 -0
  32. package/dist/server.js +41 -0
  33. package/dist/server.js.map +1 -0
  34. package/dist/tools/ai-tools.d.ts +11 -0
  35. package/dist/tools/ai-tools.d.ts.map +1 -0
  36. package/dist/tools/ai-tools.js +238 -0
  37. package/dist/tools/ai-tools.js.map +1 -0
  38. package/dist/tools/app-tools.d.ts +4 -0
  39. package/dist/tools/app-tools.d.ts.map +1 -0
  40. package/dist/tools/app-tools.js +222 -0
  41. package/dist/tools/app-tools.js.map +1 -0
  42. package/dist/tools/device-tools.d.ts +4 -0
  43. package/dist/tools/device-tools.d.ts.map +1 -0
  44. package/dist/tools/device-tools.js +104 -0
  45. package/dist/tools/device-tools.js.map +1 -0
  46. package/dist/tools/index.d.ts +21 -0
  47. package/dist/tools/index.d.ts.map +1 -0
  48. package/dist/tools/index.js +30 -0
  49. package/dist/tools/index.js.map +1 -0
  50. package/dist/tools/interaction-tools.d.ts +4 -0
  51. package/dist/tools/interaction-tools.d.ts.map +1 -0
  52. package/dist/tools/interaction-tools.js +304 -0
  53. package/dist/tools/interaction-tools.js.map +1 -0
  54. package/dist/tools/log-tools.d.ts +4 -0
  55. package/dist/tools/log-tools.d.ts.map +1 -0
  56. package/dist/tools/log-tools.js +60 -0
  57. package/dist/tools/log-tools.js.map +1 -0
  58. package/dist/tools/screen-tools.d.ts +4 -0
  59. package/dist/tools/screen-tools.d.ts.map +1 -0
  60. package/dist/tools/screen-tools.js +105 -0
  61. package/dist/tools/screen-tools.js.map +1 -0
  62. package/dist/types.d.ts +219 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +19 -0
  65. package/dist/types.js.map +1 -0
  66. package/dist/utils/discovery.d.ts +20 -0
  67. package/dist/utils/discovery.d.ts.map +1 -0
  68. package/dist/utils/discovery.js +156 -0
  69. package/dist/utils/discovery.js.map +1 -0
  70. package/dist/utils/image.d.ts +46 -0
  71. package/dist/utils/image.d.ts.map +1 -0
  72. package/dist/utils/image.js +170 -0
  73. package/dist/utils/image.js.map +1 -0
  74. package/package.json +69 -0
@@ -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":"AAKA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,cAAc,GAAG,SAAS,CAAC;IAC1D,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAChD;AAED,MAAM,WAAW,gBAAgB;IAC/B,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;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,SAAS,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAMD,MAAM,WAAW,YAAY;IAE3B,WAAW,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IACrC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACrD,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAG5E,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzF,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAGlF,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAChE,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACtE,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACzF,KAAK,CACH,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACxE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAG3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACxE,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxF,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAChF,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC9E,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC7E,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAGnF,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAGrE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CAC9D;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAMD,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,GAAG,YAAY,CAAC;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAMD,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,WAAW,GAAG,QAAQ,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,eAAO,MAAM,iBAAiB,EAAE,QAO/B,CAAC;AAMF,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,KAAK,GAAG,MAAM,CAAC;IACjC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf;AAED,eAAO,MAAM,cAAc,EAAE,YAK5B,CAAC"}
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":"AAAA,+DAA+D;AAC/D,wCAAwC;AACxC,qEAAqE;AACrE,+DAA+D;AAE/D,OAAO,EAAE,QAAQ,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE5C,MAAM,QAAQ,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;AAEvC,8CAA8C;AAC9C,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAEjC,qEAAqE;AACrE,cAAc;AACd,qEAAqE;AAErE;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,KAAK,GAAG,QAAQ,EAAE,KAAK,OAAO,CAAC;IACrC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAChC,MAAM,UAAU,GAAG,MAAM,GAAG,EAAE,CAAC;IAE/B,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,sBAAsB;IACtB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,UAAU,EAAE,CAAC;QACf,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC9B,CAAC;IAED,kBAAkB;IAClB,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChD,IAAI,WAAW,EAAE,CAAC;QAChB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,gBAAgB,EAAE,UAAU,CAAC,CAAC,CAAC;IACnE,CAAC;IAED,sBAAsB;IACtB,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACvD,IAAI,cAAc,EAAE,CAAC;QACnB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,gBAAgB,EAAE,UAAU,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,+BAA+B;IAC/B,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAEvB,8BAA8B;IAC9B,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,IAAI,KAAK,EAAE,CAAC;QACV,yBAAyB;QACzB,UAAU,CAAC,IAAI,CACb,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,EAAE,SAAS,CAAC,EAC7E,2CAA2C,CAC5C,CAAC;IACJ,CAAC;SAAM,IAAI,QAAQ,EAAE,KAAK,QAAQ,EAAE,CAAC;QACnC,QAAQ;QACR,UAAU,CAAC,IAAI,CACb,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,CAAC,CACjE,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,QAAQ;QACR,UAAU,CAAC,IAAI,CACb,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,CAAC,EACrD,yCAAyC,CAC1C,CAAC;IACJ,CAAC;IAED,sCAAsC;IACtC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IAED,qBAAqB;IACrB,KAAK,MAAM,SAAS,IAAI,MAAM,EAAE,CAAC;QAC/B,0FAA0F;QAC1F,IAAI,SAAS,KAAK,KAAK,IAAI,CAAC,0BAA0B,CAAC,SAAS,CAAC,EAAE,CAAC;YAClE,SAAS;QACX,CAAC;QAED,IAAI,MAAM,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/B,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CACb;QACE,4CAA4C;QAC5C,EAAE;QACF,uCAAuC;QACvC,0CAA0C;QAC1C,kEAAkE;QAClE,iFAAiF;QACjF,kEAAkE;QAClE,4DAA4D;KAC7D,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,0BAA0B,CAAC,SAAiB;IACnD,uDAAuD;IACvD,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1D,OAAO,IAAI,CAAC,CAAC,uCAAuC;IACtD,CAAC;IACD,OAAO,UAAU,CAAC,SAAS,CAAC,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,SAAS,CAAC,OAAe;IACtC,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,EAAE;YACtD,OAAO,EAAE,iBAAiB;YAC1B,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QACH,6CAA6C;QAC7C,OAAO,MAAM,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,mBAAmB;AACnB,qEAAqE;AAErE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAe;IAEf,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,EAAE;YACtD,OAAO,EAAE,iBAAiB;YAC1B,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,MAAM;aACjB,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;QAE3D,gCAAgC;QAChC,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAChC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;gBAC/C,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -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