@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.
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -111
- package/dist/pages/platform-page.d.ts +0 -1
- package/dist/pages/platform-page.js +1 -17
- package/dist/pages/yupao/chat.d.ts +0 -1
- package/dist/pages/yupao/chat.js +1 -18
- package/dist/pages/yupao/message-list.d.ts +0 -1
- package/dist/pages/yupao/message-list.js +1 -36
- package/dist/pages/yupao/navigation.d.ts +0 -1
- package/dist/pages/yupao/navigation.js +1 -34
- package/dist/pages/yupao/selectors.d.ts +0 -1
- package/dist/pages/yupao/selectors.js +1 -26
- package/dist/pages/zhipin/anti-detection.d.ts +0 -1
- package/dist/pages/zhipin/anti-detection.js +1 -65
- package/dist/pages/zhipin/chat-navigation.d.ts +0 -1
- package/dist/pages/zhipin/chat-navigation.js +1 -158
- package/dist/pages/zhipin/selectors.d.ts +0 -1
- package/dist/pages/zhipin/selectors.js +1 -141
- package/dist/pages/zhipin/username.d.ts +0 -1
- package/dist/pages/zhipin/username.js +1 -302
- package/dist/platforms.d.ts +0 -1
- package/dist/platforms.js +1 -20
- package/dist/runtime-holder.d.ts +0 -1
- package/dist/runtime-holder.js +1 -50
- package/dist/tools/browser-status.d.ts +0 -1
- package/dist/tools/browser-status.js +1 -49
- package/dist/tools/list-pages.d.ts +0 -1
- package/dist/tools/list-pages.js +1 -40
- package/dist/tools/navigate-active-tab.d.ts +0 -1
- package/dist/tools/navigate-active-tab.js +1 -49
- package/dist/tools/open-platform.d.ts +0 -1
- package/dist/tools/open-platform.js +1 -33
- package/dist/tools/select-page.d.ts +0 -1
- package/dist/tools/select-page.js +1 -38
- package/dist/tools/yupao-read-messages.d.ts +0 -1
- package/dist/tools/yupao-read-messages.js +1 -36
- package/dist/tools/yupao-send-reply.d.ts +0 -1
- package/dist/tools/yupao-send-reply.js +1 -40
- package/dist/tools/zhipin-close-resume.d.ts +0 -1
- package/dist/tools/zhipin-close-resume.js +1 -73
- package/dist/tools/zhipin-exchange-wechat.d.ts +0 -1
- package/dist/tools/zhipin-exchange-wechat.js +1 -218
- package/dist/tools/zhipin-get-candidate-info.d.ts +0 -1
- package/dist/tools/zhipin-get-candidate-info.js +1 -247
- package/dist/tools/zhipin-get-candidate-list.d.ts +0 -1
- package/dist/tools/zhipin-get-candidate-list.js +1 -175
- package/dist/tools/zhipin-get-username.d.ts +0 -1
- package/dist/tools/zhipin-get-username.js +1 -58
- package/dist/tools/zhipin-locate-resume-canvas.d.ts +0 -1
- package/dist/tools/zhipin-locate-resume-canvas.js +1 -90
- package/dist/tools/zhipin-open-chat.d.ts +0 -1
- package/dist/tools/zhipin-open-chat.js +1 -82
- package/dist/tools/zhipin-open-resume.d.ts +0 -1
- package/dist/tools/zhipin-open-resume.js +1 -51
- package/dist/tools/zhipin-read-messages.d.ts +0 -1
- package/dist/tools/zhipin-read-messages.js +1 -90
- package/dist/tools/zhipin-say-hello.d.ts +0 -1
- package/dist/tools/zhipin-say-hello.js +1 -118
- package/dist/tools/zhipin-send-reply.d.ts +0 -1
- package/dist/tools/zhipin-send-reply.js +1 -99
- package/package.json +4 -4
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/pages/platform-page.d.ts.map +0 -1
- package/dist/pages/platform-page.js.map +0 -1
- package/dist/pages/yupao/chat.d.ts.map +0 -1
- package/dist/pages/yupao/chat.js.map +0 -1
- package/dist/pages/yupao/message-list.d.ts.map +0 -1
- package/dist/pages/yupao/message-list.js.map +0 -1
- package/dist/pages/yupao/navigation.d.ts.map +0 -1
- package/dist/pages/yupao/navigation.js.map +0 -1
- package/dist/pages/yupao/selectors.d.ts.map +0 -1
- package/dist/pages/yupao/selectors.js.map +0 -1
- package/dist/pages/zhipin/anti-detection.d.ts.map +0 -1
- package/dist/pages/zhipin/anti-detection.js.map +0 -1
- package/dist/pages/zhipin/chat-navigation.d.ts.map +0 -1
- package/dist/pages/zhipin/chat-navigation.js.map +0 -1
- package/dist/pages/zhipin/selectors.d.ts.map +0 -1
- package/dist/pages/zhipin/selectors.js.map +0 -1
- package/dist/pages/zhipin/username.d.ts.map +0 -1
- package/dist/pages/zhipin/username.js.map +0 -1
- package/dist/platforms.d.ts.map +0 -1
- package/dist/platforms.js.map +0 -1
- package/dist/runtime-holder.d.ts.map +0 -1
- package/dist/runtime-holder.js.map +0 -1
- package/dist/tools/browser-status.d.ts.map +0 -1
- package/dist/tools/browser-status.js.map +0 -1
- package/dist/tools/list-pages.d.ts.map +0 -1
- package/dist/tools/list-pages.js.map +0 -1
- package/dist/tools/navigate-active-tab.d.ts.map +0 -1
- package/dist/tools/navigate-active-tab.js.map +0 -1
- package/dist/tools/open-platform.d.ts.map +0 -1
- package/dist/tools/open-platform.js.map +0 -1
- package/dist/tools/select-page.d.ts.map +0 -1
- package/dist/tools/select-page.js.map +0 -1
- package/dist/tools/yupao-read-messages.d.ts.map +0 -1
- package/dist/tools/yupao-read-messages.js.map +0 -1
- package/dist/tools/yupao-send-reply.d.ts.map +0 -1
- package/dist/tools/yupao-send-reply.js.map +0 -1
- package/dist/tools/zhipin-close-resume.d.ts.map +0 -1
- package/dist/tools/zhipin-close-resume.js.map +0 -1
- package/dist/tools/zhipin-exchange-wechat.d.ts.map +0 -1
- package/dist/tools/zhipin-exchange-wechat.js.map +0 -1
- package/dist/tools/zhipin-get-candidate-info.d.ts.map +0 -1
- package/dist/tools/zhipin-get-candidate-info.js.map +0 -1
- package/dist/tools/zhipin-get-candidate-list.d.ts.map +0 -1
- package/dist/tools/zhipin-get-candidate-list.js.map +0 -1
- package/dist/tools/zhipin-get-username.d.ts.map +0 -1
- package/dist/tools/zhipin-get-username.js.map +0 -1
- package/dist/tools/zhipin-locate-resume-canvas.d.ts.map +0 -1
- package/dist/tools/zhipin-locate-resume-canvas.js.map +0 -1
- package/dist/tools/zhipin-open-chat.d.ts.map +0 -1
- package/dist/tools/zhipin-open-chat.js.map +0 -1
- package/dist/tools/zhipin-open-resume.d.ts.map +0 -1
- package/dist/tools/zhipin-open-resume.js.map +0 -1
- package/dist/tools/zhipin-read-messages.d.ts.map +0 -1
- package/dist/tools/zhipin-read-messages.js.map +0 -1
- package/dist/tools/zhipin-say-hello.d.ts.map +0 -1
- package/dist/tools/zhipin-say-hello.js.map +0 -1
- package/dist/tools/zhipin-send-reply.d.ts.map +0 -1
- package/dist/tools/zhipin-send-reply.js.map +0 -1
|
@@ -1,49 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { BrowserPageInfoSchema } from "@roll-agent/browser";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { getContextManager } from "../runtime-holder.js";
|
|
5
|
-
import { detectPlatformFromUrl } from "../platforms.js";
|
|
6
|
-
const NavigateActiveTabInputSchema = z.object({
|
|
7
|
-
url: z.string().url().describe("要导航到的目标 URL"),
|
|
8
|
-
});
|
|
9
|
-
const NavigateActiveTabOutputSchema = z.object({
|
|
10
|
-
success: z.boolean(),
|
|
11
|
-
page: BrowserPageInfoSchema,
|
|
12
|
-
});
|
|
13
|
-
export const navigateActiveTab = defineTool({
|
|
14
|
-
name: "navigate_active_tab",
|
|
15
|
-
description: "将当前激活的浏览器 tab 导航到指定 URL;若 URL 属于已知平台,会自动绑定该平台当前活跃页。",
|
|
16
|
-
input: NavigateActiveTabInputSchema,
|
|
17
|
-
output: NavigateActiveTabOutputSchema,
|
|
18
|
-
execute: async (input, ctx) => {
|
|
19
|
-
const ctxManager = getContextManager();
|
|
20
|
-
ctx.logger.info(`Navigating active tab to ${input.url}`);
|
|
21
|
-
const page = await ctxManager.getActivePage();
|
|
22
|
-
if (!page) {
|
|
23
|
-
throw new Error("No active browser tab detected. Use open_platform or select_page first.");
|
|
24
|
-
}
|
|
25
|
-
await page.bringToFront().catch(() => { });
|
|
26
|
-
await page.goto(input.url, { waitUntil: "domcontentloaded" });
|
|
27
|
-
const detectedPlatform = detectPlatformFromUrl(page.url());
|
|
28
|
-
if (detectedPlatform) {
|
|
29
|
-
await ctxManager.selectPage(detectedPlatform, ctxManager.getPageId(page));
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
ctxManager.clearBindingForPage(page);
|
|
33
|
-
}
|
|
34
|
-
const url = page.url();
|
|
35
|
-
const title = await page.title().catch(() => "");
|
|
36
|
-
return {
|
|
37
|
-
success: true,
|
|
38
|
-
page: {
|
|
39
|
-
pageId: ctxManager.getPageId(page),
|
|
40
|
-
url,
|
|
41
|
-
title,
|
|
42
|
-
boundPlatform: ctxManager.getBoundPlatformForPage(page) ?? null,
|
|
43
|
-
detectedPlatform: detectPlatformFromUrl(url) ?? null,
|
|
44
|
-
isSelectedForPlatform: ctxManager.isSelectedPageForPlatform(page),
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
//# sourceMappingURL=navigate-active-tab.js.map
|
|
1
|
+
import{defineTool as t}from"@roll-agent/sdk";import{BrowserPageInfoSchema as e}from"@roll-agent/browser";import{z as o}from"zod";import{getContextManager as a}from"../runtime-holder.js";import{detectPlatformFromUrl as r}from"../platforms.js";const i=o.object({url:o.string().url().describe("要导航到的目标 URL")}),l=o.object({success:o.boolean(),page:e});export const navigateActiveTab=t({name:"navigate_active_tab",description:"将当前激活的浏览器 tab 导航到指定 URL;若 URL 属于已知平台,会自动绑定该平台当前活跃页。",input:i,output:l,execute:async(t,e)=>{const o=a();e.logger.info(`Navigating active tab to ${t.url}`);const i=await o.getActivePage();if(!i)throw new Error("No active browser tab detected. Use open_platform or select_page first.");await i.bringToFront().catch(()=>{}),await i.goto(t.url,{waitUntil:"domcontentloaded"});const l=r(i.url());l?await o.selectPage(l,o.getPageId(i)):o.clearBindingForPage(i);const c=i.url(),n=await i.title().catch(()=>"");return{success:!0,page:{pageId:o.getPageId(i),url:c,title:n,boundPlatform:o.getBoundPlatformForPage(i)??null,detectedPlatform:r(c)??null,isSelectedForPlatform:o.isSelectedPageForPlatform(i)}}}});
|
|
@@ -1,33 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { PlatformSchema } from "@roll-agent/browser";
|
|
4
|
-
import { getContextManager } from "../runtime-holder.js";
|
|
5
|
-
import { ensurePlatformHomePage } from "../pages/platform-page.js";
|
|
6
|
-
const OpenPlatformInputSchema = z.object({
|
|
7
|
-
platform: PlatformSchema.describe("目标平台:`zhipin` 代表 BOSS直聘,`yupao` 代表鱼泡"),
|
|
8
|
-
});
|
|
9
|
-
const OpenPlatformOutputSchema = z.object({
|
|
10
|
-
success: z.boolean(),
|
|
11
|
-
platform: PlatformSchema,
|
|
12
|
-
url: z.string(),
|
|
13
|
-
reusedExistingTab: z.boolean(),
|
|
14
|
-
});
|
|
15
|
-
export const openPlatform = defineTool({
|
|
16
|
-
name: "open_platform",
|
|
17
|
-
description: "打开并聚焦招聘平台主页,供用户手动登录或后续执行站内操作。",
|
|
18
|
-
input: OpenPlatformInputSchema,
|
|
19
|
-
output: OpenPlatformOutputSchema,
|
|
20
|
-
execute: async (input, ctx) => {
|
|
21
|
-
const { platform } = input;
|
|
22
|
-
const ctxManager = getContextManager();
|
|
23
|
-
ctx.logger.info(`Opening platform page for ${platform}`);
|
|
24
|
-
const { page, reusedExistingPage } = await ensurePlatformHomePage(ctxManager, platform);
|
|
25
|
-
return {
|
|
26
|
-
success: true,
|
|
27
|
-
platform,
|
|
28
|
-
url: page.url(),
|
|
29
|
-
reusedExistingTab: reusedExistingPage,
|
|
30
|
-
};
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
//# sourceMappingURL=open-platform.js.map
|
|
1
|
+
import{defineTool as o}from"@roll-agent/sdk";import{z as r}from"zod";import{PlatformSchema as e}from"@roll-agent/browser";import{getContextManager as t}from"../runtime-holder.js";import{ensurePlatformHomePage as p}from"../pages/platform-page.js";const s=r.object({platform:e.describe("目标平台:`zhipin` 代表 BOSS直聘,`yupao` 代表鱼泡")}),a=r.object({success:r.boolean(),platform:e,url:r.string(),reusedExistingTab:r.boolean()});export const openPlatform=o({name:"open_platform",description:"打开并聚焦招聘平台主页,供用户手动登录或后续执行站内操作。",input:s,output:a,execute:async(o,r)=>{const{platform:e}=o,s=t();r.logger.info(`Opening platform page for ${e}`);const{page:a,reusedExistingPage:n}=await p(s,e);return{success:!0,platform:e,url:a.url(),reusedExistingTab:n}}});
|
|
@@ -1,38 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { BrowserPageInfoSchema, PlatformSchema } from "@roll-agent/browser";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { getContextManager } from "../runtime-holder.js";
|
|
5
|
-
import { detectPlatformFromUrl } from "../platforms.js";
|
|
6
|
-
const SelectPageInputSchema = z.object({
|
|
7
|
-
platform: PlatformSchema.describe("要将该页面绑定为当前活跃页的平台"),
|
|
8
|
-
pageId: z.string().describe("通过 list_pages 返回的 pageId"),
|
|
9
|
-
});
|
|
10
|
-
const SelectPageOutputSchema = z.object({
|
|
11
|
-
success: z.boolean(),
|
|
12
|
-
page: BrowserPageInfoSchema,
|
|
13
|
-
});
|
|
14
|
-
export const selectPage = defineTool({
|
|
15
|
-
name: "select_page",
|
|
16
|
-
description: "将指定 pageId 绑定为平台当前活跃页,并切换到前台。",
|
|
17
|
-
input: SelectPageInputSchema,
|
|
18
|
-
output: SelectPageOutputSchema,
|
|
19
|
-
execute: async (input, ctx) => {
|
|
20
|
-
const ctxManager = getContextManager();
|
|
21
|
-
ctx.logger.info(`Selecting page ${input.pageId} for ${input.platform}`);
|
|
22
|
-
const page = await ctxManager.selectPage(input.platform, input.pageId);
|
|
23
|
-
const url = page.url();
|
|
24
|
-
const title = await page.title().catch(() => "");
|
|
25
|
-
return {
|
|
26
|
-
success: true,
|
|
27
|
-
page: {
|
|
28
|
-
pageId: ctxManager.getPageId(page),
|
|
29
|
-
url,
|
|
30
|
-
title,
|
|
31
|
-
boundPlatform: ctxManager.getBoundPlatformForPage(page) ?? null,
|
|
32
|
-
detectedPlatform: detectPlatformFromUrl(url) ?? null,
|
|
33
|
-
isSelectedForPlatform: ctxManager.isSelectedPageForPlatform(page),
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
//# sourceMappingURL=select-page.js.map
|
|
1
|
+
import{defineTool as e}from"@roll-agent/sdk";import{BrowserPageInfoSchema as t,PlatformSchema as o}from"@roll-agent/browser";import{z as r}from"zod";import{getContextManager as a}from"../runtime-holder.js";import{detectPlatformFromUrl as l}from"../platforms.js";const s=r.object({platform:o.describe("要将该页面绑定为当前活跃页的平台"),pageId:r.string().describe("通过 list_pages 返回的 pageId")}),c=r.object({success:r.boolean(),page:t});export const selectPage=e({name:"select_page",description:"将指定 pageId 绑定为平台当前活跃页,并切换到前台。",input:s,output:c,execute:async(e,t)=>{const o=a();t.logger.info(`Selecting page ${e.pageId} for ${e.platform}`);const r=await o.selectPage(e.platform,e.pageId),s=r.url(),c=await r.title().catch(()=>"");return{success:!0,page:{pageId:o.getPageId(r),url:s,title:c,boundPlatform:o.getBoundPlatformForPage(r)??null,detectedPlatform:l(s)??null,isSelectedForPlatform:o.isSelectedPageForPlatform(r)}}}});
|
|
@@ -1,36 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { getContextManager } from "../runtime-holder.js";
|
|
4
|
-
import { parseMessageList } from "../pages/yupao/message-list.js";
|
|
5
|
-
const ReadMessagesInputSchema = z.object({
|
|
6
|
-
limit: z.number().optional().describe("最多返回的消息条数"),
|
|
7
|
-
});
|
|
8
|
-
const MessageItemSchema = z.object({
|
|
9
|
-
conversationId: z.string(),
|
|
10
|
-
candidateName: z.string(),
|
|
11
|
-
lastMessage: z.string(),
|
|
12
|
-
unreadCount: z.number(),
|
|
13
|
-
timestamp: z.string(),
|
|
14
|
-
});
|
|
15
|
-
const ReadMessagesOutputSchema = z.object({
|
|
16
|
-
messages: z.array(MessageItemSchema),
|
|
17
|
-
total: z.number(),
|
|
18
|
-
});
|
|
19
|
-
export const yupaoReadMessages = defineTool({
|
|
20
|
-
name: "yupao_read_messages",
|
|
21
|
-
description: "读取鱼泡未读消息列表",
|
|
22
|
-
input: ReadMessagesInputSchema,
|
|
23
|
-
output: ReadMessagesOutputSchema,
|
|
24
|
-
execute: async (input, ctx) => {
|
|
25
|
-
ctx.logger.info(`Reading yupao messages (limit: ${input.limit ?? "all"})`);
|
|
26
|
-
const ctxManager = getContextManager();
|
|
27
|
-
const page = await ctxManager.getPage("yupao");
|
|
28
|
-
const messages = await parseMessageList(page, input.limit);
|
|
29
|
-
ctx.logger.info(`Found ${messages.length} messages`);
|
|
30
|
-
return {
|
|
31
|
-
messages: messages.map((m) => ({ ...m })),
|
|
32
|
-
total: messages.length,
|
|
33
|
-
};
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
//# sourceMappingURL=yupao-read-messages.js.map
|
|
1
|
+
import{defineTool as e}from"@roll-agent/sdk";import{z as t}from"zod";import{getContextManager as s}from"../runtime-holder.js";import{parseMessageList as a}from"../pages/yupao/message-list.js";const o=t.object({limit:t.number().optional().describe("最多返回的消息条数")}),i=t.object({conversationId:t.string(),candidateName:t.string(),lastMessage:t.string(),unreadCount:t.number(),timestamp:t.string()}),r=t.object({messages:t.array(i),total:t.number()});export const yupaoReadMessages=e({name:"yupao_read_messages",description:"读取鱼泡未读消息列表",input:o,output:r,execute:async(e,t)=>{t.logger.info(`Reading yupao messages (limit: ${e.limit??"all"})`);const o=s(),i=await o.getPage("yupao"),r=await a(i,e.limit);return t.logger.info(`Found ${r.length} messages`),{messages:r.map(e=>({...e})),total:r.length}}});
|
|
@@ -1,40 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { getContextManager } from "../runtime-holder.js";
|
|
4
|
-
import { sendReply } from "../pages/yupao/chat.js";
|
|
5
|
-
const SendReplyInputSchema = z.object({
|
|
6
|
-
conversationId: z.string().describe("对话 ID"),
|
|
7
|
-
message: z.string().describe("要发送的回复消息"),
|
|
8
|
-
});
|
|
9
|
-
const SendReplyOutputSchema = z.object({
|
|
10
|
-
success: z.boolean(),
|
|
11
|
-
conversationId: z.string(),
|
|
12
|
-
sentMessage: z.string(),
|
|
13
|
-
error: z.string().optional(),
|
|
14
|
-
});
|
|
15
|
-
export const yupaoSendReply = defineTool({
|
|
16
|
-
name: "yupao_send_reply",
|
|
17
|
-
description: "向鱼泡指定对话发送回复消息",
|
|
18
|
-
input: SendReplyInputSchema,
|
|
19
|
-
output: SendReplyOutputSchema,
|
|
20
|
-
execute: async (input, ctx) => {
|
|
21
|
-
const { conversationId, message } = input;
|
|
22
|
-
ctx.logger.info(`Sending reply to yupao conversation ${conversationId}`);
|
|
23
|
-
const ctxManager = getContextManager();
|
|
24
|
-
const page = await ctxManager.getPage("yupao");
|
|
25
|
-
const result = await sendReply(page, conversationId, message);
|
|
26
|
-
if (result.success) {
|
|
27
|
-
ctx.logger.info("Reply sent successfully");
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
ctx.logger.error(`Failed to send reply: ${result.error}`);
|
|
31
|
-
}
|
|
32
|
-
return {
|
|
33
|
-
success: result.success,
|
|
34
|
-
conversationId,
|
|
35
|
-
sentMessage: message,
|
|
36
|
-
error: result.error,
|
|
37
|
-
};
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
//# sourceMappingURL=yupao-send-reply.js.map
|
|
1
|
+
import{defineTool as e}from"@roll-agent/sdk";import{z as o}from"zod";import{getContextManager as s}from"../runtime-holder.js";import{sendReply as r}from"../pages/yupao/chat.js";const t=o.object({conversationId:o.string().describe("对话 ID"),message:o.string().describe("要发送的回复消息")}),n=o.object({success:o.boolean(),conversationId:o.string(),sentMessage:o.string(),error:o.string().optional()});export const yupaoSendReply=e({name:"yupao_send_reply",description:"向鱼泡指定对话发送回复消息",input:t,output:n,execute:async(e,o)=>{const{conversationId:t,message:n}=e;o.logger.info(`Sending reply to yupao conversation ${t}`);const a=s(),c=await a.getPage("yupao"),i=await r(c,t,n);return i.success?o.logger.info("Reply sent successfully"):o.logger.error(`Failed to send reply: ${i.error}`),{success:i.success,conversationId:t,sentMessage:n,error:i.error}}});
|
|
@@ -1,73 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { getContextManager } from "../runtime-holder.js";
|
|
4
|
-
const OutputSchema = z.object({
|
|
5
|
-
success: z.boolean(),
|
|
6
|
-
closed: z.boolean(),
|
|
7
|
-
error: z.string().optional(),
|
|
8
|
-
});
|
|
9
|
-
// 旧代码使用的关闭按钮选择器(boss-popup__close 系列)
|
|
10
|
-
const CLOSE_SELECTORS_IFRAME = [
|
|
11
|
-
".recommendV2 .boss-popup__close",
|
|
12
|
-
".dialog-lib-resume .boss-popup__close",
|
|
13
|
-
".boss-dialog .boss-popup__close",
|
|
14
|
-
".boss-popup__close",
|
|
15
|
-
".close-btn",
|
|
16
|
-
".dialog-close",
|
|
17
|
-
];
|
|
18
|
-
const CLOSE_SELECTORS_PAGE = [
|
|
19
|
-
".boss-popup__close",
|
|
20
|
-
".close-btn",
|
|
21
|
-
".dialog-close",
|
|
22
|
-
".modal-close",
|
|
23
|
-
];
|
|
24
|
-
export const zhipinCloseResume = defineTool({
|
|
25
|
-
name: "zhipin_close_resume",
|
|
26
|
-
description: "关闭简历详情弹窗",
|
|
27
|
-
input: z.object({}),
|
|
28
|
-
output: OutputSchema,
|
|
29
|
-
execute: async (_input, ctx) => {
|
|
30
|
-
ctx.logger.info("Closing resume detail modal");
|
|
31
|
-
const ctxManager = getContextManager();
|
|
32
|
-
const page = await ctxManager.getPage("zhipin");
|
|
33
|
-
const frame = page.frame("recommendFrame") ?? page.frames().find((f) => f.url().includes("recommend"));
|
|
34
|
-
const closed = await (async () => {
|
|
35
|
-
// 优先在 iframe 中查找
|
|
36
|
-
if (frame) {
|
|
37
|
-
for (const sel of CLOSE_SELECTORS_IFRAME) {
|
|
38
|
-
const btn = await frame.$(sel);
|
|
39
|
-
if (btn && (await btn.isVisible())) {
|
|
40
|
-
await btn.click();
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
// 回退到主页面
|
|
46
|
-
for (const sel of CLOSE_SELECTORS_PAGE) {
|
|
47
|
-
const btn = await page.$(sel);
|
|
48
|
-
if (btn && (await btn.isVisible())) {
|
|
49
|
-
await btn.click();
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return false;
|
|
54
|
-
})();
|
|
55
|
-
if (!closed)
|
|
56
|
-
return { success: false, closed: false, error: "未找到关闭按钮" };
|
|
57
|
-
// 验证关闭(轮询)
|
|
58
|
-
let verified = false;
|
|
59
|
-
for (let i = 0; i < 5; i++) {
|
|
60
|
-
await page.waitForTimeout(300);
|
|
61
|
-
const dialogExists = frame
|
|
62
|
-
? await frame.$(".boss-popup__wrapper, .dialog-lib-resume, .boss-dialog")
|
|
63
|
-
: await page.$(".boss-popup__wrapper");
|
|
64
|
-
if (!dialogExists || !(await dialogExists.isVisible())) {
|
|
65
|
-
verified = true;
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
ctx.logger.info(verified ? "Resume modal closed and verified" : "Resume modal close unverified");
|
|
70
|
-
return { success: true, closed: true };
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
//# sourceMappingURL=zhipin-close-resume.js.map
|
|
1
|
+
import{defineTool as o}from"@roll-agent/sdk";import{z as e}from"zod";import{getContextManager as s}from"../runtime-holder.js";const i=e.object({success:e.boolean(),closed:e.boolean(),error:e.string().optional()}),r=[".recommendV2 .boss-popup__close",".dialog-lib-resume .boss-popup__close",".boss-dialog .boss-popup__close",".boss-popup__close",".close-btn",".dialog-close"],a=[".boss-popup__close",".close-btn",".dialog-close",".modal-close"];export const zhipinCloseResume=o({name:"zhipin_close_resume",description:"关闭简历详情弹窗",input:e.object({}),output:i,execute:async(o,e)=>{e.logger.info("Closing resume detail modal");const i=s(),l=await i.getPage("zhipin"),t=l.frame("recommendFrame")??l.frames().find(o=>o.url().includes("recommend"));if(!await(async()=>{if(t)for(const o of r){const e=await t.$(o);if(e&&await e.isVisible())return await e.click(),!0}for(const o of a){const e=await l.$(o);if(e&&await e.isVisible())return await e.click(),!0}return!1})())return{success:!1,closed:!1,error:"未找到关闭按钮"};let c=!1;for(let o=0;o<5;o++){await l.waitForTimeout(300);const o=t?await t.$(".boss-popup__wrapper, .dialog-lib-resume, .boss-dialog"):await l.$(".boss-popup__wrapper");if(!o||!await o.isVisible()){c=!0;break}}return e.logger.info(c?"Resume modal closed and verified":"Resume modal close unverified"),{success:!0,closed:!0}}});
|
|
@@ -1,218 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { getContextManager } from "../runtime-holder.js";
|
|
4
|
-
import { randomDelay, humanDelay } from "../pages/zhipin/anti-detection.js";
|
|
5
|
-
import { ensureChatOpen } from "../pages/zhipin/chat-navigation.js";
|
|
6
|
-
const OutputSchema = z.object({
|
|
7
|
-
success: z.boolean(),
|
|
8
|
-
exchanged: z.boolean(),
|
|
9
|
-
wechatNumber: z.string().optional(),
|
|
10
|
-
error: z.string().optional(),
|
|
11
|
-
});
|
|
12
|
-
export const zhipinExchangeWechat = defineTool({
|
|
13
|
-
name: "zhipin_exchange_wechat",
|
|
14
|
-
description: '换微信。可指定 candidateName 自动打开对应聊天后执行,或不传则在当前窗口执行;例如"和鲁倩换微信"应提取 candidateName=鲁倩。',
|
|
15
|
-
input: z.object({
|
|
16
|
-
candidateName: z
|
|
17
|
-
.string()
|
|
18
|
-
.optional()
|
|
19
|
-
.describe('候选人姓名。若用户说"和鲁倩换微信",这里应提取为"鲁倩"'),
|
|
20
|
-
index: z.number().optional().describe("候选人在列表中的索引(可选)"),
|
|
21
|
-
}),
|
|
22
|
-
output: OutputSchema,
|
|
23
|
-
execute: async (input, ctx) => {
|
|
24
|
-
const ctxManager = getContextManager();
|
|
25
|
-
const page = await ctxManager.getPage("zhipin");
|
|
26
|
-
// 如果指定了候选人,先导航到对应聊天
|
|
27
|
-
const nav = await ensureChatOpen(page, {
|
|
28
|
-
candidateName: input.candidateName,
|
|
29
|
-
index: input.index,
|
|
30
|
-
});
|
|
31
|
-
if (nav && !nav.found) {
|
|
32
|
-
return { success: false, exchanged: false, error: nav.error };
|
|
33
|
-
}
|
|
34
|
-
ctx.logger.info(`Starting WeChat exchange${nav ? ` with ${nav.name}` : ""}`);
|
|
35
|
-
try {
|
|
36
|
-
// Step 1: 多策略查找"换微信"按钮
|
|
37
|
-
// 用 getBoundingClientRect 判断可见性(offsetParent 对 fixed 定位不可靠)
|
|
38
|
-
const btnData = await page.evaluate(() => {
|
|
39
|
-
const check = (el) => {
|
|
40
|
-
const r = el.getBoundingClientRect();
|
|
41
|
-
return r.width > 0 && r.height > 0;
|
|
42
|
-
};
|
|
43
|
-
// 策略 1: 直接选择器查找
|
|
44
|
-
const selectors = [
|
|
45
|
-
".operate-exchange-left .operate-btn",
|
|
46
|
-
"span.operate-btn",
|
|
47
|
-
];
|
|
48
|
-
for (const sel of selectors) {
|
|
49
|
-
const elements = Array.from(document.querySelectorAll(sel));
|
|
50
|
-
for (const el of elements) {
|
|
51
|
-
const text = el.textContent?.trim() ?? "";
|
|
52
|
-
if (text.includes("换微信") && check(el)) {
|
|
53
|
-
el.setAttribute("data-roll-wechat-btn", "true");
|
|
54
|
-
return { found: true, text };
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
// 策略 2: 全量 span 文本搜索 fallback
|
|
59
|
-
const allSpans = Array.from(document.querySelectorAll("span"));
|
|
60
|
-
for (const span of allSpans) {
|
|
61
|
-
const text = span.textContent?.trim() ?? "";
|
|
62
|
-
if (text.includes("换微信") && check(span)) {
|
|
63
|
-
span.setAttribute("data-roll-wechat-btn", "true");
|
|
64
|
-
return { found: true, text };
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return { found: false };
|
|
68
|
-
});
|
|
69
|
-
if (!btnData.found) {
|
|
70
|
-
return { success: false, exchanged: false, error: "未找到「换微信」按钮" };
|
|
71
|
-
}
|
|
72
|
-
// 用 Playwright 的 click(模拟真实鼠标事件,带坐标)
|
|
73
|
-
await randomDelay(page, 200, 400);
|
|
74
|
-
await page.click('[data-roll-wechat-btn="true"]');
|
|
75
|
-
// 清理标记
|
|
76
|
-
await page.evaluate(() => {
|
|
77
|
-
document.querySelector("[data-roll-wechat-btn]")?.removeAttribute("data-roll-wechat-btn");
|
|
78
|
-
});
|
|
79
|
-
// Step 2: 等确认对话框出现(polling 模式,对动画更宽容)
|
|
80
|
-
// 先等待 400-800ms,然后 polling 检查,最多 ~5 秒
|
|
81
|
-
await randomDelay(page, 400, 800);
|
|
82
|
-
let dialogFound = false;
|
|
83
|
-
for (let attempt = 0; attempt < 8; attempt++) {
|
|
84
|
-
const found = await page.evaluate(() => {
|
|
85
|
-
const check = (el) => {
|
|
86
|
-
const r = el.getBoundingClientRect();
|
|
87
|
-
return r.width > 0 && r.height > 0;
|
|
88
|
-
};
|
|
89
|
-
// 策略 1: .exchange-tooltip 类名
|
|
90
|
-
const tooltip = document.querySelector(".exchange-tooltip");
|
|
91
|
-
if (tooltip && check(tooltip))
|
|
92
|
-
return true;
|
|
93
|
-
// 策略 2: 文本内容匹配 — 查找包含"交换微信"的弹窗
|
|
94
|
-
const allElements = document.querySelectorAll("div, section, aside");
|
|
95
|
-
for (const el of Array.from(allElements)) {
|
|
96
|
-
const text = el.textContent ?? "";
|
|
97
|
-
if (text.includes("交换微信") && el.querySelector(".boss-btn-primary, .boss-btn")) {
|
|
98
|
-
if (check(el))
|
|
99
|
-
return true;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return false;
|
|
103
|
-
});
|
|
104
|
-
if (found) {
|
|
105
|
-
dialogFound = true;
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
await randomDelay(page, 400, 800);
|
|
109
|
-
}
|
|
110
|
-
if (!dialogFound) {
|
|
111
|
-
return { success: false, exchanged: false, error: "确认对话框未弹出" };
|
|
112
|
-
}
|
|
113
|
-
await humanDelay(page);
|
|
114
|
-
// Step 3: 多策略查找确认按钮(不限定在 .exchange-tooltip 内)
|
|
115
|
-
const confirmData = await page.evaluate(() => {
|
|
116
|
-
const check = (el) => {
|
|
117
|
-
const r = el.getBoundingClientRect();
|
|
118
|
-
return r.width > 0 && r.height > 0;
|
|
119
|
-
};
|
|
120
|
-
// 策略 1: 在 .exchange-tooltip 内查找
|
|
121
|
-
const tooltip = document.querySelector(".exchange-tooltip");
|
|
122
|
-
if (tooltip) {
|
|
123
|
-
const selectors = [
|
|
124
|
-
".btn-box .boss-btn-primary.boss-btn",
|
|
125
|
-
".btn-box span.boss-btn-primary",
|
|
126
|
-
"span.boss-btn-primary",
|
|
127
|
-
".boss-btn-primary",
|
|
128
|
-
];
|
|
129
|
-
for (const sel of selectors) {
|
|
130
|
-
const btn = tooltip.querySelector(sel);
|
|
131
|
-
if (btn && check(btn)) {
|
|
132
|
-
btn.setAttribute("data-roll-confirm-btn", "true");
|
|
133
|
-
return { found: true, text: btn.textContent?.trim() ?? "" };
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
// 策略 2: 全局查找"确定"按钮 — 限定在包含"交换微信"文本的容器内
|
|
138
|
-
const containers = document.querySelectorAll("div, section, aside");
|
|
139
|
-
for (const container of Array.from(containers)) {
|
|
140
|
-
const cText = container.textContent ?? "";
|
|
141
|
-
if (!cText.includes("交换微信"))
|
|
142
|
-
continue;
|
|
143
|
-
const btns = container.querySelectorAll("span.boss-btn-primary, button.boss-btn-primary, span.boss-btn, button.boss-btn");
|
|
144
|
-
for (const btn of Array.from(btns)) {
|
|
145
|
-
const bText = btn.textContent?.trim() ?? "";
|
|
146
|
-
if (bText === "确定" && check(btn)) {
|
|
147
|
-
btn.setAttribute("data-roll-confirm-btn", "true");
|
|
148
|
-
return { found: true, text: bText };
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return { found: false };
|
|
153
|
-
});
|
|
154
|
-
if (!confirmData.found) {
|
|
155
|
-
return { success: false, exchanged: false, error: "未找到确认按钮" };
|
|
156
|
-
}
|
|
157
|
-
// 点击前稍等(模拟人类阅读弹窗)
|
|
158
|
-
await randomDelay(page, 200, 400);
|
|
159
|
-
await page.click('[data-roll-confirm-btn="true"]');
|
|
160
|
-
// 清理标记
|
|
161
|
-
await page.evaluate(() => {
|
|
162
|
-
document.querySelector("[data-roll-confirm-btn]")?.removeAttribute("data-roll-confirm-btn");
|
|
163
|
-
});
|
|
164
|
-
// Step 4: 等待交换完成,提取微信号
|
|
165
|
-
await randomDelay(page, 1500, 2500);
|
|
166
|
-
const wechatNumber = await page.evaluate(() => {
|
|
167
|
-
// 策略 1: 从微信交换卡片提取
|
|
168
|
-
const cardSelectors = [
|
|
169
|
-
".message-card-top-wrap",
|
|
170
|
-
'[class*="d-top-text"]',
|
|
171
|
-
".message-card-top-title",
|
|
172
|
-
];
|
|
173
|
-
for (const sel of cardSelectors) {
|
|
174
|
-
const cards = Array.from(document.querySelectorAll(sel));
|
|
175
|
-
for (let i = cards.length - 1; i >= 0; i--) {
|
|
176
|
-
const text = cards[i]?.textContent ?? "";
|
|
177
|
-
const digitMatch = text.match(/\b(\d{8,15})\b/);
|
|
178
|
-
if (digitMatch)
|
|
179
|
-
return digitMatch[1];
|
|
180
|
-
const wxMatch = text.match(/微信[::号]*\s*([a-zA-Z0-9_-]{5,20})/);
|
|
181
|
-
if (wxMatch)
|
|
182
|
-
return wxMatch[1];
|
|
183
|
-
const letterMatch = text.match(/\b([a-zA-Z][a-zA-Z0-9_-]{5,19})\b/);
|
|
184
|
-
if (letterMatch && !["微信", "WeChat"].includes(letterMatch[1])) {
|
|
185
|
-
return letterMatch[1];
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
// 策略 2: 从消息列表末尾反向查找微信交换卡片
|
|
190
|
-
const msgItems = Array.from(document.querySelectorAll(".message-item"));
|
|
191
|
-
for (let i = msgItems.length - 1; i >= 0; i--) {
|
|
192
|
-
const card = msgItems[i]?.querySelector('.message-card-top-wrap, [class*="d-top-text"]');
|
|
193
|
-
if (card) {
|
|
194
|
-
const text = card.textContent ?? "";
|
|
195
|
-
const numMatch = text.match(/\b(\d{8,15})\b/);
|
|
196
|
-
if (numMatch)
|
|
197
|
-
return numMatch[1];
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return null;
|
|
201
|
-
});
|
|
202
|
-
ctx.logger.info(`WeChat exchanged${wechatNumber ? `, number: ${wechatNumber}` : ""}`);
|
|
203
|
-
return {
|
|
204
|
-
success: true,
|
|
205
|
-
exchanged: true,
|
|
206
|
-
...(wechatNumber !== null ? { wechatNumber } : {}),
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
catch (err) {
|
|
210
|
-
return {
|
|
211
|
-
success: false,
|
|
212
|
-
exchanged: false,
|
|
213
|
-
error: err instanceof Error ? err.message : String(err),
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
},
|
|
217
|
-
});
|
|
218
|
-
//# sourceMappingURL=zhipin-exchange-wechat.js.map
|
|
1
|
+
import{defineTool as t}from"@roll-agent/sdk";import{z as e}from"zod";import{getContextManager as r}from"../runtime-holder.js";import{randomDelay as n,humanDelay as o}from"../pages/zhipin/anti-detection.js";import{ensureChatOpen as a}from"../pages/zhipin/chat-navigation.js";const c=e.object({success:e.boolean(),exchanged:e.boolean(),wechatNumber:e.string().optional(),error:e.string().optional()});export const zhipinExchangeWechat=t({name:"zhipin_exchange_wechat",description:'换微信。可指定 candidateName 自动打开对应聊天后执行,或不传则在当前窗口执行;例如"和鲁倩换微信"应提取 candidateName=鲁倩。',input:e.object({candidateName:e.string().optional().describe('候选人姓名。若用户说"和鲁倩换微信",这里应提取为"鲁倩"'),index:e.number().optional().describe("候选人在列表中的索引(可选)")}),output:c,execute:async(t,e)=>{const c=r(),i=await c.getPage("zhipin"),s=await a(i,{candidateName:t.candidateName,index:t.index});if(s&&!s.found)return{success:!1,exchanged:!1,error:s.error};e.logger.info("Starting WeChat exchange"+(s?` with ${s.name}`:""));try{if(!(await i.evaluate(()=>{const t=t=>{const e=t.getBoundingClientRect();return e.width>0&&e.height>0},e=[".operate-exchange-left .operate-btn","span.operate-btn"];for(const r of e){const e=Array.from(document.querySelectorAll(r));for(const r of e){const e=r.textContent?.trim()??"";if(e.includes("换微信")&&t(r))return r.setAttribute("data-roll-wechat-btn","true"),{found:!0,text:e}}}const r=Array.from(document.querySelectorAll("span"));for(const e of r){const r=e.textContent?.trim()??"";if(r.includes("换微信")&&t(e))return e.setAttribute("data-roll-wechat-btn","true"),{found:!0,text:r}}return{found:!1}})).found)return{success:!1,exchanged:!1,error:"未找到「换微信」按钮"};await n(i,200,400),await i.click('[data-roll-wechat-btn="true"]'),await i.evaluate(()=>{document.querySelector("[data-roll-wechat-btn]")?.removeAttribute("data-roll-wechat-btn")}),await n(i,400,800);let t=!1;for(let e=0;e<8;e++){if(await i.evaluate(()=>{const t=t=>{const e=t.getBoundingClientRect();return e.width>0&&e.height>0},e=document.querySelector(".exchange-tooltip");if(e&&t(e))return!0;const r=document.querySelectorAll("div, section, aside");for(const e of Array.from(r)){if((e.textContent??"").includes("交换微信")&&e.querySelector(".boss-btn-primary, .boss-btn")&&t(e))return!0}return!1})){t=!0;break}await n(i,400,800)}if(!t)return{success:!1,exchanged:!1,error:"确认对话框未弹出"};await o(i);if(!(await i.evaluate(()=>{const t=t=>{const e=t.getBoundingClientRect();return e.width>0&&e.height>0},e=document.querySelector(".exchange-tooltip");if(e){const r=[".btn-box .boss-btn-primary.boss-btn",".btn-box span.boss-btn-primary","span.boss-btn-primary",".boss-btn-primary"];for(const n of r){const r=e.querySelector(n);if(r&&t(r))return r.setAttribute("data-roll-confirm-btn","true"),{found:!0,text:r.textContent?.trim()??""}}}const r=document.querySelectorAll("div, section, aside");for(const e of Array.from(r)){if(!(e.textContent??"").includes("交换微信"))continue;const r=e.querySelectorAll("span.boss-btn-primary, button.boss-btn-primary, span.boss-btn, button.boss-btn");for(const e of Array.from(r)){const r=e.textContent?.trim()??"";if("确定"===r&&t(e))return e.setAttribute("data-roll-confirm-btn","true"),{found:!0,text:r}}}return{found:!1}})).found)return{success:!1,exchanged:!1,error:"未找到确认按钮"};await n(i,200,400),await i.click('[data-roll-confirm-btn="true"]'),await i.evaluate(()=>{document.querySelector("[data-roll-confirm-btn]")?.removeAttribute("data-roll-confirm-btn")}),await n(i,1500,2500);const r=await i.evaluate(()=>{const t=[".message-card-top-wrap",'[class*="d-top-text"]',".message-card-top-title"];for(const e of t){const t=Array.from(document.querySelectorAll(e));for(let e=t.length-1;e>=0;e--){const r=t[e]?.textContent??"",n=r.match(/\b(\d{8,15})\b/);if(n)return n[1];const o=r.match(/微信[::号]*\s*([a-zA-Z0-9_-]{5,20})/);if(o)return o[1];const a=r.match(/\b([a-zA-Z][a-zA-Z0-9_-]{5,19})\b/);if(a&&!["微信","WeChat"].includes(a[1]))return a[1]}}const e=Array.from(document.querySelectorAll(".message-item"));for(let t=e.length-1;t>=0;t--){const r=e[t]?.querySelector('.message-card-top-wrap, [class*="d-top-text"]');if(r){const t=(r.textContent??"").match(/\b(\d{8,15})\b/);if(t)return t[1]}}return null});return e.logger.info("WeChat exchanged"+(r?`, number: ${r}`:"")),{success:!0,exchanged:!0,...null!==r?{wechatNumber:r}:{}}}catch(t){return{success:!1,exchanged:!1,error:t instanceof Error?t.message:String(t)}}}});
|