yiyan-browser-agent 1.4.5 → 1.4.7
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/LICENSE +1 -1
- package/README.md +241 -153
- package/package.json +25 -30
- package/src/agent.js +63 -0
- package/src/browser.js +589 -0
- package/src/calibrate.js +178 -0
- package/src/config.js +68 -0
- package/src/index.js +115 -0
- package/src/logger.js +118 -0
- package/src/parser.js +272 -0
- package/src/postinstall.js +26 -0
- package/src/prompt.js +188 -0
- package/src/tools.js +547 -0
- package/dist/cli.d.ts +0 -27
- package/dist/cli.js +0 -857
- package/dist/cli.js.map +0 -1
- package/dist/index.d.ts +0 -73
- package/dist/index.js +0 -666
- package/dist/index.js.map +0 -1
- package/dist/postinstall.d.ts +0 -2
- package/dist/postinstall.js +0 -70
- package/dist/postinstall.js.map +0 -1
- package/dist/types-BhQ78DYf.d.ts +0 -39
package/dist/cli.js
DELETED
|
@@ -1,857 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
var __create = Object.create;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __export = (target, all) => {
|
|
10
|
-
for (var name in all)
|
|
11
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
-
};
|
|
13
|
-
var __copyProps = (to, from, except, desc) => {
|
|
14
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
-
for (let key of __getOwnPropNames(from))
|
|
16
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
-
}
|
|
19
|
-
return to;
|
|
20
|
-
};
|
|
21
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
-
mod
|
|
28
|
-
));
|
|
29
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
-
|
|
31
|
-
// src/cli.ts
|
|
32
|
-
var cli_exports = {};
|
|
33
|
-
__export(cli_exports, {
|
|
34
|
-
formatCliOutput: () => formatCliOutput,
|
|
35
|
-
parseCliArgs: () => parseCliArgs,
|
|
36
|
-
printHelp: () => printHelp,
|
|
37
|
-
runCli: () => runCli
|
|
38
|
-
});
|
|
39
|
-
module.exports = __toCommonJS(cli_exports);
|
|
40
|
-
|
|
41
|
-
// src/agent.ts
|
|
42
|
-
var import_path = __toESM(require("path"));
|
|
43
|
-
var import_os = __toESM(require("os"));
|
|
44
|
-
|
|
45
|
-
// src/types.ts
|
|
46
|
-
var YiyanAgentError = class extends Error {
|
|
47
|
-
constructor(type, message) {
|
|
48
|
-
super(message);
|
|
49
|
-
this.type = type;
|
|
50
|
-
this.name = "YiyanAgentError";
|
|
51
|
-
}
|
|
52
|
-
type;
|
|
53
|
-
};
|
|
54
|
-
var DEFAULT_OPTIONS = {
|
|
55
|
-
timeout: 12e4,
|
|
56
|
-
retryCount: 3,
|
|
57
|
-
profileDir: "",
|
|
58
|
-
chromePath: ""
|
|
59
|
-
};
|
|
60
|
-
var YIYAN_CHAT_URL = "https://yiyan.baidu.com/";
|
|
61
|
-
|
|
62
|
-
// src/browser.ts
|
|
63
|
-
var import_playwright = require("playwright");
|
|
64
|
-
var import_fs = __toESM(require("fs"));
|
|
65
|
-
var SELECTORS = {
|
|
66
|
-
// 输入框选择器(按优先级排序)
|
|
67
|
-
chatInput: [
|
|
68
|
-
"#chat-input",
|
|
69
|
-
'textarea[placeholder*="\u8F93\u5165"]',
|
|
70
|
-
"textarea[placeholder]",
|
|
71
|
-
"textarea",
|
|
72
|
-
'[contenteditable="true"][role="textbox"]',
|
|
73
|
-
'[contenteditable="true"]'
|
|
74
|
-
],
|
|
75
|
-
// 发送按钮选择器
|
|
76
|
-
sendButton: [
|
|
77
|
-
'button[aria-label*="\u53D1\u9001"]',
|
|
78
|
-
'button[aria-label*="Send"]',
|
|
79
|
-
'[data-testid="send-button"]',
|
|
80
|
-
'button[type="submit"]',
|
|
81
|
-
'[class*="send-btn"]',
|
|
82
|
-
'[class*="sendBtn"]',
|
|
83
|
-
'[class*="send-button"]'
|
|
84
|
-
],
|
|
85
|
-
// 响应容器选择器
|
|
86
|
-
responseContainer: [
|
|
87
|
-
'[class*="answerBox"]',
|
|
88
|
-
'[class*="response"]',
|
|
89
|
-
'[class*="ai-message"]',
|
|
90
|
-
'[class*="assistant"]',
|
|
91
|
-
'[class*="markdown"]',
|
|
92
|
-
'[class*="message-content"]',
|
|
93
|
-
'[class*="chat-message"]'
|
|
94
|
-
],
|
|
95
|
-
// 停止生成按钮
|
|
96
|
-
stopButton: [
|
|
97
|
-
'button[aria-label*="\u505C\u6B62"]',
|
|
98
|
-
'button[aria-label*="Stop"]',
|
|
99
|
-
'[class*="stop-btn"]',
|
|
100
|
-
'[class*="stopBtn"]',
|
|
101
|
-
'[class*="stop-gen"]'
|
|
102
|
-
]
|
|
103
|
-
};
|
|
104
|
-
function log(verbose, msg) {
|
|
105
|
-
if (verbose) {
|
|
106
|
-
process.stderr.write(`[yiyan-browser-agent] ${msg}
|
|
107
|
-
`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
async function launchBrowser(options) {
|
|
111
|
-
const { sessionDir, headless, timeout = 3e4, verbose = false } = options;
|
|
112
|
-
process.stderr.write("[yiyan-browser-agent] \u542F\u52A8 Chromium \u6D4F\u89C8\u5668...\n");
|
|
113
|
-
try {
|
|
114
|
-
if (!import_fs.default.existsSync(sessionDir)) {
|
|
115
|
-
import_fs.default.mkdirSync(sessionDir, { recursive: true });
|
|
116
|
-
}
|
|
117
|
-
const context = await import_playwright.chromium.launchPersistentContext(sessionDir, {
|
|
118
|
-
headless,
|
|
119
|
-
viewport: { width: 1280, height: 900 },
|
|
120
|
-
userAgent: [
|
|
121
|
-
"Mozilla/5.0 (X11; Linux x86_64)",
|
|
122
|
-
"AppleWebKit/537.36 (KHTML, like Gecko)",
|
|
123
|
-
"Chrome/124.0.0.0 Safari/537.36"
|
|
124
|
-
].join(" "),
|
|
125
|
-
args: [
|
|
126
|
-
"--disable-blink-features=AutomationControlled",
|
|
127
|
-
"--no-first-run",
|
|
128
|
-
"--no-default-browser-check",
|
|
129
|
-
"--no-sandbox",
|
|
130
|
-
"--disable-setuid-sandbox",
|
|
131
|
-
"--disable-dev-shm-usage"
|
|
132
|
-
],
|
|
133
|
-
ignoreDefaultArgs: ["--enable-automation"]
|
|
134
|
-
});
|
|
135
|
-
context.setDefaultTimeout(timeout);
|
|
136
|
-
const pages = context.pages();
|
|
137
|
-
const page = pages.length > 0 ? pages[0] : await context.newPage();
|
|
138
|
-
await page.addInitScript(() => {
|
|
139
|
-
Object.defineProperty(navigator, "webdriver", { get: () => false });
|
|
140
|
-
});
|
|
141
|
-
process.stderr.write("[yiyan-browser-agent] \u6D4F\u89C8\u5668\u542F\u52A8\u5B8C\u6210\n");
|
|
142
|
-
return { context, page };
|
|
143
|
-
} catch (error) {
|
|
144
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
145
|
-
process.stderr.write(`[yiyan-browser-agent] \u2717 \u6D4F\u89C8\u5668\u542F\u52A8\u5931\u8D25: ${errorMsg}
|
|
146
|
-
`);
|
|
147
|
-
process.stderr.write("[yiyan-browser-agent] \u63D0\u793A: Ubuntu \u9700\u8981\u5148\u8FD0\u884C: npx playwright install-deps chromium\n");
|
|
148
|
-
throw new YiyanAgentError(
|
|
149
|
-
"BROWSER_LAUNCH",
|
|
150
|
-
`Failed to launch browser: ${errorMsg}`
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
async function closeBrowser(context, verbose) {
|
|
155
|
-
process.stderr.write("[yiyan-browser-agent] \u5173\u95ED\u6D4F\u89C8\u5668...\n");
|
|
156
|
-
try {
|
|
157
|
-
await context.close();
|
|
158
|
-
process.stderr.write("[yiyan-browser-agent] \u6D4F\u89C8\u5668\u5DF2\u5173\u95ED\n");
|
|
159
|
-
} catch {
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
async function navigateToYiyan(page, verbose = false) {
|
|
163
|
-
process.stderr.write("[yiyan-browser-agent] \u5BFC\u822A\u5230\u6587\u5FC3\u4E00\u8A00...\n");
|
|
164
|
-
try {
|
|
165
|
-
await page.goto(YIYAN_CHAT_URL, { waitUntil: "domcontentloaded", timeout: 3e4 });
|
|
166
|
-
await page.waitForTimeout(1500);
|
|
167
|
-
process.stderr.write("[yiyan-browser-agent] \u9875\u9762\u52A0\u8F7D\u5B8C\u6210\n");
|
|
168
|
-
} catch (err) {
|
|
169
|
-
process.stderr.write(`[yiyan-browser-agent] \u5BFC\u822A\u8B66\u544A: ${err instanceof Error ? err.message : String(err)}
|
|
170
|
-
`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
async function checkLoggedIn(page, verbose = false) {
|
|
174
|
-
await page.waitForTimeout(2e3);
|
|
175
|
-
const isLoggedIn = await page.evaluate(() => {
|
|
176
|
-
const url = window.location.href;
|
|
177
|
-
const bodyText = document.body?.innerText || "";
|
|
178
|
-
if (url.includes("/auth") || url.includes("/login") || url.includes("/sign")) return false;
|
|
179
|
-
if (bodyText.includes("\u672A\u767B\u5F55") || bodyText.includes("\u8BF7\u767B\u5F55")) return false;
|
|
180
|
-
if (document.querySelector('input[type="password"]')) return false;
|
|
181
|
-
const loginBtn = document.querySelector("button");
|
|
182
|
-
if (loginBtn && loginBtn.textContent?.trim() === "\u767B\u5F55") return false;
|
|
183
|
-
return true;
|
|
184
|
-
});
|
|
185
|
-
process.stderr.write(isLoggedIn ? "[yiyan-browser-agent] \u2713 \u5DF2\u68C0\u6D4B\u5230\u767B\u5F55\u72B6\u6001\n" : "[yiyan-browser-agent] \u26A0 \u672A\u767B\u5F55\n");
|
|
186
|
-
return isLoggedIn;
|
|
187
|
-
}
|
|
188
|
-
async function checkCaptcha(page, verbose = false) {
|
|
189
|
-
const hasCaptcha = await page.evaluate(() => {
|
|
190
|
-
const bodyText = document.body.innerText || "";
|
|
191
|
-
const indicators = ["\u9A8C\u8BC1\u7801", "\u8BF7\u5B8C\u6210\u9A8C\u8BC1", "\u5B89\u5168\u9A8C\u8BC1", "\u6ED1\u52A8\u9A8C\u8BC1", "captcha"];
|
|
192
|
-
for (const kw of indicators) {
|
|
193
|
-
if (bodyText.includes(kw)) return true;
|
|
194
|
-
}
|
|
195
|
-
const dialogs = document.querySelectorAll('[role="dialog"], [class*="captcha"], [class*="verify"]');
|
|
196
|
-
return dialogs.length > 0;
|
|
197
|
-
});
|
|
198
|
-
if (hasCaptcha) log(verbose, "\u26A0 \u68C0\u6D4B\u5230\u9A8C\u8BC1\u7801\uFF01");
|
|
199
|
-
return hasCaptcha;
|
|
200
|
-
}
|
|
201
|
-
async function waitForUserAction(page, reason, verbose = false) {
|
|
202
|
-
const messages = {
|
|
203
|
-
captcha: "\u68C0\u6D4B\u5230\u9A8C\u8BC1\u7801\uFF0C\u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u624B\u52A8\u5B8C\u6210\u9A8C\u8BC1",
|
|
204
|
-
login: "\u68C0\u6D4B\u5230\u672A\u767B\u5F55\uFF0C\u8BF7\u5728\u6D4F\u89C8\u5668\u4E2D\u624B\u52A8\u767B\u5F55",
|
|
205
|
-
"no-reply": "AI \u672A\u56DE\u590D\uFF0C\u8BF7\u68C0\u67E5\u6D4F\u89C8\u5668\u662F\u5426\u9700\u8981\u624B\u52A8\u64CD\u4F5C"
|
|
206
|
-
};
|
|
207
|
-
process.stderr.write(`
|
|
208
|
-
[yiyan-browser-agent] \u26A0 ${messages[reason]}
|
|
209
|
-
`);
|
|
210
|
-
process.stderr.write("[yiyan-browser-agent] \u7B49\u5F85\u60A8\u64CD\u4F5C\u5B8C\u6210...\uFF08\u64CD\u4F5C\u5B8C\u6210\u540E\u4F1A\u81EA\u52A8\u7EE7\u7EED\uFF09\n\n");
|
|
211
|
-
const maxWait = 18e4;
|
|
212
|
-
const start = Date.now();
|
|
213
|
-
while (Date.now() - start < maxWait) {
|
|
214
|
-
await page.waitForTimeout(2e3);
|
|
215
|
-
if (reason === "captcha" && !await checkCaptcha(page, false)) {
|
|
216
|
-
log(verbose, "\u2713 \u9A8C\u8BC1\u7801\u5DF2\u901A\u8FC7\uFF01");
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
if (reason === "login" && await checkLoggedIn(page, false)) {
|
|
220
|
-
log(verbose, "\u2713 \u767B\u5F55\u6210\u529F\uFF01");
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
if (reason === "no-reply" && await getMessageCount(page) > 0) {
|
|
224
|
-
log(verbose, "\u2713 AI \u5DF2\u5F00\u59CB\u56DE\u590D\uFF01");
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
log(verbose, "\u26A0 \u7B49\u5F85\u7528\u6237\u64CD\u4F5C\u8D85\u65F6\uFF083\u5206\u949F\uFF09");
|
|
229
|
-
}
|
|
230
|
-
async function sendMessage(page, message, verbose = false, headful = false) {
|
|
231
|
-
const input = await findInput(page, verbose);
|
|
232
|
-
if (!input) {
|
|
233
|
-
throw new YiyanAgentError("NETWORK", "Cannot find the Yiyan chat input box");
|
|
234
|
-
}
|
|
235
|
-
await input.click({ force: true });
|
|
236
|
-
await page.waitForTimeout(200);
|
|
237
|
-
await page.keyboard.press("Control+a");
|
|
238
|
-
await page.waitForTimeout(100);
|
|
239
|
-
const isTextarea = await input.evaluate((el) => el.tagName.toLowerCase() === "textarea");
|
|
240
|
-
if (isTextarea) {
|
|
241
|
-
await input.fill(message);
|
|
242
|
-
} else {
|
|
243
|
-
await input.evaluate((el, content) => {
|
|
244
|
-
el.focus();
|
|
245
|
-
document.execCommand("selectAll", false, null);
|
|
246
|
-
document.execCommand("delete", false, null);
|
|
247
|
-
document.execCommand("insertText", false, content);
|
|
248
|
-
el.dispatchEvent(new InputEvent("input", { bubbles: true, data: content }));
|
|
249
|
-
}, message);
|
|
250
|
-
}
|
|
251
|
-
await page.waitForTimeout(400);
|
|
252
|
-
log(verbose, `\u5DF2\u8F93\u5165\u95EE\u9898: "${message.substring(0, 50)}..."`);
|
|
253
|
-
const sent = await clickSendButton(page);
|
|
254
|
-
if (!sent) {
|
|
255
|
-
await page.keyboard.press("Enter");
|
|
256
|
-
}
|
|
257
|
-
await page.waitForTimeout(500);
|
|
258
|
-
if (await checkCaptcha(page, verbose)) {
|
|
259
|
-
if (headful) {
|
|
260
|
-
await waitForUserAction(page, "captcha", verbose);
|
|
261
|
-
} else {
|
|
262
|
-
throw new YiyanAgentError("CAPTCHA", "Captcha detected. Use --headful to solve it.");
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
async function findInput(page, verbose = false) {
|
|
267
|
-
for (const sel of SELECTORS.chatInput) {
|
|
268
|
-
try {
|
|
269
|
-
const el = await page.waitForSelector(sel, { timeout: 4e3, state: "visible" });
|
|
270
|
-
if (el) {
|
|
271
|
-
log(verbose, `\u2713 \u8F93\u5165\u6846: ${sel}`);
|
|
272
|
-
return el;
|
|
273
|
-
}
|
|
274
|
-
} catch {
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
return null;
|
|
278
|
-
}
|
|
279
|
-
async function clickSendButton(page) {
|
|
280
|
-
for (const sel of SELECTORS.sendButton) {
|
|
281
|
-
try {
|
|
282
|
-
const el = await page.$(sel);
|
|
283
|
-
if (el && await el.isVisible() && await el.isEnabled()) {
|
|
284
|
-
await el.click();
|
|
285
|
-
return true;
|
|
286
|
-
}
|
|
287
|
-
} catch {
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
return false;
|
|
291
|
-
}
|
|
292
|
-
async function waitForReply(page, timeout, verbose = false, headful = false) {
|
|
293
|
-
const maxWait = Math.min(timeout, 12e4);
|
|
294
|
-
const stableDelay = 2500;
|
|
295
|
-
const start = Date.now();
|
|
296
|
-
const initialCount = await getMessageCount(page);
|
|
297
|
-
let appeared = false;
|
|
298
|
-
while (Date.now() - start < 12e3) {
|
|
299
|
-
const count = await getMessageCount(page);
|
|
300
|
-
if (count > initialCount) {
|
|
301
|
-
appeared = true;
|
|
302
|
-
log(verbose, "\u2713 AI \u56DE\u590D\u5DF2\u5F00\u59CB\u751F\u6210");
|
|
303
|
-
break;
|
|
304
|
-
}
|
|
305
|
-
await page.waitForTimeout(400);
|
|
306
|
-
}
|
|
307
|
-
if (!appeared) {
|
|
308
|
-
log(verbose, "\u56DE\u590D\u53EF\u80FD\u5EF6\u8FDF\uFF0C\u7EE7\u7EED\u7B49\u5F85...");
|
|
309
|
-
}
|
|
310
|
-
let lastText = "";
|
|
311
|
-
let stableStart = null;
|
|
312
|
-
while (Date.now() - start < maxWait) {
|
|
313
|
-
const text = await extractLastMessage(page);
|
|
314
|
-
if (text !== lastText) {
|
|
315
|
-
lastText = text;
|
|
316
|
-
stableStart = null;
|
|
317
|
-
} else if (text.length > 0) {
|
|
318
|
-
if (!stableStart) stableStart = Date.now();
|
|
319
|
-
else if (Date.now() - stableStart >= stableDelay) {
|
|
320
|
-
if (!await isGenerating(page)) {
|
|
321
|
-
log(verbose, `\u2713 AI \u56DE\u590D\u5DF2\u5B8C\u6210\uFF08${text.length}\u5B57\uFF09`);
|
|
322
|
-
break;
|
|
323
|
-
}
|
|
324
|
-
stableStart = null;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
await page.waitForTimeout(500);
|
|
328
|
-
}
|
|
329
|
-
if (lastText.length === 0) {
|
|
330
|
-
if (await checkCaptcha(page, verbose)) {
|
|
331
|
-
if (headful) {
|
|
332
|
-
await waitForUserAction(page, "captcha", verbose);
|
|
333
|
-
} else {
|
|
334
|
-
throw new YiyanAgentError("CAPTCHA", "Captcha detected.");
|
|
335
|
-
}
|
|
336
|
-
} else if (headful) {
|
|
337
|
-
await waitForUserAction(page, "no-reply", verbose);
|
|
338
|
-
} else {
|
|
339
|
-
throw new YiyanAgentError("TIMEOUT", "AI reply timeout.");
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
await page.waitForTimeout(1e3);
|
|
343
|
-
}
|
|
344
|
-
async function getMessageCount(page) {
|
|
345
|
-
return await page.evaluate(() => {
|
|
346
|
-
for (const sel of [
|
|
347
|
-
'[class*="answerBox"]',
|
|
348
|
-
'[class*="assistant"]',
|
|
349
|
-
'[class*="ai-message"]',
|
|
350
|
-
'[class*="response"]'
|
|
351
|
-
]) {
|
|
352
|
-
const els = document.querySelectorAll(sel);
|
|
353
|
-
if (els.length > 0) return els.length;
|
|
354
|
-
}
|
|
355
|
-
return document.querySelectorAll('[class*="message"]').length;
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
async function isGenerating(page) {
|
|
359
|
-
return await page.evaluate(() => {
|
|
360
|
-
for (const sel of ['button[aria-label*="\u505C\u6B62"]', '[class*="stop"]', '[class*="generating"]']) {
|
|
361
|
-
const el = document.querySelector(sel);
|
|
362
|
-
if (el) {
|
|
363
|
-
const s = window.getComputedStyle(el);
|
|
364
|
-
if (s.display !== "none" && s.visibility !== "hidden") return true;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
for (const sel of ['[class*="loading"]', '[class*="typing"]', '[class*="spinner"]']) {
|
|
368
|
-
const el = document.querySelector(sel);
|
|
369
|
-
if (el) {
|
|
370
|
-
const s = window.getComputedStyle(el);
|
|
371
|
-
if (s.display !== "none" && s.visibility !== "hidden") return true;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
return false;
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
async function extractLastMessage(page) {
|
|
378
|
-
return await page.evaluate(() => {
|
|
379
|
-
function getFullText(el) {
|
|
380
|
-
if (!el) return "";
|
|
381
|
-
let result = "";
|
|
382
|
-
function walk(node) {
|
|
383
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
384
|
-
result += node.textContent || "";
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
388
|
-
const tag = node.tagName.toLowerCase();
|
|
389
|
-
if (tag === "pre") {
|
|
390
|
-
const codeEl = node.querySelector("code");
|
|
391
|
-
if (codeEl) {
|
|
392
|
-
const cls = codeEl.className || "";
|
|
393
|
-
const lang = (cls.match(/language-(\S+)/) || [])[1] || "";
|
|
394
|
-
result += "\n```" + lang + "\n" + (codeEl.textContent || "") + "\n```\n";
|
|
395
|
-
} else {
|
|
396
|
-
result += "\n```\n" + (node.textContent || "") + "\n```\n";
|
|
397
|
-
}
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
for (const child of node.childNodes) walk(child);
|
|
401
|
-
if (["p", "div", "li", "br", "h1", "h2", "h3", "h4", "h5", "h6"].includes(tag)) {
|
|
402
|
-
result += "\n";
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
walk(el);
|
|
406
|
-
return result.trim();
|
|
407
|
-
}
|
|
408
|
-
for (const sel of SELECTORS.responseContainer) {
|
|
409
|
-
const els = document.querySelectorAll(sel);
|
|
410
|
-
if (els.length > 0) {
|
|
411
|
-
const text = getFullText(els[els.length - 1]);
|
|
412
|
-
if (text.length > 10) return text;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
const mdEls = document.querySelectorAll('[class*="markdown"], [class*="prose"]');
|
|
416
|
-
if (mdEls.length > 0) {
|
|
417
|
-
const text = getFullText(mdEls[mdEls.length - 1]);
|
|
418
|
-
if (text.length > 10) return text;
|
|
419
|
-
}
|
|
420
|
-
return "";
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
async function extractReply(page, question, verbose = false) {
|
|
424
|
-
log(verbose, "\u63D0\u53D6 AI \u56DE\u590D...");
|
|
425
|
-
const text = await extractLastMessage(page);
|
|
426
|
-
if (text.length > 0) {
|
|
427
|
-
const cleaned = text.replace(/^参考\d+个网页\s*/, "").replace(/^深度思考已完成\s*/, "").replace(/^思考完成[::]\s*/, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
428
|
-
log(verbose, `\u63D0\u53D6\u6210\u529F: ${cleaned.length} \u5B57\u7B26`);
|
|
429
|
-
return cleaned;
|
|
430
|
-
}
|
|
431
|
-
throw new YiyanAgentError("TIMEOUT", "Failed to extract reply");
|
|
432
|
-
}
|
|
433
|
-
async function dumpDebugInfo(page) {
|
|
434
|
-
const info = await page.evaluate(() => {
|
|
435
|
-
const classFreq = {};
|
|
436
|
-
document.querySelectorAll("*").forEach((el) => {
|
|
437
|
-
el.classList.forEach((c) => {
|
|
438
|
-
if (c.match(/message|chat|input|send|stop|markdown|content|assistant|user|bot|answer/i)) {
|
|
439
|
-
classFreq[c] = (classFreq[c] || 0) + 1;
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
});
|
|
443
|
-
const inputs = Array.from(document.querySelectorAll("textarea, [contenteditable]")).map((e) => ({
|
|
444
|
-
tag: e.tagName,
|
|
445
|
-
id: e.id || null,
|
|
446
|
-
class: (e.className?.toString() || "").substring(0, 80),
|
|
447
|
-
placeholder: e.placeholder || null,
|
|
448
|
-
editable: e.isContentEditable,
|
|
449
|
-
visible: e.offsetParent !== null
|
|
450
|
-
}));
|
|
451
|
-
return {
|
|
452
|
-
url: window.location.href,
|
|
453
|
-
title: document.title,
|
|
454
|
-
classes: Object.entries(classFreq).sort((a, b) => b[1] - a[1]).slice(0, 40),
|
|
455
|
-
inputs
|
|
456
|
-
};
|
|
457
|
-
});
|
|
458
|
-
console.log("\n" + "\u2550".repeat(60));
|
|
459
|
-
console.log(" DOM DEBUG INFO");
|
|
460
|
-
console.log("\u2550".repeat(60));
|
|
461
|
-
console.log("URL :", info.url);
|
|
462
|
-
console.log("Title :", info.title);
|
|
463
|
-
console.log("\nInput elements:");
|
|
464
|
-
info.inputs.forEach((i) => console.log(" ", JSON.stringify(i)));
|
|
465
|
-
console.log("\nMatching CSS classes (by frequency):");
|
|
466
|
-
info.classes.forEach(([cls, count]) => console.log(` ${String(count).padStart(3)}x .${cls}`));
|
|
467
|
-
console.log("\u2550".repeat(60) + "\n");
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// src/agent.ts
|
|
471
|
-
var DEFAULT_SESSION_DIR = import_path.default.join(import_os.default.homedir(), ".yiyan-browser-agent", "session");
|
|
472
|
-
var YiyanAgent = class {
|
|
473
|
-
options;
|
|
474
|
-
sessionDir;
|
|
475
|
-
verbose;
|
|
476
|
-
_context = null;
|
|
477
|
-
// 保存当前 context 以便关闭
|
|
478
|
-
constructor(options) {
|
|
479
|
-
this.options = {
|
|
480
|
-
...DEFAULT_OPTIONS,
|
|
481
|
-
...options
|
|
482
|
-
};
|
|
483
|
-
this.sessionDir = this.options.profileDir || DEFAULT_SESSION_DIR;
|
|
484
|
-
this.verbose = options?.verbose ?? false;
|
|
485
|
-
const fs2 = require("fs");
|
|
486
|
-
fs2.mkdirSync(this.sessionDir, { recursive: true });
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* 关闭浏览器(如果正在运行)
|
|
490
|
-
*/
|
|
491
|
-
async close() {
|
|
492
|
-
if (this._context) {
|
|
493
|
-
try {
|
|
494
|
-
await this._context.close();
|
|
495
|
-
} catch {
|
|
496
|
-
}
|
|
497
|
-
this._context = null;
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
/**
|
|
501
|
-
* 发送问题并获取答案
|
|
502
|
-
* @param question 问题文本
|
|
503
|
-
* @param headful 是否使用有头浏览器(可见窗口),默认为 false(headless)
|
|
504
|
-
*/
|
|
505
|
-
async ask(question, headful = false) {
|
|
506
|
-
return this.executeAsk(question, headful);
|
|
507
|
-
}
|
|
508
|
-
/**
|
|
509
|
-
* 执行单次问答
|
|
510
|
-
*/
|
|
511
|
-
async executeAsk(question, headful) {
|
|
512
|
-
const { context, page } = await launchBrowser({
|
|
513
|
-
sessionDir: this.sessionDir,
|
|
514
|
-
headless: !headful,
|
|
515
|
-
timeout: this.options.timeout,
|
|
516
|
-
verbose: this.verbose
|
|
517
|
-
});
|
|
518
|
-
try {
|
|
519
|
-
await navigateToYiyan(page, this.verbose);
|
|
520
|
-
const isLoggedIn = await checkLoggedIn(page, this.verbose);
|
|
521
|
-
if (!isLoggedIn) {
|
|
522
|
-
if (headful) {
|
|
523
|
-
await waitForUserAction(page, "login", this.verbose);
|
|
524
|
-
} else {
|
|
525
|
-
throw new YiyanAgentError(
|
|
526
|
-
"CAPTCHA",
|
|
527
|
-
'Not logged in. Please run "yiyan-browser-agent login" first, or use headed mode.'
|
|
528
|
-
);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
await sendMessage(page, question, this.verbose, headful);
|
|
532
|
-
await waitForReply(page, this.options.timeout, this.verbose, headful);
|
|
533
|
-
const reply = await extractReply(page, question, this.verbose);
|
|
534
|
-
return reply;
|
|
535
|
-
} finally {
|
|
536
|
-
await closeBrowser(context, this.verbose);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
/**
|
|
540
|
-
* 登录文心一言(打开有头浏览器让用户手动登录)
|
|
541
|
-
* 登录成功后,后续的 ask 调用将自动使用登录状态
|
|
542
|
-
*/
|
|
543
|
-
async login() {
|
|
544
|
-
const { context, page } = await launchBrowser({
|
|
545
|
-
sessionDir: this.sessionDir,
|
|
546
|
-
headless: false,
|
|
547
|
-
timeout: 6e4,
|
|
548
|
-
verbose: this.verbose
|
|
549
|
-
});
|
|
550
|
-
try {
|
|
551
|
-
await navigateToYiyan(page, this.verbose);
|
|
552
|
-
process.stderr.write("\n[yiyan-browser-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");
|
|
553
|
-
process.stderr.write("[yiyan-browser-agent] \u2551 \u{1F510} LOGIN REQUIRED \u2551\n");
|
|
554
|
-
process.stderr.write("[yiyan-browser-agent] \u2551 \u2551\n");
|
|
555
|
-
process.stderr.write("[yiyan-browser-agent] \u2551 1. Log in to Yiyan in the browser window \u2551\n");
|
|
556
|
-
process.stderr.write("[yiyan-browser-agent] \u2551 2. Return here and press ENTER to continue \u2551\n");
|
|
557
|
-
process.stderr.write("[yiyan-browser-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");
|
|
558
|
-
await this.waitForEnter();
|
|
559
|
-
process.stderr.write("[yiyan-browser-agent] \u2713 Login complete, saving session...\n");
|
|
560
|
-
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
561
|
-
} finally {
|
|
562
|
-
await closeBrowser(context, this.verbose);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
/**
|
|
566
|
-
* 等待用户按 Enter
|
|
567
|
-
*/
|
|
568
|
-
async waitForEnter() {
|
|
569
|
-
const readline = await import("readline");
|
|
570
|
-
return new Promise((resolve) => {
|
|
571
|
-
const rl = readline.createInterface({
|
|
572
|
-
input: process.stdin,
|
|
573
|
-
output: process.stderr
|
|
574
|
-
});
|
|
575
|
-
rl.question("Press ENTER to continue...", () => {
|
|
576
|
-
rl.close();
|
|
577
|
-
resolve();
|
|
578
|
-
});
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
/**
|
|
582
|
-
* 清除保存的 session
|
|
583
|
-
*/
|
|
584
|
-
async reset() {
|
|
585
|
-
const fs2 = await import("fs/promises");
|
|
586
|
-
try {
|
|
587
|
-
await fs2.rm(this.sessionDir, { recursive: true, force: true });
|
|
588
|
-
} catch {
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
/**
|
|
592
|
-
* 检查状态
|
|
593
|
-
*/
|
|
594
|
-
status() {
|
|
595
|
-
const fs2 = require("fs");
|
|
596
|
-
return {
|
|
597
|
-
loggedIn: fs2.existsSync(this.sessionDir),
|
|
598
|
-
sessionPath: this.sessionDir
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
/**
|
|
602
|
-
* Debug 模式:启动浏览器并输出 DOM 信息
|
|
603
|
-
*/
|
|
604
|
-
async debug() {
|
|
605
|
-
const { context, page } = await launchBrowser({
|
|
606
|
-
sessionDir: this.sessionDir,
|
|
607
|
-
headless: false,
|
|
608
|
-
timeout: this.options.timeout,
|
|
609
|
-
verbose: true
|
|
610
|
-
});
|
|
611
|
-
try {
|
|
612
|
-
await navigateToYiyan(page, true);
|
|
613
|
-
await page.waitForTimeout(3e3);
|
|
614
|
-
await dumpDebugInfo(page);
|
|
615
|
-
process.stderr.write("\n[yiyan-browser-agent] \u6D4F\u89C8\u5668\u4FDD\u6301\u6253\u5F00\uFF0C\u53EF\u624B\u52A8\u6D4B\u8BD5\u3002\n");
|
|
616
|
-
process.stderr.write("[yiyan-browser-agent] \u5173\u95ED\u6D4F\u89C8\u5668\u7A97\u53E3\u7ED3\u675F debug \u6A21\u5F0F...\n");
|
|
617
|
-
await new Promise((resolve) => {
|
|
618
|
-
context.on("close", () => resolve());
|
|
619
|
-
});
|
|
620
|
-
} finally {
|
|
621
|
-
try {
|
|
622
|
-
await context.close();
|
|
623
|
-
} catch {
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
};
|
|
628
|
-
|
|
629
|
-
// src/cli.ts
|
|
630
|
-
console.log("");
|
|
631
|
-
console.log("\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\u2550\u2550\u2550\u2550\u2557");
|
|
632
|
-
console.log("\u2551 \u{1F916} Yiyan Browser Agent \u2014 \u6587\u5FC3\u4E00\u8A00\u6D4F\u89C8\u5668\u4EE3\u7406 \u2551");
|
|
633
|
-
console.log("\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\u2550\u2550\u2550\u2550\u255D");
|
|
634
|
-
console.log("");
|
|
635
|
-
function printHelp() {
|
|
636
|
-
console.log(`
|
|
637
|
-
yiyan-browser-agent - \u6587\u5FC3\u4E00\u8A00\u6D4F\u89C8\u5668\u4EE3\u7406 CLI
|
|
638
|
-
|
|
639
|
-
\u7528\u6CD5:
|
|
640
|
-
yiyan-browser-agent login \u9996\u6B21\u767B\u5F55\u6587\u5FC3\u4E00\u8A00\uFF08\u4F1A\u6253\u5F00\u6D4F\u89C8\u5668\u7A97\u53E3\uFF09
|
|
641
|
-
yiyan-browser-agent ask "\u95EE\u9898" [--timeout ms] [--headful] [--verbose]
|
|
642
|
-
yiyan-browser-agent status \u68C0\u67E5\u767B\u5F55\u72B6\u6001
|
|
643
|
-
yiyan-browser-agent reset \u6E05\u9664\u4FDD\u5B58\u7684 session
|
|
644
|
-
yiyan-browser-agent debug \u8C03\u8BD5\u6A21\u5F0F\uFF08\u8F93\u51FA DOM \u4FE1\u606F\uFF09
|
|
645
|
-
|
|
646
|
-
\u547D\u4EE4:
|
|
647
|
-
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
|
|
648
|
-
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
|
|
649
|
-
status \u68C0\u67E5\u767B\u5F55\u72B6\u6001
|
|
650
|
-
reset \u6E05\u9664\u4FDD\u5B58\u7684 session
|
|
651
|
-
debug \u8C03\u8BD5\u6A21\u5F0F\uFF1A\u542F\u52A8\u6D4F\u89C8\u5668\u8F93\u51FA DOM \u4FE1\u606F\uFF0C\u7528\u4E8E\u6392\u67E5\u9009\u62E9\u5668\u95EE\u9898
|
|
652
|
-
|
|
653
|
-
\u9009\u9879:
|
|
654
|
-
--timeout <ms> \u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09\uFF0C\u9ED8\u8BA4 120000
|
|
655
|
-
--headful \u4F7F\u7528\u6709\u5934\u6D4F\u89C8\u5668\uFF08\u53EF\u89C1\u7A97\u53E3\uFF0C\u7528\u4E8E\u624B\u52A8\u8FC7\u9A8C\u8BC1\u7801\uFF09
|
|
656
|
-
--verbose \u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7\uFF08\u8C03\u8BD5\u7528\uFF09
|
|
657
|
-
--help \u663E\u793A\u5E2E\u52A9\u4FE1\u606F
|
|
658
|
-
|
|
659
|
-
\u793A\u4F8B:
|
|
660
|
-
yiyan-browser-agent login # \u9996\u6B21\u4F7F\u7528\uFF1A\u767B\u5F55\u6587\u5FC3\u4E00\u8A00
|
|
661
|
-
yiyan-browser-agent ask "\u4EC0\u4E48\u662F TypeScript\uFF1F" # \u63D0\u95EE\uFF08\u9759\u9ED8\u6A21\u5F0F\uFF09
|
|
662
|
-
yiyan-browser-agent ask "\u89E3\u91CA Promise" --verbose # \u663E\u793A\u8BE6\u7EC6\u65E5\u5FD7
|
|
663
|
-
yiyan-browser-agent ask "30+30=" --headful # \u6709\u5934\u6A21\u5F0F\uFF08\u53EF\u624B\u52A8\u8FC7\u9A8C\u8BC1\u7801\uFF09
|
|
664
|
-
yiyan-browser-agent debug # \u8C03\u8BD5\u6A21\u5F0F\uFF0C\u8F93\u51FA DOM \u4FE1\u606F
|
|
665
|
-
yiyan-browser-agent status
|
|
666
|
-
yiyan-browser-agent reset
|
|
667
|
-
|
|
668
|
-
\u6D41\u7A0B:
|
|
669
|
-
1. \u5148\u8FD0\u884C yiyan-browser-agent login \u767B\u5F55\uFF08\u53EA\u9700\u4E00\u6B21\uFF0C\u767B\u5F55\u72B6\u6001\u4F1A\u4FDD\u5B58\uFF09
|
|
670
|
-
2. \u4E4B\u540E\u76F4\u63A5 yiyan-browser-agent ask "\u95EE\u9898" \u5373\u53EF\uFF0C\u65E0\u9700\u518D\u767B\u5F55
|
|
671
|
-
3. \u5982\u679C\u9047\u5230\u9A8C\u8BC1\u7801\uFF0C\u52A0 --headful \u5207\u6362\u6709\u5934\u6A21\u5F0F\u624B\u52A8\u5904\u7406
|
|
672
|
-
|
|
673
|
-
\u63D0\u793A:
|
|
674
|
-
Chromium \u4F1A\u81EA\u52A8\u4E0B\u8F7D\uFF08\u7EA6150MB\uFF09\uFF0C\u65E0\u9700\u624B\u52A8\u5B89\u88C5 Chrome
|
|
675
|
-
Session \u4FDD\u5B58\u5728 ~/.yiyan-browser-agent/session \u76EE\u5F55
|
|
676
|
-
`);
|
|
677
|
-
}
|
|
678
|
-
function parseCliArgs(args) {
|
|
679
|
-
const result = {
|
|
680
|
-
command: null
|
|
681
|
-
};
|
|
682
|
-
for (let i = 0; i < args.length; i++) {
|
|
683
|
-
const arg = args[i];
|
|
684
|
-
if (arg === "--help") {
|
|
685
|
-
result.command = "help";
|
|
686
|
-
return result;
|
|
687
|
-
}
|
|
688
|
-
if (arg === "ask") {
|
|
689
|
-
result.command = "ask";
|
|
690
|
-
if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
|
|
691
|
-
result.question = args[i + 1];
|
|
692
|
-
i++;
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
if (arg === "status") {
|
|
696
|
-
result.command = "status";
|
|
697
|
-
}
|
|
698
|
-
if (arg === "reset") {
|
|
699
|
-
result.command = "reset";
|
|
700
|
-
}
|
|
701
|
-
if (arg === "login") {
|
|
702
|
-
result.command = "login";
|
|
703
|
-
}
|
|
704
|
-
if (arg === "debug") {
|
|
705
|
-
result.command = "debug";
|
|
706
|
-
}
|
|
707
|
-
if (arg === "--timeout") {
|
|
708
|
-
if (i + 1 < args.length) {
|
|
709
|
-
result.timeout = parseInt(args[i + 1], 10);
|
|
710
|
-
i++;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
if (arg === "--headful") {
|
|
714
|
-
result.headful = true;
|
|
715
|
-
}
|
|
716
|
-
if (arg === "--verbose") {
|
|
717
|
-
result.verbose = true;
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
return result;
|
|
721
|
-
}
|
|
722
|
-
function formatCliOutput(output) {
|
|
723
|
-
return JSON.stringify(output, null, 2);
|
|
724
|
-
}
|
|
725
|
-
async function runCli(args) {
|
|
726
|
-
const parsed = parseCliArgs(args);
|
|
727
|
-
if (parsed.command === "help" || parsed.command === null) {
|
|
728
|
-
printHelp();
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
const options = {};
|
|
732
|
-
if (parsed.timeout) {
|
|
733
|
-
options.timeout = parsed.timeout;
|
|
734
|
-
}
|
|
735
|
-
if (parsed.verbose) {
|
|
736
|
-
options.verbose = true;
|
|
737
|
-
}
|
|
738
|
-
const agent = new YiyanAgent(options);
|
|
739
|
-
const shutdown = async () => {
|
|
740
|
-
console.log("\n[yiyan-browser-agent] \u6B63\u5728\u5173\u95ED...");
|
|
741
|
-
try {
|
|
742
|
-
await agent.close();
|
|
743
|
-
} catch {
|
|
744
|
-
}
|
|
745
|
-
process.exit(0);
|
|
746
|
-
};
|
|
747
|
-
process.on("SIGINT", shutdown);
|
|
748
|
-
process.on("SIGTERM", shutdown);
|
|
749
|
-
if (parsed.command === "login") {
|
|
750
|
-
try {
|
|
751
|
-
console.log("[yiyan-browser-agent] \u542F\u52A8\u767B\u5F55\u6D41\u7A0B...");
|
|
752
|
-
await agent.login();
|
|
753
|
-
console.log(formatCliOutput({
|
|
754
|
-
success: true,
|
|
755
|
-
question: "login",
|
|
756
|
-
answer: 'Login successful! You can now use "ask" command.',
|
|
757
|
-
duration: 0
|
|
758
|
-
}));
|
|
759
|
-
} catch (error) {
|
|
760
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
761
|
-
console.error("[yiyan-browser-agent] \u2717 \u767B\u5F55\u5931\u8D25:", errorMessage);
|
|
762
|
-
console.log(formatCliOutput({
|
|
763
|
-
success: false,
|
|
764
|
-
question: "login",
|
|
765
|
-
error: errorMessage,
|
|
766
|
-
duration: 0
|
|
767
|
-
}));
|
|
768
|
-
}
|
|
769
|
-
return;
|
|
770
|
-
}
|
|
771
|
-
if (parsed.command === "status") {
|
|
772
|
-
const status = agent.status();
|
|
773
|
-
console.log(formatCliOutput({
|
|
774
|
-
success: true,
|
|
775
|
-
question: "status",
|
|
776
|
-
answer: JSON.stringify(status),
|
|
777
|
-
duration: 0
|
|
778
|
-
}));
|
|
779
|
-
return;
|
|
780
|
-
}
|
|
781
|
-
if (parsed.command === "reset") {
|
|
782
|
-
await agent.reset();
|
|
783
|
-
console.log(formatCliOutput({
|
|
784
|
-
success: true,
|
|
785
|
-
question: "reset",
|
|
786
|
-
answer: "Session cleared successfully",
|
|
787
|
-
duration: 0
|
|
788
|
-
}));
|
|
789
|
-
return;
|
|
790
|
-
}
|
|
791
|
-
if (parsed.command === "debug") {
|
|
792
|
-
try {
|
|
793
|
-
console.log("[yiyan-browser-agent] \u542F\u52A8\u8C03\u8BD5\u6A21\u5F0F...");
|
|
794
|
-
await agent.debug();
|
|
795
|
-
console.log(formatCliOutput({
|
|
796
|
-
success: true,
|
|
797
|
-
question: "debug",
|
|
798
|
-
answer: "Debug session completed",
|
|
799
|
-
duration: 0
|
|
800
|
-
}));
|
|
801
|
-
} catch (error) {
|
|
802
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
803
|
-
console.error("[yiyan-browser-agent] \u2717 \u8C03\u8BD5\u5931\u8D25:", errorMessage);
|
|
804
|
-
console.log(formatCliOutput({
|
|
805
|
-
success: false,
|
|
806
|
-
question: "debug",
|
|
807
|
-
error: errorMessage,
|
|
808
|
-
duration: 0
|
|
809
|
-
}));
|
|
810
|
-
}
|
|
811
|
-
return;
|
|
812
|
-
}
|
|
813
|
-
if (parsed.command === "ask") {
|
|
814
|
-
if (!parsed.question) {
|
|
815
|
-
console.log(formatCliOutput({
|
|
816
|
-
success: false,
|
|
817
|
-
question: "",
|
|
818
|
-
error: 'No question provided. Usage: ask "your question"',
|
|
819
|
-
duration: 0
|
|
820
|
-
}));
|
|
821
|
-
return;
|
|
822
|
-
}
|
|
823
|
-
const startTime = Date.now();
|
|
824
|
-
try {
|
|
825
|
-
const answer = await agent.ask(parsed.question, !!parsed.headful);
|
|
826
|
-
const duration = Date.now() - startTime;
|
|
827
|
-
console.log(formatCliOutput({
|
|
828
|
-
success: true,
|
|
829
|
-
question: parsed.question,
|
|
830
|
-
answer,
|
|
831
|
-
duration
|
|
832
|
-
}));
|
|
833
|
-
} catch (error) {
|
|
834
|
-
const duration = Date.now() - startTime;
|
|
835
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
836
|
-
console.log(formatCliOutput({
|
|
837
|
-
success: false,
|
|
838
|
-
question: parsed.question,
|
|
839
|
-
error: errorMessage,
|
|
840
|
-
duration
|
|
841
|
-
}));
|
|
842
|
-
}
|
|
843
|
-
return;
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
runCli(process.argv.slice(2)).catch((err) => {
|
|
847
|
-
console.error("[yiyan-browser-agent] Uncaught error:", err);
|
|
848
|
-
process.exit(1);
|
|
849
|
-
});
|
|
850
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
851
|
-
0 && (module.exports = {
|
|
852
|
-
formatCliOutput,
|
|
853
|
-
parseCliArgs,
|
|
854
|
-
printHelp,
|
|
855
|
-
runCli
|
|
856
|
-
});
|
|
857
|
-
//# sourceMappingURL=cli.js.map
|