myagent-ai 1.15.55 → 1.15.57
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/main.py +5 -36
- package/package.json +1 -1
- package/start.js +226 -497
- package/web/ui/index.html +1 -1
package/main.py
CHANGED
|
@@ -39,7 +39,7 @@ from core.config_broadcast import ConfigBroadcaster, ReloadType
|
|
|
39
39
|
from core.update_manager import UpdateManager, UpdateType
|
|
40
40
|
from core.version import get_version
|
|
41
41
|
from core.permissions import PermissionManager
|
|
42
|
-
from core.deps_checker import check_and_install_deps
|
|
42
|
+
from core.deps_checker import check_and_install_deps # 保留导入供其他模块使用
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
def _get_screen_resolution() -> tuple[int, int]:
|
|
@@ -154,21 +154,8 @@ class MyAgentApp:
|
|
|
154
154
|
self.logger.info(f"数据目录: {self.config_mgr.data_dir}")
|
|
155
155
|
self.logger.info("=" * 60)
|
|
156
156
|
|
|
157
|
-
# 1.5
|
|
158
|
-
#
|
|
159
|
-
if os.environ.get("MYAGENT_SKIP_DEPS") == "1":
|
|
160
|
-
self.logger.info("跳过依赖检查(--skip-deps)")
|
|
161
|
-
else:
|
|
162
|
-
self.logger.info("检查依赖...")
|
|
163
|
-
deps_result = check_and_install_deps(auto_fix=True, silent=False)
|
|
164
|
-
if deps_result["installed"] > 0:
|
|
165
|
-
self.logger.info(
|
|
166
|
-
f"自动安装了 {deps_result['installed']} 个依赖"
|
|
167
|
-
)
|
|
168
|
-
if deps_result["failed"] > 0:
|
|
169
|
-
self.logger.warning(
|
|
170
|
-
f"{deps_result['failed']} 个依赖安装失败,相关功能可能不可用"
|
|
171
|
-
)
|
|
157
|
+
# 1.5 依赖检查已移至 start.js (install/reinstall 命令)
|
|
158
|
+
# 启动时不再检查依赖,确保启动速度干净利落
|
|
172
159
|
|
|
173
160
|
# 2. LLM 客户端
|
|
174
161
|
llm_cfg = self.config.llm
|
|
@@ -692,26 +679,8 @@ def create_tray_icon(app: MyAgentApp, web_port: int = 8767):
|
|
|
692
679
|
from PIL import Image, ImageDraw, ImageFont
|
|
693
680
|
except Exception:
|
|
694
681
|
# pystray 可能因缺少 GUI 环境 (X11/Wayland) 或未安装而失败
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
return None
|
|
698
|
-
try:
|
|
699
|
-
from core.deps_checker import _pip_install
|
|
700
|
-
logger.info("pystray/PIL 未安装,正在自动安装...")
|
|
701
|
-
success, msg = _pip_install(["pystray>=0.19.5", "Pillow>=10.0.0"])
|
|
702
|
-
if success:
|
|
703
|
-
logger.info(f"pystray/PIL 自动安装成功: {msg}")
|
|
704
|
-
import importlib
|
|
705
|
-
import pystray as _pystray
|
|
706
|
-
importlib.reload(_pystray)
|
|
707
|
-
import pystray
|
|
708
|
-
from PIL import Image, ImageDraw, ImageFont
|
|
709
|
-
else:
|
|
710
|
-
logger.warning(f"pystray/PIL 自动安装失败: {msg}")
|
|
711
|
-
return None
|
|
712
|
-
except Exception as e:
|
|
713
|
-
logger.warning(f"pystray 初始化失败 (可能缺少 GUI 环境): {e}")
|
|
714
|
-
return None
|
|
682
|
+
logger.info("pystray 不可用(未安装或缺少 GUI 环境),跳过系统托盘")
|
|
683
|
+
return None
|
|
715
684
|
|
|
716
685
|
# ── 图标颜色主题 ──
|
|
717
686
|
_COLOR_NORMAL = "#4A90D9" # 蓝色 = 运行中
|
package/package.json
CHANGED
package/start.js
CHANGED
|
@@ -2,22 +2,10 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* start.js - 跨平台入口脚本 (Windows/macOS/Linux)
|
|
4
4
|
* ================================================
|
|
5
|
-
*
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* - pip 升级/重装: myagent-ai reinstall
|
|
10
|
-
*
|
|
11
|
-
* 用法:
|
|
12
|
-
* myagent-ai # 交互式选择运行模式
|
|
13
|
-
* myagent-ai web # Web 管理后台
|
|
14
|
-
* myagent-ai cli # CLI 模式
|
|
15
|
-
* myagent-ai tray # 系统托盘模式
|
|
16
|
-
* myagent-ai server # API 服务模式
|
|
17
|
-
* myagent-ai setup # 配置向导
|
|
18
|
-
* myagent-ai reinstall # 重新安装依赖到 venv
|
|
19
|
-
* myagent-ai uninstall # 卸载 MyAgent(删除npm包+数据)
|
|
20
|
-
* myagent-ai --skip-deps # 跳过依赖检查,直接启动
|
|
5
|
+
* 职责分明:
|
|
6
|
+
* - install/reinstall: 创建 venv + 安装所有依赖
|
|
7
|
+
* - web/cli/tray/server: 直接启动,不检查依赖
|
|
8
|
+
* - uninstall: 卸载
|
|
21
9
|
*/
|
|
22
10
|
"use strict";
|
|
23
11
|
|
|
@@ -26,35 +14,19 @@ const path = require("path");
|
|
|
26
14
|
const fs = require("fs");
|
|
27
15
|
const os = require("os");
|
|
28
16
|
|
|
29
|
-
// ── 常量 ──────────────────────────────────────────────────
|
|
30
17
|
const IS_WIN = process.platform === "win32";
|
|
18
|
+
const PKG_NAME = "myagent-ai";
|
|
31
19
|
|
|
32
|
-
//
|
|
33
|
-
function getVenvDir() {
|
|
34
|
-
const homeDir = os.homedir();
|
|
35
|
-
return path.join(homeDir, ".myagent", "venv");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// venv 中 Python 可执行文件的路径
|
|
39
|
-
function getVenvPython(venvDir) {
|
|
40
|
-
return IS_WIN
|
|
41
|
-
? path.join(venvDir, "Scripts", "python.exe")
|
|
42
|
-
: path.join(venvDir, "bin", "python");
|
|
43
|
-
}
|
|
20
|
+
// ── 路径工具 ──────────────────────────────────────────────
|
|
44
21
|
|
|
45
|
-
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
22
|
+
function getHome() { return os.homedir(); }
|
|
23
|
+
function getDataDir() { return path.join(getHome(), ".myagent"); }
|
|
24
|
+
function getVenvDir() { return path.join(getDataDir(), "venv"); }
|
|
25
|
+
function getSentinelFile() { return path.join(getDataDir(), ".deps_installed"); }
|
|
26
|
+
function getVenvPython(d) {
|
|
27
|
+
return IS_WIN ? path.join(d, "Scripts", "python.exe") : path.join(d, "bin", "python");
|
|
50
28
|
}
|
|
51
29
|
|
|
52
|
-
// MyAgent 数据目录
|
|
53
|
-
function getDataDir() {
|
|
54
|
-
return path.join(os.homedir(), ".myagent");
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// ── 解析包目录 ────────────────────────────────────────────
|
|
58
30
|
function resolvePackageDir() {
|
|
59
31
|
let dir = __dirname;
|
|
60
32
|
if (!fs.existsSync(path.join(dir, "main.py"))) {
|
|
@@ -62,8 +34,8 @@ function resolvePackageDir() {
|
|
|
62
34
|
const npmRoot = execSync("npm root -g", {
|
|
63
35
|
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"],
|
|
64
36
|
}).trim();
|
|
65
|
-
const
|
|
66
|
-
if (fs.existsSync(path.join(
|
|
37
|
+
const c = path.join(npmRoot, PKG_NAME);
|
|
38
|
+
if (fs.existsSync(path.join(c, "main.py"))) dir = c;
|
|
67
39
|
} catch (_) {}
|
|
68
40
|
}
|
|
69
41
|
if (!fs.existsSync(path.join(dir, "main.py"))) {
|
|
@@ -76,599 +48,356 @@ function resolvePackageDir() {
|
|
|
76
48
|
return dir;
|
|
77
49
|
}
|
|
78
50
|
|
|
79
|
-
// ── 查找系统 Python(用于创建 venv) ─────────────────────
|
|
80
51
|
function findSystemPython() {
|
|
81
52
|
const candidates = IS_WIN
|
|
82
53
|
? ["python", "python3", "python3.14", "python3.13"]
|
|
83
54
|
: ["python3", "python3.14", "python3.13", "python"];
|
|
84
|
-
|
|
85
55
|
for (const cmd of candidates) {
|
|
86
56
|
try {
|
|
87
|
-
const
|
|
88
|
-
IS_WIN ? `${cmd}.exe` : cmd,
|
|
89
|
-
["--version"],
|
|
57
|
+
const r = execFileSync(
|
|
58
|
+
IS_WIN ? `${cmd}.exe` : cmd, ["--version"],
|
|
90
59
|
{ encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000 }
|
|
91
60
|
);
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
94
|
-
const major = parseInt(match[1], 10);
|
|
95
|
-
const minor = parseInt(match[2], 10);
|
|
96
|
-
if (major >= 3 && minor >= 13) return cmd;
|
|
97
|
-
}
|
|
61
|
+
const m = r.match(/Python (\d+)\.(\d+)/);
|
|
62
|
+
if (m && parseInt(m[1]) >= 3 && parseInt(m[2]) >= 13) return cmd;
|
|
98
63
|
} catch (_) {}
|
|
99
64
|
}
|
|
100
|
-
|
|
101
|
-
// Windows: 搜索常见安装路径
|
|
102
65
|
if (IS_WIN) {
|
|
103
66
|
const localAppData = process.env.LOCALAPPDATA || "";
|
|
104
67
|
const programFiles = process.env.ProgramFiles || "";
|
|
105
|
-
const
|
|
106
|
-
const searchPaths = [
|
|
68
|
+
for (const p of [
|
|
107
69
|
path.join(localAppData, "Programs", "Python", "Python314", "python.exe"),
|
|
108
70
|
path.join(localAppData, "Programs", "Python", "Python313", "python.exe"),
|
|
109
71
|
path.join(programFiles, "Python314", "python.exe"),
|
|
110
72
|
path.join(programFiles, "Python313", "python.exe"),
|
|
111
73
|
"C:\\Python314\\python.exe", "C:\\Python313\\python.exe",
|
|
112
|
-
]
|
|
113
|
-
|
|
114
|
-
if (fs.existsSync(pyPath)) return pyPath;
|
|
74
|
+
]) {
|
|
75
|
+
if (fs.existsSync(p)) return p;
|
|
115
76
|
}
|
|
116
77
|
}
|
|
117
|
-
|
|
118
78
|
return null;
|
|
119
79
|
}
|
|
120
80
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
* Returns: true if successfully installed
|
|
126
|
-
*/
|
|
127
|
-
function tryAutoInstallVenvModule(systemPython) {
|
|
128
|
-
try {
|
|
129
|
-
// 先检测是否是 apt 系统
|
|
130
|
-
execFileSync("which", ["apt-get"], {
|
|
131
|
-
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000,
|
|
132
|
-
});
|
|
133
|
-
} catch (_) {
|
|
134
|
-
// 不是 apt 系统
|
|
135
|
-
return false;
|
|
81
|
+
function getPipMirrorArgs() {
|
|
82
|
+
const envMirror = process.env.MYAGENT_PIP_MIRROR || "";
|
|
83
|
+
if (envMirror === "1" || envMirror === "true" || envMirror === "cn") {
|
|
84
|
+
return ["-i", "https://pypi.tuna.tsinghua.edu.cn/simple", "--trusted-host", "pypi.tuna.tsinghua.edu.cn"];
|
|
136
85
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const ver = execFileSync(systemPython, ["-c", "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"], {
|
|
141
|
-
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000,
|
|
142
|
-
}).trim();
|
|
143
|
-
|
|
144
|
-
console.log(` \x1b[90m[i]\x1b[0m 正在安装 python${ver}-venv (需要 sudo 权限)...`);
|
|
145
|
-
|
|
146
|
-
execFileSync("sudo", ["apt-get", "install", "-y", `python${ver}-venv`], {
|
|
147
|
-
encoding: "utf8", stdio: "inherit", timeout: 120000,
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
console.log(` \x1b[32m[✓]\x1b[0m python${ver}-venv 已安装`);
|
|
151
|
-
return true;
|
|
152
|
-
} catch (err) {
|
|
153
|
-
console.log(` \x1b[33m[!]\x1b[0m 自动安装 venv 模块失败: ${err.message}`);
|
|
154
|
-
console.log(` \x1b[90m[i]\x1b[0m 请手动运行: sudo apt install python3-venv`);
|
|
155
|
-
return false;
|
|
86
|
+
const lang = (process.env.LANG || process.env.LC_ALL || "").toLowerCase();
|
|
87
|
+
if (lang.includes("zh_cn") || lang.includes("chinese")) {
|
|
88
|
+
return ["-i", "https://pypi.tuna.tsinghua.edu.cn/simple", "--trusted-host", "pypi.tuna.tsinghua.edu.cn"];
|
|
156
89
|
}
|
|
90
|
+
return [];
|
|
157
91
|
}
|
|
158
92
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
* Returns: { venvDir, venvPython, venvPip }
|
|
162
|
-
*/
|
|
93
|
+
// ── 虚拟环境 ──────────────────────────────────────────────
|
|
94
|
+
|
|
163
95
|
function ensureVenv() {
|
|
164
96
|
const venvDir = getVenvDir();
|
|
165
97
|
const venvPython = getVenvPython(venvDir);
|
|
166
|
-
const venvPip = getVenvPip(venvDir);
|
|
167
|
-
|
|
168
|
-
// 检查现有 venv 是否完整
|
|
169
98
|
if (fs.existsSync(venvPython)) {
|
|
170
99
|
try {
|
|
171
100
|
execFileSync(venvPython, ["--version"], {
|
|
172
101
|
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000,
|
|
173
102
|
});
|
|
174
|
-
return
|
|
103
|
+
return venvPython;
|
|
175
104
|
} catch (_) {
|
|
176
|
-
// venv 损坏,需要重建
|
|
177
|
-
console.log(" \x1b[33m[!]\x1b[0m 虚拟环境已损坏,正在重建...");
|
|
178
105
|
fs.rmSync(venvDir, { recursive: true, force: true });
|
|
179
106
|
}
|
|
180
107
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
console.error("\x1b[31m[✗]\x1b[0m 未找到 Python 3.13+");
|
|
186
|
-
console.error(" 请先安装 Python: https://www.python.org/downloads/");
|
|
187
|
-
console.error(" 安装时请勾选 'Add Python to PATH'");
|
|
188
|
-
console.error("");
|
|
189
|
-
console.error(" 或一键安装:");
|
|
190
|
-
if (IS_WIN) {
|
|
191
|
-
console.error(" powershell -c \"irm https://raw.githubusercontent.com/ctz168/myagent/main/install/install.ps1 | iex\"");
|
|
192
|
-
} else {
|
|
193
|
-
console.error(" curl -fsSL https://raw.githubusercontent.com/ctz168/myagent/main/install/install.sh | bash");
|
|
194
|
-
}
|
|
108
|
+
const sysPy = findSystemPython();
|
|
109
|
+
if (!sysPy) {
|
|
110
|
+
console.error("\x1b[31m未找到 Python 3.13+\x1b[0m");
|
|
111
|
+
console.error("请先安装 Python: https://www.python.org/downloads/");
|
|
195
112
|
process.exit(1);
|
|
196
113
|
}
|
|
197
|
-
|
|
198
|
-
// 确保 ~/.myagent 目录存在
|
|
199
114
|
const dataDir = getDataDir();
|
|
200
|
-
if (!fs.existsSync(dataDir)) {
|
|
201
|
-
fs.mkdirSync(dataDir, { recursive: true });
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
console.log(" \x1b[36m[*]\x1b[0m 创建独立虚拟环境...");
|
|
205
|
-
console.log(` \x1b[90m[i]\x1b[0m 位置: ${venvDir}`);
|
|
206
|
-
console.log(` \x1b[90m[i]\x1b[0m 系统Python: ${systemPython}`);
|
|
115
|
+
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
|
|
207
116
|
|
|
117
|
+
console.log(" 创建虚拟环境...");
|
|
208
118
|
try {
|
|
209
|
-
execFileSync(
|
|
119
|
+
execFileSync(sysPy, ["-m", "venv", venvDir], {
|
|
210
120
|
encoding: "utf8", stdio: "inherit", timeout: 60000,
|
|
211
121
|
});
|
|
212
|
-
} catch (
|
|
213
|
-
// Debian/Ubuntu
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const autoInstalled = !IS_WIN && tryAutoInstallVenvModule(systemPython);
|
|
217
|
-
|
|
218
|
-
if (autoInstalled) {
|
|
219
|
-
// 重试创建 venv
|
|
122
|
+
} catch (_) {
|
|
123
|
+
// Debian/Ubuntu: 尝试安装 venv 模块
|
|
124
|
+
if (!IS_WIN) {
|
|
220
125
|
try {
|
|
221
|
-
execFileSync(
|
|
126
|
+
execFileSync("sudo", ["apt-get", "install", "-y", "python3-venv"], {
|
|
127
|
+
encoding: "utf8", stdio: "inherit", timeout: 120000,
|
|
128
|
+
});
|
|
129
|
+
execFileSync(sysPy, ["-m", "venv", venvDir], {
|
|
222
130
|
encoding: "utf8", stdio: "inherit", timeout: 60000,
|
|
223
131
|
});
|
|
224
|
-
} catch (
|
|
225
|
-
console.error(
|
|
132
|
+
} catch (_) {
|
|
133
|
+
console.error("\x1b[31m创建虚拟环境失败\x1b[0m");
|
|
134
|
+
console.error("请手动运行: sudo apt install python3-venv");
|
|
226
135
|
process.exit(1);
|
|
227
136
|
}
|
|
228
137
|
} else {
|
|
229
|
-
console.error(
|
|
230
|
-
if (!IS_WIN) {
|
|
231
|
-
console.error("");
|
|
232
|
-
console.error(" 在 Debian/Ubuntu 上,python3.xx-venv 需要单独安装:");
|
|
233
|
-
console.error(" sudo apt install python3-venv");
|
|
234
|
-
console.error(" 然后运行: myagent-ai reinstall");
|
|
235
|
-
console.error("");
|
|
236
|
-
}
|
|
138
|
+
console.error("\x1b[31m创建虚拟环境失败\x1b[0m");
|
|
237
139
|
process.exit(1);
|
|
238
140
|
}
|
|
239
141
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (!fs.existsSync(venvPython)) {
|
|
243
|
-
console.error("\x1b[31m[✗]\x1b[0m 虚拟环境创建后未找到 Python,请检查系统 Python 安装");
|
|
142
|
+
if (!fs.existsSync(getVenvPython(venvDir))) {
|
|
143
|
+
console.error("\x1b[31m虚拟环境创建失败\x1b[0m");
|
|
244
144
|
process.exit(1);
|
|
245
145
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return { venvDir, venvPython, venvPip, isNew: true };
|
|
146
|
+
console.log(" \x1b[32m✓\x1b[0m 虚拟环境已创建");
|
|
147
|
+
return getVenvPython(venvDir);
|
|
249
148
|
}
|
|
250
149
|
|
|
251
|
-
// ──
|
|
150
|
+
// ── 依赖安装(仅在 install/reinstall/首次运行时调用) ───
|
|
252
151
|
|
|
253
|
-
|
|
254
|
-
function parseRequirements(reqFile) {
|
|
255
|
-
const modules = [];
|
|
256
|
-
try {
|
|
257
|
-
const lines = fs.readFileSync(reqFile, "utf8").split("\n");
|
|
258
|
-
for (const raw of lines) {
|
|
259
|
-
const line = raw.trim();
|
|
260
|
-
if (!line || line.startsWith("#")) continue;
|
|
261
|
-
// 解析: openai>=1.12.0 → openai, python-telegram-bot>=21.0 → telegram
|
|
262
|
-
const match = line.match(/^([a-zA-Z0-9_-]+)/);
|
|
263
|
-
if (match) {
|
|
264
|
-
const pkg = match[1].toLowerCase().replace(/-/g, "_");
|
|
265
|
-
// 特殊映射: pip 包名和 import 名不一致的
|
|
266
|
-
const importMap = {
|
|
267
|
-
"beautifulsoup4": "bs4",
|
|
268
|
-
"py_getwindow": "pygetwindow",
|
|
269
|
-
"python_telegram_bot": "telegram",
|
|
270
|
-
"pillow": "PIL",
|
|
271
|
-
"psutil": "psutil",
|
|
272
|
-
"edge_tts": "edge_tts",
|
|
273
|
-
};
|
|
274
|
-
modules.push(importMap[pkg] || pkg);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
} catch (_) {
|
|
278
|
-
// 解析失败,回退到核心包列表
|
|
279
|
-
modules.push("openai", "aiohttp", "requests");
|
|
280
|
-
}
|
|
281
|
-
return modules;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// 核心依赖包(fallback 用,不依赖文件解析)
|
|
285
|
-
// 包含 tray (pystray/Pillow) 等关键依赖,确保回退安装时也能装上
|
|
286
|
-
const CORE_PACKAGES = [
|
|
287
|
-
"openai>=1.12.0",
|
|
288
|
-
"aiohttp>=3.9.0",
|
|
289
|
-
"requests>=2.31.0",
|
|
290
|
-
"duckduckgo-search>=6.0.0",
|
|
291
|
-
"beautifulsoup4>=4.12.0",
|
|
292
|
-
"lxml>=5.0.0",
|
|
293
|
-
"psutil>=5.9.0",
|
|
294
|
-
"pystray>=0.19.5",
|
|
295
|
-
"Pillow>=10.0.0",
|
|
296
|
-
"edge-tts>=6.1.0",
|
|
297
|
-
"faster-whisper>=1.0.0",
|
|
298
|
-
"anthropic>=0.18.0",
|
|
299
|
-
];
|
|
300
|
-
|
|
301
|
-
// 获取 pip 镜像源参数(国内用户自动使用清华镜像)
|
|
302
|
-
function getPipMirrorArgs() {
|
|
303
|
-
const envMirror = process.env.MYAGENT_PIP_MIRROR || "";
|
|
304
|
-
if (envMirror === "1" || envMirror === "true" || envMirror === "cn") {
|
|
305
|
-
return ["-i", "https://pypi.tuna.tsinghua.edu.cn/simple",
|
|
306
|
-
"--trusted-host", "pypi.tuna.tsinghua.edu.cn"];
|
|
307
|
-
}
|
|
308
|
-
// 通过语言环境判断是否在国内(Windows 通过环境变量,其他通过 LANG)
|
|
309
|
-
const lang = (process.env.LANG || process.env.LC_ALL || "").toLowerCase();
|
|
310
|
-
if (lang.includes("zh_cn") || lang.includes("chinese")) {
|
|
311
|
-
return ["-i", "https://pypi.tuna.tsinghua.edu.cn/simple",
|
|
312
|
-
"--trusted-host", "pypi.tuna.tsinghua.edu.cn"];
|
|
313
|
-
}
|
|
314
|
-
return [];
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function installDeps(venvPython, venvPip, pkgDir) {
|
|
152
|
+
function installAllDeps(venvPython, pkgDir) {
|
|
318
153
|
const reqFile = path.join(pkgDir, "requirements.txt");
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
// 快速检查关键模块是否都存在
|
|
322
|
-
let missing = [];
|
|
323
|
-
console.log(" \x1b[36m[*]\x1b[0m 检查依赖...");
|
|
324
|
-
|
|
325
|
-
// 检查所有模块(不超过 30 个),托盘和 GUI 依赖也必须检测
|
|
326
|
-
const checkLimit = Math.min(allModules.length, 30);
|
|
327
|
-
for (let i = 0; i < checkLimit; i++) {
|
|
328
|
-
const mod = allModules[i];
|
|
329
|
-
try {
|
|
330
|
-
execFileSync(venvPython, ["-c", `import ${mod}`], {
|
|
331
|
-
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000,
|
|
332
|
-
});
|
|
333
|
-
} catch (_) {
|
|
334
|
-
missing.push(mod);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
if (missing.length === 0) {
|
|
339
|
-
console.log(` \x1b[32m[✓]\x1b[0m 全部 ${checkLimit} 个依赖检查通过`);
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
console.log(` \x1b[33m[!]\x1b[0m 缺失 ${missing.length} 个包: ${missing.join(", ")}`);
|
|
154
|
+
const mirrorArgs = getPipMirrorArgs();
|
|
155
|
+
if (mirrorArgs.length) console.log(" 使用国内镜像源 (清华)");
|
|
344
156
|
|
|
345
|
-
//
|
|
157
|
+
// 升级 pip
|
|
346
158
|
try {
|
|
347
|
-
|
|
348
|
-
execFileSync(venvPython, ["-m", "pip", "install", "--upgrade", "pip", "--quiet"], {
|
|
159
|
+
execFileSync(venvPython, ["-m", "pip", "install", "--upgrade", "pip", "-q"], {
|
|
349
160
|
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 120000,
|
|
350
161
|
});
|
|
351
162
|
} catch (_) {}
|
|
352
163
|
|
|
353
|
-
//
|
|
354
|
-
const mirrorArgs = getPipMirrorArgs();
|
|
355
|
-
|
|
356
|
-
// Linux: 预装 evdev 预编译包,避免 pynput 安装时编译失败
|
|
164
|
+
// Linux: 预装 evdev
|
|
357
165
|
if (!IS_WIN) {
|
|
358
166
|
try {
|
|
359
|
-
execFileSync(venvPython, ["-m", "pip", "install", "evdev-binary", "
|
|
167
|
+
execFileSync(venvPython, ["-m", "pip", "install", "evdev-binary", "-q", "--disable-pip-version-check"], {
|
|
360
168
|
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 60000,
|
|
361
169
|
});
|
|
362
170
|
} catch (_) {}
|
|
363
171
|
}
|
|
364
172
|
|
|
365
|
-
//
|
|
366
|
-
let installed = false;
|
|
173
|
+
// 安装全部依赖
|
|
367
174
|
if (fs.existsSync(reqFile)) {
|
|
368
|
-
console.log(
|
|
369
|
-
if (mirrorArgs.length > 0) {
|
|
370
|
-
console.log(" \x1b[90m[i]\x1b[0m 使用国内镜像源 (清华)");
|
|
371
|
-
}
|
|
175
|
+
console.log(" 安装依赖 (可能需要几分钟)...");
|
|
372
176
|
try {
|
|
373
177
|
execFileSync(venvPython, ["-m", "pip", "install", "-r", reqFile, "--disable-pip-version-check", ...mirrorArgs], {
|
|
374
|
-
encoding: "utf8", stdio: "inherit", timeout:
|
|
178
|
+
encoding: "utf8", stdio: "inherit", timeout: 600000,
|
|
375
179
|
});
|
|
376
|
-
console.log(" \x1b[32m
|
|
377
|
-
|
|
180
|
+
console.log(" \x1b[32m✓\x1b[0m 全部依赖安装完成");
|
|
181
|
+
return true;
|
|
378
182
|
} catch (err) {
|
|
379
|
-
console.log(
|
|
380
|
-
console.log(` \x1b[90m[i]\x1b[0m 错误: ${err.message}`);
|
|
183
|
+
console.log(` \x1b[33m安装失败: ${err.message.split("\n").pop().trim()}\x1b[0m`);
|
|
381
184
|
}
|
|
382
185
|
}
|
|
383
186
|
|
|
384
|
-
//
|
|
385
|
-
|
|
187
|
+
// 回退: 核心依赖
|
|
188
|
+
const CORE = [
|
|
189
|
+
"openai>=1.12.0", "aiohttp>=3.9.0", "requests>=2.31.0",
|
|
190
|
+
"duckduckgo-search>=6.0.0", "beautifulsoup4>=4.12.0", "lxml>=5.0.0",
|
|
191
|
+
"psutil>=5.9.0", "Pillow>=10.0.0", "edge-tts>=6.1.0",
|
|
192
|
+
"anthropic>=0.18.0",
|
|
193
|
+
];
|
|
194
|
+
for (const mirrors of [mirrorArgs, ["-i", "https://mirrors.aliyun.com/pypi/simple/", "--trusted-host", "mirrors.aliyun.com"]]) {
|
|
386
195
|
try {
|
|
387
|
-
|
|
388
|
-
|
|
196
|
+
console.log(" 尝试安装核心依赖...");
|
|
197
|
+
execFileSync(venvPython, ["-m", "pip", "install", "-q", "--disable-pip-version-check", ...mirrors, ...CORE], {
|
|
198
|
+
encoding: "utf8", stdio: "inherit", timeout: 300000,
|
|
389
199
|
});
|
|
390
|
-
console.log(" \x1b[32m
|
|
391
|
-
|
|
392
|
-
} catch (_) {
|
|
393
|
-
// 策略4: 尝试阿里云镜像
|
|
394
|
-
console.log(" \x1b[33m[!]\x1b[0m 核心依赖安装失败,尝试阿里云镜像...");
|
|
395
|
-
try {
|
|
396
|
-
const aliyunArgs = ["-i", "https://mirrors.aliyun.com/pypi/simple/", "--trusted-host", "mirrors.aliyun.com"];
|
|
397
|
-
execFileSync(venvPython, ["-m", "pip", "install", "--quiet", "--disable-pip-version-check", ...aliyunArgs, ...CORE_PACKAGES], {
|
|
398
|
-
encoding: "utf8", stdio: "inherit", timeout: 180000,
|
|
399
|
-
});
|
|
400
|
-
console.log(" \x1b[32m[✓]\x1b[0m 核心依赖安装完成(阿里云镜像)");
|
|
401
|
-
} catch (_) {
|
|
402
|
-
console.error(" \x1b[31m[✗]\x1b[0m 依赖安装失败,请手动运行:");
|
|
403
|
-
console.error(` ${venvPython} -m pip install -r "${reqFile}"`);
|
|
404
|
-
console.error("");
|
|
405
|
-
console.error(" 或使用重新安装命令: myagent-ai reinstall");
|
|
406
|
-
console.error("");
|
|
407
|
-
}
|
|
408
|
-
}
|
|
200
|
+
console.log(" \x1b[32m✓\x1b[0m 核心依赖安装完成");
|
|
201
|
+
return true;
|
|
202
|
+
} catch (_) {}
|
|
409
203
|
}
|
|
204
|
+
return false;
|
|
410
205
|
}
|
|
411
206
|
|
|
412
|
-
//
|
|
413
|
-
function
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
case "server": return ["main.py", "--server"];
|
|
420
|
-
case "setup": return ["main.py", "--setup"];
|
|
421
|
-
case "reinstall":
|
|
422
|
-
return []; // 特殊处理
|
|
423
|
-
case "uninstall":
|
|
424
|
-
return []; // 特殊处理
|
|
425
|
-
default:
|
|
426
|
-
return ["main.py", ...userArgs];
|
|
427
|
-
}
|
|
207
|
+
// 标记依赖已安装
|
|
208
|
+
function markDepsInstalled(version) {
|
|
209
|
+
try {
|
|
210
|
+
const dir = getDataDir();
|
|
211
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
212
|
+
fs.writeFileSync(getSentinelFile(), version);
|
|
213
|
+
} catch (_) {}
|
|
428
214
|
}
|
|
429
215
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
console.error("\x1b[31m[✗]\x1b[0m 错误: 找不到 main.py");
|
|
437
|
-
console.error(` 搜索路径: ${pkgDir}`);
|
|
438
|
-
console.error(' 请重新安装: npm install -g myagent-ai');
|
|
439
|
-
process.exit(1);
|
|
216
|
+
function isDepsInstalled(pkgVersion) {
|
|
217
|
+
try {
|
|
218
|
+
if (!fs.existsSync(getSentinelFile())) return false;
|
|
219
|
+
return fs.readFileSync(getSentinelFile(), "utf8").trim() === pkgVersion;
|
|
220
|
+
} catch (_) {
|
|
221
|
+
return false;
|
|
440
222
|
}
|
|
223
|
+
}
|
|
441
224
|
|
|
442
|
-
|
|
443
|
-
if (userArgs[0] === "uninstall") {
|
|
444
|
-
const venvDir = getVenvDir();
|
|
445
|
-
const purgeArg = userArgs.includes("--purge") || userArgs.includes("-p");
|
|
446
|
-
const dataDir = getDataDir();
|
|
447
|
-
|
|
448
|
-
// Step 1: 停止服务
|
|
449
|
-
console.log(" \x1b[36m[*]\x1b[0m 停止 MyAgent 服务...");
|
|
450
|
-
try {
|
|
451
|
-
const http = require("http");
|
|
452
|
-
const req = http.request({ hostname: "127.0.0.1", port: 8767, path: "/api/shutdown", method: "POST", timeout: 5000 }, () => {
|
|
453
|
-
// 忽略响应
|
|
454
|
-
});
|
|
455
|
-
req.on("error", () => {});
|
|
456
|
-
req.end();
|
|
457
|
-
console.log(" \x1b[32m[✓]\x1b[0m 已发送关闭请求");
|
|
458
|
-
setTimeout(() => {}, 2000);
|
|
459
|
-
} catch (_) {
|
|
460
|
-
console.log(" \x1b[90m[i]\x1b[0m 服务未运行");
|
|
461
|
-
}
|
|
225
|
+
// ── 命令处理 ──────────────────────────────────────────────
|
|
462
226
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
227
|
+
function cmdInstall(pkgDir) {
|
|
228
|
+
console.log("");
|
|
229
|
+
console.log(" \x1b[36mMyAgent 安装依赖\x1b[0m");
|
|
230
|
+
console.log("");
|
|
231
|
+
const venvPython = ensureVenv();
|
|
232
|
+
installAllDeps(venvPython, pkgDir);
|
|
233
|
+
let ver = "";
|
|
234
|
+
try { ver = JSON.parse(fs.readFileSync(path.join(pkgDir, "package.json"), "utf8")).version || ""; } catch (_) {}
|
|
235
|
+
markDepsInstalled(ver);
|
|
236
|
+
console.log("");
|
|
237
|
+
console.log(" \x1b[32m✓ 安装完成,可以启动了: myagent-ai web\x1b[0m");
|
|
238
|
+
console.log("");
|
|
239
|
+
}
|
|
472
240
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
241
|
+
function cmdReinstall(pkgDir) {
|
|
242
|
+
console.log("");
|
|
243
|
+
console.log(" \x1b[36mMyAgent 重装依赖\x1b[0m");
|
|
244
|
+
console.log("");
|
|
245
|
+
const venvDir = getVenvDir();
|
|
246
|
+
if (fs.existsSync(venvDir)) {
|
|
247
|
+
console.log(" 删除旧虚拟环境...");
|
|
248
|
+
fs.rmSync(venvDir, { recursive: true, force: true });
|
|
249
|
+
}
|
|
250
|
+
const venvPython = ensureVenv();
|
|
251
|
+
installAllDeps(venvPython, pkgDir);
|
|
252
|
+
let ver = "";
|
|
253
|
+
try { ver = JSON.parse(fs.readFileSync(path.join(pkgDir, "package.json"), "utf8")).version || ""; } catch (_) {}
|
|
254
|
+
markDepsInstalled(ver);
|
|
255
|
+
console.log("");
|
|
256
|
+
console.log(" \x1b[32m✓ 重装完成\x1b[0m");
|
|
257
|
+
console.log("");
|
|
258
|
+
}
|
|
483
259
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
if (fs.existsSync(dataDir)) {
|
|
489
|
-
fs.rmSync(dataDir, { recursive: true, force: true });
|
|
490
|
-
console.log(" \x1b[32m[✓]\x1b[0m 数据目录已删除: " + dataDir);
|
|
491
|
-
} else {
|
|
492
|
-
console.log(" \x1b[90m[i]\x1b[0m 数据目录不存在");
|
|
493
|
-
}
|
|
494
|
-
} else {
|
|
495
|
-
console.log("");
|
|
496
|
-
console.log(" \x1b[90m[i]\x1b[0m 保留数据目录: " + dataDir);
|
|
497
|
-
console.log(" \x1b[90m[i]\x1b[0m 如需彻底清除: myagent-ai uninstall --purge");
|
|
498
|
-
}
|
|
260
|
+
function cmdUninstall() {
|
|
261
|
+
const venvDir = getVenvDir();
|
|
262
|
+
const dataDir = getDataDir();
|
|
263
|
+
const purgeArg = process.argv.includes("--purge") || process.argv.includes("-p");
|
|
499
264
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
console.log("");
|
|
504
|
-
return;
|
|
505
|
-
}
|
|
265
|
+
console.log("");
|
|
266
|
+
console.log(" \x1b[36mMyAgent 卸载\x1b[0m");
|
|
267
|
+
console.log("");
|
|
506
268
|
|
|
507
|
-
//
|
|
508
|
-
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
269
|
+
// 停止服务
|
|
270
|
+
try {
|
|
271
|
+
const http = require("http");
|
|
272
|
+
const req = http.request({ hostname: "127.0.0.1", port: 8767, path: "/api/shutdown", method: "POST", timeout: 5000 }, () => {});
|
|
273
|
+
req.on("error", () => {});
|
|
274
|
+
req.end();
|
|
275
|
+
} catch (_) {}
|
|
276
|
+
try {
|
|
277
|
+
if (IS_WIN) {
|
|
278
|
+
execSync('taskkill /F /IM python.exe /FI "WINDOWTITLE eq myagent*" 2>nul', { encoding: "utf8", timeout: 5000 });
|
|
279
|
+
} else {
|
|
280
|
+
execSync("pkill -f 'myagent|main.py' 2>/dev/null", { encoding: "utf8", timeout: 10000 });
|
|
513
281
|
}
|
|
514
|
-
|
|
515
|
-
installDeps(venvPython, venvPip, pkgDir);
|
|
516
|
-
console.log("");
|
|
517
|
-
console.log(" \x1b[32m[✓]\x1b[0m 依赖已全部重新安装到虚拟环境");
|
|
518
|
-
console.log(` \x1b[90m[i]\x1b[0m 虚拟环境: ${venvDir}`);
|
|
519
|
-
console.log("");
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
282
|
+
} catch (_) {}
|
|
522
283
|
|
|
523
|
-
// 打印 Banner(显示版本号)
|
|
524
|
-
let pkgVersion = "";
|
|
525
284
|
try {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
285
|
+
execFileSync("npm", ["uninstall", "-g", PKG_NAME], {
|
|
286
|
+
encoding: "utf8", stdio: "inherit", timeout: 60000,
|
|
287
|
+
});
|
|
529
288
|
} catch (_) {}
|
|
530
|
-
|
|
531
|
-
if (
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
console.log(" \x1b[1m\x1b[36mMyAgent\x1b[0m - 本地桌面端执行型 AI 助手");
|
|
289
|
+
|
|
290
|
+
if (purgeArg && fs.existsSync(dataDir)) {
|
|
291
|
+
fs.rmSync(dataDir, { recursive: true, force: true });
|
|
292
|
+
console.log(" 数据已清除");
|
|
535
293
|
}
|
|
536
294
|
console.log("");
|
|
295
|
+
console.log(" \x1b[32m✓ 卸载完成\x1b[0m");
|
|
296
|
+
console.log("");
|
|
297
|
+
}
|
|
537
298
|
|
|
538
|
-
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
// 确保 venv 存在
|
|
544
|
-
const { venvDir, venvPython, isNew } = ensureVenv();
|
|
545
|
-
|
|
546
|
-
// 检查并安装依赖(--skip-deps 时跳过)
|
|
547
|
-
if (skipDeps) {
|
|
548
|
-
console.log(" \x1b[90m[i]\x1b[0m 跳过依赖检查(--skip-deps)");
|
|
549
|
-
} else if (isNew) {
|
|
550
|
-
installDeps(venvPython, getVenvPip(venvDir), pkgDir);
|
|
551
|
-
} else {
|
|
552
|
-
// 即使 venv 已存在,也快速检查核心依赖
|
|
553
|
-
const venvPip = getVenvPip(venvDir);
|
|
554
|
-
installDeps(venvPython, venvPip, pkgDir);
|
|
299
|
+
function cmdRun(pkgDir, userArgs) {
|
|
300
|
+
const venvPython = getVenvPython(getVenvDir());
|
|
301
|
+
if (!fs.existsSync(venvPython)) {
|
|
302
|
+
console.error("\x1b[31m虚拟环境不存在,请先运行: myagent-ai install\x1b[0m");
|
|
303
|
+
process.exit(1);
|
|
555
304
|
}
|
|
556
305
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
306
|
+
let ver = "";
|
|
307
|
+
try { ver = JSON.parse(fs.readFileSync(path.join(pkgDir, "package.json"), "utf8")).version || ""; } catch (_) {}
|
|
308
|
+
|
|
309
|
+
// 首次运行自动安装
|
|
310
|
+
if (!isDepsInstalled(ver)) {
|
|
311
|
+
console.log("");
|
|
312
|
+
console.log(" \x1b[36m首次运行,正在安装依赖...\x1b[0m");
|
|
561
313
|
console.log("");
|
|
562
|
-
|
|
314
|
+
installAllDeps(venvPython, pkgDir);
|
|
315
|
+
markDepsInstalled(ver);
|
|
563
316
|
console.log("");
|
|
564
317
|
}
|
|
565
318
|
|
|
566
|
-
//
|
|
567
|
-
console.log(` \x1b[90m[i]\x1b[0m 虚拟环境: ${venvDir}`);
|
|
568
|
-
console.log(` \x1b[90m[i]\x1b[0m Python: ${venvPython}`);
|
|
319
|
+
// 显示版本
|
|
569
320
|
console.log("");
|
|
321
|
+
if (ver) {
|
|
322
|
+
console.log(` MyAgent v${ver}`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 首次运行检测
|
|
326
|
+
const configFile = path.join(getDataDir(), "config.json");
|
|
327
|
+
const isFirstRun = !fs.existsSync(configFile);
|
|
570
328
|
|
|
571
|
-
//
|
|
572
|
-
let mode =
|
|
573
|
-
let pyArgs
|
|
329
|
+
// 构建参数
|
|
330
|
+
let mode = userArgs[0] || "";
|
|
331
|
+
let pyArgs;
|
|
332
|
+
switch (mode) {
|
|
333
|
+
case "cli": pyArgs = ["main.py"]; break;
|
|
334
|
+
case "web": pyArgs = ["main.py", "--web"]; break;
|
|
335
|
+
case "tray": pyArgs = ["main.py", "--tray"]; break;
|
|
336
|
+
case "server": pyArgs = ["main.py", "--server"]; break;
|
|
337
|
+
case "setup": pyArgs = ["main.py", "--setup"]; break;
|
|
338
|
+
default: pyArgs = ["main.py", ...userArgs]; mode = "";
|
|
339
|
+
}
|
|
574
340
|
|
|
575
|
-
// 首次运行且未指定模式 → 自动选择 web 模式
|
|
576
341
|
if (isFirstRun && !mode) {
|
|
577
342
|
mode = "web";
|
|
578
343
|
pyArgs = ["main.py", "--web"];
|
|
579
|
-
console.log(" \x1b[90m[i]\x1b[0m 首次运行,自动启动 Web 模式进行配置...");
|
|
580
|
-
console.log("");
|
|
581
344
|
}
|
|
582
345
|
|
|
583
|
-
|
|
584
|
-
const modeNames = {
|
|
585
|
-
web: "Web 管理后台 (http://localhost:8767)",
|
|
586
|
-
cli: "CLI 交互模式",
|
|
587
|
-
tray: "系统托盘模式",
|
|
588
|
-
server: "API 服务模式",
|
|
589
|
-
setup: "配置向导",
|
|
590
|
-
};
|
|
591
|
-
console.log(`\x1b[36m启动 ${modeNames[mode] || mode}...\x1b[0m`);
|
|
592
|
-
} else {
|
|
593
|
-
console.log(`\x1b[36m启动...\x1b[0m`);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// 使用 venv 的 Python 启动
|
|
346
|
+
const venvDir = getVenvDir();
|
|
597
347
|
const spawnEnv = {
|
|
598
348
|
...process.env,
|
|
599
|
-
// 设置 VIRTUAL_ENV 环境变量,让 Python 程序知道自己运行在 venv 中
|
|
600
349
|
VIRTUAL_ENV: venvDir,
|
|
601
350
|
PATH: IS_WIN
|
|
602
351
|
? `${path.join(venvDir, "Scripts")};${process.env.PATH}`
|
|
603
352
|
: `${path.join(venvDir, "bin")}:${process.env.PATH}`,
|
|
604
353
|
};
|
|
605
|
-
// 将 --skip-deps 信号传递给 Python,让 main.py 也跳过依赖检查
|
|
606
|
-
if (skipDeps) {
|
|
607
|
-
spawnEnv.MYAGENT_SKIP_DEPS = "1";
|
|
608
|
-
}
|
|
609
|
-
const child = spawn(venvPython, pyArgs, {
|
|
610
|
-
cwd: pkgDir,
|
|
611
|
-
stdio: "inherit",
|
|
612
|
-
env: spawnEnv,
|
|
613
|
-
});
|
|
614
|
-
|
|
615
|
-
child.on("error", (err) => {
|
|
616
|
-
console.error(`\x1b[31m[✗]\x1b[0m 启动失败: ${err.message}`);
|
|
617
|
-
process.exit(1);
|
|
618
|
-
});
|
|
619
354
|
|
|
620
|
-
child
|
|
621
|
-
|
|
622
|
-
});
|
|
355
|
+
const child = spawn(venvPython, pyArgs, { cwd: pkgDir, stdio: "inherit", env: spawnEnv });
|
|
356
|
+
child.on("error", (err) => { console.error(`\x1b[31m启动失败: ${err.message}\x1b[0m`); process.exit(1); });
|
|
357
|
+
child.on("exit", (code) => { process.exit(code || 0); });
|
|
623
358
|
|
|
624
|
-
// Web
|
|
359
|
+
// Web 模式自动打开浏览器
|
|
625
360
|
if (mode === "web") {
|
|
626
|
-
const
|
|
627
|
-
const url = `http://127.0.0.1:${port}`;
|
|
361
|
+
const url = "http://127.0.0.1:8767";
|
|
628
362
|
let opened = false;
|
|
629
|
-
|
|
630
|
-
// 轮询等待服务就绪
|
|
631
|
-
const pollInterval = setInterval(() => {
|
|
363
|
+
const poll = setInterval(() => {
|
|
632
364
|
const http = require("http");
|
|
633
365
|
const req = http.get(`${url}/api/status`, (res) => {
|
|
634
|
-
clearInterval(
|
|
366
|
+
clearInterval(poll);
|
|
635
367
|
if (!opened) {
|
|
636
368
|
opened = true;
|
|
637
|
-
console.log(` \x1b[32m[✓]\x1b[0m 管理后台已就绪: ${url}`);
|
|
638
|
-
console.log(" \x1b[90m[i]\x1b[0m 正在打开浏览器...");
|
|
639
|
-
console.log("");
|
|
640
|
-
// 跨平台打开浏览器
|
|
641
369
|
try {
|
|
642
|
-
|
|
643
|
-
if (
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
} else if (process.platform === "darwin") {
|
|
647
|
-
exec(`open "${url}"`);
|
|
648
|
-
} else {
|
|
649
|
-
exec(`xdg-open "${url}"`);
|
|
650
|
-
}
|
|
651
|
-
} catch (_) {
|
|
652
|
-
console.log(` \x1b[90m[i]\x1b[0m 请手动打开浏览器访问: ${url}`);
|
|
653
|
-
}
|
|
370
|
+
if (IS_WIN) execSync(`start "" "${url}"`);
|
|
371
|
+
else if (process.platform === "darwin") execSync(`open "${url}"`);
|
|
372
|
+
else execSync(`xdg-open "${url}"`);
|
|
373
|
+
} catch (_) {}
|
|
654
374
|
}
|
|
655
375
|
});
|
|
656
|
-
req.on("error", () => {
|
|
657
|
-
|
|
658
|
-
});
|
|
659
|
-
req.setTimeout(2000, () => {
|
|
660
|
-
req.destroy();
|
|
661
|
-
});
|
|
376
|
+
req.on("error", () => {});
|
|
377
|
+
req.setTimeout(2000, () => { req.destroy(); });
|
|
662
378
|
}, 1500);
|
|
379
|
+
setTimeout(() => { clearInterval(poll); }, 120000);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
663
382
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
383
|
+
// ── 主入口 ──────────────────────────────────────────────
|
|
384
|
+
|
|
385
|
+
function main() {
|
|
386
|
+
const args = process.argv.slice(2);
|
|
387
|
+
const cmd = args[0];
|
|
388
|
+
const pkgDir = resolvePackageDir();
|
|
389
|
+
|
|
390
|
+
if (!fs.existsSync(path.join(pkgDir, "main.py"))) {
|
|
391
|
+
console.error("\x1b[31m找不到 main.py,请重新安装: npm install -g myagent-ai\x1b[0m");
|
|
392
|
+
process.exit(1);
|
|
671
393
|
}
|
|
394
|
+
|
|
395
|
+
if (cmd === "uninstall") { cmdUninstall(); return; }
|
|
396
|
+
if (cmd === "reinstall") { cmdReinstall(pkgDir); return; }
|
|
397
|
+
if (cmd === "install") { cmdInstall(pkgDir); return; }
|
|
398
|
+
|
|
399
|
+
// 默认: 直接启动
|
|
400
|
+
cmdRun(pkgDir, args);
|
|
672
401
|
}
|
|
673
402
|
|
|
674
403
|
main();
|
package/web/ui/index.html
CHANGED
|
@@ -38,7 +38,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;b
|
|
|
38
38
|
.sidebar{width:220px;background:var(--surface);border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0}
|
|
39
39
|
.sidebar .logo{padding:20px;font-size:18px;font-weight:700;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px}
|
|
40
40
|
.sidebar .logo span{color:var(--primary)}
|
|
41
|
-
.nav{flex:1;padding:0 8px 8px;overflow
|
|
41
|
+
.nav{flex:1;padding:0 8px 8px;overflow:hidden;min-height:0;display:flex;flex-direction:column}
|
|
42
42
|
.nav-item{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:var(--radius);cursor:pointer;color:var(--text2);font-size:14px;transition:all .15s;margin-bottom:2px}
|
|
43
43
|
.nav-item:hover{background:var(--surface2);color:var(--text)}
|
|
44
44
|
.nav-item.active{background:var(--primary);color:#fff}
|