binary-collections 2.0.8 → 2.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/binaries/binary-executor.cjs +43 -0
- package/{bin → binaries}/clean-nodemodule +0 -0
- package/binaries/clean-nodemodule.cjs +43 -0
- package/binaries/clean-nodemodule.cmd +4 -0
- package/{bin → binaries}/clean-nodemodules +0 -0
- package/binaries/clean-nodemodules.cjs +43 -0
- package/binaries/clean-nodemodules.cmd +5 -0
- package/{bin → binaries}/dev +0 -0
- package/binaries/dev.cjs +43 -0
- package/{bin → binaries}/empty +0 -0
- package/binaries/empty.cjs +43 -0
- package/{bin → binaries}/git-reduce-size +0 -0
- package/binaries/git-reduce-size.cjs +43 -0
- package/binaries/javakill.cjs +43 -0
- package/{bin → binaries}/javakill.cmd +0 -0
- package/{bin → binaries}/kill-process +0 -0
- package/binaries/kill-process.cjs +43 -0
- package/binaries/nodekill.cjs +43 -0
- package/{bin → binaries}/nodekill.ps1 +0 -0
- package/{bin → binaries}/prod +0 -0
- package/binaries/prod.cjs +43 -0
- package/{bin → binaries}/rmfind +0 -0
- package/binaries/rmfind.cjs +43 -0
- package/{bin → binaries}/rmx +0 -0
- package/binaries/rmx.cjs +43 -0
- package/{bin → binaries}/submodule-token +0 -0
- package/binaries/submodule-token.cjs +43 -0
- package/docs-src/clean-github-actions-caches.md +26 -0
- package/docs-src/free-chatgpt.md +26 -0
- package/lib/binary-collections-config.cjs +3 -2
- package/lib/binary-collections-config.mjs +2 -2
- package/lib/binary-collections.cjs +452 -87
- package/lib/binary-collections.mjs +247 -8
- package/lib/changelog.cjs +328 -0
- package/lib/changelog.mjs +205 -0
- package/lib/chunk-4EWQC6GZ.mjs +382 -0
- package/lib/chunk-4ZI7BQKQ.mjs +381 -0
- package/lib/chunk-5J2BEPY5.mjs +83 -0
- package/lib/{chunk-4BYBVEYC.mjs → chunk-66PAU5PS.mjs} +5 -4
- package/lib/chunk-6HHJRKFB.mjs +59 -0
- package/lib/chunk-6S4NXESK.mjs +26 -0
- package/lib/{chunk-ZYAQRPUL.mjs → chunk-7YD7IPFF.mjs} +2 -2
- package/lib/{chunk-SH3L6HHV.mjs → chunk-A2JQXI5Z.mjs} +2 -2
- package/lib/{chunk-EGSSKVDH.mjs → chunk-A3VUZEJK.mjs} +1 -1
- package/lib/chunk-AGZYRDC2.mjs +323 -0
- package/lib/{chunk-VVEZVNIV.mjs → chunk-AI4CVPJ7.mjs} +6 -6
- package/lib/chunk-BDCHCWHD.mjs +136 -0
- package/lib/chunk-BEZKJ25G.mjs +140 -0
- package/lib/chunk-DI5MDPSN.mjs +386 -0
- package/lib/{chunk-ONIBBBQ3.mjs → chunk-E6FDDAOO.mjs} +4 -3
- package/lib/chunk-FKI7IEB5.mjs +172 -0
- package/lib/chunk-GJTGHXRA.mjs +356 -0
- package/lib/{chunk-YV7DO3YV.mjs → chunk-HLGOWBEO.mjs} +1 -1
- package/lib/chunk-HMRMTYZM.mjs +40 -0
- package/lib/chunk-HN52G2YL.mjs +305 -0
- package/lib/chunk-HO6GHCOB.mjs +385 -0
- package/lib/chunk-LEM5OMRP.mjs +384 -0
- package/lib/{chunk-YX5U7XDR.mjs → chunk-M3YIYRHT.mjs} +6 -5
- package/lib/chunk-O6SWBEOQ.mjs +81 -0
- package/lib/{chunk-JGR2NW6D.mjs → chunk-PDSXF5HY.mjs} +3 -3
- package/lib/{chunk-AASHBCRW.mjs → chunk-QQ4A6DLD.mjs} +8 -0
- package/lib/chunk-RCP7DHVY.mjs +190 -0
- package/lib/chunk-SBNDSKG5.mjs +136 -0
- package/lib/chunk-U6SO4QEV.mjs +320 -0
- package/lib/{chunk-APBWENF6.mjs → chunk-V3N3JEUF.mjs} +3 -3
- package/lib/chunk-XD6BJK6Q.mjs +351 -0
- package/lib/chunk-XVBFFVCJ.mjs +209 -0
- package/lib/chunk-YXSFGA2D.mjs +383 -0
- package/lib/clean-github-actions-caches.cjs +243 -148
- package/lib/clean-github-actions-caches.mjs +4 -3
- package/lib/del-gradle.cjs +2 -2
- package/lib/del-gradle.js +1 -1
- package/lib/del-gradle.mjs +2 -2
- package/lib/del-node-modules.cjs +2 -2
- package/lib/del-node-modules.js +1 -1
- package/lib/del-node-modules.mjs +2 -2
- package/lib/del-ps.cjs +29 -8
- package/lib/del-ps.js +2 -2
- package/lib/del-ps.mjs +7 -5
- package/lib/del-yarn-caches.cjs +26 -5
- package/lib/del-yarn-caches.js +38 -3
- package/lib/del-yarn-caches.mjs +6 -6
- package/lib/find-node-modules-cli.cjs +5 -4
- package/lib/find-node-modules-cli.js +1 -1
- package/lib/find-node-modules-cli.mjs +2 -2
- package/lib/find-node-modules.cjs +4 -3
- package/lib/{find-node-modules.d.ts → find-node-modules.d.cts} +1 -1
- package/lib/find-node-modules.mjs +2 -2
- package/lib/free-chatgpt.cjs +548 -0
- package/lib/free-chatgpt.d.ts +1 -0
- package/lib/free-chatgpt.js +51 -0
- package/lib/free-chatgpt.mjs +50 -0
- package/lib/git/gitattributes.cjs +1 -1
- package/lib/git/{gitattributes.d.mts → gitattributes.d.cts} +5 -7
- package/lib/git/gitattributes.mjs +2 -2
- package/lib/git/line-endings.cjs +297 -64
- package/lib/git/line-endings.mjs +4 -4
- package/lib/git/normalize.cjs +26 -36
- package/lib/git/normalize.mjs +2 -2
- package/lib/git/permissions.cjs +77 -11
- package/lib/git/permissions.mjs +3 -3
- package/lib/git/pull-strategy.cjs +76 -9
- package/lib/git/pull-strategy.mjs +3 -3
- package/lib/git/user-config.cjs +266 -83
- package/lib/git/user-config.mjs +4 -4
- package/lib/git/utils.cjs +40 -60
- package/lib/git/utils.mjs +2 -2
- package/lib/git-diff-cli.cjs +651 -0
- package/lib/git-diff-cli.d.ts +1 -0
- package/lib/git-diff-cli.js +15 -0
- package/lib/git-diff-cli.mjs +16 -0
- package/lib/git-diff.cjs +657 -58
- package/lib/git-diff.d.ts +38 -83
- package/lib/git-diff.js +152 -0
- package/lib/git-diff.mjs +23 -85
- package/lib/git-fix.cjs +685 -97
- package/lib/git-fix.mjs +12 -11
- package/lib/git-purge.cjs +3 -3
- package/lib/git-purge.d.cts +1 -0
- package/lib/git-purge.mjs +43 -37
- package/lib/index.cjs +7 -6
- package/lib/index.d.ts +1 -1
- package/lib/index.js +2 -5
- package/lib/index.mjs +4 -4
- package/lib/npm-run-series.cjs +3 -3
- package/lib/npm-run-series.mjs +42 -36
- package/lib/package-resolutions-updater.cjs +174 -1
- package/lib/package-resolutions-updater.mjs +270 -290
- package/lib/print-directory-tree.cjs +275 -210
- package/lib/print-directory-tree.mjs +2 -2
- package/lib/ps/connected-domain.cjs +25 -2
- package/lib/ps/connected-domain.d.ts +10 -2
- package/lib/ps/connected-domain.js +5 -2
- package/lib/ps/connected-domain.mjs +8 -4
- package/lib/ps/index.cjs +345 -322
- package/lib/ps/index.d.mjs +1 -1
- package/lib/ps/index.js +1 -1
- package/lib/ps/index.mjs +179 -182
- package/lib/ps/isWin.cjs +24 -1
- package/lib/ps/isWin.d.ts +1 -1
- package/lib/ps/isWin.js +3 -1
- package/lib/ps/isWin.mjs +8 -4
- package/lib/ps/table-parser.cjs +167 -159
- package/lib/ps/table-parser.d.ts +5 -0
- package/lib/ps/table-parser.js +10 -4
- package/lib/ps/table-parser.mjs +9 -5
- package/lib/remove-module.cjs +262 -0
- package/lib/remove-module.d.mts +1 -0
- package/lib/remove-module.mjs +111 -0
- package/lib/rmpath.cjs +274 -0
- package/lib/rmpath.d.mts +3 -0
- package/lib/rmpath.mjs +108 -0
- package/lib/submodule-install.cjs +264 -85
- package/lib/submodule-install.mjs +56 -7
- package/lib/submodule-remove-cli.cjs +103 -0
- package/lib/submodule-remove-cli.d.ts +1 -0
- package/lib/submodule-remove-cli.js +31 -0
- package/lib/submodule-remove-cli.mjs +28 -0
- package/lib/submodule-remove.cjs +43 -0
- package/lib/submodule-remove.d.cts +2 -0
- package/lib/submodule-remove.mjs +6 -0
- package/lib/utils/chatgpt.cjs +383 -0
- package/lib/utils/chatgpt.d.ts +31 -0
- package/lib/utils/chatgpt.js +541 -0
- package/lib/utils/chatgpt.mjs +8 -0
- package/lib/{utils.cjs → utils/index.cjs} +1 -1
- package/lib/{utils.mjs → utils/index.mjs} +2 -2
- package/lib/yarn-reinstall.cjs +172 -38
- package/lib/yarn-reinstall.mjs +2 -2
- package/package.json +91 -71
- package/readme.md +43 -69
- package/releases/readme.md +36 -0
- package/test/README.md +101 -0
- package/test/package.json +2 -1
- package/test-project/readme.md +26 -0
- package/tmp/test-repo/README.md +35 -0
- package/tmp/typedoc/readme.md +320 -0
- package/bin/bash-dummy +0 -56
- package/bin/bash-dummy.cmd +0 -25
- package/bin/dir-tree.cmd +0 -7
- package/bin/git-diff +0 -4
- package/bin/git-diff.cmd +0 -7
- package/bin/git-fix +0 -36
- package/bin/git-fix.cmd +0 -7
- package/bin/rmpath +0 -70
- package/bin/submodule-install.txt +0 -118
- package/bin/submodule-remove +0 -46
- package/bin/submodule.txt +0 -172
- package/lib/binary-collections-config.d.mts +0 -18
- package/lib/binary-collections-config.js +0 -39
- package/lib/binary-collections.d.mts +0 -121
- package/lib/binary-collections.d.ts +0 -121
- package/lib/chunk-DPKAJKFO.mjs +0 -171
- package/lib/chunk-G3THLIDT.mjs +0 -200
- package/lib/chunk-W3ENOM53.mjs +0 -18
- package/lib/clean-github-actions-caches.d.mts +0 -169
- package/lib/clean-github-actions-caches.d.ts +0 -169
- package/lib/del-gradle.d.mts +0 -2
- package/lib/del-node-modules.d.mts +0 -2
- package/lib/del-ps.d.mts +0 -2
- package/lib/del-yarn-caches.d.mts +0 -2
- package/lib/find-node-modules-cli.d.mts +0 -1
- package/lib/find-node-modules.d.mts +0 -13
- package/lib/find-node-modules.js +0 -53
- package/lib/git/line-endings.d.mts +0 -83
- package/lib/git/line-endings.d.ts +0 -83
- package/lib/git/normalize.d.mts +0 -43
- package/lib/git/normalize.d.ts +0 -43
- package/lib/git/permissions.d.mts +0 -17
- package/lib/git/permissions.d.ts +0 -17
- package/lib/git/pull-strategy.d.mts +0 -15
- package/lib/git/pull-strategy.d.ts +0 -15
- package/lib/git/user-config.d.mts +0 -105
- package/lib/git/user-config.d.ts +0 -105
- package/lib/git/utils.d.mts +0 -69
- package/lib/git/utils.d.ts +0 -69
- package/lib/git-diff.d.mts +0 -84
- package/lib/git-fix.d.mts +0 -141
- package/lib/git-fix.d.ts +0 -141
- package/lib/git-purge.d.mts +0 -2
- package/lib/git-purge.js +0 -59
- package/lib/index.d.mts +0 -1
- package/lib/npm-run-series.d.mts +0 -1
- package/lib/npm-run-series.d.ts +0 -2
- package/lib/npm-run-series.js +0 -86
- package/lib/package-resolutions-updater.d.ts +0 -326
- package/lib/print-directory-tree.d.mts +0 -234
- package/lib/print-directory-tree.d.ts +0 -234
- package/lib/ps/connected-domain.d.mts +0 -3
- package/lib/ps/index.d.d.mts +0 -26
- package/lib/ps/index.d.d.ts +0 -26
- package/lib/ps/index.d.mts +0 -26
- package/lib/ps/isWin.d.mts +0 -3
- package/lib/ps/table-parser.d.mts +0 -3
- package/lib/submodule-install.d.mts +0 -121
- package/lib/submodule-install.d.ts +0 -121
- package/lib/utils.d.mts +0 -40
- package/lib/utils.js +0 -181
- package/lib/yarn-reinstall.d.mts +0 -49
- package/lib/yarn-reinstall.d.ts +0 -49
- package/src/package-resolutions-updater.mjs +0 -325
- package/src/print-directory-tree.cjs +0 -234
- package/src/ps/index.js +0 -286
- package/src/yarn-reinstall.cjs +0 -49
- /package/{bin → binaries}/nodekill +0 -0
- /package/{bin → binaries}/nodekill.cmd +0 -0
- /package/lib/{binary-collections-config.d.ts → binary-collections-config.d.cts} +0 -0
- /package/lib/{git-purge.d.ts → changelog.d.cts} +0 -0
- /package/lib/{git-diff.d.cts → npm-run-series.d.cts} +0 -0
- /package/lib/{utils.d.ts → utils/index.d.cts} +0 -0
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.runChatGpt = runChatGpt;
|
|
16
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
17
|
+
const puppeteer_extra_1 = __importDefault(require("puppeteer-extra"));
|
|
18
|
+
const puppeteer_extra_plugin_stealth_1 = __importDefault(require("puppeteer-extra-plugin-stealth"));
|
|
19
|
+
const upath_1 = __importDefault(require("upath"));
|
|
20
|
+
const COOKIE_DIR = upath_1.default.join(process.cwd(), "tmp", "cookies");
|
|
21
|
+
const DEFAULT_COOKIE_PATH = upath_1.default.join(COOKIE_DIR, "cookies.json");
|
|
22
|
+
fs_extra_1.default.ensureDirSync(COOKIE_DIR);
|
|
23
|
+
/**
|
|
24
|
+
* Saves cookies from a Puppeteer page to a specified file path.
|
|
25
|
+
*
|
|
26
|
+
* @param {import('puppeteer').Page} page - Puppeteer page instance.
|
|
27
|
+
* @param {string} [path=DEFAULT_COOKIE_PATH] - Path to save the cookies file.
|
|
28
|
+
* @returns {Promise<void>} Resolves when cookies are saved.
|
|
29
|
+
*/
|
|
30
|
+
function saveCookies(page_1) {
|
|
31
|
+
return __awaiter(this, arguments, void 0, function* (page, path = DEFAULT_COOKIE_PATH) {
|
|
32
|
+
const cookies = yield page.cookies();
|
|
33
|
+
fs_extra_1.default.writeFileSync(path, JSON.stringify(cookies, null, 2));
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Returns the cookie file path for a given URL's hostname.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} url - The URL to extract the hostname from.
|
|
40
|
+
* @returns {string} The path to the cookie file for the hostname, or the default cookie path if invalid.
|
|
41
|
+
*/
|
|
42
|
+
function getCookiePathForUrl(url) {
|
|
43
|
+
try {
|
|
44
|
+
const { hostname } = new URL(url);
|
|
45
|
+
return upath_1.default.join(COOKIE_DIR, `cookies_${hostname}.json`);
|
|
46
|
+
}
|
|
47
|
+
catch (_a) {
|
|
48
|
+
return DEFAULT_COOKIE_PATH;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Navigates to a URL using Puppeteer, loading cookies for the host and injecting a DOM mutation observer.
|
|
53
|
+
*
|
|
54
|
+
* @param {import('puppeteer').Page} page - Puppeteer page instance.
|
|
55
|
+
* @param {string} url - The URL to navigate to.
|
|
56
|
+
* @returns {Promise<{ waitForDomIdle: (idleMs?: number, timeout?: number) => Promise<boolean> }>} An object containing a function to wait for DOM stability.
|
|
57
|
+
*/
|
|
58
|
+
function navigatePage(page, url) {
|
|
59
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
const cookiePath = getCookiePathForUrl(url);
|
|
61
|
+
// Load cookies for the host
|
|
62
|
+
const cookies = loadCookies(cookiePath);
|
|
63
|
+
if (cookies) {
|
|
64
|
+
yield page.setCookie(...cookies);
|
|
65
|
+
}
|
|
66
|
+
// Navigate and wait until fully loaded
|
|
67
|
+
yield page.goto(url, { waitUntil: "networkidle0" });
|
|
68
|
+
// Inject DOM mutation observer to handle dynamic content
|
|
69
|
+
yield page.evaluate(() => {
|
|
70
|
+
window.__domStillUpdating = true;
|
|
71
|
+
if (window.__domObserver) {
|
|
72
|
+
window.__domObserver.disconnect();
|
|
73
|
+
}
|
|
74
|
+
window.__domObserver = new MutationObserver(() => {
|
|
75
|
+
window.__lastDomMutation = Date.now();
|
|
76
|
+
});
|
|
77
|
+
window.__lastDomMutation = Date.now();
|
|
78
|
+
window.__domObserver.observe(document.body, {
|
|
79
|
+
childList: true,
|
|
80
|
+
subtree: true,
|
|
81
|
+
attributes: true,
|
|
82
|
+
characterData: true
|
|
83
|
+
});
|
|
84
|
+
// Stop tracking after a while (optional)
|
|
85
|
+
setTimeout(() => {
|
|
86
|
+
window.__domStillUpdating = false;
|
|
87
|
+
window.__domObserver.disconnect();
|
|
88
|
+
}, 30000); // e.g. 30 seconds max
|
|
89
|
+
});
|
|
90
|
+
/**
|
|
91
|
+
* Waits until the DOM has been stable (no mutations) for a specified number of milliseconds.
|
|
92
|
+
*
|
|
93
|
+
* @param {number} [idleMs=1000] - The number of milliseconds the DOM must be stable.
|
|
94
|
+
* @param {number} [timeout=10000] - The maximum time to wait for the DOM to stabilize.
|
|
95
|
+
* @returns {Promise<boolean>} Resolves to true if the DOM was stable for idleMs within timeout, otherwise throws an error.
|
|
96
|
+
*/
|
|
97
|
+
const waitForDomIdle = (...args_1) => __awaiter(this, [...args_1], void 0, function* (idleMs = 1000, timeout = 10000) {
|
|
98
|
+
const start = Date.now();
|
|
99
|
+
while (Date.now() - start < timeout) {
|
|
100
|
+
const lastMutation = yield page.evaluate(() => window.__lastDomMutation);
|
|
101
|
+
const idle = Date.now() - lastMutation;
|
|
102
|
+
if (idle >= idleMs) {
|
|
103
|
+
return true; // DOM has been stable for idleMs
|
|
104
|
+
}
|
|
105
|
+
yield new Promise((r) => setTimeout(r, 200)); // poll every 200ms
|
|
106
|
+
}
|
|
107
|
+
throw new Error("DOM did not stabilize within timeout");
|
|
108
|
+
});
|
|
109
|
+
return { waitForDomIdle };
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Loads cookies from a specified file path.
|
|
114
|
+
*
|
|
115
|
+
* @param {string} [cookieFilePath=DEFAULT_COOKIE_PATH] - Path to the cookie file.
|
|
116
|
+
* @returns {Array|Null} Parsed cookies array, or null if file does not exist.
|
|
117
|
+
*/
|
|
118
|
+
function loadCookies(cookieFilePath = DEFAULT_COOKIE_PATH) {
|
|
119
|
+
if (!fs_extra_1.default.existsSync(cookieFilePath))
|
|
120
|
+
return null;
|
|
121
|
+
return JSON.parse(fs_extra_1.default.readFileSync(cookieFilePath));
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Restores cookies from a file to a Puppeteer page.
|
|
125
|
+
*
|
|
126
|
+
* @param {import('puppeteer').Page} page - Puppeteer page instance.
|
|
127
|
+
* @param {string} [cookieFilePath=DEFAULT_COOKIE_PATH] - Path to the cookie file.
|
|
128
|
+
* @returns {Promise<void>} Resolves when cookies are restored.
|
|
129
|
+
*/
|
|
130
|
+
function _restoreCookies(page_1) {
|
|
131
|
+
return __awaiter(this, arguments, void 0, function* (page, cookieFilePath = DEFAULT_COOKIE_PATH) {
|
|
132
|
+
const cookies = loadCookies(cookieFilePath);
|
|
133
|
+
if (cookies) {
|
|
134
|
+
yield page.setCookie(...cookies);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Writes a question to the ChatGPT prompt textarea, handling multi-line questions.
|
|
140
|
+
*
|
|
141
|
+
* @param {import('puppeteer').Page} page - Puppeteer page instance.
|
|
142
|
+
* @param {string} question - The question text to write.
|
|
143
|
+
* @returns {Promise<void>} Resolves when the question is written.
|
|
144
|
+
*/
|
|
145
|
+
function writeQuestion(page, question) {
|
|
146
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
147
|
+
const questions = question.split("\n");
|
|
148
|
+
const promptTextarea = yield page.waitForSelector("#prompt-textarea", { timeout: 30000 });
|
|
149
|
+
if (!promptTextarea) {
|
|
150
|
+
console.log("Cannot find the prompt input on the webpage. Please check whether you have access to chat.openai.com without logging in via your browser.");
|
|
151
|
+
}
|
|
152
|
+
// Clear the prompt textarea
|
|
153
|
+
yield page.evaluate(() => {
|
|
154
|
+
document.querySelector("#prompt-textarea").innerHTML = `<p></p>`;
|
|
155
|
+
});
|
|
156
|
+
// Check if the question has newlines
|
|
157
|
+
if (questions.length === 1) {
|
|
158
|
+
// If there's only one line, type it directly
|
|
159
|
+
yield page.type("#prompt-textarea", questions[0], { delay: 100 });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
// Type each question line by line
|
|
163
|
+
for (const q of questions) {
|
|
164
|
+
yield page.type("#prompt-textarea", q, { delay: 100 });
|
|
165
|
+
// Check if the line is not the last one
|
|
166
|
+
if (q !== questions[questions.length - 1]) {
|
|
167
|
+
// Simulate pressing Shift + Enter to add a new line
|
|
168
|
+
yield page.keyboard.down("Shift");
|
|
169
|
+
yield page.keyboard.press("Enter");
|
|
170
|
+
yield page.keyboard.up("Shift");
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Clicks the submit button in ChatGPT interface, trying different button variants.
|
|
177
|
+
*
|
|
178
|
+
* @param {import('puppeteer').Page} page - Puppeteer page instance.
|
|
179
|
+
* @returns {Promise<void>} Resolves when the submit button is clicked or attempt is made.
|
|
180
|
+
*/
|
|
181
|
+
function clickSubmitButton(page) {
|
|
182
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
183
|
+
try {
|
|
184
|
+
const fruitjuiceSendButton = yield page.evaluate(() => {
|
|
185
|
+
return document.querySelector('[data-testid="fruitjuice-send-button"]') !== null;
|
|
186
|
+
});
|
|
187
|
+
const sendButton = yield page.evaluate(() => {
|
|
188
|
+
return document.querySelector('[data-testid="send-button"]') !== null;
|
|
189
|
+
});
|
|
190
|
+
if (fruitjuiceSendButton) {
|
|
191
|
+
yield page.click('[data-testid="fruitjuice-send-button"]');
|
|
192
|
+
}
|
|
193
|
+
else if (sendButton) {
|
|
194
|
+
yield page.click('[data-testid="send-button"]');
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
console.log("Neither send button is present");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch (e) {
|
|
201
|
+
console.log(`Failed to click the send button: ${e}`);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
let lastMessageId = null;
|
|
206
|
+
let messageCount = 0;
|
|
207
|
+
const is_streaming = false; // Set to true if you want to stream the response
|
|
208
|
+
/**
|
|
209
|
+
* Creates a promise that resolves after a specified number of milliseconds.
|
|
210
|
+
*
|
|
211
|
+
* @param {number} ms - The number of milliseconds to wait.
|
|
212
|
+
* @returns {Promise<void>} A promise that resolves after the specified delay.
|
|
213
|
+
*/
|
|
214
|
+
function sleep(ms) {
|
|
215
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Waits for the initial assistant response to appear and finish thinking.
|
|
219
|
+
*
|
|
220
|
+
* @param {import('puppeteer').Page} page - Puppeteer page instance.
|
|
221
|
+
* @param {number} [timeout=30000] - Maximum time to wait for the response (ms).
|
|
222
|
+
* @returns {Promise<void>} Resolves when the initial response is ready.
|
|
223
|
+
*/
|
|
224
|
+
function waitForInitialResponse(page_1) {
|
|
225
|
+
return __awaiter(this, arguments, void 0, function* (page, timeout = 30000) {
|
|
226
|
+
const startTime = Date.now();
|
|
227
|
+
while (Date.now() - startTime < timeout) {
|
|
228
|
+
const assistantMessages = yield page.$$('[data-message-author-role="assistant"]');
|
|
229
|
+
const currentMessageCount = assistantMessages.length;
|
|
230
|
+
if (currentMessageCount > messageCount) {
|
|
231
|
+
const lastMessage = assistantMessages[assistantMessages.length - 1];
|
|
232
|
+
const isThinking = yield lastMessage.$(".result-thinking");
|
|
233
|
+
if (!isThinking) {
|
|
234
|
+
lastMessageId = yield page.evaluate((element) => element.getAttribute("data-message-id"), lastMessage);
|
|
235
|
+
messageCount = currentMessageCount;
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
yield sleep(100);
|
|
240
|
+
}
|
|
241
|
+
console.log("Timed out waiting for the initial response.");
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Handles streaming response from the assistant, printing output as it arrives.
|
|
246
|
+
*
|
|
247
|
+
* @param {import('puppeteer').Page} page - Puppeteer page instance.
|
|
248
|
+
* @param {string} [outputFile] - Path to save the response. Defaults to tmp/response.txt.
|
|
249
|
+
* @returns {Promise<void>} Resolves when streaming is complete.
|
|
250
|
+
*/
|
|
251
|
+
function handleStreamingResponse(page_1) {
|
|
252
|
+
return __awaiter(this, arguments, void 0, function* (page, outputFile = upath_1.default.join(process.cwd(), "tmp/response.txt")) {
|
|
253
|
+
let previousText = "";
|
|
254
|
+
let completeResponse = "";
|
|
255
|
+
let newContentDetected = false;
|
|
256
|
+
while (!newContentDetected) {
|
|
257
|
+
const assistantMessages = yield page.$$('[data-message-author-role="assistant"]');
|
|
258
|
+
if (assistantMessages.length > 0) {
|
|
259
|
+
const lastMessage = assistantMessages[assistantMessages.length - 1];
|
|
260
|
+
const currentMessageId = yield page.evaluate((element) => element.getAttribute("data-message-id"), lastMessage);
|
|
261
|
+
if (currentMessageId === lastMessageId) {
|
|
262
|
+
const currentText = yield page.evaluate((element) => element.textContent, lastMessage);
|
|
263
|
+
console.log(`Current text: ${currentText}`);
|
|
264
|
+
if (currentText !== previousText) {
|
|
265
|
+
if (is_streaming) {
|
|
266
|
+
process.stdout.write(currentText.slice(previousText.length));
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
completeResponse += currentText.slice(previousText.length);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
previousText = currentText;
|
|
273
|
+
const isStreaming = yield lastMessage.$(".result-streaming");
|
|
274
|
+
if (!isStreaming) {
|
|
275
|
+
newContentDetected = true;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
lastMessageId = currentMessageId;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
yield sleep(100);
|
|
283
|
+
}
|
|
284
|
+
if (!is_streaming) {
|
|
285
|
+
console.log(completeResponse.trim());
|
|
286
|
+
console.log("\n\n");
|
|
287
|
+
fs_extra_1.default.ensureDirSync(upath_1.default.dirname(outputFile));
|
|
288
|
+
fs_extra_1.default.writeFileSync(outputFile, completeResponse.trim());
|
|
289
|
+
console.log(`Response saved to ${outputFile}`);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Checks if the user is logged into ChatGPT by checking if login button exists and is visible.
|
|
295
|
+
*
|
|
296
|
+
* @param {import('puppeteer').Page} page - Puppeteer page instance.
|
|
297
|
+
* @returns {Promise<boolean>} True if logged in (no visible login button), false if not logged in.
|
|
298
|
+
*/
|
|
299
|
+
function isLoggedIn(page) {
|
|
300
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
301
|
+
const result = yield page.evaluate(() => {
|
|
302
|
+
const loginButton = document.querySelector('[data-testid="login-button"]');
|
|
303
|
+
// User is NOT logged in if login button exists and is visible
|
|
304
|
+
return !(loginButton && loginButton.offsetParent !== null);
|
|
305
|
+
});
|
|
306
|
+
// Ensure we always return a boolean
|
|
307
|
+
return result === true;
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Creates a new Puppeteer browser instance with StealthPlugin enabled.
|
|
312
|
+
*
|
|
313
|
+
* @param {Parameters<import("puppeteer-extra").VanillaPuppeteer["launch"]>[0]} [browserOptions={}] - Browser launch options.
|
|
314
|
+
* @returns {Promise<import("puppeteer-extra").Browser>} The created browser instance.
|
|
315
|
+
*/
|
|
316
|
+
function createBrowser() {
|
|
317
|
+
return __awaiter(this, arguments, void 0, function* (browserOptions = {}) {
|
|
318
|
+
/**
|
|
319
|
+
* @type {Parameters<import("puppeteer-extra").VanillaPuppeteer["launch"]>[0]}
|
|
320
|
+
*/
|
|
321
|
+
const defaultOptions = Object.assign({ headless: false, userDataDir: upath_1.default.join(process.cwd(), "tmp/puppeteer-profile"),
|
|
322
|
+
// Windows-specific options to handle browser launch issues
|
|
323
|
+
args: [
|
|
324
|
+
"--no-sandbox",
|
|
325
|
+
"--disable-setuid-sandbox",
|
|
326
|
+
"--disable-dev-shm-usage",
|
|
327
|
+
"--disable-accelerated-2d-canvas",
|
|
328
|
+
"--no-first-run",
|
|
329
|
+
"--no-zygote",
|
|
330
|
+
"--disable-gpu",
|
|
331
|
+
"--disable-background-timer-throttling",
|
|
332
|
+
"--disable-backgrounding-occluded-windows",
|
|
333
|
+
"--disable-renderer-backgrounding"
|
|
334
|
+
], ignoreDefaultArgs: ["--disable-extensions"] }, (process.platform === "win32" && {
|
|
335
|
+
// Additional Windows-specific options
|
|
336
|
+
executablePath: undefined // Let Puppeteer find Chrome automatically
|
|
337
|
+
}));
|
|
338
|
+
try {
|
|
339
|
+
return yield puppeteer_extra_1.default.use((0, puppeteer_extra_plugin_stealth_1.default)()).launch(Object.assign(Object.assign({}, defaultOptions), browserOptions));
|
|
340
|
+
}
|
|
341
|
+
catch (_error) {
|
|
342
|
+
console.error("Failed to launch browser with default options. Trying fallback options...");
|
|
343
|
+
// Fallback: Try with minimal options
|
|
344
|
+
try {
|
|
345
|
+
return yield puppeteer_extra_1.default.use((0, puppeteer_extra_plugin_stealth_1.default)()).launch(Object.assign({ headless: browserOptions.headless || false, args: ["--no-sandbox", "--disable-setuid-sandbox"], ignoreDefaultArgs: false }, browserOptions));
|
|
346
|
+
}
|
|
347
|
+
catch (fallbackError) {
|
|
348
|
+
console.error("Browser launch failed completely. Common solutions:");
|
|
349
|
+
console.error("1. Install Google Chrome if not installed");
|
|
350
|
+
console.error("2. Update Node.js to the latest version");
|
|
351
|
+
console.error("3. Try running: npm install puppeteer --force");
|
|
352
|
+
console.error("4. Check if antivirus is blocking browser launch");
|
|
353
|
+
throw new Error(`Browser launch failed: ${fallbackError.message}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Handles the login process for ChatGPT by launching a browser and clicking the login button if needed.
|
|
360
|
+
*
|
|
361
|
+
* @returns {Promise<void>} Resolves when the login process is complete.
|
|
362
|
+
*/
|
|
363
|
+
function loginToChatGpt() {
|
|
364
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
365
|
+
const browser = yield createBrowser({ headless: false });
|
|
366
|
+
const page = (yield browser.pages()).length > 0 ? (yield browser.pages())[0] : yield browser.newPage();
|
|
367
|
+
const url = "https://chat.openai.com";
|
|
368
|
+
const navigate = yield navigatePage(page, url);
|
|
369
|
+
// Wait for page to fully load before checking login status
|
|
370
|
+
yield navigate.waitForDomIdle(2000, 10000);
|
|
371
|
+
// Check if the login button exists
|
|
372
|
+
const loginButtonExists = yield page.evaluate(() => {
|
|
373
|
+
return document.querySelector('[data-testid="login-button"]') !== null;
|
|
374
|
+
});
|
|
375
|
+
if (loginButtonExists) {
|
|
376
|
+
console.log("Login button found, clicking to log in...");
|
|
377
|
+
yield page.click('[data-testid="login-button"]');
|
|
378
|
+
// Wait for the login process to complete
|
|
379
|
+
yield page.waitForNavigation({ waitUntil: "networkidle0" });
|
|
380
|
+
console.log("Login process completed.");
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
console.log("No login required - user appears to be already logged in.");
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Automates ChatGPT interactions using Puppeteer. Can send text questions or upload files to ChatGPT.
|
|
389
|
+
*
|
|
390
|
+
* @param {Object} [chatgptOptions={}] - Configuration options for ChatGPT automation.
|
|
391
|
+
* @param {boolean} [chatgptOptions.headless=true] - Whether to run the browser in headless mode.
|
|
392
|
+
* @param {string} [chatgptOptions.question] - Text question to send to ChatGPT. Either question or questionFile must be provided.
|
|
393
|
+
* @param {string} [chatgptOptions.questionFile] - Path to a file to upload to ChatGPT. Either question or questionFile must be provided.
|
|
394
|
+
* @param {string} [chatgptOptions.responseFile] - Path to save the response. Defaults to tmp/response.txt.
|
|
395
|
+
* @returns {Promise<void>} Resolves when the ChatGPT interaction is complete. Responses are logged to console and saved to specified file.
|
|
396
|
+
* @throws {Error} Throws an error if neither question nor questionFile is provided.
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* // Send a text question
|
|
400
|
+
* await runChatGpt({
|
|
401
|
+
* headless: false,
|
|
402
|
+
* question: "What is the capital of France?"
|
|
403
|
+
* });
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* // Upload a file for analysis
|
|
407
|
+
* await runChatGpt({
|
|
408
|
+
* headless: false,
|
|
409
|
+
* questionFile: "./path/to/document.txt"
|
|
410
|
+
* });
|
|
411
|
+
*/
|
|
412
|
+
function runChatGpt() {
|
|
413
|
+
return __awaiter(this, arguments, void 0, function* (chatgptOptions = {}) {
|
|
414
|
+
const headless = chatgptOptions.headless !== undefined ? chatgptOptions.headless : true;
|
|
415
|
+
const questionFile = chatgptOptions.questionFile;
|
|
416
|
+
let question = chatgptOptions.question;
|
|
417
|
+
const responseFile = chatgptOptions.responseFile || upath_1.default.join(process.cwd(), "tmp", "response.txt");
|
|
418
|
+
// Validate input parameters
|
|
419
|
+
const noInputProvided = !question && !questionFile;
|
|
420
|
+
const questionIsEmpty = question && question.trim().length === 0;
|
|
421
|
+
const questionFileIsEmpty = questionFile && questionFile.trim().length === 0;
|
|
422
|
+
if (noInputProvided || questionIsEmpty || questionFileIsEmpty) {
|
|
423
|
+
throw new Error("You must provide a question or a question file.");
|
|
424
|
+
}
|
|
425
|
+
let browser;
|
|
426
|
+
try {
|
|
427
|
+
browser = yield createBrowser({ headless });
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
console.error("Error running ChatGPT:", error);
|
|
431
|
+
console.error("\nTroubleshooting steps:");
|
|
432
|
+
console.error("1. Make sure Google Chrome is installed");
|
|
433
|
+
console.error("2. Try running: yarn add puppeteer --force");
|
|
434
|
+
console.error("3. Check if your antivirus is blocking the browser");
|
|
435
|
+
console.error("4. Close any running Chrome instances and try again");
|
|
436
|
+
throw error;
|
|
437
|
+
}
|
|
438
|
+
/** @type {import('puppeteer').Page} */
|
|
439
|
+
const page = (yield browser.pages()).length > 0 ? (yield browser.pages())[0] : yield browser.newPage();
|
|
440
|
+
try {
|
|
441
|
+
const url = "https://chat.openai.com";
|
|
442
|
+
const navigate = yield navigatePage(page, url);
|
|
443
|
+
// Check temporary chat - wait for page to load and try to click temporary chat button
|
|
444
|
+
yield navigate.waitForDomIdle(2000, 15000);
|
|
445
|
+
try {
|
|
446
|
+
const tempChatButton = yield page.$('button[aria-label="Turn on temporary chat"]');
|
|
447
|
+
if (tempChatButton) {
|
|
448
|
+
yield page.evaluate((el) => el.click(), tempChatButton);
|
|
449
|
+
console.log("Successfully clicked temporary chat button");
|
|
450
|
+
yield navigate.waitForDomIdle(1000, 10000);
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
console.log("Temporary chat button not found, proceeding without it.");
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
catch (error) {
|
|
457
|
+
console.log(`Failed to click temporary chat button: ${error.message}`);
|
|
458
|
+
}
|
|
459
|
+
if (question) {
|
|
460
|
+
yield writeQuestion(page, question);
|
|
461
|
+
// Submit the question
|
|
462
|
+
yield clickSubmitButton(page);
|
|
463
|
+
yield navigate.waitForDomIdle(1000, 30000); // Wait for DOM to stabilize
|
|
464
|
+
// Wait for the initial response
|
|
465
|
+
yield waitForInitialResponse(page);
|
|
466
|
+
// Handle the streaming response
|
|
467
|
+
yield handleStreamingResponse(page, responseFile);
|
|
468
|
+
// Save cookies for this host at the end
|
|
469
|
+
yield saveCookies(page, getCookiePathForUrl(url));
|
|
470
|
+
}
|
|
471
|
+
else if (questionFile) {
|
|
472
|
+
// Wait for page to fully load before checking login status
|
|
473
|
+
yield navigate.waitForDomIdle(2000, 10000);
|
|
474
|
+
// Check if logged in
|
|
475
|
+
const isUserLoggedIn = yield isLoggedIn(page);
|
|
476
|
+
console.log(`Login status: ${isUserLoggedIn ? "Logged in" : "Not logged in"}`);
|
|
477
|
+
if (!isUserLoggedIn) {
|
|
478
|
+
console.log("Not logged in. Please log in to ChatGPT in the browser window, then close it and run the command again.");
|
|
479
|
+
return loginToChatGpt();
|
|
480
|
+
}
|
|
481
|
+
// Upload the question file
|
|
482
|
+
const plusButtonExists = yield page.evaluate(() => {
|
|
483
|
+
const button = document.querySelector('[data-testid="composer-plus-btn"]');
|
|
484
|
+
return button !== null;
|
|
485
|
+
});
|
|
486
|
+
if (plusButtonExists) {
|
|
487
|
+
yield page.click('[data-testid="composer-plus-btn"]');
|
|
488
|
+
yield sleep(500); // Wait for the menu to open
|
|
489
|
+
const menuItems = yield page.$$('[role="menuitem"]');
|
|
490
|
+
let clicked = false;
|
|
491
|
+
for (const item of menuItems) {
|
|
492
|
+
const text = yield page.evaluate((el) => el.innerText, item);
|
|
493
|
+
if (text && text.includes("Add photos") && text.includes("files")) {
|
|
494
|
+
yield item.hover();
|
|
495
|
+
clicked = true;
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
if (!clicked) {
|
|
500
|
+
console.log('Could not find the "Add photos & files" menu item.');
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
// Wait for file input to appear and upload the file
|
|
504
|
+
try {
|
|
505
|
+
yield sleep(1000); // Wait for file dialog to be ready
|
|
506
|
+
// Look for the file input element
|
|
507
|
+
const fileInput = yield page.waitForSelector('input[type="file"]', { timeout: 10000 });
|
|
508
|
+
if (fileInput) {
|
|
509
|
+
console.log(`Uploading file: ${questionFile}`);
|
|
510
|
+
yield fileInput.uploadFile(questionFile);
|
|
511
|
+
// Wait for the file to be processed
|
|
512
|
+
yield navigate.waitForDomIdle(2000, 15000);
|
|
513
|
+
console.log("File uploaded successfully");
|
|
514
|
+
// Optionally submit after file upload
|
|
515
|
+
yield clickSubmitButton(page);
|
|
516
|
+
yield navigate.waitForDomIdle(1000, 30000);
|
|
517
|
+
// Wait for and handle response
|
|
518
|
+
yield waitForInitialResponse(page);
|
|
519
|
+
yield handleStreamingResponse(page, responseFile);
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
console.log("Could not find file input element");
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
catch (error) {
|
|
526
|
+
console.log(`Error uploading file: ${error.message}`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
console.log('Could not find the [data-testid="composer-plus-btn"] button.');
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
finally {
|
|
535
|
+
// Always close the browser, even if an error occurred
|
|
536
|
+
if (browser) {
|
|
537
|
+
yield browser.close();
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
require_utils
|
|
4
|
-
} from "
|
|
5
|
-
import "
|
|
4
|
+
} from "../chunk-V3N3JEUF.mjs";
|
|
5
|
+
import "../chunk-QQ4A6DLD.mjs";
|
|
6
6
|
export default require_utils();
|