myagent-ai 1.15.52 → 1.15.54
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 +98 -48
- package/package.json +1 -1
- package/web/ui/index.html +9 -7
package/core/update_manager.py
CHANGED
|
@@ -327,31 +327,35 @@ class UpdateManager:
|
|
|
327
327
|
|
|
328
328
|
registry = self._get_npm_registry()
|
|
329
329
|
|
|
330
|
+
# [v1.15.52] 优先检查官方源,镜像源可能有同步延迟
|
|
331
|
+
registries = ["https://registry.npmjs.org"]
|
|
332
|
+
if registry != "https://registry.npmjs.org":
|
|
333
|
+
registries.append(registry)
|
|
334
|
+
|
|
330
335
|
# 直接请求 npm registry HTTP API(不依赖 npm 命令行)
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
336
|
+
for reg in registries:
|
|
337
|
+
try:
|
|
338
|
+
import urllib.request
|
|
339
|
+
url = f"{reg}/{pkg_name}/latest"
|
|
340
|
+
logger.debug(f"检查 npm registry: {url}")
|
|
341
|
+
loop = asyncio.get_running_loop()
|
|
342
|
+
data = await loop.run_in_executor(
|
|
343
|
+
None,
|
|
344
|
+
lambda _url=url: urllib.request.urlopen(_url, timeout=15).read(),
|
|
345
|
+
)
|
|
346
|
+
info = json.loads(data)
|
|
347
|
+
version = info.get("version", "")
|
|
348
|
+
if version and version[0].isdigit():
|
|
349
|
+
logger.debug(f"npm registry ({'official' if reg == registries[0] else 'mirror'}) 最新版本: {version}")
|
|
350
|
+
return version
|
|
345
351
|
|
|
346
|
-
|
|
347
|
-
|
|
352
|
+
except Exception as e:
|
|
353
|
+
logger.debug(f"npm registry HTTP 检查失败 ({reg}): {e}")
|
|
348
354
|
|
|
349
355
|
# 回退: 尝试用 npm 命令行检查 (可能 urllib 失败但 npm 命令可用)
|
|
350
356
|
try:
|
|
351
357
|
loop = asyncio.get_running_loop()
|
|
352
|
-
npm_view_cmd = ["npm", "view", pkg_name, "version"]
|
|
353
|
-
if registry != "https://registry.npmjs.org":
|
|
354
|
-
npm_view_cmd.extend(["--registry", registry])
|
|
358
|
+
npm_view_cmd = ["npm", "view", pkg_name, "version", "--registry", "https://registry.npmjs.org"]
|
|
355
359
|
result = await loop.run_in_executor(
|
|
356
360
|
None,
|
|
357
361
|
lambda: subprocess.run(
|
|
@@ -621,8 +625,9 @@ class UpdateManager:
|
|
|
621
625
|
# 源码安装: git pull + pip install
|
|
622
626
|
await self._source_update(record)
|
|
623
627
|
|
|
624
|
-
#
|
|
625
|
-
|
|
628
|
+
# 验证更新是否成功 — npm 安装后需从 npm 全局目录读取新版本
|
|
629
|
+
# 因为当前进程的 PROJECT_ROOT 指向旧版本,get_version() 会读到旧版本号
|
|
630
|
+
new_version = self._read_installed_version() or get_version()
|
|
626
631
|
if new_version == record.from_version and record.to_version != "latest":
|
|
627
632
|
# 版本没变,更新可能未生效
|
|
628
633
|
logger.warning(f"更新后版本仍为 {new_version},可能与预期不符")
|
|
@@ -728,6 +733,32 @@ class UpdateManager:
|
|
|
728
733
|
logger.debug("无法确定安装方式,默认使用 npm 更新路径")
|
|
729
734
|
return True
|
|
730
735
|
|
|
736
|
+
def _read_installed_version(self) -> str:
|
|
737
|
+
"""[v1.15.52] 从 npm 全局安装目录读取实际安装的最新版本号。
|
|
738
|
+
|
|
739
|
+
npm install -g 会将新版本安装到 npm 全局目录(如 /root/.npm/.../node_modules/myagent-ai/),
|
|
740
|
+
但当前进程的 PROJECT_ROOT 仍然指向旧版本的目录,get_version() 会读到旧版本。
|
|
741
|
+
此方法通过 npm root -g 找到全局安装目录,从其中的 package.json 读取真实版本。
|
|
742
|
+
"""
|
|
743
|
+
try:
|
|
744
|
+
result = subprocess.run(
|
|
745
|
+
["npm", "root", "-g"],
|
|
746
|
+
capture_output=True, text=True, timeout=5,
|
|
747
|
+
)
|
|
748
|
+
npm_root = result.stdout.strip()
|
|
749
|
+
if not npm_root:
|
|
750
|
+
return ""
|
|
751
|
+
pkg_json = Path(npm_root) / "myagent-ai" / "package.json"
|
|
752
|
+
if pkg_json.exists():
|
|
753
|
+
data = json.loads(pkg_json.read_text(encoding="utf-8"))
|
|
754
|
+
ver = data.get("version", "")
|
|
755
|
+
if ver:
|
|
756
|
+
logger.debug(f"从 npm 全局目录读取到版本: {ver} (路径: {pkg_json})")
|
|
757
|
+
return ver
|
|
758
|
+
except Exception as e:
|
|
759
|
+
logger.debug(f"读取 npm 全局安装版本失败: {e}")
|
|
760
|
+
return ""
|
|
761
|
+
|
|
731
762
|
def _get_npm_registry(self) -> str:
|
|
732
763
|
"""获取 npm registry 地址,国内用户自动使用镜像"""
|
|
733
764
|
import locale
|
|
@@ -749,36 +780,55 @@ class UpdateManager:
|
|
|
749
780
|
pkg_data = json.loads((PROJECT_ROOT / "package.json").read_text(encoding="utf-8"))
|
|
750
781
|
pkg_name = pkg_data.get("name", "myagent-ai")
|
|
751
782
|
|
|
752
|
-
#
|
|
753
|
-
|
|
783
|
+
# [v1.15.52] 先尝试官方源安装,确保拉到最新版本
|
|
784
|
+
# 镜像源可能有同步延迟导致安装旧版本
|
|
785
|
+
registries_to_try = ["https://registry.npmjs.org"]
|
|
754
786
|
if registry != "https://registry.npmjs.org":
|
|
755
|
-
|
|
756
|
-
|
|
787
|
+
registries_to_try.append(registry) # 镜像作为备用
|
|
788
|
+
|
|
789
|
+
last_error = None
|
|
790
|
+
for reg in registries_to_try:
|
|
791
|
+
is_official = (reg == "https://registry.npmjs.org")
|
|
792
|
+
# 构建 npm install 命令 — 使用 @latest 确保拉取最新版本
|
|
793
|
+
npm_cmd = ["npm", "install", "-g", f"{pkg_name}@latest", "--registry", reg]
|
|
794
|
+
if is_official:
|
|
795
|
+
logger.info(f"使用 npm 官方源安装最新版本: {reg}")
|
|
796
|
+
else:
|
|
797
|
+
logger.info(f"使用 npm 镜像源安装: {reg}")
|
|
757
798
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
799
|
+
result = await loop.run_in_executor(
|
|
800
|
+
None,
|
|
801
|
+
lambda cmd=npm_cmd: subprocess.run(
|
|
802
|
+
cmd,
|
|
803
|
+
capture_output=True, text=True, timeout=300,
|
|
804
|
+
)
|
|
763
805
|
)
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
806
|
+
npm_output = result.stdout + result.stderr
|
|
807
|
+
record.details["update_output"] = npm_output[:2000]
|
|
808
|
+
logger.info(f"npm update ({'official' if is_official else 'mirror'}): {npm_output[:500]}")
|
|
809
|
+
|
|
810
|
+
if result.returncode != 0:
|
|
811
|
+
error_msg = npm_output[-500:] if npm_output else "未知错误"
|
|
812
|
+
# 检查是否包含真正的错误
|
|
813
|
+
has_real_error = False
|
|
814
|
+
for line in error_msg.split("\n"):
|
|
815
|
+
stripped = line.strip()
|
|
816
|
+
if stripped.startswith("ERR!") or "code E" in stripped:
|
|
817
|
+
has_real_error = True
|
|
818
|
+
break
|
|
819
|
+
if has_real_error:
|
|
820
|
+
last_error = error_msg
|
|
821
|
+
logger.warning(f"npm install 通过 {'官方源' if is_official else '镜像源'} 失败,尝试{'镜像源' if is_official else '官方源'}")
|
|
822
|
+
continue
|
|
823
|
+
else:
|
|
824
|
+
logger.warning(f"npm install 有警告但可能已成功: {error_msg}")
|
|
825
|
+
# 安装成功或只有警告,跳出循环
|
|
826
|
+
break
|
|
827
|
+
|
|
828
|
+
if last_error and not (result.returncode == 0 or not any(
|
|
829
|
+
line.strip().startswith("ERR!") for line in (npm_output.split("\n")[-20:])
|
|
830
|
+
)):
|
|
831
|
+
raise RuntimeError(f"npm install -g {pkg_name} 所有源均失败:\n{last_error}")
|
|
782
832
|
|
|
783
833
|
async def _source_update(self, record: UpdateRecord):
|
|
784
834
|
"""执行源码安装更新 (git pull + pip install)"""
|
package/package.json
CHANGED
package/web/ui/index.html
CHANGED
|
@@ -38,7 +38,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;b
|
|
|
38
38
|
.sidebar{width:220px;background:var(--surface);border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0}
|
|
39
39
|
.sidebar .logo{padding:20px;font-size:18px;font-weight:700;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px}
|
|
40
40
|
.sidebar .logo span{color:var(--primary)}
|
|
41
|
-
.nav{flex:1;padding:8px;overflow-y:auto;min-height:0}
|
|
41
|
+
.nav{flex:1;padding:0 8px 8px;overflow-y:auto;min-height:0;display:flex;flex-direction:column}
|
|
42
42
|
.nav-item{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:var(--radius);cursor:pointer;color:var(--text2);font-size:14px;transition:all .15s;margin-bottom:2px}
|
|
43
43
|
.nav-item:hover{background:var(--surface2);color:var(--text)}
|
|
44
44
|
.nav-item.active{background:var(--primary);color:#fff}
|
|
@@ -226,6 +226,7 @@ tr:hover{background:var(--surface2)}
|
|
|
226
226
|
<div class="sidebar-toggle" onclick="toggleSidebar()" id="sidebarToggle">◀</div>
|
|
227
227
|
<div class="logo"><span>🤖</span> <span class="logo-text">MyAgent</span></div>
|
|
228
228
|
<div class="nav">
|
|
229
|
+
<div style="flex:1;overflow-y:auto;padding:8px 0;min-height:0">
|
|
229
230
|
<div class="nav-item active" data-tooltip="仪表盘" onclick="showPage('dashboard')"><span class="icon">📊</span><span class="icon-text">仪表盘</span></div>
|
|
230
231
|
<div class="nav-item" data-tooltip="Agent 管理" onclick="showPage('agents')"><span class="icon">🧠</span><span class="icon-text">Agent 管理</span></div>
|
|
231
232
|
<div class="nav-item" data-tooltip="聊天平台" onclick="showPage('platforms')"><span class="icon">💬</span><span class="icon-text">聊天平台</span></div>
|
|
@@ -241,12 +242,13 @@ tr:hover{background:var(--surface2)}
|
|
|
241
242
|
<div class="nav-item" data-tooltip="工作目录" onclick="showPage('files')"><span class="icon">📁</span><span class="icon-text">工作目录</span></div>
|
|
242
243
|
<div class="nav-item" data-tooltip="查看日志" onclick="showPage('logs')"><span class="icon">📋</span><span class="icon-text">查看日志</span></div>
|
|
243
244
|
<div class="nav-item" data-tooltip="任务记录" onclick="showPage('tasks')"><span class="icon">📌</span><span class="icon-text">任务记录</span></div>
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
245
|
+
</div>
|
|
246
|
+
<div style="padding:10px 12px;border-top:1px solid var(--border);margin-top:4px;font-size:12px;color:var(--text2);flex-shrink:0" class="sidebar-footer-text">
|
|
247
|
+
<span id="sidebarVersion">v...</span>
|
|
248
|
+
<span id="updateBadge" style="display:none;margin-left:6px;color:var(--danger);font-weight:bold;cursor:pointer" onclick="doUpdate()" title="点击更新">[有新版本]</span>
|
|
249
|
+
· <a href="#" onclick="checkUpdate(true)" style="color:var(--primary)">检查更新</a>
|
|
250
|
+
· <a href="#" onclick="api('/api/status').then(r=>showToast('Running: '+r.running,'success'))" style="color:var(--primary)">状态</a>
|
|
251
|
+
</div>
|
|
250
252
|
</div>
|
|
251
253
|
</div>
|
|
252
254
|
<div class="mobile-overlay" id="adminMobileOverlay" onclick="closeMobileSidebar()"></div>
|