myagent-ai 1.3.3 → 1.4.0
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/core/version.py +1 -1
- package/install/install.ps1 +29 -25
- package/install/install.sh +32 -41
- package/package.json +1 -1
- package/setup.py +1 -1
- package/start.js +263 -244
- package/start.sh +129 -131
package/core/version.py
CHANGED
package/install/install.ps1
CHANGED
|
@@ -9,7 +9,7 @@ param(
|
|
|
9
9
|
|
|
10
10
|
$ErrorActionPreference = "Stop"
|
|
11
11
|
$PKG_NAME = "myagent-ai"
|
|
12
|
-
$PKG_VERSION = "1.
|
|
12
|
+
$PKG_VERSION = "1.4.0"
|
|
13
13
|
|
|
14
14
|
# Allow running scripts for the current process
|
|
15
15
|
if ($PSVersionTable.PSVersion.Major -ge 5) {
|
|
@@ -57,13 +57,13 @@ function Check-Python {
|
|
|
57
57
|
}
|
|
58
58
|
# 尝试在常见安装路径查找
|
|
59
59
|
$commonPaths = @(
|
|
60
|
+
"$env:LOCALAPPDATA\Programs\Python\Python314\python.exe",
|
|
61
|
+
"$env:LOCALAPPDATA\Programs\Python\Python313\python.exe",
|
|
60
62
|
"$env:LOCALAPPDATA\Programs\Python\Python312\python.exe",
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"C:\
|
|
64
|
-
"C:\
|
|
65
|
-
"C:\Program Files\Python312\python.exe",
|
|
66
|
-
"C:\Program Files\Python311\python.exe"
|
|
63
|
+
"C:\Python314\python.exe",
|
|
64
|
+
"C:\Python313\python.exe",
|
|
65
|
+
"C:\Program Files\Python314\python.exe",
|
|
66
|
+
"C:\Program Files\Python313\python.exe"
|
|
67
67
|
)
|
|
68
68
|
foreach ($pyPath in $commonPaths) {
|
|
69
69
|
if (Test-Path $pyPath) {
|
|
@@ -79,14 +79,14 @@ function Check-Python {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
function Install-Python {
|
|
82
|
-
Write-Host "[*] Installing Python 3.
|
|
82
|
+
Write-Host "[*] Installing Python 3.14 ..." -ForegroundColor Yellow
|
|
83
83
|
$installAttempted = $false
|
|
84
84
|
|
|
85
85
|
# Try winget first
|
|
86
86
|
try {
|
|
87
87
|
$null = (winget --version 2>$null)
|
|
88
88
|
Write-Host " Using winget ..." -ForegroundColor Gray
|
|
89
|
-
winget install Python.Python.3.
|
|
89
|
+
winget install Python.Python.3.14 --accept-package-agreements --accept-source-agreements
|
|
90
90
|
$installAttempted = $true
|
|
91
91
|
# 等待安装完成后刷新 PATH(重试多次)
|
|
92
92
|
for ($i = 1; $i -le 5; $i++) {
|
|
@@ -96,9 +96,9 @@ function Install-Python {
|
|
|
96
96
|
# winget 已完成安装,即使 Check-Python 失败也不再重复安装
|
|
97
97
|
Refresh-Path
|
|
98
98
|
$pyPaths = @(
|
|
99
|
-
"$env:LOCALAPPDATA\Programs\Python\
|
|
100
|
-
"C:\
|
|
101
|
-
"C:\Program Files\
|
|
99
|
+
"$env:LOCALAPPDATA\Programs\Python\Python314\python.exe",
|
|
100
|
+
"C:\Python314\python.exe",
|
|
101
|
+
"C:\Program Files\Python314\python.exe"
|
|
102
102
|
)
|
|
103
103
|
foreach ($pyPath in $pyPaths) {
|
|
104
104
|
if (Test-Path $pyPath) {
|
|
@@ -121,11 +121,11 @@ function Install-Python {
|
|
|
121
121
|
# 仅在 winget 未尝试时才从 python.org 下载(避免双重安装)
|
|
122
122
|
if (-not $installAttempted) {
|
|
123
123
|
# Fallback: download from python.org
|
|
124
|
-
$pyUrl = "https://www.python.org/ftp/python/3.
|
|
124
|
+
$pyUrl = "https://www.python.org/ftp/python/3.14.0/python-3.14.0-amd64.exe"
|
|
125
125
|
$pyExe = Join-Path $env:TEMP "python-installer.exe"
|
|
126
126
|
|
|
127
127
|
try {
|
|
128
|
-
Write-Host " Downloading Python 3.
|
|
128
|
+
Write-Host " Downloading Python 3.14.0 ..." -ForegroundColor Gray
|
|
129
129
|
Invoke-WebRequest -Uri $pyUrl -OutFile $pyExe -UseBasicParsing
|
|
130
130
|
|
|
131
131
|
Write-Host " Installing (Add to PATH) ..." -ForegroundColor Gray
|
|
@@ -216,11 +216,9 @@ function Install-MyAgent {
|
|
|
216
216
|
Write-Host "[OK] $PKG_NAME installed" -ForegroundColor Green
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
# ── Install Python dependencies
|
|
219
|
+
# ── Install Python dependencies (使用独立虚拟环境) ──
|
|
220
220
|
function Install-PythonDeps {
|
|
221
|
-
# 获取 Python 命令(使用已检测到的路径或默认)
|
|
222
221
|
$pyCmd = if ($Script:PythonCmd) { $Script:PythonCmd } else { "python" }
|
|
223
|
-
|
|
224
222
|
$npmRoot = (npm root -g 2>$null)
|
|
225
223
|
$pkgDir = Join-Path $npmRoot $PKG_NAME
|
|
226
224
|
$reqFile = Join-Path $pkgDir "requirements.txt"
|
|
@@ -234,14 +232,20 @@ function Install-PythonDeps {
|
|
|
234
232
|
}
|
|
235
233
|
}
|
|
236
234
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
235
|
+
$venvDir = "$env:USERPROFILE\.myagent\venv"
|
|
236
|
+
$venvPython = Join-Path $venvDir "Scripts\python.exe"
|
|
237
|
+
|
|
238
|
+
Write-Host "[*] Creating virtual environment at $venvDir ..." -ForegroundColor Yellow
|
|
239
|
+
New-Item -ItemType Directory -Path "$env:USERPROFILE\.myagent" -Force | Out-Null
|
|
240
|
+
& $pyCmd -m venv $venvDir
|
|
241
|
+
Write-Host "[OK] Virtual environment created" -ForegroundColor Green
|
|
242
|
+
|
|
243
|
+
Write-Host "[*] Installing Python dependencies into venv ..." -ForegroundColor Yellow
|
|
244
|
+
& $venvPython -m pip install --upgrade pip --quiet 2>$null
|
|
245
|
+
& $venvPython -m pip install -r $reqFile --disable-pip-version-check
|
|
246
|
+
Write-Host "[OK] Dependencies installed into venv" -ForegroundColor Green
|
|
247
|
+
Write-Host "[i] Virtual env: $venvDir" -ForegroundColor Gray
|
|
248
|
+
Write-Host "[i] venv will be used automatically on startup" -ForegroundColor Gray
|
|
245
249
|
}
|
|
246
250
|
|
|
247
251
|
# ── Post-install guide ──────────────────────────────
|
package/install/install.sh
CHANGED
|
@@ -21,7 +21,7 @@ NO_DEPS=false
|
|
|
21
21
|
DRY_RUN=false
|
|
22
22
|
SCRIPT_URL="https://raw.githubusercontent.com/ctz168/myagent/main/install/install.sh"
|
|
23
23
|
PKG_NAME="myagent-ai"
|
|
24
|
-
PKG_VERSION="1.
|
|
24
|
+
PKG_VERSION="1.4.0"
|
|
25
25
|
|
|
26
26
|
for arg in "$@"; do
|
|
27
27
|
case "$arg" in
|
|
@@ -82,7 +82,7 @@ check_python() {
|
|
|
82
82
|
|
|
83
83
|
# 按优先级尝试多个 Python 命令(包括版本号后缀的)
|
|
84
84
|
local py_cmd=""
|
|
85
|
-
for cmd in python3 python3.
|
|
85
|
+
for cmd in python3 python3.14 python3.13 python3.12 python; do
|
|
86
86
|
if command -v "$cmd" &>/dev/null; then
|
|
87
87
|
py_cmd="$cmd"
|
|
88
88
|
break
|
|
@@ -115,7 +115,7 @@ get_python_cmd() {
|
|
|
115
115
|
return 0
|
|
116
116
|
fi
|
|
117
117
|
hash -r 2>/dev/null || true
|
|
118
|
-
for cmd in python3 python3.
|
|
118
|
+
for cmd in python3 python3.14 python3.13 python3.12 python; do
|
|
119
119
|
if command -v "$cmd" &>/dev/null; then
|
|
120
120
|
_PYTHON_CMD="$cmd"
|
|
121
121
|
echo "$cmd"
|
|
@@ -127,30 +127,30 @@ get_python_cmd() {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
install_python() {
|
|
130
|
-
step "Installing Python 3.
|
|
130
|
+
step "Installing Python 3.14 ..."
|
|
131
131
|
local installed=false
|
|
132
132
|
|
|
133
133
|
if [ "$OS" = "macos" ]; then
|
|
134
134
|
if command -v brew &>/dev/null; then
|
|
135
135
|
info "Using Homebrew ..."
|
|
136
|
-
brew install python@3.
|
|
136
|
+
brew install python@3.14 && installed=true
|
|
137
137
|
# brew 安装后更新 PATH 并刷新缓存
|
|
138
|
-
export PATH="$(brew --prefix python@3.
|
|
138
|
+
export PATH="$(brew --prefix python@3.14)/bin:$PATH"
|
|
139
139
|
hash -r
|
|
140
140
|
# 等待文件系统同步(某些情况下 brew 完成后二进制文件不会立即可见)
|
|
141
141
|
sleep 1
|
|
142
142
|
if check_python; then return 0; fi
|
|
143
143
|
# brew 已安装但 python3 软链接可能不存在,尝试手动查找
|
|
144
144
|
local brew_py
|
|
145
|
-
brew_py="$(brew --prefix python@3.
|
|
145
|
+
brew_py="$(brew --prefix python@3.14)/bin/python3"
|
|
146
146
|
if [ -x "$brew_py" ]; then
|
|
147
147
|
_PYTHON_CMD="$brew_py"
|
|
148
148
|
success "Python found at $brew_py"
|
|
149
149
|
return 0
|
|
150
150
|
fi
|
|
151
151
|
# 尝试查找版本化的 python 二进制
|
|
152
|
-
for ver in 3.
|
|
153
|
-
brew_py="$(brew --prefix python@3.
|
|
152
|
+
for ver in 3.14 3.13 3.12; do
|
|
153
|
+
brew_py="$(brew --prefix python@3.14)/bin/python${ver}"
|
|
154
154
|
if [ -x "$brew_py" ]; then
|
|
155
155
|
_PYTHON_CMD="$brew_py"
|
|
156
156
|
success "Python found at $brew_py"
|
|
@@ -160,20 +160,20 @@ install_python() {
|
|
|
160
160
|
# brew 已成功安装,即使 check_python 失败也不再重复安装
|
|
161
161
|
if $installed; then
|
|
162
162
|
warn "Python 已通过 Homebrew 安装,但 python3 命令暂不可用"
|
|
163
|
-
info "请重启终端或运行: export PATH=\"$(brew --prefix python@3.
|
|
164
|
-
info "或直接使用: $(brew --prefix python@3.
|
|
163
|
+
info "请重启终端或运行: export PATH=\"$(brew --prefix python@3.14)/bin:\$PATH\""
|
|
164
|
+
info "或直接使用: $(brew --prefix python@3.14)/bin/python3"
|
|
165
165
|
# 将 brew python 路径设为 _PYTHON_CMD 供后续使用
|
|
166
|
-
_PYTHON_CMD="$(brew --prefix python@3.
|
|
166
|
+
_PYTHON_CMD="$(brew --prefix python@3.14)/bin/python3"
|
|
167
167
|
return 0
|
|
168
168
|
fi
|
|
169
169
|
fi
|
|
170
170
|
# 仅在 brew 未安装时才从 python.org 下载
|
|
171
171
|
if ! $installed; then
|
|
172
|
-
info "Downloading Python 3.
|
|
173
|
-
curl -fsSL -o /tmp/python.pkg "https://www.python.org/ftp/python/3.
|
|
172
|
+
info "Downloading Python 3.14 ..."
|
|
173
|
+
curl -fsSL -o /tmp/python.pkg "https://www.python.org/ftp/python/3.14.0/python-3.14.0-macos11.pkg"
|
|
174
174
|
sudo installer -pkg /tmp/python.pkg -target /
|
|
175
175
|
rm -f /tmp/python.pkg
|
|
176
|
-
export PATH="/Library/Frameworks/Python.framework/Versions/3.
|
|
176
|
+
export PATH="/Library/Frameworks/Python.framework/Versions/3.14/bin:$PATH"
|
|
177
177
|
hash -r
|
|
178
178
|
if check_python; then return 0; fi
|
|
179
179
|
fi
|
|
@@ -184,12 +184,12 @@ install_python() {
|
|
|
184
184
|
if command -v apt-get &>/dev/null; then
|
|
185
185
|
info "Using apt ..."
|
|
186
186
|
sudo apt-get update -qq
|
|
187
|
-
sudo apt-get install -y python3.
|
|
187
|
+
sudo apt-get install -y python3.14 python3.14-venv python3-pip 2>/dev/null || \
|
|
188
188
|
sudo apt-get install -y python3 python3-venv python3-pip
|
|
189
189
|
hash -r
|
|
190
190
|
if check_python; then return 0; fi
|
|
191
191
|
# apt 已安装但可能需要版本化命令
|
|
192
|
-
for ver in 3.
|
|
192
|
+
for ver in 3.14 3.13 3.12; do
|
|
193
193
|
if command -v "python${ver}" &>/dev/null; then
|
|
194
194
|
_PYTHON_CMD="python${ver}"
|
|
195
195
|
success "Python found as python${ver}"
|
|
@@ -200,7 +200,7 @@ install_python() {
|
|
|
200
200
|
fi
|
|
201
201
|
if command -v dnf &>/dev/null; then
|
|
202
202
|
info "Using dnf ..."
|
|
203
|
-
sudo dnf install -y python3.
|
|
203
|
+
sudo dnf install -y python3.14 2>/dev/null || sudo dnf install -y python3
|
|
204
204
|
hash -r
|
|
205
205
|
if check_python; then return 0; fi
|
|
206
206
|
return 1
|
|
@@ -345,14 +345,13 @@ install_myagent() {
|
|
|
345
345
|
success "$PKG_NAME installed"
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
-
# ── Install Python dependencies
|
|
348
|
+
# ── Install Python dependencies (使用独立虚拟环境) ──
|
|
349
349
|
install_python_deps() {
|
|
350
350
|
local pkg_dir
|
|
351
351
|
pkg_dir="$(npm root -g)/$PKG_NAME"
|
|
352
352
|
local req_file="$pkg_dir/requirements.txt"
|
|
353
353
|
|
|
354
354
|
if [ ! -f "$req_file" ]; then
|
|
355
|
-
# 尝试从项目目录查找
|
|
356
355
|
if [ -f "./requirements.txt" ]; then
|
|
357
356
|
req_file="./requirements.txt"
|
|
358
357
|
else
|
|
@@ -361,7 +360,6 @@ install_python_deps() {
|
|
|
361
360
|
fi
|
|
362
361
|
fi
|
|
363
362
|
|
|
364
|
-
# 获取已安装的 Python 命令
|
|
365
363
|
local py_cmd
|
|
366
364
|
py_cmd="$(get_python_cmd)"
|
|
367
365
|
if [ -z "$py_cmd" ]; then
|
|
@@ -369,26 +367,19 @@ install_python_deps() {
|
|
|
369
367
|
return 1
|
|
370
368
|
fi
|
|
371
369
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
"
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
return 0
|
|
386
|
-
fi
|
|
387
|
-
err "所有安装方式均失败"
|
|
388
|
-
info "请手动安装: $py_cmd -m pip install -r $req_file --break-system-packages"
|
|
389
|
-
return 1
|
|
390
|
-
}
|
|
391
|
-
success "Python dependencies installed"
|
|
370
|
+
local venv_dir="$HOME/.myagent/venv"
|
|
371
|
+
|
|
372
|
+
step "Creating virtual environment at $venv_dir ..."
|
|
373
|
+
mkdir -p "$HOME/.myagent"
|
|
374
|
+
"$py_cmd" -m venv "$venv_dir"
|
|
375
|
+
success "Virtual environment created"
|
|
376
|
+
|
|
377
|
+
step "Installing Python dependencies into venv ..."
|
|
378
|
+
"$venv_dir/bin/pip" install --upgrade pip --quiet 2>/dev/null || true
|
|
379
|
+
"$venv_dir/bin/pip" install -r "$req_file" --disable-pip-version-check
|
|
380
|
+
success "Dependencies installed into venv"
|
|
381
|
+
info "虚拟环境: $venv_dir"
|
|
382
|
+
info "启动时自动使用 venv,无需手动激活"
|
|
392
383
|
}
|
|
393
384
|
|
|
394
385
|
# ── 首次配置引导 ───────────────────────────────────
|
package/package.json
CHANGED
package/setup.py
CHANGED
|
@@ -9,7 +9,7 @@ from pathlib import Path
|
|
|
9
9
|
_version_path = Path(__file__).parent / "core" / "version.py"
|
|
10
10
|
_version_vars = {}
|
|
11
11
|
exec(_version_path.read_text(), _version_vars)
|
|
12
|
-
__version__ = _version_vars.get("BASE_VERSION", "1.
|
|
12
|
+
__version__ = _version_vars.get("BASE_VERSION", "1.4.0")
|
|
13
13
|
|
|
14
14
|
setup(
|
|
15
15
|
name="myagent",
|
package/start.js
CHANGED
|
@@ -2,68 +2,83 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* start.js - 跨平台入口脚本 (Windows/macOS/Linux)
|
|
4
4
|
* ================================================
|
|
5
|
-
* npm bin
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
5
|
+
* npm bin 入口,自动管理独立虚拟环境:
|
|
6
|
+
* - 虚拟环境位置: ~/.myagent/venv (与其他项目完全隔离)
|
|
7
|
+
* - 首次运行自动创建 venv 并安装所有依赖
|
|
8
|
+
* - 后续运行直接使用 venv,无需重复安装
|
|
9
|
+
* - pip 升级/重装: myagent-ai reinstall
|
|
8
10
|
*
|
|
9
11
|
* 用法:
|
|
10
|
-
* myagent-ai
|
|
11
|
-
* myagent-ai web
|
|
12
|
-
* myagent-ai cli
|
|
13
|
-
* myagent-ai tray
|
|
14
|
-
* myagent-ai server
|
|
15
|
-
* myagent-ai setup
|
|
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
|
|
16
19
|
*/
|
|
17
20
|
"use strict";
|
|
18
21
|
|
|
19
22
|
const { spawn, execSync, execFileSync } = require("child_process");
|
|
20
23
|
const path = require("path");
|
|
21
24
|
const fs = require("fs");
|
|
25
|
+
const os = require("os");
|
|
22
26
|
|
|
23
27
|
// ── 常量 ──────────────────────────────────────────────────
|
|
24
28
|
const IS_WIN = process.platform === "win32";
|
|
25
29
|
|
|
26
|
-
//
|
|
30
|
+
// 虚拟环境目录
|
|
31
|
+
function getVenvDir() {
|
|
32
|
+
const homeDir = os.homedir();
|
|
33
|
+
return path.join(homeDir, ".myagent", "venv");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// venv 中 Python 可执行文件的路径
|
|
37
|
+
function getVenvPython(venvDir) {
|
|
38
|
+
return IS_WIN
|
|
39
|
+
? path.join(venvDir, "Scripts", "python.exe")
|
|
40
|
+
: path.join(venvDir, "bin", "python");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// venv 中 pip 可执行文件的路径
|
|
44
|
+
function getVenvPip(venvDir) {
|
|
45
|
+
return IS_WIN
|
|
46
|
+
? path.join(venvDir, "Scripts", "pip.exe")
|
|
47
|
+
: path.join(venvDir, "bin", "pip");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// MyAgent 数据目录
|
|
51
|
+
function getDataDir() {
|
|
52
|
+
return path.join(os.homedir(), ".myagent");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── 解析包目录 ────────────────────────────────────────────
|
|
27
56
|
function resolvePackageDir() {
|
|
28
|
-
// 1. 当前文件所在目录
|
|
29
57
|
let dir = __dirname;
|
|
30
|
-
|
|
31
|
-
// 2. 如果 main.py 不在这里,尝试 npm global prefix
|
|
32
58
|
if (!fs.existsSync(path.join(dir, "main.py"))) {
|
|
33
59
|
try {
|
|
34
60
|
const npmRoot = execSync("npm root -g", {
|
|
35
|
-
encoding: "utf8",
|
|
36
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
61
|
+
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"],
|
|
37
62
|
}).trim();
|
|
38
63
|
const candidate = path.join(npmRoot, "myagent-ai");
|
|
39
|
-
if (fs.existsSync(path.join(candidate, "main.py")))
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
} catch (_) {
|
|
43
|
-
// ignore
|
|
44
|
-
}
|
|
64
|
+
if (fs.existsSync(path.join(candidate, "main.py"))) dir = candidate;
|
|
65
|
+
} catch (_) {}
|
|
45
66
|
}
|
|
46
|
-
|
|
47
|
-
// 3. 向上查找
|
|
48
67
|
if (!fs.existsSync(path.join(dir, "main.py"))) {
|
|
49
68
|
let up = dir;
|
|
50
69
|
for (let i = 0; i < 5; i++) {
|
|
51
70
|
up = path.dirname(up);
|
|
52
|
-
if (fs.existsSync(path.join(up, "main.py"))) {
|
|
53
|
-
dir = up;
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
71
|
+
if (fs.existsSync(path.join(up, "main.py"))) { dir = up; break; }
|
|
56
72
|
}
|
|
57
73
|
}
|
|
58
|
-
|
|
59
74
|
return dir;
|
|
60
75
|
}
|
|
61
76
|
|
|
62
|
-
// ──
|
|
63
|
-
function
|
|
77
|
+
// ── 查找系统 Python(用于创建 venv) ─────────────────────
|
|
78
|
+
function findSystemPython() {
|
|
64
79
|
const candidates = IS_WIN
|
|
65
|
-
? ["python", "python3", "python3.
|
|
66
|
-
: ["python3", "python3.
|
|
80
|
+
? ["python", "python3", "python3.14", "python3.13", "python3.12", "python3.11"]
|
|
81
|
+
: ["python3", "python3.14", "python3.13", "python3.12", "python"];
|
|
67
82
|
|
|
68
83
|
for (const cmd of candidates) {
|
|
69
84
|
try {
|
|
@@ -72,87 +87,109 @@ function findPython() {
|
|
|
72
87
|
["--version"],
|
|
73
88
|
{ encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000 }
|
|
74
89
|
);
|
|
75
|
-
// 解析版本号
|
|
76
90
|
const match = result.match(/Python (\d+)\.(\d+)/);
|
|
77
91
|
if (match) {
|
|
78
92
|
const major = parseInt(match[1], 10);
|
|
79
93
|
const minor = parseInt(match[2], 10);
|
|
80
|
-
if (major >= 3 && minor >= 10)
|
|
81
|
-
return cmd;
|
|
82
|
-
}
|
|
94
|
+
if (major >= 3 && minor >= 10) return cmd;
|
|
83
95
|
}
|
|
84
|
-
} catch (_) {
|
|
85
|
-
|
|
96
|
+
} catch (_) {}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Windows: 搜索常见安装路径
|
|
100
|
+
if (IS_WIN) {
|
|
101
|
+
const localAppData = process.env.LOCALAPPDATA || "";
|
|
102
|
+
const programFiles = process.env.ProgramFiles || "";
|
|
103
|
+
const programFilesX86 = process.env["ProgramFiles(x86)"] || "";
|
|
104
|
+
const searchPaths = [
|
|
105
|
+
path.join(localAppData, "Programs", "Python", "Python314", "python.exe"),
|
|
106
|
+
path.join(localAppData, "Programs", "Python", "Python313", "python.exe"),
|
|
107
|
+
path.join(localAppData, "Programs", "Python", "Python312", "python.exe"),
|
|
108
|
+
path.join(programFiles, "Python314", "python.exe"),
|
|
109
|
+
path.join(programFiles, "Python313", "python.exe"),
|
|
110
|
+
"C:\\Python314\\python.exe", "C:\\Python313\\python.exe",
|
|
111
|
+
];
|
|
112
|
+
for (const pyPath of searchPaths) {
|
|
113
|
+
if (fs.existsSync(pyPath)) return pyPath;
|
|
86
114
|
}
|
|
87
115
|
}
|
|
88
116
|
|
|
89
117
|
return null;
|
|
90
118
|
}
|
|
91
119
|
|
|
92
|
-
// ──
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const result = execFileSync(pyPath, ["--version"], {
|
|
115
|
-
encoding: "utf8",
|
|
116
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
117
|
-
timeout: 5000,
|
|
118
|
-
});
|
|
119
|
-
if (result.includes("Python")) {
|
|
120
|
-
return pyPath;
|
|
121
|
-
}
|
|
122
|
-
} catch (_) {
|
|
123
|
-
// continue
|
|
124
|
-
}
|
|
120
|
+
// ── 虚拟环境管理 ─────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 确保 venv 存在且可用
|
|
124
|
+
* Returns: { venvDir, venvPython, venvPip }
|
|
125
|
+
*/
|
|
126
|
+
function ensureVenv() {
|
|
127
|
+
const venvDir = getVenvDir();
|
|
128
|
+
const venvPython = getVenvPython(venvDir);
|
|
129
|
+
const venvPip = getVenvPip(venvDir);
|
|
130
|
+
|
|
131
|
+
// 检查现有 venv 是否完整
|
|
132
|
+
if (fs.existsSync(venvPython)) {
|
|
133
|
+
try {
|
|
134
|
+
execFileSync(venvPython, ["--version"], {
|
|
135
|
+
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000,
|
|
136
|
+
});
|
|
137
|
+
return { venvDir, venvPython, venvPip, isNew: false };
|
|
138
|
+
} catch (_) {
|
|
139
|
+
// venv 损坏,需要重建
|
|
140
|
+
console.log(" \x1b[33m[!]\x1b[0m 虚拟环境已损坏,正在重建...");
|
|
141
|
+
fs.rmSync(venvDir, { recursive: true, force: true });
|
|
125
142
|
}
|
|
126
143
|
}
|
|
127
144
|
|
|
128
|
-
|
|
129
|
-
|
|
145
|
+
// 创建 venv
|
|
146
|
+
const systemPython = findSystemPython();
|
|
147
|
+
if (!systemPython) {
|
|
148
|
+
console.error("\x1b[31m[✗]\x1b[0m 未找到 Python 3.10+");
|
|
149
|
+
console.error(" 请先安装 Python: https://www.python.org/downloads/");
|
|
150
|
+
console.error(" 安装时请勾选 'Add Python to PATH'");
|
|
151
|
+
console.error("");
|
|
152
|
+
console.error(" 或一键安装:");
|
|
153
|
+
if (IS_WIN) {
|
|
154
|
+
console.error(" powershell -c \"irm https://raw.githubusercontent.com/ctz168/myagent/main/install/install.ps1 | iex\"");
|
|
155
|
+
} else {
|
|
156
|
+
console.error(" curl -fsSL https://raw.githubusercontent.com/ctz168/myagent/main/install/install.sh | bash");
|
|
157
|
+
}
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
130
160
|
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
161
|
+
// 确保 ~/.myagent 目录存在
|
|
162
|
+
const dataDir = getDataDir();
|
|
163
|
+
if (!fs.existsSync(dataDir)) {
|
|
164
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log(" \x1b[36m[*]\x1b[0m 创建独立虚拟环境...");
|
|
168
|
+
console.log(` \x1b[90m[i]\x1b[0m 位置: ${venvDir}`);
|
|
169
|
+
console.log(` \x1b[90m[i]\x1b[0m 系统Python: ${systemPython}`);
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
execFileSync(systemPython, ["-m", "venv", venvDir], {
|
|
173
|
+
encoding: "utf8", stdio: "inherit", timeout: 60000,
|
|
174
|
+
});
|
|
175
|
+
} catch (err) {
|
|
176
|
+
console.error(`\x1b[31m[✗]\x1b[0m 创建虚拟环境失败: ${err.message}`);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 验证创建成功
|
|
181
|
+
if (!fs.existsSync(venvPython)) {
|
|
182
|
+
console.error("\x1b[31m[✗]\x1b[0m 虚拟环境创建后未找到 Python,请检查系统 Python 安装");
|
|
183
|
+
process.exit(1);
|
|
151
184
|
}
|
|
185
|
+
|
|
186
|
+
console.log(" \x1b[32m[✓]\x1b[0m 虚拟环境已创建");
|
|
187
|
+
return { venvDir, venvPython, venvPip, isNew: true };
|
|
152
188
|
}
|
|
153
189
|
|
|
154
|
-
// ──
|
|
155
|
-
|
|
190
|
+
// ── 依赖安装 ─────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
// 核心依赖包(fallback 用,不依赖文件解析)
|
|
156
193
|
const CORE_PACKAGES = [
|
|
157
194
|
"openai>=1.12.0",
|
|
158
195
|
"aiohttp>=3.9.0",
|
|
@@ -163,61 +200,17 @@ const CORE_PACKAGES = [
|
|
|
163
200
|
"psutil>=5.9.0",
|
|
164
201
|
];
|
|
165
202
|
|
|
166
|
-
function
|
|
167
|
-
const args = ["-m", "pip", "install", "--quiet", "--disable-pip-version-check", ...packages];
|
|
168
|
-
return execFileSync(pythonCmd, args, {
|
|
169
|
-
encoding: "utf8",
|
|
170
|
-
stdio: "inherit",
|
|
171
|
-
timeout: 180000,
|
|
172
|
-
cwd,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function pipInstallReqFile(pythonCmd, reqFile, cwd) {
|
|
177
|
-
return execFileSync(pythonCmd, ["-m", "pip", "install", "-r", reqFile, "--disable-pip-version-check"], {
|
|
178
|
-
encoding: "utf8",
|
|
179
|
-
stdio: "inherit",
|
|
180
|
-
timeout: 300000,
|
|
181
|
-
cwd,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function upgradePip(pythonCmd) {
|
|
186
|
-
try {
|
|
187
|
-
console.log(" \x1b[90m[i]\x1b[0m 升级 pip...");
|
|
188
|
-
execFileSync(pythonCmd, ["-m", "pip", "install", "--upgrade", "pip", "--quiet"], {
|
|
189
|
-
encoding: "utf8",
|
|
190
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
191
|
-
timeout: 120000,
|
|
192
|
-
});
|
|
193
|
-
} catch (_) {
|
|
194
|
-
// 升级失败不阻断,继续尝试安装
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// ── Windows 快速依赖检查 ───────────────────────────────────
|
|
199
|
-
function checkDepsWin(pkgDir) {
|
|
203
|
+
function installDeps(venvPython, venvPip, pkgDir) {
|
|
200
204
|
const required = ["openai", "aiohttp", "requests"];
|
|
201
|
-
let
|
|
205
|
+
let missing = [];
|
|
202
206
|
|
|
203
|
-
|
|
204
|
-
console.error("\x1b[31m[✗]\x1b[0m 未找到 Python 3.10+");
|
|
205
|
-
console.error(" 请先安装 Python: https://www.python.org/downloads/");
|
|
206
|
-
console.error(" 安装时请勾选 'Add Python to PATH'");
|
|
207
|
-
console.error("");
|
|
208
|
-
console.error(" 或一键安装: powershell -c \"irm https://raw.githubusercontent.com/ctz168/myagent/main/install/install.ps1 | iex\"");
|
|
209
|
-
process.exit(1);
|
|
210
|
-
}
|
|
207
|
+
console.log(" \x1b[36m[*]\x1b[0m 检查依赖...");
|
|
211
208
|
|
|
212
|
-
// 检查核心依赖
|
|
213
|
-
let missing = [];
|
|
214
209
|
for (const mod of required) {
|
|
215
210
|
try {
|
|
216
|
-
execFileSync(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
{ encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000, cwd: pkgDir }
|
|
220
|
-
);
|
|
211
|
+
execFileSync(venvPython, ["-c", `import ${mod}`], {
|
|
212
|
+
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000,
|
|
213
|
+
});
|
|
221
214
|
console.log(` \x1b[32m[✓]\x1b[0m ${mod}`);
|
|
222
215
|
} catch (_) {
|
|
223
216
|
console.log(` \x1b[33m[!]\x1b[0m ${mod}`);
|
|
@@ -225,44 +218,62 @@ function checkDepsWin(pkgDir) {
|
|
|
225
218
|
}
|
|
226
219
|
}
|
|
227
220
|
|
|
228
|
-
if (missing.length
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
221
|
+
if (missing.length === 0) return;
|
|
222
|
+
|
|
223
|
+
const reqFile = path.join(pkgDir, "requirements.txt");
|
|
224
|
+
|
|
225
|
+
// 策略1: 升级 pip
|
|
226
|
+
try {
|
|
227
|
+
console.log(" \x1b[90m[i]\x1b[0m 升级 pip...");
|
|
228
|
+
execFileSync(venvPython, ["-m", "pip", "install", "--upgrade", "pip", "--quiet"], {
|
|
229
|
+
encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 120000,
|
|
230
|
+
});
|
|
231
|
+
} catch (_) {}
|
|
232
|
+
|
|
233
|
+
// 策略2: pip install -r requirements.txt(venv 内无 PEP668 限制)
|
|
234
|
+
let installed = false;
|
|
235
|
+
if (fs.existsSync(reqFile)) {
|
|
236
|
+
console.log(`\x1b[33m[!]\x1b[0m 正在安装 ${missing.length} 个缺失依赖到虚拟环境...`);
|
|
237
|
+
try {
|
|
238
|
+
execFileSync(venvPython, ["-m", "pip", "install", "-r", reqFile, "--disable-pip-version-check"], {
|
|
239
|
+
encoding: "utf8", stdio: "inherit", timeout: 300000,
|
|
240
|
+
});
|
|
241
|
+
console.log(" \x1b[32m[✓]\x1b[0m 依赖安装完成");
|
|
242
|
+
installed = true;
|
|
243
|
+
} catch (_) {
|
|
244
|
+
console.log(" \x1b[33m[!]\x1b[0m requirements.txt 安装失败,尝试直接安装核心依赖...");
|
|
246
245
|
}
|
|
246
|
+
}
|
|
247
247
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
console.error(` ${pythonCmd} -m pip install ${pkg}`);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
248
|
+
// 策略3: 直接按包名安装
|
|
249
|
+
if (!installed) {
|
|
250
|
+
try {
|
|
251
|
+
execFileSync(venvPython, ["-m", "pip", "install", "--quiet", "--disable-pip-version-check", ...CORE_PACKAGES], {
|
|
252
|
+
encoding: "utf8", stdio: "inherit", timeout: 180000,
|
|
253
|
+
});
|
|
254
|
+
console.log(" \x1b[32m[✓]\x1b[0m 核心依赖安装完成");
|
|
255
|
+
console.log(" \x1b[90m[i]\x1b[0m 其他依赖将在启动时自动安装");
|
|
256
|
+
} catch (_) {
|
|
257
|
+
console.error(" \x1b[31m[✗]\x1b[0m 依赖安装失败,请手动运行:");
|
|
258
|
+
console.error(` ${venvPython} -m pip install -r "${reqFile}"`);
|
|
262
259
|
}
|
|
263
260
|
}
|
|
261
|
+
}
|
|
264
262
|
|
|
265
|
-
|
|
263
|
+
// ── 构建 main.py 参数 ────────────────────────────────────
|
|
264
|
+
function buildArgs(userArgs) {
|
|
265
|
+
const mode = userArgs[0] || "";
|
|
266
|
+
switch (mode) {
|
|
267
|
+
case "cli": return ["main.py"];
|
|
268
|
+
case "web": return ["main.py", "--web"];
|
|
269
|
+
case "tray": return ["main.py", "--tray"];
|
|
270
|
+
case "server": return ["main.py", "--server"];
|
|
271
|
+
case "setup": return ["main.py", "--setup"];
|
|
272
|
+
case "reinstall":
|
|
273
|
+
return []; // 特殊处理
|
|
274
|
+
default:
|
|
275
|
+
return ["main.py", ...userArgs];
|
|
276
|
+
}
|
|
266
277
|
}
|
|
267
278
|
|
|
268
279
|
// ── 主入口 ─────────────────────────────────────────────────
|
|
@@ -277,85 +288,93 @@ function main() {
|
|
|
277
288
|
process.exit(1);
|
|
278
289
|
}
|
|
279
290
|
|
|
291
|
+
// 特殊命令: reinstall
|
|
292
|
+
if (userArgs[0] === "reinstall") {
|
|
293
|
+
const venvDir = getVenvDir();
|
|
294
|
+
if (fs.existsSync(venvDir)) {
|
|
295
|
+
console.log(" \x1b[36m[*]\x1b[0m 删除旧虚拟环境...");
|
|
296
|
+
fs.rmSync(venvDir, { recursive: true, force: true });
|
|
297
|
+
}
|
|
298
|
+
const { venvPython, venvPip } = ensureVenv();
|
|
299
|
+
installDeps(venvPython, venvPip, pkgDir);
|
|
300
|
+
console.log("");
|
|
301
|
+
console.log(" \x1b[32m[✓]\x1b[0m 依赖已全部重新安装到虚拟环境");
|
|
302
|
+
console.log(` \x1b[90m[i]\x1b[0m 虚拟环境: ${venvDir}`);
|
|
303
|
+
console.log("");
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
280
307
|
// 打印 Banner
|
|
281
308
|
console.log("");
|
|
282
309
|
console.log(" \x1b[1m\x1b[36mMyAgent\x1b[0m - 本地桌面端执行型 AI 助手");
|
|
283
310
|
console.log("");
|
|
284
311
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const pythonCmd = checkDepsWin(pkgDir);
|
|
288
|
-
|
|
289
|
-
// 首次运行检测
|
|
290
|
-
const homeDir = process.env.USERPROFILE || process.env.HOME || "";
|
|
291
|
-
const configFile = path.join(homeDir, ".myagent", "config.json");
|
|
292
|
-
if (!fs.existsSync(configFile)) {
|
|
293
|
-
console.log("");
|
|
294
|
-
console.log(" \x1b[90m[i]\x1b[0m 检测到首次运行,MyAgent 将在启动后引导你完成配置。");
|
|
295
|
-
console.log(" 1. 配置 LLM API Key(支持 OpenAI/Anthropic/ModelScope 等)");
|
|
296
|
-
console.log(" 2. 选择默认运行模式");
|
|
297
|
-
console.log("");
|
|
298
|
-
}
|
|
312
|
+
// 确保 venv 存在
|
|
313
|
+
const { venvDir, venvPython, isNew } = ensureVenv();
|
|
299
314
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
tray: "系统托盘模式",
|
|
309
|
-
server: "API 服务模式",
|
|
310
|
-
setup: "配置向导",
|
|
311
|
-
};
|
|
312
|
-
console.log(`\x1b[36m启动 ${modeNames[mode] || mode}...\x1b[0m`);
|
|
313
|
-
} else {
|
|
314
|
-
console.log(`\x1b[36m启动...\x1b[0m`);
|
|
315
|
-
}
|
|
315
|
+
// 检查并安装依赖
|
|
316
|
+
if (isNew) {
|
|
317
|
+
installDeps(venvPython, getVenvPip(venvDir), pkgDir);
|
|
318
|
+
} else {
|
|
319
|
+
// 即使 venv 已存在,也快速检查核心依赖
|
|
320
|
+
const venvPip = getVenvPip(venvDir);
|
|
321
|
+
installDeps(venvPython, venvPip, pkgDir);
|
|
322
|
+
}
|
|
316
323
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
324
|
+
// 首次运行检测
|
|
325
|
+
const configFile = path.join(getDataDir(), "config.json");
|
|
326
|
+
if (!fs.existsSync(configFile)) {
|
|
327
|
+
console.log("");
|
|
328
|
+
console.log(" \x1b[90m[i]\x1b[0m 检测到首次运行,MyAgent 将在启动后引导你完成配置。");
|
|
329
|
+
console.log(" 1. 配置 LLM API Key(支持 OpenAI/Anthropic/ModelScope 等)");
|
|
330
|
+
console.log(" 2. 选择默认运行模式");
|
|
331
|
+
console.log("");
|
|
332
|
+
}
|
|
323
333
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
334
|
+
// 显示环境信息
|
|
335
|
+
console.log(` \x1b[90m[i]\x1b[0m 虚拟环境: ${venvDir}`);
|
|
336
|
+
console.log(` \x1b[90m[i]\x1b[0m Python: ${venvPython}`);
|
|
337
|
+
console.log("");
|
|
328
338
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
339
|
+
// 构建 Python 参数
|
|
340
|
+
const mode = userArgs[0] || "";
|
|
341
|
+
const pyArgs = buildArgs(userArgs);
|
|
342
|
+
|
|
343
|
+
if (mode) {
|
|
344
|
+
const modeNames = {
|
|
345
|
+
web: "Web 管理后台 (http://localhost:8767)",
|
|
346
|
+
cli: "CLI 交互模式",
|
|
347
|
+
tray: "系统托盘模式",
|
|
348
|
+
server: "API 服务模式",
|
|
349
|
+
setup: "配置向导",
|
|
350
|
+
};
|
|
351
|
+
console.log(`\x1b[36m启动 ${modeNames[mode] || mode}...\x1b[0m`);
|
|
332
352
|
} else {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const shell = path.basename(bashPath);
|
|
336
|
-
const shellArgs = shell === "bash" ? [] : ["-l"]; // zsh 需要 -l 加载 profile
|
|
337
|
-
|
|
338
|
-
const startSh = path.join(pkgDir, "start.sh");
|
|
339
|
-
if (!fs.existsSync(startSh)) {
|
|
340
|
-
console.error("\x1b[31m[✗]\x1b[0m 找不到 start.sh");
|
|
341
|
-
process.exit(1);
|
|
342
|
-
}
|
|
353
|
+
console.log(`\x1b[36m启动...\x1b[0m`);
|
|
354
|
+
}
|
|
343
355
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
356
|
+
// 使用 venv 的 Python 启动
|
|
357
|
+
const child = spawn(venvPython, pyArgs, {
|
|
358
|
+
cwd: pkgDir,
|
|
359
|
+
stdio: "inherit",
|
|
360
|
+
env: {
|
|
361
|
+
...process.env,
|
|
362
|
+
// 设置 VIRTUAL_ENV 环境变量,让 Python 程序知道自己运行在 venv 中
|
|
363
|
+
VIRTUAL_ENV: venvDir,
|
|
364
|
+
PATH: IS_WIN
|
|
365
|
+
? `${path.join(venvDir, "Scripts")};${process.env.PATH}`
|
|
366
|
+
: `${path.join(venvDir, "bin")}:${process.env.PATH}`,
|
|
367
|
+
},
|
|
368
|
+
});
|
|
349
369
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
370
|
+
child.on("error", (err) => {
|
|
371
|
+
console.error(`\x1b[31m[✗]\x1b[0m 启动失败: ${err.message}`);
|
|
372
|
+
process.exit(1);
|
|
373
|
+
});
|
|
354
374
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
375
|
+
child.on("exit", (code) => {
|
|
376
|
+
process.exit(code || 0);
|
|
377
|
+
});
|
|
359
378
|
}
|
|
360
379
|
|
|
361
380
|
main();
|
package/start.sh
CHANGED
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# MyAgent Unix/macOS 启动脚本
|
|
3
|
-
# 用法: ./start.sh [cli|web|tray|server|setup]
|
|
3
|
+
# 用法: ./start.sh [cli|web|tray|server|setup|reinstall]
|
|
4
4
|
# 支持 npm 全局安装后从任意目录运行
|
|
5
|
+
#
|
|
6
|
+
# 所有依赖安装到独立虚拟环境 ~/.myagent/venv
|
|
5
7
|
|
|
6
8
|
set -euo pipefail
|
|
7
9
|
|
|
10
|
+
# ── 颜色 ────────────────────────────────────────────
|
|
11
|
+
BOLD='\033[1m'
|
|
12
|
+
ACCENT='\033[36m'
|
|
13
|
+
INFO='\033[90m'
|
|
14
|
+
SUCCESS='\033[32m'
|
|
15
|
+
WARN='\033[33m'
|
|
16
|
+
ERROR='\033[31m'
|
|
17
|
+
NC='\033[0m'
|
|
18
|
+
|
|
19
|
+
info() { echo -e "${INFO}[i]${NC} $*"; }
|
|
20
|
+
success() { echo -e "${SUCCESS}[✓]${NC} $*"; }
|
|
21
|
+
warn() { echo -e "${WARN}[!]${NC} $*"; }
|
|
22
|
+
err() { echo -e "${ERROR}[✗]${NC} $*" >&2; }
|
|
23
|
+
|
|
8
24
|
# ── 解析脚本真实路径(兼容 symlink) ─────────
|
|
9
|
-
# npm 全局安装时 start.sh 会被 symlink 到 /usr/local/bin/myagent-ai
|
|
10
|
-
# 需要解析 symlink 找到实际的包目录
|
|
11
25
|
resolve_script_dir() {
|
|
12
26
|
local src="${BASH_SOURCE[0]}"
|
|
13
|
-
# 循环解析所有 symlink
|
|
14
27
|
while [ -L "$src" ]; do
|
|
15
28
|
local dir="$(cd -P "$(dirname "$src")" && pwd)"
|
|
16
29
|
src="$(readlink "$src")"
|
|
17
|
-
# 如果是相对路径,拼接上目录
|
|
18
30
|
[[ "$src" != /* ]] && src="$dir/$src"
|
|
19
31
|
done
|
|
20
32
|
cd -P "$(dirname "$src")" && pwd
|
|
@@ -23,148 +35,111 @@ resolve_script_dir() {
|
|
|
23
35
|
SCRIPT_DIR="$(resolve_script_dir)"
|
|
24
36
|
PROJECT_DIR="$SCRIPT_DIR"
|
|
25
37
|
|
|
26
|
-
# 如果 main.py 不在脚本目录(npm link 等场景),向上查找
|
|
27
38
|
if [ ! -f "$PROJECT_DIR/main.py" ]; then
|
|
28
|
-
# 尝试 npm global prefix 方式
|
|
29
39
|
NPM_PKG_DIR="$(npm root -g 2>/dev/null)/myagent-ai"
|
|
30
40
|
if [ -f "$NPM_PKG_DIR/main.py" ]; then
|
|
31
41
|
PROJECT_DIR="$NPM_PKG_DIR"
|
|
32
42
|
else
|
|
33
|
-
|
|
43
|
+
err "错误: 找不到 main.py"
|
|
34
44
|
echo " 脚本目录: $SCRIPT_DIR"
|
|
35
45
|
echo " 请确保从正确位置运行,或重新安装: npm install -g myagent-ai"
|
|
36
46
|
exit 1
|
|
37
47
|
fi
|
|
38
48
|
fi
|
|
39
49
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
ERROR='\033[31m'
|
|
46
|
-
NC='\033[0m'
|
|
50
|
+
# ── 虚拟环境 ────────────────────────────────────────
|
|
51
|
+
VENV_DIR="$HOME/.myagent/venv"
|
|
52
|
+
VENV_PY="$VENV_DIR/bin/python"
|
|
53
|
+
VENV_PIP="$VENV_DIR/bin/pip"
|
|
54
|
+
MYAGENT_DIR="$HOME/.myagent"
|
|
47
55
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
else
|
|
56
|
+
# 查找系统 Python
|
|
57
|
+
find_system_python() {
|
|
58
|
+
hash -r 2>/dev/null || true
|
|
59
|
+
for cmd in python3 python3.14 python3.13 python3.12 python; do
|
|
60
|
+
if command -v "$cmd" &>/dev/null; then
|
|
61
|
+
local ver
|
|
62
|
+
ver="$($cmd -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null)" || continue
|
|
63
|
+
local major minor
|
|
64
|
+
major="$(echo "$ver" | cut -d. -f1)"
|
|
65
|
+
minor="$(echo "$ver" | cut -d. -f2)"
|
|
66
|
+
if [ "$major" -ge 3 ] && [ "$minor" -ge 10 ]; then
|
|
67
|
+
echo "$cmd"
|
|
68
|
+
return 0
|
|
69
|
+
fi
|
|
70
|
+
fi
|
|
71
|
+
done
|
|
65
72
|
echo ""
|
|
66
|
-
fi
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
# 确保虚拟环境存在
|
|
76
|
+
ensure_venv() {
|
|
77
|
+
# 检查现有 venv
|
|
78
|
+
if [ -f "$VENV_PY" ]; then
|
|
79
|
+
if "$VENV_PY" --version &>/dev/null; then
|
|
80
|
+
return 0
|
|
81
|
+
fi
|
|
82
|
+
# venv 损坏,重建
|
|
83
|
+
warn "虚拟环境已损坏,正在重建..."
|
|
84
|
+
rm -rf "$VENV_DIR"
|
|
85
|
+
fi
|
|
79
86
|
|
|
80
|
-
#
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
# 需要创建
|
|
88
|
+
local sys_py
|
|
89
|
+
sys_py="$(find_system_python)"
|
|
90
|
+
if [ -z "$sys_py" ]; then
|
|
91
|
+
err "未找到 Python 3.10+"
|
|
92
|
+
echo " macOS: brew install python@3.14"
|
|
93
|
+
echo " Linux: sudo apt install python3.14 python3-pip"
|
|
94
|
+
echo ""
|
|
95
|
+
echo " 一键安装: curl -fsSL https://raw.githubusercontent.com/ctz168/myagent/main/install/install.sh | bash"
|
|
88
96
|
exit 1
|
|
89
97
|
fi
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
$PY -m pip install -r "$req_file" --break-system-packages 2>/dev/null || \
|
|
98
|
-
$PY -m pip install -r "$req_file" 2>/dev/null || \
|
|
99
|
-
{
|
|
100
|
-
warn "pip install 失败 (可能受 PEP668 限制)"
|
|
101
|
-
info "尝试使用 venv 安装..."
|
|
102
|
-
local venv_dir="$HOME/.myagent/venv"
|
|
103
|
-
$PY -m venv "$venv_dir" 2>/dev/null
|
|
104
|
-
if [ -f "$venv_dir/bin/pip" ]; then
|
|
105
|
-
"$venv_dir/bin/pip" install -r "$req_file"
|
|
106
|
-
success "已安装到虚拟环境 $venv_dir"
|
|
107
|
-
info "启动时请使用: $venv_dir/bin/python main.py --web"
|
|
108
|
-
return 0
|
|
109
|
-
fi
|
|
110
|
-
err "所有安装方式均失败"
|
|
111
|
-
info "请手动安装: pip3 install -r $req_file --break-system-packages"
|
|
112
|
-
info "或使用虚拟环境: python3 -m venv venv && source venv/bin/activate && pip install -r $req_file"
|
|
113
|
-
return 1
|
|
114
|
-
}
|
|
98
|
+
|
|
99
|
+
mkdir -p "$MYAGENT_DIR"
|
|
100
|
+
echo -e " ${ACCENT}[*]${NC} 创建独立虚拟环境..."
|
|
101
|
+
info "位置: $VENV_DIR"
|
|
102
|
+
info "系统Python: $sys_py"
|
|
103
|
+
"$sys_py" -m venv "$VENV_DIR"
|
|
104
|
+
success "虚拟环境已创建"
|
|
115
105
|
}
|
|
116
106
|
|
|
117
|
-
#
|
|
118
|
-
|
|
119
|
-
# 刷新命令缓存
|
|
120
|
-
hash -r 2>/dev/null || true
|
|
107
|
+
# 检查并安装依赖
|
|
108
|
+
install_deps() {
|
|
121
109
|
local missing=0
|
|
110
|
+
local req_file="$PROJECT_DIR/requirements.txt"
|
|
111
|
+
|
|
112
|
+
echo -e " ${ACCENT}[*]${NC} 检查依赖..."
|
|
122
113
|
|
|
123
|
-
# 必需依赖
|
|
124
114
|
for mod in openai aiohttp requests; do
|
|
125
|
-
if $
|
|
126
|
-
success "$mod"
|
|
115
|
+
if "$VENV_PY" -c "import $mod" 2>/dev/null; then
|
|
116
|
+
success " $mod"
|
|
127
117
|
else
|
|
128
|
-
warn "$mod
|
|
118
|
+
warn " $mod"
|
|
129
119
|
missing=1
|
|
130
120
|
fi
|
|
131
121
|
done
|
|
132
122
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
success "$mod"
|
|
137
|
-
else
|
|
138
|
-
warn "$mod 未安装"
|
|
139
|
-
missing=1
|
|
140
|
-
fi
|
|
141
|
-
done
|
|
123
|
+
if [ "$missing" -eq 0 ]; then
|
|
124
|
+
return 0
|
|
125
|
+
fi
|
|
142
126
|
|
|
143
|
-
#
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
success "$mod"
|
|
147
|
-
else
|
|
148
|
-
info "$mod 未安装(可选,不影响核心功能)"
|
|
149
|
-
fi
|
|
150
|
-
done
|
|
127
|
+
# 升级 pip
|
|
128
|
+
echo -e " ${INFO}[i]${NC} 升级 pip..."
|
|
129
|
+
"$VENV_PY" -m pip install --upgrade pip --quiet 2>/dev/null || true
|
|
151
130
|
|
|
152
|
-
if [ "$
|
|
153
|
-
echo ""
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
else
|
|
165
|
-
warn "未找到 requirements.txt,请手动安装依赖"
|
|
166
|
-
fi
|
|
167
|
-
echo ""
|
|
131
|
+
if [ -f "$req_file" ]; then
|
|
132
|
+
echo -e " ${WARN}[!]${NC} 正在安装缺失依赖到虚拟环境..."
|
|
133
|
+
"$VENV_PY" -m pip install -r "$req_file" --disable-pip-version-check
|
|
134
|
+
success "依赖安装完成"
|
|
135
|
+
else
|
|
136
|
+
warn "未找到 requirements.txt"
|
|
137
|
+
# fallback: 直接安装核心包
|
|
138
|
+
"$VENV_PY" -m pip install --quiet \
|
|
139
|
+
"openai>=1.12.0" "aiohttp>=3.9.0" "requests>=2.31.0" \
|
|
140
|
+
"duckduckgo-search>=6.0.0" "beautifulsoup4>=4.12.0" \
|
|
141
|
+
"lxml>=5.0.0" "psutil>=5.9.0" --disable-pip-version-check 2>/dev/null || true
|
|
142
|
+
success "核心依赖安装完成"
|
|
168
143
|
fi
|
|
169
144
|
}
|
|
170
145
|
|
|
@@ -183,6 +158,9 @@ check_first_run() {
|
|
|
183
158
|
MODE="${1:-}"
|
|
184
159
|
|
|
185
160
|
if [ -z "$MODE" ]; then
|
|
161
|
+
echo ""
|
|
162
|
+
echo -e " ${BOLD}MyAgent${NC} - 本地桌面端执行型 AI 助手"
|
|
163
|
+
echo ""
|
|
186
164
|
echo "选择运行模式:"
|
|
187
165
|
echo " 1) CLI 交互模式"
|
|
188
166
|
echo " 2) Web 管理后台 (推荐, 端口 8767)"
|
|
@@ -201,42 +179,62 @@ if [ -z "$MODE" ]; then
|
|
|
201
179
|
esac
|
|
202
180
|
fi
|
|
203
181
|
|
|
204
|
-
# 切换到项目目录(确保 main.py 的相对路径正确)
|
|
205
182
|
cd "$PROJECT_DIR"
|
|
206
183
|
|
|
184
|
+
# 特殊命令: reinstall
|
|
185
|
+
if [ "$MODE" = "reinstall" ]; then
|
|
186
|
+
if [ -d "$VENV_DIR" ]; then
|
|
187
|
+
echo -e " ${ACCENT}[*]${NC} 删除旧虚拟环境..."
|
|
188
|
+
rm -rf "$VENV_DIR"
|
|
189
|
+
fi
|
|
190
|
+
ensure_venv
|
|
191
|
+
install_deps
|
|
192
|
+
echo ""
|
|
193
|
+
success "依赖已全部重新安装到虚拟环境"
|
|
194
|
+
info "虚拟环境: $VENV_DIR"
|
|
195
|
+
echo ""
|
|
196
|
+
exit 0
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
# ── 主流程 ─────────────────────────────────────────
|
|
200
|
+
echo ""
|
|
201
|
+
echo -e " ${BOLD}${ACCENT}MyAgent${NC} - 本地桌面端执行型 AI 助手"
|
|
202
|
+
echo ""
|
|
203
|
+
|
|
204
|
+
ensure_venv
|
|
205
|
+
install_deps
|
|
206
|
+
check_first_run
|
|
207
|
+
|
|
208
|
+
info "虚拟环境: $VENV_DIR"
|
|
209
|
+
info "Python: $VENV_PY"
|
|
210
|
+
echo ""
|
|
211
|
+
|
|
207
212
|
case "$MODE" in
|
|
208
213
|
cli)
|
|
209
|
-
check_deps
|
|
210
|
-
check_first_run
|
|
211
214
|
echo -e "${ACCENT}启动 CLI 模式...${NC}"
|
|
212
|
-
exec $
|
|
215
|
+
exec "$VENV_PY" main.py
|
|
213
216
|
;;
|
|
214
217
|
web)
|
|
215
|
-
check_deps
|
|
216
|
-
check_first_run
|
|
217
218
|
echo -e "${ACCENT}启动 Web 管理后台 (http://localhost:8767)...${NC}"
|
|
218
219
|
echo -e "${INFO}按 Ctrl+C 停止${NC}"
|
|
219
220
|
echo ""
|
|
220
|
-
exec $
|
|
221
|
+
exec "$VENV_PY" main.py --web
|
|
221
222
|
;;
|
|
222
223
|
tray)
|
|
223
|
-
check_deps
|
|
224
|
-
check_first_run
|
|
225
224
|
echo -e "${ACCENT}启动系统托盘模式...${NC}"
|
|
226
|
-
exec $
|
|
225
|
+
exec "$VENV_PY" main.py --tray
|
|
227
226
|
;;
|
|
228
227
|
server)
|
|
229
|
-
check_deps
|
|
230
228
|
echo -e "${ACCENT}启动纯 API 服务...${NC}"
|
|
231
|
-
exec $
|
|
229
|
+
exec "$VENV_PY" main.py --server
|
|
232
230
|
;;
|
|
233
231
|
setup)
|
|
234
232
|
echo -e "${ACCENT}启动配置向导...${NC}"
|
|
235
|
-
exec $
|
|
233
|
+
exec "$VENV_PY" main.py --setup
|
|
236
234
|
;;
|
|
237
235
|
*)
|
|
238
236
|
err "未知模式: $MODE"
|
|
239
|
-
echo "可用模式: cli | web | tray | server | setup"
|
|
237
|
+
echo "可用模式: cli | web | tray | server | setup | reinstall"
|
|
240
238
|
exit 1
|
|
241
239
|
;;
|
|
242
240
|
esac
|