Coverage for agentos/desktop/shell.py: 0%

74 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-07-08 10:59 +0800

1""" 

2Desktop Shell — AgentOS 原生桌面壳。 

3 

4基于 pywebview,将 Web 客户端包裹为原生桌面应用窗口。 

5提供与 AutoClaw 桌面客户端类似的体验: 

6 

7功能: 

8- 原生窗口包裹 Web 前端 

9- 系统托盘(最小化到托盘) 

10- 开机自启配置 

11- 窗口置顶 / 全屏 

12- 原生通知 

13- 多平台兼容(Windows / macOS / Linux) 

14 

15依赖: pip install pywebview 

16 

17启动方式: 

18 python -m agentos.desktop.shell # 连接本地服务 

19 python -m agentos.desktop.shell --url http://1.2.3.4:19999 # 连接远程 

20""" 

21 

22from __future__ import annotations 

23 

24import argparse 

25import json 

26import os 

27import time 

28import webbrowser 

29 

30# ── 配置 ─────────────────────────────────────────────────────── 

31 

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") 

37 

38 

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 

58 

59 

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) 

64 

65 

66# ── 原生桌面壳 ───────────────────────────────────────────────── 

67 

68 

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 

91 

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 ) 

103 

104 # 系统托盘暂时关闭(pywebview 托盘支持有限) 

105 # 完整的托盘功能需要 pywebview >= 5.0 + 特定平台的额外配置 

106 webview.start(gui="cef", debug=False) 

107 

108 

109# ── 命令行入口 ── 

110 

111 

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="显示当前配置") 

133 

134 args = parser.parse_args() 

135 cfg = load_config() 

136 

137 if args.config: 

138 print(json.dumps(cfg, indent=2, ensure_ascii=False)) 

139 return 

140 

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 

148 

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 ) 

169 

170 

171if __name__ == "__main__": 

172 main()