yiyan-browser-agent 1.1.3 → 1.3.0
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 +0 -1
- package/dist/cli.js +169 -496
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +32 -7
- package/dist/index.js +189 -481
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
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
|
+
});
|
|
2
8
|
|
|
3
9
|
// src/agent.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
10
|
+
import path from "path";
|
|
11
|
+
import os from "os";
|
|
6
12
|
|
|
7
13
|
// src/types.ts
|
|
8
14
|
var YiyanAgentError = class extends Error {
|
|
@@ -21,214 +27,74 @@ var DEFAULT_OPTIONS = {
|
|
|
21
27
|
};
|
|
22
28
|
var YIYAN_CHAT_URL = "https://yiyan.baidu.com/";
|
|
23
29
|
|
|
24
|
-
// src/profile.ts
|
|
25
|
-
import path from "path";
|
|
26
|
-
import fs from "fs";
|
|
27
|
-
import fsp from "fs/promises";
|
|
28
|
-
function getChromeExecutablePath(platform) {
|
|
29
|
-
switch (platform) {
|
|
30
|
-
case "win32":
|
|
31
|
-
const programFiles = process.env.PROGRAMFILES || "C:\\Program Files";
|
|
32
|
-
return path.join(programFiles, "Google", "Chrome", "Application", "chrome.exe");
|
|
33
|
-
case "darwin":
|
|
34
|
-
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
|
|
35
|
-
case "linux":
|
|
36
|
-
return "/usr/bin/google-chrome";
|
|
37
|
-
default:
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
var PROFILE_TARGET_NAME = "chrome-profile";
|
|
42
|
-
async function clearCopiedProfile(profileDir) {
|
|
43
|
-
const targetPath = path.join(profileDir, PROFILE_TARGET_NAME);
|
|
44
|
-
if (fs.existsSync(targetPath)) {
|
|
45
|
-
await fsp.rm(targetPath, { recursive: true, force: true });
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function profileExists(profileDir) {
|
|
49
|
-
const targetPath = path.join(profileDir, PROFILE_TARGET_NAME);
|
|
50
|
-
return fs.existsSync(targetPath);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
30
|
// src/browser.ts
|
|
54
|
-
import { chromium } from "playwright
|
|
55
|
-
import
|
|
56
|
-
import { spawn, execSync } from "child_process";
|
|
57
|
-
import path2 from "path";
|
|
58
|
-
import os from "os";
|
|
59
|
-
import fs2 from "fs";
|
|
60
|
-
var CDP_PORT = 19222;
|
|
61
|
-
var DEBUG_DIR = path2.join(os.homedir(), ".yiyan-browser-agent", "debug");
|
|
31
|
+
import { chromium } from "playwright";
|
|
32
|
+
import fs from "fs";
|
|
62
33
|
function log(verbose, msg) {
|
|
63
34
|
if (verbose) {
|
|
64
35
|
process.stderr.write(`[yiyan-agent] ${msg}
|
|
65
36
|
`);
|
|
66
37
|
}
|
|
67
38
|
}
|
|
68
|
-
function ensureDebugDir() {
|
|
69
|
-
if (!fs2.existsSync(DEBUG_DIR)) {
|
|
70
|
-
fs2.mkdirSync(DEBUG_DIR, { recursive: true });
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
function killProcessOnPort(port) {
|
|
74
|
-
try {
|
|
75
|
-
if (process.platform === "win32") {
|
|
76
|
-
const output = execSync(
|
|
77
|
-
`netstat -ano | findstr :${port} | findstr LISTENING`,
|
|
78
|
-
{ encoding: "utf-8", timeout: 5e3 }
|
|
79
|
-
).trim();
|
|
80
|
-
if (output) {
|
|
81
|
-
const lines = output.split("\n").filter((l) => l.trim());
|
|
82
|
-
const pids = /* @__PURE__ */ new Set();
|
|
83
|
-
for (const line of lines) {
|
|
84
|
-
const parts = line.trim().split(/\s+/);
|
|
85
|
-
const pid = parts[parts.length - 1];
|
|
86
|
-
if (pid && /^\d+$/.test(pid)) pids.add(pid);
|
|
87
|
-
}
|
|
88
|
-
for (const pid of pids) {
|
|
89
|
-
try {
|
|
90
|
-
execSync(`taskkill /F /T /PID ${pid}`, { timeout: 5e3 });
|
|
91
|
-
} catch {
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
const output = execSync(
|
|
97
|
-
`lsof -ti :${port}`,
|
|
98
|
-
{ encoding: "utf-8", timeout: 5e3 }
|
|
99
|
-
).trim();
|
|
100
|
-
if (output) {
|
|
101
|
-
const pids = output.split("\n").filter((l) => l.trim());
|
|
102
|
-
for (const pid of pids) {
|
|
103
|
-
try {
|
|
104
|
-
process.kill(parseInt(pid, 10), "SIGKILL");
|
|
105
|
-
} catch {
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
} catch {
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
function killProcessTree(pid) {
|
|
114
|
-
try {
|
|
115
|
-
if (process.platform === "win32") {
|
|
116
|
-
execSync(`taskkill /F /T /PID ${pid}`, { timeout: 5e3 });
|
|
117
|
-
} else {
|
|
118
|
-
try {
|
|
119
|
-
process.kill(-pid, "SIGKILL");
|
|
120
|
-
} catch {
|
|
121
|
-
process.kill(pid, "SIGKILL");
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
} catch {
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
function waitForCDP(port, timeout = 15e3) {
|
|
128
|
-
return new Promise((resolve, reject) => {
|
|
129
|
-
const start = Date.now();
|
|
130
|
-
const check = () => {
|
|
131
|
-
http.get(`http://localhost:${port}/json/version`, (res) => {
|
|
132
|
-
let data = "";
|
|
133
|
-
res.on("data", (chunk) => data += chunk);
|
|
134
|
-
res.on("end", () => {
|
|
135
|
-
try {
|
|
136
|
-
resolve(JSON.parse(data));
|
|
137
|
-
} catch (e) {
|
|
138
|
-
reject(e);
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
}).on("error", () => {
|
|
142
|
-
if (Date.now() - start > timeout) reject(new Error("CDP connection timeout"));
|
|
143
|
-
else setTimeout(check, 500);
|
|
144
|
-
});
|
|
145
|
-
};
|
|
146
|
-
check();
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
39
|
async function launchBrowser(options) {
|
|
150
|
-
const {
|
|
40
|
+
const { sessionDir, headless, timeout = 3e4, verbose = false } = options;
|
|
151
41
|
try {
|
|
152
|
-
log(verbose, "\
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const chromeArgs = [
|
|
156
|
-
`--remote-debugging-port=${CDP_PORT}`,
|
|
157
|
-
`--user-data-dir=${profilePath}`,
|
|
158
|
-
"--no-first-run",
|
|
159
|
-
"--no-default-browser-check",
|
|
160
|
-
"--disable-blink-features=AutomationControlled"
|
|
161
|
-
];
|
|
162
|
-
if (headless) {
|
|
163
|
-
chromeArgs.push("--headless=new");
|
|
42
|
+
log(verbose, "\u542F\u52A8 Chromium \u6D4F\u89C8\u5668...");
|
|
43
|
+
if (!fs.existsSync(sessionDir)) {
|
|
44
|
+
fs.mkdirSync(sessionDir, { recursive: true });
|
|
164
45
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
46
|
+
const context = await chromium.launchPersistentContext(sessionDir, {
|
|
47
|
+
headless,
|
|
48
|
+
viewport: { width: 1280, height: 900 },
|
|
49
|
+
userAgent: [
|
|
50
|
+
"Mozilla/5.0 (X11; Linux x86_64)",
|
|
51
|
+
"AppleWebKit/537.36 (KHTML, like Gecko)",
|
|
52
|
+
"Chrome/124.0.0.0 Safari/537.36"
|
|
53
|
+
].join(" "),
|
|
54
|
+
args: [
|
|
55
|
+
"--disable-blink-features=AutomationControlled",
|
|
56
|
+
"--no-first-run",
|
|
57
|
+
"--no-default-browser-check",
|
|
58
|
+
"--no-sandbox",
|
|
59
|
+
"--disable-setuid-sandbox",
|
|
60
|
+
"--disable-dev-shm-usage"
|
|
61
|
+
],
|
|
62
|
+
ignoreDefaultArgs: ["--enable-automation"]
|
|
175
63
|
});
|
|
176
|
-
log(verbose, "\u7B49\u5F85 CDP \u7AEF\u53E3\u5C31\u7EEA...");
|
|
177
|
-
await Promise.race([
|
|
178
|
-
waitForCDP(CDP_PORT, timeout),
|
|
179
|
-
spawnError
|
|
180
|
-
]);
|
|
181
|
-
log(verbose, "\u901A\u8FC7 CDP \u8FDE\u63A5\u6D4F\u89C8\u5668...");
|
|
182
|
-
const cdpBrowser = await chromium.connectOverCDP(`http://localhost:${CDP_PORT}`);
|
|
183
|
-
const contexts = cdpBrowser.contexts();
|
|
184
|
-
const context = contexts.length > 0 ? contexts[0] : await cdpBrowser.newContext();
|
|
185
64
|
context.setDefaultTimeout(timeout);
|
|
186
65
|
const pages = context.pages();
|
|
187
66
|
const page = pages.length > 0 ? pages[0] : await context.newPage();
|
|
67
|
+
await page.addInitScript(() => {
|
|
68
|
+
Object.defineProperty(navigator, "webdriver", { get: () => false });
|
|
69
|
+
});
|
|
188
70
|
log(verbose, "\u6D4F\u89C8\u5668\u542F\u52A8\u5B8C\u6210");
|
|
189
|
-
return {
|
|
71
|
+
return { context, page };
|
|
190
72
|
} catch (error) {
|
|
191
|
-
killProcessOnPort(CDP_PORT);
|
|
192
73
|
throw new YiyanAgentError(
|
|
193
74
|
"BROWSER_LAUNCH",
|
|
194
75
|
`Failed to launch browser: ${error instanceof Error ? error.message : String(error)}`
|
|
195
76
|
);
|
|
196
77
|
}
|
|
197
78
|
}
|
|
198
|
-
async function closeBrowser(
|
|
79
|
+
async function closeBrowser(context, verbose) {
|
|
199
80
|
log(!!verbose, "\u5173\u95ED\u6D4F\u89C8\u5668...");
|
|
200
|
-
if (cdpBrowser) {
|
|
201
|
-
try {
|
|
202
|
-
await cdpBrowser.close();
|
|
203
|
-
} catch {
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
81
|
try {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
await page.close().catch(() => {
|
|
210
|
-
});
|
|
211
|
-
}
|
|
82
|
+
await context.close();
|
|
83
|
+
log(!!verbose, "\u6D4F\u89C8\u5668\u5DF2\u5173\u95ED");
|
|
212
84
|
} catch {
|
|
213
85
|
}
|
|
214
|
-
if (chromeProcess && chromeProcess.pid) {
|
|
215
|
-
killProcessTree(chromeProcess.pid);
|
|
216
|
-
}
|
|
217
|
-
killProcessOnPort(CDP_PORT);
|
|
218
|
-
log(!!verbose, "\u6D4F\u89C8\u5668\u5DF2\u5173\u95ED");
|
|
219
86
|
}
|
|
220
87
|
async function navigateToYiyan(page, verbose = false) {
|
|
221
88
|
log(verbose, "\u5BFC\u822A\u5230\u6587\u5FC3\u4E00\u8A00\u804A\u5929\u9875\u9762...");
|
|
222
|
-
await page.goto(YIYAN_CHAT_URL, {
|
|
223
|
-
waitUntil: "networkidle",
|
|
224
|
-
timeout: 6e4
|
|
225
|
-
});
|
|
226
|
-
await page.waitForTimeout(5e3);
|
|
227
89
|
try {
|
|
228
|
-
await page.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
90
|
+
await page.goto(YIYAN_CHAT_URL, {
|
|
91
|
+
waitUntil: "domcontentloaded",
|
|
92
|
+
timeout: 3e4
|
|
93
|
+
});
|
|
94
|
+
await page.waitForTimeout(1500);
|
|
95
|
+
log(verbose, "\u9875\u9762\u52A0\u8F7D\u5B8C\u6210");
|
|
96
|
+
} catch (err) {
|
|
97
|
+
log(verbose, `\u5BFC\u822A\u8B66\u544A: ${err instanceof Error ? err.message : String(err)}`);
|
|
232
98
|
}
|
|
233
99
|
}
|
|
234
100
|
async function checkCaptcha(page, verbose = false) {
|
|
@@ -284,7 +150,7 @@ async function waitForUserAction(page, reason, verbose = false) {
|
|
|
284
150
|
const maxWait = 18e4;
|
|
285
151
|
const startTime = Date.now();
|
|
286
152
|
while (Date.now() - startTime < maxWait) {
|
|
287
|
-
await
|
|
153
|
+
await page.waitForTimeout(2e3);
|
|
288
154
|
if (reason === "captcha") {
|
|
289
155
|
const stillHasCaptcha = await checkCaptcha(page, false);
|
|
290
156
|
if (!stillHasCaptcha) {
|
|
@@ -313,24 +179,38 @@ async function waitForUserAction(page, reason, verbose = false) {
|
|
|
313
179
|
log(verbose, "\u26A0 \u7B49\u5F85\u7528\u6237\u64CD\u4F5C\u8D85\u65F6\uFF083\u5206\u949F\uFF09");
|
|
314
180
|
}
|
|
315
181
|
async function sendMessage(page, message, verbose = false, headful = false) {
|
|
316
|
-
const
|
|
182
|
+
const inputSelectors = [
|
|
183
|
+
'[contenteditable="true"][role="textbox"]',
|
|
184
|
+
'[contenteditable="true"]',
|
|
185
|
+
"textarea[placeholder]",
|
|
186
|
+
"textarea"
|
|
187
|
+
];
|
|
317
188
|
log(verbose, "\u7B49\u5F85\u8F93\u5165\u6846\u51FA\u73B0...");
|
|
318
|
-
|
|
189
|
+
let inputElement = null;
|
|
190
|
+
for (const sel of inputSelectors) {
|
|
191
|
+
try {
|
|
192
|
+
inputElement = await page.waitForSelector(sel, { timeout: 4e3, state: "visible" });
|
|
193
|
+
if (inputElement) break;
|
|
194
|
+
} catch {
|
|
195
|
+
}
|
|
196
|
+
}
|
|
319
197
|
if (!inputElement) {
|
|
320
|
-
|
|
321
|
-
throw new YiyanAgentError("NETWORK", "Input element not found on page");
|
|
198
|
+
throw new YiyanAgentError("NETWORK", "Cannot find the Yiyan chat input box");
|
|
322
199
|
}
|
|
323
200
|
log(verbose, "\u2713 \u8F93\u5165\u6846\u5DF2\u627E\u5230");
|
|
324
|
-
await inputElement.click();
|
|
325
|
-
await page.waitForTimeout(
|
|
201
|
+
await inputElement.click({ force: true });
|
|
202
|
+
await page.waitForTimeout(200);
|
|
203
|
+
await page.keyboard.press("Control+a");
|
|
204
|
+
await page.waitForTimeout(100);
|
|
205
|
+
await page.evaluate((element, content) => {
|
|
206
|
+
element.focus();
|
|
207
|
+
document.execCommand("selectAll", false, null);
|
|
208
|
+
document.execCommand("delete", false, null);
|
|
209
|
+
document.execCommand("insertText", false, content);
|
|
210
|
+
element.dispatchEvent(new InputEvent("input", { bubbles: true, data: content }));
|
|
211
|
+
}, inputElement, message);
|
|
326
212
|
log(verbose, `\u8F93\u5165\u95EE\u9898: "${message}"`);
|
|
327
|
-
await page.
|
|
328
|
-
const filledValue = await inputElement.innerText();
|
|
329
|
-
if (filledValue.includes(message)) {
|
|
330
|
-
log(verbose, "\u2713 \u95EE\u9898\u5DF2\u6210\u529F\u586B\u5165\u8F93\u5165\u6846");
|
|
331
|
-
} else {
|
|
332
|
-
log(verbose, `\u26A0 \u8F93\u5165\u6846\u5185\u5BB9: "${filledValue.substring(0, 50)}"`);
|
|
333
|
-
}
|
|
213
|
+
await page.waitForTimeout(400);
|
|
334
214
|
log(verbose, "\u6309 Enter \u53D1\u9001\u6D88\u606F...");
|
|
335
215
|
await page.keyboard.press("Enter");
|
|
336
216
|
await page.waitForTimeout(3e3);
|
|
@@ -338,47 +218,32 @@ async function sendMessage(page, message, verbose = false, headful = false) {
|
|
|
338
218
|
if (headful) {
|
|
339
219
|
await waitForUserAction(page, "captcha", verbose);
|
|
340
220
|
log(verbose, "\u9A8C\u8BC1\u7801\u5DF2\u901A\u8FC7\uFF0C\u91CD\u65B0\u53D1\u9001\u6D88\u606F...");
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
221
|
+
for (const sel of inputSelectors) {
|
|
222
|
+
try {
|
|
223
|
+
const inputEl = await page.waitForSelector(sel, { timeout: 1e4 });
|
|
224
|
+
if (inputEl) {
|
|
225
|
+
await inputEl.click({ force: true });
|
|
226
|
+
await page.waitForTimeout(300);
|
|
227
|
+
await page.evaluate((element, content) => {
|
|
228
|
+
element.focus();
|
|
229
|
+
document.execCommand("selectAll", false, null);
|
|
230
|
+
document.execCommand("delete", false, null);
|
|
231
|
+
document.execCommand("insertText", false, content);
|
|
232
|
+
}, inputEl, message);
|
|
233
|
+
await page.keyboard.press("Enter");
|
|
234
|
+
await page.waitForTimeout(3e3);
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
} catch {
|
|
238
|
+
}
|
|
348
239
|
}
|
|
349
240
|
} else {
|
|
350
241
|
throw new YiyanAgentError(
|
|
351
242
|
"CAPTCHA",
|
|
352
|
-
|
|
243
|
+
"Yiyan detected automation and triggered a captcha. Use headed mode to manually solve it."
|
|
353
244
|
);
|
|
354
245
|
}
|
|
355
246
|
}
|
|
356
|
-
const url = page.url();
|
|
357
|
-
log(verbose, `\u5F53\u524D URL: ${url}`);
|
|
358
|
-
if (url.includes("/chat/")) {
|
|
359
|
-
log(verbose, "\u2713 \u5DF2\u81EA\u52A8\u8FDB\u5165\u5BF9\u8BDD\u9875\u9762");
|
|
360
|
-
} else if (url === "https://yiyan.baidu.com/" || url === "https://yiyan.baidu.com") {
|
|
361
|
-
log(verbose, "\u4ECD\u5728\u6B22\u8FCE\u9875\uFF0C\u5C1D\u8BD5\u70B9\u51FB\u53D1\u9001\u6309\u94AE...");
|
|
362
|
-
try {
|
|
363
|
-
const sendBtn = page.locator('[class*="send__"]').first();
|
|
364
|
-
await sendBtn.click({ timeout: 5e3 });
|
|
365
|
-
await page.waitForTimeout(3e3);
|
|
366
|
-
log(verbose, "\u2713 \u5DF2\u70B9\u51FB\u53D1\u9001\u6309\u94AE");
|
|
367
|
-
} catch {
|
|
368
|
-
log(verbose, "\u26A0 \u70B9\u51FB\u53D1\u9001\u6309\u94AE\u5931\u8D25\uFF0C\u7EE7\u7EED\u7B49\u5F85\u56DE\u590D...");
|
|
369
|
-
}
|
|
370
|
-
} else {
|
|
371
|
-
log(verbose, `\u5F53\u524D\u9875\u9762: ${url}`);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
async function saveDebugScreenshot(page, name, verbose) {
|
|
375
|
-
try {
|
|
376
|
-
ensureDebugDir();
|
|
377
|
-
const screenshotPath = path2.join(DEBUG_DIR, `${name}.png`);
|
|
378
|
-
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
379
|
-
log(verbose, `\u8C03\u8BD5\u622A\u56FE\u5DF2\u4FDD\u5B58: ${screenshotPath}`);
|
|
380
|
-
} catch {
|
|
381
|
-
}
|
|
382
247
|
}
|
|
383
248
|
async function waitForReply(page, timeout, verbose = false, headful = false) {
|
|
384
249
|
const maxWait = Math.min(timeout, 6e4);
|
|
@@ -414,13 +279,13 @@ async function waitForReply(page, timeout, verbose = false, headful = false) {
|
|
|
414
279
|
});
|
|
415
280
|
if (state.hasAiReply && state.aiTextLen > 0) {
|
|
416
281
|
if (!replyStarted) {
|
|
417
|
-
log(verbose, `\u2713 AI \u56DE\u590D\u5DF2\u5F00\u59CB\u751F\u6210\uFF08${state.aiTextLen}\u5B57\uFF09
|
|
282
|
+
log(verbose, `\u2713 AI \u56DE\u590D\u5DF2\u5F00\u59CB\u751F\u6210\uFF08${state.aiTextLen}\u5B57\uFF09`);
|
|
418
283
|
replyStarted = true;
|
|
419
284
|
}
|
|
420
285
|
if (state.aiTextLen === lastLen) {
|
|
421
286
|
stableCount++;
|
|
422
287
|
if (stableCount >= 3) {
|
|
423
|
-
log(verbose, `\u2713 AI \u56DE\u590D\u5DF2\u5B8C\u6210\uFF08${state.aiTextLen}\u5B57\
|
|
288
|
+
log(verbose, `\u2713 AI \u56DE\u590D\u5DF2\u5B8C\u6210\uFF08${state.aiTextLen}\u5B57\uFF09`);
|
|
424
289
|
break;
|
|
425
290
|
}
|
|
426
291
|
} else {
|
|
@@ -428,134 +293,31 @@ async function waitForReply(page, timeout, verbose = false, headful = false) {
|
|
|
428
293
|
lastLen = state.aiTextLen;
|
|
429
294
|
}
|
|
430
295
|
}
|
|
431
|
-
await page.waitForTimeout(
|
|
296
|
+
await page.waitForTimeout(500);
|
|
432
297
|
}
|
|
433
298
|
if (!replyStarted) {
|
|
434
299
|
if (await checkCaptcha(page, verbose)) {
|
|
435
300
|
if (headful) {
|
|
436
301
|
await waitForUserAction(page, "captcha", verbose);
|
|
437
|
-
const retryStart = Date.now();
|
|
438
|
-
const retryMaxWait = 3e4;
|
|
439
|
-
while (Date.now() - retryStart < retryMaxWait) {
|
|
440
|
-
const retryState = await page.evaluate(() => {
|
|
441
|
-
const answerBox = document.querySelector('[class*="answerBox"]');
|
|
442
|
-
if (answerBox) {
|
|
443
|
-
const text = answerBox.innerText?.trim() || "";
|
|
444
|
-
return { hasAiReply: text.length > 0, aiTextLen: text.length };
|
|
445
|
-
}
|
|
446
|
-
return { hasAiReply: false, aiTextLen: 0 };
|
|
447
|
-
});
|
|
448
|
-
if (retryState.hasAiReply) {
|
|
449
|
-
log(verbose, "\u2713 \u9A8C\u8BC1\u7801\u901A\u8FC7\u540E AI \u5DF2\u5F00\u59CB\u56DE\u590D");
|
|
450
|
-
replyStarted = true;
|
|
451
|
-
break;
|
|
452
|
-
}
|
|
453
|
-
await page.waitForTimeout(1500);
|
|
454
|
-
}
|
|
455
302
|
} else {
|
|
456
303
|
throw new YiyanAgentError(
|
|
457
304
|
"CAPTCHA",
|
|
458
|
-
|
|
305
|
+
"Yiyan detected automation and triggered a captcha."
|
|
459
306
|
);
|
|
460
307
|
}
|
|
461
308
|
} else if (headful) {
|
|
462
309
|
await waitForUserAction(page, "no-reply", verbose);
|
|
463
|
-
const retryStart = Date.now();
|
|
464
|
-
const retryMaxWait = 6e4;
|
|
465
|
-
while (Date.now() - retryStart < retryMaxWait) {
|
|
466
|
-
const retryState = await page.evaluate(() => {
|
|
467
|
-
const answerBox = document.querySelector('[class*="answerBox"]');
|
|
468
|
-
if (answerBox) {
|
|
469
|
-
const text = answerBox.innerText?.trim() || "";
|
|
470
|
-
return { hasAiReply: text.length > 0, aiTextLen: text.length };
|
|
471
|
-
}
|
|
472
|
-
return { hasAiReply: false, aiTextLen: 0 };
|
|
473
|
-
});
|
|
474
|
-
if (retryState.hasAiReply) {
|
|
475
|
-
if (retryState.aiTextLen === lastLen) {
|
|
476
|
-
stableCount++;
|
|
477
|
-
if (stableCount >= 3) {
|
|
478
|
-
log(verbose, `\u2713 \u7528\u6237\u64CD\u4F5C\u540E AI \u56DE\u590D\u5DF2\u5B8C\u6210\uFF08${retryState.aiTextLen}\u5B57\uFF09`);
|
|
479
|
-
replyStarted = true;
|
|
480
|
-
break;
|
|
481
|
-
}
|
|
482
|
-
} else {
|
|
483
|
-
stableCount = 0;
|
|
484
|
-
lastLen = retryState.aiTextLen;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
await page.waitForTimeout(1500);
|
|
488
|
-
}
|
|
489
310
|
} else {
|
|
490
311
|
throw new YiyanAgentError(
|
|
491
312
|
"TIMEOUT",
|
|
492
|
-
|
|
313
|
+
"AI reply timeout in headless mode. Try running login first, or use headed mode."
|
|
493
314
|
);
|
|
494
315
|
}
|
|
495
316
|
}
|
|
496
|
-
await page.waitForTimeout(
|
|
497
|
-
const pageDump = await page.evaluate(() => {
|
|
498
|
-
const result = {};
|
|
499
|
-
const selectors = [
|
|
500
|
-
'[class*="answerBox"]',
|
|
501
|
-
'[class*="dialogueCardList"]',
|
|
502
|
-
'[class*="dialogue_card_item"]',
|
|
503
|
-
'[class*="chatViewer"]',
|
|
504
|
-
'[class*="flowBox"]',
|
|
505
|
-
'[class*="roleSystem"]',
|
|
506
|
-
'[class*="mdRenderContainer"]',
|
|
507
|
-
'[class*="agent-markdown"]',
|
|
508
|
-
'[class*="markdown"]',
|
|
509
|
-
'[class*="content"]',
|
|
510
|
-
'[class*="chat"]',
|
|
511
|
-
'[class*="message"]'
|
|
512
|
-
];
|
|
513
|
-
for (const sel of selectors) {
|
|
514
|
-
result[`selector:${sel}`] = document.querySelectorAll(sel).length;
|
|
515
|
-
}
|
|
516
|
-
const answerBox = document.querySelector('[class*="answerBox"]');
|
|
517
|
-
if (answerBox) {
|
|
518
|
-
result["answerBox"] = {
|
|
519
|
-
textLen: answerBox.innerText?.trim().length || 0,
|
|
520
|
-
preview: (answerBox.innerText?.trim() || "").substring(0, 200),
|
|
521
|
-
classes: answerBox.className?.toString().substring(0, 100) || ""
|
|
522
|
-
};
|
|
523
|
-
}
|
|
524
|
-
result["bodyText"] = document.body.innerText?.substring(0, 1e3) || "";
|
|
525
|
-
const longTextEls = [];
|
|
526
|
-
const mainContent = document.querySelector('[class*="chatViewer"]') || document.querySelector('[class*="chat"]') || document.body;
|
|
527
|
-
for (const el of Array.from(mainContent.querySelectorAll("div, section, article"))) {
|
|
528
|
-
const text = el.innerText?.trim() || "";
|
|
529
|
-
if (text.length > 50 && text.length < 1e4) {
|
|
530
|
-
longTextEls.push({
|
|
531
|
-
tag: el.tagName,
|
|
532
|
-
classes: el.className?.toString().split(" ").slice(0, 3).join(" ") || "",
|
|
533
|
-
textLen: text.length,
|
|
534
|
-
preview: text.substring(0, 80)
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
result["longTextElements"] = longTextEls.sort((a, b) => b.textLen - a.textLen).slice(0, 10);
|
|
539
|
-
return result;
|
|
540
|
-
});
|
|
541
|
-
log(verbose, "=== \u9875\u9762\u8BCA\u65AD ===");
|
|
542
|
-
for (const [key, value] of Object.entries(pageDump)) {
|
|
543
|
-
if (key === "bodyText" || key === "longTextElements") continue;
|
|
544
|
-
log(verbose, ` ${key}: ${JSON.stringify(value)}`);
|
|
545
|
-
}
|
|
546
|
-
if (pageDump["longTextElements"] && pageDump["longTextElements"].length > 0) {
|
|
547
|
-
log(verbose, " \u957F\u6587\u672C\u5143\u7D20 TOP 10:");
|
|
548
|
-
for (const el of pageDump["longTextElements"]) {
|
|
549
|
-
log(verbose, ` <${el.tag}> .${el.classes} (${el.textLen}\u5B57): ${el.preview}...`);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
if (pageDump["bodyText"]) {
|
|
553
|
-
log(verbose, ` \u9875\u9762\u6587\u672C(\u524D1000\u5B57): ${pageDump["bodyText"].substring(0, 1e3)}`);
|
|
554
|
-
}
|
|
317
|
+
await page.waitForTimeout(1e3);
|
|
555
318
|
}
|
|
556
319
|
async function extractReply(page, question, verbose = false) {
|
|
557
320
|
log(verbose, "\u63D0\u53D6 AI \u56DE\u590D\u5185\u5BB9...");
|
|
558
|
-
await saveDebugScreenshot(page, "before-extract", verbose);
|
|
559
321
|
const reply = await page.evaluate((userQuestion) => {
|
|
560
322
|
const debugInfo = [];
|
|
561
323
|
const answerBox = document.querySelector('[class*="answerBox"]');
|
|
@@ -563,19 +325,12 @@ async function extractReply(page, question, verbose = false) {
|
|
|
563
325
|
if (answerBox) {
|
|
564
326
|
const clone = answerBox.cloneNode(true);
|
|
565
327
|
const processItems = clone.querySelectorAll('[class*="processItem"], [class*="processContent"]');
|
|
566
|
-
for (const item of processItems)
|
|
567
|
-
item.remove();
|
|
568
|
-
}
|
|
328
|
+
for (const item of processItems) item.remove();
|
|
569
329
|
const toolMessages = clone.querySelectorAll('[class*="toolMessage"]');
|
|
570
|
-
for (const msg of toolMessages)
|
|
571
|
-
msg.remove();
|
|
572
|
-
}
|
|
330
|
+
for (const msg of toolMessages) msg.remove();
|
|
573
331
|
const thinkHeaders = clone.querySelectorAll('[class*="headerMask"], [class*="topHeader"]');
|
|
574
|
-
for (const h of thinkHeaders)
|
|
575
|
-
h.remove();
|
|
576
|
-
}
|
|
332
|
+
for (const h of thinkHeaders) h.remove();
|
|
577
333
|
const text = clone.innerText?.trim() || "";
|
|
578
|
-
debugInfo.push(`answerBox after removing thinking: ${text.length} chars`);
|
|
579
334
|
const cleanedText = text.replace(/^参考\d+个网页\s*/, "").replace(/^深度思考已完成\s*/, "").replace(/^思考完成[::]\s*/, "").replace(/^准备输出结果\s*/, "").trim();
|
|
580
335
|
if (cleanedText.length > 0) {
|
|
581
336
|
debugInfo.push(`method1 success: ${cleanedText.length} chars`);
|
|
@@ -587,12 +342,10 @@ async function extractReply(page, question, verbose = false) {
|
|
|
587
342
|
for (const container of allMdContainers) {
|
|
588
343
|
const parent = container.parentElement;
|
|
589
344
|
const parentClass = parent?.className?.toString() || "";
|
|
590
|
-
if (parentClass.includes("toolMessage") || parentClass.includes("process"))
|
|
591
|
-
continue;
|
|
592
|
-
}
|
|
345
|
+
if (parentClass.includes("toolMessage") || parentClass.includes("process")) continue;
|
|
593
346
|
const text = container.innerText?.trim() || "";
|
|
594
347
|
if (text.length > 10) {
|
|
595
|
-
debugInfo.push(`method2 success: ${text.length} chars
|
|
348
|
+
debugInfo.push(`method2 success: ${text.length} chars`);
|
|
596
349
|
return JSON.stringify({ text, debug: debugInfo });
|
|
597
350
|
}
|
|
598
351
|
}
|
|
@@ -611,21 +364,6 @@ async function extractReply(page, question, verbose = false) {
|
|
|
611
364
|
}
|
|
612
365
|
}
|
|
613
366
|
}
|
|
614
|
-
const flowBoxes = document.querySelectorAll('[class*="flowBox"]');
|
|
615
|
-
debugInfo.push(`flowBoxes count: ${flowBoxes.length}`);
|
|
616
|
-
for (let i = flowBoxes.length - 1; i >= 0; i--) {
|
|
617
|
-
const box = flowBoxes[i];
|
|
618
|
-
const boxClass = box.className?.toString() || "";
|
|
619
|
-
if (boxClass.includes("questionBox")) continue;
|
|
620
|
-
const clone = box.cloneNode(true);
|
|
621
|
-
const thinkEls = clone.querySelectorAll('[class*="processItem"], [class*="processContent"], [class*="toolMessage"]');
|
|
622
|
-
for (const el of thinkEls) el.remove();
|
|
623
|
-
const text = clone.innerText?.trim() || "";
|
|
624
|
-
if (text.length > 10) {
|
|
625
|
-
debugInfo.push(`method4 success: flowBox[${i}] ${text.length} chars`);
|
|
626
|
-
return JSON.stringify({ text, debug: debugInfo });
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
367
|
const fullText = document.body.innerText;
|
|
630
368
|
const lines = fullText.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
|
|
631
369
|
const uiWords = /* @__PURE__ */ new Set([
|
|
@@ -642,27 +380,15 @@ async function extractReply(page, question, verbose = false) {
|
|
|
642
380
|
"\u672A\u767B\u5F55",
|
|
643
381
|
"\u767B\u5F55",
|
|
644
382
|
"\u5185\u5BB9\u7531AI\u751F\u6210\uFF0C\u4EC5\u4F9B\u53C2\u8003\uFF0C\u8BF7\u4ED4\u7EC6\u7504\u522B",
|
|
645
|
-
"\u53C2\u8003"
|
|
646
|
-
"\u6DF1\u5EA6\u5206\u6790\u9700\u6C42\u5E76\u89E3\u7B54",
|
|
647
|
-
"\u901A\u7528\u5199\u4F5C",
|
|
648
|
-
"\u9605\u8BFB\u5206\u6790",
|
|
649
|
-
"\u7F51\u9875\u5DE5\u574A",
|
|
650
|
-
"\u667A\u80FD\u7FFB\u8BD1",
|
|
651
|
-
"\u4EE3\u7801\u7F16\u7A0B"
|
|
383
|
+
"\u53C2\u8003"
|
|
652
384
|
]);
|
|
653
385
|
const thinkingKeywords = [
|
|
654
386
|
"\u6DF1\u5EA6\u601D\u8003\u5DF2\u5B8C\u6210",
|
|
655
387
|
"\u601D\u8003\u5B8C\u6210",
|
|
656
|
-
"\u51C6\u5907\u8F93\u51FA\u7ED3\u679C"
|
|
657
|
-
"picaole\u5728\u8BE2\u95EE",
|
|
658
|
-
"picaole\u95EE\u4E86\u4E00\u4E2A",
|
|
659
|
-
"picaole\u5728\u641C\u7D22",
|
|
660
|
-
"\u76F4\u63A5\u56DE\u7B54\u5373\u53EF",
|
|
661
|
-
"\u4E0D\u9700\u8981\u590D\u6742\u5206\u6790",
|
|
662
|
-
"\u6309\u7167\u56DE\u7B54\u65B9\u5F0F"
|
|
388
|
+
"\u51C6\u5907\u8F93\u51FA\u7ED3\u679C"
|
|
663
389
|
];
|
|
664
390
|
const userQIdx = lines.findIndex((l) => l === userQuestion || l.includes(userQuestion));
|
|
665
|
-
debugInfo.push(`userQ idx
|
|
391
|
+
debugInfo.push(`userQ idx: ${userQIdx}, lines: ${lines.length}`);
|
|
666
392
|
if (userQIdx >= 0) {
|
|
667
393
|
const replyLines = [];
|
|
668
394
|
for (let i = userQIdx + 1; i < lines.length; i++) {
|
|
@@ -671,34 +397,15 @@ async function extractReply(page, question, verbose = false) {
|
|
|
671
397
|
if (thinkingKeywords.some((kw) => line.includes(kw))) continue;
|
|
672
398
|
if (line.startsWith("\u641C\u7D22") || line.includes("\u7BC7\u8D44\u6599")) continue;
|
|
673
399
|
if (line.match(/^参考\d+个网页/)) continue;
|
|
674
|
-
if (line.length > 0)
|
|
675
|
-
replyLines.push(line);
|
|
676
|
-
}
|
|
400
|
+
if (line.length > 0) replyLines.push(line);
|
|
677
401
|
if (line === "\u5FEB\u901F" || line === "\u66F4\u591A" || line.includes("\u5185\u5BB9\u7531AI\u751F\u6210")) break;
|
|
678
402
|
}
|
|
679
403
|
if (replyLines.length > 0) {
|
|
680
|
-
debugInfo.push(`
|
|
404
|
+
debugInfo.push(`method4 success: ${replyLines.length} lines`);
|
|
681
405
|
return JSON.stringify({ text: replyLines.join("\n"), debug: debugInfo });
|
|
682
406
|
}
|
|
683
407
|
}
|
|
684
|
-
|
|
685
|
-
const blockElements = document.querySelectorAll("div, section, article, p");
|
|
686
|
-
for (const el of blockElements) {
|
|
687
|
-
const elText = el.innerText?.trim() || "";
|
|
688
|
-
if (elText.length > 50 && elText.length < 1e4) {
|
|
689
|
-
if (uiWords.has(elText)) continue;
|
|
690
|
-
if (thinkingKeywords.some((kw) => elText.includes(kw))) continue;
|
|
691
|
-
allTextBlocks.push({ text: elText, length: elText.length });
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
allTextBlocks.sort((a, b) => b.length - a.length);
|
|
695
|
-
if (allTextBlocks.length > 0) {
|
|
696
|
-
debugInfo.push(`method6 fallback: longest text block ${allTextBlocks[0].length} chars`);
|
|
697
|
-
return JSON.stringify({ text: allTextBlocks[0].text, debug: debugInfo });
|
|
698
|
-
}
|
|
699
|
-
debugInfo.push(`ALL METHODS FAILED`);
|
|
700
|
-
debugInfo.push(`body text length: ${fullText.length}`);
|
|
701
|
-
debugInfo.push(`body text first 500: ${fullText.substring(0, 500)}`);
|
|
408
|
+
debugInfo.push("ALL METHODS FAILED");
|
|
702
409
|
return JSON.stringify({ text: "", debug: debugInfo });
|
|
703
410
|
}, question);
|
|
704
411
|
let parsed;
|
|
@@ -714,68 +421,39 @@ async function extractReply(page, question, verbose = false) {
|
|
|
714
421
|
log(verbose, `\u63D0\u53D6\u6210\u529F\uFF0C\u56DE\u590D\u957F\u5EA6: ${parsed.text.length} \u5B57\u7B26`);
|
|
715
422
|
return parsed.text;
|
|
716
423
|
}
|
|
717
|
-
log(verbose, "\u63D0\u53D6\u5931\u8D25\uFF0C\u6240\u6709\u65B9\u6CD5\u5747\u672A\u627E\u5230\u56DE\u590D\u5185\u5BB9");
|
|
718
424
|
throw new YiyanAgentError("TIMEOUT", "Failed to extract reply content");
|
|
719
425
|
}
|
|
720
426
|
|
|
721
427
|
// src/agent.ts
|
|
722
|
-
var
|
|
723
|
-
os2.homedir(),
|
|
724
|
-
".yiyan-browser-agent"
|
|
725
|
-
);
|
|
428
|
+
var DEFAULT_SESSION_DIR = path.join(os.homedir(), ".yiyan-browser-agent", "session");
|
|
726
429
|
var YiyanAgent = class {
|
|
727
430
|
options;
|
|
728
|
-
|
|
431
|
+
sessionDir;
|
|
729
432
|
verbose;
|
|
730
433
|
constructor(options) {
|
|
731
434
|
this.options = {
|
|
732
435
|
...DEFAULT_OPTIONS,
|
|
733
436
|
...options
|
|
734
437
|
};
|
|
735
|
-
this.
|
|
438
|
+
this.sessionDir = this.options.profileDir || DEFAULT_SESSION_DIR;
|
|
736
439
|
this.verbose = options?.verbose ?? false;
|
|
737
440
|
}
|
|
738
441
|
/**
|
|
739
442
|
* 发送问题并获取答案
|
|
740
443
|
* @param question 问题文本
|
|
741
|
-
* @param headful 是否使用有头浏览器(可见窗口),默认为 false
|
|
742
|
-
*
|
|
743
|
-
* 策略:先尝试 headless(不弹窗),如果失败(验证码/未登录)自动回退到有头模式
|
|
444
|
+
* @param headful 是否使用有头浏览器(可见窗口),默认为 false(headless)
|
|
744
445
|
*/
|
|
745
446
|
async ask(question, headful = false) {
|
|
746
|
-
|
|
747
|
-
return this.executeAsk(question, true);
|
|
748
|
-
}
|
|
749
|
-
try {
|
|
750
|
-
return await this.executeAsk(question, false);
|
|
751
|
-
} catch (error) {
|
|
752
|
-
if (error instanceof YiyanAgentError && (error.type === "CAPTCHA" || error.type === "TIMEOUT")) {
|
|
753
|
-
if (this.verbose) {
|
|
754
|
-
process.stderr.write("[yiyan-agent] headless \u6A21\u5F0F\u5931\u8D25\uFF0C\u81EA\u52A8\u5207\u6362\u5230\u6709\u5934\u6A21\u5F0F...\n");
|
|
755
|
-
}
|
|
756
|
-
return this.executeAsk(question, true);
|
|
757
|
-
}
|
|
758
|
-
throw error;
|
|
759
|
-
}
|
|
447
|
+
return this.executeAsk(question, headful);
|
|
760
448
|
}
|
|
761
449
|
/**
|
|
762
450
|
* 执行单次问答
|
|
763
451
|
*/
|
|
764
452
|
async executeAsk(question, headful) {
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
if (!chromePath) {
|
|
768
|
-
throw new YiyanAgentError(
|
|
769
|
-
"BROWSER_LAUNCH",
|
|
770
|
-
`Unsupported platform: ${platform}`
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
|
-
const profilePath = path3.join(this.profileDir, "chrome-profile");
|
|
774
|
-
const { browser, cdpBrowser, page, chromeProcess } = await launchBrowser({
|
|
775
|
-
chromePath,
|
|
776
|
-
profilePath,
|
|
453
|
+
const { context, page } = await launchBrowser({
|
|
454
|
+
sessionDir: this.sessionDir,
|
|
777
455
|
headless: !headful,
|
|
778
|
-
timeout:
|
|
456
|
+
timeout: this.options.timeout,
|
|
779
457
|
verbose: this.verbose
|
|
780
458
|
});
|
|
781
459
|
try {
|
|
@@ -796,7 +474,7 @@ var YiyanAgent = class {
|
|
|
796
474
|
const reply = await extractReply(page, question, this.verbose);
|
|
797
475
|
return reply;
|
|
798
476
|
} finally {
|
|
799
|
-
await closeBrowser(
|
|
477
|
+
await closeBrowser(context, this.verbose);
|
|
800
478
|
}
|
|
801
479
|
}
|
|
802
480
|
/**
|
|
@@ -804,66 +482,96 @@ var YiyanAgent = class {
|
|
|
804
482
|
* 登录成功后,后续的 ask 调用将自动使用登录状态
|
|
805
483
|
*/
|
|
806
484
|
async login() {
|
|
807
|
-
const
|
|
808
|
-
|
|
809
|
-
if (!chromePath) {
|
|
810
|
-
throw new YiyanAgentError(
|
|
811
|
-
"BROWSER_LAUNCH",
|
|
812
|
-
`Unsupported platform: ${platform}`
|
|
813
|
-
);
|
|
814
|
-
}
|
|
815
|
-
const profilePath = path3.join(this.profileDir, "chrome-profile");
|
|
816
|
-
const { browser, cdpBrowser, page, chromeProcess } = await launchBrowser({
|
|
817
|
-
chromePath,
|
|
818
|
-
profilePath,
|
|
485
|
+
const { context, page } = await launchBrowser({
|
|
486
|
+
sessionDir: this.sessionDir,
|
|
819
487
|
headless: false,
|
|
820
488
|
timeout: 6e4,
|
|
821
489
|
verbose: this.verbose
|
|
822
490
|
});
|
|
823
491
|
try {
|
|
824
|
-
await page
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
process.stderr.write("
|
|
830
|
-
process.stderr.write("[yiyan-agent] \
|
|
831
|
-
|
|
832
|
-
process.stderr.write("[yiyan-agent]
|
|
833
|
-
const readline = await import("readline");
|
|
834
|
-
await new Promise((resolve) => {
|
|
835
|
-
const rl = readline.createInterface({
|
|
836
|
-
input: process.stdin,
|
|
837
|
-
output: process.stderr
|
|
838
|
-
});
|
|
839
|
-
rl.question("\u6309 Enter \u7EE7\u7EED...", () => {
|
|
840
|
-
rl.close();
|
|
841
|
-
resolve();
|
|
842
|
-
});
|
|
843
|
-
});
|
|
844
|
-
process.stderr.write("[yiyan-agent] \u2713 \u767B\u5F55\u5B8C\u6210\uFF0C\u6B63\u5728\u4FDD\u5B58\u767B\u5F55\u72B6\u6001...\n");
|
|
492
|
+
await navigateToYiyan(page, this.verbose);
|
|
493
|
+
process.stderr.write("\n[yiyan-agent] \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n");
|
|
494
|
+
process.stderr.write("[yiyan-agent] \u2551 \u{1F510} LOGIN REQUIRED \u2551\n");
|
|
495
|
+
process.stderr.write("[yiyan-agent] \u2551 \u2551\n");
|
|
496
|
+
process.stderr.write("[yiyan-agent] \u2551 1. Log in to Yiyan in the browser window \u2551\n");
|
|
497
|
+
process.stderr.write("[yiyan-agent] \u2551 2. Return here and press ENTER to continue \u2551\n");
|
|
498
|
+
process.stderr.write("[yiyan-agent] \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\n\n");
|
|
499
|
+
await this.waitForEnter();
|
|
500
|
+
process.stderr.write("[yiyan-agent] \u2713 Login complete, saving session...\n");
|
|
845
501
|
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
846
502
|
} finally {
|
|
847
|
-
await closeBrowser(
|
|
503
|
+
await closeBrowser(context, this.verbose);
|
|
848
504
|
}
|
|
849
505
|
}
|
|
850
|
-
|
|
851
|
-
|
|
506
|
+
/**
|
|
507
|
+
* 等待用户按 Enter
|
|
508
|
+
*/
|
|
509
|
+
async waitForEnter() {
|
|
510
|
+
const readline = await import("readline");
|
|
511
|
+
return new Promise((resolve) => {
|
|
512
|
+
const rl = readline.createInterface({
|
|
513
|
+
input: process.stdin,
|
|
514
|
+
output: process.stderr
|
|
515
|
+
});
|
|
516
|
+
rl.question("Press ENTER to continue...", () => {
|
|
517
|
+
rl.close();
|
|
518
|
+
resolve();
|
|
519
|
+
});
|
|
520
|
+
});
|
|
852
521
|
}
|
|
522
|
+
/**
|
|
523
|
+
* 清除保存的 session
|
|
524
|
+
*/
|
|
853
525
|
async reset() {
|
|
854
|
-
await
|
|
526
|
+
const fs3 = await import("fs/promises");
|
|
527
|
+
try {
|
|
528
|
+
await fs3.rm(this.sessionDir, { recursive: true, force: true });
|
|
529
|
+
} catch {
|
|
530
|
+
}
|
|
855
531
|
}
|
|
532
|
+
/**
|
|
533
|
+
* 检查状态
|
|
534
|
+
*/
|
|
856
535
|
status() {
|
|
536
|
+
const fs3 = __require("fs");
|
|
857
537
|
return {
|
|
858
|
-
loggedIn:
|
|
859
|
-
|
|
538
|
+
loggedIn: fs3.existsSync(this.sessionDir),
|
|
539
|
+
sessionPath: this.sessionDir
|
|
860
540
|
};
|
|
861
541
|
}
|
|
862
542
|
};
|
|
543
|
+
|
|
544
|
+
// src/profile.ts
|
|
545
|
+
import path2 from "path";
|
|
546
|
+
import os2 from "os";
|
|
547
|
+
import fs2 from "fs";
|
|
548
|
+
var CONFIG_FILE_PATH = path2.join(os2.homedir(), ".yiyan-browser-agent", "config.json");
|
|
549
|
+
var DEFAULT_SESSION_DIR2 = path2.join(os2.homedir(), ".yiyan-browser-agent", "session");
|
|
550
|
+
function readConfig() {
|
|
551
|
+
try {
|
|
552
|
+
if (fs2.existsSync(CONFIG_FILE_PATH)) {
|
|
553
|
+
const content = fs2.readFileSync(CONFIG_FILE_PATH, "utf-8");
|
|
554
|
+
return JSON.parse(content);
|
|
555
|
+
}
|
|
556
|
+
} catch {
|
|
557
|
+
}
|
|
558
|
+
return {};
|
|
559
|
+
}
|
|
560
|
+
function saveConfig(config) {
|
|
561
|
+
const configDir = path2.dirname(CONFIG_FILE_PATH);
|
|
562
|
+
if (!fs2.existsSync(configDir)) {
|
|
563
|
+
fs2.mkdirSync(configDir, { recursive: true });
|
|
564
|
+
}
|
|
565
|
+
fs2.writeFileSync(CONFIG_FILE_PATH, JSON.stringify(config, null, 2));
|
|
566
|
+
}
|
|
863
567
|
export {
|
|
568
|
+
CONFIG_FILE_PATH,
|
|
864
569
|
DEFAULT_OPTIONS,
|
|
570
|
+
DEFAULT_SESSION_DIR2 as DEFAULT_SESSION_DIR,
|
|
865
571
|
YIYAN_CHAT_URL,
|
|
866
572
|
YiyanAgent,
|
|
867
|
-
YiyanAgentError
|
|
573
|
+
YiyanAgentError,
|
|
574
|
+
readConfig,
|
|
575
|
+
saveConfig
|
|
868
576
|
};
|
|
869
577
|
//# sourceMappingURL=index.js.map
|