flow-frame-core 0.1.0

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 (416) hide show
  1. package/README.md +64 -0
  2. package/dist/Dockerfile +86 -0
  3. package/dist/GPU_DEPLOYMENT_README.md +324 -0
  4. package/dist/OPS_AGENT_README.md +174 -0
  5. package/dist/README-H100-VM.md +192 -0
  6. package/dist/README-worker-pools.md +231 -0
  7. package/dist/README.md +8 -0
  8. package/dist/WEB-ELEMENT-REQUESTS-README.md +302 -0
  9. package/dist/append.d.ts +3 -0
  10. package/dist/append.d.ts.map +1 -0
  11. package/dist/append.js +42 -0
  12. package/dist/append.js.map +1 -0
  13. package/dist/audioRoutes.d.ts +2 -0
  14. package/dist/audioRoutes.d.ts.map +1 -0
  15. package/dist/audioRoutes.js +97 -0
  16. package/dist/audioRoutes.js.map +1 -0
  17. package/dist/augment-parallel.d.ts +6 -0
  18. package/dist/augment-parallel.d.ts.map +1 -0
  19. package/dist/augment-parallel.js +128 -0
  20. package/dist/augment-parallel.js.map +1 -0
  21. package/dist/augment-worker.d.ts +2 -0
  22. package/dist/augment-worker.d.ts.map +1 -0
  23. package/dist/augment-worker.js +100 -0
  24. package/dist/augment-worker.js.map +1 -0
  25. package/dist/browerRoutes.d.ts +2 -0
  26. package/dist/browerRoutes.d.ts.map +1 -0
  27. package/dist/browerRoutes.js +323 -0
  28. package/dist/browerRoutes.js.map +1 -0
  29. package/dist/browser-utils/utils.d.ts +6 -0
  30. package/dist/browser-utils/utils.d.ts.map +1 -0
  31. package/dist/browser-utils/utils.js +133 -0
  32. package/dist/browser-utils/utils.js.map +1 -0
  33. package/dist/capture_training_data_endpoints.d.ts +158 -0
  34. package/dist/capture_training_data_endpoints.d.ts.map +1 -0
  35. package/dist/capture_training_data_endpoints.js +1812 -0
  36. package/dist/capture_training_data_endpoints.js.map +1 -0
  37. package/dist/config.json +28 -0
  38. package/dist/configEndpoints.d.ts +2 -0
  39. package/dist/configEndpoints.d.ts.map +1 -0
  40. package/dist/configEndpoints.js +459 -0
  41. package/dist/configEndpoints.js.map +1 -0
  42. package/dist/constants.d.ts +109 -0
  43. package/dist/constants.d.ts.map +1 -0
  44. package/dist/constants.js +110 -0
  45. package/dist/constants.js.map +1 -0
  46. package/dist/docs/workflow_nodes.md +257 -0
  47. package/dist/download.d.ts +11 -0
  48. package/dist/download.d.ts.map +1 -0
  49. package/dist/download.js +31 -0
  50. package/dist/download.js.map +1 -0
  51. package/dist/download.py +61 -0
  52. package/dist/ecosystem.config.json +63 -0
  53. package/dist/email-body-extractor.d.ts +20 -0
  54. package/dist/email-body-extractor.d.ts.map +1 -0
  55. package/dist/email-body-extractor.js +103 -0
  56. package/dist/email-body-extractor.js.map +1 -0
  57. package/dist/express_util.d.ts +2 -0
  58. package/dist/express_util.d.ts.map +1 -0
  59. package/dist/express_util.js +30 -0
  60. package/dist/express_util.js.map +1 -0
  61. package/dist/extension/background.d.ts +2 -0
  62. package/dist/extension/background.d.ts.map +1 -0
  63. package/dist/extension/background.js +268 -0
  64. package/dist/extension/background.js.map +1 -0
  65. package/dist/extension/manifest.json +19 -0
  66. package/dist/extensionUtils.d.ts +2 -0
  67. package/dist/extensionUtils.d.ts.map +1 -0
  68. package/dist/extensionUtils.js +48 -0
  69. package/dist/extensionUtils.js.map +1 -0
  70. package/dist/filter-gmail-poller/README.md +320 -0
  71. package/dist/filter-gmail-poller/demo.d.ts +2 -0
  72. package/dist/filter-gmail-poller/demo.d.ts.map +1 -0
  73. package/dist/filter-gmail-poller/demo.js +79 -0
  74. package/dist/filter-gmail-poller/demo.js.map +1 -0
  75. package/dist/filter-gmail-poller/example-existing-app.d.ts +2 -0
  76. package/dist/filter-gmail-poller/example-existing-app.d.ts.map +1 -0
  77. package/dist/filter-gmail-poller/example-existing-app.js +72 -0
  78. package/dist/filter-gmail-poller/example-existing-app.js.map +1 -0
  79. package/dist/filter-gmail-poller/filter-gmail-poller.d.ts +160 -0
  80. package/dist/filter-gmail-poller/filter-gmail-poller.d.ts.map +1 -0
  81. package/dist/filter-gmail-poller/filter-gmail-poller.js +1048 -0
  82. package/dist/filter-gmail-poller/filter-gmail-poller.js.map +1 -0
  83. package/dist/filter-gmail-poller/index.d.ts +3 -0
  84. package/dist/filter-gmail-poller/index.d.ts.map +1 -0
  85. package/dist/filter-gmail-poller/index.js +18 -0
  86. package/dist/filter-gmail-poller/index.js.map +1 -0
  87. package/dist/filter-gmail-poller/manual-test.d.ts +2 -0
  88. package/dist/filter-gmail-poller/manual-test.d.ts.map +1 -0
  89. package/dist/filter-gmail-poller/manual-test.js +70 -0
  90. package/dist/filter-gmail-poller/manual-test.js.map +1 -0
  91. package/dist/filter-gmail-poller/poller-prompts.d.ts +12 -0
  92. package/dist/filter-gmail-poller/poller-prompts.d.ts.map +1 -0
  93. package/dist/filter-gmail-poller/poller-prompts.js +330 -0
  94. package/dist/filter-gmail-poller/poller-prompts.js.map +1 -0
  95. package/dist/filter-gmail-poller/test.js +69 -0
  96. package/dist/flowframe-auto-firebase-adminsdk.json +13 -0
  97. package/dist/gmail-poller/README-microsoft-email-poller.md +203 -0
  98. package/dist/gmail-poller/README.md +129 -0
  99. package/dist/gmail-poller/example.d.ts +5 -0
  100. package/dist/gmail-poller/example.d.ts.map +1 -0
  101. package/dist/gmail-poller/example.js +83 -0
  102. package/dist/gmail-poller/example.js.map +1 -0
  103. package/dist/gmail-poller/gmail-poller.d.ts +82 -0
  104. package/dist/gmail-poller/gmail-poller.d.ts.map +1 -0
  105. package/dist/gmail-poller/gmail-poller.js +455 -0
  106. package/dist/gmail-poller/gmail-poller.js.map +1 -0
  107. package/dist/gmail-poller/manual-test.d.ts +2 -0
  108. package/dist/gmail-poller/manual-test.d.ts.map +1 -0
  109. package/dist/gmail-poller/manual-test.js +37 -0
  110. package/dist/gmail-poller/manual-test.js.map +1 -0
  111. package/dist/gmail-poller/microsoft-email-example.d.ts +8 -0
  112. package/dist/gmail-poller/microsoft-email-example.d.ts.map +1 -0
  113. package/dist/gmail-poller/microsoft-email-example.js +58 -0
  114. package/dist/gmail-poller/microsoft-email-example.js.map +1 -0
  115. package/dist/gmail-poller/microsoft-email-poller.d.ts +73 -0
  116. package/dist/gmail-poller/microsoft-email-poller.d.ts.map +1 -0
  117. package/dist/gmail-poller/microsoft-email-poller.js +346 -0
  118. package/dist/gmail-poller/microsoft-email-poller.js.map +1 -0
  119. package/dist/gmail-poller/setup-auth.d.ts +3 -0
  120. package/dist/gmail-poller/setup-auth.d.ts.map +1 -0
  121. package/dist/gmail-poller/setup-auth.js +36 -0
  122. package/dist/gmail-poller/setup-auth.js.map +1 -0
  123. package/dist/gmail-poller/test.js +36 -0
  124. package/dist/index.d.ts +10 -0
  125. package/dist/index.d.ts.map +1 -0
  126. package/dist/index.js +28 -0
  127. package/dist/index.js.map +1 -0
  128. package/dist/inference/augment_levels.d.ts +2 -0
  129. package/dist/inference/augment_levels.d.ts.map +1 -0
  130. package/dist/inference/augment_levels.js +1 -0
  131. package/dist/inference/augment_levels.js.map +1 -0
  132. package/dist/inference/capture-overlay.d.ts +13 -0
  133. package/dist/inference/capture-overlay.d.ts.map +1 -0
  134. package/dist/inference/capture-overlay.js +355 -0
  135. package/dist/inference/capture-overlay.js.map +1 -0
  136. package/dist/inference/capturescreenshot.d.ts +12 -0
  137. package/dist/inference/capturescreenshot.d.ts.map +1 -0
  138. package/dist/inference/capturescreenshot.js +157 -0
  139. package/dist/inference/capturescreenshot.js.map +1 -0
  140. package/dist/jsonHandler.d.ts +37 -0
  141. package/dist/jsonHandler.d.ts.map +1 -0
  142. package/dist/jsonHandler.js +191 -0
  143. package/dist/jsonHandler.js.map +1 -0
  144. package/dist/localStorage.json +11 -0
  145. package/dist/media_data_endpoints.d.ts +2 -0
  146. package/dist/media_data_endpoints.d.ts.map +1 -0
  147. package/dist/media_data_endpoints.js +102 -0
  148. package/dist/media_data_endpoints.js.map +1 -0
  149. package/dist/operations/blender-ops.d.ts +4 -0
  150. package/dist/operations/blender-ops.d.ts.map +1 -0
  151. package/dist/operations/blender-ops.js +55 -0
  152. package/dist/operations/blender-ops.js.map +1 -0
  153. package/dist/operations.d.ts +34 -0
  154. package/dist/operations.d.ts.map +1 -0
  155. package/dist/operations.js +1514 -0
  156. package/dist/operations.js.map +1 -0
  157. package/dist/pdfRoutes.d.ts +2 -0
  158. package/dist/pdfRoutes.d.ts.map +1 -0
  159. package/dist/pdfRoutes.js +56 -0
  160. package/dist/pdfRoutes.js.map +1 -0
  161. package/dist/peers.d.ts +9 -0
  162. package/dist/peers.d.ts.map +1 -0
  163. package/dist/peers.js +70 -0
  164. package/dist/peers.js.map +1 -0
  165. package/dist/playparser.d.ts +2 -0
  166. package/dist/playparser.d.ts.map +1 -0
  167. package/dist/playparser.js +281 -0
  168. package/dist/playparser.js.map +1 -0
  169. package/dist/process.d.ts +4 -0
  170. package/dist/process.d.ts.map +1 -0
  171. package/dist/process.js +375 -0
  172. package/dist/process.js.map +1 -0
  173. package/dist/promptRoutes.d.ts +7 -0
  174. package/dist/promptRoutes.d.ts.map +1 -0
  175. package/dist/promptRoutes.js +68 -0
  176. package/dist/promptRoutes.js.map +1 -0
  177. package/dist/queueManager.d.ts +23 -0
  178. package/dist/queueManager.d.ts.map +1 -0
  179. package/dist/queueManager.js +96 -0
  180. package/dist/queueManager.js.map +1 -0
  181. package/dist/run-flow.d.ts +8 -0
  182. package/dist/run-flow.d.ts.map +1 -0
  183. package/dist/run-flow.js +220 -0
  184. package/dist/run-flow.js.map +1 -0
  185. package/dist/scraper.d.ts +2 -0
  186. package/dist/scraper.d.ts.map +1 -0
  187. package/dist/scraper.js +75 -0
  188. package/dist/scraper.js.map +1 -0
  189. package/dist/scraper_endpoints.d.ts +2 -0
  190. package/dist/scraper_endpoints.d.ts.map +1 -0
  191. package/dist/scraper_endpoints.js +40 -0
  192. package/dist/scraper_endpoints.js.map +1 -0
  193. package/dist/server.d.ts +2 -0
  194. package/dist/server.d.ts.map +1 -0
  195. package/dist/server.js +528 -0
  196. package/dist/server.js.map +1 -0
  197. package/dist/services/ModelContext.d.ts +7 -0
  198. package/dist/services/ModelContext.d.ts.map +1 -0
  199. package/dist/services/ModelContext.js +7 -0
  200. package/dist/services/ModelContext.js.map +1 -0
  201. package/dist/services/agenticUiPlanner.d.ts +27 -0
  202. package/dist/services/agenticUiPlanner.d.ts.map +1 -0
  203. package/dist/services/agenticUiPlanner.js +161 -0
  204. package/dist/services/agenticUiPlanner.js.map +1 -0
  205. package/dist/services/apiKeyService.d.ts +3 -0
  206. package/dist/services/apiKeyService.d.ts.map +1 -0
  207. package/dist/services/apiKeyService.js +7 -0
  208. package/dist/services/apiKeyService.js.map +1 -0
  209. package/dist/services/audioService.d.ts +10 -0
  210. package/dist/services/audioService.d.ts.map +1 -0
  211. package/dist/services/audioService.js +140 -0
  212. package/dist/services/audioService.js.map +1 -0
  213. package/dist/services/autoPromptOptimizer.d.ts +44 -0
  214. package/dist/services/autoPromptOptimizer.d.ts.map +1 -0
  215. package/dist/services/autoPromptOptimizer.js +344 -0
  216. package/dist/services/autoPromptOptimizer.js.map +1 -0
  217. package/dist/services/autoPromptOptimizer.manual-test.d.ts +2 -0
  218. package/dist/services/autoPromptOptimizer.manual-test.d.ts.map +1 -0
  219. package/dist/services/autoPromptOptimizer.manual-test.js +27 -0
  220. package/dist/services/autoPromptOptimizer.manual-test.js.map +1 -0
  221. package/dist/services/chainExecutor.d.ts +26 -0
  222. package/dist/services/chainExecutor.d.ts.map +1 -0
  223. package/dist/services/chainExecutor.js +399 -0
  224. package/dist/services/chainExecutor.js.map +1 -0
  225. package/dist/services/classifyImageQuestion.d.ts +55 -0
  226. package/dist/services/classifyImageQuestion.d.ts.map +1 -0
  227. package/dist/services/classifyImageQuestion.js +428 -0
  228. package/dist/services/classifyImageQuestion.js.map +1 -0
  229. package/dist/services/configuration/executor.d.ts +3 -0
  230. package/dist/services/configuration/executor.d.ts.map +1 -0
  231. package/dist/services/configuration/executor.js +795 -0
  232. package/dist/services/configuration/executor.js.map +1 -0
  233. package/dist/services/error.d.ts +13 -0
  234. package/dist/services/error.d.ts.map +1 -0
  235. package/dist/services/error.js +34 -0
  236. package/dist/services/error.js.map +1 -0
  237. package/dist/services/executor.d.ts +11 -0
  238. package/dist/services/executor.d.ts.map +1 -0
  239. package/dist/services/executor.js +1587 -0
  240. package/dist/services/executor.js.map +1 -0
  241. package/dist/services/extractPdf.d.ts +26 -0
  242. package/dist/services/extractPdf.d.ts.map +1 -0
  243. package/dist/services/extractPdf.js +256 -0
  244. package/dist/services/extractPdf.js.map +1 -0
  245. package/dist/services/generateJsTransformFromPrompt.d.ts +11 -0
  246. package/dist/services/generateJsTransformFromPrompt.d.ts.map +1 -0
  247. package/dist/services/generateJsTransformFromPrompt.js +328 -0
  248. package/dist/services/generateJsTransformFromPrompt.js.map +1 -0
  249. package/dist/services/localizeFirebaseMedia.d.ts +20 -0
  250. package/dist/services/localizeFirebaseMedia.d.ts.map +1 -0
  251. package/dist/services/localizeFirebaseMedia.js +135 -0
  252. package/dist/services/localizeFirebaseMedia.js.map +1 -0
  253. package/dist/services/polyfill_canvas.d.ts +2 -0
  254. package/dist/services/polyfill_canvas.d.ts.map +1 -0
  255. package/dist/services/polyfill_canvas.js +19 -0
  256. package/dist/services/polyfill_canvas.js.map +1 -0
  257. package/dist/services/promptRoutes.d.ts +7 -0
  258. package/dist/services/promptRoutes.d.ts.map +1 -0
  259. package/dist/services/promptRoutes.js +70 -0
  260. package/dist/services/promptRoutes.js.map +1 -0
  261. package/dist/services/runPrompt.d.ts +29 -0
  262. package/dist/services/runPrompt.d.ts.map +1 -0
  263. package/dist/services/runPrompt.js +232 -0
  264. package/dist/services/runPrompt.js.map +1 -0
  265. package/dist/services/schemaInference.d.ts +2 -0
  266. package/dist/services/schemaInference.d.ts.map +1 -0
  267. package/dist/services/schemaInference.js +17 -0
  268. package/dist/services/schemaInference.js.map +1 -0
  269. package/dist/services/self-learning/api.d.ts +2 -0
  270. package/dist/services/self-learning/api.d.ts.map +1 -0
  271. package/dist/services/self-learning/api.js +84 -0
  272. package/dist/services/self-learning/api.js.map +1 -0
  273. package/dist/services/self-learning/autolearn.d.ts +23 -0
  274. package/dist/services/self-learning/autolearn.d.ts.map +1 -0
  275. package/dist/services/self-learning/autolearn.js +308 -0
  276. package/dist/services/self-learning/autolearn.js.map +1 -0
  277. package/dist/services/self-learning/discover.d.ts +11 -0
  278. package/dist/services/self-learning/discover.d.ts.map +1 -0
  279. package/dist/services/self-learning/discover.js +446 -0
  280. package/dist/services/self-learning/discover.js.map +1 -0
  281. package/dist/services/self-learning/image.d.ts +10 -0
  282. package/dist/services/self-learning/image.d.ts.map +1 -0
  283. package/dist/services/self-learning/image.js +38 -0
  284. package/dist/services/self-learning/image.js.map +1 -0
  285. package/dist/services/self-learning/injest.d.ts +25 -0
  286. package/dist/services/self-learning/injest.d.ts.map +1 -0
  287. package/dist/services/self-learning/injest.js +110 -0
  288. package/dist/services/self-learning/injest.js.map +1 -0
  289. package/dist/services/self-learning/learn.d.ts +2 -0
  290. package/dist/services/self-learning/learn.d.ts.map +1 -0
  291. package/dist/services/self-learning/learn.js +145 -0
  292. package/dist/services/self-learning/learn.js.map +1 -0
  293. package/dist/services/self-learning/matcher.d.ts +2 -0
  294. package/dist/services/self-learning/matcher.d.ts.map +1 -0
  295. package/dist/services/self-learning/matcher.js +38 -0
  296. package/dist/services/self-learning/matcher.js.map +1 -0
  297. package/dist/services/self-learning/openai.d.ts +8 -0
  298. package/dist/services/self-learning/openai.d.ts.map +1 -0
  299. package/dist/services/self-learning/openai.js +97 -0
  300. package/dist/services/self-learning/openai.js.map +1 -0
  301. package/dist/services/self-learning/phash.d.ts +5 -0
  302. package/dist/services/self-learning/phash.d.ts.map +1 -0
  303. package/dist/services/self-learning/phash.js +68 -0
  304. package/dist/services/self-learning/phash.js.map +1 -0
  305. package/dist/services/self-learning/recognize.d.ts +17 -0
  306. package/dist/services/self-learning/recognize.d.ts.map +1 -0
  307. package/dist/services/self-learning/recognize.js +116 -0
  308. package/dist/services/self-learning/recognize.js.map +1 -0
  309. package/dist/services/self-learning/record_transition.d.ts +8 -0
  310. package/dist/services/self-learning/record_transition.d.ts.map +1 -0
  311. package/dist/services/self-learning/record_transition.js +20 -0
  312. package/dist/services/self-learning/record_transition.js.map +1 -0
  313. package/dist/services/self-learning/registry.d.ts +4 -0
  314. package/dist/services/self-learning/registry.d.ts.map +1 -0
  315. package/dist/services/self-learning/registry.js +19 -0
  316. package/dist/services/self-learning/registry.js.map +1 -0
  317. package/dist/services/self-learning/schema.d.ts +114 -0
  318. package/dist/services/self-learning/schema.d.ts.map +1 -0
  319. package/dist/services/self-learning/schema.js +70 -0
  320. package/dist/services/self-learning/schema.js.map +1 -0
  321. package/dist/services/self-learning/schemaStrictify.d.ts +2 -0
  322. package/dist/services/self-learning/schemaStrictify.d.ts.map +1 -0
  323. package/dist/services/self-learning/schemaStrictify.js +34 -0
  324. package/dist/services/self-learning/schemaStrictify.js.map +1 -0
  325. package/dist/services/self-learning/transition_graph.d.ts +6 -0
  326. package/dist/services/self-learning/transition_graph.d.ts.map +1 -0
  327. package/dist/services/self-learning/transition_graph.js +83 -0
  328. package/dist/services/self-learning/transition_graph.js.map +1 -0
  329. package/dist/services/self-learning/transition_log.d.ts +3 -0
  330. package/dist/services/self-learning/transition_log.d.ts.map +1 -0
  331. package/dist/services/self-learning/transition_log.js +42 -0
  332. package/dist/services/self-learning/transition_log.js.map +1 -0
  333. package/dist/services/self-learning/util.d.ts +3 -0
  334. package/dist/services/self-learning/util.d.ts.map +1 -0
  335. package/dist/services/self-learning/util.js +11 -0
  336. package/dist/services/self-learning/util.js.map +1 -0
  337. package/dist/services/stepByStepAiPlanner.d.ts +39 -0
  338. package/dist/services/stepByStepAiPlanner.d.ts.map +1 -0
  339. package/dist/services/stepByStepAiPlanner.js +379 -0
  340. package/dist/services/stepByStepAiPlanner.js.map +1 -0
  341. package/dist/services/test-genjs.js +39 -0
  342. package/dist/services/test-genjs.manual-test.d.ts +2 -0
  343. package/dist/services/test-genjs.manual-test.d.ts.map +1 -0
  344. package/dist/services/test-genjs.manual-test.js +40 -0
  345. package/dist/services/test-genjs.manual-test.js.map +1 -0
  346. package/dist/services/uiMapPathFinder.d.ts +13 -0
  347. package/dist/services/uiMapPathFinder.d.ts.map +1 -0
  348. package/dist/services/uiMapPathFinder.js +79 -0
  349. package/dist/services/uiMapPathFinder.js.map +1 -0
  350. package/dist/services/uiMapService.d.ts +26 -0
  351. package/dist/services/uiMapService.d.ts.map +1 -0
  352. package/dist/services/uiMapService.js +275 -0
  353. package/dist/services/uiMapService.js.map +1 -0
  354. package/dist/services/uiPlanner.d.ts +54 -0
  355. package/dist/services/uiPlanner.d.ts.map +1 -0
  356. package/dist/services/uiPlanner.js +558 -0
  357. package/dist/services/uiPlanner.js.map +1 -0
  358. package/dist/services/utilityFunctions.d.ts +80 -0
  359. package/dist/services/utilityFunctions.d.ts.map +1 -0
  360. package/dist/services/utilityFunctions.js +352 -0
  361. package/dist/services/utilityFunctions.js.map +1 -0
  362. package/dist/services/variableGenerator.d.ts +39 -0
  363. package/dist/services/variableGenerator.d.ts.map +1 -0
  364. package/dist/services/variableGenerator.js +157 -0
  365. package/dist/services/variableGenerator.js.map +1 -0
  366. package/dist/services/workflow/build-workflow.d.ts +49 -0
  367. package/dist/services/workflow/build-workflow.d.ts.map +1 -0
  368. package/dist/services/workflow/build-workflow.js +119 -0
  369. package/dist/services/workflow/build-workflow.js.map +1 -0
  370. package/dist/standardRoutes.d.ts +2 -0
  371. package/dist/standardRoutes.d.ts.map +1 -0
  372. package/dist/standardRoutes.js +1495 -0
  373. package/dist/standardRoutes.js.map +1 -0
  374. package/dist/stepWorkflowRoutes.d.ts +2 -0
  375. package/dist/stepWorkflowRoutes.d.ts.map +1 -0
  376. package/dist/stepWorkflowRoutes.js +1007 -0
  377. package/dist/stepWorkflowRoutes.js.map +1 -0
  378. package/dist/storage.d.ts +19 -0
  379. package/dist/storage.d.ts.map +1 -0
  380. package/dist/storage.docker.json +61 -0
  381. package/dist/storage.js +131 -0
  382. package/dist/storage.js.map +1 -0
  383. package/dist/storage.json +78 -0
  384. package/dist/storage_cache/boxes.json +48 -0
  385. package/dist/storage_cache/suno_state.json +3 -0
  386. package/dist/suno_download.d.ts +11 -0
  387. package/dist/suno_download.d.ts.map +1 -0
  388. package/dist/suno_download.js +33 -0
  389. package/dist/suno_download.js.map +1 -0
  390. package/dist/suno_download.py +119 -0
  391. package/dist/test-web-element-requests.d.ts +6 -0
  392. package/dist/test-web-element-requests.d.ts.map +1 -0
  393. package/dist/test-web-element-requests.js +114 -0
  394. package/dist/test-web-element-requests.js.map +1 -0
  395. package/dist/test_pdf_render.d.ts +2 -0
  396. package/dist/test_pdf_render.d.ts.map +1 -0
  397. package/dist/test_pdf_render.js +50 -0
  398. package/dist/test_pdf_render.js.map +1 -0
  399. package/dist/training_data_viewer_endpoints.d.ts +2 -0
  400. package/dist/training_data_viewer_endpoints.d.ts.map +1 -0
  401. package/dist/training_data_viewer_endpoints.js +141 -0
  402. package/dist/training_data_viewer_endpoints.js.map +1 -0
  403. package/dist/utils.d.ts +353 -0
  404. package/dist/utils.d.ts.map +1 -0
  405. package/dist/utils.js +1517 -0
  406. package/dist/utils.js.map +1 -0
  407. package/dist/vm-h100.env.template +55 -0
  408. package/dist/web-element-requests.d.ts +102 -0
  409. package/dist/web-element-requests.d.ts.map +1 -0
  410. package/dist/web-element-requests.js +278 -0
  411. package/dist/web-element-requests.js.map +1 -0
  412. package/dist/workflowRoutes.d.ts +2 -0
  413. package/dist/workflowRoutes.d.ts.map +1 -0
  414. package/dist/workflowRoutes.js +441 -0
  415. package/dist/workflowRoutes.js.map +1 -0
  416. package/package.json +109 -0
package/dist/utils.js ADDED
@@ -0,0 +1,1517 @@
1
+ import path from 'path';
2
+ import { windowManager } from 'node-window-manager';
3
+ import http from 'http';
4
+ import fs from 'fs';
5
+ import os from 'os';
6
+ import extract from 'extract-zip';
7
+ import https from 'https';
8
+ import { URL } from 'url';
9
+ import { imageSize } from 'image-size';
10
+ import progressStream from 'progress-stream';
11
+ import ffmpeg from 'fluent-ffmpeg';
12
+ import robot from 'robotjs';
13
+ import { getItem, isDocker, setItem } from './storage.js';
14
+ import { KEYS, MODES } from './constants.js';
15
+ import archiver from 'archiver';
16
+ import { execSync } from 'child_process';
17
+ const console_context = {};
18
+ export function console_log_it(text, cat) {
19
+ console_context[cat || 'general'] = text;
20
+ if (true) {
21
+ return;
22
+ }
23
+ if (isDocker()) {
24
+ console.log(`${cat}: ${text}`);
25
+ return;
26
+ }
27
+ console.clear();
28
+ Object.entries(console_context).map(([key, value]) => {
29
+ if (console_context[`mode-filter`]) {
30
+ if (key.includes(console_context[`mode-filter`])) {
31
+ if (typeof value === 'object') {
32
+ return console.log(`${key}: ${JSON.stringify(value, null, 3)}`);
33
+ }
34
+ return console.log(`${key}: ${value}`);
35
+ }
36
+ return;
37
+ }
38
+ return console.log(`${key}: ${value}`);
39
+ });
40
+ }
41
+ export function getConsoleContext() {
42
+ return console_context;
43
+ }
44
+ const input_bar = '[id="desktop_input_bar"]';
45
+ const MIDJOURNEY_SELECTOR = {
46
+ queued: 'span||Queued',
47
+ [input_bar]: input_bar,
48
+ addImageBtn: '[id="AddImageIcon"]',
49
+ promptImage: '[src*="https://cdn.midjourney.com/u/',
50
+ chooseAnImage: 'button span||Choose a file',
51
+ starting: 'div[id="pageScroll"] span.relative||Starting...',
52
+ completing: 'div[id="pageScroll"] span.relative||!Complete',
53
+ firstImage: 'div[id="pageScroll"] a',
54
+ buttonSearchBtn: 'div button||Search',
55
+ searchInput: '[id="search-input"]',
56
+ downloadButton: 'button[title="Download Video for Social Media"] svg',
57
+ lightboxPrompt: '[id="lightboxPrompt"] p',
58
+ firstImageCell: '[id="pageScroll"] div div:first-child div.relative.flex-center',
59
+ ilikeRadiobutton: 'button span||I like this video',
60
+ iNeutralRadiobutton: `button span||I'm neutral on this video`,
61
+ buttonAnimate: 'button||Animate',
62
+ videoButton: 'div a video',
63
+ videoRatio: 'button[title="Image aspect ratio"] span||ar 91:51',
64
+ videoRatio2: 'button[title="Image aspect ratio"] span||ar 143:256',
65
+ videoRatio3: 'button[title="Image aspect ratio"] span||ar 51:91',
66
+ videoRatio4: 'button[title="Image aspect ratio"] span||ar 77:58',
67
+ videoRatio5: 'button[title="Image aspect ratio"] span||ar 3:2',
68
+ videoRatio6: 'button[title="Image aspect ratio"] span||ar 2:3',
69
+ optionsButton: 'button[title="Options"]',
70
+ optionsDownloadButton: 'button div span||Download for Social',
71
+ useText: 'h2||Use'
72
+ };
73
+ export function processSunoState(suno_state, project_song_states, project, all_project_songs_ready, song_states) {
74
+ suno_state = get(KEYS.suno_state) || {};
75
+ const sunoStateValues = Object.values(suno_state);
76
+ project_song_states = sunoStateValues.filter(x => x.name === project.name);
77
+ let unready_songs = project_song_states.filter(x => !x.ready || !x.url || !x.lyric);
78
+ all_project_songs_ready = !project_song_states.some(x => !x.ready || !x.url || !x.lyric);
79
+ console_log_it(`unready songs ${all_project_songs_ready} ${project_song_states.filter(x => !x.ready || !x.url || !x.lyric).length}`, 'unready-songs');
80
+ song_states = project_song_states.filter(x => x.name === project.name && x.url && x.lyric);
81
+ project.lyrics = project.lyrics || [];
82
+ project.lyrics = unique([...project.lyrics, ...song_states.map(v => v.lyric)], v => v.songId);
83
+ project.song_sources = unique([...project.song_sources, ...song_states.map(v => v.url)], v => v);
84
+ console_log_it(`${project.song_sources.length}`, 'soung_source_count');
85
+ console_log_it(`${song_states.length}/${project_song_states.length}`, 'song_progress');
86
+ console_log_it(buildProgressBar(song_states.length, project_song_states.length), 'song_progress_bar');
87
+ let skip = false;
88
+ if (unready_songs.length > 3) {
89
+ skip = true;
90
+ // Do something if there are more than 3 unready songs
91
+ console_log_it(`There are more than 3 unready songs: ${unready_songs.length}`, 'unready-songs');
92
+ }
93
+ return { suno_state, project_song_states, all_project_songs_ready, song_states, skip };
94
+ }
95
+ /**
96
+ * Detect whether an image is portrait, landscape, or square.
97
+ *
98
+ * @param {string|Buffer} input – either a path to the image file, or a Buffer
99
+ * @returns {'portrait'|'landscape'|'square'}
100
+ */
101
+ const doCache = {};
102
+ function getDetectOrientation({ width, height }) {
103
+ if (width > height) {
104
+ return 'landscape';
105
+ }
106
+ if (height > width) {
107
+ return 'portrait';
108
+ }
109
+ return 'square';
110
+ }
111
+ function detectOrientation(input) {
112
+ // image-size will accept a file path or a Buffer
113
+ // image-size will accept a file path or a Buffer
114
+ if (doCache[input]) {
115
+ return doCache[input];
116
+ }
117
+ const { width, height } = imageSize(fs.readFileSync(input));
118
+ if (width > height) {
119
+ console.log('landscape');
120
+ doCache[input] = 'landscape';
121
+ return doCache[input];
122
+ }
123
+ if (height > width) {
124
+ console.log('portrait');
125
+ doCache[input] = 'portrait';
126
+ return doCache[input];
127
+ }
128
+ console.log('square');
129
+ doCache[input] = 'square';
130
+ return doCache[input];
131
+ }
132
+ /**
133
+ * Returns one or more random element(s) from the given array.
134
+ * @param {any[]} arr — the array to pick from
135
+ * @param {number} [count=1] — how many random elements to select
136
+ * @returns {any|any[]|undefined} a single element if count is 1 (or undefined if empty),
137
+ * otherwise an array of up to count elements
138
+ */
139
+ function selectRandom(arr, count = 1) {
140
+ if (!Array.isArray(arr)) {
141
+ throw new TypeError('selectRandom expects an array');
142
+ }
143
+ if (!Number.isInteger(count) || count < 1) {
144
+ throw new TypeError('selectRandom expects count as a positive integer');
145
+ }
146
+ const len = arr.length;
147
+ if (len === 0) {
148
+ // empty array: undefined for single, empty array for multiple
149
+ return count === 1 ? undefined : [];
150
+ }
151
+ if (count === 1) {
152
+ // single pick
153
+ const idx = Math.floor(Math.random() * len);
154
+ return [arr[idx]];
155
+ }
156
+ // multiple picks — shuffle a copy of the array
157
+ const shuffled = arr.slice();
158
+ for (let i = shuffled.length - 1; i > 0; i--) {
159
+ const j = Math.floor(Math.random() * (i + 1));
160
+ [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
161
+ }
162
+ // return up to count items
163
+ return shuffled.slice(0, Math.min(count, len));
164
+ }
165
+ /**
166
+ * Node.js script to extract all zip files from a source folder to a destination folder.
167
+ *
168
+ * Usage: node unzip-all.js <source_folder> <destination_folder>
169
+ * If not provided, defaults are:
170
+ * source_folder: './zips'
171
+ * destination_folder: './unzipped'
172
+ *
173
+ * Required dependency: extract-zip (install via: npm install extract-zip)
174
+ */
175
+ async function unzip(sourceFolder, destinationFolder, subfolder = '') {
176
+ // const sourceFolder = process.argv[2] || 'F:\\ai\\historical figures\\zips';
177
+ // const destinationFolder = process.argv[3] || 'F:\\ai\\historical figures\\images';
178
+ // const subfolder = process.argv[4] || '';
179
+ // Read all files from the source folder
180
+ console.log(`unziping....`);
181
+ console.log(`sourceFolder: ${sourceFolder}`);
182
+ console.log(`destinationFolder: ${destinationFolder}`);
183
+ console.log(`subfolder: ${subfolder}`);
184
+ let files = fs.readdirSync(sourceFolder);
185
+ // Filter for .zip files
186
+ const zipFiles = files.filter(file => path.extname(file).toLowerCase() === '.zip' && file.startsWith("midjourney_session_"));
187
+ if (zipFiles.length === 0) {
188
+ console.log('No zip files found in', sourceFolder);
189
+ return Promise.resolve([]);
190
+ }
191
+ // Process each zip file
192
+ let result = Promise.resolve();
193
+ (zipFiles.map(file => {
194
+ result = result.then(() => {
195
+ const zipPath = path.join(sourceFolder, file);
196
+ // Create a destination folder for the current zip file (based on its name)
197
+ const extractTo = path.join(destinationFolder);
198
+ console.log(`Extracting to: ${extractTo}`);
199
+ // Ensure the destination subfolder exists
200
+ fs.mkdirSync(extractTo, { recursive: true });
201
+ // Extract the zip file
202
+ return extract(zipPath, { dir: extractTo })
203
+ .then(() => {
204
+ console.log(`Extracted ${file} to ${extractTo}`);
205
+ fs.unlinkSync(zipPath);
206
+ })
207
+ .catch(err => {
208
+ console.error(`Error extracting ${file}:`, err);
209
+ });
210
+ });
211
+ }));
212
+ return result;
213
+ }
214
+ async function unzipFile(zipPath, extractTo) {
215
+ await ensureDir(extractTo);
216
+ return extract(zipPath, { dir: extractTo })
217
+ .then(() => {
218
+ console.log(`Extracted ${zipPath} to ${extractTo}`);
219
+ fs.unlinkSync(zipPath);
220
+ })
221
+ .catch(err => {
222
+ console.error(`Error extracting ${zipPath}:`, err);
223
+ });
224
+ }
225
+ /**
226
+ * Remove characters not allowed in filenames and replace whitespace with underscores.
227
+ * @param {string} name
228
+ * @returns {string}
229
+ */
230
+ function sanitizeFileName(name) {
231
+ // remove reserved Windows filename characters: < > : " / \ | ? *
232
+ const illegalRe = /[<>:,.\'"\/\\|?*]/g;
233
+ // collapse one or more whitespace characters into a single underscore
234
+ const whitespaceRe = /\s+/g;
235
+ return name
236
+ .replace(illegalRe, '')
237
+ .replace(whitespaceRe, '_');
238
+ }
239
+ /**
240
+ * Finds the screen position and size (bounds) of an Explorer window
241
+ * that appears to be displaying a specific folder address.
242
+ *
243
+ * @param {string} folderAddress - The full path of the folder to find (e.g., "C:\\Users\\YourUser\\Documents").
244
+ * @returns {Object|null} The window bounds { x, y, width, height } if found, or null if not.
245
+ */
246
+ function findExplorerWindowPosition(folderName) {
247
+ // Extract the folder name from the provided address.
248
+ // Retrieve all open windows.
249
+ const windows = windowManager.getWindows();
250
+ for (const win of windows) {
251
+ // Check if the window belongs to explorer.exe.
252
+ const procPath = win.process?.path;
253
+ const title = win.getTitle().toLowerCase();
254
+ if (title === folderName) {
255
+ console.log(`title: ${title}`);
256
+ console.log(`procPath: ${procPath}`);
257
+ return (win.getBounds());
258
+ }
259
+ }
260
+ // If no matching Explorer window is found, return null.
261
+ return null;
262
+ }
263
+ async function locateExtensionIcon(filePath) {
264
+ // const filePath = path.resolve(__dirname, './chromeExtensionIconImage.png');
265
+ const fileName = path.basename(filePath);
266
+ // Get file size
267
+ const stats = await fs.promises.stat(filePath);
268
+ // Generate a unique multipart boundary
269
+ const boundary = '----NodeMultipartBoundary' + Date.now();
270
+ const lb = '\r\n';
271
+ // Build the multipart header and footer
272
+ const partHeader = `--${boundary}${lb}` +
273
+ `Content-Disposition: form-data; name="template"; filename="${fileName}"${lb}` +
274
+ `Content-Type: application/octet-stream${lb}${lb}`;
275
+ const partFooter = `${lb}--${boundary}--${lb}`;
276
+ // Calculate total content length
277
+ const contentLength = Buffer.byteLength(partHeader) +
278
+ stats.size +
279
+ Buffer.byteLength(partFooter);
280
+ const options = {
281
+ hostname: '127.0.0.1',
282
+ port: 5020,
283
+ path: '/find-image',
284
+ method: 'POST',
285
+ headers: {
286
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
287
+ 'Content-Length': contentLength
288
+ }
289
+ };
290
+ return new Promise((resolve, reject) => {
291
+ const req = http.request(options, (res) => {
292
+ let body = '';
293
+ res.setEncoding('utf8');
294
+ res.on('data', (chunk) => {
295
+ body += chunk;
296
+ });
297
+ res.on('end', () => {
298
+ try {
299
+ const json = JSON.parse(body);
300
+ resolve(json);
301
+ }
302
+ catch (err) {
303
+ reject(new Error('Invalid JSON response'));
304
+ }
305
+ });
306
+ });
307
+ req.on('error', reject);
308
+ // write header
309
+ req.write(partHeader);
310
+ // pipe the file
311
+ const fileStream = fs.createReadStream(filePath);
312
+ fileStream.pipe(req, { end: false });
313
+ fileStream.on('end', () => {
314
+ // write footer and end request
315
+ req.end(partFooter);
316
+ });
317
+ fileStream.on('error', reject);
318
+ });
319
+ }
320
+ /**
321
+ * saveProjectFile
322
+ * ----------------
323
+ * Saves an array of objects to `filePath` as newline-delimited JSON.
324
+ * Each element in `array` is stringified and written on its own line.
325
+ *
326
+ * @param {string} filePath - Path to the file where data will be written.
327
+ * @param {any[]} array - Array of serializable objects.
328
+ */
329
+ function saveProjectFile(filePath, array) {
330
+ // Serialize each object to a JSON string, join by newline
331
+ const data = array.map(obj => JSON.stringify(obj)).join('\n');
332
+ fs.writeFileSync(filePath, data, 'utf8');
333
+ }
334
+ /**
335
+ * readProjectFile
336
+ * ----------------
337
+ * Reads `filePath` synchronously and returns an array of objects.
338
+ * - If the file content (after trimming) starts with '[', parse it as a JSON array.
339
+ * - Otherwise, split on '\n', parse each non-empty line as a JSON object, and return an array of those objects.
340
+ *
341
+ * @param {string} filePath - Path to the JSON file to read.
342
+ * @returns {any[]} - Array of objects read from the file.
343
+ */
344
+ function readProjectFile(filePath) {
345
+ // Read whole file as UTF-8 text
346
+ const content = fs.readFileSync(filePath, 'utf8').trim();
347
+ if (!content) {
348
+ // Empty file: return empty array
349
+ return [];
350
+ }
351
+ if (content.startsWith('[')) {
352
+ // File is a JSON array
353
+ return JSON.parse(content);
354
+ }
355
+ else {
356
+ // Treat as newline-delimited JSON objects
357
+ return content
358
+ .split('\n')
359
+ .filter(line => line.trim() !== '')
360
+ .map(line => JSON.parse(line));
361
+ }
362
+ }
363
+ /**
364
+ * Counts the number of files (not directories) in the Downloads folder.
365
+ * @returns {number} The count of files.
366
+ */
367
+ function countFilesInDownloads() {
368
+ const downloadsPath = getDownloadsFolder();
369
+ try {
370
+ const entries = fs.readdirSync(downloadsPath);
371
+ let fileCount = 0;
372
+ for (const entry of entries) {
373
+ const fullPath = path.join(downloadsPath, entry);
374
+ if (fs.statSync(fullPath).isFile()) {
375
+ fileCount++;
376
+ }
377
+ }
378
+ return fileCount;
379
+ }
380
+ catch (err) {
381
+ console.error(`Failed to read ${downloadsPath}:`, err);
382
+ return 0;
383
+ }
384
+ }
385
+ /**
386
+ * Schedule `callback` to run in `timeoutMs` milliseconds,
387
+ * unless you call the returned cancel function first.
388
+ *
389
+ * @param {() => void} callback
390
+ * @param {number} timeoutMs
391
+ * @returns {() => void} cancel function
392
+ */
393
+ function ifYouDontFinish(callback, timeoutMs) {
394
+ const timerId = setTimeout(callback, timeoutMs);
395
+ return () => clearTimeout(timerId);
396
+ }
397
+ /**
398
+ * Find all files in the Downloads folder that start with
399
+ * "social_aporter_" and end with ".mp4".
400
+ *
401
+ * @returns {Promise<string[]>} Array of matching file names
402
+ */
403
+ async function findSocialPorterVideos() {
404
+ const downloadsDir = getDownloadsFolder();
405
+ try {
406
+ const entries = fs.readdirSync(downloadsDir);
407
+ return entries.filter(name => name.startsWith('social_') &&
408
+ name.toLowerCase().endsWith('.mp4'));
409
+ }
410
+ catch (err) {
411
+ console_log_it(`Failed to read directory ${downloadsDir}:`, 'findSocialPorterVideos');
412
+ return [];
413
+ }
414
+ }
415
+ /**
416
+ * Move an array of filenames (from findSocialPorterVideos)
417
+ * from the Downloads folder into the given target directory.
418
+ *
419
+ * @param {string[]} files - array of filenames (e.g. ['social_aporter_1.mp4', ...])
420
+ * @param {string} targetDir - absolute or relative path to destination directory
421
+ */
422
+ async function moveSocialPorterVideos(files, targetDir) {
423
+ try {
424
+ console_log_it(`moveSocialPorterVideos`, 'social_move');
425
+ const downloadsDir = getDownloadsFolder();
426
+ console_log_it(downloadsDir, 'social_move');
427
+ // ensure the target directory exists
428
+ fs.mkdirSync(targetDir, { recursive: true });
429
+ console_log_it(targetDir, 'social_move');
430
+ console_log_it(`files.length : ${files.length}`, 'social_move_length');
431
+ for (const file of files) {
432
+ const srcPath = path.join(downloadsDir, file);
433
+ const destPath = path.join(targetDir, file);
434
+ try {
435
+ // first try a fast rename
436
+ fs.renameSync(srcPath, destPath);
437
+ console_log_it(`✔ Renamed: ${file}`, 'social_move');
438
+ }
439
+ catch (err) {
440
+ if (err.code === 'EXDEV') {
441
+ // cross-device, so copy then delete
442
+ try {
443
+ fs.copyFileSync(srcPath, destPath);
444
+ fs.unlinkSync(srcPath);
445
+ console_log_it(`✔ Copied+Deleted across devices: ${file}`, 'social_move');
446
+ }
447
+ catch (copyErr) {
448
+ console_log_it(`✖ Failed to copy+delete ${file}: ${copyErr}`, 'social_move');
449
+ }
450
+ }
451
+ else {
452
+ console_log_it(`✖ Failed to rename ${file}: ${err}`, 'social_move');
453
+ }
454
+ }
455
+ }
456
+ }
457
+ catch (e) {
458
+ console_log_it(`✖ Failed to move social_porter videos: ${e}`, 'social_move');
459
+ }
460
+ }
461
+ function getDownloadsFolder() {
462
+ const home = os.homedir();
463
+ if (process.platform === 'win32') {
464
+ // Windows: check registry for the Downloads known folder
465
+ try {
466
+ const raw = execSync('reg query "HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders" /v "{374DE290-123F-4565-9164-39C4925E467B}"').toString();
467
+ // parse the REG_SZ value and expand %USERPROFILE%
468
+ const match = raw.match(/REG_SZ\s+(.*)/);
469
+ if (match) {
470
+ return match[1].replace('%USERPROFILE%', home);
471
+ }
472
+ }
473
+ catch (e) {
474
+ // fall back if registry read fails
475
+ }
476
+ }
477
+ // macOS & Linux: check XDG spec first
478
+ if (process.platform !== 'win32') {
479
+ const cfg = path.join(home, '.config', 'user-dirs.dirs');
480
+ if (fs.existsSync(cfg)) {
481
+ const content = fs.readFileSync(cfg, 'utf8');
482
+ const line = content.match(/^XDG_DOWNLOAD_DIR=(.*)$/m);
483
+ if (line) {
484
+ // strip quotes and ${HOME}
485
+ return line[1]
486
+ .replace(/^"(.*)"$/, '$1')
487
+ .replace('$HOME', home);
488
+ }
489
+ }
490
+ }
491
+ // default fallback
492
+ return path.join(home, 'Downloads');
493
+ }
494
+ /**
495
+ * Reads all entries in `dirPath` and returns those whose names start with `prefix`.
496
+ * @param {string} dirPath - The directory to scan.
497
+ * @param {string} prefix - The filename prefix to match.
498
+ * @returns {Promise<string[]>} - Resolves to an array of matching filenames.
499
+ */
500
+ async function getFilesStartingWith(dirPath, prefix) {
501
+ try {
502
+ // Read all entries in the directory
503
+ const entries = await fs.promises.readdir(dirPath);
504
+ // Filter for names that start with the prefix
505
+ const matches = entries.filter(name => name.startsWith(prefix));
506
+ return matches;
507
+ }
508
+ catch (err) {
509
+ // You might want to handle ENOENT (directory not found) specially
510
+ console.error(`Error reading directory ${dirPath}:`, err);
511
+ throw err;
512
+ }
513
+ }
514
+ /**
515
+ * Reads all entries in `dirPath` and returns those whose names start with `prefix`.
516
+ * @param {string} dirPath - The directory to scan.
517
+ * @returns {Promise<string[]>} - Resolves to an array of matching filenames.
518
+ */
519
+ async function getFiles(dirPath) {
520
+ try {
521
+ // Read all entries in the directory
522
+ const entries = await fs.promises.readdir(dirPath);
523
+ // Filter for names that start with the prefix
524
+ const matches = entries;
525
+ return matches;
526
+ }
527
+ catch (err) {
528
+ return [];
529
+ }
530
+ }
531
+ /**
532
+ * Synchronously deletes the file at `filePath`.
533
+ * @param {string} filePath - The path to the file to delete.
534
+ */
535
+ function deleteFileSync(filePath) {
536
+ try {
537
+ fs.unlinkSync(filePath);
538
+ console.log(`Deleted: ${filePath}`);
539
+ }
540
+ catch (err) {
541
+ if (err.code === 'ENOENT') {
542
+ console.warn(`File not found, could not delete: ${filePath}`);
543
+ }
544
+ else {
545
+ console.error(`Error deleting file ${filePath}:`, err);
546
+ throw err; // rethrow if it's a different error
547
+ }
548
+ }
549
+ }
550
+ /**
551
+ * Download a single file to the given destination path.
552
+ * @param {string} fileUrl - The URL of the file to download.
553
+ * @param {string} destPath - The full path (including filename) to save the file.
554
+ * @returns {Promise<void>}
555
+ */
556
+ function downloadFile(fileUrl, destPath, onProgress) {
557
+ return new Promise((resolve, reject) => {
558
+ const urlObj = new URL(fileUrl);
559
+ const client = urlObj.protocol === 'https:' ? https : http;
560
+ client.get(urlObj, response => {
561
+ if (response.statusCode !== 200) {
562
+ return reject(new Error(`Failed to get '${fileUrl}' (status: ${response.statusCode})`));
563
+ }
564
+ // Make sure the destination folder exists
565
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
566
+ // Read total size from headers
567
+ const total = parseInt(response.headers['content-length'] || '0', 10);
568
+ // Create the progress stream
569
+ const prog = progressStream({
570
+ length: total,
571
+ time: 100 // emit 'progress' events every 100ms
572
+ });
573
+ prog.on('progress', progress => {
574
+ if (onProgress) {
575
+ onProgress(`Downloading ${fileUrl}… ${progress.percentage.toFixed(2)}%`, progress.percentage);
576
+ }
577
+ });
578
+ // Pipe response → prog → fileStream
579
+ const fileStream = fs.createWriteStream(destPath);
580
+ response.pipe(prog).pipe(fileStream);
581
+ fileStream.on('finish', () => {
582
+ fileStream.close(resolve);
583
+ });
584
+ fileStream.on('error', err => {
585
+ fs.unlink(destPath, () => reject(err));
586
+ });
587
+ })
588
+ .on('error', err => {
589
+ reject(err);
590
+ });
591
+ });
592
+ }
593
+ /**
594
+ * Read and parse all .json files in a directory, including newline-delimited JSON.
595
+ *
596
+ * @param {string} dirPath - Path to folder containing your .json files
597
+ * @returns {Promise<Object[]>} - Resolves to an array of all parsed objects
598
+ */
599
+ async function readJsonFiles(dirPath) {
600
+ return new Promise((resolve, reject) => {
601
+ let files;
602
+ try {
603
+ files = fs.readdirSync(dirPath);
604
+ }
605
+ catch (err) {
606
+ return reject(err);
607
+ }
608
+ // Filter to .json files
609
+ const jsonFiles = files.filter(f => path.extname(f).toLowerCase() === '.json');
610
+ if (jsonFiles.length === 0) {
611
+ return resolve([]);
612
+ }
613
+ const results = [];
614
+ jsonFiles.forEach(filename => {
615
+ const fullPath = path.join(dirPath, filename);
616
+ results.push(fullPath);
617
+ });
618
+ resolve(results);
619
+ });
620
+ }
621
+ /**
622
+ * Get the last‑modified time of a file as a comparable number.
623
+ *
624
+ * @param {string} filePath - Path to the file
625
+ * @returns {number} - milliseconds since UNIX epoch
626
+ */
627
+ function getLastModifiedTimestampSync(filePath) {
628
+ // resolve to an absolute path (optional but avoids surprises)
629
+ const fullPath = path.resolve(filePath);
630
+ // statSync will throw if the file doesn't exist or is inaccessible
631
+ const stats = fs.statSync(fullPath);
632
+ // use getTime() for maximum compatibility across Node versions
633
+ return stats.mtime.getTime();
634
+ }
635
+ /**
636
+ * Download multiple files into a target folder.
637
+ * @param {string[]} urls - Array of file URLs to download.
638
+ * @param {string} destFolder - Path to the folder where files should be saved.
639
+ * @returns {Promise<string[]>} - Resolves to an array of the downloaded file paths.
640
+ */
641
+ async function downloadFiles(urls, destFolder) {
642
+ // Ensure destination folder exists
643
+ fs.mkdirSync(destFolder, { recursive: true });
644
+ const downloadPromises = urls.map(async (fileUrl) => {
645
+ // Derive a filename from the URL
646
+ const pathname = new URL(fileUrl).pathname;
647
+ let filename = path.basename(pathname);
648
+ if (!filename) {
649
+ // Fallback if URL ends with a slash
650
+ filename = `file-${Date.now()}`;
651
+ }
652
+ const destPath = path.join(destFolder, filename);
653
+ if (!fs.existsSync(destPath))
654
+ await downloadFile(fileUrl, destPath, null).catch(e => {
655
+ console.error(e);
656
+ });
657
+ return destPath;
658
+ });
659
+ // Wait for all downloads to complete
660
+ return Promise.all(downloadPromises);
661
+ }
662
+ /**
663
+ * Ensures that the directory at `dirPath` exists.
664
+ * Creates it (and any missing parent directories) if it doesn’t.
665
+ * @param {string} dirPath
666
+ * @returns {Promise<void>}
667
+ */
668
+ async function ensureDir(dirPath) {
669
+ return fs.promises.mkdir(dirPath, { recursive: true });
670
+ }
671
+ function unique(array, selector = x => x) {
672
+ const seen = new Set();
673
+ return array.filter(item => {
674
+ const key = selector(item);
675
+ if (seen.has(key)) {
676
+ return false;
677
+ }
678
+ seen.add(key);
679
+ return true;
680
+ });
681
+ }
682
+ /**
683
+ * Strip out punctuation and lowercase for comparison.
684
+ */
685
+ function normalize(str) {
686
+ return str.replace(/[^a-z0-9]/gi, '').toLowerCase();
687
+ }
688
+ /**
689
+ * Align every lyric line to its timed words, treating any number of
690
+ * trailing '\n' on a word as exactly one line break.
691
+ *
692
+ * @param {string[]} lines
693
+ * @param {{ word: string, start_s: number, end_s: number }[]} alignedWords
694
+ * @returns {{
695
+ * line: string,
696
+ * start_s: number|null,
697
+ * end_s: number|null,
698
+ * words: { text: string, start_s: number|null, end_s: number|null }[]
699
+ * }[]}
700
+ */
701
+ function alignLinesToWords(alignedWords) {
702
+ // 1) Bucket the alignedWords into per-line arrays by '\n+' marker:
703
+ const wordsByLine = [];
704
+ let lineIdx = 0;
705
+ wordsByLine[lineIdx] = [];
706
+ alignedWords.forEach(({ word, start_s, end_s }) => {
707
+ // detect one-or-more trailing newlines
708
+ const isLineEnd = /\n+\"?$/.test(word);
709
+ // strip ALL trailing newlines for matching
710
+ const cleanWord = word.replace(/\n+$/, '');
711
+ wordsByLine[lineIdx].push({ text: cleanWord, start_s, end_s });
712
+ // if there was at least one '\n', advance to next bucket
713
+ if (isLineEnd) {
714
+ lineIdx++;
715
+ wordsByLine[lineIdx] = [];
716
+ }
717
+ });
718
+ // 2) Now align each lyric line to its bucket of timed words
719
+ return wordsByLine.map((line, i) => {
720
+ // const tokens = line.split(/\s+/).filter(Boolean);
721
+ const timed = wordsByLine[i] || [];
722
+ const aligned = timed || [];
723
+ // compute line start/end as min/max of word timings
724
+ const starts = timed.map(w => w.start_s).filter(x => x != null);
725
+ const ends = timed.map(w => w.end_s).filter(x => x != null);
726
+ return {
727
+ line: line.map(v => v.text.trim()).join(' '),
728
+ start_s: starts.length ? Math.min(...starts) : null,
729
+ end_s: ends.length ? Math.max(...ends) : null,
730
+ start: starts.length ? Math.min(...starts) : null,
731
+ end: ends.length ? Math.max(...ends) : null,
732
+ words: aligned
733
+ };
734
+ });
735
+ }
736
+ function extractFileNameFromUrl(url) {
737
+ try {
738
+ const urlObj = new URL(url);
739
+ return path.basename(urlObj.pathname);
740
+ }
741
+ catch (error) {
742
+ console.error('Invalid URL:', url, error);
743
+ return null;
744
+ }
745
+ }
746
+ function findFileInDirectoryHasInWithAndEndingWith(dirPath, start, end) {
747
+ try {
748
+ const files = fs.readdirSync(dirPath);
749
+ return files.find(file => file.includes(start) && file.endsWith(end)) || null;
750
+ }
751
+ catch (err) {
752
+ console.error(`Error reading directory ${dirPath}:`, err);
753
+ return null;
754
+ }
755
+ }
756
+ export function getFilesToProcess(name, filesToProcess, root_folder) {
757
+ switch (get(KEYS.procedure)) {
758
+ }
759
+ return filesToProcess;
760
+ }
761
+ export function getMediaFileDuration(filePath) {
762
+ return new Promise((resolve, reject) => {
763
+ ffmpeg.ffprobe(filePath, (err, metadata) => {
764
+ if (err)
765
+ return reject(err);
766
+ if (metadata && metadata.format && typeof metadata.format.duration === 'number') {
767
+ resolve(metadata.format.duration); // duration in seconds
768
+ }
769
+ else if (metadata && metadata.streams) {
770
+ // If duration is not in format, try to find it in any stream (audio or video)
771
+ const stream = metadata.streams.find(s => s.codec_type === 'audio' || s.codec_type === 'video');
772
+ if (stream && typeof stream.duration === 'number') {
773
+ resolve(Math.round(stream.duration));
774
+ }
775
+ else {
776
+ reject(new Error('Could not determine media duration.'));
777
+ }
778
+ }
779
+ else {
780
+ reject(new Error('Could not determine media duration.'));
781
+ }
782
+ });
783
+ });
784
+ }
785
+ // Returns the duration of a video file in seconds using ffmpeg
786
+ function getVideoDuration(filePath) {
787
+ return new Promise((resolve, reject) => {
788
+ ffmpeg.ffprobe(filePath, (err, metadata) => {
789
+ if (err)
790
+ return reject(err);
791
+ if (metadata && metadata.format && typeof metadata.format.duration === 'number') {
792
+ // Find the first video stream for dimensions
793
+ const videoStream = (metadata.streams || []).find(s => s.codec_type === 'video');
794
+ const width = videoStream ? videoStream.width : undefined;
795
+ const height = videoStream ? videoStream.height : undefined;
796
+ resolve({
797
+ duration: metadata.format.duration,
798
+ width,
799
+ height
800
+ });
801
+ }
802
+ else {
803
+ reject(new Error('Could not determine video duration.'));
804
+ }
805
+ });
806
+ });
807
+ }
808
+ export function getVideoDimensions(filePath) {
809
+ return new Promise((resolve, reject) => {
810
+ ffmpeg.ffprobe(filePath, (err, metadata) => {
811
+ if (err)
812
+ return reject(err);
813
+ const videoStream = (metadata.streams || []).find(s => s.codec_type === 'video');
814
+ if (videoStream) {
815
+ resolve({
816
+ width: videoStream.width,
817
+ height: videoStream.height
818
+ });
819
+ }
820
+ else {
821
+ reject(new Error('No video stream found.'));
822
+ }
823
+ });
824
+ });
825
+ }
826
+ /**
827
+ * Sends the “refresh” key combo:
828
+ * - macOS → ⌘+R
829
+ * - Windows/Linux → F5
830
+ */
831
+ function refreshScreen(robot) {
832
+ const platform = process.platform;
833
+ if (platform === 'darwin') {
834
+ // macOS: Command+R
835
+ robot.keyTap('r', 'command');
836
+ }
837
+ else {
838
+ // Windows & most Linux desktops: F5
839
+ robot.keyTap('f5');
840
+ }
841
+ }
842
+ /**
843
+ * Reads files in a directory and sorts them by updated_at (most recent first).
844
+ * @param {string} dirPath - The path to the directory.
845
+ * @returns {Promise<string[]>} - A Promise that resolves to an array of filenames sorted by updated_at.
846
+ */
847
+ async function getSortedFilesByUpdatedAt(dirPath) {
848
+ return new Promise((resolve, reject) => {
849
+ fs.readdir(dirPath, (err, files) => {
850
+ if (err)
851
+ return reject(err);
852
+ const filePaths = files.filter(x => x.endsWith('.png')).map(file => path.join(dirPath, file));
853
+ Promise.all(filePaths.map(filePath => fs.promises.stat(filePath).then(stats => ({
854
+ file: path.basename(filePath),
855
+ updatedAt: stats.mtime.getTime()
856
+ })))).then(fileStats => {
857
+ const sorted = fileStats
858
+ .sort((a, b) => b.updatedAt - a.updatedAt)
859
+ .map(f => f.file);
860
+ resolve(sorted);
861
+ }).catch(reject);
862
+ });
863
+ });
864
+ }
865
+ /**
866
+ * Asynchronously writes text to the clipboard, waits for a short delay,
867
+ * and then simulates a paste operation.
868
+ *
869
+ * @param {string} text - The text to copy to the clipboard and paste.
870
+ */
871
+ async function pasteText(text) {
872
+ const clipboardy = (await import('clipboardy')).default;
873
+ // Asynchronously write the text to the clipboard.
874
+ await clipboardy.writeSync(`${text}`);
875
+ // Wait 100ms to ensure the clipboard is updated.
876
+ await new Promise(resolve => setTimeout(resolve, 100));
877
+ // Determine the correct modifier key based on your platform.
878
+ const modifier = process.platform === 'darwin' ? 'command' : 'control';
879
+ // Simulate the paste operation (Ctrl+V on Windows/Linux, Command+V on macOS).
880
+ robot.keyTap('v', modifier);
881
+ }
882
+ /**
883
+ * Waits for a condition to become true within a specified time window.
884
+ * Polls every 10 seconds by default.
885
+ *
886
+ * @param {() => boolean} condition - The condition to check.
887
+ * @param {number} maxWaitMs - Maximum time to wait (default: 60 seconds).
888
+ * @param {number} intervalMs - Polling interval (default: 10 seconds).
889
+ * @returns {Promise<void>} Resolves when condition is met, rejects if time runs out.
890
+ */
891
+ async function waitFor(condition, maxWaitMs = 120000, intervalMs = 100) {
892
+ const deadline = Date.now() + maxWaitMs;
893
+ const sleep = (ms) => new Promise(res => setTimeout(res, ms));
894
+ console_log_it(condition.toString(), 'wait_for_condition');
895
+ while (Date.now() < deadline) {
896
+ try {
897
+ if (await condition())
898
+ return;
899
+ }
900
+ catch (e) { }
901
+ await sleep(intervalMs);
902
+ }
903
+ console_log_it('condition not met within timeout', 'wait_for');
904
+ throw new Error('Condition not met within timeout: ' + condition.toString());
905
+ }
906
+ async function workflowOp(testCondition, moveMouse, action, checkCondition, attemps = 10, interval = 1000, console_context = 'workflowOp') {
907
+ let attempts = 0;
908
+ while (attempts < attemps) {
909
+ try {
910
+ // Check the test condition
911
+ const currentElement = await testCondition();
912
+ if (currentElement) {
913
+ // Move the mouse to the element
914
+ await moveMouse();
915
+ // Perform the action (e.g., click)
916
+ await action();
917
+ // Check the condition after the action
918
+ const conditionMet = await checkCondition();
919
+ if (conditionMet) {
920
+ console_log_it('Condition met after action', console_context);
921
+ return true; // Exit if the condition is met
922
+ }
923
+ else {
924
+ console_log_it('Condition not met after action, retrying...', console_context);
925
+ }
926
+ }
927
+ else {
928
+ console_log_it('Test condition not met, retrying...', console_context);
929
+ }
930
+ }
931
+ catch (error) {
932
+ console_log_it(`Error in workflowOp: ${error.message}`, console_context);
933
+ }
934
+ // Wait before the next attempt
935
+ await new Promise(resolve => setTimeout(resolve, interval));
936
+ attempts++;
937
+ }
938
+ console_log_it('Max attempts reached, exiting workflowOp', console_context);
939
+ return false; // Return false if the condition was never met
940
+ // Example usage:
941
+ // const MODES = getContext().mode;
942
+ }
943
+ async function isThere(condition, maxWaitMs = 6000, intervalMs = 100) {
944
+ try {
945
+ await waitFor(condition, maxWaitMs, intervalMs);
946
+ }
947
+ catch (e) {
948
+ console_log_it('is there failed ', 'isThere');
949
+ console_log_it(e, 'isThere-error');
950
+ return false;
951
+ }
952
+ return true;
953
+ }
954
+ function pressReturn() {
955
+ // You can also use 'return' on some platforms, but 'enter' is most common
956
+ robot.keyTap('enter');
957
+ }
958
+ /**
959
+ * Pause execution for a specified delay and then execute a callback.
960
+ * @param {Function} callback - The function to execute after the delay.
961
+ * @param {number} delay - The delay in milliseconds.
962
+ * @returns {Promise<void>}
963
+ */
964
+ function pause(callback, delay) {
965
+ return new Promise((resolve) => {
966
+ setTimeout(async () => {
967
+ await callback();
968
+ resolve(undefined);
969
+ }, delay);
970
+ });
971
+ }
972
+ /**
973
+ * Selects all text in the current focused field and deletes it.
974
+ */
975
+ export function clearAllText() {
976
+ // On macOS use 'command', otherwise 'control'
977
+ const modifier = process.platform === 'darwin' ? 'command' : 'control';
978
+ // Give a tiny pause to ensure focus is correct (optional)
979
+ robot.setKeyboardDelay(50);
980
+ // Select all
981
+ robot.keyTap('a', modifier);
982
+ // Delete selected text
983
+ // On some apps you may prefer 'backspace' instead of 'delete'
984
+ robot.keyTap('delete');
985
+ }
986
+ async function mouseClick() {
987
+ return new Promise((resolve) => {
988
+ robot.mouseToggle('down');
989
+ setTimeout(() => {
990
+ robot.mouseToggle('up');
991
+ resolve(undefined);
992
+ }, 100);
993
+ });
994
+ }
995
+ async function waitForQueuedRenders(waitedAmount, SPEED) {
996
+ while (getModePosition()?.[MIDJOURNEY_SELECTOR.queued]) {
997
+ console_log_it(`'waited amount ${waitedAmount}'`, 'waited_amount');
998
+ waitedAmount++;
999
+ await pause(() => { }, SPEED);
1000
+ }
1001
+ return waitedAmount;
1002
+ }
1003
+ /**
1004
+ * Extracts the version number from a string containing '_version_${number}'.
1005
+ * Returns the number as an integer, or null if not found.
1006
+ * @param {string} str
1007
+ * @returns {number|null}
1008
+ */
1009
+ function extractVersionNumber(str) {
1010
+ const match = str.match(/_version_(\d+)/);
1011
+ return match ? parseInt(match[1], 10) : null;
1012
+ }
1013
+ const context = {
1014
+ positions: null,
1015
+ position: {},
1016
+ last_updated: 0,
1017
+ last_used: 0
1018
+ };
1019
+ function getContext() {
1020
+ return context;
1021
+ }
1022
+ function setContext(key, value) {
1023
+ context[key] = value;
1024
+ context.last_updated = Date.now();
1025
+ context.last_used = Date.now();
1026
+ }
1027
+ ;
1028
+ /// ─── UTIL ──────────────────────────────────────────────────────────────────────
1029
+ /**
1030
+ * Replace placeholders in a template string with values from a data object.
1031
+ * Placeholders are in the form {key}.
1032
+ *
1033
+ * @param {string} template — the template string, e.g. "Hello, {name}!"
1034
+ * @param {Object} data — an object mapping keys to values, e.g. { name: "Alice" }
1035
+ * @returns {string} — the interpolated string
1036
+ */
1037
+ function applyTemplate(template, data) {
1038
+ return template.replace(/\{([^}]+)\}/g, (_, key) => {
1039
+ // If the key exists in data, replace it; otherwise leave it blank (or you could throw/error)
1040
+ return data[key] != null ? data[key] : "";
1041
+ });
1042
+ }
1043
+ // grab the first non-internal IPv4 on your machine
1044
+ function getLocalIp() {
1045
+ const ifaces = os.networkInterfaces();
1046
+ for (const list of Object.values(ifaces)) {
1047
+ for (const iface of list) {
1048
+ if (iface.family === 'IPv4' && !iface.internal) {
1049
+ return iface.address;
1050
+ }
1051
+ }
1052
+ }
1053
+ throw new Error('No LAN IPv4 address found');
1054
+ }
1055
+ /**
1056
+ * Build a text progress bar.
1057
+ *
1058
+ * @param {number} index - Current progress (0 … length)
1059
+ * @param {number} length - Total units of work
1060
+ * @param {number} charLength - Total characters in the bar
1061
+ * @returns {string} - Progress bar string
1062
+ */
1063
+ function buildProgressBar(index, length, charLength = 20) {
1064
+ if (length <= 0 || charLength <= 0) {
1065
+ console_log_it(`Invalid progress bar parameters`, 'progress_bar');
1066
+ return '';
1067
+ }
1068
+ // Clamp index between 0 and length
1069
+ const clampedIndex = Math.max(0, Math.min(index, length));
1070
+ // How many characters should be "filled"?
1071
+ const filledCount = Math.floor((clampedIndex / length) * charLength);
1072
+ // Build the bar
1073
+ const filledPart = '█'.repeat(filledCount);
1074
+ const emptyPart = '_'.repeat(charLength - filledCount);
1075
+ return filledPart + emptyPart + `[${index}/${length}]`;
1076
+ }
1077
+ function getMode() {
1078
+ if (!getContext().mode) {
1079
+ getContext().mode = getItem('mode');
1080
+ }
1081
+ if (getContext().mode) {
1082
+ return getContext().mode;
1083
+ }
1084
+ return MODES.MIDJOURNEY;
1085
+ }
1086
+ function getModePosition() {
1087
+ if (getContext()?.position && getContext().position[getMode()]) {
1088
+ return getContext().position[getMode()];
1089
+ }
1090
+ return null;
1091
+ }
1092
+ /**
1093
+ * Recursively searches for files containing a specific string in their name.
1094
+ * @param dir The root directory to search.
1095
+ * @param searchString The string to search for in file names.
1096
+ * @returns An array of relative file paths.
1097
+ */
1098
+ function findFilesWithString(dir, searchString) {
1099
+ const results = [];
1100
+ function search(currentPath) {
1101
+ const entries = fs.readdirSync(currentPath, { withFileTypes: true });
1102
+ for (const entry of entries) {
1103
+ const fullPath = path.join(currentPath, entry.name);
1104
+ if (entry.isDirectory()) {
1105
+ search(fullPath); // Recurse into subdirectory
1106
+ }
1107
+ else if (entry.name.includes(searchString)) {
1108
+ const relativePath = path.relative(dir, fullPath);
1109
+ results.push(relativePath);
1110
+ }
1111
+ }
1112
+ }
1113
+ search(dir);
1114
+ return results;
1115
+ }
1116
+ function getModePositionKey(key) {
1117
+ return getModePosition()?.[key];
1118
+ }
1119
+ function lerp(start, end, t) {
1120
+ return start + (end - start) * t;
1121
+ }
1122
+ function get(key) {
1123
+ if (!getContext()?.[key]) {
1124
+ getContext()[key] = getItem(key);
1125
+ }
1126
+ console_log_it(`${getItem(key)}`.substring(0, 200), key);
1127
+ return getItem(key);
1128
+ }
1129
+ function set(key, value) {
1130
+ setItem(key, value);
1131
+ console_log_it(`${value}`.substring(0, 200), key);
1132
+ setContext(key, getItem(key));
1133
+ }
1134
+ function get_jobs_path() {
1135
+ if (!getContext().jobsPath) {
1136
+ getContext().jobsPath = get(KEYS.jobs_folder);
1137
+ }
1138
+ if (getContext().jobsPath) {
1139
+ return path.join(get(KEYS.jobs_folder), 'source.jsonl');
1140
+ }
1141
+ throw 'jobs_path not set';
1142
+ }
1143
+ const jobs_context = {
1144
+ jobs: []
1145
+ };
1146
+ function getJobsContext() {
1147
+ return jobs_context;
1148
+ }
1149
+ function findJobs(prompt) {
1150
+ console_log_it(`running: read jobs: ${get_jobs_path()}`, 'jobs');
1151
+ jobs_context.jobs = fs.readFileSync(get_jobs_path(), 'utf-8').split('\n').map(v => JSON.parse(v));
1152
+ let jobs = jobs_context.jobs.filter(f => `${(f.full_command)}`.includes((prompt)));
1153
+ return jobs;
1154
+ }
1155
+ function findVideoJobs(prompt) {
1156
+ console_log_it(`running: read jobs: ${get_jobs_path()}`, 'jobs');
1157
+ let jobs = findJobs(prompt);
1158
+ return jobs.filter(job => {
1159
+ return job.event_type === "video_diffusion" || job.event_type === "video_extended";
1160
+ });
1161
+ }
1162
+ ;
1163
+ /**
1164
+ * Create a zip file containing specified folders
1165
+ * @param {string} rootFolder - Root directory path
1166
+ * @param {string[]} folderNames - Array of folder names relative to root folder
1167
+ * @param {string} zipFileName - Name of the zip file to create (without extension)
1168
+ * @param {Object} options - Additional options
1169
+ * @returns {Promise<string>} - Path to the created zip file
1170
+ */
1171
+ async function createZipFromFolders(rootFolder, folderNames, zipFileName, options = {}) {
1172
+ const { compressionLevel = 1, // 0-9, higher = more compression but slower
1173
+ onProgress = null, // Callback for progress updates
1174
+ includeEmptyFolders = true, filter = null // Function to filter files: (filePath, stat) => boolean
1175
+ } = options;
1176
+ return new Promise((resolve, reject) => {
1177
+ try {
1178
+ console_log_it(`Creating zip file: ${zipFileName}.zip`, 'create_zip_folders');
1179
+ console_log_it(`Root folder: ${rootFolder}`, 'create_zip_folders');
1180
+ console_log_it(`Folders to zip: ${folderNames.join(', ')}`, 'create_zip_folders');
1181
+ // Ensure root folder exists
1182
+ if (!fs.existsSync(rootFolder)) {
1183
+ throw new Error(`Root folder does not exist: ${rootFolder}`);
1184
+ }
1185
+ // Create zip file path
1186
+ // make sure zipFileName doesn't double up on extensions
1187
+ const zipFilePath = path.join(rootFolder, zipFileName.endsWith('.zip') ? zipFileName : `${zipFileName}.zip`);
1188
+ // Create write stream for the zip file
1189
+ const output = fs.createWriteStream(zipFilePath);
1190
+ const archive = archiver('zip', {
1191
+ zlib: { level: compressionLevel }
1192
+ });
1193
+ // Track progress
1194
+ let totalBytes = 0;
1195
+ const folderStats = [];
1196
+ // Handle stream events
1197
+ output.on('close', () => {
1198
+ console_log_it(`Zip file created successfully: ${zipFilePath}`, 'create_zip_folders');
1199
+ console_log_it(`Total size: ${(archive.pointer() / 1024 / 1024).toFixed(2)} MB`, 'create_zip_folders');
1200
+ resolve(zipFilePath);
1201
+ });
1202
+ output.on('error', (err) => {
1203
+ console_log_it(`Error writing zip file: ${err.message}`, 'create_zip_folders');
1204
+ reject(err);
1205
+ });
1206
+ archive.on('error', (err) => {
1207
+ console_log_it(`Error creating archive: ${err.message}`, 'create_zip_folders');
1208
+ reject(err);
1209
+ });
1210
+ archive.on('progress', (progress) => {
1211
+ if (onProgress) {
1212
+ const progressPercent = totalBytes > 0 ? Math.round((progress.fs.processedBytes / totalBytes) * 100) : 0;
1213
+ onProgress({
1214
+ type: 'compress',
1215
+ processedBytes: progress.fs.processedBytes,
1216
+ totalBytes: totalBytes,
1217
+ progress: progressPercent,
1218
+ entriesProcessed: progress.entries.processed,
1219
+ entriesTotal: progress.entries.total
1220
+ });
1221
+ }
1222
+ });
1223
+ // Pipe archive data to the file
1224
+ archive.pipe(output);
1225
+ // Validate and add each folder
1226
+ const validFolders = [];
1227
+ for (const folderName of folderNames) {
1228
+ const folderPath = path.join(rootFolder, folderName);
1229
+ if (!fs.existsSync(folderPath)) {
1230
+ console_log_it(`Warning: Folder does not exist, skipping: ${folderName}`, 'create_zip_folders');
1231
+ continue;
1232
+ }
1233
+ const stat = fs.statSync(folderPath);
1234
+ if (!stat.isDirectory()) {
1235
+ console_log_it(`Warning: Path is not a directory, skipping: ${folderName}`, 'create_zip_folders');
1236
+ continue;
1237
+ }
1238
+ validFolders.push({ name: folderName, path: folderPath });
1239
+ // Calculate folder size for progress tracking
1240
+ const folderSize = calculateFolderSize(folderPath);
1241
+ folderStats.push({ name: folderName, size: folderSize });
1242
+ totalBytes += folderSize;
1243
+ }
1244
+ if (validFolders.length === 0) {
1245
+ reject(new Error('No valid folders found to zip'));
1246
+ return;
1247
+ }
1248
+ console_log_it(`Total estimated size: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`, 'create_zip_folders');
1249
+ // Add each folder to the archive using a more reliable method
1250
+ const addFolderRecursive = (folderPath, zipPath) => {
1251
+ try {
1252
+ const files = fs.readdirSync(folderPath, { withFileTypes: true });
1253
+ for (const file of files) {
1254
+ const fullPath = path.join(folderPath, file.name);
1255
+ const zipFilePath = path.join(zipPath, file.name).replace(/\\/g, '/'); // Normalize path separators
1256
+ if (file.isDirectory()) {
1257
+ // Recursively add subdirectory
1258
+ addFolderRecursive(fullPath, zipFilePath);
1259
+ }
1260
+ else if (file.isFile()) {
1261
+ // Apply filter if provided
1262
+ if (filter) {
1263
+ const stat = fs.statSync(fullPath);
1264
+ if (!filter(fullPath, stat)) {
1265
+ continue;
1266
+ }
1267
+ }
1268
+ // Add individual file
1269
+ console_log_it(`Adding file: ${zipFilePath}`, 'create_zip_folders');
1270
+ archive.file(fullPath, { name: zipFilePath });
1271
+ }
1272
+ }
1273
+ }
1274
+ catch (error) {
1275
+ console_log_it(`Error processing folder ${folderPath}: ${error.message}`, 'create_zip_folders');
1276
+ throw error;
1277
+ }
1278
+ };
1279
+ // Add each valid folder
1280
+ for (const { name, path: folderPath } of validFolders) {
1281
+ console_log_it(`Adding folder to zip: ${name}`, 'create_zip_folders');
1282
+ if (onProgress) {
1283
+ onProgress({
1284
+ type: 'folder_start',
1285
+ folder: name,
1286
+ message: `Adding folder: ${name}`
1287
+ });
1288
+ }
1289
+ // Use our custom recursive function instead of archive.directory
1290
+ addFolderRecursive(folderPath, name);
1291
+ }
1292
+ // Finalize the archive
1293
+ console_log_it('Finalizing archive...', 'create_zip_folders');
1294
+ archive.finalize();
1295
+ }
1296
+ catch (error) {
1297
+ console_log_it(`Error in createZipFromFolders: ${error.message}`, 'create_zip_folders');
1298
+ reject(error);
1299
+ }
1300
+ });
1301
+ }
1302
+ /**
1303
+ * Calculate the total size of a folder (recursive)
1304
+ * @param {string} folderPath - Path to the folder
1305
+ * @returns {number} - Total size in bytes
1306
+ */
1307
+ function calculateFolderSize(folderPath) {
1308
+ let totalSize = 0;
1309
+ try {
1310
+ const items = fs.readdirSync(folderPath, { withFileTypes: true });
1311
+ for (const item of items) {
1312
+ const itemPath = path.join(folderPath, item.name);
1313
+ if (item.isDirectory()) {
1314
+ totalSize += calculateFolderSize(itemPath);
1315
+ }
1316
+ else if (item.isFile()) {
1317
+ const stat = fs.statSync(itemPath);
1318
+ totalSize += stat.size;
1319
+ }
1320
+ }
1321
+ }
1322
+ catch (error) {
1323
+ console_log_it(`Error calculating folder size for ${folderPath}: ${error.message}`, 'calculate_folder_size');
1324
+ }
1325
+ return totalSize;
1326
+ }
1327
+ function sanitizeFolderName(folderName) {
1328
+ // Remove file extension first
1329
+ const nameWithoutExtension = folderName.replace(/\.[^/.]+$/, '');
1330
+ // Then replace non-alphanumeric characters with underscores
1331
+ return nameWithoutExtension.replace(/[^a-zA-Z0-9]/g, '_');
1332
+ }
1333
+ /**
1334
+ * Create a zip file with training data from workflow steps
1335
+ * @param {string} rootFolder - Root annotations folder
1336
+ * @param {string} workflowId - Workflow ID
1337
+ * @param {string[]} stepIds - Array of step IDs to include
1338
+ * @param {Object} options - Additional options
1339
+ * @returns {Promise<string>} - Path to the created zip file
1340
+ */
1341
+ async function createTrainingDataZip(rootFolder, workflowId, stepIds, options = {}) {
1342
+ const { includeMetadata = true, onProgress = null, outputFileName = null } = options;
1343
+ try {
1344
+ console.log(`Creating training data zip for workflow: ${workflowId}`, 'create_training_zip');
1345
+ const sanitizedWorkflowId = sanitizeFolderName(workflowId);
1346
+ const workflowFolder = path.join(rootFolder, sanitizedWorkflowId);
1347
+ if (!fs.existsSync(workflowFolder)) {
1348
+ throw new Error(`Workflow folder does not exist: ${workflowFolder}`);
1349
+ }
1350
+ // Build list of step folders
1351
+ const stepFolders = stepIds.map(stepId => {
1352
+ const sanitizedStepId = sanitizeFolderName(stepId);
1353
+ return path.join(sanitizedWorkflowId, sanitizedStepId);
1354
+ });
1355
+ // Validate step folders exist
1356
+ const existingStepFolders = stepFolders.filter(stepFolder => {
1357
+ const fullPath = path.join(rootFolder, stepFolder);
1358
+ return fs.existsSync(fullPath);
1359
+ });
1360
+ if (existingStepFolders.length === 0) {
1361
+ throw new Error('No valid step folders found');
1362
+ }
1363
+ console_log_it(`Including ${existingStepFolders.length} step folders in zip`, 'create_training_zip');
1364
+ // Create zip file name with timestamp
1365
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
1366
+ const zipFileName = outputFileName || `training_data_${sanitizedWorkflowId}_${timestamp}`;
1367
+ // Create metadata file in temp location
1368
+ const tempMetadataPath = path.join(rootFolder, 'temp_metadata.json');
1369
+ fs.writeFileSync(tempMetadataPath, JSON.stringify({
1370
+ workflowId,
1371
+ stepIds,
1372
+ createdAt: new Date().toISOString(),
1373
+ stepCount: existingStepFolders.length
1374
+ }, null, 2));
1375
+ // Add metadata file to the folders list
1376
+ existingStepFolders.push('temp_metadata.json');
1377
+ // Create the zip
1378
+ const zipPath = await createZipFromFolders(rootFolder, existingStepFolders, zipFileName, {
1379
+ onProgress: (progress) => {
1380
+ if (onProgress) {
1381
+ onProgress({
1382
+ ...progress,
1383
+ workflowId,
1384
+ stepCount: existingStepFolders.length
1385
+ });
1386
+ }
1387
+ },
1388
+ filter: (filePath, stat) => {
1389
+ // Exclude temporary files and backups
1390
+ const fileName = path.basename(filePath);
1391
+ return !fileName.startsWith('temp_') &&
1392
+ !fileName.startsWith('.') &&
1393
+ !fileName.includes('_backup');
1394
+ }
1395
+ });
1396
+ // Clean up temp metadata file
1397
+ if (fs.existsSync(tempMetadataPath)) {
1398
+ fs.unlinkSync(tempMetadataPath);
1399
+ }
1400
+ return zipPath;
1401
+ }
1402
+ catch (error) {
1403
+ console_log_it(`Error creating training data zip: ${error.message}`, 'create_training_zip');
1404
+ throw error;
1405
+ }
1406
+ }
1407
+ /**
1408
+ * Add metadata file to an existing zip
1409
+ * @param {string} zipPath - Path to the zip file
1410
+ * @param {Object} metadata - Metadata object to add
1411
+ */
1412
+ async function addMetadataToZip(zipPath, metadata) {
1413
+ return new Promise((resolve, reject) => {
1414
+ try {
1415
+ const tempMetadataPath = path.join(path.dirname(zipPath), 'temp_metadata.json');
1416
+ fs.writeFileSync(tempMetadataPath, JSON.stringify(metadata, null, 2));
1417
+ const output = fs.createWriteStream(zipPath + '.tmp');
1418
+ const archive = archiver('zip');
1419
+ // Extract existing zip and add metadata
1420
+ const extract = require('extract-zip');
1421
+ const tempDir = path.join(path.dirname(zipPath), 'temp_extract');
1422
+ // This is simplified - you might want to use a proper zip library
1423
+ // that supports adding files to existing archives
1424
+ console_log_it('Adding metadata to zip (simplified implementation)', 'add_metadata_zip');
1425
+ // Clean up temp files
1426
+ if (fs.existsSync(tempMetadataPath)) {
1427
+ fs.unlinkSync(tempMetadataPath);
1428
+ }
1429
+ resolve(undefined);
1430
+ }
1431
+ catch (error) {
1432
+ reject(error);
1433
+ }
1434
+ });
1435
+ }
1436
+ /**
1437
+ * Given { classes: string[], parents: Record<string, string|null> },
1438
+ * return the list of class names from root -> deepest child.
1439
+ */
1440
+ function getRootToDeepestPath(data, depth = 10) {
1441
+ const { classes = [], parents = {} } = data ?? {};
1442
+ if (!Array.isArray(classes) || parents == null || typeof parents !== 'object') {
1443
+ throw new TypeError('Expected shape: { classes: string[], parents: Record<string, string|null> }');
1444
+ }
1445
+ // Ensure every class has an entry in parents
1446
+ for (const c of classes) {
1447
+ if (!(c in parents)) {
1448
+ throw new Error(`Missing parent mapping for "${c}"`);
1449
+ }
1450
+ }
1451
+ // The deepest child is the class that is never listed as anyone's parent
1452
+ const allParents = new Set(Object.values(parents).filter(Boolean));
1453
+ const leaves = classes.filter(c => !allParents.has(c));
1454
+ if (leaves.length !== 1) {
1455
+ throw new Error(`Expected exactly one deepest child, found ${leaves.length}: [${leaves.join(', ')}]`);
1456
+ }
1457
+ // Walk backwards from the leaf up to the root (null), then reverse.
1458
+ const path = [];
1459
+ const seen = new Set();
1460
+ let cur = leaves[0];
1461
+ while (cur) {
1462
+ if (seen.has(cur))
1463
+ throw new Error('Cycle detected in parents map');
1464
+ seen.add(cur);
1465
+ path.push(cur);
1466
+ cur = parents[cur] ?? null;
1467
+ }
1468
+ let res = path.reverse();
1469
+ res = res.slice(0, depth); // limit to max depth
1470
+ return res;
1471
+ }
1472
+ export function getFlowFrameSelectors(url, word) {
1473
+ for (let key in flow_frame_selectors.selectors) {
1474
+ if (key.startsWith(url)) {
1475
+ return flow_frame_selectors.selectors[key]?.[word]?.positions[0];
1476
+ }
1477
+ }
1478
+ }
1479
+ export function hasFlowFrameKey(url, word) {
1480
+ console.log(flow_frame_selectors);
1481
+ for (let key in flow_frame_selectors.selectors) {
1482
+ if (key.startsWith(url)) {
1483
+ url = key;
1484
+ break;
1485
+ }
1486
+ }
1487
+ return !!flow_frame_selectors?.selectors?.[url]?.[word]?.positions?.length;
1488
+ }
1489
+ export function setFlowFrameText(url, dict) {
1490
+ flow_frame_text[url] = dict;
1491
+ }
1492
+ export function getFlowFrameText(url) {
1493
+ let result = {};
1494
+ for (let url_regex of Object.keys(flow_frame_text)) {
1495
+ console.log('testing', url_regex, url);
1496
+ if (url_regex.startsWith(url)) {
1497
+ result = flow_frame_text[url_regex];
1498
+ break;
1499
+ }
1500
+ }
1501
+ return result;
1502
+ }
1503
+ export function setFlowFrameSelector(location, selector) {
1504
+ for (let key in flow_frame_text) {
1505
+ if (key.startsWith(location)) {
1506
+ location = key;
1507
+ break;
1508
+ }
1509
+ }
1510
+ flow_frame_selectors.selectors[location] = selector;
1511
+ }
1512
+ const flow_frame_selectors = {
1513
+ selectors: {}
1514
+ };
1515
+ const flow_frame_text = {};
1516
+ export { get_jobs_path, findJobs, findVideoJobs, unique, set, get, setContext, getContext, refreshScreen, getModePositionKey, getModePosition, saveProjectFile, waitForQueuedRenders, readProjectFile, extractFileNameFromUrl, findExplorerWindowPosition, locateExtensionIcon, getDownloadsFolder, findSocialPorterVideos, ifYouDontFinish, getRootToDeepestPath, moveSocialPorterVideos, countFilesInDownloads, getFilesStartingWith, pressReturn, getFiles, getLastModifiedTimestampSync, findFileInDirectoryHasInWithAndEndingWith, deleteFileSync, downloadFiles, downloadFile, ensureDir, isThere, workflowOp, selectRandom, detectOrientation, getDetectOrientation, getMode, readJsonFiles, unzip, unzipFile, sanitizeFileName, alignLinesToWords, getVideoDuration, extractVersionNumber, waitFor, pasteText, getJobsContext, getItem, mouseClick, lerp, pause, getSortedFilesByUpdatedAt, createZipFromFolders, createTrainingDataZip, MIDJOURNEY_SELECTOR, input_bar, buildProgressBar, getLocalIp, applyTemplate, findFilesWithString, sanitizeFolderName };
1517
+ //# sourceMappingURL=utils.js.map