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.
- package/README.md +64 -0
- package/dist/Dockerfile +86 -0
- package/dist/GPU_DEPLOYMENT_README.md +324 -0
- package/dist/OPS_AGENT_README.md +174 -0
- package/dist/README-H100-VM.md +192 -0
- package/dist/README-worker-pools.md +231 -0
- package/dist/README.md +8 -0
- package/dist/WEB-ELEMENT-REQUESTS-README.md +302 -0
- package/dist/append.d.ts +3 -0
- package/dist/append.d.ts.map +1 -0
- package/dist/append.js +42 -0
- package/dist/append.js.map +1 -0
- package/dist/audioRoutes.d.ts +2 -0
- package/dist/audioRoutes.d.ts.map +1 -0
- package/dist/audioRoutes.js +97 -0
- package/dist/audioRoutes.js.map +1 -0
- package/dist/augment-parallel.d.ts +6 -0
- package/dist/augment-parallel.d.ts.map +1 -0
- package/dist/augment-parallel.js +128 -0
- package/dist/augment-parallel.js.map +1 -0
- package/dist/augment-worker.d.ts +2 -0
- package/dist/augment-worker.d.ts.map +1 -0
- package/dist/augment-worker.js +100 -0
- package/dist/augment-worker.js.map +1 -0
- package/dist/browerRoutes.d.ts +2 -0
- package/dist/browerRoutes.d.ts.map +1 -0
- package/dist/browerRoutes.js +323 -0
- package/dist/browerRoutes.js.map +1 -0
- package/dist/browser-utils/utils.d.ts +6 -0
- package/dist/browser-utils/utils.d.ts.map +1 -0
- package/dist/browser-utils/utils.js +133 -0
- package/dist/browser-utils/utils.js.map +1 -0
- package/dist/capture_training_data_endpoints.d.ts +158 -0
- package/dist/capture_training_data_endpoints.d.ts.map +1 -0
- package/dist/capture_training_data_endpoints.js +1812 -0
- package/dist/capture_training_data_endpoints.js.map +1 -0
- package/dist/config.json +28 -0
- package/dist/configEndpoints.d.ts +2 -0
- package/dist/configEndpoints.d.ts.map +1 -0
- package/dist/configEndpoints.js +459 -0
- package/dist/configEndpoints.js.map +1 -0
- package/dist/constants.d.ts +109 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +110 -0
- package/dist/constants.js.map +1 -0
- package/dist/docs/workflow_nodes.md +257 -0
- package/dist/download.d.ts +11 -0
- package/dist/download.d.ts.map +1 -0
- package/dist/download.js +31 -0
- package/dist/download.js.map +1 -0
- package/dist/download.py +61 -0
- package/dist/ecosystem.config.json +63 -0
- package/dist/email-body-extractor.d.ts +20 -0
- package/dist/email-body-extractor.d.ts.map +1 -0
- package/dist/email-body-extractor.js +103 -0
- package/dist/email-body-extractor.js.map +1 -0
- package/dist/express_util.d.ts +2 -0
- package/dist/express_util.d.ts.map +1 -0
- package/dist/express_util.js +30 -0
- package/dist/express_util.js.map +1 -0
- package/dist/extension/background.d.ts +2 -0
- package/dist/extension/background.d.ts.map +1 -0
- package/dist/extension/background.js +268 -0
- package/dist/extension/background.js.map +1 -0
- package/dist/extension/manifest.json +19 -0
- package/dist/extensionUtils.d.ts +2 -0
- package/dist/extensionUtils.d.ts.map +1 -0
- package/dist/extensionUtils.js +48 -0
- package/dist/extensionUtils.js.map +1 -0
- package/dist/filter-gmail-poller/README.md +320 -0
- package/dist/filter-gmail-poller/demo.d.ts +2 -0
- package/dist/filter-gmail-poller/demo.d.ts.map +1 -0
- package/dist/filter-gmail-poller/demo.js +79 -0
- package/dist/filter-gmail-poller/demo.js.map +1 -0
- package/dist/filter-gmail-poller/example-existing-app.d.ts +2 -0
- package/dist/filter-gmail-poller/example-existing-app.d.ts.map +1 -0
- package/dist/filter-gmail-poller/example-existing-app.js +72 -0
- package/dist/filter-gmail-poller/example-existing-app.js.map +1 -0
- package/dist/filter-gmail-poller/filter-gmail-poller.d.ts +160 -0
- package/dist/filter-gmail-poller/filter-gmail-poller.d.ts.map +1 -0
- package/dist/filter-gmail-poller/filter-gmail-poller.js +1048 -0
- package/dist/filter-gmail-poller/filter-gmail-poller.js.map +1 -0
- package/dist/filter-gmail-poller/index.d.ts +3 -0
- package/dist/filter-gmail-poller/index.d.ts.map +1 -0
- package/dist/filter-gmail-poller/index.js +18 -0
- package/dist/filter-gmail-poller/index.js.map +1 -0
- package/dist/filter-gmail-poller/manual-test.d.ts +2 -0
- package/dist/filter-gmail-poller/manual-test.d.ts.map +1 -0
- package/dist/filter-gmail-poller/manual-test.js +70 -0
- package/dist/filter-gmail-poller/manual-test.js.map +1 -0
- package/dist/filter-gmail-poller/poller-prompts.d.ts +12 -0
- package/dist/filter-gmail-poller/poller-prompts.d.ts.map +1 -0
- package/dist/filter-gmail-poller/poller-prompts.js +330 -0
- package/dist/filter-gmail-poller/poller-prompts.js.map +1 -0
- package/dist/filter-gmail-poller/test.js +69 -0
- package/dist/flowframe-auto-firebase-adminsdk.json +13 -0
- package/dist/gmail-poller/README-microsoft-email-poller.md +203 -0
- package/dist/gmail-poller/README.md +129 -0
- package/dist/gmail-poller/example.d.ts +5 -0
- package/dist/gmail-poller/example.d.ts.map +1 -0
- package/dist/gmail-poller/example.js +83 -0
- package/dist/gmail-poller/example.js.map +1 -0
- package/dist/gmail-poller/gmail-poller.d.ts +82 -0
- package/dist/gmail-poller/gmail-poller.d.ts.map +1 -0
- package/dist/gmail-poller/gmail-poller.js +455 -0
- package/dist/gmail-poller/gmail-poller.js.map +1 -0
- package/dist/gmail-poller/manual-test.d.ts +2 -0
- package/dist/gmail-poller/manual-test.d.ts.map +1 -0
- package/dist/gmail-poller/manual-test.js +37 -0
- package/dist/gmail-poller/manual-test.js.map +1 -0
- package/dist/gmail-poller/microsoft-email-example.d.ts +8 -0
- package/dist/gmail-poller/microsoft-email-example.d.ts.map +1 -0
- package/dist/gmail-poller/microsoft-email-example.js +58 -0
- package/dist/gmail-poller/microsoft-email-example.js.map +1 -0
- package/dist/gmail-poller/microsoft-email-poller.d.ts +73 -0
- package/dist/gmail-poller/microsoft-email-poller.d.ts.map +1 -0
- package/dist/gmail-poller/microsoft-email-poller.js +346 -0
- package/dist/gmail-poller/microsoft-email-poller.js.map +1 -0
- package/dist/gmail-poller/setup-auth.d.ts +3 -0
- package/dist/gmail-poller/setup-auth.d.ts.map +1 -0
- package/dist/gmail-poller/setup-auth.js +36 -0
- package/dist/gmail-poller/setup-auth.js.map +1 -0
- package/dist/gmail-poller/test.js +36 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/inference/augment_levels.d.ts +2 -0
- package/dist/inference/augment_levels.d.ts.map +1 -0
- package/dist/inference/augment_levels.js +1 -0
- package/dist/inference/augment_levels.js.map +1 -0
- package/dist/inference/capture-overlay.d.ts +13 -0
- package/dist/inference/capture-overlay.d.ts.map +1 -0
- package/dist/inference/capture-overlay.js +355 -0
- package/dist/inference/capture-overlay.js.map +1 -0
- package/dist/inference/capturescreenshot.d.ts +12 -0
- package/dist/inference/capturescreenshot.d.ts.map +1 -0
- package/dist/inference/capturescreenshot.js +157 -0
- package/dist/inference/capturescreenshot.js.map +1 -0
- package/dist/jsonHandler.d.ts +37 -0
- package/dist/jsonHandler.d.ts.map +1 -0
- package/dist/jsonHandler.js +191 -0
- package/dist/jsonHandler.js.map +1 -0
- package/dist/localStorage.json +11 -0
- package/dist/media_data_endpoints.d.ts +2 -0
- package/dist/media_data_endpoints.d.ts.map +1 -0
- package/dist/media_data_endpoints.js +102 -0
- package/dist/media_data_endpoints.js.map +1 -0
- package/dist/operations/blender-ops.d.ts +4 -0
- package/dist/operations/blender-ops.d.ts.map +1 -0
- package/dist/operations/blender-ops.js +55 -0
- package/dist/operations/blender-ops.js.map +1 -0
- package/dist/operations.d.ts +34 -0
- package/dist/operations.d.ts.map +1 -0
- package/dist/operations.js +1514 -0
- package/dist/operations.js.map +1 -0
- package/dist/pdfRoutes.d.ts +2 -0
- package/dist/pdfRoutes.d.ts.map +1 -0
- package/dist/pdfRoutes.js +56 -0
- package/dist/pdfRoutes.js.map +1 -0
- package/dist/peers.d.ts +9 -0
- package/dist/peers.d.ts.map +1 -0
- package/dist/peers.js +70 -0
- package/dist/peers.js.map +1 -0
- package/dist/playparser.d.ts +2 -0
- package/dist/playparser.d.ts.map +1 -0
- package/dist/playparser.js +281 -0
- package/dist/playparser.js.map +1 -0
- package/dist/process.d.ts +4 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +375 -0
- package/dist/process.js.map +1 -0
- package/dist/promptRoutes.d.ts +7 -0
- package/dist/promptRoutes.d.ts.map +1 -0
- package/dist/promptRoutes.js +68 -0
- package/dist/promptRoutes.js.map +1 -0
- package/dist/queueManager.d.ts +23 -0
- package/dist/queueManager.d.ts.map +1 -0
- package/dist/queueManager.js +96 -0
- package/dist/queueManager.js.map +1 -0
- package/dist/run-flow.d.ts +8 -0
- package/dist/run-flow.d.ts.map +1 -0
- package/dist/run-flow.js +220 -0
- package/dist/run-flow.js.map +1 -0
- package/dist/scraper.d.ts +2 -0
- package/dist/scraper.d.ts.map +1 -0
- package/dist/scraper.js +75 -0
- package/dist/scraper.js.map +1 -0
- package/dist/scraper_endpoints.d.ts +2 -0
- package/dist/scraper_endpoints.d.ts.map +1 -0
- package/dist/scraper_endpoints.js +40 -0
- package/dist/scraper_endpoints.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +528 -0
- package/dist/server.js.map +1 -0
- package/dist/services/ModelContext.d.ts +7 -0
- package/dist/services/ModelContext.d.ts.map +1 -0
- package/dist/services/ModelContext.js +7 -0
- package/dist/services/ModelContext.js.map +1 -0
- package/dist/services/agenticUiPlanner.d.ts +27 -0
- package/dist/services/agenticUiPlanner.d.ts.map +1 -0
- package/dist/services/agenticUiPlanner.js +161 -0
- package/dist/services/agenticUiPlanner.js.map +1 -0
- package/dist/services/apiKeyService.d.ts +3 -0
- package/dist/services/apiKeyService.d.ts.map +1 -0
- package/dist/services/apiKeyService.js +7 -0
- package/dist/services/apiKeyService.js.map +1 -0
- package/dist/services/audioService.d.ts +10 -0
- package/dist/services/audioService.d.ts.map +1 -0
- package/dist/services/audioService.js +140 -0
- package/dist/services/audioService.js.map +1 -0
- package/dist/services/autoPromptOptimizer.d.ts +44 -0
- package/dist/services/autoPromptOptimizer.d.ts.map +1 -0
- package/dist/services/autoPromptOptimizer.js +344 -0
- package/dist/services/autoPromptOptimizer.js.map +1 -0
- package/dist/services/autoPromptOptimizer.manual-test.d.ts +2 -0
- package/dist/services/autoPromptOptimizer.manual-test.d.ts.map +1 -0
- package/dist/services/autoPromptOptimizer.manual-test.js +27 -0
- package/dist/services/autoPromptOptimizer.manual-test.js.map +1 -0
- package/dist/services/chainExecutor.d.ts +26 -0
- package/dist/services/chainExecutor.d.ts.map +1 -0
- package/dist/services/chainExecutor.js +399 -0
- package/dist/services/chainExecutor.js.map +1 -0
- package/dist/services/classifyImageQuestion.d.ts +55 -0
- package/dist/services/classifyImageQuestion.d.ts.map +1 -0
- package/dist/services/classifyImageQuestion.js +428 -0
- package/dist/services/classifyImageQuestion.js.map +1 -0
- package/dist/services/configuration/executor.d.ts +3 -0
- package/dist/services/configuration/executor.d.ts.map +1 -0
- package/dist/services/configuration/executor.js +795 -0
- package/dist/services/configuration/executor.js.map +1 -0
- package/dist/services/error.d.ts +13 -0
- package/dist/services/error.d.ts.map +1 -0
- package/dist/services/error.js +34 -0
- package/dist/services/error.js.map +1 -0
- package/dist/services/executor.d.ts +11 -0
- package/dist/services/executor.d.ts.map +1 -0
- package/dist/services/executor.js +1587 -0
- package/dist/services/executor.js.map +1 -0
- package/dist/services/extractPdf.d.ts +26 -0
- package/dist/services/extractPdf.d.ts.map +1 -0
- package/dist/services/extractPdf.js +256 -0
- package/dist/services/extractPdf.js.map +1 -0
- package/dist/services/generateJsTransformFromPrompt.d.ts +11 -0
- package/dist/services/generateJsTransformFromPrompt.d.ts.map +1 -0
- package/dist/services/generateJsTransformFromPrompt.js +328 -0
- package/dist/services/generateJsTransformFromPrompt.js.map +1 -0
- package/dist/services/localizeFirebaseMedia.d.ts +20 -0
- package/dist/services/localizeFirebaseMedia.d.ts.map +1 -0
- package/dist/services/localizeFirebaseMedia.js +135 -0
- package/dist/services/localizeFirebaseMedia.js.map +1 -0
- package/dist/services/polyfill_canvas.d.ts +2 -0
- package/dist/services/polyfill_canvas.d.ts.map +1 -0
- package/dist/services/polyfill_canvas.js +19 -0
- package/dist/services/polyfill_canvas.js.map +1 -0
- package/dist/services/promptRoutes.d.ts +7 -0
- package/dist/services/promptRoutes.d.ts.map +1 -0
- package/dist/services/promptRoutes.js +70 -0
- package/dist/services/promptRoutes.js.map +1 -0
- package/dist/services/runPrompt.d.ts +29 -0
- package/dist/services/runPrompt.d.ts.map +1 -0
- package/dist/services/runPrompt.js +232 -0
- package/dist/services/runPrompt.js.map +1 -0
- package/dist/services/schemaInference.d.ts +2 -0
- package/dist/services/schemaInference.d.ts.map +1 -0
- package/dist/services/schemaInference.js +17 -0
- package/dist/services/schemaInference.js.map +1 -0
- package/dist/services/self-learning/api.d.ts +2 -0
- package/dist/services/self-learning/api.d.ts.map +1 -0
- package/dist/services/self-learning/api.js +84 -0
- package/dist/services/self-learning/api.js.map +1 -0
- package/dist/services/self-learning/autolearn.d.ts +23 -0
- package/dist/services/self-learning/autolearn.d.ts.map +1 -0
- package/dist/services/self-learning/autolearn.js +308 -0
- package/dist/services/self-learning/autolearn.js.map +1 -0
- package/dist/services/self-learning/discover.d.ts +11 -0
- package/dist/services/self-learning/discover.d.ts.map +1 -0
- package/dist/services/self-learning/discover.js +446 -0
- package/dist/services/self-learning/discover.js.map +1 -0
- package/dist/services/self-learning/image.d.ts +10 -0
- package/dist/services/self-learning/image.d.ts.map +1 -0
- package/dist/services/self-learning/image.js +38 -0
- package/dist/services/self-learning/image.js.map +1 -0
- package/dist/services/self-learning/injest.d.ts +25 -0
- package/dist/services/self-learning/injest.d.ts.map +1 -0
- package/dist/services/self-learning/injest.js +110 -0
- package/dist/services/self-learning/injest.js.map +1 -0
- package/dist/services/self-learning/learn.d.ts +2 -0
- package/dist/services/self-learning/learn.d.ts.map +1 -0
- package/dist/services/self-learning/learn.js +145 -0
- package/dist/services/self-learning/learn.js.map +1 -0
- package/dist/services/self-learning/matcher.d.ts +2 -0
- package/dist/services/self-learning/matcher.d.ts.map +1 -0
- package/dist/services/self-learning/matcher.js +38 -0
- package/dist/services/self-learning/matcher.js.map +1 -0
- package/dist/services/self-learning/openai.d.ts +8 -0
- package/dist/services/self-learning/openai.d.ts.map +1 -0
- package/dist/services/self-learning/openai.js +97 -0
- package/dist/services/self-learning/openai.js.map +1 -0
- package/dist/services/self-learning/phash.d.ts +5 -0
- package/dist/services/self-learning/phash.d.ts.map +1 -0
- package/dist/services/self-learning/phash.js +68 -0
- package/dist/services/self-learning/phash.js.map +1 -0
- package/dist/services/self-learning/recognize.d.ts +17 -0
- package/dist/services/self-learning/recognize.d.ts.map +1 -0
- package/dist/services/self-learning/recognize.js +116 -0
- package/dist/services/self-learning/recognize.js.map +1 -0
- package/dist/services/self-learning/record_transition.d.ts +8 -0
- package/dist/services/self-learning/record_transition.d.ts.map +1 -0
- package/dist/services/self-learning/record_transition.js +20 -0
- package/dist/services/self-learning/record_transition.js.map +1 -0
- package/dist/services/self-learning/registry.d.ts +4 -0
- package/dist/services/self-learning/registry.d.ts.map +1 -0
- package/dist/services/self-learning/registry.js +19 -0
- package/dist/services/self-learning/registry.js.map +1 -0
- package/dist/services/self-learning/schema.d.ts +114 -0
- package/dist/services/self-learning/schema.d.ts.map +1 -0
- package/dist/services/self-learning/schema.js +70 -0
- package/dist/services/self-learning/schema.js.map +1 -0
- package/dist/services/self-learning/schemaStrictify.d.ts +2 -0
- package/dist/services/self-learning/schemaStrictify.d.ts.map +1 -0
- package/dist/services/self-learning/schemaStrictify.js +34 -0
- package/dist/services/self-learning/schemaStrictify.js.map +1 -0
- package/dist/services/self-learning/transition_graph.d.ts +6 -0
- package/dist/services/self-learning/transition_graph.d.ts.map +1 -0
- package/dist/services/self-learning/transition_graph.js +83 -0
- package/dist/services/self-learning/transition_graph.js.map +1 -0
- package/dist/services/self-learning/transition_log.d.ts +3 -0
- package/dist/services/self-learning/transition_log.d.ts.map +1 -0
- package/dist/services/self-learning/transition_log.js +42 -0
- package/dist/services/self-learning/transition_log.js.map +1 -0
- package/dist/services/self-learning/util.d.ts +3 -0
- package/dist/services/self-learning/util.d.ts.map +1 -0
- package/dist/services/self-learning/util.js +11 -0
- package/dist/services/self-learning/util.js.map +1 -0
- package/dist/services/stepByStepAiPlanner.d.ts +39 -0
- package/dist/services/stepByStepAiPlanner.d.ts.map +1 -0
- package/dist/services/stepByStepAiPlanner.js +379 -0
- package/dist/services/stepByStepAiPlanner.js.map +1 -0
- package/dist/services/test-genjs.js +39 -0
- package/dist/services/test-genjs.manual-test.d.ts +2 -0
- package/dist/services/test-genjs.manual-test.d.ts.map +1 -0
- package/dist/services/test-genjs.manual-test.js +40 -0
- package/dist/services/test-genjs.manual-test.js.map +1 -0
- package/dist/services/uiMapPathFinder.d.ts +13 -0
- package/dist/services/uiMapPathFinder.d.ts.map +1 -0
- package/dist/services/uiMapPathFinder.js +79 -0
- package/dist/services/uiMapPathFinder.js.map +1 -0
- package/dist/services/uiMapService.d.ts +26 -0
- package/dist/services/uiMapService.d.ts.map +1 -0
- package/dist/services/uiMapService.js +275 -0
- package/dist/services/uiMapService.js.map +1 -0
- package/dist/services/uiPlanner.d.ts +54 -0
- package/dist/services/uiPlanner.d.ts.map +1 -0
- package/dist/services/uiPlanner.js +558 -0
- package/dist/services/uiPlanner.js.map +1 -0
- package/dist/services/utilityFunctions.d.ts +80 -0
- package/dist/services/utilityFunctions.d.ts.map +1 -0
- package/dist/services/utilityFunctions.js +352 -0
- package/dist/services/utilityFunctions.js.map +1 -0
- package/dist/services/variableGenerator.d.ts +39 -0
- package/dist/services/variableGenerator.d.ts.map +1 -0
- package/dist/services/variableGenerator.js +157 -0
- package/dist/services/variableGenerator.js.map +1 -0
- package/dist/services/workflow/build-workflow.d.ts +49 -0
- package/dist/services/workflow/build-workflow.d.ts.map +1 -0
- package/dist/services/workflow/build-workflow.js +119 -0
- package/dist/services/workflow/build-workflow.js.map +1 -0
- package/dist/standardRoutes.d.ts +2 -0
- package/dist/standardRoutes.d.ts.map +1 -0
- package/dist/standardRoutes.js +1495 -0
- package/dist/standardRoutes.js.map +1 -0
- package/dist/stepWorkflowRoutes.d.ts +2 -0
- package/dist/stepWorkflowRoutes.d.ts.map +1 -0
- package/dist/stepWorkflowRoutes.js +1007 -0
- package/dist/stepWorkflowRoutes.js.map +1 -0
- package/dist/storage.d.ts +19 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.docker.json +61 -0
- package/dist/storage.js +131 -0
- package/dist/storage.js.map +1 -0
- package/dist/storage.json +78 -0
- package/dist/storage_cache/boxes.json +48 -0
- package/dist/storage_cache/suno_state.json +3 -0
- package/dist/suno_download.d.ts +11 -0
- package/dist/suno_download.d.ts.map +1 -0
- package/dist/suno_download.js +33 -0
- package/dist/suno_download.js.map +1 -0
- package/dist/suno_download.py +119 -0
- package/dist/test-web-element-requests.d.ts +6 -0
- package/dist/test-web-element-requests.d.ts.map +1 -0
- package/dist/test-web-element-requests.js +114 -0
- package/dist/test-web-element-requests.js.map +1 -0
- package/dist/test_pdf_render.d.ts +2 -0
- package/dist/test_pdf_render.d.ts.map +1 -0
- package/dist/test_pdf_render.js +50 -0
- package/dist/test_pdf_render.js.map +1 -0
- package/dist/training_data_viewer_endpoints.d.ts +2 -0
- package/dist/training_data_viewer_endpoints.d.ts.map +1 -0
- package/dist/training_data_viewer_endpoints.js +141 -0
- package/dist/training_data_viewer_endpoints.js.map +1 -0
- package/dist/utils.d.ts +353 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +1517 -0
- package/dist/utils.js.map +1 -0
- package/dist/vm-h100.env.template +55 -0
- package/dist/web-element-requests.d.ts +102 -0
- package/dist/web-element-requests.d.ts.map +1 -0
- package/dist/web-element-requests.js +278 -0
- package/dist/web-element-requests.js.map +1 -0
- package/dist/workflowRoutes.d.ts +2 -0
- package/dist/workflowRoutes.d.ts.map +1 -0
- package/dist/workflowRoutes.js +441 -0
- package/dist/workflowRoutes.js.map +1 -0
- package/package.json +109 -0
|
@@ -0,0 +1,1007 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import multer from 'multer';
|
|
4
|
+
import { console_log_it } from './utils.js';
|
|
5
|
+
import { localizeFirebaseStorageUrls } from "./services/localizeFirebaseMedia.js";
|
|
6
|
+
// Configure multer for file uploads
|
|
7
|
+
const createMulterConfig = (uploadDir) => {
|
|
8
|
+
// Ensure upload directory exists
|
|
9
|
+
if (!fs.existsSync(uploadDir)) {
|
|
10
|
+
fs.mkdirSync(uploadDir, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
const storage = multer.diskStorage({
|
|
13
|
+
destination: (req, file, cb) => {
|
|
14
|
+
cb(null, uploadDir);
|
|
15
|
+
},
|
|
16
|
+
filename: (req, file, cb) => {
|
|
17
|
+
// Generate unique filename with timestamp
|
|
18
|
+
const timestamp = Date.now();
|
|
19
|
+
const ext = path.extname(file.originalname);
|
|
20
|
+
const name = path.basename(file.originalname, ext);
|
|
21
|
+
const uniqueName = `${name}-${timestamp}${ext}`;
|
|
22
|
+
cb(null, uniqueName);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const fileFilter = (req, file, cb) => {
|
|
26
|
+
// Accept video, image, and automation files
|
|
27
|
+
const allowedTypes = [
|
|
28
|
+
'video/mp4', 'video/webm', 'video/ogg', 'video/mov', 'video/avi',
|
|
29
|
+
'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml',
|
|
30
|
+
'application/json', 'text/plain'
|
|
31
|
+
];
|
|
32
|
+
const isAutomationFile = file.originalname.endsWith('.automation') || file.originalname.endsWith('.json');
|
|
33
|
+
if (allowedTypes.includes(file.mimetype) || isAutomationFile) {
|
|
34
|
+
cb(null, true);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
cb(new Error('Invalid file type. Only video, image, and automation files are allowed.'), false);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
return multer({
|
|
41
|
+
storage,
|
|
42
|
+
fileFilter,
|
|
43
|
+
limits: {
|
|
44
|
+
fileSize: 500 * 1024 * 1024 // 500MB limit
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
const buildFolderStructure = (dirPath, basePath = dirPath) => {
|
|
49
|
+
const items = [];
|
|
50
|
+
try {
|
|
51
|
+
const entries = fs.readdirSync(dirPath);
|
|
52
|
+
for (const entry of entries) {
|
|
53
|
+
const fullPath = path.join(dirPath, entry);
|
|
54
|
+
const stat = fs.statSync(fullPath);
|
|
55
|
+
const relativePath = path.relative(basePath, fullPath);
|
|
56
|
+
if (stat.isDirectory()) {
|
|
57
|
+
// Recursively build folder structure
|
|
58
|
+
const children = buildFolderStructure(fullPath, basePath);
|
|
59
|
+
items.push({
|
|
60
|
+
id: `folder-${relativePath.replace(/[/\\]/g, '-')}`,
|
|
61
|
+
name: entry,
|
|
62
|
+
type: 'folder',
|
|
63
|
+
children: children,
|
|
64
|
+
icon: '📁',
|
|
65
|
+
path: relativePath,
|
|
66
|
+
lastModified: stat.mtime,
|
|
67
|
+
source: 'local'
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
else if (stat.isFile() && (entry.endsWith('.automation') || entry.endsWith('.json'))) {
|
|
71
|
+
// Add config files
|
|
72
|
+
const isWorkflow = entry.endsWith('.automation') || entry.endsWith('.json');
|
|
73
|
+
items.push({
|
|
74
|
+
id: `file-${relativePath.replace(/[/\\]/g, '-')}`,
|
|
75
|
+
name: entry,
|
|
76
|
+
type: 'file',
|
|
77
|
+
icon: isWorkflow ? '🔄' : '📄',
|
|
78
|
+
path: relativePath,
|
|
79
|
+
lastModified: stat.mtime,
|
|
80
|
+
size: stat.size,
|
|
81
|
+
isConfigFile: !isWorkflow,
|
|
82
|
+
isWorkflowFile: isWorkflow,
|
|
83
|
+
source: 'local'
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.error(`Error reading directory ${dirPath}:`, error);
|
|
90
|
+
}
|
|
91
|
+
// Sort folders first, then files, both alphabetically
|
|
92
|
+
return items.sort((a, b) => {
|
|
93
|
+
if (a.type !== b.type) {
|
|
94
|
+
return a.type === 'folder' ? -1 : 1;
|
|
95
|
+
}
|
|
96
|
+
return a.name.localeCompare(b.name);
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
export function registerStepWorkflowRoutes(app, getWorkflowsDir, getMediaDir, getUIMapDir, getPromptLibDir) {
|
|
100
|
+
const workflowsDir = getWorkflowsDir();
|
|
101
|
+
const mediaDir = getMediaDir();
|
|
102
|
+
const uiMapDir = getUIMapDir();
|
|
103
|
+
const promptLibDir = getPromptLibDir ? getPromptLibDir() : null;
|
|
104
|
+
if (!workflowsDir || !mediaDir) {
|
|
105
|
+
console_log_it('Error: Workflows or Media directory is not defined');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Ensure directories exist
|
|
109
|
+
if (!fs.existsSync(workflowsDir)) {
|
|
110
|
+
fs.mkdirSync(workflowsDir, { recursive: true });
|
|
111
|
+
}
|
|
112
|
+
if (!fs.existsSync(mediaDir)) {
|
|
113
|
+
fs.mkdirSync(mediaDir, { recursive: true });
|
|
114
|
+
}
|
|
115
|
+
if (promptLibDir && !fs.existsSync(promptLibDir)) {
|
|
116
|
+
fs.mkdirSync(promptLibDir, { recursive: true });
|
|
117
|
+
}
|
|
118
|
+
const upload = createMulterConfig(mediaDir);
|
|
119
|
+
console_log_it('Registering Step Workflow routes...');
|
|
120
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
121
|
+
// WORKFLOW MANAGEMENT ENDPOINTS
|
|
122
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
123
|
+
// Get all workflows
|
|
124
|
+
app.get('/api/workflows', (req, res) => {
|
|
125
|
+
try {
|
|
126
|
+
const files = fs.readdirSync(workflowsDir, { recursive: true, withFileTypes: true })
|
|
127
|
+
.filter(file => file.isFile() && (file.name.endsWith('.automation') || file.name.endsWith('.json')))
|
|
128
|
+
.map(file => {
|
|
129
|
+
const filePath = path.join(file.parentPath, file.name);
|
|
130
|
+
const stats = fs.statSync(filePath);
|
|
131
|
+
const relativePath = path.relative(workflowsDir, filePath).replace(/\//g, '//');
|
|
132
|
+
let workflow = {};
|
|
133
|
+
try {
|
|
134
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
135
|
+
workflow = JSON.parse(content);
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
console_log_it(`Error parsing workflow ${file.name}:`, e.message);
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
id: relativePath,
|
|
142
|
+
filename: file.name,
|
|
143
|
+
name: workflow.name || path.basename(file.name, path.extname(file.name)),
|
|
144
|
+
description: workflow.description || '',
|
|
145
|
+
stepCount: workflow.steps ? workflow.steps.length : 0,
|
|
146
|
+
mediaUrl: workflow.url || null,
|
|
147
|
+
createdAt: stats.birthtime,
|
|
148
|
+
modifiedAt: stats.mtime,
|
|
149
|
+
size: stats.size,
|
|
150
|
+
relativePath: relativePath
|
|
151
|
+
};
|
|
152
|
+
})
|
|
153
|
+
.sort((a, b) => b.modifiedAt - a.modifiedAt);
|
|
154
|
+
const folderStructure = buildFolderStructure(workflowsDir, workflowsDir);
|
|
155
|
+
res.json({ success: true, workflows: files, folderStructure });
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
console_log_it('Error listing workflows:', error);
|
|
159
|
+
res.status(500).json({ success: false, error: error.message });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
// Get specific workflow
|
|
163
|
+
app.get('/api/workflows/:id(*)', (req, res) => {
|
|
164
|
+
try {
|
|
165
|
+
const { id } = req.params;
|
|
166
|
+
let filename = id;
|
|
167
|
+
// Try to find the file with or without extension
|
|
168
|
+
const filePath = path.join(workflowsDir, filename);
|
|
169
|
+
const automationPath = path.join(workflowsDir, `${filename}.automation`);
|
|
170
|
+
const jsonPath = path.join(workflowsDir, `${filename}.json`);
|
|
171
|
+
let targetPath = null;
|
|
172
|
+
// Check exact match first (if id has extension)
|
|
173
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
174
|
+
targetPath = filePath;
|
|
175
|
+
}
|
|
176
|
+
// Check .automation
|
|
177
|
+
else if (fs.existsSync(automationPath)) {
|
|
178
|
+
targetPath = automationPath;
|
|
179
|
+
}
|
|
180
|
+
// Check .json
|
|
181
|
+
else if (fs.existsSync(jsonPath)) {
|
|
182
|
+
targetPath = jsonPath;
|
|
183
|
+
}
|
|
184
|
+
if (!targetPath) {
|
|
185
|
+
return res.status(404).json({ success: false, error: 'Workflow not found' });
|
|
186
|
+
}
|
|
187
|
+
const content = fs.readFileSync(targetPath, 'utf8');
|
|
188
|
+
const workflow = JSON.parse(content);
|
|
189
|
+
res.json({ success: true, workflow });
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
console_log_it('Error reading workflow:', error);
|
|
193
|
+
res.status(500).json({ success: false, error: error.message });
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
// Create or update workflow
|
|
197
|
+
app.post('/api/workflows/:id(*)', async (req, res) => {
|
|
198
|
+
try {
|
|
199
|
+
const { id } = req.params;
|
|
200
|
+
const workflow = req.body;
|
|
201
|
+
// Validate workflow structure
|
|
202
|
+
if (!workflow.steps || !Array.isArray(workflow.steps)) {
|
|
203
|
+
return res.status(400).json({
|
|
204
|
+
success: false,
|
|
205
|
+
error: 'Invalid workflow: steps array is required'
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
let filename = id;
|
|
209
|
+
if (!filename.endsWith('.automation') && !filename.endsWith('.json')) {
|
|
210
|
+
filename += '.automation';
|
|
211
|
+
}
|
|
212
|
+
const filePath = path.join(workflowsDir, filename);
|
|
213
|
+
// Ensure directory exists
|
|
214
|
+
const dir = path.dirname(filePath);
|
|
215
|
+
if (!fs.existsSync(dir)) {
|
|
216
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
217
|
+
}
|
|
218
|
+
// Add metadata
|
|
219
|
+
const workflowWithMeta = {
|
|
220
|
+
...workflow,
|
|
221
|
+
id,
|
|
222
|
+
lastModified: new Date().toISOString(),
|
|
223
|
+
version: workflow.version || '1.0.0'
|
|
224
|
+
};
|
|
225
|
+
try {
|
|
226
|
+
// Get the relative file path of filename
|
|
227
|
+
let relativeFilePath = path.relative(workflowsDir, filePath).replace(/\\/g, '/');
|
|
228
|
+
const result = await localizeFirebaseStorageUrls({
|
|
229
|
+
inputJson: workflowWithMeta,
|
|
230
|
+
baseFolder: mediaDir,
|
|
231
|
+
targetFolder: relativeFilePath,
|
|
232
|
+
extension: 'jpg',
|
|
233
|
+
apiPrefix: '/api/media'
|
|
234
|
+
});
|
|
235
|
+
console.log('Localized Firebase Storage URLs:', result);
|
|
236
|
+
fs.writeFileSync(filePath, JSON.stringify(result.updated, null, 2));
|
|
237
|
+
}
|
|
238
|
+
catch (e) {
|
|
239
|
+
console.error('Error localizing Firebase Storage URLs:', e);
|
|
240
|
+
}
|
|
241
|
+
res.json({ success: true, message: 'Workflow saved successfully' });
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
console_log_it('Error saving workflow:', error);
|
|
245
|
+
res.status(500).json({ success: false, error: error.message });
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
// Delete workflow
|
|
249
|
+
app.delete('/api/workflows/:id(*)', (req, res) => {
|
|
250
|
+
try {
|
|
251
|
+
const { id } = req.params;
|
|
252
|
+
let filename = id;
|
|
253
|
+
// Try to find the file with or without extension
|
|
254
|
+
const filePath = path.join(workflowsDir, filename);
|
|
255
|
+
const automationPath = path.join(workflowsDir, `${filename}.automation`);
|
|
256
|
+
const jsonPath = path.join(workflowsDir, `${filename}.json`);
|
|
257
|
+
let deleted = false;
|
|
258
|
+
// Check exact match first (if id has extension)
|
|
259
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
260
|
+
fs.unlinkSync(filePath);
|
|
261
|
+
deleted = true;
|
|
262
|
+
}
|
|
263
|
+
// Check .automation
|
|
264
|
+
else if (fs.existsSync(automationPath)) {
|
|
265
|
+
fs.unlinkSync(automationPath);
|
|
266
|
+
deleted = true;
|
|
267
|
+
}
|
|
268
|
+
// Check .json
|
|
269
|
+
else if (fs.existsSync(jsonPath)) {
|
|
270
|
+
fs.unlinkSync(jsonPath);
|
|
271
|
+
deleted = true;
|
|
272
|
+
}
|
|
273
|
+
if (!deleted) {
|
|
274
|
+
return res.status(404).json({ success: false, error: 'Workflow not found' });
|
|
275
|
+
}
|
|
276
|
+
res.json({ success: true, message: 'Workflow deleted successfully' });
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
console_log_it('Error deleting workflow:', error);
|
|
280
|
+
res.status(500).json({ success: false, error: error.message });
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
// Duplicate workflow
|
|
284
|
+
app.post('/api/workflows/:id(*)/duplicate', (req, res) => {
|
|
285
|
+
try {
|
|
286
|
+
const { id } = req.params;
|
|
287
|
+
const { newName } = req.body;
|
|
288
|
+
let sourceFilename = id;
|
|
289
|
+
if (!sourceFilename.endsWith('.automation') && !sourceFilename.endsWith('.json')) {
|
|
290
|
+
sourceFilename += '.automation';
|
|
291
|
+
}
|
|
292
|
+
// Try to find source
|
|
293
|
+
let sourcePath = path.join(workflowsDir, sourceFilename);
|
|
294
|
+
if (!fs.existsSync(sourcePath)) {
|
|
295
|
+
// Try .json if .automation failed
|
|
296
|
+
if (sourceFilename.endsWith('.automation')) {
|
|
297
|
+
sourcePath = path.join(workflowsDir, sourceFilename.replace('.automation', '.json'));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (!fs.existsSync(sourcePath)) {
|
|
301
|
+
// Try exact match if extension was already there
|
|
302
|
+
sourcePath = path.join(workflowsDir, id);
|
|
303
|
+
}
|
|
304
|
+
if (!fs.existsSync(sourcePath)) {
|
|
305
|
+
return res.status(404).json({ success: false, error: 'Source workflow not found' });
|
|
306
|
+
}
|
|
307
|
+
const content = fs.readFileSync(sourcePath, 'utf8');
|
|
308
|
+
const workflow = JSON.parse(content);
|
|
309
|
+
const newId = newName || `${id}-copy-${Date.now()}`;
|
|
310
|
+
let newFilename = newId;
|
|
311
|
+
if (!newFilename.endsWith('.automation') && !newFilename.endsWith('.json')) {
|
|
312
|
+
newFilename += '.automation';
|
|
313
|
+
}
|
|
314
|
+
const newPath = path.join(workflowsDir, newFilename);
|
|
315
|
+
// Ensure directory exists for new file
|
|
316
|
+
const dir = path.dirname(newPath);
|
|
317
|
+
if (!fs.existsSync(dir)) {
|
|
318
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
319
|
+
}
|
|
320
|
+
const duplicatedWorkflow = {
|
|
321
|
+
...workflow,
|
|
322
|
+
id: newId,
|
|
323
|
+
name: `${workflow.name || id} (Copy)`,
|
|
324
|
+
lastModified: new Date().toISOString()
|
|
325
|
+
};
|
|
326
|
+
fs.writeFileSync(newPath, JSON.stringify(duplicatedWorkflow, null, 2));
|
|
327
|
+
res.json({ success: true, id: newId, message: 'Workflow duplicated successfully' });
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
console_log_it('Error duplicating workflow:', error);
|
|
331
|
+
res.status(500).json({ success: false, error: error.message });
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
335
|
+
// UI MAP ENDPOINTS
|
|
336
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
337
|
+
// Get all UI Maps
|
|
338
|
+
app.get('/api/ui-maps', (req, res) => {
|
|
339
|
+
try {
|
|
340
|
+
const files = fs.readdirSync(uiMapDir, { recursive: true, withFileTypes: true })
|
|
341
|
+
.filter(file => file.isFile() && file.name.endsWith('.ui_map'))
|
|
342
|
+
.map(file => {
|
|
343
|
+
const filePath = path.join(file.parentPath, file.name);
|
|
344
|
+
const stats = fs.statSync(filePath);
|
|
345
|
+
const relativePath = path.relative(uiMapDir, filePath).replace(/\\/g, '/');
|
|
346
|
+
let content = null;
|
|
347
|
+
try {
|
|
348
|
+
content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
349
|
+
}
|
|
350
|
+
catch (e) {
|
|
351
|
+
console_log_it(`Error parsing UI Map ${file.name}:`, e.message);
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
id: relativePath,
|
|
355
|
+
filename: file.name,
|
|
356
|
+
name: content?.name || path.basename(file.name, '.ui_map'),
|
|
357
|
+
lastModified: stats.mtime,
|
|
358
|
+
size: stats.size,
|
|
359
|
+
relativePath: relativePath
|
|
360
|
+
};
|
|
361
|
+
})
|
|
362
|
+
.sort((a, b) => b.lastModified - a.lastModified);
|
|
363
|
+
res.json({ success: true, uiMaps: files });
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
console_log_it('Error listing UI Maps:', error);
|
|
367
|
+
res.status(500).json({ success: false, error: error.message });
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
// Get specific UI Map
|
|
371
|
+
app.get('/api/ui-maps/:id(*)', (req, res) => {
|
|
372
|
+
try {
|
|
373
|
+
const { id } = req.params;
|
|
374
|
+
let filename = id;
|
|
375
|
+
if (!filename.endsWith('.ui_map')) {
|
|
376
|
+
filename += '.ui_map';
|
|
377
|
+
}
|
|
378
|
+
const filePath = path.join(uiMapDir, filename);
|
|
379
|
+
if (!fs.existsSync(filePath)) {
|
|
380
|
+
return res.status(404).json({ success: false, error: 'UI Map not found' });
|
|
381
|
+
}
|
|
382
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
383
|
+
const uiMap = JSON.parse(content);
|
|
384
|
+
res.json({ success: true, uiMap });
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
console_log_it('Error reading UI Map:', error);
|
|
388
|
+
res.status(500).json({ success: false, error: error.message });
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
// Save UI Map
|
|
392
|
+
app.post('/api/ui-maps/:id(*)', (req, res) => {
|
|
393
|
+
try {
|
|
394
|
+
const { id } = req.params;
|
|
395
|
+
const uiMapData = req.body;
|
|
396
|
+
let filename = id;
|
|
397
|
+
if (!filename.endsWith('.ui_map')) {
|
|
398
|
+
filename += '.ui_map';
|
|
399
|
+
}
|
|
400
|
+
const filePath = path.join(uiMapDir, filename);
|
|
401
|
+
// Ensure directory exists
|
|
402
|
+
const dir = path.dirname(filePath);
|
|
403
|
+
if (!fs.existsSync(dir)) {
|
|
404
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
405
|
+
}
|
|
406
|
+
fs.writeFileSync(filePath, JSON.stringify(uiMapData, null, 2));
|
|
407
|
+
res.json({ success: true, message: 'UI Map saved successfully' });
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
console_log_it('Error saving UI Map:', error);
|
|
411
|
+
res.status(500).json({ success: false, error: error.message });
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
// Delete UI Map
|
|
415
|
+
app.delete('/api/ui-maps/:id(*)', (req, res) => {
|
|
416
|
+
try {
|
|
417
|
+
const { id } = req.params;
|
|
418
|
+
let filename = id;
|
|
419
|
+
if (!filename.endsWith('.ui_map')) {
|
|
420
|
+
filename += '.ui_map';
|
|
421
|
+
}
|
|
422
|
+
const filePath = path.join(uiMapDir, filename);
|
|
423
|
+
if (!fs.existsSync(filePath)) {
|
|
424
|
+
return res.status(404).json({ success: false, error: 'UI Map not found' });
|
|
425
|
+
}
|
|
426
|
+
fs.unlinkSync(filePath);
|
|
427
|
+
res.json({ success: true, message: 'UI Map deleted successfully' });
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
console_log_it('Error deleting UI Map:', error);
|
|
431
|
+
res.status(500).json({ success: false, error: error.message });
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
435
|
+
// PROMPT LIB ENDPOINTS
|
|
436
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
437
|
+
// Get all Prompt Libs
|
|
438
|
+
app.get('/api/prompt-libs', (req, res) => {
|
|
439
|
+
if (!promptLibDir)
|
|
440
|
+
return res.json({ success: true, promptLibs: [] });
|
|
441
|
+
try {
|
|
442
|
+
const files = fs.readdirSync(promptLibDir, { recursive: true, withFileTypes: true })
|
|
443
|
+
.filter(file => file.isFile() && file.name.endsWith('.prompt_lib'))
|
|
444
|
+
.map(file => {
|
|
445
|
+
const filePath = path.join(file.parentPath, file.name);
|
|
446
|
+
const stats = fs.statSync(filePath);
|
|
447
|
+
const relativePath = path.relative(promptLibDir, filePath).replace(/\\/g, '/');
|
|
448
|
+
let content = null;
|
|
449
|
+
try {
|
|
450
|
+
content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
451
|
+
}
|
|
452
|
+
catch (e) {
|
|
453
|
+
console_log_it(`Error parsing Prompt Lib ${file.name}:`, e.message);
|
|
454
|
+
}
|
|
455
|
+
return {
|
|
456
|
+
id: relativePath,
|
|
457
|
+
filename: file.name,
|
|
458
|
+
name: content?.name || path.basename(file.name, '.prompt_lib'),
|
|
459
|
+
lastModified: stats.mtime,
|
|
460
|
+
size: stats.size,
|
|
461
|
+
relativePath: relativePath
|
|
462
|
+
};
|
|
463
|
+
})
|
|
464
|
+
.sort((a, b) => b.lastModified - a.lastModified);
|
|
465
|
+
res.json({ success: true, promptLibs: files });
|
|
466
|
+
}
|
|
467
|
+
catch (error) {
|
|
468
|
+
console_log_it('Error listing Prompt Libs:', error);
|
|
469
|
+
res.status(500).json({ success: false, error: error.message });
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
// Get specific Prompt Lib
|
|
473
|
+
app.get('/api/prompt-libs/:id(*)', (req, res) => {
|
|
474
|
+
if (!promptLibDir)
|
|
475
|
+
return res.status(404).json({ success: false, error: 'Prompt Lib directory not configured' });
|
|
476
|
+
try {
|
|
477
|
+
const { id } = req.params;
|
|
478
|
+
let filename = id;
|
|
479
|
+
if (!filename.endsWith('.prompt_lib')) {
|
|
480
|
+
filename += '.prompt_lib';
|
|
481
|
+
}
|
|
482
|
+
const filePath = path.join(promptLibDir, filename);
|
|
483
|
+
if (!fs.existsSync(filePath)) {
|
|
484
|
+
return res.status(404).json({ success: false, error: 'Prompt Lib not found' });
|
|
485
|
+
}
|
|
486
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
487
|
+
const promptLib = JSON.parse(content);
|
|
488
|
+
res.json({ success: true, promptLib });
|
|
489
|
+
}
|
|
490
|
+
catch (error) {
|
|
491
|
+
console_log_it('Error reading Prompt Lib:', error);
|
|
492
|
+
res.status(500).json({ success: false, error: error.message });
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
// Save Prompt Lib
|
|
496
|
+
app.post('/api/prompt-libs/:id(*)', (req, res) => {
|
|
497
|
+
if (!promptLibDir)
|
|
498
|
+
return res.status(500).json({ success: false, error: 'Prompt Lib directory not configured' });
|
|
499
|
+
try {
|
|
500
|
+
const { id } = req.params;
|
|
501
|
+
const promptLibData = req.body;
|
|
502
|
+
let filename = id;
|
|
503
|
+
if (!filename.endsWith('.prompt_lib')) {
|
|
504
|
+
filename += '.prompt_lib';
|
|
505
|
+
}
|
|
506
|
+
const filePath = path.join(promptLibDir, filename);
|
|
507
|
+
// Ensure directory exists
|
|
508
|
+
const dir = path.dirname(filePath);
|
|
509
|
+
if (!fs.existsSync(dir)) {
|
|
510
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
511
|
+
}
|
|
512
|
+
fs.writeFileSync(filePath, JSON.stringify(promptLibData, null, 2));
|
|
513
|
+
res.json({ success: true, message: 'Prompt Lib saved successfully' });
|
|
514
|
+
}
|
|
515
|
+
catch (error) {
|
|
516
|
+
console_log_it('Error saving Prompt Lib:', error);
|
|
517
|
+
res.status(500).json({ success: false, error: error.message });
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
// Delete Prompt Lib
|
|
521
|
+
app.delete('/api/prompt-libs/:id(*)', (req, res) => {
|
|
522
|
+
if (!promptLibDir)
|
|
523
|
+
return res.status(500).json({ success: false, error: 'Prompt Lib directory not configured' });
|
|
524
|
+
try {
|
|
525
|
+
const { id } = req.params;
|
|
526
|
+
let filename = id;
|
|
527
|
+
if (!filename.endsWith('.prompt_lib')) {
|
|
528
|
+
filename += '.prompt_lib';
|
|
529
|
+
}
|
|
530
|
+
const filePath = path.join(promptLibDir, filename);
|
|
531
|
+
if (!fs.existsSync(filePath)) {
|
|
532
|
+
return res.status(404).json({ success: false, error: 'Prompt Lib not found' });
|
|
533
|
+
}
|
|
534
|
+
fs.unlinkSync(filePath);
|
|
535
|
+
res.json({ success: true, message: 'Prompt Lib deleted successfully' });
|
|
536
|
+
}
|
|
537
|
+
catch (error) {
|
|
538
|
+
console_log_it('Error deleting Prompt Lib:', error);
|
|
539
|
+
res.status(500).json({ success: false, error: error.message });
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
543
|
+
// MEDIA MANAGEMENT ENDPOINTS
|
|
544
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
545
|
+
// Upload media file
|
|
546
|
+
app.post('/api/media/upload', (req, res) => {
|
|
547
|
+
console_log_it('=== MEDIA UPLOAD REQUEST START ===');
|
|
548
|
+
console_log_it('Content-Type:', req.headers['content-type']);
|
|
549
|
+
console_log_it('Content-Length:', req.headers['content-length']);
|
|
550
|
+
console_log_it('Media directory:', mediaDir);
|
|
551
|
+
console_log_it('Media directory exists:', fs.existsSync(mediaDir));
|
|
552
|
+
const uploadMiddleware = upload.single('media');
|
|
553
|
+
uploadMiddleware(req, res, (err) => {
|
|
554
|
+
if (err) {
|
|
555
|
+
console_log_it('=== MULTER ERROR ===');
|
|
556
|
+
console_log_it('Error type:', err.constructor.name);
|
|
557
|
+
console_log_it('Error message:', err.message);
|
|
558
|
+
console_log_it('Error code:', err.code);
|
|
559
|
+
if (err instanceof multer.MulterError) {
|
|
560
|
+
if (err.code === 'LIMIT_FILE_SIZE') {
|
|
561
|
+
return res.status(400).json({
|
|
562
|
+
success: false,
|
|
563
|
+
error: 'File too large. Maximum size is 500MB.'
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
else if (err.code === 'LIMIT_UNEXPECTED_FILE') {
|
|
567
|
+
return res.status(400).json({
|
|
568
|
+
success: false,
|
|
569
|
+
error: 'Unexpected field name. Use "media" as the field name.'
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return res.status(500).json({
|
|
574
|
+
success: false,
|
|
575
|
+
error: err.message,
|
|
576
|
+
code: err.code || 'UNKNOWN_ERROR'
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
try {
|
|
580
|
+
console_log_it('=== MULTER SUCCESS ===');
|
|
581
|
+
console_log_it('req.file:', req.file);
|
|
582
|
+
if (!req.file) {
|
|
583
|
+
console_log_it('No file in request');
|
|
584
|
+
return res.status(400).json({ success: false, error: 'No file uploaded' });
|
|
585
|
+
}
|
|
586
|
+
const ext = path.extname(req.file.filename).toLowerCase();
|
|
587
|
+
const isVideo = ['.mp4', '.webm', '.ogg', '.mov', '.avi'].includes(ext);
|
|
588
|
+
const fileInfo = {
|
|
589
|
+
id: `${isVideo ? 'vid' : 'img'}_${req.file.filename}`, // Add ID field
|
|
590
|
+
filename: req.file.filename,
|
|
591
|
+
originalName: req.file.originalname, // Keep this for MediaLibraryContext
|
|
592
|
+
mimetype: req.file.mimetype,
|
|
593
|
+
size: req.file.size,
|
|
594
|
+
url: `/api/media/${req.file.filename}`,
|
|
595
|
+
uploadedAt: new Date().toISOString(),
|
|
596
|
+
thumbnail: null // Add thumbnail field
|
|
597
|
+
};
|
|
598
|
+
console_log_it('=== UPLOAD SUCCESS ===');
|
|
599
|
+
console_log_it('File info:', fileInfo);
|
|
600
|
+
res.json({ success: true, file: fileInfo });
|
|
601
|
+
}
|
|
602
|
+
catch (processError) {
|
|
603
|
+
console_log_it('=== PROCESSING ERROR ===');
|
|
604
|
+
console_log_it('Processing error:', processError);
|
|
605
|
+
res.status(500).json({
|
|
606
|
+
success: false,
|
|
607
|
+
error: processError.message,
|
|
608
|
+
type: 'PROCESSING_ERROR'
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
});
|
|
613
|
+
// Get media file
|
|
614
|
+
app.get('/api/media/:filename(*)', (req, res) => {
|
|
615
|
+
try {
|
|
616
|
+
const { filename } = req.params;
|
|
617
|
+
const filePath = path.join(mediaDir, filename);
|
|
618
|
+
if (!fs.existsSync(filePath)) {
|
|
619
|
+
return res.status(404).json({ success: false, error: 'Media file not found' });
|
|
620
|
+
}
|
|
621
|
+
// Set appropriate headers
|
|
622
|
+
const ext = path.extname(filename).toLowerCase();
|
|
623
|
+
const mimeTypes = {
|
|
624
|
+
'.mp4': 'video/mp4',
|
|
625
|
+
'.webm': 'video/webm',
|
|
626
|
+
'.ogg': 'video/ogg',
|
|
627
|
+
'.mov': 'video/quicktime',
|
|
628
|
+
'.avi': 'video/x-msvideo',
|
|
629
|
+
'.jpg': 'image/jpeg',
|
|
630
|
+
'.jpeg': 'image/jpeg',
|
|
631
|
+
'.png': 'image/png',
|
|
632
|
+
'.gif': 'image/gif',
|
|
633
|
+
'.webp': 'image/webp',
|
|
634
|
+
'.svg': 'image/svg+xml'
|
|
635
|
+
};
|
|
636
|
+
const mimeType = mimeTypes[ext] || 'application/octet-stream';
|
|
637
|
+
res.setHeader('Content-Type', mimeType);
|
|
638
|
+
// Enable range requests for video
|
|
639
|
+
if (mimeType.startsWith('video/')) {
|
|
640
|
+
const stat = fs.statSync(filePath);
|
|
641
|
+
const fileSize = stat.size;
|
|
642
|
+
const range = req.headers.range;
|
|
643
|
+
if (range) {
|
|
644
|
+
const parts = range.replace(/bytes=/, "").split("-");
|
|
645
|
+
const start = parseInt(parts[0], 10);
|
|
646
|
+
const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
|
|
647
|
+
const chunksize = (end - start) + 1;
|
|
648
|
+
res.status(206);
|
|
649
|
+
res.setHeader('Content-Range', `bytes ${start}-${end}/${fileSize}`);
|
|
650
|
+
res.setHeader('Accept-Ranges', 'bytes');
|
|
651
|
+
res.setHeader('Content-Length', chunksize);
|
|
652
|
+
const stream = fs.createReadStream(filePath, { start, end });
|
|
653
|
+
stream.pipe(res);
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
res.setHeader('Content-Length', fileSize);
|
|
657
|
+
fs.createReadStream(filePath).pipe(res);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
res.sendFile(filePath);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
catch (error) {
|
|
665
|
+
console_log_it('Error serving media:', error);
|
|
666
|
+
res.status(500).json({ success: false, error: error.message });
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
// List media files - UPDATE this endpoint to return the expected format
|
|
670
|
+
app.get('/api/media', (req, res) => {
|
|
671
|
+
try {
|
|
672
|
+
const files = fs.readdirSync(mediaDir)
|
|
673
|
+
.filter(file => {
|
|
674
|
+
const ext = path.extname(file).toLowerCase();
|
|
675
|
+
return ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg'].includes(ext);
|
|
676
|
+
})
|
|
677
|
+
.map(file => {
|
|
678
|
+
const filePath = path.join(mediaDir, file);
|
|
679
|
+
const stats = fs.statSync(filePath);
|
|
680
|
+
const ext = path.extname(file).toLowerCase();
|
|
681
|
+
const isVideo = ['.mp4', '.webm', '.ogg', '.mov', '.avi'].includes(ext);
|
|
682
|
+
return {
|
|
683
|
+
id: `${isVideo ? 'vid' : 'img'}_${file}`, // Add ID field that MediaLibraryContext expects
|
|
684
|
+
filename: file,
|
|
685
|
+
url: `/api/media/${file}`,
|
|
686
|
+
type: isVideo ? 'video' : 'image',
|
|
687
|
+
size: stats.size,
|
|
688
|
+
uploadedAt: stats.birthtime.toISOString(), // Convert to ISO string
|
|
689
|
+
thumbnail: null // Add thumbnail field
|
|
690
|
+
};
|
|
691
|
+
})
|
|
692
|
+
.sort((a, b) => new Date(b.uploadedAt) - new Date(a.uploadedAt));
|
|
693
|
+
// Return in the format expected by MediaLibraryContext
|
|
694
|
+
res.json({
|
|
695
|
+
success: true,
|
|
696
|
+
media: files, // Changed from 'files' to 'media'
|
|
697
|
+
count: files.length
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
catch (error) {
|
|
701
|
+
console_log_it('Error listing media:', error);
|
|
702
|
+
res.status(500).json({ success: false, error: error.message });
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
// Delete media file
|
|
706
|
+
app.delete('/api/media/:filename(*)', (req, res) => {
|
|
707
|
+
try {
|
|
708
|
+
const { filename } = req.params;
|
|
709
|
+
const filePath = path.join(mediaDir, filename);
|
|
710
|
+
if (!fs.existsSync(filePath)) {
|
|
711
|
+
return res.status(404).json({ success: false, error: 'Media file not found' });
|
|
712
|
+
}
|
|
713
|
+
fs.unlinkSync(filePath);
|
|
714
|
+
res.json({ success: true, message: 'Media file deleted successfully' });
|
|
715
|
+
}
|
|
716
|
+
catch (error) {
|
|
717
|
+
console_log_it('Error deleting media:', error);
|
|
718
|
+
res.status(500).json({ success: false, error: error.message });
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
// Add a new endpoint to delete media by ID (MediaLibraryContext expects this)
|
|
722
|
+
app.delete('/api/media/:id(*)', (req, res) => {
|
|
723
|
+
try {
|
|
724
|
+
const { id } = req.params;
|
|
725
|
+
// Extract filename from ID (remove img_ or vid_ prefix)
|
|
726
|
+
const filename = id.replace(/^(img_|vid_)/, '');
|
|
727
|
+
const filePath = path.join(mediaDir, filename);
|
|
728
|
+
if (!fs.existsSync(filePath)) {
|
|
729
|
+
return res.status(404).json({ success: false, error: 'Media file not found' });
|
|
730
|
+
}
|
|
731
|
+
fs.unlinkSync(filePath);
|
|
732
|
+
res.json({ success: true, message: 'Media file deleted successfully' });
|
|
733
|
+
}
|
|
734
|
+
catch (error) {
|
|
735
|
+
console_log_it('Error deleting media:', error);
|
|
736
|
+
res.status(500).json({ success: false, error: error.message });
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
// Add endpoint to get media info by ID
|
|
740
|
+
app.get('/api/media/info/:id(*)', (req, res) => {
|
|
741
|
+
try {
|
|
742
|
+
const { id } = req.params;
|
|
743
|
+
// Extract filename from ID
|
|
744
|
+
const filename = id.replace(/^(img_|vid_)/, '');
|
|
745
|
+
const filePath = path.join(mediaDir, filename);
|
|
746
|
+
if (!fs.existsSync(filePath)) {
|
|
747
|
+
return res.status(404).json({ success: false, error: 'Media file not found' });
|
|
748
|
+
}
|
|
749
|
+
const stats = fs.statSync(filePath);
|
|
750
|
+
const ext = path.extname(filename).toLowerCase();
|
|
751
|
+
const isVideo = ['.mp4', '.webm', '.ogg', '.mov', '.avi'].includes(ext);
|
|
752
|
+
const mediaInfo = {
|
|
753
|
+
id,
|
|
754
|
+
filename,
|
|
755
|
+
url: `/api/media/${filename}`,
|
|
756
|
+
type: isVideo ? 'video' : 'image',
|
|
757
|
+
size: stats.size,
|
|
758
|
+
uploadedAt: stats.birthtime.toISOString(),
|
|
759
|
+
thumbnail: null
|
|
760
|
+
};
|
|
761
|
+
res.json({ success: true, media: mediaInfo });
|
|
762
|
+
}
|
|
763
|
+
catch (error) {
|
|
764
|
+
console_log_it('Error getting media info:', error);
|
|
765
|
+
res.status(500).json({ success: false, error: error.message });
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
// Keep the existing /api/media/:filename endpoint for serving files (don't change this)
|
|
769
|
+
// This serves the actual file content and is different from the info endpoint above
|
|
770
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
771
|
+
// WORKFLOW EXECUTION ENDPOINTS
|
|
772
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
773
|
+
// Execute workflow
|
|
774
|
+
app.post('/api/workflows/:id(*)/execute', async (req, res) => {
|
|
775
|
+
try {
|
|
776
|
+
const { id } = req.params;
|
|
777
|
+
const { startStep = 0, endStep, dryRun = false } = req.body;
|
|
778
|
+
let filename = id;
|
|
779
|
+
if (!filename.endsWith('.automation') && !filename.endsWith('.json')) {
|
|
780
|
+
filename += '.automation';
|
|
781
|
+
}
|
|
782
|
+
let filePath = path.join(workflowsDir, filename);
|
|
783
|
+
if (!fs.existsSync(filePath)) {
|
|
784
|
+
// Try .json
|
|
785
|
+
if (filename.endsWith('.automation')) {
|
|
786
|
+
filePath = path.join(workflowsDir, filename.replace('.automation', '.json'));
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (!fs.existsSync(filePath)) {
|
|
790
|
+
// Try exact match
|
|
791
|
+
filePath = path.join(workflowsDir, id);
|
|
792
|
+
}
|
|
793
|
+
if (!fs.existsSync(filePath)) {
|
|
794
|
+
return res.status(404).json({ success: false, error: 'Workflow not found' });
|
|
795
|
+
}
|
|
796
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
797
|
+
const workflow = JSON.parse(content);
|
|
798
|
+
if (!workflow.steps || !Array.isArray(workflow.steps)) {
|
|
799
|
+
return res.status(400).json({ success: false, error: 'Invalid workflow format' });
|
|
800
|
+
}
|
|
801
|
+
const steps = workflow.steps.slice(startStep, endStep);
|
|
802
|
+
const results = [];
|
|
803
|
+
console_log_it(`Executing workflow ${id} - Steps ${startStep} to ${endStep || workflow.steps.length - 1} (Dry run: ${dryRun})`);
|
|
804
|
+
for (let i = 0; i < steps.length; i++) {
|
|
805
|
+
const step = steps[i];
|
|
806
|
+
const stepIndex = startStep + i;
|
|
807
|
+
console_log_it(`Executing step ${stepIndex + 1}: ${step.type}`);
|
|
808
|
+
const stepResult = {
|
|
809
|
+
stepIndex,
|
|
810
|
+
type: step.type,
|
|
811
|
+
description: step.description,
|
|
812
|
+
startTime: Date.now(),
|
|
813
|
+
success: false,
|
|
814
|
+
error: null,
|
|
815
|
+
data: null
|
|
816
|
+
};
|
|
817
|
+
try {
|
|
818
|
+
if (!dryRun) {
|
|
819
|
+
// Execute the actual step here
|
|
820
|
+
// This would integrate with your existing automation system
|
|
821
|
+
stepResult.data = await executeWorkflowStep(step);
|
|
822
|
+
}
|
|
823
|
+
else {
|
|
824
|
+
stepResult.data = { message: 'Dry run - step not executed' };
|
|
825
|
+
}
|
|
826
|
+
stepResult.success = true;
|
|
827
|
+
}
|
|
828
|
+
catch (error) {
|
|
829
|
+
stepResult.error = error.message;
|
|
830
|
+
console_log_it(`Step ${stepIndex + 1} failed:`, error.message);
|
|
831
|
+
if (!req.body.continueOnError) {
|
|
832
|
+
results.push({ ...stepResult, endTime: Date.now() });
|
|
833
|
+
break;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
stepResult.endTime = Date.now();
|
|
837
|
+
results.push(stepResult);
|
|
838
|
+
// Add delay between steps if specified
|
|
839
|
+
if (step.delay && !dryRun) {
|
|
840
|
+
await new Promise(resolve => setTimeout(resolve, step.delay));
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
res.json({
|
|
844
|
+
success: true,
|
|
845
|
+
message: 'Workflow execution completed',
|
|
846
|
+
results,
|
|
847
|
+
totalSteps: steps.length,
|
|
848
|
+
successfulSteps: results.filter(r => r.success).length
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
catch (error) {
|
|
852
|
+
console_log_it('Error executing workflow:', error);
|
|
853
|
+
res.status(500).json({ success: false, error: error.message });
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
// Validate workflow
|
|
857
|
+
app.post('/api/workflows/:id(*)/validate', (req, res) => {
|
|
858
|
+
try {
|
|
859
|
+
const { id } = req.params;
|
|
860
|
+
let filename = id;
|
|
861
|
+
if (!filename.endsWith('.automation') && !filename.endsWith('.json')) {
|
|
862
|
+
filename += '.automation';
|
|
863
|
+
}
|
|
864
|
+
let filePath = path.join(workflowsDir, filename);
|
|
865
|
+
if (!fs.existsSync(filePath)) {
|
|
866
|
+
// Try .json
|
|
867
|
+
if (filename.endsWith('.automation')) {
|
|
868
|
+
filePath = path.join(workflowsDir, filename.replace('.automation', '.json'));
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
if (!fs.existsSync(filePath)) {
|
|
872
|
+
// Try exact match
|
|
873
|
+
filePath = path.join(workflowsDir, id);
|
|
874
|
+
}
|
|
875
|
+
if (!fs.existsSync(filePath)) {
|
|
876
|
+
return res.status(404).json({ success: false, error: 'Workflow not found' });
|
|
877
|
+
}
|
|
878
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
879
|
+
const workflow = JSON.parse(content);
|
|
880
|
+
const validation = validateWorkflow(workflow);
|
|
881
|
+
res.json({ success: true, validation });
|
|
882
|
+
}
|
|
883
|
+
catch (error) {
|
|
884
|
+
console_log_it('Error validating workflow:', error);
|
|
885
|
+
res.status(500).json({ success: false, error: error.message });
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
// Import workflow from file
|
|
889
|
+
app.post('/api/workflows/import', upload.single('workflow'), (req, res) => {
|
|
890
|
+
try {
|
|
891
|
+
if (!req.file) {
|
|
892
|
+
return res.status(400).json({ success: false, error: 'No workflow file uploaded' });
|
|
893
|
+
}
|
|
894
|
+
const content = fs.readFileSync(req.file.path, 'utf8');
|
|
895
|
+
const workflow = JSON.parse(content);
|
|
896
|
+
// Generate unique ID if not provided
|
|
897
|
+
const id = workflow.id || `imported-${Date.now()}`;
|
|
898
|
+
const targetPath = path.join(workflowsDir, `${id}.automation`);
|
|
899
|
+
// Add import metadata
|
|
900
|
+
const importedWorkflow = {
|
|
901
|
+
...workflow,
|
|
902
|
+
id,
|
|
903
|
+
imported: true,
|
|
904
|
+
importedAt: new Date().toISOString(),
|
|
905
|
+
originalFilename: req.file.originalname
|
|
906
|
+
};
|
|
907
|
+
fs.writeFileSync(targetPath, JSON.stringify(importedWorkflow, null, 2));
|
|
908
|
+
// Clean up uploaded file
|
|
909
|
+
fs.unlinkSync(req.file.path);
|
|
910
|
+
res.json({
|
|
911
|
+
success: true,
|
|
912
|
+
message: 'Workflow imported successfully',
|
|
913
|
+
id,
|
|
914
|
+
stepCount: workflow.steps ? workflow.steps.length : 0
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
catch (error) {
|
|
918
|
+
console_log_it('Error importing workflow:', error);
|
|
919
|
+
res.status(500).json({ success: false, error: error.message });
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
console_log_it('Step Workflow routes registered successfully');
|
|
923
|
+
}
|
|
924
|
+
// Helper function to execute individual workflow steps
|
|
925
|
+
async function executeWorkflowStep(step) {
|
|
926
|
+
// This function would integrate with your existing automation system
|
|
927
|
+
// For now, it's a placeholder that simulates step execution
|
|
928
|
+
switch (step.type) {
|
|
929
|
+
case 'pause':
|
|
930
|
+
const duration = step.params?.duration || 1000;
|
|
931
|
+
await new Promise(resolve => setTimeout(resolve, duration));
|
|
932
|
+
return { message: `Paused for ${duration}ms` };
|
|
933
|
+
case 'capture':
|
|
934
|
+
// Integrate with your screenshot functionality
|
|
935
|
+
return { message: 'Screenshot captured', imagePath: '/tmp/screenshot.png' };
|
|
936
|
+
case 'ocr':
|
|
937
|
+
// Integrate with your OCR functionality
|
|
938
|
+
return { message: 'OCR analysis completed', boxes: [] };
|
|
939
|
+
case 'move-mouse':
|
|
940
|
+
// Integrate with robotjs mouse movement
|
|
941
|
+
return { message: 'Mouse moved', position: { x: 0, y: 0 } };
|
|
942
|
+
case 'click':
|
|
943
|
+
// Integrate with robotjs click
|
|
944
|
+
return { message: 'Mouse clicked' };
|
|
945
|
+
case 'type':
|
|
946
|
+
// Integrate with robotjs typing
|
|
947
|
+
const text = step.params?.keys || '';
|
|
948
|
+
return { message: `Typed: ${text}` };
|
|
949
|
+
case 'automation':
|
|
950
|
+
// Execute another automation workflow
|
|
951
|
+
const automationId = step.params?.automationId;
|
|
952
|
+
if (!automationId) {
|
|
953
|
+
throw new Error('Automation step requires automationId parameter');
|
|
954
|
+
}
|
|
955
|
+
// This would need to be implemented to actually execute the referenced automation
|
|
956
|
+
return { message: `Automation "${automationId}" executed` };
|
|
957
|
+
default:
|
|
958
|
+
throw new Error(`Unknown step type: ${step.type}`);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
// Helper function to validate workflow structure
|
|
962
|
+
function validateWorkflow(workflow) {
|
|
963
|
+
const errors = [];
|
|
964
|
+
const warnings = [];
|
|
965
|
+
if (!workflow.steps || !Array.isArray(workflow.steps)) {
|
|
966
|
+
errors.push('Workflow must have a steps array');
|
|
967
|
+
return { valid: false, errors, warnings };
|
|
968
|
+
}
|
|
969
|
+
if (workflow.steps.length === 0) {
|
|
970
|
+
warnings.push('Workflow has no steps');
|
|
971
|
+
}
|
|
972
|
+
workflow.steps.forEach((step, index) => {
|
|
973
|
+
if (!step.type) {
|
|
974
|
+
errors.push(`Step ${index + 1}: Missing step type`);
|
|
975
|
+
}
|
|
976
|
+
// Validate step-specific requirements
|
|
977
|
+
switch (step.type) {
|
|
978
|
+
case 'ocr':
|
|
979
|
+
if (!step.params?.region) {
|
|
980
|
+
warnings.push(`Step ${index + 1}: OCR step without region parameter`);
|
|
981
|
+
}
|
|
982
|
+
break;
|
|
983
|
+
case 'move-mouse':
|
|
984
|
+
if (!step.params?.filter && !step.params?.relative && !step.params?.absolute) {
|
|
985
|
+
errors.push(`Step ${index + 1}: Move mouse step needs filter, relative, or absolute parameters`);
|
|
986
|
+
}
|
|
987
|
+
break;
|
|
988
|
+
case 'type':
|
|
989
|
+
if (!step.params?.keys) {
|
|
990
|
+
errors.push(`Step ${index + 1}: Type step missing keys parameter`);
|
|
991
|
+
}
|
|
992
|
+
break;
|
|
993
|
+
case 'automation':
|
|
994
|
+
if (!step.params?.automationId) {
|
|
995
|
+
errors.push(`Step ${index + 1}: Automation step missing automationId parameter`);
|
|
996
|
+
}
|
|
997
|
+
break;
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
return {
|
|
1001
|
+
valid: errors.length === 0,
|
|
1002
|
+
errors,
|
|
1003
|
+
warnings,
|
|
1004
|
+
stepCount: workflow.steps.length
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
//# sourceMappingURL=stepWorkflowRoutes.js.map
|