autoglm-gui 1.0.0__py3-none-any.whl → 1.0.2__py3-none-any.whl

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 (41) hide show
  1. AutoGLM_GUI/api/devices.py +49 -0
  2. AutoGLM_GUI/schemas.py +16 -0
  3. AutoGLM_GUI/static/assets/{about-29B5FDM8.js → about-BOnRPlKQ.js} +1 -1
  4. AutoGLM_GUI/static/assets/chat-CGW6uMKB.js +149 -0
  5. AutoGLM_GUI/static/assets/{index-mVNV0VwM.js → index-CRFVU0eu.js} +1 -1
  6. AutoGLM_GUI/static/assets/{index-wu8Wjf12.js → index-DH-Dl4tK.js} +5 -5
  7. AutoGLM_GUI/static/assets/index-DzUQ89YC.css +1 -0
  8. AutoGLM_GUI/static/index.html +2 -2
  9. {autoglm_gui-1.0.0.dist-info → autoglm_gui-1.0.2.dist-info}/METADATA +9 -4
  10. autoglm_gui-1.0.2.dist-info/RECORD +73 -0
  11. phone_agent/__init__.py +3 -2
  12. phone_agent/actions/handler.py +124 -31
  13. phone_agent/actions/handler_ios.py +278 -0
  14. phone_agent/adb/connection.py +14 -5
  15. phone_agent/adb/device.py +47 -16
  16. phone_agent/agent.py +8 -8
  17. phone_agent/agent_ios.py +277 -0
  18. phone_agent/config/__init__.py +18 -0
  19. phone_agent/config/apps.py +1 -1
  20. phone_agent/config/apps_harmonyos.py +256 -0
  21. phone_agent/config/apps_ios.py +339 -0
  22. phone_agent/config/i18n.py +8 -0
  23. phone_agent/config/timing.py +167 -0
  24. phone_agent/device_factory.py +166 -0
  25. phone_agent/hdc/__init__.py +53 -0
  26. phone_agent/hdc/connection.py +384 -0
  27. phone_agent/hdc/device.py +269 -0
  28. phone_agent/hdc/input.py +145 -0
  29. phone_agent/hdc/screenshot.py +127 -0
  30. phone_agent/model/client.py +104 -4
  31. phone_agent/xctest/__init__.py +47 -0
  32. phone_agent/xctest/connection.py +379 -0
  33. phone_agent/xctest/device.py +472 -0
  34. phone_agent/xctest/input.py +311 -0
  35. phone_agent/xctest/screenshot.py +226 -0
  36. AutoGLM_GUI/static/assets/chat-DTN2oKtA.js +0 -149
  37. AutoGLM_GUI/static/assets/index-Dy550Qqg.css +0 -1
  38. autoglm_gui-1.0.0.dist-info/RECORD +0 -57
  39. {autoglm_gui-1.0.0.dist-info → autoglm_gui-1.0.2.dist-info}/WHEEL +0 -0
  40. {autoglm_gui-1.0.0.dist-info → autoglm_gui-1.0.2.dist-info}/entry_points.txt +0 -0
  41. {autoglm_gui-1.0.0.dist-info → autoglm_gui-1.0.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,277 @@
1
+ """iOS PhoneAgent class for orchestrating iOS phone automation."""
2
+
3
+ import json
4
+ import traceback
5
+ from dataclasses import dataclass
6
+ from typing import Any, Callable
7
+
8
+ from phone_agent.actions.handler import finish, parse_action
9
+ from phone_agent.actions.handler_ios import IOSActionHandler
10
+ from phone_agent.config import get_messages, get_system_prompt
11
+ from phone_agent.model import ModelClient, ModelConfig
12
+ from phone_agent.model.client import MessageBuilder
13
+ from phone_agent.xctest import XCTestConnection, get_current_app, get_screenshot
14
+
15
+
16
+ @dataclass
17
+ class IOSAgentConfig:
18
+ """Configuration for the iOS PhoneAgent."""
19
+
20
+ max_steps: int = 100
21
+ wda_url: str = "http://localhost:8100"
22
+ session_id: str | None = None
23
+ device_id: str | None = None # iOS device UDID
24
+ lang: str = "cn"
25
+ system_prompt: str | None = None
26
+ verbose: bool = True
27
+
28
+ def __post_init__(self):
29
+ if self.system_prompt is None:
30
+ self.system_prompt = get_system_prompt(self.lang)
31
+
32
+
33
+ @dataclass
34
+ class StepResult:
35
+ """Result of a single agent step."""
36
+
37
+ success: bool
38
+ finished: bool
39
+ action: dict[str, Any] | None
40
+ thinking: str
41
+ message: str | None = None
42
+
43
+
44
+ class IOSPhoneAgent:
45
+ """
46
+ AI-powered agent for automating iOS phone interactions.
47
+
48
+ The agent uses a vision-language model to understand screen content
49
+ and decide on actions to complete user tasks via WebDriverAgent.
50
+
51
+ Args:
52
+ model_config: Configuration for the AI model.
53
+ agent_config: Configuration for the iOS agent behavior.
54
+ confirmation_callback: Optional callback for sensitive action confirmation.
55
+ takeover_callback: Optional callback for takeover requests.
56
+
57
+ Example:
58
+ >>> from phone_agent.agent_ios import IOSPhoneAgent, IOSAgentConfig
59
+ >>> from phone_agent.model import ModelConfig
60
+ >>>
61
+ >>> model_config = ModelConfig(base_url="http://localhost:8000/v1")
62
+ >>> agent_config = IOSAgentConfig(wda_url="http://localhost:8100")
63
+ >>> agent = IOSPhoneAgent(model_config, agent_config)
64
+ >>> agent.run("Open Safari and search for Apple")
65
+ """
66
+
67
+ def __init__(
68
+ self,
69
+ model_config: ModelConfig | None = None,
70
+ agent_config: IOSAgentConfig | None = None,
71
+ confirmation_callback: Callable[[str], bool] | None = None,
72
+ takeover_callback: Callable[[str], None] | None = None,
73
+ ):
74
+ self.model_config = model_config or ModelConfig()
75
+ self.agent_config = agent_config or IOSAgentConfig()
76
+
77
+ self.model_client = ModelClient(self.model_config)
78
+
79
+ # Initialize WDA connection and create session if needed
80
+ self.wda_connection = XCTestConnection(wda_url=self.agent_config.wda_url)
81
+
82
+ # Auto-create session if not provided
83
+ if self.agent_config.session_id is None:
84
+ success, session_id = self.wda_connection.start_wda_session()
85
+ if success and session_id != "session_started":
86
+ self.agent_config.session_id = session_id
87
+ if self.agent_config.verbose:
88
+ print(f"✅ Created WDA session: {session_id}")
89
+ elif self.agent_config.verbose:
90
+ print("⚠️ Using default WDA session (no explicit session ID)")
91
+
92
+ self.action_handler = IOSActionHandler(
93
+ wda_url=self.agent_config.wda_url,
94
+ session_id=self.agent_config.session_id,
95
+ confirmation_callback=confirmation_callback,
96
+ takeover_callback=takeover_callback,
97
+ )
98
+
99
+ self._context: list[dict[str, Any]] = []
100
+ self._step_count = 0
101
+
102
+ def run(self, task: str) -> str:
103
+ """
104
+ Run the agent to complete a task.
105
+
106
+ Args:
107
+ task: Natural language description of the task.
108
+
109
+ Returns:
110
+ Final message from the agent.
111
+ """
112
+ self._context = []
113
+ self._step_count = 0
114
+
115
+ # First step with user prompt
116
+ result = self._execute_step(task, is_first=True)
117
+
118
+ if result.finished:
119
+ return result.message or "Task completed"
120
+
121
+ # Continue until finished or max steps reached
122
+ while self._step_count < self.agent_config.max_steps:
123
+ result = self._execute_step(is_first=False)
124
+
125
+ if result.finished:
126
+ return result.message or "Task completed"
127
+
128
+ return "Max steps reached"
129
+
130
+ def step(self, task: str | None = None) -> StepResult:
131
+ """
132
+ Execute a single step of the agent.
133
+
134
+ Useful for manual control or debugging.
135
+
136
+ Args:
137
+ task: Task description (only needed for first step).
138
+
139
+ Returns:
140
+ StepResult with step details.
141
+ """
142
+ is_first = len(self._context) == 0
143
+
144
+ if is_first and not task:
145
+ raise ValueError("Task is required for the first step")
146
+
147
+ return self._execute_step(task, is_first)
148
+
149
+ def reset(self) -> None:
150
+ """Reset the agent state for a new task."""
151
+ self._context = []
152
+ self._step_count = 0
153
+
154
+ def _execute_step(
155
+ self, user_prompt: str | None = None, is_first: bool = False
156
+ ) -> StepResult:
157
+ """Execute a single step of the agent loop."""
158
+ self._step_count += 1
159
+
160
+ # Capture current screen state
161
+ screenshot = get_screenshot(
162
+ wda_url=self.agent_config.wda_url,
163
+ session_id=self.agent_config.session_id,
164
+ device_id=self.agent_config.device_id,
165
+ )
166
+ current_app = get_current_app(
167
+ wda_url=self.agent_config.wda_url, session_id=self.agent_config.session_id
168
+ )
169
+
170
+ # Build messages
171
+ if is_first:
172
+ self._context.append(
173
+ MessageBuilder.create_system_message(self.agent_config.system_prompt)
174
+ )
175
+
176
+ screen_info = MessageBuilder.build_screen_info(current_app)
177
+ text_content = f"{user_prompt}\n\n{screen_info}"
178
+
179
+ self._context.append(
180
+ MessageBuilder.create_user_message(
181
+ text=text_content, image_base64=screenshot.base64_data
182
+ )
183
+ )
184
+ else:
185
+ screen_info = MessageBuilder.build_screen_info(current_app)
186
+ text_content = f"** Screen Info **\n\n{screen_info}"
187
+
188
+ self._context.append(
189
+ MessageBuilder.create_user_message(
190
+ text=text_content, image_base64=screenshot.base64_data
191
+ )
192
+ )
193
+
194
+ # Get model response
195
+ try:
196
+ response = self.model_client.request(self._context)
197
+ except Exception as e:
198
+ if self.agent_config.verbose:
199
+ traceback.print_exc()
200
+ return StepResult(
201
+ success=False,
202
+ finished=True,
203
+ action=None,
204
+ thinking="",
205
+ message=f"Model error: {e}",
206
+ )
207
+
208
+ # Parse action from response
209
+ try:
210
+ action = parse_action(response.action)
211
+ except ValueError:
212
+ if self.agent_config.verbose:
213
+ traceback.print_exc()
214
+ action = finish(message=response.action)
215
+
216
+ if self.agent_config.verbose:
217
+ # Print thinking process
218
+ msgs = get_messages(self.agent_config.lang)
219
+ print("\n" + "=" * 50)
220
+ print(f"💭 {msgs['thinking']}:")
221
+ print("-" * 50)
222
+ print(response.thinking)
223
+ print("-" * 50)
224
+ print(f"🎯 {msgs['action']}:")
225
+ print(json.dumps(action, ensure_ascii=False, indent=2))
226
+ print("=" * 50 + "\n")
227
+
228
+ # Remove image from context to save space
229
+ self._context[-1] = MessageBuilder.remove_images_from_message(self._context[-1])
230
+
231
+ # Execute action
232
+ try:
233
+ result = self.action_handler.execute(
234
+ action, screenshot.width, screenshot.height
235
+ )
236
+ except Exception as e:
237
+ if self.agent_config.verbose:
238
+ traceback.print_exc()
239
+ result = self.action_handler.execute(
240
+ finish(message=str(e)), screenshot.width, screenshot.height
241
+ )
242
+
243
+ # Add assistant response to context
244
+ self._context.append(
245
+ MessageBuilder.create_assistant_message(
246
+ f"<think>{response.thinking}</think><answer>{response.action}</answer>"
247
+ )
248
+ )
249
+
250
+ # Check if finished
251
+ finished = action.get("_metadata") == "finish" or result.should_finish
252
+
253
+ if finished and self.agent_config.verbose:
254
+ msgs = get_messages(self.agent_config.lang)
255
+ print("\n" + "🎉 " + "=" * 48)
256
+ print(
257
+ f"✅ {msgs['task_completed']}: {result.message or action.get('message', msgs['done'])}"
258
+ )
259
+ print("=" * 50 + "\n")
260
+
261
+ return StepResult(
262
+ success=result.success,
263
+ finished=finished,
264
+ action=action,
265
+ thinking=response.thinking,
266
+ message=result.message or action.get("message"),
267
+ )
268
+
269
+ @property
270
+ def context(self) -> list[dict[str, Any]]:
271
+ """Get the current conversation context."""
272
+ return self._context.copy()
273
+
274
+ @property
275
+ def step_count(self) -> int:
276
+ """Get the current step count."""
277
+ return self._step_count
@@ -1,9 +1,19 @@
1
1
  """Configuration module for Phone Agent."""
2
2
 
3
3
  from phone_agent.config.apps import APP_PACKAGES
4
+ from phone_agent.config.apps_ios import APP_PACKAGES_IOS
4
5
  from phone_agent.config.i18n import get_message, get_messages
5
6
  from phone_agent.config.prompts_en import SYSTEM_PROMPT as SYSTEM_PROMPT_EN
6
7
  from phone_agent.config.prompts_zh import SYSTEM_PROMPT as SYSTEM_PROMPT_ZH
8
+ from phone_agent.config.timing import (
9
+ TIMING_CONFIG,
10
+ ActionTimingConfig,
11
+ ConnectionTimingConfig,
12
+ DeviceTimingConfig,
13
+ TimingConfig,
14
+ get_timing_config,
15
+ update_timing_config,
16
+ )
7
17
 
8
18
 
9
19
  def get_system_prompt(lang: str = "cn") -> str:
@@ -26,10 +36,18 @@ SYSTEM_PROMPT = SYSTEM_PROMPT_ZH
26
36
 
27
37
  __all__ = [
28
38
  "APP_PACKAGES",
39
+ "APP_PACKAGES_IOS",
29
40
  "SYSTEM_PROMPT",
30
41
  "SYSTEM_PROMPT_ZH",
31
42
  "SYSTEM_PROMPT_EN",
32
43
  "get_system_prompt",
33
44
  "get_messages",
34
45
  "get_message",
46
+ "TIMING_CONFIG",
47
+ "TimingConfig",
48
+ "ActionTimingConfig",
49
+ "DeviceTimingConfig",
50
+ "ConnectionTimingConfig",
51
+ "get_timing_config",
52
+ "update_timing_config",
35
53
  ]
@@ -29,7 +29,7 @@ APP_PACKAGES: dict[str, str] = {
29
29
  "12306": "com.MobileTicket",
30
30
  "去哪儿": "com.Qunar",
31
31
  "去哪儿旅行": "com.Qunar",
32
- "滴滴出行": "com.sdu.did.psnger",
32
+ "滴滴出行": "com.sdu.didi.psnger",
33
33
  # Video & Entertainment
34
34
  "bilibili": "tv.danmaku.bili",
35
35
  "抖音": "com.ss.android.ugc.aweme",
@@ -0,0 +1,256 @@
1
+ """HarmonyOS application package name mappings.
2
+
3
+ Maps user-friendly app names to HarmonyOS bundle names.
4
+ These bundle names are used with the 'hdc shell aa start -b <bundle>' command.
5
+ """
6
+
7
+ # Custom ability names for apps that don't use the default "EntryAbility"
8
+ # Maps bundle_name -> ability_name
9
+ # Generated by: python test/find_abilities.py
10
+ APP_ABILITIES: dict[str, str] = {
11
+ # Third-party apps
12
+ "cn.wps.mobileoffice.hap": "DocumentAbility",
13
+ "com.ccb.mobilebank.hm": "CcbMainAbility",
14
+ "com.dewu.hos": "HomeAbility",
15
+ "com.larus.nova.hm": "MainAbility",
16
+ "com.luna.hm.music": "MainAbility",
17
+ "com.meitu.meitupic": "MainAbility",
18
+ "com.ss.hm.article.news": "MainAbility",
19
+ "com.ss.hm.ugc.aweme": "MainAbility",
20
+ "com.taobao.taobao4hmos": "Taobao_mainAbility",
21
+ "com.tencent.videohm": "AppAbility",
22
+ "com.ximalaya.ting.xmharmony": "MainBundleAbility",
23
+ "com.zhihu.hmos": "PhoneAbility",
24
+ # Huawei system apps
25
+ "com.huawei.hmos.browser": "MainAbility",
26
+ "com.huawei.hmos.calculator": "com.huawei.hmos.calculator.CalculatorAbility",
27
+ "com.huawei.hmos.calendar": "MainAbility",
28
+ "com.huawei.hmos.camera": "com.huawei.hmos.camera.MainAbility",
29
+ "com.huawei.hmos.clock": "com.huawei.hmos.clock.phone",
30
+ "com.huawei.hmos.clouddrive": "MainAbility",
31
+ "com.huawei.hmos.email": "ApplicationAbility",
32
+ "com.huawei.hmos.filemanager": "MainAbility",
33
+ "com.huawei.hmos.health": "Activity_card_entryAbility",
34
+ "com.huawei.hmos.notepad": "MainAbility",
35
+ "com.huawei.hmos.photos": "MainAbility",
36
+ "com.huawei.hmos.screenrecorder": "com.huawei.hmos.screenrecorder.ServiceExtAbility",
37
+ "com.huawei.hmos.screenshot": "com.huawei.hmos.screenshot.ServiceExtAbility",
38
+ "com.huawei.hmos.settings": "com.huawei.hmos.settings.MainAbility",
39
+ "com.huawei.hmos.soundrecorder": "MainAbility",
40
+ "com.huawei.hmos.vassistant": "AiCaptionServiceExtAbility",
41
+ "com.huawei.hmos.wallet": "MainAbility",
42
+ # Huawei services
43
+ "com.huawei.hmsapp.appgallery": "MainAbility",
44
+ "com.huawei.hmsapp.books": "MainAbility",
45
+ "com.huawei.hmsapp.himovie": "MainAbility",
46
+ "com.huawei.hmsapp.hisearch": "MainAbility",
47
+ "com.huawei.hmsapp.music": "MainAbility",
48
+ "com.huawei.hmsapp.thememanager": "MainAbility",
49
+ "com.huawei.hmsapp.totemweather": "com.huawei.hmsapp.totemweather.MainAbility",
50
+ # OHOS system apps
51
+ "com.ohos.callui": "com.ohos.callui.ServiceAbility",
52
+ "com.ohos.contacts": "com.ohos.contacts.MainAbility",
53
+ "com.ohos.mms": "com.ohos.mms.MainAbility",
54
+ }
55
+
56
+ APP_PACKAGES: dict[str, str] = {
57
+ # Social & Messaging
58
+ "微信": "com.tencent.wechat",
59
+ "QQ": "com.tencent.mqq",
60
+ "微博": "com.sina.weibo.stage",
61
+ # E-commerce
62
+ "淘宝": "com.taobao.taobao4hmos",
63
+ "京东": "com.jd.hm.mall",
64
+ "拼多多": "com.xunmeng.pinduoduo.hos",
65
+ "淘宝闪购": "com.taobao.taobao4hmos",
66
+ "京东秒送": "com.jd.hm.mall",
67
+ # Lifestyle & Social
68
+ "小红书": "com.xingin.xhs_hos",
69
+ "知乎": "com.zhihu.hmos",
70
+ # "豆瓣": "com.douban.frodo", # 未在 hdc 列表中找到
71
+ # Maps & Navigation
72
+ "高德地图": "com.amap.hmapp",
73
+ "百度地图": "com.baidu.hmmap",
74
+ # Food & Services
75
+ "美团": "com.sankuai.hmeituan",
76
+ "美团外卖": "com.meituan.takeaway",
77
+ "大众点评": "com.sankuai.dianping",
78
+ # "肯德基": "com.yek.android.kfc.activitys", # 未在 hdc 列表中找到
79
+ # Travel
80
+ # "携程": "ctrip.android.view", # 未在 hdc 列表中找到
81
+ "铁路12306": "com.chinarailway.ticketingHM",
82
+ "12306": "com.chinarailway.ticketingHM",
83
+ # "去哪儿": "com.Qunar", # 未在 hdc 列表中找到
84
+ # "去哪儿旅行": "com.Qunar", # 未在 hdc 列表中找到
85
+ "滴滴出行": "com.sdu.didi.hmos.psnger",
86
+ # Video & Entertainment
87
+ "bilibili": "yylx.danmaku.bili",
88
+ "抖音": "com.ss.hm.ugc.aweme",
89
+ "快手": "com.kuaishou.hmapp",
90
+ "腾讯视频": "com.tencent.videohm",
91
+ "爱奇艺": "com.qiyi.video.hmy",
92
+ "芒果TV": "com.mgtv.phone",
93
+ # "优酷视频": "com.youku.phone", # 未在 hdc 列表中找到
94
+ # "红果短剧": "com.phoenix.read", # 未在 hdc 列表中找到
95
+ # Music & Audio
96
+ # "网易云音乐": "com.netease.cloudmusic", # 未在 hdc 列表中找到
97
+ "QQ音乐": "com.tencent.hm.qqmusic",
98
+ "汽水音乐": "com.luna.hm.music",
99
+ "喜马拉雅": "com.ximalaya.ting.xmharmony",
100
+ # Reading
101
+ # "番茄小说": "com.dragon.read", # 未在 hdc 列表中找到
102
+ # "番茄免费小说": "com.dragon.read", # 未在 hdc 列表中找到
103
+ # "七猫免费小说": "com.kmxs.reader", # 未在 hdc 列表中找到
104
+ # Productivity
105
+ "飞书": "com.ss.feishu",
106
+ # "QQ邮箱": "com.tencent.androidqqmail", # 未在 hdc 列表中找到
107
+ # AI & Tools
108
+ "豆包": "com.larus.nova.hm",
109
+ # Health & Fitness
110
+ # "keep": "com.gotokeep.keep", # 未在 hdc 列表中找到
111
+ # "美柚": "com.lingan.seeyou", # 未在 hdc 列表中找到
112
+ # News & Information
113
+ # "腾讯新闻": "com.tencent.news", # 未在 hdc 列表中找到
114
+ "今日头条": "com.ss.hm.article.news",
115
+ # Real Estate
116
+ # "贝壳找房": "com.lianjia.beike", # 未在 hdc 列表中找到
117
+ # "安居客": "com.anjuke.android.app", # 未在 hdc 列表中找到
118
+ # Finance
119
+ # "同花顺": "com.hexin.plat.android", # 未在 hdc 列表中找到
120
+ # Games
121
+ # "星穹铁道": "com.miHoYo.hkrpg", # 未在 hdc 列表中找到
122
+ # "崩坏:星穹铁道": "com.miHoYo.hkrpg", # 未在 hdc 列表中找到
123
+ # "恋与深空": "com.papegames.lysk.cn", # 未在 hdc 列表中找到
124
+ # HarmonyOS 第三方应用
125
+ "百度": "com.baidu.baiduapp",
126
+ "阿里巴巴": "com.alibaba.wireless_hmos",
127
+ "WPS": "cn.wps.mobileoffice.hap",
128
+ "企业微信": "com.tencent.wework.hmos",
129
+ "同程": "com.tongcheng.hmos",
130
+ "同程旅行": "com.tongcheng.hmos",
131
+ "唯品会": "com.vip.hosapp",
132
+ "支付宝": "com.alipay.mobile.client",
133
+ "UC浏览器": "com.uc.mobile",
134
+ "闲鱼": "com.taobao.idlefish4ohos",
135
+ "转转": "com.zhuanzhuan.hmoszz",
136
+ "迅雷": "com.xunlei.thunder",
137
+ "搜狗输入法": "com.sogou.input",
138
+ "扫描全能王": "com.intsig.camscanner.hap",
139
+ "美图秀秀": "com.meitu.meitupic",
140
+ "58同城": "com.wuba.life",
141
+ "得物": "com.dewu.hos",
142
+ "海底捞": "com.haidilao.haros",
143
+ "中国移动": "com.droi.tong",
144
+ "中国联通": "com.sinovatech.unicom.ha",
145
+ "国家税务总局": "cn.gov.chinatax.gt4.hm",
146
+ "建设银行": "com.ccb.mobilebank.hm",
147
+ "快手极速版": "com.kuaishou.hmnebula",
148
+ # HarmonyOS 系统应用 - 工具类
149
+ "浏览器": "com.huawei.hmos.browser",
150
+ "计算器": "com.huawei.hmos.calculator",
151
+ "日历": "com.huawei.hmos.calendar",
152
+ "相机": "com.huawei.hmos.camera",
153
+ "时钟": "com.huawei.hmos.clock",
154
+ "云盘": "com.huawei.hmos.clouddrive",
155
+ "云空间": "com.huawei.hmos.clouddrive",
156
+ "邮件": "com.huawei.hmos.email",
157
+ "文件管理器": "com.huawei.hmos.filemanager",
158
+ "文件": "com.huawei.hmos.files",
159
+ "查找设备": "com.huawei.hmos.finddevice",
160
+ "查找手机": "com.huawei.hmos.finddevice",
161
+ "录音机": "com.huawei.hmos.soundrecorder",
162
+ "录音": "com.huawei.hmos.soundrecorder",
163
+ "录屏": "com.huawei.hmos.screenrecorder",
164
+ "截屏": "com.huawei.hmos.screenshot",
165
+ "笔记": "com.huawei.hmos.notepad",
166
+ "备忘录": "com.huawei.hmos.notepad",
167
+ # HarmonyOS 系统应用 - 媒体类
168
+ "相册": "com.huawei.hmos.photos",
169
+ "图库": "com.huawei.hmos.photos",
170
+ # "视频": "com.huawei.hmos.mediaplayer", # 未在 hdc 列表中找到,但有 com.huawei.hmsapp.himovie
171
+ # HarmonyOS 系统应用 - 通讯类
172
+ "联系人": "com.ohos.contacts",
173
+ "通讯录": "com.ohos.contacts",
174
+ "短信": "com.ohos.mms",
175
+ "信息": "com.ohos.mms",
176
+ "电话": "com.ohos.callui",
177
+ "拨号": "com.ohos.callui",
178
+ # HarmonyOS 系统应用 - 设置类
179
+ "设置": "com.huawei.hmos.settings",
180
+ "系统设置": "com.huawei.hmos.settings",
181
+ "AndroidSystemSettings": "com.huawei.hmos.settings",
182
+ "Android System Settings": "com.huawei.hmos.settings",
183
+ "Android System Settings": "com.huawei.hmos.settings",
184
+ "Android-System-Settings": "com.huawei.hmos.settings",
185
+ "Settings": "com.huawei.hmos.settings",
186
+ # HarmonyOS 系统应用 - 生活服务
187
+ "健康": "com.huawei.hmos.health",
188
+ "运动健康": "com.huawei.hmos.health",
189
+ "地图": "com.huawei.hmos.maps.app",
190
+ "华为地图": "com.huawei.hmos.maps.app",
191
+ "钱包": "com.huawei.hmos.wallet",
192
+ "华为钱包": "com.huawei.hmos.wallet",
193
+ "智慧生活": "com.huawei.hmos.ailife",
194
+ "智能助手": "com.huawei.hmos.vassistant",
195
+ "小艺": "com.huawei.hmos.vassistant",
196
+ # HarmonyOS 服务
197
+ "应用市场": "com.huawei.hmsapp.appgallery",
198
+ "华为应用市场": "com.huawei.hmsapp.appgallery",
199
+ "音乐": "com.huawei.hmsapp.music",
200
+ "华为音乐": "com.huawei.hmsapp.music",
201
+ "主题": "com.huawei.hmsapp.thememanager",
202
+ "主题管理": "com.huawei.hmsapp.thememanager",
203
+ "天气": "com.huawei.hmsapp.totemweather",
204
+ "华为天气": "com.huawei.hmsapp.totemweather",
205
+ "视频": "com.huawei.hmsapp.himovie",
206
+ "华为视频": "com.huawei.hmsapp.himovie",
207
+ "阅读": "com.huawei.hmsapp.books",
208
+ "华为阅读": "com.huawei.hmsapp.books",
209
+ "游戏中心": "com.huawei.hmsapp.gamecenter",
210
+ "华为游戏中心": "com.huawei.hmsapp.gamecenter",
211
+ "搜索": "com.huawei.hmsapp.hisearch",
212
+ "华为搜索": "com.huawei.hmsapp.hisearch",
213
+ "指南针": "com.huawei.hmsapp.compass",
214
+ "会员中心": "com.huawei.hmos.myhuawei",
215
+ "我的华为": "com.huawei.hmos.myhuawei",
216
+ "华为会员": "com.huawei.hmos.myhuawei",
217
+ }
218
+
219
+
220
+ def get_package_name(app_name: str) -> str | None:
221
+ """
222
+ Get the package name for an app.
223
+
224
+ Args:
225
+ app_name: The display name of the app.
226
+
227
+ Returns:
228
+ The HarmonyOS bundle name, or None if not found.
229
+ """
230
+ return APP_PACKAGES.get(app_name)
231
+
232
+
233
+ def get_app_name(package_name: str) -> str | None:
234
+ """
235
+ Get the app name from a package name.
236
+
237
+ Args:
238
+ package_name: The HarmonyOS bundle name.
239
+
240
+ Returns:
241
+ The display name of the app, or None if not found.
242
+ """
243
+ for name, package in APP_PACKAGES.items():
244
+ if package == package_name:
245
+ return name
246
+ return None
247
+
248
+
249
+ def list_supported_apps() -> list[str]:
250
+ """
251
+ Get a list of all supported app names.
252
+
253
+ Returns:
254
+ List of app names.
255
+ """
256
+ return list(APP_PACKAGES.keys())