navigator.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. import {
  2. puppeteer,
  3. connect,
  4. ExtensionTransport,
  5. } from 'puppeteer-core/lib/cjs/puppeteer/puppeteer-core-browser.js';
  6. import { getexecute } from '@/api/agentapi.js'
  7. export const navigator=()=> {
  8. let browser = null;
  9. async function browsercomm() {
  10. const tabitem = await getActiveTabId();
  11. let tabId=tabitem.id
  12. const connectOptions = {
  13. transport: await ExtensionTransport.connectTab(tabId), // 替换为实际标签ID
  14. defaultViewport: null,
  15. protocol: 'cdp'
  16. };
  17. try {
  18. // 连接到浏览器
  19. browser = await connect(connectOptions);
  20. const [page] = await browser.pages();
  21. await chrome.scripting.executeScript({
  22. target: {tabId: tabId},
  23. files: ['buildDomTree1.js'], // 注入外部文件
  24. world: "MAIN"
  25. });
  26. // await page.waitForFunction(() => typeof window.yourFunc === 'function');
  27. const domState = await page.evaluate(() => {
  28. const buildDomTree = window.buildDomTree({
  29. doHighlightElements: true,
  30. focusHighlightIndex: -1,
  31. viewportExpansion: 0,
  32. debugMode: false,
  33. })
  34. function findDomByXpath(xpath) {
  35. try {
  36. return document.evaluate('/' + xpath.replace(/^\/+/, ''), document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  37. } catch (e) {
  38. return null;
  39. }
  40. }
  41. function cleanHtmlTags(htmlStr, attrs = ['name', 'href']) {
  42. const doc = new DOMParser().parseFromString(htmlStr.replace(/[\r\n]+/g, ''), 'text/html');
  43. // 遍历所有元素
  44. const walker = doc.createTreeWalker(doc.body, NodeFilter.SHOW_ELEMENT);
  45. while (walker.nextNode()) {
  46. const node = walker.currentNode;
  47. // 移除指定属性
  48. attrs.forEach(attr => node.removeAttribute(attr));
  49. // 只保留当前节点和其文本内容
  50. node.innerHTML = node.textContent;
  51. }
  52. return doc.body.innerHTML;
  53. }
  54. const domList = Object.values(buildDomTree.map)
  55. .filter(n => typeof n === "object" && n.highlightIndex !== undefined)
  56. .map(n => findDomByXpath(n.xpath))
  57. .filter(x => !!x)
  58. .map((item, index) => `[${index}] ${item.outerHTML}`)
  59. .map((item)=>{
  60. const attrs = ['name', 'href','src','class','id','target',]
  61. return cleanHtmlTags(item, attrs);
  62. })
  63. .join("\n")
  64. return {buildDomTree:buildDomTree,domList:domList,body:document.body.innerText}
  65. });
  66. return {domState:domState, page:page,tabitem:tabitem};
  67. } finally {
  68. // if (browser) await browser.disconnect();
  69. }
  70. }
  71. async function browserautomate(domState,page,type,targetIndex,text) {
  72. // console.log(domState,page,type,targetIndex,browser)
  73. // 查找目标元素
  74. const targetElement = findElementByIndex(domState.map, targetIndex);
  75. if (!targetElement) throw new Error('Element not found');
  76. // 生成增强选择器
  77. const selector = generateSelector(targetElement);
  78. // console.log(domState, targetElement, selector)
  79. // const [page] = await browser.pages()
  80. try {
  81. await page.waitForSelector(selector, {visible: true, timeout: 10000});
  82. switch (type) {
  83. case 'click':
  84. await page.click(selector, {delay: 100});
  85. break;
  86. case 'key_enter':
  87. await page.click(selector, {delay: 100});
  88. break;
  89. case 'navigate':
  90. await page.goto('https://example.com', {waitUntil: 'networkidle2'});
  91. break;
  92. case 'getcontent':
  93. console.log(await page.content());
  94. break;
  95. case 'get_text':
  96. console.log(await page.content());
  97. break;
  98. case 'input_text':
  99. await page.type(selector, text, {delay: 600});
  100. break;
  101. // 添加更多命令支持,如:
  102. case 'form':
  103. const formData = [{
  104. text: 'your-username',
  105. index: 'your-password',
  106. email: 'your-email@example.com',
  107. question2: 'option2', // 假设有一个单选题,选项为 option2
  108. question3: '我非常满意这次体验', // 假设有一个文本框,答案为 “我非常满意这次体验”
  109. }];
  110. await page.type(selector, 'your-username', {delay: 600});
  111. await page.type(selector, 'your-password', {delay: 600});
  112. await page.click(selector); // 或者其他登录按钮
  113. break;
  114. default:
  115. console.log('未知指令。');
  116. }
  117. // 验证点击结果
  118. // await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 15000 });
  119. // console.log(`Successfully clicked element ${targetIndex} on attempt ${attempt}`);
  120. await page.evaluate(() => {
  121. // document.getElementById('playwright-highlight-container')?.remove();
  122. });
  123. return true;
  124. } catch (clickError) {
  125. // console.warn(`Attempt ${attempt} failed: ${clickError.message}`);
  126. // if (attempt === retries) throw clickError;
  127. // await page.waitForTimeout(delay);
  128. // await page.evaluate(() => {
  129. // document.getElementById('playwright-highlight-container')?.remove();
  130. // });
  131. // if (browser) await browser.disconnect();
  132. }finally {
  133. console.log(123456789)
  134. await page.evaluate(() => {
  135. document.getElementById('playwright-highlight-container')?.remove();
  136. });
  137. if (browser) await browser.disconnect();
  138. }
  139. }
  140. function allTabs(){
  141. return new Promise( (resolve, reject) => {
  142. chrome.tabs.query({}, (tabs) => {
  143. resolve(tabs);
  144. });
  145. })
  146. }
  147. function getActiveTabId() {
  148. return new Promise((resolve) => {
  149. setTimeout(() => {
  150. chrome.tabs.query({active: true}, (tabs) => {
  151. resolve(tabs[0]);
  152. });
  153. },500)
  154. });
  155. }
  156. // 辅助函数:通过索引查找元素
  157. function findElementByIndex(domMap, targetIndex) {
  158. return Object.values(domMap).find(
  159. node => node.highlightIndex === targetIndex
  160. );
  161. }
  162. // 辅助函数:生成 CSS 选择器
  163. function generateSelector(element) {
  164. const attrs = Object.entries(element.attributes)
  165. .map(([k, v]) => `[${k}="${v.replace(/"/g, '\\"')}"]`)
  166. .join('');
  167. return `${element.tagName}${attrs}`;
  168. }
  169. function gfe() {
  170. chrome.tabs.query({active: true, currentWindow: true}, async (tabs) => {
  171. const tabId = tabs[0].id;
  172. await smartClick(tabId, 1, {
  173. type: 'click',
  174. text: 3000
  175. });
  176. })
  177. }
  178. // 背景脚本
  179. function switchTabOrOpenNew(url) {
  180. return new Promise(resolve => {
  181. // 查询是否存在指定的标签页
  182. // chrome.tabs.query({windowId: chrome.windows.WINDOW_ID_CURRENT}, async (tabs) => {
  183. chrome.tabs.query({}, async (tabs) => {
  184. let tabId = tabs.find(tabs => tabs.url.replace(/\/$/, '') === url);
  185. if (tabId) {
  186. // 如果存在,则激活该标签页
  187. await chrome.tabs.update(tabId.id, {active: true});
  188. resolve()
  189. } else {
  190. // 如果不存在,则新打开一个标签页
  191. await chrome.tabs.create({url: url});
  192. resolve()
  193. }
  194. });
  195. })
  196. }
  197. function getexecuteapi(params) {
  198. return getexecute(params).then((res)=>{
  199. return res;
  200. })
  201. }
  202. async function agent(payload) {
  203. let isDOMType="element"
  204. for(let item of payload.steps){
  205. if(item.stepIndex === 0){
  206. let params = {
  207. conversationId: payload.conversationId,
  208. messageId: payload.messageId,
  209. messageContent: payload.messageContent,
  210. planId: payload.id,
  211. currentPlanStepId: item.id,
  212. envData: {
  213. osName: "Google Chrome",
  214. osVersion: "137.0.7151.69(正式版本)",
  215. osArch: "arm64",
  216. url: "",
  217. title:"",
  218. tabs: [],
  219. interactiveElements: ""
  220. },
  221. resultSummary: null,
  222. needSummary: true,
  223. success: false
  224. }
  225. let add = await getexecuteapi(params)
  226. let toolExecution=JSON.parse(add.data.plan.steps[0].toolExecution)[0].toolParameters
  227. if (JSON.parse(toolExecution).action == 'navigate') {
  228. await switchTabOrOpenNew(JSON.parse(toolExecution).url)
  229. }
  230. }else {
  231. let browdata= await browsercomm()
  232. console.log(browdata)
  233. const tabs = await allTabs()
  234. let params = {
  235. conversationId: payload.conversationId,
  236. messageId: payload.messageId,
  237. messageContent: payload.messageContent,
  238. planId: payload.id,
  239. currentPlanStepId: item.id,
  240. envData: {
  241. osName: "Google Chrome",
  242. osVersion: "137.0.7151.69(正式版本)",
  243. osArch: "arm64",
  244. url: browdata.tabitem.url,
  245. title: browdata.tabitem.title,
  246. tabs: tabs,
  247. interactiveElements:isDOMType=='text'?browdata.domState.body: browdata.domState.domList
  248. // interactiveElements: "[0] <a>新闻</a>\n [1] <a>hao123</a>\n [2] <a>地图</a>\n [3] <a>贴吧</a>\n [4] <a>视频</a>\n [5] <a>图片</a>\n [6] <a>网盘</a>\n [7] <a>文库</a>\n [8] <a id=\"csaitab\"></a>\n [9] <a name=\"tj_briicon\">更多</a>\n [10] <a></a>\n [11] <a name=\"tj_login\" id=\"s-top-loginbtn\">登录</a>\n [12] <input placeholder=\"梁靖崑称优势是有王楚钦\" name=\"wd\" id=\"kw\" value=\"\"></input>\n [13] <input type=\"submit\" id=\"su\" value=\"百度一下\"></input>\n [14] <a>AI搜索已支持DeepSeek R1最新版立即体验</a>\n [16] <a id=\"hotsearch-refresh-btn\">换一换</a>\n [17] <a>0让“干坡坡”变“金窝窝”</a>\n [18] <a>5福建一楼房发生爆炸 有人员"
  249. },
  250. resultSummary: null,
  251. needSummary: true,
  252. success: false
  253. }
  254. let getexecutedaat = await getexecuteapi(params)
  255. let toolExecution=JSON.parse(getexecutedaat.data.plan.steps[item.stepIndex].toolExecution)
  256. for(let item of toolExecution){
  257. if(JSON.parse(item.toolParameters).action=='get_text'){
  258. isDOMType='text'
  259. }else {
  260. isDOMType='element'
  261. }
  262. await browserautomate(browdata.domState.buildDomTree, browdata.page, JSON.parse(item.toolParameters).action, JSON.parse(item.toolParameters).index||0,JSON.parse(item.toolParameters).text||'')
  263. }
  264. }
  265. chrome.runtime.sendMessage({
  266. type: "FROM_STEP",
  267. payload: item.stepIndex});
  268. }
  269. }
  270. chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
  271. console.log(request, sender, sendResponse);
  272. if (request.type == 'FROM_PLAN') {
  273. agent(request.payload)
  274. }
  275. })
  276. }