myagent-ai 1.47.23 → 1.47.24

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.
@@ -2124,6 +2124,10 @@ class StealthBrowser:
2124
2124
  message=f"Firefox+VNC 截图已保存",
2125
2125
  data={"path": save_path},
2126
2126
  )
2127
+ logger.warning(
2128
+ f"[_firefox_screenshot] import 截图失败 (rc={result.returncode}): "
2129
+ f"{result.stderr.decode(errors='ignore')[:200]}"
2130
+ )
2127
2131
 
2128
2132
  # 方法2: 使用 xdotool + scrot
2129
2133
  scrot_cmd = shutil.which("scrot")
@@ -2140,6 +2144,10 @@ class StealthBrowser:
2140
2144
  message=f"Firefox+VNC 截图已保存 (scrot)",
2141
2145
  data={"path": save_path},
2142
2146
  )
2147
+ logger.warning(
2148
+ f"[_firefox_screenshot] scrot 截图失败 (rc={result.returncode}): "
2149
+ f"{result.stderr.decode(errors='ignore')[:200]}"
2150
+ )
2143
2151
 
2144
2152
  # 方法3: 使用 xwd + convert
2145
2153
  xwd_cmd = shutil.which("xwd")
@@ -2168,21 +2176,65 @@ class StealthBrowser:
2168
2176
  message=f"Firefox+VNC 截图已保存",
2169
2177
  data={"path": save_path},
2170
2178
  )
2179
+ logger.warning(
2180
+ f"[_firefox_screenshot] xwd+convert 截图失败 (rc={result.returncode}): "
2181
+ f"{result.stderr.decode(errors='ignore')[:200]}"
2182
+ )
2183
+
2184
+ # 方法4: 使用 xdotool + xwd + ffmpeg (proot 兼容)
2185
+ xdotool_cmd = shutil.which("xdotool")
2186
+ ffmpeg_cmd = shutil.which("ffmpeg")
2187
+ if xdotool_cmd and ffmpeg_cmd:
2188
+ xwd_tmp = save_path + ".xwd"
2189
+ # 用 xdotool 获取活动窗口并截图
2190
+ result = subprocess.run(
2191
+ [xwd_cmd or "xwd", "-root", "-display", display, "-out", xwd_tmp],
2192
+ capture_output=True, timeout=10,
2193
+ env=env, start_new_session=True,
2194
+ )
2195
+ if result.returncode == 0 and os.path.isfile(xwd_tmp):
2196
+ # 用 ffmpeg 转换 xwd 到 png
2197
+ result2 = subprocess.run(
2198
+ [ffmpeg_cmd, "-y", "-i", xwd_tmp, save_path],
2199
+ capture_output=True, timeout=10,
2200
+ env=env, start_new_session=True,
2201
+ )
2202
+ try:
2203
+ os.unlink(xwd_tmp)
2204
+ except Exception:
2205
+ pass
2206
+ if result2.returncode == 0 and os.path.isfile(save_path):
2207
+ logger.info(f"Firefox 截图已保存 (xwd+ffmpeg): {save_path}")
2208
+ return SkillResult(
2209
+ success=True,
2210
+ message=f"Firefox+VNC 截图已保存",
2211
+ data={"path": save_path},
2212
+ )
2213
+
2214
+ _available = []
2215
+ if import_cmd: _available.append("import")
2216
+ if scrot_cmd: _available.append("scrot")
2217
+ if xwd_cmd and convert_cmd: _available.append("xwd+convert")
2218
+ if xdotool_cmd and ffmpeg_cmd: _available.append("xdotool+ffmpeg")
2219
+ _tried = ", ".join(_available) if _available else "无"
2171
2220
 
2172
2221
  return SkillResult(
2173
2222
  success=False,
2174
- error="Firefox+VNC 截图失败: 没有可用的截图工具 "
2175
- "(需要 ImageMagick import / scrot / xwd+convert)",
2223
+ error=f"Firefox+VNC 截图失败 (尝试过: {_tried}) "
2224
+ f"需要 ImageMagick import / scrot / xwd+convert",
2176
2225
  )
2177
2226
  except Exception as e:
2178
2227
  return SkillResult(success=False, error=f"Firefox+VNC 截图失败: {e}")
2179
2228
 
2180
- def _firefox_read_sessionstore(self) -> dict:
2229
+ def _firefox_read_sessionstore(self, max_wait: float = 5.0) -> dict:
2181
2230
  """[v1.47.20] 读取 Firefox sessionstore 获取当前标签页 URL/标题。
2182
2231
 
2183
2232
  Firefox 运行时会将当前会话信息写入 sessionstore-backups/recovery.jsonlz4。
2184
2233
  该文件使用 mozLz4 格式(8字节自定义头 + LZ4 压缩数据)。
2185
2234
 
2235
+ Args:
2236
+ max_wait: 最长等待时间(秒),给 Firefox 时间写入 sessionstore
2237
+
2186
2238
  Returns:
2187
2239
  dict: {"url": str, "title": str, "tabs": [{"url": str, "title": str}]}
2188
2240
  """
@@ -2193,30 +2245,56 @@ class StealthBrowser:
2193
2245
  if self._firefox_profile_dir:
2194
2246
  search_dirs.append(self._firefox_profile_dir)
2195
2247
  # 也搜索 Firefox 默认 profile 和 vnc_manager 启动的 profile
2248
+ ff_base = os.path.expanduser("~/.mozilla/firefox")
2196
2249
  for extra in [
2197
- os.path.expanduser("~/.mozilla/firefox/default"),
2198
- os.path.expanduser("~/.mozilla/firefox"),
2250
+ os.path.join(ff_base, "default"),
2251
+ ff_base,
2199
2252
  ]:
2200
2253
  if os.path.isdir(extra) and extra not in search_dirs:
2201
2254
  search_dirs.append(extra)
2255
+ # [v1.47.24] 搜索所有 Firefox profile 子目录(如 xxx.default-release)
2256
+ try:
2257
+ if os.path.isdir(ff_base):
2258
+ for entry in os.listdir(ff_base):
2259
+ entry_path = os.path.join(ff_base, entry)
2260
+ if os.path.isdir(entry_path) and entry_path not in search_dirs:
2261
+ # Firefox profile 目录通常包含 user.js 或 prefs.js
2262
+ if os.path.isfile(os.path.join(entry_path, "prefs.js")) or \
2263
+ os.path.isfile(os.path.join(entry_path, "user.js")):
2264
+ search_dirs.append(entry_path)
2265
+ except Exception:
2266
+ pass
2202
2267
 
2203
- recovery_files = []
2204
- for base_dir in search_dirs:
2205
- ss_dir = os.path.join(base_dir, "sessionstore-backups")
2206
- if os.path.isdir(ss_dir):
2207
- for fname in ("recovery.jsonlz4", "recovery.baklz4",
2208
- "previous.jsonlz4"):
2209
- fpath = os.path.join(ss_dir, fname)
2268
+ # [v1.47.24] 等待 Firefox 写入 sessionstore(刚导航完可能还没写入)
2269
+ _start_time = time.time()
2270
+ while True:
2271
+ recovery_files = []
2272
+ for base_dir in search_dirs:
2273
+ ss_dir = os.path.join(base_dir, "sessionstore-backups")
2274
+ if os.path.isdir(ss_dir):
2275
+ for fname in ("recovery.jsonlz4", "recovery.baklz4",
2276
+ "previous.jsonlz4"):
2277
+ fpath = os.path.join(ss_dir, fname)
2278
+ if os.path.isfile(fpath):
2279
+ recovery_files.append(fpath)
2280
+ # 也检查 base_dir 本身(有些 Firefox 版本)
2281
+ for fname in ("sessionstore.jsonlz4", "sessionstore-backups/recovery.jsonlz4"):
2282
+ fpath = os.path.join(base_dir, fname)
2210
2283
  if os.path.isfile(fpath):
2211
2284
  recovery_files.append(fpath)
2212
- # 也检查 base_dir 本身(有些 Firefox 版本)
2213
- for fname in ("sessionstore.jsonlz4", "sessionstore-backups/recovery.jsonlz4"):
2214
- fpath = os.path.join(base_dir, fname)
2215
- if os.path.isfile(fpath):
2216
- recovery_files.append(fpath)
2285
+
2286
+ # 如果找到了文件,或者超时了,就跳出
2287
+ if recovery_files or (time.time() - _start_time) >= max_wait:
2288
+ break
2289
+
2290
+ # 等 0.5 秒再试
2291
+ time.sleep(0.5)
2217
2292
 
2218
2293
  if not recovery_files:
2219
- logger.debug("[_firefox_read_sessionstore] 未找到 sessionstore 文件")
2294
+ logger.warning(
2295
+ f"[_firefox_read_sessionstore] 未找到 sessionstore 文件 "
2296
+ f"(搜索了 {len(search_dirs)} 个目录: {[d.replace(os.path.expanduser('~'), '~') for d in search_dirs]})"
2297
+ )
2220
2298
  return result_info
2221
2299
 
2222
2300
  # 读取 mozLz4 格式
@@ -2290,9 +2368,19 @@ class StealthBrowser:
2290
2368
  screenshot_path = ""
2291
2369
  if screenshot_result.success and screenshot_result.data:
2292
2370
  screenshot_path = screenshot_result.data.get("path", "")
2371
+ logger.info(f"[_firefox_get_content] 截图成功: {screenshot_path}")
2372
+ else:
2373
+ logger.warning(
2374
+ f"[_firefox_get_content] 截图失败: "
2375
+ f"{screenshot_result.error or '无数据'}"
2376
+ )
2293
2377
 
2294
2378
  # 2. 读取 sessionstore
2295
2379
  session_info = self._firefox_read_sessionstore()
2380
+ logger.info(
2381
+ f"[_firefox_get_content] sessionstore: url={session_info.get('url', '')}, "
2382
+ f"title={session_info.get('title', '')}, tabs={len(session_info.get('tabs', []))}"
2383
+ )
2296
2384
 
2297
2385
  # 3. 组合返回信息
2298
2386
  url = session_info.get("url", "")
@@ -2315,7 +2403,17 @@ class StealthBrowser:
2315
2403
  if screenshot_path:
2316
2404
  content_parts.append(f"截图: {screenshot_path}")
2317
2405
 
2318
- content_text = "\n".join(content_parts) if content_parts else "无法获取页面内容"
2406
+ if not content_parts:
2407
+ # 全部失败 → 至少给截图失败原因
2408
+ fallback_msg = "Firefox+VNC 无法获取页面内容"
2409
+ if not screenshot_result.success:
2410
+ fallback_msg += f" (截图失败: {screenshot_result.error})"
2411
+ if not session_info.get("url"):
2412
+ fallback_msg += " (sessionstore 未读取到 URL)"
2413
+ logger.error(f"[_firefox_get_content] {fallback_msg}")
2414
+ content_text = fallback_msg
2415
+ else:
2416
+ content_text = "\n".join(content_parts)
2319
2417
 
2320
2418
  return SkillResult(
2321
2419
  success=True,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.47.23",
3
+ "version": "1.47.24",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {