autoglm.js 0.0.6 → 0.0.7

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/index.mjs CHANGED
@@ -1,36 +1,1862 @@
1
- import { a as emitter, m as setAgentConfig, n as checkSystemRequirements, r as PhoneAgent, t as checkModelApi } from "./check-DHb6ZElP.mjs";
1
+ import { exec } from "tinyexec";
2
+ import { execSync } from "node:child_process";
3
+ import path, { join } from "node:path";
4
+ import fs from "fs-extra";
5
+ import { isLinux, isMacOS, isWindows } from "std-env";
6
+ import os, { tmpdir } from "node:os";
7
+ import dayjs from "dayjs";
8
+ import "dayjs/locale/zh-cn.js";
9
+ import { Buffer } from "node:buffer";
10
+ import { unlink } from "node:fs/promises";
11
+ import { parse } from "acorn-loose";
12
+ import { randomUUID } from "node:crypto";
13
+ import sharp from "sharp";
14
+ import mitt from "mitt";
15
+ import OpenAI from "openai";
2
16
 
3
- //#region src/index.ts
4
- var AutoGLM = class AutoGLM {
5
- phoneAgent;
17
+ //#region src/adb/constants.ts
18
+ const CONNECTION_TYPE = {
19
+ USB: "usb",
20
+ TCPIP: "tcpip"
21
+ };
22
+
23
+ //#endregion
24
+ //#region src/constants/app.ts
25
+ /**
26
+ * Mapping of app names to their package names.
27
+ */
28
+ const APP_PACKAGES = {
29
+ "微信": "com.tencent.mm",
30
+ "QQ": "com.tencent.mobileqq",
31
+ "微博": "com.sina.weibo",
32
+ "淘宝": "com.taobao.taobao",
33
+ "京东": "com.jingdong.app.mall",
34
+ "拼多多": "com.xunmeng.pinduoduo",
35
+ "淘宝闪购": "com.taobao.taobao",
36
+ "京东秒送": "com.jingdong.app.mall",
37
+ "小红书": "com.xingin.xhs",
38
+ "豆瓣": "com.douban.frodo",
39
+ "知乎": "com.zhihu.android",
40
+ "高德地图": "com.autonavi.minimap",
41
+ "百度地图": "com.baidu.BaiduMap",
42
+ "美团": "com.sankuai.meituan",
43
+ "大众点评": "com.dianping.v1",
44
+ "饿了么": "me.ele",
45
+ "肯德基": "com.yek.android.kfc.activitys",
46
+ "携程": "ctrip.android.view",
47
+ "铁路12306": "com.MobileTicket",
48
+ "12306": "com.MobileTicket",
49
+ "去哪儿": "com.Qunar",
50
+ "去哪儿旅行": "com.Qunar",
51
+ "滴滴出行": "com.sdu.didi.psnger",
52
+ "航旅纵横": "com.umetrip.android.msky.app",
53
+ "bilibili": "tv.danmaku.bili",
54
+ "抖音": "com.ss.android.ugc.aweme",
55
+ "快手": "com.smile.gifmaker",
56
+ "腾讯视频": "com.tencent.qqlive",
57
+ "爱奇艺": "com.qiyi.video",
58
+ "优酷视频": "com.youku.phone",
59
+ "芒果TV": "com.hunantv.imgo.activity",
60
+ "红果短剧": "com.phoenix.read",
61
+ "网易云音乐": "com.netease.cloudmusic",
62
+ "QQ音乐": "com.tencent.qqmusic",
63
+ "汽水音乐": "com.luna.music",
64
+ "喜马拉雅": "com.ximalaya.ting.android",
65
+ "番茄小说": "com.dragon.read",
66
+ "番茄免费小说": "com.dragon.read",
67
+ "七猫免费小说": "com.kmxs.reader",
68
+ "飞书": "com.ss.android.lark",
69
+ "QQ邮箱": "com.tencent.androidqqmail",
70
+ "豆包": "com.larus.nova",
71
+ "keep": "com.gotokeep.keep",
72
+ "美柚": "com.lingan.seeyou",
73
+ "腾讯新闻": "com.tencent.news",
74
+ "今日头条": "com.ss.android.article.news",
75
+ "贝壳找房": "com.lianjia.beike",
76
+ "安居客": "com.anjuke.android.app",
77
+ "同花顺": "com.hexin.plat.android",
78
+ "星穹铁道": "com.miHoYo.hkrpg",
79
+ "崩坏:星穹铁道": "com.miHoYo.hkrpg",
80
+ "恋与深空": "com.papegames.lysk.cn",
81
+ "建设银行": "com.jingdong.app.mall",
82
+ "AndroidSystemSettings": "com.android.settings",
83
+ "Android System Settings": "com.android.settings",
84
+ "Android System Settings": "com.android.settings",
85
+ "Android-System-Settings": "com.android.settings",
86
+ "Settings": "com.android.settings",
87
+ "AudioRecorder": "com.android.soundrecorder",
88
+ "audiorecorder": "com.android.soundrecorder",
89
+ "Bluecoins": "com.rammigsoftware.bluecoins",
90
+ "bluecoins": "com.rammigsoftware.bluecoins",
91
+ "Broccoli": "com.flauschcode.broccoli",
92
+ "broccoli": "com.flauschcode.broccoli",
93
+ "Booking.com": "com.booking",
94
+ "Booking": "com.booking",
95
+ "booking.com": "com.booking",
96
+ "booking": "com.booking",
97
+ "BOOKING.COM": "com.booking",
98
+ "Chrome": "com.android.chrome",
99
+ "chrome": "com.android.chrome",
100
+ "Google Chrome": "com.android.chrome",
101
+ "Clock": "com.android.deskclock",
102
+ "clock": "com.android.deskclock",
103
+ "Contacts": "com.android.contacts",
104
+ "contacts": "com.android.contacts",
105
+ "Duolingo": "com.duolingo",
106
+ "duolingo": "com.duolingo",
107
+ "Expedia": "com.expedia.bookings",
108
+ "expedia": "com.expedia.bookings",
109
+ "Files": "com.android.fileexplorer",
110
+ "files": "com.android.fileexplorer",
111
+ "File Manager": "com.android.fileexplorer",
112
+ "file manager": "com.android.fileexplorer",
113
+ "gmail": "com.google.android.gm",
114
+ "Gmail": "com.google.android.gm",
115
+ "GoogleMail": "com.google.android.gm",
116
+ "Google Mail": "com.google.android.gm",
117
+ "GoogleFiles": "com.google.android.apps.nbu.files",
118
+ "googlefiles": "com.google.android.apps.nbu.files",
119
+ "FilesbyGoogle": "com.google.android.apps.nbu.files",
120
+ "GoogleCalendar": "com.google.android.calendar",
121
+ "Google-Calendar": "com.google.android.calendar",
122
+ "Google Calendar": "com.google.android.calendar",
123
+ "google-calendar": "com.google.android.calendar",
124
+ "google calendar": "com.google.android.calendar",
125
+ "GoogleChat": "com.google.android.apps.dynamite",
126
+ "Google Chat": "com.google.android.apps.dynamite",
127
+ "Google-Chat": "com.google.android.apps.dynamite",
128
+ "GoogleClock": "com.google.android.deskclock",
129
+ "Google Clock": "com.google.android.deskclock",
130
+ "Google-Clock": "com.google.android.deskclock",
131
+ "GoogleContacts": "com.google.android.contacts",
132
+ "Google-Contacts": "com.google.android.contacts",
133
+ "Google Contacts": "com.google.android.contacts",
134
+ "google-contacts": "com.google.android.contacts",
135
+ "google contacts": "com.google.android.contacts",
136
+ "GoogleDocs": "com.google.android.apps.docs.editors.docs",
137
+ "Google Docs": "com.google.android.apps.docs.editors.docs",
138
+ "googledocs": "com.google.android.apps.docs.editors.docs",
139
+ "google docs": "com.google.android.apps.docs.editors.docs",
140
+ "Google Drive": "com.google.android.apps.docs",
141
+ "Google-Drive": "com.google.android.apps.docs",
142
+ "google drive": "com.google.android.apps.docs",
143
+ "google-drive": "com.google.android.apps.docs",
144
+ "GoogleDrive": "com.google.android.apps.docs",
145
+ "Googledrive": "com.google.android.apps.docs",
146
+ "googledrive": "com.google.android.apps.docs",
147
+ "GoogleFit": "com.google.android.apps.fitness",
148
+ "googlefit": "com.google.android.apps.fitness",
149
+ "GoogleKeep": "com.google.android.keep",
150
+ "googlekeep": "com.google.android.keep",
151
+ "GoogleMaps": "com.google.android.apps.maps",
152
+ "Google Maps": "com.google.android.apps.maps",
153
+ "googlemaps": "com.google.android.apps.maps",
154
+ "google maps": "com.google.android.apps.maps",
155
+ "Google Play Books": "com.google.android.apps.books",
156
+ "Google-Play-Books": "com.google.android.apps.books",
157
+ "google play books": "com.google.android.apps.books",
158
+ "google-play-books": "com.google.android.apps.books",
159
+ "GooglePlayBooks": "com.google.android.apps.books",
160
+ "googleplaybooks": "com.google.android.apps.books",
161
+ "GooglePlayStore": "com.android.vending",
162
+ "Google Play Store": "com.android.vending",
163
+ "Google-Play-Store": "com.android.vending",
164
+ "GoogleSlides": "com.google.android.apps.docs.editors.slides",
165
+ "Google Slides": "com.google.android.apps.docs.editors.slides",
166
+ "Google-Slides": "com.google.android.apps.docs.editors.slides",
167
+ "GoogleTasks": "com.google.android.apps.tasks",
168
+ "Google Tasks": "com.google.android.apps.tasks",
169
+ "Google-Tasks": "com.google.android.apps.tasks",
170
+ "Joplin": "net.cozic.joplin",
171
+ "joplin": "net.cozic.joplin",
172
+ "McDonald": "com.mcdonalds.app",
173
+ "mcdonald": "com.mcdonalds.app",
174
+ "Osmand": "net.osmand",
175
+ "osmand": "net.osmand",
176
+ "PiMusicPlayer": "com.Project100Pi.themusicplayer",
177
+ "pimusicplayer": "com.Project100Pi.themusicplayer",
178
+ "Quora": "com.quora.android",
179
+ "quora": "com.quora.android",
180
+ "Reddit": "com.reddit.frontpage",
181
+ "reddit": "com.reddit.frontpage",
182
+ "RetroMusic": "code.name.monkey.retromusic",
183
+ "retromusic": "code.name.monkey.retromusic",
184
+ "SimpleCalendarPro": "com.scientificcalculatorplus.simplecalculator.basiccalculator.mathcalc",
185
+ "SimpleSMSMessenger": "com.simplemobiletools.smsmessenger",
186
+ "Telegram": "org.telegram.messenger",
187
+ "temu": "com.einnovation.temu",
188
+ "Temu": "com.einnovation.temu",
189
+ "Tiktok": "com.zhiliaoapp.musically",
190
+ "tiktok": "com.zhiliaoapp.musically",
191
+ "Twitter": "com.twitter.android",
192
+ "twitter": "com.twitter.android",
193
+ "X": "com.twitter.android",
194
+ "VLC": "org.videolan.vlc",
195
+ "WeChat": "com.tencent.mm",
196
+ "wechat": "com.tencent.mm",
197
+ "Whatsapp": "com.whatsapp",
198
+ "WhatsApp": "com.whatsapp"
199
+ };
200
+ /**
201
+ * List all supported apps.
202
+ */
203
+ function listSupportedApps() {
204
+ return Object.keys(APP_PACKAGES);
205
+ }
206
+
207
+ //#endregion
208
+ //#region src/constants/errorCodes.ts
209
+ let ErrorCode = /* @__PURE__ */ function(ErrorCode$1) {
210
+ /** unknown error */
211
+ ErrorCode$1[ErrorCode$1["UNKNOWN_ERROR"] = 0] = "UNKNOWN_ERROR";
212
+ /** adb not installed */
213
+ ErrorCode$1[ErrorCode$1["ADB_NOT_INSTALLED"] = 1] = "ADB_NOT_INSTALLED";
214
+ /** adb device not connected */
215
+ ErrorCode$1[ErrorCode$1["ADB_DEVICE_UNCONNECTED"] = 2] = "ADB_DEVICE_UNCONNECTED";
216
+ /** adb keyboard not installed */
217
+ ErrorCode$1[ErrorCode$1["ADB_KEYBOARD_UNINSTALLED"] = 3] = "ADB_KEYBOARD_UNINSTALLED";
218
+ /** model api check failed */
219
+ ErrorCode$1[ErrorCode$1["MODEL_API_CHECK_FAILED"] = 4] = "MODEL_API_CHECK_FAILED";
220
+ return ErrorCode$1;
221
+ }({});
222
+
223
+ //#endregion
224
+ //#region src/constants/filePath.ts
225
+ const AUTOGLM_FILEPATH = join(os.homedir(), ".autoglm");
226
+
227
+ //#endregion
228
+ //#region src/constants/prompts_en.ts
229
+ /**
230
+ * English system prompt for the agent.
231
+ */
232
+ const formatted_date$1 = dayjs().format("YYYY-MM-DD dddd");
233
+ const SYSTEM_PROMPT_EN = `The current date: ${formatted_date$1}.
234
+ You are a phone intelligent assistant built based on AutoGLM, capable of understanding user needs through natural language and helping users complete tasks through automated operations.
235
+
236
+ # Setup
237
+ You are a professional Android operation agent assistant that can fulfill the user's high-level instructions. Given a screenshot of the Android interface at each step, you first analyze the situation, then plan the best course of action using Python-style pseudo-code.
238
+
239
+ # More details about the code
240
+ Your response format must be structured as follows:
241
+
242
+ Think first: Use <think>...</think> to analyze the current screen, identify key elements, and determine the most efficient action.
243
+ Provide the action: Use <answer>...</answer> to return a single line of pseudo-code representing the operation.
244
+
245
+ Your output should STRICTLY follow the format:
246
+ <think>
247
+ [Your thought]
248
+ </think>
249
+ <answer>
250
+ [Your operation code]
251
+ </answer>
252
+
253
+ - **Tap**
254
+ Perform a tap action on a specified screen area. The element is a list of 2 integers, representing the coordinates of the tap point.
255
+ **Example**:
256
+ <answer>
257
+ do(action="Tap", element=[x,y])
258
+ </answer>
259
+ - **Type**
260
+ Enter text into the currently focused input field.
261
+ **Example**:
262
+ <answer>
263
+ do(action="Type", text="Hello World")
264
+ </answer>
265
+ - **Swipe**
266
+ Perform a swipe action with start point and end point.
267
+ **Examples**:
268
+ <answer>
269
+ do(action="Swipe", start=[x1,y1], end=[x2,y2])
270
+ </answer>
271
+ - **Long Press**
272
+ Perform a long press action on a specified screen area.
273
+ You can add the element to the action to specify the long press area. The element is a list of 2 integers, representing the coordinates of the long press point.
274
+ **Example**:
275
+ <answer>
276
+ do(action="Long Press", element=[x,y])
277
+ </answer>
278
+ - **Launch**
279
+ Launch an app. Try to use launch action when you need to launch an app. Check the instruction to choose the right app before you use this action.
280
+ **Example**:
281
+ <answer>
282
+ do(action="Launch", app="Settings")
283
+ </answer>
284
+ - **Back**
285
+ Press the Back button to navigate to the previous screen.
286
+ **Example**:
287
+ <answer>
288
+ do(action="Back")
289
+ </answer>
290
+ - **Finish**
291
+ Terminate the program and optionally print a message.
292
+ **Example**:
293
+ <answer>
294
+ finish(message="Task completed.")
295
+ </answer>
296
+
297
+
298
+ REMEMBER:
299
+ - Think before you act: Always analyze the current UI and the best course of action before executing any step, and output in <think> part.
300
+ - Only ONE LINE of action in <answer> part per response: Each step must contain exactly one line of executable code.
301
+ - Generate execution code strictly according to format requirements.
302
+ `;
303
+
304
+ //#endregion
305
+ //#region src/constants/prompts_zh.ts
306
+ dayjs.locale("zh-cn");
307
+ const formatted_date = dayjs().format("YYYY-MM-DD dddd");
308
+ /**
309
+ * Chinese system prompt for the agent.
310
+ */
311
+ const SYSTEM_PROMPT_ZH = `今天的日期是: ${formatted_date}
312
+ 你是一个智能体分析专家,可以根据操作历史和当前状态图执行一系列操作来完成任务。
313
+ 你必须严格按照要求输出以下格式:
314
+ <think>{think}</think>
315
+ <answer>{action}</answer>
316
+
317
+ 其中:
318
+ - {think} 是对你为什么选择这个操作的简短推理说明。
319
+ - {action} 是本次执行的具体操作指令,必须严格遵循下方定义的指令格式。
320
+
321
+ 操作指令及其作用如下:
322
+ - do(action="Launch", app="xxx")
323
+ Launch是启动目标app的操作,这比通过主屏幕导航更快。此操作完成后,您将自动收到结果状态的截图。
324
+ - do(action="Tap", element=[x,y])
325
+ Tap是点击操作,点击屏幕上的特定点。可用此操作点击按钮、选择项目、从主屏幕打开应用程序,或与任何可点击的用户界面元素进行交互。坐标系统从左上角 (0,0) 开始到右下角(999,999)结束。此操作完成后,您将自动收到结果状态的截图。
326
+ - do(action="Tap", element=[x,y], message="重要操作")
327
+ 基本功能同Tap,点击涉及财产、支付、隐私等敏感按钮时触发。
328
+ - do(action="Type", text="xxx")
329
+ Type是输入操作,在当前聚焦的输入框中输入文本。使用此操作前,请确保输入框已被聚焦(先点击它)。输入的文本将像使用键盘输入一样输入。重要提示:手机可能正在使用 ADB 键盘,该键盘不会像普通键盘那样占用屏幕空间。要确认键盘已激活,请查看屏幕底部是否显示 'ADB Keyboard {ON}' 类似的文本,或者检查输入框是否处于激活/高亮状态。不要仅仅依赖视觉上的键盘显示。自动清除文本:当你使用输入操作时,输入框中现有的任何文本(包括占位符文本和实际输入)都会在输入新文本前自动清除。你无需在输入前手动清除文本——直接使用输入操作输入所需文本即可。操作完成后,你将自动收到结果状态的截图。
330
+ - do(action="Type_Name", text="xxx")
331
+ Type_Name是输入人名的操作,基本功能同Type。
332
+ - do(action="Interact")
333
+ Interact是当有多个满足条件的选项时而触发的交互操作,询问用户如何选择。
334
+ - do(action="Swipe", start=[x1,y1], end=[x2,y2])
335
+ Swipe是滑动操作,通过从起始坐标拖动到结束坐标来执行滑动手势。可用于滚动内容、在屏幕之间导航、下拉通知栏以及项目栏或进行基于手势的导航。坐标系统从左上角 (0,0) 开始到右下角(999,999)结束。滑动持续时间会自动调整以实现自然的移动。此操作完成后,您将自动收到结果状态的截图。
336
+ - do(action="Note", message="True")
337
+ 记录当前页面内容以便后续总结。
338
+ - do(action="Call_API", instruction="xxx")
339
+ 总结或评论当前页面或已记录的内容。
340
+ - do(action="Long Press", element=[x,y])
341
+ Long Pres是长按操作,在屏幕上的特定点长按指定时间。可用于触发上下文菜单、选择文本或激活长按交互。坐标系统从左上角 (0,0) 开始到右下角(999,999)结束。此操作完成后,您将自动收到结果状态的屏幕截图。
342
+ - do(action="Double Tap", element=[x,y])
343
+ Double Tap在屏幕上的特定点快速连续点按两次。使用此操作可以激活双击交互,如缩放、选择文本或打开项目。坐标系统从左上角 (0,0) 开始到右下角(999,999)结束。此操作完成后,您将自动收到结果状态的截图。
344
+ - do(action="Take_over", message="xxx")
345
+ Take_over是接管操作,表示在登录和验证阶段需要用户协助。
346
+ - do(action="Back")
347
+ 导航返回到上一个屏幕或关闭当前对话框。相当于按下 Android 的返回按钮。使用此操作可以从更深的屏幕返回、关闭弹出窗口或退出当前上下文。此操作完成后,您将自动收到结果状态的截图。
348
+ - do(action="Home")
349
+ Home是回到系统桌面的操作,相当于按下 Android 主屏幕按钮。使用此操作可退出当前应用并返回启动器,或从已知状态启动新任务。此操作完成后,您将自动收到结果状态的截图。
350
+ - do(action="Wait", duration="x seconds")
351
+ 等待页面加载,x为需要等待多少秒。
352
+ - finish(message="xxx")
353
+ finish是结束任务的操作,表示准确完整完成任务,message是终止信息。
354
+
355
+ 必须遵循的规则:
356
+ 1. 在执行任何操作前,先检查当前app是否是目标app,如果不是,先执行 Launch。
357
+ 2. 如果进入到了无关页面,先执行 Back。如果执行Back后页面没有变化,请点击页面左上角的返回键进行返回,或者右上角的X号关闭。
358
+ 3. 如果页面未加载出内容,最多连续 Wait 三次,否则执行 Back重新进入。
359
+ 4. 如果页面显示网络问题,需要重新加载,请点击重新加载。
360
+ 5. 如果当前页面找不到目标联系人、商品、店铺等信息,可以尝试 Swipe 滑动查找。
361
+ 6. 遇到价格区间、时间区间等筛选条件,如果没有完全符合的,可以放宽要求。
362
+ 7. 在做小红书总结类任务时一定要筛选图文笔记。
363
+ 8. 购物车全选后再点击全选可以把状态设为全不选,在做购物车任务时,如果购物车里已经有商品被选中时,你需要点击全选后再点击取消全选,再去找需要购买或者删除的商品。
364
+ 9. 在做外卖任务时,如果相应店铺购物车里已经有其他商品你需要先把购物车清空再去购买用户指定的外卖。
365
+ 10. 在做点外卖任务时,如果用户需要点多个外卖,请尽量在同一店铺进行购买,如果无法找到可以下单,并说明某个商品未找到。
366
+ 11. 请严格遵循用户意图执行任务,用户的特殊要求可以执行多次搜索,滑动查找。比如(i)用户要求点一杯咖啡,要咸的,你可以直接搜索咸咖啡,或者搜索咖啡后滑动查找咸的咖啡,比如海盐咖啡。(ii)用户要找到XX群,发一条消息,你可以先搜索XX群,找不到结果后,将"群"字去掉,搜索XX重试。(iii)用户要找到宠物友好的餐厅,你可以搜索餐厅,找到筛选,找到设施,选择可带宠物,或者直接搜索可带宠物,必要时可以使用AI搜索。
367
+ 12. 在选择日期时,如果原滑动方向与预期日期越来越远,请向反方向滑动查找。
368
+ 13. 执行任务过程中如果有多个可选择的项目栏,请逐个查找每个项目栏,直到完成任务,一定不要在同一项目栏多次查找,从而陷入死循环。
369
+ 14. 在执行下一步操作前请一定要检查上一步的操作是否生效,如果点击没生效,可能因为app反应较慢,请先稍微等待一下,如果还是不生效请调整一下点击位置重试,如果仍然不生效请跳过这一步继续任务,并在finish message说明点击不生效。
370
+ 15. 在执行任务中如果遇到滑动不生效的情况,请调整一下起始点位置,增大滑动距离重试,如果还是不生效,有可能是已经滑到底了,请继续向反方向滑动,直到顶部或底部,如果仍然没有符合要求的结果,请跳过这一步继续任务,并在finish message说明但没找到要求的项目。
371
+ 16. 在做游戏任务时如果在战斗页面如果有自动战斗一定要开启自动战斗,如果多轮历史状态相似要检查自动战斗是否开启。
372
+ 17. 如果没有合适的搜索结果,可能是因为搜索页面不对,请返回到搜索页面的上一级尝试重新搜索,如果尝试三次返回上一级搜索后仍然没有符合要求的结果,执行 finish(message="原因")。
373
+ 18. 在结束任务前请一定要仔细检查任务是否完整准确的完成,如果出现错选、漏选、多选的情况,请返回之前的步骤进行纠正。
374
+ `;
375
+
376
+ //#endregion
377
+ //#region src/adb/installer.ts
378
+ var ADBAutoInstaller = class {
379
+ installPath;
380
+ adbPath;
381
+ platformToolsPath;
382
+ globalADBChecked = null;
383
+ constructor(customPlatformToolsPath) {
384
+ this.installPath = AUTOGLM_FILEPATH;
385
+ this.platformToolsPath = customPlatformToolsPath ?? path.join(this.installPath, "platform-tools");
386
+ this.adbPath = path.join(this.platformToolsPath, "adb");
387
+ }
388
+ async install() {
389
+ if (await this.check()) return;
390
+ await fs.ensureDir(this.installPath);
391
+ if (isMacOS) {
392
+ const { installADB } = await import("@autoglm.js/platform-tools-darwin");
393
+ await installADB(this.installPath);
394
+ }
395
+ if (isLinux) {
396
+ const { installADB } = await import("@autoglm.js/platform-tools-linux");
397
+ await installADB(this.installPath);
398
+ }
399
+ if (isWindows) {
400
+ const { installADB } = await import("@autoglm.js/platform-tools-windows");
401
+ await installADB(this.installPath);
402
+ }
403
+ }
404
+ async check() {
405
+ return await fs.pathExists(this.adbPath);
406
+ }
407
+ checkGlobalADB() {
408
+ if (this.globalADBChecked !== null) return this.globalADBChecked;
409
+ try {
410
+ execSync("adb version", { stdio: "ignore" });
411
+ this.globalADBChecked = true;
412
+ return true;
413
+ } catch {
414
+ this.globalADBChecked = false;
415
+ return false;
416
+ }
417
+ }
418
+ get adb() {
419
+ return this.checkGlobalADB() ? "adb" : this.adbPath;
420
+ }
421
+ };
422
+
423
+ //#endregion
424
+ //#region src/adb/connection.ts
425
+ var ADBConnection = class {
426
+ adb;
427
+ constructor() {
428
+ this.adb = new ADBAutoInstaller().adb;
429
+ }
430
+ /**
431
+ * Get ADB version.
432
+ */
433
+ async version() {
434
+ try {
435
+ const versionLine = (await exec(this.adb, ["version"])).stdout.trim().split("\n")[0];
436
+ const versionMatch = versionLine.match(/(\d+\.\d+\.\d+)/);
437
+ return {
438
+ success: true,
439
+ message: versionLine,
440
+ version: versionMatch ? versionMatch[1] : null
441
+ };
442
+ } catch (error) {
443
+ return {
444
+ success: false,
445
+ message: error instanceof Error ? error.message : "Unknown error",
446
+ version: null
447
+ };
448
+ }
449
+ }
450
+ /**
451
+ * Connect to a remote device.
452
+ */
453
+ async connect(address) {
454
+ try {
455
+ const stdout = (await exec(this.adb, ["connect", address])).stdout.trim();
456
+ if (stdout.includes("connected to") || stdout.includes("already connected")) return {
457
+ success: true,
458
+ message: stdout
459
+ };
460
+ else return {
461
+ success: false,
462
+ message: stdout
463
+ };
464
+ } catch (error) {
465
+ return {
466
+ success: false,
467
+ message: error instanceof Error ? error.message : "Unknown error"
468
+ };
469
+ }
470
+ }
471
+ /**
472
+ * Disconnect from a device.
473
+ */
474
+ async disconnect(address) {
475
+ try {
476
+ const args = address ? ["disconnect", address] : ["disconnect"];
477
+ return {
478
+ success: true,
479
+ message: (await exec(this.adb, args)).stdout.trim()
480
+ };
481
+ } catch (error) {
482
+ return {
483
+ success: false,
484
+ message: error instanceof Error ? error.message : "Unknown error"
485
+ };
486
+ }
487
+ }
488
+ /**
489
+ * Enable TCP/IP debugging on USB device.
490
+ */
491
+ async enableTCPIP(port = 5555, deviceId) {
492
+ try {
493
+ const args = deviceId ? [
494
+ "-s",
495
+ deviceId,
496
+ "tcpip",
497
+ port.toString()
498
+ ] : ["tcpip", port.toString()];
499
+ return {
500
+ success: true,
501
+ message: (await exec(this.adb, args)).stdout.trim()
502
+ };
503
+ } catch (error) {
504
+ return {
505
+ success: false,
506
+ message: error instanceof Error ? error.message : "Unknown error"
507
+ };
508
+ }
509
+ }
510
+ /**
511
+ * Get device IP address.
512
+ */
513
+ async getDeviceIp(deviceId) {
514
+ try {
515
+ const args = deviceId ? [
516
+ "-s",
517
+ deviceId,
518
+ "shell",
519
+ "ip",
520
+ "addr",
521
+ "show",
522
+ "wlan0"
523
+ ] : [
524
+ "shell",
525
+ "ip",
526
+ "addr",
527
+ "show",
528
+ "wlan0"
529
+ ];
530
+ const ipMatch = (await exec(this.adb, args)).stdout.match(/inet\s+(\d+\.\d+\.\d+\.\d+)/);
531
+ return ipMatch ? ipMatch[1] : null;
532
+ } catch {
533
+ return null;
534
+ }
535
+ }
536
+ /**
537
+ * List all connected devices.
538
+ */
539
+ async listDevices() {
540
+ const deviceLines = (await exec(this.adb, ["devices", "-l"])).stdout.trim().split("\n").slice(1).filter((line) => line.trim());
541
+ return await Promise.all(deviceLines.map(async (line) => {
542
+ const parts = line.split(/\s+/);
543
+ const deviceId = parts[0];
544
+ const status = parts[1];
545
+ const connectionType = deviceId.includes(":") ? CONNECTION_TYPE.TCPIP : CONNECTION_TYPE.USB;
546
+ let model;
547
+ for (const part of parts) if (part.startsWith("model:")) {
548
+ model = part.replace("model:", "");
549
+ break;
550
+ }
551
+ const deviceInfo = {
552
+ deviceId,
553
+ status,
554
+ connectionType,
555
+ model
556
+ };
557
+ if (status === "device") try {
558
+ const [brand, manufacturer, device, androidVersion, apiLevel] = await Promise.all([
559
+ this.getDeviceProp(deviceId, "ro.product.brand"),
560
+ this.getDeviceProp(deviceId, "ro.product.manufacturer"),
561
+ this.getDeviceProp(deviceId, "ro.product.device"),
562
+ this.getDeviceProp(deviceId, "ro.build.version.release"),
563
+ this.getDeviceProp(deviceId, "ro.build.version.sdk")
564
+ ]);
565
+ deviceInfo.brand = brand || void 0;
566
+ deviceInfo.manufacturer = manufacturer || void 0;
567
+ deviceInfo.device = device || void 0;
568
+ deviceInfo.androidVersion = androidVersion || void 0;
569
+ deviceInfo.apiLevel = apiLevel || void 0;
570
+ } catch {}
571
+ return deviceInfo;
572
+ }));
573
+ }
574
+ /**
575
+ * Get device property value.
576
+ */
577
+ async getDeviceProp(deviceId, prop) {
578
+ try {
579
+ return (await exec(this.adb, [
580
+ "-s",
581
+ deviceId,
582
+ "shell",
583
+ "getprop",
584
+ prop
585
+ ])).stdout.trim() || null;
586
+ } catch {
587
+ return null;
588
+ }
589
+ }
590
+ /**
591
+ * Check device is connected.
592
+ */
593
+ async devices() {
594
+ try {
595
+ const devices = (await exec(this.adb, ["devices"])).stdout.trim().split("\n").slice(1).filter((line) => line.trim() && line.includes(" device"));
596
+ if (devices.length === 0) return {
597
+ success: false,
598
+ message: "No devices connected"
599
+ };
600
+ else return {
601
+ success: true,
602
+ message: devices.map((d) => d.split(" ")[0]).join(", ")
603
+ };
604
+ } catch (error) {
605
+ return {
606
+ success: false,
607
+ message: error instanceof Error ? error.message : "Unknown error"
608
+ };
609
+ }
610
+ }
611
+ };
612
+
613
+ //#endregion
614
+ //#region src/utils/errorMessage.ts
615
+ function getErrorMessage(error, defaultMessage = "Unknown error") {
616
+ return error instanceof Error ? error.message : defaultMessage;
617
+ }
618
+
619
+ //#endregion
620
+ //#region src/adb/utils.ts
621
+ /**
622
+ * Get ADB command prefix with optional device specifier.
623
+ */
624
+ async function getAdbPrefix(deviceId) {
625
+ const adb = new ADBAutoInstaller().adb;
626
+ return deviceId ? [
627
+ adb,
628
+ "-s",
629
+ deviceId
630
+ ] : [adb];
631
+ }
632
+ async function runAdbCommand(deviceId, args, timeout) {
633
+ const prefix = await getAdbPrefix(deviceId);
634
+ if (timeout) return await exec(prefix[0], [...prefix.slice(1), ...args], { timeout });
635
+ else return await exec(prefix[0], [...prefix.slice(1), ...args]);
636
+ }
637
+
638
+ //#endregion
639
+ //#region src/adb/keyboard.ts
640
+ var ADBKeyboard = class {
641
+ ctx;
642
+ constructor(context) {
643
+ this.ctx = context;
644
+ }
645
+ get deviceId() {
646
+ return this.ctx.getConfig().deviceId;
647
+ }
648
+ /**
649
+ * Check ADB Keyboard is installed.
650
+ */
651
+ async isKeyboardInstalled() {
652
+ try {
653
+ if ((await runAdbCommand(this.deviceId, [
654
+ "shell",
655
+ "ime",
656
+ "list",
657
+ "-s"
658
+ ])).stdout.trim().includes("com.android.adbkeyboard/.AdbIME")) return {
659
+ success: true,
660
+ message: "ADB Keyboard is installed and enabled"
661
+ };
662
+ else return {
663
+ success: false,
664
+ message: "ADB Keyboard is not installed"
665
+ };
666
+ } catch (error) {
667
+ return {
668
+ success: false,
669
+ message: getErrorMessage(error)
670
+ };
671
+ }
672
+ }
673
+ /**
674
+ * Install ADB Keyboard.
675
+ */
676
+ async installKeyboard(customApkPath) {
677
+ try {
678
+ const { installKeyboard } = await import("@autoglm.js/adb-keyboard");
679
+ await installKeyboard(this.deviceId, customApkPath);
680
+ return {
681
+ success: true,
682
+ message: "ADB Keyboard installed and enabled"
683
+ };
684
+ } catch (error) {
685
+ return {
686
+ success: false,
687
+ message: getErrorMessage(error)
688
+ };
689
+ }
690
+ }
691
+ };
692
+
693
+ //#endregion
694
+ //#region src/adb/manager.ts
695
+ var ADBManager = class {
696
+ ctx;
697
+ connection;
698
+ keyboard;
699
+ constructor(context) {
700
+ this.ctx = context;
701
+ this.connection = new ADBConnection();
702
+ this.keyboard = new ADBKeyboard(context);
703
+ }
704
+ async install(customPlatformToolsPath) {
705
+ await new ADBAutoInstaller(customPlatformToolsPath).install();
706
+ }
707
+ async getVersion() {
708
+ return this.connection.version();
709
+ }
710
+ async getConnectedDevices() {
711
+ return this.connection.listDevices();
712
+ }
713
+ async devices() {
714
+ return this.connection.devices();
715
+ }
716
+ async connect(address) {
717
+ return this.connection.connect(address);
718
+ }
719
+ async disconnect(address) {
720
+ return this.connection.disconnect(address);
721
+ }
722
+ async enableTCPIP(port = 5555, deviceId) {
723
+ return this.connection.enableTCPIP(port, deviceId);
724
+ }
725
+ async getDeviceIp(deviceId) {
726
+ return this.connection.getDeviceIp(deviceId);
727
+ }
728
+ async isKeyboardInstalled() {
729
+ return this.keyboard.isKeyboardInstalled();
730
+ }
731
+ async installKeyboard(customApkPath) {
732
+ return this.keyboard.installKeyboard(customApkPath);
733
+ }
734
+ };
735
+
736
+ //#endregion
737
+ //#region src/adb/device.ts
738
+ /**
739
+ * Get the currently focused app name.
740
+ */
741
+ async function getCurrentApp(deviceId) {
742
+ const output = (await runAdbCommand(deviceId, [
743
+ "shell",
744
+ "dumpsys",
745
+ "window"
746
+ ])).stdout;
747
+ for (const line of output.split("\n")) if (line.includes("mCurrentFocus") || line.includes("mFocusedApp")) {
748
+ for (const [appName, appPackage] of Object.entries(APP_PACKAGES)) if (line.includes(appPackage)) return appName;
749
+ }
750
+ return "System Home";
751
+ }
752
+ /**
753
+ * Tap at the specified coordinates.
754
+ */
755
+ async function tap(x, y, deviceId, delay = 1) {
756
+ await runAdbCommand(deviceId, [
757
+ "shell",
758
+ "input",
759
+ "tap",
760
+ x.toString(),
761
+ y.toString()
762
+ ]);
763
+ await new Promise((resolve) => setTimeout(resolve, delay * 1e3));
764
+ }
765
+ /**
766
+ * Double tap at the specified coordinates.
767
+ */
768
+ async function doubleTap(x, y, deviceId, delay = 1) {
769
+ await runAdbCommand(deviceId, [
770
+ "shell",
771
+ "input",
772
+ "tap",
773
+ x.toString(),
774
+ y.toString()
775
+ ]);
776
+ await new Promise((resolve) => setTimeout(resolve, 100));
777
+ await runAdbCommand(deviceId, [
778
+ "shell",
779
+ "input",
780
+ "tap",
781
+ x.toString(),
782
+ y.toString()
783
+ ]);
784
+ await new Promise((resolve) => setTimeout(resolve, delay * 1e3));
785
+ }
786
+ /**
787
+ * Long press at the specified coordinates.
788
+ */
789
+ async function longPress(x, y, durationMs = 3e3, deviceId, delay = 1) {
790
+ await runAdbCommand(deviceId, [
791
+ "shell",
792
+ "input",
793
+ "swipe",
794
+ x.toString(),
795
+ y.toString(),
796
+ x.toString(),
797
+ y.toString(),
798
+ durationMs.toString()
799
+ ]);
800
+ await new Promise((resolve) => setTimeout(resolve, delay * 1e3));
801
+ }
802
+ /**
803
+ * Swipe from start to end coordinates.
804
+ */
805
+ async function swipe(startX, startY, endX, endY, durationMs, deviceId, delay = 1) {
806
+ if (durationMs === void 0) {
807
+ const distSq = (startX - endX) ** 2 + (startY - endY) ** 2;
808
+ durationMs = Math.max(1e3, Math.min(Math.floor(distSq / 1e3), 2e3));
809
+ }
810
+ await runAdbCommand(deviceId, [
811
+ "shell",
812
+ "input",
813
+ "swipe",
814
+ startX.toString(),
815
+ startY.toString(),
816
+ endX.toString(),
817
+ endY.toString(),
818
+ durationMs.toString()
819
+ ]);
820
+ await new Promise((resolve) => setTimeout(resolve, delay * 1e3));
821
+ }
822
+ /**
823
+ * Press the back button.
824
+ */
825
+ async function back(deviceId, delay = 1) {
826
+ await runAdbCommand(deviceId, [
827
+ "shell",
828
+ "input",
829
+ "keyevent",
830
+ "4"
831
+ ]);
832
+ await new Promise((resolve) => setTimeout(resolve, delay * 1e3));
833
+ }
834
+ /**
835
+ * Press the home button.
836
+ */
837
+ async function home(deviceId, delay = 1) {
838
+ await runAdbCommand(deviceId, [
839
+ "shell",
840
+ "input",
841
+ "keyevent",
842
+ "KEYCODE_HOME"
843
+ ]);
844
+ await new Promise((resolve) => setTimeout(resolve, delay * 1e3));
845
+ }
846
+ /**
847
+ * Launch an app by name.
848
+ */
849
+ async function launchApp(appName, deviceId, delay = 1) {
850
+ if (!APP_PACKAGES[appName]) return false;
851
+ const appPackage = APP_PACKAGES[appName];
852
+ await runAdbCommand(deviceId, [
853
+ "shell",
854
+ "monkey",
855
+ "-p",
856
+ appPackage,
857
+ "-c",
858
+ "android.intent.category.LAUNCHER",
859
+ "1"
860
+ ]);
861
+ await new Promise((resolve) => setTimeout(resolve, delay * 1e3));
862
+ return true;
863
+ }
864
+
865
+ //#endregion
866
+ //#region ../shared/src/utils.ts
867
+ function sleep(ms) {
868
+ return new Promise((resolve) => setTimeout(resolve, ms));
869
+ }
870
+
871
+ //#endregion
872
+ //#region src/adb/input.ts
873
+ /**
874
+ * Get the current input method (IME) on the device.
875
+ * @param deviceId - Optional device ID for multi-device setups
876
+ * @returns The current IME identifier (e.g., "com.google.android.inputmethod.latin/.LatinIME")
877
+ */
878
+ async function getCurrentIme(deviceId) {
879
+ try {
880
+ const result = await runAdbCommand(deviceId, [
881
+ "shell",
882
+ "settings",
883
+ "get",
884
+ "secure",
885
+ "default_input_method"
886
+ ]);
887
+ return (result.stdout + result.stderr).trim();
888
+ } catch (error) {
889
+ console.warn(`Failed to get current IME: ${error instanceof Error ? error.message : "Unknown error"}`);
890
+ return "";
891
+ }
892
+ }
893
+ /**
894
+ * Set the input method (IME) on the device.
895
+ * @param ime - The IME identifier to set
896
+ * @param deviceId - Optional device ID for multi-device setups
897
+ */
898
+ async function setIme(ime, deviceId) {
899
+ if (!ime) {
900
+ console.warn("setIme called with empty IME identifier, skipping");
901
+ return;
902
+ }
903
+ try {
904
+ await runAdbCommand(deviceId, [
905
+ "shell",
906
+ "ime",
907
+ "set",
908
+ ime
909
+ ]);
910
+ } catch (error) {
911
+ console.warn(`Failed to set IME to "${ime}": ${error instanceof Error ? error.message : "Unknown error"}`);
912
+ }
913
+ }
914
+ /**
915
+ * Detect if ADB Keyboard is available and set it as the default keyboard.
916
+ * @returns The original IME identifier for later restoration
917
+ */
918
+ async function detectAndSetAdbKeyboard(deviceId) {
919
+ const currentIme = await getCurrentIme(deviceId);
920
+ try {
921
+ if (!(await runAdbCommand(deviceId, [
922
+ "shell",
923
+ "pm",
924
+ "list",
925
+ "packages",
926
+ "com.android.adbkeyboard"
927
+ ])).stdout.includes("com.android.adbkeyboard")) {
928
+ console.warn("ADB Keyboard is not installed");
929
+ return currentIme;
930
+ }
931
+ if (!currentIme.includes("com.android.adbkeyboard/.AdbIME")) {
932
+ await setIme("com.android.adbkeyboard/.AdbIME", deviceId);
933
+ await sleep(300);
934
+ await runAdbCommand(deviceId, [
935
+ "shell",
936
+ "am",
937
+ "broadcast",
938
+ "-a",
939
+ "ADB_INPUT_B64",
940
+ "--es",
941
+ "msg",
942
+ Buffer.from("", "utf8").toString("base64")
943
+ ]);
944
+ }
945
+ return currentIme;
946
+ } catch (error) {
947
+ console.warn(`Failed to set ADB Keyboard: ${error instanceof Error ? error.message : "Unknown error"}`);
948
+ return currentIme;
949
+ }
950
+ }
951
+ /**
952
+ * Restore the previous keyboard.
953
+ * @param ime - The IME identifier to restore
954
+ * @param deviceId - Optional device ID for multi-device setups
955
+ */
956
+ async function restoreKeyboard(ime, deviceId) {
957
+ if (!ime) {
958
+ console.warn("restoreKeyboard called with empty IME identifier, skipping");
959
+ return;
960
+ }
961
+ if (ime.includes("com.android.adbkeyboard/.AdbIME")) return;
962
+ try {
963
+ await setIme(ime, deviceId);
964
+ await sleep(300);
965
+ } catch (error) {
966
+ console.warn(`Failed to restore keyboard: ${error instanceof Error ? error.message : "Unknown error"}`);
967
+ }
968
+ }
969
+ /**
970
+ * Type text using ADB Keyboard.
971
+ * Automatically saves and restores the original keyboard.
972
+ */
973
+ async function typeText(text, deviceId) {
974
+ const originalIme = await detectAndSetAdbKeyboard(deviceId);
975
+ try {
976
+ await clearText(deviceId);
977
+ await runAdbCommand(deviceId, [
978
+ "shell",
979
+ "am",
980
+ "broadcast",
981
+ "-a",
982
+ "ADB_INPUT_B64",
983
+ "--es",
984
+ "msg",
985
+ Buffer.from(text, "utf8").toString("base64")
986
+ ]);
987
+ await sleep(300);
988
+ } finally {
989
+ await restoreKeyboard(originalIme, deviceId);
990
+ }
991
+ }
992
+ /**
993
+ * Clear text by sending backspace keystrokes.
994
+ */
995
+ async function clearText(deviceId) {
996
+ await runAdbCommand(deviceId, [
997
+ "shell",
998
+ "am",
999
+ "broadcast",
1000
+ "-a",
1001
+ "ADB_CLEAR_TEXT"
1002
+ ]);
1003
+ await sleep(300);
1004
+ }
1005
+
1006
+ //#endregion
1007
+ //#region src/actions/handler.ts
1008
+ /**
1009
+ * Create a finish action.
1010
+ */
1011
+ function finish(message) {
1012
+ return {
1013
+ _metadata: "finish",
1014
+ message
1015
+ };
1016
+ }
1017
+ /**
1018
+ * Action handler that executes the actions on the device.
1019
+ */
1020
+ var ActionHandler = class {
1021
+ constructor(context, callbacks = {}) {
1022
+ this.context = context;
1023
+ this.callbacks = callbacks;
1024
+ }
1025
+ get deviceId() {
1026
+ return this.context.getConfig().deviceId;
1027
+ }
1028
+ /**
1029
+ * Execute an action.
1030
+ */
1031
+ async execute(action, screenWidth, screenHeight) {
1032
+ try {
1033
+ if (action._metadata === "finish") return {
1034
+ success: true,
1035
+ should_finish: true,
1036
+ message: action.message
1037
+ };
1038
+ switch (action.action) {
1039
+ case "Launch": return await this.executeLaunchAction(action);
1040
+ case "Tap": return await this.executeTapAction(action, screenWidth, screenHeight);
1041
+ case "Type": return await this.executeTypeAction(action);
1042
+ case "Swipe": return await this.executeSwipeAction(action, screenWidth, screenHeight);
1043
+ case "Back": return await this.executeBackAction(action);
1044
+ case "Home": return await this.executeHomeAction(action);
1045
+ case "Long Press": return await this.executeLongPressAction(action, screenWidth, screenHeight);
1046
+ case "Double Tap": return await this.executeDoubleTapAction(action, screenWidth, screenHeight);
1047
+ case "Wait": return await this.executeWaitAction(action);
1048
+ case "Take_over": return await this.executeTakeOverAction(action);
1049
+ default: return {
1050
+ success: false,
1051
+ should_finish: true,
1052
+ message: `Unknown action: ${action.action}`
1053
+ };
1054
+ }
1055
+ } catch (error) {
1056
+ return {
1057
+ success: false,
1058
+ should_finish: true,
1059
+ message: `Action execution failed: ${error instanceof Error ? error.message : "Unknown error"}`
1060
+ };
1061
+ }
1062
+ }
1063
+ /**
1064
+ * Execute launch action.
1065
+ */
1066
+ async executeLaunchAction(action) {
1067
+ if (!await launchApp(action.app, this.deviceId)) return {
1068
+ success: false,
1069
+ should_finish: false,
1070
+ message: `Failed to launch app: ${action.app}`
1071
+ };
1072
+ return {
1073
+ success: true,
1074
+ should_finish: false
1075
+ };
1076
+ }
1077
+ convertRelativeToAbsolute(element, screenWidth, screenHeight) {
1078
+ return [element[0] / 1e3 * screenWidth, element[1] / 1e3 * screenHeight];
1079
+ }
1080
+ /**
1081
+ * Execute tap action.
1082
+ */
1083
+ async executeTapAction(action, screenWidth, screenHeight) {
1084
+ const [x, y] = action.element;
1085
+ const [absoluteX, absoluteY] = this.convertRelativeToAbsolute([x, y], screenWidth, screenHeight);
1086
+ await tap(absoluteX, absoluteY, this.deviceId);
1087
+ return {
1088
+ success: true,
1089
+ should_finish: false
1090
+ };
1091
+ }
1092
+ /**
1093
+ * Execute type action.
1094
+ */
1095
+ async executeTypeAction(action) {
1096
+ await typeText(action.text, this.deviceId);
1097
+ return {
1098
+ success: true,
1099
+ should_finish: false
1100
+ };
1101
+ }
1102
+ /**
1103
+ * Execute swipe action.
1104
+ */
1105
+ async executeSwipeAction(action, screenWidth, screenHeight) {
1106
+ const [startX, startY] = this.convertRelativeToAbsolute(action.start, screenWidth, screenHeight);
1107
+ const [endX, endY] = this.convertRelativeToAbsolute(action.end, screenWidth, screenHeight);
1108
+ await swipe(startX, startY, endX, endY, action.duration, this.deviceId);
1109
+ return {
1110
+ success: true,
1111
+ should_finish: false
1112
+ };
1113
+ }
1114
+ /**
1115
+ * Execute back action.
1116
+ */
1117
+ async executeBackAction(_action) {
1118
+ await back(this.deviceId);
1119
+ return {
1120
+ success: true,
1121
+ should_finish: false
1122
+ };
1123
+ }
1124
+ /**
1125
+ * Execute home action.
1126
+ */
1127
+ async executeHomeAction(_action) {
1128
+ await home(this.deviceId);
1129
+ return {
1130
+ success: true,
1131
+ should_finish: false
1132
+ };
1133
+ }
1134
+ /**
1135
+ * Execute long press action.
1136
+ */
1137
+ async executeLongPressAction(action, screenWidth, screenHeight) {
1138
+ const [x, y] = this.convertRelativeToAbsolute(action.element, screenWidth, screenHeight);
1139
+ await longPress(x, y, action.duration, this.deviceId);
1140
+ return {
1141
+ success: true,
1142
+ should_finish: false
1143
+ };
1144
+ }
1145
+ /**
1146
+ * Execute double tap action.
1147
+ */
1148
+ async executeDoubleTapAction(action, screenWidth, screenHeight) {
1149
+ const [x, y] = this.convertRelativeToAbsolute(action.element, screenWidth, screenHeight);
1150
+ await doubleTap(x, y, this.deviceId);
1151
+ return {
1152
+ success: true,
1153
+ should_finish: false
1154
+ };
1155
+ }
1156
+ /**
1157
+ * Execute wait action.
1158
+ */
1159
+ async executeWaitAction(action) {
1160
+ const duration = action.duration || 1;
1161
+ await new Promise((resolve) => setTimeout(resolve, duration * 1e3));
1162
+ return {
1163
+ success: true,
1164
+ should_finish: false
1165
+ };
1166
+ }
1167
+ /**
1168
+ * Execute take over action.
1169
+ */
1170
+ async executeTakeOverAction(action) {
1171
+ if (this.callbacks.takeoverCallback) {
1172
+ await this.callbacks.takeoverCallback(action.reason || "Manual takeover requested");
1173
+ return {
1174
+ success: true,
1175
+ should_finish: false
1176
+ };
1177
+ }
1178
+ return {
1179
+ success: false,
1180
+ should_finish: true,
1181
+ message: `Takeover requested but no callback provided: ${action.reason}`
1182
+ };
1183
+ }
1184
+ };
1185
+
1186
+ //#endregion
1187
+ //#region src/actions/parse.ts
1188
+ function getElements(node, index = 1) {
1189
+ if (node.type !== "SequenceExpression") return;
1190
+ return node.expressions[index].right.elements.map((e) => e.value);
1191
+ }
1192
+ function getText(node) {
1193
+ if (node.type !== "SequenceExpression") return;
1194
+ return node.expressions[1].right.value;
1195
+ }
1196
+ function getActionType(node) {
1197
+ if (node.type === "SequenceExpression") return node.expressions[0].right.value;
1198
+ if (node.type === "AssignmentExpression") return node.right.value;
1199
+ }
1200
+ /**
1201
+ * Parse action from string format.
1202
+ */
1203
+ function parseAction(actionStr) {
1204
+ if (actionStr.startsWith("finish(")) {
1205
+ const match = actionStr.match(/finish\(message=(.*)\)/);
1206
+ return {
1207
+ _metadata: "finish",
1208
+ message: match ? match[1].trim() : void 0
1209
+ };
1210
+ }
1211
+ if (actionStr.startsWith("do(")) {
1212
+ const ast = parse(actionStr, { ecmaVersion: "latest" });
1213
+ if (ast.body[0].type !== "DoWhileStatement") throw new Error(`Invalid action format: ${actionStr}`);
1214
+ const expression = ast.body[0].body.expression;
1215
+ const actionType = getActionType(expression);
1216
+ if (actionType === "Launch") {
1217
+ const app = getText(expression);
1218
+ if (!app) throw new Error(`Invalid Launch action parameters: ${actionStr}`);
1219
+ return {
1220
+ _metadata: "do",
1221
+ action: "Launch",
1222
+ app
1223
+ };
1224
+ }
1225
+ if (actionType === "Tap") {
1226
+ const element = getElements(expression);
1227
+ if (!element) throw new Error(`Invalid Tap action parameters: ${actionStr}`);
1228
+ return {
1229
+ _metadata: "do",
1230
+ action: "Tap",
1231
+ element
1232
+ };
1233
+ }
1234
+ if (actionType === "Type") {
1235
+ const text = getText(expression);
1236
+ if (!text) throw new Error(`Invalid Type action parameters: ${actionStr}`);
1237
+ return {
1238
+ _metadata: "do",
1239
+ action: "Type",
1240
+ text: text.trim()
1241
+ };
1242
+ }
1243
+ if (actionType === "Swipe") {
1244
+ const start = getElements(expression, 1);
1245
+ const end = getElements(expression, 2);
1246
+ if (!start || !end) throw new Error(`Invalid Swipe action parameters: ${actionStr}`);
1247
+ return {
1248
+ _metadata: "do",
1249
+ action: "Swipe",
1250
+ start,
1251
+ end
1252
+ };
1253
+ }
1254
+ if (actionType === "Back") return {
1255
+ _metadata: "do",
1256
+ action: "Back"
1257
+ };
1258
+ if (actionType === "Home") return {
1259
+ _metadata: "do",
1260
+ action: "Home"
1261
+ };
1262
+ if (actionType === "Long Press") {
1263
+ const element = getElements(expression);
1264
+ if (!element) throw new Error(`Invalid Long Press action parameters: ${actionStr}`);
1265
+ return {
1266
+ _metadata: "do",
1267
+ action: "Long Press",
1268
+ element
1269
+ };
1270
+ }
1271
+ if (actionType === "Double Tap") {
1272
+ const element = getElements(expression);
1273
+ if (!element) throw new Error(`Invalid Double Tap action parameters: ${actionStr}`);
1274
+ return {
1275
+ _metadata: "do",
1276
+ action: "Double Tap",
1277
+ element
1278
+ };
1279
+ }
1280
+ if (actionType === "Wait") {
1281
+ const duration = getText(expression);
1282
+ if (!duration) throw new Error(`Invalid Wait action parameters: ${actionStr}`);
1283
+ return {
1284
+ _metadata: "do",
1285
+ action: "Wait",
1286
+ duration: Number.parseInt(duration, 10)
1287
+ };
1288
+ }
1289
+ if (actionType === "Take_over") {
1290
+ const reason = getText(expression);
1291
+ if (!reason) throw new Error(`Invalid Take_over action parameters: ${actionStr}`);
1292
+ return {
1293
+ _metadata: "do",
1294
+ action: "Take_over",
1295
+ reason
1296
+ };
1297
+ }
1298
+ }
1299
+ throw new Error(`Unknown action format: ${actionStr}`);
1300
+ }
1301
+
1302
+ //#endregion
1303
+ //#region src/adb/types.ts
1304
+ /**
1305
+ * Screenshot data.
1306
+ */
1307
+ var Screenshot = class {
1308
+ constructor(base64Data, width, height) {
1309
+ this.base64Data = base64Data;
1310
+ this.width = width;
1311
+ this.height = height;
1312
+ }
1313
+ };
1314
+
1315
+ //#endregion
1316
+ //#region src/adb/screenshot.ts
1317
+ /**
1318
+ * Take a screenshot and return it as a base64 encoded string.
1319
+ * Similar to the Python version, saves to temp file first then converts to base64.
1320
+ */
1321
+ async function getScreenshot(deviceId, timeout = 10, quality = 80) {
1322
+ const tempPath = join(tmpdir(), `screenshot_${randomUUID()}.png`);
1323
+ try {
1324
+ const dimensionMatch = (await runAdbCommand(deviceId, [
1325
+ "shell",
1326
+ "wm",
1327
+ "size"
1328
+ ])).stdout.match(/Physical size: (\d+)x(\d+)/);
1329
+ if (!dimensionMatch) throw new Error("Failed to get screen dimensions");
1330
+ const width = Number.parseInt(dimensionMatch[1], 10);
1331
+ const height = Number.parseInt(dimensionMatch[2], 10);
1332
+ const screenshotResult = await runAdbCommand(deviceId, [
1333
+ "shell",
1334
+ "screencap",
1335
+ "-p",
1336
+ "/sdcard/tmp_screenshot.png"
1337
+ ], timeout * 1e3);
1338
+ const output = screenshotResult.stdout + screenshotResult.stderr;
1339
+ if (output.includes("Status: -1") || output.includes("Failed")) return createFallbackScreenshot(true);
1340
+ await runAdbCommand(deviceId, [
1341
+ "pull",
1342
+ "/sdcard/tmp_screenshot.png",
1343
+ tempPath
1344
+ ], timeout * 5e3);
1345
+ const base64Data = (await sharp(tempPath).resize({
1346
+ width: 480,
1347
+ withoutEnlargement: true,
1348
+ fastShrinkOnLoad: true
1349
+ }).webp({
1350
+ lossless: false,
1351
+ quality,
1352
+ effort: 6
1353
+ }).toBuffer()).toString("base64");
1354
+ await unlink(tempPath).catch(() => {});
1355
+ await runAdbCommand(deviceId, [
1356
+ "shell",
1357
+ "rm",
1358
+ "/sdcard/tmp_screenshot.png"
1359
+ ]);
1360
+ return new Screenshot(base64Data, width, height);
1361
+ } catch (error) {
1362
+ console.error(`Screenshot error: ${error}`);
1363
+ try {
1364
+ await unlink(tempPath);
1365
+ } catch {}
1366
+ return createFallbackScreenshot(false);
1367
+ }
1368
+ }
1369
+ /**
1370
+ * Create a black fallback image when screenshot fails.
1371
+ */
1372
+ function createFallbackScreenshot(_isSensitive) {
1373
+ return new Screenshot("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==", 1080, 2400);
1374
+ }
1375
+
1376
+ //#endregion
1377
+ //#region src/context/config.ts
1378
+ /**
1379
+ * Get the system prompt based on the language.
1380
+ */
1381
+ function getSystemPrompt(lang) {
1382
+ return lang === "cn" ? SYSTEM_PROMPT_ZH : SYSTEM_PROMPT_EN;
1383
+ }
1384
+ /**
1385
+ * Configuration for the model.
1386
+ */
1387
+ var AgentConfigStore = class {
1388
+ config = {
1389
+ maxSteps: 100,
1390
+ lang: "cn",
1391
+ baseUrl: "http://localhost:8000/v1",
1392
+ apiKey: "",
1393
+ systemPrompt: void 0,
1394
+ model: "autoglm-phone",
1395
+ maxTokens: 3e3,
1396
+ temperature: 0,
1397
+ topP: .85,
1398
+ frequencyPenalty: .2,
1399
+ screenshotQuality: 80
1400
+ };
1401
+ ensureSystemPrompt() {
1402
+ if (!this.config.systemPrompt) this.config.systemPrompt = getSystemPrompt(this.config.lang);
1403
+ }
1404
+ constructor(initialConfig) {
1405
+ if (initialConfig) this.setConfig(initialConfig);
1406
+ this.ensureSystemPrompt();
1407
+ }
1408
+ setConfig(config) {
1409
+ this.config = {
1410
+ ...this.config,
1411
+ ...config
1412
+ };
1413
+ this.ensureSystemPrompt();
1414
+ }
1415
+ getConfig() {
1416
+ return this.config;
1417
+ }
1418
+ };
1419
+ function createAgentConfigStore(config) {
1420
+ return new AgentConfigStore(config);
1421
+ }
1422
+
1423
+ //#endregion
1424
+ //#region src/context/event.ts
1425
+ let EventType = /* @__PURE__ */ function(EventType$1) {
1426
+ EventType$1["START"] = "start";
1427
+ EventType$1["THINKING"] = "thinking";
1428
+ EventType$1["ACTION"] = "action";
1429
+ EventType$1["TASK_COMPLETE"] = "task_complete";
1430
+ EventType$1["ERROR"] = "error";
1431
+ EventType$1["ABORTED"] = "aborted";
1432
+ EventType$1["THINKING_STREAM"] = "thinking_stream";
1433
+ return EventType$1;
1434
+ }({});
1435
+ function createEmitter() {
1436
+ return mitt();
1437
+ }
1438
+
1439
+ //#endregion
1440
+ //#region src/context/index.ts
1441
+ var AgentContext = class {
1442
+ configStore;
1443
+ emitter;
6
1444
  constructor(config) {
7
- config && setAgentConfig({
8
- ...config,
9
- mode: "api"
1445
+ this.configStore = createAgentConfigStore(config);
1446
+ this.emitter = createEmitter();
1447
+ }
1448
+ getConfig() {
1449
+ return this.configStore.getConfig();
1450
+ }
1451
+ updateConfig(config) {
1452
+ this.configStore.setConfig(config);
1453
+ }
1454
+ emit(event, data) {
1455
+ this.emitter.emit(event, {
1456
+ message: data,
1457
+ time: dayjs().format("hh:mm:ss A")
10
1458
  });
11
- this.phoneAgent = new PhoneAgent();
12
1459
  }
13
- static async createAgent(config) {
14
- setAgentConfig({
15
- ...config,
16
- mode: "api"
1460
+ on(event, handler) {
1461
+ return this.emitter.on(event, handler);
1462
+ }
1463
+ off(event, handler) {
1464
+ return this.emitter.off(event, handler);
1465
+ }
1466
+ };
1467
+
1468
+ //#endregion
1469
+ //#region src/model/client.ts
1470
+ /**
1471
+ * Helper class for building conversation messages.
1472
+ */
1473
+ var MessageBuilder = class {
1474
+ /**
1475
+ * Create a system message.
1476
+ */
1477
+ static createSystemMessage(content) {
1478
+ return {
1479
+ role: "system",
1480
+ content
1481
+ };
1482
+ }
1483
+ /**
1484
+ * Create a user message with optional image.
1485
+ */
1486
+ static createUserMessage(text, imageBase64) {
1487
+ const content = [];
1488
+ if (imageBase64) content.push({
1489
+ type: "image_url",
1490
+ image_url: { url: `data:image/png;base64,${imageBase64}` }
17
1491
  });
18
- const instance = new AutoGLM();
19
- await checkSystemRequirements();
20
- await checkModelApi();
21
- return instance;
1492
+ content.push({
1493
+ type: "text",
1494
+ text
1495
+ });
1496
+ return {
1497
+ role: "user",
1498
+ content
1499
+ };
22
1500
  }
23
- run(task) {
24
- this.phoneAgent.run(task);
25
- return emitter;
1501
+ /**
1502
+ * Create an assistant message.
1503
+ */
1504
+ static createAssistantMessage(content) {
1505
+ return {
1506
+ role: "assistant",
1507
+ content
1508
+ };
26
1509
  }
27
- checkModelApi() {
28
- return checkModelApi();
1510
+ /**
1511
+ * Remove image content from a message to save context space.
1512
+ */
1513
+ static removeImagesFromMessage(message) {
1514
+ if (Array.isArray(message.content)) message.content = message.content.filter((item) => "type" in item && item.type === "text");
1515
+ return message;
1516
+ }
1517
+ /**
1518
+ * Build screen info string for the model.
1519
+ */
1520
+ static buildScreenInfo(currentApp, extraInfo) {
1521
+ const info = {
1522
+ current_app: currentApp,
1523
+ ...extraInfo
1524
+ };
1525
+ return JSON.stringify(info, null, 2);
1526
+ }
1527
+ };
1528
+ /**
1529
+ * Client for interacting with OpenAI-compatible vision-language models.
1530
+ */
1531
+ var ModelClient = class {
1532
+ ctx;
1533
+ client;
1534
+ constructor(ctx) {
1535
+ this.ctx = ctx;
1536
+ this.client = new OpenAI({
1537
+ baseURL: ctx.getConfig().baseUrl,
1538
+ apiKey: ctx.getConfig().apiKey
1539
+ });
1540
+ }
1541
+ get config() {
1542
+ return this.ctx.getConfig();
1543
+ }
1544
+ /**
1545
+ * Send a request to the model with streaming support.
1546
+ */
1547
+ async request(messages, options) {
1548
+ const stream = await this.client.chat.completions.create({
1549
+ messages,
1550
+ model: this.config.model,
1551
+ max_tokens: this.config.maxTokens,
1552
+ temperature: this.config.temperature,
1553
+ top_p: this.config.topP,
1554
+ frequency_penalty: this.config.frequencyPenalty,
1555
+ stream: true
1556
+ }, { signal: options?.signal });
1557
+ let rawContent = "";
1558
+ let buffer = "";
1559
+ const actionMarkers = ["finish(message=", "do(action="];
1560
+ let inActionPhase = false;
1561
+ for await (const chunk of stream) {
1562
+ if (chunk.choices.length === 0) continue;
1563
+ const content = chunk.choices[0].delta.content;
1564
+ if (content) {
1565
+ rawContent += content;
1566
+ if (inActionPhase) continue;
1567
+ buffer += content;
1568
+ let markerFound = false;
1569
+ for (const marker of actionMarkers) if (buffer.includes(marker)) {
1570
+ const thinkingPart = buffer.split(marker, 1)[0];
1571
+ this.ctx.emit(EventType.THINKING_STREAM, thinkingPart);
1572
+ inActionPhase = true;
1573
+ markerFound = true;
1574
+ break;
1575
+ }
1576
+ if (markerFound) continue;
1577
+ let isPotentialMarker = false;
1578
+ for (const marker of actionMarkers) {
1579
+ for (let i = 1; i < marker.length; i++) if (buffer.endsWith(marker.slice(0, i))) {
1580
+ isPotentialMarker = true;
1581
+ break;
1582
+ }
1583
+ if (isPotentialMarker) break;
1584
+ }
1585
+ if (!isPotentialMarker) {
1586
+ this.ctx.emit(EventType.THINKING_STREAM, buffer);
1587
+ buffer = "";
1588
+ }
1589
+ }
1590
+ }
1591
+ const [thinking, action] = this._parseResponse(rawContent);
1592
+ this.ctx.emit(EventType.THINKING, thinking);
1593
+ return {
1594
+ thinking,
1595
+ action,
1596
+ rawContent
1597
+ };
1598
+ }
1599
+ /**
1600
+ * Parse the model response into thinking and action parts.
1601
+ */
1602
+ _parseResponse(content) {
1603
+ if (content.includes("finish(message=")) {
1604
+ const parts = content.split("finish(message=");
1605
+ return [parts[0].trim(), `finish(message=${parts[1]}`];
1606
+ }
1607
+ if (content.includes("do(action=")) {
1608
+ const parts = content.split("do(action=");
1609
+ return [parts[0].trim(), `do(action=${parts[1]}`];
1610
+ }
1611
+ if (content.includes("<answer>")) {
1612
+ const parts = content.split("<answer>");
1613
+ return [parts[0].replace("<think>", "").replace("</think>", "").trim(), parts[1].replace("</answer>", "").trim()];
1614
+ }
1615
+ return ["", content];
1616
+ }
1617
+ };
1618
+
1619
+ //#endregion
1620
+ //#region src/agent/index.ts
1621
+ var PhoneAgent = class {
1622
+ ctx;
1623
+ modelClient;
1624
+ actionHandler;
1625
+ context = [];
1626
+ stepCount = 0;
1627
+ abortController = null;
1628
+ isAborted = false;
1629
+ constructor(context, confirmationCallback, takeoverCallback) {
1630
+ this.ctx = context;
1631
+ this.modelClient = new ModelClient(context);
1632
+ this.actionHandler = new ActionHandler(context, {
1633
+ confirmationCallback,
1634
+ takeoverCallback
1635
+ });
1636
+ }
1637
+ get agentConfig() {
1638
+ return this.ctx.getConfig();
1639
+ }
1640
+ abort(reason = "User aborted") {
1641
+ if (!this.abortController) return;
1642
+ this.abortController.abort(reason);
1643
+ this.isAborted = true;
1644
+ this.ctx.emit(EventType.ABORTED, reason);
1645
+ }
1646
+ checkAborted() {
1647
+ if (this.isAborted && this.abortController?.signal.aborted) return false;
1648
+ return true;
1649
+ }
1650
+ /**
1651
+ * Run the agent to complete a task.
1652
+ */
1653
+ async run(task, signal) {
1654
+ if (this.abortController) {
1655
+ this.abortController.abort();
1656
+ this.abortController = null;
1657
+ }
1658
+ this.abortController = new AbortController();
1659
+ this.isAborted = false;
1660
+ if (signal) signal.addEventListener("abort", () => {
1661
+ this.abort(signal.reason || "External signal aborted");
1662
+ });
1663
+ try {
1664
+ this.reset();
1665
+ let result = await this._executeStep(task, true);
1666
+ if (result.finished) return result.message || "Task completed";
1667
+ while (this.stepCount < this.agentConfig.maxSteps) {
1668
+ if (!this.checkAborted()) return "Agent aborted";
1669
+ result = await this._executeStep(void 0, false);
1670
+ if (result.finished) return result.message || "Task completed";
1671
+ }
1672
+ throw new Error("Max steps reached");
1673
+ } finally {
1674
+ this.abortController = null;
1675
+ }
1676
+ }
1677
+ /**
1678
+ * Execute a single step of the agent.
1679
+ * Useful for manual control or debugging.
1680
+ */
1681
+ async step(task) {
1682
+ const isFirst = this.context.length === 0;
1683
+ if (isFirst && !task) throw new Error("Task is required for the first step");
1684
+ return this._executeStep(task, isFirst);
1685
+ }
1686
+ /**
1687
+ * Reset the agent state for a new task.
1688
+ */
1689
+ reset() {
1690
+ this.context = [];
1691
+ this.stepCount = 0;
1692
+ }
1693
+ /**
1694
+ * Get the current conversation context.
1695
+ */
1696
+ getContext() {
1697
+ return [...this.context];
1698
+ }
1699
+ /**
1700
+ * Get the current step count.
1701
+ */
1702
+ getStepCount() {
1703
+ return this.stepCount;
1704
+ }
1705
+ /**
1706
+ * Execute a single step of the agent loop.
1707
+ */
1708
+ async _executeStep(task, isFirst = false) {
1709
+ this.stepCount++;
1710
+ const screenshot = await getScreenshot(this.agentConfig.deviceId, 10, this.agentConfig.screenshotQuality);
1711
+ const currentApp = await getCurrentApp(this.agentConfig.deviceId);
1712
+ if (isFirst) {
1713
+ this.context.push(MessageBuilder.createSystemMessage(this.agentConfig.systemPrompt));
1714
+ const textContent = `${task}\n\n${MessageBuilder.buildScreenInfo(currentApp)}`;
1715
+ this.context.push(MessageBuilder.createUserMessage(textContent, screenshot.base64Data));
1716
+ } else {
1717
+ const textContent = `** Screen Info **\n\n${MessageBuilder.buildScreenInfo(currentApp)}`;
1718
+ this.context.push(MessageBuilder.createUserMessage(textContent, screenshot.base64Data));
1719
+ }
1720
+ try {
1721
+ const response = await this.modelClient.request(this.context, { signal: this.abortController?.signal });
1722
+ let action;
1723
+ try {
1724
+ action = parseAction(response.action);
1725
+ } catch {
1726
+ action = finish(response.action);
1727
+ }
1728
+ this.ctx.emit(EventType.ACTION, action);
1729
+ this.context[this.context.length - 1] = MessageBuilder.removeImagesFromMessage(this.context[this.context.length - 1]);
1730
+ const result = await this.actionHandler.execute(action, screenshot.width, screenshot.height);
1731
+ this.context.push(MessageBuilder.createAssistantMessage(`<think>${response.thinking}</think><answer>${response.action}</answer>`));
1732
+ const finished = action._metadata === "finish" || result.should_finish;
1733
+ if (finished) {
1734
+ const actionMessage$1 = action._metadata === "finish" ? action.message : void 0;
1735
+ const message = result.message || actionMessage$1 || "Task completed";
1736
+ this.ctx.emit(EventType.TASK_COMPLETE, message);
1737
+ }
1738
+ const actionMessage = action._metadata === "finish" ? action.message : void 0;
1739
+ return {
1740
+ success: result.success,
1741
+ finished,
1742
+ action,
1743
+ thinking: response.thinking,
1744
+ message: result.message || actionMessage
1745
+ };
1746
+ } catch (error) {
1747
+ return {
1748
+ success: false,
1749
+ finished: true,
1750
+ action: null,
1751
+ thinking: "",
1752
+ message: `Model error: ${error instanceof Error ? error.message : "Unknown error"}`
1753
+ };
1754
+ }
1755
+ }
1756
+ };
1757
+
1758
+ //#endregion
1759
+ //#region src/check/index.ts
1760
+ async function checkSystemRequirements(ctx) {
1761
+ /**
1762
+ * check adb connection
1763
+ */
1764
+ const conn = new ADBConnection();
1765
+ const key = new ADBKeyboard(ctx);
1766
+ try {
1767
+ if (!(await conn.version()).success) return {
1768
+ success: false,
1769
+ code: ErrorCode.ADB_NOT_INSTALLED
1770
+ };
1771
+ if (!(await conn.devices()).success) return {
1772
+ success: false,
1773
+ code: ErrorCode.ADB_DEVICE_UNCONNECTED
1774
+ };
1775
+ if (!(await key.isKeyboardInstalled()).success) return {
1776
+ success: false,
1777
+ code: ErrorCode.ADB_KEYBOARD_UNINSTALLED
1778
+ };
1779
+ return { success: true };
1780
+ } catch (error) {
1781
+ return {
1782
+ success: false,
1783
+ message: getErrorMessage(error),
1784
+ code: ErrorCode.UNKNOWN_ERROR
1785
+ };
1786
+ }
1787
+ }
1788
+ async function checkModelApi(ctx) {
1789
+ const config = ctx.getConfig();
1790
+ try {
1791
+ const response = await new OpenAI({
1792
+ baseURL: config.baseUrl,
1793
+ apiKey: config.apiKey
1794
+ }).chat.completions.create({
1795
+ model: config.model,
1796
+ messages: [{
1797
+ role: "user",
1798
+ content: "hi"
1799
+ }],
1800
+ max_tokens: 5,
1801
+ temperature: 0,
1802
+ stream: false
1803
+ });
1804
+ if (!(response.choices && response.choices.length > 0)) return {
1805
+ success: false,
1806
+ code: ErrorCode.MODEL_API_CHECK_FAILED
1807
+ };
1808
+ return { success: true };
1809
+ } catch (error) {
1810
+ return {
1811
+ success: false,
1812
+ message: getErrorMessage(error),
1813
+ code: ErrorCode.UNKNOWN_ERROR
1814
+ };
1815
+ }
1816
+ }
1817
+
1818
+ //#endregion
1819
+ //#region src/autoglm.ts
1820
+ var AutoGLM = class {
1821
+ phoneAgent;
1822
+ ctx;
1823
+ adbManager;
1824
+ constructor(config) {
1825
+ this.ctx = new AgentContext(config);
1826
+ this.phoneAgent = new PhoneAgent(this.ctx);
1827
+ this.adbManager = new ADBManager(this.ctx);
1828
+ }
1829
+ get adb() {
1830
+ return this.adbManager;
1831
+ }
1832
+ abort(reason) {
1833
+ this.phoneAgent.abort(reason);
29
1834
  }
30
1835
  checkSystemRequirements() {
31
- return checkSystemRequirements();
1836
+ return checkSystemRequirements(this.ctx);
1837
+ }
1838
+ checkModelApi() {
1839
+ return checkModelApi(this.ctx);
1840
+ }
1841
+ run(task) {
1842
+ this.ctx.emit(EventType.START, task);
1843
+ return this.phoneAgent.run(task);
1844
+ }
1845
+ on(type, handler) {
1846
+ this.ctx.on(type, handler);
1847
+ return this;
1848
+ }
1849
+ off(type, handler) {
1850
+ this.ctx.off(type, handler);
1851
+ return this;
1852
+ }
1853
+ get config() {
1854
+ return this.ctx.getConfig();
1855
+ }
1856
+ updateConfig(config) {
1857
+ this.ctx.updateConfig(config);
32
1858
  }
33
1859
  };
34
1860
 
35
1861
  //#endregion
36
- export { AutoGLM };
1862
+ export { APP_PACKAGES, AUTOGLM_FILEPATH, AutoGLM, ErrorCode, EventType, SYSTEM_PROMPT_EN, SYSTEM_PROMPT_ZH, Screenshot, listSupportedApps, runAdbCommand };