myagent-ai 1.4.2 → 1.5.1
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/update_manager.py +124 -40
- package/core/version.py +1 -1
- package/install/install.ps1 +51 -13
- package/install/install.sh +51 -15
- package/main.py +362 -1
- package/package.json +1 -1
- package/setup.py +1 -1
- package/start.js +106 -13
- package/web/api_server.py +51 -15
- package/web/ui/chat.html +281 -36
- package/web/ui/index.html +361 -17
package/core/update_manager.py
CHANGED
|
@@ -220,10 +220,11 @@ class UpdateManager:
|
|
|
220
220
|
current = get_version()
|
|
221
221
|
|
|
222
222
|
try:
|
|
223
|
-
#
|
|
224
|
-
|
|
225
|
-
if not latest:
|
|
223
|
+
# 尝试多种方式检查更新(npm 安装优先用 npm registry)
|
|
224
|
+
if (PROJECT_ROOT / "package.json").exists():
|
|
226
225
|
latest = await self._check_npm_registry()
|
|
226
|
+
if not latest:
|
|
227
|
+
latest = await self._check_github_tags()
|
|
227
228
|
if not latest:
|
|
228
229
|
latest = await self._check_pypi()
|
|
229
230
|
if not latest:
|
|
@@ -320,25 +321,46 @@ class UpdateManager:
|
|
|
320
321
|
pkg_name = pkg_data.get("name", "")
|
|
321
322
|
if not pkg_name:
|
|
322
323
|
return ""
|
|
324
|
+
except Exception as e:
|
|
325
|
+
logger.debug(f"读取 package.json 失败: {e}")
|
|
326
|
+
return ""
|
|
323
327
|
|
|
324
|
-
|
|
328
|
+
# 直接请求 npm registry HTTP API(不依赖 npm 命令行)
|
|
329
|
+
try:
|
|
330
|
+
import urllib.request
|
|
331
|
+
url = f"https://registry.npmjs.org/{pkg_name}/latest"
|
|
332
|
+
logger.debug(f"检查 npm registry: {url}")
|
|
325
333
|
loop = asyncio.get_running_loop()
|
|
326
|
-
|
|
334
|
+
data = await loop.run_in_executor(
|
|
327
335
|
None,
|
|
328
|
-
lambda:
|
|
329
|
-
["npm", "view", pkg_name, "version", "--json"],
|
|
330
|
-
capture_output=True, text=True, timeout=10,
|
|
331
|
-
cwd=PROJECT_ROOT,
|
|
332
|
-
)
|
|
336
|
+
lambda: urllib.request.urlopen(url, timeout=10).read(),
|
|
333
337
|
)
|
|
334
|
-
|
|
338
|
+
info = json.loads(data)
|
|
339
|
+
version = info.get("version", "")
|
|
335
340
|
if version and version[0].isdigit():
|
|
341
|
+
logger.debug(f"npm registry 最新版本: {version}")
|
|
336
342
|
return version
|
|
337
343
|
|
|
338
|
-
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
339
|
-
pass
|
|
340
344
|
except Exception as e:
|
|
341
|
-
logger.debug(f"npm registry 检查失败: {e}")
|
|
345
|
+
logger.debug(f"npm registry HTTP 检查失败: {e}")
|
|
346
|
+
|
|
347
|
+
# 回退: 尝试用 npm 命令行检查 (可能 urllib 失败但 npm 命令可用)
|
|
348
|
+
try:
|
|
349
|
+
loop = asyncio.get_running_loop()
|
|
350
|
+
result = await loop.run_in_executor(
|
|
351
|
+
None,
|
|
352
|
+
lambda: subprocess.run(
|
|
353
|
+
["npm", "view", pkg_name, "version", "--registry",
|
|
354
|
+
"https://registry.npmjs.org/"],
|
|
355
|
+
capture_output=True, text=True, timeout=15,
|
|
356
|
+
)
|
|
357
|
+
)
|
|
358
|
+
ver = result.stdout.strip()
|
|
359
|
+
if ver and ver[0].isdigit():
|
|
360
|
+
logger.debug(f"npm view 最新版本: {ver}")
|
|
361
|
+
return ver
|
|
362
|
+
except Exception as e2:
|
|
363
|
+
logger.debug(f"npm view 回退检查也失败: {e2}")
|
|
342
364
|
|
|
343
365
|
return ""
|
|
344
366
|
|
|
@@ -562,45 +584,70 @@ class UpdateManager:
|
|
|
562
584
|
|
|
563
585
|
async def _do_full_update(self, record: UpdateRecord):
|
|
564
586
|
"""
|
|
565
|
-
全量更新: git pull +
|
|
587
|
+
全量更新: npm update -g (npm 安装) 或 git pull + pip install (源码安装) + 优雅重启。
|
|
566
588
|
使用 os.execv 替换当前进程,保持 PID 不变。
|
|
567
589
|
"""
|
|
568
590
|
self._status = UpdateStatus.UPDATING
|
|
569
591
|
logger.info("开始全量更新...")
|
|
570
592
|
|
|
571
|
-
|
|
572
|
-
result = subprocess.run(
|
|
573
|
-
["git", "pull"],
|
|
574
|
-
capture_output=True, text=True, timeout=30,
|
|
575
|
-
cwd=PROJECT_ROOT,
|
|
576
|
-
)
|
|
577
|
-
pull_output = result.stdout + result.stderr
|
|
578
|
-
logger.info(f"git pull: {pull_output[:500]}")
|
|
593
|
+
is_npm_install = (PROJECT_ROOT / "package.json").exists()
|
|
579
594
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
595
|
+
if is_npm_install:
|
|
596
|
+
# npm 安装: npm install -g myagent-ai
|
|
597
|
+
self._status = UpdateStatus.DOWNLOADING
|
|
598
|
+
loop = asyncio.get_running_loop()
|
|
599
|
+
result = await loop.run_in_executor(
|
|
600
|
+
None,
|
|
601
|
+
lambda: subprocess.run(
|
|
602
|
+
["npm", "install", "-g", "myagent-ai"],
|
|
603
|
+
capture_output=True, text=True, timeout=180,
|
|
604
|
+
)
|
|
605
|
+
)
|
|
606
|
+
npm_output = result.stdout + result.stderr
|
|
607
|
+
record.details["update_output"] = npm_output[:2000]
|
|
608
|
+
logger.info(f"npm update: {npm_output[:500]}")
|
|
609
|
+
|
|
610
|
+
if result.returncode != 0:
|
|
611
|
+
# npm 返回非零码可能是权限问题或网络问题
|
|
612
|
+
error_msg = npm_output[-500:] if npm_output else "未知错误"
|
|
613
|
+
# 尝试判断是否真的失败了(npm 有时返回非零但实际安装成功)
|
|
614
|
+
if "ERR!" in error_msg or "error" in error_msg.lower():
|
|
615
|
+
logger.warning(f"npm install 可能失败: {error_msg}")
|
|
616
|
+
# 不直接抛异常,让重启后 start.js 的依赖检查来兜底
|
|
617
|
+
else:
|
|
618
|
+
logger.warning(f"npm install 有警告但不影响: {error_msg}")
|
|
619
|
+
else:
|
|
620
|
+
# 源码安装: git pull + pip install
|
|
621
|
+
result = subprocess.run(
|
|
622
|
+
["git", "pull"],
|
|
623
|
+
capture_output=True, text=True, timeout=30,
|
|
589
624
|
cwd=PROJECT_ROOT,
|
|
590
625
|
)
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
record.details.update(pull_output=pull_output[:500], pip_output=pip_output[:1000])
|
|
626
|
+
pull_output = result.stdout + result.stderr
|
|
627
|
+
logger.info(f"git pull: {pull_output[:500]}")
|
|
594
628
|
|
|
595
|
-
|
|
596
|
-
|
|
629
|
+
self._status = UpdateStatus.DOWNLOADING
|
|
630
|
+
loop = asyncio.get_running_loop()
|
|
631
|
+
result = await loop.run_in_executor(
|
|
632
|
+
None,
|
|
633
|
+
lambda: subprocess.run(
|
|
634
|
+
[sys.executable, "-m", "pip", "install", "-e", ".",
|
|
635
|
+
"--quiet"],
|
|
636
|
+
capture_output=True, text=True, timeout=120,
|
|
637
|
+
cwd=PROJECT_ROOT,
|
|
638
|
+
)
|
|
639
|
+
)
|
|
640
|
+
pip_output = result.stdout + result.stderr
|
|
641
|
+
record.details["update_output"] = pip_output[:1000]
|
|
642
|
+
if result.returncode != 0:
|
|
643
|
+
logger.warning(f"pip install 有警告: {pip_output[-300:]}")
|
|
597
644
|
|
|
598
645
|
# Step 3: 保存更新完成标记(用于重启后验证)
|
|
599
646
|
record.status = "restarting"
|
|
600
647
|
self._save_record(record)
|
|
601
648
|
logger.info("全量更新完成,准备优雅重启...")
|
|
602
649
|
|
|
603
|
-
# Step 4: 优雅重启
|
|
650
|
+
# Step 4: 优雅重启
|
|
604
651
|
self._status = UpdateStatus.RESTARTING
|
|
605
652
|
await asyncio.sleep(0.5) # 给日志一点时间写入
|
|
606
653
|
|
|
@@ -610,8 +657,45 @@ class UpdateManager:
|
|
|
610
657
|
env["MYAGENT_UPDATED_FROM"] = record.from_version
|
|
611
658
|
env["MYAGENT_UPDATED_TO"] = record.to_version
|
|
612
659
|
|
|
613
|
-
|
|
614
|
-
|
|
660
|
+
if is_npm_install:
|
|
661
|
+
# npm 安装: 通过 start.js (Node.js) 重启,确保 venv 和依赖正确处理
|
|
662
|
+
# start.js 会自动创建/更新 venv 并安装依赖
|
|
663
|
+
start_js = PROJECT_ROOT / "start.js"
|
|
664
|
+
if start_js.exists():
|
|
665
|
+
import shutil
|
|
666
|
+
node_cmd = shutil.which("node")
|
|
667
|
+
if node_cmd:
|
|
668
|
+
# 提取原始参数 (去掉 main.py,还原 start.js 的参数格式)
|
|
669
|
+
original_args = []
|
|
670
|
+
skip_next = False
|
|
671
|
+
for arg in sys.argv[1:]:
|
|
672
|
+
if skip_next:
|
|
673
|
+
skip_next = False
|
|
674
|
+
continue
|
|
675
|
+
if arg == "--web":
|
|
676
|
+
original_args.append("web")
|
|
677
|
+
elif arg == "--tray":
|
|
678
|
+
original_args.append("tray")
|
|
679
|
+
elif arg == "--cli":
|
|
680
|
+
original_args.append("cli")
|
|
681
|
+
elif arg == "--server":
|
|
682
|
+
original_args.append("server")
|
|
683
|
+
elif arg == "--setup":
|
|
684
|
+
original_args.append("setup")
|
|
685
|
+
elif arg.startswith("--port="):
|
|
686
|
+
original_args.append(arg.replace("--port=", ""))
|
|
687
|
+
elif arg.startswith("--"):
|
|
688
|
+
skip_next = True # 跳过 --arg value 形式的参数
|
|
689
|
+
logger.info(f"通过 start.js 重启: node {start_js} {' '.join(original_args)}")
|
|
690
|
+
os.execv(node_cmd, [node_cmd, str(start_js)] + original_args)
|
|
691
|
+
else:
|
|
692
|
+
logger.warning("未找到 node 命令,回退到直接 Python 重启")
|
|
693
|
+
|
|
694
|
+
# 回退: 直接用 Python 重启(源码安装或 node 不可用)
|
|
695
|
+
# 使用绝对路径避免 cwd 变化导致找不到 main.py
|
|
696
|
+
abs_main = str((PROJECT_ROOT / "main.py").resolve())
|
|
697
|
+
logger.info(f"通过 Python 直接重启: {sys.executable} {abs_main}")
|
|
698
|
+
os.execv(sys.executable, [sys.executable, abs_main] + sys.argv[1:])
|
|
615
699
|
|
|
616
700
|
async def _do_config_update(self, record: UpdateRecord):
|
|
617
701
|
"""仅配置热重载(利用现有 ConfigManager.reload())"""
|
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.5.1"
|
|
13
13
|
|
|
14
14
|
# Allow running scripts for the current process
|
|
15
15
|
if ($PSVersionTable.PSVersion.Major -ge 5) {
|
|
@@ -248,21 +248,59 @@ function Install-PythonDeps {
|
|
|
248
248
|
Write-Host "[i] venv will be used automatically on startup" -ForegroundColor Gray
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
# ── Post-install
|
|
251
|
+
# ── Post-install: 直接启动 ──────────────────────────
|
|
252
252
|
function Show-Guide {
|
|
253
253
|
Write-Host ""
|
|
254
|
-
Write-Host "
|
|
254
|
+
Write-Host " 安装完成!正在启动 MyAgent..." -ForegroundColor Green
|
|
255
255
|
Write-Host ""
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
256
|
+
|
|
257
|
+
# 刷新 PATH(刚安装的 npm 全局命令可能在新的 PATH 中)
|
|
258
|
+
Refresh-Path
|
|
259
|
+
|
|
260
|
+
# npm 全局命令实际是 .cmd 文件,必须通过 cmd /c 执行
|
|
261
|
+
$npmBinDir = ""
|
|
262
|
+
try {
|
|
263
|
+
$npmBinDir = (npm bin -g 2>$null).Trim()
|
|
264
|
+
} catch {}
|
|
265
|
+
|
|
266
|
+
$cmdPath = ""
|
|
267
|
+
if ($npmBinDir -and (Test-Path "$npmBinDir\myagent-ai.cmd")) {
|
|
268
|
+
$cmdPath = "$npmBinDir\myagent-ai.cmd"
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if ($cmdPath) {
|
|
272
|
+
Write-Host " [i] 启动路径: $cmdPath" -ForegroundColor Gray
|
|
273
|
+
Start-Process cmd -ArgumentList "/c", "`"$cmdPath`" web" -WindowStyle Minimized
|
|
274
|
+
} else {
|
|
275
|
+
Write-Host " [i] 回退到 PATH 搜索..." -ForegroundColor Gray
|
|
276
|
+
Start-Process cmd -ArgumentList "/c", "myagent-ai web" -WindowStyle Minimized
|
|
277
|
+
}
|
|
278
|
+
Write-Host " [OK] MyAgent 已在后台启动" -ForegroundColor Green
|
|
279
|
+
|
|
280
|
+
# 由安装脚本自身轮询服务并打开浏览器(比 start.js 更可靠)
|
|
281
|
+
Write-Host " [i] 等待服务启动..." -ForegroundColor Gray
|
|
282
|
+
$url = "http://127.0.0.1:8767"
|
|
283
|
+
$maxWait = 120 # 最多等 120 秒
|
|
284
|
+
$waited = 0
|
|
285
|
+
while ($waited -lt $maxWait) {
|
|
286
|
+
Start-Sleep -Seconds 2
|
|
287
|
+
$waited += 2
|
|
288
|
+
try {
|
|
289
|
+
$null = Invoke-WebRequest -Uri "$url/api/status" -UseBasicParsing -TimeoutSec 3 -ErrorAction Stop
|
|
290
|
+
Write-Host " [OK] 服务已就绪,正在打开浏览器..." -ForegroundColor Green
|
|
291
|
+
Start-Process $url
|
|
292
|
+
Write-Host " [i] 如果没有自动打开,请访问: $url" -ForegroundColor Cyan
|
|
293
|
+
Write-Host ""
|
|
294
|
+
return
|
|
295
|
+
} catch {
|
|
296
|
+
# 服务还没启动,继续等待
|
|
297
|
+
if ($waited % 10 -eq 0) {
|
|
298
|
+
Write-Host " [i] 等待中... ($waited 秒)" -ForegroundColor Gray
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
Write-Host " [!] 等待超时" -ForegroundColor Yellow
|
|
303
|
+
Write-Host " [i] 服务可能仍在启动中,请稍等片刻后访问: $url" -ForegroundColor Cyan
|
|
266
304
|
Write-Host ""
|
|
267
305
|
}
|
|
268
306
|
|
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.5.1"
|
|
25
25
|
|
|
26
26
|
for arg in "$@"; do
|
|
27
27
|
case "$arg" in
|
|
@@ -382,23 +382,59 @@ install_python_deps() {
|
|
|
382
382
|
info "启动时自动使用 venv,无需手动激活"
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
-
# ──
|
|
385
|
+
# ── 安装完成:直接启动 ───────────────────────────────
|
|
386
386
|
post_install() {
|
|
387
387
|
echo ""
|
|
388
|
-
echo -e " ${BOLD}${SUCCESS}
|
|
388
|
+
echo -e " ${BOLD}${SUCCESS}安装完成!正在启动 MyAgent...${NC}"
|
|
389
389
|
echo ""
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
390
|
+
|
|
391
|
+
# 直接启动 Web 模式,浏览器会自动打开配置向导
|
|
392
|
+
# 使用 nohup 在后台运行,不阻塞终端
|
|
393
|
+
if command -v myagent-ai &>/dev/null; then
|
|
394
|
+
nohup myagent-ai web > /dev/null 2>&1 &
|
|
395
|
+
echo -e " ${SUCCESS}[✓]${NC} MyAgent 已在后台启动"
|
|
396
|
+
else
|
|
397
|
+
# myagent-ai 不在 PATH 中,尝试通过 npm 全局目录启动
|
|
398
|
+
local npm_bin
|
|
399
|
+
npm_bin="$(npm bin -g 2>/dev/null)/myagent-ai"
|
|
400
|
+
if [ -x "$npm_bin" ]; then
|
|
401
|
+
nohup "$npm_bin" web > /dev/null 2>&1 &
|
|
402
|
+
echo -e " ${SUCCESS}[✓]${NC} MyAgent 已在后台启动"
|
|
403
|
+
else
|
|
404
|
+
echo -e " ${WARN}[!]${NC} 启动命令未找到,请重新打开终端后运行:"
|
|
405
|
+
echo -e " ${ACCENT}myagent-ai web${NC}"
|
|
406
|
+
echo ""
|
|
407
|
+
return
|
|
408
|
+
fi
|
|
409
|
+
fi
|
|
410
|
+
|
|
411
|
+
# 由安装脚本自身轮询服务并打开浏览器(比 start.js 更可靠)
|
|
412
|
+
local url="http://127.0.0.1:8767"
|
|
413
|
+
local max_wait=120
|
|
414
|
+
local waited=0
|
|
415
|
+
|
|
416
|
+
echo -e " ${INFO}[i]${NC} 等待服务启动..."
|
|
417
|
+
while [ $waited -lt $max_wait ]; do
|
|
418
|
+
sleep 2
|
|
419
|
+
waited=$((waited + 2))
|
|
420
|
+
if curl -sf "$url/api/status" > /dev/null 2>&1; then
|
|
421
|
+
echo -e " ${SUCCESS}[✓]${NC} 服务已就绪,正在打开浏览器..."
|
|
422
|
+
# 跨平台打开浏览器
|
|
423
|
+
if [ "$OS" = "macos" ]; then
|
|
424
|
+
open "$url" 2>/dev/null || true
|
|
425
|
+
elif [ "$OS" = "linux" ]; then
|
|
426
|
+
xdg-open "$url" 2>/dev/null || sensible-browser "$url" 2>/dev/null || true
|
|
427
|
+
fi
|
|
428
|
+
echo -e " ${INFO}[i]${NC} 如果没有自动打开,请访问: ${ACCENT}$url${NC}"
|
|
429
|
+
echo ""
|
|
430
|
+
return
|
|
431
|
+
fi
|
|
432
|
+
if [ $((waited % 10)) -eq 0 ]; then
|
|
433
|
+
echo -e " ${INFO}[i]${NC} 等待中... ($waited 秒)"
|
|
434
|
+
fi
|
|
435
|
+
done
|
|
436
|
+
echo -e " ${WARN}[!]${NC} 等待超时"
|
|
437
|
+
echo -e " ${INFO}[i]${NC} 服务可能仍在启动中,请稍等片刻后访问: ${ACCENT}$url${NC}"
|
|
402
438
|
echo ""
|
|
403
439
|
}
|
|
404
440
|
|