yiyan-browser-agent 1.11.0 → 1.11.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/browser.js +52 -40
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yiyan-browser-agent",
3
- "version": "1.11.0",
3
+ "version": "1.11.1",
4
4
  "description": "AI coding agent powered by Yiyan (文心一言) via browser automation (chat.baidu.com) — no API key needed. Performance-optimized. Enhanced with comprehensive security.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/browser.js CHANGED
@@ -320,6 +320,7 @@ class YiyanBrowser {
320
320
  let lastThinkingText = '';
321
321
  let stableCount = 0;
322
322
  let lastStableTime = Date.now();
323
+ let textChangedAt = Date.now(); // 文本最后变化时间
323
324
  let dotCount = 0;
324
325
  let hasCompletionMarker = false;
325
326
 
@@ -373,6 +374,7 @@ class YiyanBrowser {
373
374
  lastText = text;
374
375
  stableCount = 0;
375
376
  lastStableTime = Date.now();
377
+ textChangedAt = Date.now();
376
378
  }
377
379
 
378
380
  // ── 完成标记检测 (chat.baidu.com) ──
@@ -404,10 +406,12 @@ class YiyanBrowser {
404
406
  }
405
407
 
406
408
  // ── 完成判断 ──
407
- // 条件1: 标记出现 + 两者都稳定 2 次
408
- // 条件2(备用): 两者都稳定 3 次(即使没有完成标记)
409
+ // 条件1: 完成标记出现 + 稳定 2 次
410
+ // 条件2: 稳定 3 次(即使没有完成标记)
411
+ // 条件3(兜底): 文本长时间稳定不变(≥5秒),强制认为完成
409
412
  const condition1 = hasCompletionMarker && stableCount >= 2;
410
413
  const condition2 = stableCount >= 3;
414
+ const longStable = (Date.now() - textChangedAt) >= 5000 && text.length > 0;
411
415
 
412
416
  if (condition1 || condition2) {
413
417
  // 检查 _isGenerating 作为最终确认
@@ -420,6 +424,13 @@ class YiyanBrowser {
420
424
  // _isGenerating 说还在生成,重置计数继续等待
421
425
  stableCount = 0;
422
426
  lastStableTime = Date.now();
427
+ } else if (longStable) {
428
+ // 兜底:文本已经 5 秒没变化了,强制完成
429
+ logger.dim('Long stable fallback — text unchanged for 5s, forcing completion');
430
+ await this.page.waitForTimeout(300);
431
+ logger.clearLine();
432
+ logger.success('Response complete (stable timeout)');
433
+ break;
423
434
  }
424
435
 
425
436
  // Progress indicator
@@ -560,33 +571,13 @@ class YiyanBrowser {
560
571
 
561
572
  async _isGenerating() {
562
573
  return await this.page.evaluate(() => {
563
- // ── 1. 检测 typing/generating 指示器 (chat.baidu.com) ──
564
- const typingSelectors = [
565
- '.cosd-markdown-content-typingall',
566
- '.markdown-typing-all',
567
- '[class*="typing"]',
568
- '[class*="generating"]',
569
- '[class*="loading-indicator"]',
570
- 'svg[class*="loading"]',
571
- 'svg[class*="spinner"]',
572
- '[class*="blink"]',
573
- '[class*="cursor-blink"]',
574
- '[class*="pulsing"]',
575
- ];
576
- for (const sel of typingSelectors) {
577
- const el = document.querySelector(sel);
578
- if (el) {
579
- const s = window.getComputedStyle(el);
580
- if (s.display !== 'none' && s.visibility !== 'hidden') return true;
581
- }
582
- }
583
-
584
- // ── 2. 检测停止按钮 ──
574
+ // ── 1. 检测停止按钮(最可靠的生成中信号)──
585
575
  const stopSelectors = [
586
576
  'button[aria-label*="Stop" i]',
587
577
  'button[aria-label*="停止"]',
588
578
  '[class*="stop-gen"]',
589
579
  '[class*="stopGen"]',
580
+ '[class*="stop-btn"]',
590
581
  ];
591
582
  for (const sel of stopSelectors) {
592
583
  const el = document.querySelector(sel);
@@ -596,35 +587,56 @@ class YiyanBrowser {
596
587
  }
597
588
  }
598
589
 
599
- // ── 3. 检测完成标记 如果有回答但没有完成标记 还在生成 ──
590
+ // ── 2. 检测活跃的 CSS 动画(真正的 loading spinner,不是永久类名)──
591
+ // 只检测 svg 动画元素和明确的 loading 指示器
592
+ const activeAnimSelectors = [
593
+ 'svg[class*="loading"]',
594
+ 'svg[class*="spinner"]',
595
+ '[class*="loading-indicator"]',
596
+ '[class*="generating-indicator"]',
597
+ ];
598
+ for (const sel of activeAnimSelectors) {
599
+ const el = document.querySelector(sel);
600
+ if (el) {
601
+ const s = window.getComputedStyle(el);
602
+ if (s.display !== 'none' && s.visibility !== 'hidden') return true;
603
+ }
604
+ }
605
+
606
+ // ── 3. 如果有完成标记 → 肯定已完成 ──
600
607
  const completionMarkers = [
601
608
  '.cos-icon.cos-icon-copy',
602
609
  '.cos-icon-copy',
610
+ '.feedback-hover-show',
603
611
  '.cos-icon.cos-icon-share1',
604
- '.cos-icon-feedback',
612
+ '.cos-icon.cos-icon-feedback',
605
613
  '[class*="copy-btn"]',
606
- '[class*="copyBtn"]',
607
614
  '[aria-label*="Copy" i]',
608
615
  '[aria-label*="复制"]',
609
- '[class*="regenerate"]',
610
- '[class*="retry"]',
611
- '[class*="action-btn"]',
612
- '.feedback-hover-show',
613
616
  ];
614
- let hasCompletionMarker = false;
615
617
  for (const sel of completionMarkers) {
616
- if (document.querySelector(sel)) {
617
- hasCompletionMarker = true;
618
- break;
619
- }
618
+ if (document.querySelector(sel)) return false;
620
619
  }
621
620
 
622
- // 如果有响应内容但没有完成标记 还在生成
621
+ // ── 4. 检查是否有光标闪烁(生成中的光标)──
622
+ // 用 getComputedStyle 检测 animation,而不是类名
623
623
  const answerArea = document.querySelector(
624
- '.ai-entry-block.ai-markdown, .answer-container, .cs-answer-container, .answer-box'
624
+ '.ai-entry-block.ai-markdown, .answer-container, .cs-answer-container'
625
625
  );
626
- if (answerArea && answerArea.innerText && answerArea.innerText.length > 5) {
627
- if (!hasCompletionMarker) {
626
+ if (answerArea) {
627
+ // 查找最后子元素是否有动画光标
628
+ const lastChild = answerArea.lastElementChild;
629
+ if (lastChild) {
630
+ const s = window.getComputedStyle(lastChild);
631
+ // 如果最后一个元素有活跃的动画 → 还在生成
632
+ if (s.animationName && s.animationName !== 'none' &&
633
+ s.display !== 'none' && s.visibility !== 'hidden') {
634
+ return true;
635
+ }
636
+ }
637
+
638
+ // 有回答内容但没有完成标记 → 可能还在生成
639
+ if (answerArea.innerText && answerArea.innerText.length > 5) {
628
640
  return true;
629
641
  }
630
642
  }