diff --git a/.env.example b/.env.example index 2b78eb3..986aac3 100644 --- a/.env.example +++ b/.env.example @@ -9,5 +9,5 @@ # ChatGPT # ----------------------------------------------------------------------------- -# see the readme for how to find this -SESSION_TOKEN= +EMAIL= +PASSWORD= diff --git a/src/demo-conversation.ts b/demos/demo-conversation.ts similarity index 76% rename from src/demo-conversation.ts rename to demos/demo-conversation.ts index 915f39b..35967f9 100644 --- a/src/demo-conversation.ts +++ b/demos/demo-conversation.ts @@ -1,7 +1,8 @@ import dotenv from 'dotenv-safe' import { oraPromise } from 'ora' -import { ChatGPTAPI } from '.' +import { ChatGPTAPI } from '../src' +import { getOpenAIAuthInfo } from './openai-auth-puppeteer' dotenv.config() @@ -13,7 +14,15 @@ dotenv.config() * ``` */ async function main() { - const api = new ChatGPTAPI({ sessionToken: process.env.SESSION_TOKEN }) + const email = process.env.EMAIL + const password = process.env.PASSWORD + + const authInfo = await getOpenAIAuthInfo({ + email, + password + }) + + const api = new ChatGPTAPI({ ...authInfo }) await api.ensureAuth() const conversation = api.getConversation() diff --git a/src/demo.ts b/demos/demo.ts similarity index 65% rename from src/demo.ts rename to demos/demo.ts index dcfd028..10568ca 100644 --- a/src/demo.ts +++ b/demos/demo.ts @@ -1,7 +1,8 @@ import dotenv from 'dotenv-safe' import { oraPromise } from 'ora' -import { ChatGPTAPI } from '.' +import { ChatGPTAPI } from '../src' +import { getOpenAIAuthInfo } from './openai-auth-puppeteer' dotenv.config() @@ -13,7 +14,15 @@ dotenv.config() * ``` */ async function main() { - const api = new ChatGPTAPI({ sessionToken: process.env.SESSION_TOKEN }) + const email = process.env.EMAIL + const password = process.env.PASSWORD + + const authInfo = await getOpenAIAuthInfo({ + email, + password + }) + + const api = new ChatGPTAPI({ ...authInfo }) await api.ensureAuth() const prompt = diff --git a/demos/openai-auth-puppeteer.ts b/demos/openai-auth-puppeteer.ts new file mode 100644 index 0000000..55f9255 --- /dev/null +++ b/demos/openai-auth-puppeteer.ts @@ -0,0 +1,113 @@ +import delay from 'delay' +import { + type Browser, + type Page, + type Protocol, + type PuppeteerLaunchOptions +} from 'puppeteer' +import puppeteer from 'puppeteer-extra' +import StealthPlugin from 'puppeteer-extra-plugin-stealth' + +puppeteer.use(StealthPlugin()) + +export type OpenAIAuthInfo = { + userAgent: string + clearanceToken: string + sessionToken: string + cookies?: Record +} + +/** + * Bypasses OpenAI's use of Cloudflare to get the cookies required to use + * ChatGPT. Uses Puppeteer with a stealth plugin under the hood. + */ +export async function getOpenAIAuthInfo({ + email, + password, + timeout = 2 * 60 * 1000, + browser +}: { + email: string + password: string + timeout?: number + browser?: Browser +}): Promise { + let page: Page + let origBrowser = browser + + try { + if (!browser) { + browser = await getBrowser() + } + + const userAgent = await browser.userAgent() + page = (await browser.pages())[0] || (await browser.newPage()) + page.setDefaultTimeout(timeout) + + await page.goto('https://chat.openai.com/auth/login') + await page.waitForSelector('#__next .btn-primary', { timeout }) + await delay(1000) + + if (email && password) { + await Promise.all([ + page.click('#__next .btn-primary'), + page.waitForNavigation({ + waitUntil: 'networkidle0' + }) + ]) + await page.type('#username', email, { delay: 10 }) + await page.click('button[type="submit"]') + await page.waitForSelector('#password') + await page.type('#password', password, { delay: 10 }) + await Promise.all([ + page.click('button[type="submit"]'), + page.waitForNavigation({ + waitUntil: 'networkidle0' + }) + ]) + } + + const pageCookies = await page.cookies() + const cookies = pageCookies.reduce( + (map, cookie) => ({ ...map, [cookie.name]: cookie }), + {} + ) + + const authInfo: OpenAIAuthInfo = { + userAgent, + clearanceToken: cookies['cf_clearance']?.value, + sessionToken: cookies['__Secure-next-auth.session-token']?.value, + cookies + } + + return authInfo + } catch (err) { + console.error(err) + throw null + } finally { + if (origBrowser) { + if (page) { + await page.close() + } + } else if (browser) { + await browser.close() + } + + page = null + browser = null + } +} + +export async function getBrowser(launchOptions?: PuppeteerLaunchOptions) { + const macChromePath = + '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' + + return puppeteer.launch({ + headless: false, + args: ['--no-sandbox', '--exclude-switches', 'enable-automation'], + ignoreHTTPSErrors: true, + // executablePath: executablePath() + executablePath: macChromePath, + ...launchOptions + }) +} diff --git a/package.json b/package.json index 92b5137..fedfee3 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,6 @@ "prepare": "husky install", "pre-commit": "lint-staged", "test": "run-p test:*", - "test:unit": "ava", "test:prettier": "prettier '**/*.{js,jsx,ts,tsx}' --check" }, "dependencies": { @@ -51,12 +50,16 @@ "@types/uuid": "^9.0.0", "ava": "^5.1.0", "del-cli": "^5.0.0", + "delay": "^5.0.0", "dotenv-safe": "^8.2.0", "husky": "^8.0.2", "lint-staged": "^13.0.3", "npm-run-all": "^4.1.5", "ora": "^6.1.2", "prettier": "^2.8.0", + "puppeteer": "^19.4.0", + "puppeteer-extra": "^3.3.4", + "puppeteer-extra-plugin-stealth": "^2.11.1", "tsup": "^6.5.0", "tsx": "^3.12.1", "typedoc": "^0.23.21", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8bb76f7..02b5c3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,6 +6,7 @@ specifiers: '@types/uuid': ^9.0.0 ava: ^5.1.0 del-cli: ^5.0.0 + delay: ^5.0.0 dotenv-safe: ^8.2.0 eventsource-parser: ^0.0.5 expiry-map: ^2.0.0 @@ -15,6 +16,9 @@ specifiers: ora: ^6.1.2 p-timeout: ^6.0.0 prettier: ^2.8.0 + puppeteer: ^19.4.0 + puppeteer-extra: ^3.3.4 + puppeteer-extra-plugin-stealth: ^2.11.1 remark: ^14.0.2 strip-markdown: ^5.0.0 tsup: ^6.5.0 @@ -42,12 +46,16 @@ devDependencies: '@types/uuid': 9.0.0 ava: 5.1.0 del-cli: 5.0.0 + delay: 5.0.0 dotenv-safe: 8.2.0 husky: 8.0.2 lint-staged: 13.0.4 npm-run-all: 4.1.5 ora: 6.1.2 prettier: 2.8.0 + puppeteer: 19.4.0 + puppeteer-extra: 3.3.4_puppeteer@19.4.0 + puppeteer-extra-plugin-stealth: 2.11.1_puppeteer-extra@3.3.4 tsup: 6.5.0_typescript@4.9.3 tsx: 3.12.1 typedoc: 0.23.21_typescript@4.9.3 @@ -420,7 +428,6 @@ packages: resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} dependencies: '@types/ms': 0.7.31 - dev: false /@types/mdast/3.0.10: resolution: {integrity: sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==} @@ -434,7 +441,6 @@ packages: /@types/ms/0.7.31: resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} - dev: false /@types/node/18.11.10: resolution: {integrity: sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==} @@ -452,6 +458,14 @@ packages: resolution: {integrity: sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==} dev: true + /@types/yauzl/2.10.0: + resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} + requiresBuild: true + dependencies: + '@types/node': 18.11.10 + dev: true + optional: true + /acorn-walk/8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} @@ -463,6 +477,15 @@ packages: hasBin: true dev: true + /agent-base/6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /aggregate-error/3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} @@ -533,6 +556,15 @@ packages: sprintf-js: 1.0.3 dev: true + /argparse/2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /arr-union/3.1.0: + resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==} + engines: {node: '>=0.10.0'} + dev: true + /array-find-index/1.0.2: resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==} engines: {node: '>=0.10.0'} @@ -639,6 +671,14 @@ packages: engines: {node: '>=8'} dev: true + /bl/4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.0 + dev: true + /bl/5.1.0: resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} dependencies: @@ -682,10 +722,21 @@ packages: update-browserslist-db: 1.0.10_browserslist@4.21.4 dev: true + /buffer-crc32/0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + dev: true + /buffer-from/1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true + /buffer/5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + /buffer/6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} dependencies: @@ -723,6 +774,11 @@ packages: get-intrinsic: 1.1.3 dev: true + /callsites/3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + /callsites/4.0.0: resolution: {integrity: sha512-y3jRROutgpKdz5vzEhWM34TidDU8vkJppF8dszITeb1PQmSqV3DTxyV8G/lyO/DNvtE1YTedehmw9MPZsCBHxQ==} engines: {node: '>=12.20'} @@ -787,6 +843,10 @@ packages: fsevents: 2.3.2 dev: true + /chownr/1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + dev: true + /chunkd/2.0.1: resolution: {integrity: sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==} dev: true @@ -861,6 +921,17 @@ packages: wrap-ansi: 7.0.0 dev: true + /clone-deep/0.2.4: + resolution: {integrity: sha512-we+NuQo2DHhSl+DP6jlUiAhyAjBQrYnpOk15rN6c6JSPScjiCLh8IbSU+VTcph6YS3o7mASE8a0+gbZ7ChLpgg==} + engines: {node: '>=0.10.0'} + dependencies: + for-own: 0.1.5 + is-plain-object: 2.0.4 + kind-of: 3.2.2 + lazy-cache: 1.0.4 + shallow-clone: 0.1.2 + dev: true + /clone/1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} @@ -939,6 +1010,24 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true + /cosmiconfig/8.0.0: + resolution: {integrity: sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==} + engines: {node: '>=14'} + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + dev: true + + /cross-fetch/3.1.5: + resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==} + dependencies: + node-fetch: 2.6.7 + transitivePeerDependencies: + - encoding + dev: true + /cross-spawn/6.0.5: resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} engines: {node: '>=4.8'} @@ -1008,6 +1097,11 @@ packages: character-entities: 2.0.2 dev: false + /deepmerge/4.2.2: + resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} + engines: {node: '>=0.10.0'} + dev: true + /defaults/1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: @@ -1045,11 +1139,20 @@ packages: slash: 4.0.0 dev: true + /delay/5.0.0: + resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==} + engines: {node: '>=10'} + dev: true + /dequal/2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} dev: false + /devtools-protocol/0.0.1068969: + resolution: {integrity: sha512-ATFTrPbY1dKYhPPvpjtwWKSK2mIwGmRwX54UASn9THEuIZCe2n9k3vVuMmt6jWeL+e5QaaguEv/pMyR+JQB7VQ==} + dev: true + /diff/5.1.0: resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} engines: {node: '>=0.3.1'} @@ -1094,6 +1197,12 @@ packages: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: true + /end-of-stream/1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: true + /error-ex/1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: @@ -1426,6 +1535,20 @@ packages: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} dev: false + /extract-zip/2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + dependencies: + debug: 4.3.4 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.0 + transitivePeerDependencies: + - supports-color + dev: true + /fast-diff/1.2.0: resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} dev: true @@ -1447,6 +1570,12 @@ packages: reusify: 1.0.4 dev: true + /fd-slicer/1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + dependencies: + pend: 1.2.0 + dev: true + /figures/5.0.0: resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} engines: {node: '>=14'} @@ -1478,6 +1607,36 @@ packages: path-exists: 5.0.0 dev: true + /for-in/0.1.8: + resolution: {integrity: sha512-F0to7vbBSHP8E3l6dCjxNOLuSFAACIxFy3UehTUlG7svlXi37HHsDkyVcHo0Pq8QwrE+pXvWSVX3ZT1T9wAZ9g==} + engines: {node: '>=0.10.0'} + dev: true + + /for-in/1.0.2: + resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} + engines: {node: '>=0.10.0'} + dev: true + + /for-own/0.1.5: + resolution: {integrity: sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==} + engines: {node: '>=0.10.0'} + dependencies: + for-in: 1.0.2 + dev: true + + /fs-constants/1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + dev: true + + /fs-extra/10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + dependencies: + graceful-fs: 4.2.10 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + /fs.realpath/1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true @@ -1526,6 +1685,13 @@ packages: has-symbols: 1.0.3 dev: true + /get-stream/5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + dependencies: + pump: 3.0.0 + dev: true + /get-stream/6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -1667,6 +1833,16 @@ packages: lru-cache: 6.0.0 dev: true + /https-proxy-agent/5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /human-signals/2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -1697,6 +1873,14 @@ packages: engines: {node: '>= 4'} dev: true + /import-fresh/3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + /imurmurhash/0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -1762,6 +1946,10 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-buffer/1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + dev: true + /is-buffer/2.0.5: resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} engines: {node: '>=4'} @@ -1789,6 +1977,11 @@ packages: resolution: {integrity: sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==} dev: true + /is-extendable/0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + dev: true + /is-extglob/2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1853,6 +2046,13 @@ packages: engines: {node: '>=12'} dev: false + /is-plain-object/2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + dependencies: + isobject: 3.0.1 + dev: true + /is-plain-object/5.0.0: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} @@ -1915,6 +2115,11 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true + /isobject/3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + dev: true + /javascript-natural-sort/0.7.1: resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} dev: true @@ -1941,6 +2146,13 @@ packages: esprima: 4.0.1 dev: true + /js-yaml/4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + /jsesc/2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} @@ -1965,6 +2177,28 @@ packages: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} dev: true + /jsonfile/6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.0 + optionalDependencies: + graceful-fs: 4.2.10 + dev: true + + /kind-of/2.0.1: + resolution: {integrity: sha512-0u8i1NZ/mg0b+W3MGGw5I7+6Eib2nx72S/QvXa0hYjEkjTknYmEYQJwGu3mLC0BrhtJjtQafTkyRUQ75Kx0LVg==} + engines: {node: '>=0.10.0'} + dependencies: + is-buffer: 1.1.6 + dev: true + + /kind-of/3.2.2: + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + engines: {node: '>=0.10.0'} + dependencies: + is-buffer: 1.1.6 + dev: true + /kind-of/6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -1975,6 +2209,16 @@ packages: engines: {node: '>=6'} dev: false + /lazy-cache/0.2.7: + resolution: {integrity: sha512-gkX52wvU/R8DVMMt78ATVPFMJqfW8FPz1GZ1sVHBVQHmu/WvhIWE4cE1GBzhJNFicDeYhnwp6Rl35BcAIM3YOQ==} + engines: {node: '>=0.10.0'} + dev: true + + /lazy-cache/1.0.4: + resolution: {integrity: sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==} + engines: {node: '>=0.10.0'} + dev: true + /lilconfig/2.0.6: resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==} engines: {node: '>=10'} @@ -2211,6 +2455,15 @@ packages: yargs-parser: 20.2.9 dev: true + /merge-deep/3.0.3: + resolution: {integrity: sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA==} + engines: {node: '>=0.10.0'} + dependencies: + arr-union: 3.1.0 + clone-deep: 0.2.4 + kind-of: 3.2.2 + dev: true + /merge-stream/2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} dev: true @@ -2445,6 +2698,18 @@ packages: resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} dev: true + /mixin-object/2.0.1: + resolution: {integrity: sha512-ALGF1Jt9ouehcaXaHhn6t1yGWRqGaHkPFndtFVHfZXOvkIZ/yoGaSi0AHVTafb3ZBGg4dr/bDwnaEKqCXzchMA==} + engines: {node: '>=0.10.0'} + dependencies: + for-in: 0.1.8 + is-extendable: 0.1.1 + dev: true + + /mkdirp-classic/0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + dev: true + /mri/1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -2473,6 +2738,18 @@ packages: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} dev: true + /node-fetch/2.6.7: + resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: true + /node-releases/2.0.6: resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} dev: true @@ -2658,6 +2935,13 @@ packages: engines: {node: '>=14.16'} dev: false + /parent-module/1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + /parse-json/4.0.0: resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} engines: {node: '>=4'} @@ -2727,6 +3011,10 @@ packages: engines: {node: '>=8'} dev: true + /pend/1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + dev: true + /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true @@ -2802,11 +3090,171 @@ packages: parse-ms: 3.0.0 dev: true + /progress/2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + dev: true + + /proxy-from-env/1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: true + + /pump/3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: true + /punycode/2.1.1: resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} engines: {node: '>=6'} dev: true + /puppeteer-core/19.4.0: + resolution: {integrity: sha512-gG/jxseleZStinBn86x8r7trjcE4jcjx1hIQWOpACQhquHYMuKnrWxkzg+EDn8sN3wUtF/Ry9mtJgjM49oUOFQ==} + engines: {node: '>=14.1.0'} + dependencies: + cross-fetch: 3.1.5 + debug: 4.3.4 + devtools-protocol: 0.0.1068969 + extract-zip: 2.0.1 + https-proxy-agent: 5.0.1 + proxy-from-env: 1.1.0 + rimraf: 3.0.2 + tar-fs: 2.1.1 + unbzip2-stream: 1.4.3 + ws: 8.10.0 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: true + + /puppeteer-extra-plugin-stealth/2.11.1_puppeteer-extra@3.3.4: + resolution: {integrity: sha512-n0wdC0Ilc9tk5L6FWLyd0P2gT8b2fp+2NuB+KB0oTSw3wXaZ0D6WNakjJsayJ4waGzIJFCUHkmK9zgx5NKMoFw==} + engines: {node: '>=8'} + peerDependencies: + playwright-extra: '*' + puppeteer-extra: '*' + peerDependenciesMeta: + playwright-extra: + optional: true + puppeteer-extra: + optional: true + dependencies: + debug: 4.3.4 + puppeteer-extra: 3.3.4_puppeteer@19.4.0 + puppeteer-extra-plugin: 3.2.2_puppeteer-extra@3.3.4 + puppeteer-extra-plugin-user-preferences: 2.4.0_puppeteer-extra@3.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /puppeteer-extra-plugin-user-data-dir/2.4.0_puppeteer-extra@3.3.4: + resolution: {integrity: sha512-qrhYPTGIqzL2hpeJ5DXjf8xMy5rt1UvcqSgpGTTOUOjIMz1ROWnKHjBoE9fNBJ4+ToRZbP8MzIDXWlEk/e1zJA==} + engines: {node: '>=8'} + peerDependencies: + playwright-extra: '*' + puppeteer-extra: '*' + peerDependenciesMeta: + playwright-extra: + optional: true + puppeteer-extra: + optional: true + dependencies: + debug: 4.3.4 + fs-extra: 10.1.0 + puppeteer-extra: 3.3.4_puppeteer@19.4.0 + puppeteer-extra-plugin: 3.2.2_puppeteer-extra@3.3.4 + rimraf: 3.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /puppeteer-extra-plugin-user-preferences/2.4.0_puppeteer-extra@3.3.4: + resolution: {integrity: sha512-4XxMhMkJ+qqLsPY9ULF90qS9Bj1Qrwwgp1TY9zTdp1dJuy7QSgYE7xlyamq3cKrRuzg3QUOqygJo52sVeXSg5A==} + engines: {node: '>=8'} + peerDependencies: + playwright-extra: '*' + puppeteer-extra: '*' + peerDependenciesMeta: + playwright-extra: + optional: true + puppeteer-extra: + optional: true + dependencies: + debug: 4.3.4 + deepmerge: 4.2.2 + puppeteer-extra: 3.3.4_puppeteer@19.4.0 + puppeteer-extra-plugin: 3.2.2_puppeteer-extra@3.3.4 + puppeteer-extra-plugin-user-data-dir: 2.4.0_puppeteer-extra@3.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /puppeteer-extra-plugin/3.2.2_puppeteer-extra@3.3.4: + resolution: {integrity: sha512-0uatQxzuVn8yegbrEwSk03wvwpMB5jNs7uTTnermylLZzoT+1rmAQaJXwlS3+vADUbw6ELNgNEHC7Skm0RqHbQ==} + engines: {node: '>=9.11.2'} + peerDependencies: + playwright-extra: '*' + puppeteer-extra: '*' + peerDependenciesMeta: + playwright-extra: + optional: true + puppeteer-extra: + optional: true + dependencies: + '@types/debug': 4.1.7 + debug: 4.3.4 + merge-deep: 3.0.3 + puppeteer-extra: 3.3.4_puppeteer@19.4.0 + transitivePeerDependencies: + - supports-color + dev: true + + /puppeteer-extra/3.3.4_puppeteer@19.4.0: + resolution: {integrity: sha512-fN5pHvSMJ8d1o7Z8wLLTQOUBpORD2BcFn+KDs7QnkGZs9SV69hcUcce67vX4L4bNSEG3A0P6Osrv+vWNhhdm8w==} + engines: {node: '>=8'} + peerDependencies: + '@types/puppeteer': '*' + puppeteer: '*' + puppeteer-core: '*' + peerDependenciesMeta: + '@types/puppeteer': + optional: true + puppeteer: + optional: true + puppeteer-core: + optional: true + dependencies: + '@types/debug': 4.1.7 + debug: 4.3.4 + deepmerge: 4.2.2 + puppeteer: 19.4.0 + transitivePeerDependencies: + - supports-color + dev: true + + /puppeteer/19.4.0: + resolution: {integrity: sha512-sRzWEfFSZCCcFUJflGtYI2V7A6qK4Jht+2JiI2LZgn+Nv/LOZZsBDEaGl98ZrS8oEcUA5on4p2yJbE0nzHNzIg==} + engines: {node: '>=14.1.0'} + requiresBuild: true + dependencies: + cosmiconfig: 8.0.0 + devtools-protocol: 0.0.1068969 + https-proxy-agent: 5.0.1 + progress: 2.0.3 + proxy-from-env: 1.1.0 + puppeteer-core: 19.4.0 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: true + /queue-microtask/1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true @@ -2918,6 +3366,11 @@ packages: resolve-from: 5.0.0 dev: true + /resolve-from/4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + /resolve-from/5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -3028,6 +3481,16 @@ packages: type-fest: 0.13.1 dev: true + /shallow-clone/0.1.2: + resolution: {integrity: sha512-J1zdXCky5GmNnuauESROVu31MQSnLoYvlyEn6j2Ztk6Q5EHFIhxkMhYcv6vuDzl2XEzoRr856QwzMgWM/TmZgw==} + engines: {node: '>=0.10.0'} + dependencies: + is-extendable: 0.1.1 + kind-of: 2.0.1 + lazy-cache: 0.2.7 + mixin-object: 2.0.1 + dev: true + /shebang-command/1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -3308,6 +3771,26 @@ packages: engines: {node: '>= 0.4'} dev: true + /tar-fs/2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + dev: true + + /tar-stream/2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.0 + dev: true + /temp-dir/3.0.0: resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==} engines: {node: '>=14.16'} @@ -3347,6 +3830,10 @@ packages: is-number: 7.0.0 dev: true + /tr46/0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: true + /tr46/1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} dependencies: @@ -3483,6 +3970,13 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /unbzip2-stream/1.4.3: + resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + dependencies: + buffer: 5.7.1 + through: 2.3.8 + dev: true + /undici/5.13.0: resolution: {integrity: sha512-UDZKtwb2k7KRsK4SdXWG7ErXiL7yTGgLWvk2AXO1JMjgjh404nFo6tWSCM2xMpJwMPx3J8i/vfqEh1zOqvj82Q==} engines: {node: '>=12.18'} @@ -3529,6 +4023,11 @@ packages: unist-util-visit-parents: 5.1.1 dev: false + /universalify/2.0.0: + resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + engines: {node: '>= 10.0.0'} + dev: true + /update-browserslist-db/1.0.10_browserslist@4.21.4: resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} hasBin: true @@ -3597,6 +4096,10 @@ packages: defaults: 1.0.4 dev: true + /webidl-conversions/3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: true + /webidl-conversions/4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} dev: true @@ -3606,6 +4109,13 @@ packages: engines: {node: '>=6'} dev: true + /whatwg-url/5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: true + /whatwg-url/7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} dependencies: @@ -3673,6 +4183,19 @@ packages: signal-exit: 3.0.7 dev: true + /ws/8.10.0: + resolution: {integrity: sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + /y18n/5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -3715,6 +4238,13 @@ packages: yargs-parser: 21.1.1 dev: true + /yauzl/2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + dev: true + /yocto-queue/0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} diff --git a/readme.md b/readme.md index 4a8e0d4..5a19876 100644 --- a/readme.md +++ b/readme.md @@ -1,22 +1,16 @@ -# Update December 11, 2022 +# Update December 12, 2022 -Today, OpenAI added additional Cloudflare protections that make it more difficult to access the unofficial API. _This is affecting all ChatGPT API wrappers at the moment_, including the Python ones. See [this issue](https://github.com/transitive-bullshit/chatgpt-api/issues/96). +Yesterday, OpenAI added additional Cloudflare protections that make it more difficult to access the unofficial API. -**As a temporary workaround**, make sure you're using the latest version of this package and Node.js >= 18: +The demos have been updated to use Puppeteer to log in to ChatGPT and extract the Cloudflare `cf_clearance` cookie and OpenAI session token. 🔥 -1. Log into https://chat.openai.com/chat and copy a fresh session token (same instructions as below). - -2. Copy the value of the `cf_clearance` cookie and store it in a `CLEARANCE_TOKEN` environment variable in addition to your `SESSION_TOKEN`. - -3. Copy your browser's `user-agent` header from any request in your browser's network tab. - -4. Use both tokens when creating the API wrapper: +To use the updated version, first make sure you're using the latest version of this package and Node.js >= 18: ```ts const api = new ChatGPTAPI({ sessionToken: process.env.SESSION_TOKEN, clearanceToken: process.env.CLEARANCE_TOKEN, - userAgent: '' // replace to match your real browser's user agent + userAgent: '' // needs to match your browser's user agent }) await api.ensureAuth() @@ -24,9 +18,10 @@ await api.ensureAuth() Restrictions on this method: -- Cloudflare `cf_clearance` **tokens expire after 2 hours**, so right now you'll have to manually log in and extract it by hand every so often +- Cloudflare `cf_clearance` **tokens expire after 2 hours**, so right now we recommend that you refresh your `cf_clearance` token every ~45 minutes or so. - Your `user-agent` and `IP address` **must match** from the real browser window you're logged in with to the one you're using for `ChatGPTAPI`. - This means that you currently can't log in with your laptop and then run the bot on a server or proxy somewhere. +- Cloudflare will still sometimes ask you to complete a CAPTCHA, so you may need to keep an eye on it and manually resolve the CAPTCHA. Automated CAPTCHA bypass is a WIP. - You must use `node >= 18`. I'm using `v19.2.0` in my testing, but for some reason, all `fetch` requests using Node.js `v16` and `v17` fail at the moment (these use `undici` under the hood, whereas Node.js v18 and above use a built-in `fetch` based on `undici`). - You should not be using this account while the bot is using it, because that browser window may refresh one of your tokens and invalidate the bot's session. @@ -47,7 +42,7 @@ Travis [![NPM](https://img.shields.io/npm/v/chatgpt.svg)](https://www.npmjs.com/package/chatgpt) [![Build Status](https://github.com/transitive-bullshit/chatgpt-api/actions/workflows/test.yml/badge.svg)](https://github.com/transitive-bullshit/chatgpt-api/actions/workflows/test.yml) [![MIT License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/transitive-bullshit/chatgpt-api/blob/main/license) [![Prettier Code Formatting](https://img.shields.io/badge/code_style-prettier-brightgreen.svg)](https://prettier.io) -- [Update December 11, 2022](#update-december-11-2022) +- [Update December 12, 2022](#update-december-12-2022) - [Intro](#intro) - [Install](#install) - [Usage](#usage) @@ -77,12 +72,12 @@ npm install chatgpt import { ChatGPTAPI } from 'chatgpt' async function example() { - // sessionToken is required; see below for details const api = new ChatGPTAPI({ - sessionToken: process.env.SESSION_TOKEN + sessionToken: process.env.SESSION_TOKEN, + clearanceToken: process.env.CLEARANCE_TOKEN, + userAgent: 'TODO' }) - // ensure the API is properly authenticated await api.ensureAuth() // send a message and wait for the response @@ -100,6 +95,8 @@ ChatGPT responses are formatted as markdown by default. If you want to work with ```ts const api = new ChatGPTAPI({ sessionToken: process.env.SESSION_TOKEN, + clearanceToken: process.env.CLEARANCE_TOKEN, + userAgent: 'TODO', markdown: false }) ``` @@ -108,7 +105,9 @@ If you want to automatically track the conversation, you can use `ChatGPTAPI.get ```ts const api = new ChatGPTAPI({ - sessionToken: process.env.SESSION_TOKEN + sessionToken: process.env.SESSION_TOKEN, + clearanceToken: process.env.CLEARANCE_TOKEN, + userAgent: 'TODO' }) const conversation = api.getConversation() @@ -145,7 +144,9 @@ async function example() { const { ChatGPTAPI } = await import('chatgpt') const api = new ChatGPTAPI({ - sessionToken: process.env.SESSION_TOKEN + sessionToken: process.env.SESSION_TOKEN, + clearanceToken: process.env.CLEARANCE_TOKEN, + userAgent: 'TODO' }) await api.ensureAuth() @@ -162,23 +163,21 @@ See the [auto-generated docs](./docs/classes/ChatGPTAPI.md) for more info on met ### Demos -A [basic demo](./src/demo.ts) is included for testing purposes: +To run the included demos: + +1. clone repo +2. install node deps +3. set `EMAIL` and `PASSWORD` in .env + +A [basic demo](./demos/demo.ts) is included for testing purposes: ```bash -# 1. clone repo -# 2. install node deps -# 3. set `SESSION_TOKEN` in .env -# 4. run: npx tsx src/demo.ts ``` -A [conversation demo](./src/demo-conversation.ts) is also included: +A [conversation demo](./demos/demo-conversation.ts) is also included: ```bash -# 1. clone repo -# 2. install node deps -# 3. set `SESSION_TOKEN` in .env -# 4. run: npx tsx src/demo-conversation.ts ``` @@ -186,15 +185,18 @@ npx tsx src/demo-conversation.ts **This package requires a valid session token from ChatGPT to access it's unofficial REST API.** -To get a session token: +As of December 11, 2021, it also requires a valid Cloudflare clearance token. + +There are two options to get these; either manually, or automated. For the automated way, see the `demos/` folder using Puppeteer. + +To get a session token manually: 1. Go to https://chat.openai.com/chat and log in or sign up. 2. Open dev tools. 3. Open `Application` > `Cookies`. ![ChatGPT cookies](./media/session-token.png) 4. Copy the value for `__Secure-next-auth.session-token` and save it to your environment. - -If you want to run the built-in demo, store this value as `SESSION_TOKEN` in a local `.env` file. +5. Copy the value for `cf_clearance` and save it to your environment. > **Note** > This package will switch to using the official API once it's released. diff --git a/src/chatgpt-api.test.ts b/src/chatgpt-api.test.ts index 78a06e4..e2b3be4 100644 --- a/src/chatgpt-api.test.ts +++ b/src/chatgpt-api.test.ts @@ -11,13 +11,16 @@ const isCI = !!process.env.CI test('ChatGPTAPI invalid session token', async (t) => { t.timeout(30 * 1000) // 30 seconds - t.throws(() => new ChatGPTAPI({ sessionToken: null }), { + t.throws(() => new ChatGPTAPI({ sessionToken: null, clearanceToken: null }), { message: 'ChatGPT invalid session token' }) await t.throwsAsync( async () => { - const chatgpt = new ChatGPTAPI({ sessionToken: 'invalid' }) + const chatgpt = new ChatGPTAPI({ + sessionToken: 'invalid', + clearanceToken: 'invalid' + }) await chatgpt.ensureAuth() }, { @@ -33,13 +36,18 @@ test('ChatGPTAPI valid session token', async (t) => { } t.notThrows( - () => new ChatGPTAPI({ sessionToken: 'fake valid session token' }) + () => + new ChatGPTAPI({ + sessionToken: 'fake valid session token', + clearanceToken: 'invalid' + }) ) await t.notThrowsAsync( (async () => { const chatgpt = new ChatGPTAPI({ - sessionToken: process.env.SESSION_TOKEN + sessionToken: process.env.SESSION_TOKEN, + clearanceToken: process.env.CLEARANCE_TOKEN }) // Don't make any real API calls using our session token if we're running on CI @@ -62,7 +70,10 @@ if (!isCI) { await t.throwsAsync( async () => { - const chatgpt = new ChatGPTAPI({ sessionToken: expiredSessionToken }) + const chatgpt = new ChatGPTAPI({ + sessionToken: expiredSessionToken, + clearanceToken: 'invalid' + }) await chatgpt.ensureAuth() }, { @@ -81,7 +92,8 @@ if (!isCI) { await t.throwsAsync( async () => { const chatgpt = new ChatGPTAPI({ - sessionToken: process.env.SESSION_TOKEN + sessionToken: process.env.SESSION_TOKEN, + clearanceToken: process.env.CLEARANCE_TOKEN }) await chatgpt.sendMessage('test', { @@ -100,7 +112,8 @@ if (!isCI) { await t.throwsAsync( async () => { const chatgpt = new ChatGPTAPI({ - sessionToken: process.env.SESSION_TOKEN + sessionToken: process.env.SESSION_TOKEN, + clearanceToken: process.env.CLEARANCE_TOKEN }) const abortController = new AbortController() diff --git a/src/chatgpt-api.ts b/src/chatgpt-api.ts index 8bd0a93..8a4bf3e 100644 --- a/src/chatgpt-api.ts +++ b/src/chatgpt-api.ts @@ -10,7 +10,7 @@ import { markdownToText } from './utils' const KEY_ACCESS_TOKEN = 'accessToken' const USER_AGENT = - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36' export class ChatGPTAPI { protected _sessionToken: string @@ -22,7 +22,6 @@ export class ChatGPTAPI { protected _headers: Record // Stores access tokens for `accessTokenTTL` milliseconds before needing to refresh - // (defaults to 60 seconds) protected _accessTokenCache: ExpiryMap protected _user: types.User | null = null @@ -52,10 +51,12 @@ export class ChatGPTAPI { /** @defaultValue `'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'` **/ userAgent?: string - /** @defaultValue 60000 (60 seconds) */ + /** @defaultValue 1 hour */ accessTokenTTL?: number accessToken?: string + + headers?: Record }) { const { sessionToken, @@ -64,8 +65,9 @@ export class ChatGPTAPI { apiBaseUrl = 'https://chat.openai.com/api', backendApiBaseUrl = 'https://chat.openai.com/backend-api', userAgent = USER_AGENT, - accessTokenTTL = 60000, // 60 seconds - accessToken + accessTokenTTL = 60 * 60000, // 1 hour + accessToken, + headers } = opts this._sessionToken = sessionToken @@ -75,11 +77,18 @@ export class ChatGPTAPI { this._backendApiBaseUrl = backendApiBaseUrl this._userAgent = userAgent this._headers = { - 'User-Agent': this._userAgent, + 'user-agent': this._userAgent, 'x-openai-assistant-app-id': '', 'accept-language': 'en-US,en;q=0.9', origin: 'https://chat.openai.com', - referer: 'https://chat.openai.com/chat' + referer: 'https://chat.openai.com/chat', + 'sec-ch-ua': + '"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"', + 'sec-ch-ua-platform': '"macOS"', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-origin', + ...headers } this._accessTokenCache = new ExpiryMap(accessTokenTTL) @@ -90,6 +99,10 @@ export class ChatGPTAPI { if (!this._sessionToken) { throw new types.ChatGPTError('ChatGPT invalid session token') } + + if (!this._clearanceToken) { + throw new types.ChatGPTError('ChatGPT invalid clearance token') + } } /** @@ -269,11 +282,15 @@ export class ChatGPTAPI { let response: Response try { + const headers = { + ...this._headers, + cookie: `cf_clearance=${this._clearanceToken}; __Secure-next-auth.session-token=${this._sessionToken}`, + accept: '*/*' + } + console.log(`${this._apiBaseUrl}/auth/session`, headers) + const res = await fetch(`${this._apiBaseUrl}/auth/session`, { - headers: { - ...this._headers, - cookie: `cf_clearance=${this._clearanceToken}; __Secure-next-auth.session-token=${this._sessionToken}` - } + headers }).then((r) => { response = r