myagent-ai 1.15.56 → 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 +225 -548
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,651 +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 };
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// ── 依赖安装 ─────────────────────────────────────────────
|
|
252
|
-
|
|
253
|
-
// 从 requirements.txt 解析出 Python 模块名(用于 import 检查)
|
|
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;
|
|
146
|
+
console.log(" \x1b[32m✓\x1b[0m 虚拟环境已创建");
|
|
147
|
+
return getVenvPython(venvDir);
|
|
282
148
|
}
|
|
283
149
|
|
|
284
|
-
//
|
|
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
|
-
}
|
|
150
|
+
// ── 依赖安装(仅在 install/reinstall/首次运行时调用) ───
|
|
316
151
|
|
|
317
|
-
function
|
|
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
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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"]]) {
|
|
389
195
|
try {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const pipName = m[1].toLowerCase().replace(/-/g, "_");
|
|
397
|
-
const pipSpec = line.split(/[;#]/)[0].trim();
|
|
398
|
-
importToPip[pipName] = pipSpec;
|
|
399
|
-
// 特殊映射 (和 parseRequirements 中的 importMap 一致)
|
|
400
|
-
const specialMap = {
|
|
401
|
-
"beautifulsoup4": "bs4",
|
|
402
|
-
"py_getwindow": "pygetwindow",
|
|
403
|
-
"python_telegram_bot": "telegram",
|
|
404
|
-
"pillow": "PIL",
|
|
405
|
-
"psutil": "psutil",
|
|
406
|
-
"edge_tts": "edge_tts",
|
|
407
|
-
};
|
|
408
|
-
const importName = specialMap[pipName] || pipName;
|
|
409
|
-
importToPip[importName] = pipSpec;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
196
|
+
console.log(" 尝试安装核心依赖...");
|
|
197
|
+
execFileSync(venvPython, ["-m", "pip", "install", "-q", "--disable-pip-version-check", ...mirrors, ...CORE], {
|
|
198
|
+
encoding: "utf8", stdio: "inherit", timeout: 300000,
|
|
199
|
+
});
|
|
200
|
+
console.log(" \x1b[32m✓\x1b[0m 核心依赖安装完成");
|
|
201
|
+
return true;
|
|
412
202
|
} catch (_) {}
|
|
413
|
-
|
|
414
|
-
let failCount = 0;
|
|
415
|
-
for (const mod of missing) {
|
|
416
|
-
try {
|
|
417
|
-
const pkgSpec = importToPip[mod] || mod;
|
|
418
|
-
execFileSync(venvPython, ["-m", "pip", "install", "--disable-pip-version-check", ...mirrorArgs, pkgSpec], {
|
|
419
|
-
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 120000,
|
|
420
|
-
});
|
|
421
|
-
console.log(` \x1b[32m[✓]\x1b[0m ${mod} 安装成功`);
|
|
422
|
-
} catch (_) {
|
|
423
|
-
failCount++;
|
|
424
|
-
console.log(` \x1b[31m[✗]\x1b[0m ${mod} 安装失败`);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
if (failCount === 0) {
|
|
428
|
-
console.log(" \x1b[32m[✓]\x1b[0m 所有缺失依赖安装成功");
|
|
429
|
-
installed = true;
|
|
430
|
-
} else if (failCount < missing.length) {
|
|
431
|
-
console.log(` \x1b[33m[!]\x1b[0m ${missing.length - failCount}/${missing.length} 个依赖安装成功,${failCount} 个失败`);
|
|
432
|
-
installed = true; // 部分成功也算,核心依赖优先
|
|
433
|
-
}
|
|
434
203
|
}
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
435
206
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
console.log(" \x1b[90m[i]\x1b[0m 其他依赖将在启动时自动安装");
|
|
444
|
-
} catch (_) {
|
|
445
|
-
// 策略4: 尝试阿里云镜像
|
|
446
|
-
console.log(" \x1b[33m[!]\x1b[0m 核心依赖安装失败,尝试阿里云镜像...");
|
|
447
|
-
try {
|
|
448
|
-
const aliyunArgs = ["-i", "https://mirrors.aliyun.com/pypi/simple/", "--trusted-host", "mirrors.aliyun.com"];
|
|
449
|
-
execFileSync(venvPython, ["-m", "pip", "install", "--quiet", "--disable-pip-version-check", ...aliyunArgs, ...CORE_PACKAGES], {
|
|
450
|
-
encoding: "utf8", stdio: "inherit", timeout: 180000,
|
|
451
|
-
});
|
|
452
|
-
console.log(" \x1b[32m[✓]\x1b[0m 核心依赖安装完成(阿里云镜像)");
|
|
453
|
-
} catch (_) {
|
|
454
|
-
console.error(" \x1b[31m[✗]\x1b[0m 依赖安装失败,请手动运行:");
|
|
455
|
-
console.error(` ${venvPython} -m pip install -r "${reqFile}"`);
|
|
456
|
-
console.error("");
|
|
457
|
-
console.error(" 或使用重新安装命令: myagent-ai reinstall");
|
|
458
|
-
console.error("");
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
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 (_) {}
|
|
462
214
|
}
|
|
463
215
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
case "tray": return ["main.py", "--tray"];
|
|
471
|
-
case "server": return ["main.py", "--server"];
|
|
472
|
-
case "setup": return ["main.py", "--setup"];
|
|
473
|
-
case "reinstall":
|
|
474
|
-
return []; // 特殊处理
|
|
475
|
-
case "uninstall":
|
|
476
|
-
return []; // 特殊处理
|
|
477
|
-
default:
|
|
478
|
-
return ["main.py", ...userArgs];
|
|
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;
|
|
479
222
|
}
|
|
480
223
|
}
|
|
481
224
|
|
|
482
|
-
// ──
|
|
483
|
-
function main() {
|
|
484
|
-
const userArgs = process.argv.slice(2);
|
|
485
|
-
const pkgDir = resolvePackageDir();
|
|
486
|
-
|
|
487
|
-
if (!fs.existsSync(path.join(pkgDir, "main.py"))) {
|
|
488
|
-
console.error("\x1b[31m[✗]\x1b[0m 错误: 找不到 main.py");
|
|
489
|
-
console.error(` 搜索路径: ${pkgDir}`);
|
|
490
|
-
console.error(' 请重新安装: npm install -g myagent-ai');
|
|
491
|
-
process.exit(1);
|
|
492
|
-
}
|
|
225
|
+
// ── 命令处理 ──────────────────────────────────────────────
|
|
493
226
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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
|
+
}
|
|
499
240
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
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
|
+
}
|
|
514
259
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
} else {
|
|
520
|
-
execSync("pkill -f 'myagent|main.py' 2>/dev/null; sleep 1", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 10000 });
|
|
521
|
-
}
|
|
522
|
-
} catch (_) {}
|
|
523
|
-
console.log(" \x1b[32m[✓]\x1b[0m 进程已停止");
|
|
260
|
+
function cmdUninstall() {
|
|
261
|
+
const venvDir = getVenvDir();
|
|
262
|
+
const dataDir = getDataDir();
|
|
263
|
+
const purgeArg = process.argv.includes("--purge") || process.argv.includes("-p");
|
|
524
264
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
execFileSync("npm", ["uninstall", "-g", PKG_NAME], {
|
|
529
|
-
encoding: "utf8", stdio: "inherit", timeout: 60000,
|
|
530
|
-
});
|
|
531
|
-
console.log(" \x1b[32m[✓]\x1b[0m npm 全局包已卸载");
|
|
532
|
-
} catch (_) {
|
|
533
|
-
console.log(" \x1b[90m[i]\x1b[0m npm 全局包未找到");
|
|
534
|
-
}
|
|
265
|
+
console.log("");
|
|
266
|
+
console.log(" \x1b[36mMyAgent 卸载\x1b[0m");
|
|
267
|
+
console.log("");
|
|
535
268
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
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 });
|
|
546
279
|
} else {
|
|
547
|
-
|
|
548
|
-
console.log(" \x1b[90m[i]\x1b[0m 保留数据目录: " + dataDir);
|
|
549
|
-
console.log(" \x1b[90m[i]\x1b[0m 如需彻底清除: myagent-ai uninstall --purge");
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
console.log("");
|
|
553
|
-
console.log(" \x1b[32m[✓]\x1b[0m 卸载完成!");
|
|
554
|
-
console.log(" \x1b[90m[i]\x1b[0m 重新安装: npm install -g myagent-ai");
|
|
555
|
-
console.log("");
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// 特殊命令: reinstall
|
|
560
|
-
if (userArgs[0] === "reinstall") {
|
|
561
|
-
const venvDir = getVenvDir();
|
|
562
|
-
if (fs.existsSync(venvDir)) {
|
|
563
|
-
console.log(" \x1b[36m[*]\x1b[0m 删除旧虚拟环境...");
|
|
564
|
-
fs.rmSync(venvDir, { recursive: true, force: true });
|
|
280
|
+
execSync("pkill -f 'myagent|main.py' 2>/dev/null", { encoding: "utf8", timeout: 10000 });
|
|
565
281
|
}
|
|
566
|
-
|
|
567
|
-
installDeps(venvPython, venvPip, pkgDir);
|
|
568
|
-
console.log("");
|
|
569
|
-
console.log(" \x1b[32m[✓]\x1b[0m 依赖已全部重新安装到虚拟环境");
|
|
570
|
-
console.log(` \x1b[90m[i]\x1b[0m 虚拟环境: ${venvDir}`);
|
|
571
|
-
console.log("");
|
|
572
|
-
return;
|
|
573
|
-
}
|
|
282
|
+
} catch (_) {}
|
|
574
283
|
|
|
575
|
-
// 打印 Banner(显示版本号)
|
|
576
|
-
let pkgVersion = "";
|
|
577
284
|
try {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
285
|
+
execFileSync("npm", ["uninstall", "-g", PKG_NAME], {
|
|
286
|
+
encoding: "utf8", stdio: "inherit", timeout: 60000,
|
|
287
|
+
});
|
|
581
288
|
} catch (_) {}
|
|
582
|
-
|
|
583
|
-
if (
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
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(" 数据已清除");
|
|
587
293
|
}
|
|
588
294
|
console.log("");
|
|
295
|
+
console.log(" \x1b[32m✓ 卸载完成\x1b[0m");
|
|
296
|
+
console.log("");
|
|
297
|
+
}
|
|
589
298
|
|
|
590
|
-
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
// 确保 venv 存在
|
|
596
|
-
const { venvDir, venvPython, isNew } = ensureVenv();
|
|
597
|
-
|
|
598
|
-
// 检查并安装依赖(--skip-deps 时跳过)
|
|
599
|
-
if (skipDeps) {
|
|
600
|
-
console.log(" \x1b[90m[i]\x1b[0m 跳过依赖检查(--skip-deps)");
|
|
601
|
-
} else if (isNew) {
|
|
602
|
-
installDeps(venvPython, getVenvPip(venvDir), pkgDir);
|
|
603
|
-
} else {
|
|
604
|
-
// 即使 venv 已存在,也快速检查核心依赖
|
|
605
|
-
const venvPip = getVenvPip(venvDir);
|
|
606
|
-
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);
|
|
607
304
|
}
|
|
608
305
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
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");
|
|
613
313
|
console.log("");
|
|
614
|
-
|
|
314
|
+
installAllDeps(venvPython, pkgDir);
|
|
315
|
+
markDepsInstalled(ver);
|
|
615
316
|
console.log("");
|
|
616
317
|
}
|
|
617
318
|
|
|
618
|
-
//
|
|
619
|
-
console.log(` \x1b[90m[i]\x1b[0m 虚拟环境: ${venvDir}`);
|
|
620
|
-
console.log(` \x1b[90m[i]\x1b[0m Python: ${venvPython}`);
|
|
319
|
+
// 显示版本
|
|
621
320
|
console.log("");
|
|
321
|
+
if (ver) {
|
|
322
|
+
console.log(` MyAgent v${ver}`);
|
|
323
|
+
}
|
|
622
324
|
|
|
623
|
-
//
|
|
624
|
-
|
|
625
|
-
|
|
325
|
+
// 首次运行检测
|
|
326
|
+
const configFile = path.join(getDataDir(), "config.json");
|
|
327
|
+
const isFirstRun = !fs.existsSync(configFile);
|
|
328
|
+
|
|
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
|
+
}
|
|
626
340
|
|
|
627
|
-
// 首次运行且未指定模式 → 自动选择 web 模式
|
|
628
341
|
if (isFirstRun && !mode) {
|
|
629
342
|
mode = "web";
|
|
630
343
|
pyArgs = ["main.py", "--web"];
|
|
631
|
-
console.log(" \x1b[90m[i]\x1b[0m 首次运行,自动启动 Web 模式进行配置...");
|
|
632
|
-
console.log("");
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
if (mode) {
|
|
636
|
-
const modeNames = {
|
|
637
|
-
web: "Web 管理后台 (http://localhost:8767)",
|
|
638
|
-
cli: "CLI 交互模式",
|
|
639
|
-
tray: "系统托盘模式",
|
|
640
|
-
server: "API 服务模式",
|
|
641
|
-
setup: "配置向导",
|
|
642
|
-
};
|
|
643
|
-
console.log(`\x1b[36m启动 ${modeNames[mode] || mode}...\x1b[0m`);
|
|
644
|
-
} else {
|
|
645
|
-
console.log(`\x1b[36m启动...\x1b[0m`);
|
|
646
344
|
}
|
|
647
345
|
|
|
648
|
-
|
|
346
|
+
const venvDir = getVenvDir();
|
|
649
347
|
const spawnEnv = {
|
|
650
348
|
...process.env,
|
|
651
|
-
// 设置 VIRTUAL_ENV 环境变量,让 Python 程序知道自己运行在 venv 中
|
|
652
349
|
VIRTUAL_ENV: venvDir,
|
|
653
350
|
PATH: IS_WIN
|
|
654
351
|
? `${path.join(venvDir, "Scripts")};${process.env.PATH}`
|
|
655
352
|
: `${path.join(venvDir, "bin")}:${process.env.PATH}`,
|
|
656
353
|
};
|
|
657
|
-
// 将 --skip-deps 信号传递给 Python,让 main.py 也跳过依赖检查
|
|
658
|
-
if (skipDeps) {
|
|
659
|
-
spawnEnv.MYAGENT_SKIP_DEPS = "1";
|
|
660
|
-
}
|
|
661
|
-
const child = spawn(venvPython, pyArgs, {
|
|
662
|
-
cwd: pkgDir,
|
|
663
|
-
stdio: "inherit",
|
|
664
|
-
env: spawnEnv,
|
|
665
|
-
});
|
|
666
|
-
|
|
667
|
-
child.on("error", (err) => {
|
|
668
|
-
console.error(`\x1b[31m[✗]\x1b[0m 启动失败: ${err.message}`);
|
|
669
|
-
process.exit(1);
|
|
670
|
-
});
|
|
671
354
|
|
|
672
|
-
child
|
|
673
|
-
|
|
674
|
-
});
|
|
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); });
|
|
675
358
|
|
|
676
|
-
// Web
|
|
359
|
+
// Web 模式自动打开浏览器
|
|
677
360
|
if (mode === "web") {
|
|
678
|
-
const
|
|
679
|
-
const url = `http://127.0.0.1:${port}`;
|
|
361
|
+
const url = "http://127.0.0.1:8767";
|
|
680
362
|
let opened = false;
|
|
681
|
-
|
|
682
|
-
// 轮询等待服务就绪
|
|
683
|
-
const pollInterval = setInterval(() => {
|
|
363
|
+
const poll = setInterval(() => {
|
|
684
364
|
const http = require("http");
|
|
685
365
|
const req = http.get(`${url}/api/status`, (res) => {
|
|
686
|
-
clearInterval(
|
|
366
|
+
clearInterval(poll);
|
|
687
367
|
if (!opened) {
|
|
688
368
|
opened = true;
|
|
689
|
-
console.log(` \x1b[32m[✓]\x1b[0m 管理后台已就绪: ${url}`);
|
|
690
|
-
console.log(" \x1b[90m[i]\x1b[0m 正在打开浏览器...");
|
|
691
|
-
console.log("");
|
|
692
|
-
// 跨平台打开浏览器
|
|
693
369
|
try {
|
|
694
|
-
|
|
695
|
-
if (
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
} else if (process.platform === "darwin") {
|
|
699
|
-
exec(`open "${url}"`);
|
|
700
|
-
} else {
|
|
701
|
-
exec(`xdg-open "${url}"`);
|
|
702
|
-
}
|
|
703
|
-
} catch (_) {
|
|
704
|
-
console.log(` \x1b[90m[i]\x1b[0m 请手动打开浏览器访问: ${url}`);
|
|
705
|
-
}
|
|
370
|
+
if (IS_WIN) execSync(`start "" "${url}"`);
|
|
371
|
+
else if (process.platform === "darwin") execSync(`open "${url}"`);
|
|
372
|
+
else execSync(`xdg-open "${url}"`);
|
|
373
|
+
} catch (_) {}
|
|
706
374
|
}
|
|
707
375
|
});
|
|
708
|
-
req.on("error", () => {
|
|
709
|
-
|
|
710
|
-
});
|
|
711
|
-
req.setTimeout(2000, () => {
|
|
712
|
-
req.destroy();
|
|
713
|
-
});
|
|
376
|
+
req.on("error", () => {});
|
|
377
|
+
req.setTimeout(2000, () => { req.destroy(); });
|
|
714
378
|
}, 1500);
|
|
379
|
+
setTimeout(() => { clearInterval(poll); }, 120000);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
715
382
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
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);
|
|
723
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);
|
|
724
401
|
}
|
|
725
402
|
|
|
726
403
|
main();
|