yiyan-browser-agent 1.0.1 → 1.1.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.
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +55 -79
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +41 -70
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
-
}) : x)(function(x) {
|
|
5
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
-
});
|
|
8
2
|
|
|
9
3
|
// src/agent.ts
|
|
10
4
|
import path3 from "path";
|
|
@@ -461,7 +455,7 @@ async function waitForReply(page, timeout, verbose = false, headful = false) {
|
|
|
461
455
|
} else {
|
|
462
456
|
throw new YiyanAgentError(
|
|
463
457
|
"CAPTCHA",
|
|
464
|
-
'Yiyan detected automation and triggered a captcha. Use
|
|
458
|
+
'Yiyan detected automation and triggered a captcha. Use --headful flag to manually solve it, or run "login" first.'
|
|
465
459
|
);
|
|
466
460
|
}
|
|
467
461
|
} else if (headful) {
|
|
@@ -493,7 +487,10 @@ async function waitForReply(page, timeout, verbose = false, headful = false) {
|
|
|
493
487
|
await page.waitForTimeout(1500);
|
|
494
488
|
}
|
|
495
489
|
} else {
|
|
496
|
-
|
|
490
|
+
throw new YiyanAgentError(
|
|
491
|
+
"TIMEOUT",
|
|
492
|
+
'AI reply timeout in headless mode. Try running "yiyan-agent login" first, or use --headful flag.'
|
|
493
|
+
);
|
|
497
494
|
}
|
|
498
495
|
}
|
|
499
496
|
await page.waitForTimeout(2e3);
|
|
@@ -717,40 +714,30 @@ var YiyanAgent = class {
|
|
|
717
714
|
/**
|
|
718
715
|
* 发送问题并获取答案
|
|
719
716
|
* @param question 问题文本
|
|
720
|
-
* @param headful 是否使用有头浏览器(可见窗口),默认为
|
|
717
|
+
* @param headful 是否使用有头浏览器(可见窗口),默认为 false(优先 headless,登录后无需弹窗)
|
|
718
|
+
*
|
|
719
|
+
* 策略:先尝试 headless(不弹窗),如果失败(验证码/未登录)自动回退到有头模式
|
|
721
720
|
*/
|
|
722
|
-
async ask(question, headful =
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
if (error instanceof YiyanAgentError) {
|
|
733
|
-
lastError = error;
|
|
734
|
-
if (lastError.type === "CAPTCHA") {
|
|
735
|
-
throw lastError;
|
|
736
|
-
}
|
|
737
|
-
} else {
|
|
738
|
-
lastError = new YiyanAgentError(
|
|
739
|
-
"NETWORK",
|
|
740
|
-
error instanceof Error ? error.message : String(error)
|
|
741
|
-
);
|
|
742
|
-
}
|
|
743
|
-
if (attempt < this.options.retryCount - 1) {
|
|
744
|
-
await this.sleep(2e3);
|
|
721
|
+
async ask(question, headful = false) {
|
|
722
|
+
if (headful) {
|
|
723
|
+
return this.executeAsk(question, true);
|
|
724
|
+
}
|
|
725
|
+
try {
|
|
726
|
+
return await this.executeAsk(question, false);
|
|
727
|
+
} catch (error) {
|
|
728
|
+
if (error instanceof YiyanAgentError && (error.type === "CAPTCHA" || error.type === "TIMEOUT")) {
|
|
729
|
+
if (this.verbose) {
|
|
730
|
+
process.stderr.write("[yiyan-agent] headless \u6A21\u5F0F\u5931\u8D25\uFF0C\u81EA\u52A8\u5207\u6362\u5230\u6709\u5934\u6A21\u5F0F...\n");
|
|
745
731
|
}
|
|
732
|
+
return this.executeAsk(question, true);
|
|
746
733
|
}
|
|
734
|
+
throw error;
|
|
747
735
|
}
|
|
748
|
-
throw lastError || new YiyanAgentError("TIMEOUT", "Unknown error");
|
|
749
736
|
}
|
|
750
737
|
/**
|
|
751
738
|
* 执行单次问答
|
|
752
739
|
*/
|
|
753
|
-
async executeAsk(question, headful
|
|
740
|
+
async executeAsk(question, headful) {
|
|
754
741
|
const platform = os2.platform();
|
|
755
742
|
const chromePath = this.options.chromePath || getChromeExecutablePath(platform);
|
|
756
743
|
if (!chromePath) {
|
|
@@ -760,24 +747,28 @@ var YiyanAgent = class {
|
|
|
760
747
|
);
|
|
761
748
|
}
|
|
762
749
|
const profilePath = path3.join(this.profileDir, "chrome-profile");
|
|
763
|
-
const useHeadful = headful !== false;
|
|
764
750
|
const { browser, cdpBrowser, page, chromeProcess } = await launchBrowser({
|
|
765
751
|
chromePath,
|
|
766
752
|
profilePath,
|
|
767
|
-
headless: !
|
|
753
|
+
headless: !headful,
|
|
768
754
|
timeout: 3e4,
|
|
769
755
|
verbose: this.verbose
|
|
770
756
|
});
|
|
771
757
|
try {
|
|
772
758
|
await navigateToYiyan(page, this.verbose);
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
if (
|
|
759
|
+
const isLoggedIn = await checkLoggedIn(page, this.verbose);
|
|
760
|
+
if (!isLoggedIn) {
|
|
761
|
+
if (headful) {
|
|
776
762
|
await waitForUserAction(page, "login", this.verbose);
|
|
763
|
+
} else {
|
|
764
|
+
throw new YiyanAgentError(
|
|
765
|
+
"CAPTCHA",
|
|
766
|
+
'Not logged in. Please run "yiyan-agent login" first, or use headed mode.'
|
|
767
|
+
);
|
|
777
768
|
}
|
|
778
769
|
}
|
|
779
|
-
await sendMessage(page, question, this.verbose,
|
|
780
|
-
await waitForReply(page, this.options.timeout, this.verbose,
|
|
770
|
+
await sendMessage(page, question, this.verbose, headful);
|
|
771
|
+
await waitForReply(page, this.options.timeout, this.verbose, headful);
|
|
781
772
|
const reply = await extractReply(page, question, this.verbose);
|
|
782
773
|
return reply;
|
|
783
774
|
} finally {
|
|
@@ -811,42 +802,22 @@ var YiyanAgent = class {
|
|
|
811
802
|
timeout: 6e4
|
|
812
803
|
});
|
|
813
804
|
await page.waitForTimeout(5e3);
|
|
814
|
-
process.stderr.write("[yiyan-agent]
|
|
815
|
-
process.stderr.write("[yiyan-agent] \
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
const isLoggedIn = await page.evaluate(() => {
|
|
821
|
-
const bodyText = document.body.innerText || "";
|
|
822
|
-
if (bodyText.includes("\u672A\u767B\u5F55")) return false;
|
|
823
|
-
const loginButtons = Array.from(document.querySelectorAll("button"));
|
|
824
|
-
for (const btn of loginButtons) {
|
|
825
|
-
const text = btn.textContent?.trim() || "";
|
|
826
|
-
if (text === "\u767B\u5F55") return false;
|
|
827
|
-
}
|
|
828
|
-
return true;
|
|
829
|
-
});
|
|
830
|
-
if (isLoggedIn) {
|
|
831
|
-
process.stderr.write("[yiyan-agent] \u2713 \u68C0\u6D4B\u5230\u767B\u5F55\u6210\u529F\uFF01\n");
|
|
832
|
-
return true;
|
|
833
|
-
}
|
|
834
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
835
|
-
}
|
|
836
|
-
return false;
|
|
837
|
-
});
|
|
838
|
-
const enterPressed = new Promise((resolve) => {
|
|
839
|
-
const readline = __require("readline");
|
|
805
|
+
process.stderr.write("\n[yiyan-agent] ============================================\n");
|
|
806
|
+
process.stderr.write("[yiyan-agent] \u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u767B\u5F55\u6587\u5FC3\u4E00\u8A00\n");
|
|
807
|
+
process.stderr.write("[yiyan-agent] \u767B\u5F55\u6210\u529F\u540E\uFF0C\u56DE\u5230\u6B64\u5904\u6309 Enter \u952E\u7EE7\u7EED\n");
|
|
808
|
+
process.stderr.write("[yiyan-agent] ============================================\n\n");
|
|
809
|
+
const readline = await import("readline");
|
|
810
|
+
await new Promise((resolve) => {
|
|
840
811
|
const rl = readline.createInterface({
|
|
841
812
|
input: process.stdin,
|
|
842
813
|
output: process.stderr
|
|
843
814
|
});
|
|
844
|
-
rl.question("", () => {
|
|
815
|
+
rl.question("\u6309 Enter \u7EE7\u7EED...", () => {
|
|
845
816
|
rl.close();
|
|
846
|
-
resolve(
|
|
817
|
+
resolve();
|
|
847
818
|
});
|
|
848
819
|
});
|
|
849
|
-
|
|
820
|
+
process.stderr.write("[yiyan-agent] \u2713 \u767B\u5F55\u5B8C\u6210\uFF0C\u6B63\u5728\u4FDD\u5B58\u767B\u5F55\u72B6\u6001...\n");
|
|
850
821
|
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
851
822
|
} finally {
|
|
852
823
|
await closeBrowser(browser, chromeProcess, cdpBrowser, this.verbose);
|
|
@@ -873,29 +844,34 @@ yiyan-agent - \u6587\u5FC3\u4E00\u8A00\u6D4F\u89C8\u5668\u4EE3\u7406 CLI
|
|
|
873
844
|
|
|
874
845
|
\u7528\u6CD5:
|
|
875
846
|
yiyan-agent login \u9996\u6B21\u767B\u5F55\u6587\u5FC3\u4E00\u8A00\uFF08\u4F1A\u6253\u5F00\u6D4F\u89C8\u5668\u7A97\u53E3\uFF09
|
|
876
|
-
yiyan-agent ask "\u95EE\u9898" [--timeout ms] [--retry n] [--
|
|
847
|
+
yiyan-agent ask "\u95EE\u9898" [--timeout ms] [--retry n] [--headful]
|
|
877
848
|
yiyan-agent status \u68C0\u67E5\u767B\u5F55\u72B6\u6001
|
|
878
849
|
yiyan-agent reset \u6E05\u9664\u4FDD\u5B58\u7684 profile
|
|
879
850
|
|
|
880
851
|
\u547D\u4EE4:
|
|
881
|
-
login \u6253\u5F00\u6D4F\u89C8\u5668\u624B\u52A8\u767B\u5F55\u6587\u5FC3\u4E00\u8A00\uFF08\u9996\u6B21\u4F7F\u7528\
|
|
882
|
-
ask \u53D1\u9001\u95EE\u9898\u5E76\u83B7\u53D6\u7B54\u6848\uFF08\u9ED8\u8BA4\
|
|
852
|
+
login \u6253\u5F00\u6D4F\u89C8\u5668\u624B\u52A8\u767B\u5F55\u6587\u5FC3\u4E00\u8A00\uFF08\u9996\u6B21\u4F7F\u7528\u5FC5\u987B\u5148\u767B\u5F55\uFF09
|
|
853
|
+
ask \u53D1\u9001\u95EE\u9898\u5E76\u83B7\u53D6\u7B54\u6848\uFF08\u9ED8\u8BA4\u65E0\u5934\u6A21\u5F0F\uFF0C\u4E0D\u5F39\u7A97\uFF1B\u767B\u5F55\u540E\u76F4\u63A5\u4F7F\u7528\uFF09
|
|
883
854
|
status \u68C0\u67E5\u767B\u5F55\u72B6\u6001
|
|
884
855
|
reset \u6E05\u9664\u4FDD\u5B58\u7684 profile
|
|
885
856
|
|
|
886
857
|
\u9009\u9879:
|
|
887
858
|
--timeout <ms> \u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09\uFF0C\u9ED8\u8BA4 120000
|
|
888
859
|
--retry <n> \u91CD\u8BD5\u6B21\u6570\uFF0C\u9ED8\u8BA4 3
|
|
889
|
-
--
|
|
860
|
+
--headful \u4F7F\u7528\u6709\u5934\u6D4F\u89C8\u5668\uFF08\u53EF\u89C1\u7A97\u53E3\uFF0C\u7528\u4E8E\u624B\u52A8\u8FC7\u9A8C\u8BC1\u7801\uFF09
|
|
890
861
|
--help \u663E\u793A\u5E2E\u52A9\u4FE1\u606F
|
|
891
862
|
|
|
892
863
|
\u793A\u4F8B:
|
|
893
864
|
yiyan-agent login # \u9996\u6B21\u4F7F\u7528\uFF1A\u767B\u5F55\u6587\u5FC3\u4E00\u8A00
|
|
894
|
-
yiyan-agent ask "\u4EC0\u4E48\u662F TypeScript\uFF1F" # \u63D0\u95EE\uFF08\u9ED8\u8BA4\
|
|
865
|
+
yiyan-agent ask "\u4EC0\u4E48\u662F TypeScript\uFF1F" # \u63D0\u95EE\uFF08\u9ED8\u8BA4\u65E0\u5934\uFF0C\u4E0D\u5F39\u7A97\uFF09
|
|
895
866
|
yiyan-agent ask "\u89E3\u91CA Promise" --timeout 60000 --retry 5
|
|
896
|
-
yiyan-agent ask "30+30=" --
|
|
867
|
+
yiyan-agent ask "30+30=" --headful # \u6709\u5934\u6A21\u5F0F\uFF08\u53EF\u624B\u52A8\u8FC7\u9A8C\u8BC1\u7801\uFF09
|
|
897
868
|
yiyan-agent status
|
|
898
869
|
yiyan-agent reset
|
|
870
|
+
|
|
871
|
+
\u6D41\u7A0B:
|
|
872
|
+
1. \u5148\u8FD0\u884C yiyan-agent login \u767B\u5F55\uFF08\u53EA\u9700\u4E00\u6B21\uFF0C\u767B\u5F55\u72B6\u6001\u4F1A\u4FDD\u5B58\uFF09
|
|
873
|
+
2. \u4E4B\u540E\u76F4\u63A5 yiyan-agent ask "\u95EE\u9898" \u5373\u53EF\uFF0C\u65E0\u9700\u518D\u767B\u5F55
|
|
874
|
+
3. \u5982\u679C\u9047\u5230\u9A8C\u8BC1\u7801\uFF0C\u52A0 --headful \u5207\u6362\u6709\u5934\u6A21\u5F0F\u624B\u52A8\u5904\u7406
|
|
899
875
|
`);
|
|
900
876
|
}
|
|
901
877
|
function parseCliArgs(args) {
|
|
@@ -936,8 +912,8 @@ function parseCliArgs(args) {
|
|
|
936
912
|
i++;
|
|
937
913
|
}
|
|
938
914
|
}
|
|
939
|
-
if (arg === "--
|
|
940
|
-
result.
|
|
915
|
+
if (arg === "--headful") {
|
|
916
|
+
result.headful = true;
|
|
941
917
|
}
|
|
942
918
|
}
|
|
943
919
|
return result;
|
|
@@ -1012,7 +988,7 @@ async function runCli(args) {
|
|
|
1012
988
|
}
|
|
1013
989
|
const startTime = Date.now();
|
|
1014
990
|
try {
|
|
1015
|
-
const answer = await agent.ask(parsed.question,
|
|
991
|
+
const answer = await agent.ask(parsed.question, !!parsed.headful);
|
|
1016
992
|
const duration = Date.now() - startTime;
|
|
1017
993
|
console.log(formatCliOutput({
|
|
1018
994
|
success: true,
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/agent.ts","../src/types.ts","../src/profile.ts","../src/browser.ts","../src/cli.ts"],"sourcesContent":["// src/agent.ts\nimport path from 'path';\nimport os from 'os';\nimport {\n YiyanAgentOptions,\n DEFAULT_OPTIONS,\n YiyanAgentError,\n} from './types';\nimport {\n getChromeExecutablePath,\n clearCopiedProfile,\n profileExists,\n} from './profile';\nimport {\n launchBrowser,\n closeBrowser,\n navigateToYiyan,\n sendMessage,\n waitForReply,\n extractReply,\n checkLoggedIn,\n waitForUserAction,\n} from './browser';\n\n/** 默认 profile 存储目录 */\nconst DEFAULT_PROFILE_BASE_DIR = path.join(\n os.homedir(),\n '.yiyan-browser-agent'\n);\n\n/**\n * 文心一言浏览器代理\n */\nexport class YiyanAgent {\n private options: Required<YiyanAgentOptions>;\n private profileDir: string;\n private verbose: boolean;\n\n constructor(options?: YiyanAgentOptions & { verbose?: boolean }) {\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n\n this.profileDir =\n this.options.profileDir || DEFAULT_PROFILE_BASE_DIR;\n\n this.verbose = options?.verbose ?? true; // 默认开启日志\n }\n\n /**\n * 发送问题并获取答案\n * @param question 问题文本\n * @param headful 是否使用有头浏览器(可见窗口),默认为 true(headless 模式会被检测拦截)\n */\n async ask(question: string, headful = true): Promise<string> {\n let lastError: YiyanAgentError | null = null;\n\n for (let attempt = 0; attempt < this.options.retryCount; attempt++) {\n try {\n if (attempt > 0) {\n process.stderr.write(`[yiyan-agent] 第 ${attempt + 1} 次重试...\\n`);\n }\n return await this.executeAsk(question, headful);\n } catch (error) {\n if (error instanceof YiyanAgentError) {\n lastError = error;\n if (lastError.type === 'CAPTCHA') {\n throw lastError;\n }\n } else {\n lastError = new YiyanAgentError(\n 'NETWORK',\n error instanceof Error ? error.message : String(error)\n );\n }\n\n if (attempt < this.options.retryCount - 1) {\n await this.sleep(2000);\n }\n }\n }\n\n throw lastError || new YiyanAgentError('TIMEOUT', 'Unknown error');\n }\n\n /**\n * 执行单次问答\n */\n private async executeAsk(question: string, headful = true): Promise<string> {\n const platform = os.platform();\n const chromePath =\n this.options.chromePath || getChromeExecutablePath(platform);\n\n if (!chromePath) {\n throw new YiyanAgentError(\n 'BROWSER_LAUNCH',\n `Unsupported platform: ${platform}`\n );\n }\n\n // profile 目录直接使用 chrome-profile 子目录\n const profilePath = path.join(this.profileDir, 'chrome-profile');\n\n // 启动浏览器(CDP 模式)—— 默认使用有头模式\n const useHeadful = headful !== false;\n const { browser, cdpBrowser, page, chromeProcess } = await launchBrowser({\n chromePath,\n profilePath,\n headless: !useHeadful,\n timeout: 30000,\n verbose: this.verbose,\n });\n\n try {\n // 导航到文心一言\n await navigateToYiyan(page, this.verbose);\n\n // 检查登录状态(有头模式下如果未登录,暂停等待用户登录)\n if (useHeadful) {\n const isLoggedIn = await checkLoggedIn(page, this.verbose);\n if (!isLoggedIn) {\n await waitForUserAction(page, 'login', this.verbose);\n }\n }\n\n // 发送消息\n await sendMessage(page, question, this.verbose, useHeadful);\n\n // 等待回复\n await waitForReply(page, this.options.timeout, this.verbose, useHeadful);\n\n // 提取回复\n const reply = await extractReply(page, question, this.verbose);\n\n return reply;\n } finally {\n await closeBrowser(browser, chromeProcess, cdpBrowser, this.verbose);\n }\n }\n\n /**\n * 登录文心一言(打开有头浏览器让用户手动登录)\n * 登录成功后,后续的 ask 调用将自动使用登录状态\n */\n async login(): Promise<void> {\n const platform = os.platform();\n const chromePath =\n this.options.chromePath || getChromeExecutablePath(platform);\n\n if (!chromePath) {\n throw new YiyanAgentError(\n 'BROWSER_LAUNCH',\n `Unsupported platform: ${platform}`\n );\n }\n\n const profilePath = path.join(this.profileDir, 'chrome-profile');\n\n // 启动有头浏览器(用户可以看到并操作)\n const { browser, cdpBrowser, page, chromeProcess } = await launchBrowser({\n chromePath,\n profilePath,\n headless: false,\n timeout: 60000,\n verbose: this.verbose,\n });\n\n try {\n await page.goto('https://yiyan.baidu.com/', {\n waitUntil: 'networkidle',\n timeout: 60000,\n });\n\n // 等待 SPA 渲染\n await page.waitForTimeout(5000);\n\n // 输出提示信息\n process.stderr.write('[yiyan-agent] 等待您在浏览器中登录文心一言...\\n');\n process.stderr.write('[yiyan-agent] 登录成功后,请按 Enter 键继续...\\n');\n\n // 策略1:检测登录成功的标志\n const loginDetected = Promise.resolve().then(async () => {\n const maxWait = 300000;\n const startTime = Date.now();\n while (Date.now() - startTime < maxWait) {\n const isLoggedIn = await page.evaluate(() => {\n const bodyText = document.body.innerText || '';\n if (bodyText.includes('未登录')) return false;\n const loginButtons = Array.from(document.querySelectorAll('button'));\n for (const btn of loginButtons) {\n const text = btn.textContent?.trim() || '';\n if (text === '登录') return false;\n }\n return true;\n });\n\n if (isLoggedIn) {\n process.stderr.write('[yiyan-agent] ✓ 检测到登录成功!\\n');\n return true;\n }\n\n await new Promise(resolve => setTimeout(resolve, 2000));\n }\n return false;\n });\n\n // 策略2:等待用户按 Enter 键确认\n const enterPressed = new Promise<boolean>((resolve) => {\n const readline = require('readline');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n rl.question('', () => {\n rl.close();\n resolve(true);\n });\n });\n\n // 两种方式任一触发即可\n await Promise.race([loginDetected, enterPressed]);\n\n // 等待一下确保登录状态保存到 profile\n await new Promise(resolve => setTimeout(resolve, 3000));\n } finally {\n await closeBrowser(browser, chromeProcess, cdpBrowser, this.verbose);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n async reset(): Promise<void> {\n await clearCopiedProfile(this.profileDir);\n }\n\n status(): { loggedIn: boolean; profilePath: string } {\n return {\n loggedIn: profileExists(this.profileDir),\n profilePath: path.join(this.profileDir, 'chrome-profile'),\n };\n }\n}\n","/**\n * yiyan-browser-agent 类型定义\n */\n\n/** 配置选项 */\nexport interface YiyanAgentOptions {\n /** 超时毫秒,默认 120000 */\n timeout?: number;\n /** 重试次数,默认 3 */\n retryCount?: number;\n /** 自定义 profile 目录 */\n profileDir?: string;\n /** 自定义 Chrome 可执行文件路径 */\n chromePath?: string;\n}\n\n/** CLI 输出格式 */\nexport interface CliOutput {\n success: boolean;\n /** 用户发送的问题 */\n question: string;\n /** 成功时有值 */\n answer?: string;\n /** 失败时有值 */\n error?: string;\n /** 耗时毫秒 */\n duration: number;\n}\n\n/** 错误类型 */\nexport type YiyanAgentErrorType =\n | 'BROWSER_LAUNCH'\n | 'PROFILE_COPY'\n | 'TIMEOUT'\n | 'NETWORK'\n | 'CAPTCHA';\n\n/** 自定义错误类 */\nexport class YiyanAgentError extends Error {\n constructor(\n public type: YiyanAgentErrorType,\n message: string\n ) {\n super(message);\n this.name = 'YiyanAgentError';\n }\n}\n\n/** 默认配置 */\nexport const DEFAULT_OPTIONS: Required<YiyanAgentOptions> = {\n timeout: 120000,\n retryCount: 3,\n profileDir: '',\n chromePath: '',\n};\n\n/** 文心一言网页 URL */\nexport const YIYAN_CHAT_URL = 'https://yiyan.baidu.com/';\n","// src/profile.ts\nimport path from 'path';\nimport os from 'os';\nimport fs from 'fs';\nimport fsp from 'fs/promises';\nimport { YiyanAgentError } from './types';\n\n/**\n * 获取 Chrome 用户数据目录路径\n */\nexport function getChromeProfilePath(platform: NodeJS.Platform): string | null {\n const home = os.homedir();\n\n switch (platform) {\n case 'win32':\n const localAppData = process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');\n return path.join(localAppData, 'Google', 'Chrome', 'User Data');\n case 'darwin':\n return path.join(home, 'Library', 'Application Support', 'Google', 'Chrome');\n case 'linux':\n return path.join(home, '.config', 'google-chrome');\n default:\n return null;\n }\n}\n\n/**\n * 获取 Chrome 可执行文件路径\n */\nexport function getChromeExecutablePath(platform: NodeJS.Platform): string | null {\n switch (platform) {\n case 'win32':\n const programFiles = process.env.PROGRAMFILES || 'C:\\\\Program Files';\n return path.join(programFiles, 'Google', 'Chrome', 'Application', 'chrome.exe');\n case 'darwin':\n return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';\n case 'linux':\n return '/usr/bin/google-chrome';\n default:\n return null;\n }\n}\n\n/** Profile 子目录名称 */\nconst PROFILE_TARGET_NAME = 'chrome-profile';\n\n/**\n * 检查并复制 Chrome profile 到目标目录\n * 如果已存在且未过期则直接返回路径,否则重新复制\n */\nexport async function copyChromeProfile(\n platform: NodeJS.Platform,\n targetBaseDir: string\n): Promise<string> {\n const targetPath = path.join(targetBaseDir, PROFILE_TARGET_NAME);\n\n // 检查已复制的 profile 是否有效(5分钟内不需要重新复制)\n if (fs.existsSync(targetPath)) {\n const stat = fs.statSync(targetPath);\n const ageMinutes = (Date.now() - stat.mtimeMs) / 60000;\n if (ageMinutes < 5) {\n return targetPath;\n }\n // 过期了,删除旧的\n await fsp.rm(targetPath, { recursive: true, force: true });\n }\n\n // 获取源 profile 路径\n const sourcePath = getChromeProfilePath(platform);\n if (!sourcePath) {\n throw new YiyanAgentError('PROFILE_COPY', `Unsupported platform: ${platform}`);\n }\n\n // 检查源是否存在\n try {\n await fsp.access(sourcePath);\n } catch {\n throw new YiyanAgentError(\n 'PROFILE_COPY',\n `Chrome profile not found at: ${sourcePath}`\n );\n }\n\n // 创建目标目录\n await fsp.mkdir(targetPath, { recursive: true });\n\n // 复制根目录关键文件\n const rootFiles = [\n 'Local State',\n 'First Run',\n 'Default',\n ];\n\n for (const file of rootFiles) {\n const srcFile = path.join(sourcePath, file);\n const destFile = path.join(targetPath, file);\n\n try {\n const stat = await fsp.stat(srcFile);\n if (stat.isDirectory()) {\n await copyDir(srcFile, destFile);\n } else {\n await fsp.copyFile(srcFile, destFile);\n }\n } catch {\n continue;\n }\n }\n\n // 复制 Default 目录下的关键文件(只复制登录和 Cookie 相关的小文件)\n const defaultDir = path.join(sourcePath, 'Default');\n if (fs.existsSync(defaultDir)) {\n const destDefaultDir = path.join(targetPath, 'Default');\n await fsp.mkdir(destDefaultDir, { recursive: true });\n\n const defaultFiles = [\n 'Cookies',\n 'Cookies-journal',\n 'Login Data',\n 'Login Data-journal',\n 'Login Data For Account',\n 'Login Data For Account-journal',\n 'Preferences',\n 'Secure Preferences',\n 'Network',\n 'Network Action Predictor',\n 'Web Data',\n 'Web Data-journal',\n 'Extension Cookies',\n 'Extension Cookies-journal',\n ];\n\n for (const file of defaultFiles) {\n const srcFile = path.join(defaultDir, file);\n const destFile = path.join(destDefaultDir, file);\n\n try {\n const stat = await fsp.stat(srcFile);\n if (stat.isDirectory()) {\n await copyDir(srcFile, destFile);\n } else {\n await fsp.copyFile(srcFile, destFile);\n }\n } catch {\n continue;\n }\n }\n }\n\n // 更新目录修改时间\n await fsp.utimes(targetPath, new Date(), new Date());\n\n return targetPath;\n}\n\n/**\n * 复制目录\n */\nasync function copyDir(src: string, dest: string): Promise<void> {\n await fsp.mkdir(dest, { recursive: true });\n const entries = await fsp.readdir(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n\n if (entry.isDirectory()) {\n await copyDir(srcPath, destPath);\n } else {\n await fsp.copyFile(srcPath, destPath);\n }\n }\n}\n\n/**\n * 清除复制的 profile\n */\nexport async function clearCopiedProfile(profileDir: string): Promise<void> {\n const targetPath = path.join(profileDir, PROFILE_TARGET_NAME);\n if (fs.existsSync(targetPath)) {\n await fsp.rm(targetPath, { recursive: true, force: true });\n }\n}\n\n/**\n * 检查 profile 是否存在\n */\nexport function profileExists(profileDir: string): boolean {\n const targetPath = path.join(profileDir, PROFILE_TARGET_NAME);\n return fs.existsSync(targetPath);\n}\n","// src/browser.ts\nimport { chromium, Page, BrowserContext, Browser } from 'playwright-core';\nimport { YiyanAgentError, YIYAN_CHAT_URL } from './types';\nimport http from 'http';\nimport { spawn, ChildProcess, execSync } from 'child_process';\nimport path from 'path';\nimport os from 'os';\nimport fs from 'fs';\n\nexport interface LaunchBrowserOptions {\n chromePath: string;\n profilePath: string;\n headless: boolean;\n timeout?: number;\n /** 是否输出进度日志 */\n verbose?: boolean;\n}\n\nconst CDP_PORT = 19222;\n\n/** 调试截图保存目录 */\nconst DEBUG_DIR = path.join(os.homedir(), '.yiyan-browser-agent', 'debug');\n\n/** 日志输出(仅 verbose 模式) */\nfunction log(verbose: boolean, msg: string): void {\n if (verbose) {\n process.stderr.write(`[yiyan-agent] ${msg}\\n`);\n }\n}\n\n/** 确保调试目录存在 */\nfunction ensureDebugDir(): void {\n if (!fs.existsSync(DEBUG_DIR)) {\n fs.mkdirSync(DEBUG_DIR, { recursive: true });\n }\n}\n\n/**\n * 杀死占用指定端口的进程(启动前清理残留 Chrome)\n */\nfunction killProcessOnPort(port: number): void {\n try {\n if (process.platform === 'win32') {\n const output = execSync(\n `netstat -ano | findstr :${port} | findstr LISTENING`,\n { encoding: 'utf-8', timeout: 5000 }\n ).trim();\n if (output) {\n const lines = output.split('\\n').filter(l => l.trim());\n const pids = new Set<string>();\n for (const line of lines) {\n const parts = line.trim().split(/\\s+/);\n const pid = parts[parts.length - 1];\n if (pid && /^\\d+$/.test(pid)) pids.add(pid);\n }\n for (const pid of pids) {\n try {\n execSync(`taskkill /F /T /PID ${pid}`, { timeout: 5000 });\n } catch {\n // 进程可能已经退出\n }\n }\n }\n } else {\n const output = execSync(\n `lsof -ti :${port}`,\n { encoding: 'utf-8', timeout: 5000 }\n ).trim();\n if (output) {\n const pids = output.split('\\n').filter(l => l.trim());\n for (const pid of pids) {\n try {\n process.kill(parseInt(pid, 10), 'SIGKILL');\n } catch {\n // 进程可能已经退出\n }\n }\n }\n }\n } catch {\n // 没有找到占用端口的进程,正常情况\n }\n}\n\n/**\n * 杀死进程树(Windows 用 taskkill /T,其他平台用 process.kill 负 PID)\n */\nfunction killProcessTree(pid: number): void {\n try {\n if (process.platform === 'win32') {\n execSync(`taskkill /F /T /PID ${pid}`, { timeout: 5000 });\n } else {\n try {\n process.kill(-pid, 'SIGKILL');\n } catch {\n process.kill(pid, 'SIGKILL');\n }\n }\n } catch {\n // 进程可能已经退出\n }\n}\n\n/**\n * 等待 CDP 端口就绪\n */\nfunction waitForCDP(port: number, timeout = 15000): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const start = Date.now();\n const check = () => {\n http.get(`http://localhost:${port}/json/version`, (res) => {\n let data = '';\n res.on('data', chunk => data += chunk);\n res.on('end', () => {\n try { resolve(JSON.parse(data)); } catch (e) { reject(e); }\n });\n }).on('error', () => {\n if (Date.now() - start > timeout) reject(new Error('CDP connection timeout'));\n else setTimeout(check, 500);\n });\n };\n check();\n });\n}\n\n/**\n * 启动 Chrome 浏览器(通过 CDP 连接模式)\n */\nexport async function launchBrowser(\n options: LaunchBrowserOptions\n): Promise<{ browser: BrowserContext; cdpBrowser: Browser; page: Page; chromeProcess: ChildProcess | null }> {\n const { chromePath, profilePath, headless, timeout = 30000, verbose = false } = options;\n\n try {\n log(verbose, '清理残留 Chrome 进程...');\n killProcessOnPort(CDP_PORT);\n await new Promise(resolve => setTimeout(resolve, 500));\n\n const chromeArgs = [\n `--remote-debugging-port=${CDP_PORT}`,\n `--user-data-dir=${profilePath}`,\n '--no-first-run',\n '--no-default-browser-check',\n '--disable-blink-features=AutomationControlled',\n ];\n\n if (headless) {\n chromeArgs.push('--headless=new');\n }\n\n log(verbose, `启动 Chrome: ${chromePath} ${headless ? '(headless)' : '(headed)'}`);\n const chromeProcess = spawn(chromePath, chromeArgs, {\n detached: true,\n stdio: 'ignore',\n });\n chromeProcess.unref();\n\n const spawnError = new Promise<never>((_, reject) => {\n chromeProcess.on('error', (err) => {\n reject(new YiyanAgentError('BROWSER_LAUNCH', `Failed to spawn Chrome: ${err.message}`));\n });\n });\n\n log(verbose, '等待 CDP 端口就绪...');\n await Promise.race([\n waitForCDP(CDP_PORT, timeout),\n spawnError,\n ]);\n\n log(verbose, '通过 CDP 连接浏览器...');\n const cdpBrowser = await chromium.connectOverCDP(`http://localhost:${CDP_PORT}`);\n\n const contexts = cdpBrowser.contexts();\n const context = contexts.length > 0 ? contexts[0] : await cdpBrowser.newContext();\n context.setDefaultTimeout(timeout);\n const pages = context.pages();\n const page = pages.length > 0 ? pages[0] : await context.newPage();\n\n log(verbose, '浏览器启动完成');\n return { browser: context, cdpBrowser, page, chromeProcess };\n } catch (error) {\n killProcessOnPort(CDP_PORT);\n throw new YiyanAgentError(\n 'BROWSER_LAUNCH',\n `Failed to launch browser: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * 关闭浏览器(彻底清理:断开 CDP → 关闭页面 → 杀进程树 → 清理残留端口)\n */\nexport async function closeBrowser(\n browser: BrowserContext,\n chromeProcess: ChildProcess | null,\n cdpBrowser?: Browser,\n verbose?: boolean\n): Promise<void> {\n log(!!verbose, '关闭浏览器...');\n\n if (cdpBrowser) {\n try {\n await cdpBrowser.close();\n } catch {\n // 忽略关闭错误\n }\n }\n\n try {\n const pages = browser.pages();\n for (const page of pages) {\n await page.close().catch(() => {});\n }\n } catch {\n // 忽略关闭错误\n }\n\n if (chromeProcess && chromeProcess.pid) {\n killProcessTree(chromeProcess.pid);\n }\n\n killProcessOnPort(CDP_PORT);\n log(!!verbose, '浏览器已关闭');\n}\n\n/**\n * 导航到文心一言聊天页面\n */\nexport async function navigateToYiyan(page: Page, verbose = false): Promise<void> {\n log(verbose, '导航到文心一言聊天页面...');\n await page.goto(YIYAN_CHAT_URL, {\n waitUntil: 'networkidle',\n timeout: 60000,\n });\n\n // 文心一言是 SPA,需要额外等待渲染\n await page.waitForTimeout(5000);\n\n try {\n await page.waitForSelector('[contenteditable=\"true\"]', { timeout: 15000 });\n log(verbose, '页面加载完成,输入框已就绪');\n } catch {\n log(verbose, '警告:未检测到输入框,继续执行');\n }\n}\n\n/**\n * 检测文心一言验证码弹窗\n */\nexport async function checkCaptcha(page: Page, verbose = false): Promise<boolean> {\n const captchaIndicators = [\n '请完成下列验证后继续',\n '按住左边按钮拖动',\n '滑动验证',\n '点击验证',\n '安全验证',\n 'captcha',\n '请完成验证',\n ];\n\n const hasCaptcha = await page.evaluate((indicators) => {\n const bodyText = document.body.innerText || '';\n for (const indicator of indicators) {\n if (bodyText.includes(indicator)) return true;\n }\n // 检查是否有弹窗/遮罩层\n const dialogs = document.querySelectorAll('[role=\"dialog\"], [class*=\"captcha\"], [class*=\"verify\"]');\n if (dialogs.length > 0) return true;\n return false;\n }, captchaIndicators);\n\n if (hasCaptcha) {\n log(verbose, '⚠ 检测到验证码弹窗!');\n }\n return hasCaptcha;\n}\n\n/**\n * 检测是否已登录文心一言\n */\nexport async function checkLoggedIn(page: Page, verbose = false): Promise<boolean> {\n const isLoggedIn = await page.evaluate(() => {\n // 文心一言未登录时:页面有\"登录\"按钮和\"未登录\"文字\n const bodyText = document.body.innerText || '';\n\n // 如果有\"未登录\"文字 = 未登录\n if (bodyText.includes('未登录')) return false;\n\n // 检查是否有\"登录\"按钮\n const loginButtons = document.querySelectorAll('button');\n for (const btn of loginButtons) {\n const text = btn.textContent?.trim() || '';\n if (text === '登录') {\n return false; // 有登录按钮 = 未登录\n }\n }\n\n // 如果没有\"登录\"按钮和\"未登录\"文字,说明已登录\n return true;\n });\n\n if (isLoggedIn) {\n log(verbose, '✓ 已检测到登录状态');\n } else {\n log(verbose, '⚠ 未检测到登录状态');\n }\n return isLoggedIn;\n}\n\n/**\n * 有头模式下,暂停等待用户手动操作(过验证码/登录),然后继续\n * 检测到问题已解决后自动继续\n */\nexport async function waitForUserAction(\n page: Page,\n reason: 'captcha' | 'login' | 'no-reply',\n verbose = false\n): Promise<void> {\n const reasonText = reason === 'captcha'\n ? '检测到验证码,请在浏览器中手动完成验证'\n : reason === 'login'\n ? '检测到未登录,请在浏览器中手动登录'\n : 'AI 未回复,请检查浏览器是否需要手动操作(验证码/登录)';\n\n process.stderr.write(`\\n[yiyan-agent] ⚠ ${reasonText}\\n`);\n process.stderr.write('[yiyan-agent] 等待您操作完成...(操作完成后会自动继续)\\n\\n');\n\n // 轮询检测问题是否已解决\n const maxWait = 180000; // 最多等 3 分钟\n const startTime = Date.now();\n\n while (Date.now() - startTime < maxWait) {\n await new Promise(resolve => setTimeout(resolve, 2000));\n\n if (reason === 'captcha') {\n const stillHasCaptcha = await checkCaptcha(page, false);\n if (!stillHasCaptcha) {\n log(verbose, '✓ 验证码已通过!');\n return;\n }\n } else if (reason === 'login') {\n const loggedIn = await checkLoggedIn(page, false);\n if (loggedIn) {\n log(verbose, '✓ 登录成功!');\n return;\n }\n } else {\n // no-reply:检测 AI 回复是否出现\n const hasReply = await page.evaluate(() => {\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (!answerBox) return false;\n const text = answerBox.innerText?.trim() || '';\n return text.length > 0;\n });\n if (hasReply) {\n log(verbose, '✓ AI 已开始回复!');\n return;\n }\n }\n }\n\n log(verbose, '⚠ 等待用户操作超时(3分钟)');\n}\n\n/**\n * 发送消息到文心一言\n * @param headful 是否为有头模式(有头模式下检测到验证码会暂停等待用户操作)\n */\nexport async function sendMessage(page: Page, message: string, verbose = false, headful = false): Promise<void> {\n // 文心一言使用 contenteditable div 而不是 textarea\n const inputSelector = '[contenteditable=\"true\"]';\n\n log(verbose, '等待输入框出现...');\n const inputElement = await page.waitForSelector(inputSelector, { timeout: 15000 });\n if (!inputElement) {\n log(verbose, '⚠ 输入框未找到!');\n throw new YiyanAgentError('NETWORK', 'Input element not found on page');\n }\n log(verbose, '✓ 输入框已找到');\n\n // 点击输入框获取焦点\n await inputElement.click();\n await page.waitForTimeout(500);\n\n // 输入问题(文心一言用 contenteditable,需要用 keyboard.type 而不是 fill)\n log(verbose, `输入问题: \"${message}\"`);\n await page.keyboard.type(message, { delay: 30 });\n\n // 验证填入是否成功\n const filledValue = await inputElement.innerText();\n if (filledValue.includes(message)) {\n log(verbose, '✓ 问题已成功填入输入框');\n } else {\n log(verbose, `⚠ 输入框内容: \"${filledValue.substring(0, 50)}\"`);\n }\n\n // 按 Enter 发送\n log(verbose, '按 Enter 发送消息...');\n await page.keyboard.press('Enter');\n\n // 等待发送后的页面变化\n await page.waitForTimeout(3000);\n\n // 检查是否触发了验证码\n if (await checkCaptcha(page, verbose)) {\n if (headful) {\n await waitForUserAction(page, 'captcha', verbose);\n // 验证码通过后,重新输入并发送消息\n log(verbose, '验证码已通过,重新发送消息...');\n const inputEl = await page.waitForSelector(inputSelector, { timeout: 10000 });\n if (inputEl) {\n await inputEl.click();\n await page.waitForTimeout(300);\n await page.keyboard.type(message, { delay: 30 });\n await page.keyboard.press('Enter');\n await page.waitForTimeout(3000);\n }\n } else {\n throw new YiyanAgentError(\n 'CAPTCHA',\n 'Yiyan detected automation and triggered a captcha. Use headed mode (default) to manually solve it, or run \"login\" first.'\n );\n }\n }\n\n // 检查 URL 变化(文心一言发送消息后会跳转到 /chat/xxx)\n const url = page.url();\n log(verbose, `当前 URL: ${url}`);\n\n if (url.includes('/chat/')) {\n log(verbose, '✓ 已自动进入对话页面');\n } else if (url === 'https://yiyan.baidu.com/' || url === 'https://yiyan.baidu.com') {\n log(verbose, '仍在欢迎页,尝试点击发送按钮...');\n try {\n // 点击发送按钮\n const sendBtn = page.locator('[class*=\"send__\"]').first();\n await sendBtn.click({ timeout: 5000 });\n await page.waitForTimeout(3000);\n log(verbose, '✓ 已点击发送按钮');\n } catch {\n log(verbose, '⚠ 点击发送按钮失败,继续等待回复...');\n }\n } else {\n log(verbose, `当前页面: ${url}`);\n }\n}\n\n/**\n * 保存调试截图\n */\nasync function saveDebugScreenshot(page: Page, name: string, verbose: boolean): Promise<void> {\n try {\n ensureDebugDir();\n const screenshotPath = path.join(DEBUG_DIR, `${name}.png`);\n await page.screenshot({ path: screenshotPath, fullPage: true });\n log(verbose, `调试截图已保存: ${screenshotPath}`);\n } catch {\n // 截图失败不影响主流程\n }\n}\n\n/**\n * 等待回复完成\n * @param headful 是否为有头模式(超时且有头模式下会等待用户手动操作)\n */\nexport async function waitForReply(\n page: Page,\n timeout: number,\n verbose = false,\n headful = false\n): Promise<void> {\n const maxWait = Math.min(timeout, 60000);\n\n log(verbose, `等待 AI 回复(最多 ${maxWait / 1000} 秒)...`);\n\n // 文心一言 AI 回复在 answerBox 容器中,流式渲染\n const startTime = Date.now();\n let lastLen = 0;\n let stableCount = 0;\n let replyStarted = false;\n\n while (Date.now() - startTime < maxWait) {\n const state = await page.evaluate(() => {\n // 文心一言 AI 回复在 answerBox 或 dialogueCardList 中\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n return {\n hasAiReply: text.length > 0,\n aiTextLen: text.length,\n aiPreview: text.substring(0, 100),\n };\n }\n\n // 备选:检查 dialogueCardList\n const cardList = document.querySelector('[class*=\"dialogueCardList\"]');\n if (cardList) {\n const lastCard = cardList.lastElementChild as HTMLElement;\n if (lastCard) {\n const text = lastCard.innerText?.trim() || '';\n return {\n hasAiReply: text.length > 0,\n aiTextLen: text.length,\n aiPreview: text.substring(0, 100),\n };\n }\n }\n\n return { hasAiReply: false, aiTextLen: 0, aiPreview: '' };\n });\n\n if (state.hasAiReply && state.aiTextLen > 0) {\n if (!replyStarted) {\n log(verbose, `✓ AI 回复已开始生成(${state.aiTextLen}字): ${state.aiPreview.substring(0, 60)}...`);\n replyStarted = true;\n }\n\n // 文本长度不再变化 → 回复完成\n if (state.aiTextLen === lastLen) {\n stableCount++;\n if (stableCount >= 3) {\n log(verbose, `✓ AI 回复已完成(${state.aiTextLen}字,连续 ${stableCount} 次长度不变)`);\n break;\n }\n } else {\n stableCount = 0;\n lastLen = state.aiTextLen;\n }\n }\n\n await page.waitForTimeout(1500);\n }\n\n if (!replyStarted) {\n // 超时后检查是否触发了验证码\n if (await checkCaptcha(page, verbose)) {\n if (headful) {\n await waitForUserAction(page, 'captcha', verbose);\n const retryStart = Date.now();\n const retryMaxWait = 30000;\n while (Date.now() - retryStart < retryMaxWait) {\n const retryState = await page.evaluate(() => {\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n return { hasAiReply: text.length > 0, aiTextLen: text.length };\n }\n return { hasAiReply: false, aiTextLen: 0 };\n });\n if (retryState.hasAiReply) {\n log(verbose, '✓ 验证码通过后 AI 已开始回复');\n replyStarted = true;\n break;\n }\n await page.waitForTimeout(1500);\n }\n } else {\n throw new YiyanAgentError(\n 'CAPTCHA',\n 'Yiyan detected automation and triggered a captcha. Use headed mode (default) to manually solve it, or run \"login\" first.'\n );\n }\n } else if (headful) {\n await waitForUserAction(page, 'no-reply', verbose);\n const retryStart = Date.now();\n const retryMaxWait = 60000;\n while (Date.now() - retryStart < retryMaxWait) {\n const retryState = await page.evaluate(() => {\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n return { hasAiReply: text.length > 0, aiTextLen: text.length };\n }\n return { hasAiReply: false, aiTextLen: 0 };\n });\n if (retryState.hasAiReply) {\n if (retryState.aiTextLen === lastLen) {\n stableCount++;\n if (stableCount >= 3) {\n log(verbose, `✓ 用户操作后 AI 回复已完成(${retryState.aiTextLen}字)`);\n replyStarted = true;\n break;\n }\n } else {\n stableCount = 0;\n lastLen = retryState.aiTextLen;\n }\n }\n await page.waitForTimeout(1500);\n }\n } else {\n log(verbose, '⚠ 等待超时,AI 回复文本未出现');\n }\n }\n\n // 额外等待一小段时间确保最终渲染\n await page.waitForTimeout(2000);\n\n // 页面诊断\n const pageDump = await page.evaluate(() => {\n const result: Record<string, any> = {};\n\n const selectors = [\n '[class*=\"answerBox\"]', '[class*=\"dialogueCardList\"]', '[class*=\"dialogue_card_item\"]',\n '[class*=\"chatViewer\"]', '[class*=\"flowBox\"]', '[class*=\"roleSystem\"]',\n '[class*=\"mdRenderContainer\"]', '[class*=\"agent-markdown\"]', '[class*=\"markdown\"]',\n '[class*=\"content\"]', '[class*=\"chat\"]', '[class*=\"message\"]',\n ];\n for (const sel of selectors) {\n result[`selector:${sel}`] = document.querySelectorAll(sel).length;\n }\n\n // answerBox 子元素详情\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n result['answerBox'] = {\n textLen: answerBox.innerText?.trim().length || 0,\n preview: (answerBox.innerText?.trim() || '').substring(0, 200),\n classes: answerBox.className?.toString().substring(0, 100) || '',\n };\n }\n\n // body 文本(前 1000 字符)\n result['bodyText'] = document.body.innerText?.substring(0, 1000) || '';\n\n // 长文本元素\n const longTextEls: { tag: string; classes: string; textLen: number; preview: string }[] = [];\n const mainContent = document.querySelector('[class*=\"chatViewer\"]') || document.querySelector('[class*=\"chat\"]') || document.body;\n for (const el of Array.from(mainContent.querySelectorAll('div, section, article'))) {\n const text = (el as HTMLElement).innerText?.trim() || '';\n if (text.length > 50 && text.length < 10000) {\n longTextEls.push({\n tag: el.tagName,\n classes: (el as HTMLElement).className?.toString().split(' ').slice(0, 3).join(' ') || '',\n textLen: text.length,\n preview: text.substring(0, 80),\n });\n }\n }\n result['longTextElements'] = longTextEls\n .sort((a, b) => b.textLen - a.textLen)\n .slice(0, 10);\n\n return result;\n });\n\n // 输出诊断\n log(verbose, '=== 页面诊断 ===');\n for (const [key, value] of Object.entries(pageDump)) {\n if (key === 'bodyText' || key === 'longTextElements') continue;\n log(verbose, ` ${key}: ${JSON.stringify(value)}`);\n }\n if (pageDump['longTextElements'] && (pageDump['longTextElements'] as any[]).length > 0) {\n log(verbose, ' 长文本元素 TOP 10:');\n for (const el of pageDump['longTextElements'] as any[]) {\n log(verbose, ` <${el.tag}> .${el.classes} (${el.textLen}字): ${el.preview}...`);\n }\n }\n if (pageDump['bodyText']) {\n log(verbose, ` 页面文本(前1000字): ${(pageDump['bodyText'] as string).substring(0, 1000)}`);\n }\n}\n\n/**\n * 提取回复内容\n */\nexport async function extractReply(page: Page, question: string, verbose = false): Promise<string> {\n log(verbose, '提取 AI 回复内容...');\n\n // 先保存截图,方便排查问题\n await saveDebugScreenshot(page, 'before-extract', verbose);\n\n const reply = await page.evaluate((userQuestion: string) => {\n const debugInfo: string[] = [];\n\n // ===== 方法1(最佳):answerBox 容器 =====\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n debugInfo.push(`answerBox found: ${!!answerBox}`);\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n debugInfo.push(`answerBox textLen: ${text.length}`);\n if (text.length > 0) {\n // 去除思考过程,只取最终回复\n // 文心一言思考过程在 processItem 中,最终回复在后面\n // 尝试只取 agent-markdown 或 mdRenderContainer 的内容\n const mdContainer = answerBox.querySelector('[class*=\"agent-markdown\"], [class*=\"mdRenderContainer\"]');\n if (mdContainer) {\n const mdText = mdContainer.innerText?.trim() || '';\n if (mdText.length > 0) {\n debugInfo.push(`method1 markdown success: ${mdText.length} chars`);\n return JSON.stringify({ text: mdText, debug: debugInfo });\n }\n }\n\n debugInfo.push(`method1 answerBox success: ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法2:dialogueCardList 中最后一个对话卡片 =====\n const cardList = document.querySelector('[class*=\"dialogueCardList\"]');\n debugInfo.push(`dialogueCardList found: ${!!cardList}`);\n if (cardList && cardList.children.length > 0) {\n // 最后一个卡片通常是 AI 回复\n const lastCard = cardList.lastElementChild as HTMLElement;\n const text = lastCard?.innerText?.trim() || '';\n debugInfo.push(`lastCard textLen: ${text.length}`);\n if (text.length > 0) {\n debugInfo.push(`method2 success: ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法3:flowBox(answerBox 的父级容器) =====\n const flowBoxes = document.querySelectorAll('[class*=\"flowBox\"]');\n debugInfo.push(`flowBoxes count: ${flowBoxes.length}`);\n for (let i = flowBoxes.length - 1; i >= 0; i--) {\n const box = flowBoxes[i] as HTMLElement;\n const text = box.innerText?.trim() || '';\n if (text.length > 10) {\n debugInfo.push(`method3 success: flowBox[${i}] ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法4:roleSystemBot(AI 回复角色标识的父容器) =====\n const botContainers = document.querySelectorAll('[class*=\"roleSystemBot\"]');\n debugInfo.push(`roleSystemBot count: ${botContainers.length}`);\n for (let i = botContainers.length - 1; i >= 0; i--) {\n const container = botContainers[i] as HTMLElement;\n const text = container.innerText?.trim() || '';\n if (text.length > 10) {\n debugInfo.push(`method4 success: botContainer[${i}] ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法5:mdRenderContainer / agent-markdown =====\n const mdContainers = document.querySelectorAll('[class*=\"mdRenderContainer\"], [class*=\"agent-markdown\"]');\n debugInfo.push(`md containers count: ${mdContainers.length}`);\n for (let i = mdContainers.length - 1; i >= 0; i--) {\n const container = mdContainers[i] as HTMLElement;\n const text = container.innerText?.trim() || '';\n if (text.length > 10) {\n debugInfo.push(`method5 success: mdContainer[${i}] ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法6:页面文本解析(fallback)=====\n const fullText = document.body.innerText;\n const lines = fullText.split('\\n').map(l => l.trim()).filter(l => l.length > 0);\n\n const uiWords = new Set([\n '文心一言', '新对话', '创意写作', '智慧绘图', '超级智能体', '更多',\n '我的收藏', '项目', '对话', '暂无记录', '未登录', '登录',\n '内容由AI生成,仅供参考,请仔细甄别', '参考',\n '深度分析需求并解答', '通用写作', '阅读分析',\n '网页工坊', '智能翻译', '代码编程',\n ]);\n\n const userQIdx = lines.findIndex(l => l === userQuestion || l.includes(userQuestion));\n debugInfo.push(`userQ idx in body text: ${userQIdx}, total lines: ${lines.length}`);\n\n if (userQIdx >= 0) {\n const replyLines: string[] = [];\n for (let i = userQIdx + 1; i < lines.length; i++) {\n const line = lines[i];\n if (line.length > 0 && !uiWords.has(line) && !line.startsWith('搜索') && !line.includes('篇资料')) {\n replyLines.push(line);\n }\n if (line === '快速' || line === '更多' || line.includes('内容由AI生成')) break;\n }\n if (replyLines.length > 0) {\n debugInfo.push(`method6 success: ${replyLines.length} lines`);\n return JSON.stringify({ text: replyLines.join('\\n'), debug: debugInfo });\n }\n }\n\n // ===== 方法7:取页面中最长的非 UI 文本块 =====\n const allTextBlocks: { text: string; length: number }[] = [];\n const blockElements = document.querySelectorAll('div, section, article, p, span');\n for (const el of blockElements) {\n const elText = (el as HTMLElement).innerText?.trim() || '';\n if (elText.length > 50 && elText.length < 10000) {\n if (!uiWords.has(elText)) {\n allTextBlocks.push({ text: elText, length: elText.length });\n }\n }\n }\n allTextBlocks.sort((a, b) => b.length - a.length);\n if (allTextBlocks.length > 0) {\n debugInfo.push(`method7 fallback: longest text block ${allTextBlocks[0].length} chars`);\n return JSON.stringify({ text: allTextBlocks[0].text, debug: debugInfo });\n }\n\n // ===== 全部失败 =====\n debugInfo.push(`ALL METHODS FAILED`);\n debugInfo.push(`body text length: ${fullText.length}`);\n debugInfo.push(`body text first 500: ${fullText.substring(0, 500)}`);\n return JSON.stringify({ text: '', debug: debugInfo });\n }, question);\n\n // 解析返回的 JSON\n let parsed: { text: string; debug: string[] };\n try {\n parsed = JSON.parse(reply);\n } catch {\n parsed = { text: reply, debug: [] };\n }\n\n // 输出调试信息\n for (const line of parsed.debug) {\n log(verbose, ` [extract] ${line}`);\n }\n\n if (parsed.text && parsed.text.length > 0) {\n log(verbose, `提取成功,回复长度: ${parsed.text.length} 字符`);\n return parsed.text;\n }\n\n log(verbose, '提取失败,所有方法均未找到回复内容');\n throw new YiyanAgentError('TIMEOUT', 'Failed to extract reply content');\n}\n","/**\n * CLI 命令行工具\n */\nimport { YiyanAgent } from './agent';\nimport { CliOutput, YiyanAgentOptions } from './types';\n\n/** CLI 帮助信息 */\nexport function printHelp(): void {\n console.log(`\nyiyan-agent - 文心一言浏览器代理 CLI\n\n用法:\n yiyan-agent login 首次登录文心一言(会打开浏览器窗口)\n yiyan-agent ask \"问题\" [--timeout ms] [--retry n] [--headless]\n yiyan-agent status 检查登录状态\n yiyan-agent reset 清除保存的 profile\n\n命令:\n login 打开浏览器手动登录文心一言(首次使用建议先登录)\n ask 发送问题并获取答案(默认有头模式,遇到验证码/未登录会暂停等待手动操作)\n status 检查登录状态\n reset 清除保存的 profile\n\n选项:\n --timeout <ms> 超时时间(毫秒),默认 120000\n --retry <n> 重试次数,默认 3\n --headless 使用无头浏览器(不可见窗口,不推荐)\n --help 显示帮助信息\n\n示例:\n yiyan-agent login # 首次使用:登录文心一言\n yiyan-agent ask \"什么是 TypeScript?\" # 提问(默认有头模式,可手动过验证码)\n yiyan-agent ask \"解释 Promise\" --timeout 60000 --retry 5\n yiyan-agent ask \"30+30=\" --headless # 无头模式(不推荐)\n yiyan-agent status\n yiyan-agent reset\n`);\n}\n\n/** 解析后的 CLI 参数 */\ninterface ParsedArgs {\n command: 'ask' | 'status' | 'reset' | 'login' | 'help' | null;\n question?: string;\n timeout?: number;\n retry?: number;\n headless?: boolean;\n}\n\n/**\n * 解析 CLI 参数\n */\nexport function parseCliArgs(args: string[]): ParsedArgs {\n const result: ParsedArgs = {\n command: null,\n };\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n\n if (arg === '--help') {\n result.command = 'help';\n return result;\n }\n\n if (arg === 'ask') {\n result.command = 'ask';\n if (i + 1 < args.length && !args[i + 1].startsWith('--')) {\n result.question = args[i + 1];\n i++;\n }\n }\n\n if (arg === 'status') {\n result.command = 'status';\n }\n\n if (arg === 'reset') {\n result.command = 'reset';\n }\n\n if (arg === 'login') {\n result.command = 'login';\n }\n\n if (arg === '--timeout') {\n if (i + 1 < args.length) {\n result.timeout = parseInt(args[i + 1], 10);\n i++;\n }\n }\n\n if (arg === '--retry') {\n if (i + 1 < args.length) {\n result.retry = parseInt(args[i + 1], 10);\n i++;\n }\n }\n\n if (arg === '--headless') {\n result.headless = true;\n }\n }\n\n return result;\n}\n\n/**\n * 格式化 CLI 输出为 JSON\n */\nexport function formatCliOutput(output: CliOutput): string {\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * CLI 主入口\n */\nexport async function runCli(args: string[]): Promise<void> {\n const parsed = parseCliArgs(args);\n\n if (parsed.command === 'help' || parsed.command === null) {\n printHelp();\n return;\n }\n\n const options: YiyanAgentOptions = {};\n if (parsed.timeout) {\n options.timeout = parsed.timeout;\n }\n if (parsed.retry) {\n options.retryCount = parsed.retry;\n }\n\n const agent = new YiyanAgent(options);\n\n if (parsed.command === 'login') {\n console.log('Opening browser for login... Please login to Yiyan (文心一言) in the browser window.');\n try {\n await agent.login();\n console.log(formatCliOutput({\n success: true,\n question: 'login',\n answer: 'Login successful! You can now use \"ask\" command.',\n duration: 0,\n }));\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.log(formatCliOutput({\n success: false,\n question: 'login',\n error: errorMessage,\n duration: 0,\n }));\n }\n return;\n }\n\n if (parsed.command === 'status') {\n const status = agent.status();\n console.log(formatCliOutput({\n success: true,\n question: 'status',\n answer: JSON.stringify(status),\n duration: 0,\n }));\n return;\n }\n\n if (parsed.command === 'reset') {\n await agent.reset();\n console.log(formatCliOutput({\n success: true,\n question: 'reset',\n answer: 'Profile cleared successfully',\n duration: 0,\n }));\n return;\n }\n\n if (parsed.command === 'ask') {\n if (!parsed.question) {\n console.log(formatCliOutput({\n success: false,\n question: '',\n error: 'No question provided. Usage: ask \"your question\"',\n duration: 0,\n }));\n return;\n }\n\n const startTime = Date.now();\n try {\n const answer = await agent.ask(parsed.question, !parsed.headless);\n const duration = Date.now() - startTime;\n console.log(formatCliOutput({\n success: true,\n question: parsed.question,\n answer,\n duration,\n }));\n } catch (error) {\n const duration = Date.now() - startTime;\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.log(formatCliOutput({\n success: false,\n question: parsed.question,\n error: errorMessage,\n duration,\n }));\n }\n return;\n }\n}\n\n// 如果直接运行此文件,执行 CLI\nconst scriptPath = process.argv[1]?.replace(/\\\\/g, '/');\nconst importUrl = import.meta.url.replace(/^file:\\/\\//, '');\nif (scriptPath && (importUrl === scriptPath || importUrl === '/' + scriptPath)) {\n const args = process.argv.slice(2);\n runCli(args).catch(console.error);\n}\n"],"mappings":"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","names":["path","os","path","fs","path","os"]}
|
|
1
|
+
{"version":3,"sources":["../src/agent.ts","../src/types.ts","../src/profile.ts","../src/browser.ts","../src/cli.ts"],"sourcesContent":["// src/agent.ts\nimport path from 'path';\nimport os from 'os';\nimport {\n YiyanAgentOptions,\n DEFAULT_OPTIONS,\n YiyanAgentError,\n} from './types';\nimport {\n getChromeExecutablePath,\n clearCopiedProfile,\n profileExists,\n} from './profile';\nimport {\n launchBrowser,\n closeBrowser,\n navigateToYiyan,\n sendMessage,\n waitForReply,\n extractReply,\n checkLoggedIn,\n waitForUserAction,\n} from './browser';\n\n/** 默认 profile 存储目录 */\nconst DEFAULT_PROFILE_BASE_DIR = path.join(\n os.homedir(),\n '.yiyan-browser-agent'\n);\n\n/**\n * 文心一言浏览器代理\n */\nexport class YiyanAgent {\n private options: Required<YiyanAgentOptions>;\n private profileDir: string;\n private verbose: boolean;\n\n constructor(options?: YiyanAgentOptions & { verbose?: boolean }) {\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n\n this.profileDir =\n this.options.profileDir || DEFAULT_PROFILE_BASE_DIR;\n\n this.verbose = options?.verbose ?? true; // 默认开启日志\n }\n\n /**\n * 发送问题并获取答案\n * @param question 问题文本\n * @param headful 是否使用有头浏览器(可见窗口),默认为 false(优先 headless,登录后无需弹窗)\n * \n * 策略:先尝试 headless(不弹窗),如果失败(验证码/未登录)自动回退到有头模式\n */\n async ask(question: string, headful = false): Promise<string> {\n // 如果用户显式要求有头模式,直接有头\n if (headful) {\n return this.executeAsk(question, true);\n }\n\n // 否则先尝试 headless\n try {\n return await this.executeAsk(question, false);\n } catch (error) {\n // headless 遇到验证码或未登录,自动回退有头模式\n if (error instanceof YiyanAgentError && \n (error.type === 'CAPTCHA' || error.type === 'TIMEOUT')) {\n if (this.verbose) {\n process.stderr.write('[yiyan-agent] headless 模式失败,自动切换到有头模式...\\n');\n }\n return this.executeAsk(question, true);\n }\n throw error;\n }\n }\n\n /**\n * 执行单次问答\n */\n private async executeAsk(question: string, headful: boolean): Promise<string> {\n const platform = os.platform();\n const chromePath =\n this.options.chromePath || getChromeExecutablePath(platform);\n\n if (!chromePath) {\n throw new YiyanAgentError(\n 'BROWSER_LAUNCH',\n `Unsupported platform: ${platform}`\n );\n }\n\n // profile 目录直接使用 chrome-profile 子目录\n const profilePath = path.join(this.profileDir, 'chrome-profile');\n\n // 启动浏览器(CDP 模式)\n const { browser, cdpBrowser, page, chromeProcess } = await launchBrowser({\n chromePath,\n profilePath,\n headless: !headful,\n timeout: 30000,\n verbose: this.verbose,\n });\n\n try {\n // 导航到文心一言\n await navigateToYiyan(page, this.verbose);\n\n // 检查登录状态\n const isLoggedIn = await checkLoggedIn(page, this.verbose);\n \n if (!isLoggedIn) {\n if (headful) {\n // 有头模式:暂停等待用户手动登录\n await waitForUserAction(page, 'login', this.verbose);\n } else {\n // headless 模式未登录:抛出错误,让上层回退到有头模式\n throw new YiyanAgentError(\n 'CAPTCHA',\n 'Not logged in. Please run \"yiyan-agent login\" first, or use headed mode.'\n );\n }\n }\n\n // 发送消息\n await sendMessage(page, question, this.verbose, headful);\n\n // 等待回复\n await waitForReply(page, this.options.timeout, this.verbose, headful);\n\n // 提取回复\n const reply = await extractReply(page, question, this.verbose);\n\n return reply;\n } finally {\n await closeBrowser(browser, chromeProcess, cdpBrowser, this.verbose);\n }\n }\n\n /**\n * 登录文心一言(打开有头浏览器让用户手动登录)\n * 登录成功后,后续的 ask 调用将自动使用登录状态\n */\n async login(): Promise<void> {\n const platform = os.platform();\n const chromePath =\n this.options.chromePath || getChromeExecutablePath(platform);\n\n if (!chromePath) {\n throw new YiyanAgentError(\n 'BROWSER_LAUNCH',\n `Unsupported platform: ${platform}`\n );\n }\n\n const profilePath = path.join(this.profileDir, 'chrome-profile');\n\n // 启动有头浏览器(用户可以看到并操作)\n const { browser, cdpBrowser, page, chromeProcess } = await launchBrowser({\n chromePath,\n profilePath,\n headless: false,\n timeout: 60000,\n verbose: this.verbose,\n });\n\n try {\n await page.goto('https://yiyan.baidu.com/', {\n waitUntil: 'networkidle',\n timeout: 60000,\n });\n\n // 等待 SPA 渲染\n await page.waitForTimeout(5000);\n\n // 输出提示信息\n process.stderr.write('\\n[yiyan-agent] ============================================\\n');\n process.stderr.write('[yiyan-agent] 请在浏览器中完成登录文心一言\\n');\n process.stderr.write('[yiyan-agent] 登录成功后,回到此处按 Enter 键继续\\n');\n process.stderr.write('[yiyan-agent] ============================================\\n\\n');\n\n // 等待用户按 Enter 键确认(ESM 兼容方式)\n const readline = await import('readline');\n await new Promise<void>((resolve) => {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n rl.question('按 Enter 继续...', () => {\n rl.close();\n resolve();\n });\n });\n\n process.stderr.write('[yiyan-agent] ✓ 登录完成,正在保存登录状态...\\n');\n\n // 等待一下确保登录状态保存到 profile\n await new Promise(resolve => setTimeout(resolve, 3000));\n } finally {\n await closeBrowser(browser, chromeProcess, cdpBrowser, this.verbose);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n async reset(): Promise<void> {\n await clearCopiedProfile(this.profileDir);\n }\n\n status(): { loggedIn: boolean; profilePath: string } {\n return {\n loggedIn: profileExists(this.profileDir),\n profilePath: path.join(this.profileDir, 'chrome-profile'),\n };\n }\n}\n","/**\n * yiyan-browser-agent 类型定义\n */\n\n/** 配置选项 */\nexport interface YiyanAgentOptions {\n /** 超时毫秒,默认 120000 */\n timeout?: number;\n /** 重试次数,默认 3 */\n retryCount?: number;\n /** 自定义 profile 目录 */\n profileDir?: string;\n /** 自定义 Chrome 可执行文件路径 */\n chromePath?: string;\n}\n\n/** CLI 输出格式 */\nexport interface CliOutput {\n success: boolean;\n /** 用户发送的问题 */\n question: string;\n /** 成功时有值 */\n answer?: string;\n /** 失败时有值 */\n error?: string;\n /** 耗时毫秒 */\n duration: number;\n}\n\n/** 错误类型 */\nexport type YiyanAgentErrorType =\n | 'BROWSER_LAUNCH'\n | 'PROFILE_COPY'\n | 'TIMEOUT'\n | 'NETWORK'\n | 'CAPTCHA';\n\n/** 自定义错误类 */\nexport class YiyanAgentError extends Error {\n constructor(\n public type: YiyanAgentErrorType,\n message: string\n ) {\n super(message);\n this.name = 'YiyanAgentError';\n }\n}\n\n/** 默认配置 */\nexport const DEFAULT_OPTIONS: Required<YiyanAgentOptions> = {\n timeout: 120000,\n retryCount: 3,\n profileDir: '',\n chromePath: '',\n};\n\n/** 文心一言网页 URL */\nexport const YIYAN_CHAT_URL = 'https://yiyan.baidu.com/';\n","// src/profile.ts\nimport path from 'path';\nimport os from 'os';\nimport fs from 'fs';\nimport fsp from 'fs/promises';\nimport { YiyanAgentError } from './types';\n\n/**\n * 获取 Chrome 用户数据目录路径\n */\nexport function getChromeProfilePath(platform: NodeJS.Platform): string | null {\n const home = os.homedir();\n\n switch (platform) {\n case 'win32':\n const localAppData = process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');\n return path.join(localAppData, 'Google', 'Chrome', 'User Data');\n case 'darwin':\n return path.join(home, 'Library', 'Application Support', 'Google', 'Chrome');\n case 'linux':\n return path.join(home, '.config', 'google-chrome');\n default:\n return null;\n }\n}\n\n/**\n * 获取 Chrome 可执行文件路径\n */\nexport function getChromeExecutablePath(platform: NodeJS.Platform): string | null {\n switch (platform) {\n case 'win32':\n const programFiles = process.env.PROGRAMFILES || 'C:\\\\Program Files';\n return path.join(programFiles, 'Google', 'Chrome', 'Application', 'chrome.exe');\n case 'darwin':\n return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';\n case 'linux':\n return '/usr/bin/google-chrome';\n default:\n return null;\n }\n}\n\n/** Profile 子目录名称 */\nconst PROFILE_TARGET_NAME = 'chrome-profile';\n\n/**\n * 检查并复制 Chrome profile 到目标目录\n * 如果已存在且未过期则直接返回路径,否则重新复制\n */\nexport async function copyChromeProfile(\n platform: NodeJS.Platform,\n targetBaseDir: string\n): Promise<string> {\n const targetPath = path.join(targetBaseDir, PROFILE_TARGET_NAME);\n\n // 检查已复制的 profile 是否有效(5分钟内不需要重新复制)\n if (fs.existsSync(targetPath)) {\n const stat = fs.statSync(targetPath);\n const ageMinutes = (Date.now() - stat.mtimeMs) / 60000;\n if (ageMinutes < 5) {\n return targetPath;\n }\n // 过期了,删除旧的\n await fsp.rm(targetPath, { recursive: true, force: true });\n }\n\n // 获取源 profile 路径\n const sourcePath = getChromeProfilePath(platform);\n if (!sourcePath) {\n throw new YiyanAgentError('PROFILE_COPY', `Unsupported platform: ${platform}`);\n }\n\n // 检查源是否存在\n try {\n await fsp.access(sourcePath);\n } catch {\n throw new YiyanAgentError(\n 'PROFILE_COPY',\n `Chrome profile not found at: ${sourcePath}`\n );\n }\n\n // 创建目标目录\n await fsp.mkdir(targetPath, { recursive: true });\n\n // 复制根目录关键文件\n const rootFiles = [\n 'Local State',\n 'First Run',\n 'Default',\n ];\n\n for (const file of rootFiles) {\n const srcFile = path.join(sourcePath, file);\n const destFile = path.join(targetPath, file);\n\n try {\n const stat = await fsp.stat(srcFile);\n if (stat.isDirectory()) {\n await copyDir(srcFile, destFile);\n } else {\n await fsp.copyFile(srcFile, destFile);\n }\n } catch {\n continue;\n }\n }\n\n // 复制 Default 目录下的关键文件(只复制登录和 Cookie 相关的小文件)\n const defaultDir = path.join(sourcePath, 'Default');\n if (fs.existsSync(defaultDir)) {\n const destDefaultDir = path.join(targetPath, 'Default');\n await fsp.mkdir(destDefaultDir, { recursive: true });\n\n const defaultFiles = [\n 'Cookies',\n 'Cookies-journal',\n 'Login Data',\n 'Login Data-journal',\n 'Login Data For Account',\n 'Login Data For Account-journal',\n 'Preferences',\n 'Secure Preferences',\n 'Network',\n 'Network Action Predictor',\n 'Web Data',\n 'Web Data-journal',\n 'Extension Cookies',\n 'Extension Cookies-journal',\n ];\n\n for (const file of defaultFiles) {\n const srcFile = path.join(defaultDir, file);\n const destFile = path.join(destDefaultDir, file);\n\n try {\n const stat = await fsp.stat(srcFile);\n if (stat.isDirectory()) {\n await copyDir(srcFile, destFile);\n } else {\n await fsp.copyFile(srcFile, destFile);\n }\n } catch {\n continue;\n }\n }\n }\n\n // 更新目录修改时间\n await fsp.utimes(targetPath, new Date(), new Date());\n\n return targetPath;\n}\n\n/**\n * 复制目录\n */\nasync function copyDir(src: string, dest: string): Promise<void> {\n await fsp.mkdir(dest, { recursive: true });\n const entries = await fsp.readdir(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n\n if (entry.isDirectory()) {\n await copyDir(srcPath, destPath);\n } else {\n await fsp.copyFile(srcPath, destPath);\n }\n }\n}\n\n/**\n * 清除复制的 profile\n */\nexport async function clearCopiedProfile(profileDir: string): Promise<void> {\n const targetPath = path.join(profileDir, PROFILE_TARGET_NAME);\n if (fs.existsSync(targetPath)) {\n await fsp.rm(targetPath, { recursive: true, force: true });\n }\n}\n\n/**\n * 检查 profile 是否存在\n */\nexport function profileExists(profileDir: string): boolean {\n const targetPath = path.join(profileDir, PROFILE_TARGET_NAME);\n return fs.existsSync(targetPath);\n}\n","// src/browser.ts\nimport { chromium, Page, BrowserContext, Browser } from 'playwright-core';\nimport { YiyanAgentError, YIYAN_CHAT_URL } from './types';\nimport http from 'http';\nimport { spawn, ChildProcess, execSync } from 'child_process';\nimport path from 'path';\nimport os from 'os';\nimport fs from 'fs';\n\nexport interface LaunchBrowserOptions {\n chromePath: string;\n profilePath: string;\n headless: boolean;\n timeout?: number;\n /** 是否输出进度日志 */\n verbose?: boolean;\n}\n\nconst CDP_PORT = 19222;\n\n/** 调试截图保存目录 */\nconst DEBUG_DIR = path.join(os.homedir(), '.yiyan-browser-agent', 'debug');\n\n/** 日志输出(仅 verbose 模式) */\nfunction log(verbose: boolean, msg: string): void {\n if (verbose) {\n process.stderr.write(`[yiyan-agent] ${msg}\\n`);\n }\n}\n\n/** 确保调试目录存在 */\nfunction ensureDebugDir(): void {\n if (!fs.existsSync(DEBUG_DIR)) {\n fs.mkdirSync(DEBUG_DIR, { recursive: true });\n }\n}\n\n/**\n * 杀死占用指定端口的进程(启动前清理残留 Chrome)\n */\nfunction killProcessOnPort(port: number): void {\n try {\n if (process.platform === 'win32') {\n const output = execSync(\n `netstat -ano | findstr :${port} | findstr LISTENING`,\n { encoding: 'utf-8', timeout: 5000 }\n ).trim();\n if (output) {\n const lines = output.split('\\n').filter(l => l.trim());\n const pids = new Set<string>();\n for (const line of lines) {\n const parts = line.trim().split(/\\s+/);\n const pid = parts[parts.length - 1];\n if (pid && /^\\d+$/.test(pid)) pids.add(pid);\n }\n for (const pid of pids) {\n try {\n execSync(`taskkill /F /T /PID ${pid}`, { timeout: 5000 });\n } catch {\n // 进程可能已经退出\n }\n }\n }\n } else {\n const output = execSync(\n `lsof -ti :${port}`,\n { encoding: 'utf-8', timeout: 5000 }\n ).trim();\n if (output) {\n const pids = output.split('\\n').filter(l => l.trim());\n for (const pid of pids) {\n try {\n process.kill(parseInt(pid, 10), 'SIGKILL');\n } catch {\n // 进程可能已经退出\n }\n }\n }\n }\n } catch {\n // 没有找到占用端口的进程,正常情况\n }\n}\n\n/**\n * 杀死进程树(Windows 用 taskkill /T,其他平台用 process.kill 负 PID)\n */\nfunction killProcessTree(pid: number): void {\n try {\n if (process.platform === 'win32') {\n execSync(`taskkill /F /T /PID ${pid}`, { timeout: 5000 });\n } else {\n try {\n process.kill(-pid, 'SIGKILL');\n } catch {\n process.kill(pid, 'SIGKILL');\n }\n }\n } catch {\n // 进程可能已经退出\n }\n}\n\n/**\n * 等待 CDP 端口就绪\n */\nfunction waitForCDP(port: number, timeout = 15000): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const start = Date.now();\n const check = () => {\n http.get(`http://localhost:${port}/json/version`, (res) => {\n let data = '';\n res.on('data', chunk => data += chunk);\n res.on('end', () => {\n try { resolve(JSON.parse(data)); } catch (e) { reject(e); }\n });\n }).on('error', () => {\n if (Date.now() - start > timeout) reject(new Error('CDP connection timeout'));\n else setTimeout(check, 500);\n });\n };\n check();\n });\n}\n\n/**\n * 启动 Chrome 浏览器(通过 CDP 连接模式)\n */\nexport async function launchBrowser(\n options: LaunchBrowserOptions\n): Promise<{ browser: BrowserContext; cdpBrowser: Browser; page: Page; chromeProcess: ChildProcess | null }> {\n const { chromePath, profilePath, headless, timeout = 30000, verbose = false } = options;\n\n try {\n log(verbose, '清理残留 Chrome 进程...');\n killProcessOnPort(CDP_PORT);\n await new Promise(resolve => setTimeout(resolve, 500));\n\n const chromeArgs = [\n `--remote-debugging-port=${CDP_PORT}`,\n `--user-data-dir=${profilePath}`,\n '--no-first-run',\n '--no-default-browser-check',\n '--disable-blink-features=AutomationControlled',\n ];\n\n if (headless) {\n chromeArgs.push('--headless=new');\n }\n\n log(verbose, `启动 Chrome: ${chromePath} ${headless ? '(headless)' : '(headed)'}`);\n const chromeProcess = spawn(chromePath, chromeArgs, {\n detached: true,\n stdio: 'ignore',\n });\n chromeProcess.unref();\n\n const spawnError = new Promise<never>((_, reject) => {\n chromeProcess.on('error', (err) => {\n reject(new YiyanAgentError('BROWSER_LAUNCH', `Failed to spawn Chrome: ${err.message}`));\n });\n });\n\n log(verbose, '等待 CDP 端口就绪...');\n await Promise.race([\n waitForCDP(CDP_PORT, timeout),\n spawnError,\n ]);\n\n log(verbose, '通过 CDP 连接浏览器...');\n const cdpBrowser = await chromium.connectOverCDP(`http://localhost:${CDP_PORT}`);\n\n const contexts = cdpBrowser.contexts();\n const context = contexts.length > 0 ? contexts[0] : await cdpBrowser.newContext();\n context.setDefaultTimeout(timeout);\n const pages = context.pages();\n const page = pages.length > 0 ? pages[0] : await context.newPage();\n\n log(verbose, '浏览器启动完成');\n return { browser: context, cdpBrowser, page, chromeProcess };\n } catch (error) {\n killProcessOnPort(CDP_PORT);\n throw new YiyanAgentError(\n 'BROWSER_LAUNCH',\n `Failed to launch browser: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * 关闭浏览器(彻底清理:断开 CDP → 关闭页面 → 杀进程树 → 清理残留端口)\n */\nexport async function closeBrowser(\n browser: BrowserContext,\n chromeProcess: ChildProcess | null,\n cdpBrowser?: Browser,\n verbose?: boolean\n): Promise<void> {\n log(!!verbose, '关闭浏览器...');\n\n if (cdpBrowser) {\n try {\n await cdpBrowser.close();\n } catch {\n // 忽略关闭错误\n }\n }\n\n try {\n const pages = browser.pages();\n for (const page of pages) {\n await page.close().catch(() => {});\n }\n } catch {\n // 忽略关闭错误\n }\n\n if (chromeProcess && chromeProcess.pid) {\n killProcessTree(chromeProcess.pid);\n }\n\n killProcessOnPort(CDP_PORT);\n log(!!verbose, '浏览器已关闭');\n}\n\n/**\n * 导航到文心一言聊天页面\n */\nexport async function navigateToYiyan(page: Page, verbose = false): Promise<void> {\n log(verbose, '导航到文心一言聊天页面...');\n await page.goto(YIYAN_CHAT_URL, {\n waitUntil: 'networkidle',\n timeout: 60000,\n });\n\n // 文心一言是 SPA,需要额外等待渲染\n await page.waitForTimeout(5000);\n\n try {\n await page.waitForSelector('[contenteditable=\"true\"]', { timeout: 15000 });\n log(verbose, '页面加载完成,输入框已就绪');\n } catch {\n log(verbose, '警告:未检测到输入框,继续执行');\n }\n}\n\n/**\n * 检测文心一言验证码弹窗\n */\nexport async function checkCaptcha(page: Page, verbose = false): Promise<boolean> {\n const captchaIndicators = [\n '请完成下列验证后继续',\n '按住左边按钮拖动',\n '滑动验证',\n '点击验证',\n '安全验证',\n 'captcha',\n '请完成验证',\n ];\n\n const hasCaptcha = await page.evaluate((indicators) => {\n const bodyText = document.body.innerText || '';\n for (const indicator of indicators) {\n if (bodyText.includes(indicator)) return true;\n }\n // 检查是否有弹窗/遮罩层\n const dialogs = document.querySelectorAll('[role=\"dialog\"], [class*=\"captcha\"], [class*=\"verify\"]');\n if (dialogs.length > 0) return true;\n return false;\n }, captchaIndicators);\n\n if (hasCaptcha) {\n log(verbose, '⚠ 检测到验证码弹窗!');\n }\n return hasCaptcha;\n}\n\n/**\n * 检测是否已登录文心一言\n */\nexport async function checkLoggedIn(page: Page, verbose = false): Promise<boolean> {\n const isLoggedIn = await page.evaluate(() => {\n // 文心一言未登录时:页面有\"登录\"按钮和\"未登录\"文字\n const bodyText = document.body.innerText || '';\n\n // 如果有\"未登录\"文字 = 未登录\n if (bodyText.includes('未登录')) return false;\n\n // 检查是否有\"登录\"按钮\n const loginButtons = document.querySelectorAll('button');\n for (const btn of loginButtons) {\n const text = btn.textContent?.trim() || '';\n if (text === '登录') {\n return false; // 有登录按钮 = 未登录\n }\n }\n\n // 如果没有\"登录\"按钮和\"未登录\"文字,说明已登录\n return true;\n });\n\n if (isLoggedIn) {\n log(verbose, '✓ 已检测到登录状态');\n } else {\n log(verbose, '⚠ 未检测到登录状态');\n }\n return isLoggedIn;\n}\n\n/**\n * 有头模式下,暂停等待用户手动操作(过验证码/登录),然后继续\n * 检测到问题已解决后自动继续\n */\nexport async function waitForUserAction(\n page: Page,\n reason: 'captcha' | 'login' | 'no-reply',\n verbose = false\n): Promise<void> {\n const reasonText = reason === 'captcha'\n ? '检测到验证码,请在浏览器中手动完成验证'\n : reason === 'login'\n ? '检测到未登录,请在浏览器中手动登录'\n : 'AI 未回复,请检查浏览器是否需要手动操作(验证码/登录)';\n\n process.stderr.write(`\\n[yiyan-agent] ⚠ ${reasonText}\\n`);\n process.stderr.write('[yiyan-agent] 等待您操作完成...(操作完成后会自动继续)\\n\\n');\n\n // 轮询检测问题是否已解决\n const maxWait = 180000; // 最多等 3 分钟\n const startTime = Date.now();\n\n while (Date.now() - startTime < maxWait) {\n await new Promise(resolve => setTimeout(resolve, 2000));\n\n if (reason === 'captcha') {\n const stillHasCaptcha = await checkCaptcha(page, false);\n if (!stillHasCaptcha) {\n log(verbose, '✓ 验证码已通过!');\n return;\n }\n } else if (reason === 'login') {\n const loggedIn = await checkLoggedIn(page, false);\n if (loggedIn) {\n log(verbose, '✓ 登录成功!');\n return;\n }\n } else {\n // no-reply:检测 AI 回复是否出现\n const hasReply = await page.evaluate(() => {\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (!answerBox) return false;\n const text = answerBox.innerText?.trim() || '';\n return text.length > 0;\n });\n if (hasReply) {\n log(verbose, '✓ AI 已开始回复!');\n return;\n }\n }\n }\n\n log(verbose, '⚠ 等待用户操作超时(3分钟)');\n}\n\n/**\n * 发送消息到文心一言\n * @param headful 是否为有头模式(有头模式下检测到验证码会暂停等待用户操作)\n */\nexport async function sendMessage(page: Page, message: string, verbose = false, headful = false): Promise<void> {\n // 文心一言使用 contenteditable div 而不是 textarea\n const inputSelector = '[contenteditable=\"true\"]';\n\n log(verbose, '等待输入框出现...');\n const inputElement = await page.waitForSelector(inputSelector, { timeout: 15000 });\n if (!inputElement) {\n log(verbose, '⚠ 输入框未找到!');\n throw new YiyanAgentError('NETWORK', 'Input element not found on page');\n }\n log(verbose, '✓ 输入框已找到');\n\n // 点击输入框获取焦点\n await inputElement.click();\n await page.waitForTimeout(500);\n\n // 输入问题(文心一言用 contenteditable,需要用 keyboard.type 而不是 fill)\n log(verbose, `输入问题: \"${message}\"`);\n await page.keyboard.type(message, { delay: 30 });\n\n // 验证填入是否成功\n const filledValue = await inputElement.innerText();\n if (filledValue.includes(message)) {\n log(verbose, '✓ 问题已成功填入输入框');\n } else {\n log(verbose, `⚠ 输入框内容: \"${filledValue.substring(0, 50)}\"`);\n }\n\n // 按 Enter 发送\n log(verbose, '按 Enter 发送消息...');\n await page.keyboard.press('Enter');\n\n // 等待发送后的页面变化\n await page.waitForTimeout(3000);\n\n // 检查是否触发了验证码\n if (await checkCaptcha(page, verbose)) {\n if (headful) {\n await waitForUserAction(page, 'captcha', verbose);\n // 验证码通过后,重新输入并发送消息\n log(verbose, '验证码已通过,重新发送消息...');\n const inputEl = await page.waitForSelector(inputSelector, { timeout: 10000 });\n if (inputEl) {\n await inputEl.click();\n await page.waitForTimeout(300);\n await page.keyboard.type(message, { delay: 30 });\n await page.keyboard.press('Enter');\n await page.waitForTimeout(3000);\n }\n } else {\n throw new YiyanAgentError(\n 'CAPTCHA',\n 'Yiyan detected automation and triggered a captcha. Use headed mode (default) to manually solve it, or run \"login\" first.'\n );\n }\n }\n\n // 检查 URL 变化(文心一言发送消息后会跳转到 /chat/xxx)\n const url = page.url();\n log(verbose, `当前 URL: ${url}`);\n\n if (url.includes('/chat/')) {\n log(verbose, '✓ 已自动进入对话页面');\n } else if (url === 'https://yiyan.baidu.com/' || url === 'https://yiyan.baidu.com') {\n log(verbose, '仍在欢迎页,尝试点击发送按钮...');\n try {\n // 点击发送按钮\n const sendBtn = page.locator('[class*=\"send__\"]').first();\n await sendBtn.click({ timeout: 5000 });\n await page.waitForTimeout(3000);\n log(verbose, '✓ 已点击发送按钮');\n } catch {\n log(verbose, '⚠ 点击发送按钮失败,继续等待回复...');\n }\n } else {\n log(verbose, `当前页面: ${url}`);\n }\n}\n\n/**\n * 保存调试截图\n */\nasync function saveDebugScreenshot(page: Page, name: string, verbose: boolean): Promise<void> {\n try {\n ensureDebugDir();\n const screenshotPath = path.join(DEBUG_DIR, `${name}.png`);\n await page.screenshot({ path: screenshotPath, fullPage: true });\n log(verbose, `调试截图已保存: ${screenshotPath}`);\n } catch {\n // 截图失败不影响主流程\n }\n}\n\n/**\n * 等待回复完成\n * @param headful 是否为有头模式(超时且有头模式下会等待用户手动操作)\n */\nexport async function waitForReply(\n page: Page,\n timeout: number,\n verbose = false,\n headful = false\n): Promise<void> {\n const maxWait = Math.min(timeout, 60000);\n\n log(verbose, `等待 AI 回复(最多 ${maxWait / 1000} 秒)...`);\n\n // 文心一言 AI 回复在 answerBox 容器中,流式渲染\n const startTime = Date.now();\n let lastLen = 0;\n let stableCount = 0;\n let replyStarted = false;\n\n while (Date.now() - startTime < maxWait) {\n const state = await page.evaluate(() => {\n // 文心一言 AI 回复在 answerBox 或 dialogueCardList 中\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n return {\n hasAiReply: text.length > 0,\n aiTextLen: text.length,\n aiPreview: text.substring(0, 100),\n };\n }\n\n // 备选:检查 dialogueCardList\n const cardList = document.querySelector('[class*=\"dialogueCardList\"]');\n if (cardList) {\n const lastCard = cardList.lastElementChild as HTMLElement;\n if (lastCard) {\n const text = lastCard.innerText?.trim() || '';\n return {\n hasAiReply: text.length > 0,\n aiTextLen: text.length,\n aiPreview: text.substring(0, 100),\n };\n }\n }\n\n return { hasAiReply: false, aiTextLen: 0, aiPreview: '' };\n });\n\n if (state.hasAiReply && state.aiTextLen > 0) {\n if (!replyStarted) {\n log(verbose, `✓ AI 回复已开始生成(${state.aiTextLen}字): ${state.aiPreview.substring(0, 60)}...`);\n replyStarted = true;\n }\n\n // 文本长度不再变化 → 回复完成\n if (state.aiTextLen === lastLen) {\n stableCount++;\n if (stableCount >= 3) {\n log(verbose, `✓ AI 回复已完成(${state.aiTextLen}字,连续 ${stableCount} 次长度不变)`);\n break;\n }\n } else {\n stableCount = 0;\n lastLen = state.aiTextLen;\n }\n }\n\n await page.waitForTimeout(1500);\n }\n\n if (!replyStarted) {\n // 超时后检查是否触发了验证码\n if (await checkCaptcha(page, verbose)) {\n if (headful) {\n await waitForUserAction(page, 'captcha', verbose);\n const retryStart = Date.now();\n const retryMaxWait = 30000;\n while (Date.now() - retryStart < retryMaxWait) {\n const retryState = await page.evaluate(() => {\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n return { hasAiReply: text.length > 0, aiTextLen: text.length };\n }\n return { hasAiReply: false, aiTextLen: 0 };\n });\n if (retryState.hasAiReply) {\n log(verbose, '✓ 验证码通过后 AI 已开始回复');\n replyStarted = true;\n break;\n }\n await page.waitForTimeout(1500);\n }\n } else {\n throw new YiyanAgentError(\n 'CAPTCHA',\n 'Yiyan detected automation and triggered a captcha. Use --headful flag to manually solve it, or run \"login\" first.'\n );\n }\n } else if (headful) {\n await waitForUserAction(page, 'no-reply', verbose);\n const retryStart = Date.now();\n const retryMaxWait = 60000;\n while (Date.now() - retryStart < retryMaxWait) {\n const retryState = await page.evaluate(() => {\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n return { hasAiReply: text.length > 0, aiTextLen: text.length };\n }\n return { hasAiReply: false, aiTextLen: 0 };\n });\n if (retryState.hasAiReply) {\n if (retryState.aiTextLen === lastLen) {\n stableCount++;\n if (stableCount >= 3) {\n log(verbose, `✓ 用户操作后 AI 回复已完成(${retryState.aiTextLen}字)`);\n replyStarted = true;\n break;\n }\n } else {\n stableCount = 0;\n lastLen = retryState.aiTextLen;\n }\n }\n await page.waitForTimeout(1500);\n }\n } else {\n // headless 模式超时且无验证码 → 可能是未登录或其他问题\n throw new YiyanAgentError(\n 'TIMEOUT',\n 'AI reply timeout in headless mode. Try running \"yiyan-agent login\" first, or use --headful flag.'\n );\n }\n }\n\n // 额外等待一小段时间确保最终渲染\n await page.waitForTimeout(2000);\n\n // 页面诊断\n const pageDump = await page.evaluate(() => {\n const result: Record<string, any> = {};\n\n const selectors = [\n '[class*=\"answerBox\"]', '[class*=\"dialogueCardList\"]', '[class*=\"dialogue_card_item\"]',\n '[class*=\"chatViewer\"]', '[class*=\"flowBox\"]', '[class*=\"roleSystem\"]',\n '[class*=\"mdRenderContainer\"]', '[class*=\"agent-markdown\"]', '[class*=\"markdown\"]',\n '[class*=\"content\"]', '[class*=\"chat\"]', '[class*=\"message\"]',\n ];\n for (const sel of selectors) {\n result[`selector:${sel}`] = document.querySelectorAll(sel).length;\n }\n\n // answerBox 子元素详情\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n result['answerBox'] = {\n textLen: answerBox.innerText?.trim().length || 0,\n preview: (answerBox.innerText?.trim() || '').substring(0, 200),\n classes: answerBox.className?.toString().substring(0, 100) || '',\n };\n }\n\n // body 文本(前 1000 字符)\n result['bodyText'] = document.body.innerText?.substring(0, 1000) || '';\n\n // 长文本元素\n const longTextEls: { tag: string; classes: string; textLen: number; preview: string }[] = [];\n const mainContent = document.querySelector('[class*=\"chatViewer\"]') || document.querySelector('[class*=\"chat\"]') || document.body;\n for (const el of Array.from(mainContent.querySelectorAll('div, section, article'))) {\n const text = (el as HTMLElement).innerText?.trim() || '';\n if (text.length > 50 && text.length < 10000) {\n longTextEls.push({\n tag: el.tagName,\n classes: (el as HTMLElement).className?.toString().split(' ').slice(0, 3).join(' ') || '',\n textLen: text.length,\n preview: text.substring(0, 80),\n });\n }\n }\n result['longTextElements'] = longTextEls\n .sort((a, b) => b.textLen - a.textLen)\n .slice(0, 10);\n\n return result;\n });\n\n // 输出诊断\n log(verbose, '=== 页面诊断 ===');\n for (const [key, value] of Object.entries(pageDump)) {\n if (key === 'bodyText' || key === 'longTextElements') continue;\n log(verbose, ` ${key}: ${JSON.stringify(value)}`);\n }\n if (pageDump['longTextElements'] && (pageDump['longTextElements'] as any[]).length > 0) {\n log(verbose, ' 长文本元素 TOP 10:');\n for (const el of pageDump['longTextElements'] as any[]) {\n log(verbose, ` <${el.tag}> .${el.classes} (${el.textLen}字): ${el.preview}...`);\n }\n }\n if (pageDump['bodyText']) {\n log(verbose, ` 页面文本(前1000字): ${(pageDump['bodyText'] as string).substring(0, 1000)}`);\n }\n}\n\n/**\n * 提取回复内容\n */\nexport async function extractReply(page: Page, question: string, verbose = false): Promise<string> {\n log(verbose, '提取 AI 回复内容...');\n\n // 先保存截图,方便排查问题\n await saveDebugScreenshot(page, 'before-extract', verbose);\n\n const reply = await page.evaluate((userQuestion: string) => {\n const debugInfo: string[] = [];\n\n // ===== 方法1(最佳):answerBox 容器 =====\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n debugInfo.push(`answerBox found: ${!!answerBox}`);\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n debugInfo.push(`answerBox textLen: ${text.length}`);\n if (text.length > 0) {\n // 去除思考过程,只取最终回复\n // 文心一言思考过程在 processItem 中,最终回复在后面\n // 尝试只取 agent-markdown 或 mdRenderContainer 的内容\n const mdContainer = answerBox.querySelector('[class*=\"agent-markdown\"], [class*=\"mdRenderContainer\"]');\n if (mdContainer) {\n const mdText = mdContainer.innerText?.trim() || '';\n if (mdText.length > 0) {\n debugInfo.push(`method1 markdown success: ${mdText.length} chars`);\n return JSON.stringify({ text: mdText, debug: debugInfo });\n }\n }\n\n debugInfo.push(`method1 answerBox success: ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法2:dialogueCardList 中最后一个对话卡片 =====\n const cardList = document.querySelector('[class*=\"dialogueCardList\"]');\n debugInfo.push(`dialogueCardList found: ${!!cardList}`);\n if (cardList && cardList.children.length > 0) {\n // 最后一个卡片通常是 AI 回复\n const lastCard = cardList.lastElementChild as HTMLElement;\n const text = lastCard?.innerText?.trim() || '';\n debugInfo.push(`lastCard textLen: ${text.length}`);\n if (text.length > 0) {\n debugInfo.push(`method2 success: ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法3:flowBox(answerBox 的父级容器) =====\n const flowBoxes = document.querySelectorAll('[class*=\"flowBox\"]');\n debugInfo.push(`flowBoxes count: ${flowBoxes.length}`);\n for (let i = flowBoxes.length - 1; i >= 0; i--) {\n const box = flowBoxes[i] as HTMLElement;\n const text = box.innerText?.trim() || '';\n if (text.length > 10) {\n debugInfo.push(`method3 success: flowBox[${i}] ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法4:roleSystemBot(AI 回复角色标识的父容器) =====\n const botContainers = document.querySelectorAll('[class*=\"roleSystemBot\"]');\n debugInfo.push(`roleSystemBot count: ${botContainers.length}`);\n for (let i = botContainers.length - 1; i >= 0; i--) {\n const container = botContainers[i] as HTMLElement;\n const text = container.innerText?.trim() || '';\n if (text.length > 10) {\n debugInfo.push(`method4 success: botContainer[${i}] ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法5:mdRenderContainer / agent-markdown =====\n const mdContainers = document.querySelectorAll('[class*=\"mdRenderContainer\"], [class*=\"agent-markdown\"]');\n debugInfo.push(`md containers count: ${mdContainers.length}`);\n for (let i = mdContainers.length - 1; i >= 0; i--) {\n const container = mdContainers[i] as HTMLElement;\n const text = container.innerText?.trim() || '';\n if (text.length > 10) {\n debugInfo.push(`method5 success: mdContainer[${i}] ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法6:页面文本解析(fallback)=====\n const fullText = document.body.innerText;\n const lines = fullText.split('\\n').map(l => l.trim()).filter(l => l.length > 0);\n\n const uiWords = new Set([\n '文心一言', '新对话', '创意写作', '智慧绘图', '超级智能体', '更多',\n '我的收藏', '项目', '对话', '暂无记录', '未登录', '登录',\n '内容由AI生成,仅供参考,请仔细甄别', '参考',\n '深度分析需求并解答', '通用写作', '阅读分析',\n '网页工坊', '智能翻译', '代码编程',\n ]);\n\n const userQIdx = lines.findIndex(l => l === userQuestion || l.includes(userQuestion));\n debugInfo.push(`userQ idx in body text: ${userQIdx}, total lines: ${lines.length}`);\n\n if (userQIdx >= 0) {\n const replyLines: string[] = [];\n for (let i = userQIdx + 1; i < lines.length; i++) {\n const line = lines[i];\n if (line.length > 0 && !uiWords.has(line) && !line.startsWith('搜索') && !line.includes('篇资料')) {\n replyLines.push(line);\n }\n if (line === '快速' || line === '更多' || line.includes('内容由AI生成')) break;\n }\n if (replyLines.length > 0) {\n debugInfo.push(`method6 success: ${replyLines.length} lines`);\n return JSON.stringify({ text: replyLines.join('\\n'), debug: debugInfo });\n }\n }\n\n // ===== 方法7:取页面中最长的非 UI 文本块 =====\n const allTextBlocks: { text: string; length: number }[] = [];\n const blockElements = document.querySelectorAll('div, section, article, p, span');\n for (const el of blockElements) {\n const elText = (el as HTMLElement).innerText?.trim() || '';\n if (elText.length > 50 && elText.length < 10000) {\n if (!uiWords.has(elText)) {\n allTextBlocks.push({ text: elText, length: elText.length });\n }\n }\n }\n allTextBlocks.sort((a, b) => b.length - a.length);\n if (allTextBlocks.length > 0) {\n debugInfo.push(`method7 fallback: longest text block ${allTextBlocks[0].length} chars`);\n return JSON.stringify({ text: allTextBlocks[0].text, debug: debugInfo });\n }\n\n // ===== 全部失败 =====\n debugInfo.push(`ALL METHODS FAILED`);\n debugInfo.push(`body text length: ${fullText.length}`);\n debugInfo.push(`body text first 500: ${fullText.substring(0, 500)}`);\n return JSON.stringify({ text: '', debug: debugInfo });\n }, question);\n\n // 解析返回的 JSON\n let parsed: { text: string; debug: string[] };\n try {\n parsed = JSON.parse(reply);\n } catch {\n parsed = { text: reply, debug: [] };\n }\n\n // 输出调试信息\n for (const line of parsed.debug) {\n log(verbose, ` [extract] ${line}`);\n }\n\n if (parsed.text && parsed.text.length > 0) {\n log(verbose, `提取成功,回复长度: ${parsed.text.length} 字符`);\n return parsed.text;\n }\n\n log(verbose, '提取失败,所有方法均未找到回复内容');\n throw new YiyanAgentError('TIMEOUT', 'Failed to extract reply content');\n}\n","/**\n * CLI 命令行工具\n */\nimport { YiyanAgent } from './agent';\nimport { CliOutput, YiyanAgentOptions } from './types';\n\n/** CLI 帮助信息 */\nexport function printHelp(): void {\n console.log(`\nyiyan-agent - 文心一言浏览器代理 CLI\n\n用法:\n yiyan-agent login 首次登录文心一言(会打开浏览器窗口)\n yiyan-agent ask \"问题\" [--timeout ms] [--retry n] [--headful]\n yiyan-agent status 检查登录状态\n yiyan-agent reset 清除保存的 profile\n\n命令:\n login 打开浏览器手动登录文心一言(首次使用必须先登录)\n ask 发送问题并获取答案(默认无头模式,不弹窗;登录后直接使用)\n status 检查登录状态\n reset 清除保存的 profile\n\n选项:\n --timeout <ms> 超时时间(毫秒),默认 120000\n --retry <n> 重试次数,默认 3\n --headful 使用有头浏览器(可见窗口,用于手动过验证码)\n --help 显示帮助信息\n\n示例:\n yiyan-agent login # 首次使用:登录文心一言\n yiyan-agent ask \"什么是 TypeScript?\" # 提问(默认无头,不弹窗)\n yiyan-agent ask \"解释 Promise\" --timeout 60000 --retry 5\n yiyan-agent ask \"30+30=\" --headful # 有头模式(可手动过验证码)\n yiyan-agent status\n yiyan-agent reset\n\n流程:\n 1. 先运行 yiyan-agent login 登录(只需一次,登录状态会保存)\n 2. 之后直接 yiyan-agent ask \"问题\" 即可,无需再登录\n 3. 如果遇到验证码,加 --headful 切换有头模式手动处理\n`);\n}\n\n/** 解析后的 CLI 参数 */\ninterface ParsedArgs {\n command: 'ask' | 'status' | 'reset' | 'login' | 'help' | null;\n question?: string;\n timeout?: number;\n retry?: number;\n headful?: boolean;\n}\n\n/**\n * 解析 CLI 参数\n */\nexport function parseCliArgs(args: string[]): ParsedArgs {\n const result: ParsedArgs = {\n command: null,\n };\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n\n if (arg === '--help') {\n result.command = 'help';\n return result;\n }\n\n if (arg === 'ask') {\n result.command = 'ask';\n if (i + 1 < args.length && !args[i + 1].startsWith('--')) {\n result.question = args[i + 1];\n i++;\n }\n }\n\n if (arg === 'status') {\n result.command = 'status';\n }\n\n if (arg === 'reset') {\n result.command = 'reset';\n }\n\n if (arg === 'login') {\n result.command = 'login';\n }\n\n if (arg === '--timeout') {\n if (i + 1 < args.length) {\n result.timeout = parseInt(args[i + 1], 10);\n i++;\n }\n }\n\n if (arg === '--retry') {\n if (i + 1 < args.length) {\n result.retry = parseInt(args[i + 1], 10);\n i++;\n }\n }\n\n if (arg === '--headful') {\n result.headful = true;\n }\n }\n\n return result;\n}\n\n/**\n * 格式化 CLI 输出为 JSON\n */\nexport function formatCliOutput(output: CliOutput): string {\n return JSON.stringify(output, null, 2);\n}\n\n/**\n * CLI 主入口\n */\nexport async function runCli(args: string[]): Promise<void> {\n const parsed = parseCliArgs(args);\n\n if (parsed.command === 'help' || parsed.command === null) {\n printHelp();\n return;\n }\n\n const options: YiyanAgentOptions = {};\n if (parsed.timeout) {\n options.timeout = parsed.timeout;\n }\n if (parsed.retry) {\n options.retryCount = parsed.retry;\n }\n\n const agent = new YiyanAgent(options);\n\n if (parsed.command === 'login') {\n console.log('Opening browser for login... Please login to Yiyan (文心一言) in the browser window.');\n try {\n await agent.login();\n console.log(formatCliOutput({\n success: true,\n question: 'login',\n answer: 'Login successful! You can now use \"ask\" command.',\n duration: 0,\n }));\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.log(formatCliOutput({\n success: false,\n question: 'login',\n error: errorMessage,\n duration: 0,\n }));\n }\n return;\n }\n\n if (parsed.command === 'status') {\n const status = agent.status();\n console.log(formatCliOutput({\n success: true,\n question: 'status',\n answer: JSON.stringify(status),\n duration: 0,\n }));\n return;\n }\n\n if (parsed.command === 'reset') {\n await agent.reset();\n console.log(formatCliOutput({\n success: true,\n question: 'reset',\n answer: 'Profile cleared successfully',\n duration: 0,\n }));\n return;\n }\n\n if (parsed.command === 'ask') {\n if (!parsed.question) {\n console.log(formatCliOutput({\n success: false,\n question: '',\n error: 'No question provided. Usage: ask \"your question\"',\n duration: 0,\n }));\n return;\n }\n\n const startTime = Date.now();\n try {\n const answer = await agent.ask(parsed.question, !!parsed.headful);\n const duration = Date.now() - startTime;\n console.log(formatCliOutput({\n success: true,\n question: parsed.question,\n answer,\n duration,\n }));\n } catch (error) {\n const duration = Date.now() - startTime;\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.log(formatCliOutput({\n success: false,\n question: parsed.question,\n error: errorMessage,\n duration,\n }));\n }\n return;\n }\n}\n\n// 如果直接运行此文件,执行 CLI\nconst scriptPath = process.argv[1]?.replace(/\\\\/g, '/');\nconst importUrl = import.meta.url.replace(/^file:\\/\\//, '');\nif (scriptPath && (importUrl === scriptPath || importUrl === '/' + scriptPath)) {\n const args = process.argv.slice(2);\n runCli(args).catch(console.error);\n}\n"],"mappings":"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","names":["path","os","path","fs","path","os"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,9 @@ declare class YiyanAgent {
|
|
|
14
14
|
/**
|
|
15
15
|
* 发送问题并获取答案
|
|
16
16
|
* @param question 问题文本
|
|
17
|
-
* @param headful 是否使用有头浏览器(可见窗口),默认为
|
|
17
|
+
* @param headful 是否使用有头浏览器(可见窗口),默认为 false(优先 headless,登录后无需弹窗)
|
|
18
|
+
*
|
|
19
|
+
* 策略:先尝试 headless(不弹窗),如果失败(验证码/未登录)自动回退到有头模式
|
|
18
20
|
*/
|
|
19
21
|
ask(question: string, headful?: boolean): Promise<string>;
|
|
20
22
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
-
}) : x)(function(x) {
|
|
5
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
-
});
|
|
8
2
|
|
|
9
3
|
// src/agent.ts
|
|
10
4
|
import path3 from "path";
|
|
@@ -461,7 +455,7 @@ async function waitForReply(page, timeout, verbose = false, headful = false) {
|
|
|
461
455
|
} else {
|
|
462
456
|
throw new YiyanAgentError(
|
|
463
457
|
"CAPTCHA",
|
|
464
|
-
'Yiyan detected automation and triggered a captcha. Use
|
|
458
|
+
'Yiyan detected automation and triggered a captcha. Use --headful flag to manually solve it, or run "login" first.'
|
|
465
459
|
);
|
|
466
460
|
}
|
|
467
461
|
} else if (headful) {
|
|
@@ -493,7 +487,10 @@ async function waitForReply(page, timeout, verbose = false, headful = false) {
|
|
|
493
487
|
await page.waitForTimeout(1500);
|
|
494
488
|
}
|
|
495
489
|
} else {
|
|
496
|
-
|
|
490
|
+
throw new YiyanAgentError(
|
|
491
|
+
"TIMEOUT",
|
|
492
|
+
'AI reply timeout in headless mode. Try running "yiyan-agent login" first, or use --headful flag.'
|
|
493
|
+
);
|
|
497
494
|
}
|
|
498
495
|
}
|
|
499
496
|
await page.waitForTimeout(2e3);
|
|
@@ -717,40 +714,30 @@ var YiyanAgent = class {
|
|
|
717
714
|
/**
|
|
718
715
|
* 发送问题并获取答案
|
|
719
716
|
* @param question 问题文本
|
|
720
|
-
* @param headful 是否使用有头浏览器(可见窗口),默认为
|
|
717
|
+
* @param headful 是否使用有头浏览器(可见窗口),默认为 false(优先 headless,登录后无需弹窗)
|
|
718
|
+
*
|
|
719
|
+
* 策略:先尝试 headless(不弹窗),如果失败(验证码/未登录)自动回退到有头模式
|
|
721
720
|
*/
|
|
722
|
-
async ask(question, headful =
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
if (error instanceof YiyanAgentError) {
|
|
733
|
-
lastError = error;
|
|
734
|
-
if (lastError.type === "CAPTCHA") {
|
|
735
|
-
throw lastError;
|
|
736
|
-
}
|
|
737
|
-
} else {
|
|
738
|
-
lastError = new YiyanAgentError(
|
|
739
|
-
"NETWORK",
|
|
740
|
-
error instanceof Error ? error.message : String(error)
|
|
741
|
-
);
|
|
742
|
-
}
|
|
743
|
-
if (attempt < this.options.retryCount - 1) {
|
|
744
|
-
await this.sleep(2e3);
|
|
721
|
+
async ask(question, headful = false) {
|
|
722
|
+
if (headful) {
|
|
723
|
+
return this.executeAsk(question, true);
|
|
724
|
+
}
|
|
725
|
+
try {
|
|
726
|
+
return await this.executeAsk(question, false);
|
|
727
|
+
} catch (error) {
|
|
728
|
+
if (error instanceof YiyanAgentError && (error.type === "CAPTCHA" || error.type === "TIMEOUT")) {
|
|
729
|
+
if (this.verbose) {
|
|
730
|
+
process.stderr.write("[yiyan-agent] headless \u6A21\u5F0F\u5931\u8D25\uFF0C\u81EA\u52A8\u5207\u6362\u5230\u6709\u5934\u6A21\u5F0F...\n");
|
|
745
731
|
}
|
|
732
|
+
return this.executeAsk(question, true);
|
|
746
733
|
}
|
|
734
|
+
throw error;
|
|
747
735
|
}
|
|
748
|
-
throw lastError || new YiyanAgentError("TIMEOUT", "Unknown error");
|
|
749
736
|
}
|
|
750
737
|
/**
|
|
751
738
|
* 执行单次问答
|
|
752
739
|
*/
|
|
753
|
-
async executeAsk(question, headful
|
|
740
|
+
async executeAsk(question, headful) {
|
|
754
741
|
const platform = os2.platform();
|
|
755
742
|
const chromePath = this.options.chromePath || getChromeExecutablePath(platform);
|
|
756
743
|
if (!chromePath) {
|
|
@@ -760,24 +747,28 @@ var YiyanAgent = class {
|
|
|
760
747
|
);
|
|
761
748
|
}
|
|
762
749
|
const profilePath = path3.join(this.profileDir, "chrome-profile");
|
|
763
|
-
const useHeadful = headful !== false;
|
|
764
750
|
const { browser, cdpBrowser, page, chromeProcess } = await launchBrowser({
|
|
765
751
|
chromePath,
|
|
766
752
|
profilePath,
|
|
767
|
-
headless: !
|
|
753
|
+
headless: !headful,
|
|
768
754
|
timeout: 3e4,
|
|
769
755
|
verbose: this.verbose
|
|
770
756
|
});
|
|
771
757
|
try {
|
|
772
758
|
await navigateToYiyan(page, this.verbose);
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
if (
|
|
759
|
+
const isLoggedIn = await checkLoggedIn(page, this.verbose);
|
|
760
|
+
if (!isLoggedIn) {
|
|
761
|
+
if (headful) {
|
|
776
762
|
await waitForUserAction(page, "login", this.verbose);
|
|
763
|
+
} else {
|
|
764
|
+
throw new YiyanAgentError(
|
|
765
|
+
"CAPTCHA",
|
|
766
|
+
'Not logged in. Please run "yiyan-agent login" first, or use headed mode.'
|
|
767
|
+
);
|
|
777
768
|
}
|
|
778
769
|
}
|
|
779
|
-
await sendMessage(page, question, this.verbose,
|
|
780
|
-
await waitForReply(page, this.options.timeout, this.verbose,
|
|
770
|
+
await sendMessage(page, question, this.verbose, headful);
|
|
771
|
+
await waitForReply(page, this.options.timeout, this.verbose, headful);
|
|
781
772
|
const reply = await extractReply(page, question, this.verbose);
|
|
782
773
|
return reply;
|
|
783
774
|
} finally {
|
|
@@ -811,42 +802,22 @@ var YiyanAgent = class {
|
|
|
811
802
|
timeout: 6e4
|
|
812
803
|
});
|
|
813
804
|
await page.waitForTimeout(5e3);
|
|
814
|
-
process.stderr.write("[yiyan-agent]
|
|
815
|
-
process.stderr.write("[yiyan-agent] \
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
const isLoggedIn = await page.evaluate(() => {
|
|
821
|
-
const bodyText = document.body.innerText || "";
|
|
822
|
-
if (bodyText.includes("\u672A\u767B\u5F55")) return false;
|
|
823
|
-
const loginButtons = Array.from(document.querySelectorAll("button"));
|
|
824
|
-
for (const btn of loginButtons) {
|
|
825
|
-
const text = btn.textContent?.trim() || "";
|
|
826
|
-
if (text === "\u767B\u5F55") return false;
|
|
827
|
-
}
|
|
828
|
-
return true;
|
|
829
|
-
});
|
|
830
|
-
if (isLoggedIn) {
|
|
831
|
-
process.stderr.write("[yiyan-agent] \u2713 \u68C0\u6D4B\u5230\u767B\u5F55\u6210\u529F\uFF01\n");
|
|
832
|
-
return true;
|
|
833
|
-
}
|
|
834
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
835
|
-
}
|
|
836
|
-
return false;
|
|
837
|
-
});
|
|
838
|
-
const enterPressed = new Promise((resolve) => {
|
|
839
|
-
const readline = __require("readline");
|
|
805
|
+
process.stderr.write("\n[yiyan-agent] ============================================\n");
|
|
806
|
+
process.stderr.write("[yiyan-agent] \u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u767B\u5F55\u6587\u5FC3\u4E00\u8A00\n");
|
|
807
|
+
process.stderr.write("[yiyan-agent] \u767B\u5F55\u6210\u529F\u540E\uFF0C\u56DE\u5230\u6B64\u5904\u6309 Enter \u952E\u7EE7\u7EED\n");
|
|
808
|
+
process.stderr.write("[yiyan-agent] ============================================\n\n");
|
|
809
|
+
const readline = await import("readline");
|
|
810
|
+
await new Promise((resolve) => {
|
|
840
811
|
const rl = readline.createInterface({
|
|
841
812
|
input: process.stdin,
|
|
842
813
|
output: process.stderr
|
|
843
814
|
});
|
|
844
|
-
rl.question("", () => {
|
|
815
|
+
rl.question("\u6309 Enter \u7EE7\u7EED...", () => {
|
|
845
816
|
rl.close();
|
|
846
|
-
resolve(
|
|
817
|
+
resolve();
|
|
847
818
|
});
|
|
848
819
|
});
|
|
849
|
-
|
|
820
|
+
process.stderr.write("[yiyan-agent] \u2713 \u767B\u5F55\u5B8C\u6210\uFF0C\u6B63\u5728\u4FDD\u5B58\u767B\u5F55\u72B6\u6001...\n");
|
|
850
821
|
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
851
822
|
} finally {
|
|
852
823
|
await closeBrowser(browser, chromeProcess, cdpBrowser, this.verbose);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/agent.ts","../src/types.ts","../src/profile.ts","../src/browser.ts"],"sourcesContent":["// src/agent.ts\nimport path from 'path';\nimport os from 'os';\nimport {\n YiyanAgentOptions,\n DEFAULT_OPTIONS,\n YiyanAgentError,\n} from './types';\nimport {\n getChromeExecutablePath,\n clearCopiedProfile,\n profileExists,\n} from './profile';\nimport {\n launchBrowser,\n closeBrowser,\n navigateToYiyan,\n sendMessage,\n waitForReply,\n extractReply,\n checkLoggedIn,\n waitForUserAction,\n} from './browser';\n\n/** 默认 profile 存储目录 */\nconst DEFAULT_PROFILE_BASE_DIR = path.join(\n os.homedir(),\n '.yiyan-browser-agent'\n);\n\n/**\n * 文心一言浏览器代理\n */\nexport class YiyanAgent {\n private options: Required<YiyanAgentOptions>;\n private profileDir: string;\n private verbose: boolean;\n\n constructor(options?: YiyanAgentOptions & { verbose?: boolean }) {\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n\n this.profileDir =\n this.options.profileDir || DEFAULT_PROFILE_BASE_DIR;\n\n this.verbose = options?.verbose ?? true; // 默认开启日志\n }\n\n /**\n * 发送问题并获取答案\n * @param question 问题文本\n * @param headful 是否使用有头浏览器(可见窗口),默认为 true(headless 模式会被检测拦截)\n */\n async ask(question: string, headful = true): Promise<string> {\n let lastError: YiyanAgentError | null = null;\n\n for (let attempt = 0; attempt < this.options.retryCount; attempt++) {\n try {\n if (attempt > 0) {\n process.stderr.write(`[yiyan-agent] 第 ${attempt + 1} 次重试...\\n`);\n }\n return await this.executeAsk(question, headful);\n } catch (error) {\n if (error instanceof YiyanAgentError) {\n lastError = error;\n if (lastError.type === 'CAPTCHA') {\n throw lastError;\n }\n } else {\n lastError = new YiyanAgentError(\n 'NETWORK',\n error instanceof Error ? error.message : String(error)\n );\n }\n\n if (attempt < this.options.retryCount - 1) {\n await this.sleep(2000);\n }\n }\n }\n\n throw lastError || new YiyanAgentError('TIMEOUT', 'Unknown error');\n }\n\n /**\n * 执行单次问答\n */\n private async executeAsk(question: string, headful = true): Promise<string> {\n const platform = os.platform();\n const chromePath =\n this.options.chromePath || getChromeExecutablePath(platform);\n\n if (!chromePath) {\n throw new YiyanAgentError(\n 'BROWSER_LAUNCH',\n `Unsupported platform: ${platform}`\n );\n }\n\n // profile 目录直接使用 chrome-profile 子目录\n const profilePath = path.join(this.profileDir, 'chrome-profile');\n\n // 启动浏览器(CDP 模式)—— 默认使用有头模式\n const useHeadful = headful !== false;\n const { browser, cdpBrowser, page, chromeProcess } = await launchBrowser({\n chromePath,\n profilePath,\n headless: !useHeadful,\n timeout: 30000,\n verbose: this.verbose,\n });\n\n try {\n // 导航到文心一言\n await navigateToYiyan(page, this.verbose);\n\n // 检查登录状态(有头模式下如果未登录,暂停等待用户登录)\n if (useHeadful) {\n const isLoggedIn = await checkLoggedIn(page, this.verbose);\n if (!isLoggedIn) {\n await waitForUserAction(page, 'login', this.verbose);\n }\n }\n\n // 发送消息\n await sendMessage(page, question, this.verbose, useHeadful);\n\n // 等待回复\n await waitForReply(page, this.options.timeout, this.verbose, useHeadful);\n\n // 提取回复\n const reply = await extractReply(page, question, this.verbose);\n\n return reply;\n } finally {\n await closeBrowser(browser, chromeProcess, cdpBrowser, this.verbose);\n }\n }\n\n /**\n * 登录文心一言(打开有头浏览器让用户手动登录)\n * 登录成功后,后续的 ask 调用将自动使用登录状态\n */\n async login(): Promise<void> {\n const platform = os.platform();\n const chromePath =\n this.options.chromePath || getChromeExecutablePath(platform);\n\n if (!chromePath) {\n throw new YiyanAgentError(\n 'BROWSER_LAUNCH',\n `Unsupported platform: ${platform}`\n );\n }\n\n const profilePath = path.join(this.profileDir, 'chrome-profile');\n\n // 启动有头浏览器(用户可以看到并操作)\n const { browser, cdpBrowser, page, chromeProcess } = await launchBrowser({\n chromePath,\n profilePath,\n headless: false,\n timeout: 60000,\n verbose: this.verbose,\n });\n\n try {\n await page.goto('https://yiyan.baidu.com/', {\n waitUntil: 'networkidle',\n timeout: 60000,\n });\n\n // 等待 SPA 渲染\n await page.waitForTimeout(5000);\n\n // 输出提示信息\n process.stderr.write('[yiyan-agent] 等待您在浏览器中登录文心一言...\\n');\n process.stderr.write('[yiyan-agent] 登录成功后,请按 Enter 键继续...\\n');\n\n // 策略1:检测登录成功的标志\n const loginDetected = Promise.resolve().then(async () => {\n const maxWait = 300000;\n const startTime = Date.now();\n while (Date.now() - startTime < maxWait) {\n const isLoggedIn = await page.evaluate(() => {\n const bodyText = document.body.innerText || '';\n if (bodyText.includes('未登录')) return false;\n const loginButtons = Array.from(document.querySelectorAll('button'));\n for (const btn of loginButtons) {\n const text = btn.textContent?.trim() || '';\n if (text === '登录') return false;\n }\n return true;\n });\n\n if (isLoggedIn) {\n process.stderr.write('[yiyan-agent] ✓ 检测到登录成功!\\n');\n return true;\n }\n\n await new Promise(resolve => setTimeout(resolve, 2000));\n }\n return false;\n });\n\n // 策略2:等待用户按 Enter 键确认\n const enterPressed = new Promise<boolean>((resolve) => {\n const readline = require('readline');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n rl.question('', () => {\n rl.close();\n resolve(true);\n });\n });\n\n // 两种方式任一触发即可\n await Promise.race([loginDetected, enterPressed]);\n\n // 等待一下确保登录状态保存到 profile\n await new Promise(resolve => setTimeout(resolve, 3000));\n } finally {\n await closeBrowser(browser, chromeProcess, cdpBrowser, this.verbose);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n async reset(): Promise<void> {\n await clearCopiedProfile(this.profileDir);\n }\n\n status(): { loggedIn: boolean; profilePath: string } {\n return {\n loggedIn: profileExists(this.profileDir),\n profilePath: path.join(this.profileDir, 'chrome-profile'),\n };\n }\n}\n","/**\n * yiyan-browser-agent 类型定义\n */\n\n/** 配置选项 */\nexport interface YiyanAgentOptions {\n /** 超时毫秒,默认 120000 */\n timeout?: number;\n /** 重试次数,默认 3 */\n retryCount?: number;\n /** 自定义 profile 目录 */\n profileDir?: string;\n /** 自定义 Chrome 可执行文件路径 */\n chromePath?: string;\n}\n\n/** CLI 输出格式 */\nexport interface CliOutput {\n success: boolean;\n /** 用户发送的问题 */\n question: string;\n /** 成功时有值 */\n answer?: string;\n /** 失败时有值 */\n error?: string;\n /** 耗时毫秒 */\n duration: number;\n}\n\n/** 错误类型 */\nexport type YiyanAgentErrorType =\n | 'BROWSER_LAUNCH'\n | 'PROFILE_COPY'\n | 'TIMEOUT'\n | 'NETWORK'\n | 'CAPTCHA';\n\n/** 自定义错误类 */\nexport class YiyanAgentError extends Error {\n constructor(\n public type: YiyanAgentErrorType,\n message: string\n ) {\n super(message);\n this.name = 'YiyanAgentError';\n }\n}\n\n/** 默认配置 */\nexport const DEFAULT_OPTIONS: Required<YiyanAgentOptions> = {\n timeout: 120000,\n retryCount: 3,\n profileDir: '',\n chromePath: '',\n};\n\n/** 文心一言网页 URL */\nexport const YIYAN_CHAT_URL = 'https://yiyan.baidu.com/';\n","// src/profile.ts\nimport path from 'path';\nimport os from 'os';\nimport fs from 'fs';\nimport fsp from 'fs/promises';\nimport { YiyanAgentError } from './types';\n\n/**\n * 获取 Chrome 用户数据目录路径\n */\nexport function getChromeProfilePath(platform: NodeJS.Platform): string | null {\n const home = os.homedir();\n\n switch (platform) {\n case 'win32':\n const localAppData = process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');\n return path.join(localAppData, 'Google', 'Chrome', 'User Data');\n case 'darwin':\n return path.join(home, 'Library', 'Application Support', 'Google', 'Chrome');\n case 'linux':\n return path.join(home, '.config', 'google-chrome');\n default:\n return null;\n }\n}\n\n/**\n * 获取 Chrome 可执行文件路径\n */\nexport function getChromeExecutablePath(platform: NodeJS.Platform): string | null {\n switch (platform) {\n case 'win32':\n const programFiles = process.env.PROGRAMFILES || 'C:\\\\Program Files';\n return path.join(programFiles, 'Google', 'Chrome', 'Application', 'chrome.exe');\n case 'darwin':\n return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';\n case 'linux':\n return '/usr/bin/google-chrome';\n default:\n return null;\n }\n}\n\n/** Profile 子目录名称 */\nconst PROFILE_TARGET_NAME = 'chrome-profile';\n\n/**\n * 检查并复制 Chrome profile 到目标目录\n * 如果已存在且未过期则直接返回路径,否则重新复制\n */\nexport async function copyChromeProfile(\n platform: NodeJS.Platform,\n targetBaseDir: string\n): Promise<string> {\n const targetPath = path.join(targetBaseDir, PROFILE_TARGET_NAME);\n\n // 检查已复制的 profile 是否有效(5分钟内不需要重新复制)\n if (fs.existsSync(targetPath)) {\n const stat = fs.statSync(targetPath);\n const ageMinutes = (Date.now() - stat.mtimeMs) / 60000;\n if (ageMinutes < 5) {\n return targetPath;\n }\n // 过期了,删除旧的\n await fsp.rm(targetPath, { recursive: true, force: true });\n }\n\n // 获取源 profile 路径\n const sourcePath = getChromeProfilePath(platform);\n if (!sourcePath) {\n throw new YiyanAgentError('PROFILE_COPY', `Unsupported platform: ${platform}`);\n }\n\n // 检查源是否存在\n try {\n await fsp.access(sourcePath);\n } catch {\n throw new YiyanAgentError(\n 'PROFILE_COPY',\n `Chrome profile not found at: ${sourcePath}`\n );\n }\n\n // 创建目标目录\n await fsp.mkdir(targetPath, { recursive: true });\n\n // 复制根目录关键文件\n const rootFiles = [\n 'Local State',\n 'First Run',\n 'Default',\n ];\n\n for (const file of rootFiles) {\n const srcFile = path.join(sourcePath, file);\n const destFile = path.join(targetPath, file);\n\n try {\n const stat = await fsp.stat(srcFile);\n if (stat.isDirectory()) {\n await copyDir(srcFile, destFile);\n } else {\n await fsp.copyFile(srcFile, destFile);\n }\n } catch {\n continue;\n }\n }\n\n // 复制 Default 目录下的关键文件(只复制登录和 Cookie 相关的小文件)\n const defaultDir = path.join(sourcePath, 'Default');\n if (fs.existsSync(defaultDir)) {\n const destDefaultDir = path.join(targetPath, 'Default');\n await fsp.mkdir(destDefaultDir, { recursive: true });\n\n const defaultFiles = [\n 'Cookies',\n 'Cookies-journal',\n 'Login Data',\n 'Login Data-journal',\n 'Login Data For Account',\n 'Login Data For Account-journal',\n 'Preferences',\n 'Secure Preferences',\n 'Network',\n 'Network Action Predictor',\n 'Web Data',\n 'Web Data-journal',\n 'Extension Cookies',\n 'Extension Cookies-journal',\n ];\n\n for (const file of defaultFiles) {\n const srcFile = path.join(defaultDir, file);\n const destFile = path.join(destDefaultDir, file);\n\n try {\n const stat = await fsp.stat(srcFile);\n if (stat.isDirectory()) {\n await copyDir(srcFile, destFile);\n } else {\n await fsp.copyFile(srcFile, destFile);\n }\n } catch {\n continue;\n }\n }\n }\n\n // 更新目录修改时间\n await fsp.utimes(targetPath, new Date(), new Date());\n\n return targetPath;\n}\n\n/**\n * 复制目录\n */\nasync function copyDir(src: string, dest: string): Promise<void> {\n await fsp.mkdir(dest, { recursive: true });\n const entries = await fsp.readdir(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n\n if (entry.isDirectory()) {\n await copyDir(srcPath, destPath);\n } else {\n await fsp.copyFile(srcPath, destPath);\n }\n }\n}\n\n/**\n * 清除复制的 profile\n */\nexport async function clearCopiedProfile(profileDir: string): Promise<void> {\n const targetPath = path.join(profileDir, PROFILE_TARGET_NAME);\n if (fs.existsSync(targetPath)) {\n await fsp.rm(targetPath, { recursive: true, force: true });\n }\n}\n\n/**\n * 检查 profile 是否存在\n */\nexport function profileExists(profileDir: string): boolean {\n const targetPath = path.join(profileDir, PROFILE_TARGET_NAME);\n return fs.existsSync(targetPath);\n}\n","// src/browser.ts\nimport { chromium, Page, BrowserContext, Browser } from 'playwright-core';\nimport { YiyanAgentError, YIYAN_CHAT_URL } from './types';\nimport http from 'http';\nimport { spawn, ChildProcess, execSync } from 'child_process';\nimport path from 'path';\nimport os from 'os';\nimport fs from 'fs';\n\nexport interface LaunchBrowserOptions {\n chromePath: string;\n profilePath: string;\n headless: boolean;\n timeout?: number;\n /** 是否输出进度日志 */\n verbose?: boolean;\n}\n\nconst CDP_PORT = 19222;\n\n/** 调试截图保存目录 */\nconst DEBUG_DIR = path.join(os.homedir(), '.yiyan-browser-agent', 'debug');\n\n/** 日志输出(仅 verbose 模式) */\nfunction log(verbose: boolean, msg: string): void {\n if (verbose) {\n process.stderr.write(`[yiyan-agent] ${msg}\\n`);\n }\n}\n\n/** 确保调试目录存在 */\nfunction ensureDebugDir(): void {\n if (!fs.existsSync(DEBUG_DIR)) {\n fs.mkdirSync(DEBUG_DIR, { recursive: true });\n }\n}\n\n/**\n * 杀死占用指定端口的进程(启动前清理残留 Chrome)\n */\nfunction killProcessOnPort(port: number): void {\n try {\n if (process.platform === 'win32') {\n const output = execSync(\n `netstat -ano | findstr :${port} | findstr LISTENING`,\n { encoding: 'utf-8', timeout: 5000 }\n ).trim();\n if (output) {\n const lines = output.split('\\n').filter(l => l.trim());\n const pids = new Set<string>();\n for (const line of lines) {\n const parts = line.trim().split(/\\s+/);\n const pid = parts[parts.length - 1];\n if (pid && /^\\d+$/.test(pid)) pids.add(pid);\n }\n for (const pid of pids) {\n try {\n execSync(`taskkill /F /T /PID ${pid}`, { timeout: 5000 });\n } catch {\n // 进程可能已经退出\n }\n }\n }\n } else {\n const output = execSync(\n `lsof -ti :${port}`,\n { encoding: 'utf-8', timeout: 5000 }\n ).trim();\n if (output) {\n const pids = output.split('\\n').filter(l => l.trim());\n for (const pid of pids) {\n try {\n process.kill(parseInt(pid, 10), 'SIGKILL');\n } catch {\n // 进程可能已经退出\n }\n }\n }\n }\n } catch {\n // 没有找到占用端口的进程,正常情况\n }\n}\n\n/**\n * 杀死进程树(Windows 用 taskkill /T,其他平台用 process.kill 负 PID)\n */\nfunction killProcessTree(pid: number): void {\n try {\n if (process.platform === 'win32') {\n execSync(`taskkill /F /T /PID ${pid}`, { timeout: 5000 });\n } else {\n try {\n process.kill(-pid, 'SIGKILL');\n } catch {\n process.kill(pid, 'SIGKILL');\n }\n }\n } catch {\n // 进程可能已经退出\n }\n}\n\n/**\n * 等待 CDP 端口就绪\n */\nfunction waitForCDP(port: number, timeout = 15000): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const start = Date.now();\n const check = () => {\n http.get(`http://localhost:${port}/json/version`, (res) => {\n let data = '';\n res.on('data', chunk => data += chunk);\n res.on('end', () => {\n try { resolve(JSON.parse(data)); } catch (e) { reject(e); }\n });\n }).on('error', () => {\n if (Date.now() - start > timeout) reject(new Error('CDP connection timeout'));\n else setTimeout(check, 500);\n });\n };\n check();\n });\n}\n\n/**\n * 启动 Chrome 浏览器(通过 CDP 连接模式)\n */\nexport async function launchBrowser(\n options: LaunchBrowserOptions\n): Promise<{ browser: BrowserContext; cdpBrowser: Browser; page: Page; chromeProcess: ChildProcess | null }> {\n const { chromePath, profilePath, headless, timeout = 30000, verbose = false } = options;\n\n try {\n log(verbose, '清理残留 Chrome 进程...');\n killProcessOnPort(CDP_PORT);\n await new Promise(resolve => setTimeout(resolve, 500));\n\n const chromeArgs = [\n `--remote-debugging-port=${CDP_PORT}`,\n `--user-data-dir=${profilePath}`,\n '--no-first-run',\n '--no-default-browser-check',\n '--disable-blink-features=AutomationControlled',\n ];\n\n if (headless) {\n chromeArgs.push('--headless=new');\n }\n\n log(verbose, `启动 Chrome: ${chromePath} ${headless ? '(headless)' : '(headed)'}`);\n const chromeProcess = spawn(chromePath, chromeArgs, {\n detached: true,\n stdio: 'ignore',\n });\n chromeProcess.unref();\n\n const spawnError = new Promise<never>((_, reject) => {\n chromeProcess.on('error', (err) => {\n reject(new YiyanAgentError('BROWSER_LAUNCH', `Failed to spawn Chrome: ${err.message}`));\n });\n });\n\n log(verbose, '等待 CDP 端口就绪...');\n await Promise.race([\n waitForCDP(CDP_PORT, timeout),\n spawnError,\n ]);\n\n log(verbose, '通过 CDP 连接浏览器...');\n const cdpBrowser = await chromium.connectOverCDP(`http://localhost:${CDP_PORT}`);\n\n const contexts = cdpBrowser.contexts();\n const context = contexts.length > 0 ? contexts[0] : await cdpBrowser.newContext();\n context.setDefaultTimeout(timeout);\n const pages = context.pages();\n const page = pages.length > 0 ? pages[0] : await context.newPage();\n\n log(verbose, '浏览器启动完成');\n return { browser: context, cdpBrowser, page, chromeProcess };\n } catch (error) {\n killProcessOnPort(CDP_PORT);\n throw new YiyanAgentError(\n 'BROWSER_LAUNCH',\n `Failed to launch browser: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * 关闭浏览器(彻底清理:断开 CDP → 关闭页面 → 杀进程树 → 清理残留端口)\n */\nexport async function closeBrowser(\n browser: BrowserContext,\n chromeProcess: ChildProcess | null,\n cdpBrowser?: Browser,\n verbose?: boolean\n): Promise<void> {\n log(!!verbose, '关闭浏览器...');\n\n if (cdpBrowser) {\n try {\n await cdpBrowser.close();\n } catch {\n // 忽略关闭错误\n }\n }\n\n try {\n const pages = browser.pages();\n for (const page of pages) {\n await page.close().catch(() => {});\n }\n } catch {\n // 忽略关闭错误\n }\n\n if (chromeProcess && chromeProcess.pid) {\n killProcessTree(chromeProcess.pid);\n }\n\n killProcessOnPort(CDP_PORT);\n log(!!verbose, '浏览器已关闭');\n}\n\n/**\n * 导航到文心一言聊天页面\n */\nexport async function navigateToYiyan(page: Page, verbose = false): Promise<void> {\n log(verbose, '导航到文心一言聊天页面...');\n await page.goto(YIYAN_CHAT_URL, {\n waitUntil: 'networkidle',\n timeout: 60000,\n });\n\n // 文心一言是 SPA,需要额外等待渲染\n await page.waitForTimeout(5000);\n\n try {\n await page.waitForSelector('[contenteditable=\"true\"]', { timeout: 15000 });\n log(verbose, '页面加载完成,输入框已就绪');\n } catch {\n log(verbose, '警告:未检测到输入框,继续执行');\n }\n}\n\n/**\n * 检测文心一言验证码弹窗\n */\nexport async function checkCaptcha(page: Page, verbose = false): Promise<boolean> {\n const captchaIndicators = [\n '请完成下列验证后继续',\n '按住左边按钮拖动',\n '滑动验证',\n '点击验证',\n '安全验证',\n 'captcha',\n '请完成验证',\n ];\n\n const hasCaptcha = await page.evaluate((indicators) => {\n const bodyText = document.body.innerText || '';\n for (const indicator of indicators) {\n if (bodyText.includes(indicator)) return true;\n }\n // 检查是否有弹窗/遮罩层\n const dialogs = document.querySelectorAll('[role=\"dialog\"], [class*=\"captcha\"], [class*=\"verify\"]');\n if (dialogs.length > 0) return true;\n return false;\n }, captchaIndicators);\n\n if (hasCaptcha) {\n log(verbose, '⚠ 检测到验证码弹窗!');\n }\n return hasCaptcha;\n}\n\n/**\n * 检测是否已登录文心一言\n */\nexport async function checkLoggedIn(page: Page, verbose = false): Promise<boolean> {\n const isLoggedIn = await page.evaluate(() => {\n // 文心一言未登录时:页面有\"登录\"按钮和\"未登录\"文字\n const bodyText = document.body.innerText || '';\n\n // 如果有\"未登录\"文字 = 未登录\n if (bodyText.includes('未登录')) return false;\n\n // 检查是否有\"登录\"按钮\n const loginButtons = document.querySelectorAll('button');\n for (const btn of loginButtons) {\n const text = btn.textContent?.trim() || '';\n if (text === '登录') {\n return false; // 有登录按钮 = 未登录\n }\n }\n\n // 如果没有\"登录\"按钮和\"未登录\"文字,说明已登录\n return true;\n });\n\n if (isLoggedIn) {\n log(verbose, '✓ 已检测到登录状态');\n } else {\n log(verbose, '⚠ 未检测到登录状态');\n }\n return isLoggedIn;\n}\n\n/**\n * 有头模式下,暂停等待用户手动操作(过验证码/登录),然后继续\n * 检测到问题已解决后自动继续\n */\nexport async function waitForUserAction(\n page: Page,\n reason: 'captcha' | 'login' | 'no-reply',\n verbose = false\n): Promise<void> {\n const reasonText = reason === 'captcha'\n ? '检测到验证码,请在浏览器中手动完成验证'\n : reason === 'login'\n ? '检测到未登录,请在浏览器中手动登录'\n : 'AI 未回复,请检查浏览器是否需要手动操作(验证码/登录)';\n\n process.stderr.write(`\\n[yiyan-agent] ⚠ ${reasonText}\\n`);\n process.stderr.write('[yiyan-agent] 等待您操作完成...(操作完成后会自动继续)\\n\\n');\n\n // 轮询检测问题是否已解决\n const maxWait = 180000; // 最多等 3 分钟\n const startTime = Date.now();\n\n while (Date.now() - startTime < maxWait) {\n await new Promise(resolve => setTimeout(resolve, 2000));\n\n if (reason === 'captcha') {\n const stillHasCaptcha = await checkCaptcha(page, false);\n if (!stillHasCaptcha) {\n log(verbose, '✓ 验证码已通过!');\n return;\n }\n } else if (reason === 'login') {\n const loggedIn = await checkLoggedIn(page, false);\n if (loggedIn) {\n log(verbose, '✓ 登录成功!');\n return;\n }\n } else {\n // no-reply:检测 AI 回复是否出现\n const hasReply = await page.evaluate(() => {\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (!answerBox) return false;\n const text = answerBox.innerText?.trim() || '';\n return text.length > 0;\n });\n if (hasReply) {\n log(verbose, '✓ AI 已开始回复!');\n return;\n }\n }\n }\n\n log(verbose, '⚠ 等待用户操作超时(3分钟)');\n}\n\n/**\n * 发送消息到文心一言\n * @param headful 是否为有头模式(有头模式下检测到验证码会暂停等待用户操作)\n */\nexport async function sendMessage(page: Page, message: string, verbose = false, headful = false): Promise<void> {\n // 文心一言使用 contenteditable div 而不是 textarea\n const inputSelector = '[contenteditable=\"true\"]';\n\n log(verbose, '等待输入框出现...');\n const inputElement = await page.waitForSelector(inputSelector, { timeout: 15000 });\n if (!inputElement) {\n log(verbose, '⚠ 输入框未找到!');\n throw new YiyanAgentError('NETWORK', 'Input element not found on page');\n }\n log(verbose, '✓ 输入框已找到');\n\n // 点击输入框获取焦点\n await inputElement.click();\n await page.waitForTimeout(500);\n\n // 输入问题(文心一言用 contenteditable,需要用 keyboard.type 而不是 fill)\n log(verbose, `输入问题: \"${message}\"`);\n await page.keyboard.type(message, { delay: 30 });\n\n // 验证填入是否成功\n const filledValue = await inputElement.innerText();\n if (filledValue.includes(message)) {\n log(verbose, '✓ 问题已成功填入输入框');\n } else {\n log(verbose, `⚠ 输入框内容: \"${filledValue.substring(0, 50)}\"`);\n }\n\n // 按 Enter 发送\n log(verbose, '按 Enter 发送消息...');\n await page.keyboard.press('Enter');\n\n // 等待发送后的页面变化\n await page.waitForTimeout(3000);\n\n // 检查是否触发了验证码\n if (await checkCaptcha(page, verbose)) {\n if (headful) {\n await waitForUserAction(page, 'captcha', verbose);\n // 验证码通过后,重新输入并发送消息\n log(verbose, '验证码已通过,重新发送消息...');\n const inputEl = await page.waitForSelector(inputSelector, { timeout: 10000 });\n if (inputEl) {\n await inputEl.click();\n await page.waitForTimeout(300);\n await page.keyboard.type(message, { delay: 30 });\n await page.keyboard.press('Enter');\n await page.waitForTimeout(3000);\n }\n } else {\n throw new YiyanAgentError(\n 'CAPTCHA',\n 'Yiyan detected automation and triggered a captcha. Use headed mode (default) to manually solve it, or run \"login\" first.'\n );\n }\n }\n\n // 检查 URL 变化(文心一言发送消息后会跳转到 /chat/xxx)\n const url = page.url();\n log(verbose, `当前 URL: ${url}`);\n\n if (url.includes('/chat/')) {\n log(verbose, '✓ 已自动进入对话页面');\n } else if (url === 'https://yiyan.baidu.com/' || url === 'https://yiyan.baidu.com') {\n log(verbose, '仍在欢迎页,尝试点击发送按钮...');\n try {\n // 点击发送按钮\n const sendBtn = page.locator('[class*=\"send__\"]').first();\n await sendBtn.click({ timeout: 5000 });\n await page.waitForTimeout(3000);\n log(verbose, '✓ 已点击发送按钮');\n } catch {\n log(verbose, '⚠ 点击发送按钮失败,继续等待回复...');\n }\n } else {\n log(verbose, `当前页面: ${url}`);\n }\n}\n\n/**\n * 保存调试截图\n */\nasync function saveDebugScreenshot(page: Page, name: string, verbose: boolean): Promise<void> {\n try {\n ensureDebugDir();\n const screenshotPath = path.join(DEBUG_DIR, `${name}.png`);\n await page.screenshot({ path: screenshotPath, fullPage: true });\n log(verbose, `调试截图已保存: ${screenshotPath}`);\n } catch {\n // 截图失败不影响主流程\n }\n}\n\n/**\n * 等待回复完成\n * @param headful 是否为有头模式(超时且有头模式下会等待用户手动操作)\n */\nexport async function waitForReply(\n page: Page,\n timeout: number,\n verbose = false,\n headful = false\n): Promise<void> {\n const maxWait = Math.min(timeout, 60000);\n\n log(verbose, `等待 AI 回复(最多 ${maxWait / 1000} 秒)...`);\n\n // 文心一言 AI 回复在 answerBox 容器中,流式渲染\n const startTime = Date.now();\n let lastLen = 0;\n let stableCount = 0;\n let replyStarted = false;\n\n while (Date.now() - startTime < maxWait) {\n const state = await page.evaluate(() => {\n // 文心一言 AI 回复在 answerBox 或 dialogueCardList 中\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n return {\n hasAiReply: text.length > 0,\n aiTextLen: text.length,\n aiPreview: text.substring(0, 100),\n };\n }\n\n // 备选:检查 dialogueCardList\n const cardList = document.querySelector('[class*=\"dialogueCardList\"]');\n if (cardList) {\n const lastCard = cardList.lastElementChild as HTMLElement;\n if (lastCard) {\n const text = lastCard.innerText?.trim() || '';\n return {\n hasAiReply: text.length > 0,\n aiTextLen: text.length,\n aiPreview: text.substring(0, 100),\n };\n }\n }\n\n return { hasAiReply: false, aiTextLen: 0, aiPreview: '' };\n });\n\n if (state.hasAiReply && state.aiTextLen > 0) {\n if (!replyStarted) {\n log(verbose, `✓ AI 回复已开始生成(${state.aiTextLen}字): ${state.aiPreview.substring(0, 60)}...`);\n replyStarted = true;\n }\n\n // 文本长度不再变化 → 回复完成\n if (state.aiTextLen === lastLen) {\n stableCount++;\n if (stableCount >= 3) {\n log(verbose, `✓ AI 回复已完成(${state.aiTextLen}字,连续 ${stableCount} 次长度不变)`);\n break;\n }\n } else {\n stableCount = 0;\n lastLen = state.aiTextLen;\n }\n }\n\n await page.waitForTimeout(1500);\n }\n\n if (!replyStarted) {\n // 超时后检查是否触发了验证码\n if (await checkCaptcha(page, verbose)) {\n if (headful) {\n await waitForUserAction(page, 'captcha', verbose);\n const retryStart = Date.now();\n const retryMaxWait = 30000;\n while (Date.now() - retryStart < retryMaxWait) {\n const retryState = await page.evaluate(() => {\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n return { hasAiReply: text.length > 0, aiTextLen: text.length };\n }\n return { hasAiReply: false, aiTextLen: 0 };\n });\n if (retryState.hasAiReply) {\n log(verbose, '✓ 验证码通过后 AI 已开始回复');\n replyStarted = true;\n break;\n }\n await page.waitForTimeout(1500);\n }\n } else {\n throw new YiyanAgentError(\n 'CAPTCHA',\n 'Yiyan detected automation and triggered a captcha. Use headed mode (default) to manually solve it, or run \"login\" first.'\n );\n }\n } else if (headful) {\n await waitForUserAction(page, 'no-reply', verbose);\n const retryStart = Date.now();\n const retryMaxWait = 60000;\n while (Date.now() - retryStart < retryMaxWait) {\n const retryState = await page.evaluate(() => {\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n return { hasAiReply: text.length > 0, aiTextLen: text.length };\n }\n return { hasAiReply: false, aiTextLen: 0 };\n });\n if (retryState.hasAiReply) {\n if (retryState.aiTextLen === lastLen) {\n stableCount++;\n if (stableCount >= 3) {\n log(verbose, `✓ 用户操作后 AI 回复已完成(${retryState.aiTextLen}字)`);\n replyStarted = true;\n break;\n }\n } else {\n stableCount = 0;\n lastLen = retryState.aiTextLen;\n }\n }\n await page.waitForTimeout(1500);\n }\n } else {\n log(verbose, '⚠ 等待超时,AI 回复文本未出现');\n }\n }\n\n // 额外等待一小段时间确保最终渲染\n await page.waitForTimeout(2000);\n\n // 页面诊断\n const pageDump = await page.evaluate(() => {\n const result: Record<string, any> = {};\n\n const selectors = [\n '[class*=\"answerBox\"]', '[class*=\"dialogueCardList\"]', '[class*=\"dialogue_card_item\"]',\n '[class*=\"chatViewer\"]', '[class*=\"flowBox\"]', '[class*=\"roleSystem\"]',\n '[class*=\"mdRenderContainer\"]', '[class*=\"agent-markdown\"]', '[class*=\"markdown\"]',\n '[class*=\"content\"]', '[class*=\"chat\"]', '[class*=\"message\"]',\n ];\n for (const sel of selectors) {\n result[`selector:${sel}`] = document.querySelectorAll(sel).length;\n }\n\n // answerBox 子元素详情\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n result['answerBox'] = {\n textLen: answerBox.innerText?.trim().length || 0,\n preview: (answerBox.innerText?.trim() || '').substring(0, 200),\n classes: answerBox.className?.toString().substring(0, 100) || '',\n };\n }\n\n // body 文本(前 1000 字符)\n result['bodyText'] = document.body.innerText?.substring(0, 1000) || '';\n\n // 长文本元素\n const longTextEls: { tag: string; classes: string; textLen: number; preview: string }[] = [];\n const mainContent = document.querySelector('[class*=\"chatViewer\"]') || document.querySelector('[class*=\"chat\"]') || document.body;\n for (const el of Array.from(mainContent.querySelectorAll('div, section, article'))) {\n const text = (el as HTMLElement).innerText?.trim() || '';\n if (text.length > 50 && text.length < 10000) {\n longTextEls.push({\n tag: el.tagName,\n classes: (el as HTMLElement).className?.toString().split(' ').slice(0, 3).join(' ') || '',\n textLen: text.length,\n preview: text.substring(0, 80),\n });\n }\n }\n result['longTextElements'] = longTextEls\n .sort((a, b) => b.textLen - a.textLen)\n .slice(0, 10);\n\n return result;\n });\n\n // 输出诊断\n log(verbose, '=== 页面诊断 ===');\n for (const [key, value] of Object.entries(pageDump)) {\n if (key === 'bodyText' || key === 'longTextElements') continue;\n log(verbose, ` ${key}: ${JSON.stringify(value)}`);\n }\n if (pageDump['longTextElements'] && (pageDump['longTextElements'] as any[]).length > 0) {\n log(verbose, ' 长文本元素 TOP 10:');\n for (const el of pageDump['longTextElements'] as any[]) {\n log(verbose, ` <${el.tag}> .${el.classes} (${el.textLen}字): ${el.preview}...`);\n }\n }\n if (pageDump['bodyText']) {\n log(verbose, ` 页面文本(前1000字): ${(pageDump['bodyText'] as string).substring(0, 1000)}`);\n }\n}\n\n/**\n * 提取回复内容\n */\nexport async function extractReply(page: Page, question: string, verbose = false): Promise<string> {\n log(verbose, '提取 AI 回复内容...');\n\n // 先保存截图,方便排查问题\n await saveDebugScreenshot(page, 'before-extract', verbose);\n\n const reply = await page.evaluate((userQuestion: string) => {\n const debugInfo: string[] = [];\n\n // ===== 方法1(最佳):answerBox 容器 =====\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n debugInfo.push(`answerBox found: ${!!answerBox}`);\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n debugInfo.push(`answerBox textLen: ${text.length}`);\n if (text.length > 0) {\n // 去除思考过程,只取最终回复\n // 文心一言思考过程在 processItem 中,最终回复在后面\n // 尝试只取 agent-markdown 或 mdRenderContainer 的内容\n const mdContainer = answerBox.querySelector('[class*=\"agent-markdown\"], [class*=\"mdRenderContainer\"]');\n if (mdContainer) {\n const mdText = mdContainer.innerText?.trim() || '';\n if (mdText.length > 0) {\n debugInfo.push(`method1 markdown success: ${mdText.length} chars`);\n return JSON.stringify({ text: mdText, debug: debugInfo });\n }\n }\n\n debugInfo.push(`method1 answerBox success: ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法2:dialogueCardList 中最后一个对话卡片 =====\n const cardList = document.querySelector('[class*=\"dialogueCardList\"]');\n debugInfo.push(`dialogueCardList found: ${!!cardList}`);\n if (cardList && cardList.children.length > 0) {\n // 最后一个卡片通常是 AI 回复\n const lastCard = cardList.lastElementChild as HTMLElement;\n const text = lastCard?.innerText?.trim() || '';\n debugInfo.push(`lastCard textLen: ${text.length}`);\n if (text.length > 0) {\n debugInfo.push(`method2 success: ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法3:flowBox(answerBox 的父级容器) =====\n const flowBoxes = document.querySelectorAll('[class*=\"flowBox\"]');\n debugInfo.push(`flowBoxes count: ${flowBoxes.length}`);\n for (let i = flowBoxes.length - 1; i >= 0; i--) {\n const box = flowBoxes[i] as HTMLElement;\n const text = box.innerText?.trim() || '';\n if (text.length > 10) {\n debugInfo.push(`method3 success: flowBox[${i}] ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法4:roleSystemBot(AI 回复角色标识的父容器) =====\n const botContainers = document.querySelectorAll('[class*=\"roleSystemBot\"]');\n debugInfo.push(`roleSystemBot count: ${botContainers.length}`);\n for (let i = botContainers.length - 1; i >= 0; i--) {\n const container = botContainers[i] as HTMLElement;\n const text = container.innerText?.trim() || '';\n if (text.length > 10) {\n debugInfo.push(`method4 success: botContainer[${i}] ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法5:mdRenderContainer / agent-markdown =====\n const mdContainers = document.querySelectorAll('[class*=\"mdRenderContainer\"], [class*=\"agent-markdown\"]');\n debugInfo.push(`md containers count: ${mdContainers.length}`);\n for (let i = mdContainers.length - 1; i >= 0; i--) {\n const container = mdContainers[i] as HTMLElement;\n const text = container.innerText?.trim() || '';\n if (text.length > 10) {\n debugInfo.push(`method5 success: mdContainer[${i}] ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法6:页面文本解析(fallback)=====\n const fullText = document.body.innerText;\n const lines = fullText.split('\\n').map(l => l.trim()).filter(l => l.length > 0);\n\n const uiWords = new Set([\n '文心一言', '新对话', '创意写作', '智慧绘图', '超级智能体', '更多',\n '我的收藏', '项目', '对话', '暂无记录', '未登录', '登录',\n '内容由AI生成,仅供参考,请仔细甄别', '参考',\n '深度分析需求并解答', '通用写作', '阅读分析',\n '网页工坊', '智能翻译', '代码编程',\n ]);\n\n const userQIdx = lines.findIndex(l => l === userQuestion || l.includes(userQuestion));\n debugInfo.push(`userQ idx in body text: ${userQIdx}, total lines: ${lines.length}`);\n\n if (userQIdx >= 0) {\n const replyLines: string[] = [];\n for (let i = userQIdx + 1; i < lines.length; i++) {\n const line = lines[i];\n if (line.length > 0 && !uiWords.has(line) && !line.startsWith('搜索') && !line.includes('篇资料')) {\n replyLines.push(line);\n }\n if (line === '快速' || line === '更多' || line.includes('内容由AI生成')) break;\n }\n if (replyLines.length > 0) {\n debugInfo.push(`method6 success: ${replyLines.length} lines`);\n return JSON.stringify({ text: replyLines.join('\\n'), debug: debugInfo });\n }\n }\n\n // ===== 方法7:取页面中最长的非 UI 文本块 =====\n const allTextBlocks: { text: string; length: number }[] = [];\n const blockElements = document.querySelectorAll('div, section, article, p, span');\n for (const el of blockElements) {\n const elText = (el as HTMLElement).innerText?.trim() || '';\n if (elText.length > 50 && elText.length < 10000) {\n if (!uiWords.has(elText)) {\n allTextBlocks.push({ text: elText, length: elText.length });\n }\n }\n }\n allTextBlocks.sort((a, b) => b.length - a.length);\n if (allTextBlocks.length > 0) {\n debugInfo.push(`method7 fallback: longest text block ${allTextBlocks[0].length} chars`);\n return JSON.stringify({ text: allTextBlocks[0].text, debug: debugInfo });\n }\n\n // ===== 全部失败 =====\n debugInfo.push(`ALL METHODS FAILED`);\n debugInfo.push(`body text length: ${fullText.length}`);\n debugInfo.push(`body text first 500: ${fullText.substring(0, 500)}`);\n return JSON.stringify({ text: '', debug: debugInfo });\n }, question);\n\n // 解析返回的 JSON\n let parsed: { text: string; debug: string[] };\n try {\n parsed = JSON.parse(reply);\n } catch {\n parsed = { text: reply, debug: [] };\n }\n\n // 输出调试信息\n for (const line of parsed.debug) {\n log(verbose, ` [extract] ${line}`);\n }\n\n if (parsed.text && parsed.text.length > 0) {\n log(verbose, `提取成功,回复长度: ${parsed.text.length} 字符`);\n return parsed.text;\n }\n\n log(verbose, '提取失败,所有方法均未找到回复内容');\n throw new YiyanAgentError('TIMEOUT', 'Failed to extract reply content');\n}\n"],"mappings":"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","names":["path","os","path","fs","path","os"]}
|
|
1
|
+
{"version":3,"sources":["../src/agent.ts","../src/types.ts","../src/profile.ts","../src/browser.ts"],"sourcesContent":["// src/agent.ts\nimport path from 'path';\nimport os from 'os';\nimport {\n YiyanAgentOptions,\n DEFAULT_OPTIONS,\n YiyanAgentError,\n} from './types';\nimport {\n getChromeExecutablePath,\n clearCopiedProfile,\n profileExists,\n} from './profile';\nimport {\n launchBrowser,\n closeBrowser,\n navigateToYiyan,\n sendMessage,\n waitForReply,\n extractReply,\n checkLoggedIn,\n waitForUserAction,\n} from './browser';\n\n/** 默认 profile 存储目录 */\nconst DEFAULT_PROFILE_BASE_DIR = path.join(\n os.homedir(),\n '.yiyan-browser-agent'\n);\n\n/**\n * 文心一言浏览器代理\n */\nexport class YiyanAgent {\n private options: Required<YiyanAgentOptions>;\n private profileDir: string;\n private verbose: boolean;\n\n constructor(options?: YiyanAgentOptions & { verbose?: boolean }) {\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n\n this.profileDir =\n this.options.profileDir || DEFAULT_PROFILE_BASE_DIR;\n\n this.verbose = options?.verbose ?? true; // 默认开启日志\n }\n\n /**\n * 发送问题并获取答案\n * @param question 问题文本\n * @param headful 是否使用有头浏览器(可见窗口),默认为 false(优先 headless,登录后无需弹窗)\n * \n * 策略:先尝试 headless(不弹窗),如果失败(验证码/未登录)自动回退到有头模式\n */\n async ask(question: string, headful = false): Promise<string> {\n // 如果用户显式要求有头模式,直接有头\n if (headful) {\n return this.executeAsk(question, true);\n }\n\n // 否则先尝试 headless\n try {\n return await this.executeAsk(question, false);\n } catch (error) {\n // headless 遇到验证码或未登录,自动回退有头模式\n if (error instanceof YiyanAgentError && \n (error.type === 'CAPTCHA' || error.type === 'TIMEOUT')) {\n if (this.verbose) {\n process.stderr.write('[yiyan-agent] headless 模式失败,自动切换到有头模式...\\n');\n }\n return this.executeAsk(question, true);\n }\n throw error;\n }\n }\n\n /**\n * 执行单次问答\n */\n private async executeAsk(question: string, headful: boolean): Promise<string> {\n const platform = os.platform();\n const chromePath =\n this.options.chromePath || getChromeExecutablePath(platform);\n\n if (!chromePath) {\n throw new YiyanAgentError(\n 'BROWSER_LAUNCH',\n `Unsupported platform: ${platform}`\n );\n }\n\n // profile 目录直接使用 chrome-profile 子目录\n const profilePath = path.join(this.profileDir, 'chrome-profile');\n\n // 启动浏览器(CDP 模式)\n const { browser, cdpBrowser, page, chromeProcess } = await launchBrowser({\n chromePath,\n profilePath,\n headless: !headful,\n timeout: 30000,\n verbose: this.verbose,\n });\n\n try {\n // 导航到文心一言\n await navigateToYiyan(page, this.verbose);\n\n // 检查登录状态\n const isLoggedIn = await checkLoggedIn(page, this.verbose);\n \n if (!isLoggedIn) {\n if (headful) {\n // 有头模式:暂停等待用户手动登录\n await waitForUserAction(page, 'login', this.verbose);\n } else {\n // headless 模式未登录:抛出错误,让上层回退到有头模式\n throw new YiyanAgentError(\n 'CAPTCHA',\n 'Not logged in. Please run \"yiyan-agent login\" first, or use headed mode.'\n );\n }\n }\n\n // 发送消息\n await sendMessage(page, question, this.verbose, headful);\n\n // 等待回复\n await waitForReply(page, this.options.timeout, this.verbose, headful);\n\n // 提取回复\n const reply = await extractReply(page, question, this.verbose);\n\n return reply;\n } finally {\n await closeBrowser(browser, chromeProcess, cdpBrowser, this.verbose);\n }\n }\n\n /**\n * 登录文心一言(打开有头浏览器让用户手动登录)\n * 登录成功后,后续的 ask 调用将自动使用登录状态\n */\n async login(): Promise<void> {\n const platform = os.platform();\n const chromePath =\n this.options.chromePath || getChromeExecutablePath(platform);\n\n if (!chromePath) {\n throw new YiyanAgentError(\n 'BROWSER_LAUNCH',\n `Unsupported platform: ${platform}`\n );\n }\n\n const profilePath = path.join(this.profileDir, 'chrome-profile');\n\n // 启动有头浏览器(用户可以看到并操作)\n const { browser, cdpBrowser, page, chromeProcess } = await launchBrowser({\n chromePath,\n profilePath,\n headless: false,\n timeout: 60000,\n verbose: this.verbose,\n });\n\n try {\n await page.goto('https://yiyan.baidu.com/', {\n waitUntil: 'networkidle',\n timeout: 60000,\n });\n\n // 等待 SPA 渲染\n await page.waitForTimeout(5000);\n\n // 输出提示信息\n process.stderr.write('\\n[yiyan-agent] ============================================\\n');\n process.stderr.write('[yiyan-agent] 请在浏览器中完成登录文心一言\\n');\n process.stderr.write('[yiyan-agent] 登录成功后,回到此处按 Enter 键继续\\n');\n process.stderr.write('[yiyan-agent] ============================================\\n\\n');\n\n // 等待用户按 Enter 键确认(ESM 兼容方式)\n const readline = await import('readline');\n await new Promise<void>((resolve) => {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n rl.question('按 Enter 继续...', () => {\n rl.close();\n resolve();\n });\n });\n\n process.stderr.write('[yiyan-agent] ✓ 登录完成,正在保存登录状态...\\n');\n\n // 等待一下确保登录状态保存到 profile\n await new Promise(resolve => setTimeout(resolve, 3000));\n } finally {\n await closeBrowser(browser, chromeProcess, cdpBrowser, this.verbose);\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n async reset(): Promise<void> {\n await clearCopiedProfile(this.profileDir);\n }\n\n status(): { loggedIn: boolean; profilePath: string } {\n return {\n loggedIn: profileExists(this.profileDir),\n profilePath: path.join(this.profileDir, 'chrome-profile'),\n };\n }\n}\n","/**\n * yiyan-browser-agent 类型定义\n */\n\n/** 配置选项 */\nexport interface YiyanAgentOptions {\n /** 超时毫秒,默认 120000 */\n timeout?: number;\n /** 重试次数,默认 3 */\n retryCount?: number;\n /** 自定义 profile 目录 */\n profileDir?: string;\n /** 自定义 Chrome 可执行文件路径 */\n chromePath?: string;\n}\n\n/** CLI 输出格式 */\nexport interface CliOutput {\n success: boolean;\n /** 用户发送的问题 */\n question: string;\n /** 成功时有值 */\n answer?: string;\n /** 失败时有值 */\n error?: string;\n /** 耗时毫秒 */\n duration: number;\n}\n\n/** 错误类型 */\nexport type YiyanAgentErrorType =\n | 'BROWSER_LAUNCH'\n | 'PROFILE_COPY'\n | 'TIMEOUT'\n | 'NETWORK'\n | 'CAPTCHA';\n\n/** 自定义错误类 */\nexport class YiyanAgentError extends Error {\n constructor(\n public type: YiyanAgentErrorType,\n message: string\n ) {\n super(message);\n this.name = 'YiyanAgentError';\n }\n}\n\n/** 默认配置 */\nexport const DEFAULT_OPTIONS: Required<YiyanAgentOptions> = {\n timeout: 120000,\n retryCount: 3,\n profileDir: '',\n chromePath: '',\n};\n\n/** 文心一言网页 URL */\nexport const YIYAN_CHAT_URL = 'https://yiyan.baidu.com/';\n","// src/profile.ts\nimport path from 'path';\nimport os from 'os';\nimport fs from 'fs';\nimport fsp from 'fs/promises';\nimport { YiyanAgentError } from './types';\n\n/**\n * 获取 Chrome 用户数据目录路径\n */\nexport function getChromeProfilePath(platform: NodeJS.Platform): string | null {\n const home = os.homedir();\n\n switch (platform) {\n case 'win32':\n const localAppData = process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');\n return path.join(localAppData, 'Google', 'Chrome', 'User Data');\n case 'darwin':\n return path.join(home, 'Library', 'Application Support', 'Google', 'Chrome');\n case 'linux':\n return path.join(home, '.config', 'google-chrome');\n default:\n return null;\n }\n}\n\n/**\n * 获取 Chrome 可执行文件路径\n */\nexport function getChromeExecutablePath(platform: NodeJS.Platform): string | null {\n switch (platform) {\n case 'win32':\n const programFiles = process.env.PROGRAMFILES || 'C:\\\\Program Files';\n return path.join(programFiles, 'Google', 'Chrome', 'Application', 'chrome.exe');\n case 'darwin':\n return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';\n case 'linux':\n return '/usr/bin/google-chrome';\n default:\n return null;\n }\n}\n\n/** Profile 子目录名称 */\nconst PROFILE_TARGET_NAME = 'chrome-profile';\n\n/**\n * 检查并复制 Chrome profile 到目标目录\n * 如果已存在且未过期则直接返回路径,否则重新复制\n */\nexport async function copyChromeProfile(\n platform: NodeJS.Platform,\n targetBaseDir: string\n): Promise<string> {\n const targetPath = path.join(targetBaseDir, PROFILE_TARGET_NAME);\n\n // 检查已复制的 profile 是否有效(5分钟内不需要重新复制)\n if (fs.existsSync(targetPath)) {\n const stat = fs.statSync(targetPath);\n const ageMinutes = (Date.now() - stat.mtimeMs) / 60000;\n if (ageMinutes < 5) {\n return targetPath;\n }\n // 过期了,删除旧的\n await fsp.rm(targetPath, { recursive: true, force: true });\n }\n\n // 获取源 profile 路径\n const sourcePath = getChromeProfilePath(platform);\n if (!sourcePath) {\n throw new YiyanAgentError('PROFILE_COPY', `Unsupported platform: ${platform}`);\n }\n\n // 检查源是否存在\n try {\n await fsp.access(sourcePath);\n } catch {\n throw new YiyanAgentError(\n 'PROFILE_COPY',\n `Chrome profile not found at: ${sourcePath}`\n );\n }\n\n // 创建目标目录\n await fsp.mkdir(targetPath, { recursive: true });\n\n // 复制根目录关键文件\n const rootFiles = [\n 'Local State',\n 'First Run',\n 'Default',\n ];\n\n for (const file of rootFiles) {\n const srcFile = path.join(sourcePath, file);\n const destFile = path.join(targetPath, file);\n\n try {\n const stat = await fsp.stat(srcFile);\n if (stat.isDirectory()) {\n await copyDir(srcFile, destFile);\n } else {\n await fsp.copyFile(srcFile, destFile);\n }\n } catch {\n continue;\n }\n }\n\n // 复制 Default 目录下的关键文件(只复制登录和 Cookie 相关的小文件)\n const defaultDir = path.join(sourcePath, 'Default');\n if (fs.existsSync(defaultDir)) {\n const destDefaultDir = path.join(targetPath, 'Default');\n await fsp.mkdir(destDefaultDir, { recursive: true });\n\n const defaultFiles = [\n 'Cookies',\n 'Cookies-journal',\n 'Login Data',\n 'Login Data-journal',\n 'Login Data For Account',\n 'Login Data For Account-journal',\n 'Preferences',\n 'Secure Preferences',\n 'Network',\n 'Network Action Predictor',\n 'Web Data',\n 'Web Data-journal',\n 'Extension Cookies',\n 'Extension Cookies-journal',\n ];\n\n for (const file of defaultFiles) {\n const srcFile = path.join(defaultDir, file);\n const destFile = path.join(destDefaultDir, file);\n\n try {\n const stat = await fsp.stat(srcFile);\n if (stat.isDirectory()) {\n await copyDir(srcFile, destFile);\n } else {\n await fsp.copyFile(srcFile, destFile);\n }\n } catch {\n continue;\n }\n }\n }\n\n // 更新目录修改时间\n await fsp.utimes(targetPath, new Date(), new Date());\n\n return targetPath;\n}\n\n/**\n * 复制目录\n */\nasync function copyDir(src: string, dest: string): Promise<void> {\n await fsp.mkdir(dest, { recursive: true });\n const entries = await fsp.readdir(src, { withFileTypes: true });\n\n for (const entry of entries) {\n const srcPath = path.join(src, entry.name);\n const destPath = path.join(dest, entry.name);\n\n if (entry.isDirectory()) {\n await copyDir(srcPath, destPath);\n } else {\n await fsp.copyFile(srcPath, destPath);\n }\n }\n}\n\n/**\n * 清除复制的 profile\n */\nexport async function clearCopiedProfile(profileDir: string): Promise<void> {\n const targetPath = path.join(profileDir, PROFILE_TARGET_NAME);\n if (fs.existsSync(targetPath)) {\n await fsp.rm(targetPath, { recursive: true, force: true });\n }\n}\n\n/**\n * 检查 profile 是否存在\n */\nexport function profileExists(profileDir: string): boolean {\n const targetPath = path.join(profileDir, PROFILE_TARGET_NAME);\n return fs.existsSync(targetPath);\n}\n","// src/browser.ts\nimport { chromium, Page, BrowserContext, Browser } from 'playwright-core';\nimport { YiyanAgentError, YIYAN_CHAT_URL } from './types';\nimport http from 'http';\nimport { spawn, ChildProcess, execSync } from 'child_process';\nimport path from 'path';\nimport os from 'os';\nimport fs from 'fs';\n\nexport interface LaunchBrowserOptions {\n chromePath: string;\n profilePath: string;\n headless: boolean;\n timeout?: number;\n /** 是否输出进度日志 */\n verbose?: boolean;\n}\n\nconst CDP_PORT = 19222;\n\n/** 调试截图保存目录 */\nconst DEBUG_DIR = path.join(os.homedir(), '.yiyan-browser-agent', 'debug');\n\n/** 日志输出(仅 verbose 模式) */\nfunction log(verbose: boolean, msg: string): void {\n if (verbose) {\n process.stderr.write(`[yiyan-agent] ${msg}\\n`);\n }\n}\n\n/** 确保调试目录存在 */\nfunction ensureDebugDir(): void {\n if (!fs.existsSync(DEBUG_DIR)) {\n fs.mkdirSync(DEBUG_DIR, { recursive: true });\n }\n}\n\n/**\n * 杀死占用指定端口的进程(启动前清理残留 Chrome)\n */\nfunction killProcessOnPort(port: number): void {\n try {\n if (process.platform === 'win32') {\n const output = execSync(\n `netstat -ano | findstr :${port} | findstr LISTENING`,\n { encoding: 'utf-8', timeout: 5000 }\n ).trim();\n if (output) {\n const lines = output.split('\\n').filter(l => l.trim());\n const pids = new Set<string>();\n for (const line of lines) {\n const parts = line.trim().split(/\\s+/);\n const pid = parts[parts.length - 1];\n if (pid && /^\\d+$/.test(pid)) pids.add(pid);\n }\n for (const pid of pids) {\n try {\n execSync(`taskkill /F /T /PID ${pid}`, { timeout: 5000 });\n } catch {\n // 进程可能已经退出\n }\n }\n }\n } else {\n const output = execSync(\n `lsof -ti :${port}`,\n { encoding: 'utf-8', timeout: 5000 }\n ).trim();\n if (output) {\n const pids = output.split('\\n').filter(l => l.trim());\n for (const pid of pids) {\n try {\n process.kill(parseInt(pid, 10), 'SIGKILL');\n } catch {\n // 进程可能已经退出\n }\n }\n }\n }\n } catch {\n // 没有找到占用端口的进程,正常情况\n }\n}\n\n/**\n * 杀死进程树(Windows 用 taskkill /T,其他平台用 process.kill 负 PID)\n */\nfunction killProcessTree(pid: number): void {\n try {\n if (process.platform === 'win32') {\n execSync(`taskkill /F /T /PID ${pid}`, { timeout: 5000 });\n } else {\n try {\n process.kill(-pid, 'SIGKILL');\n } catch {\n process.kill(pid, 'SIGKILL');\n }\n }\n } catch {\n // 进程可能已经退出\n }\n}\n\n/**\n * 等待 CDP 端口就绪\n */\nfunction waitForCDP(port: number, timeout = 15000): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const start = Date.now();\n const check = () => {\n http.get(`http://localhost:${port}/json/version`, (res) => {\n let data = '';\n res.on('data', chunk => data += chunk);\n res.on('end', () => {\n try { resolve(JSON.parse(data)); } catch (e) { reject(e); }\n });\n }).on('error', () => {\n if (Date.now() - start > timeout) reject(new Error('CDP connection timeout'));\n else setTimeout(check, 500);\n });\n };\n check();\n });\n}\n\n/**\n * 启动 Chrome 浏览器(通过 CDP 连接模式)\n */\nexport async function launchBrowser(\n options: LaunchBrowserOptions\n): Promise<{ browser: BrowserContext; cdpBrowser: Browser; page: Page; chromeProcess: ChildProcess | null }> {\n const { chromePath, profilePath, headless, timeout = 30000, verbose = false } = options;\n\n try {\n log(verbose, '清理残留 Chrome 进程...');\n killProcessOnPort(CDP_PORT);\n await new Promise(resolve => setTimeout(resolve, 500));\n\n const chromeArgs = [\n `--remote-debugging-port=${CDP_PORT}`,\n `--user-data-dir=${profilePath}`,\n '--no-first-run',\n '--no-default-browser-check',\n '--disable-blink-features=AutomationControlled',\n ];\n\n if (headless) {\n chromeArgs.push('--headless=new');\n }\n\n log(verbose, `启动 Chrome: ${chromePath} ${headless ? '(headless)' : '(headed)'}`);\n const chromeProcess = spawn(chromePath, chromeArgs, {\n detached: true,\n stdio: 'ignore',\n });\n chromeProcess.unref();\n\n const spawnError = new Promise<never>((_, reject) => {\n chromeProcess.on('error', (err) => {\n reject(new YiyanAgentError('BROWSER_LAUNCH', `Failed to spawn Chrome: ${err.message}`));\n });\n });\n\n log(verbose, '等待 CDP 端口就绪...');\n await Promise.race([\n waitForCDP(CDP_PORT, timeout),\n spawnError,\n ]);\n\n log(verbose, '通过 CDP 连接浏览器...');\n const cdpBrowser = await chromium.connectOverCDP(`http://localhost:${CDP_PORT}`);\n\n const contexts = cdpBrowser.contexts();\n const context = contexts.length > 0 ? contexts[0] : await cdpBrowser.newContext();\n context.setDefaultTimeout(timeout);\n const pages = context.pages();\n const page = pages.length > 0 ? pages[0] : await context.newPage();\n\n log(verbose, '浏览器启动完成');\n return { browser: context, cdpBrowser, page, chromeProcess };\n } catch (error) {\n killProcessOnPort(CDP_PORT);\n throw new YiyanAgentError(\n 'BROWSER_LAUNCH',\n `Failed to launch browser: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * 关闭浏览器(彻底清理:断开 CDP → 关闭页面 → 杀进程树 → 清理残留端口)\n */\nexport async function closeBrowser(\n browser: BrowserContext,\n chromeProcess: ChildProcess | null,\n cdpBrowser?: Browser,\n verbose?: boolean\n): Promise<void> {\n log(!!verbose, '关闭浏览器...');\n\n if (cdpBrowser) {\n try {\n await cdpBrowser.close();\n } catch {\n // 忽略关闭错误\n }\n }\n\n try {\n const pages = browser.pages();\n for (const page of pages) {\n await page.close().catch(() => {});\n }\n } catch {\n // 忽略关闭错误\n }\n\n if (chromeProcess && chromeProcess.pid) {\n killProcessTree(chromeProcess.pid);\n }\n\n killProcessOnPort(CDP_PORT);\n log(!!verbose, '浏览器已关闭');\n}\n\n/**\n * 导航到文心一言聊天页面\n */\nexport async function navigateToYiyan(page: Page, verbose = false): Promise<void> {\n log(verbose, '导航到文心一言聊天页面...');\n await page.goto(YIYAN_CHAT_URL, {\n waitUntil: 'networkidle',\n timeout: 60000,\n });\n\n // 文心一言是 SPA,需要额外等待渲染\n await page.waitForTimeout(5000);\n\n try {\n await page.waitForSelector('[contenteditable=\"true\"]', { timeout: 15000 });\n log(verbose, '页面加载完成,输入框已就绪');\n } catch {\n log(verbose, '警告:未检测到输入框,继续执行');\n }\n}\n\n/**\n * 检测文心一言验证码弹窗\n */\nexport async function checkCaptcha(page: Page, verbose = false): Promise<boolean> {\n const captchaIndicators = [\n '请完成下列验证后继续',\n '按住左边按钮拖动',\n '滑动验证',\n '点击验证',\n '安全验证',\n 'captcha',\n '请完成验证',\n ];\n\n const hasCaptcha = await page.evaluate((indicators) => {\n const bodyText = document.body.innerText || '';\n for (const indicator of indicators) {\n if (bodyText.includes(indicator)) return true;\n }\n // 检查是否有弹窗/遮罩层\n const dialogs = document.querySelectorAll('[role=\"dialog\"], [class*=\"captcha\"], [class*=\"verify\"]');\n if (dialogs.length > 0) return true;\n return false;\n }, captchaIndicators);\n\n if (hasCaptcha) {\n log(verbose, '⚠ 检测到验证码弹窗!');\n }\n return hasCaptcha;\n}\n\n/**\n * 检测是否已登录文心一言\n */\nexport async function checkLoggedIn(page: Page, verbose = false): Promise<boolean> {\n const isLoggedIn = await page.evaluate(() => {\n // 文心一言未登录时:页面有\"登录\"按钮和\"未登录\"文字\n const bodyText = document.body.innerText || '';\n\n // 如果有\"未登录\"文字 = 未登录\n if (bodyText.includes('未登录')) return false;\n\n // 检查是否有\"登录\"按钮\n const loginButtons = document.querySelectorAll('button');\n for (const btn of loginButtons) {\n const text = btn.textContent?.trim() || '';\n if (text === '登录') {\n return false; // 有登录按钮 = 未登录\n }\n }\n\n // 如果没有\"登录\"按钮和\"未登录\"文字,说明已登录\n return true;\n });\n\n if (isLoggedIn) {\n log(verbose, '✓ 已检测到登录状态');\n } else {\n log(verbose, '⚠ 未检测到登录状态');\n }\n return isLoggedIn;\n}\n\n/**\n * 有头模式下,暂停等待用户手动操作(过验证码/登录),然后继续\n * 检测到问题已解决后自动继续\n */\nexport async function waitForUserAction(\n page: Page,\n reason: 'captcha' | 'login' | 'no-reply',\n verbose = false\n): Promise<void> {\n const reasonText = reason === 'captcha'\n ? '检测到验证码,请在浏览器中手动完成验证'\n : reason === 'login'\n ? '检测到未登录,请在浏览器中手动登录'\n : 'AI 未回复,请检查浏览器是否需要手动操作(验证码/登录)';\n\n process.stderr.write(`\\n[yiyan-agent] ⚠ ${reasonText}\\n`);\n process.stderr.write('[yiyan-agent] 等待您操作完成...(操作完成后会自动继续)\\n\\n');\n\n // 轮询检测问题是否已解决\n const maxWait = 180000; // 最多等 3 分钟\n const startTime = Date.now();\n\n while (Date.now() - startTime < maxWait) {\n await new Promise(resolve => setTimeout(resolve, 2000));\n\n if (reason === 'captcha') {\n const stillHasCaptcha = await checkCaptcha(page, false);\n if (!stillHasCaptcha) {\n log(verbose, '✓ 验证码已通过!');\n return;\n }\n } else if (reason === 'login') {\n const loggedIn = await checkLoggedIn(page, false);\n if (loggedIn) {\n log(verbose, '✓ 登录成功!');\n return;\n }\n } else {\n // no-reply:检测 AI 回复是否出现\n const hasReply = await page.evaluate(() => {\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (!answerBox) return false;\n const text = answerBox.innerText?.trim() || '';\n return text.length > 0;\n });\n if (hasReply) {\n log(verbose, '✓ AI 已开始回复!');\n return;\n }\n }\n }\n\n log(verbose, '⚠ 等待用户操作超时(3分钟)');\n}\n\n/**\n * 发送消息到文心一言\n * @param headful 是否为有头模式(有头模式下检测到验证码会暂停等待用户操作)\n */\nexport async function sendMessage(page: Page, message: string, verbose = false, headful = false): Promise<void> {\n // 文心一言使用 contenteditable div 而不是 textarea\n const inputSelector = '[contenteditable=\"true\"]';\n\n log(verbose, '等待输入框出现...');\n const inputElement = await page.waitForSelector(inputSelector, { timeout: 15000 });\n if (!inputElement) {\n log(verbose, '⚠ 输入框未找到!');\n throw new YiyanAgentError('NETWORK', 'Input element not found on page');\n }\n log(verbose, '✓ 输入框已找到');\n\n // 点击输入框获取焦点\n await inputElement.click();\n await page.waitForTimeout(500);\n\n // 输入问题(文心一言用 contenteditable,需要用 keyboard.type 而不是 fill)\n log(verbose, `输入问题: \"${message}\"`);\n await page.keyboard.type(message, { delay: 30 });\n\n // 验证填入是否成功\n const filledValue = await inputElement.innerText();\n if (filledValue.includes(message)) {\n log(verbose, '✓ 问题已成功填入输入框');\n } else {\n log(verbose, `⚠ 输入框内容: \"${filledValue.substring(0, 50)}\"`);\n }\n\n // 按 Enter 发送\n log(verbose, '按 Enter 发送消息...');\n await page.keyboard.press('Enter');\n\n // 等待发送后的页面变化\n await page.waitForTimeout(3000);\n\n // 检查是否触发了验证码\n if (await checkCaptcha(page, verbose)) {\n if (headful) {\n await waitForUserAction(page, 'captcha', verbose);\n // 验证码通过后,重新输入并发送消息\n log(verbose, '验证码已通过,重新发送消息...');\n const inputEl = await page.waitForSelector(inputSelector, { timeout: 10000 });\n if (inputEl) {\n await inputEl.click();\n await page.waitForTimeout(300);\n await page.keyboard.type(message, { delay: 30 });\n await page.keyboard.press('Enter');\n await page.waitForTimeout(3000);\n }\n } else {\n throw new YiyanAgentError(\n 'CAPTCHA',\n 'Yiyan detected automation and triggered a captcha. Use headed mode (default) to manually solve it, or run \"login\" first.'\n );\n }\n }\n\n // 检查 URL 变化(文心一言发送消息后会跳转到 /chat/xxx)\n const url = page.url();\n log(verbose, `当前 URL: ${url}`);\n\n if (url.includes('/chat/')) {\n log(verbose, '✓ 已自动进入对话页面');\n } else if (url === 'https://yiyan.baidu.com/' || url === 'https://yiyan.baidu.com') {\n log(verbose, '仍在欢迎页,尝试点击发送按钮...');\n try {\n // 点击发送按钮\n const sendBtn = page.locator('[class*=\"send__\"]').first();\n await sendBtn.click({ timeout: 5000 });\n await page.waitForTimeout(3000);\n log(verbose, '✓ 已点击发送按钮');\n } catch {\n log(verbose, '⚠ 点击发送按钮失败,继续等待回复...');\n }\n } else {\n log(verbose, `当前页面: ${url}`);\n }\n}\n\n/**\n * 保存调试截图\n */\nasync function saveDebugScreenshot(page: Page, name: string, verbose: boolean): Promise<void> {\n try {\n ensureDebugDir();\n const screenshotPath = path.join(DEBUG_DIR, `${name}.png`);\n await page.screenshot({ path: screenshotPath, fullPage: true });\n log(verbose, `调试截图已保存: ${screenshotPath}`);\n } catch {\n // 截图失败不影响主流程\n }\n}\n\n/**\n * 等待回复完成\n * @param headful 是否为有头模式(超时且有头模式下会等待用户手动操作)\n */\nexport async function waitForReply(\n page: Page,\n timeout: number,\n verbose = false,\n headful = false\n): Promise<void> {\n const maxWait = Math.min(timeout, 60000);\n\n log(verbose, `等待 AI 回复(最多 ${maxWait / 1000} 秒)...`);\n\n // 文心一言 AI 回复在 answerBox 容器中,流式渲染\n const startTime = Date.now();\n let lastLen = 0;\n let stableCount = 0;\n let replyStarted = false;\n\n while (Date.now() - startTime < maxWait) {\n const state = await page.evaluate(() => {\n // 文心一言 AI 回复在 answerBox 或 dialogueCardList 中\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n return {\n hasAiReply: text.length > 0,\n aiTextLen: text.length,\n aiPreview: text.substring(0, 100),\n };\n }\n\n // 备选:检查 dialogueCardList\n const cardList = document.querySelector('[class*=\"dialogueCardList\"]');\n if (cardList) {\n const lastCard = cardList.lastElementChild as HTMLElement;\n if (lastCard) {\n const text = lastCard.innerText?.trim() || '';\n return {\n hasAiReply: text.length > 0,\n aiTextLen: text.length,\n aiPreview: text.substring(0, 100),\n };\n }\n }\n\n return { hasAiReply: false, aiTextLen: 0, aiPreview: '' };\n });\n\n if (state.hasAiReply && state.aiTextLen > 0) {\n if (!replyStarted) {\n log(verbose, `✓ AI 回复已开始生成(${state.aiTextLen}字): ${state.aiPreview.substring(0, 60)}...`);\n replyStarted = true;\n }\n\n // 文本长度不再变化 → 回复完成\n if (state.aiTextLen === lastLen) {\n stableCount++;\n if (stableCount >= 3) {\n log(verbose, `✓ AI 回复已完成(${state.aiTextLen}字,连续 ${stableCount} 次长度不变)`);\n break;\n }\n } else {\n stableCount = 0;\n lastLen = state.aiTextLen;\n }\n }\n\n await page.waitForTimeout(1500);\n }\n\n if (!replyStarted) {\n // 超时后检查是否触发了验证码\n if (await checkCaptcha(page, verbose)) {\n if (headful) {\n await waitForUserAction(page, 'captcha', verbose);\n const retryStart = Date.now();\n const retryMaxWait = 30000;\n while (Date.now() - retryStart < retryMaxWait) {\n const retryState = await page.evaluate(() => {\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n return { hasAiReply: text.length > 0, aiTextLen: text.length };\n }\n return { hasAiReply: false, aiTextLen: 0 };\n });\n if (retryState.hasAiReply) {\n log(verbose, '✓ 验证码通过后 AI 已开始回复');\n replyStarted = true;\n break;\n }\n await page.waitForTimeout(1500);\n }\n } else {\n throw new YiyanAgentError(\n 'CAPTCHA',\n 'Yiyan detected automation and triggered a captcha. Use --headful flag to manually solve it, or run \"login\" first.'\n );\n }\n } else if (headful) {\n await waitForUserAction(page, 'no-reply', verbose);\n const retryStart = Date.now();\n const retryMaxWait = 60000;\n while (Date.now() - retryStart < retryMaxWait) {\n const retryState = await page.evaluate(() => {\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n return { hasAiReply: text.length > 0, aiTextLen: text.length };\n }\n return { hasAiReply: false, aiTextLen: 0 };\n });\n if (retryState.hasAiReply) {\n if (retryState.aiTextLen === lastLen) {\n stableCount++;\n if (stableCount >= 3) {\n log(verbose, `✓ 用户操作后 AI 回复已完成(${retryState.aiTextLen}字)`);\n replyStarted = true;\n break;\n }\n } else {\n stableCount = 0;\n lastLen = retryState.aiTextLen;\n }\n }\n await page.waitForTimeout(1500);\n }\n } else {\n // headless 模式超时且无验证码 → 可能是未登录或其他问题\n throw new YiyanAgentError(\n 'TIMEOUT',\n 'AI reply timeout in headless mode. Try running \"yiyan-agent login\" first, or use --headful flag.'\n );\n }\n }\n\n // 额外等待一小段时间确保最终渲染\n await page.waitForTimeout(2000);\n\n // 页面诊断\n const pageDump = await page.evaluate(() => {\n const result: Record<string, any> = {};\n\n const selectors = [\n '[class*=\"answerBox\"]', '[class*=\"dialogueCardList\"]', '[class*=\"dialogue_card_item\"]',\n '[class*=\"chatViewer\"]', '[class*=\"flowBox\"]', '[class*=\"roleSystem\"]',\n '[class*=\"mdRenderContainer\"]', '[class*=\"agent-markdown\"]', '[class*=\"markdown\"]',\n '[class*=\"content\"]', '[class*=\"chat\"]', '[class*=\"message\"]',\n ];\n for (const sel of selectors) {\n result[`selector:${sel}`] = document.querySelectorAll(sel).length;\n }\n\n // answerBox 子元素详情\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n if (answerBox) {\n result['answerBox'] = {\n textLen: answerBox.innerText?.trim().length || 0,\n preview: (answerBox.innerText?.trim() || '').substring(0, 200),\n classes: answerBox.className?.toString().substring(0, 100) || '',\n };\n }\n\n // body 文本(前 1000 字符)\n result['bodyText'] = document.body.innerText?.substring(0, 1000) || '';\n\n // 长文本元素\n const longTextEls: { tag: string; classes: string; textLen: number; preview: string }[] = [];\n const mainContent = document.querySelector('[class*=\"chatViewer\"]') || document.querySelector('[class*=\"chat\"]') || document.body;\n for (const el of Array.from(mainContent.querySelectorAll('div, section, article'))) {\n const text = (el as HTMLElement).innerText?.trim() || '';\n if (text.length > 50 && text.length < 10000) {\n longTextEls.push({\n tag: el.tagName,\n classes: (el as HTMLElement).className?.toString().split(' ').slice(0, 3).join(' ') || '',\n textLen: text.length,\n preview: text.substring(0, 80),\n });\n }\n }\n result['longTextElements'] = longTextEls\n .sort((a, b) => b.textLen - a.textLen)\n .slice(0, 10);\n\n return result;\n });\n\n // 输出诊断\n log(verbose, '=== 页面诊断 ===');\n for (const [key, value] of Object.entries(pageDump)) {\n if (key === 'bodyText' || key === 'longTextElements') continue;\n log(verbose, ` ${key}: ${JSON.stringify(value)}`);\n }\n if (pageDump['longTextElements'] && (pageDump['longTextElements'] as any[]).length > 0) {\n log(verbose, ' 长文本元素 TOP 10:');\n for (const el of pageDump['longTextElements'] as any[]) {\n log(verbose, ` <${el.tag}> .${el.classes} (${el.textLen}字): ${el.preview}...`);\n }\n }\n if (pageDump['bodyText']) {\n log(verbose, ` 页面文本(前1000字): ${(pageDump['bodyText'] as string).substring(0, 1000)}`);\n }\n}\n\n/**\n * 提取回复内容\n */\nexport async function extractReply(page: Page, question: string, verbose = false): Promise<string> {\n log(verbose, '提取 AI 回复内容...');\n\n // 先保存截图,方便排查问题\n await saveDebugScreenshot(page, 'before-extract', verbose);\n\n const reply = await page.evaluate((userQuestion: string) => {\n const debugInfo: string[] = [];\n\n // ===== 方法1(最佳):answerBox 容器 =====\n const answerBox = document.querySelector('[class*=\"answerBox\"]');\n debugInfo.push(`answerBox found: ${!!answerBox}`);\n if (answerBox) {\n const text = answerBox.innerText?.trim() || '';\n debugInfo.push(`answerBox textLen: ${text.length}`);\n if (text.length > 0) {\n // 去除思考过程,只取最终回复\n // 文心一言思考过程在 processItem 中,最终回复在后面\n // 尝试只取 agent-markdown 或 mdRenderContainer 的内容\n const mdContainer = answerBox.querySelector('[class*=\"agent-markdown\"], [class*=\"mdRenderContainer\"]');\n if (mdContainer) {\n const mdText = mdContainer.innerText?.trim() || '';\n if (mdText.length > 0) {\n debugInfo.push(`method1 markdown success: ${mdText.length} chars`);\n return JSON.stringify({ text: mdText, debug: debugInfo });\n }\n }\n\n debugInfo.push(`method1 answerBox success: ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法2:dialogueCardList 中最后一个对话卡片 =====\n const cardList = document.querySelector('[class*=\"dialogueCardList\"]');\n debugInfo.push(`dialogueCardList found: ${!!cardList}`);\n if (cardList && cardList.children.length > 0) {\n // 最后一个卡片通常是 AI 回复\n const lastCard = cardList.lastElementChild as HTMLElement;\n const text = lastCard?.innerText?.trim() || '';\n debugInfo.push(`lastCard textLen: ${text.length}`);\n if (text.length > 0) {\n debugInfo.push(`method2 success: ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法3:flowBox(answerBox 的父级容器) =====\n const flowBoxes = document.querySelectorAll('[class*=\"flowBox\"]');\n debugInfo.push(`flowBoxes count: ${flowBoxes.length}`);\n for (let i = flowBoxes.length - 1; i >= 0; i--) {\n const box = flowBoxes[i] as HTMLElement;\n const text = box.innerText?.trim() || '';\n if (text.length > 10) {\n debugInfo.push(`method3 success: flowBox[${i}] ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法4:roleSystemBot(AI 回复角色标识的父容器) =====\n const botContainers = document.querySelectorAll('[class*=\"roleSystemBot\"]');\n debugInfo.push(`roleSystemBot count: ${botContainers.length}`);\n for (let i = botContainers.length - 1; i >= 0; i--) {\n const container = botContainers[i] as HTMLElement;\n const text = container.innerText?.trim() || '';\n if (text.length > 10) {\n debugInfo.push(`method4 success: botContainer[${i}] ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法5:mdRenderContainer / agent-markdown =====\n const mdContainers = document.querySelectorAll('[class*=\"mdRenderContainer\"], [class*=\"agent-markdown\"]');\n debugInfo.push(`md containers count: ${mdContainers.length}`);\n for (let i = mdContainers.length - 1; i >= 0; i--) {\n const container = mdContainers[i] as HTMLElement;\n const text = container.innerText?.trim() || '';\n if (text.length > 10) {\n debugInfo.push(`method5 success: mdContainer[${i}] ${text.length} chars`);\n return JSON.stringify({ text, debug: debugInfo });\n }\n }\n\n // ===== 方法6:页面文本解析(fallback)=====\n const fullText = document.body.innerText;\n const lines = fullText.split('\\n').map(l => l.trim()).filter(l => l.length > 0);\n\n const uiWords = new Set([\n '文心一言', '新对话', '创意写作', '智慧绘图', '超级智能体', '更多',\n '我的收藏', '项目', '对话', '暂无记录', '未登录', '登录',\n '内容由AI生成,仅供参考,请仔细甄别', '参考',\n '深度分析需求并解答', '通用写作', '阅读分析',\n '网页工坊', '智能翻译', '代码编程',\n ]);\n\n const userQIdx = lines.findIndex(l => l === userQuestion || l.includes(userQuestion));\n debugInfo.push(`userQ idx in body text: ${userQIdx}, total lines: ${lines.length}`);\n\n if (userQIdx >= 0) {\n const replyLines: string[] = [];\n for (let i = userQIdx + 1; i < lines.length; i++) {\n const line = lines[i];\n if (line.length > 0 && !uiWords.has(line) && !line.startsWith('搜索') && !line.includes('篇资料')) {\n replyLines.push(line);\n }\n if (line === '快速' || line === '更多' || line.includes('内容由AI生成')) break;\n }\n if (replyLines.length > 0) {\n debugInfo.push(`method6 success: ${replyLines.length} lines`);\n return JSON.stringify({ text: replyLines.join('\\n'), debug: debugInfo });\n }\n }\n\n // ===== 方法7:取页面中最长的非 UI 文本块 =====\n const allTextBlocks: { text: string; length: number }[] = [];\n const blockElements = document.querySelectorAll('div, section, article, p, span');\n for (const el of blockElements) {\n const elText = (el as HTMLElement).innerText?.trim() || '';\n if (elText.length > 50 && elText.length < 10000) {\n if (!uiWords.has(elText)) {\n allTextBlocks.push({ text: elText, length: elText.length });\n }\n }\n }\n allTextBlocks.sort((a, b) => b.length - a.length);\n if (allTextBlocks.length > 0) {\n debugInfo.push(`method7 fallback: longest text block ${allTextBlocks[0].length} chars`);\n return JSON.stringify({ text: allTextBlocks[0].text, debug: debugInfo });\n }\n\n // ===== 全部失败 =====\n debugInfo.push(`ALL METHODS FAILED`);\n debugInfo.push(`body text length: ${fullText.length}`);\n debugInfo.push(`body text first 500: ${fullText.substring(0, 500)}`);\n return JSON.stringify({ text: '', debug: debugInfo });\n }, question);\n\n // 解析返回的 JSON\n let parsed: { text: string; debug: string[] };\n try {\n parsed = JSON.parse(reply);\n } catch {\n parsed = { text: reply, debug: [] };\n }\n\n // 输出调试信息\n for (const line of parsed.debug) {\n log(verbose, ` [extract] ${line}`);\n }\n\n if (parsed.text && parsed.text.length > 0) {\n log(verbose, `提取成功,回复长度: ${parsed.text.length} 字符`);\n return parsed.text;\n }\n\n log(verbose, '提取失败,所有方法均未找到回复内容');\n throw new YiyanAgentError('TIMEOUT', 'Failed to extract reply content');\n}\n"],"mappings":"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","names":["path","os","path","fs","path","os"]}
|