yiyan-browser-agent 1.6.3 → 1.6.4
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/package.json +1 -1
- package/src/browser.js +75 -3
package/package.json
CHANGED
package/src/browser.js
CHANGED
|
@@ -293,9 +293,10 @@ class YiyanBrowser {
|
|
|
293
293
|
|
|
294
294
|
if (!appeared) logger.warn('Response may have been delayed — continuing to wait...');
|
|
295
295
|
|
|
296
|
-
// Phase 2: wait for text to stabilise
|
|
296
|
+
// Phase 2: wait for text to stabilise (with multiple checks)
|
|
297
297
|
let lastText = '';
|
|
298
298
|
let stableStart = null;
|
|
299
|
+
let stableCount = 0; // 连续稳定次数计数
|
|
299
300
|
let dotCount = 0;
|
|
300
301
|
|
|
301
302
|
while (Date.now() - start < timeout) {
|
|
@@ -304,11 +305,22 @@ class YiyanBrowser {
|
|
|
304
305
|
if (text !== lastText) {
|
|
305
306
|
lastText = text;
|
|
306
307
|
stableStart = null;
|
|
308
|
+
stableCount = 0; // 内容变化,重置计数
|
|
307
309
|
} else if (text.length > 0) {
|
|
308
310
|
if (!stableStart) stableStart = Date.now();
|
|
309
311
|
else if (Date.now() - stableStart >= stableDelay) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
+
// 增加稳定性计数
|
|
313
|
+
stableCount++;
|
|
314
|
+
|
|
315
|
+
// 需要连续稳定检测 + _isGenerating 确认
|
|
316
|
+
if (stableCount >= 2) { // 连续 2 次稳定检测
|
|
317
|
+
if (!await this._isGenerating()) break;
|
|
318
|
+
stableCount = 0; // _isGenerating 说还在生成,重置
|
|
319
|
+
stableStart = null;
|
|
320
|
+
} else {
|
|
321
|
+
// 第一次稳定,继续等待下一次检测
|
|
322
|
+
stableStart = null;
|
|
323
|
+
}
|
|
312
324
|
}
|
|
313
325
|
}
|
|
314
326
|
|
|
@@ -479,11 +491,14 @@ class YiyanBrowser {
|
|
|
479
491
|
|
|
480
492
|
async _isGenerating() {
|
|
481
493
|
return await this.page.evaluate(() => {
|
|
494
|
+
// ── 1. 检测停止按钮/生成状态 UI ──
|
|
482
495
|
const stopSelectors = [
|
|
483
496
|
'button[aria-label*="Stop" i]',
|
|
497
|
+
'button[aria-label*="停止"]',
|
|
484
498
|
'[class*="stop-gen"]',
|
|
485
499
|
'[class*="stopGen"]',
|
|
486
500
|
'[class*="generating"]',
|
|
501
|
+
'[class*=" Generating"]', // Yiyan 可能的 class
|
|
487
502
|
];
|
|
488
503
|
for (const sel of stopSelectors) {
|
|
489
504
|
const el = document.querySelector(sel);
|
|
@@ -493,6 +508,7 @@ class YiyanBrowser {
|
|
|
493
508
|
}
|
|
494
509
|
}
|
|
495
510
|
|
|
511
|
+
// ── 2. 检测加载动画/typing指示器 ──
|
|
496
512
|
const loaderSelectors = [
|
|
497
513
|
'[class*="typing"]',
|
|
498
514
|
'[class*="loading"]',
|
|
@@ -500,8 +516,12 @@ class YiyanBrowser {
|
|
|
500
516
|
'[class*="blink"]',
|
|
501
517
|
'[class*="cursor"]',
|
|
502
518
|
'[class*="pulsing"]',
|
|
519
|
+
'[class*="thinking"]',
|
|
503
520
|
'svg[class*="loading"]',
|
|
504
521
|
'svg[class*="spinner"]',
|
|
522
|
+
// Yiyan 特有的加载指示
|
|
523
|
+
'.loading-indicator',
|
|
524
|
+
'.generating-indicator',
|
|
505
525
|
];
|
|
506
526
|
for (const sel of loaderSelectors) {
|
|
507
527
|
const el = document.querySelector(sel);
|
|
@@ -511,6 +531,58 @@ class YiyanBrowser {
|
|
|
511
531
|
}
|
|
512
532
|
}
|
|
513
533
|
|
|
534
|
+
// ── 3. 检测输入框状态(生成时通常禁用)──
|
|
535
|
+
const inputSelectors = [
|
|
536
|
+
'[contenteditable="true"]',
|
|
537
|
+
'textarea',
|
|
538
|
+
'[role="textbox"]',
|
|
539
|
+
'.editable',
|
|
540
|
+
];
|
|
541
|
+
for (const sel of inputSelectors) {
|
|
542
|
+
const input = document.querySelector(sel);
|
|
543
|
+
if (input) {
|
|
544
|
+
// 检查是否禁用
|
|
545
|
+
if (input.disabled) return true;
|
|
546
|
+
if (input.getAttribute('aria-disabled') === 'true') return true;
|
|
547
|
+
if (input.getAttribute('disabled') !== null) return true;
|
|
548
|
+
|
|
549
|
+
// 检查是否只读(有时会用 readonly 表示生成中)
|
|
550
|
+
if (input.readOnly || input.getAttribute('readonly') !== null) {
|
|
551
|
+
// 但要排除本来就是 readonly 的输入框
|
|
552
|
+
// 这里暂时不返回 true,需要更多测试
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// ── 4. 检测是否缺少完成标记元素 ──
|
|
558
|
+
// 如果没有复制按钮/重新生成按钮,可能还在生成
|
|
559
|
+
const completionMarkers = [
|
|
560
|
+
'[class*="copy-btn"]',
|
|
561
|
+
'[class*="copyBtn"]',
|
|
562
|
+
'[aria-label*="Copy" i]',
|
|
563
|
+
'[aria-label*="复制"]',
|
|
564
|
+
'[class*="regenerate"]',
|
|
565
|
+
'[class*="retry"]',
|
|
566
|
+
'[class*="action-btn"]', // Yiyan 的操作按钮区域
|
|
567
|
+
];
|
|
568
|
+
let hasCompletionMarker = false;
|
|
569
|
+
for (const sel of completionMarkers) {
|
|
570
|
+
if (document.querySelector(sel)) {
|
|
571
|
+
hasCompletionMarker = true;
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// 如果有响应内容但没有完成标记 → 可能还在生成
|
|
577
|
+
// 这个检测放在后面,避免误判空响应
|
|
578
|
+
const answerArea = document.querySelector('[class*="answer"], [class*="response"], [class*="markdown"]');
|
|
579
|
+
if (answerArea && answerArea.innerText && answerArea.innerText.length > 50) {
|
|
580
|
+
if (!hasCompletionMarker) {
|
|
581
|
+
// 有内容但没有完成按钮,可能还在生成
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
514
586
|
return false;
|
|
515
587
|
});
|
|
516
588
|
}
|