From 1a1ec4b45f4f9c3770ff153147ab2b90e7c0bac9 Mon Sep 17 00:00:00 2001 From: fofolee Date: Sun, 19 Jan 2025 13:43:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=88=86=E7=B1=BB=EF=BC=9A?= =?UTF-8?q?=E6=B5=8F=E8=A7=88=E5=99=A8=E6=8E=A7=E5=88=B6=EF=BC=8C=E5=9F=BA?= =?UTF-8?q?=E4=BA=8ECDP=E5=AF=B9edge=E5=92=8Cchrome=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=8C=96=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugin/lib/quickcomposer/browser/browser.js | 400 +++++++++++ plugin/lib/quickcomposer/browser/index.js | 3 + plugin/package-lock.json | 708 +++++++++++--------- plugin/package.json | 23 +- src/js/composer/commands/browserCommands.js | 399 +++++++++++ src/js/composer/commands/index.js | 2 + 6 files changed, 1199 insertions(+), 336 deletions(-) create mode 100644 plugin/lib/quickcomposer/browser/browser.js create mode 100644 plugin/lib/quickcomposer/browser/index.js create mode 100644 src/js/composer/commands/browserCommands.js diff --git a/plugin/lib/quickcomposer/browser/browser.js b/plugin/lib/quickcomposer/browser/browser.js new file mode 100644 index 0000000..a632786 --- /dev/null +++ b/plugin/lib/quickcomposer/browser/browser.js @@ -0,0 +1,400 @@ +const CDP = require("chrome-remote-interface"); +const { exec } = require("child_process"); +const path = require("path"); +const os = require("os"); +const http = require("http"); +const axios = require("axios"); +const fs = require("fs"); + +let client = null; +let Page = null; +let Runtime = null; +let DOM = null; + +const getBrowserPath = (browser = "msedge") => { + const platform = os.platform(); + let paths = null; + if (platform === "win32") { + paths = { + chrome: [ + path.join( + process.env["ProgramFiles"], + "Google/Chrome/Application/chrome.exe" + ), + path.join( + process.env["ProgramFiles(x86)"], + "Google/Chrome/Application/chrome.exe" + ), + path.join( + process.env["LocalAppData"], + "Google/Chrome/Application/chrome.exe" + ), + ], + msedge: [ + path.join( + process.env["ProgramFiles"], + "Microsoft/Edge/Application/msedge.exe" + ), + path.join( + process.env["ProgramFiles(x86)"], + "Microsoft/Edge/Application/msedge.exe" + ), + path.join( + process.env["LocalAppData"], + "Microsoft/Edge/Application/msedge.exe" + ), + ], + }; + } else if (platform === "darwin") { + paths = { + chrome: ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"], + msedge: [ + "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge", + ], + }; + } else if (platform === "linux") { + paths = { + chrome: [ + "/opt/google/chrome/chrome", + "/usr/bin/google-chrome", + "/usr/bin/google-chrome-stable", + ], + msedge: [ + "/opt/microsoft/msedge/msedge", + "/usr/bin/microsoft-edge", + "/usr/bin/microsoft-edge-stable", + ], + }; + } else { + throw new Error("不支持的操作系统"); + } + return paths[browser].find((p) => fs.existsSync(p)); +}; + +const isPortAvailable = (port) => { + return new Promise((resolve) => { + const server = http.createServer(); + server.listen(port, () => { + server.close(() => resolve(true)); + }); + server.on("error", () => resolve(false)); + }); +}; + +const waitForPort = async (port, timeout = 30000) => { + const startTime = Date.now(); + while (Date.now() - startTime < timeout) { + try { + const response = await axios.get(`http://localhost:${port}/json/version`); + if (response.status === 200) return true; + } catch (e) { + await new Promise((resolve) => setTimeout(resolve, 100)); + } + } + return false; +}; + +const findAvailablePort = async (startPort) => { + let port = startPort; + while (port < startPort + 100) { + const available = await isPortAvailable(port); + if (available) { + return port; + } + port++; + } + throw new Error("无法找到可用的调试端口"); +}; + +const launchBrowser = async (options) => { + const { + browserType = "msedge", + useSingleUserDataDir = true, + headless = false, + proxy = null, + browserPath = getBrowserPath(browserType), + windowSize = null, + } = options; + + if (!browserPath) { + throw new Error("未找到浏览器,或未指定浏览器路径"); + } + + // 查找可用端口 + const port = await findAvailablePort(9222); + + const args = [ + `--remote-debugging-port=${port}`, + "--no-first-run", + "--no-default-browser-check", + "--start-maximized", + headless ? "--headless" : "", + windowSize ? `--window-size=${windowSize}` : "", + proxy ? `--proxy-server=${proxy}` : "", + useSingleUserDataDir + ? `--user-data-dir=${path.join( + os.tmpdir(), + `${browserType}-debug-${port}` + )}` + : "", + ].filter(Boolean); + + return new Promise(async (resolve, reject) => { + // 如果使用独立用户数据目录,则需要先杀死已有的浏览器进程 + if (!useSingleUserDataDir) { + try { + await killRunningBrowser(browserType); + } catch (e) { + reject(e); + return; + } + } + const child = exec( + `"${browserPath}" ${args.join(" ")}`, + { + windowsHide: true, + }, + async (error) => { + if (error) { + reject(error); + return; + } + } + ); + + // 等待端口可用 + waitForPort(port).then((success) => { + if (success) { + resolve({ + pid: child.pid, + port, + }); // 返回使用的端口号 + } else { + reject(new Error("浏览器启动超时,请检查是否有权限问题或防火墙限制")); + } + }); + }); +}; + +const killRunningBrowser = (browserType = "msedge") => { + return new Promise((resolve, reject) => { + if (os.platform() === "win32") { + exec(`taskkill /F /IM ${browserType}.exe`, (error) => { + if (error) reject(error); + else resolve(); + }); + } else { + exec(`kill -9 $(pgrep ${browserType})`, (error) => { + if (error) reject(error); + else resolve(); + }); + } + }); +}; + +const initCDP = async (port) => { + if (!client) { + try { + client = await CDP({ port }); + ({ Page, Runtime, DOM } = client); + await Promise.all([Page.enable(), Runtime.enable(), DOM.enable()]); + } catch (err) { + console.log(err); + throw new Error( + `请确保浏览器已启动,且开启了远程调试端口(--remote-debugging-port=${port})` + ); + } + } + return { Page, Runtime, DOM }; +}; + +const getUrl = async () => { + const { Page } = await initCDP(); + const { frameTree } = await Page.getFrameTree(); + return frameTree.frame.url; +}; + +const setUrl = async (url) => { + const { Page } = await initCDP(); + await Page.navigate({ url }); + await Page.loadEventFired(); +}; + +const executeScript = async (script, args = {}) => { + const { Runtime } = await initCDP(); + // 构建参数列表 + const argNames = Object.keys(args); + const argValues = Object.values(args).map((v) => JSON.stringify(v)); + + const wrappedScript = ` + (function(${argNames.join(", ")}) { + ${script} + })(${argValues.join(", ")}) + `; + const { result } = await Runtime.evaluate({ + expression: wrappedScript, + returnByValue: true, + awaitPromise: true, + }); + return result.value; +}; + +const getTabs = async () => { + const targets = await CDP.List(); + return targets + .filter((target) => target.type === "page") + .map((target) => ({ + url: target.url, + title: target.title, + })); +}; + +const activateTab = async (index) => { + const targets = await CDP.List(); + const pages = targets.filter((target) => target.type === "page"); + if (index > 0 && index <= pages.length) { + const targetId = pages[index - 1].id; + await CDP.Activate({ id: targetId }); + } +}; + +const clickElement = async (selector) => { + return await executeScript(`document.querySelector('${selector}').click()`); +}; + +const inputText = async (selector, text) => { + return await executeScript( + ` + const el = document.querySelector('${selector}'); + el.value = '${text}'; + el.dispatchEvent(new Event('input')); + el.dispatchEvent(new Event('change')); + ` + ); +}; + +const getText = async (selector) => { + return await executeScript( + `document.querySelector('${selector}')?.textContent || ''` + ); +}; + +const getHtml = async (selector) => { + return await executeScript( + `const element = document.querySelector('${selector}'); + return element ? element.innerHTML : '';` + ); +}; + +const hideElement = async (selector) => { + return await executeScript( + `document.querySelector('${selector}').style.display = 'none'` + ); +}; + +const showElement = async (selector) => { + return await executeScript( + `document.querySelector('${selector}').style.display = ''` + ); +}; + +const injectCSS = async (css) => { + return await executeScript( + ` + const style = document.createElement('style'); + style.textContent = \`${css}\`; + document.head.appendChild(style); + ` + ); +}; + +const setCookie = async (cookies, options = {}) => { + const { Network } = await initCDP(); + for (const cookie of cookies) { + await Network.setCookie({ + name: cookie.name, + value: cookie.value, + domain: options.domain || undefined, + path: options.path || "/", + secure: options.secure || false, + expires: options.expires + ? Math.floor(Date.now() / 1000) + options.expires * 3600 + : undefined, + }); + } +}; + +const getCookie = async (name) => { + const { Network } = await initCDP(); + const cookies = await Network.getCookies(); + return cookies.find((cookie) => cookie.name === name); +}; + +const scrollTo = async (x, y) => { + return await executeScript(`window.scrollTo(${x}, ${y})`); +}; + +const scrollToElement = async (selector) => { + return await executeScript( + `document.querySelector('${selector}').scrollIntoView()` + ); +}; + +const getScrollPosition = async () => { + return await executeScript(` + return JSON.stringify({ + x: window.pageXOffset || document.documentElement.scrollLeft, + y: window.pageYOffset || document.documentElement.scrollTop + }); + `); +}; + +const getPageSize = async () => { + return await executeScript(` + return JSON.stringify({ + width: Math.max( + document.documentElement.scrollWidth, + document.documentElement.clientWidth + ), + height: Math.max( + document.documentElement.scrollHeight, + document.documentElement.clientHeight + ) + }); + `); +}; + +const waitForElement = async (selector, timeout = 5000) => { + const startTime = Date.now(); + while (Date.now() - startTime < timeout) { + const result = await executeScript( + `!!document.querySelector('${selector}')` + ); + if (result) return; + await new Promise((resolve) => setTimeout(resolve, 100)); + } + throw new Error(`等待元素 ${selector} 超时`); +}; + +module.exports = { + launchBrowser, + getUrl, + setUrl, + executeScript, + getTabs, + activateTab, + clickElement, + inputText, + getText, + getHtml, + hideElement, + showElement, + injectCSS, + setCookie, + getCookie, + scrollTo, + scrollToElement, + getScrollPosition, + getPageSize, + waitForElement, +}; diff --git a/plugin/lib/quickcomposer/browser/index.js b/plugin/lib/quickcomposer/browser/index.js new file mode 100644 index 0000000..bc979db --- /dev/null +++ b/plugin/lib/quickcomposer/browser/index.js @@ -0,0 +1,3 @@ +const browser = require("./browser"); + +module.exports = browser; diff --git a/plugin/package-lock.json b/plugin/package-lock.json index 4aae12c..6a50566 100644 --- a/plugin/package-lock.json +++ b/plugin/package-lock.json @@ -1,330 +1,388 @@ { - "name": "plugin", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "dependencies": { - "axios": "^1.7.9", - "crypto-js": "^4.2.0", - "exif-reader": "^2.0.1", - "iconv-lite": "^0.6.3", - "node-forge": "^1.3.1", - "png2icons": "^2.0.1", - "ses": "^1.10.0", - "sm-crypto": "^0.3.13", - "tree-kill": "^1.2.2" - } - }, - "node_modules/@endo/env-options": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@endo/env-options/-/env-options-1.1.8.tgz", - "integrity": "sha512-Xtxw9n33I4guo8q0sDyZiRuxlfaopM454AKiELgU7l3tqsylCut6IBZ0fPy4ltSHsBib7M3yF7OEMoIuLwzWVg==", - "license": "Apache-2.0" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "license": "MIT" - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/exif-reader": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/exif-reader/-/exif-reader-2.0.1.tgz", - "integrity": "sha512-gCQ/86RiAWSjeSlalj1G99IC6XnxbwkvB91HLqhh8somj/YBtC/2xuplvyjDjlfO7NsmYREPPElu/Syuy/H52g==", - "license": "MIT" - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "license": "MIT" - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/png2icons": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/png2icons/-/png2icons-2.0.1.tgz", - "integrity": "sha512-GDEQJr8OG4e6JMp7mABtXFSEpgJa1CCpbQiAR+EjhkHJHnUL9zPPtbOrjsMD8gUbikgv3j7x404b0YJsV3aVFA==", - "license": "MIT", - "bin": { - "png2icons": "png2icons-cli.js" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/ses": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/ses/-/ses-1.10.0.tgz", - "integrity": "sha512-HXmJbNEgY/4hsQfaz5dna39vVKNyvlElRmJYk+bjTqSXSElT0Hr6NKwWVg4j0TxP6IuHp/PNMoWJKIRXzmLbAQ==", - "license": "Apache-2.0", - "dependencies": { - "@endo/env-options": "^1.1.8" - } - }, - "node_modules/sm-crypto": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/sm-crypto/-/sm-crypto-0.3.13.tgz", - "integrity": "sha512-ztNF+pZq6viCPMA1A6KKu3bgpkmYti5avykRHbcFIdSipFdkVmfUw2CnpM2kBJyppIalqvczLNM3wR8OQ0pT5w==", - "license": "MIT", - "dependencies": { - "jsbn": "^1.1.0" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "bin": { - "tree-kill": "cli.js" - } - } + "name": "plugin", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "axios": "^1.7.9", + "chrome-remote-interface": "^0.33.2", + "crypto-js": "^4.2.0", + "exif-reader": "^2.0.1", + "iconv-lite": "^0.6.3", + "node-forge": "^1.3.1", + "png2icons": "^2.0.1", + "ses": "^1.10.0", + "sm-crypto": "^0.3.13", + "tree-kill": "^1.2.2" + } }, - "dependencies": { - "@endo/env-options": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@endo/env-options/-/env-options-1.1.8.tgz", - "integrity": "sha512-Xtxw9n33I4guo8q0sDyZiRuxlfaopM454AKiELgU7l3tqsylCut6IBZ0fPy4ltSHsBib7M3yF7OEMoIuLwzWVg==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", - "requires": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "exif-reader": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/exif-reader/-/exif-reader-2.0.1.tgz", - "integrity": "sha512-gCQ/86RiAWSjeSlalj1G99IC6XnxbwkvB91HLqhh8somj/YBtC/2xuplvyjDjlfO7NsmYREPPElu/Syuy/H52g==" - }, - "follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==" - }, - "form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, - "jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" - }, - "png2icons": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/png2icons/-/png2icons-2.0.1.tgz", - "integrity": "sha512-GDEQJr8OG4e6JMp7mABtXFSEpgJa1CCpbQiAR+EjhkHJHnUL9zPPtbOrjsMD8gUbikgv3j7x404b0YJsV3aVFA==" - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "ses": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/ses/-/ses-1.10.0.tgz", - "integrity": "sha512-HXmJbNEgY/4hsQfaz5dna39vVKNyvlElRmJYk+bjTqSXSElT0Hr6NKwWVg4j0TxP6IuHp/PNMoWJKIRXzmLbAQ==", - "requires": { - "@endo/env-options": "^1.1.8" - } - }, - "sm-crypto": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/sm-crypto/-/sm-crypto-0.3.13.tgz", - "integrity": "sha512-ztNF+pZq6viCPMA1A6KKu3bgpkmYti5avykRHbcFIdSipFdkVmfUw2CnpM2kBJyppIalqvczLNM3wR8OQ0pT5w==", - "requires": { - "jsbn": "^1.1.0" - } - }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==" + "node_modules/@endo/env-options": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@endo/env-options/-/env-options-1.1.8.tgz", + "integrity": "sha512-Xtxw9n33I4guo8q0sDyZiRuxlfaopM454AKiELgU7l3tqsylCut6IBZ0fPy4ltSHsBib7M3yF7OEMoIuLwzWVg==", + "license": "Apache-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/chrome-remote-interface": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.33.2.tgz", + "integrity": "sha512-wvm9cOeBTrb218EC+6DteGt92iXr2iY0+XJP30f15JVDhqvWvJEVACh9GvUm8b9Yd8bxQivaLSb8k7mgrbyomQ==", + "dependencies": { + "commander": "2.11.x", + "ws": "^7.2.0" + }, + "bin": { + "chrome-remote-interface": "bin/client.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/exif-reader": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/exif-reader/-/exif-reader-2.0.1.tgz", + "integrity": "sha512-gCQ/86RiAWSjeSlalj1G99IC6XnxbwkvB91HLqhh8somj/YBtC/2xuplvyjDjlfO7NsmYREPPElu/Syuy/H52g==", + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/png2icons": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/png2icons/-/png2icons-2.0.1.tgz", + "integrity": "sha512-GDEQJr8OG4e6JMp7mABtXFSEpgJa1CCpbQiAR+EjhkHJHnUL9zPPtbOrjsMD8gUbikgv3j7x404b0YJsV3aVFA==", + "license": "MIT", + "bin": { + "png2icons": "png2icons-cli.js" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/ses": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/ses/-/ses-1.10.0.tgz", + "integrity": "sha512-HXmJbNEgY/4hsQfaz5dna39vVKNyvlElRmJYk+bjTqSXSElT0Hr6NKwWVg4j0TxP6IuHp/PNMoWJKIRXzmLbAQ==", + "license": "Apache-2.0", + "dependencies": { + "@endo/env-options": "^1.1.8" + } + }, + "node_modules/sm-crypto": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/sm-crypto/-/sm-crypto-0.3.13.tgz", + "integrity": "sha512-ztNF+pZq6viCPMA1A6KKu3bgpkmYti5avykRHbcFIdSipFdkVmfUw2CnpM2kBJyppIalqvczLNM3wR8OQ0pT5w==", + "license": "MIT", + "dependencies": { + "jsbn": "^1.1.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } } + }, + "dependencies": { + "@endo/env-options": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@endo/env-options/-/env-options-1.1.8.tgz", + "integrity": "sha512-Xtxw9n33I4guo8q0sDyZiRuxlfaopM454AKiELgU7l3tqsylCut6IBZ0fPy4ltSHsBib7M3yF7OEMoIuLwzWVg==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "requires": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "chrome-remote-interface": { + "version": "0.33.2", + "resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.33.2.tgz", + "integrity": "sha512-wvm9cOeBTrb218EC+6DteGt92iXr2iY0+XJP30f15JVDhqvWvJEVACh9GvUm8b9Yd8bxQivaLSb8k7mgrbyomQ==", + "requires": { + "commander": "2.11.x", + "ws": "^7.2.0" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" + }, + "crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "exif-reader": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/exif-reader/-/exif-reader-2.0.1.tgz", + "integrity": "sha512-gCQ/86RiAWSjeSlalj1G99IC6XnxbwkvB91HLqhh8somj/YBtC/2xuplvyjDjlfO7NsmYREPPElu/Syuy/H52g==" + }, + "follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==" + }, + "form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, + "png2icons": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/png2icons/-/png2icons-2.0.1.tgz", + "integrity": "sha512-GDEQJr8OG4e6JMp7mABtXFSEpgJa1CCpbQiAR+EjhkHJHnUL9zPPtbOrjsMD8gUbikgv3j7x404b0YJsV3aVFA==" + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "ses": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/ses/-/ses-1.10.0.tgz", + "integrity": "sha512-HXmJbNEgY/4hsQfaz5dna39vVKNyvlElRmJYk+bjTqSXSElT0Hr6NKwWVg4j0TxP6IuHp/PNMoWJKIRXzmLbAQ==", + "requires": { + "@endo/env-options": "^1.1.8" + } + }, + "sm-crypto": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/sm-crypto/-/sm-crypto-0.3.13.tgz", + "integrity": "sha512-ztNF+pZq6viCPMA1A6KKu3bgpkmYti5avykRHbcFIdSipFdkVmfUw2CnpM2kBJyppIalqvczLNM3wR8OQ0pT5w==", + "requires": { + "jsbn": "^1.1.0" + } + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==" + }, + "ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "requires": {} + } + } } diff --git a/plugin/package.json b/plugin/package.json index 8a1b620..d1ef758 100644 --- a/plugin/package.json +++ b/plugin/package.json @@ -1,13 +1,14 @@ { - "dependencies": { - "axios": "^1.7.9", - "crypto-js": "^4.2.0", - "exif-reader": "^2.0.1", - "iconv-lite": "^0.6.3", - "node-forge": "^1.3.1", - "png2icons": "^2.0.1", - "ses": "^1.10.0", - "sm-crypto": "^0.3.13", - "tree-kill": "^1.2.2" - } + "dependencies": { + "axios": "^1.7.9", + "chrome-remote-interface": "^0.33.2", + "crypto-js": "^4.2.0", + "exif-reader": "^2.0.1", + "iconv-lite": "^0.6.3", + "node-forge": "^1.3.1", + "png2icons": "^2.0.1", + "ses": "^1.10.0", + "sm-crypto": "^0.3.13", + "tree-kill": "^1.2.2" + } } diff --git a/src/js/composer/commands/browserCommands.js b/src/js/composer/commands/browserCommands.js new file mode 100644 index 0000000..9cbe762 --- /dev/null +++ b/src/js/composer/commands/browserCommands.js @@ -0,0 +1,399 @@ +import { newVarInputVal } from "js/composer/varInputValManager"; + +export const browserCommands = { + label: "浏览器控制", + icon: "web", + defaultOpened: false, + commands: [ + { + value: "quickcomposer.browser.launchBrowser", + label: "启动浏览器", + icon: "launch", + isAsync: true, + config: [ + { + component: "OptionEditor", + icon: "settings", + width: 12, + options: { + browserType: { + component: "ButtonGroup", + defaultValue: "msedge", + options: [ + { label: "Edge", value: "msedge" }, + { label: "Chrome", value: "chrome" }, + ], + width: 12, + }, + useSingleUserDataDir: { + label: "使用独立用户数据目录", + component: "CheckButton", + width: 6, + }, + headless: { + label: "启用无头模式", + component: "CheckButton", + width: 6, + }, + windowSize: { + label: "窗口尺寸", + component: "VariableInput", + icon: "window", + width: 6, + placeholder: "如1920x1080,不设置则最大化", + }, + proxy: { + label: "代理", + component: "VariableInput", + icon: "vpn_lock", + width: 6, + placeholder: "如 socks5://127.0.0.1:7890", + }, + browserPath: { + label: "浏览器路径", + component: "VariableInput", + icon: "folder", + width: 12, + options: { + dialog: { + type: "open", + options: { + title: "选择浏览器", + properties: ["openFile"], + }, + }, + }, + placeholder: "二进制绝对路径,留空则自动查找", + }, + }, + defaultValue: { + browserType: "msedge", + useSingleUserDataDir: true, + headless: false, + }, + }, + ], + }, + { + value: "quickcomposer.browser.getUrl", + label: "浏览器操作", + icon: "web", + isAsync: true, + subCommands: [ + { + value: "quickcomposer.browser.getUrl", + label: "获取当前地址", + icon: "link", + }, + { + value: "quickcomposer.browser.setUrl", + label: "设置当前地址", + icon: "link", + config: [ + { + label: "网址", + component: "VariableInput", + icon: "link", + width: 12, + placeholder: "输入网址", + }, + ], + }, + { + value: "quickcomposer.browser.getTabs", + label: "获取标签列表", + icon: "tab", + }, + { + value: "quickcomposer.browser.activateTab", + label: "切换标签", + icon: "tab_unselected", + config: [ + { + label: "标签索引", + component: "NumberInput", + icon: "tab", + min: 1, + defaultValue: 1, + width: 12, + }, + ], + }, + { + value: "quickcomposer.browser.executeScript", + label: "执行脚本", + icon: "code", + config: [ + { + label: "脚本内容", + component: "CodeEditor", + icon: "code", + width: 12, + placeholder: "输入JavaScript代码", + }, + { + topLabel: "要传递的参数", + component: "DictEditor", + icon: "data_array", + width: 12, + }, + ], + }, + { + value: "quickcomposer.browser.clickElement", + label: "点击元素", + icon: "mouse", + config: [ + { + label: "选择器", + component: "VariableInput", + icon: "code", + width: 12, + placeholder: "输入CSS选择器", + }, + ], + }, + { + value: "quickcomposer.browser.inputText", + label: "输入文本", + icon: "edit", + config: [ + { + label: "选择器", + component: "VariableInput", + icon: "code", + width: 12, + placeholder: "输入CSS选择器", + }, + { + label: "文本内容", + component: "VariableInput", + icon: "edit", + width: 12, + placeholder: "输入要填写的文本", + }, + ], + }, + { + value: "quickcomposer.browser.getText", + label: "获取文本", + icon: "text_fields", + config: [ + { + label: "选择器", + component: "VariableInput", + icon: "code", + width: 12, + placeholder: "输入CSS选择器", + }, + ], + }, + { + value: "quickcomposer.browser.getHtml", + label: "获取HTML", + icon: "code", + config: [ + { + label: "选择器", + component: "VariableInput", + icon: "code", + width: 12, + placeholder: "输入CSS选择器", + }, + ], + }, + { + value: "quickcomposer.browser.hideElement", + label: "隐藏元素", + icon: "visibility_off", + config: [ + { + label: "选择器", + component: "VariableInput", + icon: "code", + width: 12, + placeholder: "输入CSS选择器", + }, + ], + }, + { + value: "quickcomposer.browser.showElement", + label: "显示元素", + icon: "visibility", + config: [ + { + label: "选择器", + component: "VariableInput", + icon: "code", + width: 12, + placeholder: "输入CSS选择器", + }, + ], + }, + { + value: "quickcomposer.browser.injectCSS", + label: "注入CSS", + icon: "style", + config: [ + { + label: "CSS内容", + component: "CodeEditor", + icon: "style", + width: 12, + placeholder: "输入CSS代码", + }, + ], + }, + { + value: "quickcomposer.browser.setCookie", + label: "设置Cookie", + icon: "cookie", + config: [ + { + label: "Cookie", + component: "ArrayEditor", + icon: "cookie", + width: 12, + columns: { + name: { + label: "名称", + defaultValue: newVarInputVal("str"), + }, + value: { + label: "值", + defaultValue: newVarInputVal("str"), + }, + }, + }, + { + label: "选项", + component: "OptionEditor", + icon: "settings", + width: 12, + options: { + expires: { + label: "过期时间", + component: "QSelect", + icon: "timer", + width: 6, + options: [ + { label: "关闭浏览器失效", value: false }, + { label: "1小时", value: 1 }, + { label: "1天", value: 24 }, + { label: "1年", value: 24 * 365 }, + ], + }, + path: { + label: "路径", + component: "VariableInput", + icon: "folder", + width: 6, + }, + domain: { + label: "域名", + component: "VariableInput", + icon: "domain", + width: 6, + }, + secure: { + label: "安全", + component: "CheckButton", + icon: "lock", + width: 6, + }, + }, + defaultValue: { + expires: false, + path: newVarInputVal("str", "/"), + domain: newVarInputVal("str", ""), + secure: false, + }, + }, + ], + }, + { + value: "quickcomposer.browser.getCookie", + label: "获取Cookie", + icon: "cookie", + config: [ + { + label: "名称", + component: "VariableInput", + icon: "label", + width: 12, + placeholder: "输入Cookie名称", + }, + ], + }, + { + value: "quickcomposer.browser.scrollTo", + label: "滚动到位置", + icon: "open_in_full", + config: [ + { + label: "X坐标", + component: "NumberInput", + icon: "arrow_right", + width: 12, + defaultValue: 0, + }, + { + label: "Y坐标", + component: "NumberInput", + icon: "arrow_drop_down", + width: 12, + defaultValue: 0, + }, + ], + }, + { + value: "quickcomposer.browser.scrollToElement", + label: "滚动到元素", + icon: "open_in_full", + config: [ + { + label: "选择器", + component: "VariableInput", + icon: "code", + width: 12, + placeholder: "输入CSS选择器", + }, + ], + }, + { + value: "quickcomposer.browser.getScrollPosition", + label: "获取滚动位置", + icon: "open_in_full", + }, + { + value: "quickcomposer.browser.getPageSize", + label: "获取页面尺寸", + icon: "open_in_full", + }, + { + value: "quickcomposer.browser.waitForElement", + label: "等待元素", + icon: "hourglass_empty", + config: [ + { + label: "选择器", + component: "VariableInput", + icon: "code", + width: 12, + placeholder: "输入CSS选择器", + }, + { + label: "超时时间", + component: "NumberInput", + icon: "timer", + width: 12, + defaultValue: 5000, + min: 1000, + step: 1000, + }, + ], + }, + ], + }, + ], +}; diff --git a/src/js/composer/commands/index.js b/src/js/composer/commands/index.js index f2c510c..961f9d2 100644 --- a/src/js/composer/commands/index.js +++ b/src/js/composer/commands/index.js @@ -18,6 +18,7 @@ import { windowsCommands } from "./windowsCommands"; import { statusCommands } from "./statusCommands"; import { macosCommands } from "./macosCommands"; import { scriptCommands } from "./scriptCommands"; +import { browserCommands } from "./browserCommands"; const platformCommands = { win32: [windowsCommands], @@ -33,6 +34,7 @@ export const commandCategories = [ imageCommands, utoolsCommands, ...platformCommands[window.processPlatform], + browserCommands, dataCommands, codingCommands, controlCommands,