@roll-agent/browser-use-agent 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/index.d.ts +0 -1
  2. package/dist/index.js +1 -111
  3. package/dist/pages/platform-page.d.ts +0 -1
  4. package/dist/pages/platform-page.js +1 -17
  5. package/dist/pages/yupao/chat.d.ts +0 -1
  6. package/dist/pages/yupao/chat.js +1 -18
  7. package/dist/pages/yupao/message-list.d.ts +0 -1
  8. package/dist/pages/yupao/message-list.js +1 -36
  9. package/dist/pages/yupao/navigation.d.ts +0 -1
  10. package/dist/pages/yupao/navigation.js +1 -34
  11. package/dist/pages/yupao/selectors.d.ts +0 -1
  12. package/dist/pages/yupao/selectors.js +1 -26
  13. package/dist/pages/zhipin/anti-detection.d.ts +0 -1
  14. package/dist/pages/zhipin/anti-detection.js +1 -65
  15. package/dist/pages/zhipin/chat-navigation.d.ts +0 -1
  16. package/dist/pages/zhipin/chat-navigation.js +1 -158
  17. package/dist/pages/zhipin/selectors.d.ts +0 -1
  18. package/dist/pages/zhipin/selectors.js +1 -141
  19. package/dist/pages/zhipin/username.d.ts +0 -1
  20. package/dist/pages/zhipin/username.js +1 -302
  21. package/dist/platforms.d.ts +0 -1
  22. package/dist/platforms.js +1 -20
  23. package/dist/runtime-holder.d.ts +0 -1
  24. package/dist/runtime-holder.js +1 -50
  25. package/dist/tools/browser-status.d.ts +0 -1
  26. package/dist/tools/browser-status.js +1 -49
  27. package/dist/tools/list-pages.d.ts +0 -1
  28. package/dist/tools/list-pages.js +1 -40
  29. package/dist/tools/navigate-active-tab.d.ts +0 -1
  30. package/dist/tools/navigate-active-tab.js +1 -49
  31. package/dist/tools/open-platform.d.ts +0 -1
  32. package/dist/tools/open-platform.js +1 -33
  33. package/dist/tools/select-page.d.ts +0 -1
  34. package/dist/tools/select-page.js +1 -38
  35. package/dist/tools/yupao-read-messages.d.ts +0 -1
  36. package/dist/tools/yupao-read-messages.js +1 -36
  37. package/dist/tools/yupao-send-reply.d.ts +0 -1
  38. package/dist/tools/yupao-send-reply.js +1 -40
  39. package/dist/tools/zhipin-close-resume.d.ts +0 -1
  40. package/dist/tools/zhipin-close-resume.js +1 -73
  41. package/dist/tools/zhipin-exchange-wechat.d.ts +0 -1
  42. package/dist/tools/zhipin-exchange-wechat.js +1 -218
  43. package/dist/tools/zhipin-get-candidate-info.d.ts +0 -1
  44. package/dist/tools/zhipin-get-candidate-info.js +1 -247
  45. package/dist/tools/zhipin-get-candidate-list.d.ts +0 -1
  46. package/dist/tools/zhipin-get-candidate-list.js +1 -175
  47. package/dist/tools/zhipin-get-username.d.ts +0 -1
  48. package/dist/tools/zhipin-get-username.js +1 -58
  49. package/dist/tools/zhipin-locate-resume-canvas.d.ts +0 -1
  50. package/dist/tools/zhipin-locate-resume-canvas.js +1 -90
  51. package/dist/tools/zhipin-open-chat.d.ts +0 -1
  52. package/dist/tools/zhipin-open-chat.js +1 -82
  53. package/dist/tools/zhipin-open-resume.d.ts +0 -1
  54. package/dist/tools/zhipin-open-resume.js +1 -51
  55. package/dist/tools/zhipin-read-messages.d.ts +0 -1
  56. package/dist/tools/zhipin-read-messages.js +1 -90
  57. package/dist/tools/zhipin-say-hello.d.ts +0 -1
  58. package/dist/tools/zhipin-say-hello.js +1 -118
  59. package/dist/tools/zhipin-send-reply.d.ts +0 -1
  60. package/dist/tools/zhipin-send-reply.js +1 -99
  61. package/package.json +4 -4
  62. package/dist/index.d.ts.map +0 -1
  63. package/dist/index.js.map +0 -1
  64. package/dist/pages/platform-page.d.ts.map +0 -1
  65. package/dist/pages/platform-page.js.map +0 -1
  66. package/dist/pages/yupao/chat.d.ts.map +0 -1
  67. package/dist/pages/yupao/chat.js.map +0 -1
  68. package/dist/pages/yupao/message-list.d.ts.map +0 -1
  69. package/dist/pages/yupao/message-list.js.map +0 -1
  70. package/dist/pages/yupao/navigation.d.ts.map +0 -1
  71. package/dist/pages/yupao/navigation.js.map +0 -1
  72. package/dist/pages/yupao/selectors.d.ts.map +0 -1
  73. package/dist/pages/yupao/selectors.js.map +0 -1
  74. package/dist/pages/zhipin/anti-detection.d.ts.map +0 -1
  75. package/dist/pages/zhipin/anti-detection.js.map +0 -1
  76. package/dist/pages/zhipin/chat-navigation.d.ts.map +0 -1
  77. package/dist/pages/zhipin/chat-navigation.js.map +0 -1
  78. package/dist/pages/zhipin/selectors.d.ts.map +0 -1
  79. package/dist/pages/zhipin/selectors.js.map +0 -1
  80. package/dist/pages/zhipin/username.d.ts.map +0 -1
  81. package/dist/pages/zhipin/username.js.map +0 -1
  82. package/dist/platforms.d.ts.map +0 -1
  83. package/dist/platforms.js.map +0 -1
  84. package/dist/runtime-holder.d.ts.map +0 -1
  85. package/dist/runtime-holder.js.map +0 -1
  86. package/dist/tools/browser-status.d.ts.map +0 -1
  87. package/dist/tools/browser-status.js.map +0 -1
  88. package/dist/tools/list-pages.d.ts.map +0 -1
  89. package/dist/tools/list-pages.js.map +0 -1
  90. package/dist/tools/navigate-active-tab.d.ts.map +0 -1
  91. package/dist/tools/navigate-active-tab.js.map +0 -1
  92. package/dist/tools/open-platform.d.ts.map +0 -1
  93. package/dist/tools/open-platform.js.map +0 -1
  94. package/dist/tools/select-page.d.ts.map +0 -1
  95. package/dist/tools/select-page.js.map +0 -1
  96. package/dist/tools/yupao-read-messages.d.ts.map +0 -1
  97. package/dist/tools/yupao-read-messages.js.map +0 -1
  98. package/dist/tools/yupao-send-reply.d.ts.map +0 -1
  99. package/dist/tools/yupao-send-reply.js.map +0 -1
  100. package/dist/tools/zhipin-close-resume.d.ts.map +0 -1
  101. package/dist/tools/zhipin-close-resume.js.map +0 -1
  102. package/dist/tools/zhipin-exchange-wechat.d.ts.map +0 -1
  103. package/dist/tools/zhipin-exchange-wechat.js.map +0 -1
  104. package/dist/tools/zhipin-get-candidate-info.d.ts.map +0 -1
  105. package/dist/tools/zhipin-get-candidate-info.js.map +0 -1
  106. package/dist/tools/zhipin-get-candidate-list.d.ts.map +0 -1
  107. package/dist/tools/zhipin-get-candidate-list.js.map +0 -1
  108. package/dist/tools/zhipin-get-username.d.ts.map +0 -1
  109. package/dist/tools/zhipin-get-username.js.map +0 -1
  110. package/dist/tools/zhipin-locate-resume-canvas.d.ts.map +0 -1
  111. package/dist/tools/zhipin-locate-resume-canvas.js.map +0 -1
  112. package/dist/tools/zhipin-open-chat.d.ts.map +0 -1
  113. package/dist/tools/zhipin-open-chat.js.map +0 -1
  114. package/dist/tools/zhipin-open-resume.d.ts.map +0 -1
  115. package/dist/tools/zhipin-open-resume.js.map +0 -1
  116. package/dist/tools/zhipin-read-messages.d.ts.map +0 -1
  117. package/dist/tools/zhipin-read-messages.js.map +0 -1
  118. package/dist/tools/zhipin-say-hello.d.ts.map +0 -1
  119. package/dist/tools/zhipin-say-hello.js.map +0 -1
  120. package/dist/tools/zhipin-send-reply.d.ts.map +0 -1
  121. package/dist/tools/zhipin-send-reply.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,111 +1 @@
1
- import { defineAgent } from "@roll-agent/sdk";
2
- import { BrowserRuntimeConfigSchema } from "@roll-agent/browser";
3
- import { browserStatus } from "./tools/browser-status.js";
4
- import { listPages } from "./tools/list-pages.js";
5
- import { navigateActiveTab } from "./tools/navigate-active-tab.js";
6
- import { openPlatform } from "./tools/open-platform.js";
7
- import { selectPage } from "./tools/select-page.js";
8
- // Zhipin — 聊天
9
- import { zhipinReadMessages } from "./tools/zhipin-read-messages.js";
10
- import { zhipinOpenChat } from "./tools/zhipin-open-chat.js";
11
- import { zhipinGetCandidateInfo } from "./tools/zhipin-get-candidate-info.js";
12
- import { zhipinSendReply } from "./tools/zhipin-send-reply.js";
13
- import { zhipinExchangeWechat } from "./tools/zhipin-exchange-wechat.js";
14
- import { zhipinGetUsername } from "./tools/zhipin-get-username.js";
15
- // Zhipin — 推荐列表
16
- import { zhipinGetCandidateList } from "./tools/zhipin-get-candidate-list.js";
17
- import { zhipinSayHello } from "./tools/zhipin-say-hello.js";
18
- import { zhipinOpenResume } from "./tools/zhipin-open-resume.js";
19
- import { zhipinLocateResumeCanvas } from "./tools/zhipin-locate-resume-canvas.js";
20
- import { zhipinCloseResume } from "./tools/zhipin-close-resume.js";
21
- // Yupao
22
- import { yupaoReadMessages } from "./tools/yupao-read-messages.js";
23
- import { yupaoSendReply } from "./tools/yupao-send-reply.js";
24
- import { initRuntime, shutdownRuntime } from "./runtime-holder.js";
25
- function parseBooleanEnv(value) {
26
- if (value === undefined)
27
- return undefined;
28
- if (value === "true")
29
- return true;
30
- if (value === "false")
31
- return false;
32
- throw new Error(`Expected boolean env value "true" or "false", received "${value}".`);
33
- }
34
- function parseIntegerEnv(value, label) {
35
- if (value === undefined)
36
- return undefined;
37
- const parsed = Number.parseInt(value, 10);
38
- if (!Number.isInteger(parsed)) {
39
- throw new Error(`${label} must be an integer, received "${value}".`);
40
- }
41
- return parsed;
42
- }
43
- function parseArgsJson(value) {
44
- if (value === undefined)
45
- return undefined;
46
- const parsed = JSON.parse(value);
47
- if (!Array.isArray(parsed) || !parsed.every((item) => typeof item === "string")) {
48
- throw new Error("BROWSER_ARGS_JSON must be a JSON string array.");
49
- }
50
- return parsed;
51
- }
52
- function loadRuntimeConfigFromEnv() {
53
- return BrowserRuntimeConfigSchema.parse({
54
- mode: process.env["BROWSER_MODE"],
55
- headless: parseBooleanEnv(process.env["BROWSER_HEADLESS"]),
56
- cdpUrl: process.env["BROWSER_CDP_URL"],
57
- cdpHost: process.env["BROWSER_CDP_HOST"],
58
- cdpPort: parseIntegerEnv(process.env["BROWSER_CDP_PORT"], "BROWSER_CDP_PORT"),
59
- channel: process.env["BROWSER_CHANNEL"],
60
- executablePath: process.env["BROWSER_EXECUTABLE_PATH"],
61
- userDataDir: process.env["BROWSER_USER_DATA_DIR"],
62
- args: parseArgsJson(process.env["BROWSER_ARGS_JSON"]),
63
- sessionsDir: process.env["BROWSER_SESSIONS_DIR"],
64
- });
65
- }
66
- const agent = defineAgent({
67
- name: "browser-use-agent",
68
- tools: [
69
- // 通用
70
- browserStatus,
71
- listPages,
72
- navigateActiveTab,
73
- openPlatform,
74
- selectPage,
75
- // Zhipin 聊天
76
- zhipinReadMessages,
77
- zhipinOpenChat,
78
- zhipinGetCandidateInfo,
79
- zhipinSendReply,
80
- zhipinExchangeWechat,
81
- zhipinGetUsername,
82
- // Zhipin 推荐列表
83
- zhipinGetCandidateList,
84
- zhipinSayHello,
85
- zhipinOpenResume,
86
- zhipinLocateResumeCanvas,
87
- zhipinCloseResume,
88
- // Yupao
89
- yupaoReadMessages,
90
- yupaoSendReply,
91
- ],
92
- }, {
93
- onShutdown: shutdownRuntime,
94
- });
95
- async function main() {
96
- await initRuntime(loadRuntimeConfigFromEnv());
97
- // 以 HTTP 模式启动 MCP Server
98
- await agent.listen({
99
- transport: {
100
- type: "http",
101
- port: parseInt(process.env["BROWSER_AGENT_PORT"] ?? "3100", 10),
102
- host: process.env["BROWSER_AGENT_HOST"] ?? "127.0.0.1",
103
- },
104
- });
105
- }
106
- main().catch(async (err) => {
107
- console.error("Fatal error:", err);
108
- await shutdownRuntime().catch(() => { });
109
- process.exit(1);
110
- });
111
- //# sourceMappingURL=index.js.map
1
+ import{defineAgent as o}from"@roll-agent/sdk";import{BrowserRuntimeConfigSchema as r}from"@roll-agent/browser";import{browserStatus as e}from"./tools/browser-status.js";import{listPages as s}from"./tools/list-pages.js";import{navigateActiveTab as t}from"./tools/navigate-active-tab.js";import{openPlatform as n}from"./tools/open-platform.js";import{selectPage as i}from"./tools/select-page.js";import{zhipinReadMessages as p}from"./tools/zhipin-read-messages.js";import{zhipinOpenChat as a}from"./tools/zhipin-open-chat.js";import{zhipinGetCandidateInfo as m}from"./tools/zhipin-get-candidate-info.js";import{zhipinSendReply as c}from"./tools/zhipin-send-reply.js";import{zhipinExchangeWechat as l}from"./tools/zhipin-exchange-wechat.js";import{zhipinGetUsername as f}from"./tools/zhipin-get-username.js";import{zhipinGetCandidateList as R}from"./tools/zhipin-get-candidate-list.js";import{zhipinSayHello as u}from"./tools/zhipin-say-hello.js";import{zhipinOpenResume as S}from"./tools/zhipin-open-resume.js";import{zhipinLocateResumeCanvas as E}from"./tools/zhipin-locate-resume-canvas.js";import{zhipinCloseResume as h}from"./tools/zhipin-close-resume.js";import{yupaoReadMessages as _}from"./tools/yupao-read-messages.js";import{yupaoSendReply as O}from"./tools/yupao-send-reply.js";import{initRuntime as v,shutdownRuntime as d}from"./runtime-holder.js";function j(o){if(void 0!==o){if("true"===o)return!0;if("false"===o)return!1;throw new Error(`Expected boolean env value "true" or "false", received "${o}".`)}}function g(o,r){if(void 0===o)return;const e=Number.parseInt(o,10);if(!Number.isInteger(e))throw new Error(`${r} must be an integer, received "${o}".`);return e}function B(o){if(void 0===o)return;const r=JSON.parse(o);if(!Array.isArray(r)||!r.every(o=>"string"==typeof o))throw new Error("BROWSER_ARGS_JSON must be a JSON string array.");return r}function w(){return r.parse({mode:process.env.BROWSER_MODE,headless:j(process.env.BROWSER_HEADLESS),cdpUrl:process.env.BROWSER_CDP_URL,cdpHost:process.env.BROWSER_CDP_HOST,cdpPort:g(process.env.BROWSER_CDP_PORT,"BROWSER_CDP_PORT"),channel:process.env.BROWSER_CHANNEL,executablePath:process.env.BROWSER_EXECUTABLE_PATH,userDataDir:process.env.BROWSER_USER_DATA_DIR,args:B(process.env.BROWSER_ARGS_JSON),sessionsDir:process.env.BROWSER_SESSIONS_DIR})}const W=o({name:"browser-use-agent",tools:[e,s,t,n,i,p,a,m,c,l,f,R,u,S,E,h,_,O]},{onShutdown:d});async function y(){await v(w()),await W.listen({transport:{type:"http",port:parseInt(process.env.BROWSER_AGENT_PORT??"3100",10),host:process.env.BROWSER_AGENT_HOST??"127.0.0.1"}})}y().catch(async o=>{console.error("Fatal error:",o),await d().catch(()=>{}),process.exit(1)});
@@ -5,4 +5,3 @@ export type ResolvedPlatformPage = {
5
5
  };
6
6
  export declare function findExistingPlatformPage(ctxManager: BrowserContextManager, platform: Platform): Promise<Page | undefined>;
7
7
  export declare function ensurePlatformHomePage(ctxManager: BrowserContextManager, platform: Platform): Promise<ResolvedPlatformPage>;
8
- //# sourceMappingURL=platform-page.d.ts.map
@@ -1,17 +1 @@
1
- import { PLATFORM_HOME, matchesPlatformHost } from "../platforms.js";
2
- export async function findExistingPlatformPage(ctxManager, platform) {
3
- return ctxManager.useExistingPage(platform, (page) => matchesPlatformHost(page.url(), platform));
4
- }
5
- export async function ensurePlatformHomePage(ctxManager, platform) {
6
- const matchedPage = await findExistingPlatformPage(ctxManager, platform);
7
- const page = matchedPage ?? (await ctxManager.getPage(platform));
8
- await page.bringToFront().catch(() => { });
9
- if (!matchesPlatformHost(page.url(), platform)) {
10
- await page.goto(PLATFORM_HOME[platform], { waitUntil: "domcontentloaded" });
11
- }
12
- return {
13
- page,
14
- reusedExistingPage: matchedPage !== undefined,
15
- };
16
- }
17
- //# sourceMappingURL=platform-page.js.map
1
+ import{PLATFORM_HOME as t,matchesPlatformHost as a}from"../platforms.js";export async function findExistingPlatformPage(t,i){return t.useExistingPage(i,t=>a(t.url(),i))}export async function ensurePlatformHomePage(i,n){const e=await findExistingPlatformPage(i,n),o=e??await i.getPage(n);return await o.bringToFront().catch(()=>{}),a(o.url(),n)||await o.goto(t[n],{waitUntil:"domcontentloaded"}),{page:o,reusedExistingPage:void 0!==e}}
@@ -4,4 +4,3 @@ export declare function sendReply(page: Page, conversationId: string, message: s
4
4
  success: boolean;
5
5
  error?: string;
6
6
  }>;
7
- //# sourceMappingURL=chat.d.ts.map
@@ -1,18 +1 @@
1
- import { waitForSelector, typeText, clickElement } from "@roll-agent/browser";
2
- import { YUPAO_SELECTORS } from "./selectors.js";
3
- import { goToConversation } from "./navigation.js";
4
- /** 在指定对话中发送回复消息 */
5
- export async function sendReply(page, conversationId, message) {
6
- try {
7
- await goToConversation(page, conversationId);
8
- await typeText(page, YUPAO_SELECTORS.chat.input, message);
9
- await clickElement(page, YUPAO_SELECTORS.chat.sendButton);
10
- await waitForSelector(page, YUPAO_SELECTORS.chat.messageItem, { timeout: 5_000 });
11
- return { success: true };
12
- }
13
- catch (err) {
14
- const errorMessage = err instanceof Error ? err.message : String(err);
15
- return { success: false, error: errorMessage };
16
- }
17
- }
18
- //# sourceMappingURL=chat.js.map
1
+ import{waitForSelector as t,typeText as r,clickElement as e}from"@roll-agent/browser";import{YUPAO_SELECTORS as a}from"./selectors.js";import{goToConversation as s}from"./navigation.js";export async function sendReply(o,n,i){try{return await s(o,n),await r(o,a.chat.input,i),await e(o,a.chat.sendButton),await t(o,a.chat.messageItem,{timeout:5e3}),{success:!0}}catch(t){return{success:!1,error:t instanceof Error?t.message:String(t)}}}
@@ -8,4 +8,3 @@ export interface MessageListItem {
8
8
  }
9
9
  /** 解析鱼泡消息列表页 */
10
10
  export declare function parseMessageList(page: Page, limit?: number): Promise<ReadonlyArray<MessageListItem>>;
11
- //# sourceMappingURL=message-list.d.ts.map
@@ -1,36 +1 @@
1
- import { waitForSelector } from "@roll-agent/browser";
2
- import { YUPAO_SELECTORS } from "./selectors.js";
3
- import { ensureOnMessageList } from "./navigation.js";
4
- /** 解析鱼泡消息列表页 */
5
- export async function parseMessageList(page, limit) {
6
- await ensureOnMessageList(page);
7
- await waitForSelector(page, YUPAO_SELECTORS.messageList.item, { timeout: 10_000 });
8
- const sel = YUPAO_SELECTORS.messageList;
9
- const items = await page.$$eval(sel.item, (elements, arg) => {
10
- const result = [];
11
- const itemsToProcess = arg.maxItems ? elements.slice(0, arg.maxItems) : elements;
12
- for (const el of itemsToProcess) {
13
- const nameEl = el.querySelector(arg.sel.candidateName);
14
- const msgEl = el.querySelector(arg.sel.lastMessage);
15
- const badgeEl = el.querySelector(arg.sel.unreadBadge);
16
- const timeEl = el.querySelector(arg.sel.timestamp);
17
- const conversationId = el.getAttribute("data-id") ??
18
- el.getAttribute("data-conversation-id") ??
19
- el
20
- .querySelector("a")
21
- ?.getAttribute("href")
22
- ?.match(/id=([^&]+)/)?.[1] ??
23
- "";
24
- result.push({
25
- conversationId,
26
- candidateName: nameEl?.textContent?.trim() ?? "",
27
- lastMessage: msgEl?.textContent?.trim() ?? "",
28
- unreadCount: parseInt(badgeEl?.textContent?.trim() ?? "0", 10) || 0,
29
- timestamp: timeEl?.textContent?.trim() ?? "",
30
- });
31
- }
32
- return result;
33
- }, { sel, maxItems: limit });
34
- return items;
35
- }
36
- //# sourceMappingURL=message-list.js.map
1
+ import{waitForSelector as t}from"@roll-agent/browser";import{YUPAO_SELECTORS as e}from"./selectors.js";import{ensureOnMessageList as a}from"./navigation.js";export async function parseMessageList(s,r){await a(s),await t(s,e.messageList.item,{timeout:1e4});const o=e.messageList;return await s.$$eval(o.item,(t,e)=>{const a=[],s=e.maxItems?t.slice(0,e.maxItems):t;for(const t of s){const s=t.querySelector(e.sel.candidateName),r=t.querySelector(e.sel.lastMessage),o=t.querySelector(e.sel.unreadBadge),i=t.querySelector(e.sel.timestamp),n=t.getAttribute("data-id")??t.getAttribute("data-conversation-id")??t.querySelector("a")?.getAttribute("href")?.match(/id=([^&]+)/)?.[1]??"";a.push({conversationId:n,candidateName:s?.textContent?.trim()??"",lastMessage:r?.textContent?.trim()??"",unreadCount:parseInt(o?.textContent?.trim()??"0",10)||0,timestamp:i?.textContent?.trim()??""})}return a},{sel:o,maxItems:r})}
@@ -7,4 +7,3 @@ export declare function goToLoginPage(page: Page): Promise<void>;
7
7
  export declare function goToConversation(page: Page, conversationId: string): Promise<void>;
8
8
  /** 检测是否已登录 */
9
9
  export declare function isLoggedIn(page: Page): Promise<boolean>;
10
- //# sourceMappingURL=navigation.d.ts.map
@@ -1,34 +1 @@
1
- import { navigateTo, waitForSelector } from "@roll-agent/browser";
2
- import { YUPAO_SELECTORS } from "./selectors.js";
3
- const YUPAO_BASE = "https://www.yupao.com";
4
- const MESSAGE_LIST_URL = `${YUPAO_BASE}/chat`;
5
- const LOGIN_URL = `${YUPAO_BASE}/login`;
6
- /** 导航到消息列表页 */
7
- export async function ensureOnMessageList(page) {
8
- const currentUrl = page.url();
9
- if (!currentUrl.includes("/chat")) {
10
- await navigateTo(page, MESSAGE_LIST_URL);
11
- }
12
- await waitForSelector(page, YUPAO_SELECTORS.messageList.container, { timeout: 15_000 });
13
- }
14
- /** 导航到登录页 */
15
- export async function goToLoginPage(page) {
16
- await navigateTo(page, LOGIN_URL);
17
- }
18
- /** 导航到指定对话 */
19
- export async function goToConversation(page, conversationId) {
20
- const url = `${YUPAO_BASE}/chat?id=${encodeURIComponent(conversationId)}`;
21
- await navigateTo(page, url);
22
- await waitForSelector(page, YUPAO_SELECTORS.chat.input, { timeout: 15_000 });
23
- }
24
- /** 检测是否已登录 */
25
- export async function isLoggedIn(page) {
26
- try {
27
- await waitForSelector(page, YUPAO_SELECTORS.login.loginSuccess, { timeout: 3_000 });
28
- return true;
29
- }
30
- catch {
31
- return false;
32
- }
33
- }
34
- //# sourceMappingURL=navigation.js.map
1
+ import{navigateTo as t,waitForSelector as o}from"@roll-agent/browser";import{YUPAO_SELECTORS as n}from"./selectors.js";const e="https://www.yupao.com",a=`${e}/chat`,i=`${e}/login`;export async function ensureOnMessageList(e){e.url().includes("/chat")||await t(e,a),await o(e,n.messageList.container,{timeout:15e3})}export async function goToLoginPage(o){await t(o,i)}export async function goToConversation(a,i){const c=`${e}/chat?id=${encodeURIComponent(i)}`;await t(a,c),await o(a,n.chat.input,{timeout:15e3})}export async function isLoggedIn(t){try{return await o(t,n.login.loginSuccess,{timeout:3e3}),!0}catch{return!1}}
@@ -23,4 +23,3 @@ export declare const YUPAO_SELECTORS: {
23
23
  readonly messageText: ".chat-msg .text, .msg-bubble .text";
24
24
  };
25
25
  };
26
- //# sourceMappingURL=selectors.d.ts.map
@@ -1,26 +1 @@
1
- /**
2
- * 鱼泡 CSS/aria 选择器常量。
3
- *
4
- * DOM 结构变更时只需更新此文件。
5
- */
6
- export const YUPAO_SELECTORS = {
7
- login: {
8
- qrCode: ".login-qr img, .qr-code img",
9
- loginSuccess: ".user-info, .header-user",
10
- },
11
- messageList: {
12
- container: ".chat-list, .msg-list",
13
- item: ".chat-item, .msg-item",
14
- candidateName: ".chat-item .name, .msg-item .name",
15
- lastMessage: ".chat-item .msg, .msg-item .content",
16
- unreadBadge: ".chat-item .unread, .msg-item .badge",
17
- timestamp: ".chat-item .time, .msg-item .time",
18
- },
19
- chat: {
20
- input: ".chat-input textarea, .msg-input textarea",
21
- sendButton: ".btn-send, .send-btn",
22
- messageItem: ".chat-msg, .msg-bubble",
23
- messageText: ".chat-msg .text, .msg-bubble .text",
24
- },
25
- };
26
- //# sourceMappingURL=selectors.js.map
1
+ export const YUPAO_SELECTORS={login:{qrCode:".login-qr img, .qr-code img",loginSuccess:".user-info, .header-user"},messageList:{container:".chat-list, .msg-list",item:".chat-item, .msg-item",candidateName:".chat-item .name, .msg-item .name",lastMessage:".chat-item .msg, .msg-item .content",unreadBadge:".chat-item .unread, .msg-item .badge",timestamp:".chat-item .time, .msg-item .time"},chat:{input:".chat-input textarea, .msg-input textarea",sendButton:".btn-send, .send-btn",messageItem:".chat-msg, .msg-bubble",messageText:".chat-msg .text, .msg-bubble .text"}};
@@ -15,4 +15,3 @@ export declare function performInitialScrollPattern(page: Page): Promise<void>;
15
15
  export declare function generateTypingDelay(): number;
16
16
  /** 概率性触发随机行为 */
17
17
  export declare function shouldAddRandomBehavior(probability?: number): boolean;
18
- //# sourceMappingURL=anti-detection.d.ts.map
@@ -1,65 +1 @@
1
- /** 随机延迟 */
2
- export async function randomDelay(page, min = 300, max = 800) {
3
- const ms = Math.floor(Math.random() * (max - min)) + min;
4
- await page.waitForTimeout(ms);
5
- }
6
- /** 模拟人类操作延迟(概率分布) */
7
- export async function humanDelay(page) {
8
- const rand = Math.random();
9
- let ms;
10
- if (rand < 0.5) {
11
- ms = 800 + Math.random() * 1200;
12
- }
13
- else if (rand < 0.8) {
14
- ms = 500 + Math.random() * 300;
15
- }
16
- else if (rand < 0.95) {
17
- ms = 2000 + Math.random() * 2000;
18
- }
19
- else {
20
- ms = 4000 + Math.random() * 2000;
21
- }
22
- await page.waitForTimeout(Math.floor(ms));
23
- }
24
- /** 模拟随机滚动 */
25
- export async function performRandomScroll(page, options) {
26
- const min = options?.minDistance ?? 50;
27
- const max = options?.maxDistance ?? 200;
28
- const direction = options?.direction ?? "both";
29
- const distance = Math.floor(Math.random() * (max - min)) + min;
30
- const sign = direction === "up" ? -1 : direction === "down" ? 1 : Math.random() > 0.5 ? 1 : -1;
31
- await page.evaluate((scrollY) => {
32
- window.scrollBy({ top: scrollY, behavior: "smooth" });
33
- }, distance * sign);
34
- await randomDelay(page, 200, 500);
35
- }
36
- /** 初始浏览模式滚动(模拟用户进入页面时的行为) */
37
- export async function performInitialScrollPattern(page) {
38
- if (Math.random() < 0.8) {
39
- const distance = 100 + Math.floor(Math.random() * 100);
40
- await page.evaluate((d) => {
41
- window.scrollBy({ top: d, behavior: "smooth" });
42
- }, distance);
43
- await humanDelay(page);
44
- }
45
- if (Math.random() < 0.5) {
46
- const distance = (50 + Math.floor(Math.random() * 100)) * (Math.random() > 0.5 ? 1 : -1);
47
- await page.evaluate((d) => {
48
- window.scrollBy({ top: d, behavior: "smooth" });
49
- }, distance);
50
- }
51
- }
52
- /** 生成打字延迟(模拟键盘输入速度) */
53
- export function generateTypingDelay() {
54
- let base = 60 + Math.random() * 60;
55
- if (Math.random() < 0.1)
56
- base += 200 + Math.random() * 100;
57
- if (Math.random() < 0.05)
58
- base = 30 + Math.random() * 30;
59
- return Math.floor(base);
60
- }
61
- /** 概率性触发随机行为 */
62
- export function shouldAddRandomBehavior(probability = 0.3) {
63
- return Math.random() < probability;
64
- }
65
- //# sourceMappingURL=anti-detection.js.map
1
+ export async function randomDelay(a,o=300,t=800){const n=Math.floor(Math.random()*(t-o))+o;await a.waitForTimeout(n)}export async function humanDelay(a){const o=Math.random();let t;t=o<.5?800+1200*Math.random():o<.8?500+300*Math.random():o<.95?2e3+2e3*Math.random():4e3+2e3*Math.random(),await a.waitForTimeout(Math.floor(t))}export async function performRandomScroll(a,o){const t=o?.minDistance??50,n=o?.maxDistance??200,r=o?.direction??"both",e=Math.floor(Math.random()*(n-t))+t,h="up"===r?-1:"down"===r||Math.random()>.5?1:-1;await a.evaluate(a=>{window.scrollBy({top:a,behavior:"smooth"})},e*h),await randomDelay(a,200,500)}export async function performInitialScrollPattern(a){if(Math.random()<.8){const o=100+Math.floor(100*Math.random());await a.evaluate(a=>{window.scrollBy({top:a,behavior:"smooth"})},o),await humanDelay(a)}if(Math.random()<.5){const o=(50+Math.floor(100*Math.random()))*(Math.random()>.5?1:-1);await a.evaluate(a=>{window.scrollBy({top:a,behavior:"smooth"})},o)}}export function generateTypingDelay(){let a=60+60*Math.random();return Math.random()<.1&&(a+=200+100*Math.random()),Math.random()<.05&&(a=30+30*Math.random()),Math.floor(a)}export function shouldAddRandomBehavior(a=.3){return Math.random()<a}
@@ -25,4 +25,3 @@ export declare function getChatCandidates(page: Page): Promise<ReadonlyArray<Cha
25
25
  * - 都没有 → 不做任何导航,假设当前窗口已就绪
26
26
  */
27
27
  export declare function ensureChatOpen(page: Page, target: ChatTarget): Promise<OpenChatResult | undefined>;
28
- //# sourceMappingURL=chat-navigation.d.ts.map
@@ -1,158 +1 @@
1
- import { randomDelay } from "./anti-detection.js";
2
- function normalizeCandidateName(name) {
3
- return name.trim().toLocaleLowerCase("zh-CN");
4
- }
5
- function countMatchedCharacters(left, right) {
6
- let matched = 0;
7
- for (const char of left) {
8
- if (right.includes(char))
9
- matched += 1;
10
- }
11
- return matched;
12
- }
13
- export function selectChatCandidate(candidates, target) {
14
- if (target.index !== undefined) {
15
- return candidates[target.index];
16
- }
17
- const rawName = target.candidateName;
18
- if (!rawName) {
19
- return undefined;
20
- }
21
- const expectedName = normalizeCandidateName(rawName);
22
- const namedCandidates = candidates.filter((candidate) => candidate.name.length > 0);
23
- let selected = namedCandidates.find((candidate) => normalizeCandidateName(candidate.name) === expectedName);
24
- if (selected)
25
- return selected;
26
- selected = namedCandidates.find((candidate) => {
27
- const actualName = normalizeCandidateName(candidate.name);
28
- return actualName.includes(expectedName) || expectedName.includes(actualName);
29
- });
30
- if (selected)
31
- return selected;
32
- const requiredRatio = expectedName.length <= 2 ? 1 : expectedName.length <= 4 ? 0.75 : 0.6;
33
- return namedCandidates.find((candidate) => {
34
- const actualName = normalizeCandidateName(candidate.name);
35
- const matched = countMatchedCharacters(expectedName, actualName);
36
- return matched >= Math.ceil(Math.min(expectedName.length, actualName.length) * requiredRatio);
37
- });
38
- }
39
- export async function ensureChatListLoaded(page) {
40
- if (!page.url().includes("/web/geek/chat") && !page.url().includes("/web/chat")) {
41
- await page.goto("https://www.zhipin.com/web/geek/chat", { waitUntil: "domcontentloaded" });
42
- }
43
- try {
44
- await page.waitForSelector(".geek-item", { timeout: 10_000 });
45
- return true;
46
- }
47
- catch {
48
- return false;
49
- }
50
- }
51
- export async function getChatCandidates(page) {
52
- return page.evaluate(() => {
53
- const items = Array.from(document.querySelectorAll(".geek-item"));
54
- return items.map((item, idx) => {
55
- const nameEl = item.querySelector('[class*="name"], .nickname, .geek-name, .candidate-name');
56
- const name = nameEl?.textContent?.trim() ?? "";
57
- const badgeEl = item.querySelector(".badge-count");
58
- const unreadCount = parseInt(badgeEl?.textContent?.trim() ?? "0", 10) || 0;
59
- const hasUnread = unreadCount > 0 || item.querySelector(".red-dot") !== null;
60
- const lastMessageTime = item.querySelector(".time, .time-shadow")?.textContent?.trim() ?? "";
61
- const messagePreview = (item.querySelector(".push-text, .chat-last-msg")?.textContent?.trim() ?? "").slice(0, 100);
62
- return {
63
- name,
64
- index: idx,
65
- hasUnread,
66
- unreadCount,
67
- lastMessageTime,
68
- messagePreview,
69
- };
70
- });
71
- });
72
- }
73
- async function clickChatItem(page, index) {
74
- return page.evaluate((targetIndex) => {
75
- const items = Array.from(document.querySelectorAll(".geek-item"));
76
- const target = items[targetIndex];
77
- if (!target)
78
- return false;
79
- const clickArea = target.querySelector(".chat-item-content") ?? target;
80
- clickArea.click();
81
- return true;
82
- }, index);
83
- }
84
- async function waitForChatReady(page, candidateName) {
85
- if (candidateName.length === 0) {
86
- await randomDelay(page, 500, 900);
87
- return;
88
- }
89
- const expectedName = normalizeCandidateName(candidateName);
90
- try {
91
- await page.waitForFunction((name) => {
92
- const selectors = [".name-box", ".geek-name", ".base-name", ".chat-user-name"];
93
- for (const selector of selectors) {
94
- const headerText = document.querySelector(selector)?.textContent?.trim();
95
- if (!headerText)
96
- continue;
97
- const normalized = headerText.trim().toLocaleLowerCase("zh-CN");
98
- if (normalized.includes(name) || name.includes(normalized)) {
99
- return true;
100
- }
101
- }
102
- return false;
103
- }, expectedName, { timeout: 5_000 });
104
- }
105
- catch {
106
- await randomDelay(page, 800, 1_200);
107
- }
108
- }
109
- /**
110
- * 确保指定候选人的聊天窗口已打开。
111
- *
112
- * - 有 candidateName/index → 导航到聊天列表 → 选择候选人 → 等待聊天头部切换
113
- * - 都没有 → 不做任何导航,假设当前窗口已就绪
114
- */
115
- export async function ensureChatOpen(page, target) {
116
- if (target.candidateName === undefined && target.index === undefined) {
117
- return undefined;
118
- }
119
- const listReady = await ensureChatListLoaded(page);
120
- if (!listReady) {
121
- return {
122
- found: false,
123
- name: "",
124
- index: -1,
125
- hasUnread: false,
126
- unreadCount: 0,
127
- lastMessageTime: "",
128
- messagePreview: "",
129
- error: "消息列表未加载",
130
- };
131
- }
132
- const candidates = await getChatCandidates(page);
133
- const selected = selectChatCandidate(candidates, target);
134
- if (!selected) {
135
- const who = target.candidateName ?? `index ${target.index}`;
136
- return {
137
- found: false,
138
- name: "",
139
- index: -1,
140
- hasUnread: false,
141
- unreadCount: 0,
142
- lastMessageTime: "",
143
- messagePreview: "",
144
- error: `未找到候选人: ${who}`,
145
- };
146
- }
147
- const clicked = await clickChatItem(page, selected.index);
148
- if (!clicked) {
149
- return {
150
- ...selected,
151
- found: false,
152
- error: `打开候选人聊天失败: ${selected.name || `index ${selected.index}`}`,
153
- };
154
- }
155
- await waitForChatReady(page, selected.name);
156
- return { ...selected, found: true };
157
- }
158
- //# sourceMappingURL=chat-navigation.js.map
1
+ import{randomDelay as e}from"./anti-detection.js";function t(e){return e.trim().toLocaleLowerCase("zh-CN")}function n(e,t){let n=0;for(const r of e)t.includes(r)&&(n+=1);return n}export function selectChatCandidate(e,r){if(void 0!==r.index)return e[r.index];const a=r.candidateName;if(!a)return;const i=t(a),o=e.filter(e=>e.name.length>0);let c=o.find(e=>t(e.name)===i);if(c)return c;if(c=o.find(e=>{const n=t(e.name);return n.includes(i)||i.includes(n)}),c)return c;const u=i.length<=2?1:i.length<=4?.75:.6;return o.find(e=>{const r=t(e.name);return n(i,r)>=Math.ceil(Math.min(i.length,r.length)*u)})}export async function ensureChatListLoaded(e){e.url().includes("/web/geek/chat")||e.url().includes("/web/chat")||await e.goto("https://www.zhipin.com/web/geek/chat",{waitUntil:"domcontentloaded"});try{return await e.waitForSelector(".geek-item",{timeout:1e4}),!0}catch{return!1}}export async function getChatCandidates(e){return e.evaluate(()=>Array.from(document.querySelectorAll(".geek-item")).map((e,t)=>{const n=e.querySelector('[class*="name"], .nickname, .geek-name, .candidate-name'),r=n?.textContent?.trim()??"",a=e.querySelector(".badge-count"),i=parseInt(a?.textContent?.trim()??"0",10)||0;return{name:r,index:t,hasUnread:i>0||null!==e.querySelector(".red-dot"),unreadCount:i,lastMessageTime:e.querySelector(".time, .time-shadow")?.textContent?.trim()??"",messagePreview:(e.querySelector(".push-text, .chat-last-msg")?.textContent?.trim()??"").slice(0,100)}}))}async function r(e,t){return e.evaluate(e=>{const t=Array.from(document.querySelectorAll(".geek-item"))[e];if(!t)return!1;return(t.querySelector(".chat-item-content")??t).click(),!0},t)}async function a(n,r){if(0===r.length)return void await e(n,500,900);const a=t(r);try{await n.waitForFunction(e=>{const t=[".name-box",".geek-name",".base-name",".chat-user-name"];for(const n of t){const t=document.querySelector(n)?.textContent?.trim();if(!t)continue;const r=t.trim().toLocaleLowerCase("zh-CN");if(r.includes(e)||e.includes(r))return!0}return!1},a,{timeout:5e3})}catch{await e(n,800,1200)}}export async function ensureChatOpen(e,t){if(void 0===t.candidateName&&void 0===t.index)return;if(!await ensureChatListLoaded(e))return{found:!1,name:"",index:-1,hasUnread:!1,unreadCount:0,lastMessageTime:"",messagePreview:"",error:"消息列表未加载"};const n=selectChatCandidate(await getChatCandidates(e),t);if(!n){return{found:!1,name:"",index:-1,hasUnread:!1,unreadCount:0,lastMessageTime:"",messagePreview:"",error:`未找到候选人: ${t.candidateName??`index ${t.index}`}`}}return await r(e,n.index)?(await a(e,n.name),{...n,found:!0}):{...n,found:!1,error:`打开候选人聊天失败: ${n.name||`index ${n.index}`}`}}
@@ -125,4 +125,3 @@ export declare const TIMING: {
125
125
  readonly retryDelay: 1000;
126
126
  readonly pollInterval: 500;
127
127
  };
128
- //# sourceMappingURL=selectors.d.ts.map