1"""

2 Desktop 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

22 from __future__ import annotations

23

24 import argparse

25 import sys

26 import os

27 import json

28 import threading

29 import time

30 import webbrowser

31

32

33# ── 配置 ───────────────────────────────────────────────────────

34

35 APP_NAME = "AgentOS Desktop"

36 APP_VERSION = "1.7.1"

37 DEFAULT_URL = "http://127.0.0.1:19999"

38 CONFIG_DIR = os.path.join(os.path.expanduser("~"), ".agentos")

39 CONFIG_FILE = os.path.join(CONFIG_DIR, "desktop.json")

40

41

42 def load_config() -> dict:

43 """加载本地配置。"""

44 defaults = {

45 "url": DEFAULT_URL,

46 "width": 1200,

47 "height": 800,

48 "fullscreen": False,

49 "always_on_top": False,

50 "auto_start": False,

51 "minimize_to_tray": True,

52 "title": f"{APP_NAME} v{APP_VERSION}",

53 }

54 if os.path.isfile(CONFIG_FILE):

55 try:

56 with open(CONFIG_FILE, "r") as f:

57 defaults.update(json.load(f))

58 except Exception:

59 pass

60 return defaults

61

62

63 def save_config(cfg: dict) -> None:

64 os.makedirs(CONFIG_DIR, exist_ok=True)

65 with open(CONFIG_FILE, "w") as f:

66 json.dump(cfg, f, indent=2)

67

68

69# ── 原生桌面壳 ─────────────────────────────────────────────────

70

71

72 def create_native_shell(url: str, width: int = 1200, height: int = 800,

73 title: str = APP_NAME, fullscreen: bool = False,

74 on_top: bool = False, minimize_to_tray: bool = True) -> None:

75 """使用 pywebview 创建原生桌面窗口。"""

76 try:

77 import webview

78 except ImportError:

79 print("需要安装 pywebview: pip install pywebview")

80 print("自动打开浏览器作为降级方案...")

81 webbrowser.open(url)

82 try:

83 while True:

84 time.sleep(1)

85 except KeyboardInterrupt:

86 pass

87 return

88

89 window = webview.create_window(

90 title=title,

91 url=url,

92 width=width,

93 height=height,

94 fullscreen=fullscreen,

95 on_top=on_top,

96 easy_drag=False,

97 confirm_close=minimize_to_tray,

98 text_select=True,

99 )

100

101 # 系统托盘暂时关闭（pywebview 托盘支持有限）

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

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

104

105

106# ── 命令行入口 ──

107

108

109 def main() -> None:

110 parser = argparse.ArgumentParser(

111 description="AgentOS Desktop Shell — 原生桌面壳",

112 formatter_class=argparse.RawDescriptionHelpFormatter,

113 epilog="""

114 示例:

115 agentos desktop-shell # 连接本地 127.0.0.1:19999

116 agentos desktop-shell --url http://remote:19999 # 连接远程服务

117 agentos desktop-shell --fullscreen # 全屏模式

118 agentos desktop-shell --on-top # 窗口置顶

119 agentos desktop-shell --browser # 直接用浏览器打开（降级）

120 """,

121 )

122 parser.add_argument("--url", default=None, help=f"服务端地址（默认: {DEFAULT_URL}）")

123 parser.add_argument("--width", type=int, default=1200, help="窗口宽度（默认: 1200）")

124 parser.add_argument("--height", type=int, default=800, help="窗口高度（默认: 800）")

125 parser.add_argument("--fullscreen", action="store_true", help="全屏启动")

126 parser.add_argument("--on-top", action="store_true", help="窗口置顶")

127 parser.add_argument("--no-tray", action="store_true", help="禁用最小化到托盘")

128 parser.add_argument("--browser", action="store_true", help="直接用系统浏览器打开")

129 parser.add_argument("--config", action="store_true", help="显示当前配置")

130

131 args = parser.parse_args()

132 cfg = load_config()

133

134 if args.config:

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

136 return

137

138 url = args.url or cfg.get("url", DEFAULT_URL)

139 width = args.width

140 height = args.height

141 title = cfg.get("title", APP_NAME)

142 fullscreen = args.fullscreen

143 on_top = args.on_top

144 tray = not args.no_tray

145

146 if args.browser:

147 print(f"用浏览器打开 {url} ...")

148 webbrowser.open(url)

149 try:

150 while True:

151 time.sleep(1)

152 except KeyboardInterrupt:

153 pass

154 else:

155 print(f"启动 AgentOS Desktop Shell v{APP_VERSION}")

156 print(f"连接: {url}")

157 create_native_shell(

158 url=url,

159 width=width,

160 height=height,

161 title=title,

162 fullscreen=fullscreen,

163 on_top=on_top,

164 minimize_to_tray=tray,

165 )

166

167

168 if __name__ == "__main__":

169 main()