binary-collections 2.0.10 → 2.0.12

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.
Files changed (290) hide show
  1. package/.opencode/package.json +5 -0
  2. package/.puppeterrc.cjs +25 -0
  3. package/binaries/binary-executor.cjs +138 -3
  4. package/binaries/clean-nodemodule.cjs +138 -3
  5. package/binaries/clean-nodemodules.cjs +138 -3
  6. package/binaries/dev.cjs +138 -3
  7. package/binaries/empty.cjs +138 -3
  8. package/binaries/git-reduce-size.cjs +138 -3
  9. package/binaries/javakill.cjs +138 -3
  10. package/binaries/kill-night-crows.bat +7 -0
  11. package/binaries/kill-night-crows.ps1 +172 -0
  12. package/binaries/kill-process.cjs +138 -3
  13. package/binaries/nodekill.cjs +138 -3
  14. package/binaries/prod.cjs +138 -3
  15. package/binaries/py +111 -0
  16. package/binaries/py.cjs +178 -0
  17. package/binaries/py.cmd +49 -0
  18. package/binaries/rmfind.cjs +138 -3
  19. package/binaries/rmx +15 -1
  20. package/binaries/rmx.cjs +138 -3
  21. package/binaries/rmx.cmd +12 -0
  22. package/binaries/submodule-token.cjs +138 -3
  23. package/binaries/test-cjs +18 -0
  24. package/binaries/test-cjs.cjs +178 -0
  25. package/binaries/test-cjs.cmd +26 -0
  26. package/binaries/test-esm +16 -0
  27. package/binaries/test-esm.cjs +178 -0
  28. package/binaries/test-esm.cmd +23 -0
  29. package/binaries/yarn-clean +32 -0
  30. package/binaries/yarn-clean.cjs +178 -0
  31. package/binaries/yarn-clean.cmd +30 -0
  32. package/binaries/yarn-clean.py +166 -0
  33. package/binaries/yc +110 -0
  34. package/binaries/yc.cjs +178 -0
  35. package/lib/binary-collections/config.cjs +126 -0
  36. package/lib/binary-collections/config.mjs +7 -0
  37. package/lib/binary-collections/executeScript.cjs +19 -0
  38. package/lib/binary-collections/executeScript.d.cts +12 -0
  39. package/lib/binary-collections/executeScript.mjs +6 -0
  40. package/lib/binary-collections/findScript.cjs +284 -0
  41. package/lib/binary-collections/findScript.d.cts +12 -0
  42. package/lib/binary-collections/findScript.mjs +7 -0
  43. package/lib/binary-collections/listScript.cjs +266 -0
  44. package/lib/binary-collections/listScript.d.cts +19 -0
  45. package/lib/binary-collections/listScript.mjs +7 -0
  46. package/lib/binary-collections.cjs +342 -194
  47. package/lib/binary-collections.mjs +22 -268
  48. package/lib/changelog.cjs +61 -13
  49. package/lib/changelog.mjs +1 -1
  50. package/lib/{chunk-E6FDDAOO.mjs → chunk-2LSRSEXF.mjs} +2 -2
  51. package/lib/chunk-34IQDTLZ.mjs +27 -0
  52. package/lib/chunk-3HFFECCI.mjs +27 -0
  53. package/lib/{chunk-V3N3JEUF.mjs → chunk-5RTXZVCW.mjs} +60 -13
  54. package/lib/chunk-66KDU4TX.mjs +268 -0
  55. package/lib/chunk-6PU7BAHB.mjs +61 -0
  56. package/lib/{chunk-6HHJRKFB.mjs → chunk-BZWVHODJ.mjs} +4 -1
  57. package/lib/chunk-C6D2TTYU.mjs +32 -0
  58. package/lib/chunk-FB2WKVJD.mjs +158 -0
  59. package/lib/{chunk-66PAU5PS.mjs → chunk-G5UUEWUO.mjs} +9 -5
  60. package/lib/{chunk-HO6GHCOB.mjs → chunk-GAGABICI.mjs} +198 -63
  61. package/lib/chunk-H44UWUFY.mjs +105 -0
  62. package/lib/chunk-NCZPTKDV.mjs +79 -0
  63. package/lib/chunk-NGFK3EYW.mjs +28 -0
  64. package/lib/chunk-NVEG3LEZ.mjs +143 -0
  65. package/lib/chunk-OGXVGBRI.mjs +29 -0
  66. package/lib/chunk-OXV52GD5.mjs +62 -0
  67. package/lib/{chunk-PDSXF5HY.mjs → chunk-PDN26I7O.mjs} +1 -0
  68. package/lib/chunk-PXBMHE7O.mjs +35 -0
  69. package/lib/chunk-R5FJOR63.mjs +47 -0
  70. package/lib/chunk-SPTECFE5.mjs +180 -0
  71. package/lib/chunk-UXCFNAR6.mjs +55 -0
  72. package/lib/chunk-V2IBPCEV.mjs +39 -0
  73. package/lib/chunk-XPJGCDOD.mjs +14 -0
  74. package/lib/{chunk-AI4CVPJ7.mjs → chunk-ZDMWBSYF.mjs} +4 -4
  75. package/lib/chunk-ZOWVMII3.mjs +228 -0
  76. package/lib/clean-github-actions-caches-cli.cjs +465 -0
  77. package/lib/clean-github-actions-caches-cli.d.cts +1 -0
  78. package/lib/clean-github-actions-caches-cli.mjs +56 -0
  79. package/lib/clean-github-actions-caches.cjs +152 -160
  80. package/lib/clean-github-actions-caches.d.cts +15 -1
  81. package/lib/clean-github-actions-caches.mjs +4 -130
  82. package/lib/cross-env/command.cjs +63 -0
  83. package/lib/cross-env/command.d.ts +8 -0
  84. package/lib/cross-env/command.js +45 -0
  85. package/lib/cross-env/command.mjs +9 -0
  86. package/lib/cross-env/index.cjs +178 -0
  87. package/lib/cross-env/index.d.ts +8 -0
  88. package/lib/cross-env/index.js +102 -0
  89. package/lib/cross-env/index.mjs +101 -0
  90. package/lib/cross-env/variable.cjs +60 -0
  91. package/lib/cross-env/variable.d.ts +7 -0
  92. package/lib/cross-env/variable.js +59 -0
  93. package/lib/cross-env/variable.mjs +9 -0
  94. package/lib/del-gradle.cjs +61 -13
  95. package/lib/del-gradle.js +9 -8
  96. package/lib/del-gradle.mjs +1 -1
  97. package/lib/del-node-modules.cjs +143 -148
  98. package/lib/del-node-modules.js +210 -14
  99. package/lib/del-node-modules.mjs +148 -17
  100. package/lib/del-ps.cjs +61 -13
  101. package/lib/del-ps.js +9 -8
  102. package/lib/del-ps.mjs +1 -1
  103. package/lib/del-yarn-caches.cjs +61 -13
  104. package/lib/del-yarn-caches.js +2 -2
  105. package/lib/del-yarn-caches.mjs +1 -1
  106. package/lib/file/copy-cli.cjs +92 -0
  107. package/lib/file/copy-cli.d.mts +1 -0
  108. package/lib/file/copy-cli.mjs +55 -0
  109. package/lib/file/copy.cjs +56 -0
  110. package/lib/file/copy.d.mts +1 -0
  111. package/lib/file/copy.mjs +8 -0
  112. package/lib/file/move-cli.cjs +91 -0
  113. package/lib/file/move-cli.d.mts +1 -0
  114. package/lib/file/move-cli.mjs +55 -0
  115. package/lib/file/move.cjs +55 -0
  116. package/lib/file/move.d.mts +1 -0
  117. package/lib/file/move.mjs +8 -0
  118. package/lib/find-node-modules-cli.js +2 -1
  119. package/lib/free-chatgpt.cjs +259 -47
  120. package/lib/free-chatgpt.js +10 -10
  121. package/lib/free-chatgpt.mjs +2 -2
  122. package/lib/git/gitattributes.cjs +1 -0
  123. package/lib/git/gitattributes.d.cts +7 -2
  124. package/lib/git/gitattributes.mjs +1 -1
  125. package/lib/git/line-endings.cjs +2 -1
  126. package/lib/git/line-endings.mjs +2 -2
  127. package/lib/git/undo-commit-cli.cjs +110 -0
  128. package/lib/git/undo-commit-cli.d.ts +1 -0
  129. package/lib/git/undo-commit-cli.js +4 -0
  130. package/lib/git/undo-commit-cli.mjs +14 -0
  131. package/lib/git/undo-commit.cjs +81 -0
  132. package/lib/git/undo-commit.d.cts +1 -0
  133. package/lib/git/undo-commit.mjs +7 -0
  134. package/lib/git/undo-staged-cli.cjs +110 -0
  135. package/lib/git/undo-staged-cli.d.ts +1 -0
  136. package/lib/git/undo-staged-cli.js +4 -0
  137. package/lib/git/undo-staged-cli.mjs +14 -0
  138. package/lib/git/undo-staged.cjs +81 -0
  139. package/lib/git/undo-staged.d.cts +1 -0
  140. package/lib/git/undo-staged.mjs +7 -0
  141. package/lib/git/user-config.cjs +61 -14
  142. package/lib/git/user-config.mjs +2 -2
  143. package/lib/git-diff-cli.cjs +427 -75
  144. package/lib/git-diff-cli.d.ts +1 -0
  145. package/lib/git-diff-cli.js +1 -0
  146. package/lib/git-diff-cli.mjs +6 -4
  147. package/lib/git-diff.cjs +426 -75
  148. package/lib/git-diff.d.ts +2 -1
  149. package/lib/git-diff.js +91 -34
  150. package/lib/git-diff.mjs +5 -4
  151. package/lib/git-fix.cjs +64 -16
  152. package/lib/git-fix.mjs +10 -10
  153. package/lib/git-purge.cjs +61 -13
  154. package/lib/git-purge.mjs +1 -1
  155. package/lib/index.d.ts +1 -1
  156. package/lib/kill-night-crows.cjs +87 -0
  157. package/lib/kill-night-crows.d.mts +1 -0
  158. package/lib/kill-night-crows.mjs +65 -0
  159. package/lib/node-cache-cleaner/npm.cjs +65 -0
  160. package/lib/node-cache-cleaner/npm.d.ts +2 -0
  161. package/lib/node-cache-cleaner/npm.js +41 -0
  162. package/lib/node-cache-cleaner/npm.mjs +10 -0
  163. package/lib/node-cache-cleaner/npx.cjs +89 -0
  164. package/lib/node-cache-cleaner/npx.d.ts +4 -0
  165. package/lib/node-cache-cleaner/npx.js +82 -0
  166. package/lib/{ps/index.d.mjs → node-cache-cleaner/npx.mjs} +6 -5
  167. package/lib/node-cache-cleaner/yarn.cjs +73 -0
  168. package/lib/node-cache-cleaner/yarn.d.ts +2 -0
  169. package/lib/node-cache-cleaner/yarn.js +62 -0
  170. package/lib/node-cache-cleaner/yarn.mjs +10 -0
  171. package/lib/node-cache-cleaner-cli.cjs +182 -0
  172. package/lib/node-cache-cleaner-cli.d.ts +2 -0
  173. package/lib/node-cache-cleaner-cli.js +60 -0
  174. package/lib/node-cache-cleaner-cli.mjs +56 -0
  175. package/lib/node-executor.cjs +91 -0
  176. package/lib/node-executor.d.cts +2 -0
  177. package/lib/node-executor.mjs +103 -0
  178. package/lib/npm-run-series.cjs +79 -39
  179. package/lib/npm-run-series.mjs +20 -6
  180. package/lib/package-resolutions-updater-cli.cjs +645 -0
  181. package/lib/package-resolutions-updater-cli.d.mts +1 -0
  182. package/lib/package-resolutions-updater-cli.mjs +102 -0
  183. package/lib/package-resolutions-updater.cjs +259 -133
  184. package/lib/package-resolutions-updater.d.mts +51 -1
  185. package/lib/package-resolutions-updater.mjs +18 -294
  186. package/lib/php-cs-fixer-staged.cjs +105 -0
  187. package/lib/php-cs-fixer-staged.d.cts +2 -0
  188. package/lib/php-cs-fixer-staged.mjs +117 -0
  189. package/lib/print-directory-tree.cjs +62 -14
  190. package/lib/print-directory-tree.mjs +2 -2
  191. package/lib/ps/connected-domain.d.ts +1 -1
  192. package/lib/ps/connected-domain.js +10 -10
  193. package/lib/ps/index.cjs +1 -1
  194. package/lib/ps/index.mjs +177 -171
  195. package/lib/ps/isWin.js +1 -1
  196. package/lib/ps/table-parser.js +6 -6
  197. package/lib/remove-module.cjs +61 -13
  198. package/lib/remove-module.mjs +1 -1
  199. package/lib/rm-node-module-cli.cjs +222 -0
  200. package/lib/rm-node-module-cli.d.cts +1 -0
  201. package/lib/rm-node-module-cli.mjs +89 -0
  202. package/lib/rm-node-modules.cjs +127 -0
  203. package/lib/rm-node-modules.d.cts +35 -0
  204. package/lib/{binary-collections-config.mjs → rm-node-modules.mjs} +3 -3
  205. package/lib/rmpath.cjs +63 -15
  206. package/lib/rmpath.mjs +2 -2
  207. package/lib/submodule-install.cjs +107 -47
  208. package/lib/submodule-install.mjs +48 -35
  209. package/lib/submodule-remove-cli.cjs +6 -3
  210. package/lib/submodule-remove-cli.js +3 -3
  211. package/lib/submodule-remove-cli.mjs +2 -3
  212. package/lib/submodule-remove.cjs +4 -1
  213. package/lib/submodule-remove.mjs +1 -1
  214. package/lib/utils/chatgpt.cjs +198 -34
  215. package/lib/utils/chatgpt.js +260 -93
  216. package/lib/utils/chatgpt.mjs +1 -1
  217. package/lib/utils/findEnvFiles.cjs +89 -0
  218. package/lib/utils/findEnvFiles.d.cts +19 -0
  219. package/lib/utils/findEnvFiles.mjs +6 -0
  220. package/lib/utils/findWorkspaceRoot.cjs +70 -0
  221. package/lib/utils/findWorkspaceRoot.d.ts +9 -0
  222. package/lib/utils/findWorkspaceRoot.js +57 -0
  223. package/lib/utils/findWorkspaceRoot.mjs +40 -0
  224. package/lib/utils/index.cjs +60 -13
  225. package/lib/utils/index.mjs +1 -1
  226. package/lib/utils/isGithubTokenValid.cjs +64 -0
  227. package/lib/utils/isGithubTokenValid.d.ts +7 -0
  228. package/lib/utils/isGithubTokenValid.js +48 -0
  229. package/lib/utils/isGithubTokenValid.mjs +36 -0
  230. package/lib/{ps/index.d.cjs → utils/isWindows.cjs} +17 -3
  231. package/lib/utils/isWindows.d.ts +5 -0
  232. package/lib/utils/isWindows.js +10 -0
  233. package/lib/utils/isWindows.mjs +8 -0
  234. package/lib/utils/runBash.cjs +53 -0
  235. package/lib/utils/runBash.d.cts +12 -0
  236. package/lib/utils/runBash.mjs +66 -0
  237. package/lib/yarn-per-branch-lock-installer.cjs +97 -0
  238. package/lib/yarn-per-branch-lock-installer.d.cts +2 -0
  239. package/lib/yarn-per-branch-lock-installer.mjs +109 -0
  240. package/lib/yarn-reinstall.cjs +61 -13
  241. package/lib/yarn-reinstall.mjs +1 -1
  242. package/package.json +133 -110
  243. package/readme.html +784 -0
  244. package/readme.md +116 -229
  245. package/releases/readme.md +1 -1
  246. package/requirements.txt +1 -0
  247. package/test/README.md +2 -2
  248. package/test-project/package.json +8 -2
  249. package/test-project/workspaces/workspace-a/package.json +135 -0
  250. package/test-project/workspaces/workspace-a/readme.md +20 -0
  251. package/test-project/workspaces/workspace-a/release/readme.md +42 -0
  252. package/test-project/workspaces/workspace-a/test/demo/package.json +25 -0
  253. package/test-project/workspaces/workspace-a/test/readme.md +12 -0
  254. package/test-project/workspaces/workspace-b/package.json +139 -0
  255. package/test-project/workspaces/workspace-b/readme.md +94 -0
  256. package/test-project/workspaces/workspace-b/requirements.txt +1 -0
  257. package/test-project/workspaces/workspace-b/test/sample-project/package.json +7 -0
  258. package/test-project/workspaces/workspace-b/themes/hexo-theme-flowbite/package.json +96 -0
  259. package/test-project/workspaces/workspace-b/themes/hexo-theme-flowbite/readme.md +156 -0
  260. package/tmp/rm-node-modules-test-project/package.json +17 -0
  261. package/tmp/rm-node-modules-test-project/packages/workspace-a/package.json +16 -0
  262. package/tmp/rm-node-modules-test-project/packages/workspace-b/package.json +16 -0
  263. package/tmp/test-repo/README.md +2 -35
  264. package/tmp/test-repo/package.json +13 -3
  265. package/docs-src/clean-github-actions-caches.md +0 -26
  266. package/docs-src/free-chatgpt.md +0 -26
  267. package/lib/binary-collections-config.cjs +0 -15
  268. package/lib/chunk-4EWQC6GZ.mjs +0 -382
  269. package/lib/chunk-4ZI7BQKQ.mjs +0 -381
  270. package/lib/chunk-5J2BEPY5.mjs +0 -83
  271. package/lib/chunk-AGZYRDC2.mjs +0 -323
  272. package/lib/chunk-BDCHCWHD.mjs +0 -136
  273. package/lib/chunk-BEZKJ25G.mjs +0 -140
  274. package/lib/chunk-DI5MDPSN.mjs +0 -386
  275. package/lib/chunk-GJTGHXRA.mjs +0 -356
  276. package/lib/chunk-HMRMTYZM.mjs +0 -40
  277. package/lib/chunk-HN52G2YL.mjs +0 -305
  278. package/lib/chunk-LEM5OMRP.mjs +0 -384
  279. package/lib/chunk-O6SWBEOQ.mjs +0 -81
  280. package/lib/chunk-RCP7DHVY.mjs +0 -190
  281. package/lib/chunk-SBNDSKG5.mjs +0 -136
  282. package/lib/chunk-U6SO4QEV.mjs +0 -320
  283. package/lib/chunk-XD6BJK6Q.mjs +0 -351
  284. package/lib/chunk-YXSFGA2D.mjs +0 -383
  285. package/lib/git/gitattributes.d.ts +0 -33
  286. package/lib/git/gitattributes.js +0 -223
  287. package/lib/ps/index.d.ts +0 -2
  288. package/lib/ps/index.js +0 -253
  289. package/tmp/typedoc/readme.md +0 -320
  290. /package/lib/{binary-collections-config.d.cts → binary-collections/config.d.cts} +0 -0
@@ -17,9 +17,31 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
17
17
  const puppeteer_extra_1 = __importDefault(require("puppeteer-extra"));
18
18
  const puppeteer_extra_plugin_stealth_1 = __importDefault(require("puppeteer-extra-plugin-stealth"));
19
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");
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
+ const NAVIGATION_TIMEOUT_MS = 90000;
23
+ const NETWORK_IDLE_TIMEOUT_MS = 15000;
24
+ const MAX_INLINE_QUESTION_FILE_BYTES = 2 * 1024;
22
25
  fs_extra_1.default.ensureDirSync(COOKIE_DIR);
26
+ /**
27
+ * Navigates to a page with a resilient strategy for apps that keep long-lived network connections.
28
+ *
29
+ * @param {import('puppeteer').Page} page - Puppeteer page instance.
30
+ * @param {string} url - URL to navigate to.
31
+ * @returns {Promise<void>} Resolves when the page is at least DOM-ready.
32
+ */
33
+ function gotoWithFallback(page, url) {
34
+ return __awaiter(this, void 0, void 0, function* () {
35
+ yield page.goto(url, { waitUntil: 'domcontentloaded', timeout: NAVIGATION_TIMEOUT_MS });
36
+ // Best effort: settle initial bursty requests without hard-failing on persistent streams.
37
+ try {
38
+ yield page.waitForNetworkIdle({ idleTime: 1000, timeout: NETWORK_IDLE_TIMEOUT_MS });
39
+ }
40
+ catch (_a) {
41
+ // Ignore network-idle timeouts because ChatGPT keeps active connections open.
42
+ }
43
+ });
44
+ }
23
45
  /**
24
46
  * Saves cookies from a Puppeteer page to a specified file path.
25
47
  *
@@ -63,8 +85,8 @@ function navigatePage(page, url) {
63
85
  if (cookies) {
64
86
  yield page.setCookie(...cookies);
65
87
  }
66
- // Navigate and wait until fully loaded
67
- yield page.goto(url, { waitUntil: "networkidle0" });
88
+ // Navigate with fallback for pages that keep persistent network connections.
89
+ yield gotoWithFallback(page, url);
68
90
  // Inject DOM mutation observer to handle dynamic content
69
91
  yield page.evaluate(() => {
70
92
  window.__domStillUpdating = true;
@@ -104,7 +126,7 @@ function navigatePage(page, url) {
104
126
  }
105
127
  yield new Promise((r) => setTimeout(r, 200)); // poll every 200ms
106
128
  }
107
- throw new Error("DOM did not stabilize within timeout");
129
+ throw new Error('DOM did not stabilize within timeout');
108
130
  });
109
131
  return { waitForDomIdle };
110
132
  });
@@ -144,31 +166,41 @@ function _restoreCookies(page_1) {
144
166
  */
145
167
  function writeQuestion(page, question) {
146
168
  return __awaiter(this, void 0, void 0, function* () {
147
- const questions = question.split("\n");
148
- const promptTextarea = yield page.waitForSelector("#prompt-textarea", { timeout: 30000 });
169
+ const promptTextarea = yield page.waitForSelector('#prompt-textarea', { timeout: 30000 });
149
170
  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 });
171
+ 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.');
160
172
  return;
161
173
  }
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");
174
+ // Inject the full prompt instantly and emit input-like events so the UI reacts.
175
+ yield page.evaluate((text) => {
176
+ const promptEl = document.querySelector('#prompt-textarea');
177
+ if (!promptEl) {
178
+ return;
179
+ }
180
+ promptEl.focus();
181
+ promptEl.innerHTML = '';
182
+ const lines = String(text).split('\n');
183
+ for (const line of lines) {
184
+ const p = document.createElement('p');
185
+ p.textContent = line;
186
+ promptEl.appendChild(p);
171
187
  }
188
+ promptEl.dispatchEvent(new InputEvent('beforeinput', { bubbles: true, inputType: 'insertFromPaste', data: text }));
189
+ promptEl.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'insertFromPaste', data: text }));
190
+ promptEl.dispatchEvent(new Event('change', { bubbles: true }));
191
+ }, question);
192
+ // If the app state did not pick up the DOM injection, use keyboard insertion as a reliable fallback.
193
+ const hasPromptText = yield page.evaluate(() => {
194
+ const promptEl = document.querySelector('#prompt-textarea');
195
+ return Boolean(promptEl && promptEl.textContent && promptEl.textContent.trim().length > 0);
196
+ });
197
+ if (!hasPromptText) {
198
+ console.log('Prompt state not updated by DOM injection. Falling back to keyboard insertText.');
199
+ yield promptTextarea.click({ clickCount: 1 });
200
+ yield page.keyboard.down('Control');
201
+ yield page.keyboard.press('KeyA');
202
+ yield page.keyboard.up('Control');
203
+ yield page.keyboard.insertText(question);
172
204
  }
173
205
  });
174
206
  }
@@ -176,29 +208,121 @@ function writeQuestion(page, question) {
176
208
  * Clicks the submit button in ChatGPT interface, trying different button variants.
177
209
  *
178
210
  * @param {import('puppeteer').Page} page - Puppeteer page instance.
179
- * @returns {Promise<void>} Resolves when the submit button is clicked or attempt is made.
211
+ * @returns {Promise<boolean>} Resolves to true when submission is detected, otherwise false.
180
212
  */
181
213
  function clickSubmitButton(page) {
182
214
  return __awaiter(this, void 0, void 0, function* () {
215
+ console.log('Attempting to click the submit button...');
183
216
  try {
184
- const fruitjuiceSendButton = yield page.evaluate(() => {
185
- return document.querySelector('[data-testid="fruitjuice-send-button"]') !== null;
217
+ const userMessageCountBefore = yield page.$$eval('[data-message-author-role="user"]', (elements) => elements.length);
218
+ const waitForSubmit = (...args_1) => __awaiter(this, [...args_1], void 0, function* (timeout = 5000) {
219
+ try {
220
+ yield page.waitForFunction((previousCount) => {
221
+ const currentCount = document.querySelectorAll('[data-message-author-role="user"]').length;
222
+ return currentCount > previousCount;
223
+ }, { timeout }, userMessageCountBefore);
224
+ return true;
225
+ }
226
+ catch (_a) {
227
+ return false;
228
+ }
186
229
  });
187
- const sendButton = yield page.evaluate(() => {
188
- return document.querySelector('[data-testid="send-button"]') !== null;
230
+ yield page
231
+ .waitForFunction(() => {
232
+ const candidates = [
233
+ document.querySelector('[data-testid="fruitjuice-send-button"]'),
234
+ document.querySelector('#composer-submit-button'),
235
+ document.querySelector('[data-testid="send-button"]')
236
+ ].filter(Boolean);
237
+ return candidates.some((button) => {
238
+ const isDisabled = button.disabled || button.getAttribute('aria-disabled') === 'true';
239
+ const isVisible = button.offsetParent !== null;
240
+ return !isDisabled && isVisible;
241
+ });
242
+ }, { timeout: 5000 })
243
+ .catch(() => {
244
+ // Continue to diagnostics below even if no enabled button was found within timeout.
245
+ });
246
+ const buttonDetails = yield page.evaluate(() => {
247
+ const selectors = [
248
+ '[data-testid="fruitjuice-send-button"]',
249
+ '#composer-submit-button',
250
+ '[data-testid="send-button"]'
251
+ ];
252
+ const details = selectors.map((selector) => {
253
+ const el = document.querySelector(selector);
254
+ const exists = Boolean(el);
255
+ const disabled = exists ? Boolean(el.disabled || el.getAttribute('aria-disabled') === 'true') : null;
256
+ const visible = exists ? el.offsetParent !== null : null;
257
+ return { selector, exists, disabled, visible };
258
+ });
259
+ return details;
189
260
  });
190
- if (fruitjuiceSendButton) {
191
- yield page.click('[data-testid="fruitjuice-send-button"]');
261
+ console.log(`Submit button details: ${JSON.stringify(buttonDetails)}`);
262
+ const clickable = buttonDetails.find((item) => item.exists && item.visible && item.disabled === false);
263
+ const selectedSelector = clickable ? clickable.selector : null;
264
+ if (selectedSelector) {
265
+ yield page.click(selectedSelector);
266
+ console.log(`Clicked submit button selector: ${selectedSelector}`);
267
+ if (yield waitForSubmit(5000)) {
268
+ console.log('Submission detected after selector click.');
269
+ return true;
270
+ }
271
+ // Fallback: force a DOM click in case pointer-interception blocked page.click.
272
+ const forcedClickWorked = yield page.evaluate((selector) => {
273
+ const el = document.querySelector(selector);
274
+ if (!el) {
275
+ return false;
276
+ }
277
+ el.click();
278
+ return true;
279
+ }, selectedSelector);
280
+ if (forcedClickWorked) {
281
+ console.log(`Forced DOM click on selector: ${selectedSelector}`);
282
+ if (yield waitForSubmit(5000)) {
283
+ console.log('Submission detected after forced DOM click.');
284
+ return true;
285
+ }
286
+ }
192
287
  }
193
- else if (sendButton) {
194
- yield page.click('[data-testid="send-button"]');
288
+ console.log('Submit button path did not submit. Trying Enter key fallback on prompt.');
289
+ yield page.focus('#prompt-textarea');
290
+ yield page.keyboard.press('Enter');
291
+ if (yield waitForSubmit(5000)) {
292
+ console.log('Submission detected after Enter key fallback.');
293
+ return true;
195
294
  }
196
- else {
197
- console.log("Neither send button is present");
295
+ // Final fallback: submit the nearest composer form.
296
+ const didRequestSubmit = yield page.evaluate(() => {
297
+ const prompt = document.querySelector('#prompt-textarea');
298
+ if (!prompt) {
299
+ return false;
300
+ }
301
+ const form = prompt.closest('form');
302
+ if (!form) {
303
+ return false;
304
+ }
305
+ if (typeof form.requestSubmit === 'function') {
306
+ form.requestSubmit();
307
+ }
308
+ else {
309
+ form.submit();
310
+ }
311
+ return true;
312
+ });
313
+ if (didRequestSubmit) {
314
+ console.log('Triggered form submit fallback.');
315
+ if (yield waitForSubmit(5000)) {
316
+ console.log('Submission detected after form submit fallback.');
317
+ return true;
318
+ }
198
319
  }
320
+ console.log('Failed to submit prompt after all strategies.');
321
+ return false;
199
322
  }
200
323
  catch (e) {
201
324
  console.log(`Failed to click the send button: ${e}`);
325
+ return false;
202
326
  }
203
327
  });
204
328
  }
@@ -229,16 +353,16 @@ function waitForInitialResponse(page_1) {
229
353
  const currentMessageCount = assistantMessages.length;
230
354
  if (currentMessageCount > messageCount) {
231
355
  const lastMessage = assistantMessages[assistantMessages.length - 1];
232
- const isThinking = yield lastMessage.$(".result-thinking");
356
+ const isThinking = yield lastMessage.$('.result-thinking');
233
357
  if (!isThinking) {
234
- lastMessageId = yield page.evaluate((element) => element.getAttribute("data-message-id"), lastMessage);
358
+ lastMessageId = yield page.evaluate((element) => element.getAttribute('data-message-id'), lastMessage);
235
359
  messageCount = currentMessageCount;
236
360
  return;
237
361
  }
238
362
  }
239
363
  yield sleep(100);
240
364
  }
241
- console.log("Timed out waiting for the initial response.");
365
+ console.log('Timed out waiting for the initial response.');
242
366
  });
243
367
  }
244
368
  /**
@@ -249,15 +373,15 @@ function waitForInitialResponse(page_1) {
249
373
  * @returns {Promise<void>} Resolves when streaming is complete.
250
374
  */
251
375
  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 = "";
376
+ return __awaiter(this, arguments, void 0, function* (page, outputFile = upath_1.default.join(process.cwd(), 'tmp/response.txt')) {
377
+ let previousText = '';
378
+ let completeResponse = '';
255
379
  let newContentDetected = false;
256
380
  while (!newContentDetected) {
257
381
  const assistantMessages = yield page.$$('[data-message-author-role="assistant"]');
258
382
  if (assistantMessages.length > 0) {
259
383
  const lastMessage = assistantMessages[assistantMessages.length - 1];
260
- const currentMessageId = yield page.evaluate((element) => element.getAttribute("data-message-id"), lastMessage);
384
+ const currentMessageId = yield page.evaluate((element) => element.getAttribute('data-message-id'), lastMessage);
261
385
  if (currentMessageId === lastMessageId) {
262
386
  const currentText = yield page.evaluate((element) => element.textContent, lastMessage);
263
387
  console.log(`Current text: ${currentText}`);
@@ -270,7 +394,7 @@ function handleStreamingResponse(page_1) {
270
394
  }
271
395
  }
272
396
  previousText = currentText;
273
- const isStreaming = yield lastMessage.$(".result-streaming");
397
+ const isStreaming = yield lastMessage.$('.result-streaming');
274
398
  if (!isStreaming) {
275
399
  newContentDetected = true;
276
400
  }
@@ -283,7 +407,7 @@ function handleStreamingResponse(page_1) {
283
407
  }
284
408
  if (!is_streaming) {
285
409
  console.log(completeResponse.trim());
286
- console.log("\n\n");
410
+ console.log('\n\n');
287
411
  fs_extra_1.default.ensureDirSync(upath_1.default.dirname(outputFile));
288
412
  fs_extra_1.default.writeFileSync(outputFile, completeResponse.trim());
289
413
  console.log(`Response saved to ${outputFile}`);
@@ -315,41 +439,46 @@ function isLoggedIn(page) {
315
439
  */
316
440
  function createBrowser() {
317
441
  return __awaiter(this, arguments, void 0, function* (browserOptions = {}) {
442
+ const windowsChromeExecutable = 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe';
443
+ const hasWindowsChrome = process.platform === 'win32' && fs_extra_1.default.existsSync(windowsChromeExecutable);
318
444
  /**
319
445
  * @type {Parameters<import("puppeteer-extra").VanillaPuppeteer["launch"]>[0]}
320
446
  */
321
- const defaultOptions = Object.assign({ headless: false, userDataDir: upath_1.default.join(process.cwd(), "tmp/puppeteer-profile"),
447
+ const defaultOptions = Object.assign({ headless: false, defaultViewport: null, userDataDir: upath_1.default.join(process.cwd(), 'tmp/puppeteer-profile'),
322
448
  // Windows-specific options to handle browser launch issues
323
449
  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
450
+ '--start-maximized',
451
+ '--no-sandbox',
452
+ '--disable-setuid-sandbox',
453
+ '--disable-dev-shm-usage',
454
+ '--disable-accelerated-2d-canvas',
455
+ '--no-first-run',
456
+ '--no-zygote',
457
+ '--disable-gpu',
458
+ '--disable-background-timer-throttling',
459
+ '--disable-backgrounding-occluded-windows',
460
+ '--disable-renderer-backgrounding'
461
+ ], ignoreDefaultArgs: ['--disable-extensions'] }, (hasWindowsChrome && {
462
+ // Prefer local Chrome installation when present on Windows.
463
+ executablePath: windowsChromeExecutable
337
464
  }));
338
465
  try {
339
466
  return yield puppeteer_extra_1.default.use((0, puppeteer_extra_plugin_stealth_1.default)()).launch(Object.assign(Object.assign({}, defaultOptions), browserOptions));
340
467
  }
341
468
  catch (_error) {
342
- console.error("Failed to launch browser with default options. Trying fallback options...");
469
+ console.error('Failed to launch browser with default options. Trying fallback options...');
343
470
  // Fallback: Try with minimal options
344
471
  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));
472
+ return yield puppeteer_extra_1.default.use((0, puppeteer_extra_plugin_stealth_1.default)()).launch(Object.assign(Object.assign({ headless: browserOptions.headless || false, defaultViewport: null, args: ['--start-maximized', '--no-sandbox', '--disable-setuid-sandbox'], ignoreDefaultArgs: false }, (hasWindowsChrome && {
473
+ executablePath: windowsChromeExecutable
474
+ })), browserOptions));
346
475
  }
347
476
  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");
477
+ console.error('Browser launch failed completely. Common solutions:');
478
+ console.error('1. Install Google Chrome if not installed');
479
+ console.error('2. Update Node.js to the latest version');
480
+ console.error('3. Try running: npm install puppeteer --force');
481
+ console.error('4. Check if antivirus is blocking browser launch');
353
482
  throw new Error(`Browser launch failed: ${fallbackError.message}`);
354
483
  }
355
484
  }
@@ -364,7 +493,7 @@ function loginToChatGpt() {
364
493
  return __awaiter(this, void 0, void 0, function* () {
365
494
  const browser = yield createBrowser({ headless: false });
366
495
  const page = (yield browser.pages()).length > 0 ? (yield browser.pages())[0] : yield browser.newPage();
367
- const url = "https://chat.openai.com";
496
+ const url = 'https://chat.openai.com';
368
497
  const navigate = yield navigatePage(page, url);
369
498
  // Wait for page to fully load before checking login status
370
499
  yield navigate.waitForDomIdle(2000, 10000);
@@ -373,14 +502,20 @@ function loginToChatGpt() {
373
502
  return document.querySelector('[data-testid="login-button"]') !== null;
374
503
  });
375
504
  if (loginButtonExists) {
376
- console.log("Login button found, clicking to log in...");
505
+ console.log('Login button found, clicking to log in...');
377
506
  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.");
507
+ // Wait for the login process to complete without requiring full network idleness.
508
+ yield page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: NAVIGATION_TIMEOUT_MS });
509
+ try {
510
+ yield page.waitForNetworkIdle({ idleTime: 1000, timeout: NETWORK_IDLE_TIMEOUT_MS });
511
+ }
512
+ catch (_a) {
513
+ // Ignore: authentication pages can keep background connections active.
514
+ }
515
+ console.log('Login process completed.');
381
516
  }
382
517
  else {
383
- console.log("No login required - user appears to be already logged in.");
518
+ console.log('No login required - user appears to be already logged in.');
384
519
  }
385
520
  });
386
521
  }
@@ -414,31 +549,57 @@ function runChatGpt() {
414
549
  const headless = chatgptOptions.headless !== undefined ? chatgptOptions.headless : true;
415
550
  const questionFile = chatgptOptions.questionFile;
416
551
  let question = chatgptOptions.question;
417
- const responseFile = chatgptOptions.responseFile || upath_1.default.join(process.cwd(), "tmp", "response.txt");
552
+ let shouldUploadQuestionFile = Boolean(questionFile);
553
+ const responseFile = chatgptOptions.responseFile || upath_1.default.join(process.cwd(), 'tmp', 'response.txt');
418
554
  // Validate input parameters
419
555
  const noInputProvided = !question && !questionFile;
420
556
  const questionIsEmpty = question && question.trim().length === 0;
421
557
  const questionFileIsEmpty = questionFile && questionFile.trim().length === 0;
422
558
  if (noInputProvided || questionIsEmpty || questionFileIsEmpty) {
423
- throw new Error("You must provide a question or a question file.");
559
+ throw new Error('You must provide a question or a question file.');
560
+ }
561
+ // For small files, send content as plain text to avoid file-upload login requirements.
562
+ if (!question && questionFile) {
563
+ if (!fs_extra_1.default.existsSync(questionFile)) {
564
+ throw new Error(`Question file does not exist: ${questionFile}`);
565
+ }
566
+ const questionFileStats = fs_extra_1.default.statSync(questionFile);
567
+ if (questionFileStats.size <= MAX_INLINE_QUESTION_FILE_BYTES) {
568
+ question = fs_extra_1.default.readFileSync(questionFile, 'utf8').trim();
569
+ if (!question) {
570
+ throw new Error('Question file is empty.');
571
+ }
572
+ shouldUploadQuestionFile = false;
573
+ console.log(`Question file is ${questionFileStats.size} bytes (<= ${MAX_INLINE_QUESTION_FILE_BYTES}). Sending as text prompt.`);
574
+ }
424
575
  }
425
576
  let browser;
426
577
  try {
427
578
  browser = yield createBrowser({ headless });
428
579
  }
429
580
  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");
581
+ console.error('Error running ChatGPT:', error);
582
+ console.error('\nTroubleshooting steps:');
583
+ console.error('1. Make sure Google Chrome is installed');
584
+ console.error('2. Try running: yarn add puppeteer --force');
585
+ console.error('3. Check if your antivirus is blocking the browser');
586
+ console.error('4. Close any running Chrome instances and try again');
436
587
  throw error;
437
588
  }
589
+ const allPages = yield browser.pages();
438
590
  /** @type {import('puppeteer').Page} */
439
- const page = (yield browser.pages()).length > 0 ? (yield browser.pages())[0] : yield browser.newPage();
591
+ const page = allPages.length > 0 ? allPages[0] : yield browser.newPage();
592
+ yield page.bringToFront();
593
+ // Close other pages if more than one page is open.
594
+ if (allPages.length > 1) {
595
+ for (const p of allPages) {
596
+ if (p !== page) {
597
+ yield p.close();
598
+ }
599
+ }
600
+ }
440
601
  try {
441
- const url = "https://chat.openai.com";
602
+ const url = 'https://chat.openai.com';
442
603
  const navigate = yield navigatePage(page, url);
443
604
  // Check temporary chat - wait for page to load and try to click temporary chat button
444
605
  yield navigate.waitForDomIdle(2000, 15000);
@@ -446,11 +607,11 @@ function runChatGpt() {
446
607
  const tempChatButton = yield page.$('button[aria-label="Turn on temporary chat"]');
447
608
  if (tempChatButton) {
448
609
  yield page.evaluate((el) => el.click(), tempChatButton);
449
- console.log("Successfully clicked temporary chat button");
610
+ console.log('Successfully clicked temporary chat button');
450
611
  yield navigate.waitForDomIdle(1000, 10000);
451
612
  }
452
613
  else {
453
- console.log("Temporary chat button not found, proceeding without it.");
614
+ console.log('Temporary chat button not found, proceeding without it.');
454
615
  }
455
616
  }
456
617
  catch (error) {
@@ -459,7 +620,10 @@ function runChatGpt() {
459
620
  if (question) {
460
621
  yield writeQuestion(page, question);
461
622
  // Submit the question
462
- yield clickSubmitButton(page);
623
+ const didSubmit = yield clickSubmitButton(page);
624
+ if (!didSubmit) {
625
+ throw new Error('Prompt was not submitted. The composer button may be disabled or blocked.');
626
+ }
463
627
  yield navigate.waitForDomIdle(1000, 30000); // Wait for DOM to stabilize
464
628
  // Wait for the initial response
465
629
  yield waitForInitialResponse(page);
@@ -468,14 +632,14 @@ function runChatGpt() {
468
632
  // Save cookies for this host at the end
469
633
  yield saveCookies(page, getCookiePathForUrl(url));
470
634
  }
471
- else if (questionFile) {
635
+ else if (shouldUploadQuestionFile && questionFile) {
472
636
  // Wait for page to fully load before checking login status
473
637
  yield navigate.waitForDomIdle(2000, 10000);
474
638
  // Check if logged in
475
639
  const isUserLoggedIn = yield isLoggedIn(page);
476
- console.log(`Login status: ${isUserLoggedIn ? "Logged in" : "Not logged in"}`);
640
+ console.log(`Login status: ${isUserLoggedIn ? 'Logged in' : 'Not logged in'}`);
477
641
  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.");
642
+ console.log('Not logged in. Please log in to ChatGPT in the browser window, then close it and run the command again.');
479
643
  return loginToChatGpt();
480
644
  }
481
645
  // Upload the question file
@@ -490,7 +654,7 @@ function runChatGpt() {
490
654
  let clicked = false;
491
655
  for (const item of menuItems) {
492
656
  const text = yield page.evaluate((el) => el.innerText, item);
493
- if (text && text.includes("Add photos") && text.includes("files")) {
657
+ if (text && text.includes('Add photos') && text.includes('files')) {
494
658
  yield item.hover();
495
659
  clicked = true;
496
660
  break;
@@ -510,16 +674,19 @@ function runChatGpt() {
510
674
  yield fileInput.uploadFile(questionFile);
511
675
  // Wait for the file to be processed
512
676
  yield navigate.waitForDomIdle(2000, 15000);
513
- console.log("File uploaded successfully");
677
+ console.log('File uploaded successfully');
514
678
  // Optionally submit after file upload
515
- yield clickSubmitButton(page);
679
+ const didSubmit = yield clickSubmitButton(page);
680
+ if (!didSubmit) {
681
+ throw new Error('Prompt was not submitted after file upload.');
682
+ }
516
683
  yield navigate.waitForDomIdle(1000, 30000);
517
684
  // Wait for and handle response
518
685
  yield waitForInitialResponse(page);
519
686
  yield handleStreamingResponse(page, responseFile);
520
687
  }
521
688
  else {
522
- console.log("Could not find file input element");
689
+ console.log('Could not find file input element');
523
690
  }
524
691
  }
525
692
  catch (error) {
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from 'module'; const require = createRequire(import.meta.url);
2
2
  import {
3
3
  runChatGpt
4
- } from "../chunk-GJTGHXRA.mjs";
4
+ } from "../chunk-GAGABICI.mjs";
5
5
  import "../chunk-QQ4A6DLD.mjs";
6
6
  export {
7
7
  runChatGpt
@@ -0,0 +1,89 @@
1
+ // src/utils/findEnvFiles.cjs
2
+ var fs = require("fs");
3
+ var path = require("path");
4
+ var glob = require("glob");
5
+ var DEFAULT_IGNORES = [
6
+ "**/node_modules/**",
7
+ "**/.git/**",
8
+ "**/.yarn/**",
9
+ "**/.pnpm/**",
10
+ "**/dist/**",
11
+ "**/build/**",
12
+ "**/coverage/**",
13
+ "**/vendor/**",
14
+ "**/tmp/**",
15
+ "**/.cache/**",
16
+ "**/assets/**",
17
+ "**/logs/**",
18
+ "**/output/**",
19
+ "**/public/**",
20
+ "**/static/**",
21
+ "**/temp/**",
22
+ "**/backup/**",
23
+ "**/backups/**",
24
+ "**/examples/**",
25
+ "**/docs/**",
26
+ "**/tests/**",
27
+ "**/__tests__/**",
28
+ "**/spec/**",
29
+ "**/__specs__/**",
30
+ "**/scripts/**",
31
+ "**/bin/**",
32
+ "**/hooks/**",
33
+ "**/config/**",
34
+ "**/configs/**",
35
+ "**/settings/**",
36
+ "**/.vscode/**",
37
+ "**/.idea/**"
38
+ ];
39
+ function findEnvFiles(startDir = process.cwd(), filter) {
40
+ const found = /* @__PURE__ */ new Set();
41
+ function addFile(file) {
42
+ const normalized = path.normalize(file);
43
+ if (typeof filter === "function" && !filter(normalized)) {
44
+ return;
45
+ }
46
+ found.add(normalized);
47
+ }
48
+ let current = path.resolve(startDir);
49
+ while (true) {
50
+ const envPath = path.join(current, ".env");
51
+ if (fs.existsSync(envPath)) {
52
+ addFile(envPath);
53
+ }
54
+ const parent = path.dirname(current);
55
+ if (parent === current) {
56
+ break;
57
+ }
58
+ current = parent;
59
+ }
60
+ const files = glob.globSync("**/.env*", {
61
+ cwd: startDir,
62
+ absolute: true,
63
+ nodir: true,
64
+ ignore: DEFAULT_IGNORES
65
+ });
66
+ for (const file of files) {
67
+ addFile(file);
68
+ }
69
+ return [...found];
70
+ }
71
+ function findEnvWithToken(startDir = process.cwd(), tokenName = "GITHUB_TOKEN") {
72
+ const envFiles = findEnvFiles(startDir);
73
+ return envFiles.find((file) => {
74
+ try {
75
+ const content = fs.readFileSync(file, "utf-8");
76
+ const regex = new RegExp(`^\\s*${tokenName}\\s*=`, "m");
77
+ return regex.test(content);
78
+ } catch (err) {
79
+ console.warn(`Failed to read ${file}: ${err instanceof Error ? err.message : String(err)}`);
80
+ return false;
81
+ }
82
+ });
83
+ }
84
+ module.exports = {
85
+ DEFAULT_IGNORES,
86
+ findEnvFiles,
87
+ findEnvWithToken,
88
+ default: findEnvFiles
89
+ };