myagent-ai 1.47.27 → 1.47.29

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.
Files changed (2) hide show
  1. package/core/vnc_manager.py +118 -299
  2. package/package.json +1 -1
@@ -357,7 +357,8 @@ class VNCManager:
357
357
  # [v1.47.27] 策略B: 尝试 apt-get -f install 修复依赖
358
358
  try:
359
359
  result_fix = subprocess.run(
360
- sudo + ["apt-get", "-f", "install", "-y"],
360
+ sudo + ["apt-get", "-f", "install", "-y",
361
+ "-o", "Acquire::ForceIPv4=true"],
361
362
  capture_output=True, text=True, timeout=30,
362
363
  env=no_service_env,
363
364
  start_new_session=True,
@@ -663,7 +664,7 @@ class VNCManager:
663
664
  # [v1.36.0] 窗口管理器 — 总是安装 icewm(即使已有 fluxbox),因为 icewm 是目标 WM
664
665
  _has_icewm = shutil.which("icewm-session") or shutil.which("icewm")
665
666
  if not _has_icewm:
666
- packages_to_install.extend(["icewm", "icewm-themes"])
667
+ packages_to_install.append("icewm") # [v1.47.28] 移除 icewm-themes — ARM64 无安装候选
667
668
 
668
669
  # [v1.36.0] 文件管理器 — 总是安装 thunar(即使已有 pcmanfm),thunar 在 proot 下更稳定
669
670
  if not shutil.which("thunar"):
@@ -721,7 +722,8 @@ class VNCManager:
721
722
 
722
723
  try:
723
724
  result = subprocess.run(
724
- sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends"] + packages_to_install,
725
+ sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends",
726
+ "-o", "Acquire::ForceIPv4=true"] + packages_to_install,
725
727
  capture_output=True, text=True, timeout=180,
726
728
  start_new_session=True, # [v1.40.0] 进程隔离
727
729
  )
@@ -733,7 +735,8 @@ class VNCManager:
733
735
  self._fix_dpkg_interrupted(sudo)
734
736
  try:
735
737
  result2 = subprocess.run(
736
- sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends"] + packages_to_install,
738
+ sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends",
739
+ "-o", "Acquire::ForceIPv4=true"] + packages_to_install,
737
740
  capture_output=True, text=True, timeout=180,
738
741
  start_new_session=True,
739
742
  )
@@ -797,7 +800,8 @@ class VNCManager:
797
800
  logger.info(f"快速安装核心依赖: {packages}")
798
801
  try:
799
802
  result = subprocess.run(
800
- sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends"] + packages,
803
+ sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends",
804
+ "-o", "Acquire::ForceIPv4=true"] + packages,
801
805
  capture_output=True, text=True, timeout=60,
802
806
  start_new_session=True, # [v1.40.0] 进程隔离
803
807
  )
@@ -809,7 +813,8 @@ class VNCManager:
809
813
  self._fix_dpkg_interrupted(sudo)
810
814
  try:
811
815
  result2 = subprocess.run(
812
- sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends"] + packages,
816
+ sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends",
817
+ "-o", "Acquire::ForceIPv4=true"] + packages,
813
818
  capture_output=True, text=True, timeout=60,
814
819
  start_new_session=True,
815
820
  )
@@ -877,7 +882,8 @@ class VNCManager:
877
882
  try:
878
883
  logger.info(f"尝试单独安装: {pkg}")
879
884
  result = subprocess.run(
880
- sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends", pkg],
885
+ sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends",
886
+ "-o", "Acquire::ForceIPv4=true", pkg],
881
887
  capture_output=True, text=True, timeout=45,
882
888
  start_new_session=True,
883
889
  )
@@ -1088,14 +1094,19 @@ class VNCManager:
1088
1094
  logger.info("=== 后台组件安装完成 ===")
1089
1095
 
1090
1096
  def _install_browser_background(self) -> None:
1091
- """[v1.44.0] 后台安装浏览器。
1097
+ """[v1.47.28] 后台安装浏览器 — 纯 apt 方式,不使用 FTP 下载。
1092
1098
 
1093
1099
  策略(按优先级):
1094
- 1. apt install firefox — 最简单,用户验证可用
1095
- Ubuntu 22.04/24.04 firefox 可能是 .deb 或 snap 包装器
1096
- 如果是 snap 包装器则跳过(proot 不支持 snap)
1097
- 2. apt install chromium — 备选,同样是 snap 包装器则跳过
1098
- 3. 如果以上都失败,不安装(用户可手动 apt install
1100
+ 1. apt update + apt install firefox — 标准方式
1101
+ 强制 IPv4 避免 proot 环境下 IPv6 连接失败
1102
+ 2. 手动添加 mozillateam PPA 源 + apt install firefox
1103
+ 3. apt install firefox-esr
1104
+ 4. apt install chromium
1105
+
1106
+ [v1.47.28] 关键变更:
1107
+ - 删除 FTP 下载 Firefox 回退代码,只用 apt 方式
1108
+ - 所有 apt 命令强制 IPv4(proot 下 IPv6 经常不可达)
1109
+ - 安装前先 apt update 确保包索引最新
1099
1110
 
1100
1111
  安全措施:
1101
1112
  - start_new_session=True 进程隔离,安装崩溃不影响 VNC
@@ -1120,13 +1131,27 @@ class VNCManager:
1120
1131
  logger.info(f"已有浏览器 {existing_browser} 是 snap 包装器,尝试安装真正的浏览器")
1121
1132
 
1122
1133
  sudo = [] if os.getuid() == 0 else ["sudo"]
1134
+ # [v1.47.28] proot 环境下 IPv6 经常不可达,强制 IPv4
1135
+ apt_ipv4_opts = ["-o", "Acquire::ForceIPv4=true"]
1136
+
1137
+ # ── 预备: apt update 确保包索引最新 ──
1138
+ try:
1139
+ logger.info("执行 apt update 确保包索引最新...")
1140
+ subprocess.run(
1141
+ sudo + ["apt-get", "update"] + apt_ipv4_opts,
1142
+ capture_output=True, text=True, timeout=120,
1143
+ env={**os.environ, "DEBIAN_FRONTEND": "noninteractive"},
1144
+ start_new_session=True,
1145
+ )
1146
+ except Exception as e:
1147
+ logger.debug(f"apt update 异常(继续): {e}")
1123
1148
 
1124
1149
  # ── 方案1: apt install firefox ──
1125
1150
  firefox_apt_ok = False
1126
1151
  try:
1127
1152
  logger.info("尝试 apt install firefox(约需 20 分钟)...")
1128
1153
  result = subprocess.run(
1129
- sudo + ["apt-get", "install", "-y", "--no-install-recommends", "firefox"],
1154
+ sudo + ["apt-get", "install", "-y", "--no-install-recommends"] + apt_ipv4_opts + ["firefox"],
1130
1155
  capture_output=True, text=True, timeout=1500, # 25 分钟
1131
1156
  env={**os.environ, "DEBIAN_FRONTEND": "noninteractive"},
1132
1157
  start_new_session=True,
@@ -1146,13 +1171,13 @@ class VNCManager:
1146
1171
  else:
1147
1172
  stderr = (result.stderr or "")[-300:]
1148
1173
  logger.warning(f"apt install firefox 失败: {stderr}")
1149
- # [v1.47.27] dpkg 中断时先修复再重试
1174
+ # dpkg 中断时先修复再重试
1150
1175
  if "dpkg was interrupted" in stderr:
1151
1176
  logger.info("dpkg 中断,尝试修复后重试 firefox 安装...")
1152
1177
  self._fix_dpkg_interrupted(sudo)
1153
1178
  try:
1154
1179
  result2 = subprocess.run(
1155
- sudo + ["apt-get", "install", "-y", "--no-install-recommends", "firefox"],
1180
+ sudo + ["apt-get", "install", "-y", "--no-install-recommends"] + apt_ipv4_opts + ["firefox"],
1156
1181
  capture_output=True, text=True, timeout=1500,
1157
1182
  env={**os.environ, "DEBIAN_FRONTEND": "noninteractive"},
1158
1183
  start_new_session=True,
@@ -1173,12 +1198,62 @@ class VNCManager:
1173
1198
  if firefox_apt_ok:
1174
1199
  return
1175
1200
 
1176
- # ── 方案2: apt install chromium ──
1201
+ # ── 方案2: 手动添加 mozillateam PPA 源 + apt install firefox ──
1202
+ # [v1.47.28] Ubuntu 默认源可能没有 firefox .deb,需要 PPA
1203
+ try:
1204
+ logger.info("尝试手动添加 mozillateam PPA 安装 Firefox...")
1205
+ ppa_installed = self._add_mozillateam_ppa_manual(sudo, "arm64")
1206
+ if ppa_installed:
1207
+ result = subprocess.run(
1208
+ sudo + ["apt-get", "install", "-y", "--fix-broken"] + apt_ipv4_opts + ["firefox"],
1209
+ capture_output=True, text=True, timeout=1500,
1210
+ env={**os.environ, "DEBIAN_FRONTEND": "noninteractive"},
1211
+ start_new_session=True,
1212
+ )
1213
+ firefox_path = shutil.which("firefox")
1214
+ if firefox_path and not self._is_snap_wrapper(firefox_path):
1215
+ logger.info(f"Firefox PPA 安装成功: {firefox_path}")
1216
+ self._create_browser_wrapper(firefox_path)
1217
+ return
1218
+ else:
1219
+ logger.warning("PPA 安装后 Firefox 仍是 snap 或未找到")
1220
+ except subprocess.TimeoutExpired:
1221
+ logger.warning("mozillateam PPA 安装超时")
1222
+ except Exception as e:
1223
+ logger.debug(f"mozillateam PPA 安装异常: {e}")
1224
+
1225
+ # ── 方案3: apt install firefox-esr ──
1226
+ try:
1227
+ logger.info("尝试 apt install firefox-esr...")
1228
+ result = subprocess.run(
1229
+ sudo + ["apt-get", "install", "-y", "--no-install-recommends"] + apt_ipv4_opts + ["firefox-esr"],
1230
+ capture_output=True, text=True, timeout=1500,
1231
+ env={**os.environ, "DEBIAN_FRONTEND": "noninteractive"},
1232
+ start_new_session=True,
1233
+ )
1234
+ if result.returncode == 0:
1235
+ firefox_path = shutil.which("firefox-esr") or shutil.which("firefox")
1236
+ if firefox_path and not self._is_snap_wrapper(firefox_path):
1237
+ logger.info(f"Firefox ESR 安装成功: {firefox_path}")
1238
+ self._create_browser_wrapper(firefox_path)
1239
+ return
1240
+ else:
1241
+ logger.warning("apt install firefox-esr 安装的是 snap 包装器,跳过")
1242
+ else:
1243
+ stderr = (result.stderr or "")[-200:]
1244
+ logger.warning(f"apt install firefox-esr 失败: {stderr}")
1245
+ except subprocess.TimeoutExpired:
1246
+ logger.warning("apt install firefox-esr 超时")
1247
+ except Exception as e:
1248
+ logger.warning(f"apt install firefox-esr 异常: {e}")
1249
+
1250
+ # ── 方案4: apt install chromium ──
1177
1251
  try:
1178
1252
  logger.info("尝试 apt install chromium...")
1179
1253
  result = subprocess.run(
1180
- sudo + ["apt-get", "install", "-y", "--no-install-recommends", "chromium"],
1254
+ sudo + ["apt-get", "install", "-y", "--no-install-recommends"] + apt_ipv4_opts + ["chromium"],
1181
1255
  capture_output=True, text=True, timeout=600,
1256
+ env={**os.environ, "DEBIAN_FRONTEND": "noninteractive"},
1182
1257
  start_new_session=True,
1183
1258
  )
1184
1259
  if result.returncode == 0:
@@ -1197,17 +1272,7 @@ class VNCManager:
1197
1272
  except Exception as e:
1198
1273
  logger.warning(f"apt install chromium 异常: {e}")
1199
1274
 
1200
- # ── 方案3: 旧的 proot 专用安装方式(二进制下载等)──
1201
- # 只在前两种方式都失败时尝试
1202
- if not shutil.which("firefox") and not shutil.which("chromium"):
1203
- logger.info("apt 安装浏览器均失败,尝试 proot 专用安装方式...")
1204
- try:
1205
- browser_binary = self._install_chromium_proot()
1206
- if browser_binary:
1207
- self._create_browser_wrapper(browser_binary)
1208
- except Exception as e:
1209
- logger.warning(f"proot 专用浏览器安装异常: {e}")
1210
-
1275
+ logger.warning("所有 apt 安装浏览器方式均失败,请手动运行: sudo apt install firefox")
1211
1276
  logger.info("浏览器安装流程结束")
1212
1277
 
1213
1278
  def _create_titlebar_button_pixmaps(self, theme_dir: str) -> None:
@@ -3841,7 +3906,7 @@ exec {backup_path} "${{args[@]}}"
3841
3906
  return False
3842
3907
 
3843
3908
  # 安装 icewm(Windows风格) + thunar(文件管理器) + xterm
3844
- packages = ["icewm", "icewm-themes", "thunar", "xterm"]
3909
+ packages = ["icewm", "thunar", "xterm"] # [v1.47.28] 移除 icewm-themes — ARM64 无安装候选
3845
3910
  _apt_cmd = ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends"] + packages
3846
3911
  try:
3847
3912
  # 尝试直接执行(root 环境)
@@ -4243,13 +4308,13 @@ exec {backup_path} "${{args[@]}}"
4243
4308
  try:
4244
4309
  if downloader == "curl":
4245
4310
  result = subprocess.run(
4246
- sudo + ["curl", "-fsSL", "-o", gpg_path, keyserver_url],
4311
+ sudo + ["curl", "-fsSL", "-4", "-o", gpg_path, keyserver_url],
4247
4312
  capture_output=True, text=True, timeout=30,
4248
4313
  start_new_session=True,
4249
4314
  )
4250
4315
  else:
4251
4316
  result = subprocess.run(
4252
- sudo + ["wget", "-q", "-O", gpg_path, keyserver_url],
4317
+ sudo + ["wget", "-q", "-4", "-O", gpg_path, keyserver_url],
4253
4318
  capture_output=True, text=True, timeout=30,
4254
4319
  start_new_session=True,
4255
4320
  )
@@ -4268,7 +4333,7 @@ exec {backup_path} "${{args[@]}}"
4268
4333
  continue
4269
4334
  if downloader == "curl":
4270
4335
  subprocess.run(
4271
- ["curl", "-fsSL", keyserver_url],
4336
+ ["curl", "-fsSL", "-4", keyserver_url],
4272
4337
  capture_output=True, text=True, timeout=30,
4273
4338
  start_new_session=True,
4274
4339
  )
@@ -4276,7 +4341,7 @@ exec {backup_path} "${{args[@]}}"
4276
4341
  if shutil.which("apt-key"):
4277
4342
  result = subprocess.run(
4278
4343
  sudo + ["bash", "-c",
4279
- f"curl -fsSL '{keyserver_url}' | apt-key add -"],
4344
+ f"curl -fsSL -4 '{keyserver_url}' | apt-key add -"],
4280
4345
  capture_output=True, text=True, timeout=30,
4281
4346
  start_new_session=True,
4282
4347
  )
@@ -4346,10 +4411,10 @@ exec {backup_path} "${{args[@]}}"
4346
4411
  except Exception:
4347
4412
  pass
4348
4413
 
4349
- # 5. apt-get update
4414
+ # 5. apt-get update(强制 IPv4)
4350
4415
  logger.info("执行 apt-get update(更新 PPA 源)...")
4351
4416
  result = subprocess.run(
4352
- sudo + ["apt-get", "update"],
4417
+ sudo + ["apt-get", "update", "-o", "Acquire::ForceIPv4=true"],
4353
4418
  capture_output=True, text=True, timeout=120,
4354
4419
  env={**os.environ, "DEBIAN_FRONTEND": "noninteractive"},
4355
4420
  start_new_session=True,
@@ -4370,7 +4435,7 @@ exec {backup_path} "${{args[@]}}"
4370
4435
  logger.warning(f"手动添加 mozillateam PPA 异常: {e}")
4371
4436
  return False
4372
4437
 
4373
- def _is_snap_wrapper(binary_path: str) -> bool:
4438
+ def _is_snap_wrapper(self, binary_path: str) -> bool:
4374
4439
  """[v1.23.50] 检测二进制是否为 snap 包装器脚本。
4375
4440
 
4376
4441
  Ubuntu 24.04+ 的 chromium-browser 只是一个 snap 引导脚本,
@@ -4450,6 +4515,7 @@ exec {backup_path} "${{args[@]}}"
4450
4515
  # 先尝试 chromium 包(Debian 有真 .deb)
4451
4516
  result = subprocess.run(
4452
4517
  sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends",
4518
+ "-o", "Acquire::ForceIPv4=true",
4453
4519
  "chromium", "chromium-common"],
4454
4520
  capture_output=True, text=True, timeout=90,
4455
4521
  start_new_session=True,
@@ -4608,20 +4674,16 @@ exec {backup_path} "${{args[@]}}"
4608
4674
  return None
4609
4675
 
4610
4676
  def _install_firefox_proot(self, sudo: list, arch: str = "arm64") -> Optional[str]:
4611
- """[v1.47.27] 在 proot/Termux 环境安装 Firefox
4677
+ """[v1.47.28] 在 proot/Termux 环境安装 Firefox — 纯 apt 方式。
4612
4678
 
4613
4679
  策略优先级:
4614
4680
  1. 检查已有非 snap 的 Firefox
4615
- 2. apt install firefox(检测 snap 包装器)
4681
+ 2. apt install firefox/firefox-esr(强制 IPv4,检测 snap 包装器)
4616
4682
  3. mozillateam PPA 手动添加源文件(不走 add-apt-repository,proot 兼容)
4617
- 4. apt install firefox-esr
4618
- 5. [最后手段] 下载 Mozilla Firefox 二进制 tar.bz2
4619
4683
 
4620
- [v1.47.27] 关键变更:
4621
- - proot 下也先尝试 apt install firefox,不再无条件跳过
4622
- - 添加 mozillateam PPA 手动源文件方式(直接写 sources.list.d + gpg key)
4623
- 绕过 add-apt-repository(proot 下 futex 崩溃)
4624
- - FTP 下载降级为最后手段
4684
+ [v1.47.28] 关键变更:
4685
+ - 删除 FTP 下载回退代码,只用 apt 方式
4686
+ - 所有 apt 命令强制 IPv4(proot IPv6 经常不可达)
4625
4687
 
4626
4688
  Returns:
4627
4689
  Firefox 二进制路径,失败返回 None
@@ -4633,14 +4695,16 @@ exec {backup_path} "${{args[@]}}"
4633
4695
  logger.info(f"检测到已有非snap Firefox: {existing_path}")
4634
4696
  return existing_path
4635
4697
 
4636
- # ── 策略2: apt install firefox(proot 下也尝试)──
4698
+ # ── 策略2: apt install firefox(proot 下也尝试,强制 IPv4)──
4637
4699
  # [v1.47.27] 不再无条件跳过 apt install,而是先尝试再检测 snap
4700
+ # [v1.47.28] 强制 IPv4,proot 环境下 IPv6 经常不可达
4701
+ apt_ipv4 = ["-o", "Acquire::ForceIPv4=true"]
4638
4702
  # 如果 apt 的 Firefox 是 snap 包装器,才跳到下一策略
4639
4703
  for pkg in ["firefox", "firefox-esr"]:
4640
4704
  try:
4641
4705
  logger.info(f"尝试 apt install {pkg}...")
4642
4706
  result = subprocess.run(
4643
- sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends", pkg],
4707
+ sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends"] + apt_ipv4 + [pkg],
4644
4708
  capture_output=True, text=True, timeout=180,
4645
4709
  env={**os.environ, "DEBIAN_FRONTEND": "noninteractive"},
4646
4710
  start_new_session=True,
@@ -4663,7 +4727,7 @@ exec {backup_path} "${{args[@]}}"
4663
4727
  self._fix_dpkg_interrupted(sudo)
4664
4728
  # 修复后重试
4665
4729
  result2 = subprocess.run(
4666
- sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends", pkg],
4730
+ sudo + ["apt-get", "install", "-y", "--fix-broken", "--no-install-recommends"] + apt_ipv4 + [pkg],
4667
4731
  capture_output=True, text=True, timeout=180,
4668
4732
  env={**os.environ, "DEBIAN_FRONTEND": "noninteractive"},
4669
4733
  start_new_session=True,
@@ -4687,7 +4751,7 @@ exec {backup_path} "${{args[@]}}"
4687
4751
  ppa_installed = self._add_mozillateam_ppa_manual(sudo, arch)
4688
4752
  if ppa_installed:
4689
4753
  result = subprocess.run(
4690
- sudo + ["apt-get", "install", "-y", "--fix-broken", "firefox"],
4754
+ sudo + ["apt-get", "install", "-y", "--fix-broken"] + apt_ipv4 + ["firefox"],
4691
4755
  capture_output=True, text=True, timeout=300,
4692
4756
  env={**os.environ, "DEBIAN_FRONTEND": "noninteractive"},
4693
4757
  start_new_session=True,
@@ -4706,255 +4770,10 @@ exec {backup_path} "${{args[@]}}"
4706
4770
  except Exception as e:
4707
4771
  logger.debug(f"mozillateam PPA 安装异常: {e}")
4708
4772
 
4709
- # ── 策略4: 下载 Mozilla Firefox 二进制 tar.bz2 ──
4710
- # [v1.47.27] 降级为最后手段,仅当 apt PPA 都失败时使用
4711
- try:
4712
- firefox_install_dir = "/opt/firefox"
4713
- firefox_bin = os.path.join(firefox_install_dir, "firefox")
4714
-
4715
- # 如果已存在,直接使用
4716
- if os.path.isfile(firefox_bin) and os.access(firefox_bin, os.X_OK):
4717
- logger.info(f"Mozilla Firefox 二进制版已存在: {firefox_bin}")
4718
- link_path = "/usr/local/bin/firefox"
4719
- if not os.path.exists(link_path):
4720
- try:
4721
- os.symlink(firefox_bin, link_path)
4722
- except Exception:
4723
- pass
4724
- return firefox_bin
4725
-
4726
- import tempfile
4727
- import re
4728
- tar_path = os.path.join(tempfile.gettempdir(), "firefox.tar.bz2")
4729
-
4730
- # [v1.40.0] 动态构建 URL 列表 — 改进版
4731
- ftp_arch = "linux-aarch64" if arch in ("arm64", "aarch64") else "linux-x86_64"
4732
- download_urls = []
4733
-
4734
- # 方法A: 动态查询 Mozilla FTP 获取最新可用版本
4735
- # 改进:先检查版本目录下是否真的有对应架构的文件
4736
- try:
4737
- logger.info("查询 Mozilla FTP 获取最新 Firefox 版本...")
4738
- ftp_index_url = "https://ftp.mozilla.org/pub/firefox/releases/"
4739
- ftp_index = ""
4740
- for dl in ["curl", "wget"]:
4741
- if not shutil.which(dl):
4742
- continue
4743
- try:
4744
- if dl == "curl":
4745
- idx_result = subprocess.run(
4746
- ["curl", "-sL", "--max-time", "15", ftp_index_url],
4747
- capture_output=True, text=True, timeout=20,
4748
- start_new_session=True,
4749
- )
4750
- else:
4751
- idx_result = subprocess.run(
4752
- ["wget", "-q", "-O", "-", ftp_index_url],
4753
- capture_output=True, text=True, timeout=20,
4754
- start_new_session=True,
4755
- )
4756
- if idx_result.returncode == 0 and idx_result.stdout:
4757
- ftp_index = idx_result.stdout
4758
- break
4759
- except Exception:
4760
- pass
4761
-
4762
- if ftp_index:
4763
- # 从 HTML 中提取版本目录名
4764
- # 格式: <a href="137.0.2/">137.0.2/</a>
4765
- # 匹配 ESR 和正式版版本号
4766
- versions = re.findall(r'href="(\d+\.\d+(?:\.\d+)?(?:esr)?)/"', ftp_index)
4767
- # 过滤无效版本(0.x, 太旧的)并排序
4768
- valid_versions = []
4769
- for v in versions:
4770
- try:
4771
- major = int(v.replace("esr", "").split(".")[0])
4772
- if major >= 115: # 115+ 是可用的
4773
- valid_versions.append(v)
4774
- except (ValueError, IndexError):
4775
- pass
4776
-
4777
- # 去重并倒序(最新版本优先)
4778
- seen = set()
4779
- unique_versions = []
4780
- for v in reversed(valid_versions):
4781
- if v not in seen:
4782
- seen.add(v)
4783
- unique_versions.append(v)
4784
-
4785
- # 取最新的几个版本构建 URL(ESR 和正式版都尝试)
4786
- esr_versions = [v for v in unique_versions if "esr" in v]
4787
- regular_versions = [v for v in unique_versions if "esr" not in v]
4788
-
4789
- # 优先 ESR(ARM64 上更稳定),然后正式版
4790
- # [v1.40.0] 只取最新的几个,避免过多无效下载尝试
4791
- for v in (esr_versions[:2] + regular_versions[:2]):
4792
- url = f"https://ftp.mozilla.org/pub/firefox/releases/{v}/{ftp_arch}/en-US/firefox-{v}.tar.bz2"
4793
- download_urls.append(url)
4794
-
4795
- if download_urls:
4796
- logger.info(f"FTP 发现 {len(unique_versions)} 个版本,尝试最新: "
4797
- f"{[v for v in (esr_versions[:2] + regular_versions[:2])]}")
4798
- except Exception as e:
4799
- logger.debug(f"FTP 版本查询异常: {e}")
4800
-
4801
- # 方法B: download.mozilla.org 重定向器
4802
- # [v1.40.0] 注意:aarch64 重定向器有时返回 HTML 错误页而非 tar.bz2
4803
- # 已有魔术字节验证,但优先级放在 FTP 直接链接之后
4804
- if arch in ("arm64", "aarch64"):
4805
- download_urls.extend([
4806
- "https://download.mozilla.org/?product=firefox-esr-latest-ssl&os=linux64-aarch64&lang=en-US",
4807
- "https://download.mozilla.org/?product=firefox-latest-ssl&os=linux64-aarch64&lang=en-US",
4808
- ])
4809
- else:
4810
- download_urls.extend([
4811
- "https://download.mozilla.org/?product=firefox-latest-ssl&os=linux64&lang=en-US",
4812
- "https://download.mozilla.org/?product=firefox-esr-latest-ssl&os=linux64&lang=en-US",
4813
- ])
4814
-
4815
- # 方法C: 硬编码回退(以防动态查询和重定向器都失败)
4816
- if arch in ("arm64", "aarch64"):
4817
- hardcoded = [
4818
- "https://ftp.mozilla.org/pub/firefox/releases/128.8.0esr/linux-aarch64/en-US/firefox-128.8.0esr.tar.bz2",
4819
- "https://ftp.mozilla.org/pub/firefox/releases/115.24.0esr/linux-aarch64/en-US/firefox-115.24.0esr.tar.bz2",
4820
- ]
4821
- else:
4822
- hardcoded = [
4823
- "https://ftp.mozilla.org/pub/firefox/releases/128.8.0esr/linux-x86_64/en-US/firefox-128.8.0esr.tar.bz2",
4824
- ]
4825
- # 只添加尚未在列表中的 URL
4826
- for url in hardcoded:
4827
- if url not in download_urls:
4828
- download_urls.append(url)
4829
-
4830
- download_ok = False
4831
- for url in download_urls:
4832
- if download_ok:
4833
- break
4834
- # 清理之前的下载残留
4835
- if os.path.exists(tar_path):
4836
- try:
4837
- os.remove(tar_path)
4838
- except Exception:
4839
- pass
4840
-
4841
- logger.info(f"尝试下载 Firefox: {url[:80]}...")
4842
-
4843
- for downloader in ["curl", "wget"]:
4844
- if not shutil.which(downloader):
4845
- continue
4846
- try:
4847
- if downloader == "wget":
4848
- dl_result = subprocess.run(
4849
- ["wget", "-q", "-L", "--timeout=30", "--tries=2",
4850
- "-O", tar_path, url],
4851
- capture_output=True, text=True, timeout=180,
4852
- start_new_session=True,
4853
- )
4854
- else:
4855
- dl_result = subprocess.run(
4856
- ["curl", "-sL", "--max-time", "60", "--retry", "2",
4857
- "-o", tar_path, url],
4858
- capture_output=True, text=True, timeout=180,
4859
- start_new_session=True,
4860
- )
4861
- if dl_result.returncode != 0 or not os.path.exists(tar_path):
4862
- logger.debug(f"{downloader} 下载返回 rc={dl_result.returncode if os.path.exists(tar_path) else 'no file'}")
4863
- continue
4864
-
4865
- file_size = os.path.getsize(tar_path)
4866
- # 验证文件是否为有效的 bzip2 格式
4867
- # bzip2 文件以 'BZ' 魔术字节开头
4868
- # download.mozilla.org 的 aarch64 重定向可能返回 HTML 错误页
4869
- is_valid_bz2 = False
4870
- try:
4871
- with open(tar_path, "rb") as f:
4872
- magic = f.read(3)
4873
- is_valid_bz2 = (magic[:2] == b'BZ')
4874
- except Exception:
4875
- pass
4876
-
4877
- if file_size > 5000000 and is_valid_bz2:
4878
- download_ok = True
4879
- break
4880
- else:
4881
- # [v1.40.0] 输出文件头部内容帮助调试
4882
- head_info = ""
4883
- try:
4884
- with open(tar_path, "rb") as f:
4885
- head_info = f.read(200).hex()[:100]
4886
- except Exception:
4887
- pass
4888
- logger.info(
4889
- f"下载的文件无效 (size={file_size}, "
4890
- f"valid_bz2={is_valid_bz2}, head={head_info}, "
4891
- f"url={url[:60]}...)"
4892
- )
4893
- try:
4894
- os.remove(tar_path)
4895
- except Exception:
4896
- pass
4897
- except subprocess.TimeoutExpired:
4898
- logger.warning(f"{downloader} 下载超时")
4899
- except Exception as e:
4900
- logger.debug(f"{downloader} 下载异常: {e}")
4901
-
4902
- if not download_ok:
4903
- logger.warning("Mozilla Firefox 二进制版下载失败,所有 URL 均无效")
4904
- return None
4905
-
4906
- # 解压到 /opt
4907
- logger.info("解压 Firefox 到 /opt/...")
4908
- os.makedirs(firefox_install_dir, exist_ok=True)
4909
- result = subprocess.run(
4910
- ["tar", "xjf", tar_path, "-C", "/opt/"],
4911
- capture_output=True, text=True, timeout=60,
4912
- start_new_session=True,
4913
- )
4914
-
4915
- # 清理
4916
- try:
4917
- os.remove(tar_path)
4918
- except Exception:
4919
- pass
4920
-
4921
- if result.returncode != 0:
4922
- logger.warning(f"Firefox 解压失败: {result.stderr[:200]}")
4923
- return None
4924
-
4925
- # 验证
4926
- if os.path.isfile(firefox_bin) and os.access(firefox_bin, os.X_OK):
4927
- # 创建符号链接
4928
- link_path = "/usr/local/bin/firefox"
4929
- try:
4930
- if os.path.exists(link_path) or os.islink(link_path):
4931
- os.remove(link_path)
4932
- os.symlink(firefox_bin, link_path)
4933
- except Exception:
4934
- pass
4935
- # 安装依赖库
4936
- # [v1.40.0] 使用 start_new_session 防止崩溃传播
4937
- try:
4938
- subprocess.run(
4939
- sudo + ["apt-get", "install", "-y", "--no-install-recommends",
4940
- "libgtk-3-0", "libdbus-glib-1-2", "libxt6",
4941
- "libasound2", "libatk1.0-0", "libatk-bridge2.0-0",
4942
- "libcups2", "libxdamage1", "libxrandr2",
4943
- "libxcomposite1", "libpango-1.0-0",
4944
- "libcairo2", "libx11-xcb1"],
4945
- capture_output=True, text=True, timeout=60,
4946
- start_new_session=True,
4947
- )
4948
- except Exception:
4949
- pass
4950
- logger.info(f"Mozilla Firefox 二进制版安装成功: {firefox_bin}")
4951
- return firefox_bin
4952
- else:
4953
- logger.warning("Firefox 解压后未找到可执行文件")
4954
-
4955
- except Exception as e:
4956
- logger.warning(f"Mozilla Firefox 二进制版安装异常: {e}")
4957
-
4773
+ # [v1.47.28] 已删除 FTP 下载 Firefox 回退代码
4774
+ # 用户要求只用 apt 方式安装,不使用 FTP 下载
4775
+ # 如果以上所有 apt 方式都失败,返回 None
4776
+ logger.warning("所有 apt 方式安装 Firefox 均失败,请手动运行: sudo apt install firefox")
4958
4777
  return None
4959
4778
 
4960
4779
  def _create_browser_wrapper(self, browser_binary: str) -> None:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.47.27",
3
+ "version": "1.47.29",
4
4
  "description": "\u672c\u5730\u684c\u9762\u7aef\u6267\u884c\u578bAI\u52a9\u624b - Open Interpreter \u98ce\u683c | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {