claude-chrome-parallel 1.0.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/LICENSE +21 -0
- package/README.md +501 -0
- package/assets/demo.svg +278 -0
- package/dist/cdp/client.d.ts +218 -0
- package/dist/cdp/client.d.ts.map +1 -0
- package/dist/cdp/client.js +797 -0
- package/dist/cdp/client.js.map +1 -0
- package/dist/cdp/connection-pool.d.ts +125 -0
- package/dist/cdp/connection-pool.d.ts.map +1 -0
- package/dist/cdp/connection-pool.js +443 -0
- package/dist/cdp/connection-pool.js.map +1 -0
- package/dist/cdp/screenshot-scheduler.d.ts +54 -0
- package/dist/cdp/screenshot-scheduler.d.ts.map +1 -0
- package/dist/cdp/screenshot-scheduler.js +87 -0
- package/dist/cdp/screenshot-scheduler.js.map +1 -0
- package/dist/chrome/launcher.d.ts +55 -0
- package/dist/chrome/launcher.d.ts.map +1 -0
- package/dist/chrome/launcher.js +383 -0
- package/dist/chrome/launcher.js.map +1 -0
- package/dist/chrome/pool.d.ts +54 -0
- package/dist/chrome/pool.d.ts.map +1 -0
- package/dist/chrome/pool.js +301 -0
- package/dist/chrome/pool.js.map +1 -0
- package/dist/chrome/profile-detector.d.ts +52 -0
- package/dist/chrome/profile-detector.d.ts.map +1 -0
- package/dist/chrome/profile-detector.js +246 -0
- package/dist/chrome/profile-detector.js.map +1 -0
- package/dist/cli/claude-session.d.ts +11 -0
- package/dist/cli/claude-session.js +349 -0
- package/dist/cli/claude-session.js.map +1 -0
- package/dist/cli/index.d.ts +14 -0
- package/dist/cli/index.js +858 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/install.d.ts +16 -0
- package/dist/cli/install.js +185 -0
- package/dist/cli/install.js.map +1 -0
- package/dist/cli/uninstall.d.ts +7 -0
- package/dist/cli/uninstall.js +126 -0
- package/dist/cli/uninstall.js.map +1 -0
- package/dist/cli/update-check.d.ts +9 -0
- package/dist/cli/update-check.js +141 -0
- package/dist/cli/update-check.js.map +1 -0
- package/dist/config/config-recovery.d.ts +69 -0
- package/dist/config/config-recovery.d.ts.map +1 -0
- package/dist/config/config-recovery.js +302 -0
- package/dist/config/config-recovery.js.map +1 -0
- package/dist/config/global.d.ts +49 -0
- package/dist/config/global.d.ts.map +1 -0
- package/dist/config/global.js +24 -0
- package/dist/config/global.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +23 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/session-isolator.d.ts +76 -0
- package/dist/config/session-isolator.d.ts.map +1 -0
- package/dist/config/session-isolator.js +268 -0
- package/dist/config/session-isolator.js.map +1 -0
- package/dist/dashboard/activity-tracker.d.ts +76 -0
- package/dist/dashboard/activity-tracker.d.ts.map +1 -0
- package/dist/dashboard/activity-tracker.js +219 -0
- package/dist/dashboard/activity-tracker.js.map +1 -0
- package/dist/dashboard/ansi.d.ts +117 -0
- package/dist/dashboard/ansi.d.ts.map +1 -0
- package/dist/dashboard/ansi.js +199 -0
- package/dist/dashboard/ansi.js.map +1 -0
- package/dist/dashboard/index.d.ts +110 -0
- package/dist/dashboard/index.d.ts.map +1 -0
- package/dist/dashboard/index.js +412 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/dashboard/keyboard-handler.d.ts +43 -0
- package/dist/dashboard/keyboard-handler.d.ts.map +1 -0
- package/dist/dashboard/keyboard-handler.js +215 -0
- package/dist/dashboard/keyboard-handler.js.map +1 -0
- package/dist/dashboard/operation-controller.d.ts +76 -0
- package/dist/dashboard/operation-controller.d.ts.map +1 -0
- package/dist/dashboard/operation-controller.js +167 -0
- package/dist/dashboard/operation-controller.js.map +1 -0
- package/dist/dashboard/renderer.d.ts +76 -0
- package/dist/dashboard/renderer.d.ts.map +1 -0
- package/dist/dashboard/renderer.js +193 -0
- package/dist/dashboard/renderer.js.map +1 -0
- package/dist/dashboard/types.d.ts +56 -0
- package/dist/dashboard/types.d.ts.map +1 -0
- package/dist/dashboard/types.js +12 -0
- package/dist/dashboard/types.js.map +1 -0
- package/dist/dashboard/views/main-view.d.ts +23 -0
- package/dist/dashboard/views/main-view.d.ts.map +1 -0
- package/dist/dashboard/views/main-view.js +143 -0
- package/dist/dashboard/views/main-view.js.map +1 -0
- package/dist/dashboard/views/sessions-view.d.ts +22 -0
- package/dist/dashboard/views/sessions-view.d.ts.map +1 -0
- package/dist/dashboard/views/sessions-view.js +104 -0
- package/dist/dashboard/views/sessions-view.js.map +1 -0
- package/dist/dashboard/views/tabs-view.d.ts +21 -0
- package/dist/dashboard/views/tabs-view.d.ts.map +1 -0
- package/dist/dashboard/views/tabs-view.js +92 -0
- package/dist/dashboard/views/tabs-view.js.map +1 -0
- package/dist/hints/hint-engine.d.ts +77 -0
- package/dist/hints/hint-engine.d.ts.map +1 -0
- package/dist/hints/hint-engine.js +191 -0
- package/dist/hints/hint-engine.js.map +1 -0
- package/dist/hints/index.d.ts +8 -0
- package/dist/hints/index.d.ts.map +1 -0
- package/dist/hints/index.js +11 -0
- package/dist/hints/index.js.map +1 -0
- package/dist/hints/pattern-learner.d.ts +76 -0
- package/dist/hints/pattern-learner.d.ts.map +1 -0
- package/dist/hints/pattern-learner.js +254 -0
- package/dist/hints/pattern-learner.js.map +1 -0
- package/dist/hints/rules/composite-suggestions.d.ts +6 -0
- package/dist/hints/rules/composite-suggestions.d.ts.map +1 -0
- package/dist/hints/rules/composite-suggestions.js +66 -0
- package/dist/hints/rules/composite-suggestions.js.map +1 -0
- package/dist/hints/rules/error-recovery.d.ts +7 -0
- package/dist/hints/rules/error-recovery.d.ts.map +1 -0
- package/dist/hints/rules/error-recovery.js +55 -0
- package/dist/hints/rules/error-recovery.js.map +1 -0
- package/dist/hints/rules/learned-rules.d.ts +13 -0
- package/dist/hints/rules/learned-rules.d.ts.map +1 -0
- package/dist/hints/rules/learned-rules.js +27 -0
- package/dist/hints/rules/learned-rules.js.map +1 -0
- package/dist/hints/rules/repetition-detection.d.ts +7 -0
- package/dist/hints/rules/repetition-detection.d.ts.map +1 -0
- package/dist/hints/rules/repetition-detection.js +82 -0
- package/dist/hints/rules/repetition-detection.js.map +1 -0
- package/dist/hints/rules/sequence-detection.d.ts +6 -0
- package/dist/hints/rules/sequence-detection.d.ts.map +1 -0
- package/dist/hints/rules/sequence-detection.js +89 -0
- package/dist/hints/rules/sequence-detection.js.map +1 -0
- package/dist/hints/rules/success-hints.d.ts +6 -0
- package/dist/hints/rules/success-hints.d.ts.map +1 -0
- package/dist/hints/rules/success-hints.js +62 -0
- package/dist/hints/rules/success-hints.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +272 -0
- package/dist/index.js.map +1 -0
- package/dist/lightpanda/launcher.d.ts +58 -0
- package/dist/lightpanda/launcher.d.ts.map +1 -0
- package/dist/lightpanda/launcher.js +199 -0
- package/dist/lightpanda/launcher.js.map +1 -0
- package/dist/mcp-server.d.ts +129 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +641 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/memory/domain-memory.d.ts +68 -0
- package/dist/memory/domain-memory.d.ts.map +1 -0
- package/dist/memory/domain-memory.js +227 -0
- package/dist/memory/domain-memory.js.map +1 -0
- package/dist/orchestration/plan-executor.d.ts +19 -0
- package/dist/orchestration/plan-executor.d.ts.map +1 -0
- package/dist/orchestration/plan-executor.js +284 -0
- package/dist/orchestration/plan-executor.js.map +1 -0
- package/dist/orchestration/plan-registry.d.ts +55 -0
- package/dist/orchestration/plan-registry.d.ts.map +1 -0
- package/dist/orchestration/plan-registry.js +255 -0
- package/dist/orchestration/plan-registry.js.map +1 -0
- package/dist/orchestration/state-manager.d.ts +127 -0
- package/dist/orchestration/state-manager.d.ts.map +1 -0
- package/dist/orchestration/state-manager.js +438 -0
- package/dist/orchestration/state-manager.js.map +1 -0
- package/dist/orchestration/workflow-engine.d.ts +162 -0
- package/dist/orchestration/workflow-engine.d.ts.map +1 -0
- package/dist/orchestration/workflow-engine.js +731 -0
- package/dist/orchestration/workflow-engine.js.map +1 -0
- package/dist/resources/usage-guide.d.ts +13 -0
- package/dist/resources/usage-guide.d.ts.map +1 -0
- package/dist/resources/usage-guide.js +101 -0
- package/dist/resources/usage-guide.js.map +1 -0
- package/dist/router/browser-router.d.ts +51 -0
- package/dist/router/browser-router.d.ts.map +1 -0
- package/dist/router/browser-router.js +178 -0
- package/dist/router/browser-router.js.map +1 -0
- package/dist/router/cookie-sync.d.ts +48 -0
- package/dist/router/cookie-sync.d.ts.map +1 -0
- package/dist/router/cookie-sync.js +106 -0
- package/dist/router/cookie-sync.js.map +1 -0
- package/dist/router/index.d.ts +5 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +10 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/tool-routing-registry.d.ts +21 -0
- package/dist/router/tool-routing-registry.d.ts.map +1 -0
- package/dist/router/tool-routing-registry.js +90 -0
- package/dist/router/tool-routing-registry.js.map +1 -0
- package/dist/session-manager.d.ts +251 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +912 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/tools/batch-execute.d.ts +11 -0
- package/dist/tools/batch-execute.d.ts.map +1 -0
- package/dist/tools/batch-execute.js +226 -0
- package/dist/tools/batch-execute.js.map +1 -0
- package/dist/tools/click-element.d.ts +8 -0
- package/dist/tools/click-element.d.ts.map +1 -0
- package/dist/tools/click-element.js +455 -0
- package/dist/tools/click-element.js.map +1 -0
- package/dist/tools/computer.d.ts +6 -0
- package/dist/tools/computer.d.ts.map +1 -0
- package/dist/tools/computer.js +638 -0
- package/dist/tools/computer.js.map +1 -0
- package/dist/tools/console-capture.d.ts +6 -0
- package/dist/tools/console-capture.d.ts.map +1 -0
- package/dist/tools/console-capture.js +320 -0
- package/dist/tools/console-capture.js.map +1 -0
- package/dist/tools/cookies.d.ts +6 -0
- package/dist/tools/cookies.d.ts.map +1 -0
- package/dist/tools/cookies.js +263 -0
- package/dist/tools/cookies.js.map +1 -0
- package/dist/tools/drag-drop.d.ts +6 -0
- package/dist/tools/drag-drop.d.ts.map +1 -0
- package/dist/tools/drag-drop.js +252 -0
- package/dist/tools/drag-drop.js.map +1 -0
- package/dist/tools/emulate-device.d.ts +6 -0
- package/dist/tools/emulate-device.d.ts.map +1 -0
- package/dist/tools/emulate-device.js +221 -0
- package/dist/tools/emulate-device.js.map +1 -0
- package/dist/tools/file-upload.d.ts +6 -0
- package/dist/tools/file-upload.d.ts.map +1 -0
- package/dist/tools/file-upload.js +208 -0
- package/dist/tools/file-upload.js.map +1 -0
- package/dist/tools/fill-form.d.ts +8 -0
- package/dist/tools/fill-form.d.ts.map +1 -0
- package/dist/tools/fill-form.js +342 -0
- package/dist/tools/fill-form.js.map +1 -0
- package/dist/tools/find.d.ts +6 -0
- package/dist/tools/find.d.ts.map +1 -0
- package/dist/tools/find.js +330 -0
- package/dist/tools/find.js.map +1 -0
- package/dist/tools/form-input.d.ts +6 -0
- package/dist/tools/form-input.d.ts.map +1 -0
- package/dist/tools/form-input.js +181 -0
- package/dist/tools/form-input.js.map +1 -0
- package/dist/tools/geolocation.d.ts +6 -0
- package/dist/tools/geolocation.d.ts.map +1 -0
- package/dist/tools/geolocation.js +172 -0
- package/dist/tools/geolocation.js.map +1 -0
- package/dist/tools/http-auth.d.ts +6 -0
- package/dist/tools/http-auth.d.ts.map +1 -0
- package/dist/tools/http-auth.js +136 -0
- package/dist/tools/http-auth.js.map +1 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +104 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/javascript.d.ts +6 -0
- package/dist/tools/javascript.d.ts.map +1 -0
- package/dist/tools/javascript.js +138 -0
- package/dist/tools/javascript.js.map +1 -0
- package/dist/tools/lightweight-scroll.d.ts +11 -0
- package/dist/tools/lightweight-scroll.d.ts.map +1 -0
- package/dist/tools/lightweight-scroll.js +266 -0
- package/dist/tools/lightweight-scroll.js.map +1 -0
- package/dist/tools/memory.d.ts +10 -0
- package/dist/tools/memory.d.ts.map +1 -0
- package/dist/tools/memory.js +141 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/navigate.d.ts +6 -0
- package/dist/tools/navigate.d.ts.map +1 -0
- package/dist/tools/navigate.js +241 -0
- package/dist/tools/navigate.js.map +1 -0
- package/dist/tools/network.d.ts +6 -0
- package/dist/tools/network.d.ts.map +1 -0
- package/dist/tools/network.js +215 -0
- package/dist/tools/network.js.map +1 -0
- package/dist/tools/orchestration.d.ts +6 -0
- package/dist/tools/orchestration.d.ts.map +1 -0
- package/dist/tools/orchestration.js +746 -0
- package/dist/tools/orchestration.js.map +1 -0
- package/dist/tools/page-content.d.ts +6 -0
- package/dist/tools/page-content.d.ts.map +1 -0
- package/dist/tools/page-content.js +120 -0
- package/dist/tools/page-content.js.map +1 -0
- package/dist/tools/page-pdf.d.ts +6 -0
- package/dist/tools/page-pdf.d.ts.map +1 -0
- package/dist/tools/page-pdf.js +245 -0
- package/dist/tools/page-pdf.js.map +1 -0
- package/dist/tools/page-reload.d.ts +6 -0
- package/dist/tools/page-reload.d.ts.map +1 -0
- package/dist/tools/page-reload.js +89 -0
- package/dist/tools/page-reload.js.map +1 -0
- package/dist/tools/performance-metrics.d.ts +6 -0
- package/dist/tools/performance-metrics.d.ts.map +1 -0
- package/dist/tools/performance-metrics.js +158 -0
- package/dist/tools/performance-metrics.js.map +1 -0
- package/dist/tools/read-page.d.ts +6 -0
- package/dist/tools/read-page.d.ts.map +1 -0
- package/dist/tools/read-page.js +287 -0
- package/dist/tools/read-page.js.map +1 -0
- package/dist/tools/request-intercept.d.ts +6 -0
- package/dist/tools/request-intercept.d.ts.map +1 -0
- package/dist/tools/request-intercept.js +439 -0
- package/dist/tools/request-intercept.js.map +1 -0
- package/dist/tools/selector-query.d.ts +6 -0
- package/dist/tools/selector-query.d.ts.map +1 -0
- package/dist/tools/selector-query.js +206 -0
- package/dist/tools/selector-query.js.map +1 -0
- package/dist/tools/shutdown.d.ts +12 -0
- package/dist/tools/shutdown.d.ts.map +1 -0
- package/dist/tools/shutdown.js +120 -0
- package/dist/tools/shutdown.js.map +1 -0
- package/dist/tools/storage.d.ts +6 -0
- package/dist/tools/storage.d.ts.map +1 -0
- package/dist/tools/storage.js +264 -0
- package/dist/tools/storage.js.map +1 -0
- package/dist/tools/tabs-close.d.ts +6 -0
- package/dist/tools/tabs-close.d.ts.map +1 -0
- package/dist/tools/tabs-close.js +124 -0
- package/dist/tools/tabs-close.js.map +1 -0
- package/dist/tools/tabs-context.d.ts +6 -0
- package/dist/tools/tabs-context.d.ts.map +1 -0
- package/dist/tools/tabs-context.js +92 -0
- package/dist/tools/tabs-context.js.map +1 -0
- package/dist/tools/tabs-create.d.ts +6 -0
- package/dist/tools/tabs-create.d.ts.map +1 -0
- package/dist/tools/tabs-create.js +73 -0
- package/dist/tools/tabs-create.js.map +1 -0
- package/dist/tools/user-agent.d.ts +6 -0
- package/dist/tools/user-agent.d.ts.map +1 -0
- package/dist/tools/user-agent.js +128 -0
- package/dist/tools/user-agent.js.map +1 -0
- package/dist/tools/wait-and-click.d.ts +8 -0
- package/dist/tools/wait-and-click.d.ts.map +1 -0
- package/dist/tools/wait-and-click.js +290 -0
- package/dist/tools/wait-and-click.js.map +1 -0
- package/dist/tools/wait-for.d.ts +6 -0
- package/dist/tools/wait-for.d.ts.map +1 -0
- package/dist/tools/wait-for.js +248 -0
- package/dist/tools/wait-for.js.map +1 -0
- package/dist/tools/worker-create.d.ts +7 -0
- package/dist/tools/worker-create.d.ts.map +1 -0
- package/dist/tools/worker-create.js +62 -0
- package/dist/tools/worker-create.js.map +1 -0
- package/dist/tools/worker-delete.d.ts +6 -0
- package/dist/tools/worker-delete.d.ts.map +1 -0
- package/dist/tools/worker-delete.js +80 -0
- package/dist/tools/worker-delete.js.map +1 -0
- package/dist/tools/worker-list.d.ts +6 -0
- package/dist/tools/worker-list.d.ts.map +1 -0
- package/dist/tools/worker-list.js +67 -0
- package/dist/tools/worker-list.js.map +1 -0
- package/dist/tools/xpath-query.d.ts +6 -0
- package/dist/tools/xpath-query.d.ts.map +1 -0
- package/dist/tools/xpath-query.js +230 -0
- package/dist/tools/xpath-query.js.map +1 -0
- package/dist/types/browser-backend.d.ts +30 -0
- package/dist/types/browser-backend.d.ts.map +1 -0
- package/dist/types/browser-backend.js +9 -0
- package/dist/types/browser-backend.js.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +19 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/mcp.d.ts +54 -0
- package/dist/types/mcp.d.ts.map +1 -0
- package/dist/types/mcp.js +14 -0
- package/dist/types/mcp.js.map +1 -0
- package/dist/types/plan-cache.d.ts +121 -0
- package/dist/types/plan-cache.d.ts.map +1 -0
- package/dist/types/plan-cache.js +9 -0
- package/dist/types/plan-cache.js.map +1 -0
- package/dist/types/profile.d.ts +76 -0
- package/dist/types/profile.d.ts.map +1 -0
- package/dist/types/profile.js +35 -0
- package/dist/types/profile.js.map +1 -0
- package/dist/types/session.d.ts +65 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +6 -0
- package/dist/types/session.js.map +1 -0
- package/dist/types/tool-manifest.d.ts +52 -0
- package/dist/types/tool-manifest.d.ts.map +1 -0
- package/dist/types/tool-manifest.js +37 -0
- package/dist/types/tool-manifest.js.map +1 -0
- package/dist/utils/atomic-file.d.ts +50 -0
- package/dist/utils/atomic-file.d.ts.map +1 -0
- package/dist/utils/atomic-file.js +217 -0
- package/dist/utils/atomic-file.js.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +22 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/json-validator.d.ts +40 -0
- package/dist/utils/json-validator.d.ts.map +1 -0
- package/dist/utils/json-validator.js +295 -0
- package/dist/utils/json-validator.js.map +1 -0
- package/dist/utils/ref-id-manager.d.ts +26 -0
- package/dist/utils/ref-id-manager.d.ts.map +1 -0
- package/dist/utils/ref-id-manager.js +81 -0
- package/dist/utils/ref-id-manager.js.map +1 -0
- package/dist/utils/request-queue.d.ts +37 -0
- package/dist/utils/request-queue.d.ts.map +1 -0
- package/dist/utils/request-queue.js +110 -0
- package/dist/utils/request-queue.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Workflow Engine - Executes parallel browser workflows
|
|
4
|
+
* Manages worker lifecycle and result aggregation
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.WorkflowEngine = void 0;
|
|
8
|
+
exports.getWorkflowEngine = getWorkflowEngine;
|
|
9
|
+
const session_manager_1 = require("../session-manager");
|
|
10
|
+
const state_manager_1 = require("./state-manager");
|
|
11
|
+
const connection_pool_1 = require("../cdp/connection-pool");
|
|
12
|
+
const domain_memory_1 = require("../memory/domain-memory");
|
|
13
|
+
class WorkflowEngine {
|
|
14
|
+
sessionManager = (0, session_manager_1.getSessionManager)();
|
|
15
|
+
stateManager = (0, state_manager_1.getOrchestrationStateManager)();
|
|
16
|
+
/**
|
|
17
|
+
* In-memory workflow state. Keyed by orchestrationId.
|
|
18
|
+
* This is the source of truth for completion tracking — avoids file-based race conditions.
|
|
19
|
+
*/
|
|
20
|
+
workflowStates = new Map();
|
|
21
|
+
/**
|
|
22
|
+
* Per-worker runtime state for timeout and circuit breaker tracking.
|
|
23
|
+
* Keyed by workerName.
|
|
24
|
+
*/
|
|
25
|
+
workerRuntimeStates = new Map();
|
|
26
|
+
/**
|
|
27
|
+
* Timeout handles for worker absolute timeouts. Keyed by workerName.
|
|
28
|
+
*/
|
|
29
|
+
workerTimeoutHandles = new Map();
|
|
30
|
+
/**
|
|
31
|
+
* Global workflow timeout handle. Keyed by orchestrationId.
|
|
32
|
+
*/
|
|
33
|
+
globalTimeoutHandles = new Map();
|
|
34
|
+
/**
|
|
35
|
+
* Promise-based mutex for serializing completeWorker operations.
|
|
36
|
+
* Prevents lost-update races when multiple workers complete simultaneously.
|
|
37
|
+
*/
|
|
38
|
+
completionLock = Promise.resolve();
|
|
39
|
+
/**
|
|
40
|
+
* Acquire the completion lock. Returns a release function.
|
|
41
|
+
* All completeWorker calls are serialized through this lock.
|
|
42
|
+
*/
|
|
43
|
+
async acquireLock() {
|
|
44
|
+
let release;
|
|
45
|
+
const next = new Promise(resolve => {
|
|
46
|
+
release = resolve;
|
|
47
|
+
});
|
|
48
|
+
const prev = this.completionLock;
|
|
49
|
+
this.completionLock = next;
|
|
50
|
+
await prev;
|
|
51
|
+
return release;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Initialize a new workflow
|
|
55
|
+
* Creates workers, tabs, and scratchpads
|
|
56
|
+
*/
|
|
57
|
+
async initWorkflow(sessionId, workflow) {
|
|
58
|
+
const orchestrationId = `orch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
59
|
+
// Phase 1: Create all workers (no pages yet)
|
|
60
|
+
const createdWorkers = await Promise.all(workflow.steps.map(async (step) => {
|
|
61
|
+
const worker = await this.sessionManager.createWorker(sessionId, {
|
|
62
|
+
id: step.workerId,
|
|
63
|
+
name: step.workerName,
|
|
64
|
+
shareCookies: step.shareCookies,
|
|
65
|
+
targetUrl: step.url,
|
|
66
|
+
});
|
|
67
|
+
return { worker, step };
|
|
68
|
+
}));
|
|
69
|
+
// Phase 2: Batch-acquire pages from the pool to prevent about:blank proliferation.
|
|
70
|
+
// acquireBatch suppresses per-page replenishment, avoiding 60-80 ghost tabs.
|
|
71
|
+
const pool = (0, connection_pool_1.getCDPConnectionPool)();
|
|
72
|
+
const batchPages = await pool.acquireBatch(createdWorkers.length);
|
|
73
|
+
// Phase 3: Assign pages to workers and navigate to target URLs
|
|
74
|
+
const workers = await Promise.all(createdWorkers.map(async ({ worker, step }, i) => {
|
|
75
|
+
const page = batchPages[i];
|
|
76
|
+
// Navigate the pre-acquired page to the target URL
|
|
77
|
+
if (step.url) {
|
|
78
|
+
await page.goto(step.url, { waitUntil: 'domcontentloaded' }).catch(() => {
|
|
79
|
+
// Navigation may fail for some URLs; worker will retry
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// Register the page as a target in the session manager
|
|
83
|
+
const targetId = page.target()._targetId;
|
|
84
|
+
this.sessionManager.registerExistingTarget(sessionId, worker.id, targetId);
|
|
85
|
+
return {
|
|
86
|
+
workerId: worker.id,
|
|
87
|
+
workerName: step.workerName,
|
|
88
|
+
tabId: targetId,
|
|
89
|
+
task: step.task,
|
|
90
|
+
};
|
|
91
|
+
}));
|
|
92
|
+
// Initialize file-based orchestration state (for scratchpads / debugging)
|
|
93
|
+
await this.stateManager.initOrchestration(orchestrationId, workflow.name, workers);
|
|
94
|
+
// Initialize in-memory state — this is the authoritative source for completion tracking
|
|
95
|
+
const workerStatuses = new Map();
|
|
96
|
+
for (const w of workers) {
|
|
97
|
+
workerStatuses.set(w.workerName, { status: 'INIT', resultSummary: '' });
|
|
98
|
+
}
|
|
99
|
+
const workerTimeoutMs = workflow.timeout || 60_000;
|
|
100
|
+
const maxStaleIterations = workflow.maxStaleIterations ?? 5;
|
|
101
|
+
const globalTimeoutMs = workflow.globalTimeoutMs ?? 300_000;
|
|
102
|
+
const memState = {
|
|
103
|
+
orchestrationId,
|
|
104
|
+
task: workflow.name,
|
|
105
|
+
createdAt: Date.now(),
|
|
106
|
+
totalWorkers: workers.length,
|
|
107
|
+
completedWorkers: 0,
|
|
108
|
+
failedWorkers: 0,
|
|
109
|
+
workerStatuses,
|
|
110
|
+
overallStatus: 'INIT',
|
|
111
|
+
allDone: false,
|
|
112
|
+
workerTimeoutMs,
|
|
113
|
+
maxStaleIterations,
|
|
114
|
+
globalTimeoutMs,
|
|
115
|
+
};
|
|
116
|
+
this.workflowStates.set(orchestrationId, memState);
|
|
117
|
+
// Initialize per-worker runtime state and set up timeouts
|
|
118
|
+
for (const w of workers) {
|
|
119
|
+
const runtimeState = {
|
|
120
|
+
workerName: w.workerName,
|
|
121
|
+
startTime: Date.now(),
|
|
122
|
+
lastDataHash: '',
|
|
123
|
+
staleCount: 0,
|
|
124
|
+
lastUpdateTime: Date.now(),
|
|
125
|
+
timedOut: false,
|
|
126
|
+
};
|
|
127
|
+
this.workerRuntimeStates.set(w.workerName, runtimeState);
|
|
128
|
+
// Set absolute timeout per worker
|
|
129
|
+
const timeoutHandle = setTimeout(() => {
|
|
130
|
+
this.forceCompleteWorker(w.workerName, 'timeout', `Worker exceeded max duration of ${workerTimeoutMs}ms`);
|
|
131
|
+
}, workerTimeoutMs);
|
|
132
|
+
timeoutHandle.unref();
|
|
133
|
+
this.workerTimeoutHandles.set(w.workerName, timeoutHandle);
|
|
134
|
+
}
|
|
135
|
+
// Set global workflow timeout
|
|
136
|
+
const globalHandle = setTimeout(() => {
|
|
137
|
+
this.forceCompleteAllRunningWorkers(orchestrationId, `Global workflow timeout of ${memState.globalTimeoutMs}ms exceeded`);
|
|
138
|
+
}, memState.globalTimeoutMs);
|
|
139
|
+
globalHandle.unref();
|
|
140
|
+
this.globalTimeoutHandles.set(orchestrationId, globalHandle);
|
|
141
|
+
console.error(`[WorkflowEngine] Initialized workflow ${orchestrationId} with ${workers.length} workers (timeout: ${workerTimeoutMs}ms/worker, ${memState.globalTimeoutMs}ms global)`);
|
|
142
|
+
return {
|
|
143
|
+
orchestrationId,
|
|
144
|
+
workers: workers.map(w => ({
|
|
145
|
+
workerId: w.workerId,
|
|
146
|
+
workerName: w.workerName,
|
|
147
|
+
tabId: w.tabId,
|
|
148
|
+
})),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Update worker progress with circuit breaker check
|
|
153
|
+
*/
|
|
154
|
+
async updateWorkerProgress(workerName, update) {
|
|
155
|
+
if (update.status || update.iteration !== undefined || update.extractedData !== undefined) {
|
|
156
|
+
await this.stateManager.updateWorkerState(workerName, {
|
|
157
|
+
status: update.status,
|
|
158
|
+
iteration: update.iteration,
|
|
159
|
+
extractedData: update.extractedData,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
if (update.action && update.result) {
|
|
163
|
+
await this.stateManager.addProgressEntry(workerName, update.action, update.result, update.error);
|
|
164
|
+
}
|
|
165
|
+
// Circuit breaker: check for stale data (no progress)
|
|
166
|
+
if (update.extractedData !== undefined) {
|
|
167
|
+
const runtimeState = this.workerRuntimeStates.get(workerName);
|
|
168
|
+
if (runtimeState && !runtimeState.timedOut) {
|
|
169
|
+
const newHash = this.hashData(update.extractedData);
|
|
170
|
+
runtimeState.lastUpdateTime = Date.now();
|
|
171
|
+
if (newHash === runtimeState.lastDataHash) {
|
|
172
|
+
runtimeState.staleCount++;
|
|
173
|
+
// Find max stale iterations from workflow config
|
|
174
|
+
let maxStale = 5;
|
|
175
|
+
for (const ws of this.workflowStates.values()) {
|
|
176
|
+
if (ws.workerStatuses.has(workerName)) {
|
|
177
|
+
maxStale = ws.maxStaleIterations;
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (runtimeState.staleCount >= maxStale) {
|
|
182
|
+
console.error(`[WorkflowEngine] Circuit breaker: Worker "${workerName}" data unchanged for ${maxStale} updates`);
|
|
183
|
+
this.forceCompleteWorker(workerName, 'stale', `No data change for ${maxStale} consecutive updates`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
runtimeState.staleCount = 0;
|
|
188
|
+
runtimeState.lastDataHash = newHash;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Hash extracted data for circuit breaker comparison
|
|
195
|
+
*/
|
|
196
|
+
hashData(data) {
|
|
197
|
+
const str = JSON.stringify(data) ?? '';
|
|
198
|
+
return str.length.toString() + '_' + str.slice(0, 200);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Force-complete a single worker due to timeout or circuit breaker
|
|
202
|
+
*/
|
|
203
|
+
async forceCompleteWorker(workerName, reason, message) {
|
|
204
|
+
// Prevent double-completion
|
|
205
|
+
const runtimeState = this.workerRuntimeStates.get(workerName);
|
|
206
|
+
if (runtimeState) {
|
|
207
|
+
if (runtimeState.timedOut)
|
|
208
|
+
return;
|
|
209
|
+
runtimeState.timedOut = true;
|
|
210
|
+
}
|
|
211
|
+
// Clear the timeout handle
|
|
212
|
+
const handle = this.workerTimeoutHandles.get(workerName);
|
|
213
|
+
if (handle) {
|
|
214
|
+
clearTimeout(handle);
|
|
215
|
+
this.workerTimeoutHandles.delete(workerName);
|
|
216
|
+
}
|
|
217
|
+
console.error(`[WorkflowEngine] Force-completing worker "${workerName}" (${reason}): ${message}`);
|
|
218
|
+
// Complete with PARTIAL status — preserves any data collected so far
|
|
219
|
+
await this.completeWorker(workerName, 'PARTIAL', `[${reason}] ${message}`, null);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Force-complete all running workers in a workflow (global timeout)
|
|
223
|
+
*/
|
|
224
|
+
async forceCompleteAllRunningWorkers(orchestrationId, message) {
|
|
225
|
+
const memState = this.workflowStates.get(orchestrationId);
|
|
226
|
+
if (!memState)
|
|
227
|
+
return;
|
|
228
|
+
console.error(`[WorkflowEngine] Global timeout for workflow ${orchestrationId}: ${message}`);
|
|
229
|
+
const runningWorkers = [];
|
|
230
|
+
for (const [workerName, ws] of memState.workerStatuses) {
|
|
231
|
+
if (ws.status !== 'SUCCESS' && ws.status !== 'PARTIAL' && ws.status !== 'FAIL') {
|
|
232
|
+
runningWorkers.push(workerName);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
for (const workerName of runningWorkers) {
|
|
236
|
+
await this.forceCompleteWorker(workerName, 'timeout', message);
|
|
237
|
+
}
|
|
238
|
+
// Clear global timeout handle
|
|
239
|
+
const handle = this.globalTimeoutHandles.get(orchestrationId);
|
|
240
|
+
if (handle) {
|
|
241
|
+
clearTimeout(handle);
|
|
242
|
+
this.globalTimeoutHandles.delete(orchestrationId);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Mark worker as complete.
|
|
247
|
+
*
|
|
248
|
+
* Race-condition safe: all concurrent calls are serialized via a promise-based mutex.
|
|
249
|
+
* In-memory state is the source of truth for completion counting; file writes are
|
|
250
|
+
* write-behind (persistence/debugging only).
|
|
251
|
+
*/
|
|
252
|
+
async completeWorker(workerName, status, resultSummary, extractedData) {
|
|
253
|
+
// Update the worker scratchpad file (outside the lock — file writes per worker don't conflict)
|
|
254
|
+
await this.stateManager.updateWorkerState(workerName, {
|
|
255
|
+
status,
|
|
256
|
+
extractedData,
|
|
257
|
+
});
|
|
258
|
+
// Serialize completion accounting through the lock to prevent lost updates
|
|
259
|
+
const release = await this.acquireLock();
|
|
260
|
+
try {
|
|
261
|
+
// Find the in-memory workflow state that contains this worker
|
|
262
|
+
let memState;
|
|
263
|
+
for (const s of this.workflowStates.values()) {
|
|
264
|
+
if (s.workerStatuses.has(workerName)) {
|
|
265
|
+
memState = s;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (!memState) {
|
|
270
|
+
// Fallback: no in-memory state (e.g. engine restarted). Fall back to file-based path.
|
|
271
|
+
console.error(`[WorkflowEngine] No in-memory state for worker "${workerName}", falling back to file read`);
|
|
272
|
+
await this._completeWorkerFileFallback(workerName, status, resultSummary);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const prev = memState.workerStatuses.get(workerName);
|
|
276
|
+
const previousStatus = prev.status;
|
|
277
|
+
const wasAlreadyCompleted = previousStatus === 'SUCCESS' || previousStatus === 'PARTIAL' || previousStatus === 'FAIL';
|
|
278
|
+
// Update worker entry in-memory
|
|
279
|
+
memState.workerStatuses.set(workerName, { status, resultSummary });
|
|
280
|
+
// Adjust counters — prevent double-counting on repeated calls
|
|
281
|
+
if (!wasAlreadyCompleted) {
|
|
282
|
+
if (status === 'SUCCESS' || status === 'PARTIAL') {
|
|
283
|
+
memState.completedWorkers++;
|
|
284
|
+
}
|
|
285
|
+
else if (status === 'FAIL') {
|
|
286
|
+
memState.failedWorkers++;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
// Status transition between completed states — adjust counters accordingly
|
|
291
|
+
const wasCompleted = previousStatus === 'SUCCESS' || previousStatus === 'PARTIAL';
|
|
292
|
+
const wasFailed = previousStatus === 'FAIL';
|
|
293
|
+
const isNowCompleted = status === 'SUCCESS' || status === 'PARTIAL';
|
|
294
|
+
const isNowFailed = status === 'FAIL';
|
|
295
|
+
if (wasCompleted && isNowFailed) {
|
|
296
|
+
memState.completedWorkers--;
|
|
297
|
+
memState.failedWorkers++;
|
|
298
|
+
}
|
|
299
|
+
else if (wasFailed && isNowCompleted) {
|
|
300
|
+
memState.failedWorkers--;
|
|
301
|
+
memState.completedWorkers++;
|
|
302
|
+
}
|
|
303
|
+
// Same category transition (e.g. SUCCESS→PARTIAL): no counter change needed
|
|
304
|
+
}
|
|
305
|
+
// Check if all workers are done
|
|
306
|
+
const allDone = Array.from(memState.workerStatuses.values()).every(w => w.status === 'SUCCESS' || w.status === 'PARTIAL' || w.status === 'FAIL');
|
|
307
|
+
memState.allDone = allDone;
|
|
308
|
+
if (allDone) {
|
|
309
|
+
if (memState.failedWorkers === memState.totalWorkers) {
|
|
310
|
+
memState.overallStatus = 'FAILED';
|
|
311
|
+
}
|
|
312
|
+
else if (memState.failedWorkers > 0) {
|
|
313
|
+
memState.overallStatus = 'PARTIAL';
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
memState.overallStatus = 'COMPLETED';
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
memState.overallStatus = 'RUNNING';
|
|
321
|
+
}
|
|
322
|
+
console.error(`[WorkflowEngine] Worker "${workerName}" completed with ${status}. ` +
|
|
323
|
+
`Progress: ${memState.completedWorkers + memState.failedWorkers}/${memState.totalWorkers} ` +
|
|
324
|
+
`(${memState.completedWorkers} ok, ${memState.failedWorkers} failed). ` +
|
|
325
|
+
`Overall: ${memState.overallStatus}`);
|
|
326
|
+
// Write-behind: persist to file for debugging/visibility (not for correctness)
|
|
327
|
+
await this._writeOrchestrationStateBehind(memState);
|
|
328
|
+
}
|
|
329
|
+
finally {
|
|
330
|
+
release();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Write orchestration state to file from in-memory state (write-behind).
|
|
335
|
+
* This is for persistence/debugging only — correctness is maintained in memory.
|
|
336
|
+
*/
|
|
337
|
+
async _writeOrchestrationStateBehind(memState) {
|
|
338
|
+
const workers = Array.from(memState.workerStatuses.entries()).map(([workerName, ws]) => ({
|
|
339
|
+
workerId: workerName, // best-effort: workerId not stored separately in memState
|
|
340
|
+
workerName,
|
|
341
|
+
status: ws.status,
|
|
342
|
+
resultSummary: ws.resultSummary,
|
|
343
|
+
}));
|
|
344
|
+
const orchState = {
|
|
345
|
+
orchestrationId: memState.orchestrationId,
|
|
346
|
+
status: memState.overallStatus,
|
|
347
|
+
createdAt: memState.createdAt,
|
|
348
|
+
updatedAt: Date.now(),
|
|
349
|
+
task: memState.task,
|
|
350
|
+
workers,
|
|
351
|
+
completedWorkers: memState.completedWorkers,
|
|
352
|
+
failedWorkers: memState.failedWorkers,
|
|
353
|
+
};
|
|
354
|
+
try {
|
|
355
|
+
await this.stateManager.writeOrchestrationState(orchState);
|
|
356
|
+
}
|
|
357
|
+
catch (err) {
|
|
358
|
+
// Write-behind failure is non-fatal — in-memory state remains correct
|
|
359
|
+
console.error(`[WorkflowEngine] Write-behind failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Fallback for completeWorker when no in-memory state exists (engine restart scenario).
|
|
364
|
+
* Uses the original file-based read-modify-write approach.
|
|
365
|
+
*/
|
|
366
|
+
async _completeWorkerFileFallback(workerName, status, resultSummary) {
|
|
367
|
+
const orch = await this.stateManager.readOrchestrationState();
|
|
368
|
+
if (!orch)
|
|
369
|
+
return;
|
|
370
|
+
const workerIdx = orch.workers.findIndex(w => w.workerName === workerName);
|
|
371
|
+
if (workerIdx === -1)
|
|
372
|
+
return;
|
|
373
|
+
const previousStatus = orch.workers[workerIdx].status;
|
|
374
|
+
const wasAlreadyCompleted = previousStatus === 'SUCCESS' || previousStatus === 'PARTIAL' || previousStatus === 'FAIL';
|
|
375
|
+
orch.workers[workerIdx].status = status;
|
|
376
|
+
orch.workers[workerIdx].resultSummary = resultSummary;
|
|
377
|
+
if (!wasAlreadyCompleted) {
|
|
378
|
+
if (status === 'SUCCESS' || status === 'PARTIAL') {
|
|
379
|
+
orch.completedWorkers++;
|
|
380
|
+
}
|
|
381
|
+
else if (status === 'FAIL') {
|
|
382
|
+
orch.failedWorkers++;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
const wasCompleted = previousStatus === 'SUCCESS' || previousStatus === 'PARTIAL';
|
|
387
|
+
const wasFailed = previousStatus === 'FAIL';
|
|
388
|
+
const isNowCompleted = status === 'SUCCESS' || status === 'PARTIAL';
|
|
389
|
+
const isNowFailed = status === 'FAIL';
|
|
390
|
+
if (wasCompleted && isNowFailed) {
|
|
391
|
+
orch.completedWorkers--;
|
|
392
|
+
orch.failedWorkers++;
|
|
393
|
+
}
|
|
394
|
+
else if (wasFailed && isNowCompleted) {
|
|
395
|
+
orch.failedWorkers--;
|
|
396
|
+
orch.completedWorkers++;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
const allDone = orch.workers.every(w => w.status === 'SUCCESS' || w.status === 'PARTIAL' || w.status === 'FAIL');
|
|
400
|
+
if (allDone) {
|
|
401
|
+
if (orch.failedWorkers === orch.workers.length) {
|
|
402
|
+
orch.status = 'FAILED';
|
|
403
|
+
}
|
|
404
|
+
else if (orch.failedWorkers > 0) {
|
|
405
|
+
orch.status = 'PARTIAL';
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
orch.status = 'COMPLETED';
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
orch.status = 'RUNNING';
|
|
413
|
+
}
|
|
414
|
+
await this.stateManager.writeOrchestrationState(orch);
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Get current orchestration status.
|
|
418
|
+
* Returns in-memory state when available (most current); falls back to file.
|
|
419
|
+
*/
|
|
420
|
+
async getOrchestrationStatus() {
|
|
421
|
+
// If there is exactly one active workflow in memory, return it
|
|
422
|
+
if (this.workflowStates.size > 0) {
|
|
423
|
+
// Return the most recently created workflow
|
|
424
|
+
let latest;
|
|
425
|
+
for (const s of this.workflowStates.values()) {
|
|
426
|
+
if (!latest || s.createdAt > latest.createdAt) {
|
|
427
|
+
latest = s;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (latest) {
|
|
431
|
+
const workers = Array.from(latest.workerStatuses.entries()).map(([workerName, ws]) => ({
|
|
432
|
+
workerId: workerName,
|
|
433
|
+
workerName,
|
|
434
|
+
status: ws.status,
|
|
435
|
+
resultSummary: ws.resultSummary,
|
|
436
|
+
}));
|
|
437
|
+
return {
|
|
438
|
+
orchestrationId: latest.orchestrationId,
|
|
439
|
+
status: latest.overallStatus,
|
|
440
|
+
createdAt: latest.createdAt,
|
|
441
|
+
updatedAt: Date.now(),
|
|
442
|
+
task: latest.task,
|
|
443
|
+
workers,
|
|
444
|
+
completedWorkers: latest.completedWorkers,
|
|
445
|
+
failedWorkers: latest.failedWorkers,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// Fallback to file-based state (e.g. engine restarted)
|
|
450
|
+
return this.stateManager.readOrchestrationState();
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Get all worker states
|
|
454
|
+
*/
|
|
455
|
+
async getAllWorkerStates() {
|
|
456
|
+
return this.stateManager.getAllWorkerStates();
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Get worker state by name
|
|
460
|
+
*/
|
|
461
|
+
async getWorkerState(workerName) {
|
|
462
|
+
return this.stateManager.readWorkerState(workerName);
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Collect final results from all workers.
|
|
466
|
+
* Uses in-memory orchestration status for correctness; reads per-worker detail from files.
|
|
467
|
+
*/
|
|
468
|
+
async collectResults() {
|
|
469
|
+
const orch = await this.getOrchestrationStatus();
|
|
470
|
+
if (!orch)
|
|
471
|
+
return null;
|
|
472
|
+
const workerResults = [];
|
|
473
|
+
const workerStates = await this.stateManager.getAllWorkerStates();
|
|
474
|
+
for (const state of workerStates) {
|
|
475
|
+
workerResults.push({
|
|
476
|
+
workerId: state.workerId,
|
|
477
|
+
workerName: state.workerName,
|
|
478
|
+
tabId: state.tabId,
|
|
479
|
+
status: state.status === 'SUCCESS' ? 'SUCCESS'
|
|
480
|
+
: state.status === 'PARTIAL' ? 'PARTIAL'
|
|
481
|
+
: 'FAIL',
|
|
482
|
+
resultSummary: `${state.status}: ${state.iteration} iterations`,
|
|
483
|
+
dataExtracted: state.extractedData,
|
|
484
|
+
iterations: state.iteration,
|
|
485
|
+
errors: state.errors,
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
const completedCount = workerResults.filter(r => r.status === 'SUCCESS' || r.status === 'PARTIAL').length;
|
|
489
|
+
const failedCount = workerResults.filter(r => r.status === 'FAIL').length;
|
|
490
|
+
const duration = Date.now() - orch.createdAt;
|
|
491
|
+
return {
|
|
492
|
+
orchestrationId: orch.orchestrationId,
|
|
493
|
+
status: orch.status === 'COMPLETED' ? 'COMPLETED'
|
|
494
|
+
: orch.status === 'PARTIAL' ? 'PARTIAL'
|
|
495
|
+
: 'FAILED',
|
|
496
|
+
workerResults,
|
|
497
|
+
completedCount,
|
|
498
|
+
failedCount,
|
|
499
|
+
duration,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Cleanup workflow resources
|
|
504
|
+
*/
|
|
505
|
+
async cleanupWorkflow(sessionId) {
|
|
506
|
+
// Get all workers from orchestration state
|
|
507
|
+
const orch = await this.getOrchestrationStatus();
|
|
508
|
+
if (!orch)
|
|
509
|
+
return;
|
|
510
|
+
// Delete workers (which closes tabs and contexts)
|
|
511
|
+
for (const worker of orch.workers) {
|
|
512
|
+
try {
|
|
513
|
+
await this.sessionManager.deleteWorker(sessionId, worker.workerId);
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
// Worker might already be deleted
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
// Clear all timeout handles for this workflow's workers
|
|
520
|
+
for (const worker of orch.workers) {
|
|
521
|
+
const handle = this.workerTimeoutHandles.get(worker.workerName);
|
|
522
|
+
if (handle) {
|
|
523
|
+
clearTimeout(handle);
|
|
524
|
+
this.workerTimeoutHandles.delete(worker.workerName);
|
|
525
|
+
}
|
|
526
|
+
this.workerRuntimeStates.delete(worker.workerName);
|
|
527
|
+
}
|
|
528
|
+
// Clear global timeout
|
|
529
|
+
const globalHandle = this.globalTimeoutHandles.get(orch.orchestrationId);
|
|
530
|
+
if (globalHandle) {
|
|
531
|
+
clearTimeout(globalHandle);
|
|
532
|
+
this.globalTimeoutHandles.delete(orch.orchestrationId);
|
|
533
|
+
}
|
|
534
|
+
// Remove in-memory state for this workflow
|
|
535
|
+
this.workflowStates.delete(orch.orchestrationId);
|
|
536
|
+
// Cleanup state files
|
|
537
|
+
await this.stateManager.cleanup();
|
|
538
|
+
console.error(`[WorkflowEngine] Cleaned up workflow resources`);
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Generate MCP tool documentation from a ToolEntry array.
|
|
542
|
+
* Groups tools by category and formats each tool with its parameters.
|
|
543
|
+
*/
|
|
544
|
+
generateToolDocs(tools, tabId) {
|
|
545
|
+
const categoryDisplayNames = {
|
|
546
|
+
navigation: 'Navigation',
|
|
547
|
+
interaction: 'Interaction',
|
|
548
|
+
content: 'Content Reading',
|
|
549
|
+
javascript: 'JavaScript Execution',
|
|
550
|
+
composite: 'Smart Actions',
|
|
551
|
+
network: 'Network',
|
|
552
|
+
tabs: 'Tabs',
|
|
553
|
+
media: 'Media',
|
|
554
|
+
emulation: 'Emulation',
|
|
555
|
+
orchestration: 'Orchestration',
|
|
556
|
+
worker: 'Worker',
|
|
557
|
+
performance: 'Performance',
|
|
558
|
+
lifecycle: 'Lifecycle',
|
|
559
|
+
};
|
|
560
|
+
// Group tools by category
|
|
561
|
+
const grouped = {};
|
|
562
|
+
for (const tool of tools) {
|
|
563
|
+
const cat = tool.category;
|
|
564
|
+
if (!grouped[cat])
|
|
565
|
+
grouped[cat] = [];
|
|
566
|
+
grouped[cat].push(tool);
|
|
567
|
+
}
|
|
568
|
+
const sections = [];
|
|
569
|
+
for (const [category, categoryTools] of Object.entries(grouped)) {
|
|
570
|
+
const displayName = categoryDisplayNames[category] || category;
|
|
571
|
+
const toolDocs = [];
|
|
572
|
+
for (const tool of categoryTools) {
|
|
573
|
+
const fullName = `mcp__claude-chrome-parallel__${tool.name}`;
|
|
574
|
+
const props = tool.inputSchema.properties;
|
|
575
|
+
const paramLines = [];
|
|
576
|
+
for (const [paramName, paramSchema] of Object.entries(props)) {
|
|
577
|
+
if (paramName === 'tabId')
|
|
578
|
+
continue; // handled separately below
|
|
579
|
+
const paramType = paramSchema.type ?? 'unknown';
|
|
580
|
+
const paramDesc = paramSchema.description ? ` — ${paramSchema.description}` : '';
|
|
581
|
+
paramLines.push(`- ${paramName}: ${paramType}${paramDesc}`);
|
|
582
|
+
}
|
|
583
|
+
paramLines.push(`- tabId: "${tabId}" (required, always include)`);
|
|
584
|
+
toolDocs.push(`**${fullName}**\n${tool.description}\nParameters:\n${paramLines.join('\n')}`);
|
|
585
|
+
}
|
|
586
|
+
sections.push(`### ${displayName}\n\n${toolDocs.join('\n\n')}`);
|
|
587
|
+
}
|
|
588
|
+
return sections.join('\n\n');
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Generate worker agent prompt for Background Task
|
|
592
|
+
*/
|
|
593
|
+
generateWorkerPrompt(workerId, workerName, tabId, task, successCriteria, manifestTools, targetUrl) {
|
|
594
|
+
// CE: Isolate — inject only target domain's knowledge
|
|
595
|
+
let domainKnowledgeSection = '';
|
|
596
|
+
if (targetUrl) {
|
|
597
|
+
const domain = (0, domain_memory_1.extractDomainFromUrl)(targetUrl);
|
|
598
|
+
if (domain) {
|
|
599
|
+
const entries = (0, domain_memory_1.getDomainMemory)().query(domain);
|
|
600
|
+
if (entries.length > 0) {
|
|
601
|
+
const lines = entries.map((e) => `- **${e.key}**: ${e.value} (confidence: ${e.confidence.toFixed(1)})`);
|
|
602
|
+
domainKnowledgeSection = `\n\n## Domain Knowledge (${domain})\n\nPreviously learned knowledge for this domain. Validate after use with memory_validate.\n\n${lines.join('\n')}`;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return `## Chrome-Sisyphus Worker Agent
|
|
607
|
+
|
|
608
|
+
You are an autonomous browser automation worker. Execute your assigned task completely before returning.
|
|
609
|
+
|
|
610
|
+
### Configuration
|
|
611
|
+
- Worker ID: ${workerId}
|
|
612
|
+
- Worker Name: ${workerName}
|
|
613
|
+
- Tab ID: ${tabId}
|
|
614
|
+
- Scratchpad: .agent/chrome-sisyphus/worker-${workerName}.md
|
|
615
|
+
|
|
616
|
+
### Your Task
|
|
617
|
+
${task}
|
|
618
|
+
|
|
619
|
+
### Success Criteria
|
|
620
|
+
${successCriteria}
|
|
621
|
+
|
|
622
|
+
---
|
|
623
|
+
|
|
624
|
+
## CRITICAL RULES
|
|
625
|
+
|
|
626
|
+
1. **ALWAYS include tabId="${tabId}" in EVERY MCP tool call**
|
|
627
|
+
2. **Update scratchpad after EVERY action using Write tool**
|
|
628
|
+
3. **Maximum 5 iterations**
|
|
629
|
+
4. **Return compressed result only - NO screenshots or full DOM**
|
|
630
|
+
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
${manifestTools && manifestTools.length > 0
|
|
634
|
+
? `## Pre-loaded MCP Tools (verified — DO NOT call ToolSearch)
|
|
635
|
+
|
|
636
|
+
The following tools are pre-loaded and ready to use immediately.
|
|
637
|
+
CRITICAL: Do NOT call ToolSearch. These tool schemas are verified and current.
|
|
638
|
+
|
|
639
|
+
${this.generateToolDocs(manifestTools, tabId)}`
|
|
640
|
+
: `## Available MCP Tools
|
|
641
|
+
|
|
642
|
+
### Navigation
|
|
643
|
+
mcp__chrome-parallel__navigate
|
|
644
|
+
- url: string (required)
|
|
645
|
+
- tabId: "${tabId}" (required)
|
|
646
|
+
|
|
647
|
+
### Interaction
|
|
648
|
+
mcp__chrome-parallel__computer
|
|
649
|
+
- action: "left_click" | "type" | "screenshot" | "scroll" | "key"
|
|
650
|
+
- tabId: "${tabId}" (required)
|
|
651
|
+
- coordinate: [x, y] (for clicks)
|
|
652
|
+
- text: string (for typing)
|
|
653
|
+
|
|
654
|
+
### Page Reading
|
|
655
|
+
mcp__chrome-parallel__read_page
|
|
656
|
+
- tabId: "${tabId}" (required)
|
|
657
|
+
- filter: "interactive" | "all"
|
|
658
|
+
|
|
659
|
+
### Element Finding
|
|
660
|
+
mcp__chrome-parallel__find
|
|
661
|
+
- query: string (natural language)
|
|
662
|
+
- tabId: "${tabId}" (required)
|
|
663
|
+
|
|
664
|
+
### Form Input
|
|
665
|
+
mcp__chrome-parallel__form_input
|
|
666
|
+
- ref: string (element reference from find/read_page)
|
|
667
|
+
- value: string | boolean | number
|
|
668
|
+
- tabId: "${tabId}" (required)
|
|
669
|
+
|
|
670
|
+
### JavaScript Execution
|
|
671
|
+
mcp__chrome-parallel__javascript_tool
|
|
672
|
+
- action: "javascript_exec"
|
|
673
|
+
- text: string (JS code)
|
|
674
|
+
- tabId: "${tabId}" (required)`}
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
## Execution Algorithm (Ralph Loop)
|
|
679
|
+
|
|
680
|
+
for iteration in 1..5:
|
|
681
|
+
1. Assess current state (read page or check scratchpad)
|
|
682
|
+
2. Decide next action
|
|
683
|
+
3. Execute MCP tool with tabId="${tabId}"
|
|
684
|
+
4. Update scratchpad with Write tool
|
|
685
|
+
5. Check if success criteria met -> if yes, return SUCCESS
|
|
686
|
+
|
|
687
|
+
---
|
|
688
|
+
|
|
689
|
+
## Final Output Format
|
|
690
|
+
|
|
691
|
+
When done, your LAST message MUST contain:
|
|
692
|
+
|
|
693
|
+
---RESULT---
|
|
694
|
+
{
|
|
695
|
+
"status": "SUCCESS" | "PARTIAL" | "FAIL",
|
|
696
|
+
"workerName": "${workerName}",
|
|
697
|
+
"resultSummary": "Brief summary (max 100 chars)",
|
|
698
|
+
"dataExtracted": {
|
|
699
|
+
// Your extracted data here
|
|
700
|
+
},
|
|
701
|
+
"scratchpadPath": ".agent/chrome-sisyphus/worker-${workerName}.md",
|
|
702
|
+
"iterations": 3,
|
|
703
|
+
"errors": [],
|
|
704
|
+
"EXIT_SIGNAL": true
|
|
705
|
+
}
|
|
706
|
+
---END---
|
|
707
|
+
|
|
708
|
+
---
|
|
709
|
+
|
|
710
|
+
## Error Handling
|
|
711
|
+
|
|
712
|
+
| Error | Strategy |
|
|
713
|
+
|-------|----------|
|
|
714
|
+
| Element not found | Try find with different query |
|
|
715
|
+
| Page timeout | Refresh and retry |
|
|
716
|
+
| Captcha | Report FAIL |
|
|
717
|
+
| Network error | Wait 2s, retry |
|
|
718
|
+
|
|
719
|
+
Now begin your task. Navigate to the target site and complete the assigned work.${domainKnowledgeSection}`;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
exports.WorkflowEngine = WorkflowEngine;
|
|
723
|
+
// Singleton instance
|
|
724
|
+
let workflowEngineInstance = null;
|
|
725
|
+
function getWorkflowEngine() {
|
|
726
|
+
if (!workflowEngineInstance) {
|
|
727
|
+
workflowEngineInstance = new WorkflowEngine();
|
|
728
|
+
}
|
|
729
|
+
return workflowEngineInstance;
|
|
730
|
+
}
|
|
731
|
+
//# sourceMappingURL=workflow-engine.js.map
|