Coverage for agentos/desktop/shell.py: 0%
74 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-08 13:14 +0800
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-08 13:14 +0800
1"""
2Desktop Shell — AgentOS 原生桌面壳。
4基于 pywebview,将 Web 客户端包裹为原生桌面应用窗口。
5提供与 AutoClaw 桌面客户端类似的体验:
7功能:
8- 原生窗口包裹 Web 前端
9- 系统托盘(最小化到托盘)
10- 开机自启配置
11- 窗口置顶 / 全屏
12- 原生通知
13- 多平台兼容(Windows / macOS / Linux)
15依赖: pip install pywebview
17启动方式:
18 python -m agentos.desktop.shell # 连接本地服务
19 python -m agentos.desktop.shell --url http://1.2.3.4:19999 # 连接远程
20"""
22from __future__ import annotations
24import argparse
25import json
26import os
27import time
28import webbrowser
30# ── 配置 ───────────────────────────────────────────────────────
32APP_NAME = "AgentOS Desktop"
33APP_VERSION = "1.7.1"
34DEFAULT_URL = "http://127.0.0.1:19999"
35CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".agentos")
36CONFIG_FILE = os.path.join(CONFIG_DIR, "desktop.json")
39def load_config() -> dict:
40 """加载本地配置。"""
41 defaults = {
42 "url": DEFAULT_URL,
43 "width": 1200,
44 "height": 800,
45 "fullscreen": False,
46 "always_on_top": False,
47 "auto_start": False,
48 "minimize_to_tray": True,
49 "title": f"{APP_NAME} v{APP_VERSION}",
50 }
51 if os.path.isfile(CONFIG_FILE):
52 try:
53 with open(CONFIG_FILE) as f:
54 defaults.update(json.load(f))
55 except Exception:
56 pass
57 return defaults
60def save_config(cfg: dict) -> None:
61 os.makedirs(CONFIG_DIR, exist_ok=True)
62 with open(CONFIG_FILE, "w") as f:
63 json.dump(cfg, f, indent=2)
66# ── 原生桌面壳 ─────────────────────────────────────────────────
69def create_native_shell(
70 url: str,
71 width: int = 1200,
72 height: int = 800,
73 title: str = APP_NAME,
74 fullscreen: bool = False,
75 on_top: bool = False,
76 minimize_to_tray: bool = True,
77) -> None:
78 """使用 pywebview 创建原生桌面窗口。"""
79 try:
80 import webview
81 except ImportError:
82 print("需要安装 pywebview: pip install pywebview")
83 print("自动打开浏览器作为降级方案...")
84 webbrowser.open(url)
85 try:
86 while True:
87 time.sleep(1)
88 except KeyboardInterrupt:
89 pass
90 return
92 webview.create_window(
93 title=title,
94 url=url,
95 width=width,
96 height=height,
97 fullscreen=fullscreen,
98 on_top=on_top,
99 easy_drag=False,
100 confirm_close=minimize_to_tray,
101 text_select=True,
102 )
104 # 系统托盘暂时关闭(pywebview 托盘支持有限)
105 # 完整的托盘功能需要 pywebview >= 5.0 + 特定平台的额外配置
106 webview.start(gui="cef", debug=False)
109# ── 命令行入口 ──
112def main() -> None:
113 parser = argparse.ArgumentParser(
114 description="AgentOS Desktop Shell — 原生桌面壳",
115 formatter_class=argparse.RawDescriptionHelpFormatter,
116 epilog="""
117示例:
118 agentos desktop-shell # 连接本地 127.0.0.1:19999
119 agentos desktop-shell --url http://remote:19999 # 连接远程服务
120 agentos desktop-shell --fullscreen # 全屏模式
121 agentos desktop-shell --on-top # 窗口置顶
122 agentos desktop-shell --browser # 直接用浏览器打开(降级)
123 """,
124 )
125 parser.add_argument("--url", default=None, help=f"服务端地址(默认: {DEFAULT_URL})")
126 parser.add_argument("--width", type=int, default=1200, help="窗口宽度(默认: 1200)")
127 parser.add_argument("--height", type=int, default=800, help="窗口高度(默认: 800)")
128 parser.add_argument("--fullscreen", action="store_true", help="全屏启动")
129 parser.add_argument("--on-top", action="store_true", help="窗口置顶")
130 parser.add_argument("--no-tray", action="store_true", help="禁用最小化到托盘")
131 parser.add_argument("--browser", action="store_true", help="直接用系统浏览器打开")
132 parser.add_argument("--config", action="store_true", help="显示当前配置")
134 args = parser.parse_args()
135 cfg = load_config()
137 if args.config:
138 print(json.dumps(cfg, indent=2, ensure_ascii=False))
139 return
141 url = args.url or cfg.get("url", DEFAULT_URL)
142 width = args.width
143 height = args.height
144 title = cfg.get("title", APP_NAME)
145 fullscreen = args.fullscreen
146 on_top = args.on_top
147 tray = not args.no_tray
149 if args.browser:
150 print(f"用浏览器打开 {url} ...")
151 webbrowser.open(url)
152 try:
153 while True:
154 time.sleep(1)
155 except KeyboardInterrupt:
156 pass
157 else:
158 print(f"启动 AgentOS Desktop Shell v{APP_VERSION}")
159 print(f"连接: {url}")
160 create_native_shell(
161 url=url,
162 width=width,
163 height=height,
164 title=title,
165 fullscreen=fullscreen,
166 on_top=on_top,
167 minimize_to_tray=tray,
168 )
171if __name__ == "__main__":
172 main()