navigator.js 13 KB

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