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.
@@ -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
- try:
332
- import urllib.request
333
- url = f"{registry}/{pkg_name}/latest"
334
- logger.debug(f"检查 npm registry: {url}")
335
- loop = asyncio.get_running_loop()
336
- data = await loop.run_in_executor(
337
- None,
338
- lambda: urllib.request.urlopen(url, timeout=15).read(),
339
- )
340
- info = json.loads(data)
341
- version = info.get("version", "")
342
- if version and version[0].isdigit():
343
- logger.debug(f"npm registry 最新版本: {version}")
344
- return version
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
- except Exception as e:
347
- logger.debug(f"npm registry HTTP 检查失败: {e}")
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
- # 验证更新是否成功(读取更新后的 package.json 版本)
625
- new_version = get_version()
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
- # 构建 npm install 命令 — 使用 @latest 确保拉取最新版本,避免缓存
753
- npm_cmd = ["npm", "install", "-g", f"{pkg_name}@latest"]
783
+ # [v1.15.52] 先尝试官方源安装,确保拉到最新版本
784
+ # 镜像源可能有同步延迟导致安装旧版本
785
+ registries_to_try = ["https://registry.npmjs.org"]
754
786
  if registry != "https://registry.npmjs.org":
755
- npm_cmd.extend(["--registry", registry])
756
- logger.info(f"使用 npm 镜像: {registry}")
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
- result = await loop.run_in_executor(
759
- None,
760
- lambda: subprocess.run(
761
- npm_cmd,
762
- capture_output=True, text=True, timeout=300,
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
- npm_output = result.stdout + result.stderr
766
- record.details["update_output"] = npm_output[:2000]
767
- logger.info(f"npm update: {npm_output[:500]}")
768
-
769
- if result.returncode != 0:
770
- error_msg = npm_output[-500:] if npm_output else "未知错误"
771
- # 检查是否包含真正的错误(npm 有时返回非零码但有警告信息)
772
- has_real_error = False
773
- for line in error_msg.split("\n"):
774
- stripped = line.strip()
775
- if stripped.startswith("ERR!") or "code E" in stripped:
776
- has_real_error = True
777
- break
778
- if has_real_error:
779
- raise RuntimeError(f"npm install -g {pkg_name} 失败:\n{error_msg}")
780
- else:
781
- logger.warning(f"npm install 有警告但可能已成功: {error_msg}")
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.15.52",
3
+ "version": "1.15.54",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
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
- </div>
245
- <div style="padding:12px;border-top:1px solid var(--border);font-size:12px;color:var(--text2);flex-shrink:0" class="sidebar-footer-text">
246
- <span id="sidebarVersion">v...</span>
247
- <span id="updateBadge" style="display:none;margin-left:6px;color:var(--danger);font-weight:bold;cursor:pointer" onclick="doUpdate()" title="点击更新">[有新版本]</span>
248
- · <a href="#" onclick="api('/api/status').then(r=>showToast('Running: '+r.running,'success'))" style="color:var(--primary)">状态</a>
249
- · <a href="#" onclick="checkUpdate(true)" style="color:var(--primary)">检查更新</a>
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>